Test yourself · category 1 of 20
Build, link and boot How make, the linker script and mkfs produce kernel/kernel and fs.img, and how three harts get from QEMU’s boot ROM through _entry, start and main to the first process.
All Not yet answered Got wrong Reset this category
Every hart runs these lines of _entry at the same time. Why does the stack pointer
use hartid + 1 instead of hartid?
kernel/entry.S
8 # set up a stack for C.
9 # stack0 is declared in start.c,
10 # with a 4096-byte stack per CPU.
11 # sp = stack0 + ((hartid + 1) * 4096)
18 # jump to start() in start.c
Check Try again
In this build, stack0 is at 0x80007890. What value does sp hold on hart 2
right after line 17 (add sp, sp, a0)? Answer in hex.
kernel/entry.S
8 # set up a stack for C.
9 # stack0 is declared in start.c,
10 # with a 4096-byte stack per CPU.
11 # sp = stack0 + ((hartid + 1) * 4096)
18 # jump to start() in start.c
Check Try again
Check Try again
Match each build input with its job.
kernel/kernel.ldchoose… places _entry at 0x80000000 and puts the trampoline on a page of its own writes the 2000-block disk image on the build machine makes the compiler write .d files listing the headers each object depends on links every user program starting at virtual address 0 generates the assembly stubs that put a system-call number in a7 and execute ecall user/user.ldchoose… places _entry at 0x80000000 and puts the trampoline on a page of its own writes the 2000-block disk image on the build machine makes the compiler write .d files listing the headers each object depends on links every user program starting at virtual address 0 generates the assembly stubs that put a system-call number in a7 and execute ecall mkfs/mkfschoose… places _entry at 0x80000000 and puts the trampoline on a page of its own writes the 2000-block disk image on the build machine makes the compiler write .d files listing the headers each object depends on links every user program starting at virtual address 0 generates the assembly stubs that put a system-call number in a7 and execute ecall user/usys.plchoose… places _entry at 0x80000000 and puts the trampoline on a page of its own writes the 2000-block disk image on the build machine makes the compiler write .d files listing the headers each object depends on links every user program starting at virtual address 0 generates the assembly stubs that put a system-call number in a7 and execute ecall the -MD flag choose… places _entry at 0x80000000 and puts the trampoline on a page of its own writes the 2000-block disk image on the build machine makes the compiler write .d files listing the headers each object depends on links every user program starting at virtual address 0 generates the assembly stubs that put a system-call number in a7 and execute ecall
Check Try again
5 warm-up True or false, and why True or false: when hart 0 calls kvminithart on line 21 of main , paging is
turned on for all three harts.
kernel/main.c
19 kinit (); // physical page allocator
Check Try again
userinit creates process 1 with no user memory at all (p->sz stays 0). How
does process 1 get the code of /init?
kernel/proc.c
217 // Set up first user process.
Check Try again
7 warm-up Choose all that apply While hart 0 runs the if branch of main (lines 14–33), what are harts 1
and 2 doing? Choose all that apply.
kernel/main.c
9 // start() jumps here in supervisor mode on all CPUs.
19 kinit (); // physical page allocator
33 __atomic_store_n(& started , 1 , __ATOMIC_RELEASE);
35 while ( __atomic_load_n(& started , __ATOMIC_ACQUIRE) == 0 )
Check Try again
binit and iinit run in main , but fsinit , which reads the
superblock from the disk, does not. Why must it wait until the first process runs?
kernel/proc.c
519 // Still holding p->lock from scheduler.
525 // File system initialization must be run in the context of a
526 // regular process (e.g., because it calls sleep), and thus cannot
527 // be run from main().
530 // We can invoke kexec() now that file system is initialized.
531 // Put the return value (argc) of kexec into a0.
Check Try again
kvmmake maps everything below etext read+execute and everything above it
read+write, page by page, so etext must be a multiple of 0x1000. Click the line that
makes it so.
kernel/kernel.ld
19 ASSERT (. - _trampoline == 0x1000 , "error: trampoline larger than one page" );
Your pick: none yet (click a line in the code)
Check Try again
Put these events on hart 0 in the order they happen, from power-on to the first
moment it can take an interrupt.
mret drops the hart to supervisor mode at main↑ ↓ QEMU’s boot ROM at 0x1000 jumps to 0x80000000 ↑ ↓ _entry points sp at the top of hart 0’s slice of stack0↑ ↓ kvminithart writes satp: paging on↑ ↓ started is set to 1 with a release store↑ ↓ start sets mstatus.MPP to S and mepc to main↑ ↓ the scheduler’s first intr_on() ↑ ↓ trapinithart sets stvec to kernelvec↑ ↓ Check Try again
11 solid Choose all that apply Which of these functions does hart 1 call during boot? Choose all that apply.
kernel/main.c
9 // start() jumps here in supervisor mode on all CPUs.
19 kinit (); // physical page allocator
33 __atomic_store_n(& started , 1 , __ATOMIC_RELEASE);
35 while ( __atomic_load_n(& started , __ATOMIC_ACQUIRE) == 0 )
Check Try again
12 solid Fill in the machine state Hart 1 is spinning on line 35 of main , waiting for started . Hart 0 is
somewhere in kinit . Fill in hart 1’s state.
kernel/main.c
35 while ( __atomic_load_n(& started , __ATOMIC_ACQUIRE) == 0 )
Privilege mode choose… M (machine) S (supervisor) U (user) Active stack choose… user stack the process's kernel stack scheduler stack (stack0) boot stack (stack0) no usable stack Page table (satp) choose… paging off kernel page table user page table Interrupts (sstatus.SIE) choose… on off noff choose… 0 1 2 3 4
Check Try again
The kernel’s .bss section is 0x19350 bytes and holds proc ,
stack0 , cpus and the other zero-initialized globals. The C code assumes they
start as zero. Who zeroes them?
Check Try again
14 solid Choose all that apply Which of these live in the kernel’s .bss section (0x80007860–0x80020bb0 in this
build)? Choose all that apply.
Check Try again
How many metadata blocks (nmeta) does mkfs compute? In this tree FSSIZE = 2000,
BSIZE = 1024, LOGBLOCKS = 30, sizeof(struct dinode) = 64 (so IPB = 16) and
BPB = 1024 × 8 = 8192.
mkfs/mkfs.c
26 // [ boot block | sb block | log | inode blocks | free bit map | data blocks ]
31 int nmeta ; // Number of meta blocks (boot, sb, nlog, inode, bitmap)
Check Try again
The comment on line 24 says writing main’s address into mepc “requires gcc
-mcmodel=medany”. Why?
kernel/start.c
23 // set M Exception Program Counter to main, for mret.
24 // requires gcc -mcmodel=medany
Check Try again
make runs mkfs/mkfs fs.img README $(UPROGS). mkfs gives the root directory the
first inode, then one inode to each file in command-line order. What is the inode number
of /init?
Check Try again
18 solid True or false, and why True or false: right after kvminithart turns on paging, hart 0’s sp (still in its
slice of stack0 ) points at usable memory, with no change to sp.
kernel/vm.c
38 // map kernel text executable and read-only.
41 // map kernel data and the physical RAM we'll make use of.
Check Try again
To leave for user mode the first time, forkret calls userret through the address
TRAMPOLINE + (userret - trampoline) (0x3ffffff09c), not at its link address
0x8000609c. Why?
kernel/proc.c
538 // return to user space, mimicing usertrap()'s return.
Check Try again
In this build end is 0x80020bb0 and PHYSTOP is 0x88000000. How many pages
does kinit hand to the page allocator?
Check Try again
You delete line 15 of kernel/kernel.ld (the ALIGN(0x1000) before
_trampoline = .) and run make. In this build the ordinary kernel code ends at
0x80005bc0 and the trampoline code is 0x124 bytes long. What happens?
kernel/kernel.ld
19 ASSERT (. - _trampoline == 0x1000 , "error: trampoline larger than one page" );
Check Try again
NCPU is 8, so stack0 has 8 slices and cpus has 8 entries. You run
make qemu CPUS=10. What happens?
kernel/start.c
10 // entry.S needs one stack per CPU.
Check Try again
Process 1 runs fsinit with interrupts off on its hart (the scheduler’s acquire
recorded intena = 0, so forkret 's release leaves them off). Its first disk read
sleeps waiting for the completion interrupt. Which hart can take that interrupt?
kernel/proc.c
519 // Still holding p->lock from scheduler.
525 // File system initialization must be run in the context of a
526 // regular process (e.g., because it calls sleep), and thus cannot
527 // be run from main().
Check Try again
24 deep Choose all that apply Suppose harts 1 and 2, after seeing started == 1, also ran these functions themselves.
Which would corrupt shared kernel state? Choose all that apply.
kernel/main.c
19 kinit (); // physical page allocator
33 __atomic_store_n(& started , 1 , __ATOMIC_RELEASE);
35 while ( __atomic_load_n(& started , __ATOMIC_ACQUIRE) == 0 )
Check Try again
Put the steps of process 1’s life in order, from its creation to its first user
instruction.
kexec("/init") loads the program and installs its new page table↑ ↓ prepare_return points stvec at uservec and sets sepc to the entry point↑ ↓ fsinit reads the superblock and recovers the log↑ ↓ userret switches satp to the user page table and executes sret↑ ↓ forkret releases p->lock↑ ↓ a scheduler sets RUNNING and calls swtch, whose ret lands in forkret ↑ ↓ allocproc sets p->context.ra = forkret and p->context.sp to the top of its kernel stack↑ ↓ userinit sets p->state = RUNNABLE↑ ↓ Check Try again
All categories RISC-V privilege and CSRs →