my solution to lab 6

This commit is contained in:
winPond
2019-07-17 16:08:06 +08:00
parent 8147d99448
commit c62da1c86d
534 changed files with 60875 additions and 6513 deletions

View File

@@ -94,6 +94,10 @@ CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
# mon_backtrace()'s function prologue on gcc version: (Debian 4.7.2-5) 4.7.2
CFLAGS += -fno-tree-ch
CFLAGS += -I$(TOP)/net/lwip/include \
-I$(TOP)/net/lwip/include/ipv4 \
-I$(TOP)/net/lwip/jos
# Add -fno-stack-protector if the option exists.
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
@@ -142,16 +146,22 @@ include kern/Makefrag
include lib/Makefrag
include user/Makefrag
include fs/Makefrag
include net/Makefrag
CPUS ?= 1
PORT7 := $(shell expr $(GDBPORT) + 1)
PORT80 := $(shell expr $(GDBPORT) + 2)
QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::$(GDBPORT)
QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi)
IMAGES = $(OBJDIR)/kern/kernel.img
QEMUOPTS += -smp $(CPUS)
QEMUOPTS += -drive file=$(OBJDIR)/fs/fs.img,index=1,media=disk,format=raw
IMAGES += $(OBJDIR)/fs/fs.img
QEMUOPTS += -net user -net nic,model=e1000 -redir tcp:$(PORT7)::7 \
-redir tcp:$(PORT80)::80 -redir udp:$(PORT7)::7 -net dump,file=qemu.pcap
QEMUOPTS += $(QEMUEXTRA)
.gdbinit: .gdbinit.tmpl
@@ -161,6 +171,8 @@ gdb:
$(GDB) -n -x .gdbinit
pre-qemu: .gdbinit
# QEMU doesn't truncate the pcap file. Work around this.
@rm -f qemu.pcap
qemu: $(IMAGES) pre-qemu
$(QEMU) $(QEMUOPTS)
@@ -308,6 +320,7 @@ myapi.key:
# @./handin-prep
# For test runs
prep-net_%: override INIT_CFLAGS+=-DTEST_NO_NS
prep-%:
$(V)$(MAKE) "INIT_CFLAGS=${INIT_CFLAGS} -DTEST=`case $* in *_*) echo $*;; *) echo user_$*;; esac`" $(IMAGES)
@@ -324,6 +337,23 @@ run-%-nox: prep-% pre-qemu
run-%: prep-% pre-qemu
$(QEMU) $(QEMUOPTS)
# For network connections
which-ports:
@echo "Local port $(PORT7) forwards to JOS port 7 (echo server)"
@echo "Local port $(PORT80) forwards to JOS port 80 (web server)"
nc-80:
nc localhost $(PORT80)
nc-7:
nc localhost $(PORT7)
telnet-80:
telnet localhost $(PORT80)
telnet-7:
telnet localhost $(PORT7)
# This magic automatically generates makefile dependencies
# for header files included from C source files we compile,
# and keeps those dependencies up-to-date every time we recompile.

View File

@@ -1,151 +0,0 @@
#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);
}

View File

@@ -1,169 +0,0 @@
#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);
}

View File

@@ -1,578 +0,0 @@
/* 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");
}

View File

@@ -1,577 +0,0 @@
/* 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");
}

View File

@@ -1,10 +0,0 @@
#include <inc/lib.h>
void
exit(void)
{
close_all();
sys_env_destroy(0);
}

View File

@@ -1,22 +0,0 @@
// 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");
}

View File

@@ -1,180 +0,0 @@
#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);
}

View File

@@ -1,167 +0,0 @@
// 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;
}

View File

@@ -1,456 +0,0 @@
#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));
}

View File

@@ -1,515 +0,0 @@
#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));
}

View File

@@ -1,180 +0,0 @@
/* 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);
}

View File

@@ -1,172 +0,0 @@
/* 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_icode, 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);
}

View File

@@ -1,174 +0,0 @@
/* 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);
}

View File

@@ -1,345 +0,0 @@
/*
* 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();
}

View File

@@ -1,322 +0,0 @@
#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);
}
}

View File

@@ -1,307 +0,0 @@
#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;
}

View File

@@ -1,436 +0,0 @@
#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);
}
}

View File

@@ -1,430 +0,0 @@
#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);
}
}

View File

@@ -1,422 +0,0 @@
#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);
}
}

View File

@@ -1,421 +0,0 @@
#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);
}
}

View File

@@ -1,430 +0,0 @@
#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);
}
}

View File

@@ -1,3 +0,0 @@
---- IOPL Matches (2 in 2 files) ----
env_create in env.c (kern) : newenv->env_tf.tf_eflags |= IOPL
syscall.c (kern) line 134 : // protection level 3 (CPL 3), interrupts enabled, and IOPL of 0.

Some files were not shown because too many files have changed in this diff Show More