xv6, line by line
test yourself

Test yourself · category 4 of 20

Trampoline and trapframe

The border between user and kernel - one trampoline page mapped at the same address in every page table, one trapframe page per process, and the stackless assembly in uservec and userret that crosses it in both directions.

1warm-upChoose one

proc_pagetable maps the trampoline page at TRAMPOLINE in every user page table, and kvmmake maps the same page at the same address in the kernel page table. Why must the address be the same in both?

kernel/proc.c
185 // map the trampoline code (for system call return)
186 // at the highest user virtual address.
187 // only the supervisor uses it, on the way
188 // to/from user space, so not PTE_U.
190 PTE_R | PTE_X) < 0) {
192 return 0;
2warm-upChoose one

The first instruction of uservec is csrw sscratch, a0. Why does it start there?

kernel/trampoline.S
23 #
24 # trap.c sets stvec to point here, so
25 # traps from user space start here,
26 # in supervisor mode, but with a
27 # user page table.
28 #
30 # save user a0 in sscratch so
31 # a0 can be used to get at TRAPFRAME.
32 csrw sscratch, a0
34 # each process has a separate p->trapframe memory area,
35 # but it's mapped to the same virtual address
36 # (TRAPFRAME) in every process's user page table.
37 li a0, TRAPFRAME
3warm-upType a number

What is the virtual address TRAPFRAME in this kernel? (Answer in hex.)

kernel/memlayout.h
46// map the trampoline page to the highest address,
47// in both user and kernel space.
50// map kernel stacks beneath the trampoline,
51// each surrounded by invalid guard pages.
52#define KSTACK(p) (TRAMPOLINE - ((p) + 1) * 2 * PGSIZE)
54// User memory layout.
55// Address zero first:
56// text
57// original data and bss
58// fixed-size stack
59// expandable heap
60// ...
61// TRAPFRAME (p->trapframe, used by the trampoline)
62// TRAMPOLINE (the same page as in the kernel)
decimal, 0x hex or 0b binary
4warm-upTrue or false, and why

True or false: every process has its own trampoline page.

Why?

5warm-upType a number

How many general-purpose registers does uservec save into the trapframe?

kernel/trampoline.S
39 # save the user registers in TRAPFRAME
40 sd ra, 40(a0)
41 sd sp, 48(a0)
42 sd gp, 56(a0)
43 sd tp, 64(a0)
44 sd t0, 72(a0)
45 sd t1, 80(a0)
46 sd t2, 88(a0)
47 sd s0, 96(a0)
48 sd s1, 104(a0)
49 sd a1, 120(a0)
50 sd a2, 128(a0)
51 sd a3, 136(a0)
52 sd a4, 144(a0)
53 sd a5, 152(a0)
54 sd a6, 160(a0)
55 sd a7, 168(a0)
56 sd s2, 176(a0)
57 sd s3, 184(a0)
58 sd s4, 192(a0)
59 sd s5, 200(a0)
60 sd s6, 208(a0)
61 sd s7, 216(a0)
62 sd s8, 224(a0)
63 sd s9, 232(a0)
64 sd s10, 240(a0)
65 sd s11, 248(a0)
66 sd t3, 256(a0)
67 sd t4, 264(a0)
68 sd t5, 272(a0)
69 sd t6, 280(a0)
71 # save the user a0 in p->trapframe->a0
72 csrr t0, sscratch
73 sd t0, 112(a0)
decimal, 0x hex or 0b binary
6warm-upClick the line

In userret, one register must be restored last. Click the line that restores it.

kernel/trampoline.S
114 li a0, TRAPFRAME
116 # restore all but a0 from TRAPFRAME
117 ld ra, 40(a0)
118 ld sp, 48(a0)
119 ld gp, 56(a0)
120 ld tp, 64(a0)
121 ld t0, 72(a0)
122 ld t1, 80(a0)
123 ld t2, 88(a0)
124 ld s0, 96(a0)
125 ld s1, 104(a0)
126 ld a1, 120(a0)
127 ld a2, 128(a0)
128 ld a3, 136(a0)
129 ld a4, 144(a0)
130 ld a5, 152(a0)
131 ld a6, 160(a0)
132 ld a7, 168(a0)
133 ld s2, 176(a0)
134 ld s3, 184(a0)
135 ld s4, 192(a0)
136 ld s5, 200(a0)
137 ld s6, 208(a0)
138 ld s7, 216(a0)
139 ld s8, 224(a0)
140 ld s9, 232(a0)
141 ld s10, 240(a0)
142 ld s11, 248(a0)
143 ld t3, 256(a0)
144 ld t4, 264(a0)
145 ld t5, 272(a0)
146 ld t6, 280(a0)
148 # restore user a0
149 ld a0, 112(a0)
151 # return to user mode and user pc.
152 # prepare_return() sets up sstatus and sepc.
153 sret

Your pick: none yet (click a line in the code)

7warm-upPut in order

Put the steps of userret in the order it executes them.

kernel/trampoline.S
100.globl userret
102 # usertrap() returns here, with user satp in a0.
103 # return from kernel to user.
105 # flush icache, in case this is the first time
106 # we're running this proc on this hart.
107 fence.i
109 # switch to the user page table.
110 sfence.vma zero, zero
111 csrw satp, a0
112 sfence.vma zero, zero
114 li a0, TRAPFRAME
116 # restore all but a0 from TRAPFRAME
117 ld ra, 40(a0)
118 ld sp, 48(a0)
119 ld gp, 56(a0)
120 ld tp, 64(a0)
121 ld t0, 72(a0)
122 ld t1, 80(a0)
123 ld t2, 88(a0)
124 ld s0, 96(a0)
125 ld s1, 104(a0)
126 ld a1, 120(a0)
127 ld a2, 128(a0)
128 ld a3, 136(a0)
129 ld a4, 144(a0)
130 ld a5, 152(a0)
131 ld a6, 160(a0)
132 ld a7, 168(a0)
133 ld s2, 176(a0)
134 ld s3, 184(a0)
135 ld s4, 192(a0)
136 ld s5, 200(a0)
137 ld s6, 208(a0)
138 ld s7, 216(a0)
139 ld s8, 224(a0)
140 ld s9, 232(a0)
141 ld s10, 240(a0)
142 ld s11, 248(a0)
143 ld t3, 256(a0)
144 ld t4, 264(a0)
145 ld t5, 272(a0)
146 ld t6, 280(a0)
148 # restore user a0
149 ld a0, 112(a0)
151 # return to user mode and user pc.
152 # prepare_return() sets up sstatus and sepc.
153 sret
  1. fence.i: make this hart’s instruction fetches see what is now in memory
  2. sret: drop to user mode at sepc
  3. li a0, TRAPFRAME
  4. load sp and the other user registers, except a0
  5. ld a0, 112(a0): the user’s a0
  6. csrw satp, a0 between two sfence.vma: install the user page table
8warm-upChoose one

Neither the TRAMPOLINE nor the TRAPFRAME mapping has PTE_U. What does that achieve?

kernel/proc.c
185 // map the trampoline code (for system call return)
186 // at the highest user virtual address.
187 // only the supervisor uses it, on the way
188 // to/from user space, so not PTE_U.
190 PTE_R | PTE_X) < 0) {
192 return 0;
193 }
195 // map the trapframe page just below the trampoline page, for
196 // trampoline.S.
198 PTE_R | PTE_W) < 0) {
201 return 0;
202 }
9solidMatch the pairs

Match each load in the trampoline with what it fetches from the trapframe.

kernel/trampoline.S
75 # initialize kernel stack pointer, from p->trapframe->kernel_sp
76 ld sp, 8(a0)
78 # make tp hold the current hartid, from p->trapframe->kernel_hartid
79 ld tp, 32(a0)
81 # load the address of usertrap(), from p->trapframe->kernel_trap
82 ld t0, 16(a0)
84 # fetch the kernel page table address, from p->trapframe->kernel_satp.
85 ld t1, 0(a0)
10solidClick the line

Line 76 puts the kernel-stack address into sp, but at that moment the address is not mapped. Click the line that installs the page table in which it maps the kernel stack.

kernel/trampoline.S
72 csrr t0, sscratch
73 sd t0, 112(a0)
75 # initialize kernel stack pointer, from p->trapframe->kernel_sp
76 ld sp, 8(a0)
78 # make tp hold the current hartid, from p->trapframe->kernel_hartid
79 ld tp, 32(a0)
81 # load the address of usertrap(), from p->trapframe->kernel_trap
82 ld t0, 16(a0)
84 # fetch the kernel page table address, from p->trapframe->kernel_satp.
85 ld t1, 0(a0)
87 # wait for any previous memory operations to complete, so that
88 # they use the user page table.
89 sfence.vma zero, zero
91 # install the kernel page table.
92 csrw satp, t1
94 # flush now-stale user entries from the TLB.
95 sfence.vma zero, zero
97 # call usertrap()
98 jalr t0

Your pick: none yet (click a line in the code)

11solidFill in the machine state

A process has just made a system call. Hart 0 has executed line 76 of uservec, ld sp, 8(a0), and nothing after it. What is its state? (For stack, give the stack the hart could safely push to right now; see The stacks of xv6.)

kernel/trampoline.S
72 csrr t0, sscratch
73 sd t0, 112(a0)
75 # initialize kernel stack pointer, from p->trapframe->kernel_sp
76 ld sp, 8(a0)
78 # make tp hold the current hartid, from p->trapframe->kernel_hartid
79 ld tp, 32(a0)
81 # load the address of usertrap(), from p->trapframe->kernel_trap
82 ld t0, 16(a0)
84 # fetch the kernel page table address, from p->trapframe->kernel_satp.
85 ld t1, 0(a0)
87 # wait for any previous memory operations to complete, so that
88 # they use the user page table.
89 sfence.vma zero, zero
91 # install the kernel page table.
92 csrw satp, t1
94 # flush now-stale user entries from the TLB.
95 sfence.vma zero, zero
97 # call usertrap()
98 jalr t0
12solidDecode the bits

gdb shows sstatus = 0x8000000200006020 on hart 0 at the sret in userret. Decode the bits xv6 cares about.

Value: 0x8000000200006020

13solidDecode the bits

forkret computes MAKE_SATP(p->pagetable) for init and gets 0x8000000000087f52. Decode it.

Value: 0x8000000000087f52

14solidChoose one

prepare_return calls intr_off() (line 108) before it points stvec at uservec (line 112). What would go wrong if an interrupt could arrive after line 112?

kernel/trap.c
100void
103 struct proc *p = myproc();
105 // we're about to switch the destination of traps from
106 // kerneltrap() to usertrap(). because a trap from kernel
107 // code to usertrap would be a disaster, turn off interrupts.
110 // send syscalls, interrupts, and exceptions to uservec in trampoline.S
15solidChoose all that apply

Which trapframe fields does prepare_return write?

kernel/trap.c
100void
103 struct proc *p = myproc();
105 // we're about to switch the destination of traps from
106 // kerneltrap() to usertrap(). because a trap from kernel
107 // code to usertrap would be a disaster, turn off interrupts.
110 // send syscalls, interrupts, and exceptions to uservec in trampoline.S
114 // set up trapframe values that uservec will need when
115 // the process next traps into the kernel.
116 p->trapframe->kernel_satp = r_satp(); // kernel page table
117 p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
119 p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
121 // set up the registers that trampoline.S's sret will use
122 // to get to user space.
124 // set S Previous Privilege mode to User.
125 unsigned long x = r_sstatus();
126 x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
127 x |= SSTATUS_SPIE; // enable interrupts in user mode
130 // set S Exception Program Counter to the saved user pc.
16solidChoose all that apply

Which of these are sret’s own doing, done by the hardware when userret executes it?

kernel/trampoline.S
148 # restore user a0
149 ld a0, 112(a0)
151 # return to user mode and user pc.
152 # prepare_return() sets up sstatus and sepc.
153 sret
17deepChoose all that apply

Which of these are the same for every process in this kernel?

18solidChoose one

Hart 0 (running cat) and hart 1 (running grep) both execute sd ra, 40(a0) in uservec at the same instant, both with a0 = 0x3fffffe000. Why does neither overwrite the other’s saved ra?

kernel/trampoline.S
37 li a0, TRAPFRAME
39 # save the user registers in TRAPFRAME
40 sd ra, 40(a0)
41 sd sp, 48(a0)
19solidChoose one

In this tree, what does sscratch hold while a process runs in user mode?

20solidChoose one

usertrap computes the user satp at the very end and hands it to userret in a0. Why not reuse the satp value that was installed when the trap arrived?

kernel/trap.c
84 // give up the CPU if this is a timer interrupt.
85 if (which_dev == 2)
90 // the user page table to switch to, for trampoline.S
93 // return to trampoline.S; satp value in a0.
94 return satp;
21deepType a number

A process lives in proc[2]. What value does prepare_return store in its trapframe->kernel_sp? (Answer in hex.)

kernel/memlayout.h
46// map the trampoline page to the highest address,
47// in both user and kernel space.
50// map kernel stacks beneath the trampoline,
51// each surrounded by invalid guard pages.
52#define KSTACK(p) (TRAMPOLINE - ((p) + 1) * 2 * PGSIZE)
decimal, 0x hex or 0b binary
22deepChoose one

uservec reaches usertrap with ld t0, 16(a0) … jalr t0 instead of simply call usertrap. Why?

kernel/trampoline.S
75 # initialize kernel stack pointer, from p->trapframe->kernel_sp
76 ld sp, 8(a0)
78 # make tp hold the current hartid, from p->trapframe->kernel_hartid
79 ld tp, 32(a0)
81 # load the address of usertrap(), from p->trapframe->kernel_trap
82 ld t0, 16(a0)
84 # fetch the kernel page table address, from p->trapframe->kernel_satp.
85 ld t1, 0(a0)
87 # wait for any previous memory operations to complete, so that
88 # they use the user page table.
89 sfence.vma zero, zero
91 # install the kernel page table.
92 csrw satp, t1
94 # flush now-stale user entries from the TLB.
95 sfence.vma zero, zero
97 # call usertrap()
98 jalr t0
23deepChoose one

Imagine a bug that leaves the TRAPFRAME mapping out of a process’s page table. What happens the first time that process traps into the kernel?

kernel/trampoline.S
30 # save user a0 in sscratch so
31 # a0 can be used to get at TRAPFRAME.
32 csrw sscratch, a0
34 # each process has a separate p->trapframe memory area,
35 # but it's mapped to the same virtual address
36 # (TRAPFRAME) in every process's user page table.
37 li a0, TRAPFRAME
39 # save the user registers in TRAPFRAME
40 sd ra, 40(a0)
41 sd sp, 48(a0)
42 sd gp, 56(a0)
24deepTrue or false, and why

True or false: in this kernel, the twelve loads ld s0 … ld s11 in userret are redundant on every return to user mode, because the s registers already hold the user’s values when usertrap returns.

kernel/trampoline.S
116 # restore all but a0 from TRAPFRAME
117 ld ra, 40(a0)
118 ld sp, 48(a0)
119 ld gp, 56(a0)
120 ld tp, 64(a0)
121 ld t0, 72(a0)
122 ld t1, 80(a0)
123 ld t2, 88(a0)
124 ld s0, 96(a0)
125 ld s1, 104(a0)
126 ld a1, 120(a0)
127 ld a2, 128(a0)
128 ld a3, 136(a0)
129 ld a4, 144(a0)
130 ld a5, 152(a0)
131 ld a6, 160(a0)
132 ld a7, 168(a0)
133 ld s2, 176(a0)
134 ld s3, 184(a0)
135 ld s4, 192(a0)
136 ld s5, 200(a0)
137 ld s6, 208(a0)
138 ld s7, 216(a0)
139 ld s8, 224(a0)
140 ld s9, 232(a0)
141 ld s10, 240(a0)
142 ld s11, 248(a0)
143 ld t3, 256(a0)
144 ld t4, 264(a0)
145 ld t5, 272(a0)
146 ld t6, 280(a0)

Why?

25deepChoose one

uservec sets tp from trapframe->kernel_hartid, a value written by prepare_return the last time this process left the kernel. Why is it guaranteed to be the ID of the hart now executing uservec?

kernel/trap.c
114 // set up trapframe values that uservec will need when
115 // the process next traps into the kernel.
116 p->trapframe->kernel_satp = r_satp(); // kernel page table
117 p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
119 p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()