owned this note
owned this note
Published
Linked with GitHub
# XV-6 啟動程序以及 main.c 解析以及論文啟動的 Related Work
## 啟動程序

---
## Boot loader
請參考 :
[XV6 A simple, Unix-like Teaching Operating System Before Read & Appendix](https://hackmd.io/AwdghgHArApgRgRgLQ2AgTEgLAkMkCcwAZnEsQMZwDMAJtcFuhJUA===#appendix-b-the-boot-loader)
簡單來說,Boot loader 裡面分兩個檔案(bootasm.S , bootmain.c),先執行 bootasm.S 直到 "call bootmain" 跳到 bootmain.c。接著在 bootmain.c 裡會載入 Kernal ,Kernal 以 ELF 的方式存著,所以利用 elf.h 來讀檔隨後 Call "Entry()" 執行 entry.S,在 entrys.S 中執行 "mov $main, %eax","jmp *%eax" 進入 main() 也等於切換到 Kernal。
**詳細組語介紹可參閱:**
[xv6启动源码阅读- CSDN博客 - CSDN Blog](http://blog.csdn.net/vally1989/article/details/71796482)
---
## Kernal
---
### main.c
```c=
// Bootstrap processor starts running C code here.
// Allocate a real stack and switch to it, first
// doing some setup required for memory allocator to work.
int
main(void)
{
kinit1(end, P2V(4*1024*1024)); // phys page allocator
kvmalloc(); // kernel page table
mpinit(); // collect info about this machine
lapicinit();
seginit(); // set up segments
cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
picinit(); // interrupt controller
ioapicinit(); // another interrupt controller
consoleinit(); // I/O devices & their interrupts
uartinit(); // serial port
pinit(); // process table
tvinit(); // trap vectors
binit(); // buffer cache
fileinit(); // file table
iinit(); // inode cache
ideinit(); // disk
if(!ismp)
timerinit(); // uniprocessor timer
startothers(); // start other processors
kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
userinit(); // first user process
// Finish setting up this processor in mpmain.
mpmain();
}
```
- [kinit1()](https://hackmd.io/CwBgHARgTAZsUFoCcBGESHACYFNjIEMR8YwwDSp4iUg=#-分配器的初始化-kinit1-kinit2)
- [kvmalloc()](https://hackmd.io/CwBgHARgTAZsUFoCcBGESHACYFNjIEMR8YwwDSp4iUg=#process-address-space)
- mpinit()
- [lapicinit()](https://hackmd.io/GwJgxgjAhhDsAmBaK9bEQFgAwDMlQGYcBWRHAgThwwCMAOWG4jHIA===#-lapicinit)
- seginit()
- picinit()
- ioapicinit()
- consoleinit()
- uartinit()
- pinit()
```c=
void
pinit(void)
{
initlock(&ptable.lock, "ptable");
}
```
- [tvinit()](https://hackmd.io/GwJgxgjAhhDsAmBaK9bEQFgAwDMlQGYcBWRHAgThwwCMAOWG4jHIA===#code-assembly-trap-handlers)
- binit()
- fileinit()
```c=
void
fileinit(void)
{
initlock(&ftable.lock, "ftable");
}
```
- iinit()
```c=
void
fileinit(void)
{
initlock(&ftable.lock, "icache");
}
```
- [ideinit()](https://hackmd.io/GwJgxgjAhhDsAmBaK9bEQFgAwDMlQGYcBWRHAgThwwCMAOWG4jHIA===#code-disk-driver)
- startothers()
- [kinit2()](https://hackmd.io/CwBgHARgTAZsUFoCcBGESHACYFNjIEMR8YwwDSp4iUg=#-分配器的初始化-kinit1-kinit2)
- [userinit()](https://hackmd.io/IYIwTAjALADAzAYwLQHYBsBTESoFYZRIAcKuGSwAZgCbWwRrRECcQA==#process-code)
- mpmain()
### mpenter()
```cpp=
// File:main.c
// Other CPUs jump here from entryother.S.
static void
mpenter(void)
{
switchkvm();
seginit();
lapicinit();
mpmain();
}
```
### mpmain()
```cpp=
// File:main.c
// Common CPU setup code.
static void
mpmain(void)
{
cprintf("cpu%d: starting\n", cpu->id);
idtinit(); // load idt register
xchg(&cpu->started, 1); // tell startothers() we're up
scheduler(); // start running processes
}
```
### startothers()
```cpp=
// File:main.c
// Start the non-boot (AP) processors.
static void
startothers(void)
{
extern uchar _binary_entryother_start[], _binary_entryother_size[];
uchar *code;
struct cpu *c;
char *stack;
// Write entry code to unused memory at 0x7000.
// The linker has placed the image of entryother.S in
// _binary_entryother_start.
code = p2v(0x7000);
memmove(code, _binary_entryother_start, (uint)_binary_entryother_size);
for(c = cpus; c < cpus+ncpu; c++){
if(c == cpus+cpunum()) // We've started already.
continue;
// Tell entryother.S what stack to use, where to enter, and what
// pgdir to use. We cannot use kpgdir yet, because the AP processor
// is running in low memory, so we use entrypgdir for the APs too.
stack = kalloc();
*(void**)(code-4) = stack + KSTACKSIZE;
*(void**)(code-8) = mpenter;
*(int**)(code-12) = (void *) v2p(entrypgdir);
lapicstartap(c->id, v2p(code));
// wait for cpu to finish mpmain()
while(c->started == 0)
;
}
}
```
---
### mpinit()
Collect info about this machine
```cpp=
// File:Mp.c
void
mpinit(void)
{
uchar *p, *e;
struct mp *mp;
struct mpconf *conf;
struct mpproc *proc;
struct mpioapic *ioapic;
bcpu = &cpus[0];
if((conf = mpconfig(&mp)) == 0)
return;
ismp = 1;
lapic = (uint*)conf->lapicaddr;
for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
switch(*p){
case MPPROC:
proc = (struct mpproc*)p;
if(ncpu != proc->apicid){
cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
ismp = 0;
}
if(proc->flags & MPBOOT)
bcpu = &cpus[ncpu];
cpus[ncpu].id = ncpu;
ncpu++;
p += sizeof(struct mpproc);
continue;
case MPIOAPIC:
ioapic = (struct mpioapic*)p;
ioapicid = ioapic->apicno;
p += sizeof(struct mpioapic);
continue;
case MPBUS:
case MPIOINTR:
case MPLINTR:
p += 8;
continue;
default:
cprintf("mpinit: unknown config type %x\n", *p);
ismp = 0;
}
}
if(!ismp){
// Didn't like what we found; fall back to no MP.
ncpu = 1;
lapic = 0;
ioapicid = 0;
return;
}
if(mp->imcrp){
// Bochs doesn't support IMCR, so this doesn't run on Bochs.
// But it would on real hardware.
outb(0x22, 0x70); // Select IMCR
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
}
}
```
---
### seginit()
Set up segments
```cpp=
// File:Vm.c
// Set up CPU's kernel segment descriptors.
// Run once on entry on each CPU.
void
seginit(void)
{
struct cpu *c;
// Map "logical" addresses to virtual addresses using identity map.
// Cannot share a CODE descriptor for both kernel and user
// because it would have to have DPL_USR, but the CPU forbids
// an interrupt from CPL=0 to DPL=3.
c = &cpus[cpunum()];
c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER);
c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
// Map cpu, and curproc
c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0);
lgdt(c->gdt, sizeof(c->gdt));
loadgs(SEG_KCPU << 3);
// Initialize cpu-local storage.
cpu = c;
proc = 0;
}
```
---
### picinit()
Interrupt controller
```c=
// File:Picirq.c
// Initialize the 8259A interrupt controllers.
void
picinit(void)
{
// mask all interrupts
outb(IO_PIC1+1, 0xFF);
outb(IO_PIC2+1, 0xFF);
// Set up master (8259A-1)
// ICW1: 0001g0hi
// g: 0 = edge triggering, 1 = level triggering
// h: 0 = cascaded PICs, 1 = master only
// i: 0 = no ICW4, 1 = ICW4 required
outb(IO_PIC1, 0x11);
// ICW2: Vector offset
outb(IO_PIC1+1, T_IRQ0);
// ICW3: (master PIC) bit mask of IR lines connected to slaves
// (slave PIC) 3-bit # of slave's connection to master
outb(IO_PIC1+1, 1<<IRQ_SLAVE);
// ICW4: 000nbmap
// n: 1 = special fully nested mode
// b: 1 = buffered mode
// m: 0 = slave PIC, 1 = master PIC
// (ignored when b is 0, as the master/slave role
// can be hardwired).
// a: 1 = Automatic EOI mode
// p: 0 = MCS-80/85 mode, 1 = intel x86 mode
outb(IO_PIC1+1, 0x3);
// Set up slave (8259A-2)
outb(IO_PIC2, 0x11); // ICW1
outb(IO_PIC2+1, T_IRQ0 + 8); // ICW2
outb(IO_PIC2+1, IRQ_SLAVE); // ICW3
// NB Automatic EOI mode doesn't tend to work on the slave.
// Linux source code says it's "to be investigated".
outb(IO_PIC2+1, 0x3); // ICW4
// OCW3: 0ef01prs
// ef: 0x = NOP, 10 = clear specific mask, 11 = set specific mask
// p: 0 = no polling, 1 = polling mode
// rs: 0x = NOP, 10 = read IRR, 11 = read ISR
outb(IO_PIC1, 0x68); // clear specific mask
outb(IO_PIC1, 0x0a); // read IRR by default
outb(IO_PIC2, 0x68); // OCW3
outb(IO_PIC2, 0x0a); // OCW3
if(irqmask != 0xFFFF)
picsetmask(irqmask);
}
```
### ioapicinit()
Another interrupt controller
```c=
//File:Ioapic.c
void
ioapicinit(void)
{
int i, id, maxintr;
if(!ismp)
return;
ioapic = (volatile struct ioapic*)IOAPIC;
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
id = ioapicread(REG_ID) >> 24;
if(id != ioapicid)
cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n");
// Mark all interrupts edge-triggered, active high, disabled,
// and not routed to any CPUs.
for(i = 0; i <= maxintr; i++){
ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i));
ioapicwrite(REG_TABLE+2*i+1, 0);
}
}
```
---
### consoleinit()
```
//File:Console.c
void
consoleinit(void)
{
initlock(&cons.lock, "console");
initlock(&input.lock, "input");
devsw[CONSOLE].write = consolewrite;
devsw[CONSOLE].read = consoleread;
cons.locking = 1;
picenable(IRQ_KBD);
ioapicenable(IRQ_KBD, 0);
}
```
---
### binit()
Buffer cache
```c=
void
binit(void)
{
struct buf *b;
initlock(&bcache.lock, "bcache");
//PAGEBREAK!
// Create linked list of buffers
bcache.head.prev = &bcache.head;
bcache.head.next = &bcache.head;
for(b = bcache.buf; b < bcache.buf+NBUF; b++){
b->next = bcache.head.next;
b->prev = &bcache.head;
b->dev = -1;
bcache.head.next->prev = b;
bcache.head.next = b;
}
}
```
---
FreeRTOS 讓使用者自行實作啟動。
## Related Work
我需要
- **kinit1()**
- **kvmalloc()**
- ~~mpinit()~~
- **lapicinit()**
- ~~seginit()~~
- **picinit()**
- **ioapicinit()**
- ~~consoleinit()~~
- **uartinit()**
- **pinit()**
- **tvinit()**
- **binit()**
- ~~fileinit()~~
- ~~iinit()~~
- ~~ideinit()~~
- **startothers()**
- **kinit2()**
- **userinit()**
- **mpmain()**
## 疑問
- 在 Context Switch 時,有與沒有 PageTable 的差別 ?
( Switch Process,Task )