mirror of
https://github.com/SmallPond/MIT6.828_OS.git
synced 2026-04-29 21:31:05 +08:00
my solution to lab5
This commit is contained in:
151
lab/Untitled Project.si4project/Backup/bc(1629).c
Normal file
151
lab/Untitled Project.si4project/Backup/bc(1629).c
Normal file
@@ -0,0 +1,151 @@
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
// Return the virtual address of this disk block.
|
||||
void*
|
||||
diskaddr(uint32_t blockno)
|
||||
{
|
||||
if (blockno == 0 || (super && blockno >= super->s_nblocks))
|
||||
panic("bad block number %08x in diskaddr", blockno);
|
||||
return (char*) (DISKMAP + blockno * BLKSIZE);
|
||||
}
|
||||
|
||||
// Is this virtual address mapped?
|
||||
bool
|
||||
va_is_mapped(void *va)
|
||||
{
|
||||
return (uvpd[PDX(va)] & PTE_P) && (uvpt[PGNUM(va)] & PTE_P);
|
||||
}
|
||||
|
||||
// Is this virtual address dirty?
|
||||
bool
|
||||
va_is_dirty(void *va)
|
||||
{
|
||||
return (uvpt[PGNUM(va)] & PTE_D) != 0;
|
||||
}
|
||||
|
||||
// Fault any disk block that is read in to memory by
|
||||
// loading it from disk.
|
||||
static void
|
||||
bc_pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void *) utf->utf_fault_va;
|
||||
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
|
||||
int r;
|
||||
|
||||
// Check that the fault was within the block cache region
|
||||
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
|
||||
panic("page fault in FS: eip %08x, va %08x, err %04x",
|
||||
utf->utf_eip, addr, utf->utf_err);
|
||||
|
||||
// Sanity check the block number.
|
||||
if (super && blockno >= super->s_nblocks)
|
||||
panic("reading non-existent block %08x\n", blockno);
|
||||
|
||||
// Allocate a page in the disk map region, read the contents
|
||||
// of the block from the disk into that page.
|
||||
// Hint: first round addr to page boundary. fs/ide.c has code to read
|
||||
// the disk.
|
||||
//
|
||||
// LAB 5: you code here:
|
||||
|
||||
// Clear the dirty bit for the disk block page since we just read the
|
||||
// block from disk
|
||||
if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
|
||||
panic("in bc_pgfault, sys_page_map: %e", r);
|
||||
|
||||
// Check that the block we read was allocated. (exercise for
|
||||
// the reader: why do we do this *after* reading the block
|
||||
// in?)
|
||||
if (bitmap && block_is_free(blockno))
|
||||
panic("reading free block %08x\n", blockno);
|
||||
}
|
||||
|
||||
// Flush the contents of the block containing VA out to disk if
|
||||
// necessary, then clear the PTE_D bit using sys_page_map.
|
||||
// If the block is not in the block cache or is not dirty, does
|
||||
// nothing.
|
||||
// Hint: Use va_is_mapped, va_is_dirty, and ide_write.
|
||||
// Hint: Use the PTE_SYSCALL constant when calling sys_page_map.
|
||||
// Hint: Don't forget to round addr down.
|
||||
void
|
||||
flush_block(void *addr)
|
||||
{
|
||||
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
|
||||
|
||||
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
|
||||
panic("flush_block of bad va %08x", addr);
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("flush_block not implemented");
|
||||
}
|
||||
|
||||
// Test that the block cache works, by smashing the superblock and
|
||||
// reading it back.
|
||||
static void
|
||||
check_bc(void)
|
||||
{
|
||||
struct Super backup;
|
||||
|
||||
// back up super block
|
||||
memmove(&backup, diskaddr(1), sizeof backup);
|
||||
|
||||
// smash it
|
||||
strcpy(diskaddr(1), "OOPS!\n");
|
||||
flush_block(diskaddr(1));
|
||||
assert(va_is_mapped(diskaddr(1)));
|
||||
assert(!va_is_dirty(diskaddr(1)));
|
||||
|
||||
// clear it out
|
||||
sys_page_unmap(0, diskaddr(1));
|
||||
assert(!va_is_mapped(diskaddr(1)));
|
||||
|
||||
// read it back in
|
||||
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
|
||||
|
||||
// fix it
|
||||
memmove(diskaddr(1), &backup, sizeof backup);
|
||||
flush_block(diskaddr(1));
|
||||
|
||||
// Now repeat the same experiment, but pass an unaligned address to
|
||||
// flush_block.
|
||||
|
||||
// back up super block
|
||||
memmove(&backup, diskaddr(1), sizeof backup);
|
||||
|
||||
// smash it
|
||||
strcpy(diskaddr(1), "OOPS!\n");
|
||||
|
||||
// Pass an unaligned address to flush_block.
|
||||
flush_block(diskaddr(1) + 20);
|
||||
assert(va_is_mapped(diskaddr(1)));
|
||||
|
||||
// Skip the !va_is_dirty() check because it makes the bug somewhat
|
||||
// obscure and hence harder to debug.
|
||||
//assert(!va_is_dirty(diskaddr(1)));
|
||||
|
||||
// clear it out
|
||||
sys_page_unmap(0, diskaddr(1));
|
||||
assert(!va_is_mapped(diskaddr(1)));
|
||||
|
||||
// read it back in
|
||||
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
|
||||
|
||||
// fix it
|
||||
memmove(diskaddr(1), &backup, sizeof backup);
|
||||
flush_block(diskaddr(1));
|
||||
|
||||
cprintf("block cache is good\n");
|
||||
}
|
||||
|
||||
void
|
||||
bc_init(void)
|
||||
{
|
||||
struct Super super;
|
||||
set_pgfault_handler(bc_pgfault);
|
||||
check_bc();
|
||||
|
||||
// cache the super block by reading it once
|
||||
memmove(&super, diskaddr(1), sizeof super);
|
||||
}
|
||||
|
||||
169
lab/Untitled Project.si4project/Backup/bc(3621).c
Normal file
169
lab/Untitled Project.si4project/Backup/bc(3621).c
Normal file
@@ -0,0 +1,169 @@
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
// Return the virtual address of this disk block.
|
||||
void*
|
||||
diskaddr(uint32_t blockno)
|
||||
{
|
||||
if (blockno == 0 || (super && blockno >= super->s_nblocks))
|
||||
panic("bad block number %08x in diskaddr", blockno);
|
||||
return (char*) (DISKMAP + blockno * BLKSIZE);
|
||||
}
|
||||
|
||||
// Is this virtual address mapped?
|
||||
bool
|
||||
va_is_mapped(void *va)
|
||||
{
|
||||
return (uvpd[PDX(va)] & PTE_P) && (uvpt[PGNUM(va)] & PTE_P);
|
||||
}
|
||||
|
||||
// Is this virtual address dirty?
|
||||
bool
|
||||
va_is_dirty(void *va)
|
||||
{
|
||||
return (uvpt[PGNUM(va)] & PTE_D) != 0;
|
||||
}
|
||||
|
||||
// Fault any disk block that is read in to memory by
|
||||
// loading it from disk.
|
||||
static void
|
||||
bc_pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void *) utf->utf_fault_va;
|
||||
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
|
||||
int r;
|
||||
|
||||
// Check that the fault was within the block cache region
|
||||
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
|
||||
panic("page fault in FS: eip %08x, va %08x, err %04x",
|
||||
utf->utf_eip, addr, utf->utf_err);
|
||||
|
||||
// Sanity check the block number.
|
||||
if (super && blockno >= super->s_nblocks)
|
||||
panic("reading non-existent block %08x\n", blockno);
|
||||
|
||||
// Allocate a page in the disk map region, read the contents
|
||||
// of the block from the disk into that page.
|
||||
// Hint: first round addr to page boundary. fs/ide.c has code to read
|
||||
// the disk.
|
||||
//
|
||||
// LAB 5: you code here:
|
||||
// envid 传入 0? 在最初的哪个进程下 alloc 一个page ?
|
||||
addr =(void *) ROUNDDOWN(addr, PGSIZE);
|
||||
if ( (r = sys_page_alloc(0, addr, PTE_P
|
||||
|PTE_W|PTE_U)) < 0) {
|
||||
panic("in bc_pgfault, sys_page_alloc: %e", r);
|
||||
}
|
||||
// size_t secno = (addr - DISKMAP) / BLKSIZE;
|
||||
if ( (r = ide_read(blockno*BLKSECTS, addr, BLKSECTS)) < 0) {
|
||||
panic("in bc_pgfault, ide_read: %e",r);
|
||||
}
|
||||
|
||||
// Clear the dirty bit for the disk block page since we just read the
|
||||
// block from disk
|
||||
// 只是为了修改标志位
|
||||
if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
|
||||
panic("in bc_pgfault, sys_page_map: %e", r);
|
||||
|
||||
// Check that the block we read was allocated. (exercise for
|
||||
// the reader: why do we do this *after* reading the block
|
||||
// in?)
|
||||
if (bitmap && block_is_free(blockno))
|
||||
panic("reading free block %08x\n", blockno);
|
||||
}
|
||||
|
||||
// Flush the contents of the block containing VA out to disk if
|
||||
// necessary, then clear the PTE_D bit using sys_page_map.
|
||||
// If the block is not in the block cache or is not dirty, does
|
||||
// nothing.
|
||||
// Hint: Use va_is_mapped, va_is_dirty, and ide_write.
|
||||
// Hint: Use the PTE_SYSCALL constant when calling sys_page_map.
|
||||
// Hint: Don't forget to round addr down.
|
||||
void
|
||||
flush_block(void *addr)
|
||||
{
|
||||
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
|
||||
|
||||
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
|
||||
panic("flush_block of bad va %08x", addr);
|
||||
int r;
|
||||
// LAB 5: Your code here.
|
||||
addr = (void *)ROUNDDOWN(addr, PGSIZE);
|
||||
if (va_is_mapped(addr) && va_is_dirty(addr)) {
|
||||
|
||||
ide_write(blockno*BLKSECTS, addr , BLKSECTS);
|
||||
if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
|
||||
panic("in flush_block, sys_page_map: %e", r);
|
||||
}
|
||||
|
||||
// panic("flush_block not implemented");
|
||||
}
|
||||
|
||||
// Test that the block cache works, by smashing the superblock and
|
||||
// reading it back.
|
||||
static void
|
||||
check_bc(void)
|
||||
{
|
||||
struct Super backup;
|
||||
|
||||
// back up super block
|
||||
memmove(&backup, diskaddr(1), sizeof backup);
|
||||
|
||||
// smash it
|
||||
strcpy(diskaddr(1), "OOPS!\n");
|
||||
flush_block(diskaddr(1));
|
||||
assert(va_is_mapped(diskaddr(1)));
|
||||
assert(!va_is_dirty(diskaddr(1)));
|
||||
|
||||
// clear it out
|
||||
sys_page_unmap(0, diskaddr(1));
|
||||
assert(!va_is_mapped(diskaddr(1)));
|
||||
|
||||
// read it back in
|
||||
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
|
||||
|
||||
// fix it
|
||||
memmove(diskaddr(1), &backup, sizeof backup);
|
||||
flush_block(diskaddr(1));
|
||||
|
||||
// Now repeat the same experiment, but pass an unaligned address to
|
||||
// flush_block.
|
||||
|
||||
// back up super block
|
||||
memmove(&backup, diskaddr(1), sizeof backup);
|
||||
|
||||
// smash it
|
||||
strcpy(diskaddr(1), "OOPS!\n");
|
||||
|
||||
// Pass an unaligned address to flush_block.
|
||||
flush_block(diskaddr(1) + 20);
|
||||
assert(va_is_mapped(diskaddr(1)));
|
||||
|
||||
// Skip the !va_is_dirty() check because it makes the bug somewhat
|
||||
// obscure and hence harder to debug.
|
||||
//assert(!va_is_dirty(diskaddr(1)));
|
||||
|
||||
// clear it out
|
||||
sys_page_unmap(0, diskaddr(1));
|
||||
assert(!va_is_mapped(diskaddr(1)));
|
||||
|
||||
// read it back in
|
||||
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
|
||||
|
||||
// fix it
|
||||
memmove(diskaddr(1), &backup, sizeof backup);
|
||||
flush_block(diskaddr(1));
|
||||
|
||||
cprintf("block cache is good\n");
|
||||
}
|
||||
|
||||
void
|
||||
bc_init(void)
|
||||
{
|
||||
struct Super super;
|
||||
set_pgfault_handler(bc_pgfault);
|
||||
check_bc();
|
||||
|
||||
// cache the super block by reading it once
|
||||
memmove(&super, diskaddr(1), sizeof super);
|
||||
}
|
||||
578
lab/Untitled Project.si4project/Backup/env(3753).c
Normal file
578
lab/Untitled Project.si4project/Backup/env(3753).c
Normal file
@@ -0,0 +1,578 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/elf.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
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.
|
||||
e->env_tf.tf_eflags |= FL_IF;
|
||||
// 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.
|
||||
<<<<<<< HEAD
|
||||
|
||||
// If this is the file server (type == ENV_TYPE_FS) give it I/O privileges.
|
||||
// LAB 5: 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);
|
||||
|
||||
>>>>>>> lab4
|
||||
}
|
||||
|
||||
//
|
||||
// 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");
|
||||
}
|
||||
|
||||
577
lab/Untitled Project.si4project/Backup/env(840).c
Normal file
577
lab/Untitled Project.si4project/Backup/env(840).c
Normal file
@@ -0,0 +1,577 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/elf.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
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.
|
||||
e->env_tf.tf_eflags |= FL_IF;
|
||||
// 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;
|
||||
if (type == ENV_TYPE_FS) {
|
||||
newenv->env_tf.tf_eflags |= FL_IOPL_MASK;
|
||||
}
|
||||
load_icode(newenv, binary);
|
||||
// If this is the file server (type == ENV_TYPE_FS) give it I/O privileges.
|
||||
// LAB 5: Your code here.
|
||||
}
|
||||
|
||||
//
|
||||
// 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");
|
||||
}
|
||||
|
||||
10
lab/Untitled Project.si4project/Backup/exit(6898).c
Normal file
10
lab/Untitled Project.si4project/Backup/exit(6898).c
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
exit(void)
|
||||
{
|
||||
close_all();
|
||||
sys_env_destroy(0);
|
||||
}
|
||||
|
||||
22
lab/Untitled Project.si4project/Backup/faultio(2032).c
Normal file
22
lab/Untitled Project.si4project/Backup/faultio(2032).c
Normal file
@@ -0,0 +1,22 @@
|
||||
// test user-level fault handler -- alloc pages to fix faults
|
||||
|
||||
#include <inc/lib.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int x, r;
|
||||
int nsecs = 1;
|
||||
int secno = 0;
|
||||
int diskno = 1;
|
||||
|
||||
if (read_eflags() & FL_IOPL_3)
|
||||
cprintf("eflags wrong\n");
|
||||
|
||||
// this outb to select disk 1 should result in a general protection
|
||||
// fault, because user-level code shouldn't be able to use the io space.
|
||||
outb(0x1F6, 0xE0 | (1<<4));
|
||||
|
||||
cprintf("%s: made it here --- bug\n");
|
||||
}
|
||||
180
lab/Untitled Project.si4project/Backup/file(8133).c
Normal file
180
lab/Untitled Project.si4project/Backup/file(8133).c
Normal file
@@ -0,0 +1,180 @@
|
||||
#include <inc/fs.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define debug 0
|
||||
|
||||
union Fsipc fsipcbuf __attribute__((aligned(PGSIZE)));
|
||||
|
||||
// Send an inter-environment request to the file server, and wait for
|
||||
// a reply. The request body should be in fsipcbuf, and parts of the
|
||||
// response may be written back to fsipcbuf.
|
||||
// type: request code, passed as the simple integer IPC value.
|
||||
// dstva: virtual address at which to receive reply page, 0 if none.
|
||||
// Returns result from the file server.
|
||||
static int
|
||||
fsipc(unsigned type, void *dstva)
|
||||
{
|
||||
static envid_t fsenv;
|
||||
if (fsenv == 0)
|
||||
fsenv = ipc_find_env(ENV_TYPE_FS);
|
||||
|
||||
static_assert(sizeof(fsipcbuf) == PGSIZE);
|
||||
|
||||
if (debug)
|
||||
cprintf("[%08x] fsipc %d %08x\n", thisenv->env_id, type, *(uint32_t *)&fsipcbuf);
|
||||
|
||||
ipc_send(fsenv, type, &fsipcbuf, PTE_P | PTE_W | PTE_U);
|
||||
return ipc_recv(NULL, dstva, NULL);
|
||||
}
|
||||
|
||||
static int devfile_flush(struct Fd *fd);
|
||||
static ssize_t devfile_read(struct Fd *fd, void *buf, size_t n);
|
||||
static ssize_t devfile_write(struct Fd *fd, const void *buf, size_t n);
|
||||
static int devfile_stat(struct Fd *fd, struct Stat *stat);
|
||||
static int devfile_trunc(struct Fd *fd, off_t newsize);
|
||||
|
||||
struct Dev devfile =
|
||||
{
|
||||
.dev_id = 'f',
|
||||
.dev_name = "file",
|
||||
.dev_read = devfile_read,
|
||||
.dev_close = devfile_flush,
|
||||
.dev_stat = devfile_stat,
|
||||
.dev_write = devfile_write,
|
||||
.dev_trunc = devfile_trunc
|
||||
};
|
||||
|
||||
// Open a file (or directory).
|
||||
//
|
||||
// Returns:
|
||||
// The file descriptor index on success
|
||||
// -E_BAD_PATH if the path is too long (>= MAXPATHLEN)
|
||||
// < 0 for other errors.
|
||||
int
|
||||
open(const char *path, int mode)
|
||||
{
|
||||
// Find an unused file descriptor page using fd_alloc.
|
||||
// Then send a file-open request to the file server.
|
||||
// Include 'path' and 'omode' in request,
|
||||
// and map the returned file descriptor page
|
||||
// at the appropriate fd address.
|
||||
// FSREQ_OPEN returns 0 on success, < 0 on failure.
|
||||
//
|
||||
// (fd_alloc does not allocate a page, it just returns an
|
||||
// unused fd address. Do you need to allocate a page?)
|
||||
//
|
||||
// Return the file descriptor index.
|
||||
// If any step after fd_alloc fails, use fd_close to free the
|
||||
// file descriptor.
|
||||
|
||||
int r;
|
||||
struct Fd *fd;
|
||||
|
||||
if (strlen(path) >= MAXPATHLEN)
|
||||
return -E_BAD_PATH;
|
||||
|
||||
if ((r = fd_alloc(&fd)) < 0)
|
||||
return r;
|
||||
|
||||
strcpy(fsipcbuf.open.req_path, path);
|
||||
fsipcbuf.open.req_omode = mode;
|
||||
|
||||
if ((r = fsipc(FSREQ_OPEN, fd)) < 0) {
|
||||
fd_close(fd, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
return fd2num(fd);
|
||||
}
|
||||
|
||||
// Flush the file descriptor. After this the fileid is invalid.
|
||||
//
|
||||
// This function is called by fd_close. fd_close will take care of
|
||||
// unmapping the FD page from this environment. Since the server uses
|
||||
// the reference counts on the FD pages to detect which files are
|
||||
// open, unmapping it is enough to free up server-side resources.
|
||||
// Other than that, we just have to make sure our changes are flushed
|
||||
// to disk.
|
||||
static int
|
||||
devfile_flush(struct Fd *fd)
|
||||
{
|
||||
fsipcbuf.flush.req_fileid = fd->fd_file.id;
|
||||
return fsipc(FSREQ_FLUSH, NULL);
|
||||
}
|
||||
|
||||
// Read at most 'n' bytes from 'fd' at the current position into 'buf'.
|
||||
//
|
||||
// Returns:
|
||||
// The number of bytes successfully read.
|
||||
// < 0 on error.
|
||||
static ssize_t
|
||||
devfile_read(struct Fd *fd, void *buf, size_t n)
|
||||
{
|
||||
// Make an FSREQ_READ request to the file system server after
|
||||
// filling fsipcbuf.read with the request arguments. The
|
||||
// bytes read will be written back to fsipcbuf by the file
|
||||
// system server.
|
||||
int r;
|
||||
|
||||
fsipcbuf.read.req_fileid = fd->fd_file.id;
|
||||
fsipcbuf.read.req_n = n;
|
||||
if ((r = fsipc(FSREQ_READ, NULL)) < 0)
|
||||
return r;
|
||||
assert(r <= n);
|
||||
assert(r <= PGSIZE);
|
||||
memmove(buf, fsipcbuf.readRet.ret_buf, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// Write at most 'n' bytes from 'buf' to 'fd' at the current seek position.
|
||||
//
|
||||
// Returns:
|
||||
// The number of bytes successfully written.
|
||||
// < 0 on error.
|
||||
static ssize_t
|
||||
devfile_write(struct Fd *fd, const void *buf, size_t n)
|
||||
{
|
||||
// Make an FSREQ_WRITE request to the file system server. Be
|
||||
// careful: fsipcbuf.write.req_buf is only so large, but
|
||||
// remember that write is always allowed to write *fewer*
|
||||
// bytes than requested.
|
||||
// LAB 5: Your code here
|
||||
panic("devfile_write not implemented");
|
||||
}
|
||||
|
||||
static int
|
||||
devfile_stat(struct Fd *fd, struct Stat *st)
|
||||
{
|
||||
int r;
|
||||
|
||||
fsipcbuf.stat.req_fileid = fd->fd_file.id;
|
||||
if ((r = fsipc(FSREQ_STAT, NULL)) < 0)
|
||||
return r;
|
||||
strcpy(st->st_name, fsipcbuf.statRet.ret_name);
|
||||
st->st_size = fsipcbuf.statRet.ret_size;
|
||||
st->st_isdir = fsipcbuf.statRet.ret_isdir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Truncate or extend an open file to 'size' bytes
|
||||
static int
|
||||
devfile_trunc(struct Fd *fd, off_t newsize)
|
||||
{
|
||||
fsipcbuf.set_size.req_fileid = fd->fd_file.id;
|
||||
fsipcbuf.set_size.req_size = newsize;
|
||||
return fsipc(FSREQ_SET_SIZE, NULL);
|
||||
}
|
||||
|
||||
|
||||
// Synchronize disk with buffer cache
|
||||
int
|
||||
sync(void)
|
||||
{
|
||||
// Ask the file server to update the disk
|
||||
// by writing any dirty blocks in the buffer cache.
|
||||
|
||||
return fsipc(FSREQ_SYNC, NULL);
|
||||
}
|
||||
|
||||
167
lab/Untitled Project.si4project/Backup/fork(6291).c
Normal file
167
lab/Untitled Project.si4project/Backup/fork(6291).c
Normal file
@@ -0,0 +1,167 @@
|
||||
// implement fork from user space
|
||||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
// 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 <inc/memlayout.h>).
|
||||
|
||||
// LAB 4: Your code here.
|
||||
if (! ( (err & FEC_WR) && (uvpd[PDX(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_COW)))
|
||||
panic("Neither the fault is a write nor COW page. \n");
|
||||
// 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.
|
||||
envid_t envid = sys_getenvid();
|
||||
// cprintf("pgfault: envid: %d\n", ENVX(envid));
|
||||
// 临时页暂存
|
||||
if ((r = sys_page_alloc(envid, (void *)PFTEMP, PTE_P| PTE_W|PTE_U)) < 0)
|
||||
panic("pgfault: page allocation fault:%e\n", r);
|
||||
addr = ROUNDDOWN(addr, PGSIZE);
|
||||
memcpy((void *) PFTEMP, (const void *) addr, PGSIZE);
|
||||
if ((r = sys_page_map(envid, (void *) PFTEMP, envid, addr , PTE_P|PTE_W|PTE_U)) < 0 )
|
||||
panic("pgfault: page map failed %e\n", r);
|
||||
|
||||
if ((r = sys_page_unmap(envid, (void *) PFTEMP)) < 0)
|
||||
panic("pgfault: page unmap failed %e\n", r);
|
||||
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
// LAB 4: Your code here.
|
||||
pte_t *pte;
|
||||
int ret;
|
||||
// 用户空间的地址较低
|
||||
uint32_t va = pn * PGSIZE;
|
||||
|
||||
|
||||
if ( (uvpt[pn] & PTE_W) || (uvpt[pn] & PTE_COW)) {
|
||||
|
||||
// 子进程标记
|
||||
if ((ret = sys_page_map(thisenv->env_id, (void *) va, envid, (void *) va, PTE_P|PTE_U|PTE_COW)) < 0)
|
||||
return ret;
|
||||
// 父进程标记
|
||||
if ((ret = sys_page_map(thisenv->env_id, (void *)va, thisenv->env_id, (void *)va, PTE_P|PTE_U|PTE_COW)) < 0)
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
// 简单映射
|
||||
if((ret = sys_page_map(thisenv->env_id, (void *) va, envid, (void * )va, PTE_P|PTE_U)) <0 )
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// panic("duppage not implemented");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 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.
|
||||
envid_t envid;
|
||||
int r;
|
||||
size_t i, j, pn;
|
||||
// Set up our page fault handler
|
||||
set_pgfault_handler(pgfault);
|
||||
|
||||
envid = sys_exofork();
|
||||
|
||||
if (envid < 0) {
|
||||
panic("sys_exofork failed: %e", envid);
|
||||
}
|
||||
|
||||
if (envid == 0) {
|
||||
// child
|
||||
thisenv = &envs[ENVX(sys_getenvid())];
|
||||
return 0;
|
||||
}
|
||||
// here is parent !
|
||||
// Copy our address space and page fault handler setup to the child.
|
||||
|
||||
for (pn = PGNUM(UTEXT); pn < PGNUM(USTACKTOP); pn++) {
|
||||
if ( (uvpd[pn >> 10] & PTE_P) && (uvpt[pn] & PTE_P)) {
|
||||
// 页表
|
||||
if ( (r = duppage(envid, pn)) < 0)
|
||||
return r;
|
||||
|
||||
}
|
||||
}
|
||||
// alloc a page and map child exception stack
|
||||
if ((r = sys_page_alloc(envid, (void *)(UXSTACKTOP-PGSIZE), PTE_U | PTE_P | PTE_W)) < 0)
|
||||
return r;
|
||||
extern void _pgfault_upcall(void);
|
||||
if ((r = sys_env_set_pgfault_upcall(envid, _pgfault_upcall)) < 0)
|
||||
return r;
|
||||
|
||||
// Start the child environment running
|
||||
if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0)
|
||||
panic("sys_env_set_status: %e", r);
|
||||
|
||||
return envid;
|
||||
// panic("fork not implemented");
|
||||
}
|
||||
|
||||
// Challenge!
|
||||
int
|
||||
sfork(void)
|
||||
{
|
||||
panic("sfork not implemented");
|
||||
return -E_INVAL;
|
||||
}
|
||||
456
lab/Untitled Project.si4project/Backup/fs(6931).c
Normal file
456
lab/Untitled Project.si4project/Backup/fs(6931).c
Normal file
@@ -0,0 +1,456 @@
|
||||
#include <inc/string.h>
|
||||
#include <inc/partition.h>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Super block
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Validate the file system super-block.
|
||||
void
|
||||
check_super(void)
|
||||
{
|
||||
if (super->s_magic != FS_MAGIC)
|
||||
panic("bad file system magic number");
|
||||
|
||||
if (super->s_nblocks > DISKSIZE/BLKSIZE)
|
||||
panic("file system is too large");
|
||||
|
||||
cprintf("superblock is good\n");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Free block bitmap
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Check to see if the block bitmap indicates that block 'blockno' is free.
|
||||
// Return 1 if the block is free, 0 if not.
|
||||
bool
|
||||
block_is_free(uint32_t blockno)
|
||||
{
|
||||
if (super == 0 || blockno >= super->s_nblocks)
|
||||
return 0;
|
||||
if (bitmap[blockno / 32] & (1 << (blockno % 32)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mark a block free in the bitmap
|
||||
void
|
||||
free_block(uint32_t blockno)
|
||||
{
|
||||
// Blockno zero is the null pointer of block numbers.
|
||||
if (blockno == 0)
|
||||
panic("attempt to free zero block");
|
||||
bitmap[blockno/32] |= 1<<(blockno%32);
|
||||
}
|
||||
|
||||
// Search the bitmap for a free block and allocate it. When you
|
||||
// allocate a block, immediately flush the changed bitmap block
|
||||
// to disk.
|
||||
//
|
||||
// Return block number allocated on success,
|
||||
// -E_NO_DISK if we are out of blocks.
|
||||
//
|
||||
// Hint: use free_block as an example for manipulating the bitmap.
|
||||
int
|
||||
alloc_block(void)
|
||||
{
|
||||
// The bitmap consists of one or more blocks. A single bitmap block
|
||||
// contains the in-use bits for BLKBITSIZE blocks. There are
|
||||
// super->s_nblocks blocks in the disk altogether.
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("alloc_block not implemented");
|
||||
return -E_NO_DISK;
|
||||
}
|
||||
|
||||
// Validate the file system bitmap.
|
||||
//
|
||||
// Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves --
|
||||
// are all marked as in-use.
|
||||
void
|
||||
check_bitmap(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
// Make sure all bitmap blocks are marked in-use
|
||||
for (i = 0; i * BLKBITSIZE < super->s_nblocks; i++)
|
||||
assert(!block_is_free(2+i));
|
||||
|
||||
// Make sure the reserved and root blocks are marked in-use.
|
||||
assert(!block_is_free(0));
|
||||
assert(!block_is_free(1));
|
||||
|
||||
cprintf("bitmap is good\n");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File system structures
|
||||
// --------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
// Initialize the file system
|
||||
void
|
||||
fs_init(void)
|
||||
{
|
||||
static_assert(sizeof(struct File) == 256);
|
||||
|
||||
// Find a JOS disk. Use the second IDE disk (number 1) if available
|
||||
if (ide_probe_disk1())
|
||||
ide_set_disk(1);
|
||||
else
|
||||
ide_set_disk(0);
|
||||
bc_init();
|
||||
|
||||
// Set "super" to point to the super block.
|
||||
super = diskaddr(1);
|
||||
check_super();
|
||||
|
||||
// Set "bitmap" to the beginning of the first bitmap block.
|
||||
bitmap = diskaddr(2);
|
||||
check_bitmap();
|
||||
|
||||
}
|
||||
|
||||
// Find the disk block number slot for the 'filebno'th block in file 'f'.
|
||||
// Set '*ppdiskbno' to point to that slot.
|
||||
// The slot will be one of the f->f_direct[] entries,
|
||||
// or an entry in the indirect block.
|
||||
// When 'alloc' is set, this function will allocate an indirect block
|
||||
// if necessary.
|
||||
//
|
||||
// Returns:
|
||||
// 0 on success (but note that *ppdiskbno might equal 0).
|
||||
// -E_NOT_FOUND if the function needed to allocate an indirect block, but
|
||||
// alloc was 0.
|
||||
// -E_NO_DISK if there's no space on the disk for an indirect block.
|
||||
// -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT).
|
||||
//
|
||||
// Analogy: This is like pgdir_walk for files.
|
||||
// Hint: Don't forget to clear any block you allocate.
|
||||
static int
|
||||
file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
panic("file_block_walk not implemented");
|
||||
}
|
||||
|
||||
// Set *blk to the address in memory where the filebno'th
|
||||
// block of file 'f' would be mapped.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_NO_DISK if a block needed to be allocated but the disk is full.
|
||||
// -E_INVAL if filebno is out of range.
|
||||
//
|
||||
// Hint: Use file_block_walk and alloc_block.
|
||||
int
|
||||
file_get_block(struct File *f, uint32_t filebno, char **blk)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
panic("file_get_block not implemented");
|
||||
}
|
||||
|
||||
// Try to find a file named "name" in dir. If so, set *file to it.
|
||||
//
|
||||
// Returns 0 and sets *file on success, < 0 on error. Errors are:
|
||||
// -E_NOT_FOUND if the file is not found
|
||||
static int
|
||||
dir_lookup(struct File *dir, const char *name, struct File **file)
|
||||
{
|
||||
int r;
|
||||
uint32_t i, j, nblock;
|
||||
char *blk;
|
||||
struct File *f;
|
||||
|
||||
// Search dir for name.
|
||||
// We maintain the invariant that the size of a directory-file
|
||||
// is always a multiple of the file system's block size.
|
||||
assert((dir->f_size % BLKSIZE) == 0);
|
||||
nblock = dir->f_size / BLKSIZE;
|
||||
for (i = 0; i < nblock; i++) {
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
for (j = 0; j < BLKFILES; j++)
|
||||
if (strcmp(f[j].f_name, name) == 0) {
|
||||
*file = &f[j];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -E_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Set *file to point at a free File structure in dir. The caller is
|
||||
// responsible for filling in the File fields.
|
||||
static int
|
||||
dir_alloc_file(struct File *dir, struct File **file)
|
||||
{
|
||||
int r;
|
||||
uint32_t nblock, i, j;
|
||||
char *blk;
|
||||
struct File *f;
|
||||
|
||||
assert((dir->f_size % BLKSIZE) == 0);
|
||||
nblock = dir->f_size / BLKSIZE;
|
||||
for (i = 0; i < nblock; i++) {
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
for (j = 0; j < BLKFILES; j++)
|
||||
if (f[j].f_name[0] == '\0') {
|
||||
*file = &f[j];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
dir->f_size += BLKSIZE;
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
*file = &f[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Skip over slashes.
|
||||
static const char*
|
||||
skip_slash(const char *p)
|
||||
{
|
||||
while (*p == '/')
|
||||
p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Evaluate a path name, starting at the root.
|
||||
// On success, set *pf to the file we found
|
||||
// and set *pdir to the directory the file is in.
|
||||
// If we cannot find the file but find the directory
|
||||
// it should be in, set *pdir and copy the final path
|
||||
// element into lastelem.
|
||||
static int
|
||||
walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)
|
||||
{
|
||||
const char *p;
|
||||
char name[MAXNAMELEN];
|
||||
struct File *dir, *f;
|
||||
int r;
|
||||
|
||||
// if (*path != '/')
|
||||
// return -E_BAD_PATH;
|
||||
path = skip_slash(path);
|
||||
f = &super->s_root;
|
||||
dir = 0;
|
||||
name[0] = 0;
|
||||
|
||||
if (pdir)
|
||||
*pdir = 0;
|
||||
*pf = 0;
|
||||
while (*path != '\0') {
|
||||
dir = f;
|
||||
p = path;
|
||||
while (*path != '/' && *path != '\0')
|
||||
path++;
|
||||
if (path - p >= MAXNAMELEN)
|
||||
return -E_BAD_PATH;
|
||||
memmove(name, p, path - p);
|
||||
name[path - p] = '\0';
|
||||
path = skip_slash(path);
|
||||
|
||||
if (dir->f_type != FTYPE_DIR)
|
||||
return -E_NOT_FOUND;
|
||||
|
||||
if ((r = dir_lookup(dir, name, &f)) < 0) {
|
||||
if (r == -E_NOT_FOUND && *path == '\0') {
|
||||
if (pdir)
|
||||
*pdir = dir;
|
||||
if (lastelem)
|
||||
strcpy(lastelem, name);
|
||||
*pf = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdir)
|
||||
*pdir = dir;
|
||||
*pf = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File operations
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Create "path". On success set *pf to point at the file and return 0.
|
||||
// On error return < 0.
|
||||
int
|
||||
file_create(const char *path, struct File **pf)
|
||||
{
|
||||
char name[MAXNAMELEN];
|
||||
int r;
|
||||
struct File *dir, *f;
|
||||
|
||||
if ((r = walk_path(path, &dir, &f, name)) == 0)
|
||||
return -E_FILE_EXISTS;
|
||||
if (r != -E_NOT_FOUND || dir == 0)
|
||||
return r;
|
||||
if ((r = dir_alloc_file(dir, &f)) < 0)
|
||||
return r;
|
||||
|
||||
strcpy(f->f_name, name);
|
||||
*pf = f;
|
||||
file_flush(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Open "path". On success set *pf to point at the file and return 0.
|
||||
// On error return < 0.
|
||||
int
|
||||
file_open(const char *path, struct File **pf)
|
||||
{
|
||||
return walk_path(path, 0, pf, 0);
|
||||
}
|
||||
|
||||
// Read count bytes from f into buf, starting from seek position
|
||||
// offset. This meant to mimic the standard pread function.
|
||||
// Returns the number of bytes read, < 0 on error.
|
||||
ssize_t
|
||||
file_read(struct File *f, void *buf, size_t count, off_t offset)
|
||||
{
|
||||
int r, bn;
|
||||
off_t pos;
|
||||
char *blk;
|
||||
|
||||
if (offset >= f->f_size)
|
||||
return 0;
|
||||
|
||||
count = MIN(count, f->f_size - offset);
|
||||
|
||||
for (pos = offset; pos < offset + count; ) {
|
||||
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
|
||||
return r;
|
||||
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
|
||||
memmove(buf, blk + pos % BLKSIZE, bn);
|
||||
pos += bn;
|
||||
buf += bn;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// Write count bytes from buf into f, starting at seek position
|
||||
// offset. This is meant to mimic the standard pwrite function.
|
||||
// Extends the file if necessary.
|
||||
// Returns the number of bytes written, < 0 on error.
|
||||
int
|
||||
file_write(struct File *f, const void *buf, size_t count, off_t offset)
|
||||
{
|
||||
int r, bn;
|
||||
off_t pos;
|
||||
char *blk;
|
||||
|
||||
// Extend file if necessary
|
||||
if (offset + count > f->f_size)
|
||||
if ((r = file_set_size(f, offset + count)) < 0)
|
||||
return r;
|
||||
|
||||
for (pos = offset; pos < offset + count; ) {
|
||||
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
|
||||
return r;
|
||||
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
|
||||
memmove(blk + pos % BLKSIZE, buf, bn);
|
||||
pos += bn;
|
||||
buf += bn;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Remove a block from file f. If it's not there, just silently succeed.
|
||||
// Returns 0 on success, < 0 on error.
|
||||
static int
|
||||
file_free_block(struct File *f, uint32_t filebno)
|
||||
{
|
||||
int r;
|
||||
uint32_t *ptr;
|
||||
|
||||
if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0)
|
||||
return r;
|
||||
if (*ptr) {
|
||||
free_block(*ptr);
|
||||
*ptr = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove any blocks currently used by file 'f',
|
||||
// but not necessary for a file of size 'newsize'.
|
||||
// For both the old and new sizes, figure out the number of blocks required,
|
||||
// and then clear the blocks from new_nblocks to old_nblocks.
|
||||
// If the new_nblocks is no more than NDIRECT, and the indirect block has
|
||||
// been allocated (f->f_indirect != 0), then free the indirect block too.
|
||||
// (Remember to clear the f->f_indirect pointer so you'll know
|
||||
// whether it's valid!)
|
||||
// Do not change f->f_size.
|
||||
static void
|
||||
file_truncate_blocks(struct File *f, off_t newsize)
|
||||
{
|
||||
int r;
|
||||
uint32_t bno, old_nblocks, new_nblocks;
|
||||
|
||||
old_nblocks = (f->f_size + BLKSIZE - 1) / BLKSIZE;
|
||||
new_nblocks = (newsize + BLKSIZE - 1) / BLKSIZE;
|
||||
for (bno = new_nblocks; bno < old_nblocks; bno++)
|
||||
if ((r = file_free_block(f, bno)) < 0)
|
||||
cprintf("warning: file_free_block: %e", r);
|
||||
|
||||
if (new_nblocks <= NDIRECT && f->f_indirect) {
|
||||
free_block(f->f_indirect);
|
||||
f->f_indirect = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the size of file f, truncating or extending as necessary.
|
||||
int
|
||||
file_set_size(struct File *f, off_t newsize)
|
||||
{
|
||||
if (f->f_size > newsize)
|
||||
file_truncate_blocks(f, newsize);
|
||||
f->f_size = newsize;
|
||||
flush_block(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flush the contents and metadata of file f out to disk.
|
||||
// Loop over all the blocks in file.
|
||||
// Translate the file block number into a disk block number
|
||||
// and then check whether that disk block is dirty. If so, write it out.
|
||||
void
|
||||
file_flush(struct File *f)
|
||||
{
|
||||
int i;
|
||||
uint32_t *pdiskbno;
|
||||
|
||||
for (i = 0; i < (f->f_size + BLKSIZE - 1) / BLKSIZE; i++) {
|
||||
if (file_block_walk(f, i, &pdiskbno, 0) < 0 ||
|
||||
pdiskbno == NULL || *pdiskbno == 0)
|
||||
continue;
|
||||
flush_block(diskaddr(*pdiskbno));
|
||||
}
|
||||
flush_block(f);
|
||||
if (f->f_indirect)
|
||||
flush_block(diskaddr(f->f_indirect));
|
||||
}
|
||||
|
||||
|
||||
// Sync the entire file system. A big hammer.
|
||||
void
|
||||
fs_sync(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < super->s_nblocks; i++)
|
||||
flush_block(diskaddr(i));
|
||||
}
|
||||
|
||||
515
lab/Untitled Project.si4project/Backup/fs(7516).c
Normal file
515
lab/Untitled Project.si4project/Backup/fs(7516).c
Normal file
@@ -0,0 +1,515 @@
|
||||
#include <inc/string.h>
|
||||
#include <inc/partition.h>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Super block
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Validate the file system super-block.
|
||||
void
|
||||
check_super(void)
|
||||
{
|
||||
if (super->s_magic != FS_MAGIC)
|
||||
panic("bad file system magic number");
|
||||
|
||||
if (super->s_nblocks > DISKSIZE/BLKSIZE)
|
||||
panic("file system is too large");
|
||||
|
||||
cprintf("superblock is good\n");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Free block bitmap
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Check to see if the block bitmap indicates that block 'blockno' is free.
|
||||
// Return 1 if the block is free, 0 if not.
|
||||
bool
|
||||
block_is_free(uint32_t blockno)
|
||||
{
|
||||
if (super == 0 || blockno >= super->s_nblocks)
|
||||
return 0;
|
||||
if (bitmap[blockno / 32] & (1 << (blockno % 32)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mark a block free in the bitmap
|
||||
void
|
||||
free_block(uint32_t blockno)
|
||||
{
|
||||
// Blockno zero is the null pointer of block numbers.
|
||||
// 0 块启动块
|
||||
if (blockno == 0)
|
||||
panic("attempt to free zero block");
|
||||
bitmap[blockno/32] |= 1<<(blockno%32);
|
||||
}
|
||||
|
||||
// Search the bitmap for a free block and allocate it. When you
|
||||
// allocate a block, immediately flush the changed bitmap block
|
||||
// to disk.
|
||||
//
|
||||
// Return block number allocated on success,
|
||||
// -E_NO_DISK if we are out of blocks.
|
||||
//
|
||||
// Hint: use free_block as an example for manipulating the bitmap.
|
||||
int
|
||||
alloc_block(void)
|
||||
{
|
||||
// The bitmap consists of one or more blocks. A single bitmap block
|
||||
// contains the in-use bits for BLKBITSIZE blocks. There are
|
||||
// super->s_nblocks blocks in the disk altogether.
|
||||
|
||||
// LAB 5: Your code here.
|
||||
size_t i;
|
||||
for(i=1; i < super->s_nblocks; i++) {
|
||||
if (block_is_free(i)) {
|
||||
bitmap[i/32] &= ~(1<<(i%32));
|
||||
flush_block(&bitmap[i/32]);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// panic("alloc_block not implemented");
|
||||
return -E_NO_DISK;
|
||||
}
|
||||
|
||||
// Validate the file system bitmap.
|
||||
//
|
||||
// Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves --
|
||||
// are all marked as in-use.
|
||||
void
|
||||
check_bitmap(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
// Make sure all bitmap blocks are marked in-use
|
||||
for (i = 0; i * BLKBITSIZE < super->s_nblocks; i++)
|
||||
assert(!block_is_free(2+i));
|
||||
|
||||
// Make sure the reserved and root blocks are marked in-use.
|
||||
assert(!block_is_free(0));
|
||||
assert(!block_is_free(1));
|
||||
|
||||
cprintf("bitmap is good\n");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File system structures
|
||||
// --------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
// Initialize the file system
|
||||
void
|
||||
fs_init(void)
|
||||
{
|
||||
static_assert(sizeof(struct File) == 256);
|
||||
|
||||
// Find a JOS disk. Use the second IDE disk (number 1) if available
|
||||
if (ide_probe_disk1())
|
||||
ide_set_disk(1);
|
||||
else
|
||||
ide_set_disk(0);
|
||||
bc_init();
|
||||
|
||||
// Set "super" to point to the super block.
|
||||
super = diskaddr(1);
|
||||
check_super();
|
||||
|
||||
// Set "bitmap" to the beginning of the first bitmap block.
|
||||
bitmap = diskaddr(2);
|
||||
check_bitmap();
|
||||
|
||||
}
|
||||
|
||||
// Find the disk block number slot for the 'filebno'th block in file 'f'.
|
||||
// Set '*ppdiskbno' to point to that slot.
|
||||
// The slot will be one of the f->f_direct[] entries,
|
||||
// or an entry in the indirect block.
|
||||
// When 'alloc' is set, this function will allocate an indirect block
|
||||
// if necessary.
|
||||
//
|
||||
// Returns:
|
||||
// 0 on success (but note that *ppdiskbno might equal 0).
|
||||
// -E_NOT_FOUND if the function needed to allocate an indirect block, but
|
||||
// alloc was 0.
|
||||
// -E_NO_DISK if there's no space on the disk for an indirect block.
|
||||
// -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT).
|
||||
//
|
||||
// Analogy: This is like pgdir_walk for files.
|
||||
// Hint: Don't forget to clear any block you allocate.
|
||||
static int
|
||||
file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
// ppdiskbno 块指针
|
||||
if (filebno < NDIRECT) {
|
||||
// but note that *ppdiskbno might equal 0
|
||||
if(ppdiskbno)
|
||||
*ppdiskbno = &(f->f_direct[filebno]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filebno >= (NDIRECT + NINDIRECT))
|
||||
return -E_INVAL;
|
||||
|
||||
filebno -= NDIRECT;
|
||||
// indirect 还未分配
|
||||
if (!f->f_indirect) {
|
||||
if (alloc == 0)
|
||||
return -E_NOT_FOUND;
|
||||
// 分配一个 indirect block
|
||||
uint32_t blockno;
|
||||
if ( (blockno = alloc_block()) < 0)
|
||||
return blockno;
|
||||
// f_indirect 直接记录块号,而不是记地址
|
||||
// f->f_indirect = (uint32_t)diskaddr(blockno);
|
||||
f->f_indirect = blockno;
|
||||
memset(diskaddr(blockno), 0, BLKSIZE);
|
||||
flush_block(diskaddr(blockno));
|
||||
}
|
||||
if (ppdiskbno)
|
||||
*ppdiskbno = (uint32_t *)diskaddr(f->f_indirect) + filebno;
|
||||
return 0;
|
||||
// panic("file_block_walk not implemented");
|
||||
}
|
||||
|
||||
// Set *blk to the address in memory where the filebno'th
|
||||
// block of file 'f' would be mapped.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_NO_DISK if a block needed to be allocated but the disk is full.
|
||||
// -E_INVAL if filebno is out of range.
|
||||
//
|
||||
// Hint: Use file_block_walk and alloc_block.
|
||||
int
|
||||
file_get_block(struct File *f, uint32_t filebno, char **blk)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
uint32_t *pdiskbno;
|
||||
int r;
|
||||
if ( (r = file_block_walk(f, filebno, &pdiskbno, 1))< 0)
|
||||
return r;
|
||||
|
||||
if(*pdiskbno == 0) {
|
||||
// 文件块还未分配
|
||||
if ( (r = alloc_block()) < 0)
|
||||
return r;
|
||||
*pdiskbno = r;
|
||||
memset(diskaddr(r), 0, BLKSIZE);
|
||||
flush_block(diskaddr(r));
|
||||
}
|
||||
|
||||
// 最终指向块
|
||||
*blk = diskaddr(*pdiskbno);
|
||||
return 0;
|
||||
//panic("file_get_block not implemented");
|
||||
}
|
||||
|
||||
// Try to find a file named "name" in dir. If so, set *file to it.
|
||||
//
|
||||
// Returns 0 and sets *file on success, < 0 on error. Errors are:
|
||||
// -E_NOT_FOUND if the file is not found
|
||||
static int
|
||||
dir_lookup(struct File *dir, const char *name, struct File **file)
|
||||
{
|
||||
int r;
|
||||
uint32_t i, j, nblock;
|
||||
char *blk;
|
||||
struct File *f;
|
||||
|
||||
// Search dir for name.
|
||||
// We maintain the invariant that the size of a directory-file
|
||||
// is always a multiple of the file system's block size.
|
||||
// 目录size 必须为 文件系统块size的倍数。
|
||||
assert((dir->f_size % BLKSIZE) == 0);
|
||||
nblock = dir->f_size / BLKSIZE;
|
||||
for (i = 0; i < nblock; i++) {
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
for (j = 0; j < BLKFILES; j++)
|
||||
// 不会出现子目录与文件同名吗?
|
||||
if (strcmp(f[j].f_name, name) == 0) {
|
||||
*file = &f[j];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -E_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Set *file to point at a free File structure in dir. The caller is
|
||||
// responsible for filling in the File fields.
|
||||
static int
|
||||
dir_alloc_file(struct File *dir, struct File **file)
|
||||
{
|
||||
int r;
|
||||
uint32_t nblock, i, j;
|
||||
char *blk;
|
||||
struct File *f;
|
||||
|
||||
assert((dir->f_size % BLKSIZE) == 0);
|
||||
nblock = dir->f_size / BLKSIZE;
|
||||
for (i = 0; i < nblock; i++) {
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
for (j = 0; j < BLKFILES; j++)
|
||||
if (f[j].f_name[0] == '\0') {
|
||||
*file = &f[j];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// 目录里没有空项,增添一个块
|
||||
dir->f_size += BLKSIZE;
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
*file = &f[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Skip over slashes.
|
||||
static const char*
|
||||
skip_slash(const char *p)
|
||||
{
|
||||
while (*p == '/')
|
||||
p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Evaluate a path name, starting at the root.
|
||||
// On success, set *pf to the file we found
|
||||
// and set *pdir to the directory the file is in.
|
||||
// If we cannot find the file but find the directory
|
||||
// it should be in, set *pdir and copy the final path
|
||||
// element into lastelem.
|
||||
static int
|
||||
walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)
|
||||
{
|
||||
const char *p;
|
||||
char name[MAXNAMELEN];
|
||||
struct File *dir, *f;
|
||||
int r;
|
||||
|
||||
// if (*path != '/')
|
||||
// return -E_BAD_PATH;
|
||||
path = skip_slash(path);
|
||||
f = &super->s_root;
|
||||
dir = 0;
|
||||
name[0] = 0;
|
||||
|
||||
if (pdir)
|
||||
*pdir = 0;
|
||||
*pf = 0;
|
||||
while (*path != '\0') {
|
||||
dir = f;
|
||||
p = path;
|
||||
while (*path != '/' && *path != '\0')
|
||||
path++;
|
||||
if (path - p >= MAXNAMELEN)
|
||||
return -E_BAD_PATH;
|
||||
memmove(name, p, path - p);
|
||||
name[path - p] = '\0';
|
||||
path = skip_slash(path);
|
||||
|
||||
if (dir->f_type != FTYPE_DIR)
|
||||
return -E_NOT_FOUND;
|
||||
|
||||
if ((r = dir_lookup(dir, name, &f)) < 0) {
|
||||
if (r == -E_NOT_FOUND && *path == '\0') {
|
||||
if (pdir)
|
||||
*pdir = dir;
|
||||
if (lastelem)
|
||||
strcpy(lastelem, name);
|
||||
*pf = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdir)
|
||||
*pdir = dir;
|
||||
*pf = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File operations
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Create "path". On success set *pf to point at the file and return 0.
|
||||
// On error return < 0.
|
||||
int
|
||||
file_create(const char *path, struct File **pf)
|
||||
{
|
||||
char name[MAXNAMELEN];
|
||||
int r;
|
||||
struct File *dir, *f;
|
||||
|
||||
if ((r = walk_path(path, &dir, &f, name)) == 0)
|
||||
return -E_FILE_EXISTS;
|
||||
if (r != -E_NOT_FOUND || dir == 0)
|
||||
return r;
|
||||
if ((r = dir_alloc_file(dir, &f)) < 0)
|
||||
return r;
|
||||
|
||||
strcpy(f->f_name, name);
|
||||
*pf = f;
|
||||
file_flush(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Open "path". On success set *pf to point at the file and return 0.
|
||||
// On error return < 0.
|
||||
int
|
||||
file_open(const char *path, struct File **pf)
|
||||
{
|
||||
return walk_path(path, 0, pf, 0);
|
||||
}
|
||||
|
||||
// Read count bytes from f into buf, starting from seek position
|
||||
// offset. This meant to mimic the standard pread function.
|
||||
// Returns the number of bytes read, < 0 on error.
|
||||
ssize_t
|
||||
file_read(struct File *f, void *buf, size_t count, off_t offset)
|
||||
{
|
||||
int r, bn;
|
||||
off_t pos;
|
||||
char *blk;
|
||||
|
||||
if (offset >= f->f_size)
|
||||
return 0;
|
||||
|
||||
count = MIN(count, f->f_size - offset);
|
||||
|
||||
for (pos = offset; pos < offset + count; ) {
|
||||
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
|
||||
return r;
|
||||
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
|
||||
memmove(buf, blk + pos % BLKSIZE, bn);
|
||||
pos += bn;
|
||||
buf += bn;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// Write count bytes from buf into f, starting at seek position
|
||||
// offset. This is meant to mimic the standard pwrite function.
|
||||
// Extends the file if necessary.
|
||||
// Returns the number of bytes written, < 0 on error.
|
||||
int
|
||||
file_write(struct File *f, const void *buf, size_t count, off_t offset)
|
||||
{
|
||||
int r, bn;
|
||||
off_t pos;
|
||||
char *blk;
|
||||
|
||||
// Extend file if necessary
|
||||
if (offset + count > f->f_size)
|
||||
if ((r = file_set_size(f, offset + count)) < 0)
|
||||
return r;
|
||||
|
||||
for (pos = offset; pos < offset + count; ) {
|
||||
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
|
||||
return r;
|
||||
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
|
||||
memmove(blk + pos % BLKSIZE, buf, bn);
|
||||
pos += bn;
|
||||
buf += bn;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Remove a block from file f. If it's not there, just silently succeed.
|
||||
// Returns 0 on success, < 0 on error.
|
||||
static int
|
||||
file_free_block(struct File *f, uint32_t filebno)
|
||||
{
|
||||
int r;
|
||||
uint32_t *ptr;
|
||||
|
||||
if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0)
|
||||
return r;
|
||||
if (*ptr) {
|
||||
free_block(*ptr);
|
||||
*ptr = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove any blocks currently used by file 'f',
|
||||
// but not necessary for a file of size 'newsize'.
|
||||
// For both the old and new sizes, figure out the number of blocks required,
|
||||
// and then clear the blocks from new_nblocks to old_nblocks.
|
||||
// If the new_nblocks is no more than NDIRECT, and the indirect block has
|
||||
// been allocated (f->f_indirect != 0), then free the indirect block too.
|
||||
// (Remember to clear the f->f_indirect pointer so you'll know
|
||||
// whether it's valid!)
|
||||
// Do not change f->f_size.
|
||||
static void
|
||||
file_truncate_blocks(struct File *f, off_t newsize)
|
||||
{
|
||||
int r;
|
||||
uint32_t bno, old_nblocks, new_nblocks;
|
||||
|
||||
old_nblocks = (f->f_size + BLKSIZE - 1) / BLKSIZE;
|
||||
new_nblocks = (newsize + BLKSIZE - 1) / BLKSIZE;
|
||||
for (bno = new_nblocks; bno < old_nblocks; bno++)
|
||||
if ((r = file_free_block(f, bno)) < 0)
|
||||
cprintf("warning: file_free_block: %e", r);
|
||||
|
||||
if (new_nblocks <= NDIRECT && f->f_indirect) {
|
||||
free_block(f->f_indirect);
|
||||
f->f_indirect = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the size of file f, truncating or extending as necessary.
|
||||
int
|
||||
file_set_size(struct File *f, off_t newsize)
|
||||
{
|
||||
if (f->f_size > newsize)
|
||||
file_truncate_blocks(f, newsize);
|
||||
f->f_size = newsize;
|
||||
flush_block(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flush the contents and metadata of file f out to disk.
|
||||
// Loop over all the blocks in file.
|
||||
// Translate the file block number into a disk block number
|
||||
// and then check whether that disk block is dirty. If so, write it out.
|
||||
void
|
||||
file_flush(struct File *f)
|
||||
{
|
||||
int i;
|
||||
uint32_t *pdiskbno;
|
||||
|
||||
for (i = 0; i < (f->f_size + BLKSIZE - 1) / BLKSIZE; i++) {
|
||||
if (file_block_walk(f, i, &pdiskbno, 0) < 0 ||
|
||||
pdiskbno == NULL || *pdiskbno == 0)
|
||||
continue;
|
||||
flush_block(diskaddr(*pdiskbno));
|
||||
}
|
||||
flush_block(f);
|
||||
if (f->f_indirect)
|
||||
flush_block(diskaddr(f->f_indirect));
|
||||
}
|
||||
|
||||
|
||||
// Sync the entire file system. A big hammer.
|
||||
void
|
||||
fs_sync(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < super->s_nblocks; i++)
|
||||
flush_block(diskaddr(i));
|
||||
}
|
||||
|
||||
180
lab/Untitled Project.si4project/Backup/init(5052).c
Normal file
180
lab/Untitled Project.si4project/Backup/init(5052).c
Normal file
@@ -0,0 +1,180 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
static void boot_aps(void);
|
||||
|
||||
|
||||
void
|
||||
i386_init(void)
|
||||
{
|
||||
// Initialize the console.
|
||||
// Can't call cprintf until after we do this!
|
||||
cons_init();
|
||||
|
||||
cprintf("6828 decimal is %o octal!\n", 6828);
|
||||
|
||||
// Lab 2 memory management initialization functions
|
||||
mem_init();
|
||||
|
||||
// Lab 3 user environment initialization functions
|
||||
env_init();
|
||||
trap_init();
|
||||
|
||||
// Lab 4 multiprocessor initialization functions
|
||||
mp_init();
|
||||
lapic_init();
|
||||
|
||||
// Lab 4 multitasking initialization functions
|
||||
pic_init();
|
||||
|
||||
// Acquire the big kernel lock before waking up APs
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
// Starting non-boot CPUs
|
||||
boot_aps();
|
||||
|
||||
// Start fs.
|
||||
ENV_CREATE(fs_fs, ENV_TYPE_FS);
|
||||
|
||||
#if defined(TEST)
|
||||
// Don't touch -- used by grading script!
|
||||
ENV_CREATE(TEST, ENV_TYPE_USER);
|
||||
#else
|
||||
// Touch all you want.
|
||||
<<<<<<< HEAD
|
||||
ENV_CREATE(user_icode, ENV_TYPE_USER);
|
||||
=======
|
||||
//ENV_CREATE(user_primes, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
|
||||
|
||||
>>>>>>> lab4
|
||||
#endif // TEST*
|
||||
|
||||
// Should not be necessary - drains keyboard because interrupt has given up.
|
||||
kbd_intr();
|
||||
|
||||
// Schedule and run the first user environment!
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// While boot_aps is booting a given CPU, it communicates the per-core
|
||||
// stack pointer that should be loaded by mpentry.S to that CPU in
|
||||
// this variable.
|
||||
void *mpentry_kstack;
|
||||
|
||||
// Start the non-boot (AP) processors.
|
||||
static void
|
||||
boot_aps(void)
|
||||
{
|
||||
extern unsigned char mpentry_start[], mpentry_end[];
|
||||
void *code;
|
||||
struct CpuInfo *c;
|
||||
|
||||
// Write entry code to unused memory at MPENTRY_PADDR
|
||||
code = KADDR(MPENTRY_PADDR);
|
||||
memmove(code, mpentry_start, mpentry_end - mpentry_start);
|
||||
|
||||
// Boot each AP one at a time
|
||||
for (c = cpus; c < cpus + ncpu; c++) {
|
||||
if (c == cpus + cpunum()) // We've started already.
|
||||
continue;
|
||||
|
||||
// Tell mpentry.S what stack to use
|
||||
mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE;
|
||||
// Start the CPU at mpentry_start
|
||||
lapic_startap(c->cpu_id, PADDR(code));
|
||||
// Wait for the CPU to finish some basic setup in mp_main()
|
||||
while(c->cpu_status != CPU_STARTED)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup code for APs
|
||||
void
|
||||
mp_main(void)
|
||||
{
|
||||
// We are in high EIP now, safe to switch to kern_pgdir
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
cprintf("SMP: CPU %d starting\n", cpunum());
|
||||
|
||||
lapic_init();
|
||||
env_init_percpu();
|
||||
trap_init_percpu();
|
||||
xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up
|
||||
|
||||
// Now that we have finished some basic setup, call sched_yield()
|
||||
// to start running processes on this CPU. But make sure that
|
||||
// only one CPU can enter the scheduler at a time!
|
||||
//
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
|
||||
sched_yield();
|
||||
// Remove this after you finish Exercise 6
|
||||
for (;;);
|
||||
}
|
||||
|
||||
/*
|
||||
* Variable panicstr contains argument to first call to panic; used as flag
|
||||
* to indicate that the kernel has already called panic.
|
||||
*/
|
||||
const char *panicstr;
|
||||
|
||||
/*
|
||||
* Panic is called on unresolvable fatal errors.
|
||||
* It prints "panic: mesg", and then enters the kernel monitor.
|
||||
*/
|
||||
void
|
||||
_panic(const char *file, int line, const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (panicstr)
|
||||
goto dead;
|
||||
panicstr = fmt;
|
||||
|
||||
// Be extra sure that the machine is in as reasonable state
|
||||
asm volatile("cli; cld");
|
||||
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
|
||||
dead:
|
||||
/* break into the kernel monitor */
|
||||
while (1)
|
||||
monitor(NULL);
|
||||
}
|
||||
|
||||
/* like panic, but don't */
|
||||
void
|
||||
_warn(const char *file, int line, const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel warning at %s:%d: ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
}
|
||||
174
lab/Untitled Project.si4project/Backup/init(7465).c
Normal file
174
lab/Untitled Project.si4project/Backup/init(7465).c
Normal file
@@ -0,0 +1,174 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
static void boot_aps(void);
|
||||
|
||||
|
||||
void
|
||||
i386_init(void)
|
||||
{
|
||||
// Initialize the console.
|
||||
// Can't call cprintf until after we do this!
|
||||
cons_init();
|
||||
|
||||
cprintf("6828 decimal is %o octal!\n", 6828);
|
||||
|
||||
// Lab 2 memory management initialization functions
|
||||
mem_init();
|
||||
|
||||
// Lab 3 user environment initialization functions
|
||||
env_init();
|
||||
trap_init();
|
||||
|
||||
// Lab 4 multiprocessor initialization functions
|
||||
mp_init();
|
||||
lapic_init();
|
||||
|
||||
// Lab 4 multitasking initialization functions
|
||||
pic_init();
|
||||
|
||||
// Acquire the big kernel lock before waking up APs
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
// Starting non-boot CPUs
|
||||
boot_aps();
|
||||
|
||||
// Start fs.
|
||||
ENV_CREATE(fs_fs, ENV_TYPE_FS);
|
||||
|
||||
#if defined(TEST)
|
||||
// Don't touch -- used by grading script!
|
||||
ENV_CREATE(TEST, ENV_TYPE_USER);
|
||||
#else
|
||||
// Touch all you want.
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
|
||||
#endif // TEST*
|
||||
|
||||
// Should not be necessary - drains keyboard because interrupt has given up.
|
||||
kbd_intr();
|
||||
|
||||
// Schedule and run the first user environment!
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// While boot_aps is booting a given CPU, it communicates the per-core
|
||||
// stack pointer that should be loaded by mpentry.S to that CPU in
|
||||
// this variable.
|
||||
void *mpentry_kstack;
|
||||
|
||||
// Start the non-boot (AP) processors.
|
||||
static void
|
||||
boot_aps(void)
|
||||
{
|
||||
extern unsigned char mpentry_start[], mpentry_end[];
|
||||
void *code;
|
||||
struct CpuInfo *c;
|
||||
|
||||
// Write entry code to unused memory at MPENTRY_PADDR
|
||||
code = KADDR(MPENTRY_PADDR);
|
||||
memmove(code, mpentry_start, mpentry_end - mpentry_start);
|
||||
|
||||
// Boot each AP one at a time
|
||||
for (c = cpus; c < cpus + ncpu; c++) {
|
||||
if (c == cpus + cpunum()) // We've started already.
|
||||
continue;
|
||||
|
||||
// Tell mpentry.S what stack to use
|
||||
mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE;
|
||||
// Start the CPU at mpentry_start
|
||||
lapic_startap(c->cpu_id, PADDR(code));
|
||||
// Wait for the CPU to finish some basic setup in mp_main()
|
||||
while(c->cpu_status != CPU_STARTED)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup code for APs
|
||||
void
|
||||
mp_main(void)
|
||||
{
|
||||
// We are in high EIP now, safe to switch to kern_pgdir
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
cprintf("SMP: CPU %d starting\n", cpunum());
|
||||
|
||||
lapic_init();
|
||||
env_init_percpu();
|
||||
trap_init_percpu();
|
||||
xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up
|
||||
|
||||
// Now that we have finished some basic setup, call sched_yield()
|
||||
// to start running processes on this CPU. But make sure that
|
||||
// only one CPU can enter the scheduler at a time!
|
||||
//
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
|
||||
sched_yield();
|
||||
// Remove this after you finish Exercise 6
|
||||
for (;;);
|
||||
}
|
||||
|
||||
/*
|
||||
* Variable panicstr contains argument to first call to panic; used as flag
|
||||
* to indicate that the kernel has already called panic.
|
||||
*/
|
||||
const char *panicstr;
|
||||
|
||||
/*
|
||||
* Panic is called on unresolvable fatal errors.
|
||||
* It prints "panic: mesg", and then enters the kernel monitor.
|
||||
*/
|
||||
void
|
||||
_panic(const char *file, int line, const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (panicstr)
|
||||
goto dead;
|
||||
panicstr = fmt;
|
||||
|
||||
// Be extra sure that the machine is in as reasonable state
|
||||
asm volatile("cli; cld");
|
||||
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
|
||||
dead:
|
||||
/* break into the kernel monitor */
|
||||
while (1)
|
||||
monitor(NULL);
|
||||
}
|
||||
|
||||
/* like panic, but don't */
|
||||
void
|
||||
_warn(const char *file, int line, const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel warning at %s:%d: ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
}
|
||||
345
lab/Untitled Project.si4project/Backup/serv(2429).c
Normal file
345
lab/Untitled Project.si4project/Backup/serv(2429).c
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* File system server main loop -
|
||||
* serves IPC requests from other environments.
|
||||
*/
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/string.h>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
|
||||
#define debug 0
|
||||
|
||||
// The file system server maintains three structures
|
||||
// for each open file.
|
||||
//
|
||||
// 1. The on-disk 'struct File' is mapped into the part of memory
|
||||
// that maps the disk. This memory is kept private to the file
|
||||
// server.
|
||||
// 2. Each open file has a 'struct Fd' as well, which sort of
|
||||
// corresponds to a Unix file descriptor. This 'struct Fd' is kept
|
||||
// on *its own page* in memory, and it is shared with any
|
||||
// environments that have the file open.
|
||||
// 3. 'struct OpenFile' links these other two structures, and is kept
|
||||
// private to the file server. The server maintains an array of
|
||||
// all open files, indexed by "file ID". (There can be at most
|
||||
// MAXOPEN files open concurrently.) The client uses file IDs to
|
||||
// communicate with the server. File IDs are a lot like
|
||||
// environment IDs in the kernel. Use openfile_lookup to translate
|
||||
// file IDs to struct OpenFile.
|
||||
|
||||
struct OpenFile {
|
||||
uint32_t o_fileid; // file id
|
||||
struct File *o_file; // mapped descriptor for open file
|
||||
int o_mode; // open mode
|
||||
struct Fd *o_fd; // Fd page
|
||||
};
|
||||
|
||||
// Max number of open files in the file system at once
|
||||
#define MAXOPEN 1024
|
||||
#define FILEVA 0xD0000000
|
||||
|
||||
// initialize to force into data section
|
||||
struct OpenFile opentab[MAXOPEN] = {
|
||||
{ 0, 0, 1, 0 }
|
||||
};
|
||||
|
||||
// Virtual address at which to receive page mappings containing client requests.
|
||||
union Fsipc *fsreq = (union Fsipc *)0x0ffff000;
|
||||
|
||||
void
|
||||
serve_init(void)
|
||||
{
|
||||
int i;
|
||||
uintptr_t va = FILEVA;
|
||||
for (i = 0; i < MAXOPEN; i++) {
|
||||
opentab[i].o_fileid = i;
|
||||
opentab[i].o_fd = (struct Fd*) va;
|
||||
va += PGSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate an open file.
|
||||
int
|
||||
openfile_alloc(struct OpenFile **o)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
// Find an available open-file table entry
|
||||
for (i = 0; i < MAXOPEN; i++) {
|
||||
switch (pageref(opentab[i].o_fd)) {
|
||||
case 0:
|
||||
if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
return r;
|
||||
/* fall through */
|
||||
case 1:
|
||||
opentab[i].o_fileid += MAXOPEN;
|
||||
*o = &opentab[i];
|
||||
memset(opentab[i].o_fd, 0, PGSIZE);
|
||||
return (*o)->o_fileid;
|
||||
}
|
||||
}
|
||||
return -E_MAX_OPEN;
|
||||
}
|
||||
|
||||
// Look up an open file for envid.
|
||||
int
|
||||
openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po)
|
||||
{
|
||||
struct OpenFile *o;
|
||||
|
||||
o = &opentab[fileid % MAXOPEN];
|
||||
if (pageref(o->o_fd) <= 1 || o->o_fileid != fileid)
|
||||
return -E_INVAL;
|
||||
*po = o;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Open req->req_path in mode req->req_omode, storing the Fd page and
|
||||
// permissions to return to the calling environment in *pg_store and
|
||||
// *perm_store respectively.
|
||||
int
|
||||
serve_open(envid_t envid, struct Fsreq_open *req,
|
||||
void **pg_store, int *perm_store)
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
struct File *f;
|
||||
int fileid;
|
||||
int r;
|
||||
struct OpenFile *o;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_open %08x %s 0x%x\n", envid, req->req_path, req->req_omode);
|
||||
|
||||
// Copy in the path, making sure it's null-terminated
|
||||
memmove(path, req->req_path, MAXPATHLEN);
|
||||
path[MAXPATHLEN-1] = 0;
|
||||
|
||||
// Find an open file ID
|
||||
if ((r = openfile_alloc(&o)) < 0) {
|
||||
if (debug)
|
||||
cprintf("openfile_alloc failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
fileid = r;
|
||||
|
||||
// Open the file
|
||||
if (req->req_omode & O_CREAT) {
|
||||
if ((r = file_create(path, &f)) < 0) {
|
||||
if (!(req->req_omode & O_EXCL) && r == -E_FILE_EXISTS)
|
||||
goto try_open;
|
||||
if (debug)
|
||||
cprintf("file_create failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
try_open:
|
||||
if ((r = file_open(path, &f)) < 0) {
|
||||
if (debug)
|
||||
cprintf("file_open failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate
|
||||
if (req->req_omode & O_TRUNC) {
|
||||
if ((r = file_set_size(f, 0)) < 0) {
|
||||
if (debug)
|
||||
cprintf("file_set_size failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if ((r = file_open(path, &f)) < 0) {
|
||||
if (debug)
|
||||
cprintf("file_open failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Save the file pointer
|
||||
o->o_file = f;
|
||||
|
||||
// Fill out the Fd structure
|
||||
o->o_fd->fd_file.id = o->o_fileid;
|
||||
o->o_fd->fd_omode = req->req_omode & O_ACCMODE;
|
||||
o->o_fd->fd_dev_id = devfile.dev_id;
|
||||
o->o_mode = req->req_omode;
|
||||
|
||||
if (debug)
|
||||
cprintf("sending success, page %08x\n", (uintptr_t) o->o_fd);
|
||||
|
||||
// Share the FD page with the caller by setting *pg_store,
|
||||
// store its permission in *perm_store
|
||||
*pg_store = o->o_fd;
|
||||
*perm_store = PTE_P|PTE_U|PTE_W|PTE_SHARE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the size of req->req_fileid to req->req_size bytes, truncating
|
||||
// or extending the file as necessary.
|
||||
int
|
||||
serve_set_size(envid_t envid, struct Fsreq_set_size *req)
|
||||
{
|
||||
struct OpenFile *o;
|
||||
int r;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_set_size %08x %08x %08x\n", envid, req->req_fileid, req->req_size);
|
||||
|
||||
// Every file system IPC call has the same general structure.
|
||||
// Here's how it goes.
|
||||
|
||||
// First, use openfile_lookup to find the relevant open file.
|
||||
// On failure, return the error code to the client with ipc_send.
|
||||
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
|
||||
return r;
|
||||
|
||||
// Second, call the relevant file system function (from fs/fs.c).
|
||||
// On failure, return the error code to the client.
|
||||
return file_set_size(o->o_file, req->req_size);
|
||||
}
|
||||
|
||||
// Read at most ipc->read.req_n bytes from the current seek position
|
||||
// in ipc->read.req_fileid. Return the bytes read from the file to
|
||||
// the caller in ipc->readRet, then update the seek position. Returns
|
||||
// the number of bytes successfully read, or < 0 on error.
|
||||
int
|
||||
serve_read(envid_t envid, union Fsipc *ipc)
|
||||
{
|
||||
struct Fsreq_read *req = &ipc->read;
|
||||
struct Fsret_read *ret = &ipc->readRet;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
|
||||
|
||||
// Lab 5: Your code here:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Write req->req_n bytes from req->req_buf to req_fileid, starting at
|
||||
// the current seek position, and update the seek position
|
||||
// accordingly. Extend the file if necessary. Returns the number of
|
||||
// bytes written, or < 0 on error.
|
||||
int
|
||||
serve_write(envid_t envid, struct Fsreq_write *req)
|
||||
{
|
||||
if (debug)
|
||||
cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("serve_write not implemented");
|
||||
}
|
||||
|
||||
// Stat ipc->stat.req_fileid. Return the file's struct Stat to the
|
||||
// caller in ipc->statRet.
|
||||
int
|
||||
serve_stat(envid_t envid, union Fsipc *ipc)
|
||||
{
|
||||
struct Fsreq_stat *req = &ipc->stat;
|
||||
struct Fsret_stat *ret = &ipc->statRet;
|
||||
struct OpenFile *o;
|
||||
int r;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_stat %08x %08x\n", envid, req->req_fileid);
|
||||
|
||||
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
|
||||
return r;
|
||||
|
||||
strcpy(ret->ret_name, o->o_file->f_name);
|
||||
ret->ret_size = o->o_file->f_size;
|
||||
ret->ret_isdir = (o->o_file->f_type == FTYPE_DIR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flush all data and metadata of req->req_fileid to disk.
|
||||
int
|
||||
serve_flush(envid_t envid, struct Fsreq_flush *req)
|
||||
{
|
||||
struct OpenFile *o;
|
||||
int r;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_flush %08x %08x\n", envid, req->req_fileid);
|
||||
|
||||
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
|
||||
return r;
|
||||
file_flush(o->o_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
serve_sync(envid_t envid, union Fsipc *req)
|
||||
{
|
||||
fs_sync();
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*fshandler)(envid_t envid, union Fsipc *req);
|
||||
|
||||
fshandler handlers[] = {
|
||||
// Open is handled specially because it passes pages
|
||||
/* [FSREQ_OPEN] = (fshandler)serve_open, */
|
||||
[FSREQ_READ] = serve_read,
|
||||
[FSREQ_STAT] = serve_stat,
|
||||
[FSREQ_FLUSH] = (fshandler)serve_flush,
|
||||
[FSREQ_WRITE] = (fshandler)serve_write,
|
||||
[FSREQ_SET_SIZE] = (fshandler)serve_set_size,
|
||||
[FSREQ_SYNC] = serve_sync
|
||||
};
|
||||
|
||||
void
|
||||
serve(void)
|
||||
{
|
||||
uint32_t req, whom;
|
||||
int perm, r;
|
||||
void *pg;
|
||||
|
||||
while (1) {
|
||||
perm = 0;
|
||||
req = ipc_recv((int32_t *) &whom, fsreq, &perm);
|
||||
if (debug)
|
||||
cprintf("fs req %d from %08x [page %08x: %s]\n",
|
||||
req, whom, uvpt[PGNUM(fsreq)], fsreq);
|
||||
|
||||
// All requests must contain an argument page
|
||||
if (!(perm & PTE_P)) {
|
||||
cprintf("Invalid request from %08x: no argument page\n",
|
||||
whom);
|
||||
continue; // just leave it hanging...
|
||||
}
|
||||
|
||||
pg = NULL;
|
||||
if (req == FSREQ_OPEN) {
|
||||
r = serve_open(whom, (struct Fsreq_open*)fsreq, &pg, &perm);
|
||||
} else if (req < ARRAY_SIZE(handlers) && handlers[req]) {
|
||||
r = handlers[req](whom, fsreq);
|
||||
} else {
|
||||
cprintf("Invalid request code %d from %08x\n", req, whom);
|
||||
r = -E_INVAL;
|
||||
}
|
||||
ipc_send(whom, r, pg, perm);
|
||||
sys_page_unmap(0, fsreq);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
static_assert(sizeof(struct File) == 256);
|
||||
binaryname = "fs";
|
||||
cprintf("FS is running\n");
|
||||
|
||||
// Check that we are able to do I/O
|
||||
outw(0x8A00, 0x8A00);
|
||||
cprintf("FS can do I/O\n");
|
||||
|
||||
serve_init();
|
||||
fs_init();
|
||||
fs_test();
|
||||
serve();
|
||||
}
|
||||
|
||||
322
lab/Untitled Project.si4project/Backup/sh(7186).c
Normal file
322
lab/Untitled Project.si4project/Backup/sh(7186).c
Normal file
@@ -0,0 +1,322 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define BUFSIZ 1024 /* Find the buffer overrun bug! */
|
||||
int debug = 0;
|
||||
|
||||
|
||||
// gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
|
||||
// gettoken(0, token) parses a shell token from the previously set string,
|
||||
// null-terminates that token, stores the token pointer in '*token',
|
||||
// and returns a token ID (0, '<', '>', '|', or 'w').
|
||||
// Subsequent calls to 'gettoken(0, token)' will return subsequent
|
||||
// tokens from the string.
|
||||
int gettoken(char *s, char **token);
|
||||
|
||||
|
||||
// Parse a shell command from string 's' and execute it.
|
||||
// Do not return until the shell command is finished.
|
||||
// runcmd() is called in a forked child,
|
||||
// so it's OK to manipulate file descriptor state.
|
||||
#define MAXARGS 16
|
||||
void
|
||||
runcmd(char* s)
|
||||
{
|
||||
char *argv[MAXARGS], *t, argv0buf[BUFSIZ];
|
||||
int argc, c, i, r, p[2], fd, pipe_child;
|
||||
|
||||
pipe_child = 0;
|
||||
gettoken(s, 0);
|
||||
|
||||
again:
|
||||
argc = 0;
|
||||
while (1) {
|
||||
switch ((c = gettoken(0, &t))) {
|
||||
|
||||
case 'w': // Add an argument
|
||||
if (argc == MAXARGS) {
|
||||
cprintf("too many arguments\n");
|
||||
exit();
|
||||
}
|
||||
argv[argc++] = t;
|
||||
break;
|
||||
|
||||
case '<': // Input redirection
|
||||
// Grab the filename from the argument list
|
||||
if (gettoken(0, &t) != 'w') {
|
||||
cprintf("syntax error: < not followed by word\n");
|
||||
exit();
|
||||
}
|
||||
// Open 't' for reading as file descriptor 0
|
||||
// (which environments use as standard input).
|
||||
// We can't open a file onto a particular descriptor,
|
||||
// so open the file as 'fd',
|
||||
// then check whether 'fd' is 0.
|
||||
// If not, dup 'fd' onto file descriptor 0,
|
||||
// then close the original 'fd'.
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("< redirection not implemented");
|
||||
break;
|
||||
|
||||
case '>': // Output redirection
|
||||
// Grab the filename from the argument list
|
||||
if (gettoken(0, &t) != 'w') {
|
||||
cprintf("syntax error: > not followed by word\n");
|
||||
exit();
|
||||
}
|
||||
if ((fd = open(t, O_WRONLY|O_CREAT|O_TRUNC)) < 0) {
|
||||
cprintf("open %s for write: %e", t, fd);
|
||||
exit();
|
||||
}
|
||||
if (fd != 1) {
|
||||
dup(fd, 1);
|
||||
close(fd);
|
||||
}
|
||||
break;
|
||||
|
||||
case '|': // Pipe
|
||||
if ((r = pipe(p)) < 0) {
|
||||
cprintf("pipe: %e", r);
|
||||
exit();
|
||||
}
|
||||
if (debug)
|
||||
cprintf("PIPE: %d %d\n", p[0], p[1]);
|
||||
if ((r = fork()) < 0) {
|
||||
cprintf("fork: %e", r);
|
||||
exit();
|
||||
}
|
||||
if (r == 0) {
|
||||
if (p[0] != 0) {
|
||||
dup(p[0], 0);
|
||||
close(p[0]);
|
||||
}
|
||||
close(p[1]);
|
||||
goto again;
|
||||
} else {
|
||||
pipe_child = r;
|
||||
if (p[1] != 1) {
|
||||
dup(p[1], 1);
|
||||
close(p[1]);
|
||||
}
|
||||
close(p[0]);
|
||||
goto runit;
|
||||
}
|
||||
panic("| not implemented");
|
||||
break;
|
||||
|
||||
case 0: // String is complete
|
||||
// Run the current command!
|
||||
goto runit;
|
||||
|
||||
default:
|
||||
panic("bad return %d from gettoken", c);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
runit:
|
||||
// Return immediately if command line was empty.
|
||||
if(argc == 0) {
|
||||
if (debug)
|
||||
cprintf("EMPTY COMMAND\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up command line.
|
||||
// Read all commands from the filesystem: add an initial '/' to
|
||||
// the command name.
|
||||
// This essentially acts like 'PATH=/'.
|
||||
if (argv[0][0] != '/') {
|
||||
argv0buf[0] = '/';
|
||||
strcpy(argv0buf + 1, argv[0]);
|
||||
argv[0] = argv0buf;
|
||||
}
|
||||
argv[argc] = 0;
|
||||
|
||||
// Print the command.
|
||||
if (debug) {
|
||||
cprintf("[%08x] SPAWN:", thisenv->env_id);
|
||||
for (i = 0; argv[i]; i++)
|
||||
cprintf(" %s", argv[i]);
|
||||
cprintf("\n");
|
||||
}
|
||||
|
||||
// Spawn the command!
|
||||
if ((r = spawn(argv[0], (const char**) argv)) < 0)
|
||||
cprintf("spawn %s: %e\n", argv[0], r);
|
||||
|
||||
// In the parent, close all file descriptors and wait for the
|
||||
// spawned command to exit.
|
||||
close_all();
|
||||
if (r >= 0) {
|
||||
if (debug)
|
||||
cprintf("[%08x] WAIT %s %08x\n", thisenv->env_id, argv[0], r);
|
||||
wait(r);
|
||||
if (debug)
|
||||
cprintf("[%08x] wait finished\n", thisenv->env_id);
|
||||
}
|
||||
|
||||
// If we were the left-hand part of a pipe,
|
||||
// wait for the right-hand part to finish.
|
||||
if (pipe_child) {
|
||||
if (debug)
|
||||
cprintf("[%08x] WAIT pipe_child %08x\n", thisenv->env_id, pipe_child);
|
||||
wait(pipe_child);
|
||||
if (debug)
|
||||
cprintf("[%08x] wait finished\n", thisenv->env_id);
|
||||
}
|
||||
|
||||
// Done!
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
// Get the next token from string s.
|
||||
// Set *p1 to the beginning of the token and *p2 just past the token.
|
||||
// Returns
|
||||
// 0 for end-of-string;
|
||||
// < for <;
|
||||
// > for >;
|
||||
// | for |;
|
||||
// w for a word.
|
||||
//
|
||||
// Eventually (once we parse the space where the \0 will go),
|
||||
// words get nul-terminated.
|
||||
#define WHITESPACE " \t\r\n"
|
||||
#define SYMBOLS "<|>&;()"
|
||||
|
||||
int
|
||||
_gettoken(char *s, char **p1, char **p2)
|
||||
{
|
||||
int t;
|
||||
|
||||
if (s == 0) {
|
||||
if (debug > 1)
|
||||
cprintf("GETTOKEN NULL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (debug > 1)
|
||||
cprintf("GETTOKEN: %s\n", s);
|
||||
|
||||
*p1 = 0;
|
||||
*p2 = 0;
|
||||
|
||||
while (strchr(WHITESPACE, *s))
|
||||
*s++ = 0;
|
||||
if (*s == 0) {
|
||||
if (debug > 1)
|
||||
cprintf("EOL\n");
|
||||
return 0;
|
||||
}
|
||||
if (strchr(SYMBOLS, *s)) {
|
||||
t = *s;
|
||||
*p1 = s;
|
||||
*s++ = 0;
|
||||
*p2 = s;
|
||||
if (debug > 1)
|
||||
cprintf("TOK %c\n", t);
|
||||
return t;
|
||||
}
|
||||
*p1 = s;
|
||||
while (*s && !strchr(WHITESPACE SYMBOLS, *s))
|
||||
s++;
|
||||
*p2 = s;
|
||||
if (debug > 1) {
|
||||
t = **p2;
|
||||
**p2 = 0;
|
||||
cprintf("WORD: %s\n", *p1);
|
||||
**p2 = t;
|
||||
}
|
||||
return 'w';
|
||||
}
|
||||
|
||||
int
|
||||
gettoken(char *s, char **p1)
|
||||
{
|
||||
static int c, nc;
|
||||
static char* np1, *np2;
|
||||
|
||||
if (s) {
|
||||
nc = _gettoken(s, &np1, &np2);
|
||||
return 0;
|
||||
}
|
||||
c = nc;
|
||||
*p1 = np1;
|
||||
nc = _gettoken(np2, &np1, &np2);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
cprintf("usage: sh [-dix] [command-file]\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r, interactive, echocmds;
|
||||
struct Argstate args;
|
||||
|
||||
interactive = '?';
|
||||
echocmds = 0;
|
||||
argstart(&argc, argv, &args);
|
||||
while ((r = argnext(&args)) >= 0)
|
||||
switch (r) {
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
case 'i':
|
||||
interactive = 1;
|
||||
break;
|
||||
case 'x':
|
||||
echocmds = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
usage();
|
||||
if (argc == 2) {
|
||||
close(0);
|
||||
if ((r = open(argv[1], O_RDONLY)) < 0)
|
||||
panic("open %s: %e", argv[1], r);
|
||||
assert(r == 0);
|
||||
}
|
||||
if (interactive == '?')
|
||||
interactive = iscons(0);
|
||||
|
||||
while (1) {
|
||||
char *buf;
|
||||
|
||||
buf = readline(interactive ? "$ " : NULL);
|
||||
if (buf == NULL) {
|
||||
if (debug)
|
||||
cprintf("EXITING\n");
|
||||
exit(); // end of file
|
||||
}
|
||||
if (debug)
|
||||
cprintf("LINE: %s\n", buf);
|
||||
if (buf[0] == '#')
|
||||
continue;
|
||||
if (echocmds)
|
||||
printf("# %s\n", buf);
|
||||
if (debug)
|
||||
cprintf("BEFORE FORK\n");
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (debug)
|
||||
cprintf("FORK: %d\n", r);
|
||||
if (r == 0) {
|
||||
runcmd(buf);
|
||||
exit();
|
||||
} else
|
||||
wait(r);
|
||||
}
|
||||
}
|
||||
|
||||
307
lab/Untitled Project.si4project/Backup/spawn(5260).c
Normal file
307
lab/Untitled Project.si4project/Backup/spawn(5260).c
Normal file
@@ -0,0 +1,307 @@
|
||||
#include <inc/lib.h>
|
||||
#include <inc/elf.h>
|
||||
|
||||
#define UTEMP2USTACK(addr) ((void*) (addr) + (USTACKTOP - PGSIZE) - UTEMP)
|
||||
#define UTEMP2 (UTEMP + PGSIZE)
|
||||
#define UTEMP3 (UTEMP2 + PGSIZE)
|
||||
|
||||
// Helper functions for spawn.
|
||||
static int init_stack(envid_t child, const char **argv, uintptr_t *init_esp);
|
||||
static int map_segment(envid_t child, uintptr_t va, size_t memsz,
|
||||
int fd, size_t filesz, off_t fileoffset, int perm);
|
||||
static int copy_shared_pages(envid_t child);
|
||||
|
||||
// Spawn a child process from a program image loaded from the file system.
|
||||
// prog: the pathname of the program to run.
|
||||
// argv: pointer to null-terminated array of pointers to strings,
|
||||
// which will be passed to the child as its command-line arguments.
|
||||
// Returns child envid on success, < 0 on failure.
|
||||
int
|
||||
spawn(const char *prog, const char **argv)
|
||||
{
|
||||
unsigned char elf_buf[512];
|
||||
struct Trapframe child_tf;
|
||||
envid_t child;
|
||||
|
||||
int fd, i, r;
|
||||
struct Elf *elf;
|
||||
struct Proghdr *ph;
|
||||
int perm;
|
||||
|
||||
// This code follows this procedure:
|
||||
//
|
||||
// - Open the program file.
|
||||
//
|
||||
// - Read the ELF header, as you have before, and sanity check its
|
||||
// magic number. (Check out your load_icode!)
|
||||
//
|
||||
// - Use sys_exofork() to create a new environment.
|
||||
//
|
||||
// - Set child_tf to an initial struct Trapframe for the child.
|
||||
//
|
||||
// - Call the init_stack() function above to set up
|
||||
// the initial stack page for the child environment.
|
||||
//
|
||||
// - Map all of the program's segments that are of p_type
|
||||
// ELF_PROG_LOAD into the new environment's address space.
|
||||
// Use the p_flags field in the Proghdr for each segment
|
||||
// to determine how to map the segment:
|
||||
//
|
||||
// * If the ELF flags do not include ELF_PROG_FLAG_WRITE,
|
||||
// then the segment contains text and read-only data.
|
||||
// Use read_map() to read the contents of this segment,
|
||||
// and map the pages it returns directly into the child
|
||||
// so that multiple instances of the same program
|
||||
// will share the same copy of the program text.
|
||||
// Be sure to map the program text read-only in the child.
|
||||
// Read_map is like read but returns a pointer to the data in
|
||||
// *blk rather than copying the data into another buffer.
|
||||
//
|
||||
// * If the ELF segment flags DO include ELF_PROG_FLAG_WRITE,
|
||||
// then the segment contains read/write data and bss.
|
||||
// As with load_icode() in Lab 3, such an ELF segment
|
||||
// occupies p_memsz bytes in memory, but only the FIRST
|
||||
// p_filesz bytes of the segment are actually loaded
|
||||
// from the executable file - you must clear the rest to zero.
|
||||
// For each page to be mapped for a read/write segment,
|
||||
// allocate a page in the parent temporarily at UTEMP,
|
||||
// read() the appropriate portion of the file into that page
|
||||
// and/or use memset() to zero non-loaded portions.
|
||||
// (You can avoid calling memset(), if you like, if
|
||||
// page_alloc() returns zeroed pages already.)
|
||||
// Then insert the page mapping into the child.
|
||||
// Look at init_stack() for inspiration.
|
||||
// Be sure you understand why you can't use read_map() here.
|
||||
//
|
||||
// Note: None of the segment addresses or lengths above
|
||||
// are guaranteed to be page-aligned, so you must deal with
|
||||
// these non-page-aligned values appropriately.
|
||||
// The ELF linker does, however, guarantee that no two segments
|
||||
// will overlap on the same page; and it guarantees that
|
||||
// PGOFF(ph->p_offset) == PGOFF(ph->p_va).
|
||||
//
|
||||
// - Call sys_env_set_trapframe(child, &child_tf) to set up the
|
||||
// correct initial eip and esp values in the child.
|
||||
//
|
||||
// - Start the child process running with sys_env_set_status().
|
||||
|
||||
if ((r = open(prog, O_RDONLY)) < 0)
|
||||
return r;
|
||||
fd = r;
|
||||
|
||||
// Read elf header
|
||||
elf = (struct Elf*) elf_buf;
|
||||
if (readn(fd, elf_buf, sizeof(elf_buf)) != sizeof(elf_buf)
|
||||
|| elf->e_magic != ELF_MAGIC) {
|
||||
close(fd);
|
||||
cprintf("elf magic %08x want %08x\n", elf->e_magic, ELF_MAGIC);
|
||||
return -E_NOT_EXEC;
|
||||
}
|
||||
|
||||
// Create new child environment
|
||||
if ((r = sys_exofork()) < 0)
|
||||
return r;
|
||||
child = r;
|
||||
|
||||
// Set up trap frame, including initial stack.
|
||||
child_tf = envs[ENVX(child)].env_tf;
|
||||
child_tf.tf_eip = elf->e_entry;
|
||||
|
||||
if ((r = init_stack(child, argv, &child_tf.tf_esp)) < 0)
|
||||
return r;
|
||||
|
||||
// Set up program segments as defined in ELF header.
|
||||
ph = (struct Proghdr*) (elf_buf + elf->e_phoff);
|
||||
for (i = 0; i < elf->e_phnum; i++, ph++) {
|
||||
if (ph->p_type != ELF_PROG_LOAD)
|
||||
continue;
|
||||
perm = PTE_P | PTE_U;
|
||||
if (ph->p_flags & ELF_PROG_FLAG_WRITE)
|
||||
perm |= PTE_W;
|
||||
if ((r = map_segment(child, ph->p_va, ph->p_memsz,
|
||||
fd, ph->p_filesz, ph->p_offset, perm)) < 0)
|
||||
goto error;
|
||||
}
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
// Copy shared library state.
|
||||
if ((r = copy_shared_pages(child)) < 0)
|
||||
panic("copy_shared_pages: %e", r);
|
||||
|
||||
child_tf.tf_eflags |= FL_IOPL_3; // devious: see user/faultio.c
|
||||
if ((r = sys_env_set_trapframe(child, &child_tf)) < 0)
|
||||
panic("sys_env_set_trapframe: %e", r);
|
||||
|
||||
if ((r = sys_env_set_status(child, ENV_RUNNABLE)) < 0)
|
||||
panic("sys_env_set_status: %e", r);
|
||||
|
||||
return child;
|
||||
|
||||
error:
|
||||
sys_env_destroy(child);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Spawn, taking command-line arguments array directly on the stack.
|
||||
// NOTE: Must have a sentinal of NULL at the end of the args
|
||||
// (none of the args may be NULL).
|
||||
int
|
||||
spawnl(const char *prog, const char *arg0, ...)
|
||||
{
|
||||
// We calculate argc by advancing the args until we hit NULL.
|
||||
// The contract of the function guarantees that the last
|
||||
// argument will always be NULL, and that none of the other
|
||||
// arguments will be NULL.
|
||||
int argc=0;
|
||||
va_list vl;
|
||||
va_start(vl, arg0);
|
||||
while(va_arg(vl, void *) != NULL)
|
||||
argc++;
|
||||
va_end(vl);
|
||||
|
||||
// Now that we have the size of the args, do a second pass
|
||||
// and store the values in a VLA, which has the format of argv
|
||||
const char *argv[argc+2];
|
||||
argv[0] = arg0;
|
||||
argv[argc+1] = NULL;
|
||||
|
||||
va_start(vl, arg0);
|
||||
unsigned i;
|
||||
for(i=0;i<argc;i++)
|
||||
argv[i+1] = va_arg(vl, const char *);
|
||||
va_end(vl);
|
||||
return spawn(prog, argv);
|
||||
}
|
||||
|
||||
|
||||
// Set up the initial stack page for the new child process with envid 'child'
|
||||
// using the arguments array pointed to by 'argv',
|
||||
// which is a null-terminated array of pointers to null-terminated strings.
|
||||
//
|
||||
// On success, returns 0 and sets *init_esp
|
||||
// to the initial stack pointer with which the child should start.
|
||||
// Returns < 0 on failure.
|
||||
static int
|
||||
init_stack(envid_t child, const char **argv, uintptr_t *init_esp)
|
||||
{
|
||||
size_t string_size;
|
||||
int argc, i, r;
|
||||
char *string_store;
|
||||
uintptr_t *argv_store;
|
||||
|
||||
// Count the number of arguments (argc)
|
||||
// and the total amount of space needed for strings (string_size).
|
||||
string_size = 0;
|
||||
for (argc = 0; argv[argc] != 0; argc++)
|
||||
string_size += strlen(argv[argc]) + 1;
|
||||
|
||||
// Determine where to place the strings and the argv array.
|
||||
// Set up pointers into the temporary page 'UTEMP'; we'll map a page
|
||||
// there later, then remap that page into the child environment
|
||||
// at (USTACKTOP - PGSIZE).
|
||||
// strings is the topmost thing on the stack.
|
||||
string_store = (char*) UTEMP + PGSIZE - string_size;
|
||||
// argv is below that. There's one argument pointer per argument, plus
|
||||
// a null pointer.
|
||||
argv_store = (uintptr_t*) (ROUNDDOWN(string_store, 4) - 4 * (argc + 1));
|
||||
|
||||
// Make sure that argv, strings, and the 2 words that hold 'argc'
|
||||
// and 'argv' themselves will all fit in a single stack page.
|
||||
if ((void*) (argv_store - 2) < (void*) UTEMP)
|
||||
return -E_NO_MEM;
|
||||
|
||||
// Allocate the single stack page at UTEMP.
|
||||
if ((r = sys_page_alloc(0, (void*) UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
return r;
|
||||
|
||||
|
||||
// * Initialize 'argv_store[i]' to point to argument string i,
|
||||
// for all 0 <= i < argc.
|
||||
// Also, copy the argument strings from 'argv' into the
|
||||
// newly-allocated stack page.
|
||||
//
|
||||
// * Set 'argv_store[argc]' to 0 to null-terminate the args array.
|
||||
//
|
||||
// * Push two more words onto the child's stack below 'args',
|
||||
// containing the argc and argv parameters to be passed
|
||||
// to the child's umain() function.
|
||||
// argv should be below argc on the stack.
|
||||
// (Again, argv should use an address valid in the child's
|
||||
// environment.)
|
||||
//
|
||||
// * Set *init_esp to the initial stack pointer for the child,
|
||||
// (Again, use an address valid in the child's environment.)
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv_store[i] = UTEMP2USTACK(string_store);
|
||||
strcpy(string_store, argv[i]);
|
||||
string_store += strlen(argv[i]) + 1;
|
||||
}
|
||||
argv_store[argc] = 0;
|
||||
assert(string_store == (char*)UTEMP + PGSIZE);
|
||||
|
||||
argv_store[-1] = UTEMP2USTACK(argv_store);
|
||||
argv_store[-2] = argc;
|
||||
|
||||
*init_esp = UTEMP2USTACK(&argv_store[-2]);
|
||||
|
||||
// After completing the stack, map it into the child's address space
|
||||
// and unmap it from ours!
|
||||
if ((r = sys_page_map(0, UTEMP, child, (void*) (USTACKTOP - PGSIZE), PTE_P | PTE_U | PTE_W)) < 0)
|
||||
goto error;
|
||||
if ((r = sys_page_unmap(0, UTEMP)) < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
sys_page_unmap(0, UTEMP);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
map_segment(envid_t child, uintptr_t va, size_t memsz,
|
||||
int fd, size_t filesz, off_t fileoffset, int perm)
|
||||
{
|
||||
int i, r;
|
||||
void *blk;
|
||||
|
||||
//cprintf("map_segment %x+%x\n", va, memsz);
|
||||
|
||||
if ((i = PGOFF(va))) {
|
||||
va -= i;
|
||||
memsz += i;
|
||||
filesz += i;
|
||||
fileoffset -= i;
|
||||
}
|
||||
|
||||
for (i = 0; i < memsz; i += PGSIZE) {
|
||||
if (i >= filesz) {
|
||||
// allocate a blank page
|
||||
if ((r = sys_page_alloc(child, (void*) (va + i), perm)) < 0)
|
||||
return r;
|
||||
} else {
|
||||
// from file
|
||||
if ((r = sys_page_alloc(0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
return r;
|
||||
if ((r = seek(fd, fileoffset + i)) < 0)
|
||||
return r;
|
||||
if ((r = readn(fd, UTEMP, MIN(PGSIZE, filesz-i))) < 0)
|
||||
return r;
|
||||
if ((r = sys_page_map(0, UTEMP, child, (void*) (va + i), perm)) < 0)
|
||||
panic("spawn: sys_page_map data: %e", r);
|
||||
sys_page_unmap(0, UTEMP);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the mappings for shared pages into the child address space.
|
||||
static int
|
||||
copy_shared_pages(envid_t child)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
459
lab/Untitled Project.si4project/Backup/syscall(7911).c
Normal file
459
lab/Untitled Project.si4project/Backup/syscall(7911).c
Normal file
@@ -0,0 +1,459 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/sched.h>
|
||||
|
||||
// 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;
|
||||
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 envid's trap frame to 'tf'.
|
||||
// tf is modified to make sure that user environments always run at code
|
||||
// protection level 3 (CPL 3), interrupts enabled, and IOPL of 0.
|
||||
//
|
||||
// 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_trapframe(envid_t envid, struct Trapframe *tf)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
// Remember to check whether the user has supplied us with a good
|
||||
// address!
|
||||
panic("sys_env_set_trapframe 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.
|
||||
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.
|
||||
struct Env *dstenv;
|
||||
pte_t *pte;
|
||||
struct PageInfo *pp;
|
||||
int r;
|
||||
if ( (r = envid2env( envid, &dstenv, 0)) < 0)
|
||||
return r;
|
||||
|
||||
// 不处于等待接收状态, 或有进程已经请求发送数据
|
||||
if ( (dstenv->env_ipc_recving != true) || dstenv->env_ipc_from != 0)
|
||||
return -E_IPC_NOT_RECV;
|
||||
|
||||
if ((uint32_t)srcva < UTOP) {
|
||||
if ( PGOFF(srcva))
|
||||
return -E_INVAL;
|
||||
if ( !(perm & PTE_P ) || !(perm & PTE_U) )
|
||||
return -E_INVAL;
|
||||
if (perm & (~ PTE_SYSCALL))
|
||||
return -E_INVAL;
|
||||
|
||||
|
||||
if ((pp = page_lookup(curenv->env_pgdir, srcva, &pte)) == NULL )
|
||||
return -E_INVAL;
|
||||
|
||||
|
||||
if ((perm & PTE_W) && !(*pte & PTE_W) )
|
||||
return -E_INVAL;
|
||||
|
||||
// 接收进程愿意接收一个页
|
||||
if (dstenv->env_ipc_dstva) {
|
||||
// 开始映射
|
||||
if( (r = page_insert(dstenv->env_pgdir, pp, dstenv->env_ipc_dstva, perm)) < 0)
|
||||
return r;
|
||||
dstenv->env_ipc_perm = perm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
dstenv->env_ipc_from = curenv->env_id;
|
||||
dstenv->env_ipc_recving = false;
|
||||
dstenv->env_ipc_value = value;
|
||||
dstenv->env_status = ENV_RUNNABLE;
|
||||
// 返回值
|
||||
dstenv->env_tf.tf_regs.reg_eax = 0;
|
||||
return 0;
|
||||
|
||||
//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.
|
||||
if ((uint32_t) dstva < UTOP ) {
|
||||
if (PGOFF(dstva))
|
||||
return -E_INVAL;
|
||||
|
||||
}
|
||||
// 大于小于都可以赋值为desva。
|
||||
curenv->env_ipc_dstva = dstva;
|
||||
curenv->env_status = ENV_NOT_RUNNABLE;
|
||||
curenv->env_ipc_recving = true;
|
||||
curenv->env_ipc_from = 0;
|
||||
sched_yield();
|
||||
// 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 SYS_ipc_recv:
|
||||
return sys_ipc_recv( (void *) a1);
|
||||
case SYS_ipc_try_send:
|
||||
return sys_ipc_try_send((envid_t) a1, (uint32_t) a2, (void *) a3, (int) a4);
|
||||
case NSYSCALLS:
|
||||
return -E_INVAL;
|
||||
|
||||
default:
|
||||
return -E_INVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
464
lab/Untitled Project.si4project/Backup/syscall(8111).c
Normal file
464
lab/Untitled Project.si4project/Backup/syscall(8111).c
Normal file
@@ -0,0 +1,464 @@
|
||||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/sched.h>
|
||||
|
||||
// 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 envid's trap frame to 'tf'.
|
||||
// tf is modified to make sure that user environments always run at code
|
||||
// protection level 3 (CPL 3), interrupts enabled, and IOPL of 0.
|
||||
//
|
||||
// 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_trapframe(envid_t envid, struct Trapframe *tf)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
// Remember to check whether the user has supplied us with a good
|
||||
// address!
|
||||
panic("sys_env_set_trapframe 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.
|
||||
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.
|
||||
struct Env *dstenv;
|
||||
pte_t *pte;
|
||||
struct PageInfo *pp;
|
||||
int r;
|
||||
if ( (r = envid2env( envid, &dstenv, 0)) < 0)
|
||||
return r;
|
||||
|
||||
// 不处于等待接收状态, 或有进程已经请求发送数据
|
||||
if ( (dstenv->env_ipc_recving != true) || dstenv->env_ipc_from != 0)
|
||||
return -E_IPC_NOT_RECV;
|
||||
|
||||
if ((uint32_t)srcva < UTOP) {
|
||||
if ( PGOFF(srcva))
|
||||
return -E_INVAL;
|
||||
if ( !(perm & PTE_P ) || !(perm & PTE_U) )
|
||||
return -E_INVAL;
|
||||
if (perm & (~ PTE_SYSCALL))
|
||||
return -E_INVAL;
|
||||
|
||||
|
||||
if ((pp = page_lookup(curenv->env_pgdir, srcva, &pte)) == NULL )
|
||||
return -E_INVAL;
|
||||
|
||||
|
||||
if ((perm & PTE_W) && !(*pte & PTE_W) )
|
||||
return -E_INVAL;
|
||||
|
||||
// 接收进程愿意接收一个页
|
||||
if (dstenv->env_ipc_dstva) {
|
||||
// 开始映射
|
||||
if( (r = page_insert(dstenv->env_pgdir, pp, dstenv->env_ipc_dstva, perm)) < 0)
|
||||
return r;
|
||||
dstenv->env_ipc_perm = perm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
dstenv->env_ipc_from = curenv->env_id;
|
||||
dstenv->env_ipc_recving = false;
|
||||
dstenv->env_ipc_value = value;
|
||||
dstenv->env_status = ENV_RUNNABLE;
|
||||
// 返回值
|
||||
dstenv->env_tf.tf_regs.reg_eax = 0;
|
||||
return 0;
|
||||
|
||||
//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.
|
||||
if ((uint32_t) dstva < UTOP ) {
|
||||
if (PGOFF(dstva))
|
||||
return -E_INVAL;
|
||||
|
||||
}
|
||||
// 大于小于都可以赋值为desva。
|
||||
curenv->env_ipc_dstva = dstva;
|
||||
curenv->env_status = ENV_NOT_RUNNABLE;
|
||||
curenv->env_ipc_recving = true;
|
||||
curenv->env_ipc_from = 0;
|
||||
sched_yield();
|
||||
// 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 SYS_ipc_recv:
|
||||
return sys_ipc_recv( (void *) a1);
|
||||
case SYS_ipc_try_send:
|
||||
return sys_ipc_try_send((envid_t) a1, (uint32_t) a2, (void *) a3, (int) a4);
|
||||
case NSYSCALLS:
|
||||
return -E_INVAL;
|
||||
|
||||
default:
|
||||
return -E_INVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
436
lab/Untitled Project.si4project/Backup/trap(3657).c
Normal file
436
lab/Untitled Project.si4project/Backup/trap(3657).c
Normal file
@@ -0,0 +1,436 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
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();
|
||||
void timer_handler();
|
||||
void kbd_handler();
|
||||
void serial_handler();
|
||||
void spurious_handler();
|
||||
void ide_handler();
|
||||
void error_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);
|
||||
// IRQ
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_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;
|
||||
case (IRQ_OFFSET + IRQ_TIMER):
|
||||
// 回应8259A 接收中断。
|
||||
lapic_eoi();
|
||||
sched_yield();
|
||||
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.
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Handle keyboard and serial interrupts.
|
||||
// LAB 5: 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;
|
||||
}
|
||||
=======
|
||||
|
||||
>>>>>>> lab4
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
430
lab/Untitled Project.si4project/Backup/trap(5971).c
Normal file
430
lab/Untitled Project.si4project/Backup/trap(5971).c
Normal file
@@ -0,0 +1,430 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
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();
|
||||
void timer_handler();
|
||||
void kbd_handler();
|
||||
void serial_handler();
|
||||
void spurious_handler();
|
||||
void ide_handler();
|
||||
void error_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);
|
||||
// IRQ
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_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;
|
||||
// Handle clock interrupts. Don't forget to acknowledge the
|
||||
// interrupt using lapic_eoi() before calling the scheduler!
|
||||
// LAB 4: Your code here.
|
||||
case (IRQ_OFFSET + IRQ_TIMER):
|
||||
// 回应8259A 接收中断。
|
||||
lapic_eoi();
|
||||
sched_yield();
|
||||
break;
|
||||
|
||||
case (IRQ_OFFSET + IRQ_KBD):
|
||||
lapic_eoi();
|
||||
kbd_intr();
|
||||
break;
|
||||
case (IRQ_OFFSET + IRQ_SERIAL):
|
||||
lapic_eoi();
|
||||
serial_intr();
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
422
lab/Untitled Project.si4project/Backup/trap(6591).c
Normal file
422
lab/Untitled Project.si4project/Backup/trap(6591).c
Normal file
@@ -0,0 +1,422 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
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();
|
||||
void timer_handler();
|
||||
void kbd_handler();
|
||||
void serial_handler();
|
||||
void spurious_handler();
|
||||
void ide_handler();
|
||||
void error_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);
|
||||
// IRQ
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_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;
|
||||
case (IRQ_OFFSET + IRQ_TIMER):
|
||||
// 回应8259A 接收中断。
|
||||
lapic_eoi();
|
||||
sched_yield();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
421
lab/Untitled Project.si4project/Backup/trap(6593).c
Normal file
421
lab/Untitled Project.si4project/Backup/trap(6593).c
Normal file
@@ -0,0 +1,421 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
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();
|
||||
void timer_handler();
|
||||
void kbd_handler();
|
||||
void serial_handler();
|
||||
void spurious_handler();
|
||||
void ide_handler();
|
||||
void error_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);
|
||||
// IRQ
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_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;
|
||||
// Handle clock interrupts. Don't forget to acknowledge the
|
||||
// interrupt using lapic_eoi() before calling the scheduler!
|
||||
// LAB 4: Your code here.
|
||||
case (IRQ_OFFSET + IRQ_TIMER):
|
||||
// 回应8259A 接收中断。
|
||||
lapic_eoi();
|
||||
sched_yield();
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
430
lab/Untitled Project.si4project/Backup/trap(7420).c
Normal file
430
lab/Untitled Project.si4project/Backup/trap(7420).c
Normal file
@@ -0,0 +1,430 @@
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
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();
|
||||
void timer_handler();
|
||||
void kbd_handler();
|
||||
void serial_handler();
|
||||
void spurious_handler();
|
||||
void ide_handler();
|
||||
void error_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);
|
||||
// IRQ
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, timer_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, kbd_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, serial_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, spurious_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, ide_handler, 3);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, error_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;
|
||||
// Handle clock interrupts. Don't forget to acknowledge the
|
||||
// interrupt using lapic_eoi() before calling the scheduler!
|
||||
// LAB 4: Your code here.
|
||||
case (IRQ_OFFSET + IRQ_TIMER):
|
||||
// 回应8259A 接收中断。
|
||||
lapic_eoi();
|
||||
sched_yield();
|
||||
break;
|
||||
|
||||
case (IRQ_OFFSET + IRQ_KBD):
|
||||
lapic_eoi();
|
||||
kbd_intr();
|
||||
break;
|
||||
case (IRQ_OFFSET + IRQ_SERIAL):
|
||||
lapic_eoi();
|
||||
serial_intr();
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user