xv6, line by line
tour 42
Tours42 One hart's stacks, from power-on to the first user instruction

Tour 42 · The dance of privilege · about 30 minutes · 21 steps

One hart's stacks, from power-on to the first user instruction

Every instruction a CPU executes runs with some value in sp, and that value is always a claim: “this is my stack.” This tour follows the claim on hart 0 from the moment QEMU powers it on, when sp is simply 0, to the moment init executes its first user instruction on a stack that kexec built for it.

On the way, sp points into four different pieces of memory: nothing at all, hart 0’s slice of the boot array stack0 (which quietly becomes the scheduler stack), the first process’s empty kernel stack, and finally init’s user stack. Only four instructions in the whole kernel ever move sp from one stack to another. This tour runs three of them and points at the fourth.

Every address in this tour comes from this build: xv6/kernel/kernel.sym, and gdb attached to QEMU, stopping at each point and reading sp. One surprise from the measurements shapes the story: the first process doesn’t stay on hart 0. Partway through, the thread leaves the hart, and the tour follows the stack, not the hart. That turns out to be the main lesson.

Best after: 2. Power-on to main, on every hart at once, 4. From the first process to the shell prompt, 41. Every transition: mode, stack and page table

Who is running where

Three harts power on together. All three run the same early code, each on its own stack; this tour watches hart 0, which builds the kernel.

Hart What it is doing
0 The tour’s hart: boots, runs main's setup, becomes a scheduler
1 Boots in parallel, then spins in main until hart 0 says started
2 The same as hart 1
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. 1Power-on: pc = 0x1000, sp = 0 kernel/memlayout.h
  2. 2_entry gives each hart a slice of stack0 kernel/entry.S
  3. 3One array, eight slices, no guard page kernel/start.c
  4. 4start runs in machine mode on the boot stack kernel/start.c
  5. 5mret changes the mode, and leaves sp alone kernel/start.c
  6. 6main, on the boot stack with paging off kernel/main.c
  7. 7The kernel stacks exist before any process does kernel/proc.c
  8. 8Paging turns on, and the boot stack survives it kernel/vm.c
  9. 9allocproc forges a context that points at an empty stack kernel/proc.c
  10. 10main calls scheduler, and the boot stack changes its job kernel/main.c
  11. 11Hart 0's scheduler picks init kernel/proc.c
  12. 12swtch saves where the scheduler stands kernel/swtch.S
  13. 13One load, and sp is on init's kernel stack kernel/swtch.S
  14. 14forkret releases the lock; interrupts stay off kernel/proc.c
  15. 15init sleeps, and hart 0 goes back to its own stack kernel/proc.c
  16. 16kexec builds init's user stack kernel/exec.c
  17. 17prepare_return, on whatever hart this is kernel/trap.c
  18. 18forkret jumps into the trampoline kernel/proc.c
  19. 19The page table switches; sp points at nothing kernel/trampoline.S
  20. 20ld sp, and init has a stack of its own kernel/trampoline.S
  21. 21sret, and the first user instruction user/ulib.c

Keys: ← → step · Home start