6.828:Lab3 User Environments 实验总结

Part A: User Environments and Exception Handling

ENV=PROCESS

Environment State

struct Env {
	struct Trapframe env_tf;	// Saved registers
	struct Env *env_link;		// Next free Env
	envid_t env_id;			// Unique environment identifier
	envid_t env_parent_id;		// env_id of this env's parent
	enum EnvType env_type;		// Indicates special system environments
	unsigned env_status;		// Status of the environment
	uint32_t env_runs;		// Number of times environment has run

	// Address space
	pde_t *env_pgdir;		// Kernel virtual address of page dir
};

Allocating the Environments Array

重新运行qemu时,出现了kernel panic at kern/pmap.c:147: PADDR called with invalid kva 00000000,问题原因详见MIT 6.828 labs walkthroughs: Lab 3 User Environments

Exercise1

// Make 'envs' point to an array of size 'NENV' of 'struct Env'.
// LAB 3: Your code here.
envs = (struct Env*) boot_alloc(sizeof(struct Env) * NENV);
// Map the 'envs' array read-only by the user at linear address UENVS
// (ie. perm = PTE_U | PTE_P).
// Permissions:
//    - the new image at UENVS  -- kernel R, user R
//    - envs itself -- kernel RW, user NONE
// LAB 3: Your code here.
boot_map_region(kern_pgdir, UENVS, PTSIZE, PADDR(envs), PTE_U);

Creating and Running Environments

Exercise2

env_init()

for (int i = NENV - 1; i >= 0; i--) {
    envs[i].env_status = ENV_FREE;
    envs[i].env_id = 0;
    envs[i].env_link = env_free_list;
    env_free_list = &envs[i];
}

env_setup_vm()

e->env_pgdir = page2kva(p);
p->pp_ref += 1;
memcpy(e->env_pgdir, kern_pgdir, PGSIZE);

region_alloc()

void *begin = ROUNDDOWN(va, PGSIZE);
void *end = ROUNDUP(va + len, PGSIZE);

if ((uint32_t)end > UTOP) {
    panic("region_alloc: cannot allocate pages over UTOP");
}

while (begin < end) {
    struct PageInfo *pp;

    if ((pp = page_alloc(0)) == NULL) {
        panic("region_alloc: out of free memory");
    }

    int r = page_insert(e->env_pgdir, pp, begin, PTE_U | PTE_W);

    if (r != 0) {
        panic("region_alloc: %e", r);
    }
    begin += PGSIZE;
}

load_icode()

struct Elf *elfhr = (struct Elf *)binary;

if (elfhr->e_magic != ELF_MAGIC) {
    panic("load_icode: invalid elf header");
}

// switch to env's address space
lcr3(PADDR(e->env_pgdir));

// load each program segment
struct Proghdr *ph = (struct Proghdr *)(binary + elfhr->e_phoff);
struct Proghdr *eph = ph + elfhr->e_phnum;

for (; ph < eph; ph++) {
    if (ph->p_type != ELF_PROG_LOAD)
        continue;
    region_alloc(e, (uint8_t *)ph->p_va, ph->p_memsz);
    memcpy((uint8_t *)ph->p_va, binary + ph->p_offset, ph->p_filesz);
    memset((uint8_t *)ph->p_va + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
}

// make eip points to the entry point
e->env_tf.tf_eip = elfhr->e_entry;

// Now map one page for the program's initial stack
// at virtual address USTACKTOP - PGSIZE.

// LAB 3: Your code here.

region_alloc(e, (void *)(USTACKTOP - PGSIZE), PGSIZE);

// switch back to kernel address space
lcr3(PADDR(kern_pgdir));

env_create()

struct Env *env;
int r;

if((r = env_alloc(&env, 0)) != 0)
    panic("env_create: %e", r);

load_icode(env, binary);
env->env_type = type;

env_run()

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

Handling Interrupts and Exceptions

Basics of Protected Control Transfer

Interrupt Descriptor Table

Task State Segment

Types of Exceptions and Interrupts

0-31:synchronous exceptions
32-255:software interrupts(int)、asynchronous hardware interrupts
48:software interrupts(int)

An Example

Nested Exceptions and Interrupts

在内核模式中发生异常或中断

Setting Up the IDT

Exercise4

/*
 * Lab 3: Your code here for generating entry points for the different traps.
 */

TRAPHANDLER_NOEC(th_divide, T_DIVIDE)
TRAPHANDLER_NOEC(th_debug, T_DEBUG)
TRAPHANDLER_NOEC(th_nmi, T_NMI)
TRAPHANDLER_NOEC(th_brkpt, T_BRKPT)
TRAPHANDLER_NOEC(th_oflow, T_OFLOW)
TRAPHANDLER_NOEC(th_bound, T_BOUND)
TRAPHANDLER_NOEC(th_illop, T_ILLOP)
TRAPHANDLER_NOEC(th_device, T_DEVICE)
TRAPHANDLER(th_dblflt, T_DBLFLT)
TRAPHANDLER(th_tss, T_TSS)
TRAPHANDLER(th_segnp, T_SEGNP)
TRAPHANDLER(th_stack, T_STACK)
TRAPHANDLER(th_gpflt, T_GPFLT)
TRAPHANDLER(th_pgflt, T_PGFLT)
TRAPHANDLER_NOEC(th_fperr, T_FPERR)
TRAPHANDLER(th_align, T_ALIGN)
TRAPHANDLER_NOEC(th_mchk, T_MCHK)
TRAPHANDLER_NOEC(th_simderr, T_SIMDERR)

/*
 * Lab 3: Your code here for _alltraps
 */

_alltraps:
    pushl %ds
    pushl %es
    pushal
    movw $GD_KD, %ax
    movw %ax, %ds
    movw %ax, %es
    pushl %esp
    call trap
void th_divide();
void th_debug();
void th_nmi();
void th_brkpt();
void th_oflow();
void th_bound();
void th_illop();
void th_device();
void th_dblflt();
void th_tss();
void th_segnp();
void th_stack();
void th_gpflt();
void th_pgflt();
void th_fperr();
void th_align();
void th_mchk();
void th_simderr();

SETGATE(idt[T_DIVIDE], 0, GD_KT, &th_divide, 0);
SETGATE(idt[T_DEBUG], 0, GD_KT, &th_debug, 0);
SETGATE(idt[T_NMI], 0, GD_KT, &th_nmi, 0);
SETGATE(idt[T_BRKPT], 0, GD_KT, &th_brkpt, 0);
SETGATE(idt[T_OFLOW], 0, GD_KT, &th_oflow, 0);
SETGATE(idt[T_BOUND], 0, GD_KT, &th_bound, 0);
SETGATE(idt[T_ILLOP], 0, GD_KT, &th_illop, 0);
SETGATE(idt[T_DEVICE], 0, GD_KT, &th_device, 0);
SETGATE(idt[T_DBLFLT], 0, GD_KT, &th_dblflt, 0);
SETGATE(idt[T_TSS], 0, GD_KT, &th_tss, 0);
SETGATE(idt[T_SEGNP], 0, GD_KT, &th_segnp, 0);
SETGATE(idt[T_STACK], 0, GD_KT, &th_stack, 0);
SETGATE(idt[T_GPFLT], 0, GD_KT, &th_gpflt, 0);
SETGATE(idt[T_PGFLT], 0, GD_KT, &th_pgflt, 0);
SETGATE(idt[T_FPERR], 0, GD_KT, &th_fperr, 0);
SETGATE(idt[T_ALIGN], 0, GD_KT, &th_align, 0);
SETGATE(idt[T_MCHK], 0, GD_KT, &th_mchk, 0);
SETGATE(idt[T_SIMDERR], 0, GD_KT, &th_simderr, 0);

Question1

  1. To push the corresponding error code onto the stack. This is used for the codes going to handle it further like trap_dispatch() to distinguish the interrupts.

  2. To provide permission control or isolation. For each standalone interrupt handler, we can define it whether can be triggered by a user program or not. By putting such limits on interrupt handlers, we can ensure user programs would not interfere with the kernel, corrupt the kernel or even take control of the whole computer.

Question2

I didn’t have to do anything extra to do. It triggers an Interrupt 13 because only the kernel running in Ring 0 can trigger the handler of page fault as we defined above. This meets the “Executing the INT n instruction when the CPL is greater than the DPL of the referenced interrupt, trap, or task gate.” condition, so the processor triggers a General Protection Exception (Interrupt 13)

If we allow a page fault to be triggered by a user program like softint. It can manipulate virtual memory and may cause serious security issues.

Part B: Page Faults, Breakpoints Exceptions, and System Calls

Handling Page Faults

Exercise5

switch (tf->tf_trapno) {
    case T_PGFLT:
        page_fault_handler(tf);
        return;
    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;
        }
}

The Breakpoint Exception

Exercise6

trap_dispatch()中增加case调用kern/monitor中的monitor()

case T_BRKPT:
    monitor(tf);
    return;

因为现在Breakpoint Exception只能在内核模式下调用,所以kern/trap.ctrap_init()里的

SETGATE(idt[T_BRKPT], 0, GD_KT, &th_brkpt, 0);

应该更改为:

SETGATE(idt[T_BRKPT], 0, GD_KT, &th_brkpt, 3);

Question3

导致产生general protection fault的几种情况:

  1. Accessing a gate that contains a null segment selector.
  2. Executing the INT n instruction when the CPL is greater than the DPL of the referenced interrupt, trap, or task gate.
  3. The segment selector in a call, interrupt, or trap gate does not point to a code segment.

Question4

保护机制

System calls

Exercise7

kern/trapentry.S

TRAPHANDLER_NOEC(th_syscall, T_SYSCALL)

kern/trap.c:trap_init()

void th_syscall();
SETGATE(idt[T_SYSCALL], 0, GD_KT, &th_syscall, 3);

kern/trap.c:trap_dispatch()

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);
    return;

kern/syscall.c:syscall()

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

User-mode startup

Exercise8

thisenv = &envs[ENVX(sys_getenvid())];

Page faults and memory protection

Exercise9&10

kern/trap.c:page_fault_handler()

if ((tf->tf_cs & 0x3) == 0) {
    panic("page_fault_handler: page fault in kernel mode");
}

kern/pmap.c:user_mem_check()

int
user_mem_check(struct Env *env, const void *va, size_t len, int perm)
{
    // LAB 3: Your code here.
    uint32_t addr = (uint32_t)va;
    uint32_t begin = ROUNDDOWN(addr, PGSIZE);
    uint32_t end = ROUNDUP(addr + len, PGSIZE);

    while (begin < end) {
        pte_t *pte = pgdir_walk(env->env_pgdir, (void *)begin, 0);

        // Thanks @trace-andreason for telling me the mistake on next line
        if (begin >= ULIM || pte == NULL || !(*pte & PTE_P) || (*pte & perm) != perm) {
            user_mem_check_addr = (begin < addr) ? addr : begin;
            return -E_FAULT;
        }
        begin += PGSIZE;
    }
    return 0;
}

kern/syscall.c:sys_cputs()

user_mem_assert(curenv, s, len, PTE_U);

kern/kdebug.c:debuginfo_eip()

if (user_mem_check(curenv, usd, sizeof(struct UserStabData), PTE_U) < 0) {
    return -1;
}

...
if (user_mem_check(curenv, stabs, stab_end - stabs, PTE_U) < 0) {
    return -1;
}
if (user_mem_check(curenv, stabstr, stabstr_end - stabstr, PTE_U) < 0) {
    return -1;
}

Reference

MIT 6.828 labs walkthroughs: Lab 3 User Environments

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值