xv6, line by line
tour 43
Tours43 A system call, CSR by CSR

Tour 43 · The dance of privilege · about 31 minutes · 20 steps

A system call, CSR by CSR

Tour 5: Life of a system call followed echo hi’s write(1, "hi", 2) through the kernel’s layers. This tour follows the same system call with a different lens: the control and status registers (CSRs) and the three answers to the master question, which mode, which stack, which page table, at every instruction that changes one of them.

The values are not invented. We attached gdb to QEMU, stopped echo on its ecall, and read every relevant register after each step, in this build (kernel at commit 06aad25, three harts). In our run echo was pid 3, in process slot 2, running on hart 0.

The lesson you should leave with: the hardware does very little. ecall changes the mode, the pc and four CSRs. sret changes the mode, the pc and three bits. Everything else, the stack, the page table, the registers, the trap vector, is a sequence of ordinary instructions that xv6 wrote and that you can read below.

Best after: 5. Life of a system call, 7. The trampoline and the trapframe, 41. Every transition: mode, stack and page table

Who is running where

The machine has three harts. When the tour starts:

Hart What it is doing
0 Running echo (pid 3) in user mode: the process this tour follows
1 In its scheduler, with nothing to run
2 In its scheduler, with nothing to run

The shell (pid 2) is asleep in kwait; init (pid 1) is asleep too. So echo is the only user process that can trap, which is what let us catch its trap with gdb without confusing it with anyone else’s.

Three harts are running. This tour follows one path through the code, but the machine has three CPUs executing at the same time. Watch the locks held display at the top of each step, and read the Meanwhile, on other harts boxes: they show what the other CPUs could be doing at that very moment.
The route
  1. 1Before the ecall, the CSRs hold leftovers user/usys.S
  2. 2ecall: the hardware's whole contribution user/usys.S
  3. 3What ecall did not touch kernel/trampoline.S
  4. 4sscratch: one register's worth of room kernel/trampoline.S
  5. 5Thirty-one stores through the user page table kernel/trampoline.S
  6. 6ld sp: a stack that cannot be used yet kernel/trampoline.S
  7. 7csrw satp: the page table changes under the running code kernel/trampoline.S
  8. 8jalr t0: into C, with ra pointing back into the trampoline kernel/trampoline.S
  9. 9usertrap checks SPP, then moves stvec kernel/trap.c
  10. 10Copy sepc before anything can overwrite it kernel/trap.c
  11. 11intr_on, and a tick that was waiting kernel/trap.c
  12. 12The system call runs, deeper on the kernel stack kernel/syscall.c
  13. 13prepare_return: interrupts off, then stvec back to the trampoline kernel/trap.c
  14. 14The trapframe's message for next time kernel/trap.c
  15. 15Arming sret: SPP, SPIE and sepc kernel/trap.c
  16. 16usertrap hands back echo's satp kernel/trap.c
  17. 17userret: the page table goes back first kernel/trampoline.S
  18. 18ld sp, then every register, a0 last kernel/trampoline.S
  19. 19sret, exactly kernel/trampoline.S
  20. 20Back in echo, and the ledger user/usys.S

Keys: ← → step · Home start