September 7, 2017

MIT 6.828: JOS Lab

Part A: Multiprocessor Support and Cooperative Multitasking

Questions

1. Compare kern/mpentry.S side by side with boot/boot.S. Bearing in mind that kern/mpentry.S is compiled and linked to run above KERNBASE just like everything else in the kernel, what is the purpose of macro MPBOOTPHYS? Why is it necessary in kern/mpentry.S but not in boot/boot.S? In other words, what could go wrong if it were omitted in kern/mpentry.S?
1. The MPBOOTPHYS macro is needed because mpentry.S is linked at high addresses but gets loaded by boot_aps() at the low address MPENTRY_ADDR. The bootloader doesn't need a macro like this because it is linked and loaded at the same low address (0x00007c00).
1. During a trap/interrupt, the trapframe is pushed onto the stack without holding the kernel lock. Say CPU 1 enters the kernel on a system call and while it is in the kernel, CPU 2 attempts to enter the kernel on a timer interrupt. CPU 2 can't enter the kernel, it will be spinning at the lock in trap(). However, it will have pushed its trap frame on top of the trap frame already pushed by CPU 1. This of course means that when CPU 1 returns to user mode, it will pop off CPU 2's frame and return in that environment instead of its own.
1. In your implementation of env_run() you should have called lcr3(). Before and after the call to lcr3(), your code makes references (at least it should) to the variable e, the argument to env_run. Upon loading the %cr3 register, the addressing context used by the MMU is instantly changed. But a virtual address (namely e) has meaning relative to a given address context--the address context specifies the physical address to which the virtual address maps. Why can the pointer e be dereferenced both before and after the addressing switch?
1. Because all environment page directories share certain mappings. The envs array is allocated and mapped to UENVS in mem_init() and those mappings are then copied to all new page directories in env_setup_vm().
2. It's a like the caller/callee save calling convention except here it is for interrupting the whole process. Processes keep temporary data and variables in registers and the process assumes that when it returns, all those values will still be there. The registers are saved on the user environment's stack as part of the trapframe constructed by the int instruction and the code in alltraps. To restore the state of a new process, JOS uses the env_pop_tf() function, which switches first to the new process' stack and the pops all the registers in place.