qemu loads kernel at 0x80000000 and causes each CPU to jump there.
kernel.ld
[kernel/kernel.ld] put
entry.S
[kernel/entry.S]
at 0x80000000 and CPU starts executing _entry
function, which set up a stack
for C program to run and jump to function start()
in
start.c
[kernel/start.c:21].
start()
initializes a machine-mode timer interrupt in
timerinit()
[kernel/start.c:57],
disable paging and write medeleg
, mideleg
and sie
registers to delegate all
interrupts and exceptions to supervisor mode [kernel/start.c:36].
It also sets up mepc
and mstatus.MPP
to switch to supervisor mode and jump to
main()
in main.c
[kernel/main.c:11].
main()
creates kernel page table and turn on kernel paging. Kernel pagetable is created by kvmmake()
,
in which it maps the trampoline page for trap entry/exit to the highest virtual address
in kernel pagetable [kernel/vm.c:44].
After that, it installs kernel trap vector by writing kernelvec
address to stvec
register
in trapinithart()
[kernel/trap.c:29].
From this point, all interrupts will trap to kernelvec()
[kernel/kernelvec.S:10].
When an interrupt occur in kernel mode, the following things happen:
- RISC-V hardware does some saving context works, like saving interrupt enabling status, current mode and
pc
pc
is set tostvec
, which points tokernelvec()
kernelvec
save all registers into its stack and then callkerneltrap()
intrap.c
[kernel/trap.c:134].kerneltrap
does some check onsstatus
register then calldevintr()
to handle the interrupt. [kernel/trap.c:146]- If the interrupt is a timer interrupt from machine mode,
kerneltrap
callsyield
to give up the CPU for other processes. [kernel/trap.c:153] - After
kerneltrap
returns,kernelvec
will restores all saved register by coping data from its stack to registers, then unallocate stack [kernel/kernelvec.S:51] - Call
sret
, which tells hardware to restore context to the point before the interrupt occurs. [kernel/kernelvec.S:86]
Summary:
user space asks for new process -> fork()
-> allocproc()
-> forkret()
-> usertrapret()
-> new process in user space
Details:
userinit()
[kernel/proc.c:226] sets up the first user process, which initializes the shell.- When users want to run a program, the shell call
fork()
andexec()
to allocate new process and load program code into memory. fork()
callsallocproc()
to allocate new process, in which it sets up new context to start atforkret
[kernel/proc.c:141].forkret
callsusertrapret()
to let process return to user space [kernel/proc.c:523].usertrapret
sets thestvec
register touservec
. By this way all syscalls, interrupts and exceptions will causepc
jump touservec()
intrampoline.S
[kernel/trampoline.S:16].
Summary:
user space -> uservec()
-> usertrap()
-> kerneltrap()
-> kerneltrapret()
-> usertrapret()
-> user space
Details:
- Interrupt from user space start at
uservec()
in supervisor mode but RISC-V does not switch the page table during trap, so at that timesatp
holds user page table. - To continue executing
satp
must be set to kernel page table, xv6 resolves this problem by mapping trampoline page to the same virtual address in both kernel address space [kernel/vm.c:42] and user address space [kernel/proc.c:181]. - When
uservec
starts,sscratch
holds process's trapframe, which is saved byuserret
[kernel/trampoline.S:137]. It swapsa0
andsscratch
then usea0
to save all other registers in process's trapframe [kernel/trampoline.S:29]. uservec
switch to kernel context by restoring kernel stack pointer and page table [kernel/trampoline.S:67-79]uservec
callsusertrap()
to handle the traps, which sends all the interrupts and exceptions tokerneltrap()
by settingstvec
tokernelvec
[kernel/trap.c:37].- When kernel has finished handling the traps, it calls
usertrapret()
which prepare to return to user space. usertrapret
disables interrupts and changestvec
back touservec
[kernel/trap.c:97]. It also set up trapframe values thatuservec
will need when process next re-enters the kernel and restorepc
,sstatus
andsatp
to prepare for continuing user program execution. After that, it callsuserret
to return to user space.userret
takes process's trapframe and pagetable as arguments. It first switchs to the user page table then restore all saved all registers from trapframe excepta0
. Finally, it swapsa0
andsscratch
, which restores usera0
and save trapframe tosscratch
for later use inuservec
, then callsret
to continue from userpc
in user mode.
- The page is called "trampoline" because it "bounces" between kernel and user space.