MIT6.828 Fall2018 笔记 - Lab 3: User Environments

Lab 3: User Environments

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

阅读Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3A: System Programming Guide, Part 1的第五章

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;
        }

猜你喜欢

转载自www.cnblogs.com/zsmumu/p/12729463.html