6.828 Lab3 User Environments Experiment Summary

Hits: 0

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

When re-running qemu, it appeared. For the kernel panic at kern/pmap.c:147: PADDR called with invalid kva 00000000cause of the problem, see 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

An exception or interrupt occurs in kernel mode

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
 */

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

Question 1

  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()Add the case in the call kern/monitortomonitor()

case T_BRKPT:
    monitor(tf);
    return;

Because now Breakpoint Exception can only be called in kernel mode, so kern/trap.cthetrap_init()

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

should be changed to:

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

Question3

general protection faultSeveral situations that lead to :

  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

protection mechanism

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

You may also like...

Leave a Reply

Your email address will not be published.