PS: 这个实验中使用术语 environment 而不是传统的 process
这个lab需要你了解 GCC inline assembly language:
Part A: User Environments and Exception Handling
JOS的Env结构体与xv6中的proc结构体相似
Exercise 1
修改kern/pmap.c
中的mem_init()
:
// Make 'envs' point to an array of size 'NENV' of 'struct Env'.
// LAB 3: Your code here.
envs = (struct Env*)boot_alloc(NENV * sizeof(struct Env));
memset(envs, 0, NENV * sizeof(struct Env));
// 省略...
// Map the 'envs' array read-only by the user at linear address UENVS
// LAB 3: Your code here.
boot_map_region(kern_pgdir, UENVS, NENV * sizeof(struct Env), PADDR(envs), PTE_U);
运行,会出现check_kern_pgdir() succeeded!
Exercise 2
文件env.c
中的env_init()
:
void
env_init(void)
{
// Set up envs array
// LAB 3: Your code here.
env_free_list = NULL;
for (int i = NENV - 1; i >= 0; i--) {
envs[i].env_id = 0;
envs[i].env_link = env_free_list;
env_free_list = &envs[i];
}
// Per-CPU part of the initialization
env_init_percpu();
}
env_setup_vm()
:
static int
env_setup_vm(struct Env *e)
{
int i;
struct PageInfo *p = NULL;
// Allocate a page for the page directory
if (!(p = page_alloc(ALLOC_ZERO)))
return -E_NO_MEM;
// LAB 3: Your code here.
e->env_pgdir = (pde_t*)page2kva(p);
memcpy(e->env_pgdir, kern_pgdir, PGSIZE);
p->pp_ref++;
// UVPT maps the env's own page table read-only.
// Permissions: kernel R, user R
e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;
return 0;
}
region_alloc()
:
static void
region_alloc(struct Env *e, void *va, size_t len)
{
struct PageInfo* p;
uintptr_t end = ROUNDUP((uintptr_t)va + len, PGSIZE);
uintptr_t start = ROUNDDOWN((uintptr_t)va, PGSIZE);
size_t pgnum = (end - start) >> PGSHIFT;
for (size_t i = 0; i < pgnum; i++) {
if ((p = page_alloc(0)) == NULL) {
panic("region_alloc: %e", -E_NO_MEM);
}
int r = page_insert(e->env_pgdir, p, (void*)(start + i * PGSIZE), PTE_W | PTE_U);
if (r < 0) {
panic("region_alloc: %e", r);
}
}
}
load_icode()
:
static void
load_icode(struct Env *e, uint8_t *binary)
{
// LAB 3: Your code here.
struct Elf* elf = (struct Elf*)binary;
struct Proghdr *ph, *eph;
if (elf->e_magic != ELF_MAGIC) {
panic("load_icode: not an ELF file");
}
ph = (struct Proghdr*)(binary + elf->e_phoff);
eph = ph + elf->e_phnum;
lcr3(PADDR(e->env_pgdir));
for (; ph < eph; ph++) {
if (ph->p_type == ELF_PROG_LOAD) {
if (ph->p_filesz > ph->p_memsz) {
panic("load_icode: p_filesz > p_memsz");
}
region_alloc(e, (void*)(ph->p_va), ph->p_memsz);
memcpy((void*)(ph->p_va), (void*)(binary + ph->p_offset), ph->p_filesz);
memset((void*)(ph->p_va + ph->p_filesz), 0, ph->p_memsz - ph->p_filesz);
}
}
e->env_tf.tf_eip = elf->e_entry;
// LAB 3: Your code here.
region_alloc(e, (void*)(USTACKTOP - PGSIZE), PGSIZE);
lcr3(PADDR(kern_pgdir));
}
env_create()
:
void
env_create(uint8_t *binary, enum EnvType type)
{
// LAB 3: Your code here.
struct Env* e;
int r = env_alloc(&e, 0);
if (r < 0) {
panic("env_create: %e", r);
}
e->env_type = type;
load_icode(e, binary);
}
env_run()
:
void
env_run(struct Env *e)
{
// LAB 3: Your code here.
if (curenv && curenv->env_status == ENV_RUNNING) {
curenv->env_status = ENV_RUNNABLE;
}
curenv = e;
e->env_status = ENV_RUNNING;
e->env_runs++;
lcr3(PADDR(e->env_pgdir));
env_pop_tf(&e->env_tf);
panic("env_run not yet implemented");
}
如果是用 6.828 补丁版的 QEMU,运行后会看到"Triple fault"的报错信息,如果不是 6.828 的,会看到CPU重置,系统重启。
Exercise 3
Part B: Page Faults, Breakpoints Exceptions, and System Calls
Exercise 4
kern/trapentry.S
/*
* Lab 3: Your code here for generating entry points for the different traps.
*/
TRAPHANDLER_NOEC(vector0, T_DIVIDE)
TRAPHANDLER_NOEC(vector1, T_DEBUG)
TRAPHANDLER_NOEC(vector2, T_NMI)
TRAPHANDLER_NOEC(vector3, T_BRKPT)
TRAPHANDLER_NOEC(vector4, T_OFLOW)
TRAPHANDLER_NOEC(vector5, T_BOUND)
TRAPHANDLER_NOEC(vector6, T_ILLOP)
TRAPHANDLER_NOEC(vector7, T_DEVICE)
TRAPHANDLER(vector8, T_DBLFLT)
TRAPHANDLER(vector10, T_TSS)
TRAPHANDLER(vector11, T_SEGNP)
TRAPHANDLER(vector12, T_STACK)
TRAPHANDLER(vector13, T_GPFLT)
TRAPHANDLER(vector14, T_PGFLT)
TRAPHANDLER_NOEC(vector16, T_FPERR)
TRAPHANDLER(vector17, T_ALIGN)
TRAPHANDLER_NOEC(vector18, T_MCHK)
TRAPHANDLER_NOEC(vector19, T_SIMDERR)
TRAPHANDLER_NOEC(vector48, T_SYSCALL)
TRAPHANDLER_NOEC(vector500, T_DEFAULT)
/*
* Lab 3: Your code here for _alltraps
*/
.global _alltraps
_alltraps:
pushl %ds
pushl %es
pushal
movw $GD_KD, %ax
movw %ax, %ds
movw %ax, %es
pushl %esp
call trap
kern/trap.c
void
trap_init(void)
{
extern struct Segdesc gdt[];
// LAB 3: Your code here.
void vector0();
void vector1();
void vector2();
void vector3();
void vector4();
void vector5();
void vector6();
void vector7();
void vector8();
void vector10();
void vector11();
void vector12();
void vector13();
void vector14();
void vector16();
void vector17();
void vector18();
void vector19();
void vector48();
void vector500();
SETGATE(idt[T_DIVIDE], 1, GD_KT, vector0, 0)
SETGATE(idt[T_DEBUG], 1, GD_KT, vector1, 0)
SETGATE(idt[T_NMI], 1, GD_KT, vector2, 0)
SETGATE(idt[T_BRKPT], 1, GD_KT, vector3, 3)
SETGATE(idt[T_OFLOW], 1, GD_KT, vector4, 0)
SETGATE(idt[T_BOUND], 1, GD_KT, vector5, 0)
SETGATE(idt[T_ILLOP], 1, GD_KT, vector6, 0)
SETGATE(idt[T_DEVICE], 1, GD_KT, vector7, 0)
SETGATE(idt[T_DBLFLT], 1, GD_KT, vector8, 0)
SETGATE(idt[T_TSS], 1, GD_KT, vector10, 0)
SETGATE(idt[T_SEGNP], 1, GD_KT, vector11, 0)
SETGATE(idt[T_STACK], 1, GD_KT, vector12, 0)
SETGATE(idt[T_GPFLT], 1, GD_KT, vector13, 0)
SETGATE(idt[T_PGFLT], 1, GD_KT, vector14, 0)
SETGATE(idt[T_FPERR], 1, GD_KT, vector16, 0)
SETGATE(idt[T_ALIGN], 1, GD_KT, vector17, 0)
SETGATE(idt[T_MCHK], 1, GD_KT, vector18, 0)
SETGATE(idt[T_SIMDERR], 1, GD_KT, vector19, 0)
SETGATE(idt[T_SYSCALL], 0, GD_KT, vector48, 3)
SETGATE(idt[T_DEFAULT], 0, GD_KT, vector500, 0)
// Per-CPU setup
trap_init_percpu();
}
Exercise 5 和 Exercise 6
trap.c
文件:
static void
trap_dispatch(struct Trapframe *tf)
{
// Handle processor exceptions.
// LAB 3: Your code here.
switch (tf->tf_trapno) {
case T_BRKPT:
monitor(tf);
break;
case T_PGFLT:
page_fault_handler(tf);
break;
default: // Unexpected trap: The user process or the kernel has a bug.
print_trapframe(tf);
if (tf->tf_cs == GD_KT)
panic("unhandled trap in kernel");
else {
env_destroy(curenv);
return;
}
}
}
测试breakpoint失败了,查看文件jos.out.breakpoint
,发现trap 0x0000000d General Protection
,这是因为我们在用户模式进行int 3
进入更高特权等级的内核,要求CPL<=DPL,所以DPL应该设置为3,将trap_init
函数里的改成SETGATE(idt[T_BRKPT], 1, GD_KT, vector3, 3)
即可。
Exercise 7
static void
trap_dispatch(struct Trapframe *tf)
{
// Handle processor exceptions.
// LAB 3: Your code here.
switch (tf->tf_trapno) {
case T_BRKPT:
monitor(tf);
break;
case T_PGFLT:
page_fault_handler(tf);
break;
case T_SYSCALL:
tf->tf_regs.reg_eax =
syscall(tf->tf_regs.reg_eax, tf->tf_regs.reg_edx, tf->tf_regs.reg_ecx, tf->tf_regs.reg_ebx, tf->tf_regs.reg_edi, tf->tf_regs.reg_esi);
break;
default: // Unexpected trap: The user process or the kernel has a bug.
print_trapframe(tf);
if (tf->tf_cs == GD_KT)
panic("unhandled trap in kernel");
else {
env_destroy(curenv);
return;
}
}
}
int32_t
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
{
switch (syscallno) {
case SYS_cputs:
sys_cputs((const char*)a1, a2);
return 0;
case SYS_cgetc:
return sys_cgetc();
case SYS_getenvid:
return sys_getenvid();
case SYS_env_destroy:
return sys_env_destroy(a1);
default:
return -E_INVAL;
}
}
Exercise 8
void
libmain(int argc, char **argv)
{
// set thisenv to point at our Env structure in envs[].
// LAB 3: Your code here.
thisenv = 0;
envid_t env_id = sys_getenvid();
for (int i = 0; i < NENV; i++) {
if (envs[i].env_id == env_id) {
thisenv = &envs[i];
}
}
Exercise 9
void
page_fault_handler(struct Trapframe *tf)
{
uint32_t fault_va;
// Read processor's CR2 register to find the faulting address
fault_va = rcr2();
// Handle kernel-mode page faults.
// LAB 3: Your code here.
// 如果是在kernel中page fault
if ((tf->tf_cs & 0x3) == 0) {
panic("page fault in kernel mode!");
}
int
user_mem_check(struct Env *env, const void *va, size_t len, int perm)
{
// LAB 3: Your code here.
uintptr_t start = ROUNDDOWN((uint32_t)va, PGSIZE);
uintptr_t end = ROUNDDOWN((uint32_t)va + len - 1, PGSIZE);
pte_t* pte;
perm |= PTE_P;
for (uintptr_t cur = start;; cur += PGSIZE) {
pte = pgdir_walk(env->env_pgdir, (void*)cur, 0);
if (pte == NULL || (*pte & perm) != perm || cur >= ULIM) {
user_mem_check_addr = cur == start ? (uintptr_t)va : cur;
return -E_FAULT;
}
if (cur == end)
return 0;
}
return 0;
}
static void
sys_cputs(const char *s, size_t len)
{
// Check that the user has permission to read memory [s, s+len).
// Destroy the environment if not.
// LAB 3: Your code here.
user_mem_assert(curenv, s, len, PTE_U);
// Print the string supplied by the user.
cprintf("%.*s", len, s);
}
int
debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)
{
// 省略
// Make sure this memory is valid.
// Return -1 if it is not. Hint: Call user_mem_check.
// LAB 3: Your code here.
if (user_mem_check(curenv, (void*)usd, sizeof(struct UserStabData), PTE_U) < 0) {
return -1;
}
// 省略
// Make sure the STABS and string table memory is valid.
// LAB 3: Your code here.
if (user_mem_check(curenv, (void*)stabs, stab_end - stabs, PTE_U) < 0) {
return -1;
}
if (user_mem_check(curenv, (void*)stabstr, stabstr_end - stabstr, PTE_U) < 0) {
return -1;
}