cause it doesn’t know how to translate virtual addresses yet; it doesn’t have a page table
yet. The symbol entrypgdir refers to an address in high memory, and the macro
V2P_WO (0220) subtracts KERNBASE in order to find the physical address. To enable the
paging hardware, xv6 sets the flag CR0_PG in the control register %cr0.
The processor is still executing instructions at low addresses after paging is en-
abled, which works since entrypgdir maps low addresses. If xv6 had omitted entry 0
from entrypgdir, the computer would have crashed when trying to execute the in-
struction after the one that enabled paging.
Now entry needs to transfer to the kernel’s C code, and run it in high memory.
First it makes the stack pointer, %esp, point to memory to be used as a stack (1054). All
symbols have high addresses, including stack, so the stack will still be valid even
when the low mappings are removed. Finally entry jumps to main, which is also a
high address. The indirect jump is needed because the assembler would otherwise
generate a PC-relative direct jump, which would execute the low-memory version of
main. Main cannot return, since the there’s no return PC on the stack. Now the kernel
is running in high addresses in the function main (1217).
Code: creating the first process
After main initializes several devices and subsystems, it creates the first process by
calling userinit (1239). Userinit’s first action is to call allocproc. The job of al-
locproc (2205) is to allocate a slot (a struct proc) in the process table and to initial-
ize the parts of the process’s state required for its kernel thread to execute. Allocproc
is called for each new process, while userinit is called only for the very first process.
Allocproc scans the proc table for a slot with state UNUSED (2211-2213). When it finds
an unused slot, allocproc sets the state to EMBRYO to mark it as used and gives the
process a unique pid (2201-2219). Next, it tries to allocate a kernel stack for the process’s
kernel thread. If the memory allocation fails, allocproc changes the state back to UN-
USED and returns zero to signal failure.
Now allocproc must set up the new process’s kernel stack. allocproc is written
so that it can be used by fork as well as when creating the first process. allocproc
sets up the new process with a specially prepared kernel stack and set of kernel regis-
ters that cause it to ‘‘return’’ to user space when it first runs. The layout of the pre-
pared kernel stack will be as shown in Figure 1-3. allocproc does part of this work
by setting up return program counter values that will cause the new process’s kernel
thread to first execute in forkret and then in trapret (2236-2241). The kernel thread
will start executing with register contents copied from p->context. Thus setting p-
>context->eip to forkret will cause the kernel thread to execute at the start of
forkret (2533). This function will return to whatever address is at the bottom of the
stack. The context switch code (2708) sets the stack pointer to point just beyond the
end of p->context. allocproc places p->context on the stack, and puts a pointer to
trapret just above it; that is where forkret will return. trapret restores user regis-
ters from values stored at the top of the kernel stack and jumps into the process (3027).
This setup is the same for ordinary fork and for creating the first process, though in
the latter case the process will start executing at user-space location zero rather than at
DRAFT as of August 28, 2012 20 http://pdos.csail.mit.edu/6.828/xv6/
V2P_WO+code
CR0_PG+code
main+code
main+code
main+code
allocproc+code
EMBRYO+code
pid+code
forkret+code
trapret+code
p->context+code
forkret+code
trapret+code
forkret+code
trapret+code