diff --git a/lab b/lab deleted file mode 160000 index da14b2e..0000000 --- a/lab +++ /dev/null @@ -1 +0,0 @@ -Subproject commit da14b2e50ce4907bf9aea5895f8c1ec1110e979d diff --git a/lab/LAB4.si4project/Backup/dumbfork(162).c b/lab/LAB4.si4project/Backup/dumbfork(162).c new file mode 100644 index 0000000..e5e433c --- /dev/null +++ b/lab/LAB4.si4project/Backup/dumbfork(162).c @@ -0,0 +1,80 @@ +// Ping-pong a counter between two processes. +// Only need to start one of these -- splits into two, crudely. + +#include +#include + +envid_t dumbfork(void); + +void +umain(int argc, char **argv) +{ + envid_t who; + int i; + + // fork a child process + who = dumbfork(); + + // print a message and yield to the other a few times + for (i = 0; i < (who ? 10 : 20); i++) { + cprintf("%d: I am the %s!\n", i, who ? "parent" : "child"); + sys_yield(); + } +} + +void +duppage(envid_t dstenv, void *addr) +{ + int r; + + // This is NOT what you should do in your fork. + if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_alloc: %e", r); + if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_map: %e", r); + memmove(UTEMP, addr, PGSIZE); + if ((r = sys_page_unmap(0, UTEMP)) < 0) + panic("sys_page_unmap: %e", r); +} + +envid_t +dumbfork(void) +{ + envid_t envid; + uint8_t *addr; + int r; + extern unsigned char end[]; + + // Allocate a new child environment. + // The kernel will initialize it with a copy of our register state, + // so that the child will appear to have called sys_exofork() too - + // except that in the child, this "fake" call to sys_exofork() + // will return 0 instead of the envid of the child. + envid = sys_exofork(); + if (envid < 0) + panic("sys_exofork: %e", envid); + if (envid == 0) { + // We're the child. + // The copied value of the global variable 'thisenv' + // is no longer valid (it refers to the parent!). + // Fix it and return 0. + thisenv = &envs[ENVX(sys_getenvid())]; + return 0; + } + + // We're the parent. + // Eagerly copy our entire address space into the child. + // This is NOT what you should do in your fork implementation. + for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE) + duppage(envid, addr); + + // Also copy the stack we are currently running on. + duppage(envid, ROUNDDOWN(&addr, PGSIZE)); + + // Start the child environment running + if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) + panic("sys_env_set_status: %e", r); + + return envid; +} + diff --git a/lab/LAB4.si4project/Backup/dumbfork(7443).c b/lab/LAB4.si4project/Backup/dumbfork(7443).c new file mode 100644 index 0000000..e5e433c --- /dev/null +++ b/lab/LAB4.si4project/Backup/dumbfork(7443).c @@ -0,0 +1,80 @@ +// Ping-pong a counter between two processes. +// Only need to start one of these -- splits into two, crudely. + +#include +#include + +envid_t dumbfork(void); + +void +umain(int argc, char **argv) +{ + envid_t who; + int i; + + // fork a child process + who = dumbfork(); + + // print a message and yield to the other a few times + for (i = 0; i < (who ? 10 : 20); i++) { + cprintf("%d: I am the %s!\n", i, who ? "parent" : "child"); + sys_yield(); + } +} + +void +duppage(envid_t dstenv, void *addr) +{ + int r; + + // This is NOT what you should do in your fork. + if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_alloc: %e", r); + if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_map: %e", r); + memmove(UTEMP, addr, PGSIZE); + if ((r = sys_page_unmap(0, UTEMP)) < 0) + panic("sys_page_unmap: %e", r); +} + +envid_t +dumbfork(void) +{ + envid_t envid; + uint8_t *addr; + int r; + extern unsigned char end[]; + + // Allocate a new child environment. + // The kernel will initialize it with a copy of our register state, + // so that the child will appear to have called sys_exofork() too - + // except that in the child, this "fake" call to sys_exofork() + // will return 0 instead of the envid of the child. + envid = sys_exofork(); + if (envid < 0) + panic("sys_exofork: %e", envid); + if (envid == 0) { + // We're the child. + // The copied value of the global variable 'thisenv' + // is no longer valid (it refers to the parent!). + // Fix it and return 0. + thisenv = &envs[ENVX(sys_getenvid())]; + return 0; + } + + // We're the parent. + // Eagerly copy our entire address space into the child. + // This is NOT what you should do in your fork implementation. + for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE) + duppage(envid, addr); + + // Also copy the stack we are currently running on. + duppage(envid, ROUNDDOWN(&addr, PGSIZE)); + + // Start the child environment running + if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) + panic("sys_env_set_status: %e", r); + + return envid; +} + diff --git a/lab/LAB4.si4project/Backup/dumbfork(8040).c b/lab/LAB4.si4project/Backup/dumbfork(8040).c new file mode 100644 index 0000000..281f4bf --- /dev/null +++ b/lab/LAB4.si4project/Backup/dumbfork(8040).c @@ -0,0 +1,89 @@ +// Ping-pong a counter between two processes. +// Only need to start one of these -- splits into two, crudely. + +#include +#include + +envid_t dumbfork(void); + +void +umain(int argc, char **argv) +{ + envid_t who; + int i; + + // fork a child process + who = dumbfork(); + + // print a message and yield to the other a few times + for (i = 0; i < (who ? 10 : 20); i++) { + cprintf("%d: I am the %s!\n", i, who ? "parent" : "child"); + sys_yield(); + } +} + +void +duppage(envid_t dstenv, void *addr) +{ + int r; + + // This is NOT what you should do in your fork. + if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_alloc: %e", r); + // dstenv 的 addr 映射到 envs[0]的UTEMP, + if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0) + panic("sys_page_map: %e", r); + + memmove(UTEMP, addr, PGSIZE); + if ((r = sys_page_unmap(0, UTEMP)) < 0) + panic("sys_page_unmap: %e", r); +} + +envid_t +dumbfork(void) +{ + envid_t envid; + uint8_t *addr; + int r; + extern unsigned char end[]; + + // Allocate a new child environment. + // The kernel will initialize it with a copy of our register state, + // so that the child will appear to have called sys_exofork() too - + // except that in the child, this "fake" call to sys_exofork() + // will return 0 instead of the envid of the child. + envid = sys_exofork(); + cprintf("envid: %d\n",envid); + if (envid < 0) + panic("sys_exofork: %e", envid); + if (envid == 0) { + // We're the child. + // The copied value of the global variable 'thisenv' + // is no longer valid (it refers to the parent!). + // Fix it and return 0. + // "child, sysgetenvid() : 1 + // cprintf("child, sysgetenvid() :%d\n", ENVX(sys_getenvid())); + thisenv = &envs[ENVX(sys_getenvid())]; + return 0; + } + + // We're the parent. + // Eagerly copy our entire address space into the child. + // This is NOT what you should do in your fork implementation. + for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE) { + // copying addr :00800000 + // copying addr :00801000 + // copying addr :00802000 + cprintf("copying addr :%08x\n", (uint32_t)addr); + duppage(envid, addr); + } + // Also copy the stack we are currently running on. + duppage(envid, ROUNDDOWN(&addr, PGSIZE)); + + // Start the child environment running + if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) + panic("sys_env_set_status: %e", r); + + return envid; +} + diff --git a/lab/LAB4.si4project/Backup/entry(6508).S b/lab/LAB4.si4project/Backup/entry(6508).S new file mode 100644 index 0000000..222d16c --- /dev/null +++ b/lab/LAB4.si4project/Backup/entry(6508).S @@ -0,0 +1,35 @@ +#include +#include + +.data +// Define the global symbols 'envs', 'pages', 'uvpt', and 'uvpd' +// so that they can be used in C as if they were ordinary global arrays. + .globl envs + .set envs, UENVS + .globl pages + .set pages, UPAGES + .globl uvpt + .set uvpt, UVPT + .globl uvpd + .set uvpd, (UVPT+(UVPT>>12)*4) + + +// Entrypoint - this is where the kernel (or our parent environment) +// starts us running when we are initially loaded into a new environment. +.text +.globl _start +_start: + // See if we were started with arguments on the stack + cmpl $USTACKTOP, %esp + jne args_exist + + // If not, push dummy argc/argv arguments. + // This happens when we are loaded by the kernel, + // because the kernel does not know about passing arguments. + pushl $0 + pushl $0 + +args_exist: + call libmain +1: jmp 1b + diff --git a/lab/LAB4.si4project/Backup/env(1818).c b/lab/LAB4.si4project/Backup/env(1818).c new file mode 100644 index 0000000..746327b --- /dev/null +++ b/lab/LAB4.si4project/Backup/env(1818).c @@ -0,0 +1,569 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct Env *envs = NULL; // All environments +static struct Env *env_free_list; // Free environment list + // (linked by Env->env_link) + +#define ENVGENSHIFT 12 // >= LOGNENV + +// Global descriptor table. +// +// Set up global descriptor table (GDT) with separate segments for +// kernel mode and user mode. Segments serve many purposes on the x86. +// We don't use any of their memory-mapping capabilities, but we need +// them to switch privilege levels. +// +// The kernel and user segments are identical except for the DPL. +// To load the SS register, the CPL must equal the DPL. Thus, +// we must duplicate the segments for the user and the kernel. +// +// In particular, the last argument to the SEG macro used in the +// definition of gdt specifies the Descriptor Privilege Level (DPL) +// of that descriptor: 0 for kernel and 3 for user. +// +struct Segdesc gdt[NCPU + 5] = +{ + // 0x0 - unused (always faults -- for trapping NULL far pointers) + SEG_NULL, + + // 0x8 - kernel code segment + [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0), + + // 0x10 - kernel data segment + [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0), + + // 0x18 - user code segment + [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3), + + // 0x20 - user data segment + [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3), + + // Per-CPU TSS descriptors (starting from GD_TSS0) are initialized + // in trap_init_percpu() + [GD_TSS0 >> 3] = SEG_NULL +}; + +struct Pseudodesc gdt_pd = { + sizeof(gdt) - 1, (unsigned long) gdt +}; + +// +// Converts an envid to an env pointer. +// If checkperm is set, the specified environment must be either the +// current environment or an immediate child of the current environment. +// +// RETURNS +// 0 on success, -E_BAD_ENV on error. +// On success, sets *env_store to the environment. +// On error, sets *env_store to NULL. +// +int +envid2env(envid_t envid, struct Env **env_store, bool checkperm) +{ + struct Env *e; + + // If envid is zero, return the current environment. + if (envid == 0) { + *env_store = curenv; + return 0; + } + + // Look up the Env structure via the index part of the envid, + // then check the env_id field in that struct Env + // to ensure that the envid is not stale + // (i.e., does not refer to a _previous_ environment + // that used the same slot in the envs[] array). + e = &envs[ENVX(envid)]; + if (e->env_status == ENV_FREE || e->env_id != envid) { + *env_store = 0; + return -E_BAD_ENV; + } + + // Check that the calling environment has legitimate permission + // to manipulate the specified environment. + // If checkperm is set, the specified environment + // must be either the current environment + // or an immediate child of the current environment. + if (checkperm && e != curenv && e->env_parent_id != curenv->env_id) { + *env_store = 0; + return -E_BAD_ENV; + } + + *env_store = e; + return 0; +} + +// Mark all environments in 'envs' as free, set their env_ids to 0, +// and insert them into the env_free_list. +// Make sure the environments are in the free list in the same order +// they are in the envs array (i.e., so that the first call to +// env_alloc() returns envs[0]). +// +void +env_init(void) +{ + // Set up envs array + // LAB 3: Your code here. + int i; + // 确保最小的env在最前端 + for (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(); +} + +// Load GDT and segment descriptors. +void +env_init_percpu(void) +{ + lgdt(&gdt_pd); + // The kernel never uses GS or FS, so we leave those set to + // the user data segment. + asm volatile("movw %%ax,%%gs" : : "a" (GD_UD|3)); + asm volatile("movw %%ax,%%fs" : : "a" (GD_UD|3)); + // The kernel does use ES, DS, and SS. We'll change between + // the kernel and user data segments as needed. + asm volatile("movw %%ax,%%es" : : "a" (GD_KD)); + asm volatile("movw %%ax,%%ds" : : "a" (GD_KD)); + asm volatile("movw %%ax,%%ss" : : "a" (GD_KD)); + // Load the kernel text segment into CS. + asm volatile("ljmp %0,$1f\n 1:\n" : : "i" (GD_KT)); + // For good measure, clear the local descriptor table (LDT), + // since we don't use it. + lldt(0); +} + +// +// Initialize the kernel virtual memory layout for environment e. +// Allocate a page directory, set e->env_pgdir accordingly, +// and initialize the kernel portion of the new environment's address space. +// Do NOT (yet) map anything into the user portion +// of the environment's virtual address space. +// +// Returns 0 on success, < 0 on error. Errors include: +// -E_NO_MEM if page directory or table could not be allocated. +// 这里又为每个环境分配一个页目录,有点晕了。 +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; + + // Now, set e->env_pgdir and initialize the page directory. + // + // Hint: + // - The VA space of all envs is identical above UTOP + // (except at UVPT, which we've set below). + // See inc/memlayout.h for permissions and layout. + // Can you use kern_pgdir as a template? Hint: Yes. + // (Make sure you got the permissions right in Lab 2.) + // - The initial VA below UTOP is empty. + // - You do not need to make any more calls to page_alloc. + // - Note: In general, pp_ref is not maintained for + // physical pages mapped only above UTOP, but env_pgdir + // is an exception -- you need to increment env_pgdir's + // pp_ref for env_free to work correctly. + // - The functions in kern/pmap.h are handy. + + + // LAB 3: Your code here. + // 申请一个页表存用户环境的页目录 + + e->env_pgdir = page2kva(p); + // why ?因为在UTOP之上都是一样的,所以可以直接把kern_pgdir的内容全部拷贝过来 + + 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 + // 但是唯独UVPT这个地方是不一样的,因为要放的是自己的页表目录 + e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U; + + return 0; +} + +// +// Allocates and initializes a new environment. +// On success, the new environment is stored in *newenv_store. +// +// Returns 0 on success, < 0 on failure. Errors include: +// -E_NO_FREE_ENV if all NENV environments are allocated +// -E_NO_MEM on memory exhaustion +// +int +env_alloc(struct Env **newenv_store, envid_t parent_id) +{ + int32_t generation; + int r; + struct Env *e; + + if (!(e = env_free_list)) + return -E_NO_FREE_ENV; + + // Allocate and set up the page directory for this environment. + if ((r = env_setup_vm(e)) < 0) + return r; + + // Generate an env_id for this environment. + generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1); + if (generation <= 0) // Don't create a negative env_id. + generation = 1 << ENVGENSHIFT; + e->env_id = generation | (e - envs); + + // Set the basic status variables. + e->env_parent_id = parent_id; + e->env_type = ENV_TYPE_USER; + e->env_status = ENV_RUNNABLE; + e->env_runs = 0; + + // Clear out all the saved register state, + // to prevent the register values + // of a prior environment inhabiting this Env structure + // from "leaking" into our new environment. + memset(&e->env_tf, 0, sizeof(e->env_tf)); + + // Set up appropriate initial values for the segment registers. + // GD_UD is the user data segment selector in the GDT, and + // GD_UT is the user text segment selector (see inc/memlayout.h). + // The low 2 bits of each segment register contains the + // Requestor Privilege Level (RPL); 3 means user mode. When + // we switch privilege levels, the hardware does various + // checks involving the RPL and the Descriptor Privilege Level + // (DPL) stored in the descriptors themselves. + e->env_tf.tf_ds = GD_UD | 3; + e->env_tf.tf_es = GD_UD | 3; + e->env_tf.tf_ss = GD_UD | 3; + e->env_tf.tf_esp = USTACKTOP; + e->env_tf.tf_cs = GD_UT | 3; + // You will set e->env_tf.tf_eip later. + + // Enable interrupts while in user mode. + // LAB 4: Your code here. + + // Clear the page fault handler until user installs one. + e->env_pgfault_upcall = 0; + + // Also clear the IPC receiving flag. + e->env_ipc_recving = 0; + + // commit the allocation + env_free_list = e->env_link; + *newenv_store = e; + + cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + return 0; +} + +// +// Allocate len bytes of physical memory for environment env, +// and map it at virtual address va in the environment's address space. +// Does not zero or otherwise initialize the mapped pages in any way. +// Pages should be writable by user and kernel. +// Panic if any allocation attempt fails. +// +static void +region_alloc(struct Env *e, void *va, size_t len) +{ + // LAB 3: Your code here. + // (But only if you need it for load_icode.) + // + struct PageInfo *pp; + size_t i; + size_t pgs = ROUNDUP(len, PGSIZE)/PGSIZE; + size_t pva = ROUNDDOWN((size_t)va, PGSIZE); + + + for(i=0; i < pgs; i++) { + if (!(pp = page_alloc(ALLOC_ZERO))) { + int ex = -E_NO_MEM; + panic("region_alloc: %e", ex); + } + page_insert(e->env_pgdir, pp, (void *)pva, PTE_U|PTE_W|PTE_P); + pva += PGSIZE; + + } + // Hint: It is easier to use region_alloc if the caller can pass + // 'va' and 'len' values that are not page-aligned. + // You should round va down, and round (va + len) up. + // (Watch out for corner-cases!) +} + +// +// Set up the initial program binary, stack, and processor flags +// for a user process. +// This function is ONLY called during kernel initialization, +// before running the first user-mode environment. +// +// This function loads all loadable segments from the ELF binary image +// into the environment's user memory, starting at the appropriate +// virtual addresses indicated in the ELF program header. +// At the same time it clears to zero any portions of these segments +// that are marked in the program header as being mapped +// but not actually present in the ELF file - i.e., the program's bss section. +// +// All this is very similar to what our boot loader does, except the boot +// loader also needs to read the code from disk. Take a look at +// boot/main.c to get ideas. +// +// Finally, this function maps one page for the program's initial stack. +// +// load_icode panics if it encounters problems. +// - How might load_icode fail? What might be wrong with the given input? +// +static void +load_icode(struct Env *e, uint8_t *binary) +{ + // Hints: + // Load each program segment into virtual memory + // at the address specified in the ELF segment header. + // You should only load segments with ph->p_type == ELF_PROG_LOAD. + // Each segment's virtual address can be found in ph->p_va + // and its size in memory can be found in ph->p_memsz. + // The ph->p_filesz bytes from the ELF binary, starting at + // 'binary + ph->p_offset', should be copied to virtual address + // ph->p_va. Any remaining memory bytes should be cleared to zero. + // (The ELF header should have ph->p_filesz <= ph->p_memsz.) + // Use functions from the previous lab to allocate and map pages. + // + // All page protection bits should be user read/write for now. + // ELF segments are not necessarily page-aligned, but you can + // assume for this function that no two segments will touch + // the same virtual page. + // + // You may find a function like region_alloc useful. + // + // Loading the segments is much simpler if you can move data + // directly into the virtual addresses stored in the ELF binary. + // So which page directory should be in force during + // this function? + // + // You must also do something with the program's entry point, + // to make sure that the environment starts executing there. + // What? (See env_run() and env_pop_tf() below.) + + // LAB 3: Your code here. + // 怎么得到program的ELF地址? 就是bianry + struct Proghdr *ph, *eph; + struct Elf *elfhdr = (struct Elf *)binary; + + // is this a valid ELF? + if (elfhdr->e_magic != ELF_MAGIC) + panic("elf header's magic is not correct\n"); + + // 所有程序段 + ph = (struct Proghdr *)((uint8_t *)elfhdr + elfhdr->e_phoff); + eph = ph + elfhdr->e_phnum; + // 转换到用户页目录,用户空间映射 + lcr3(PADDR(e->env_pgdir)); + + for(; ph < eph; ph++) { + + if (ph->p_type != ELF_PROG_LOAD) + continue; + if (ph->p_filesz > ph->p_memsz) + panic("file size is great than memmory size\n"); + + region_alloc(e, (void *)ph->p_va, ph->p_memsz); + memcpy((void *)ph->p_va, binary + ph->p_offset, ph->p_filesz); + // clear bss section + memset((void *)ph->p_va + ph->p_filesz, 0, (ph->p_memsz - ph->p_filesz)); + + } + e->env_tf.tf_eip = elfhdr->e_entry; + // Now map one page for the program's initial stack + // at virtual address USTACKTOP - PGSIZE. + + // LAB 3: Your code here. + + // map the user stack + region_alloc(e, (void *)USTACKTOP-PGSIZE, PGSIZE); + lcr3(PADDR(kern_pgdir)); +} + +// +// Allocates a new env with env_alloc, loads the named elf +// binary into it with load_icode, and sets its env_type. +// This function is ONLY called during kernel initialization, +// before running the first user-mode environment. +// The new env's parent ID is set to 0. +// +void +env_create(uint8_t *binary, enum EnvType type) +{ + // LAB 3: Your code here. + struct Env *newenv; + int ret = 0; + if ((ret = env_alloc(&newenv, 0)) < 0) { + panic("env_create: %e\n", ret); + } + newenv->env_type = type; + load_icode(newenv, binary); + +} + +// +// Frees env e and all memory it uses. +// +void +env_free(struct Env *e) +{ + pte_t *pt; + uint32_t pdeno, pteno; + physaddr_t pa; + + // If freeing the current environment, switch to kern_pgdir + // before freeing the page directory, just in case the page + // gets reused. + if (e == curenv) + lcr3(PADDR(kern_pgdir)); + + // Note the environment's demise. + cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + + // Flush all mapped pages in the user portion of the address space + static_assert(UTOP % PTSIZE == 0); + for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) { + + // only look at mapped page tables + if (!(e->env_pgdir[pdeno] & PTE_P)) + continue; + + // find the pa and va of the page table + pa = PTE_ADDR(e->env_pgdir[pdeno]); + pt = (pte_t*) KADDR(pa); + + // unmap all PTEs in this page table + for (pteno = 0; pteno <= PTX(~0); pteno++) { + if (pt[pteno] & PTE_P) + page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0)); + } + + // free the page table itself + e->env_pgdir[pdeno] = 0; + page_decref(pa2page(pa)); + } + + // free the page directory + pa = PADDR(e->env_pgdir); + e->env_pgdir = 0; + page_decref(pa2page(pa)); + + // return the environment to the free list + e->env_status = ENV_FREE; + e->env_link = env_free_list; + env_free_list = e; +} + +// +// Frees environment e. +// If e was the current env, then runs a new environment (and does not return +// to the caller). +// +void +env_destroy(struct Env *e) +{ + // If e is currently running on other CPUs, we change its state to + // ENV_DYING. A zombie environment will be freed the next time + // it traps to the kernel. + if (e->env_status == ENV_RUNNING && curenv != e) { + e->env_status = ENV_DYING; + return; + } + + env_free(e); + + if (curenv == e) { + curenv = NULL; + sched_yield(); + } +} + + +// +// Restores the register values in the Trapframe with the 'iret' instruction. +// This exits the kernel and starts executing some environment's code. +// +// This function does not return. +// +void +env_pop_tf(struct Trapframe *tf) +{ + // Record the CPU we are running on for user-space debugging + curenv->env_cpunum = cpunum(); + + asm volatile( + "\tmovl %0,%%esp\n" + "\tpopal\n" + "\tpopl %%es\n" + "\tpopl %%ds\n" + "\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */ + "\tiret\n" + : : "g" (tf) : "memory"); + panic("iret failed"); /* mostly to placate the compiler */ +} + +// +// Context switch from curenv to env e. +// Note: if this is the first call to env_run, curenv is NULL. +// +// This function does not return. +// +void +env_run(struct Env *e) +{ + // Step 1: If this is a context switch (a new environment is running): + // 1. Set the current environment (if any) back to + // ENV_RUNNABLE if it is ENV_RUNNING (think about + // what other states it can be in), + // 2. Set 'curenv' to the new environment, + // 3. Set its status to ENV_RUNNING, + // 4. Update its 'env_runs' counter, + // 5. Use lcr3() to switch to its address space. + // Step 2: Use env_pop_tf() to restore the environment's + // registers and drop into user mode in the + // environment. + + // Hint: This function loads the new environment's state from + // e->env_tf. Go back through the code you wrote above + // and make sure you have set the relevant parts of + // e->env_tf to sensible values. + + // LAB 3: Your code here. + if (curenv && curenv->env_status == ENV_RUNNING) { + curenv->env_status = ENV_RUNNABLE; + } + curenv = e; + curenv->env_status = ENV_RUNNING; + curenv->env_runs++; + lcr3(PADDR(curenv->env_pgdir)); + // 将当前进程的trapframe通过弹栈的形式,切换当前的运行环境 + env_pop_tf(&(curenv->env_tf)); + + unlock_kernel(); + // panic("env_run not yet implemented"); +} + diff --git a/lab/LAB4.si4project/Backup/env(4388).h b/lab/LAB4.si4project/Backup/env(4388).h new file mode 100644 index 0000000..b04f1a3 --- /dev/null +++ b/lab/LAB4.si4project/Backup/env(4388).h @@ -0,0 +1,70 @@ +/* See COPYRIGHT for copyright information. */ + +#ifndef JOS_INC_ENV_H +#define JOS_INC_ENV_H + +#include +#include +#include + +typedef int32_t envid_t; + +// An environment ID 'envid_t' has three parts: +// +// +1+---------------21-----------------+--------10--------+ +// |0| Uniqueifier | Environment | +// | | | Index | +// +------------------------------------+------------------+ +// \--- ENVX(eid) --/ +// +// The environment index ENVX(eid) equals the environment's index in the +// 'envs[]' array. The uniqueifier distinguishes environments that were +// created at different times, but share the same environment index. +// +// All real environments are greater than 0 (so the sign bit is zero). +// envid_ts less than 0 signify errors. The envid_t == 0 is special, and +// stands for the current environment. + +#define LOG2NENV 10 +#define NENV (1 << LOG2NENV) +#define ENVX(envid) ((envid) & (NENV - 1)) + +// Values of env_status in struct Env +enum { + ENV_FREE = 0, + ENV_DYING, + ENV_RUNNABLE, + ENV_RUNNING, + ENV_NOT_RUNNABLE +}; + +// Special environment types +enum EnvType { + ENV_TYPE_USER = 0, +}; + +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 + int env_cpunum; // The CPU that the env is running on + + // Address space + pde_t *env_pgdir; // Kernel virtual address of page dir + + // Exception handling + void *env_pgfault_upcall; // Page fault upcall entry point + + // Lab 4 IPC + bool env_ipc_recving; // Env is blocked receiving + void *env_ipc_dstva; // VA at which to map received page + uint32_t env_ipc_value; // Data value sent to us + envid_t env_ipc_from; // envid of the sender + int env_ipc_perm; // Perm of page mapping received +}; + +#endif // !JOS_INC_ENV_H diff --git a/lab/LAB4.si4project/Backup/env(572).c b/lab/LAB4.si4project/Backup/env(572).c new file mode 100644 index 0000000..7396b21 --- /dev/null +++ b/lab/LAB4.si4project/Backup/env(572).c @@ -0,0 +1,572 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct Env *envs = NULL; // All environments +static struct Env *env_free_list; // Free environment list + // (linked by Env->env_link) + +#define ENVGENSHIFT 12 // >= LOGNENV + +// Global descriptor table. +// +// Set up global descriptor table (GDT) with separate segments for +// kernel mode and user mode. Segments serve many purposes on the x86. +// We don't use any of their memory-mapping capabilities, but we need +// them to switch privilege levels. +// +// The kernel and user segments are identical except for the DPL. +// To load the SS register, the CPL must equal the DPL. Thus, +// we must duplicate the segments for the user and the kernel. +// +// In particular, the last argument to the SEG macro used in the +// definition of gdt specifies the Descriptor Privilege Level (DPL) +// of that descriptor: 0 for kernel and 3 for user. +// +struct Segdesc gdt[NCPU + 5] = +{ + // 0x0 - unused (always faults -- for trapping NULL far pointers) + SEG_NULL, + + // 0x8 - kernel code segment + [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0), + + // 0x10 - kernel data segment + [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0), + + // 0x18 - user code segment + [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3), + + // 0x20 - user data segment + [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3), + + // Per-CPU TSS descriptors (starting from GD_TSS0) are initialized + // in trap_init_percpu() + [GD_TSS0 >> 3] = SEG_NULL +}; + +struct Pseudodesc gdt_pd = { + sizeof(gdt) - 1, (unsigned long) gdt +}; + +// +// Converts an envid to an env pointer. +// If checkperm is set, the specified environment must be either the +// current environment or an immediate child of the current environment. +// +// RETURNS +// 0 on success, -E_BAD_ENV on error. +// On success, sets *env_store to the environment. +// On error, sets *env_store to NULL. +// +int +envid2env(envid_t envid, struct Env **env_store, bool checkperm) +{ + struct Env *e; + + // If envid is zero, return the current environment. + if (envid == 0) { + *env_store = curenv; + return 0; + } + + // Look up the Env structure via the index part of the envid, + // then check the env_id field in that struct Env + // to ensure that the envid is not stale + // (i.e., does not refer to a _previous_ environment + // that used the same slot in the envs[] array). + e = &envs[ENVX(envid)]; + if (e->env_status == ENV_FREE || e->env_id != envid) { + *env_store = 0; + return -E_BAD_ENV; + } + + // Check that the calling environment has legitimate permission + // to manipulate the specified environment. + // If checkperm is set, the specified environment + // must be either the current environment + // or an immediate child of the current environment. + if (checkperm && e != curenv && e->env_parent_id != curenv->env_id) { + *env_store = 0; + return -E_BAD_ENV; + } + + *env_store = e; + return 0; +} + +// Mark all environments in 'envs' as free, set their env_ids to 0, +// and insert them into the env_free_list. +// Make sure the environments are in the free list in the same order +// they are in the envs array (i.e., so that the first call to +// env_alloc() returns envs[0]). +// +void +env_init(void) +{ + // Set up envs array + // LAB 3: Your code here. + int i; + // 确保最小的env在最前端 + for (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(); +} + +// Load GDT and segment descriptors. +void +env_init_percpu(void) +{ + lgdt(&gdt_pd); + // The kernel never uses GS or FS, so we leave those set to + // the user data segment. + asm volatile("movw %%ax,%%gs" : : "a" (GD_UD|3)); + asm volatile("movw %%ax,%%fs" : : "a" (GD_UD|3)); + // The kernel does use ES, DS, and SS. We'll change between + // the kernel and user data segments as needed. + asm volatile("movw %%ax,%%es" : : "a" (GD_KD)); + asm volatile("movw %%ax,%%ds" : : "a" (GD_KD)); + asm volatile("movw %%ax,%%ss" : : "a" (GD_KD)); + // Load the kernel text segment into CS. + asm volatile("ljmp %0,$1f\n 1:\n" : : "i" (GD_KT)); + // For good measure, clear the local descriptor table (LDT), + // since we don't use it. + lldt(0); +} + +// +// Initialize the kernel virtual memory layout for environment e. +// Allocate a page directory, set e->env_pgdir accordingly, +// and initialize the kernel portion of the new environment's address space. +// Do NOT (yet) map anything into the user portion +// of the environment's virtual address space. +// +// Returns 0 on success, < 0 on error. Errors include: +// -E_NO_MEM if page directory or table could not be allocated. +// 这里又为每个环境分配一个页目录,有点晕了。 +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; + + // Now, set e->env_pgdir and initialize the page directory. + // + // Hint: + // - The VA space of all envs is identical above UTOP + // (except at UVPT, which we've set below). + // See inc/memlayout.h for permissions and layout. + // Can you use kern_pgdir as a template? Hint: Yes. + // (Make sure you got the permissions right in Lab 2.) + // - The initial VA below UTOP is empty. + // - You do not need to make any more calls to page_alloc. + // - Note: In general, pp_ref is not maintained for + // physical pages mapped only above UTOP, but env_pgdir + // is an exception -- you need to increment env_pgdir's + // pp_ref for env_free to work correctly. + // - The functions in kern/pmap.h are handy. + + + // LAB 3: Your code here. + // 申请一个页表存用户环境的页目录 + + e->env_pgdir = page2kva(p); + // why ?因为在UTOP之上都是一样的,所以可以直接把kern_pgdir的内容全部拷贝过来 + + 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 + // 但是唯独UVPT这个地方是不一样的,因为要放的是自己的页表目录 + e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U; + + return 0; +} + +// +// Allocates and initializes a new environment. +// On success, the new environment is stored in *newenv_store. +// +// Returns 0 on success, < 0 on failure. Errors include: +// -E_NO_FREE_ENV if all NENV environments are allocated +// -E_NO_MEM on memory exhaustion +// +int +env_alloc(struct Env **newenv_store, envid_t parent_id) +{ + int32_t generation; + int r; + struct Env *e; + + if (!(e = env_free_list)) + return -E_NO_FREE_ENV; + + // Allocate and set up the page directory for this environment. + if ((r = env_setup_vm(e)) < 0) + return r; + + // Generate an env_id for this environment. + generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1); + if (generation <= 0) // Don't create a negative env_id. + generation = 1 << ENVGENSHIFT; + e->env_id = generation | (e - envs); + + // Set the basic status variables. + e->env_parent_id = parent_id; + e->env_type = ENV_TYPE_USER; + e->env_status = ENV_RUNNABLE; + e->env_runs = 0; + + // Clear out all the saved register state, + // to prevent the register values + // of a prior environment inhabiting this Env structure + // from "leaking" into our new environment. + memset(&e->env_tf, 0, sizeof(e->env_tf)); + + // Set up appropriate initial values for the segment registers. + // GD_UD is the user data segment selector in the GDT, and + // GD_UT is the user text segment selector (see inc/memlayout.h). + // The low 2 bits of each segment register contains the + // Requestor Privilege Level (RPL); 3 means user mode. When + // we switch privilege levels, the hardware does various + // checks involving the RPL and the Descriptor Privilege Level + // (DPL) stored in the descriptors themselves. + e->env_tf.tf_ds = GD_UD | 3; + e->env_tf.tf_es = GD_UD | 3; + e->env_tf.tf_ss = GD_UD | 3; + e->env_tf.tf_esp = USTACKTOP; + e->env_tf.tf_cs = GD_UT | 3; + // You will set e->env_tf.tf_eip later. + + // Enable interrupts while in user mode. + // LAB 4: Your code here. + + // Clear the page fault handler until user installs one. + e->env_pgfault_upcall = 0; + + // Also clear the IPC receiving flag. + e->env_ipc_recving = 0; + + // commit the allocation + env_free_list = e->env_link; + *newenv_store = e; + + cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + return 0; +} + +// +// Allocate len bytes of physical memory for environment env, +// and map it at virtual address va in the environment's address space. +// Does not zero or otherwise initialize the mapped pages in any way. +// Pages should be writable by user and kernel. +// Panic if any allocation attempt fails. +// +static void +region_alloc(struct Env *e, void *va, size_t len) +{ + // LAB 3: Your code here. + // (But only if you need it for load_icode.) + // + struct PageInfo *pp; + size_t i; + size_t pgs = ROUNDUP(len, PGSIZE)/PGSIZE; + size_t pva = ROUNDDOWN((size_t)va, PGSIZE); + + + for(i=0; i < pgs; i++) { + if (!(pp = page_alloc(ALLOC_ZERO))) { + int ex = -E_NO_MEM; + panic("region_alloc: %e", ex); + } + page_insert(e->env_pgdir, pp, (void *)pva, PTE_U|PTE_W|PTE_P); + pva += PGSIZE; + + } + // Hint: It is easier to use region_alloc if the caller can pass + // 'va' and 'len' values that are not page-aligned. + // You should round va down, and round (va + len) up. + // (Watch out for corner-cases!) +} + +// +// Set up the initial program binary, stack, and processor flags +// for a user process. +// This function is ONLY called during kernel initialization, +// before running the first user-mode environment. +// +// This function loads all loadable segments from the ELF binary image +// into the environment's user memory, starting at the appropriate +// virtual addresses indicated in the ELF program header. +// At the same time it clears to zero any portions of these segments +// that are marked in the program header as being mapped +// but not actually present in the ELF file - i.e., the program's bss section. +// +// All this is very similar to what our boot loader does, except the boot +// loader also needs to read the code from disk. Take a look at +// boot/main.c to get ideas. +// +// Finally, this function maps one page for the program's initial stack. +// +// load_icode panics if it encounters problems. +// - How might load_icode fail? What might be wrong with the given input? +// +static void +load_icode(struct Env *e, uint8_t *binary) +{ + // Hints: + // Load each program segment into virtual memory + // at the address specified in the ELF segment header. + // You should only load segments with ph->p_type == ELF_PROG_LOAD. + // Each segment's virtual address can be found in ph->p_va + // and its size in memory can be found in ph->p_memsz. + // The ph->p_filesz bytes from the ELF binary, starting at + // 'binary + ph->p_offset', should be copied to virtual address + // ph->p_va. Any remaining memory bytes should be cleared to zero. + // (The ELF header should have ph->p_filesz <= ph->p_memsz.) + // Use functions from the previous lab to allocate and map pages. + // + // All page protection bits should be user read/write for now. + // ELF segments are not necessarily page-aligned, but you can + // assume for this function that no two segments will touch + // the same virtual page. + // + // You may find a function like region_alloc useful. + // + // Loading the segments is much simpler if you can move data + // directly into the virtual addresses stored in the ELF binary. + // So which page directory should be in force during + // this function? + // + // You must also do something with the program's entry point, + // to make sure that the environment starts executing there. + // What? (See env_run() and env_pop_tf() below.) + + // LAB 3: Your code here. + // 怎么得到program的ELF地址? 就是bianry + struct Proghdr *ph, *eph; + struct Elf *elfhdr = (struct Elf *)binary; + + // is this a valid ELF? + if (elfhdr->e_magic != ELF_MAGIC) + panic("elf header's magic is not correct\n"); + + // 所有程序段 + ph = (struct Proghdr *)((uint8_t *)elfhdr + elfhdr->e_phoff); + eph = ph + elfhdr->e_phnum; + // 转换到用户页目录,用户空间映射 + lcr3(PADDR(e->env_pgdir)); + + for(; ph < eph; ph++) { + + if (ph->p_type != ELF_PROG_LOAD) + continue; + if (ph->p_filesz > ph->p_memsz) + panic("file size is great than memmory size\n"); + + region_alloc(e, (void *)ph->p_va, ph->p_memsz); + memcpy((void *)ph->p_va, binary + ph->p_offset, ph->p_filesz); + // clear bss section + memset((void *)ph->p_va + ph->p_filesz, 0, (ph->p_memsz - ph->p_filesz)); + + } + e->env_tf.tf_eip = elfhdr->e_entry; + // Now map one page for the program's initial stack + // at virtual address USTACKTOP - PGSIZE. + + // LAB 3: Your code here. + + // map the user stack + region_alloc(e, (void *)USTACKTOP-PGSIZE, PGSIZE); + lcr3(PADDR(kern_pgdir)); +} + +// +// Allocates a new env with env_alloc, loads the named elf +// binary into it with load_icode, and sets its env_type. +// This function is ONLY called during kernel initialization, +// before running the first user-mode environment. +// The new env's parent ID is set to 0. +// +void +env_create(uint8_t *binary, enum EnvType type) +{ + // LAB 3: Your code here. + struct Env *newenv; + int ret = 0; + if ((ret = env_alloc(&newenv, 0)) < 0) { + panic("env_create: %e\n", ret); + } + newenv->env_type = type; + load_icode(newenv, binary); + +} + +// +// Frees env e and all memory it uses. +// +void +env_free(struct Env *e) +{ + pte_t *pt; + uint32_t pdeno, pteno; + physaddr_t pa; + + // If freeing the current environment, switch to kern_pgdir + // before freeing the page directory, just in case the page + // gets reused. + if (e == curenv) + lcr3(PADDR(kern_pgdir)); + + // Note the environment's demise. + cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id); + + // Flush all mapped pages in the user portion of the address space + static_assert(UTOP % PTSIZE == 0); + for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) { + + // only look at mapped page tables + if (!(e->env_pgdir[pdeno] & PTE_P)) + continue; + + // find the pa and va of the page table + pa = PTE_ADDR(e->env_pgdir[pdeno]); + pt = (pte_t*) KADDR(pa); + + // unmap all PTEs in this page table + for (pteno = 0; pteno <= PTX(~0); pteno++) { + if (pt[pteno] & PTE_P) + page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0)); + } + + // free the page table itself + e->env_pgdir[pdeno] = 0; + page_decref(pa2page(pa)); + } + + // free the page directory + pa = PADDR(e->env_pgdir); + e->env_pgdir = 0; + page_decref(pa2page(pa)); + + // return the environment to the free list + e->env_status = ENV_FREE; + e->env_link = env_free_list; + env_free_list = e; +} + +// +// Frees environment e. +// If e was the current env, then runs a new environment (and does not return +// to the caller). +// +void +env_destroy(struct Env *e) +{ + // If e is currently running on other CPUs, we change its state to + // ENV_DYING. A zombie environment will be freed the next time + // it traps to the kernel. + if (e->env_status == ENV_RUNNING && curenv != e) { + e->env_status = ENV_DYING; + return; + } + + env_free(e); + + if (curenv == e) { + curenv = NULL; + sched_yield(); + } +} + + +// +// Restores the register values in the Trapframe with the 'iret' instruction. +// This exits the kernel and starts executing some environment's code. +// +// This function does not return. +// +void +env_pop_tf(struct Trapframe *tf) +{ + // Record the CPU we are running on for user-space debugging + curenv->env_cpunum = cpunum(); + + asm volatile( + "\tmovl %0,%%esp\n" + "\tpopal\n" + "\tpopl %%es\n" + "\tpopl %%ds\n" + "\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */ + "\tiret\n" + : : "g" (tf) : "memory"); + panic("iret failed"); /* mostly to placate the compiler */ +} + +// +// Context switch from curenv to env e. +// Note: if this is the first call to env_run, curenv is NULL. +// +// This function does not return. +// +void +env_run(struct Env *e) +{ + // Step 1: If this is a context switch (a new environment is running): + // 1. Set the current environment (if any) back to + // ENV_RUNNABLE if it is ENV_RUNNING (think about + // what other states it can be in), + // 2. Set 'curenv' to the new environment, + // 3. Set its status to ENV_RUNNING, + // 4. Update its 'env_runs' counter, + // 5. Use lcr3() to switch to its address space. + // Step 2: Use env_pop_tf() to restore the environment's + // registers and drop into user mode in the + // environment. + + // Hint: This function loads the new environment's state from + // e->env_tf. Go back through the code you wrote above + // and make sure you have set the relevant parts of + // e->env_tf to sensible values. + + // LAB 3: Your code here. + if (curenv && curenv->env_status == ENV_RUNNING) { + curenv->env_status = ENV_RUNNABLE; + } + curenv = e; + curenv->env_status = ENV_RUNNING; + curenv->env_runs++; + lcr3(PADDR(curenv->env_pgdir)); + + unlock_kernel(); + // iret退出内核, 回到用户环境执行, + // 在load_icode() 中 env_tf保存了可执行文件的eip等信息 + env_pop_tf(&(curenv->env_tf)); + + + // panic("env_run not yet implemented"); +} + diff --git a/lab/LAB4.si4project/Backup/fork(3728).c b/lab/LAB4.si4project/Backup/fork(3728).c new file mode 100644 index 0000000..61264da --- /dev/null +++ b/lab/LAB4.si4project/Backup/fork(3728).c @@ -0,0 +1,90 @@ +// implement fork from user space + +#include +#include + +// PTE_COW marks copy-on-write page table entries. +// It is one of the bits explicitly allocated to user processes (PTE_AVAIL). +#define PTE_COW 0x800 + +// +// Custom page fault handler - if faulting page is copy-on-write, +// map in our own private writable copy. +// +static void +pgfault(struct UTrapframe *utf) +{ + void *addr = (void *) utf->utf_fault_va; + uint32_t err = utf->utf_err; + int r; + + // Check that the faulting access was (1) a write, and (2) to a + // copy-on-write page. If not, panic. + // Hint: + // Use the read-only page table mappings at uvpt + // (see ). + + // LAB 4: Your code here. + + // Allocate a new page, map it at a temporary location (PFTEMP), + // copy the data from the old page to the new page, then move the new + // page to the old page's address. + // Hint: + // You should make three system calls. + + // LAB 4: Your code here. + + panic("pgfault not implemented"); +} + +// +// Map our virtual page pn (address pn*PGSIZE) into the target envid +// at the same virtual address. If the page is writable or copy-on-write, +// the new mapping must be created copy-on-write, and then our mapping must be +// marked copy-on-write as well. (Exercise: Why do we need to mark ours +// copy-on-write again if it was already copy-on-write at the beginning of +// this function?) +// +// Returns: 0 on success, < 0 on error. +// It is also OK to panic on error. +// +static int +duppage(envid_t envid, unsigned pn) +{ + int r; + + // LAB 4: Your code here. + panic("duppage not implemented"); + return 0; +} + +// +// User-level fork with copy-on-write. +// Set up our page fault handler appropriately. +// Create a child. +// Copy our address space and page fault handler setup to the child. +// Then mark the child as runnable and return. +// +// Returns: child's envid to the parent, 0 to the child, < 0 on error. +// It is also OK to panic on error. +// +// Hint: +// Use uvpd, uvpt, and duppage. +// Remember to fix "thisenv" in the child process. +// Neither user exception stack should ever be marked copy-on-write, +// so you must allocate a new page for the child's user exception stack. +// +envid_t +fork(void) +{ + // LAB 4: Your code here. + panic("fork not implemented"); +} + +// Challenge! +int +sfork(void) +{ + panic("sfork not implemented"); + return -E_INVAL; +} diff --git a/lab/LAB4.si4project/Backup/ipc(963).c b/lab/LAB4.si4project/Backup/ipc(963).c new file mode 100644 index 0000000..2e222b9 --- /dev/null +++ b/lab/LAB4.si4project/Backup/ipc(963).c @@ -0,0 +1,56 @@ +// User-level IPC library routines + +#include + +// Receive a value via IPC and return it. +// If 'pg' is nonnull, then any page sent by the sender will be mapped at +// that address. +// If 'from_env_store' is nonnull, then store the IPC sender's envid in +// *from_env_store. +// If 'perm_store' is nonnull, then store the IPC sender's page permission +// in *perm_store (this is nonzero iff a page was successfully +// transferred to 'pg'). +// If the system call fails, then store 0 in *fromenv and *perm (if +// they're nonnull) and return the error. +// Otherwise, return the value sent by the sender +// +// Hint: +// Use 'thisenv' to discover the value and who sent it. +// If 'pg' is null, pass sys_ipc_recv a value that it will understand +// as meaning "no page". (Zero is not the right value, since that's +// a perfectly valid place to map a page.) +int32_t +ipc_recv(envid_t *from_env_store, void *pg, int *perm_store) +{ + // LAB 4: Your code here. + panic("ipc_recv not implemented"); + return 0; +} + +// Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'. +// This function keeps trying until it succeeds. +// It should panic() on any error other than -E_IPC_NOT_RECV. +// +// Hint: +// Use sys_yield() to be CPU-friendly. +// If 'pg' is null, pass sys_ipc_try_send a value that it will understand +// as meaning "no page". (Zero is not the right value.) +void +ipc_send(envid_t to_env, uint32_t val, void *pg, int perm) +{ + // LAB 4: Your code here. + panic("ipc_send not implemented"); +} + +// Find the first environment of the given type. We'll use this to +// find special environments. +// Returns 0 if no such environment exists. +envid_t +ipc_find_env(enum EnvType type) +{ + int i; + for (i = 0; i < NENV; i++) + if (envs[i].env_type == type) + return envs[i].env_id; + return 0; +} diff --git a/lab/LAB4.si4project/Backup/pfentry(173).S b/lab/LAB4.si4project/Backup/pfentry(173).S new file mode 100644 index 0000000..f40aeeb --- /dev/null +++ b/lab/LAB4.si4project/Backup/pfentry(173).S @@ -0,0 +1,82 @@ +#include +#include + +// Page fault upcall entrypoint. + +// This is where we ask the kernel to redirect us to whenever we cause +// a page fault in user space (see the call to sys_set_pgfault_handler +// in pgfault.c). +// +// When a page fault actually occurs, the kernel switches our ESP to +// point to the user exception stack if we're not already on the user +// exception stack, and then it pushes a UTrapframe onto our user +// exception stack: +// +// trap-time esp +// trap-time eflags +// trap-time eip +// utf_regs.reg_eax +// ... +// utf_regs.reg_esi +// utf_regs.reg_edi +// utf_err (error code) +// utf_fault_va <-- %esp +// +// If this is a recursive fault, the kernel will reserve for us a +// blank word above the trap-time esp for scratch work when we unwind +// the recursive call. +// +// We then have call up to the appropriate page fault handler in C +// code, pointed to by the global variable '_pgfault_handler'. + +.text +.globl _pgfault_upcall +_pgfault_upcall: + // Call the C page fault handler. + pushl %esp // function argument: pointer to UTF + movl _pgfault_handler, %eax + call *%eax + addl $4, %esp // pop function argument + + // Now the C page fault handler has returned and you must return + // to the trap time state. + // Push trap-time %eip onto the trap-time stack. + // + // Explanation: + // We must prepare the trap-time stack for our eventual return to + // re-execute the instruction that faulted. + // Unfortunately, we can't return directly from the exception stack: + // We can't call 'jmp', since that requires that we load the address + // into a register, and all registers must have their trap-time + // values after the return. + // We can't call 'ret' from the exception stack either, since if we + // did, %esp would have the wrong value. + // So instead, we push the trap-time %eip onto the *trap-time* stack! + // Below we'll switch to that stack and call 'ret', which will + // restore %eip to its pre-fault value. + // + // In the case of a recursive fault on the exception stack, + // note that the word we're pushing now will fit in the + // blank word that the kernel reserved for us. + // + // Throughout the remaining code, think carefully about what + // registers are available for intermediate calculations. You + // may find that you have to rearrange your code in non-obvious + // ways as registers become unavailable as scratch space. + // + // LAB 4: Your code here. + + // Restore the trap-time registers. After you do this, you + // can no longer modify any general-purpose registers. + // LAB 4: Your code here. + + // Restore eflags from the stack. After you do this, you can + // no longer use arithmetic operations or anything else that + // modifies eflags. + // LAB 4: Your code here. + + // Switch back to the adjusted trap-time stack. + // LAB 4: Your code here. + + // Return to re-execute the instruction that faulted. + // LAB 4: Your code here. diff --git a/lab/LAB4.si4project/Backup/pgfault(4647).c b/lab/LAB4.si4project/Backup/pgfault(4647).c new file mode 100644 index 0000000..a975518 --- /dev/null +++ b/lab/LAB4.si4project/Backup/pgfault(4647).c @@ -0,0 +1,37 @@ +// User-level page fault handler support. +// Rather than register the C page fault handler directly with the +// kernel as the page fault handler, we register the assembly language +// wrapper in pfentry.S, which in turns calls the registered C +// function. + +#include + + +// Assembly language pgfault entrypoint defined in lib/pfentry.S. +extern void _pgfault_upcall(void); + +// Pointer to currently installed C-language pgfault handler. +void (*_pgfault_handler)(struct UTrapframe *utf); + +// +// Set the page fault handler function. +// If there isn't one yet, _pgfault_handler will be 0. +// The first time we register a handler, we need to +// allocate an exception stack (one page of memory with its top +// at UXSTACKTOP), and tell the kernel to call the assembly-language +// _pgfault_upcall routine when a page fault occurs. +// +void +set_pgfault_handler(void (*handler)(struct UTrapframe *utf)) +{ + int r; + + if (_pgfault_handler == 0) { + // First time through! + // LAB 4: Your code here. + panic("set_pgfault_handler not implemented"); + } + + // Save handler pointer for assembly to call. + _pgfault_handler = handler; +} diff --git a/lab/LAB4.si4project/Backup/picirq(7101).c b/lab/LAB4.si4project/Backup/picirq(7101).c new file mode 100644 index 0000000..8cb3e62 --- /dev/null +++ b/lab/LAB4.si4project/Backup/picirq(7101).c @@ -0,0 +1,86 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include + +#include + + +// Current IRQ mask. +// Initial IRQ mask has interrupt 2 enabled (for slave 8259A). +uint16_t irq_mask_8259A = 0xFFFF & ~(1<> 8)); + cprintf("enabled interrupts:"); + for (i = 0; i < 16; i++) + if (~mask & (1< +#include +#include +#include +#include + +#include +#include +#include +#include + +// These variables are set by i386_detect_memory() +size_t npages; // Amount of physical memory (in pages) +static size_t npages_basemem; // Amount of base memory (in pages) + +// These variables are set in mem_init() +pde_t *kern_pgdir; // Kernel's initial page directory +struct PageInfo *pages; // Physical page state array +static struct PageInfo *page_free_list; // Free list of physical pages + + +// -------------------------------------------------------------- +// Detect machine's physical memory setup. +// -------------------------------------------------------------- + +static int +nvram_read(int r) +{ + return mc146818_read(r) | (mc146818_read(r + 1) << 8); +} + +static void +i386_detect_memory(void) +{ + size_t basemem, extmem, ext16mem, totalmem; + + // Use CMOS calls to measure available base & extended memory. + // (CMOS calls return results in kilobytes.) + basemem = nvram_read(NVRAM_BASELO); + extmem = nvram_read(NVRAM_EXTLO); + ext16mem = nvram_read(NVRAM_EXT16LO) * 64; + + // Calculate the number of physical pages available in both base + // and extended memory. + if (ext16mem) + totalmem = 16 * 1024 + ext16mem; + else if (extmem) + totalmem = 1 * 1024 + extmem; + else + totalmem = basemem; + + npages = totalmem / (PGSIZE / 1024); + npages_basemem = basemem / (PGSIZE / 1024); + + cprintf("Physical memory: %uK available, base = %uK, extended = %uK\n", + totalmem, basemem, totalmem - basemem); +} + + +// -------------------------------------------------------------- +// Set up memory mappings above UTOP. +// -------------------------------------------------------------- + +static void mem_init_mp(void); +static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm); +static void check_page_free_list(bool only_low_memory); +static void check_page_alloc(void); +static void check_kern_pgdir(void); +static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va); +static void check_page(void); +static void check_page_installed_pgdir(void); + +// This simple physical memory allocator is used only while JOS is setting +// up its virtual memory system. page_alloc() is the real allocator. +// +// If n>0, allocates enough pages of contiguous physical memory to hold 'n' +// bytes. Doesn't initialize the memory. Returns a kernel virtual address. +// +// If n==0, returns the address of the next free page without allocating +// anything. +// +// If we're out of memory, boot_alloc should panic. +// This function may ONLY be used during initialization, +// before the page_free_list list has been set up. +static void * +boot_alloc(uint32_t n) +{ + static char *nextfree; // virtual address of next byte of free memory + char *result; + + // Initialize nextfree if this is the first time. + // 'end' is a magic symbol automatically generated by the linker, + // which points to the end of the kernel's bss segment: + // the first virtual address that the linker did *not* assign + // to any kernel code or global variables. + if (!nextfree) { + extern char end[]; + nextfree = ROUNDUP((char *) end, PGSIZE); + } + + // Allocate a chunk large enough to hold 'n' bytes, then update + // nextfree. Make sure nextfree is kept aligned + // to a multiple of PGSIZE. + // + // LAB 2: Your code here. + result = nextfree; + nextfree = ROUNDUP(nextfree+n, PGSIZE); + + return result; +} + +// Set up a two-level page table: +// kern_pgdir is its linear (virtual) address of the root +// +// This function only sets up the kernel part of the address space +// (ie. addresses >= UTOP). The user part of the address space +// will be set up later. +// +// From UTOP to ULIM, the user is allowed to read but not write. +// Above ULIM the user cannot read or write. +void +mem_init(void) +{ + uint32_t cr0; + size_t n; + + // Find out how much memory the machine has (npages & npages_basemem). + i386_detect_memory(); + + // Remove this line when you're ready to test this function. + //panic("mem_init: This function is not finished\n"); + + ////////////////////////////////////////////////////////////////////// + // create initial page directory. + kern_pgdir = (pde_t *) boot_alloc(PGSIZE); + memset(kern_pgdir, 0, PGSIZE); + + ////////////////////////////////////////////////////////////////////// + // Recursively insert PD in itself as a page table, to form + // a virtual page table at virtual address UVPT. + // (For now, you don't have understand the greater purpose of the + // following line.) + + // Permissions: kernel R, user R + kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P; + + ////////////////////////////////////////////////////////////////////// + // Allocate an array of npages 'struct PageInfo's and store it in 'pages'. + // The kernel uses this array to keep track of physical pages: for + // each physical page, there is a corresponding struct PageInfo in this + // array. 'npages' is the number of physical pages in memory. Use memset + // to initialize all fields of each struct PageInfo to 0. + // Your code goes here: + pages = (struct PageInfo*)boot_alloc(sizeof(struct PageInfo)*npages); + memset(pages, 0, sizeof(struct PageInfo)*npages); + + ////////////////////////////////////////////////////////////////////// + // 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); + memset(envs, 0, sizeof(struct Env)*NENV); + ////////////////////////////////////////////////////////////////////// + // Now that we've allocated the initial kernel data structures, we set + // up the list of free physical pages. Once we've done so, all further + // memory management will go through the page_* functions. In + // particular, we can now map memory using boot_map_region + // or page_insert + page_init(); + + check_page_free_list(1); + check_page_alloc(); + check_page(); + + ////////////////////////////////////////////////////////////////////// + // Now we set up virtual memory + + ////////////////////////////////////////////////////////////////////// + // Map 'pages' read-only by the user at linear address UPAGES + // Permissions: + // - the new image at UPAGES -- kernel R, user R + // (ie. perm = PTE_U | PTE_P) + // - pages itself -- kernel RW, user NONE + // Your code goes here: + // 要求把pages结构体所在的页面和虚拟地址UPAGES相互映射。 + // 这里只要计算出pages结构体的大小,就可以进行映射了。 + // 说实话,之前注释有点没看懂。以为要实现虚存对pages指向的物理页的映射 + boot_map_region(kern_pgdir, + UPAGES, + ROUNDUP((sizeof(struct PageInfo)*npages), PGSIZE), + PADDR(pages), + PTE_U ); + ////////////////////////////////////////////////////////////////////// + // 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, + ROUNDUP((sizeof(struct Env)*NENV), PGSIZE), + PADDR(envs), + PTE_U); + ////////////////////////////////////////////////////////////////////// + // Use the physical memory that 'bootstack' refers to as the kernel + // stack. The kernel stack grows down from virtual address KSTACKTOP. + // We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP) + // to be the kernel stack, but break this into two pieces: + // * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory + // * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if + // the kernel overflows its stack, it will fault rather than + // overwrite memory. Known as a "guard page". + // Permissions: kernel RW, user NONE + // Your code goes here: + // extern char bootstacktop[], bootstack[]; + boot_map_region(kern_pgdir, + KSTACKTOP-KSTKSIZE, + KSTKSIZE, + PADDR(bootstack), + PTE_W ); + ////////////////////////////////////////////////////////////////////// + // Map all of physical memory at KERNBASE. + // Ie. the VA range [KERNBASE, 2^32) should map to + // the PA range [0, 2^32 - KERNBASE) + // We might not have 2^32 - KERNBASE bytes of physical memory, but + // we just set up the mapping anyway. + // Permissions: kernel RW, user NONE + // Your code goes here: + uint32_t kern_size = ROUNDUP((0xFFFFFFFF-KERNBASE), PGSIZE); + // cprintf("size: %d pages:%d\n", kern_size, kern_size/PGSIZE); + boot_map_region(kern_pgdir, + (uintptr_t) KERNBASE, + kern_size, + (physaddr_t)0, + PTE_W ); + + // Initialize the SMP-related parts of the memory map + // 这部分与上面的stack映射有点重复吧。 + mem_init_mp(); + + + // Check that the initial page directory has been set up correctly. + check_kern_pgdir(); + + // Switch from the minimal entry page directory to the full kern_pgdir + // page table we just created. Our instruction pointer should be + // somewhere between KERNBASE and KERNBASE+4MB right now, which is + // mapped the same way by both page tables. + // + // If the machine reboots at this point, you've probably set up your + // kern_pgdir wrong. + lcr3(PADDR(kern_pgdir)); + + check_page_free_list(0); + + // entry.S set the really important flags in cr0 (including enabling + // paging). Here we configure the rest of the flags that we care about. + cr0 = rcr0(); + cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP; + cr0 &= ~(CR0_TS|CR0_EM); + lcr0(cr0); + + // Some more checks, only possible after kern_pgdir is installed. + check_page_installed_pgdir(); +} + +// Modify mappings in kern_pgdir to support SMP +// - Map the per-CPU stacks in the region [KSTACKTOP-PTSIZE, KSTACKTOP) +// +static void +mem_init_mp(void) +{ + // Map per-CPU stacks starting at KSTACKTOP, for up to 'NCPU' CPUs. + // + // For CPU i, use the physical memory that 'percpu_kstacks[i]' refers + // to as its kernel stack. CPU i's kernel stack grows down from virtual + // address kstacktop_i = KSTACKTOP - i * (KSTKSIZE + KSTKGAP), and is + // divided into two pieces, just like the single stack you set up in + // mem_init: + // * [kstacktop_i - KSTKSIZE, kstacktop_i) + // -- backed by physical memory + // * [kstacktop_i - (KSTKSIZE + KSTKGAP), kstacktop_i - KSTKSIZE) + // -- not backed; so if the kernel overflows its stack, + // it will fault rather than overwrite another CPU's stack. + // Known as a "guard page". + // Permissions: kernel RW, user NONE + // + // LAB 4: Your code here: + size_t i; + size_t kstacktop_i; + for(i = 0; i < NCPU; i++) { + kstacktop_i = KSTACKTOP - i * (KSTKSIZE + KSTKGAP); + boot_map_region(kern_pgdir, + kstacktop_i - KSTKSIZE, + KSTKSIZE, + PADDR(&percpu_kstacks[i]), + PTE_W ); + + } +} + +// -------------------------------------------------------------- +// Tracking of physical pages. +// The 'pages' array has one 'struct PageInfo' entry per physical page. +// Pages are reference counted, and free pages are kept on a linked list. +// -------------------------------------------------------------- + +// +// Initialize page structure and memory free list. +// After this is done, NEVER use boot_alloc again. ONLY use the page +// allocator functions below to allocate and deallocate physical +// memory via the page_free_list. +// +void +page_init(void) +{ + // LAB 4: + // Change your code to mark the physical page at MPENTRY_PADDR + // as in use + + // The example code here marks all physical pages as free. + // However this is not truly the case. What memory is free? + // 1) Mark physical page 0 as in use. + // This way we preserve the real-mode IDT and BIOS structures + // in case we ever need them. (Currently we don't, but...) + // 2) The rest of base memory, [PGSIZE, npages_basemem * PGSIZE) + // is free. + // 3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must + // never be allocated. + // 4) Then extended memory [EXTPHYSMEM, ...). + // Some of it is in use, some is free. Where is the kernel + // in physical memory? Which pages are already in use for + // page tables and other data structures? + // + // Change the code to reflect this. + // NB: DO NOT actually touch the physical memory corresponding to + // free pages! + + // 1.mark page 0 as in use + // 这样我们就可以保留实模式IDT和BIOS结构,以备不时之需。 + pages[0].pp_ref = 1; + + // 2. + size_t i; + for (i = 1; i < MPENTRY_PADDR/PGSIZE; i++) { + pages[i].pp_ref = 0; + pages[i].pp_link = page_free_list; + page_free_list = &pages[i]; + } + // cprintf("npage_basemem: %d\n", npages_basemem); npage_basemem:160 + // boot APs entry code + extern unsigned char mpentry_start[], mpentry_end[]; + size_t size = mpentry_end - mpentry_start; + size = ROUNDUP(size, PGSIZE); + for(;i<(MPENTRY_PADDR+size)/PGSIZE; i++) { + pages[i].pp_ref = 1; + } + + for (; i < npages_basemem; i++) { + pages[i].pp_ref = 0; + pages[i].pp_link = page_free_list; + page_free_list = &pages[i]; + } + + // 3.[IOPHYSMEM, EXTPHYSMEM) + // mark I/O hole + for (;ipp_link; + pp->pp_link = NULL; + + //page2kva 返回值 KernelBase + 物理页号<pp_ref reaches 0.) +// +void +page_free(struct PageInfo *pp) +{ + // Fill this function in + // Hint: You may want to panic if pp->pp_ref is nonzero or + // pp->pp_link is not NULL. + if(pp->pp_link || pp->pp_ref) { + panic("pp->pp_ref is nonzero or pp->pp_link is not NULL\n"); + } + pp->pp_link = page_free_list; + page_free_list = pp; +} + +// +// Decrement the reference count on a page, +// freeing it if there are no more refs. +// +void +page_decref(struct PageInfo* pp) +{ + if (--pp->pp_ref == 0) + page_free(pp); +} + +// Given 'pgdir', a pointer to a page directory, pgdir_walk returns +// a pointer to the page table entry (PTE) for linear address 'va'. +// This requires walking the two-level page table structure. +// +// The relevant page table page might not exist yet. +// If this is true, and create == false, then pgdir_walk returns NULL. +// Otherwise, pgdir_walk allocates a new page table page with page_alloc. +// - If the allocation fails, pgdir_walk returns NULL. +// - Otherwise, the new page's reference count is incremented, +// the page is cleared, +// and pgdir_walk returns a pointer into the new page table page. +// +// Hint 1: you can turn a PageInfo * into the physical address of the +// page it refers to with page2pa() from kern/pmap.h. +// +// Hint 2: the x86 MMU checks permission bits in both the page directory +// and the page table, so it's safe to leave permissions in the page +// directory more permissive than strictly necessary. +// +// Hint 3: look at inc/mmu.h for useful macros that manipulate page +// table and page directory entries. +// +pte_t * +pgdir_walk(pde_t *pgdir, const void *va, int create) +{ + // Fill this function in + uint32_t pdx = PDX(va); // 页目录项索引 + uint32_t ptx = PTX(va); // 页表项索引 + pte_t *pde; // 页目录项指针 + pte_t *pte; // 页表项指针 + struct PageInfo *pp; + + pde = &pgdir[pdx]; //获取页目录项 + + if (*pde & PTE_P) { + // 二级页表有效 + // PTE_ADDR得到物理地址,KADDR转为虚拟地址 + pte = (KADDR(PTE_ADDR(*pde))); + } + else { + + // 二级页表不存在, + if (!create) { + return NULL; + } + // 获取一页的内存,创建一个新的页表,来存放页表项 + if(!(pp = page_alloc(ALLOC_ZERO))) { + return NULL; + } + pte = (pte_t *)page2kva(pp); + pp->pp_ref++; + *pde = PADDR(pte) | (PTE_P | PTE_W | PTE_U); // 设置页目录项 + } + // 返回页表项的虚拟地址 + return &pte[ptx]; +} + +// +// Map [va, va+size) of virtual address space to physical [pa, pa+size) +// in the page table rooted at pgdir. Size is a multiple of PGSIZE, and +// va and pa are both page-aligned. +// Use permission bits perm|PTE_P for the entries. +// +// This function is only intended to set up the ``static'' mappings +// above UTOP. As such, it should *not* change the pp_ref field on the +// mapped pages. +// +// Hint: the TA solution uses pgdir_walk +static void +boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm) +{ + size_t pgs = size / PGSIZE; + if (size % PGSIZE != 0) { + pgs++; + } //计算总共有多少页 + for (int i = 0; i < pgs; i++) { + // 其实可以使用page_insert实现 + pte_t *pte = pgdir_walk(pgdir, (void *)va, 1);//获取va对应的PTE的地址 + if (pte == NULL) { + panic("boot_map_region(): out of memory\n"); + } + *pte = pa | PTE_P | perm; //修改va对应的PTE的值 + pa += PGSIZE; //更新pa和va,进行下一轮循环 + va += PGSIZE; + } +} + + +// +// Map the physical page 'pp' at virtual address 'va'. +// The permissions (the low 12 bits) of the page table entry +// should be set to 'perm|PTE_P'. +// +// Requirements +// - If there is already a page mapped at 'va', it should be page_remove()d. +// - If necessary, on demand, a page table should be allocated and inserted +// into 'pgdir'. // create +// - pp->pp_ref should be incremented if the insertion succeeds. +// - The TLB must be invalidated if a page was formerly present at 'va'. +// +// Corner-case hint: Make sure to consider what happens when the same +// pp is re-inserted at the same virtual address in the same pgdir. +// However, try not to distinguish this case in your code, as this +// frequently leads to subtle bugs; there's an elegant way to handle +// everything in one code path. +// +// RETURNS: +// 0 on success +// -E_NO_MEM, if page table couldn't be allocated +// +// Hint: The TA solution is implemented using pgdir_walk, page_remove, +// and page2pa. +// +int +page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) +{ + // Fill this function in + pte_t *pte = pgdir_walk(pgdir, va, 1); + + if (!pte) { + + return -E_NO_MEM; + } + + if (*pte & PTE_P) { + if (PTE_ADDR(*pte) == page2pa(pp)) { + + // 插入的是同一个页面,只需要修改权限等即可 + pp->pp_ref--; + } + else { + + page_remove(pgdir, va); + } + + } + + pp->pp_ref++; + *pte = page2pa(pp)| perm | PTE_P; + + return 0; +} + +// +// Return the page mapped at virtual address 'va'. +// If pte_store is not zero, then we store in it the address +// of the pte for this page. This is used by page_remove and +// can be used to verify page permissions for syscall arguments, +// but should not be used by most callers. +// +// Return NULL if there is no page mapped at va. +// +// Hint: the TA solution uses pgdir_walk and pa2page. +// +struct PageInfo * +page_lookup(pde_t *pgdir, void *va, pte_t **pte_store) +{ + // Fill this function in + pte_t *pte = pgdir_walk(pgdir, va, 0); + if (!pte) { + + return NULL; + } + if (pte_store) { + *pte_store = pte; // 通过指针的指针返回指针给调用者 + } + + // 难道不用考虑页表项是否存在 + + if (*pte & PTE_P) { + + return (pa2page(PTE_ADDR(*pte))); + } + + return NULL; + + //return pa2page(PTE_ADDR(*pte)); +} + +// +// Unmaps the physical page at virtual address 'va'. +// If there is no physical page at that address, silently does nothing. +// +// Details: +// - The ref count on the physical page should decrement. +// - The physical page should be freed if the refcount reaches 0. +// - The pg table entry corresponding to 'va' should be set to 0. +// (if such a PTE exists) +// - The TLB must be invalidated if you remove an entry from +// the page table. +// +// Hint: The TA solution is implemented using page_lookup, +// tlb_invalidate, and page_decref. +// + +void +page_remove(pde_t *pgdir, void *va) +{ + // Fill this function in + // 二级指针有点晕 + pte_t *pte; + pte_t **pte_store = &pte; + + struct PageInfo *pi = page_lookup(pgdir, va, pte_store); + if (!pi) { + return ; + } + + page_decref(pi); // 减引用 + + **pte_store = 0; // 取消映射 + tlb_invalidate(pgdir, va); + + +} + +// +// Invalidate a TLB entry, but only if the page tables being +// edited are the ones currently in use by the processor. +// +void +tlb_invalidate(pde_t *pgdir, void *va) +{ + // Flush the entry only if we're modifying the current address space. + if (!curenv || curenv->env_pgdir == pgdir) + invlpg(va); +} + +// +// Reserve size bytes in the MMIO region and map [pa,pa+size) at this +// location. Return the base of the reserved region. size does *not* +// have to be multiple of PGSIZE. +// +void * +mmio_map_region(physaddr_t pa, size_t size) +{ + // Where to start the next region. Initially, this is the + // beginning of the MMIO region. Because this is static, its + // value will be preserved between calls to mmio_map_region + // (just like nextfree in boot_alloc). + static uintptr_t base = MMIOBASE; + + // Reserve size bytes of virtual memory starting at base and + // map physical pages [pa,pa+size) to virtual addresses + // [base,base+size). Since this is device memory and not + // regular DRAM, you'll have to tell the CPU that it isn't + // safe to cache access to this memory. Luckily, the page + // tables provide bits for this purpose; simply create the + // mapping with PTE_PCD|PTE_PWT (cache-disable and + // write-through) in addition to PTE_W. (If you're interested + // in more details on this, see section 10.5 of IA32 volume + // 3A.) + // + // Be sure to round size up to a multiple of PGSIZE and to + // handle if this reservation would overflow MMIOLIM (it's + // okay to simply panic if this happens). + // + // Hint: The staff solution uses boot_map_region. + // + // Your code here: + // ret -> MMIOBASE 暂存 + void *ret = (void *)base; + size = ROUNDUP(size, PGSIZE); + if (base + size > MMIOLIM || base + size < base) { + panic("mmio_map_region reservation overflow\n"); + } + + boot_map_region(kern_pgdir, base, size, pa, PTE_W|PTE_PCD|PTE_PWT); + // 为什么需要加以下操作? base 为static! + base += size; + return ret; +} + +static uintptr_t user_mem_check_addr; + +// +// Check that an environment is allowed to access the range of memory +// [va, va+len) with permissions 'perm | PTE_P'. +// Normally 'perm' will contain PTE_U at least, but this is not required. +// 'va' and 'len' need not be page-aligned; you must test every page that +// contains any of that range. You will test either 'len/PGSIZE', +// 'len/PGSIZE + 1', or 'len/PGSIZE + 2' pages. +// +// A user program can access a virtual address if (1) the address is below +// ULIM, and (2) the page table gives it permission. These are exactly +// the tests you should implement here. +// +// If there is an error, set the 'user_mem_check_addr' variable to the first +// erroneous virtual address. +// +// Returns 0 if the user program can access this range of addresses, +// and -E_FAULT otherwise. +// +/* +int +user_mem_check(struct Env *env, const void *va, size_t len, int perm) +{ + // LAB 3: Your code here. + + size_t vae = ROUNDUP((size_t)(va+len), PGSIZE); + size_t vas = ROUNDDOWN((size_t)va, PGSIZE); + + + pte_t * pte; + + for (; vas < vae; vas += PGSIZE ) { + pte = pgdir_walk(env->env_pgdir, (void *)vas, 0); + if(!( vas < ULIM && pte==NULL && (*pte & perm))) { + if (vas < (size_t)va) + { + user_mem_check_addr = (uintptr_t)va; + } + else + { + user_mem_check_addr = (uintptr_t)vas; + } + return -E_FAULT; + } + } + return 0; +} +*/ +int +user_mem_check(struct Env *env, const void *va, size_t len, int perm) +{ + // LAB 3: Your code here. + uint32_t start = (uint32_t)ROUNDDOWN((char *)va, PGSIZE); + uint32_t end = (uint32_t)ROUNDUP((char *)va+len, PGSIZE); + for(; start < end; start += PGSIZE) { + pte_t *pte = pgdir_walk(env->env_pgdir, (void*)start, 0); + if((start >= ULIM) || (pte == NULL) || !(*pte & PTE_P) || ((*pte & perm) != perm)) { + user_mem_check_addr = (start < (uint32_t)va ? (uint32_t)va : start); + return -E_FAULT; + } + } + return 0; +} + + +// +// Checks that environment 'env' is allowed to access the range +// of memory [va, va+len) with permissions 'perm | PTE_U | PTE_P'. +// If it can, then the function simply returns. +// If it cannot, 'env' is destroyed and, if env is the current +// environment, this function will not return. +// +void +user_mem_assert(struct Env *env, const void *va, size_t len, int perm) +{ + if (user_mem_check(env, va, len, perm | PTE_U) < 0) { + cprintf("[%08x] user_mem_check assertion failure for " + "va %08x\n", env->env_id, user_mem_check_addr); + env_destroy(env); // may not return + } +} + + +// -------------------------------------------------------------- +// Checking functions. +// -------------------------------------------------------------- + +// +// Check that the pages on the page_free_list are reasonable. +// +static void +check_page_free_list(bool only_low_memory) +{ + struct PageInfo *pp; + unsigned pdx_limit = only_low_memory ? 1 : NPDENTRIES; + int nfree_basemem = 0, nfree_extmem = 0; + char *first_free_page; + + if (!page_free_list) + panic("'page_free_list' is a null pointer!"); + + if (only_low_memory) { + // Move pages with lower addresses first in the free + // list, since entry_pgdir does not map all pages. + struct PageInfo *pp1, *pp2; + struct PageInfo **tp[2] = { &pp1, &pp2 }; + for (pp = page_free_list; pp; pp = pp->pp_link) { + int pagetype = PDX(page2pa(pp)) >= pdx_limit; + *tp[pagetype] = pp; + tp[pagetype] = &pp->pp_link; + } + *tp[1] = 0; + *tp[0] = pp2; + page_free_list = pp1; + } + + // if there's a page that shouldn't be on the free list, + // try to make sure it eventually causes trouble. + for (pp = page_free_list; pp; pp = pp->pp_link) + if (PDX(page2pa(pp)) < pdx_limit) + memset(page2kva(pp), 0x97, 128); + + first_free_page = (char *) boot_alloc(0); + for (pp = page_free_list; pp; pp = pp->pp_link) { + // check that we didn't corrupt the free list itself + assert(pp >= pages); + assert(pp < pages + npages); + assert(((char *) pp - (char *) pages) % sizeof(*pp) == 0); + + // check a few pages that shouldn't be on the free list + assert(page2pa(pp) != 0); + assert(page2pa(pp) != IOPHYSMEM); + assert(page2pa(pp) != EXTPHYSMEM - PGSIZE); + assert(page2pa(pp) != EXTPHYSMEM); + assert(page2pa(pp) < EXTPHYSMEM || (char *) page2kva(pp) >= first_free_page); + // (new test for lab 4) + assert(page2pa(pp) != MPENTRY_PADDR); + + if (page2pa(pp) < EXTPHYSMEM) + ++nfree_basemem; + else + ++nfree_extmem; + } + + assert(nfree_basemem > 0); + assert(nfree_extmem > 0); + + cprintf("check_page_free_list() succeeded!\n"); +} + +// +// Check the physical page allocator (page_alloc(), page_free(), +// and page_init()). +// +static void +check_page_alloc(void) +{ + struct PageInfo *pp, *pp0, *pp1, *pp2; + int nfree; + struct PageInfo *fl; + char *c; + int i; + + if (!pages) + panic("'pages' is a null pointer!"); + + // check number of free pages + for (pp = page_free_list, nfree = 0; pp; pp = pp->pp_link) + ++nfree; + + // should be able to allocate three pages + pp0 = pp1 = pp2 = 0; + assert((pp0 = page_alloc(0))); + assert((pp1 = page_alloc(0))); + assert((pp2 = page_alloc(0))); + + assert(pp0); + assert(pp1 && pp1 != pp0); + assert(pp2 && pp2 != pp1 && pp2 != pp0); + assert(page2pa(pp0) < npages*PGSIZE); + assert(page2pa(pp1) < npages*PGSIZE); + assert(page2pa(pp2) < npages*PGSIZE); + + // temporarily steal the rest of the free pages + fl = page_free_list; + page_free_list = 0; + + // should be no free memory + assert(!page_alloc(0)); + + // free and re-allocate? + page_free(pp0); + page_free(pp1); + page_free(pp2); + pp0 = pp1 = pp2 = 0; + assert((pp0 = page_alloc(0))); + assert((pp1 = page_alloc(0))); + assert((pp2 = page_alloc(0))); + assert(pp0); + assert(pp1 && pp1 != pp0); + assert(pp2 && pp2 != pp1 && pp2 != pp0); + assert(!page_alloc(0)); + + // test flags + memset(page2kva(pp0), 1, PGSIZE); + page_free(pp0); + assert((pp = page_alloc(ALLOC_ZERO))); + assert(pp && pp0 == pp); + c = page2kva(pp); + for (i = 0; i < PGSIZE; i++) + assert(c[i] == 0); + + // give free list back + page_free_list = fl; + + // free the pages we took + page_free(pp0); + page_free(pp1); + page_free(pp2); + + // number of free pages should be the same + for (pp = page_free_list; pp; pp = pp->pp_link) + --nfree; + assert(nfree == 0); + + cprintf("check_page_alloc() succeeded!\n"); +} + +// +// Checks that the kernel part of virtual address space +// has been set up roughly correctly (by mem_init()). +// +// This function doesn't test every corner case, +// but it is a pretty good sanity check. +// + +static void +check_kern_pgdir(void) +{ + uint32_t i, n; + pde_t *pgdir; + + pgdir = kern_pgdir; + + // check pages array + n = ROUNDUP(npages*sizeof(struct PageInfo), PGSIZE); + for (i = 0; i < n; i += PGSIZE) + assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i); + + // check envs array (new test for lab 3) + n = ROUNDUP(NENV*sizeof(struct Env), PGSIZE); + for (i = 0; i < n; i += PGSIZE) + assert(check_va2pa(pgdir, UENVS + i) == PADDR(envs) + i); + + // check phys mem + for (i = 0; i < npages * PGSIZE; i += PGSIZE) + assert(check_va2pa(pgdir, KERNBASE + i) == i); + + // check kernel stack + // (updated in lab 4 to check per-CPU kernel stacks) + for (n = 0; n < NCPU; n++) { + uint32_t base = KSTACKTOP - (KSTKSIZE + KSTKGAP) * (n + 1); + for (i = 0; i < KSTKSIZE; i += PGSIZE) + assert(check_va2pa(pgdir, base + KSTKGAP + i) + == PADDR(percpu_kstacks[n]) + i); + for (i = 0; i < KSTKGAP; i += PGSIZE) + assert(check_va2pa(pgdir, base + i) == ~0); + } + + // check PDE permissions + for (i = 0; i < NPDENTRIES; i++) { + switch (i) { + case PDX(UVPT): + case PDX(KSTACKTOP-1): + case PDX(UPAGES): + case PDX(UENVS): + case PDX(MMIOBASE): + assert(pgdir[i] & PTE_P); + break; + default: + if (i >= PDX(KERNBASE)) { + assert(pgdir[i] & PTE_P); + assert(pgdir[i] & PTE_W); + } else + assert(pgdir[i] == 0); + break; + } + } + cprintf("check_kern_pgdir() succeeded!\n"); +} + +// This function returns the physical address of the page containing 'va', +// defined by the page directory 'pgdir'. The hardware normally performs +// this functionality for us! We define our own version to help check +// the check_kern_pgdir() function; it shouldn't be used elsewhere. + +static physaddr_t +check_va2pa(pde_t *pgdir, uintptr_t va) +{ + pte_t *p; + + pgdir = &pgdir[PDX(va)]; + if (!(*pgdir & PTE_P)) + return ~0; + p = (pte_t*) KADDR(PTE_ADDR(*pgdir)); + if (!(p[PTX(va)] & PTE_P)) + return ~0; + return PTE_ADDR(p[PTX(va)]); +} + + +// check page_insert, page_remove, &c +static void +check_page(void) +{ + struct PageInfo *pp, *pp0, *pp1, *pp2; + struct PageInfo *fl; + pte_t *ptep, *ptep1; + void *va; + uintptr_t mm1, mm2; + int i; + extern pde_t entry_pgdir[]; + + // should be able to allocate three pages + pp0 = pp1 = pp2 = 0; + assert((pp0 = page_alloc(0))); + assert((pp1 = page_alloc(0))); + assert((pp2 = page_alloc(0))); + + assert(pp0); + assert(pp1 && pp1 != pp0); + assert(pp2 && pp2 != pp1 && pp2 != pp0); + + // temporarily steal the rest of the free pages + fl = page_free_list; + page_free_list = 0; + + // should be no free memory + assert(!page_alloc(0)); + + // there is no page allocated at address 0 + assert(page_lookup(kern_pgdir, (void *) 0x0, &ptep) == NULL); + + // there is no free memory, so we can't allocate a page table + assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) < 0); + + // free pp0 and try again: pp0 should be used for page table + page_free(pp0); + assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) == 0); + assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0)); + assert(check_va2pa(kern_pgdir, 0x0) == page2pa(pp1)); + assert(pp1->pp_ref == 1); + assert(pp0->pp_ref == 1); + + // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table + assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0); + assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2)); + assert(pp2->pp_ref == 1); + + // should be no free memory + assert(!page_alloc(0)); + + // should be able to map pp2 at PGSIZE because it's already there + assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0); + assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2)); + assert(pp2->pp_ref == 1); + + // pp2 should NOT be on the free list + // could happen in ref counts are handled sloppily in page_insert + assert(!page_alloc(0)); + + // check that pgdir_walk returns a pointer to the pte + ptep = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(PGSIZE)])); + assert(pgdir_walk(kern_pgdir, (void*)PGSIZE, 0) == ptep+PTX(PGSIZE)); + + // should be able to change permissions too. + assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W|PTE_U) == 0); + assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2)); + assert(pp2->pp_ref == 1); + assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U); + assert(kern_pgdir[0] & PTE_U); + + // should be able to remap with fewer permissions + assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0); + assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_W); + assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U)); + + // should not be able to map at PTSIZE because need free page for page table + assert(page_insert(kern_pgdir, pp0, (void*) PTSIZE, PTE_W) < 0); + + // insert pp1 at PGSIZE (replacing pp2) + assert(page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W) == 0); + assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U)); + + // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ... + assert(check_va2pa(kern_pgdir, 0) == page2pa(pp1)); + assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp1)); + // ... and ref counts should reflect this + assert(pp1->pp_ref == 2); + assert(pp2->pp_ref == 0); + + // pp2 should be returned by page_alloc + assert((pp = page_alloc(0)) && pp == pp2); + + // unmapping pp1 at 0 should keep pp1 at PGSIZE + page_remove(kern_pgdir, 0x0); + assert(check_va2pa(kern_pgdir, 0x0) == ~0); + assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp1)); + assert(pp1->pp_ref == 1); + assert(pp2->pp_ref == 0); + + // test re-inserting pp1 at PGSIZE + assert(page_insert(kern_pgdir, pp1, (void*) PGSIZE, 0) == 0); + assert(pp1->pp_ref); + assert(pp1->pp_link == NULL); + + // unmapping pp1 at PGSIZE should free it + page_remove(kern_pgdir, (void*) PGSIZE); + assert(check_va2pa(kern_pgdir, 0x0) == ~0); + assert(check_va2pa(kern_pgdir, PGSIZE) == ~0); + assert(pp1->pp_ref == 0); + assert(pp2->pp_ref == 0); + + // so it should be returned by page_alloc + assert((pp = page_alloc(0)) && pp == pp1); + + // should be no free memory + assert(!page_alloc(0)); + + // forcibly take pp0 back + assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0)); + kern_pgdir[0] = 0; + assert(pp0->pp_ref == 1); + pp0->pp_ref = 0; + + // check pointer arithmetic in pgdir_walk + page_free(pp0); + va = (void*)(PGSIZE * NPDENTRIES + PGSIZE); + ptep = pgdir_walk(kern_pgdir, va, 1); + ptep1 = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(va)])); + assert(ptep == ptep1 + PTX(va)); + kern_pgdir[PDX(va)] = 0; + pp0->pp_ref = 0; + + // check that new page tables get cleared + memset(page2kva(pp0), 0xFF, PGSIZE); + page_free(pp0); + pgdir_walk(kern_pgdir, 0x0, 1); + ptep = (pte_t *) page2kva(pp0); + for(i=0; ipp_ref = 0; + + // give free list back + page_free_list = fl; + + // free the pages we took + page_free(pp0); + page_free(pp1); + page_free(pp2); + + // test mmio_map_region + mm1 = (uintptr_t) mmio_map_region(0, 4097); + mm2 = (uintptr_t) mmio_map_region(0, 4096); + // check that they're in the right region + assert(mm1 >= MMIOBASE && mm1 + 8192 < MMIOLIM); + assert(mm2 >= MMIOBASE && mm2 + 8192 < MMIOLIM); + // check that they're page-aligned + assert(mm1 % PGSIZE == 0 && mm2 % PGSIZE == 0); + // check that they don't overlap + assert(mm1 + 8192 <= mm2); + // check page mappings + assert(check_va2pa(kern_pgdir, mm1) == 0); + assert(check_va2pa(kern_pgdir, mm1+PGSIZE) == PGSIZE); + assert(check_va2pa(kern_pgdir, mm2) == 0); + assert(check_va2pa(kern_pgdir, mm2+PGSIZE) == ~0); + // check permissions + assert(*pgdir_walk(kern_pgdir, (void*) mm1, 0) & (PTE_W|PTE_PWT|PTE_PCD)); + assert(!(*pgdir_walk(kern_pgdir, (void*) mm1, 0) & PTE_U)); + // clear the mappings + *pgdir_walk(kern_pgdir, (void*) mm1, 0) = 0; + *pgdir_walk(kern_pgdir, (void*) mm1 + PGSIZE, 0) = 0; + *pgdir_walk(kern_pgdir, (void*) mm2, 0) = 0; + + cprintf("check_page() succeeded!\n"); +} + +// check page_insert, page_remove, &c, with an installed kern_pgdir +static void +check_page_installed_pgdir(void) +{ + struct PageInfo *pp, *pp0, *pp1, *pp2; + struct PageInfo *fl; + pte_t *ptep, *ptep1; + uintptr_t va; + int i; + + // check that we can read and write installed pages + pp1 = pp2 = 0; + assert((pp0 = page_alloc(0))); + assert((pp1 = page_alloc(0))); + assert((pp2 = page_alloc(0))); + page_free(pp0); + memset(page2kva(pp1), 1, PGSIZE); + memset(page2kva(pp2), 2, PGSIZE); + page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W); + assert(pp1->pp_ref == 1); + assert(*(uint32_t *)PGSIZE == 0x01010101U); + page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W); + assert(*(uint32_t *)PGSIZE == 0x02020202U); + assert(pp2->pp_ref == 1); + assert(pp1->pp_ref == 0); + *(uint32_t *)PGSIZE = 0x03030303U; + assert(*(uint32_t *)page2kva(pp2) == 0x03030303U); + page_remove(kern_pgdir, (void*) PGSIZE); + assert(pp2->pp_ref == 0); + + // forcibly take pp0 back + assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0)); + kern_pgdir[0] = 0; + assert(pp0->pp_ref == 1); + pp0->pp_ref = 0; + + // free the pages we took + page_free(pp0); + + cprintf("check_page_installed_pgdir() succeeded!\n"); +} diff --git a/lab/LAB4.si4project/Backup/sched(4329).c b/lab/LAB4.si4project/Backup/sched(4329).c new file mode 100644 index 0000000..f595bb1 --- /dev/null +++ b/lab/LAB4.si4project/Backup/sched(4329).c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include + +void sched_halt(void); + +// Choose a user environment to run and run it. +void +sched_yield(void) +{ + struct Env *idle; + + // Implement simple round-robin scheduling. + // + // Search through 'envs' for an ENV_RUNNABLE environment in + // circular fashion starting just after the env this CPU was + // last running. Switch to the first such environment found. + // + // If no envs are runnable, but the environment previously + // running on this CPU is still ENV_RUNNING, it's okay to + // choose that environment. + // + // Never choose an environment that's currently running on + // another CPU (env_status == ENV_RUNNING). If there are + // no runnable environments, simply drop through to the code + // below to halt the cpu. + + // LAB 4: Your code here. + + // sched_halt never returns + sched_halt(); +} + +// Halt this CPU when there is nothing to do. Wait until the +// timer interrupt wakes it up. This function never returns. +// +void +sched_halt(void) +{ + int i; + + // For debugging and testing purposes, if there are no runnable + // environments in the system, then drop into the kernel monitor. + for (i = 0; i < NENV; i++) { + if ((envs[i].env_status == ENV_RUNNABLE || + envs[i].env_status == ENV_RUNNING || + envs[i].env_status == ENV_DYING)) + break; + } + if (i == NENV) { + cprintf("No runnable environments in the system!\n"); + while (1) + monitor(NULL); + } + + // Mark that no environment is running on this CPU + curenv = NULL; + lcr3(PADDR(kern_pgdir)); + + // Mark that this CPU is in the HALT state, so that when + // timer interupts come in, we know we should re-acquire the + // big kernel lock + xchg(&thiscpu->cpu_status, CPU_HALTED); + + // Release the big kernel lock as if we were "leaving" the kernel + unlock_kernel(); + + // Reset stack pointer, enable interrupts and then halt. + asm volatile ( + "movl $0, %%ebp\n" + "movl %0, %%esp\n" + "pushl $0\n" + "pushl $0\n" + // Uncomment the following line after completing exercise 13 + //"sti\n" + "1:\n" + "hlt\n" + "jmp 1b\n" + : : "a" (thiscpu->cpu_ts.ts_esp0)); +} + diff --git a/lab/LAB4.si4project/Backup/sched(5719).c b/lab/LAB4.si4project/Backup/sched(5719).c new file mode 100644 index 0000000..04e9617 --- /dev/null +++ b/lab/LAB4.si4project/Backup/sched(5719).c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include + +void sched_halt(void); + +// Choose a user environment to run and run it. +void +sched_yield(void) +{ + struct Env *idle; + + // Implement simple round-robin scheduling. + // + // Search through 'envs' for an ENV_RUNNABLE environment in + // circular fashion starting just after the env this CPU was + // last running. Switch to the first such environment found. + // + // If no envs are runnable, but the environment previously + // running on this CPU is still ENV_RUNNING, it's okay to + // choose that environment. + // + // Never choose an environment that's currently running on + // another CPU (env_status == ENV_RUNNING). If there are + // no runnable environments, simply drop through to the code + // below to halt the cpu. + + // LAB 4: Your code here. + + idle = curenv; + int start_envid = idle ? ENVX(idle->env_id)+1 : 0; + + for (int i = 0; i < NENV; i++) { + int j = (start_envid + i) % NENV; + if (envs[j].env_status == ENV_RUNNABLE) { + env_run(&envs[j]); + } + } + + if (idle && idle->env_status == ENV_RUNNING) { + env_run(idle); + } + + // sched_halt never returns + sched_halt(); + +} + +// Halt this CPU when there is nothing to do. Wait until the +// timer interrupt wakes it up. This function never returns. +// +void +sched_halt(void) +{ + int i; + + // For debugging and testing purposes, if there are no runnable + // environments in the system, then drop into the kernel monitor. + for (i = 0; i < NENV; i++) { + if ((envs[i].env_status == ENV_RUNNABLE || + envs[i].env_status == ENV_RUNNING || + envs[i].env_status == ENV_DYING)) + break; + } + if (i == NENV) { + cprintf("No runnable environments in the system!\n"); + while (1) + monitor(NULL); + } + + // Mark that no environment is running on this CPU + curenv = NULL; + lcr3(PADDR(kern_pgdir)); + + // Mark that this CPU is in the HALT state, so that when + // timer interupts come in, we know we should re-acquire the + // big kernel lock + xchg(&thiscpu->cpu_status, CPU_HALTED); + + // Release the big kernel lock as if we were "leaving" the kernel + unlock_kernel(); + + // Reset stack pointer, enable interrupts and then halt. + asm volatile ( + "movl $0, %%ebp\n" + "movl %0, %%esp\n" + "pushl $0\n" + "pushl $0\n" + // Uncomment the following line after completing exercise 13 + //"sti\n" + "1:\n" + "hlt\n" + "jmp 1b\n" + : : "a" (thiscpu->cpu_ts.ts_esp0)); +} + diff --git a/lab/LAB4.si4project/Backup/sched(6307).c b/lab/LAB4.si4project/Backup/sched(6307).c new file mode 100644 index 0000000..04e9617 --- /dev/null +++ b/lab/LAB4.si4project/Backup/sched(6307).c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include + +void sched_halt(void); + +// Choose a user environment to run and run it. +void +sched_yield(void) +{ + struct Env *idle; + + // Implement simple round-robin scheduling. + // + // Search through 'envs' for an ENV_RUNNABLE environment in + // circular fashion starting just after the env this CPU was + // last running. Switch to the first such environment found. + // + // If no envs are runnable, but the environment previously + // running on this CPU is still ENV_RUNNING, it's okay to + // choose that environment. + // + // Never choose an environment that's currently running on + // another CPU (env_status == ENV_RUNNING). If there are + // no runnable environments, simply drop through to the code + // below to halt the cpu. + + // LAB 4: Your code here. + + idle = curenv; + int start_envid = idle ? ENVX(idle->env_id)+1 : 0; + + for (int i = 0; i < NENV; i++) { + int j = (start_envid + i) % NENV; + if (envs[j].env_status == ENV_RUNNABLE) { + env_run(&envs[j]); + } + } + + if (idle && idle->env_status == ENV_RUNNING) { + env_run(idle); + } + + // sched_halt never returns + sched_halt(); + +} + +// Halt this CPU when there is nothing to do. Wait until the +// timer interrupt wakes it up. This function never returns. +// +void +sched_halt(void) +{ + int i; + + // For debugging and testing purposes, if there are no runnable + // environments in the system, then drop into the kernel monitor. + for (i = 0; i < NENV; i++) { + if ((envs[i].env_status == ENV_RUNNABLE || + envs[i].env_status == ENV_RUNNING || + envs[i].env_status == ENV_DYING)) + break; + } + if (i == NENV) { + cprintf("No runnable environments in the system!\n"); + while (1) + monitor(NULL); + } + + // Mark that no environment is running on this CPU + curenv = NULL; + lcr3(PADDR(kern_pgdir)); + + // Mark that this CPU is in the HALT state, so that when + // timer interupts come in, we know we should re-acquire the + // big kernel lock + xchg(&thiscpu->cpu_status, CPU_HALTED); + + // Release the big kernel lock as if we were "leaving" the kernel + unlock_kernel(); + + // Reset stack pointer, enable interrupts and then halt. + asm volatile ( + "movl $0, %%ebp\n" + "movl %0, %%esp\n" + "pushl $0\n" + "pushl $0\n" + // Uncomment the following line after completing exercise 13 + //"sti\n" + "1:\n" + "hlt\n" + "jmp 1b\n" + : : "a" (thiscpu->cpu_ts.ts_esp0)); +} + diff --git a/lab/LAB4.si4project/Backup/syscall(1135).c b/lab/LAB4.si4project/Backup/syscall(1135).c new file mode 100644 index 0000000..055deef --- /dev/null +++ b/lab/LAB4.si4project/Backup/syscall(1135).c @@ -0,0 +1,378 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Print a string to the system console. +// The string is exactly 'len' characters long. +// Destroys the environment on memory errors. +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, (const void *) s, len, 0); + // Print the string supplied by the user. + cprintf("%.*s", len, s); +} + +// Read a character from the system console without blocking. +// Returns the character, or 0 if there is no input waiting. +static int +sys_cgetc(void) +{ + return cons_getc(); +} + +// Returns the current environment's envid. +static envid_t +sys_getenvid(void) +{ + return curenv->env_id; +} + +// Destroy a given environment (possibly the currently running environment). +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_destroy(envid_t envid) +{ + int r; + struct Env *e; + + if ((r = envid2env(envid, &e, 1)) < 0) + return r; + if (e == curenv) + cprintf("[%08x] exiting gracefully\n", curenv->env_id); + else + cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id); + env_destroy(e); + return 0; +} + +// Deschedule current environment and pick a different one to run. +static void +sys_yield(void) +{ + sched_yield(); +} + + +// Allocate a new environment. +// Returns envid of new environment, or < 0 on error. Errors are: +// -E_NO_FREE_ENV if no free environment is available. +// -E_NO_MEM on memory exhaustion. +static envid_t +sys_exofork(void) +{ + // Create the new environment with env_alloc(), from kern/env.c. + // It should be left as env_alloc created it, except that + // status is set to ENV_NOT_RUNNABLE, and the register set is copied + // from the current environment -- but tweaked so sys_exofork + // will appear to return 0. + // LAB 4: Your code here. + struct Env *newenv; + int32_t ret; + if ((ret = env_alloc(&newenv, sys_getenvid())) < 0) { + // 两个函数的返回值是一样的 + return ret; + } + newenv->env_status = ENV_NOT_RUNNABLE; + newenv->env_tf = curenv->env_tf; + // newenv的返回值为0, 实现子进程返回0值 + newenv->env_tf.tf_regs.reg_eax = 0; + // 返回值存放在eax中 + return newenv->env_id; + + // panic("sys_exofork not implemented"); +} + +// Set envid's env_status to status, which must be ENV_RUNNABLE +// or ENV_NOT_RUNNABLE. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if status is not a valid status for an environment. +static int +sys_env_set_status(envid_t envid, int status) +{ + // Hint: Use the 'envid2env' function from kern/env.c to translate an + // envid to a struct Env. + // You should set envid2env's third argument to 1, which will + // check whether the current environment has permission to set + // envid's status. + + // LAB 4: Your code here. + struct Env *e; + if (envid2env(envid, &e, 1)) return -E_BAD_ENV; + + if (status != ENV_NOT_RUNNABLE && status != ENV_RUNNABLE) + return -E_INVAL; + + e->env_status = status; + return 0; +} + +// Set the page fault upcall for 'envid' by modifying the corresponding struct +// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the +// kernel will push a fault record onto the exception stack, then branch to +// 'func'. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_set_pgfault_upcall(envid_t envid, void *func) +{ + // LAB 4: Your code here. + + + panic("sys_env_set_pgfault_upcall not implemented"); +} + +// Allocate a page of memory and map it at 'va' with permission +// 'perm' in the address space of 'envid'. +// The page's contents are set to 0. +// If a page is already mapped at 'va', that page is unmapped as a +// side effect. +// +// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set, +// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +// -E_INVAL if perm is inappropriate (see above). +// -E_NO_MEM if there's no memory to allocate the new page, +// or to allocate any necessary page tables. +static int +sys_page_alloc(envid_t envid, void *va, int perm) +{ + // Hint: This function is a wrapper around page_alloc() and + // page_insert() from kern/pmap.c. + // Most of the new code you write should be to check the + // parameters for correctness. + // If page_insert() fails, remember to free the page you + // allocated! + + // LAB 4: Your code here. + int ret = 0; + struct Env *env; + + if ((ret = envid2env(envid, &env, 1)) < 0) + return -E_BAD_ENV; + + if((uintptr_t)va >= UTOP || PGOFF(va)) + return -E_INVAL; + if ((perm & PTE_U) == 0 || (perm & PTE_P) == 0) + return -E_INVAL; + if (perm & ~PTE_SYSCALL) + return -E_INVAL; + + struct PageInfo *pp = page_alloc(ALLOC_ZERO); + if(!pp) + return -E_NO_MEM; + + if (page_insert(env->env_pgdir, pp, va, perm) < 0) + return -E_NO_MEM; + + return 0; + // panic("sys_page_alloc not implemented"); +} + +// Map the page of memory at 'srcva' in srcenvid's address space +// at 'dstva' in dstenvid's address space with permission 'perm'. +// Perm has the same restrictions as in sys_page_alloc, except +// that it also must not grant write access to a read-only +// page. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, +// or the caller doesn't have permission to change one of them. +// -E_INVAL if srcva >= UTOP or srcva is not page-aligned, +// or dstva >= UTOP or dstva is not page-aligned. +// -E_INVAL is srcva is not mapped in srcenvid's address space. +// -E_INVAL if perm is inappropriate (see sys_page_alloc). +// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's +// address space. +// -E_NO_MEM if there's no memory to allocate any necessary page tables. +static int +sys_page_map(envid_t srcenvid, void *srcva, + envid_t dstenvid, void *dstva, int perm) +{ + // Hint: This function is a wrapper around page_lookup() and + // page_insert() from kern/pmap.c. + // Again, most of the new code you write should be to check the + // parameters for correctness. + // Use the third argument to page_lookup() to + // check the current permissions on the page. + + // LAB 4: Your code here. + int ret = 0; + struct Env *srcenv, *dstenv; + struct PageInfo *srcpp, *dstpp; + pte_t *pte; + if ((envid2env(srcenvid, &srcenv, 1) < 0 )|| ( envid2env(dstenvid, &dstenv, 1) < 0)) + return -E_BAD_ENV; + if ((uintptr_t)srcva >= UTOP || PGOFF(srcva) || (uintptr_t)dstva >= UTOP || PGOFF(dstva)) + return -E_INVAL; + if ( (perm & PTE_U) == 0 || (perm & PTE_P) == 0 || (perm & ~PTE_SYSCALL)) + return -E_INVAL; + if (!(srcpp = page_lookup(srcenv->env_pgdir, srcva, &pte))) + return -E_INVAL; + if ((perm & PTE_W) && ((*pte & PTE_W) == 0)) + return -E_INVAL; + if (page_insert(dstenv->env_pgdir, srcpp, dstva, perm) < 0) + return -E_NO_MEM; + + return 0; + // panic("sys_page_map not implemented"); +} + +// Unmap the page of memory at 'va' in the address space of 'envid'. +// If no page is mapped, the function silently succeeds. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +static int +sys_page_unmap(envid_t envid, void *va) +{ + // Hint: This function is a wrapper around page_remove(). + + // LAB 4: Your code here. + int ret = 0; + struct Env *env; + + if ((ret = envid2env(envid, &env, 1)) < 0) + return -E_BAD_ENV; + if ((uintptr_t)va >= UTOP || PGOFF(va)) + return -E_INVAL; + page_remove(env->env_pgdir, va); + return 0; + // panic("sys_page_unmap not implemented"); +} + +// Try to send 'value' to the target env 'envid'. +// If srcva < UTOP, then also send page currently mapped at 'srcva', +// so that receiver gets a duplicate mapping of the same page. +// +// The send fails with a return value of -E_IPC_NOT_RECV if the +// target is not blocked, waiting for an IPC. +// +// The send also can fail for the other reasons listed below. +// +// Otherwise, the send succeeds, and the target's ipc fields are +// updated as follows: +// env_ipc_recving is set to 0 to block future sends; +// env_ipc_from is set to the sending envid; +// env_ipc_value is set to the 'value' parameter; +// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. +// The target environment is marked runnable again, returning 0 +// from the paused sys_ipc_recv system call. (Hint: does the +// sys_ipc_recv function ever actually return?) +// +// If the sender wants to send a page but the receiver isn't asking for one, +// then no page mapping is transferred, but no error occurs. +// The ipc only happens when no errors occur. +// +// Returns 0 on success, < 0 on error. +// Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist. +// (No need to check permissions.) +// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, +// or another environment managed to send first. +// -E_INVAL if srcva < UTOP but srcva is not page-aligned. +// -E_INVAL if srcva < UTOP and perm is inappropriate +// (see sys_page_alloc). +// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's +// address space. +// -E_INVAL if (perm & PTE_W), but srcva is read-only in the +// current environment's address space. +// -E_NO_MEM if there's not enough memory to map srcva in envid's +// address space. +static int +sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) +{ + // LAB 4: Your code here. + panic("sys_ipc_try_send not implemented"); +} + +// Block until a value is ready. Record that you want to receive +// using the env_ipc_recving and env_ipc_dstva fields of struct Env, +// mark yourself not runnable, and then give up the CPU. +// +// If 'dstva' is < UTOP, then you are willing to receive a page of data. +// 'dstva' is the virtual address at which the sent page should be mapped. +// +// This function only returns on error, but the system call will eventually +// return 0 on success. +// Return < 0 on error. Errors are: +// -E_INVAL if dstva < UTOP but dstva is not page-aligned. +static int +sys_ipc_recv(void *dstva) +{ + // LAB 4: Your code here. + panic("sys_ipc_recv not implemented"); + return 0; +} + +// Dispatches to the correct kernel function, passing the arguments. +int32_t +syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) +{ + // Call the function corresponding to the 'syscallno' parameter. + // Return any appropriate return value. + // LAB 3: Your code here. + switch (syscallno) { + case SYS_cputs: + sys_cputs((const char*)a1, a2); + break; + case SYS_cgetc: + return sys_cgetc(); + case SYS_getenvid: + ret = sys_getenvid(); + break; + case SYS_env_destroy: + return sys_env_destroy(a1); + case SYS_yield: + sys_yield(); + break; + case SYS_page_alloc: + return sys_page_alloc((envid_t)a1, (void * )a2, (int )a3); + + case SYS_page_map: + return sys_page_map((envid_t) a1, (void *) a2, (envid_t) a3, (void *) a4, (int) a5); + + case SYS_page_unmap: + return sys_page_unmap((envid_t) a1, (void *) a2); + + case SYS_exofork: + return sys_exofork(); + + case SYS_env_set_status: + return sys_env_set_status((envid_t) a1, (int) a2); + case NSYSCALLS: + return -E_INVAL; + + default: + return -E_INVAL; + } +} + diff --git a/lab/LAB4.si4project/Backup/syscall(1286).c b/lab/LAB4.si4project/Backup/syscall(1286).c new file mode 100644 index 0000000..6ee15ee --- /dev/null +++ b/lab/LAB4.si4project/Backup/syscall(1286).c @@ -0,0 +1,383 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Print a string to the system console. +// The string is exactly 'len' characters long. +// Destroys the environment on memory errors. +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, (const void *) s, len, 0); + // Print the string supplied by the user. + cprintf("%.*s", len, s); +} + +// Read a character from the system console without blocking. +// Returns the character, or 0 if there is no input waiting. +static int +sys_cgetc(void) +{ + return cons_getc(); +} + +// Returns the current environment's envid. +static envid_t +sys_getenvid(void) +{ + return curenv->env_id; +} + +// Destroy a given environment (possibly the currently running environment). +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_destroy(envid_t envid) +{ + int r; + struct Env *e; + + if ((r = envid2env(envid, &e, 1)) < 0) + return r; + if (e == curenv) + cprintf("[%08x] exiting gracefully\n", curenv->env_id); + else + cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id); + env_destroy(e); + return 0; +} + +// Deschedule current environment and pick a different one to run. +static void +sys_yield(void) +{ + sched_yield(); +} + + +// Allocate a new environment. +// Returns envid of new environment, or < 0 on error. Errors are: +// -E_NO_FREE_ENV if no free environment is available. +// -E_NO_MEM on memory exhaustion. +static envid_t +sys_exofork(void) +{ + // Create the new environment with env_alloc(), from kern/env.c. + // It should be left as env_alloc created it, except that + // status is set to ENV_NOT_RUNNABLE, and the register set is copied + // from the current environment -- but tweaked so sys_exofork + // will appear to return 0. + // LAB 4: Your code here. + struct Env *newenv; + int32_t ret; + if ((ret = env_alloc(&newenv, sys_getenvid())) < 0) { + // 两个函数的返回值是一样的 + return ret; + } + newenv->env_status = ENV_NOT_RUNNABLE; + newenv->env_tf = curenv->env_tf; + // newenv的返回值为0, 实现子进程返回0值 + newenv->env_tf.tf_regs.reg_eax = 0; + // 返回值存放在eax中 + return newenv->env_id; + + // panic("sys_exofork not implemented"); +} + +// Set envid's env_status to status, which must be ENV_RUNNABLE +// or ENV_NOT_RUNNABLE. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if status is not a valid status for an environment. +static int +sys_env_set_status(envid_t envid, int status) +{ + // Hint: Use the 'envid2env' function from kern/env.c to translate an + // envid to a struct Env. + // You should set envid2env's third argument to 1, which will + // check whether the current environment has permission to set + // envid's status. + + // LAB 4: Your code here. + struct Env *e; + if (envid2env(envid, &e, 1)) return -E_BAD_ENV; + + if (status != ENV_NOT_RUNNABLE && status != ENV_RUNNABLE) + return -E_INVAL; + + e->env_status = status; + return 0; +} + +// Set the page fault upcall for 'envid' by modifying the corresponding struct +// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the +// kernel will push a fault record onto the exception stack, then branch to +// 'func'. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_set_pgfault_upcall(envid_t envid, void *func) +{ + // LAB 4: Your code here. + struct Env *e; + if (envid2env(envid, &e, 1)) + return -E_BAD_ENV; + + e->env_pgfault_upcall = func; + return 0; + // panic("sys_env_set_pgfault_upcall not implemented"); +} + +// Allocate a page of memory and map it at 'va' with permission +// 'perm' in the address space of 'envid'. +// The page's contents are set to 0. +// If a page is already mapped at 'va', that page is unmapped as a +// side effect. +// +// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set, +// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +// -E_INVAL if perm is inappropriate (see above). +// -E_NO_MEM if there's no memory to allocate the new page, +// or to allocate any necessary page tables. +static int +sys_page_alloc(envid_t envid, void *va, int perm) +{ + // Hint: This function is a wrapper around page_alloc() and + // page_insert() from kern/pmap.c. + // Most of the new code you write should be to check the + // parameters for correctness. + // If page_insert() fails, remember to free the page you + // allocated! + + // LAB 4: Your code here. + int ret = 0; + struct Env *env; + + if ((ret = envid2env(envid, &env, 1)) < 0) + return -E_BAD_ENV; + + if((uintptr_t)va >= UTOP || PGOFF(va)) + return -E_INVAL; + if ((perm & PTE_U) == 0 || (perm & PTE_P) == 0) + return -E_INVAL; + if (perm & ~PTE_SYSCALL) + return -E_INVAL; + + struct PageInfo *pp = page_alloc(ALLOC_ZERO); + if(!pp) + return -E_NO_MEM; + + if (page_insert(env->env_pgdir, pp, va, perm) < 0) + return -E_NO_MEM; + + return 0; + // panic("sys_page_alloc not implemented"); +} + +// Map the page of memory at 'srcva' in srcenvid's address space +// at 'dstva' in dstenvid's address space with permission 'perm'. +// Perm has the same restrictions as in sys_page_alloc, except +// that it also must not grant write access to a read-only +// page. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, +// or the caller doesn't have permission to change one of them. +// -E_INVAL if srcva >= UTOP or srcva is not page-aligned, +// or dstva >= UTOP or dstva is not page-aligned. +// -E_INVAL is srcva is not mapped in srcenvid's address space. +// -E_INVAL if perm is inappropriate (see sys_page_alloc). +// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's +// address space. +// -E_NO_MEM if there's no memory to allocate any necessary page tables. +static int +sys_page_map(envid_t srcenvid, void *srcva, + envid_t dstenvid, void *dstva, int perm) +{ + // Hint: This function is a wrapper around page_lookup() and + // page_insert() from kern/pmap.c. + // Again, most of the new code you write should be to check the + // parameters for correctness. + // Use the third argument to page_lookup() to + // check the current permissions on the page. + + // LAB 4: Your code here. + int ret = 0; + struct Env *srcenv, *dstenv; + struct PageInfo *srcpp, *dstpp; + pte_t *pte; + if ((envid2env(srcenvid, &srcenv, 1) < 0 )|| ( envid2env(dstenvid, &dstenv, 1) < 0)) + return -E_BAD_ENV; + if ((uintptr_t)srcva >= UTOP || PGOFF(srcva) || (uintptr_t)dstva >= UTOP || PGOFF(dstva)) + return -E_INVAL; + if ( (perm & PTE_U) == 0 || (perm & PTE_P) == 0 || (perm & ~PTE_SYSCALL)) + return -E_INVAL; + if (!(srcpp = page_lookup(srcenv->env_pgdir, srcva, &pte))) + return -E_INVAL; + if ((perm & PTE_W) && ((*pte & PTE_W) == 0)) + return -E_INVAL; + if (page_insert(dstenv->env_pgdir, srcpp, dstva, perm) < 0) + return -E_NO_MEM; + + return 0; + // panic("sys_page_map not implemented"); +} + +// Unmap the page of memory at 'va' in the address space of 'envid'. +// If no page is mapped, the function silently succeeds. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +static int +sys_page_unmap(envid_t envid, void *va) +{ + // Hint: This function is a wrapper around page_remove(). + + // LAB 4: Your code here. + int ret = 0; + struct Env *env; + + if ((ret = envid2env(envid, &env, 1)) < 0) + return -E_BAD_ENV; + if ((uintptr_t)va >= UTOP || PGOFF(va)) + return -E_INVAL; + page_remove(env->env_pgdir, va); + return 0; + // panic("sys_page_unmap not implemented"); +} + +// Try to send 'value' to the target env 'envid'. +// If srcva < UTOP, then also send page currently mapped at 'srcva', +// so that receiver gets a duplicate mapping of the same page. +// +// The send fails with a return value of -E_IPC_NOT_RECV if the +// target is not blocked, waiting for an IPC. +// +// The send also can fail for the other reasons listed below. +// +// Otherwise, the send succeeds, and the target's ipc fields are +// updated as follows: +// env_ipc_recving is set to 0 to block future sends; +// env_ipc_from is set to the sending envid; +// env_ipc_value is set to the 'value' parameter; +// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. +// The target environment is marked runnable again, returning 0 +// from the paused sys_ipc_recv system call. (Hint: does the +// sys_ipc_recv function ever actually return?) +// +// If the sender wants to send a page but the receiver isn't asking for one, +// then no page mapping is transferred, but no error occurs. +// The ipc only happens when no errors occur. +// +// Returns 0 on success, < 0 on error. +// Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist. +// (No need to check permissions.) +// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, +// or another environment managed to send first. +// -E_INVAL if srcva < UTOP but srcva is not page-aligned. +// -E_INVAL if srcva < UTOP and perm is inappropriate +// (see sys_page_alloc). +// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's +// address space. +// -E_INVAL if (perm & PTE_W), but srcva is read-only in the +// current environment's address space. +// -E_NO_MEM if there's not enough memory to map srcva in envid's +// address space. +static int +sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) +{ + // LAB 4: Your code here. + panic("sys_ipc_try_send not implemented"); +} + +// Block until a value is ready. Record that you want to receive +// using the env_ipc_recving and env_ipc_dstva fields of struct Env, +// mark yourself not runnable, and then give up the CPU. +// +// If 'dstva' is < UTOP, then you are willing to receive a page of data. +// 'dstva' is the virtual address at which the sent page should be mapped. +// +// This function only returns on error, but the system call will eventually +// return 0 on success. +// Return < 0 on error. Errors are: +// -E_INVAL if dstva < UTOP but dstva is not page-aligned. +static int +sys_ipc_recv(void *dstva) +{ + // LAB 4: Your code here. + panic("sys_ipc_recv not implemented"); + return 0; +} + +// Dispatches to the correct kernel function, passing the arguments. +int32_t +syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) +{ + // Call the function corresponding to the 'syscallno' parameter. + // Return any appropriate return value. + // LAB 3: Your code here. + switch (syscallno) { + case SYS_cputs: + sys_cputs((const char*)a1, a2); + break; + case SYS_cgetc: + return sys_cgetc(); + case SYS_getenvid: + return sys_getenvid(); + case SYS_env_destroy: + return sys_env_destroy(a1); + case SYS_yield: + sys_yield(); + break; + case SYS_page_alloc: + return sys_page_alloc((envid_t)a1, (void * )a2, (int )a3); + case SYS_env_set_pgfault_upcall: + return sys_env_set_pgfault_upcall((envid_t) a1, (void *) a2); + case SYS_page_map: + return sys_page_map((envid_t) a1, (void *) a2, (envid_t) a3, (void *) a4, (int) a5); + + case SYS_page_unmap: + return sys_page_unmap((envid_t) a1, (void *) a2); + + case SYS_exofork: + return sys_exofork(); + + case SYS_env_set_status: + return sys_env_set_status((envid_t) a1, (int) a2); + case NSYSCALLS: + return -E_INVAL; + + default: + return -E_INVAL; + } + return 0; +} + diff --git a/lab/LAB4.si4project/Backup/syscall(2799).c b/lab/LAB4.si4project/Backup/syscall(2799).c new file mode 100644 index 0000000..406b1df --- /dev/null +++ b/lab/LAB4.si4project/Backup/syscall(2799).c @@ -0,0 +1,301 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Print a string to the system console. +// The string is exactly 'len' characters long. +// Destroys the environment on memory errors. +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, (const void *) s, len, 0); + // Print the string supplied by the user. + cprintf("%.*s", len, s); +} + +// Read a character from the system console without blocking. +// Returns the character, or 0 if there is no input waiting. +static int +sys_cgetc(void) +{ + return cons_getc(); +} + +// Returns the current environment's envid. +static envid_t +sys_getenvid(void) +{ + return curenv->env_id; +} + +// Destroy a given environment (possibly the currently running environment). +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_destroy(envid_t envid) +{ + int r; + struct Env *e; + + if ((r = envid2env(envid, &e, 1)) < 0) + return r; + if (e == curenv) + cprintf("[%08x] exiting gracefully\n", curenv->env_id); + else + cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id); + env_destroy(e); + return 0; +} + +// Deschedule current environment and pick a different one to run. +static void +sys_yield(void) +{ + sched_yield(); +} + +// Allocate a new environment. +// Returns envid of new environment, or < 0 on error. Errors are: +// -E_NO_FREE_ENV if no free environment is available. +// -E_NO_MEM on memory exhaustion. +static envid_t +sys_exofork(void) +{ + // Create the new environment with env_alloc(), from kern/env.c. + // It should be left as env_alloc created it, except that + // status is set to ENV_NOT_RUNNABLE, and the register set is copied + // from the current environment -- but tweaked so sys_exofork + // will appear to return 0. + + // LAB 4: Your code here. + panic("sys_exofork not implemented"); +} + +// Set envid's env_status to status, which must be ENV_RUNNABLE +// or ENV_NOT_RUNNABLE. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if status is not a valid status for an environment. +static int +sys_env_set_status(envid_t envid, int status) +{ + // Hint: Use the 'envid2env' function from kern/env.c to translate an + // envid to a struct Env. + // You should set envid2env's third argument to 1, which will + // check whether the current environment has permission to set + // envid's status. + + // LAB 4: Your code here. + panic("sys_env_set_status not implemented"); +} + +// Set the page fault upcall for 'envid' by modifying the corresponding struct +// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the +// kernel will push a fault record onto the exception stack, then branch to +// 'func'. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_set_pgfault_upcall(envid_t envid, void *func) +{ + // LAB 4: Your code here. + panic("sys_env_set_pgfault_upcall not implemented"); +} + +// Allocate a page of memory and map it at 'va' with permission +// 'perm' in the address space of 'envid'. +// The page's contents are set to 0. +// If a page is already mapped at 'va', that page is unmapped as a +// side effect. +// +// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set, +// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +// -E_INVAL if perm is inappropriate (see above). +// -E_NO_MEM if there's no memory to allocate the new page, +// or to allocate any necessary page tables. +static int +sys_page_alloc(envid_t envid, void *va, int perm) +{ + // Hint: This function is a wrapper around page_alloc() and + // page_insert() from kern/pmap.c. + // Most of the new code you write should be to check the + // parameters for correctness. + // If page_insert() fails, remember to free the page you + // allocated! + + // LAB 4: Your code here. + panic("sys_page_alloc not implemented"); +} + +// Map the page of memory at 'srcva' in srcenvid's address space +// at 'dstva' in dstenvid's address space with permission 'perm'. +// Perm has the same restrictions as in sys_page_alloc, except +// that it also must not grant write access to a read-only +// page. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, +// or the caller doesn't have permission to change one of them. +// -E_INVAL if srcva >= UTOP or srcva is not page-aligned, +// or dstva >= UTOP or dstva is not page-aligned. +// -E_INVAL is srcva is not mapped in srcenvid's address space. +// -E_INVAL if perm is inappropriate (see sys_page_alloc). +// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's +// address space. +// -E_NO_MEM if there's no memory to allocate any necessary page tables. +static int +sys_page_map(envid_t srcenvid, void *srcva, + envid_t dstenvid, void *dstva, int perm) +{ + // Hint: This function is a wrapper around page_lookup() and + // page_insert() from kern/pmap.c. + // Again, most of the new code you write should be to check the + // parameters for correctness. + // Use the third argument to page_lookup() to + // check the current permissions on the page. + + // LAB 4: Your code here. + panic("sys_page_map not implemented"); +} + +// Unmap the page of memory at 'va' in the address space of 'envid'. +// If no page is mapped, the function silently succeeds. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +static int +sys_page_unmap(envid_t envid, void *va) +{ + // Hint: This function is a wrapper around page_remove(). + + // LAB 4: Your code here. + panic("sys_page_unmap not implemented"); +} + +// Try to send 'value' to the target env 'envid'. +// If srcva < UTOP, then also send page currently mapped at 'srcva', +// so that receiver gets a duplicate mapping of the same page. +// +// The send fails with a return value of -E_IPC_NOT_RECV if the +// target is not blocked, waiting for an IPC. +// +// The send also can fail for the other reasons listed below. +// +// Otherwise, the send succeeds, and the target's ipc fields are +// updated as follows: +// env_ipc_recving is set to 0 to block future sends; +// env_ipc_from is set to the sending envid; +// env_ipc_value is set to the 'value' parameter; +// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. +// The target environment is marked runnable again, returning 0 +// from the paused sys_ipc_recv system call. (Hint: does the +// sys_ipc_recv function ever actually return?) +// +// If the sender wants to send a page but the receiver isn't asking for one, +// then no page mapping is transferred, but no error occurs. +// The ipc only happens when no errors occur. +// +// Returns 0 on success, < 0 on error. +// Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist. +// (No need to check permissions.) +// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, +// or another environment managed to send first. +// -E_INVAL if srcva < UTOP but srcva is not page-aligned. +// -E_INVAL if srcva < UTOP and perm is inappropriate +// (see sys_page_alloc). +// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's +// address space. +// -E_INVAL if (perm & PTE_W), but srcva is read-only in the +// current environment's address space. +// -E_NO_MEM if there's not enough memory to map srcva in envid's +// address space. +static int +sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) +{ + // LAB 4: Your code here. + panic("sys_ipc_try_send not implemented"); +} + +// Block until a value is ready. Record that you want to receive +// using the env_ipc_recving and env_ipc_dstva fields of struct Env, +// mark yourself not runnable, and then give up the CPU. +// +// If 'dstva' is < UTOP, then you are willing to receive a page of data. +// 'dstva' is the virtual address at which the sent page should be mapped. +// +// This function only returns on error, but the system call will eventually +// return 0 on success. +// Return < 0 on error. Errors are: +// -E_INVAL if dstva < UTOP but dstva is not page-aligned. +static int +sys_ipc_recv(void *dstva) +{ + // LAB 4: Your code here. + panic("sys_ipc_recv not implemented"); + return 0; +} + +// Dispatches to the correct kernel function, passing the arguments. +int32_t +syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) +{ + // Call the function corresponding to the 'syscallno' parameter. + // Return any appropriate return value. + // LAB 3: Your code here. + + + // panic("syscall not implemented"); + + int ret = 0; + switch (syscallno) { + case SYS_cputs: + sys_cputs((const char*)a1, a2); + break; + case SYS_cgetc: + ret = sys_cgetc(); + + break; + case SYS_getenvid: + ret = sys_getenvid(); + break; + case SYS_env_destroy: + sys_env_destroy(a1); + break; + case NSYSCALLS: + return -E_INVAL; + + default: + return -E_INVAL; + } + + return ret; +} + diff --git a/lab/LAB4.si4project/Backup/syscall(5045).h b/lab/LAB4.si4project/Backup/syscall(5045).h new file mode 100644 index 0000000..71b3512 --- /dev/null +++ b/lab/LAB4.si4project/Backup/syscall(5045).h @@ -0,0 +1,22 @@ +#ifndef JOS_INC_SYSCALL_H +#define JOS_INC_SYSCALL_H + +/* system call numbers */ +enum { + SYS_cputs = 0, + SYS_cgetc, + SYS_getenvid, + SYS_env_destroy, + SYS_page_alloc, + SYS_page_map, + SYS_page_unmap, + SYS_exofork, + SYS_env_set_status, + SYS_env_set_pgfault_upcall, + SYS_yield, + SYS_ipc_try_send, + SYS_ipc_recv, + NSYSCALLS +}; + +#endif /* !JOS_INC_SYSCALL_H */ diff --git a/lab/LAB4.si4project/Backup/syscall(7459).c b/lab/LAB4.si4project/Backup/syscall(7459).c new file mode 100644 index 0000000..4b2925a --- /dev/null +++ b/lab/LAB4.si4project/Backup/syscall(7459).c @@ -0,0 +1,384 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Print a string to the system console. +// The string is exactly 'len' characters long. +// Destroys the environment on memory errors. +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, (const void *) s, len, 0); + // Print the string supplied by the user. + cprintf("%.*s", len, s); +} + +// Read a character from the system console without blocking. +// Returns the character, or 0 if there is no input waiting. +static int +sys_cgetc(void) +{ + return cons_getc(); +} + +// Returns the current environment's envid. +static envid_t +sys_getenvid(void) +{ + return curenv->env_id; +} + +// Destroy a given environment (possibly the currently running environment). +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_destroy(envid_t envid) +{ + int r; + struct Env *e; + + if ((r = envid2env(envid, &e, 1)) < 0) + return r; + if (e == curenv) + cprintf("[%08x] exiting gracefully\n", curenv->env_id); + else + cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id); + env_destroy(e); + return 0; +} + +// Deschedule current environment and pick a different one to run. +static void +sys_yield(void) +{ + sched_yield(); +} + + +// Allocate a new environment. +// Returns envid of new environment, or < 0 on error. Errors are: +// -E_NO_FREE_ENV if no free environment is available. +// -E_NO_MEM on memory exhaustion. +static envid_t +sys_exofork(void) +{ + // Create the new environment with env_alloc(), from kern/env.c. + // It should be left as env_alloc created it, except that + // status is set to ENV_NOT_RUNNABLE, and the register set is copied + // from the current environment -- but tweaked so sys_exofork + // will appear to return 0. + // LAB 4: Your code here. + struct Env *newenv; + int32_t ret; + if ((ret = env_alloc(&newenv, sys_getenvid())) < 0) { + // 两个函数的返回值是一样的 + return ret; + } + newenv->env_status = ENV_NOT_RUNNABLE; + newenv->env_tf = curenv->env_tf; + // newenv的返回值为0, 实现子进程返回0值 + newenv->env_tf.tf_regs.reg_eax = 0; + + // 返回值存放在eax中 + return newenv->env_id; + + // panic("sys_exofork not implemented"); +} + +// Set envid's env_status to status, which must be ENV_RUNNABLE +// or ENV_NOT_RUNNABLE. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if status is not a valid status for an environment. +static int +sys_env_set_status(envid_t envid, int status) +{ + // Hint: Use the 'envid2env' function from kern/env.c to translate an + // envid to a struct Env. + // You should set envid2env's third argument to 1, which will + // check whether the current environment has permission to set + // envid's status. + + // LAB 4: Your code here. + struct Env *e; + if (envid2env(envid, &e, 1)) return -E_BAD_ENV; + + if (status != ENV_NOT_RUNNABLE && status != ENV_RUNNABLE) + return -E_INVAL; + + e->env_status = status; + return 0; +} + +// Set the page fault upcall for 'envid' by modifying the corresponding struct +// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the +// kernel will push a fault record onto the exception stack, then branch to +// 'func'. +// +// Returns 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +static int +sys_env_set_pgfault_upcall(envid_t envid, void *func) +{ + // LAB 4: Your code here. + struct Env *e; + if (envid2env(envid, &e, 1)) + return -E_BAD_ENV; + // cprintf("set pgfault upcall\n"); + e->env_pgfault_upcall = func; + return 0; + // panic("sys_env_set_pgfault_upcall not implemented"); +} + +// Allocate a page of memory and map it at 'va' with permission +// 'perm' in the address space of 'envid'. +// The page's contents are set to 0. +// If a page is already mapped at 'va', that page is unmapped as a +// side effect. +// +// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set, +// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +// -E_INVAL if perm is inappropriate (see above). +// -E_NO_MEM if there's no memory to allocate the new page, +// or to allocate any necessary page tables. +static int +sys_page_alloc(envid_t envid, void *va, int perm) +{ + // Hint: This function is a wrapper around page_alloc() and + // page_insert() from kern/pmap.c. + // Most of the new code you write should be to check the + // parameters for correctness. + // If page_insert() fails, remember to free the page you + // allocated! + + // LAB 4: Your code here. + int ret = 0; + struct Env *env; + + if ((ret = envid2env(envid, &env, 1)) < 0) + return -E_BAD_ENV; + + if((uintptr_t)va >= UTOP || PGOFF(va)) + return -E_INVAL; + if ((perm & PTE_U) == 0 || (perm & PTE_P) == 0) + return -E_INVAL; + if (perm & ~PTE_SYSCALL) + return -E_INVAL; + + struct PageInfo *pp = page_alloc(ALLOC_ZERO); + if(!pp) + return -E_NO_MEM; + + if (page_insert(env->env_pgdir, pp, va, perm) < 0) + return -E_NO_MEM; + + return 0; + // panic("sys_page_alloc not implemented"); +} + +// Map the page of memory at 'srcva' in srcenvid's address space +// at 'dstva' in dstenvid's address space with permission 'perm'. +// Perm has the same restrictions as in sys_page_alloc, except +// that it also must not grant write access to a read-only +// page. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, +// or the caller doesn't have permission to change one of them. +// -E_INVAL if srcva >= UTOP or srcva is not page-aligned, +// or dstva >= UTOP or dstva is not page-aligned. +// -E_INVAL is srcva is not mapped in srcenvid's address space. +// -E_INVAL if perm is inappropriate (see sys_page_alloc). +// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's +// address space. +// -E_NO_MEM if there's no memory to allocate any necessary page tables. +static int +sys_page_map(envid_t srcenvid, void *srcva, + envid_t dstenvid, void *dstva, int perm) +{ + // Hint: This function is a wrapper around page_lookup() and + // page_insert() from kern/pmap.c. + // Again, most of the new code you write should be to check the + // parameters for correctness. + // Use the third argument to page_lookup() to + // check the current permissions on the page. + + // LAB 4: Your code here. + int ret = 0; + struct Env *srcenv, *dstenv; + struct PageInfo *srcpp, *dstpp; + pte_t *pte; + if ((envid2env(srcenvid, &srcenv, 1) < 0 )|| ( envid2env(dstenvid, &dstenv, 1) < 0)) + return -E_BAD_ENV; + if ((uintptr_t)srcva >= UTOP || PGOFF(srcva) || (uintptr_t)dstva >= UTOP || PGOFF(dstva)) + return -E_INVAL; + if ( (perm & PTE_U) == 0 || (perm & PTE_P) == 0 || (perm & ~PTE_SYSCALL)) + return -E_INVAL; + if (!(srcpp = page_lookup(srcenv->env_pgdir, srcva, &pte))) + return -E_INVAL; + if ((perm & PTE_W) && ((*pte & PTE_W) == 0)) + return -E_INVAL; + if (page_insert(dstenv->env_pgdir, srcpp, dstva, perm) < 0) + return -E_NO_MEM; + + return 0; + // panic("sys_page_map not implemented"); +} + +// Unmap the page of memory at 'va' in the address space of 'envid'. +// If no page is mapped, the function silently succeeds. +// +// Return 0 on success, < 0 on error. Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist, +// or the caller doesn't have permission to change envid. +// -E_INVAL if va >= UTOP, or va is not page-aligned. +static int +sys_page_unmap(envid_t envid, void *va) +{ + // Hint: This function is a wrapper around page_remove(). + + // LAB 4: Your code here. + int ret = 0; + struct Env *env; + + if ((ret = envid2env(envid, &env, 1)) < 0) + return -E_BAD_ENV; + if ((uintptr_t)va >= UTOP || PGOFF(va)) + return -E_INVAL; + page_remove(env->env_pgdir, va); + return 0; + // panic("sys_page_unmap not implemented"); +} + +// Try to send 'value' to the target env 'envid'. +// If srcva < UTOP, then also send page currently mapped at 'srcva', +// so that receiver gets a duplicate mapping of the same page. +// +// The send fails with a return value of -E_IPC_NOT_RECV if the +// target is not blocked, waiting for an IPC. +// +// The send also can fail for the other reasons listed below. +// +// Otherwise, the send succeeds, and the target's ipc fields are +// updated as follows: +// env_ipc_recving is set to 0 to block future sends; +// env_ipc_from is set to the sending envid; +// env_ipc_value is set to the 'value' parameter; +// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. +// The target environment is marked runnable again, returning 0 +// from the paused sys_ipc_recv system call. (Hint: does the +// sys_ipc_recv function ever actually return?) +// +// If the sender wants to send a page but the receiver isn't asking for one, +// then no page mapping is transferred, but no error occurs. +// The ipc only happens when no errors occur. +// +// Returns 0 on success, < 0 on error. +// Errors are: +// -E_BAD_ENV if environment envid doesn't currently exist. +// (No need to check permissions.) +// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, +// or another environment managed to send first. +// -E_INVAL if srcva < UTOP but srcva is not page-aligned. +// -E_INVAL if srcva < UTOP and perm is inappropriate +// (see sys_page_alloc). +// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's +// address space. +// -E_INVAL if (perm & PTE_W), but srcva is read-only in the +// current environment's address space. +// -E_NO_MEM if there's not enough memory to map srcva in envid's +// address space. +static int +sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) +{ + // LAB 4: Your code here. + panic("sys_ipc_try_send not implemented"); +} + +// Block until a value is ready. Record that you want to receive +// using the env_ipc_recving and env_ipc_dstva fields of struct Env, +// mark yourself not runnable, and then give up the CPU. +// +// If 'dstva' is < UTOP, then you are willing to receive a page of data. +// 'dstva' is the virtual address at which the sent page should be mapped. +// +// This function only returns on error, but the system call will eventually +// return 0 on success. +// Return < 0 on error. Errors are: +// -E_INVAL if dstva < UTOP but dstva is not page-aligned. +static int +sys_ipc_recv(void *dstva) +{ + // LAB 4: Your code here. + panic("sys_ipc_recv not implemented"); + return 0; +} + +// Dispatches to the correct kernel function, passing the arguments. +int32_t +syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) +{ + // Call the function corresponding to the 'syscallno' parameter. + // Return any appropriate return value. + // LAB 3: Your code here. + switch (syscallno) { + case SYS_cputs: + sys_cputs((const char*)a1, a2); + break; + case SYS_cgetc: + return sys_cgetc(); + case SYS_getenvid: + return sys_getenvid(); + case SYS_env_destroy: + return sys_env_destroy(a1); + case SYS_yield: + sys_yield(); + break; + case SYS_page_alloc: + return sys_page_alloc((envid_t)a1, (void * )a2, (int )a3); + case SYS_env_set_pgfault_upcall: + return sys_env_set_pgfault_upcall((envid_t) a1, (void *) a2); + case SYS_page_map: + return sys_page_map((envid_t) a1, (void *) a2, (envid_t) a3, (void *) a4, (int) a5); + + case SYS_page_unmap: + return sys_page_unmap((envid_t) a1, (void *) a2); + + case SYS_exofork: + return sys_exofork(); + + case SYS_env_set_status: + return sys_env_set_status((envid_t) a1, (int) a2); + case NSYSCALLS: + return -E_INVAL; + + default: + return -E_INVAL; + } + return 0; +} + diff --git a/lab/LAB4.si4project/Backup/trap(1475).c b/lab/LAB4.si4project/Backup/trap(1475).c new file mode 100644 index 0000000..b80f42b --- /dev/null +++ b/lab/LAB4.si4project/Backup/trap(1475).c @@ -0,0 +1,402 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct Taskstate ts; + +/* For debugging, so print_trapframe can distinguish between printing + * a saved trapframe and printing the current trapframe and print some + * additional information in the latter case. + */ +static struct Trapframe *last_tf; + +/* Interrupt descriptor table. (Must be built at run time because + * shifted function addresses can't be represented in relocation records.) + */ +struct Gatedesc idt[256] = { { 0 } }; +struct Pseudodesc idt_pd = { + sizeof(idt) - 1, (uint32_t) idt +}; + + +static const char *trapname(int trapno) +{ + static const char * const excnames[] = { + "Divide error", + "Debug", + "Non-Maskable Interrupt", + "Breakpoint", + "Overflow", + "BOUND Range Exceeded", + "Invalid Opcode", + "Device Not Available", + "Double Fault", + "Coprocessor Segment Overrun", + "Invalid TSS", + "Segment Not Present", + "Stack Fault", + "General Protection", + "Page Fault", + "(unknown trap)", + "x87 FPU Floating-Point Error", + "Alignment Check", + "Machine-Check", + "SIMD Floating-Point Exception" + }; + + if (trapno < ARRAY_SIZE(excnames)) + return excnames[trapno]; + if (trapno == T_SYSCALL) + return "System call"; + if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) + return "Hardware Interrupt"; + return "(unknown trap)"; +} + +// You will also need to modify trap_init() to initialize the idt to +// point to each of these entry points defined in trapentry.S; +// the SETGATE macro will be helpful here +void +trap_init(void) +{ + + extern struct Segdesc gdt[]; + void divide_handler(); + void debug_handler(); + void nmi_handler(); + void brkpt_handler(); + void oflow_handler(); + void bound_handler(); + void device_handler(); + void illop_handler(); + void tss_handler(); + void segnp_handler(); + void stack_handler(); + void gpflt_handler(); + void pgflt_handler(); + void fperr_handler(); + void align_handler(); + void mchk_handler(); + void simderr_handler(); + void syscall_handler(); + void dblflt_handler(); + + // LAB 3: Your code here. + // GD_KT 全局描述符, kernel text + SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); + SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); + SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); + SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); + SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); + SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); + SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); + SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); + SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); + SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); + SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); + SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); + SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); + SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); + SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); + SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); + SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); + SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); + SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); + + // Per-CPU setup + trap_init_percpu(); +} + +// Initialize and load the per-CPU TSS and IDT +void +trap_init_percpu(void) +{ + // The example code here sets up the Task State Segment (TSS) and + // the TSS descriptor for CPU 0. But it is incorrect if we are + // running on other CPUs because each CPU has its own kernel stack. + // Fix the code so that it works for all CPUs. + // + // Hints: + // - The macro "thiscpu" always refers to the current CPU's + // struct CpuInfo; + // - The ID of the current CPU is given by cpunum() or + // thiscpu->cpu_id; + // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, + // rather than the global "ts" variable; + // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; + // - You mapped the per-CPU kernel stacks in mem_init_mp() + // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments + // from doing IO (0 is not the correct value!) + // + // ltr sets a 'busy' flag in the TSS selector, so if you + // accidentally load the same TSS on more than one CPU, you'll + // get a triple fault. If you set up an individual CPU's TSS + // wrong, you may not get a fault until you try to return from + // user space on that CPU. + // + // LAB 4: Your code here: + thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); + thiscpu->cpu_ts.ts_ss0 = GD_KD; + + // Initialize the TSS slot of the gdt. + gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); + gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; + + // Load the TSS selector (like other segment selectors, the + // bottom three bits are special; we leave them 0) + ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); + + // Load the IDT + lidt(&idt_pd); +} + +void +print_trapframe(struct Trapframe *tf) +{ + cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); + print_regs(&tf->tf_regs); + cprintf(" es 0x----%04x\n", tf->tf_es); + cprintf(" ds 0x----%04x\n", tf->tf_ds); + cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); + // If this trap was a page fault that just happened + // (so %cr2 is meaningful), print the faulting linear address. + if (tf == last_tf && tf->tf_trapno == T_PGFLT) + cprintf(" cr2 0x%08x\n", rcr2()); + cprintf(" err 0x%08x", tf->tf_err); + // For page faults, print decoded fault error code: + // U/K=fault occurred in user/kernel mode + // W/R=a write/read caused the fault + // PR=a protection violation caused the fault (NP=page not present). + if (tf->tf_trapno == T_PGFLT) + cprintf(" [%s, %s, %s]\n", + tf->tf_err & 4 ? "user" : "kernel", + tf->tf_err & 2 ? "write" : "read", + tf->tf_err & 1 ? "protection" : "not-present"); + else + cprintf("\n"); + cprintf(" eip 0x%08x\n", tf->tf_eip); + cprintf(" cs 0x----%04x\n", tf->tf_cs); + cprintf(" flag 0x%08x\n", tf->tf_eflags); + if ((tf->tf_cs & 3) != 0) { + cprintf(" esp 0x%08x\n", tf->tf_esp); + cprintf(" ss 0x----%04x\n", tf->tf_ss); + } +} + +void +print_regs(struct PushRegs *regs) +{ + cprintf(" edi 0x%08x\n", regs->reg_edi); + cprintf(" esi 0x%08x\n", regs->reg_esi); + cprintf(" ebp 0x%08x\n", regs->reg_ebp); + cprintf(" oesp 0x%08x\n", regs->reg_oesp); + cprintf(" ebx 0x%08x\n", regs->reg_ebx); + cprintf(" edx 0x%08x\n", regs->reg_edx); + cprintf(" ecx 0x%08x\n", regs->reg_ecx); + cprintf(" eax 0x%08x\n", regs->reg_eax); +} + +static void +trap_dispatch(struct Trapframe *tf) +{ + // Handle processor exceptions. + // LAB 3: Your code here. + switch(tf->tf_trapno) { + case T_PGFLT: + page_fault_handler(tf); + break; + case T_BRKPT: + monitor(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; + } + break; + + } + + // Handle clock interrupts. Don't forget to acknowledge the + // interrupt using lapic_eoi() before calling the scheduler! + // LAB 4: Your code here. + + +} + +void +trap(struct Trapframe *tf) +{ + // The environment may have set DF and some versions + // of GCC rely on DF being clear + asm volatile("cld" ::: "cc"); + + // Halt the CPU if some other CPU has called panic() + extern char *panicstr; + if (panicstr) + asm volatile("hlt"); + + // Re-acqurie the big kernel lock if we were halted in + // sched_yield() + if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) + lock_kernel(); + // Check that interrupts are disabled. If this assertion + // fails, DO NOT be tempted to fix it by inserting a "cli" in + // the interrupt path. + assert(!(read_eflags() & FL_IF)); + + if ((tf->tf_cs & 3) == 3) { + // Trapped from user mode. + // Acquire the big kernel lock before doing any + // serious kernel work. + // LAB 4: Your code here. + lock_kernel(); + assert(curenv); + + // Garbage collect if current enviroment is a zombie + if (curenv->env_status == ENV_DYING) { + env_free(curenv); + curenv = NULL; + sched_yield(); + } + + // Copy trap frame (which is currently on the stack) + // into 'curenv->env_tf', so that running the environment + // will restart at the trap point. + curenv->env_tf = *tf; + // The trapframe on the stack should be ignored from here on. + tf = &curenv->env_tf; + } + + // Record that tf is the last real trapframe so + // print_trapframe can print some additional information. + last_tf = tf; + + // Dispatch based on what type of trap occurred + trap_dispatch(tf); + + // If we made it to this point, then no other environment was + // scheduled, so we should return to the current environment + // if doing so makes sense. + if (curenv && curenv->env_status == ENV_RUNNING) + env_run(curenv); + else + sched_yield(); +} + + +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. + + // 怎么判断是内核模式, CPL位 + + if(tf->tf_cs && 3 == 0) { + panic("page_fault in kernel mode, fault address %d\n", fault_va); + } + + // We've already handled kernel-mode exceptions, so if we get here, + // the page fault happened in user mode. + + + // Call the environment's page fault upcall, if one exists. Set up a + // page fault stack frame on the user exception stack (below + // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. + // + // The page fault upcall might cause another page fault, in which case + // we branch to the page fault upcall recursively, pushing another + // page fault stack frame on top of the user exception stack. + // + // It is convenient for our code which returns from a page fault + // (lib/pfentry.S) to have one word of scratch space at the top of the + // trap-time stack; it allows us to more easily restore the eip/esp. In + // the non-recursive case, we don't have to worry about this because + // the top of the regular user stack is free. In the recursive case, + // this means we have to leave an extra word between the current top of + // the exception stack and the new stack frame because the exception + // stack _is_ the trap-time stack. + // + // If there's no page fault upcall, the environment didn't allocate a + // page for its exception stack or can't write to it, or the exception + // stack overflows, then destroy the environment that caused the fault. + // Note that the grade script assumes you will first check for the page + // fault upcall and print the "user fault va" message below if there is + // none. The remaining three checks can be combined into a single test. + // + // Hints: + // user_mem_assert() and env_run() are useful here. + // To change what the user environment runs, modify 'curenv->env_tf' + // (the 'tf' variable points at 'curenv->env_tf'). + + // LAB 4: Your code here. + struct UTrapframe *utf; + // cprintf("I'M in page_fault_handler [%08x] user fault va %08x \n",curenv->env_id, fault_va); + if (curenv->env_pgfault_upcall) { + + if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) { + // 异常模式下陷入 + utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4); + + } + else { + // 非异常模式下陷入 + utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe)); + } + // 检查异常栈是否溢出 + user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_P|PTE_W); + + utf->utf_fault_va = fault_va; + utf->utf_err = tf->tf_trapno; + utf->utf_regs = tf->tf_regs; + utf->utf_eflags = tf->tf_eflags; + // 保存陷入时现场,用于返回 + utf->utf_eip = tf->tf_eip; + utf->utf_esp = tf->tf_esp; + // 再次转向执行 + curenv->env_tf.tf_eip = (uint32_t) curenv->env_pgfault_upcall; + // 异常栈 + curenv->env_tf.tf_esp = (uint32_t) utf; + env_run(curenv); + } + else { + // Destroy the environment that caused the fault. + cprintf("[%08x] user fault va %08x ip %08x\n", + curenv->env_id, fault_va, tf->tf_eip); + print_trapframe(tf); + env_destroy(curenv); + } +} + diff --git a/lab/LAB4.si4project/Backup/trap(2812).c b/lab/LAB4.si4project/Backup/trap(2812).c new file mode 100644 index 0000000..d9a201f --- /dev/null +++ b/lab/LAB4.si4project/Backup/trap(2812).c @@ -0,0 +1,375 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct Taskstate ts; + +/* For debugging, so print_trapframe can distinguish between printing + * a saved trapframe and printing the current trapframe and print some + * additional information in the latter case. + */ +static struct Trapframe *last_tf; + +/* Interrupt descriptor table. (Must be built at run time because + * shifted function addresses can't be represented in relocation records.) + */ +struct Gatedesc idt[256] = { { 0 } }; +struct Pseudodesc idt_pd = { + sizeof(idt) - 1, (uint32_t) idt +}; + + +static const char *trapname(int trapno) +{ + static const char * const excnames[] = { + "Divide error", + "Debug", + "Non-Maskable Interrupt", + "Breakpoint", + "Overflow", + "BOUND Range Exceeded", + "Invalid Opcode", + "Device Not Available", + "Double Fault", + "Coprocessor Segment Overrun", + "Invalid TSS", + "Segment Not Present", + "Stack Fault", + "General Protection", + "Page Fault", + "(unknown trap)", + "x87 FPU Floating-Point Error", + "Alignment Check", + "Machine-Check", + "SIMD Floating-Point Exception" + }; + + if (trapno < ARRAY_SIZE(excnames)) + return excnames[trapno]; + if (trapno == T_SYSCALL) + return "System call"; + if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) + return "Hardware Interrupt"; + return "(unknown trap)"; +} + +// You will also need to modify trap_init() to initialize the idt to +// point to each of these entry points defined in trapentry.S; +// the SETGATE macro will be helpful here +void +trap_init(void) +{ + + extern struct Segdesc gdt[]; + void divide_handler(); + void debug_handler(); + void nmi_handler(); + void brkpt_handler(); + void oflow_handler(); + void bound_handler(); + void device_handler(); + void illop_handler(); + void tss_handler(); + void segnp_handler(); + void stack_handler(); + void gpflt_handler(); + void pgflt_handler(); + void fperr_handler(); + void align_handler(); + void mchk_handler(); + void simderr_handler(); + void syscall_handler(); + void dblflt_handler(); + + // LAB 3: Your code here. + // GD_KT 全局描述符, kernel text + SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); + SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); + SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); + SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); + SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); + SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); + SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); + SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); + SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); + SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); + SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); + SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); + SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); + SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); + SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); + SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); + SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); + SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); + SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); + + // Per-CPU setup + trap_init_percpu(); +} + +// Initialize and load the per-CPU TSS and IDT +void +trap_init_percpu(void) +{ + // The example code here sets up the Task State Segment (TSS) and + // the TSS descriptor for CPU 0. But it is incorrect if we are + // running on other CPUs because each CPU has its own kernel stack. + // Fix the code so that it works for all CPUs. + // + // Hints: + // - The macro "thiscpu" always refers to the current CPU's + // struct CpuInfo; + // - The ID of the current CPU is given by cpunum() or + // thiscpu->cpu_id; + // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, + // rather than the global "ts" variable; + // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; + // - You mapped the per-CPU kernel stacks in mem_init_mp() + // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments + // from doing IO (0 is not the correct value!) + // + // ltr sets a 'busy' flag in the TSS selector, so if you + // accidentally load the same TSS on more than one CPU, you'll + // get a triple fault. If you set up an individual CPU's TSS + // wrong, you may not get a fault until you try to return from + // user space on that CPU. + // + // LAB 4: Your code here: + thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); + thiscpu->cpu_ts.ts_ss0 = GD_KD; + + // Initialize the TSS slot of the gdt. + gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); + gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; + + // Load the TSS selector (like other segment selectors, the + // bottom three bits are special; we leave them 0) + ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); + + // Load the IDT + lidt(&idt_pd); +} + +void +print_trapframe(struct Trapframe *tf) +{ + cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); + print_regs(&tf->tf_regs); + cprintf(" es 0x----%04x\n", tf->tf_es); + cprintf(" ds 0x----%04x\n", tf->tf_ds); + cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); + // If this trap was a page fault that just happened + // (so %cr2 is meaningful), print the faulting linear address. + if (tf == last_tf && tf->tf_trapno == T_PGFLT) + cprintf(" cr2 0x%08x\n", rcr2()); + cprintf(" err 0x%08x", tf->tf_err); + // For page faults, print decoded fault error code: + // U/K=fault occurred in user/kernel mode + // W/R=a write/read caused the fault + // PR=a protection violation caused the fault (NP=page not present). + if (tf->tf_trapno == T_PGFLT) + cprintf(" [%s, %s, %s]\n", + tf->tf_err & 4 ? "user" : "kernel", + tf->tf_err & 2 ? "write" : "read", + tf->tf_err & 1 ? "protection" : "not-present"); + else + cprintf("\n"); + cprintf(" eip 0x%08x\n", tf->tf_eip); + cprintf(" cs 0x----%04x\n", tf->tf_cs); + cprintf(" flag 0x%08x\n", tf->tf_eflags); + if ((tf->tf_cs & 3) != 0) { + cprintf(" esp 0x%08x\n", tf->tf_esp); + cprintf(" ss 0x----%04x\n", tf->tf_ss); + } +} + +void +print_regs(struct PushRegs *regs) +{ + cprintf(" edi 0x%08x\n", regs->reg_edi); + cprintf(" esi 0x%08x\n", regs->reg_esi); + cprintf(" ebp 0x%08x\n", regs->reg_ebp); + cprintf(" oesp 0x%08x\n", regs->reg_oesp); + cprintf(" ebx 0x%08x\n", regs->reg_ebx); + cprintf(" edx 0x%08x\n", regs->reg_edx); + cprintf(" ecx 0x%08x\n", regs->reg_ecx); + cprintf(" eax 0x%08x\n", regs->reg_eax); +} + +static void +trap_dispatch(struct Trapframe *tf) +{ + // Handle processor exceptions. + // LAB 3: Your code here. + switch(tf->tf_trapno) { + case T_PGFLT: + page_fault_handler(tf); + break; + case T_BRKPT: + monitor(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; + } + break; + + } + + // Handle clock interrupts. Don't forget to acknowledge the + // interrupt using lapic_eoi() before calling the scheduler! + // LAB 4: Your code here. + + +} + +void +trap(struct Trapframe *tf) +{ + // The environment may have set DF and some versions + // of GCC rely on DF being clear + asm volatile("cld" ::: "cc"); + + // Halt the CPU if some other CPU has called panic() + extern char *panicstr; + if (panicstr) + asm volatile("hlt"); + + // Re-acqurie the big kernel lock if we were halted in + // sched_yield() + if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) + lock_kernel(); + // Check that interrupts are disabled. If this assertion + // fails, DO NOT be tempted to fix it by inserting a "cli" in + // the interrupt path. + assert(!(read_eflags() & FL_IF)); + + if ((tf->tf_cs & 3) == 3) { + // Trapped from user mode. + // Acquire the big kernel lock before doing any + // serious kernel work. + // LAB 4: Your code here. + lock_kernel(); + assert(curenv); + + // Garbage collect if current enviroment is a zombie + if (curenv->env_status == ENV_DYING) { + env_free(curenv); + curenv = NULL; + sched_yield(); + } + + // Copy trap frame (which is currently on the stack) + // into 'curenv->env_tf', so that running the environment + // will restart at the trap point. + curenv->env_tf = *tf; + // The trapframe on the stack should be ignored from here on. + tf = &curenv->env_tf; + } + + // Record that tf is the last real trapframe so + // print_trapframe can print some additional information. + last_tf = tf; + + // Dispatch based on what type of trap occurred + trap_dispatch(tf); + + // If we made it to this point, then no other environment was + // scheduled, so we should return to the current environment + // if doing so makes sense. + if (curenv && curenv->env_status == ENV_RUNNING) + env_run(curenv); + else + sched_yield(); +} + + +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. + + // 怎么判断是内核模式, CPL位 + + if(tf->tf_cs && 3 == 0) { + panic("page_fault in kernel mode, fault address %d\n", fault_va); + } + + // We've already handled kernel-mode exceptions, so if we get here, + // the page fault happened in user mode. + + + // Call the environment's page fault upcall, if one exists. Set up a + // page fault stack frame on the user exception stack (below + // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. + // + // The page fault upcall might cause another page fault, in which case + // we branch to the page fault upcall recursively, pushing another + // page fault stack frame on top of the user exception stack. + // + // It is convenient for our code which returns from a page fault + // (lib/pfentry.S) to have one word of scratch space at the top of the + // trap-time stack; it allows us to more easily restore the eip/esp. In + // the non-recursive case, we don't have to worry about this because + // the top of the regular user stack is free. In the recursive case, + // this means we have to leave an extra word between the current top of + // the exception stack and the new stack frame because the exception + // stack _is_ the trap-time stack. + // + // If there's no page fault upcall, the environment didn't allocate a + // page for its exception stack or can't write to it, or the exception + // stack overflows, then destroy the environment that caused the fault. + // Note that the grade script assumes you will first check for the page + // fault upcall and print the "user fault va" message below if there is + // none. The remaining three checks can be combined into a single test. + // + // Hints: + // user_mem_assert() and env_run() are useful here. + // To change what the user environment runs, modify 'curenv->env_tf' + // (the 'tf' variable points at 'curenv->env_tf'). + + // LAB 4: Your code here. + + + // user_mem_assert(curenv, (const void *) fault_va, PGSIZE, 0); + + // Destroy the environment that caused the fault. + cprintf("[%08x] user fault va %08x ip %08x\n", + curenv->env_id, fault_va, tf->tf_eip); + print_trapframe(tf); + env_destroy(curenv); +} + diff --git a/lab/LAB4.si4project/Backup/trap(359).c b/lab/LAB4.si4project/Backup/trap(359).c new file mode 100644 index 0000000..2621500 --- /dev/null +++ b/lab/LAB4.si4project/Backup/trap(359).c @@ -0,0 +1,401 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct Taskstate ts; + +/* For debugging, so print_trapframe can distinguish between printing + * a saved trapframe and printing the current trapframe and print some + * additional information in the latter case. + */ +static struct Trapframe *last_tf; + +/* Interrupt descriptor table. (Must be built at run time because + * shifted function addresses can't be represented in relocation records.) + */ +struct Gatedesc idt[256] = { { 0 } }; +struct Pseudodesc idt_pd = { + sizeof(idt) - 1, (uint32_t) idt +}; + + +static const char *trapname(int trapno) +{ + static const char * const excnames[] = { + "Divide error", + "Debug", + "Non-Maskable Interrupt", + "Breakpoint", + "Overflow", + "BOUND Range Exceeded", + "Invalid Opcode", + "Device Not Available", + "Double Fault", + "Coprocessor Segment Overrun", + "Invalid TSS", + "Segment Not Present", + "Stack Fault", + "General Protection", + "Page Fault", + "(unknown trap)", + "x87 FPU Floating-Point Error", + "Alignment Check", + "Machine-Check", + "SIMD Floating-Point Exception" + }; + + if (trapno < ARRAY_SIZE(excnames)) + return excnames[trapno]; + if (trapno == T_SYSCALL) + return "System call"; + if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) + return "Hardware Interrupt"; + return "(unknown trap)"; +} + +// You will also need to modify trap_init() to initialize the idt to +// point to each of these entry points defined in trapentry.S; +// the SETGATE macro will be helpful here +void +trap_init(void) +{ + + extern struct Segdesc gdt[]; + void divide_handler(); + void debug_handler(); + void nmi_handler(); + void brkpt_handler(); + void oflow_handler(); + void bound_handler(); + void device_handler(); + void illop_handler(); + void tss_handler(); + void segnp_handler(); + void stack_handler(); + void gpflt_handler(); + void pgflt_handler(); + void fperr_handler(); + void align_handler(); + void mchk_handler(); + void simderr_handler(); + void syscall_handler(); + void dblflt_handler(); + + // LAB 3: Your code here. + // GD_KT 全局描述符, kernel text + SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); + SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); + SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); + SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); + SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); + SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); + SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); + SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); + SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); + SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); + SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); + SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); + SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); + SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); + SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); + SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); + SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); + SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); + SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); + + // Per-CPU setup + trap_init_percpu(); +} + +// Initialize and load the per-CPU TSS and IDT +void +trap_init_percpu(void) +{ + // The example code here sets up the Task State Segment (TSS) and + // the TSS descriptor for CPU 0. But it is incorrect if we are + // running on other CPUs because each CPU has its own kernel stack. + // Fix the code so that it works for all CPUs. + // + // Hints: + // - The macro "thiscpu" always refers to the current CPU's + // struct CpuInfo; + // - The ID of the current CPU is given by cpunum() or + // thiscpu->cpu_id; + // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, + // rather than the global "ts" variable; + // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; + // - You mapped the per-CPU kernel stacks in mem_init_mp() + // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments + // from doing IO (0 is not the correct value!) + // + // ltr sets a 'busy' flag in the TSS selector, so if you + // accidentally load the same TSS on more than one CPU, you'll + // get a triple fault. If you set up an individual CPU's TSS + // wrong, you may not get a fault until you try to return from + // user space on that CPU. + // + // LAB 4: Your code here: + thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); + thiscpu->cpu_ts.ts_ss0 = GD_KD; + + // Initialize the TSS slot of the gdt. + gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); + gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; + + // Load the TSS selector (like other segment selectors, the + // bottom three bits are special; we leave them 0) + ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); + + // Load the IDT + lidt(&idt_pd); +} + +void +print_trapframe(struct Trapframe *tf) +{ + cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); + print_regs(&tf->tf_regs); + cprintf(" es 0x----%04x\n", tf->tf_es); + cprintf(" ds 0x----%04x\n", tf->tf_ds); + cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); + // If this trap was a page fault that just happened + // (so %cr2 is meaningful), print the faulting linear address. + if (tf == last_tf && tf->tf_trapno == T_PGFLT) + cprintf(" cr2 0x%08x\n", rcr2()); + cprintf(" err 0x%08x", tf->tf_err); + // For page faults, print decoded fault error code: + // U/K=fault occurred in user/kernel mode + // W/R=a write/read caused the fault + // PR=a protection violation caused the fault (NP=page not present). + if (tf->tf_trapno == T_PGFLT) + cprintf(" [%s, %s, %s]\n", + tf->tf_err & 4 ? "user" : "kernel", + tf->tf_err & 2 ? "write" : "read", + tf->tf_err & 1 ? "protection" : "not-present"); + else + cprintf("\n"); + cprintf(" eip 0x%08x\n", tf->tf_eip); + cprintf(" cs 0x----%04x\n", tf->tf_cs); + cprintf(" flag 0x%08x\n", tf->tf_eflags); + if ((tf->tf_cs & 3) != 0) { + cprintf(" esp 0x%08x\n", tf->tf_esp); + cprintf(" ss 0x----%04x\n", tf->tf_ss); + } +} + +void +print_regs(struct PushRegs *regs) +{ + cprintf(" edi 0x%08x\n", regs->reg_edi); + cprintf(" esi 0x%08x\n", regs->reg_esi); + cprintf(" ebp 0x%08x\n", regs->reg_ebp); + cprintf(" oesp 0x%08x\n", regs->reg_oesp); + cprintf(" ebx 0x%08x\n", regs->reg_ebx); + cprintf(" edx 0x%08x\n", regs->reg_edx); + cprintf(" ecx 0x%08x\n", regs->reg_ecx); + cprintf(" eax 0x%08x\n", regs->reg_eax); +} + +static void +trap_dispatch(struct Trapframe *tf) +{ + // Handle processor exceptions. + // LAB 3: Your code here. + switch(tf->tf_trapno) { + case T_PGFLT: + page_fault_handler(tf); + break; + case T_BRKPT: + monitor(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; + } + break; + + } + + // Handle clock interrupts. Don't forget to acknowledge the + // interrupt using lapic_eoi() before calling the scheduler! + // LAB 4: Your code here. + + +} + +void +trap(struct Trapframe *tf) +{ + // The environment may have set DF and some versions + // of GCC rely on DF being clear + asm volatile("cld" ::: "cc"); + + // Halt the CPU if some other CPU has called panic() + extern char *panicstr; + if (panicstr) + asm volatile("hlt"); + + // Re-acqurie the big kernel lock if we were halted in + // sched_yield() + if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) + lock_kernel(); + // Check that interrupts are disabled. If this assertion + // fails, DO NOT be tempted to fix it by inserting a "cli" in + // the interrupt path. + assert(!(read_eflags() & FL_IF)); + + if ((tf->tf_cs & 3) == 3) { + // Trapped from user mode. + // Acquire the big kernel lock before doing any + // serious kernel work. + // LAB 4: Your code here. + lock_kernel(); + assert(curenv); + + // Garbage collect if current enviroment is a zombie + if (curenv->env_status == ENV_DYING) { + env_free(curenv); + curenv = NULL; + sched_yield(); + } + + // Copy trap frame (which is currently on the stack) + // into 'curenv->env_tf', so that running the environment + // will restart at the trap point. + curenv->env_tf = *tf; + // The trapframe on the stack should be ignored from here on. + tf = &curenv->env_tf; + } + + // Record that tf is the last real trapframe so + // print_trapframe can print some additional information. + last_tf = tf; + + // Dispatch based on what type of trap occurred + trap_dispatch(tf); + + // If we made it to this point, then no other environment was + // scheduled, so we should return to the current environment + // if doing so makes sense. + if (curenv && curenv->env_status == ENV_RUNNING) + env_run(curenv); + else + sched_yield(); +} + + +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. + + // 怎么判断是内核模式, CPL位 + + if(tf->tf_cs && 3 == 0) { + panic("page_fault in kernel mode, fault address %d\n", fault_va); + } + + // We've already handled kernel-mode exceptions, so if we get here, + // the page fault happened in user mode. + + + // Call the environment's page fault upcall, if one exists. Set up a + // page fault stack frame on the user exception stack (below + // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. + // + // The page fault upcall might cause another page fault, in which case + // we branch to the page fault upcall recursively, pushing another + // page fault stack frame on top of the user exception stack. + // + // It is convenient for our code which returns from a page fault + // (lib/pfentry.S) to have one word of scratch space at the top of the + // trap-time stack; it allows us to more easily restore the eip/esp. In + // the non-recursive case, we don't have to worry about this because + // the top of the regular user stack is free. In the recursive case, + // this means we have to leave an extra word between the current top of + // the exception stack and the new stack frame because the exception + // stack _is_ the trap-time stack. + // + // If there's no page fault upcall, the environment didn't allocate a + // page for its exception stack or can't write to it, or the exception + // stack overflows, then destroy the environment that caused the fault. + // Note that the grade script assumes you will first check for the page + // fault upcall and print the "user fault va" message below if there is + // none. The remaining three checks can be combined into a single test. + // + // Hints: + // user_mem_assert() and env_run() are useful here. + // To change what the user environment runs, modify 'curenv->env_tf' + // (the 'tf' variable points at 'curenv->env_tf'). + + // LAB 4: Your code here. + struct UTrapframe *utf; + + if (curenv->env_pgfault_upcall) { + + if (tf->tf_esp >= UXSTACKTOP-PGSIZE && tf->tf_esp < UXSTACKTOP) { + // 异常模式下陷入 + utf = (struct UTrapframe *)(tf->tf_esp - sizeof(struct UTrapframe) - 4); + + } + else { + // 非异常模式下陷入 + utf = (struct UTrapframe *)(UXSTACKTOP - sizeof(struct UTrapframe) - 4); + } + // 检查异常栈是否溢出 + user_mem_assert(curenv, (const void *) utf, sizeof(struct UTrapframe), PTE_U|PTE_W); + + utf->utf_fault_va = fault_va; + utf->utf_err = tf->tf_trapno; + utf->utf_regs = tf->tf_regs; + utf->utf_eflags = tf->tf_eflags; + // 保存上次陷入时现场,用于返回 + utf->utf_eip = tf->tf_eip; + utf->utf_esp = tf->tf_esp; + // 再次转向执行 + tf->tf_eip = (uint32_t) curenv->env_pgfault_upcall; + tf->tf_esp = (uint32_t) utf; + env_run(curenv); + } + + + // Destroy the environment that caused the fault. + cprintf("[%08x] user fault va %08x ip %08x\n", + curenv->env_id, fault_va, tf->tf_eip); + print_trapframe(tf); + env_destroy(curenv); +} + diff --git a/lab/LAB4.si4project/Backup/trap(6309).c b/lab/LAB4.si4project/Backup/trap(6309).c new file mode 100644 index 0000000..e814e98 --- /dev/null +++ b/lab/LAB4.si4project/Backup/trap(6309).c @@ -0,0 +1,384 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct Taskstate ts; + +/* For debugging, so print_trapframe can distinguish between printing + * a saved trapframe and printing the current trapframe and print some + * additional information in the latter case. + */ +static struct Trapframe *last_tf; + +/* Interrupt descriptor table. (Must be built at run time because + * shifted function addresses can't be represented in relocation records.) + */ +struct Gatedesc idt[256] = { { 0 } }; +struct Pseudodesc idt_pd = { + sizeof(idt) - 1, (uint32_t) idt +}; + + +static const char *trapname(int trapno) +{ + static const char * const excnames[] = { + "Divide error", + "Debug", + "Non-Maskable Interrupt", + "Breakpoint", + "Overflow", + "BOUND Range Exceeded", + "Invalid Opcode", + "Device Not Available", + "Double Fault", + "Coprocessor Segment Overrun", + "Invalid TSS", + "Segment Not Present", + "Stack Fault", + "General Protection", + "Page Fault", + "(unknown trap)", + "x87 FPU Floating-Point Error", + "Alignment Check", + "Machine-Check", + "SIMD Floating-Point Exception" + }; + + if (trapno < ARRAY_SIZE(excnames)) + return excnames[trapno]; + if (trapno == T_SYSCALL) + return "System call"; + if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) + return "Hardware Interrupt"; + return "(unknown trap)"; +} + +// You will also need to modify trap_init() to initialize the idt to +// point to each of these entry points defined in trapentry.S; +// the SETGATE macro will be helpful here +void +trap_init(void) +{ + + extern struct Segdesc gdt[]; + void divide_handler(); + void debug_handler(); + void nmi_handler(); + void brkpt_handler(); + void oflow_handler(); + void bound_handler(); + void device_handler(); + void illop_handler(); + void tss_handler(); + void segnp_handler(); + void stack_handler(); + void gpflt_handler(); + void pgflt_handler(); + void fperr_handler(); + void align_handler(); + void mchk_handler(); + void simderr_handler(); + void syscall_handler(); + void dblflt_handler(); + + // LAB 3: Your code here. + // GD_KT 全局描述符, kernel text + SETGATE(idt[T_DIVIDE], 0, GD_KT, divide_handler, 0); + SETGATE(idt[T_DEBUG], 0, GD_KT, debug_handler, 0); + SETGATE(idt[T_NMI], 0, GD_KT, nmi_handler, 0); + SETGATE(idt[T_BRKPT], 0, GD_KT, brkpt_handler, 3); + SETGATE(idt[T_OFLOW], 0, GD_KT, oflow_handler, 0); + SETGATE(idt[T_BOUND], 0, GD_KT, bound_handler, 0); + SETGATE(idt[T_DEVICE], 0, GD_KT, device_handler, 0); + SETGATE(idt[T_ILLOP], 0, GD_KT, illop_handler, 0); + SETGATE(idt[T_DBLFLT], 0, GD_KT, dblflt_handler, 0); + SETGATE(idt[T_TSS], 0, GD_KT, tss_handler, 0); + SETGATE(idt[T_SEGNP], 0, GD_KT, segnp_handler, 0); + SETGATE(idt[T_STACK], 0, GD_KT, stack_handler, 0); + SETGATE(idt[T_GPFLT], 0, GD_KT, gpflt_handler, 0); + SETGATE(idt[T_PGFLT], 0, GD_KT, pgflt_handler, 0); + SETGATE(idt[T_FPERR], 0, GD_KT, fperr_handler, 0); + SETGATE(idt[T_ALIGN], 0, GD_KT, align_handler, 0); + SETGATE(idt[T_MCHK], 0, GD_KT, mchk_handler, 0); + SETGATE(idt[T_SIMDERR], 0, GD_KT, simderr_handler, 0); + SETGATE(idt[T_SYSCALL], 0, GD_KT, syscall_handler, 3); + + // Per-CPU setup + trap_init_percpu(); +} + +// Initialize and load the per-CPU TSS and IDT +void +trap_init_percpu(void) +{ + // The example code here sets up the Task State Segment (TSS) and + // the TSS descriptor for CPU 0. But it is incorrect if we are + // running on other CPUs because each CPU has its own kernel stack. + // Fix the code so that it works for all CPUs. + // + // Hints: + // - The macro "thiscpu" always refers to the current CPU's + // struct CpuInfo; + // - The ID of the current CPU is given by cpunum() or + // thiscpu->cpu_id; + // - Use "thiscpu->cpu_ts" as the TSS for the current CPU, + // rather than the global "ts" variable; + // - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor; + // - You mapped the per-CPU kernel stacks in mem_init_mp() + // - Initialize cpu_ts.ts_iomb to prevent unauthorized environments + // from doing IO (0 is not the correct value!) + // + // ltr sets a 'busy' flag in the TSS selector, so if you + // accidentally load the same TSS on more than one CPU, you'll + // get a triple fault. If you set up an individual CPU's TSS + // wrong, you may not get a fault until you try to return from + // user space on that CPU. + // + // LAB 4: Your code here: + thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - cpunum() * (KSTKGAP + KSTKSIZE); + thiscpu->cpu_ts.ts_ss0 = GD_KD; + + // Initialize the TSS slot of the gdt. + gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts), sizeof(struct Taskstate) - 1, 0); + gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0; + + // Load the TSS selector (like other segment selectors, the + // bottom three bits are special; we leave them 0) + ltr(GD_TSS0 + sizeof(struct Segdesc) * cpunum()); + + // Load the IDT + lidt(&idt_pd); +} + +void +print_trapframe(struct Trapframe *tf) +{ + cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum()); + print_regs(&tf->tf_regs); + cprintf(" es 0x----%04x\n", tf->tf_es); + cprintf(" ds 0x----%04x\n", tf->tf_ds); + cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); + // If this trap was a page fault that just happened + // (so %cr2 is meaningful), print the faulting linear address. + if (tf == last_tf && tf->tf_trapno == T_PGFLT) + cprintf(" cr2 0x%08x\n", rcr2()); + cprintf(" err 0x%08x", tf->tf_err); + // For page faults, print decoded fault error code: + // U/K=fault occurred in user/kernel mode + // W/R=a write/read caused the fault + // PR=a protection violation caused the fault (NP=page not present). + if (tf->tf_trapno == T_PGFLT) + cprintf(" [%s, %s, %s]\n", + tf->tf_err & 4 ? "user" : "kernel", + tf->tf_err & 2 ? "write" : "read", + tf->tf_err & 1 ? "protection" : "not-present"); + else + cprintf("\n"); + cprintf(" eip 0x%08x\n", tf->tf_eip); + cprintf(" cs 0x----%04x\n", tf->tf_cs); + cprintf(" flag 0x%08x\n", tf->tf_eflags); + if ((tf->tf_cs & 3) != 0) { + cprintf(" esp 0x%08x\n", tf->tf_esp); + cprintf(" ss 0x----%04x\n", tf->tf_ss); + } +} + +void +print_regs(struct PushRegs *regs) +{ + cprintf(" edi 0x%08x\n", regs->reg_edi); + cprintf(" esi 0x%08x\n", regs->reg_esi); + cprintf(" ebp 0x%08x\n", regs->reg_ebp); + cprintf(" oesp 0x%08x\n", regs->reg_oesp); + cprintf(" ebx 0x%08x\n", regs->reg_ebx); + cprintf(" edx 0x%08x\n", regs->reg_edx); + cprintf(" ecx 0x%08x\n", regs->reg_ecx); + cprintf(" eax 0x%08x\n", regs->reg_eax); +} + +static void +trap_dispatch(struct Trapframe *tf) +{ + // Handle processor exceptions. + // LAB 3: Your code here. + switch(tf->tf_trapno) { + case T_PGFLT: + page_fault_handler(tf); + break; + case T_BRKPT: + monitor(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; + } + break; + + } + + // Handle clock interrupts. Don't forget to acknowledge the + // interrupt using lapic_eoi() before calling the scheduler! + // LAB 4: Your code here. + + // 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; + + } + +} + +void +trap(struct Trapframe *tf) +{ + // The environment may have set DF and some versions + // of GCC rely on DF being clear + asm volatile("cld" ::: "cc"); + + // Halt the CPU if some other CPU has called panic() + extern char *panicstr; + if (panicstr) + asm volatile("hlt"); + + // Re-acqurie the big kernel lock if we were halted in + // sched_yield() + if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) + lock_kernel(); + // Check that interrupts are disabled. If this assertion + // fails, DO NOT be tempted to fix it by inserting a "cli" in + // the interrupt path. + assert(!(read_eflags() & FL_IF)); + + if ((tf->tf_cs & 3) == 3) { + // Trapped from user mode. + // Acquire the big kernel lock before doing any + // serious kernel work. + // LAB 4: Your code here. + lock_kernel(); + assert(curenv); + + // Garbage collect if current enviroment is a zombie + if (curenv->env_status == ENV_DYING) { + env_free(curenv); + curenv = NULL; + sched_yield(); + } + + // Copy trap frame (which is currently on the stack) + // into 'curenv->env_tf', so that running the environment + // will restart at the trap point. + curenv->env_tf = *tf; + // The trapframe on the stack should be ignored from here on. + tf = &curenv->env_tf; + } + + // Record that tf is the last real trapframe so + // print_trapframe can print some additional information. + last_tf = tf; + + // Dispatch based on what type of trap occurred + trap_dispatch(tf); + + // If we made it to this point, then no other environment was + // scheduled, so we should return to the current environment + // if doing so makes sense. + if (curenv && curenv->env_status == ENV_RUNNING) + env_run(curenv); + else + sched_yield(); +} + + +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. + + // 怎么判断是内核模式, CPL位 + + if(tf->tf_cs && 3 == 0) { + panic("page_fault in kernel mode, fault address %d\n", fault_va); + } + + // We've already handled kernel-mode exceptions, so if we get here, + // the page fault happened in user mode. + + + // Call the environment's page fault upcall, if one exists. Set up a + // page fault stack frame on the user exception stack (below + // UXSTACKTOP), then branch to curenv->env_pgfault_upcall. + // + // The page fault upcall might cause another page fault, in which case + // we branch to the page fault upcall recursively, pushing another + // page fault stack frame on top of the user exception stack. + // + // It is convenient for our code which returns from a page fault + // (lib/pfentry.S) to have one word of scratch space at the top of the + // trap-time stack; it allows us to more easily restore the eip/esp. In + // the non-recursive case, we don't have to worry about this because + // the top of the regular user stack is free. In the recursive case, + // this means we have to leave an extra word between the current top of + // the exception stack and the new stack frame because the exception + // stack _is_ the trap-time stack. + // + // If there's no page fault upcall, the environment didn't allocate a + // page for its exception stack or can't write to it, or the exception + // stack overflows, then destroy the environment that caused the fault. + // Note that the grade script assumes you will first check for the page + // fault upcall and print the "user fault va" message below if there is + // none. The remaining three checks can be combined into a single test. + // + // Hints: + // user_mem_assert() and env_run() are useful here. + // To change what the user environment runs, modify 'curenv->env_tf' + // (the 'tf' variable points at 'curenv->env_tf'). + + // LAB 4: Your code here. + + + // user_mem_assert(curenv, (const void *) fault_va, PGSIZE, 0); + + // Destroy the environment that caused the fault. + cprintf("[%08x] user fault va %08x ip %08x\n", + curenv->env_id, fault_va, tf->tf_eip); + print_trapframe(tf); + env_destroy(curenv); +} + diff --git a/lab/LAB4.si4project/Backup/trapentry(2731).S b/lab/LAB4.si4project/Backup/trapentry(2731).S new file mode 100644 index 0000000..7487913 --- /dev/null +++ b/lab/LAB4.si4project/Backup/trapentry(2731).S @@ -0,0 +1,100 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include + +#include + + +################################################################### +# exceptions/interrupts +################################################################### + +/* TRAPHANDLER defines a globally-visible function for handling a trap. + * It pushes a trap number onto the stack, then jumps to _alltraps. + * Use TRAPHANDLER for traps where the CPU automatically pushes an error code. + * + * You shouldn't call a TRAPHANDLER function from C, but you may + * need to _declare_ one in C (for instance, to get a function pointer + * during IDT setup). You can declare the function with + * void NAME(); + * where NAME is the argument passed to TRAPHANDLER. + */ +#define TRAPHANDLER(name, num) \ + .globl name; /* define global symbol for 'name' */ \ + .type name, @function; /* symbol type is function */ \ + .align 2; /* align function definition */ \ + name: /* function starts here */ \ + pushl $(num); \ + jmp _alltraps + +/* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code. + * It pushes a 0 in place of the error code, so the trap frame has the same + * format in either case. + + * tarp handler no error code + */ + +#define TRAPHANDLER_NOEC(name, num) \ + .globl name; \ + .type name, @function; \ + .align 2; \ + name: \ + pushl $0; \ + pushl $(num); \ + jmp _alltraps + +.text + +/* + * 我们知道哪些trap是有错误代码的? https://wiki.osdev.org/Exceptions + * Lab 3: Your code here for generating entry points for the different traps. + */ +TRAPHANDLER_NOEC(divide_handler, T_DIVIDE); +TRAPHANDLER_NOEC(debug_handler, T_DEBUG); +TRAPHANDLER_NOEC(nmi_handler, T_NMI); +TRAPHANDLER_NOEC(brkpt_handler, T_BRKPT); +TRAPHANDLER_NOEC(oflow_handler, T_OFLOW); +TRAPHANDLER_NOEC(bound_handler, T_BOUND); +TRAPHANDLER_NOEC(illop_handler, T_ILLOP); +TRAPHANDLER_NOEC(device_handler, T_DEVICE); +TRAPHANDLER(dblflt_handler, T_DBLFLT); +TRAPHANDLER(tss_handler, T_TSS); +TRAPHANDLER(segnp_handler, T_SEGNP); +TRAPHANDLER(stack_handler, T_STACK); +TRAPHANDLER(gpflt_handler, T_GPFLT); +TRAPHANDLER(pgflt_handler, T_PGFLT); +TRAPHANDLER_NOEC(fperr_handler, T_FPERR); +TRAPHANDLER(align_handler, T_ALIGN); +TRAPHANDLER_NOEC(mchk_handler, T_MCHK); +TRAPHANDLER_NOEC(simderr_handler, T_SIMDERR); +TRAPHANDLER_NOEC(syscall_handler, T_SYSCALL); + +/* +Your _alltraps should: + 1. push values to make the stack look like a struct Trapframe + 2. load GD_KD into %ds and %es + 3. pushl %esp to pass a pointer to the Trapframe as an argument to trap() + 4. call trap (can trap ever return?) +*/ + +/* + * Lab 3: Your code here for _alltraps + *what is padding ? 填充 pushl %ds;小端模式,是指数据的高字节保存在内存的高地址中 + */ +.globl _alltraps +_alltraps: + + pushl %ds; + pushl %es; + pushal; + + movw $GD_KD, %ax; + movw %ax, %ds; + movw %ax, %es; + + /*push esp, trap 能自己读esp, 我有点觉得是赋值*/ + pushl %esp; + call trap + \ No newline at end of file diff --git a/lab/LAB4.si4project/Backup/trapentry(7165).S b/lab/LAB4.si4project/Backup/trapentry(7165).S new file mode 100644 index 0000000..7487913 --- /dev/null +++ b/lab/LAB4.si4project/Backup/trapentry(7165).S @@ -0,0 +1,100 @@ +/* See COPYRIGHT for copyright information. */ + +#include +#include +#include + +#include + + +################################################################### +# exceptions/interrupts +################################################################### + +/* TRAPHANDLER defines a globally-visible function for handling a trap. + * It pushes a trap number onto the stack, then jumps to _alltraps. + * Use TRAPHANDLER for traps where the CPU automatically pushes an error code. + * + * You shouldn't call a TRAPHANDLER function from C, but you may + * need to _declare_ one in C (for instance, to get a function pointer + * during IDT setup). You can declare the function with + * void NAME(); + * where NAME is the argument passed to TRAPHANDLER. + */ +#define TRAPHANDLER(name, num) \ + .globl name; /* define global symbol for 'name' */ \ + .type name, @function; /* symbol type is function */ \ + .align 2; /* align function definition */ \ + name: /* function starts here */ \ + pushl $(num); \ + jmp _alltraps + +/* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code. + * It pushes a 0 in place of the error code, so the trap frame has the same + * format in either case. + + * tarp handler no error code + */ + +#define TRAPHANDLER_NOEC(name, num) \ + .globl name; \ + .type name, @function; \ + .align 2; \ + name: \ + pushl $0; \ + pushl $(num); \ + jmp _alltraps + +.text + +/* + * 我们知道哪些trap是有错误代码的? https://wiki.osdev.org/Exceptions + * Lab 3: Your code here for generating entry points for the different traps. + */ +TRAPHANDLER_NOEC(divide_handler, T_DIVIDE); +TRAPHANDLER_NOEC(debug_handler, T_DEBUG); +TRAPHANDLER_NOEC(nmi_handler, T_NMI); +TRAPHANDLER_NOEC(brkpt_handler, T_BRKPT); +TRAPHANDLER_NOEC(oflow_handler, T_OFLOW); +TRAPHANDLER_NOEC(bound_handler, T_BOUND); +TRAPHANDLER_NOEC(illop_handler, T_ILLOP); +TRAPHANDLER_NOEC(device_handler, T_DEVICE); +TRAPHANDLER(dblflt_handler, T_DBLFLT); +TRAPHANDLER(tss_handler, T_TSS); +TRAPHANDLER(segnp_handler, T_SEGNP); +TRAPHANDLER(stack_handler, T_STACK); +TRAPHANDLER(gpflt_handler, T_GPFLT); +TRAPHANDLER(pgflt_handler, T_PGFLT); +TRAPHANDLER_NOEC(fperr_handler, T_FPERR); +TRAPHANDLER(align_handler, T_ALIGN); +TRAPHANDLER_NOEC(mchk_handler, T_MCHK); +TRAPHANDLER_NOEC(simderr_handler, T_SIMDERR); +TRAPHANDLER_NOEC(syscall_handler, T_SYSCALL); + +/* +Your _alltraps should: + 1. push values to make the stack look like a struct Trapframe + 2. load GD_KD into %ds and %es + 3. pushl %esp to pass a pointer to the Trapframe as an argument to trap() + 4. call trap (can trap ever return?) +*/ + +/* + * Lab 3: Your code here for _alltraps + *what is padding ? 填充 pushl %ds;小端模式,是指数据的高字节保存在内存的高地址中 + */ +.globl _alltraps +_alltraps: + + pushl %ds; + pushl %es; + pushal; + + movw $GD_KD, %ax; + movw %ax, %ds; + movw %ax, %es; + + /*push esp, trap 能自己读esp, 我有点觉得是赋值*/ + pushl %esp; + call trap + \ No newline at end of file diff --git a/lab/LAB4.si4project/LAB4.SearchResults b/lab/LAB4.si4project/LAB4.SearchResults new file mode 100644 index 0000000..d4dfec4 --- /dev/null +++ b/lab/LAB4.si4project/LAB4.SearchResults @@ -0,0 +1,4 @@ +---- sched_halt Matches (3 in 1 files) ---- +sched.c (kern) line 8 : void sched_halt(void); +sched_yield in sched.c (kern) : sched_halt(); +sched.c (kern) line 56 : sched_halt(void) diff --git a/lab/LAB4.si4project/LAB4.bookmarks.xml b/lab/LAB4.si4project/LAB4.bookmarks.xml new file mode 100644 index 0000000..c8b9cb1 --- /dev/null +++ b/lab/LAB4.si4project/LAB4.bookmarks.xml @@ -0,0 +1,7 @@ + + + + diff --git a/lab/LAB4.si4project/LAB4.sip_sym b/lab/LAB4.si4project/LAB4.sip_sym new file mode 100644 index 0000000..7d59ad9 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_sym differ diff --git a/lab/LAB4.si4project/LAB4.sip_xab b/lab/LAB4.si4project/LAB4.sip_xab new file mode 100644 index 0000000..c021212 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xab differ diff --git a/lab/LAB4.si4project/LAB4.sip_xad b/lab/LAB4.si4project/LAB4.sip_xad new file mode 100644 index 0000000..408fcf3 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xad differ diff --git a/lab/LAB4.si4project/LAB4.sip_xc b/lab/LAB4.si4project/LAB4.sip_xc new file mode 100644 index 0000000..1656324 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xc differ diff --git a/lab/LAB4.si4project/LAB4.sip_xf b/lab/LAB4.si4project/LAB4.sip_xf new file mode 100644 index 0000000..09e8462 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xf differ diff --git a/lab/LAB4.si4project/LAB4.sip_xm b/lab/LAB4.si4project/LAB4.sip_xm new file mode 100644 index 0000000..2b014e0 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xm differ diff --git a/lab/LAB4.si4project/LAB4.sip_xr b/lab/LAB4.si4project/LAB4.sip_xr new file mode 100644 index 0000000..0eb429b Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xr differ diff --git a/lab/LAB4.si4project/LAB4.sip_xsb b/lab/LAB4.si4project/LAB4.sip_xsb new file mode 100644 index 0000000..def0f34 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xsb differ diff --git a/lab/LAB4.si4project/LAB4.sip_xsd b/lab/LAB4.si4project/LAB4.sip_xsd new file mode 100644 index 0000000..1ae7230 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.sip_xsd differ diff --git a/lab/LAB4.si4project/LAB4.siproj b/lab/LAB4.si4project/LAB4.siproj new file mode 100644 index 0000000..89a2a82 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.siproj differ diff --git a/lab/LAB4.si4project/LAB4.siproj_settings.xml b/lab/LAB4.si4project/LAB4.siproj_settings.xml new file mode 100644 index 0000000..ca84b35 --- /dev/null +++ b/lab/LAB4.si4project/LAB4.siproj_settings.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/lab/LAB4.si4project/LAB4.siwork b/lab/LAB4.si4project/LAB4.siwork new file mode 100644 index 0000000..9e1dd08 Binary files /dev/null and b/lab/LAB4.si4project/LAB4.siwork differ diff --git a/lab/LAB4.si4project/LAB4.snippets.xml b/lab/LAB4.si4project/LAB4.snippets.xml new file mode 100644 index 0000000..a337f1d --- /dev/null +++ b/lab/LAB4.si4project/LAB4.snippets.xml @@ -0,0 +1,7 @@ + + + + diff --git a/lab/LAB4.si4project/cache/parse/boot_main.c.sisc b/lab/LAB4.si4project/cache/parse/boot_main.c.sisc new file mode 100644 index 0000000..b538876 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/boot_main.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/fs_test.c.sisc b/lab/LAB4.si4project/cache/parse/fs_test.c.sisc new file mode 100644 index 0000000..bb1775d Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/fs_test.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/gradelib.py.sisc b/lab/LAB4.si4project/cache/parse/gradelib.py.sisc new file mode 100644 index 0000000..0d64c45 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/gradelib.py.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_assert.h.sisc b/lab/LAB4.si4project/cache/parse/inc_assert.h.sisc new file mode 100644 index 0000000..4d48776 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_assert.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_elf.h.sisc b/lab/LAB4.si4project/cache/parse/inc_elf.h.sisc new file mode 100644 index 0000000..57c8451 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_elf.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_env.h.sisc b/lab/LAB4.si4project/cache/parse/inc_env.h.sisc new file mode 100644 index 0000000..0ecbd52 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_env.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_error.h.sisc b/lab/LAB4.si4project/cache/parse/inc_error.h.sisc new file mode 100644 index 0000000..ebc864d Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_error.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_kbdreg.h.sisc b/lab/LAB4.si4project/cache/parse/inc_kbdreg.h.sisc new file mode 100644 index 0000000..7df5bb3 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_kbdreg.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_lib.h.sisc b/lab/LAB4.si4project/cache/parse/inc_lib.h.sisc new file mode 100644 index 0000000..314f201 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_lib.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_memlayout.h.sisc b/lab/LAB4.si4project/cache/parse/inc_memlayout.h.sisc new file mode 100644 index 0000000..bcc4294 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_memlayout.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_mmu.h.sisc b/lab/LAB4.si4project/cache/parse/inc_mmu.h.sisc new file mode 100644 index 0000000..b0c1e71 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_mmu.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_stab.h.sisc b/lab/LAB4.si4project/cache/parse/inc_stab.h.sisc new file mode 100644 index 0000000..16ba2ad Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_stab.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_stdarg.h.sisc b/lab/LAB4.si4project/cache/parse/inc_stdarg.h.sisc new file mode 100644 index 0000000..a96ff79 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_stdarg.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_stdio.h.sisc b/lab/LAB4.si4project/cache/parse/inc_stdio.h.sisc new file mode 100644 index 0000000..0e4d523 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_stdio.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_string.h.sisc b/lab/LAB4.si4project/cache/parse/inc_string.h.sisc new file mode 100644 index 0000000..c54d79f Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_string.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_syscall.h.sisc b/lab/LAB4.si4project/cache/parse/inc_syscall.h.sisc new file mode 100644 index 0000000..7410a23 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_syscall.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_trap.h.sisc b/lab/LAB4.si4project/cache/parse/inc_trap.h.sisc new file mode 100644 index 0000000..a6b4471 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_trap.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_types.h.sisc b/lab/LAB4.si4project/cache/parse/inc_types.h.sisc new file mode 100644 index 0000000..9048bad Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_types.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/inc_x86.h.sisc b/lab/LAB4.si4project/cache/parse/inc_x86.h.sisc new file mode 100644 index 0000000..cdd006c Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/inc_x86.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_console.c.sisc b/lab/LAB4.si4project/cache/parse/kern_console.c.sisc new file mode 100644 index 0000000..e08be43 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_console.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_console.h.sisc b/lab/LAB4.si4project/cache/parse/kern_console.h.sisc new file mode 100644 index 0000000..f4df42f Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_console.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_cpu.h.sisc b/lab/LAB4.si4project/cache/parse/kern_cpu.h.sisc new file mode 100644 index 0000000..bb59bbb Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_cpu.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_entrypgdir.c.sisc b/lab/LAB4.si4project/cache/parse/kern_entrypgdir.c.sisc new file mode 100644 index 0000000..ad405fe Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_entrypgdir.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_env.c.sisc b/lab/LAB4.si4project/cache/parse/kern_env.c.sisc new file mode 100644 index 0000000..b245379 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_env.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_env.h.sisc b/lab/LAB4.si4project/cache/parse/kern_env.h.sisc new file mode 100644 index 0000000..6fd7040 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_env.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_init.c.sisc b/lab/LAB4.si4project/cache/parse/kern_init.c.sisc new file mode 100644 index 0000000..558f838 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_init.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_kclock.c.sisc b/lab/LAB4.si4project/cache/parse/kern_kclock.c.sisc new file mode 100644 index 0000000..5f1618d Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_kclock.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_kclock.h.sisc b/lab/LAB4.si4project/cache/parse/kern_kclock.h.sisc new file mode 100644 index 0000000..a4a04b4 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_kclock.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_kdebug.c.sisc b/lab/LAB4.si4project/cache/parse/kern_kdebug.c.sisc new file mode 100644 index 0000000..283db2c Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_kdebug.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_kdebug.h.sisc b/lab/LAB4.si4project/cache/parse/kern_kdebug.h.sisc new file mode 100644 index 0000000..ea89564 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_kdebug.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_lapic.c.sisc b/lab/LAB4.si4project/cache/parse/kern_lapic.c.sisc new file mode 100644 index 0000000..e3d2e33 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_lapic.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_monitor.c.sisc b/lab/LAB4.si4project/cache/parse/kern_monitor.c.sisc new file mode 100644 index 0000000..c65b70b Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_monitor.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_monitor.h.sisc b/lab/LAB4.si4project/cache/parse/kern_monitor.h.sisc new file mode 100644 index 0000000..504848e Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_monitor.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_mpconfig.c.sisc b/lab/LAB4.si4project/cache/parse/kern_mpconfig.c.sisc new file mode 100644 index 0000000..65c70d5 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_mpconfig.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_picirq.c.sisc b/lab/LAB4.si4project/cache/parse/kern_picirq.c.sisc new file mode 100644 index 0000000..9c8a44c Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_picirq.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_picirq.h.sisc b/lab/LAB4.si4project/cache/parse/kern_picirq.h.sisc new file mode 100644 index 0000000..b6c1a4c Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_picirq.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_pmap.c.sisc b/lab/LAB4.si4project/cache/parse/kern_pmap.c.sisc new file mode 100644 index 0000000..9948953 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_pmap.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_pmap.h.sisc b/lab/LAB4.si4project/cache/parse/kern_pmap.h.sisc new file mode 100644 index 0000000..eaa9109 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_pmap.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_printf.c.sisc b/lab/LAB4.si4project/cache/parse/kern_printf.c.sisc new file mode 100644 index 0000000..fda30fe Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_printf.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_sched.c.sisc b/lab/LAB4.si4project/cache/parse/kern_sched.c.sisc new file mode 100644 index 0000000..d0f5a44 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_sched.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_sched.h.sisc b/lab/LAB4.si4project/cache/parse/kern_sched.h.sisc new file mode 100644 index 0000000..fd9e774 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_sched.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_spinlock.c.sisc b/lab/LAB4.si4project/cache/parse/kern_spinlock.c.sisc new file mode 100644 index 0000000..71d85a8 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_spinlock.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_spinlock.h.sisc b/lab/LAB4.si4project/cache/parse/kern_spinlock.h.sisc new file mode 100644 index 0000000..3fbd34d Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_spinlock.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_syscall.c.sisc b/lab/LAB4.si4project/cache/parse/kern_syscall.c.sisc new file mode 100644 index 0000000..eaaf1cd Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_syscall.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_syscall.h.sisc b/lab/LAB4.si4project/cache/parse/kern_syscall.h.sisc new file mode 100644 index 0000000..184330c Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_syscall.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_trap.c.sisc b/lab/LAB4.si4project/cache/parse/kern_trap.c.sisc new file mode 100644 index 0000000..3c3fc32 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_trap.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/kern_trap.h.sisc b/lab/LAB4.si4project/cache/parse/kern_trap.h.sisc new file mode 100644 index 0000000..74bd5a4 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/kern_trap.h.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_console.c.sisc b/lab/LAB4.si4project/cache/parse/lib_console.c.sisc new file mode 100644 index 0000000..4e0d806 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_console.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_exit.c.sisc b/lab/LAB4.si4project/cache/parse/lib_exit.c.sisc new file mode 100644 index 0000000..6bf2a4c Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_exit.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_fork.c.sisc b/lab/LAB4.si4project/cache/parse/lib_fork.c.sisc new file mode 100644 index 0000000..b16393e Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_fork.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_ipc.c.sisc b/lab/LAB4.si4project/cache/parse/lib_ipc.c.sisc new file mode 100644 index 0000000..fe1ff75 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_ipc.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_libmain.c.sisc b/lab/LAB4.si4project/cache/parse/lib_libmain.c.sisc new file mode 100644 index 0000000..29cc64f Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_libmain.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_panic.c.sisc b/lab/LAB4.si4project/cache/parse/lib_panic.c.sisc new file mode 100644 index 0000000..a5ec958 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_panic.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_pgfault.c.sisc b/lab/LAB4.si4project/cache/parse/lib_pgfault.c.sisc new file mode 100644 index 0000000..75639df Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_pgfault.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_printf.c.sisc b/lab/LAB4.si4project/cache/parse/lib_printf.c.sisc new file mode 100644 index 0000000..64eaab5 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_printf.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_printfmt.c.sisc b/lab/LAB4.si4project/cache/parse/lib_printfmt.c.sisc new file mode 100644 index 0000000..61a63cf Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_printfmt.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_readline.c.sisc b/lab/LAB4.si4project/cache/parse/lib_readline.c.sisc new file mode 100644 index 0000000..7d024d0 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_readline.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_string.c.sisc b/lab/LAB4.si4project/cache/parse/lib_string.c.sisc new file mode 100644 index 0000000..71b687f Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_string.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/lib_syscall.c.sisc b/lab/LAB4.si4project/cache/parse/lib_syscall.c.sisc new file mode 100644 index 0000000..120c2f9 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/lib_syscall.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_badsegment.c.sisc b/lab/LAB4.si4project/cache/parse/user_badsegment.c.sisc new file mode 100644 index 0000000..dea973b Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_badsegment.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_breakpoint.c.sisc b/lab/LAB4.si4project/cache/parse/user_breakpoint.c.sisc new file mode 100644 index 0000000..5d1acfc Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_breakpoint.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_buggyhello.c.sisc b/lab/LAB4.si4project/cache/parse/user_buggyhello.c.sisc new file mode 100644 index 0000000..b7c8e68 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_buggyhello.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_buggyhello2.c.sisc b/lab/LAB4.si4project/cache/parse/user_buggyhello2.c.sisc new file mode 100644 index 0000000..be83d74 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_buggyhello2.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_divzero.c.sisc b/lab/LAB4.si4project/cache/parse/user_divzero.c.sisc new file mode 100644 index 0000000..23debe4 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_divzero.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_dumbfork.c.sisc b/lab/LAB4.si4project/cache/parse/user_dumbfork.c.sisc new file mode 100644 index 0000000..922a43b Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_dumbfork.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_evilhello.c.sisc b/lab/LAB4.si4project/cache/parse/user_evilhello.c.sisc new file mode 100644 index 0000000..893f330 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_evilhello.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_fairness.c.sisc b/lab/LAB4.si4project/cache/parse/user_fairness.c.sisc new file mode 100644 index 0000000..a7556fc Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_fairness.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultalloc.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultalloc.c.sisc new file mode 100644 index 0000000..b377b33 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultalloc.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultallocbad.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultallocbad.c.sisc new file mode 100644 index 0000000..f1d041b Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultallocbad.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultbadhandler.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultbadhandler.c.sisc new file mode 100644 index 0000000..725d33c Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultbadhandler.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultdie.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultdie.c.sisc new file mode 100644 index 0000000..c33871d Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultdie.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultevilhandler.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultevilhandler.c.sisc new file mode 100644 index 0000000..0fc8efc Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultevilhandler.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultnostack.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultnostack.c.sisc new file mode 100644 index 0000000..d3c4a51 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultnostack.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultread.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultread.c.sisc new file mode 100644 index 0000000..adb06bc Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultread.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultreadkernel.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultreadkernel.c.sisc new file mode 100644 index 0000000..0ba502a Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultreadkernel.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultregs.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultregs.c.sisc new file mode 100644 index 0000000..a248b5a Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultregs.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultwrite.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultwrite.c.sisc new file mode 100644 index 0000000..cbf5c38 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultwrite.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_faultwritekernel.c.sisc b/lab/LAB4.si4project/cache/parse/user_faultwritekernel.c.sisc new file mode 100644 index 0000000..89b7ff2 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_faultwritekernel.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_forktree.c.sisc b/lab/LAB4.si4project/cache/parse/user_forktree.c.sisc new file mode 100644 index 0000000..8984602 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_forktree.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_hello.c.sisc b/lab/LAB4.si4project/cache/parse/user_hello.c.sisc new file mode 100644 index 0000000..6601029 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_hello.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_idle.c.sisc b/lab/LAB4.si4project/cache/parse/user_idle.c.sisc new file mode 100644 index 0000000..b9ba423 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_idle.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_pingpong.c.sisc b/lab/LAB4.si4project/cache/parse/user_pingpong.c.sisc new file mode 100644 index 0000000..01ae47d Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_pingpong.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_pingpongs.c.sisc b/lab/LAB4.si4project/cache/parse/user_pingpongs.c.sisc new file mode 100644 index 0000000..3fc4c1e Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_pingpongs.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_primes.c.sisc b/lab/LAB4.si4project/cache/parse/user_primes.c.sisc new file mode 100644 index 0000000..2a8d8b9 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_primes.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_sendpage.c.sisc b/lab/LAB4.si4project/cache/parse/user_sendpage.c.sisc new file mode 100644 index 0000000..02cf01a Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_sendpage.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_softint.c.sisc b/lab/LAB4.si4project/cache/parse/user_softint.c.sisc new file mode 100644 index 0000000..35fccfb Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_softint.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_spin.c.sisc b/lab/LAB4.si4project/cache/parse/user_spin.c.sisc new file mode 100644 index 0000000..4ef7835 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_spin.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_stresssched.c.sisc b/lab/LAB4.si4project/cache/parse/user_stresssched.c.sisc new file mode 100644 index 0000000..3001b8d Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_stresssched.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_testbss.c.sisc b/lab/LAB4.si4project/cache/parse/user_testbss.c.sisc new file mode 100644 index 0000000..d8e5700 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_testbss.c.sisc differ diff --git a/lab/LAB4.si4project/cache/parse/user_yield.c.sisc b/lab/LAB4.si4project/cache/parse/user_yield.c.sisc new file mode 100644 index 0000000..8e99b55 Binary files /dev/null and b/lab/LAB4.si4project/cache/parse/user_yield.c.sisc differ diff --git a/lab/gradelib.py b/lab/gradelib.py new file mode 100644 index 0000000..07a7a84 --- /dev/null +++ b/lab/gradelib.py @@ -0,0 +1,547 @@ +from __future__ import print_function + +import sys, os, re, time, socket, select, subprocess, errno, shutil, traceback +from subprocess import check_call, Popen +from optparse import OptionParser + +__all__ = [] + +################################################################## +# Test structure +# + +__all__ += ["test", "end_part", "run_tests", "get_current_test"] + +TESTS = [] +TOTAL = POSSIBLE = 0 +PART_TOTAL = PART_POSSIBLE = 0 +CURRENT_TEST = None + +def test(points, title=None, parent=None): + """Decorator for declaring test functions. If title is None, the + title of the test will be derived from the function name by + stripping the leading "test_" and replacing underscores with + spaces.""" + + def register_test(fn, title=title): + if not title: + assert fn.__name__.startswith("test_") + title = fn.__name__[5:].replace("_", " ") + if parent: + title = " " + title + + def run_test(): + global TOTAL, POSSIBLE, CURRENT_TEST + + # Handle test dependencies + if run_test.complete: + return + run_test.complete = True + if parent: + parent() + + # Run the test + fail = None + start = time.time() + CURRENT_TEST = run_test + sys.stdout.write("%s: " % title) + sys.stdout.flush() + try: + fn() + except AssertionError as e: + fail = "".join(traceback.format_exception_only(type(e), e)) + + # Display and handle test result + POSSIBLE += points + if points: + print("%s" % \ + (color("red", "FAIL") if fail else color("green", "OK")), end=' ') + if time.time() - start > 0.1: + print("(%.1fs)" % (time.time() - start), end=' ') + print() + if fail: + print(" %s" % fail.replace("\n", "\n ")) + else: + TOTAL += points + for callback in run_test.on_finish: + callback(fail) + CURRENT_TEST = None + + # Record test metadata on the test wrapper function + run_test.__name__ = fn.__name__ + run_test.title = title + run_test.complete = False + run_test.on_finish = [] + TESTS.append(run_test) + return run_test + return register_test + +def end_part(name): + def show_part(): + global PART_TOTAL, PART_POSSIBLE + print("Part %s score: %d/%d" % \ + (name, TOTAL - PART_TOTAL, POSSIBLE - PART_POSSIBLE)) + print() + PART_TOTAL, PART_POSSIBLE = TOTAL, POSSIBLE + show_part.title = "" + TESTS.append(show_part) + +def run_tests(): + """Set up for testing and run the registered test functions.""" + + # Handle command line + global options + parser = OptionParser(usage="usage: %prog [-v] [filters...]") + parser.add_option("-v", "--verbose", action="store_true", + help="print commands") + parser.add_option("--color", choices=["never", "always", "auto"], + default="auto", help="never, always, or auto") + (options, args) = parser.parse_args() + + # Start with a full build to catch build errors + make() + + # Clean the file system if there is one + reset_fs() + + # Run tests + limit = list(map(str.lower, args)) + try: + for test in TESTS: + if not limit or any(l in test.title.lower() for l in limit): + test() + if not limit: + print("Score: %d/%d" % (TOTAL, POSSIBLE)) + except KeyboardInterrupt: + pass + if TOTAL < POSSIBLE: + sys.exit(1) + +def get_current_test(): + if not CURRENT_TEST: + raise RuntimeError("No test is running") + return CURRENT_TEST + +################################################################## +# Assertions +# + +__all__ += ["assert_equal", "assert_lines_match"] + +def assert_equal(got, expect, msg=""): + if got == expect: + return + if msg: + msg += "\n" + raise AssertionError("%sgot:\n %s\nexpected:\n %s" % + (msg, str(got).replace("\n", "\n "), + str(expect).replace("\n", "\n "))) + +def assert_lines_match(text, *regexps, **kw): + """Assert that all of regexps match some line in text. If a 'no' + keyword argument is given, it must be a list of regexps that must + *not* match any line in text.""" + + def assert_lines_match_kw(no=[]): + return no + no = assert_lines_match_kw(**kw) + + # Check text against regexps + lines = text.splitlines() + good = set() + bad = set() + for i, line in enumerate(lines): + if any(re.match(r, line) for r in regexps): + good.add(i) + regexps = [r for r in regexps if not re.match(r, line)] + if any(re.match(r, line) for r in no): + bad.add(i) + + if not regexps and not bad: + return + + # We failed; construct an informative failure message + show = set() + for lineno in good.union(bad): + for offset in range(-2, 3): + show.add(lineno + offset) + if regexps: + show.update(n for n in range(len(lines) - 5, len(lines))) + + msg = [] + last = -1 + for lineno in sorted(show): + if 0 <= lineno < len(lines): + if lineno != last + 1: + msg.append("...") + last = lineno + msg.append("%s %s" % (color("red", "BAD ") if lineno in bad else + color("green", "GOOD") if lineno in good + else " ", + lines[lineno])) + if last != len(lines) - 1: + msg.append("...") + if bad: + msg.append("unexpected lines in output") + for r in regexps: + msg.append(color("red", "MISSING") + " '%s'" % r) + raise AssertionError("\n".join(msg)) + +################################################################## +# Utilities +# + +__all__ += ["make", "maybe_unlink", "reset_fs", "color"] + +MAKE_TIMESTAMP = 0 + +def pre_make(): + """Delay prior to running make to ensure file mtimes change.""" + while int(time.time()) == MAKE_TIMESTAMP: + time.sleep(0.1) + +def post_make(): + """Record the time after make completes so that the next run of + make can be delayed if needed.""" + global MAKE_TIMESTAMP + MAKE_TIMESTAMP = int(time.time()) + +def make(*target): + pre_make() + if Popen(("make",) + target).wait(): + sys.exit(1) + post_make() + +def show_command(cmd): + from pipes import quote + print("\n$", " ".join(map(quote, cmd))) + +def maybe_unlink(*paths): + for path in paths: + try: + os.unlink(path) + except EnvironmentError as e: + if e.errno != errno.ENOENT: + raise + +COLORS = {"default": "\033[0m", "red": "\033[31m", "green": "\033[32m"} + +def color(name, text): + if options.color == "always" or (options.color == "auto" and os.isatty(1)): + return COLORS[name] + text + COLORS["default"] + return text + +def reset_fs(): + if os.path.exists("obj/fs/clean-fs.img"): + shutil.copyfile("obj/fs/clean-fs.img", "obj/fs/fs.img") + +################################################################## +# Controllers +# + +__all__ += ["QEMU", "GDBClient"] + +class QEMU(object): + _GDBPORT = None + + def __init__(self, *make_args): + # Check that QEMU is not currently running + try: + GDBClient(self.get_gdb_port(), timeout=0).close() + except socket.error: + pass + else: + print("""\ +GDB stub found on port %d. +QEMU appears to already be running. Please exit it if possible or use +'killall qemu' or 'killall qemu.real'.""" % self.get_gdb_port(), file=sys.stderr) + sys.exit(1) + + if options.verbose: + show_command(("make",) + make_args) + cmd = ("make", "-s", "--no-print-directory") + make_args + self.proc = Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE) + # Accumulated output as a string + self.output = "" + # Accumulated output as a bytearray + self.outbytes = bytearray() + self.on_output = [] + + @staticmethod + def get_gdb_port(): + if QEMU._GDBPORT is None: + p = Popen(["make", "-s", "--no-print-directory", "print-gdbport"], + stdout=subprocess.PIPE) + (out, _) = p.communicate() + if p.returncode: + raise RuntimeError( + "Failed to get gdbport: make exited with %d" % + p.returncode) + QEMU._GDBPORT = int(out) + return QEMU._GDBPORT + + def fileno(self): + if self.proc: + return self.proc.stdout.fileno() + + def handle_read(self): + buf = os.read(self.proc.stdout.fileno(), 4096) + self.outbytes.extend(buf) + self.output = self.outbytes.decode("utf-8", "replace") + for callback in self.on_output: + callback(buf) + if buf == b"": + self.wait() + return + + def wait(self): + if self.proc: + self.proc.wait() + self.proc = None + + def kill(self): + if self.proc: + self.proc.terminate() + +class GDBClient(object): + def __init__(self, port, timeout=15): + start = time.time() + while True: + self.sock = socket.socket() + try: + self.sock.settimeout(1) + self.sock.connect(("localhost", port)) + break + except socket.error: + if time.time() >= start + timeout: + raise + self.__buf = "" + + def fileno(self): + if self.sock: + return self.sock.fileno() + + def handle_read(self): + try: + data = self.sock.recv(4096).decode("ascii", "replace") + except socket.error: + data = "" + if data == "": + self.sock.close() + self.sock = None + return + self.__buf += data + + while True: + m = re.search(r"\$([^#]*)#[0-9a-zA-Z]{2}", self.__buf) + if not m: + break + pkt = m.group(1) + self.__buf = self.__buf[m.end():] + + if pkt.startswith("T05"): + # Breakpoint + raise TerminateTest + + def __send(self, cmd): + packet = "$%s#%02x" % (cmd, sum(map(ord, cmd)) % 256) + self.sock.sendall(packet.encode("ascii")) + + def __send_break(self): + self.sock.sendall(b"\x03") + + def close(self): + if self.sock: + self.sock.close() + self.sock = None + + def cont(self): + self.__send("c") + + def breakpoint(self, addr): + self.__send("Z1,%x,1" % addr) + + +################################################################## +# QEMU test runner +# + +__all__ += ["TerminateTest", "Runner"] + +class TerminateTest(Exception): + pass + +class Runner(): + def __init__(self, *default_monitors): + self.__default_monitors = default_monitors + + def run_qemu(self, *monitors, **kw): + """Run a QEMU-based test. monitors should functions that will + be called with this Runner instance once QEMU and GDB are + started. Typically, they should register callbacks that throw + TerminateTest when stop events occur. The target_base + argument gives the make target to run. The make_args argument + should be a list of additional arguments to pass to make. The + timeout argument bounds how long to run before returning.""" + + def run_qemu_kw(target_base="qemu", make_args=[], timeout=30): + return target_base, make_args, timeout + target_base, make_args, timeout = run_qemu_kw(**kw) + + # Start QEMU + pre_make() + self.qemu = QEMU(target_base + "-nox-gdb", *make_args) + self.gdb = None + + try: + # Wait for QEMU to start or make to fail. This will set + # self.gdb if QEMU starts. + self.qemu.on_output = [self.__monitor_start] + self.__react([self.qemu], timeout=30) + self.qemu.on_output = [] + if self.gdb is None: + print("Failed to connect to QEMU; output:") + print(self.qemu.output) + sys.exit(1) + post_make() + + # QEMU and GDB are up + self.reactors = [self.qemu, self.gdb] + + # Start monitoring + for m in self.__default_monitors + monitors: + m(self) + + # Run and react + self.gdb.cont() + self.__react(self.reactors, timeout) + finally: + # Shutdown QEMU + try: + if self.gdb is None: + sys.exit(1) + self.qemu.kill() + self.__react(self.reactors, 5) + self.gdb.close() + self.qemu.wait() + except: + print("""\ +Failed to shutdown QEMU. You might need to 'killall qemu' or +'killall qemu.real'. +""") + raise + + def __monitor_start(self, output): + if b"\n" in output: + try: + self.gdb = GDBClient(self.qemu.get_gdb_port(), timeout=30) + raise TerminateTest + except socket.error: + pass + if not len(output): + raise TerminateTest + + def __react(self, reactors, timeout): + deadline = time.time() + timeout + try: + while True: + timeleft = deadline - time.time() + if timeleft < 0: + sys.stdout.write("Timeout! ") + sys.stdout.flush() + return + + rset = [r for r in reactors if r.fileno() is not None] + if not rset: + return + + rset, _, _ = select.select(rset, [], [], timeleft) + for reactor in rset: + reactor.handle_read() + except TerminateTest: + pass + + def user_test(self, binary, *monitors, **kw): + """Run a user test using the specified binary. Monitors and + keyword arguments are as for run_qemu. This runs on a disk + snapshot unless the keyword argument 'snapshot' is False.""" + + maybe_unlink("obj/kern/init.o", "obj/kern/kernel") + if kw.pop("snapshot", True): + kw.setdefault("make_args", []).append("QEMUEXTRA+=-snapshot") + self.run_qemu(target_base="run-%s" % binary, *monitors, **kw) + + def match(self, *args, **kwargs): + """Shortcut to call assert_lines_match on the most recent QEMU + output.""" + + assert_lines_match(self.qemu.output, *args, **kwargs) + +################################################################## +# Monitors +# + +__all__ += ["save", "stop_breakpoint", "call_on_line", "stop_on_line"] + +def save(path): + """Return a monitor that writes QEMU's output to path. If the + test fails, copy the output to path.test-name.""" + + def setup_save(runner): + f.seek(0) + f.truncate() + runner.qemu.on_output.append(f.write) + get_current_test().on_finish.append(save_on_finish) + + def save_on_finish(fail): + f.flush() + save_path = path + "." + get_current_test().__name__[5:] + if fail: + shutil.copyfile(path, save_path) + print(" QEMU output saved to %s" % save_path) + elif os.path.exists(save_path): + os.unlink(save_path) + print(" (Old %s failure log removed)" % save_path) + + f = open(path, "wb") + return setup_save + +def stop_breakpoint(addr): + """Returns a monitor that stops when addr is reached. addr may be + a number or the name of a symbol.""" + + def setup_breakpoint(runner): + if isinstance(addr, str): + addrs = [int(sym[:8], 16) for sym in open("obj/kern/kernel.sym") + if sym[11:].strip() == addr] + assert len(addrs), "Symbol %s not found" % addr + runner.gdb.breakpoint(addrs[0]) + else: + runner.gdb.breakpoint(addr) + return setup_breakpoint + +def call_on_line(regexp, callback): + """Returns a monitor that calls 'callback' when QEMU prints a line + matching 'regexp'.""" + + def setup_call_on_line(runner): + buf = bytearray() + def handle_output(output): + buf.extend(output) + while b"\n" in buf: + line, buf[:] = buf.split(b"\n", 1) + line = line.decode("utf-8", "replace") + if re.match(regexp, line): + callback(line) + runner.qemu.on_output.append(handle_output) + return setup_call_on_line + +def stop_on_line(regexp): + """Returns a monitor that stops when QEMU prints a line matching + 'regexp'.""" + + def stop(line): + raise TerminateTest + return call_on_line(regexp, stop) diff --git a/lab/mergedep.pl b/lab/mergedep.pl new file mode 100644 index 0000000..1730d53 --- /dev/null +++ b/lab/mergedep.pl @@ -0,0 +1,86 @@ +#!/usr/bin/perl +# Copyright 2003 Bryan Ford +# Distributed under the GNU General Public License. +# +# Usage: mergedep [ ...] +# +# This script merges the contents of all specified +# on the command line into the single file , +# which may or may not previously exist. +# Dependencies in the will override +# any existing dependencies for the same targets in . +# The are deleted after is updated. +# +# The are typically generated by GCC with the -MD option, +# and the is typically included from a Makefile, +# as shown here for GNU 'make': +# +# .deps: $(wildcard *.d) +# perl mergedep $@ $^ +# -include .deps +# +# This script properly handles multiple dependencies per , +# including dependencies having no target, +# so it is compatible with GCC3's -MP option. +# + +sub readdeps { + my $filename = shift; + + open(DEPFILE, $filename) or return 0; + while () { + if (/([^:]*):([^\\:]*)([\\]?)$/) { + my $target = $1; + my $deplines = $2; + my $slash = $3; + while ($slash ne '') { + $_ = ; + defined($_) or die + "Unterminated dependency in $filename"; + /(^[ \t][^\\]*)([\\]?)$/ or die + "Bad continuation line in $filename"; + $deplines = "$deplines\\\n$1"; + $slash = $2; + } + #print "DEPENDENCY [[$target]]: [[$deplines]]\n"; + $dephash{$target} = $deplines; + } elsif (/^[#]?[ \t]*$/) { + # ignore blank lines and comments + } else { + die "Bad dependency line in $filename: $_"; + } + } + close DEPFILE; + return 1; +} + + +if ($#ARGV < 0) { + print "Usage: mergedep [ ..]\n"; + exit(1); +} + +%dephash = (); + +# Read the main dependency file +$maindeps = $ARGV[0]; +readdeps($maindeps); + +# Read and merge in the new dependency files +foreach $i (1 .. $#ARGV) { + readdeps($ARGV[$i]) or die "Can't open $ARGV[$i]"; +} + +# Update the main dependency file +open(DEPFILE, ">$maindeps.tmp") or die "Can't open output file $maindeps.tmp"; +foreach $target (keys %dephash) { + print DEPFILE "$target:$dephash{$target}"; +} +close DEPFILE; +rename("$maindeps.tmp", "$maindeps") or die "Can't overwrite $maindeps"; + +# Finally, delete the new dependency files +foreach $i (1 .. $#ARGV) { + unlink($ARGV[$i]) or print "Error removing $ARGV[$i]\n"; +} + diff --git a/lab/mitlab4.si4project/mitlab4.sip_sym b/lab/mitlab4.si4project/mitlab4.sip_sym new file mode 100644 index 0000000..c5f073a Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_sym differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xab b/lab/mitlab4.si4project/mitlab4.sip_xab new file mode 100644 index 0000000..054a26c Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xab differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xad b/lab/mitlab4.si4project/mitlab4.sip_xad new file mode 100644 index 0000000..fab3fde Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xad differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xc b/lab/mitlab4.si4project/mitlab4.sip_xc new file mode 100644 index 0000000..aa86611 Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xc differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xf b/lab/mitlab4.si4project/mitlab4.sip_xf new file mode 100644 index 0000000..2a43253 Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xf differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xm b/lab/mitlab4.si4project/mitlab4.sip_xm new file mode 100644 index 0000000..53bf598 Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xm differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xr b/lab/mitlab4.si4project/mitlab4.sip_xr new file mode 100644 index 0000000..9072e54 Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xr differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xsb b/lab/mitlab4.si4project/mitlab4.sip_xsb new file mode 100644 index 0000000..b596e2a Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xsb differ diff --git a/lab/mitlab4.si4project/mitlab4.sip_xsd b/lab/mitlab4.si4project/mitlab4.sip_xsd new file mode 100644 index 0000000..e390b06 Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.sip_xsd differ diff --git a/lab/mitlab4.si4project/mitlab4.siproj b/lab/mitlab4.si4project/mitlab4.siproj new file mode 100644 index 0000000..ed772f4 Binary files /dev/null and b/lab/mitlab4.si4project/mitlab4.siproj differ diff --git a/lab/mitlab4.si4project/mitlab4.snippets.xml b/lab/mitlab4.si4project/mitlab4.snippets.xml new file mode 100644 index 0000000..a337f1d --- /dev/null +++ b/lab/mitlab4.si4project/mitlab4.snippets.xml @@ -0,0 +1,7 @@ + + + +