MIT 6.828 Lab3
Notes of MIT 6.828 Lab3
Part A: User Environments and Exception Handling
Environment State
In JOS, Environment is a similar concept to process, which couples the concepts of “thread” and “address space”. The thread is defined primarily by the saved registers (the env_tf
field), and the address space is defined by the page directory and page tables pointed to by env_pgdir
.
We use Env
structure to hold each environment.
1 | struct Env { |
The kernel maintains three main global variables pertaining to environments:
1 | struct Env *envs = NULL; // All environments |
Allocating the Environments Array
Modify mem_init()
further to allocate a similar array of Env structures, called envs.
Exercise 1
Like above, we use boot_alloc()
to allocate space for envs
.
1 | envs = (struct Env *) boot_alloc(NENV * sizeof(struct Env)); |
Creating and Running Environments
Implement the following functions to create a user environment, load a static binary image that is embedded within the kernel and run the environment.
Exercise 2
env_init()
initializes all of the Env
structures in the env
array and add them to the env_free_list
. Also calls env_init_percpu
, which configures the segmentation hardware with separate segments for privilege level 0 (kernel) and privilege level 3 (user).
We need to make sure the environments are in the free list in the same order they are in the envs array, so the order of loop is reverse.
1 | void |
env_setup_vm()
allocates a page directory for a new environment and initialize the kernel portion of the new environment’s address space.
1 | static int |
region_alloc()
allocates and maps physical memory for an environment.
This function will be used in load_icode
.
1 | static void |
load_icode()
parses an ELF binary image, much like the boot loader already does, and load its contents into the user address space of a new environment.
1 | static void |
env_create()
allocates an environment with env_alloc and call load_icode to load an ELF binary into it.
1 | void |
Setting Up the IDT
Exercise 4 & Challenge
Question1
The purpose of having an individual handler function for each exception/interrupt is to set a unique interrupt number for each exception/interrupt. So different exception/interrupt can be identified later.
The result is caused by the DPL bit in the interrupt descriptor. In
trap_init()
, we set the DPLs of all exceptions exceptT_BRKPT
andT_SYSCALL
to 0.When a user program invokes
int $14
, the cpu detects that it tries to violate the privilege level. Therefore, general protect exception is produced.If the kernel actually allows softint’s
int $14
instruction to invoke the kernel’s page fault handler, there will be security issues.
Challenge
Part B: Page Faults, Breakpoints Exceptions, and System Calls
Handling Page Faults
Exercise 5
The Breakpoint Exception
Exercise 6
Question2
In
trap_init()
, we set the DPL ofT_BRKPT
to 3, so that the user can invoke the breakpoint exception and won’t trigger a general protection exception.The point of these mechanisms is to isolate user from invoking system exceptions by specifying the privilege bit in
IDT
.