Compare commits
2 Commits
lab5
...
lab3-final
| Author | SHA1 | Date | |
|---|---|---|---|
| 7965d399dc | |||
| 5e04fb305e |
@@ -88,7 +88,6 @@ CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -O1 -fno-builtin -I$(TOP) -MD
|
|||||||
CFLAGS += -fno-omit-frame-pointer
|
CFLAGS += -fno-omit-frame-pointer
|
||||||
CFLAGS += -std=gnu99
|
CFLAGS += -std=gnu99
|
||||||
CFLAGS += -static
|
CFLAGS += -static
|
||||||
CFLAGS += -fno-pie
|
|
||||||
CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
|
CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
|
||||||
# -fno-tree-ch prevented gcc from sometimes reordering read_ebp() before
|
# -fno-tree-ch prevented gcc from sometimes reordering read_ebp() before
|
||||||
# mon_backtrace()'s function prologue on gcc version: (Debian 4.7.2-5) 4.7.2
|
# mon_backtrace()'s function prologue on gcc version: (Debian 4.7.2-5) 4.7.2
|
||||||
@@ -141,17 +140,11 @@ include boot/Makefrag
|
|||||||
include kern/Makefrag
|
include kern/Makefrag
|
||||||
include lib/Makefrag
|
include lib/Makefrag
|
||||||
include user/Makefrag
|
include user/Makefrag
|
||||||
include fs/Makefrag
|
|
||||||
|
|
||||||
|
|
||||||
CPUS ?= 1
|
|
||||||
|
|
||||||
QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::$(GDBPORT)
|
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)
|
QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi)
|
||||||
IMAGES = $(OBJDIR)/kern/kernel.img
|
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 += $(QEMUEXTRA)
|
QEMUOPTS += $(QEMUEXTRA)
|
||||||
|
|
||||||
.gdbinit: .gdbinit.tmpl
|
.gdbinit: .gdbinit.tmpl
|
||||||
|
|||||||
18
answers-lab3.txt
Normal file
18
answers-lab3.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
1. Some interrupts receive a different "shape" of stack frame - the kernel pushes
|
||||||
|
an error code for some, but not for the others. We thus need individual handlers
|
||||||
|
that would make sure that what we have on the stack is consistent, and
|
||||||
|
then call one centralized function for that. The handlers additionally
|
||||||
|
are able to pass in a trap number to that centrallized function. This way, a lot
|
||||||
|
of code can be re-used.
|
||||||
|
|
||||||
|
Conceptually, each trap has a different semantic meaning. Thus, it makes no sense
|
||||||
|
to perform the same action for both traps - they don't mean the same thing.
|
||||||
|
|
||||||
|
2. The general protection fault occurs when a process can access memory, but not do something else with it. We do not want page faults to be trigger-able by users (this is the case for many other traps). Thus, when the user tries to invoke int 14,
|
||||||
|
it's not allowed to execute it from userspace, so it is thrown a general protection fault, which it then prints.
|
||||||
|
|
||||||
|
3. Unlike most other traps, the debugging trap should be possible to trigger from user code. If the "privillege level" is set to 0 in the IDT, when the INT3 occurs, the user does not have permission to call that interrupt,
|
||||||
|
and thus, a general protection fault occurs. In order to make it work correctly, the privillege level needs to be 3 (for userland).
|
||||||
|
|
||||||
|
4. It's probably so that users cannot deliberately trigger certain faults for malicious purposes. It doesn't make sense to trigger a page fault without properly setting up CR2, and even then, though I can't think
|
||||||
|
of anything right now, there's likely security issues that could arise if users can deliberately page fault their own process.
|
||||||
@@ -39,7 +39,6 @@ void
|
|||||||
bootmain(void)
|
bootmain(void)
|
||||||
{
|
{
|
||||||
struct Proghdr *ph, *eph;
|
struct Proghdr *ph, *eph;
|
||||||
int i;
|
|
||||||
|
|
||||||
// read 1st page off disk
|
// read 1st page off disk
|
||||||
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
|
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
|
||||||
@@ -51,14 +50,10 @@ bootmain(void)
|
|||||||
// load each program segment (ignores ph flags)
|
// load each program segment (ignores ph flags)
|
||||||
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
|
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
|
||||||
eph = ph + ELFHDR->e_phnum;
|
eph = ph + ELFHDR->e_phnum;
|
||||||
for (; ph < eph; ph++) {
|
for (; ph < eph; ph++)
|
||||||
// p_pa is the load address of this segment (as well
|
// p_pa is the load address of this segment (as well
|
||||||
// as the physical address)
|
// as the physical address)
|
||||||
readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
|
readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
|
||||||
for (i = 0; i < ph->p_memsz - ph->p_filesz; i++) {
|
|
||||||
*((char *) ph->p_pa + ph->p_filesz + i) = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// call the entry point from the ELF header
|
// call the entry point from the ELF header
|
||||||
// note: does not return!
|
// note: does not return!
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
LAB=5
|
LAB=3
|
||||||
PACKAGEDATE=Wed Oct 24 20:44:37 EDT 2018
|
PACKAGEDATE=Tue Sep 25 12:21:10 EDT 2018
|
||||||
|
|||||||
77
fs/Makefrag
77
fs/Makefrag
@@ -1,77 +0,0 @@
|
|||||||
|
|
||||||
OBJDIRS += fs
|
|
||||||
|
|
||||||
FSOFILES := $(OBJDIR)/fs/ide.o \
|
|
||||||
$(OBJDIR)/fs/bc.o \
|
|
||||||
$(OBJDIR)/fs/fs.o \
|
|
||||||
$(OBJDIR)/fs/serv.o \
|
|
||||||
$(OBJDIR)/fs/test.o \
|
|
||||||
|
|
||||||
USERAPPS := $(OBJDIR)/user/init
|
|
||||||
|
|
||||||
FSIMGTXTFILES := fs/newmotd \
|
|
||||||
fs/motd
|
|
||||||
|
|
||||||
|
|
||||||
USERAPPS := $(USERAPPS) \
|
|
||||||
$(OBJDIR)/user/cat \
|
|
||||||
$(OBJDIR)/user/echo \
|
|
||||||
$(OBJDIR)/user/init \
|
|
||||||
$(OBJDIR)/user/ls \
|
|
||||||
$(OBJDIR)/user/lsfd \
|
|
||||||
$(OBJDIR)/user/num \
|
|
||||||
$(OBJDIR)/user/forktree \
|
|
||||||
$(OBJDIR)/user/primes \
|
|
||||||
$(OBJDIR)/user/primespipe \
|
|
||||||
$(OBJDIR)/user/sh \
|
|
||||||
$(OBJDIR)/user/testfdsharing \
|
|
||||||
$(OBJDIR)/user/testkbd \
|
|
||||||
$(OBJDIR)/user/testpipe \
|
|
||||||
$(OBJDIR)/user/testpteshare \
|
|
||||||
$(OBJDIR)/user/testshell \
|
|
||||||
$(OBJDIR)/user/hello \
|
|
||||||
$(OBJDIR)/user/faultio \
|
|
||||||
|
|
||||||
FSIMGTXTFILES := $(FSIMGTXTFILES) \
|
|
||||||
fs/lorem \
|
|
||||||
fs/script \
|
|
||||||
fs/testshell.key \
|
|
||||||
fs/testshell.sh
|
|
||||||
|
|
||||||
|
|
||||||
FSIMGFILES := $(FSIMGTXTFILES) $(USERAPPS)
|
|
||||||
|
|
||||||
$(OBJDIR)/fs/%.o: fs/%.c fs/fs.h inc/lib.h $(OBJDIR)/.vars.USER_CFLAGS
|
|
||||||
@echo + cc[USER] $<
|
|
||||||
@mkdir -p $(@D)
|
|
||||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
$(OBJDIR)/fs/fs: $(FSOFILES) $(OBJDIR)/lib/entry.o $(OBJDIR)/lib/libjos.a user/user.ld
|
|
||||||
@echo + ld $@
|
|
||||||
$(V)mkdir -p $(@D)
|
|
||||||
$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib \
|
|
||||||
$(OBJDIR)/lib/entry.o $(FSOFILES) \
|
|
||||||
-L$(OBJDIR)/lib -ljos $(GCC_LIB)
|
|
||||||
$(V)$(OBJDUMP) -S $@ >$@.asm
|
|
||||||
|
|
||||||
# How to build the file system image
|
|
||||||
$(OBJDIR)/fs/fsformat: fs/fsformat.c
|
|
||||||
@echo + mk $(OBJDIR)/fs/fsformat
|
|
||||||
$(V)mkdir -p $(@D)
|
|
||||||
$(V)$(NCC) $(NATIVE_CFLAGS) -o $(OBJDIR)/fs/fsformat fs/fsformat.c
|
|
||||||
|
|
||||||
$(OBJDIR)/fs/clean-fs.img: $(OBJDIR)/fs/fsformat $(FSIMGFILES)
|
|
||||||
@echo + mk $(OBJDIR)/fs/clean-fs.img
|
|
||||||
$(V)mkdir -p $(@D)
|
|
||||||
$(V)$(OBJDIR)/fs/fsformat $(OBJDIR)/fs/clean-fs.img 1024 $(FSIMGFILES)
|
|
||||||
|
|
||||||
$(OBJDIR)/fs/fs.img: $(OBJDIR)/fs/clean-fs.img
|
|
||||||
@echo + cp $(OBJDIR)/fs/clean-fs.img $@
|
|
||||||
$(V)cp $(OBJDIR)/fs/clean-fs.img $@
|
|
||||||
|
|
||||||
all: $(OBJDIR)/fs/fs.img
|
|
||||||
|
|
||||||
#all: $(addsuffix .sym, $(USERAPPS))
|
|
||||||
|
|
||||||
#all: $(addsuffix .asm, $(USERAPPS))
|
|
||||||
|
|
||||||
166
fs/bc.c
166
fs/bc.c
@@ -1,166 +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;
|
|
||||||
uint32_t secno = blockno * (BLKSIZE / SECTSIZE);
|
|
||||||
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:
|
|
||||||
addr = ROUNDDOWN(addr, PGSIZE);
|
|
||||||
if((r = sys_page_alloc(0, addr, PTE_U | PTE_W | PTE_P)) < 0)
|
|
||||||
panic("failed to allocate page for block cache");
|
|
||||||
if((r = ide_read(secno, addr, BLKSIZE / SECTSIZE)) < 0)
|
|
||||||
panic("failed to read from disk into block cache");
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
uint32_t secno = blockno * (BLKSIZE / SECTSIZE);
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
|
|
||||||
panic("flush_block of bad va %08x", addr);
|
|
||||||
|
|
||||||
// LAB 5: Your code here.
|
|
||||||
addr = ROUNDDOWN(addr, PGSIZE);
|
|
||||||
if(!(va_is_mapped(addr) && va_is_dirty(addr))) return;
|
|
||||||
|
|
||||||
if((r = ide_write(secno, addr, BLKSIZE / SECTSIZE)) < 0)
|
|
||||||
panic("failed to flush block into cache");
|
|
||||||
|
|
||||||
if((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
|
|
||||||
panic("failed to remove dirty bit from page");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
490
fs/fs.c
490
fs/fs.c
@@ -1,490 +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.
|
|
||||||
uint32_t* search = bitmap + 1;
|
|
||||||
for(uint32_t block = 1; block < super->s_nblocks; block++) {
|
|
||||||
if(bitmap[block / 32] & (1 << (block % 32))) {
|
|
||||||
bitmap[block / 32] &= ~(1 << (block % 32));
|
|
||||||
flush_block(&bitmap[block / 32]);
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
int newblock;
|
|
||||||
if(filebno < NDIRECT) {
|
|
||||||
*ppdiskbno = &f->f_direct[filebno];
|
|
||||||
return 0;
|
|
||||||
} else if(filebno < NDIRECT + NINDIRECT) {
|
|
||||||
filebno -= NDIRECT;
|
|
||||||
if(!f->f_indirect) {
|
|
||||||
if(!alloc) return -E_NOT_FOUND;
|
|
||||||
if((newblock = alloc_block()) < 0) return newblock;
|
|
||||||
f->f_indirect = newblock;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t* indirect = diskaddr(f->f_indirect);
|
|
||||||
*ppdiskbno = &indirect[filebno];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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* blockno;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if ((r = file_block_walk(f, filebno, &blockno, 1)) < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if(*blockno == 0) {
|
|
||||||
if((r = alloc_block()) < 0) return r;
|
|
||||||
*blockno = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
*blk = diskaddr(*blockno);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
49
fs/fs.h
49
fs/fs.h
@@ -1,49 +0,0 @@
|
|||||||
#include <inc/fs.h>
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
#define SECTSIZE 512 // bytes per disk sector
|
|
||||||
#define BLKSECTS (BLKSIZE / SECTSIZE) // sectors per block
|
|
||||||
|
|
||||||
/* Disk block n, when in memory, is mapped into the file system
|
|
||||||
* server's address space at DISKMAP + (n*BLKSIZE). */
|
|
||||||
#define DISKMAP 0x10000000
|
|
||||||
|
|
||||||
/* Maximum disk size we can handle (3GB) */
|
|
||||||
#define DISKSIZE 0xC0000000
|
|
||||||
|
|
||||||
struct Super *super; // superblock
|
|
||||||
uint32_t *bitmap; // bitmap blocks mapped in memory
|
|
||||||
|
|
||||||
/* ide.c */
|
|
||||||
bool ide_probe_disk1(void);
|
|
||||||
void ide_set_disk(int diskno);
|
|
||||||
void ide_set_partition(uint32_t first_sect, uint32_t nsect);
|
|
||||||
int ide_read(uint32_t secno, void *dst, size_t nsecs);
|
|
||||||
int ide_write(uint32_t secno, const void *src, size_t nsecs);
|
|
||||||
|
|
||||||
/* bc.c */
|
|
||||||
void* diskaddr(uint32_t blockno);
|
|
||||||
bool va_is_mapped(void *va);
|
|
||||||
bool va_is_dirty(void *va);
|
|
||||||
void flush_block(void *addr);
|
|
||||||
void bc_init(void);
|
|
||||||
|
|
||||||
/* fs.c */
|
|
||||||
void fs_init(void);
|
|
||||||
int file_get_block(struct File *f, uint32_t file_blockno, char **pblk);
|
|
||||||
int file_create(const char *path, struct File **f);
|
|
||||||
int file_open(const char *path, struct File **f);
|
|
||||||
ssize_t file_read(struct File *f, void *buf, size_t count, off_t offset);
|
|
||||||
int file_write(struct File *f, const void *buf, size_t count, off_t offset);
|
|
||||||
int file_set_size(struct File *f, off_t newsize);
|
|
||||||
void file_flush(struct File *f);
|
|
||||||
int file_remove(const char *path);
|
|
||||||
void fs_sync(void);
|
|
||||||
|
|
||||||
/* int map_block(uint32_t); */
|
|
||||||
bool block_is_free(uint32_t blockno);
|
|
||||||
int alloc_block(void);
|
|
||||||
|
|
||||||
/* test.c */
|
|
||||||
void fs_test(void);
|
|
||||||
|
|
||||||
244
fs/fsformat.c
244
fs/fsformat.c
@@ -1,244 +0,0 @@
|
|||||||
/*
|
|
||||||
* JOS file system format
|
|
||||||
*/
|
|
||||||
|
|
||||||
// We don't actually want to define off_t!
|
|
||||||
#define off_t xxx_off_t
|
|
||||||
#define bool xxx_bool
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#undef off_t
|
|
||||||
#undef bool
|
|
||||||
|
|
||||||
// Prevent inc/types.h, included from inc/fs.h,
|
|
||||||
// from attempting to redefine types defined in the host's inttypes.h.
|
|
||||||
#define JOS_INC_TYPES_H
|
|
||||||
// Typedef the types that inc/mmu.h needs.
|
|
||||||
typedef uint32_t physaddr_t;
|
|
||||||
typedef uint32_t off_t;
|
|
||||||
typedef int bool;
|
|
||||||
|
|
||||||
#include <inc/mmu.h>
|
|
||||||
#include <inc/fs.h>
|
|
||||||
|
|
||||||
#define ROUNDUP(n, v) ((n) - 1 + (v) - ((n) - 1) % (v))
|
|
||||||
#define MAX_DIR_ENTS 128
|
|
||||||
|
|
||||||
struct Dir
|
|
||||||
{
|
|
||||||
struct File *f;
|
|
||||||
struct File *ents;
|
|
||||||
int n;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t nblocks;
|
|
||||||
char *diskmap, *diskpos;
|
|
||||||
struct Super *super;
|
|
||||||
uint32_t *bitmap;
|
|
||||||
|
|
||||||
void
|
|
||||||
panic(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
fputc('\n', stderr);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
readn(int f, void *out, size_t n)
|
|
||||||
{
|
|
||||||
size_t p = 0;
|
|
||||||
while (p < n) {
|
|
||||||
ssize_t m = read(f, out + p, n - p);
|
|
||||||
if (m < 0)
|
|
||||||
panic("read: %s", strerror(errno));
|
|
||||||
if (m == 0)
|
|
||||||
panic("read: Unexpected EOF");
|
|
||||||
p += m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
blockof(void *pos)
|
|
||||||
{
|
|
||||||
return ((char*)pos - diskmap) / BLKSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
alloc(uint32_t bytes)
|
|
||||||
{
|
|
||||||
void *start = diskpos;
|
|
||||||
diskpos += ROUNDUP(bytes, BLKSIZE);
|
|
||||||
if (blockof(diskpos) >= nblocks)
|
|
||||||
panic("out of disk blocks");
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
opendisk(const char *name)
|
|
||||||
{
|
|
||||||
int r, diskfd, nbitblocks;
|
|
||||||
|
|
||||||
if ((diskfd = open(name, O_RDWR | O_CREAT, 0666)) < 0)
|
|
||||||
panic("open %s: %s", name, strerror(errno));
|
|
||||||
|
|
||||||
if ((r = ftruncate(diskfd, 0)) < 0
|
|
||||||
|| (r = ftruncate(diskfd, nblocks * BLKSIZE)) < 0)
|
|
||||||
panic("truncate %s: %s", name, strerror(errno));
|
|
||||||
|
|
||||||
if ((diskmap = mmap(NULL, nblocks * BLKSIZE, PROT_READ|PROT_WRITE,
|
|
||||||
MAP_SHARED, diskfd, 0)) == MAP_FAILED)
|
|
||||||
panic("mmap %s: %s", name, strerror(errno));
|
|
||||||
|
|
||||||
close(diskfd);
|
|
||||||
|
|
||||||
diskpos = diskmap;
|
|
||||||
alloc(BLKSIZE);
|
|
||||||
super = alloc(BLKSIZE);
|
|
||||||
super->s_magic = FS_MAGIC;
|
|
||||||
super->s_nblocks = nblocks;
|
|
||||||
super->s_root.f_type = FTYPE_DIR;
|
|
||||||
strcpy(super->s_root.f_name, "/");
|
|
||||||
|
|
||||||
nbitblocks = (nblocks + BLKBITSIZE - 1) / BLKBITSIZE;
|
|
||||||
bitmap = alloc(nbitblocks * BLKSIZE);
|
|
||||||
memset(bitmap, 0xFF, nbitblocks * BLKSIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
finishdisk(void)
|
|
||||||
{
|
|
||||||
int r, i;
|
|
||||||
|
|
||||||
for (i = 0; i < blockof(diskpos); ++i)
|
|
||||||
bitmap[i/32] &= ~(1<<(i%32));
|
|
||||||
|
|
||||||
if ((r = msync(diskmap, nblocks * BLKSIZE, MS_SYNC)) < 0)
|
|
||||||
panic("msync: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
finishfile(struct File *f, uint32_t start, uint32_t len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
f->f_size = len;
|
|
||||||
len = ROUNDUP(len, BLKSIZE);
|
|
||||||
for (i = 0; i < len / BLKSIZE && i < NDIRECT; ++i)
|
|
||||||
f->f_direct[i] = start + i;
|
|
||||||
if (i == NDIRECT) {
|
|
||||||
uint32_t *ind = alloc(BLKSIZE);
|
|
||||||
f->f_indirect = blockof(ind);
|
|
||||||
for (; i < len / BLKSIZE; ++i)
|
|
||||||
ind[i - NDIRECT] = start + i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
startdir(struct File *f, struct Dir *dout)
|
|
||||||
{
|
|
||||||
dout->f = f;
|
|
||||||
dout->ents = malloc(MAX_DIR_ENTS * sizeof *dout->ents);
|
|
||||||
dout->n = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct File *
|
|
||||||
diradd(struct Dir *d, uint32_t type, const char *name)
|
|
||||||
{
|
|
||||||
struct File *out = &d->ents[d->n++];
|
|
||||||
if (d->n > MAX_DIR_ENTS)
|
|
||||||
panic("too many directory entries");
|
|
||||||
strcpy(out->f_name, name);
|
|
||||||
out->f_type = type;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
finishdir(struct Dir *d)
|
|
||||||
{
|
|
||||||
int size = d->n * sizeof(struct File);
|
|
||||||
struct File *start = alloc(size);
|
|
||||||
memmove(start, d->ents, size);
|
|
||||||
finishfile(d->f, blockof(start), ROUNDUP(size, BLKSIZE));
|
|
||||||
free(d->ents);
|
|
||||||
d->ents = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
writefile(struct Dir *dir, const char *name)
|
|
||||||
{
|
|
||||||
int r, fd;
|
|
||||||
struct File *f;
|
|
||||||
struct stat st;
|
|
||||||
const char *last;
|
|
||||||
char *start;
|
|
||||||
|
|
||||||
if ((fd = open(name, O_RDONLY)) < 0)
|
|
||||||
panic("open %s: %s", name, strerror(errno));
|
|
||||||
if ((r = fstat(fd, &st)) < 0)
|
|
||||||
panic("stat %s: %s", name, strerror(errno));
|
|
||||||
if (!S_ISREG(st.st_mode))
|
|
||||||
panic("%s is not a regular file", name);
|
|
||||||
if (st.st_size >= MAXFILESIZE)
|
|
||||||
panic("%s too large", name);
|
|
||||||
|
|
||||||
last = strrchr(name, '/');
|
|
||||||
if (last)
|
|
||||||
last++;
|
|
||||||
else
|
|
||||||
last = name;
|
|
||||||
|
|
||||||
f = diradd(dir, FTYPE_REG, last);
|
|
||||||
start = alloc(st.st_size);
|
|
||||||
readn(fd, start, st.st_size);
|
|
||||||
finishfile(f, blockof(start), st.st_size);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage(void)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage: fsformat fs.img NBLOCKS files...\n");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
char *s;
|
|
||||||
struct Dir root;
|
|
||||||
|
|
||||||
assert(BLKSIZE % sizeof(struct File) == 0);
|
|
||||||
|
|
||||||
if (argc < 3)
|
|
||||||
usage();
|
|
||||||
|
|
||||||
nblocks = strtol(argv[2], &s, 0);
|
|
||||||
if (*s || s == argv[2] || nblocks < 2 || nblocks > 1024)
|
|
||||||
usage();
|
|
||||||
|
|
||||||
opendisk(argv[1]);
|
|
||||||
|
|
||||||
startdir(&super->s_root, &root);
|
|
||||||
for (i = 3; i < argc; i++)
|
|
||||||
writefile(&root, argv[i]);
|
|
||||||
finishdir(&root);
|
|
||||||
|
|
||||||
finishdisk();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
112
fs/ide.c
112
fs/ide.c
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimal PIO-based (non-interrupt-driven) IDE driver code.
|
|
||||||
* For information about what all this IDE/ATA magic means,
|
|
||||||
* see the materials available on the class references page.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fs.h"
|
|
||||||
#include <inc/x86.h>
|
|
||||||
|
|
||||||
#define IDE_BSY 0x80
|
|
||||||
#define IDE_DRDY 0x40
|
|
||||||
#define IDE_DF 0x20
|
|
||||||
#define IDE_ERR 0x01
|
|
||||||
|
|
||||||
static int diskno = 1;
|
|
||||||
|
|
||||||
static int
|
|
||||||
ide_wait_ready(bool check_error)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
while (((r = inb(0x1F7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY)
|
|
||||||
/* do nothing */;
|
|
||||||
|
|
||||||
if (check_error && (r & (IDE_DF|IDE_ERR)) != 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ide_probe_disk1(void)
|
|
||||||
{
|
|
||||||
int r, x;
|
|
||||||
|
|
||||||
// wait for Device 0 to be ready
|
|
||||||
ide_wait_ready(0);
|
|
||||||
|
|
||||||
// switch to Device 1
|
|
||||||
outb(0x1F6, 0xE0 | (1<<4));
|
|
||||||
|
|
||||||
// check for Device 1 to be ready for a while
|
|
||||||
for (x = 0;
|
|
||||||
x < 1000 && ((r = inb(0x1F7)) & (IDE_BSY|IDE_DF|IDE_ERR)) != 0;
|
|
||||||
x++)
|
|
||||||
/* do nothing */;
|
|
||||||
|
|
||||||
// switch back to Device 0
|
|
||||||
outb(0x1F6, 0xE0 | (0<<4));
|
|
||||||
|
|
||||||
cprintf("Device 1 presence: %d\n", (x < 1000));
|
|
||||||
return (x < 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ide_set_disk(int d)
|
|
||||||
{
|
|
||||||
if (d != 0 && d != 1)
|
|
||||||
panic("bad disk number");
|
|
||||||
diskno = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
ide_read(uint32_t secno, void *dst, size_t nsecs)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(nsecs <= 256);
|
|
||||||
|
|
||||||
ide_wait_ready(0);
|
|
||||||
|
|
||||||
outb(0x1F2, nsecs);
|
|
||||||
outb(0x1F3, secno & 0xFF);
|
|
||||||
outb(0x1F4, (secno >> 8) & 0xFF);
|
|
||||||
outb(0x1F5, (secno >> 16) & 0xFF);
|
|
||||||
outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F));
|
|
||||||
outb(0x1F7, 0x20); // CMD 0x20 means read sector
|
|
||||||
|
|
||||||
for (; nsecs > 0; nsecs--, dst += SECTSIZE) {
|
|
||||||
if ((r = ide_wait_ready(1)) < 0)
|
|
||||||
return r;
|
|
||||||
insl(0x1F0, dst, SECTSIZE/4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
ide_write(uint32_t secno, const void *src, size_t nsecs)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(nsecs <= 256);
|
|
||||||
|
|
||||||
ide_wait_ready(0);
|
|
||||||
|
|
||||||
outb(0x1F2, nsecs);
|
|
||||||
outb(0x1F3, secno & 0xFF);
|
|
||||||
outb(0x1F4, (secno >> 8) & 0xFF);
|
|
||||||
outb(0x1F5, (secno >> 16) & 0xFF);
|
|
||||||
outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F));
|
|
||||||
outb(0x1F7, 0x30); // CMD 0x30 means write sector
|
|
||||||
|
|
||||||
for (; nsecs > 0; nsecs--, src += SECTSIZE) {
|
|
||||||
if ((r = ide_wait_ready(1)) < 0)
|
|
||||||
return r;
|
|
||||||
outsl(0x1F0, src, SECTSIZE/4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
12
fs/lorem
12
fs/lorem
@@ -1,12 +0,0 @@
|
|||||||
Lorem ipsum dolor sit amet, consectetur
|
|
||||||
adipisicing elit, sed do eiusmod tempor
|
|
||||||
incididunt ut labore et dolore magna
|
|
||||||
aliqua. Ut enim ad minim veniam, quis
|
|
||||||
nostrud exercitation ullamco laboris
|
|
||||||
nisi ut aliquip ex ea commodo consequat.
|
|
||||||
Duis aute irure dolor in reprehenderit
|
|
||||||
in voluptate velit esse cillum dolore eu
|
|
||||||
fugiat nulla pariatur. Excepteur sint
|
|
||||||
occaecat cupidatat non proident, sunt in
|
|
||||||
culpa qui officia deserunt mollit anim
|
|
||||||
id est laborum.
|
|
||||||
4
fs/motd
4
fs/motd
@@ -1,4 +0,0 @@
|
|||||||
This is /motd, the message of the day.
|
|
||||||
|
|
||||||
Welcome to the JOS kernel, now with a file system!
|
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
This is the NEW message of the day!
|
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
echo This is from the script.
|
|
||||||
cat lorem | num | cat
|
|
||||||
echo These are my file descriptors.
|
|
||||||
lsfd -1
|
|
||||||
echo This is the end of the script.
|
|
||||||
361
fs/serv.c
361
fs/serv.c
@@ -1,361 +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;
|
|
||||||
struct OpenFile* open_file;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
|
|
||||||
|
|
||||||
if((r = openfile_lookup(envid, req->req_fileid, &open_file)) < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if((r = file_read(open_file->o_file, ret->ret_buf, req->req_n, open_file->o_fd->fd_offset)) < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
open_file->o_fd->fd_offset += r;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
struct OpenFile* open_file;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if((r = openfile_lookup(envid, req->req_fileid, &open_file)) < 0)
|
|
||||||
return r;
|
|
||||||
if((r = file_write(open_file->o_file, req->req_buf, req->req_n, open_file->o_fd->fd_offset)) < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
open_file->o_fd->fd_offset += r;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
echo hello world | cat
|
|
||||||
cat lorem
|
|
||||||
cat lorem |num
|
|
||||||
cat lorem |num |num |num |num |num
|
|
||||||
lsfd -1
|
|
||||||
cat script
|
|
||||||
sh <script
|
|
||||||
190
grade-lab4
190
grade-lab4
@@ -1,190 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import re
|
|
||||||
from gradelib import *
|
|
||||||
|
|
||||||
r = Runner(save("jos.out"),
|
|
||||||
stop_breakpoint("readline"))
|
|
||||||
|
|
||||||
def E(s, trim=False):
|
|
||||||
"""Expand $En in s to the environment ID of the n'th user
|
|
||||||
environment."""
|
|
||||||
|
|
||||||
tmpl = "%x" if trim else "%08x"
|
|
||||||
return re.sub(r"\$E([0-9]+)",
|
|
||||||
lambda m: tmpl % (0x1000 + int(m.group(1))-1), s)
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_dumbfork():
|
|
||||||
r.user_test("dumbfork")
|
|
||||||
r.match(E(".00000000. new env $E1"),
|
|
||||||
E(".$E1. new env $E2"),
|
|
||||||
"0: I am the parent.",
|
|
||||||
"9: I am the parent.",
|
|
||||||
"0: I am the child.",
|
|
||||||
"9: I am the child.",
|
|
||||||
"19: I am the child.",
|
|
||||||
E(".$E1. exiting gracefully"),
|
|
||||||
E(".$E1. free env $E1"),
|
|
||||||
E(".$E2. exiting gracefully"),
|
|
||||||
E(".$E2. free env $E2"))
|
|
||||||
|
|
||||||
end_part("A")
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultread():
|
|
||||||
r.user_test("faultread")
|
|
||||||
r.match(E(".$E1. user fault va 00000000 ip 008....."),
|
|
||||||
"TRAP frame at 0xf....... from CPU .",
|
|
||||||
" trap 0x0000000e Page Fault",
|
|
||||||
" err 0x00000004.*",
|
|
||||||
E(".$E1. free env $E1"),
|
|
||||||
no=["I read ........ from location 0."])
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultwrite():
|
|
||||||
r.user_test("faultwrite")
|
|
||||||
r.match(E(".$E1. user fault va 00000000 ip 008....."),
|
|
||||||
"TRAP frame at 0xf....... from CPU .",
|
|
||||||
" trap 0x0000000e Page Fault",
|
|
||||||
" err 0x00000006.*",
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultdie():
|
|
||||||
r.user_test("faultdie")
|
|
||||||
r.match("i faulted at va deadbeef, err 6",
|
|
||||||
E(".$E1. exiting gracefully"),
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultregs():
|
|
||||||
r.user_test("faultregs")
|
|
||||||
r.match("Registers in UTrapframe OK",
|
|
||||||
"Registers after page-fault OK",
|
|
||||||
no=["Registers in UTrapframe MISMATCH",
|
|
||||||
"Registers after page-fault MISMATCH"])
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultalloc():
|
|
||||||
r.user_test("faultalloc")
|
|
||||||
r.match("fault deadbeef",
|
|
||||||
"this string was faulted in at deadbeef",
|
|
||||||
"fault cafebffe",
|
|
||||||
"fault cafec000",
|
|
||||||
"this string was faulted in at cafebffe",
|
|
||||||
E(".$E1. exiting gracefully"),
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultallocbad():
|
|
||||||
r.user_test("faultallocbad")
|
|
||||||
r.match(E(".$E1. user_mem_check assertion failure for va deadbeef"),
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultnostack():
|
|
||||||
r.user_test("faultnostack")
|
|
||||||
r.match(E(".$E1. user_mem_check assertion failure for va eebfff.."),
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultbadhandler():
|
|
||||||
r.user_test("faultbadhandler")
|
|
||||||
r.match(E(".$E1. user_mem_check assertion failure for va (deadb|eebfe)..."),
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_faultevilhandler():
|
|
||||||
r.user_test("faultevilhandler")
|
|
||||||
r.match(E(".$E1. user_mem_check assertion failure for va (f0100|eebfe)..."),
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_forktree():
|
|
||||||
r.user_test("forktree")
|
|
||||||
r.match("....: I am .0.",
|
|
||||||
"....: I am .1.",
|
|
||||||
"....: I am .000.",
|
|
||||||
"....: I am .100.",
|
|
||||||
"....: I am .110.",
|
|
||||||
"....: I am .111.",
|
|
||||||
"....: I am .011.",
|
|
||||||
"....: I am .001.",
|
|
||||||
E(".$E1. exiting gracefully"),
|
|
||||||
E(".$E2. exiting gracefully"),
|
|
||||||
".0000200.. exiting gracefully",
|
|
||||||
".0000200.. free env 0000200.")
|
|
||||||
|
|
||||||
end_part("B")
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_spin():
|
|
||||||
r.user_test("spin")
|
|
||||||
r.match(E(".00000000. new env $E1"),
|
|
||||||
"I am the parent. Forking the child...",
|
|
||||||
E(".$E1. new env $E2"),
|
|
||||||
"I am the parent. Running the child...",
|
|
||||||
"I am the child. Spinning...",
|
|
||||||
"I am the parent. Killing the child...",
|
|
||||||
E(".$E1. destroying $E2"),
|
|
||||||
E(".$E1. free env $E2"),
|
|
||||||
E(".$E1. exiting gracefully"),
|
|
||||||
E(".$E1. free env $E1"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_stresssched():
|
|
||||||
r.user_test("stresssched", make_args=["CPUS=4"])
|
|
||||||
r.match(".000010... stresssched on CPU 0",
|
|
||||||
".000010... stresssched on CPU 1",
|
|
||||||
".000010... stresssched on CPU 2",
|
|
||||||
".000010... stresssched on CPU 3",
|
|
||||||
no=[".*ran on two CPUs at once"])
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_sendpage():
|
|
||||||
r.user_test("sendpage", make_args=["CPUS=2"])
|
|
||||||
r.match(".00000000. new env 00001000",
|
|
||||||
E(".00000000. new env $E1"),
|
|
||||||
E(".$E1. new env $E2"),
|
|
||||||
E("$E1 got message: hello child environment! how are you?", trim=True),
|
|
||||||
E("child received correct message", trim=True),
|
|
||||||
E("$E2 got message: hello parent environment! I'm good", trim=True),
|
|
||||||
E("parent received correct message", trim=True),
|
|
||||||
E(".$E1. exiting gracefully"),
|
|
||||||
E(".$E1. free env $E1"),
|
|
||||||
E(".$E2. exiting gracefully"),
|
|
||||||
E(".$E2. free env $E2"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_pingpong():
|
|
||||||
r.user_test("pingpong", make_args=["CPUS=4"])
|
|
||||||
r.match(E(".00000000. new env $E1"),
|
|
||||||
E(".$E1. new env $E2"),
|
|
||||||
E("send 0 from $E1 to $E2", trim=True),
|
|
||||||
E("$E2 got 0 from $E1", trim=True),
|
|
||||||
E("$E1 got 1 from $E2", trim=True),
|
|
||||||
E("$E2 got 8 from $E1", trim=True),
|
|
||||||
E("$E1 got 9 from $E2", trim=True),
|
|
||||||
E("$E2 got 10 from $E1", trim=True),
|
|
||||||
E(".$E1. exiting gracefully"),
|
|
||||||
E(".$E1. free env $E1"),
|
|
||||||
E(".$E2. exiting gracefully"),
|
|
||||||
E(".$E2. free env $E2"))
|
|
||||||
|
|
||||||
@test(5)
|
|
||||||
def test_primes():
|
|
||||||
r.user_test("primes", stop_on_line("CPU .: 1877"), stop_on_line(".*panic"),
|
|
||||||
make_args=["CPUS=4"], timeout=60)
|
|
||||||
r.match(E(".00000000. new env $E1"),
|
|
||||||
E(".$E1. new env $E2"),
|
|
||||||
E("CPU .: 2 .$E2. new env $E3"),
|
|
||||||
E("CPU .: 3 .$E3. new env $E4"),
|
|
||||||
E("CPU .: 5 .$E4. new env $E5"),
|
|
||||||
E("CPU .: 7 .$E5. new env $E6"),
|
|
||||||
E("CPU .: 11 .$E6. new env $E7"),
|
|
||||||
E("CPU .: 1877 .$E289. new env $E290"))
|
|
||||||
|
|
||||||
end_part("C")
|
|
||||||
|
|
||||||
run_tests()
|
|
||||||
112
grade-lab5
112
grade-lab5
@@ -1,112 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
from gradelib import *
|
|
||||||
|
|
||||||
r = Runner(save("jos.out"),
|
|
||||||
stop_breakpoint("readline"))
|
|
||||||
|
|
||||||
def matchtest(parent, name, *args, **kw):
|
|
||||||
def do_test():
|
|
||||||
r.match(*args, **kw)
|
|
||||||
test(5, name, parent=parent)(do_test)
|
|
||||||
|
|
||||||
@test(5, "internal FS tests [fs/test.c]")
|
|
||||||
def test_fs():
|
|
||||||
r.user_test("hello")
|
|
||||||
matchtest(test_fs, "fs i/o",
|
|
||||||
"FS can do I/O")
|
|
||||||
matchtest(test_fs, "check_bc",
|
|
||||||
"block cache is good")
|
|
||||||
matchtest(test_fs, "check_super",
|
|
||||||
"superblock is good")
|
|
||||||
matchtest(test_fs, "check_bitmap",
|
|
||||||
"bitmap is good")
|
|
||||||
matchtest(test_fs, "alloc_block",
|
|
||||||
"alloc_block is good")
|
|
||||||
matchtest(test_fs, "file_open",
|
|
||||||
"file_open is good")
|
|
||||||
matchtest(test_fs, "file_get_block",
|
|
||||||
"file_get_block is good")
|
|
||||||
matchtest(test_fs, "file_flush/file_truncate/file rewrite",
|
|
||||||
"file_flush is good",
|
|
||||||
"file_truncate is good",
|
|
||||||
"file rewrite is good")
|
|
||||||
|
|
||||||
@test(10, "testfile")
|
|
||||||
def test_testfile():
|
|
||||||
r.user_test("testfile")
|
|
||||||
matchtest(test_testfile, "serve_open/file_stat/file_close",
|
|
||||||
"serve_open is good",
|
|
||||||
"file_stat is good",
|
|
||||||
"file_close is good",
|
|
||||||
"stale fileid is good")
|
|
||||||
matchtest(test_testfile, "file_read",
|
|
||||||
"file_read is good")
|
|
||||||
matchtest(test_testfile, "file_write",
|
|
||||||
"file_write is good")
|
|
||||||
matchtest(test_testfile, "file_read after file_write",
|
|
||||||
"file_read after file_write is good")
|
|
||||||
matchtest(test_testfile, "open",
|
|
||||||
"open is good")
|
|
||||||
matchtest(test_testfile, "large file",
|
|
||||||
"large file is good")
|
|
||||||
|
|
||||||
@test(10, "spawn via spawnhello")
|
|
||||||
def test_spawn():
|
|
||||||
r.user_test("spawnhello")
|
|
||||||
r.match('i am parent environment 00001001',
|
|
||||||
'hello, world',
|
|
||||||
'i am environment 00001002',
|
|
||||||
'No runnable environments in the system!')
|
|
||||||
|
|
||||||
@test(5, "Protection I/O space")
|
|
||||||
def test_faultio():
|
|
||||||
r.user_test("spawnfaultio")
|
|
||||||
r.match('TRAP')
|
|
||||||
|
|
||||||
@test(10, "PTE_SHARE [testpteshare]")
|
|
||||||
def test_pte_share():
|
|
||||||
r.user_test("testpteshare")
|
|
||||||
r.match('fork handles PTE_SHARE right',
|
|
||||||
'spawn handles PTE_SHARE right')
|
|
||||||
|
|
||||||
@test(5, "PTE_SHARE [testfdsharing]")
|
|
||||||
def test_fd_share():
|
|
||||||
r.user_test("testfdsharing")
|
|
||||||
r.match('read in child succeeded',
|
|
||||||
'read in parent succeeded')
|
|
||||||
|
|
||||||
@test(10, "start the shell [icode]")
|
|
||||||
def test_icode():
|
|
||||||
r.user_test("icode")
|
|
||||||
r.match('icode: read /motd',
|
|
||||||
'This is /motd, the message of the day.',
|
|
||||||
'icode: spawn /init',
|
|
||||||
'init: running',
|
|
||||||
'init: data seems okay',
|
|
||||||
'icode: exiting',
|
|
||||||
'init: bss seems okay',
|
|
||||||
"init: args: 'init' 'initarg1' 'initarg2'",
|
|
||||||
'init: running sh',
|
|
||||||
'\$ ')
|
|
||||||
|
|
||||||
@test(15)
|
|
||||||
def test_testshell():
|
|
||||||
r.user_test("testshell", timeout=60)
|
|
||||||
r.match("shell ran correctly")
|
|
||||||
|
|
||||||
def gen_primes(n):
|
|
||||||
rest = range(2, n)
|
|
||||||
while rest:
|
|
||||||
yield rest[0]
|
|
||||||
rest = [n for n in rest if n % rest[0]]
|
|
||||||
|
|
||||||
@test(10)
|
|
||||||
def test_primespipe():
|
|
||||||
r.user_test("primespipe", stop_on_line("[0-9]{4}$"), timeout=120)
|
|
||||||
primes = set(gen_primes(1000))
|
|
||||||
nonprimes = set(range(1000)) - primes
|
|
||||||
r.match(no=["%d$" % np for np in nonprimes],
|
|
||||||
*["%d$" % p for p in primes])
|
|
||||||
|
|
||||||
run_tests()
|
|
||||||
82
inc/args.h
82
inc/args.h
@@ -1,82 +0,0 @@
|
|||||||
#ifndef JOS_INC_ARGS_H
|
|
||||||
#define JOS_INC_ARGS_H
|
|
||||||
|
|
||||||
struct Argstate;
|
|
||||||
|
|
||||||
// JOS command-line parsing functions.
|
|
||||||
|
|
||||||
// Initializes the Argstate buffer from argc and argv.
|
|
||||||
// (Note: it is safe to use a 'const char **' for argv.)
|
|
||||||
void argstart(int *argc, char **argv, struct Argstate *args);
|
|
||||||
|
|
||||||
// Returns the next flag in the argument list,
|
|
||||||
// or -1 if there are no more flags.
|
|
||||||
//
|
|
||||||
// Flags stop at a non-flag (anything that doesn't start with '-'),
|
|
||||||
// at the end of the argument list, before "-", or after "--",
|
|
||||||
// whichever comes first. Any "--" argument is not returned.
|
|
||||||
//
|
|
||||||
// Consumes arguments from the argc/argv array passed in to argstart.
|
|
||||||
// If you argstart with an argc/argv array of ["sh", "-i", "foo"],
|
|
||||||
// the first call to argnext will return 'i' and change the array to
|
|
||||||
// ["sh", "foo"]. Thus, when argnext returns -1, the argc/argv array
|
|
||||||
// contains just the non-flag arguments.
|
|
||||||
int argnext(struct Argstate *);
|
|
||||||
|
|
||||||
// Returns the next value for the current flag, or 0 if it has no value.
|
|
||||||
// For example, given an argument list ["-fval1", "val2", "val3"],
|
|
||||||
// a call to argnext() will return 'f', after which repeated calls to
|
|
||||||
// argnextvalue will return "val1", "val2", and "val3".
|
|
||||||
// Consumes arguments from the argc/argv array.
|
|
||||||
char *argnextvalue(struct Argstate *);
|
|
||||||
|
|
||||||
// Returns the current flag's value, or 0 if it has no value.
|
|
||||||
// Behaves like argnextvalue, except that repeated calls to argvalue will
|
|
||||||
// return the same value.
|
|
||||||
char *argvalue(struct Argstate *);
|
|
||||||
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// #include <inc/lib.h>
|
|
||||||
//
|
|
||||||
// void
|
|
||||||
// umain(int argc, char **argv)
|
|
||||||
// {
|
|
||||||
// int i;
|
|
||||||
// struct Argstate args;
|
|
||||||
//
|
|
||||||
// argstart(&argc, argv, &args);
|
|
||||||
// while ((i = argnext(&args)) >= 0)
|
|
||||||
// switch (i) {
|
|
||||||
// case 'r':
|
|
||||||
// case 'x':
|
|
||||||
// cprintf("'-%c' flag\n", i);
|
|
||||||
// break;
|
|
||||||
// case 'f':
|
|
||||||
// cprintf("'-f %s' flag\n", argvalue(&args));
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// cprintf("unknown flag\n");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (i = 1; i < argc; i++)
|
|
||||||
// cprintf("argument '%s'\n", argv[i]);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// If this program is run with the arguments
|
|
||||||
// ["-rx", "-f", "foo", "--", "-r", "duh"]
|
|
||||||
// it will print out
|
|
||||||
// '-r' flag
|
|
||||||
// '-x' flag
|
|
||||||
// '-f foo' flag
|
|
||||||
// argument '-r'
|
|
||||||
// argument 'duh'
|
|
||||||
|
|
||||||
struct Argstate {
|
|
||||||
int *argc;
|
|
||||||
const char **argv;
|
|
||||||
const char *curarg;
|
|
||||||
const char *argvalue;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
12
inc/env.h
12
inc/env.h
@@ -41,7 +41,6 @@ enum {
|
|||||||
// Special environment types
|
// Special environment types
|
||||||
enum EnvType {
|
enum EnvType {
|
||||||
ENV_TYPE_USER = 0,
|
ENV_TYPE_USER = 0,
|
||||||
ENV_TYPE_FS, // File system server
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Env {
|
struct Env {
|
||||||
@@ -52,20 +51,9 @@ struct Env {
|
|||||||
enum EnvType env_type; // Indicates special system environments
|
enum EnvType env_type; // Indicates special system environments
|
||||||
unsigned env_status; // Status of the environment
|
unsigned env_status; // Status of the environment
|
||||||
uint32_t env_runs; // Number of times environment has run
|
uint32_t env_runs; // Number of times environment has run
|
||||||
int env_cpunum; // The CPU that the env is running on
|
|
||||||
|
|
||||||
// Address space
|
// Address space
|
||||||
pde_t *env_pgdir; // Kernel virtual address of page dir
|
pde_t *env_pgdir; // Kernel virtual address of page dir
|
||||||
|
|
||||||
// Exception handling
|
|
||||||
void *env_pgfault_upcall; // Page fault upcall entry point
|
|
||||||
|
|
||||||
// Lab 4 IPC
|
|
||||||
bool env_ipc_recving; // Env is blocked receiving
|
|
||||||
void *env_ipc_dstva; // VA at which to map received page
|
|
||||||
uint32_t env_ipc_value; // Data value sent to us
|
|
||||||
envid_t env_ipc_from; // envid of the sender
|
|
||||||
int env_ipc_perm; // Perm of page mapping received
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !JOS_INC_ENV_H
|
#endif // !JOS_INC_ENV_H
|
||||||
|
|||||||
12
inc/error.h
12
inc/error.h
@@ -14,18 +14,6 @@ enum {
|
|||||||
// the maximum allowed
|
// the maximum allowed
|
||||||
E_FAULT , // Memory fault
|
E_FAULT , // Memory fault
|
||||||
|
|
||||||
E_IPC_NOT_RECV , // Attempt to send to env that is not recving
|
|
||||||
E_EOF , // Unexpected end of file
|
|
||||||
|
|
||||||
// File system error codes -- only seen in user-level
|
|
||||||
E_NO_DISK , // No free space left on disk
|
|
||||||
E_MAX_OPEN , // Too many files are open
|
|
||||||
E_NOT_FOUND , // File or block not found
|
|
||||||
E_BAD_PATH , // Bad path
|
|
||||||
E_FILE_EXISTS , // File already exists
|
|
||||||
E_NOT_EXEC , // File not a valid executable
|
|
||||||
E_NOT_SUPP , // Operation not supported
|
|
||||||
|
|
||||||
MAXERROR
|
MAXERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
58
inc/fd.h
58
inc/fd.h
@@ -1,58 +0,0 @@
|
|||||||
// Public definitions for the POSIX-like file descriptor emulation layer
|
|
||||||
// that our user-land support library implements for the use of applications.
|
|
||||||
// See the code in the lib directory for the implementation details.
|
|
||||||
|
|
||||||
#ifndef JOS_INC_FD_H
|
|
||||||
#define JOS_INC_FD_H
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
#include <inc/fs.h>
|
|
||||||
|
|
||||||
struct Fd;
|
|
||||||
struct Stat;
|
|
||||||
struct Dev;
|
|
||||||
|
|
||||||
// Per-device-class file descriptor operations
|
|
||||||
struct Dev {
|
|
||||||
int dev_id;
|
|
||||||
const char *dev_name;
|
|
||||||
ssize_t (*dev_read)(struct Fd *fd, void *buf, size_t len);
|
|
||||||
ssize_t (*dev_write)(struct Fd *fd, const void *buf, size_t len);
|
|
||||||
int (*dev_close)(struct Fd *fd);
|
|
||||||
int (*dev_stat)(struct Fd *fd, struct Stat *stat);
|
|
||||||
int (*dev_trunc)(struct Fd *fd, off_t length);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FdFile {
|
|
||||||
int id;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Fd {
|
|
||||||
int fd_dev_id;
|
|
||||||
off_t fd_offset;
|
|
||||||
int fd_omode;
|
|
||||||
union {
|
|
||||||
// File server files
|
|
||||||
struct FdFile fd_file;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Stat {
|
|
||||||
char st_name[MAXNAMELEN];
|
|
||||||
off_t st_size;
|
|
||||||
int st_isdir;
|
|
||||||
struct Dev *st_dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
char* fd2data(struct Fd *fd);
|
|
||||||
int fd2num(struct Fd *fd);
|
|
||||||
int fd_alloc(struct Fd **fd_store);
|
|
||||||
int fd_close(struct Fd *fd, bool must_exist);
|
|
||||||
int fd_lookup(int fdnum, struct Fd **fd_store);
|
|
||||||
int dev_lookup(int devid, struct Dev **dev_store);
|
|
||||||
|
|
||||||
extern struct Dev devfile;
|
|
||||||
extern struct Dev devcons;
|
|
||||||
extern struct Dev devpipe;
|
|
||||||
|
|
||||||
#endif // not JOS_INC_FD_H
|
|
||||||
116
inc/fs.h
116
inc/fs.h
@@ -1,116 +0,0 @@
|
|||||||
// See COPYRIGHT for copyright information.
|
|
||||||
|
|
||||||
#ifndef JOS_INC_FS_H
|
|
||||||
#define JOS_INC_FS_H
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
#include <inc/mmu.h>
|
|
||||||
|
|
||||||
// File nodes (both in-memory and on-disk)
|
|
||||||
|
|
||||||
// Bytes per file system block - same as page size
|
|
||||||
#define BLKSIZE PGSIZE
|
|
||||||
#define BLKBITSIZE (BLKSIZE * 8)
|
|
||||||
|
|
||||||
// Maximum size of a filename (a single path component), including null
|
|
||||||
// Must be a multiple of 4
|
|
||||||
#define MAXNAMELEN 128
|
|
||||||
|
|
||||||
// Maximum size of a complete pathname, including null
|
|
||||||
#define MAXPATHLEN 1024
|
|
||||||
|
|
||||||
// Number of block pointers in a File descriptor
|
|
||||||
#define NDIRECT 10
|
|
||||||
// Number of direct block pointers in an indirect block
|
|
||||||
#define NINDIRECT (BLKSIZE / 4)
|
|
||||||
|
|
||||||
#define MAXFILESIZE ((NDIRECT + NINDIRECT) * BLKSIZE)
|
|
||||||
|
|
||||||
struct File {
|
|
||||||
char f_name[MAXNAMELEN]; // filename
|
|
||||||
off_t f_size; // file size in bytes
|
|
||||||
uint32_t f_type; // file type
|
|
||||||
|
|
||||||
// Block pointers.
|
|
||||||
// A block is allocated iff its value is != 0.
|
|
||||||
uint32_t f_direct[NDIRECT]; // direct blocks
|
|
||||||
uint32_t f_indirect; // indirect block
|
|
||||||
|
|
||||||
// Pad out to 256 bytes; must do arithmetic in case we're compiling
|
|
||||||
// fsformat on a 64-bit machine.
|
|
||||||
uint8_t f_pad[256 - MAXNAMELEN - 8 - 4*NDIRECT - 4];
|
|
||||||
} __attribute__((packed)); // required only on some 64-bit machines
|
|
||||||
|
|
||||||
// An inode block contains exactly BLKFILES 'struct File's
|
|
||||||
#define BLKFILES (BLKSIZE / sizeof(struct File))
|
|
||||||
|
|
||||||
// File types
|
|
||||||
#define FTYPE_REG 0 // Regular file
|
|
||||||
#define FTYPE_DIR 1 // Directory
|
|
||||||
|
|
||||||
|
|
||||||
// File system super-block (both in-memory and on-disk)
|
|
||||||
|
|
||||||
#define FS_MAGIC 0x4A0530AE // related vaguely to 'J\0S!'
|
|
||||||
|
|
||||||
struct Super {
|
|
||||||
uint32_t s_magic; // Magic number: FS_MAGIC
|
|
||||||
uint32_t s_nblocks; // Total number of blocks on disk
|
|
||||||
struct File s_root; // Root directory node
|
|
||||||
};
|
|
||||||
|
|
||||||
// Definitions for requests from clients to file system
|
|
||||||
enum {
|
|
||||||
FSREQ_OPEN = 1,
|
|
||||||
FSREQ_SET_SIZE,
|
|
||||||
// Read returns a Fsret_read on the request page
|
|
||||||
FSREQ_READ,
|
|
||||||
FSREQ_WRITE,
|
|
||||||
// Stat returns a Fsret_stat on the request page
|
|
||||||
FSREQ_STAT,
|
|
||||||
FSREQ_FLUSH,
|
|
||||||
FSREQ_REMOVE,
|
|
||||||
FSREQ_SYNC
|
|
||||||
};
|
|
||||||
|
|
||||||
union Fsipc {
|
|
||||||
struct Fsreq_open {
|
|
||||||
char req_path[MAXPATHLEN];
|
|
||||||
int req_omode;
|
|
||||||
} open;
|
|
||||||
struct Fsreq_set_size {
|
|
||||||
int req_fileid;
|
|
||||||
off_t req_size;
|
|
||||||
} set_size;
|
|
||||||
struct Fsreq_read {
|
|
||||||
int req_fileid;
|
|
||||||
size_t req_n;
|
|
||||||
} read;
|
|
||||||
struct Fsret_read {
|
|
||||||
char ret_buf[PGSIZE];
|
|
||||||
} readRet;
|
|
||||||
struct Fsreq_write {
|
|
||||||
int req_fileid;
|
|
||||||
size_t req_n;
|
|
||||||
char req_buf[PGSIZE - (sizeof(int) + sizeof(size_t))];
|
|
||||||
} write;
|
|
||||||
struct Fsreq_stat {
|
|
||||||
int req_fileid;
|
|
||||||
} stat;
|
|
||||||
struct Fsret_stat {
|
|
||||||
char ret_name[MAXNAMELEN];
|
|
||||||
off_t ret_size;
|
|
||||||
int ret_isdir;
|
|
||||||
} statRet;
|
|
||||||
struct Fsreq_flush {
|
|
||||||
int req_fileid;
|
|
||||||
} flush;
|
|
||||||
struct Fsreq_remove {
|
|
||||||
char req_path[MAXPATHLEN];
|
|
||||||
} remove;
|
|
||||||
|
|
||||||
// Ensure Fsipc is one page
|
|
||||||
char _pad[PGSIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* !JOS_INC_FS_H */
|
|
||||||
75
inc/lib.h
75
inc/lib.h
@@ -16,10 +16,6 @@
|
|||||||
#include <inc/env.h>
|
#include <inc/env.h>
|
||||||
#include <inc/memlayout.h>
|
#include <inc/memlayout.h>
|
||||||
#include <inc/syscall.h>
|
#include <inc/syscall.h>
|
||||||
#include <inc/trap.h>
|
|
||||||
#include <inc/fs.h>
|
|
||||||
#include <inc/fd.h>
|
|
||||||
#include <inc/args.h>
|
|
||||||
|
|
||||||
#define USED(x) (void)(x)
|
#define USED(x) (void)(x)
|
||||||
|
|
||||||
@@ -35,9 +31,6 @@ extern const volatile struct PageInfo pages[];
|
|||||||
// exit.c
|
// exit.c
|
||||||
void exit(void);
|
void exit(void);
|
||||||
|
|
||||||
// pgfault.c
|
|
||||||
void set_pgfault_handler(void (*handler)(struct UTrapframe *utf));
|
|
||||||
|
|
||||||
// readline.c
|
// readline.c
|
||||||
char* readline(const char *buf);
|
char* readline(const char *buf);
|
||||||
|
|
||||||
@@ -46,76 +39,8 @@ void sys_cputs(const char *string, size_t len);
|
|||||||
int sys_cgetc(void);
|
int sys_cgetc(void);
|
||||||
envid_t sys_getenvid(void);
|
envid_t sys_getenvid(void);
|
||||||
int sys_env_destroy(envid_t);
|
int sys_env_destroy(envid_t);
|
||||||
void sys_yield(void);
|
|
||||||
static envid_t sys_exofork(void);
|
|
||||||
int sys_env_set_status(envid_t env, int status);
|
|
||||||
int sys_env_set_trapframe(envid_t env, struct Trapframe *tf);
|
|
||||||
int sys_env_set_pgfault_upcall(envid_t env, void *upcall);
|
|
||||||
int sys_page_alloc(envid_t env, void *pg, int perm);
|
|
||||||
int sys_page_map(envid_t src_env, void *src_pg,
|
|
||||||
envid_t dst_env, void *dst_pg, int perm);
|
|
||||||
int sys_page_unmap(envid_t env, void *pg);
|
|
||||||
int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm);
|
|
||||||
int sys_ipc_recv(void *rcv_pg);
|
|
||||||
|
|
||||||
// This must be inlined. Exercise for reader: why?
|
|
||||||
static inline envid_t __attribute__((always_inline))
|
|
||||||
sys_exofork(void)
|
|
||||||
{
|
|
||||||
envid_t ret;
|
|
||||||
asm volatile("int %2"
|
|
||||||
: "=a" (ret)
|
|
||||||
: "a" (SYS_exofork), "i" (T_SYSCALL));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ipc.c
|
|
||||||
void ipc_send(envid_t to_env, uint32_t value, void *pg, int perm);
|
|
||||||
int32_t ipc_recv(envid_t *from_env_store, void *pg, int *perm_store);
|
|
||||||
envid_t ipc_find_env(enum EnvType type);
|
|
||||||
|
|
||||||
// fork.c
|
|
||||||
#define PTE_SHARE 0x400
|
|
||||||
envid_t fork(void);
|
|
||||||
envid_t sfork(void); // Challenge!
|
|
||||||
|
|
||||||
// fd.c
|
|
||||||
int close(int fd);
|
|
||||||
ssize_t read(int fd, void *buf, size_t nbytes);
|
|
||||||
ssize_t write(int fd, const void *buf, size_t nbytes);
|
|
||||||
int seek(int fd, off_t offset);
|
|
||||||
void close_all(void);
|
|
||||||
ssize_t readn(int fd, void *buf, size_t nbytes);
|
|
||||||
int dup(int oldfd, int newfd);
|
|
||||||
int fstat(int fd, struct Stat *statbuf);
|
|
||||||
int stat(const char *path, struct Stat *statbuf);
|
|
||||||
|
|
||||||
// file.c
|
|
||||||
int open(const char *path, int mode);
|
|
||||||
int ftruncate(int fd, off_t size);
|
|
||||||
int remove(const char *path);
|
|
||||||
int sync(void);
|
|
||||||
|
|
||||||
// pageref.c
|
|
||||||
int pageref(void *addr);
|
|
||||||
|
|
||||||
|
|
||||||
// spawn.c
|
|
||||||
envid_t spawn(const char *program, const char **argv);
|
|
||||||
envid_t spawnl(const char *program, const char *arg0, ...);
|
|
||||||
|
|
||||||
// console.c
|
|
||||||
void cputchar(int c);
|
|
||||||
int getchar(void);
|
|
||||||
int iscons(int fd);
|
|
||||||
int opencons(void);
|
|
||||||
|
|
||||||
// pipe.c
|
|
||||||
int pipe(int pipefds[2]);
|
|
||||||
int pipeisclosed(int pipefd);
|
|
||||||
|
|
||||||
// wait.c
|
|
||||||
void wait(envid_t env);
|
|
||||||
|
|
||||||
/* File open modes */
|
/* File open modes */
|
||||||
#define O_RDONLY 0x0000 /* open for reading only */
|
#define O_RDONLY 0x0000 /* open for reading only */
|
||||||
|
|||||||
@@ -138,9 +138,6 @@
|
|||||||
// The location of the user-level STABS data structure
|
// The location of the user-level STABS data structure
|
||||||
#define USTABDATA (PTSIZE / 2)
|
#define USTABDATA (PTSIZE / 2)
|
||||||
|
|
||||||
// Physical address of startup code for non-boot CPUs (APs)
|
|
||||||
#define MPENTRY_PADDR 0x7000
|
|
||||||
|
|
||||||
#ifndef __ASSEMBLER__
|
#ifndef __ASSEMBLER__
|
||||||
|
|
||||||
typedef uint32_t pte_t;
|
typedef uint32_t pte_t;
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#ifndef JOS_INC_PARTITION_H
|
|
||||||
#define JOS_INC_PARTITION_H
|
|
||||||
#include <inc/types.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file contains definitions for x86 partition tables, and comes from
|
|
||||||
* Mike Mammarella.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Offset of 1st partition descriptor in a partition sector
|
|
||||||
#define PTABLE_OFFSET 446
|
|
||||||
// 2-byte partition table magic number location and value
|
|
||||||
#define PTABLE_MAGIC_OFFSET 510
|
|
||||||
#define PTABLE_MAGIC "\x55\xAA"
|
|
||||||
|
|
||||||
// Partition type constants
|
|
||||||
#define PTYPE_JOS_KERN 0x27 // JOS kernel
|
|
||||||
#define PTYPE_JOSFS 0x28 // JOS file system
|
|
||||||
// Extended partition identifiers
|
|
||||||
#define PTYPE_DOS_EXTENDED 0x05
|
|
||||||
#define PTYPE_W95_EXTENDED 0x0F
|
|
||||||
#define PTYPE_LINUX_EXTENDED 0x85
|
|
||||||
|
|
||||||
struct Partitiondesc {
|
|
||||||
uint8_t boot;
|
|
||||||
uint8_t chs_begin[3];
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t chs_end[3];
|
|
||||||
uint32_t lba_start;
|
|
||||||
uint32_t lba_length;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -7,16 +7,6 @@ enum {
|
|||||||
SYS_cgetc,
|
SYS_cgetc,
|
||||||
SYS_getenvid,
|
SYS_getenvid,
|
||||||
SYS_env_destroy,
|
SYS_env_destroy,
|
||||||
SYS_page_alloc,
|
|
||||||
SYS_page_map,
|
|
||||||
SYS_page_unmap,
|
|
||||||
SYS_exofork,
|
|
||||||
SYS_env_set_status,
|
|
||||||
SYS_env_set_trapframe,
|
|
||||||
SYS_env_set_pgfault_upcall,
|
|
||||||
SYS_yield,
|
|
||||||
SYS_ipc_try_send,
|
|
||||||
SYS_ipc_recv,
|
|
||||||
NSYSCALLS
|
NSYSCALLS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
11
inc/trap.h
11
inc/trap.h
@@ -74,17 +74,6 @@ struct Trapframe {
|
|||||||
uint16_t tf_padding4;
|
uint16_t tf_padding4;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct UTrapframe {
|
|
||||||
/* information about the fault */
|
|
||||||
uint32_t utf_fault_va; /* va for T_PGFLT, 0 otherwise */
|
|
||||||
uint32_t utf_err;
|
|
||||||
/* trap-time return state */
|
|
||||||
struct PushRegs utf_regs;
|
|
||||||
uintptr_t utf_eip;
|
|
||||||
uint32_t utf_eflags;
|
|
||||||
/* the trap-time stack to return to */
|
|
||||||
uintptr_t utf_esp;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
#endif /* !__ASSEMBLER__ */
|
#endif /* !__ASSEMBLER__ */
|
||||||
|
|
||||||
|
|||||||
@@ -33,12 +33,6 @@ KERN_SRCFILES := kern/entry.S \
|
|||||||
lib/readline.c \
|
lib/readline.c \
|
||||||
lib/string.c
|
lib/string.c
|
||||||
|
|
||||||
# Source files for LAB4
|
|
||||||
KERN_SRCFILES += kern/mpentry.S \
|
|
||||||
kern/mpconfig.c \
|
|
||||||
kern/lapic.c \
|
|
||||||
kern/spinlock.c
|
|
||||||
|
|
||||||
# Only build files if they exist.
|
# Only build files if they exist.
|
||||||
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
|
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
|
||||||
|
|
||||||
@@ -59,43 +53,6 @@ KERN_BINFILES := user/hello \
|
|||||||
user/faultwritekernel \
|
user/faultwritekernel \
|
||||||
user/getc
|
user/getc
|
||||||
|
|
||||||
# Binary files for LAB4
|
|
||||||
KERN_BINFILES += user/idle \
|
|
||||||
user/yield \
|
|
||||||
user/dumbfork \
|
|
||||||
user/stresssched \
|
|
||||||
user/faultdie \
|
|
||||||
user/faultregs \
|
|
||||||
user/faultalloc \
|
|
||||||
user/faultallocbad \
|
|
||||||
user/faultnostack \
|
|
||||||
user/faultbadhandler \
|
|
||||||
user/faultevilhandler \
|
|
||||||
user/forktree \
|
|
||||||
user/sendpage \
|
|
||||||
user/spin \
|
|
||||||
user/fairness \
|
|
||||||
user/pingpong \
|
|
||||||
user/pingpongs \
|
|
||||||
user/primes
|
|
||||||
# Binary files for LAB5
|
|
||||||
KERN_BINFILES += user/faultio\
|
|
||||||
user/spawnfaultio\
|
|
||||||
user/testfile \
|
|
||||||
user/spawnhello \
|
|
||||||
user/icode \
|
|
||||||
fs/fs
|
|
||||||
|
|
||||||
# Binary files for LAB5
|
|
||||||
KERN_BINFILES += user/testpteshare \
|
|
||||||
user/testfdsharing \
|
|
||||||
user/testpipe \
|
|
||||||
user/testpiperace \
|
|
||||||
user/testpiperace2 \
|
|
||||||
user/primespipe \
|
|
||||||
user/testkbd \
|
|
||||||
user/testshell
|
|
||||||
|
|
||||||
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
|
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
|
||||||
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
|
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
|
||||||
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES))
|
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES))
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#include <inc/assert.h>
|
#include <inc/assert.h>
|
||||||
|
|
||||||
#include <kern/console.h>
|
#include <kern/console.h>
|
||||||
#include <kern/trap.h>
|
|
||||||
#include <kern/picirq.h>
|
|
||||||
#include <kern/ansi.h>
|
#include <kern/ansi.h>
|
||||||
|
|
||||||
static void cons_intr(int (*proc)(void));
|
static void cons_intr(int (*proc)(void));
|
||||||
@@ -103,9 +101,6 @@ serial_init(void)
|
|||||||
(void) inb(COM1+COM_IIR);
|
(void) inb(COM1+COM_IIR);
|
||||||
(void) inb(COM1+COM_RX);
|
(void) inb(COM1+COM_RX);
|
||||||
|
|
||||||
// Enable serial interrupts
|
|
||||||
if (serial_exists)
|
|
||||||
irq_setmask_8259A(irq_mask_8259A & ~(1<<IRQ_SERIAL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -389,9 +384,6 @@ kbd_intr(void)
|
|||||||
static void
|
static void
|
||||||
kbd_init(void)
|
kbd_init(void)
|
||||||
{
|
{
|
||||||
// Drain the kbd buffer so that QEMU generates interrupts.
|
|
||||||
kbd_intr();
|
|
||||||
irq_setmask_8259A(irq_mask_8259A & ~(1<<IRQ_KBD));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
46
kern/cpu.h
46
kern/cpu.h
@@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
#ifndef JOS_INC_CPU_H
|
|
||||||
#define JOS_INC_CPU_H
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
#include <inc/memlayout.h>
|
|
||||||
#include <inc/mmu.h>
|
|
||||||
#include <inc/env.h>
|
|
||||||
|
|
||||||
// Maximum number of CPUs
|
|
||||||
#define NCPU 8
|
|
||||||
|
|
||||||
// Values of status in struct Cpu
|
|
||||||
enum {
|
|
||||||
CPU_UNUSED = 0,
|
|
||||||
CPU_STARTED,
|
|
||||||
CPU_HALTED,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Per-CPU state
|
|
||||||
struct CpuInfo {
|
|
||||||
uint8_t cpu_id; // Local APIC ID; index into cpus[] below
|
|
||||||
volatile unsigned cpu_status; // The status of the CPU
|
|
||||||
struct Env *cpu_env; // The currently-running environment.
|
|
||||||
struct Taskstate cpu_ts; // Used by x86 to find stack for interrupt
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialized in mpconfig.c
|
|
||||||
extern struct CpuInfo cpus[NCPU];
|
|
||||||
extern int ncpu; // Total number of CPUs in the system
|
|
||||||
extern struct CpuInfo *bootcpu; // The boot-strap processor (BSP)
|
|
||||||
extern physaddr_t lapicaddr; // Physical MMIO address of the local APIC
|
|
||||||
|
|
||||||
// Per-CPU kernel stacks
|
|
||||||
extern unsigned char percpu_kstacks[NCPU][KSTKSIZE];
|
|
||||||
|
|
||||||
int cpunum(void);
|
|
||||||
#define thiscpu (&cpus[cpunum()])
|
|
||||||
|
|
||||||
void mp_init(void);
|
|
||||||
void lapic_init(void);
|
|
||||||
void lapic_startap(uint8_t apicid, uint32_t addr);
|
|
||||||
void lapic_eoi(void);
|
|
||||||
void lapic_ipi(int vector);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
56
kern/env.c
56
kern/env.c
@@ -11,11 +11,9 @@
|
|||||||
#include <kern/pmap.h>
|
#include <kern/pmap.h>
|
||||||
#include <kern/trap.h>
|
#include <kern/trap.h>
|
||||||
#include <kern/monitor.h>
|
#include <kern/monitor.h>
|
||||||
#include <kern/sched.h>
|
|
||||||
#include <kern/cpu.h>
|
|
||||||
#include <kern/spinlock.h>
|
|
||||||
|
|
||||||
struct Env *envs = NULL; // All environments
|
struct Env *envs = NULL; // All environments
|
||||||
|
struct Env *curenv = NULL; // The current env
|
||||||
static struct Env *env_free_list; // Free environment list
|
static struct Env *env_free_list; // Free environment list
|
||||||
// (linked by Env->env_link)
|
// (linked by Env->env_link)
|
||||||
|
|
||||||
@@ -36,7 +34,7 @@ static struct Env *env_free_list; // Free environment list
|
|||||||
// definition of gdt specifies the Descriptor Privilege Level (DPL)
|
// definition of gdt specifies the Descriptor Privilege Level (DPL)
|
||||||
// of that descriptor: 0 for kernel and 3 for user.
|
// of that descriptor: 0 for kernel and 3 for user.
|
||||||
//
|
//
|
||||||
struct Segdesc gdt[NCPU + 5] =
|
struct Segdesc gdt[] =
|
||||||
{
|
{
|
||||||
// 0x0 - unused (always faults -- for trapping NULL far pointers)
|
// 0x0 - unused (always faults -- for trapping NULL far pointers)
|
||||||
SEG_NULL,
|
SEG_NULL,
|
||||||
@@ -53,8 +51,7 @@ struct Segdesc gdt[NCPU + 5] =
|
|||||||
// 0x20 - user data segment
|
// 0x20 - user data segment
|
||||||
[GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
|
[GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
|
||||||
|
|
||||||
// Per-CPU TSS descriptors (starting from GD_TSS0) are initialized
|
// 0x28 - tss, initialized in trap_init_percpu()
|
||||||
// in trap_init_percpu()
|
|
||||||
[GD_TSS0 >> 3] = SEG_NULL
|
[GD_TSS0 >> 3] = SEG_NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -253,21 +250,11 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
|
|||||||
e->env_tf.tf_cs = GD_UT | 3;
|
e->env_tf.tf_cs = GD_UT | 3;
|
||||||
// You will set e->env_tf.tf_eip later.
|
// 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
|
// commit the allocation
|
||||||
env_free_list = e->env_link;
|
env_free_list = e->env_link;
|
||||||
*newenv_store = e;
|
*newenv_store = e;
|
||||||
|
|
||||||
// cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +276,7 @@ region_alloc(struct Env *e, void *va, size_t len)
|
|||||||
// You should round va down, and round (va + len) up.
|
// You should round va down, and round (va + len) up.
|
||||||
// (Watch out for corner-cases!)
|
// (Watch out for corner-cases!)
|
||||||
va = ROUNDDOWN(va, PGSIZE);
|
va = ROUNDDOWN(va, PGSIZE);
|
||||||
size_t count = ROUNDUP(len, PGSIZE) / PGSIZE + 1;
|
size_t count = ROUNDUP(len, PGSIZE) / PGSIZE;
|
||||||
struct PageInfo* p;
|
struct PageInfo* p;
|
||||||
|
|
||||||
while(count--) {
|
while(count--) {
|
||||||
@@ -357,7 +344,6 @@ load_icode(struct Env *e, uint8_t *binary)
|
|||||||
// LAB 3: Your code here.
|
// LAB 3: Your code here.
|
||||||
|
|
||||||
// TODO validate the headers
|
// TODO validate the headers
|
||||||
lcr3(PADDR(e->env_pgdir));
|
|
||||||
struct Elf* elf = (struct Elf*) binary;
|
struct Elf* elf = (struct Elf*) binary;
|
||||||
struct Proghdr* ph = (struct Proghdr*) (binary + elf->e_phoff);
|
struct Proghdr* ph = (struct Proghdr*) (binary + elf->e_phoff);
|
||||||
struct Proghdr* phend = ph + elf->e_phnum;
|
struct Proghdr* phend = ph + elf->e_phnum;
|
||||||
@@ -365,10 +351,11 @@ load_icode(struct Env *e, uint8_t *binary)
|
|||||||
if(ph->p_type != ELF_PROG_LOAD) continue;
|
if(ph->p_type != ELF_PROG_LOAD) continue;
|
||||||
|
|
||||||
region_alloc(e, (void*) ph->p_va, ph->p_memsz);
|
region_alloc(e, (void*) ph->p_va, ph->p_memsz);
|
||||||
|
lcr3(PADDR(e->env_pgdir));
|
||||||
memcpy((void*) ph->p_va, binary + ph->p_offset, ph->p_filesz);
|
memcpy((void*) ph->p_va, binary + ph->p_offset, ph->p_filesz);
|
||||||
memset((void*) ph->p_va + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
|
memset((void*) ph->p_va + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
|
||||||
|
lcr3(PADDR(kern_pgdir));
|
||||||
}
|
}
|
||||||
lcr3(PADDR(kern_pgdir));
|
|
||||||
e->env_tf.tf_eip = elf->e_entry;
|
e->env_tf.tf_eip = elf->e_entry;
|
||||||
|
|
||||||
// Now map one page for the program's initial stack
|
// Now map one page for the program's initial stack
|
||||||
@@ -387,16 +374,10 @@ void
|
|||||||
env_create(uint8_t *binary, enum EnvType type)
|
env_create(uint8_t *binary, enum EnvType type)
|
||||||
{
|
{
|
||||||
// LAB 3: Your code here.
|
// LAB 3: Your code here.
|
||||||
// If this is the file server (type == ENV_TYPE_FS) give it I/O privileges.
|
|
||||||
// LAB 5: Your code here.
|
|
||||||
struct Env* new_env;
|
struct Env* new_env;
|
||||||
if(env_alloc(&new_env, 0) < 0)
|
if(env_alloc(&new_env, 0) < 0)
|
||||||
panic("Failed to allocate environment");
|
panic("Failed to allocate environment");
|
||||||
new_env->env_type = type;
|
new_env->env_type = type;
|
||||||
|
|
||||||
if(type == ENV_TYPE_FS)
|
|
||||||
new_env->env_tf.tf_eflags |= FL_IOPL_3;
|
|
||||||
|
|
||||||
load_icode(new_env, binary);
|
load_icode(new_env, binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +398,7 @@ env_free(struct Env *e)
|
|||||||
lcr3(PADDR(kern_pgdir));
|
lcr3(PADDR(kern_pgdir));
|
||||||
|
|
||||||
// Note the environment's demise.
|
// Note the environment's demise.
|
||||||
// cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
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
|
// Flush all mapped pages in the user portion of the address space
|
||||||
static_assert(UTOP % PTSIZE == 0);
|
static_assert(UTOP % PTSIZE == 0);
|
||||||
@@ -455,26 +436,15 @@ env_free(struct Env *e)
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Frees environment e.
|
// Frees environment e.
|
||||||
// If e was the current env, then runs a new environment (and does not return
|
|
||||||
// to the caller).
|
|
||||||
//
|
//
|
||||||
void
|
void
|
||||||
env_destroy(struct Env *e)
|
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);
|
env_free(e);
|
||||||
|
|
||||||
if (curenv == e) {
|
cprintf("Destroyed the only environment - nothing more to do!\n");
|
||||||
curenv = NULL;
|
while (1)
|
||||||
sched_yield();
|
monitor(NULL);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -487,9 +457,6 @@ env_destroy(struct Env *e)
|
|||||||
void
|
void
|
||||||
env_pop_tf(struct Trapframe *tf)
|
env_pop_tf(struct Trapframe *tf)
|
||||||
{
|
{
|
||||||
// Record the CPU we are running on for user-space debugging
|
|
||||||
curenv->env_cpunum = cpunum();
|
|
||||||
|
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"\tmovl %0,%%esp\n"
|
"\tmovl %0,%%esp\n"
|
||||||
"\tpopal\n"
|
"\tpopal\n"
|
||||||
@@ -535,7 +502,6 @@ env_run(struct Env *e)
|
|||||||
e->env_status = ENV_RUNNING;
|
e->env_status = ENV_RUNNING;
|
||||||
e->env_runs++;
|
e->env_runs++;
|
||||||
lcr3(PADDR(e->env_pgdir));
|
lcr3(PADDR(e->env_pgdir));
|
||||||
unlock_kernel();
|
|
||||||
env_pop_tf(&e->env_tf);
|
env_pop_tf(&e->env_tf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
#define JOS_KERN_ENV_H
|
#define JOS_KERN_ENV_H
|
||||||
|
|
||||||
#include <inc/env.h>
|
#include <inc/env.h>
|
||||||
#include <kern/cpu.h>
|
|
||||||
|
|
||||||
extern struct Env *envs; // All environments
|
extern struct Env *envs; // All environments
|
||||||
#define curenv (thiscpu->cpu_env) // Current environment
|
extern struct Env *curenv; // Current environment
|
||||||
extern struct Segdesc gdt[];
|
extern struct Segdesc gdt[];
|
||||||
|
|
||||||
void env_init(void);
|
void env_init(void);
|
||||||
|
|||||||
97
kern/init.c
97
kern/init.c
@@ -11,18 +11,19 @@
|
|||||||
#include <kern/kclock.h>
|
#include <kern/kclock.h>
|
||||||
#include <kern/env.h>
|
#include <kern/env.h>
|
||||||
#include <kern/trap.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 sysenter_handler();
|
void sysenter_handler();
|
||||||
|
|
||||||
void
|
void
|
||||||
i386_init(void)
|
i386_init(void)
|
||||||
{
|
{
|
||||||
|
extern char edata[], end[];
|
||||||
|
|
||||||
|
// Before doing anything else, complete the ELF loading process.
|
||||||
|
// Clear the uninitialized global data (BSS) section of our program.
|
||||||
|
// This ensures that all static/global variables start out zero.
|
||||||
|
memset(edata, 0, end - edata);
|
||||||
|
|
||||||
// Initialize the console.
|
// Initialize the console.
|
||||||
// Can't call cprintf until after we do this!
|
// Can't call cprintf until after we do this!
|
||||||
cons_init();
|
cons_init();
|
||||||
@@ -46,94 +47,18 @@ i386_init(void)
|
|||||||
env_init();
|
env_init();
|
||||||
trap_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)
|
#if defined(TEST)
|
||||||
// Don't touch -- used by grading script!
|
// Don't touch -- used by grading script!
|
||||||
ENV_CREATE(TEST, ENV_TYPE_USER);
|
ENV_CREATE(TEST, ENV_TYPE_USER);
|
||||||
#else
|
#else
|
||||||
// Touch all you want.
|
// Touch all you want.
|
||||||
ENV_CREATE(user_icode, ENV_TYPE_USER);
|
ENV_CREATE(user_hello, ENV_TYPE_USER);
|
||||||
#endif // TEST*
|
#endif // TEST*
|
||||||
|
|
||||||
// Should not be necessary - drains keyboard because interrupt has given up.
|
// We only have one user environment for now, so just run it.
|
||||||
kbd_intr();
|
env_run(&envs[0]);
|
||||||
|
|
||||||
// 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
|
* Variable panicstr contains argument to first call to panic; used as flag
|
||||||
@@ -158,7 +83,7 @@ _panic(const char *file, int line, const char *fmt,...)
|
|||||||
asm volatile("cli; cld");
|
asm volatile("cli; cld");
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line);
|
cprintf("kernel panic at %s:%d: ", file, line);
|
||||||
vcprintf(fmt, ap);
|
vcprintf(fmt, ap);
|
||||||
cprintf("\n");
|
cprintf("\n");
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|||||||
@@ -44,19 +44,18 @@ SECTIONS
|
|||||||
|
|
||||||
/* The data segment */
|
/* The data segment */
|
||||||
.data : {
|
.data : {
|
||||||
*(.data .data.*)
|
*(.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
PROVIDE(edata = .);
|
PROVIDE(edata = .);
|
||||||
*(.dynbss)
|
*(.bss)
|
||||||
*(.bss .bss.*)
|
|
||||||
*(COMMON)
|
|
||||||
PROVIDE(end = .);
|
PROVIDE(end = .);
|
||||||
|
BYTE(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/DISCARD/ : {
|
/DISCARD/ : {
|
||||||
*(.eh_frame .note.GNU-stack .comment .note)
|
*(.eh_frame .note.GNU-stack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
182
kern/lapic.c
182
kern/lapic.c
@@ -1,182 +0,0 @@
|
|||||||
// The local APIC manages internal (non-I/O) interrupts.
|
|
||||||
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
#include <inc/memlayout.h>
|
|
||||||
#include <inc/trap.h>
|
|
||||||
#include <inc/mmu.h>
|
|
||||||
#include <inc/stdio.h>
|
|
||||||
#include <inc/x86.h>
|
|
||||||
#include <kern/pmap.h>
|
|
||||||
#include <kern/cpu.h>
|
|
||||||
|
|
||||||
// Local APIC registers, divided by 4 for use as uint32_t[] indices.
|
|
||||||
#define ID (0x0020/4) // ID
|
|
||||||
#define VER (0x0030/4) // Version
|
|
||||||
#define TPR (0x0080/4) // Task Priority
|
|
||||||
#define EOI (0x00B0/4) // EOI
|
|
||||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
|
||||||
#define ENABLE 0x00000100 // Unit Enable
|
|
||||||
#define ESR (0x0280/4) // Error Status
|
|
||||||
#define ICRLO (0x0300/4) // Interrupt Command
|
|
||||||
#define INIT 0x00000500 // INIT/RESET
|
|
||||||
#define STARTUP 0x00000600 // Startup IPI
|
|
||||||
#define DELIVS 0x00001000 // Delivery status
|
|
||||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
|
||||||
#define DEASSERT 0x00000000
|
|
||||||
#define LEVEL 0x00008000 // Level triggered
|
|
||||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
|
||||||
#define OTHERS 0x000C0000 // Send to all APICs, excluding self.
|
|
||||||
#define BUSY 0x00001000
|
|
||||||
#define FIXED 0x00000000
|
|
||||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
|
||||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
|
||||||
#define X1 0x0000000B // divide counts by 1
|
|
||||||
#define PERIODIC 0x00020000 // Periodic
|
|
||||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
|
||||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
|
||||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
|
||||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
|
||||||
#define MASKED 0x00010000 // Interrupt masked
|
|
||||||
#define TICR (0x0380/4) // Timer Initial Count
|
|
||||||
#define TCCR (0x0390/4) // Timer Current Count
|
|
||||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
|
||||||
|
|
||||||
physaddr_t lapicaddr; // Initialized in mpconfig.c
|
|
||||||
volatile uint32_t *lapic;
|
|
||||||
|
|
||||||
static void
|
|
||||||
lapicw(int index, int value)
|
|
||||||
{
|
|
||||||
lapic[index] = value;
|
|
||||||
lapic[ID]; // wait for write to finish, by reading
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
lapic_init(void)
|
|
||||||
{
|
|
||||||
if (!lapicaddr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// lapicaddr is the physical address of the LAPIC's 4K MMIO
|
|
||||||
// region. Map it in to virtual memory so we can access it.
|
|
||||||
lapic = mmio_map_region(lapicaddr, 4096);
|
|
||||||
|
|
||||||
// Enable local APIC; set spurious interrupt vector.
|
|
||||||
lapicw(SVR, ENABLE | (IRQ_OFFSET + IRQ_SPURIOUS));
|
|
||||||
|
|
||||||
// The timer repeatedly counts down at bus frequency
|
|
||||||
// from lapic[TICR] and then issues an interrupt.
|
|
||||||
// If we cared more about precise timekeeping,
|
|
||||||
// TICR would be calibrated using an external time source.
|
|
||||||
lapicw(TDCR, X1);
|
|
||||||
lapicw(TIMER, PERIODIC | (IRQ_OFFSET + IRQ_TIMER));
|
|
||||||
lapicw(TICR, 10000000);
|
|
||||||
|
|
||||||
// Leave LINT0 of the BSP enabled so that it can get
|
|
||||||
// interrupts from the 8259A chip.
|
|
||||||
//
|
|
||||||
// According to Intel MP Specification, the BIOS should initialize
|
|
||||||
// BSP's local APIC in Virtual Wire Mode, in which 8259A's
|
|
||||||
// INTR is virtually connected to BSP's LINTIN0. In this mode,
|
|
||||||
// we do not need to program the IOAPIC.
|
|
||||||
if (thiscpu != bootcpu)
|
|
||||||
lapicw(LINT0, MASKED);
|
|
||||||
|
|
||||||
// Disable NMI (LINT1) on all CPUs
|
|
||||||
lapicw(LINT1, MASKED);
|
|
||||||
|
|
||||||
// Disable performance counter overflow interrupts
|
|
||||||
// on machines that provide that interrupt entry.
|
|
||||||
if (((lapic[VER]>>16) & 0xFF) >= 4)
|
|
||||||
lapicw(PCINT, MASKED);
|
|
||||||
|
|
||||||
// Map error interrupt to IRQ_ERROR.
|
|
||||||
lapicw(ERROR, IRQ_OFFSET + IRQ_ERROR);
|
|
||||||
|
|
||||||
// Clear error status register (requires back-to-back writes).
|
|
||||||
lapicw(ESR, 0);
|
|
||||||
lapicw(ESR, 0);
|
|
||||||
|
|
||||||
// Ack any outstanding interrupts.
|
|
||||||
lapicw(EOI, 0);
|
|
||||||
|
|
||||||
// Send an Init Level De-Assert to synchronize arbitration ID's.
|
|
||||||
lapicw(ICRHI, 0);
|
|
||||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
|
||||||
while(lapic[ICRLO] & DELIVS)
|
|
||||||
;
|
|
||||||
|
|
||||||
// Enable interrupts on the APIC (but not on the processor).
|
|
||||||
lapicw(TPR, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
cpunum(void)
|
|
||||||
{
|
|
||||||
if (lapic)
|
|
||||||
return lapic[ID] >> 24;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acknowledge interrupt.
|
|
||||||
void
|
|
||||||
lapic_eoi(void)
|
|
||||||
{
|
|
||||||
if (lapic)
|
|
||||||
lapicw(EOI, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spin for a given number of microseconds.
|
|
||||||
// On real hardware would want to tune this dynamically.
|
|
||||||
static void
|
|
||||||
microdelay(int us)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#define IO_RTC 0x70
|
|
||||||
|
|
||||||
// Start additional processor running entry code at addr.
|
|
||||||
// See Appendix B of MultiProcessor Specification.
|
|
||||||
void
|
|
||||||
lapic_startap(uint8_t apicid, uint32_t addr)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint16_t *wrv;
|
|
||||||
|
|
||||||
// "The BSP must initialize CMOS shutdown code to 0AH
|
|
||||||
// and the warm reset vector (DWORD based at 40:67) to point at
|
|
||||||
// the AP startup code prior to the [universal startup algorithm]."
|
|
||||||
outb(IO_RTC, 0xF); // offset 0xF is shutdown code
|
|
||||||
outb(IO_RTC+1, 0x0A);
|
|
||||||
wrv = (uint16_t *)KADDR((0x40 << 4 | 0x67)); // Warm reset vector
|
|
||||||
wrv[0] = 0;
|
|
||||||
wrv[1] = addr >> 4;
|
|
||||||
|
|
||||||
// "Universal startup algorithm."
|
|
||||||
// Send INIT (level-triggered) interrupt to reset other CPU.
|
|
||||||
lapicw(ICRHI, apicid << 24);
|
|
||||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
|
||||||
microdelay(200);
|
|
||||||
lapicw(ICRLO, INIT | LEVEL);
|
|
||||||
microdelay(100); // should be 10ms, but too slow in Bochs!
|
|
||||||
|
|
||||||
// Send startup IPI (twice!) to enter code.
|
|
||||||
// Regular hardware is supposed to only accept a STARTUP
|
|
||||||
// when it is in the halted state due to an INIT. So the second
|
|
||||||
// should be ignored, but it is part of the official Intel algorithm.
|
|
||||||
// Bochs complains about the second one. Too bad for Bochs.
|
|
||||||
for (i = 0; i < 2; i++) {
|
|
||||||
lapicw(ICRHI, apicid << 24);
|
|
||||||
lapicw(ICRLO, STARTUP | (addr >> 12));
|
|
||||||
microdelay(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
lapic_ipi(int vector)
|
|
||||||
{
|
|
||||||
lapicw(ICRLO, OTHERS | FIXED | vector);
|
|
||||||
while (lapic[ICRLO] & DELIVS)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
225
kern/mpconfig.c
225
kern/mpconfig.c
@@ -1,225 +0,0 @@
|
|||||||
// Search for and parse the multiprocessor configuration table
|
|
||||||
// See http://developer.intel.com/design/pentium/datashts/24201606.pdf
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
#include <inc/string.h>
|
|
||||||
#include <inc/memlayout.h>
|
|
||||||
#include <inc/x86.h>
|
|
||||||
#include <inc/mmu.h>
|
|
||||||
#include <inc/env.h>
|
|
||||||
#include <kern/cpu.h>
|
|
||||||
#include <kern/pmap.h>
|
|
||||||
|
|
||||||
struct CpuInfo cpus[NCPU];
|
|
||||||
struct CpuInfo *bootcpu;
|
|
||||||
int ismp;
|
|
||||||
int ncpu;
|
|
||||||
|
|
||||||
// Per-CPU kernel stacks
|
|
||||||
unsigned char percpu_kstacks[NCPU][KSTKSIZE]
|
|
||||||
__attribute__ ((aligned(PGSIZE)));
|
|
||||||
|
|
||||||
|
|
||||||
// See MultiProcessor Specification Version 1.[14]
|
|
||||||
|
|
||||||
struct mp { // floating pointer [MP 4.1]
|
|
||||||
uint8_t signature[4]; // "_MP_"
|
|
||||||
physaddr_t physaddr; // phys addr of MP config table
|
|
||||||
uint8_t length; // 1
|
|
||||||
uint8_t specrev; // [14]
|
|
||||||
uint8_t checksum; // all bytes must add up to 0
|
|
||||||
uint8_t type; // MP system config type
|
|
||||||
uint8_t imcrp;
|
|
||||||
uint8_t reserved[3];
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
struct mpconf { // configuration table header [MP 4.2]
|
|
||||||
uint8_t signature[4]; // "PCMP"
|
|
||||||
uint16_t length; // total table length
|
|
||||||
uint8_t version; // [14]
|
|
||||||
uint8_t checksum; // all bytes must add up to 0
|
|
||||||
uint8_t product[20]; // product id
|
|
||||||
physaddr_t oemtable; // OEM table pointer
|
|
||||||
uint16_t oemlength; // OEM table length
|
|
||||||
uint16_t entry; // entry count
|
|
||||||
physaddr_t lapicaddr; // address of local APIC
|
|
||||||
uint16_t xlength; // extended table length
|
|
||||||
uint8_t xchecksum; // extended table checksum
|
|
||||||
uint8_t reserved;
|
|
||||||
uint8_t entries[0]; // table entries
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
struct mpproc { // processor table entry [MP 4.3.1]
|
|
||||||
uint8_t type; // entry type (0)
|
|
||||||
uint8_t apicid; // local APIC id
|
|
||||||
uint8_t version; // local APIC version
|
|
||||||
uint8_t flags; // CPU flags
|
|
||||||
uint8_t signature[4]; // CPU signature
|
|
||||||
uint32_t feature; // feature flags from CPUID instruction
|
|
||||||
uint8_t reserved[8];
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
// mpproc flags
|
|
||||||
#define MPPROC_BOOT 0x02 // This mpproc is the bootstrap processor
|
|
||||||
|
|
||||||
// Table entry types
|
|
||||||
#define MPPROC 0x00 // One per processor
|
|
||||||
#define MPBUS 0x01 // One per bus
|
|
||||||
#define MPIOAPIC 0x02 // One per I/O APIC
|
|
||||||
#define MPIOINTR 0x03 // One per bus interrupt source
|
|
||||||
#define MPLINTR 0x04 // One per system interrupt source
|
|
||||||
|
|
||||||
static uint8_t
|
|
||||||
sum(void *addr, int len)
|
|
||||||
{
|
|
||||||
int i, sum;
|
|
||||||
|
|
||||||
sum = 0;
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
sum += ((uint8_t *)addr)[i];
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for an MP structure in the len bytes at physical address addr.
|
|
||||||
static struct mp *
|
|
||||||
mpsearch1(physaddr_t a, int len)
|
|
||||||
{
|
|
||||||
struct mp *mp = KADDR(a), *end = KADDR(a + len);
|
|
||||||
|
|
||||||
for (; mp < end; mp++)
|
|
||||||
if (memcmp(mp->signature, "_MP_", 4) == 0 &&
|
|
||||||
sum(mp, sizeof(*mp)) == 0)
|
|
||||||
return mp;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for the MP Floating Pointer Structure, which according to
|
|
||||||
// [MP 4] is in one of the following three locations:
|
|
||||||
// 1) in the first KB of the EBDA;
|
|
||||||
// 2) if there is no EBDA, in the last KB of system base memory;
|
|
||||||
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
|
|
||||||
static struct mp *
|
|
||||||
mpsearch(void)
|
|
||||||
{
|
|
||||||
uint8_t *bda;
|
|
||||||
uint32_t p;
|
|
||||||
struct mp *mp;
|
|
||||||
|
|
||||||
static_assert(sizeof(*mp) == 16);
|
|
||||||
|
|
||||||
// The BIOS data area lives in 16-bit segment 0x40.
|
|
||||||
bda = (uint8_t *) KADDR(0x40 << 4);
|
|
||||||
|
|
||||||
// [MP 4] The 16-bit segment of the EBDA is in the two bytes
|
|
||||||
// starting at byte 0x0E of the BDA. 0 if not present.
|
|
||||||
if ((p = *(uint16_t *) (bda + 0x0E))) {
|
|
||||||
p <<= 4; // Translate from segment to PA
|
|
||||||
if ((mp = mpsearch1(p, 1024)))
|
|
||||||
return mp;
|
|
||||||
} else {
|
|
||||||
// The size of base memory, in KB is in the two bytes
|
|
||||||
// starting at 0x13 of the BDA.
|
|
||||||
p = *(uint16_t *) (bda + 0x13) * 1024;
|
|
||||||
if ((mp = mpsearch1(p - 1024, 1024)))
|
|
||||||
return mp;
|
|
||||||
}
|
|
||||||
return mpsearch1(0xF0000, 0x10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for an MP configuration table. For now, don't accept the
|
|
||||||
// default configurations (physaddr == 0).
|
|
||||||
// Check for the correct signature, checksum, and version.
|
|
||||||
static struct mpconf *
|
|
||||||
mpconfig(struct mp **pmp)
|
|
||||||
{
|
|
||||||
struct mpconf *conf;
|
|
||||||
struct mp *mp;
|
|
||||||
|
|
||||||
if ((mp = mpsearch()) == 0)
|
|
||||||
return NULL;
|
|
||||||
if (mp->physaddr == 0 || mp->type != 0) {
|
|
||||||
cprintf("SMP: Default configurations not implemented\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
conf = (struct mpconf *) KADDR(mp->physaddr);
|
|
||||||
if (memcmp(conf, "PCMP", 4) != 0) {
|
|
||||||
cprintf("SMP: Incorrect MP configuration table signature\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (sum(conf, conf->length) != 0) {
|
|
||||||
cprintf("SMP: Bad MP configuration checksum\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (conf->version != 1 && conf->version != 4) {
|
|
||||||
cprintf("SMP: Unsupported MP version %d\n", conf->version);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if ((sum((uint8_t *)conf + conf->length, conf->xlength) + conf->xchecksum) & 0xff) {
|
|
||||||
cprintf("SMP: Bad MP configuration extended checksum\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
*pmp = mp;
|
|
||||||
return conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mp_init(void)
|
|
||||||
{
|
|
||||||
struct mp *mp;
|
|
||||||
struct mpconf *conf;
|
|
||||||
struct mpproc *proc;
|
|
||||||
uint8_t *p;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
bootcpu = &cpus[0];
|
|
||||||
if ((conf = mpconfig(&mp)) == 0)
|
|
||||||
return;
|
|
||||||
ismp = 1;
|
|
||||||
lapicaddr = conf->lapicaddr;
|
|
||||||
|
|
||||||
for (p = conf->entries, i = 0; i < conf->entry; i++) {
|
|
||||||
switch (*p) {
|
|
||||||
case MPPROC:
|
|
||||||
proc = (struct mpproc *)p;
|
|
||||||
if (proc->flags & MPPROC_BOOT)
|
|
||||||
bootcpu = &cpus[ncpu];
|
|
||||||
if (ncpu < NCPU) {
|
|
||||||
cpus[ncpu].cpu_id = ncpu;
|
|
||||||
ncpu++;
|
|
||||||
} else {
|
|
||||||
cprintf("SMP: too many CPUs, CPU %d disabled\n",
|
|
||||||
proc->apicid);
|
|
||||||
}
|
|
||||||
p += sizeof(struct mpproc);
|
|
||||||
continue;
|
|
||||||
case MPBUS:
|
|
||||||
case MPIOAPIC:
|
|
||||||
case MPIOINTR:
|
|
||||||
case MPLINTR:
|
|
||||||
p += 8;
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
cprintf("mpinit: unknown config type %x\n", *p);
|
|
||||||
ismp = 0;
|
|
||||||
i = conf->entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bootcpu->cpu_status = CPU_STARTED;
|
|
||||||
if (!ismp) {
|
|
||||||
// Didn't like what we found; fall back to no MP.
|
|
||||||
ncpu = 1;
|
|
||||||
lapicaddr = 0;
|
|
||||||
cprintf("SMP: configuration not found, SMP disabled\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cprintf("SMP: CPU %d found %d CPU(s)\n", bootcpu->cpu_id, ncpu);
|
|
||||||
|
|
||||||
if (mp->imcrp) {
|
|
||||||
// [MP 3.2.6.1] If the hardware implements PIC mode,
|
|
||||||
// switch to getting interrupts from the LAPIC.
|
|
||||||
cprintf("SMP: Setting IMCR to switch from PIC mode to symmetric I/O mode\n");
|
|
||||||
outb(0x22, 0x70); // Select IMCR
|
|
||||||
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
/* See COPYRIGHT for copyright information. */
|
|
||||||
|
|
||||||
#include <inc/mmu.h>
|
|
||||||
#include <inc/memlayout.h>
|
|
||||||
|
|
||||||
###################################################################
|
|
||||||
# entry point for APs
|
|
||||||
###################################################################
|
|
||||||
|
|
||||||
# Each non-boot CPU ("AP") is started up in response to a STARTUP
|
|
||||||
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
|
|
||||||
# Specification says that the AP will start in real mode with CS:IP
|
|
||||||
# set to XY00:0000, where XY is an 8-bit value sent with the
|
|
||||||
# STARTUP. Thus this code must start at a 4096-byte boundary.
|
|
||||||
#
|
|
||||||
# Because this code sets DS to zero, it must run from an address in
|
|
||||||
# the low 2^16 bytes of physical memory.
|
|
||||||
#
|
|
||||||
# boot_aps() (in init.c) copies this code to MPENTRY_PADDR (which
|
|
||||||
# satisfies the above restrictions). Then, for each AP, it stores the
|
|
||||||
# address of the pre-allocated per-core stack in mpentry_kstack, sends
|
|
||||||
# the STARTUP IPI, and waits for this code to acknowledge that it has
|
|
||||||
# started (which happens in mp_main in init.c).
|
|
||||||
#
|
|
||||||
# This code is similar to boot/boot.S except that
|
|
||||||
# - it does not need to enable A20
|
|
||||||
# - it uses MPBOOTPHYS to calculate absolute addresses of its
|
|
||||||
# symbols, rather than relying on the linker to fill them
|
|
||||||
|
|
||||||
#define RELOC(x) ((x) - KERNBASE)
|
|
||||||
#define MPBOOTPHYS(s) ((s) - mpentry_start + MPENTRY_PADDR)
|
|
||||||
|
|
||||||
.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
|
|
||||||
.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
|
|
||||||
|
|
||||||
.code16
|
|
||||||
.globl mpentry_start
|
|
||||||
mpentry_start:
|
|
||||||
cli
|
|
||||||
|
|
||||||
xorw %ax, %ax
|
|
||||||
movw %ax, %ds
|
|
||||||
movw %ax, %es
|
|
||||||
movw %ax, %ss
|
|
||||||
|
|
||||||
lgdt MPBOOTPHYS(gdtdesc)
|
|
||||||
movl %cr0, %eax
|
|
||||||
orl $CR0_PE, %eax
|
|
||||||
movl %eax, %cr0
|
|
||||||
|
|
||||||
ljmpl $(PROT_MODE_CSEG), $(MPBOOTPHYS(start32))
|
|
||||||
|
|
||||||
.code32
|
|
||||||
start32:
|
|
||||||
movw $(PROT_MODE_DSEG), %ax
|
|
||||||
movw %ax, %ds
|
|
||||||
movw %ax, %es
|
|
||||||
movw %ax, %ss
|
|
||||||
movw $0, %ax
|
|
||||||
movw %ax, %fs
|
|
||||||
movw %ax, %gs
|
|
||||||
|
|
||||||
# Set up initial page table. We cannot use kern_pgdir yet because
|
|
||||||
# we are still running at a low EIP.
|
|
||||||
movl $(RELOC(entry_pgdir)), %eax
|
|
||||||
movl %eax, %cr3
|
|
||||||
# Turn on paging.
|
|
||||||
movl %cr0, %eax
|
|
||||||
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
|
|
||||||
movl %eax, %cr0
|
|
||||||
|
|
||||||
# Switch to the per-cpu stack allocated in boot_aps()
|
|
||||||
movl mpentry_kstack, %esp
|
|
||||||
movl $0x0, %ebp # nuke frame pointer
|
|
||||||
|
|
||||||
# Call mp_main(). (Exercise for the reader: why the indirect call?)
|
|
||||||
movl $mp_main, %eax
|
|
||||||
call *%eax
|
|
||||||
|
|
||||||
# If mp_main returns (it shouldn't), loop.
|
|
||||||
spin:
|
|
||||||
jmp spin
|
|
||||||
|
|
||||||
# Bootstrap GDT
|
|
||||||
.p2align 2 # force 4 byte alignment
|
|
||||||
gdt:
|
|
||||||
SEG_NULL # null seg
|
|
||||||
SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
|
||||||
SEG(STA_W, 0x0, 0xffffffff) # data seg
|
|
||||||
|
|
||||||
gdtdesc:
|
|
||||||
.word 0x17 # sizeof(gdt) - 1
|
|
||||||
.long MPBOOTPHYS(gdt) # address gdt
|
|
||||||
|
|
||||||
.globl mpentry_end
|
|
||||||
mpentry_end:
|
|
||||||
nop
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/* See COPYRIGHT for copyright information. */
|
|
||||||
|
|
||||||
#include <inc/assert.h>
|
|
||||||
#include <inc/trap.h>
|
|
||||||
|
|
||||||
#include <kern/picirq.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Current IRQ mask.
|
|
||||||
// Initial IRQ mask has interrupt 2 enabled (for slave 8259A).
|
|
||||||
uint16_t irq_mask_8259A = 0xFFFF & ~(1<<IRQ_SLAVE);
|
|
||||||
static bool didinit;
|
|
||||||
|
|
||||||
/* Initialize the 8259A interrupt controllers. */
|
|
||||||
void
|
|
||||||
pic_init(void)
|
|
||||||
{
|
|
||||||
didinit = 1;
|
|
||||||
|
|
||||||
// mask all interrupts
|
|
||||||
outb(IO_PIC1+1, 0xFF);
|
|
||||||
outb(IO_PIC2+1, 0xFF);
|
|
||||||
|
|
||||||
// Set up master (8259A-1)
|
|
||||||
|
|
||||||
// ICW1: 0001g0hi
|
|
||||||
// g: 0 = edge triggering, 1 = level triggering
|
|
||||||
// h: 0 = cascaded PICs, 1 = master only
|
|
||||||
// i: 0 = no ICW4, 1 = ICW4 required
|
|
||||||
outb(IO_PIC1, 0x11);
|
|
||||||
|
|
||||||
// ICW2: Vector offset
|
|
||||||
outb(IO_PIC1+1, IRQ_OFFSET);
|
|
||||||
|
|
||||||
// ICW3: bit mask of IR lines connected to slave PICs (master PIC),
|
|
||||||
// 3-bit No of IR line at which slave connects to master(slave PIC).
|
|
||||||
outb(IO_PIC1+1, 1<<IRQ_SLAVE);
|
|
||||||
|
|
||||||
// ICW4: 000nbmap
|
|
||||||
// n: 1 = special fully nested mode
|
|
||||||
// b: 1 = buffered mode
|
|
||||||
// m: 0 = slave PIC, 1 = master PIC
|
|
||||||
// (ignored when b is 0, as the master/slave role
|
|
||||||
// can be hardwired).
|
|
||||||
// a: 1 = Automatic EOI mode
|
|
||||||
// p: 0 = MCS-80/85 mode, 1 = intel x86 mode
|
|
||||||
outb(IO_PIC1+1, 0x3);
|
|
||||||
|
|
||||||
// Set up slave (8259A-2)
|
|
||||||
outb(IO_PIC2, 0x11); // ICW1
|
|
||||||
outb(IO_PIC2+1, IRQ_OFFSET + 8); // ICW2
|
|
||||||
outb(IO_PIC2+1, IRQ_SLAVE); // ICW3
|
|
||||||
// NB Automatic EOI mode doesn't tend to work on the slave.
|
|
||||||
// Linux source code says it's "to be investigated".
|
|
||||||
outb(IO_PIC2+1, 0x01); // ICW4
|
|
||||||
|
|
||||||
// OCW3: 0ef01prs
|
|
||||||
// ef: 0x = NOP, 10 = clear specific mask, 11 = set specific mask
|
|
||||||
// p: 0 = no polling, 1 = polling mode
|
|
||||||
// rs: 0x = NOP, 10 = read IRR, 11 = read ISR
|
|
||||||
outb(IO_PIC1, 0x68); /* clear specific mask */
|
|
||||||
outb(IO_PIC1, 0x0a); /* read IRR by default */
|
|
||||||
|
|
||||||
outb(IO_PIC2, 0x68); /* OCW3 */
|
|
||||||
outb(IO_PIC2, 0x0a); /* OCW3 */
|
|
||||||
|
|
||||||
if (irq_mask_8259A != 0xFFFF)
|
|
||||||
irq_setmask_8259A(irq_mask_8259A);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
irq_setmask_8259A(uint16_t mask)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
irq_mask_8259A = mask;
|
|
||||||
if (!didinit)
|
|
||||||
return;
|
|
||||||
outb(IO_PIC1+1, (char)mask);
|
|
||||||
outb(IO_PIC2+1, (char)(mask >> 8));
|
|
||||||
cprintf("enabled interrupts:");
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
if (~mask & (1<<i))
|
|
||||||
cprintf(" %d", i);
|
|
||||||
cprintf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/* See COPYRIGHT for copyright information. */
|
|
||||||
|
|
||||||
#ifndef JOS_KERN_PICIRQ_H
|
|
||||||
#define JOS_KERN_PICIRQ_H
|
|
||||||
#ifndef JOS_KERNEL
|
|
||||||
# error "This is a JOS kernel header; user programs should not #include it"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_IRQS 16 // Number of IRQs
|
|
||||||
|
|
||||||
// I/O Addresses of the two 8259A programmable interrupt controllers
|
|
||||||
#define IO_PIC1 0x20 // Master (IRQs 0-7)
|
|
||||||
#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
|
|
||||||
|
|
||||||
#define IRQ_SLAVE 2 // IRQ at which slave connects to master
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef __ASSEMBLER__
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
#include <inc/x86.h>
|
|
||||||
|
|
||||||
extern uint16_t irq_mask_8259A;
|
|
||||||
void pic_init(void);
|
|
||||||
void irq_setmask_8259A(uint16_t mask);
|
|
||||||
#endif // !__ASSEMBLER__
|
|
||||||
|
|
||||||
#endif // !JOS_KERN_PICIRQ_H
|
|
||||||
129
kern/pmap.c
129
kern/pmap.c
@@ -10,7 +10,6 @@
|
|||||||
#include <kern/pmap.h>
|
#include <kern/pmap.h>
|
||||||
#include <kern/kclock.h>
|
#include <kern/kclock.h>
|
||||||
#include <kern/env.h>
|
#include <kern/env.h>
|
||||||
#include <kern/cpu.h>
|
|
||||||
|
|
||||||
// These variables are set by i386_detect_memory()
|
// These variables are set by i386_detect_memory()
|
||||||
size_t npages; // Amount of physical memory (in pages)
|
size_t npages; // Amount of physical memory (in pages)
|
||||||
@@ -64,7 +63,6 @@ i386_detect_memory(void)
|
|||||||
// Set up memory mappings above UTOP.
|
// Set up memory mappings above UTOP.
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
static void mem_init_mp(void);
|
|
||||||
static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm);
|
static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm);
|
||||||
static void check_page_free_list(bool only_low_memory);
|
static void check_page_free_list(bool only_low_memory);
|
||||||
static void check_page_alloc(void);
|
static void check_page_alloc(void);
|
||||||
@@ -85,8 +83,6 @@ static void check_page_installed_pgdir(void);
|
|||||||
// If we're out of memory, boot_alloc should panic.
|
// If we're out of memory, boot_alloc should panic.
|
||||||
// This function may ONLY be used during initialization,
|
// This function may ONLY be used during initialization,
|
||||||
// before the page_free_list list has been set up.
|
// before the page_free_list list has been set up.
|
||||||
// Note that when this function is called, we are still using entry_pgdir,
|
|
||||||
// which only maps the first 4MB of physical memory.
|
|
||||||
static void *
|
static void *
|
||||||
boot_alloc(uint32_t n)
|
boot_alloc(uint32_t n)
|
||||||
{
|
{
|
||||||
@@ -197,7 +193,6 @@ mem_init(void)
|
|||||||
// - the new image at UENVS -- kernel R, user R
|
// - the new image at UENVS -- kernel R, user R
|
||||||
// - envs itself -- kernel RW, user NONE
|
// - envs itself -- kernel RW, user NONE
|
||||||
// LAB 3: Your code here.
|
// LAB 3: Your code here.
|
||||||
cprintf("Mapping envs from %p to %p\n", UENVS, ROUNDUP(envs_size, PGSIZE));
|
|
||||||
boot_map_region(kern_pgdir,
|
boot_map_region(kern_pgdir,
|
||||||
UENVS, ROUNDUP(envs_size, PGSIZE),
|
UENVS, ROUNDUP(envs_size, PGSIZE),
|
||||||
PADDR(envs), PTE_U);
|
PADDR(envs), PTE_U);
|
||||||
@@ -229,9 +224,6 @@ mem_init(void)
|
|||||||
KERNBASE, 0x100000000 - KERNBASE,
|
KERNBASE, 0x100000000 - KERNBASE,
|
||||||
0, PTE_W);
|
0, PTE_W);
|
||||||
|
|
||||||
// Initialize the SMP-related parts of the memory map
|
|
||||||
mem_init_mp();
|
|
||||||
|
|
||||||
// Check that the initial page directory has been set up correctly.
|
// Check that the initial page directory has been set up correctly.
|
||||||
check_kern_pgdir();
|
check_kern_pgdir();
|
||||||
|
|
||||||
@@ -257,36 +249,6 @@ mem_init(void)
|
|||||||
check_page_installed_pgdir();
|
check_page_installed_pgdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify mappings in kern_pgdir to support SMP
|
|
||||||
// - Map the per-CPU stacks in the region [KSTACKTOP-PTSIZE, KSTACKTOP)
|
|
||||||
//
|
|
||||||
static void
|
|
||||||
mem_init_mp(void)
|
|
||||||
{
|
|
||||||
// Map per-CPU stacks starting at KSTACKTOP, for up to 'NCPU' CPUs.
|
|
||||||
//
|
|
||||||
// For CPU i, use the physical memory that 'percpu_kstacks[i]' refers
|
|
||||||
// to as its kernel stack. CPU i's kernel stack grows down from virtual
|
|
||||||
// address kstacktop_i = KSTACKTOP - i * (KSTKSIZE + KSTKGAP), and is
|
|
||||||
// divided into two pieces, just like the single stack you set up in
|
|
||||||
// mem_init:
|
|
||||||
// * [kstacktop_i - KSTKSIZE, kstacktop_i)
|
|
||||||
// -- backed by physical memory
|
|
||||||
// * [kstacktop_i - (KSTKSIZE + KSTKGAP), kstacktop_i - KSTKSIZE)
|
|
||||||
// -- not backed; so if the kernel overflows its stack,
|
|
||||||
// it will fault rather than overwrite another CPU's stack.
|
|
||||||
// Known as a "guard page".
|
|
||||||
// Permissions: kernel RW, user NONE
|
|
||||||
//
|
|
||||||
// LAB 4: Your code here:
|
|
||||||
for(int i = 0; i < NCPU; i++) {
|
|
||||||
uintptr_t kstacktop = KSTACKTOP - i * (KSTKSIZE + KSTKGAP);
|
|
||||||
boot_map_region(kern_pgdir, kstacktop - KSTKSIZE,
|
|
||||||
KSTKSIZE, PADDR(percpu_kstacks[i]), PTE_W);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
// Tracking of physical pages.
|
// Tracking of physical pages.
|
||||||
// The 'pages' array has one 'struct PageInfo' entry per physical page.
|
// The 'pages' array has one 'struct PageInfo' entry per physical page.
|
||||||
@@ -297,7 +259,6 @@ is_reserved(size_t pagenum) {
|
|||||||
if(pagenum == 0) return true;
|
if(pagenum == 0) return true;
|
||||||
if(pagenum >= PGNUM(IOPHYSMEM) &&
|
if(pagenum >= PGNUM(IOPHYSMEM) &&
|
||||||
pagenum < PGNUM(PADDR(boot_alloc(0)))) return true;
|
pagenum < PGNUM(PADDR(boot_alloc(0)))) return true;
|
||||||
if(pagenum == PGNUM(MPENTRY_PADDR)) return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -311,10 +272,6 @@ is_reserved(size_t pagenum) {
|
|||||||
void
|
void
|
||||||
page_init(void)
|
page_init(void)
|
||||||
{
|
{
|
||||||
// LAB 4:
|
|
||||||
// Change your code to mark the physical page at MPENTRY_PADDR
|
|
||||||
// as in use
|
|
||||||
|
|
||||||
// The example code here marks all physical pages as free.
|
// The example code here marks all physical pages as free.
|
||||||
// However this is not truly the case. What memory is free?
|
// However this is not truly the case. What memory is free?
|
||||||
// 1) Mark physical page 0 as in use.
|
// 1) Mark physical page 0 as in use.
|
||||||
@@ -569,51 +526,8 @@ void
|
|||||||
tlb_invalidate(pde_t *pgdir, void *va)
|
tlb_invalidate(pde_t *pgdir, void *va)
|
||||||
{
|
{
|
||||||
// Flush the entry only if we're modifying the current address space.
|
// Flush the entry only if we're modifying the current address space.
|
||||||
if (!curenv || curenv->env_pgdir == pgdir)
|
// For now, there is only one address space, so always invalidate.
|
||||||
invlpg(va);
|
invlpg(va);
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Reserve size bytes in the MMIO region and map [pa,pa+size) at this
|
|
||||||
// location. Return the base of the reserved region. size does *not*
|
|
||||||
// have to be multiple of PGSIZE.
|
|
||||||
//
|
|
||||||
void *
|
|
||||||
mmio_map_region(physaddr_t pa, size_t size)
|
|
||||||
{
|
|
||||||
// Where to start the next region. Initially, this is the
|
|
||||||
// beginning of the MMIO region. Because this is static, its
|
|
||||||
// value will be preserved between calls to mmio_map_region
|
|
||||||
// (just like nextfree in boot_alloc).
|
|
||||||
static uintptr_t base = MMIOBASE;
|
|
||||||
|
|
||||||
// Reserve size bytes of virtual memory starting at base and
|
|
||||||
// map physical pages [pa,pa+size) to virtual addresses
|
|
||||||
// [base,base+size). Since this is device memory and not
|
|
||||||
// regular DRAM, you'll have to tell the CPU that it isn't
|
|
||||||
// safe to cache access to this memory. Luckily, the page
|
|
||||||
// tables provide bits for this purpose; simply create the
|
|
||||||
// mapping with PTE_PCD|PTE_PWT (cache-disable and
|
|
||||||
// write-through) in addition to PTE_W. (If you're interested
|
|
||||||
// in more details on this, see section 10.5 of IA32 volume
|
|
||||||
// 3A.)
|
|
||||||
//
|
|
||||||
// Be sure to round size up to a multiple of PGSIZE and to
|
|
||||||
// handle if this reservation would overflow MMIOLIM (it's
|
|
||||||
// okay to simply panic if this happens).
|
|
||||||
//
|
|
||||||
// Hint: The staff solution uses boot_map_region.
|
|
||||||
//
|
|
||||||
// Your code here:
|
|
||||||
size = ROUNDUP(size, PGSIZE);
|
|
||||||
if((base + size) > MMIOLIM)
|
|
||||||
panic("Not enough memory-mapped IO space!");
|
|
||||||
|
|
||||||
boot_map_region(kern_pgdir, base, size, pa, PTE_PCD | PTE_PWT | PTE_W);
|
|
||||||
uintptr_t to_return = base;
|
|
||||||
base += size;
|
|
||||||
|
|
||||||
return (void*) to_return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintptr_t user_mem_check_addr;
|
static uintptr_t user_mem_check_addr;
|
||||||
@@ -731,8 +645,6 @@ check_page_free_list(bool only_low_memory)
|
|||||||
assert(page2pa(pp) != EXTPHYSMEM - PGSIZE);
|
assert(page2pa(pp) != EXTPHYSMEM - PGSIZE);
|
||||||
assert(page2pa(pp) != EXTPHYSMEM);
|
assert(page2pa(pp) != EXTPHYSMEM);
|
||||||
assert(page2pa(pp) < EXTPHYSMEM || (char *) page2kva(pp) >= first_free_page);
|
assert(page2pa(pp) < EXTPHYSMEM || (char *) page2kva(pp) >= first_free_page);
|
||||||
// (new test for lab 4)
|
|
||||||
assert(page2pa(pp) != MPENTRY_PADDR);
|
|
||||||
|
|
||||||
if (page2pa(pp) < EXTPHYSMEM)
|
if (page2pa(pp) < EXTPHYSMEM)
|
||||||
++nfree_basemem;
|
++nfree_basemem;
|
||||||
@@ -855,15 +767,9 @@ check_kern_pgdir(void)
|
|||||||
assert(check_va2pa(pgdir, KERNBASE + i) == i);
|
assert(check_va2pa(pgdir, KERNBASE + i) == i);
|
||||||
|
|
||||||
// check kernel stack
|
// check kernel stack
|
||||||
// (updated in lab 4 to check per-CPU kernel stacks)
|
for (i = 0; i < KSTKSIZE; i += PGSIZE)
|
||||||
for (n = 0; n < NCPU; n++) {
|
assert(check_va2pa(pgdir, KSTACKTOP - KSTKSIZE + i) == PADDR(bootstack) + i);
|
||||||
uint32_t base = KSTACKTOP - (KSTKSIZE + KSTKGAP) * (n + 1);
|
assert(check_va2pa(pgdir, KSTACKTOP - PTSIZE) == ~0);
|
||||||
for (i = 0; i < KSTKSIZE; i += PGSIZE)
|
|
||||||
assert(check_va2pa(pgdir, base + KSTKGAP + i)
|
|
||||||
== PADDR(percpu_kstacks[n]) + i);
|
|
||||||
for (i = 0; i < KSTKGAP; i += PGSIZE)
|
|
||||||
assert(check_va2pa(pgdir, base + i) == ~0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check PDE permissions
|
// check PDE permissions
|
||||||
for (i = 0; i < NPDENTRIES; i++) {
|
for (i = 0; i < NPDENTRIES; i++) {
|
||||||
@@ -872,7 +778,6 @@ check_kern_pgdir(void)
|
|||||||
case PDX(KSTACKTOP-1):
|
case PDX(KSTACKTOP-1):
|
||||||
case PDX(UPAGES):
|
case PDX(UPAGES):
|
||||||
case PDX(UENVS):
|
case PDX(UENVS):
|
||||||
case PDX(MMIOBASE):
|
|
||||||
assert(pgdir[i] & PTE_P);
|
assert(pgdir[i] & PTE_P);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -915,7 +820,6 @@ check_page(void)
|
|||||||
struct PageInfo *fl;
|
struct PageInfo *fl;
|
||||||
pte_t *ptep, *ptep1;
|
pte_t *ptep, *ptep1;
|
||||||
void *va;
|
void *va;
|
||||||
uintptr_t mm1, mm2;
|
|
||||||
int i;
|
int i;
|
||||||
extern pde_t entry_pgdir[];
|
extern pde_t entry_pgdir[];
|
||||||
|
|
||||||
@@ -1058,29 +962,6 @@ check_page(void)
|
|||||||
page_free(pp1);
|
page_free(pp1);
|
||||||
page_free(pp2);
|
page_free(pp2);
|
||||||
|
|
||||||
// test mmio_map_region
|
|
||||||
mm1 = (uintptr_t) mmio_map_region(0, 4097);
|
|
||||||
mm2 = (uintptr_t) mmio_map_region(0, 4096);
|
|
||||||
// check that they're in the right region
|
|
||||||
assert(mm1 >= MMIOBASE && mm1 + 8192 < MMIOLIM);
|
|
||||||
assert(mm2 >= MMIOBASE && mm2 + 8192 < MMIOLIM);
|
|
||||||
// check that they're page-aligned
|
|
||||||
assert(mm1 % PGSIZE == 0 && mm2 % PGSIZE == 0);
|
|
||||||
// check that they don't overlap
|
|
||||||
assert(mm1 + 8192 <= mm2);
|
|
||||||
// check page mappings
|
|
||||||
assert(check_va2pa(kern_pgdir, mm1) == 0);
|
|
||||||
assert(check_va2pa(kern_pgdir, mm1+PGSIZE) == PGSIZE);
|
|
||||||
assert(check_va2pa(kern_pgdir, mm2) == 0);
|
|
||||||
assert(check_va2pa(kern_pgdir, mm2+PGSIZE) == ~0);
|
|
||||||
// check permissions
|
|
||||||
assert(*pgdir_walk(kern_pgdir, (void*) mm1, 0) & (PTE_W|PTE_PWT|PTE_PCD));
|
|
||||||
assert(!(*pgdir_walk(kern_pgdir, (void*) mm1, 0) & PTE_U));
|
|
||||||
// clear the mappings
|
|
||||||
*pgdir_walk(kern_pgdir, (void*) mm1, 0) = 0;
|
|
||||||
*pgdir_walk(kern_pgdir, (void*) mm1 + PGSIZE, 0) = 0;
|
|
||||||
*pgdir_walk(kern_pgdir, (void*) mm2, 0) = 0;
|
|
||||||
|
|
||||||
cprintf("check_page() succeeded!\n");
|
cprintf("check_page() succeeded!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ void page_decref(struct PageInfo *pp);
|
|||||||
|
|
||||||
void tlb_invalidate(pde_t *pgdir, void *va);
|
void tlb_invalidate(pde_t *pgdir, void *va);
|
||||||
|
|
||||||
void * mmio_map_region(physaddr_t pa, size_t size);
|
|
||||||
|
|
||||||
int user_mem_check(struct Env *env, const void *va, size_t len, int perm);
|
int user_mem_check(struct Env *env, const void *va, size_t len, int perm);
|
||||||
void user_mem_assert(struct Env *env, const void *va, size_t len, int perm);
|
void user_mem_assert(struct Env *env, const void *va, size_t len, int perm);
|
||||||
|
|
||||||
|
|||||||
103
kern/sched.c
103
kern/sched.c
@@ -1,103 +0,0 @@
|
|||||||
#include <inc/assert.h>
|
|
||||||
#include <inc/x86.h>
|
|
||||||
#include <kern/spinlock.h>
|
|
||||||
#include <kern/env.h>
|
|
||||||
#include <kern/pmap.h>
|
|
||||||
#include <kern/monitor.h>
|
|
||||||
|
|
||||||
void sched_halt(void);
|
|
||||||
|
|
||||||
// Choose a user environment to run and run it.
|
|
||||||
void
|
|
||||||
sched_yield(void)
|
|
||||||
{
|
|
||||||
struct Env *idle;
|
|
||||||
|
|
||||||
// Implement simple round-robin scheduling.
|
|
||||||
//
|
|
||||||
// Search through 'envs' for an ENV_RUNNABLE environment in
|
|
||||||
// circular fashion starting just after the env this CPU was
|
|
||||||
// last running. Switch to the first such environment found.
|
|
||||||
//
|
|
||||||
// If no envs are runnable, but the environment previously
|
|
||||||
// running on this CPU is still ENV_RUNNING, it's okay to
|
|
||||||
// choose that environment. Make sure curenv is not null before
|
|
||||||
// dereferencing it.
|
|
||||||
//
|
|
||||||
// Never choose an environment that's currently running on
|
|
||||||
// another CPU (env_status == ENV_RUNNING). If there are
|
|
||||||
// no runnable environments, simply drop through to the code
|
|
||||||
// below to halt the cpu.
|
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
struct Env* next_env = curenv ? curenv + 1 : envs;
|
|
||||||
struct Env* end_env = envs + NENV;
|
|
||||||
struct Env* to_run = NULL;
|
|
||||||
|
|
||||||
for(int i = 0; i < NENV; i++, next_env++) {
|
|
||||||
if(next_env == end_env) next_env = envs;
|
|
||||||
if(next_env->env_status == ENV_RUNNABLE) {
|
|
||||||
to_run = next_env;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!to_run && curenv && curenv->env_status == ENV_RUNNING) {
|
|
||||||
to_run = curenv;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(to_run) env_run(to_run);
|
|
||||||
|
|
||||||
// sched_halt never returns
|
|
||||||
sched_halt();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Halt this CPU when there is nothing to do. Wait until the
|
|
||||||
// timer interrupt wakes it up. This function never returns.
|
|
||||||
//
|
|
||||||
void
|
|
||||||
sched_halt(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// For debugging and testing purposes, if there are no runnable
|
|
||||||
// environments in the system, then drop into the kernel monitor.
|
|
||||||
for (i = 0; i < NENV; i++) {
|
|
||||||
if ((envs[i].env_status == ENV_RUNNABLE ||
|
|
||||||
envs[i].env_status == ENV_RUNNING ||
|
|
||||||
envs[i].env_status == ENV_DYING))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == NENV) {
|
|
||||||
cprintf("No runnable environments in the system!\n");
|
|
||||||
while (1)
|
|
||||||
monitor(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark that no environment is running on this CPU
|
|
||||||
curenv = NULL;
|
|
||||||
lcr3(PADDR(kern_pgdir));
|
|
||||||
|
|
||||||
// Mark that this CPU is in the HALT state, so that when
|
|
||||||
// timer interupts come in, we know we should re-acquire the
|
|
||||||
// big kernel lock
|
|
||||||
xchg(&thiscpu->cpu_status, CPU_HALTED);
|
|
||||||
|
|
||||||
// Release the big kernel lock as if we were "leaving" the kernel
|
|
||||||
unlock_kernel();
|
|
||||||
|
|
||||||
// Reset stack pointer, enable interrupts and then halt.
|
|
||||||
asm volatile (
|
|
||||||
"movl $0, %%ebp\n"
|
|
||||||
"movl %0, %%esp\n"
|
|
||||||
"pushl $0\n"
|
|
||||||
"pushl $0\n"
|
|
||||||
// LAB 4:
|
|
||||||
// Uncomment the following line after completing exercise 13
|
|
||||||
"sti\n"
|
|
||||||
"1:\n"
|
|
||||||
"hlt\n"
|
|
||||||
"jmp 1b\n"
|
|
||||||
: : "a" (thiscpu->cpu_ts.ts_esp0));
|
|
||||||
}
|
|
||||||
|
|
||||||
12
kern/sched.h
12
kern/sched.h
@@ -1,12 +0,0 @@
|
|||||||
/* See COPYRIGHT for copyright information. */
|
|
||||||
|
|
||||||
#ifndef JOS_KERN_SCHED_H
|
|
||||||
#define JOS_KERN_SCHED_H
|
|
||||||
#ifndef JOS_KERNEL
|
|
||||||
# error "This is a JOS kernel header; user programs should not #include it"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This function does not return.
|
|
||||||
void sched_yield(void) __attribute__((noreturn));
|
|
||||||
|
|
||||||
#endif // !JOS_KERN_SCHED_H
|
|
||||||
116
kern/spinlock.c
116
kern/spinlock.c
@@ -1,116 +0,0 @@
|
|||||||
// Mutual exclusion spin locks.
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
#include <inc/assert.h>
|
|
||||||
#include <inc/x86.h>
|
|
||||||
#include <inc/memlayout.h>
|
|
||||||
#include <inc/string.h>
|
|
||||||
#include <kern/cpu.h>
|
|
||||||
#include <kern/spinlock.h>
|
|
||||||
#include <kern/kdebug.h>
|
|
||||||
|
|
||||||
// The big kernel lock
|
|
||||||
struct spinlock kernel_lock = {
|
|
||||||
#ifdef DEBUG_SPINLOCK
|
|
||||||
.name = "kernel_lock"
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef DEBUG_SPINLOCK
|
|
||||||
// Record the current call stack in pcs[] by following the %ebp chain.
|
|
||||||
static void
|
|
||||||
get_caller_pcs(uint32_t pcs[])
|
|
||||||
{
|
|
||||||
uint32_t *ebp;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
ebp = (uint32_t *)read_ebp();
|
|
||||||
for (i = 0; i < 10; i++){
|
|
||||||
if (ebp == 0 || ebp < (uint32_t *)ULIM)
|
|
||||||
break;
|
|
||||||
pcs[i] = ebp[1]; // saved %eip
|
|
||||||
ebp = (uint32_t *)ebp[0]; // saved %ebp
|
|
||||||
}
|
|
||||||
for (; i < 10; i++)
|
|
||||||
pcs[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether this CPU is holding the lock.
|
|
||||||
static int
|
|
||||||
holding(struct spinlock *lock)
|
|
||||||
{
|
|
||||||
return lock->locked && lock->cpu == thiscpu;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
|
||||||
__spin_initlock(struct spinlock *lk, char *name)
|
|
||||||
{
|
|
||||||
lk->locked = 0;
|
|
||||||
#ifdef DEBUG_SPINLOCK
|
|
||||||
lk->name = name;
|
|
||||||
lk->cpu = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acquire the lock.
|
|
||||||
// Loops (spins) until the lock is acquired.
|
|
||||||
// Holding a lock for a long time may cause
|
|
||||||
// other CPUs to waste time spinning to acquire it.
|
|
||||||
void
|
|
||||||
spin_lock(struct spinlock *lk)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_SPINLOCK
|
|
||||||
if (holding(lk))
|
|
||||||
panic("CPU %d cannot acquire %s: already holding", cpunum(), lk->name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The xchg is atomic.
|
|
||||||
// It also serializes, so that reads after acquire are not
|
|
||||||
// reordered before it.
|
|
||||||
while (xchg(&lk->locked, 1) != 0)
|
|
||||||
asm volatile ("pause");
|
|
||||||
|
|
||||||
// Record info about lock acquisition for debugging.
|
|
||||||
#ifdef DEBUG_SPINLOCK
|
|
||||||
lk->cpu = thiscpu;
|
|
||||||
get_caller_pcs(lk->pcs);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release the lock.
|
|
||||||
void
|
|
||||||
spin_unlock(struct spinlock *lk)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_SPINLOCK
|
|
||||||
if (!holding(lk)) {
|
|
||||||
int i;
|
|
||||||
uint32_t pcs[10];
|
|
||||||
// Nab the acquiring EIP chain before it gets released
|
|
||||||
memmove(pcs, lk->pcs, sizeof pcs);
|
|
||||||
cprintf("CPU %d cannot release %s: held by CPU %d\nAcquired at:",
|
|
||||||
cpunum(), lk->name, lk->cpu->cpu_id);
|
|
||||||
for (i = 0; i < 10 && pcs[i]; i++) {
|
|
||||||
struct Eipdebuginfo info;
|
|
||||||
if (debuginfo_eip(pcs[i], &info) >= 0)
|
|
||||||
cprintf(" %08x %s:%d: %.*s+%x\n", pcs[i],
|
|
||||||
info.eip_file, info.eip_line,
|
|
||||||
info.eip_fn_namelen, info.eip_fn_name,
|
|
||||||
pcs[i] - info.eip_fn_addr);
|
|
||||||
else
|
|
||||||
cprintf(" %08x\n", pcs[i]);
|
|
||||||
}
|
|
||||||
panic("spin_unlock");
|
|
||||||
}
|
|
||||||
|
|
||||||
lk->pcs[0] = 0;
|
|
||||||
lk->cpu = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The xchg instruction is atomic (i.e. uses the "lock" prefix) with
|
|
||||||
// respect to any other instruction which references the same memory.
|
|
||||||
// x86 CPUs will not reorder loads/stores across locked instructions
|
|
||||||
// (vol 3, 8.2.2). Because xchg() is implemented using asm volatile,
|
|
||||||
// gcc will not reorder C statements across the xchg.
|
|
||||||
xchg(&lk->locked, 0);
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#ifndef JOS_INC_SPINLOCK_H
|
|
||||||
#define JOS_INC_SPINLOCK_H
|
|
||||||
|
|
||||||
#include <inc/types.h>
|
|
||||||
|
|
||||||
// Comment this to disable spinlock debugging
|
|
||||||
#define DEBUG_SPINLOCK
|
|
||||||
|
|
||||||
// Mutual exclusion lock.
|
|
||||||
struct spinlock {
|
|
||||||
unsigned locked; // Is the lock held?
|
|
||||||
|
|
||||||
#ifdef DEBUG_SPINLOCK
|
|
||||||
// For debugging:
|
|
||||||
char *name; // Name of lock.
|
|
||||||
struct CpuInfo *cpu; // The CPU holding the lock.
|
|
||||||
uintptr_t pcs[10]; // The call stack (an array of program counters)
|
|
||||||
// that locked the lock.
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
void __spin_initlock(struct spinlock *lk, char *name);
|
|
||||||
void spin_lock(struct spinlock *lk);
|
|
||||||
void spin_unlock(struct spinlock *lk);
|
|
||||||
|
|
||||||
#define spin_initlock(lock) __spin_initlock(lock, #lock)
|
|
||||||
|
|
||||||
extern struct spinlock kernel_lock;
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
lock_kernel(void)
|
|
||||||
{
|
|
||||||
spin_lock(&kernel_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
unlock_kernel(void)
|
|
||||||
{
|
|
||||||
spin_unlock(&kernel_lock);
|
|
||||||
|
|
||||||
// Normally we wouldn't need to do this, but QEMU only runs
|
|
||||||
// one CPU at a time and has a long time-slice. Without the
|
|
||||||
// pause, this CPU is likely to reacquire the lock before
|
|
||||||
// another CPU has even been given a chance to acquire it.
|
|
||||||
asm volatile("pause");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
352
kern/syscall.c
352
kern/syscall.c
@@ -10,7 +10,6 @@
|
|||||||
#include <kern/trap.h>
|
#include <kern/trap.h>
|
||||||
#include <kern/syscall.h>
|
#include <kern/syscall.h>
|
||||||
#include <kern/console.h>
|
#include <kern/console.h>
|
||||||
#include <kern/sched.h>
|
|
||||||
|
|
||||||
// Print a string to the system console.
|
// Print a string to the system console.
|
||||||
// The string is exactly 'len' characters long.
|
// The string is exactly 'len' characters long.
|
||||||
@@ -53,336 +52,14 @@ sys_env_destroy(envid_t envid)
|
|||||||
|
|
||||||
if ((r = envid2env(envid, &e, 1)) < 0)
|
if ((r = envid2env(envid, &e, 1)) < 0)
|
||||||
return r;
|
return r;
|
||||||
|
if (e == curenv)
|
||||||
|
cprintf("[%08x] exiting gracefully\n", curenv->env_id);
|
||||||
|
else
|
||||||
|
cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id);
|
||||||
env_destroy(e);
|
env_destroy(e);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deschedule current environment and pick a different one to run.
|
|
||||||
static void
|
|
||||||
sys_yield(void)
|
|
||||||
{
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a new environment.
|
|
||||||
// Returns envid of new environment, or < 0 on error. Errors are:
|
|
||||||
// -E_NO_FREE_ENV if no free environment is available.
|
|
||||||
// -E_NO_MEM on memory exhaustion.
|
|
||||||
static envid_t
|
|
||||||
sys_exofork(void)
|
|
||||||
{
|
|
||||||
// Create the new environment with env_alloc(), from kern/env.c.
|
|
||||||
// It should be left as env_alloc created it, except that
|
|
||||||
// status is set to ENV_NOT_RUNNABLE, and the register set is copied
|
|
||||||
// from the current environment -- but tweaked so sys_exofork
|
|
||||||
// will appear to return 0.
|
|
||||||
struct Env* new_env;
|
|
||||||
int error_code;
|
|
||||||
|
|
||||||
error_code = env_alloc(&new_env, curenv->env_id);
|
|
||||||
if(error_code < 0) return error_code;
|
|
||||||
|
|
||||||
new_env->env_tf = curenv->env_tf;
|
|
||||||
new_env->env_tf.tf_regs.reg_eax = 0;
|
|
||||||
new_env->env_status = ENV_NOT_RUNNABLE;
|
|
||||||
return new_env->env_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set envid's env_status to status, which must be ENV_RUNNABLE
|
|
||||||
// or ENV_NOT_RUNNABLE.
|
|
||||||
//
|
|
||||||
// Returns 0 on success, < 0 on error. Errors are:
|
|
||||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
|
||||||
// or the caller doesn't have permission to change envid.
|
|
||||||
// -E_INVAL if status is not a valid status for an environment.
|
|
||||||
static int
|
|
||||||
sys_env_set_status(envid_t envid, int status)
|
|
||||||
{
|
|
||||||
// Hint: Use the 'envid2env' function from kern/env.c to translate an
|
|
||||||
// envid to a struct Env.
|
|
||||||
// You should set envid2env's third argument to 1, which will
|
|
||||||
// check whether the current environment has permission to set
|
|
||||||
// envid's status.
|
|
||||||
struct Env* env;
|
|
||||||
int error_code;
|
|
||||||
|
|
||||||
error_code = envid2env(envid, &env, 1);
|
|
||||||
if(error_code < 0) return error_code;
|
|
||||||
|
|
||||||
if(status != ENV_RUNNABLE && status != ENV_NOT_RUNNABLE)
|
|
||||||
return -E_INVAL;
|
|
||||||
|
|
||||||
env->env_status = status;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SYS_CHECKPERMS(perm) \
|
|
||||||
((((perm) & (PTE_P | PTE_U)) == (PTE_P | PTE_U)) && \
|
|
||||||
(((perm) & ~(PTE_P | PTE_U | PTE_W | PTE_AVAIL)) == 0))
|
|
||||||
#define SYS_CHECKADDR(addr) (((uintptr_t) (addr) < UTOP) && ((uintptr_t) (addr) % PGSIZE == 0))
|
|
||||||
|
|
||||||
// Set envid's trap frame to 'tf'.
|
|
||||||
// tf is modified to make sure that user environments always run at code
|
|
||||||
// protection level 3 (CPL 3), interrupts enabled, and IOPL of 0.
|
|
||||||
//
|
|
||||||
// Returns 0 on success, < 0 on error. Errors are:
|
|
||||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
|
||||||
// or the caller doesn't have permission to change envid.
|
|
||||||
static int
|
|
||||||
sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
|
|
||||||
{
|
|
||||||
// LAB 5: Your code here.
|
|
||||||
// Remember to check whether the user has supplied us with a good
|
|
||||||
// address!
|
|
||||||
int r;
|
|
||||||
struct Env* chenv;
|
|
||||||
SYS_CHECKADDR(tf);
|
|
||||||
|
|
||||||
if((r = envid2env(envid, &chenv, true)) < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
tf->tf_cs |= 3;
|
|
||||||
tf->tf_eflags &= ~(FL_IOPL_3);
|
|
||||||
tf->tf_eflags |= FL_IF;
|
|
||||||
|
|
||||||
chenv->env_tf = *tf;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the page fault upcall for 'envid' by modifying the corresponding struct
|
|
||||||
// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the
|
|
||||||
// kernel will push a fault record onto the exception stack, then branch to
|
|
||||||
// 'func'.
|
|
||||||
//
|
|
||||||
// Returns 0 on success, < 0 on error. Errors are:
|
|
||||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
|
||||||
// or the caller doesn't have permission to change envid.
|
|
||||||
static int
|
|
||||||
sys_env_set_pgfault_upcall(envid_t envid, void *func)
|
|
||||||
{
|
|
||||||
struct Env* env;
|
|
||||||
int return_code;
|
|
||||||
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
|
||||||
env->env_pgfault_upcall = func;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a page of memory and map it at 'va' with permission
|
|
||||||
// 'perm' in the address space of 'envid'.
|
|
||||||
// The page's contents are set to 0.
|
|
||||||
// If a page is already mapped at 'va', that page is unmapped as a
|
|
||||||
// side effect.
|
|
||||||
//
|
|
||||||
// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
|
|
||||||
// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h.
|
|
||||||
//
|
|
||||||
// Return 0 on success, < 0 on error. Errors are:
|
|
||||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
|
||||||
// or the caller doesn't have permission to change envid.
|
|
||||||
// -E_INVAL if va >= UTOP, or va is not page-aligned.
|
|
||||||
// -E_INVAL if perm is inappropriate (see above).
|
|
||||||
// -E_NO_MEM if there's no memory to allocate the new page,
|
|
||||||
// or to allocate any necessary page tables.
|
|
||||||
static int
|
|
||||||
sys_page_alloc(envid_t envid, void *va, int perm)
|
|
||||||
{
|
|
||||||
// Hint: This function is a wrapper around page_alloc() and
|
|
||||||
// page_insert() from kern/pmap.c.
|
|
||||||
// Most of the new code you write should be to check the
|
|
||||||
// parameters for correctness.
|
|
||||||
// If page_insert() fails, remember to free the page you
|
|
||||||
// allocated!
|
|
||||||
struct Env* env;
|
|
||||||
int return_code;
|
|
||||||
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
|
||||||
|
|
||||||
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
|
|
||||||
if(!SYS_CHECKADDR(va)) return -E_INVAL;
|
|
||||||
|
|
||||||
struct PageInfo* page = page_alloc(1);
|
|
||||||
if(!page) return -E_NO_MEM;
|
|
||||||
|
|
||||||
if((return_code = page_insert(env->env_pgdir, page, va, perm)) < 0) {
|
|
||||||
page_free(page);
|
|
||||||
return return_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map the page of memory at 'srcva' in srcenvid's address space
|
|
||||||
// at 'dstva' in dstenvid's address space with permission 'perm'.
|
|
||||||
// Perm has the same restrictions as in sys_page_alloc, except
|
|
||||||
// that it also must not grant write access to a read-only
|
|
||||||
// page.
|
|
||||||
//
|
|
||||||
// Return 0 on success, < 0 on error. Errors are:
|
|
||||||
// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
|
|
||||||
// or the caller doesn't have permission to change one of them.
|
|
||||||
// -E_INVAL if srcva >= UTOP or srcva is not page-aligned,
|
|
||||||
// or dstva >= UTOP or dstva is not page-aligned.
|
|
||||||
// -E_INVAL is srcva is not mapped in srcenvid's address space.
|
|
||||||
// -E_INVAL if perm is inappropriate (see sys_page_alloc).
|
|
||||||
// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
|
|
||||||
// address space.
|
|
||||||
// -E_NO_MEM if there's no memory to allocate any necessary page tables.
|
|
||||||
static int
|
|
||||||
sys_page_map(envid_t srcenvid, void *srcva,
|
|
||||||
envid_t dstenvid, void *dstva, int perm)
|
|
||||||
{
|
|
||||||
// Hint: This function is a wrapper around page_lookup() and
|
|
||||||
// page_insert() from kern/pmap.c.
|
|
||||||
// Again, most of the new code you write should be to check the
|
|
||||||
// parameters for correctness.
|
|
||||||
// Use the third argument to page_lookup() to
|
|
||||||
// check the current permissions on the page.
|
|
||||||
struct Env *srcenv, *dstenv;
|
|
||||||
pte_t* srcpte;
|
|
||||||
int return_code;
|
|
||||||
|
|
||||||
if((return_code = envid2env(srcenvid, &srcenv, 1)) < 0) return return_code;
|
|
||||||
if((return_code = envid2env(dstenvid, &dstenv, 1)) < 0) return return_code;
|
|
||||||
|
|
||||||
if(!SYS_CHECKADDR(srcva)) return -E_INVAL;
|
|
||||||
if(!SYS_CHECKADDR(dstva)) return -E_INVAL;
|
|
||||||
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
|
|
||||||
|
|
||||||
struct PageInfo* page = page_lookup(srcenv->env_pgdir, srcva, &srcpte);
|
|
||||||
if(page == NULL) return -E_INVAL;
|
|
||||||
if(perm & PTE_W && !(*srcpte & PTE_W)) return -E_INVAL;
|
|
||||||
|
|
||||||
if((return_code = page_insert(dstenv->env_pgdir, page, dstva, perm)) < 0)
|
|
||||||
return return_code;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmap the page of memory at 'va' in the address space of 'envid'.
|
|
||||||
// If no page is mapped, the function silently succeeds.
|
|
||||||
//
|
|
||||||
// Return 0 on success, < 0 on error. Errors are:
|
|
||||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
|
||||||
// or the caller doesn't have permission to change envid.
|
|
||||||
// -E_INVAL if va >= UTOP, or va is not page-aligned.
|
|
||||||
static int
|
|
||||||
sys_page_unmap(envid_t envid, void *va)
|
|
||||||
{
|
|
||||||
// Hint: This function is a wrapper around page_remove().
|
|
||||||
struct Env* env;
|
|
||||||
int return_code;
|
|
||||||
|
|
||||||
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
|
||||||
if(!SYS_CHECKADDR(va)) return -E_INVAL;
|
|
||||||
|
|
||||||
page_remove(env->env_pgdir, va);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to send 'value' to the target env 'envid'.
|
|
||||||
// If srcva < UTOP, then also send page currently mapped at 'srcva',
|
|
||||||
// so that receiver gets a duplicate mapping of the same page.
|
|
||||||
//
|
|
||||||
// The send fails with a return value of -E_IPC_NOT_RECV if the
|
|
||||||
// target is not blocked, waiting for an IPC.
|
|
||||||
//
|
|
||||||
// The send also can fail for the other reasons listed below.
|
|
||||||
//
|
|
||||||
// Otherwise, the send succeeds, and the target's ipc fields are
|
|
||||||
// updated as follows:
|
|
||||||
// env_ipc_recving is set to 0 to block future sends;
|
|
||||||
// env_ipc_from is set to the sending envid;
|
|
||||||
// env_ipc_value is set to the 'value' parameter;
|
|
||||||
// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise.
|
|
||||||
// The target environment is marked runnable again, returning 0
|
|
||||||
// from the paused sys_ipc_recv system call. (Hint: does the
|
|
||||||
// sys_ipc_recv function ever actually return?)
|
|
||||||
//
|
|
||||||
// If the sender wants to send a page but the receiver isn't asking for one,
|
|
||||||
// then no page mapping is transferred, but no error occurs.
|
|
||||||
// The ipc only happens when no errors occur.
|
|
||||||
//
|
|
||||||
// Returns 0 on success, < 0 on error.
|
|
||||||
// Errors are:
|
|
||||||
// -E_BAD_ENV if environment envid doesn't currently exist.
|
|
||||||
// (No need to check permissions.)
|
|
||||||
// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
|
|
||||||
// or another environment managed to send first.
|
|
||||||
// -E_INVAL if srcva < UTOP but srcva is not page-aligned.
|
|
||||||
// -E_INVAL if srcva < UTOP and perm is inappropriate
|
|
||||||
// (see sys_page_alloc).
|
|
||||||
// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's
|
|
||||||
// address space.
|
|
||||||
// -E_INVAL if (perm & PTE_W), but srcva is read-only in the
|
|
||||||
// current environment's address space.
|
|
||||||
// -E_NO_MEM if there's not enough memory to map srcva in envid's
|
|
||||||
// address space.
|
|
||||||
static int
|
|
||||||
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
|
|
||||||
{
|
|
||||||
struct Env* dest_env;
|
|
||||||
struct Env* src_env;
|
|
||||||
int return_code;
|
|
||||||
|
|
||||||
if((return_code = envid2env(0, &src_env, 0)) < 0)
|
|
||||||
return return_code;
|
|
||||||
if((return_code = envid2env(envid, &dest_env, 0)) < 0)
|
|
||||||
return return_code;
|
|
||||||
|
|
||||||
if(!dest_env->env_ipc_recving)
|
|
||||||
return -E_IPC_NOT_RECV;
|
|
||||||
|
|
||||||
if((uintptr_t) srcva < UTOP && dest_env->env_ipc_dstva) {
|
|
||||||
if(!SYS_CHECKADDR(srcva)) return -E_INVAL;
|
|
||||||
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
|
|
||||||
|
|
||||||
pte_t* srcpte;
|
|
||||||
struct PageInfo* page = page_lookup(src_env->env_pgdir, srcva, &srcpte);
|
|
||||||
if(page == NULL) return -E_INVAL;
|
|
||||||
if(perm & PTE_W && !(*srcpte & PTE_W)) return -E_INVAL;
|
|
||||||
|
|
||||||
page_insert(dest_env->env_pgdir, page, dest_env->env_ipc_dstva, perm);
|
|
||||||
dest_env->env_ipc_perm = perm;
|
|
||||||
}
|
|
||||||
|
|
||||||
dest_env->env_ipc_from = src_env->env_id;
|
|
||||||
dest_env->env_ipc_value = value;
|
|
||||||
dest_env->env_ipc_recving = false;
|
|
||||||
if(dest_env->env_status == ENV_NOT_RUNNABLE)
|
|
||||||
dest_env->env_status = ENV_RUNNABLE;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block until a value is ready. Record that you want to receive
|
|
||||||
// using the env_ipc_recving and env_ipc_dstva fields of struct Env,
|
|
||||||
// mark yourself not runnable, and then give up the CPU.
|
|
||||||
//
|
|
||||||
// If 'dstva' is < UTOP, then you are willing to receive a page of data.
|
|
||||||
// 'dstva' is the virtual address at which the sent page should be mapped.
|
|
||||||
//
|
|
||||||
// This function only returns on error, but the system call will eventually
|
|
||||||
// return 0 on success.
|
|
||||||
// Return < 0 on error. Errors are:
|
|
||||||
// -E_INVAL if dstva < UTOP but dstva is not page-aligned.
|
|
||||||
static int
|
|
||||||
sys_ipc_recv(void *dstva)
|
|
||||||
{
|
|
||||||
struct Env* env;
|
|
||||||
int return_code;
|
|
||||||
|
|
||||||
if((return_code = envid2env(0, &env, 1)) < 0)
|
|
||||||
return return_code;
|
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
if((uintptr_t) dstva < UTOP) {
|
|
||||||
if(!SYS_CHECKADDR(dstva)) return -E_INVAL;
|
|
||||||
env->env_ipc_dstva = dstva;
|
|
||||||
}
|
|
||||||
env->env_ipc_recving = true;
|
|
||||||
env->env_status = ENV_NOT_RUNNABLE;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatches to the correct kernel function, passing the arguments.
|
// Dispatches to the correct kernel function, passing the arguments.
|
||||||
int32_t
|
int32_t
|
||||||
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
||||||
@@ -401,27 +78,6 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
|
|||||||
return sys_getenvid();
|
return sys_getenvid();
|
||||||
case SYS_env_destroy:
|
case SYS_env_destroy:
|
||||||
return sys_env_destroy(a1);
|
return sys_env_destroy(a1);
|
||||||
case SYS_yield:
|
|
||||||
sys_yield();
|
|
||||||
return 0;
|
|
||||||
case SYS_exofork:
|
|
||||||
return sys_exofork();
|
|
||||||
case SYS_env_set_status:
|
|
||||||
return sys_env_set_status(a1, a2);
|
|
||||||
case SYS_env_set_pgfault_upcall:
|
|
||||||
return sys_env_set_pgfault_upcall(a1, (void*) a2);
|
|
||||||
case SYS_page_alloc:
|
|
||||||
return sys_page_alloc(a1, (void*) a2, a3);
|
|
||||||
case SYS_page_map:
|
|
||||||
return sys_page_map(a1, (void*) a2, a3, (void*) a4, a5);
|
|
||||||
case SYS_page_unmap:
|
|
||||||
return sys_page_unmap(a1, (void*) a2);
|
|
||||||
case SYS_ipc_try_send:
|
|
||||||
return sys_ipc_try_send(a1, a2, (void*) a3, a4);
|
|
||||||
case SYS_ipc_recv:
|
|
||||||
return sys_ipc_recv((void*) a1);
|
|
||||||
case SYS_env_set_trapframe:
|
|
||||||
return sys_env_set_trapframe(a1, (void*) a2);
|
|
||||||
default:
|
default:
|
||||||
return -E_INVAL;
|
return -E_INVAL;
|
||||||
}
|
}
|
||||||
|
|||||||
188
kern/trap.c
188
kern/trap.c
@@ -8,11 +8,6 @@
|
|||||||
#include <kern/monitor.h>
|
#include <kern/monitor.h>
|
||||||
#include <kern/env.h>
|
#include <kern/env.h>
|
||||||
#include <kern/syscall.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;
|
static struct Taskstate ts;
|
||||||
|
|
||||||
@@ -60,8 +55,6 @@ static const char *trapname(int trapno)
|
|||||||
return excnames[trapno];
|
return excnames[trapno];
|
||||||
if (trapno == T_SYSCALL)
|
if (trapno == T_SYSCALL)
|
||||||
return "System call";
|
return "System call";
|
||||||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16)
|
|
||||||
return "Hardware Interrupt";
|
|
||||||
return "(unknown trap)";
|
return "(unknown trap)";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,13 +82,6 @@ void t_simderr();
|
|||||||
void t_syscall();
|
void t_syscall();
|
||||||
void t_default();
|
void t_default();
|
||||||
|
|
||||||
void irq_timer();
|
|
||||||
void irq_kbd();
|
|
||||||
void irq_serial();
|
|
||||||
void irq_spurious();
|
|
||||||
void irq_ide();
|
|
||||||
void irq_error();
|
|
||||||
|
|
||||||
void
|
void
|
||||||
trap_init(void)
|
trap_init(void)
|
||||||
{
|
{
|
||||||
@@ -132,13 +118,6 @@ trap_init(void)
|
|||||||
SETGATE(idt[T_SYSCALL], 0, GD_KT, t_syscall, 3);
|
SETGATE(idt[T_SYSCALL], 0, GD_KT, t_syscall, 3);
|
||||||
SETGATE(idt[T_DEFAULT], 0, GD_KT, t_default, 0);
|
SETGATE(idt[T_DEFAULT], 0, GD_KT, t_default, 0);
|
||||||
|
|
||||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, irq_timer, 0);
|
|
||||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, irq_kbd, 0);
|
|
||||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, irq_serial, 0);
|
|
||||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, irq_spurious, 0);
|
|
||||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, irq_ide, 0);
|
|
||||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, irq_error, 0);
|
|
||||||
|
|
||||||
// Per-CPU setup
|
// Per-CPU setup
|
||||||
trap_init_percpu();
|
trap_init_percpu();
|
||||||
}
|
}
|
||||||
@@ -147,45 +126,20 @@ trap_init(void)
|
|||||||
void
|
void
|
||||||
trap_init_percpu(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:
|
|
||||||
|
|
||||||
// Setup a TSS so that we get the right stack
|
// Setup a TSS so that we get the right stack
|
||||||
// when we trap to the kernel.
|
// when we trap to the kernel.
|
||||||
thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - thiscpu->cpu_id * (KSTKSIZE + KSTKGAP);
|
ts.ts_esp0 = KSTACKTOP;
|
||||||
thiscpu->cpu_ts.ts_ss0 = GD_KD;
|
ts.ts_ss0 = GD_KD;
|
||||||
thiscpu->cpu_ts.ts_iomb = sizeof(struct Taskstate);
|
ts.ts_iomb = sizeof(struct Taskstate);
|
||||||
|
|
||||||
// Initialize the TSS slot of the gdt.
|
// Initialize the TSS slot of the gdt.
|
||||||
gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts),
|
gdt[GD_TSS0 >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
|
||||||
sizeof(struct Taskstate) - 1, 0);
|
sizeof(struct Taskstate) - 1, 0);
|
||||||
gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0;
|
gdt[GD_TSS0 >> 3].sd_s = 0;
|
||||||
|
|
||||||
// Load the TSS selector (like other segment selectors, the
|
// Load the TSS selector (like other segment selectors, the
|
||||||
// bottom three bits are special; we leave them 0)
|
// bottom three bits are special; we leave them 0)
|
||||||
ltr(GD_TSS0 + (cpunum() << 3));
|
ltr(GD_TSS0);
|
||||||
|
|
||||||
// Load the IDT
|
// Load the IDT
|
||||||
lidt(&idt_pd);
|
lidt(&idt_pd);
|
||||||
@@ -194,7 +148,7 @@ trap_init_percpu(void)
|
|||||||
void
|
void
|
||||||
print_trapframe(struct Trapframe *tf)
|
print_trapframe(struct Trapframe *tf)
|
||||||
{
|
{
|
||||||
cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum());
|
cprintf("TRAP frame at %p\n", tf);
|
||||||
print_regs(&tf->tf_regs);
|
print_regs(&tf->tf_regs);
|
||||||
cprintf(" es 0x----%04x\n", tf->tf_es);
|
cprintf(" es 0x----%04x\n", tf->tf_es);
|
||||||
cprintf(" ds 0x----%04x\n", tf->tf_ds);
|
cprintf(" ds 0x----%04x\n", tf->tf_ds);
|
||||||
@@ -257,33 +211,8 @@ trap_dispatch(struct Trapframe *tf)
|
|||||||
tf->tf_regs.reg_esi);
|
tf->tf_regs.reg_esi);
|
||||||
tf->tf_regs.reg_eax = returned;
|
tf->tf_regs.reg_eax = returned;
|
||||||
return;
|
return;
|
||||||
} else if (tf->tf_trapno == IRQ_OFFSET + IRQ_TIMER) {
|
|
||||||
lapic_eoi();
|
|
||||||
sched_yield();
|
|
||||||
} else if (tf->tf_trapno == IRQ_OFFSET + IRQ_KBD) {
|
|
||||||
kbd_intr();
|
|
||||||
return;
|
|
||||||
} else if (tf->tf_trapno == IRQ_OFFSET + IRQ_SERIAL) {
|
|
||||||
serial_intr();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle spurious interrupts
|
|
||||||
// The hardware sometimes raises these because of noise on the
|
|
||||||
// IRQ line or other reasons. We don't care.
|
|
||||||
if (tf->tf_trapno == IRQ_OFFSET + IRQ_SPURIOUS) {
|
|
||||||
cprintf("Spurious interrupt on irq 7\n");
|
|
||||||
print_trapframe(tf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle clock interrupts. Don't forget to acknowledge the
|
|
||||||
// interrupt using lapic_eoi() before calling the scheduler!
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
|
|
||||||
// Handle keyboard and serial interrupts.
|
|
||||||
// LAB 5: Your code here.
|
|
||||||
|
|
||||||
// Unexpected trap: The user process or the kernel has a bug.
|
// Unexpected trap: The user process or the kernel has a bug.
|
||||||
print_trapframe(tf);
|
print_trapframe(tf);
|
||||||
if (tf->tf_cs == GD_KT)
|
if (tf->tf_cs == GD_KT)
|
||||||
@@ -301,35 +230,17 @@ trap(struct Trapframe *tf)
|
|||||||
// of GCC rely on DF being clear
|
// of GCC rely on DF being clear
|
||||||
asm volatile("cld" ::: "cc");
|
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
|
// Check that interrupts are disabled. If this assertion
|
||||||
// fails, DO NOT be tempted to fix it by inserting a "cli" in
|
// fails, DO NOT be tempted to fix it by inserting a "cli" in
|
||||||
// the interrupt path.
|
// the interrupt path.
|
||||||
assert(!(read_eflags() & FL_IF));
|
assert(!(read_eflags() & FL_IF));
|
||||||
|
|
||||||
|
cprintf("Incoming TRAP frame at %p\n", tf);
|
||||||
|
|
||||||
if ((tf->tf_cs & 3) == 3) {
|
if ((tf->tf_cs & 3) == 3) {
|
||||||
// Trapped from user mode.
|
// Trapped from user mode.
|
||||||
// Acquire the big kernel lock before doing any
|
|
||||||
// serious kernel work.
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
lock_kernel();
|
|
||||||
assert(curenv);
|
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)
|
// Copy trap frame (which is currently on the stack)
|
||||||
// into 'curenv->env_tf', so that running the environment
|
// into 'curenv->env_tf', so that running the environment
|
||||||
// will restart at the trap point.
|
// will restart at the trap point.
|
||||||
@@ -345,13 +256,9 @@ trap(struct Trapframe *tf)
|
|||||||
// Dispatch based on what type of trap occurred
|
// Dispatch based on what type of trap occurred
|
||||||
trap_dispatch(tf);
|
trap_dispatch(tf);
|
||||||
|
|
||||||
// If we made it to this point, then no other environment was
|
// Return to the current environment, which should be running.
|
||||||
// scheduled, so we should return to the current environment
|
assert(curenv && curenv->env_status == ENV_RUNNING);
|
||||||
// if doing so makes sense.
|
env_run(curenv);
|
||||||
if (curenv && curenv->env_status == ENV_RUNNING)
|
|
||||||
env_run(curenv);
|
|
||||||
else
|
|
||||||
sched_yield();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -366,75 +273,14 @@ page_fault_handler(struct Trapframe *tf)
|
|||||||
// Handle kernel-mode page faults.
|
// Handle kernel-mode page faults.
|
||||||
|
|
||||||
// LAB 3: Your code here.
|
// LAB 3: Your code here.
|
||||||
if(!curenv)
|
|
||||||
panic("kernel-level page fault, va %p, eip %p", fault_va, tf->tf_eip);
|
|
||||||
|
|
||||||
// We've already handled kernel-mode exceptions, so if we get here,
|
// We've already handled kernel-mode exceptions, so if we get here,
|
||||||
// the page fault happened in user mode.
|
// the page fault happened in user mode.
|
||||||
|
|
||||||
// Call the environment's page fault upcall, if one exists. Set up a
|
// Destroy the environment that caused the fault.
|
||||||
// page fault stack frame on the user exception stack (below
|
cprintf("[%08x] user fault va %08x ip %08x\n",
|
||||||
// UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
|
curenv->env_id, fault_va, tf->tf_eip);
|
||||||
//
|
print_trapframe(tf);
|
||||||
// The page fault upcall might cause another page fault, in which case
|
env_destroy(curenv);
|
||||||
// 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.
|
|
||||||
if(!curenv->env_pgfault_upcall) {
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
user_mem_assert(curenv, curenv->env_pgfault_upcall, 1, PTE_U | PTE_P);
|
|
||||||
user_mem_assert(curenv, (void*) UXSTACKTOP - 1, 1, PTE_U | PTE_P | PTE_W);
|
|
||||||
|
|
||||||
uintptr_t top_addr = UXSTACKTOP;
|
|
||||||
if(tf->tf_esp <= UXSTACKTOP && tf->tf_esp >= (UXSTACKTOP - PGSIZE)) {
|
|
||||||
top_addr = tf->tf_esp - 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UTrapframe utf;
|
|
||||||
utf.utf_eflags = tf->tf_eflags;
|
|
||||||
utf.utf_eip = tf->tf_eip;
|
|
||||||
utf.utf_err = tf->tf_err;
|
|
||||||
utf.utf_esp = tf->tf_esp;
|
|
||||||
utf.utf_fault_va = fault_va;
|
|
||||||
utf.utf_regs = tf->tf_regs;
|
|
||||||
|
|
||||||
struct UTrapframe* push_to = (struct UTrapframe*) top_addr - 1;
|
|
||||||
if((uintptr_t) push_to < USTACKTOP - PGSIZE) {
|
|
||||||
cprintf("[%08x] stack overflow in page fault handler\n",
|
|
||||||
curenv->env_id);
|
|
||||||
env_destroy(curenv);
|
|
||||||
}
|
|
||||||
|
|
||||||
*push_to = utf;
|
|
||||||
curenv->env_tf.tf_eip = (uintptr_t) curenv->env_pgfault_upcall;
|
|
||||||
curenv->env_tf.tf_esp = (uintptr_t) push_to;
|
|
||||||
env_run(curenv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <inc/memlayout.h>
|
#include <inc/memlayout.h>
|
||||||
#include <inc/trap.h>
|
#include <inc/trap.h>
|
||||||
|
|
||||||
#include <kern/picirq.h>
|
|
||||||
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
@@ -82,12 +81,6 @@ TRAPHANDLER_NOEC(t_mchk, T_MCHK);
|
|||||||
TRAPHANDLER_NOEC(t_simderr, T_SIMDERR);
|
TRAPHANDLER_NOEC(t_simderr, T_SIMDERR);
|
||||||
TRAPHANDLER_NOEC(t_syscall, T_SYSCALL);
|
TRAPHANDLER_NOEC(t_syscall, T_SYSCALL);
|
||||||
TRAPHANDLER(t_default, T_DEFAULT);
|
TRAPHANDLER(t_default, T_DEFAULT);
|
||||||
TRAPHANDLER_NOEC(irq_timer, IRQ_OFFSET + IRQ_TIMER);
|
|
||||||
TRAPHANDLER_NOEC(irq_kbd, IRQ_OFFSET + IRQ_KBD);
|
|
||||||
TRAPHANDLER_NOEC(irq_serial, IRQ_OFFSET + IRQ_SERIAL);
|
|
||||||
TRAPHANDLER_NOEC(irq_spurious, IRQ_OFFSET + IRQ_SPURIOUS);
|
|
||||||
TRAPHANDLER_NOEC(irq_ide, IRQ_OFFSET + IRQ_IDE);
|
|
||||||
TRAPHANDLER_NOEC(irq_error, IRQ_OFFSET + IRQ_ERROR);
|
|
||||||
|
|
||||||
// HINT 1 : TRAPHANDLER_NOEC(t_divide, T_DIVIDE);
|
// HINT 1 : TRAPHANDLER_NOEC(t_divide, T_DIVIDE);
|
||||||
// Do something like this if there is no error code for the trap
|
// Do something like this if there is no error code for the trap
|
||||||
|
|||||||
15
lib/Makefrag
15
lib/Makefrag
@@ -10,23 +10,8 @@ LIB_SRCFILES := lib/console.c \
|
|||||||
lib/string.c \
|
lib/string.c \
|
||||||
lib/syscall.c
|
lib/syscall.c
|
||||||
|
|
||||||
LIB_SRCFILES := $(LIB_SRCFILES) \
|
|
||||||
lib/pgfault.c \
|
|
||||||
lib/pfentry.S \
|
|
||||||
lib/fork.c \
|
|
||||||
lib/ipc.c
|
|
||||||
|
|
||||||
LIB_SRCFILES := $(LIB_SRCFILES) \
|
|
||||||
lib/args.c \
|
|
||||||
lib/fd.c \
|
|
||||||
lib/file.c \
|
|
||||||
lib/fprintf.c \
|
|
||||||
lib/pageref.c \
|
|
||||||
lib/spawn.c
|
|
||||||
|
|
||||||
LIB_SRCFILES := $(LIB_SRCFILES) \
|
|
||||||
lib/pipe.c \
|
|
||||||
lib/wait.c
|
|
||||||
|
|
||||||
LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
|
LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
|
||||||
LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))
|
LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))
|
||||||
|
|||||||
73
lib/args.c
73
lib/args.c
@@ -1,73 +0,0 @@
|
|||||||
#include <inc/args.h>
|
|
||||||
#include <inc/string.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
argstart(int *argc, char **argv, struct Argstate *args)
|
|
||||||
{
|
|
||||||
args->argc = argc;
|
|
||||||
args->argv = (const char **) argv;
|
|
||||||
args->curarg = (*argc > 1 && argv ? "" : 0);
|
|
||||||
args->argvalue = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
argnext(struct Argstate *args)
|
|
||||||
{
|
|
||||||
int arg;
|
|
||||||
|
|
||||||
args->argvalue = 0;
|
|
||||||
|
|
||||||
// Done processing arguments if args->curarg == 0
|
|
||||||
if (args->curarg == 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!*args->curarg) {
|
|
||||||
// Need to process the next argument
|
|
||||||
// Check for end of argument list
|
|
||||||
if (*args->argc == 1
|
|
||||||
|| args->argv[1][0] != '-'
|
|
||||||
|| args->argv[1][1] == '\0')
|
|
||||||
goto endofargs;
|
|
||||||
// Shift arguments down one
|
|
||||||
args->curarg = args->argv[1] + 1;
|
|
||||||
memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1));
|
|
||||||
(*args->argc)--;
|
|
||||||
// Check for "--": end of argument list
|
|
||||||
if (args->curarg[0] == '-' && args->curarg[1] == '\0')
|
|
||||||
goto endofargs;
|
|
||||||
}
|
|
||||||
|
|
||||||
arg = (unsigned char) *args->curarg;
|
|
||||||
args->curarg++;
|
|
||||||
return arg;
|
|
||||||
|
|
||||||
endofargs:
|
|
||||||
args->curarg = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
argvalue(struct Argstate *args)
|
|
||||||
{
|
|
||||||
return (char*) (args->argvalue ? args->argvalue : argnextvalue(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
argnextvalue(struct Argstate *args)
|
|
||||||
{
|
|
||||||
if (!args->curarg)
|
|
||||||
return 0;
|
|
||||||
if (*args->curarg) {
|
|
||||||
args->argvalue = args->curarg;
|
|
||||||
args->curarg = "";
|
|
||||||
} else if (*args->argc > 1) {
|
|
||||||
args->argvalue = args->argv[1];
|
|
||||||
memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1));
|
|
||||||
(*args->argc)--;
|
|
||||||
} else {
|
|
||||||
args->argvalue = 0;
|
|
||||||
args->curarg = 0;
|
|
||||||
}
|
|
||||||
return (char*) args->argvalue;
|
|
||||||
}
|
|
||||||
|
|
||||||
111
lib/console.c
111
lib/console.c
@@ -15,114 +15,11 @@ cputchar(int ch)
|
|||||||
int
|
int
|
||||||
getchar(void)
|
getchar(void)
|
||||||
{
|
{
|
||||||
unsigned char c;
|
|
||||||
int r;
|
int r;
|
||||||
|
// sys_cgetc does not block, but getchar should.
|
||||||
// JOS does, however, support standard _input_ redirection,
|
while ((r = sys_cgetc()) == 0)
|
||||||
// allowing the user to redirect script files to the shell and such.
|
;
|
||||||
// getchar() reads a character from file descriptor 0.
|
return r;
|
||||||
r = read(0, &c, 1);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
if (r < 1)
|
|
||||||
return -E_EOF;
|
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// "Real" console file descriptor implementation.
|
|
||||||
// The putchar/getchar functions above will still come here by default,
|
|
||||||
// but now can be redirected to files, pipes, etc., via the fd layer.
|
|
||||||
|
|
||||||
static ssize_t devcons_read(struct Fd*, void*, size_t);
|
|
||||||
static ssize_t devcons_write(struct Fd*, const void*, size_t);
|
|
||||||
static int devcons_close(struct Fd*);
|
|
||||||
static int devcons_stat(struct Fd*, struct Stat*);
|
|
||||||
|
|
||||||
struct Dev devcons =
|
|
||||||
{
|
|
||||||
.dev_id = 'c',
|
|
||||||
.dev_name = "cons",
|
|
||||||
.dev_read = devcons_read,
|
|
||||||
.dev_write = devcons_write,
|
|
||||||
.dev_close = devcons_close,
|
|
||||||
.dev_stat = devcons_stat
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
|
||||||
iscons(int fdnum)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Fd *fd;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
|
||||||
return r;
|
|
||||||
return fd->fd_dev_id == devcons.dev_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
opencons(void)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Fd* fd;
|
|
||||||
|
|
||||||
if ((r = fd_alloc(&fd)) < 0)
|
|
||||||
return r;
|
|
||||||
if ((r = sys_page_alloc(0, fd, PTE_P|PTE_U|PTE_W|PTE_SHARE)) < 0)
|
|
||||||
return r;
|
|
||||||
fd->fd_dev_id = devcons.dev_id;
|
|
||||||
fd->fd_omode = O_RDWR;
|
|
||||||
return fd2num(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
devcons_read(struct Fd *fd, void *vbuf, size_t n)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
|
|
||||||
if (n == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
while ((c = sys_cgetc()) == 0)
|
|
||||||
sys_yield();
|
|
||||||
if (c < 0)
|
|
||||||
return c;
|
|
||||||
if (c == 0x04) // ctl-d is eof
|
|
||||||
return 0;
|
|
||||||
*(char*)vbuf = c;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
devcons_write(struct Fd *fd, const void *vbuf, size_t n)
|
|
||||||
{
|
|
||||||
int tot, m;
|
|
||||||
char buf[128];
|
|
||||||
|
|
||||||
// mistake: have to nul-terminate arg to sys_cputs,
|
|
||||||
// so we have to copy vbuf into buf in chunks and nul-terminate.
|
|
||||||
for (tot = 0; tot < n; tot += m) {
|
|
||||||
m = n - tot;
|
|
||||||
if (m > sizeof(buf) - 1)
|
|
||||||
m = sizeof(buf) - 1;
|
|
||||||
memmove(buf, (char*)vbuf + tot, m);
|
|
||||||
sys_cputs(buf, m);
|
|
||||||
}
|
|
||||||
return tot;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
devcons_close(struct Fd *fd)
|
|
||||||
{
|
|
||||||
USED(fd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
devcons_stat(struct Fd *fd, struct Stat *stat)
|
|
||||||
{
|
|
||||||
strcpy(stat->st_name, "<cons>");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
void
|
void
|
||||||
exit(void)
|
exit(void)
|
||||||
{
|
{
|
||||||
close_all();
|
|
||||||
sys_env_destroy(0);
|
sys_env_destroy(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
320
lib/fd.c
320
lib/fd.c
@@ -1,320 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
#define debug 0
|
|
||||||
|
|
||||||
// Maximum number of file descriptors a program may hold open concurrently
|
|
||||||
#define MAXFD 32
|
|
||||||
// Bottom of file descriptor area
|
|
||||||
#define FDTABLE 0xD0000000
|
|
||||||
// Bottom of file data area. We reserve one data page for each FD,
|
|
||||||
// which devices can use if they choose.
|
|
||||||
#define FILEDATA (FDTABLE + MAXFD*PGSIZE)
|
|
||||||
|
|
||||||
// Return the 'struct Fd*' for file descriptor index i
|
|
||||||
#define INDEX2FD(i) ((struct Fd*) (FDTABLE + (i)*PGSIZE))
|
|
||||||
// Return the file data page for file descriptor index i
|
|
||||||
#define INDEX2DATA(i) ((char*) (FILEDATA + (i)*PGSIZE))
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------
|
|
||||||
// File descriptor manipulators
|
|
||||||
// --------------------------------------------------------------
|
|
||||||
|
|
||||||
int
|
|
||||||
fd2num(struct Fd *fd)
|
|
||||||
{
|
|
||||||
return ((uintptr_t) fd - FDTABLE) / PGSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
char*
|
|
||||||
fd2data(struct Fd *fd)
|
|
||||||
{
|
|
||||||
return INDEX2DATA(fd2num(fd));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finds the smallest i from 0 to MAXFD-1 that doesn't have
|
|
||||||
// its fd page mapped.
|
|
||||||
// Sets *fd_store to the corresponding fd page virtual address.
|
|
||||||
//
|
|
||||||
// fd_alloc does NOT actually allocate an fd page.
|
|
||||||
// It is up to the caller to allocate the page somehow.
|
|
||||||
// This means that if someone calls fd_alloc twice in a row
|
|
||||||
// without allocating the first page we return, we'll return the same
|
|
||||||
// page the second time.
|
|
||||||
//
|
|
||||||
// Hint: Use INDEX2FD.
|
|
||||||
//
|
|
||||||
// Returns 0 on success, < 0 on error. Errors are:
|
|
||||||
// -E_MAX_FD: no more file descriptors
|
|
||||||
// On error, *fd_store is set to 0.
|
|
||||||
int
|
|
||||||
fd_alloc(struct Fd **fd_store)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct Fd *fd;
|
|
||||||
|
|
||||||
for (i = 0; i < MAXFD; i++) {
|
|
||||||
fd = INDEX2FD(i);
|
|
||||||
if ((uvpd[PDX(fd)] & PTE_P) == 0 || (uvpt[PGNUM(fd)] & PTE_P) == 0) {
|
|
||||||
*fd_store = fd;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*fd_store = 0;
|
|
||||||
return -E_MAX_OPEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that fdnum is in range and mapped.
|
|
||||||
// If it is, set *fd_store to the fd page virtual address.
|
|
||||||
//
|
|
||||||
// Returns 0 on success (the page is in range and mapped), < 0 on error.
|
|
||||||
// Errors are:
|
|
||||||
// -E_INVAL: fdnum was either not in range or not mapped.
|
|
||||||
int
|
|
||||||
fd_lookup(int fdnum, struct Fd **fd_store)
|
|
||||||
{
|
|
||||||
struct Fd *fd;
|
|
||||||
|
|
||||||
if (fdnum < 0 || fdnum >= MAXFD) {
|
|
||||||
if (debug)
|
|
||||||
cprintf("[%08x] bad fd %d\n", thisenv->env_id, fdnum);
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
fd = INDEX2FD(fdnum);
|
|
||||||
if (!(uvpd[PDX(fd)] & PTE_P) || !(uvpt[PGNUM(fd)] & PTE_P)) {
|
|
||||||
if (debug)
|
|
||||||
cprintf("[%08x] closed fd %d\n", thisenv->env_id, fdnum);
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
*fd_store = fd;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frees file descriptor 'fd' by closing the corresponding file
|
|
||||||
// and unmapping the file descriptor page.
|
|
||||||
// If 'must_exist' is 0, then fd can be a closed or nonexistent file
|
|
||||||
// descriptor; the function will return 0 and have no other effect.
|
|
||||||
// If 'must_exist' is 1, then fd_close returns -E_INVAL when passed a
|
|
||||||
// closed or nonexistent file descriptor.
|
|
||||||
// Returns 0 on success, < 0 on error.
|
|
||||||
int
|
|
||||||
fd_close(struct Fd *fd, bool must_exist)
|
|
||||||
{
|
|
||||||
struct Fd *fd2;
|
|
||||||
struct Dev *dev;
|
|
||||||
int r;
|
|
||||||
if ((r = fd_lookup(fd2num(fd), &fd2)) < 0
|
|
||||||
|| fd != fd2)
|
|
||||||
return (must_exist ? r : 0);
|
|
||||||
if ((r = dev_lookup(fd->fd_dev_id, &dev)) >= 0) {
|
|
||||||
if (dev->dev_close)
|
|
||||||
r = (*dev->dev_close)(fd);
|
|
||||||
else
|
|
||||||
r = 0;
|
|
||||||
}
|
|
||||||
// Make sure fd is unmapped. Might be a no-op if
|
|
||||||
// (*dev->dev_close)(fd) already unmapped it.
|
|
||||||
(void) sys_page_unmap(0, fd);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------
|
|
||||||
// File functions
|
|
||||||
// --------------------------------------------------------------
|
|
||||||
|
|
||||||
static struct Dev *devtab[] =
|
|
||||||
{
|
|
||||||
&devfile,
|
|
||||||
&devpipe,
|
|
||||||
&devcons,
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
|
||||||
dev_lookup(int dev_id, struct Dev **dev)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; devtab[i]; i++)
|
|
||||||
if (devtab[i]->dev_id == dev_id) {
|
|
||||||
*dev = devtab[i];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
cprintf("[%08x] unknown device type %d\n", thisenv->env_id, dev_id);
|
|
||||||
*dev = 0;
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
close(int fdnum)
|
|
||||||
{
|
|
||||||
struct Fd *fd;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
|
||||||
return r;
|
|
||||||
else
|
|
||||||
return fd_close(fd, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
close_all(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < MAXFD; i++)
|
|
||||||
close(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make file descriptor 'newfdnum' a duplicate of file descriptor 'oldfdnum'.
|
|
||||||
// For instance, writing onto either file descriptor will affect the
|
|
||||||
// file and the file offset of the other.
|
|
||||||
// Closes any previously open file descriptor at 'newfdnum'.
|
|
||||||
// This is implemented using virtual memory tricks (of course!).
|
|
||||||
int
|
|
||||||
dup(int oldfdnum, int newfdnum)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
char *ova, *nva;
|
|
||||||
pte_t pte;
|
|
||||||
struct Fd *oldfd, *newfd;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(oldfdnum, &oldfd)) < 0)
|
|
||||||
return r;
|
|
||||||
close(newfdnum);
|
|
||||||
|
|
||||||
newfd = INDEX2FD(newfdnum);
|
|
||||||
ova = fd2data(oldfd);
|
|
||||||
nva = fd2data(newfd);
|
|
||||||
|
|
||||||
if ((uvpd[PDX(ova)] & PTE_P) && (uvpt[PGNUM(ova)] & PTE_P))
|
|
||||||
if ((r = sys_page_map(0, ova, 0, nva, uvpt[PGNUM(ova)] & PTE_SYSCALL)) < 0)
|
|
||||||
goto err;
|
|
||||||
if ((r = sys_page_map(0, oldfd, 0, newfd, uvpt[PGNUM(oldfd)] & PTE_SYSCALL)) < 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
return newfdnum;
|
|
||||||
|
|
||||||
err:
|
|
||||||
sys_page_unmap(0, newfd);
|
|
||||||
sys_page_unmap(0, nva);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
read(int fdnum, void *buf, size_t n)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Dev *dev;
|
|
||||||
struct Fd *fd;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
|
||||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
|
||||||
return r;
|
|
||||||
if ((fd->fd_omode & O_ACCMODE) == O_WRONLY) {
|
|
||||||
cprintf("[%08x] read %d -- bad mode\n", thisenv->env_id, fdnum);
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
if (!dev->dev_read)
|
|
||||||
return -E_NOT_SUPP;
|
|
||||||
return (*dev->dev_read)(fd, buf, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
readn(int fdnum, void *buf, size_t n)
|
|
||||||
{
|
|
||||||
int m, tot;
|
|
||||||
|
|
||||||
for (tot = 0; tot < n; tot += m) {
|
|
||||||
m = read(fdnum, (char*)buf + tot, n - tot);
|
|
||||||
if (m < 0)
|
|
||||||
return m;
|
|
||||||
if (m == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return tot;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
write(int fdnum, const void *buf, size_t n)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Dev *dev;
|
|
||||||
struct Fd *fd;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
|
||||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
|
||||||
return r;
|
|
||||||
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
|
|
||||||
cprintf("[%08x] write %d -- bad mode\n", thisenv->env_id, fdnum);
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
if (debug)
|
|
||||||
cprintf("write %d %p %d via dev %s\n",
|
|
||||||
fdnum, buf, n, dev->dev_name);
|
|
||||||
if (!dev->dev_write)
|
|
||||||
return -E_NOT_SUPP;
|
|
||||||
return (*dev->dev_write)(fd, buf, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
seek(int fdnum, off_t offset)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Fd *fd;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
|
||||||
return r;
|
|
||||||
fd->fd_offset = offset;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
ftruncate(int fdnum, off_t newsize)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Dev *dev;
|
|
||||||
struct Fd *fd;
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
|
||||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
|
||||||
return r;
|
|
||||||
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
|
|
||||||
cprintf("[%08x] ftruncate %d -- bad mode\n",
|
|
||||||
thisenv->env_id, fdnum);
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
if (!dev->dev_trunc)
|
|
||||||
return -E_NOT_SUPP;
|
|
||||||
return (*dev->dev_trunc)(fd, newsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
fstat(int fdnum, struct Stat *stat)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Dev *dev;
|
|
||||||
struct Fd *fd;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
|
||||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
|
||||||
return r;
|
|
||||||
if (!dev->dev_stat)
|
|
||||||
return -E_NOT_SUPP;
|
|
||||||
stat->st_name[0] = 0;
|
|
||||||
stat->st_size = 0;
|
|
||||||
stat->st_isdir = 0;
|
|
||||||
stat->st_dev = dev;
|
|
||||||
return (*dev->dev_stat)(fd, stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
stat(const char *path, struct Stat *stat)
|
|
||||||
{
|
|
||||||
int fd, r;
|
|
||||||
|
|
||||||
if ((fd = open(path, O_RDONLY)) < 0)
|
|
||||||
return fd;
|
|
||||||
r = fstat(fd, stat);
|
|
||||||
close(fd);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
186
lib/file.c
186
lib/file.c
@@ -1,186 +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
|
|
||||||
size_t writesize = MIN(sizeof(fsipcbuf.write.req_buf), n);
|
|
||||||
|
|
||||||
fsipcbuf.write.req_fileid = fd->fd_file.id;
|
|
||||||
fsipcbuf.write.req_n = writesize;
|
|
||||||
memcpy(fsipcbuf.write.req_buf, buf, writesize);
|
|
||||||
|
|
||||||
return fsipc(FSREQ_WRITE, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
147
lib/fork.c
147
lib/fork.c
@@ -1,147 +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>).
|
|
||||||
if(!((err & FEC_WR) && (uvpt[(uintptr_t) addr >> PGSHIFT] & PTE_COW)))
|
|
||||||
panic("page fault (addr %p)! %c", addr, (err & FEC_WR) ? 'w' : 'r');
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
void* temp_addr = (void*) PFTEMP;
|
|
||||||
void* fault_addr = ROUNDDOWN(addr, PGSIZE);
|
|
||||||
if(sys_page_alloc(0, temp_addr, PTE_P | PTE_W | PTE_U) < 0)
|
|
||||||
panic("failed to allocate new page");
|
|
||||||
|
|
||||||
memcpy(temp_addr, fault_addr, PGSIZE);
|
|
||||||
sys_page_map(0, temp_addr, 0, fault_addr, PTE_P | PTE_U | PTE_W);
|
|
||||||
sys_page_unmap(0, temp_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Map our virtual page pn (address pn*PGSIZE) into the target envid
|
|
||||||
// at the same virtual address. If the page is writable or copy-on-write,
|
|
||||||
// the new mapping must be created copy-on-write, and then our mapping must be
|
|
||||||
// marked copy-on-write as well. (Exercise: Why do we need to mark ours
|
|
||||||
// copy-on-write again if it was already copy-on-write at the beginning of
|
|
||||||
// this function?)
|
|
||||||
//
|
|
||||||
// Returns: 0 on success, < 0 on error.
|
|
||||||
// It is also OK to panic on error.
|
|
||||||
//
|
|
||||||
static int
|
|
||||||
duppage(envid_t envid, unsigned pn)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
bool change_own = false;
|
|
||||||
pte_t new_pte = uvpt[pn];
|
|
||||||
pte_t perms = new_pte & (PTE_P | PTE_U | PTE_W | PTE_AVAIL);
|
|
||||||
void* addr = (void*) (pn * PGSIZE);
|
|
||||||
|
|
||||||
// If we're writable, remove write permission
|
|
||||||
if(((new_pte & PTE_W) && !(new_pte & PTE_SHARE)) || (new_pte & PTE_COW)) {
|
|
||||||
perms = (perms & ~PTE_W) | PTE_COW;
|
|
||||||
change_own = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map either with the same permissions or with COW.
|
|
||||||
if((r = sys_page_map(0, addr, envid, addr, perms)) < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
// Update our own permissions if necessary
|
|
||||||
if(change_own) {
|
|
||||||
if((r = sys_page_map(0, addr, 0, addr, perms)) < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// User-level fork with copy-on-write.
|
|
||||||
// Set up our page fault handler appropriately.
|
|
||||||
// Create a child.
|
|
||||||
// Copy our address space and page fault handler setup to the child.
|
|
||||||
// Then mark the child as runnable and return.
|
|
||||||
//
|
|
||||||
// Returns: child's envid to the parent, 0 to the child, < 0 on error.
|
|
||||||
// It is also OK to panic on error.
|
|
||||||
//
|
|
||||||
// Hint:
|
|
||||||
// Use uvpd, uvpt, and duppage.
|
|
||||||
// Remember to fix "thisenv" in the child process.
|
|
||||||
// Neither user exception stack should ever be marked copy-on-write,
|
|
||||||
// so you must allocate a new page for the child's user exception stack.
|
|
||||||
//
|
|
||||||
envid_t
|
|
||||||
fork(void)
|
|
||||||
{
|
|
||||||
set_pgfault_handler(pgfault);
|
|
||||||
|
|
||||||
int return_code;
|
|
||||||
envid_t forked;
|
|
||||||
|
|
||||||
forked = sys_exofork();
|
|
||||||
if(forked < 0) return forked;
|
|
||||||
if(forked == 0) { thisenv = &envs[ENVX(sys_getenvid())]; return 0; }
|
|
||||||
|
|
||||||
// Map all accessible page directory entries
|
|
||||||
for(int pde_i = 0; pde_i < PDX(UTOP); pde_i++) {
|
|
||||||
pde_t pde = uvpd[pde_i];
|
|
||||||
if(!(pde & PTE_P)) continue;
|
|
||||||
|
|
||||||
// For each PDE, map all the underlying PTEs
|
|
||||||
for(int pte_i = 0; pte_i < NPTENTRIES; pte_i++) {
|
|
||||||
int pn = pde_i * NPTENTRIES + pte_i;
|
|
||||||
pte_t pte = uvpt[pn];
|
|
||||||
if(!(pte & PTE_P)) continue;
|
|
||||||
|
|
||||||
// Do not map user exception stack, though
|
|
||||||
if(pn == ((UXSTACKTOP - PGSIZE) >> PGSHIFT)) continue;
|
|
||||||
|
|
||||||
if((return_code = duppage(forked, pn)) < 0) return return_code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate new page for the exception stack
|
|
||||||
return_code = sys_page_alloc(forked, (void*) UXSTACKTOP - PGSIZE,
|
|
||||||
PTE_P | PTE_U | PTE_W);
|
|
||||||
if(return_code < 0) return return_code;
|
|
||||||
|
|
||||||
// Set the upcall entry point
|
|
||||||
sys_env_set_pgfault_upcall(forked, thisenv->env_pgfault_upcall);
|
|
||||||
sys_env_set_status(forked, ENV_RUNNABLE);
|
|
||||||
|
|
||||||
return forked;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Challenge!
|
|
||||||
int
|
|
||||||
sfork(void)
|
|
||||||
{
|
|
||||||
panic("sfork not implemented");
|
|
||||||
return -E_INVAL;
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
// Collect up to 256 characters into a buffer
|
|
||||||
// and perform ONE system call to print all of them,
|
|
||||||
// in order to make the lines output to the console atomic
|
|
||||||
// and prevent interrupts from causing context switches
|
|
||||||
// in the middle of a console output line and such.
|
|
||||||
struct printbuf {
|
|
||||||
int fd; // file descriptor
|
|
||||||
int idx; // current buffer index
|
|
||||||
ssize_t result; // accumulated results from write
|
|
||||||
int error; // first error that occurred
|
|
||||||
char buf[256];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
writebuf(struct printbuf *b)
|
|
||||||
{
|
|
||||||
if (b->error > 0) {
|
|
||||||
ssize_t result = write(b->fd, b->buf, b->idx);
|
|
||||||
if (result > 0)
|
|
||||||
b->result += result;
|
|
||||||
if (result != b->idx) // error, or wrote less than supplied
|
|
||||||
b->error = (result < 0 ? result : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
putch(int ch, void *thunk)
|
|
||||||
{
|
|
||||||
struct printbuf *b = (struct printbuf *) thunk;
|
|
||||||
b->buf[b->idx++] = ch;
|
|
||||||
if (b->idx == 256) {
|
|
||||||
writebuf(b);
|
|
||||||
b->idx = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
vfprintf(int fd, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
struct printbuf b;
|
|
||||||
|
|
||||||
b.fd = fd;
|
|
||||||
b.idx = 0;
|
|
||||||
b.result = 0;
|
|
||||||
b.error = 1;
|
|
||||||
vprintfmt(putch, &b, fmt, ap);
|
|
||||||
if (b.idx > 0)
|
|
||||||
writebuf(&b);
|
|
||||||
|
|
||||||
return (b.result ? b.result : b.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
fprintf(int fd, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int cnt;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
cnt = vfprintf(fd, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
printf(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int cnt;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
cnt = vfprintf(1, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
65
lib/ipc.c
65
lib/ipc.c
@@ -1,65 +0,0 @@
|
|||||||
// User-level IPC library routines
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
// Receive a value via IPC and return it.
|
|
||||||
// If 'pg' is nonnull, then any page sent by the sender will be mapped at
|
|
||||||
// that address.
|
|
||||||
// If 'from_env_store' is nonnull, then store the IPC sender's envid in
|
|
||||||
// *from_env_store.
|
|
||||||
// If 'perm_store' is nonnull, then store the IPC sender's page permission
|
|
||||||
// in *perm_store (this is nonzero iff a page was successfully
|
|
||||||
// transferred to 'pg').
|
|
||||||
// If the system call fails, then store 0 in *fromenv and *perm (if
|
|
||||||
// they're nonnull) and return the error.
|
|
||||||
// Otherwise, return the value sent by the sender
|
|
||||||
//
|
|
||||||
// Hint:
|
|
||||||
// Use 'thisenv' to discover the value and who sent it.
|
|
||||||
// If 'pg' is null, pass sys_ipc_recv a value that it will understand
|
|
||||||
// as meaning "no page". (Zero is not the right value, since that's
|
|
||||||
// a perfectly valid place to map a page.)
|
|
||||||
int32_t
|
|
||||||
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
|
|
||||||
{
|
|
||||||
int return_code;
|
|
||||||
if((return_code = sys_ipc_recv(pg ? pg : (void*) ~0)) < 0)
|
|
||||||
return return_code;
|
|
||||||
|
|
||||||
if(from_env_store) *from_env_store = thisenv->env_ipc_from;
|
|
||||||
if(perm_store) *perm_store = thisenv->env_ipc_perm;
|
|
||||||
|
|
||||||
return thisenv->env_ipc_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'.
|
|
||||||
// This function keeps trying until it succeeds.
|
|
||||||
// It should panic() on any error other than -E_IPC_NOT_RECV.
|
|
||||||
//
|
|
||||||
// Hint:
|
|
||||||
// Use sys_yield() to be CPU-friendly.
|
|
||||||
// If 'pg' is null, pass sys_ipc_try_send a value that it will understand
|
|
||||||
// as meaning "no page". (Zero is not the right value.)
|
|
||||||
void
|
|
||||||
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
|
|
||||||
{
|
|
||||||
int return_code = -E_IPC_NOT_RECV;
|
|
||||||
while(return_code == -E_IPC_NOT_RECV) {
|
|
||||||
return_code = sys_ipc_try_send(to_env, val, pg ? pg : (void*) ~0, perm);
|
|
||||||
sys_yield();
|
|
||||||
}
|
|
||||||
if(return_code != 0) panic("failed to send\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the first environment of the given type. We'll use this to
|
|
||||||
// find special environments.
|
|
||||||
// Returns 0 if no such environment exists.
|
|
||||||
envid_t
|
|
||||||
ipc_find_env(enum EnvType type)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < NENV; i++)
|
|
||||||
if (envs[i].env_type == type)
|
|
||||||
return envs[i].env_id;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
int
|
|
||||||
pageref(void *v)
|
|
||||||
{
|
|
||||||
pte_t pte;
|
|
||||||
|
|
||||||
if (!(uvpd[PDX(v)] & PTE_P))
|
|
||||||
return 0;
|
|
||||||
pte = uvpt[PGNUM(v)];
|
|
||||||
if (!(pte & PTE_P))
|
|
||||||
return 0;
|
|
||||||
return pages[PGNUM(pte)].pp_ref;
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#include <inc/mmu.h>
|
|
||||||
#include <inc/memlayout.h>
|
|
||||||
|
|
||||||
// Page fault upcall entrypoint.
|
|
||||||
|
|
||||||
// This is where we ask the kernel to redirect us to whenever we cause
|
|
||||||
// a page fault in user space (see the call to sys_set_pgfault_handler
|
|
||||||
// in pgfault.c).
|
|
||||||
//
|
|
||||||
// When a page fault actually occurs, the kernel switches our ESP to
|
|
||||||
// point to the user exception stack if we're not already on the user
|
|
||||||
// exception stack, and then it pushes a UTrapframe onto our user
|
|
||||||
// exception stack:
|
|
||||||
//
|
|
||||||
// trap-time esp
|
|
||||||
// trap-time eflags
|
|
||||||
// trap-time eip
|
|
||||||
// utf_regs.reg_eax
|
|
||||||
// ...
|
|
||||||
// utf_regs.reg_esi
|
|
||||||
// utf_regs.reg_edi
|
|
||||||
// utf_err (error code)
|
|
||||||
// utf_fault_va <-- %esp
|
|
||||||
//
|
|
||||||
// If this is a recursive fault, the kernel will reserve for us a
|
|
||||||
// blank word above the trap-time esp for scratch work when we unwind
|
|
||||||
// the recursive call.
|
|
||||||
//
|
|
||||||
// We then have call up to the appropriate page fault handler in C
|
|
||||||
// code, pointed to by the global variable '_pgfault_handler'.
|
|
||||||
|
|
||||||
.text
|
|
||||||
.globl _pgfault_upcall
|
|
||||||
_pgfault_upcall:
|
|
||||||
// Call the C page fault handler.
|
|
||||||
pushl %esp // function argument: pointer to UTF
|
|
||||||
movl _pgfault_handler, %eax
|
|
||||||
call *%eax
|
|
||||||
addl $4, %esp // pop function argument
|
|
||||||
|
|
||||||
// Now the C page fault handler has returned and you must return
|
|
||||||
// to the trap time state.
|
|
||||||
// Push trap-time %eip onto the trap-time stack.
|
|
||||||
//
|
|
||||||
// Explanation:
|
|
||||||
// We must prepare the trap-time stack for our eventual return to
|
|
||||||
// re-execute the instruction that faulted.
|
|
||||||
// Unfortunately, we can't return directly from the exception stack:
|
|
||||||
// We can't call 'jmp', since that requires that we load the address
|
|
||||||
// into a register, and all registers must have their trap-time
|
|
||||||
// values after the return.
|
|
||||||
// We can't call 'ret' from the exception stack either, since if we
|
|
||||||
// did, %esp would have the wrong value.
|
|
||||||
// So instead, we push the trap-time %eip onto the *trap-time* stack!
|
|
||||||
// Below we'll switch to that stack and call 'ret', which will
|
|
||||||
// restore %eip to its pre-fault value.
|
|
||||||
//
|
|
||||||
// In the case of a recursive fault on the exception stack,
|
|
||||||
// note that the word we're pushing now will fit in the
|
|
||||||
// blank word that the kernel reserved for us.
|
|
||||||
//
|
|
||||||
// Throughout the remaining code, think carefully about what
|
|
||||||
// registers are available for intermediate calculations. You
|
|
||||||
// may find that you have to rearrange your code in non-obvious
|
|
||||||
// ways as registers become unavailable as scratch space.
|
|
||||||
//
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
mov 40(%esp), %eax // Take the EIP from memory
|
|
||||||
mov 48(%esp), %ebp // Take the ESP from memory
|
|
||||||
sub $4, %ebp // Push onto trap-time ESP
|
|
||||||
mov %eax, (%ebp)
|
|
||||||
mov %ebp, 48(%esp) // Put ESP back
|
|
||||||
|
|
||||||
// Restore the trap-time registers. After you do this, you
|
|
||||||
// can no longer modify any general-purpose registers.
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
add $0x8, %esp
|
|
||||||
popal
|
|
||||||
|
|
||||||
// Restore eflags from the stack. After you do this, you can
|
|
||||||
// no longer use arithmetic operations or anything else that
|
|
||||||
// modifies eflags.
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
add $0x4, %esp
|
|
||||||
popfl
|
|
||||||
|
|
||||||
// Switch back to the adjusted trap-time stack.
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
pop %esp
|
|
||||||
|
|
||||||
// Return to re-execute the instruction that faulted.
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
ret
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// User-level page fault handler support.
|
|
||||||
// Rather than register the C page fault handler directly with the
|
|
||||||
// kernel as the page fault handler, we register the assembly language
|
|
||||||
// wrapper in pfentry.S, which in turns calls the registered C
|
|
||||||
// function.
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Assembly language pgfault entrypoint defined in lib/pfentry.S.
|
|
||||||
extern void _pgfault_upcall(void);
|
|
||||||
|
|
||||||
// Pointer to currently installed C-language pgfault handler.
|
|
||||||
void (*_pgfault_handler)(struct UTrapframe *utf);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Set the page fault handler function.
|
|
||||||
// If there isn't one yet, _pgfault_handler will be 0.
|
|
||||||
// The first time we register a handler, we need to
|
|
||||||
// allocate an exception stack (one page of memory with its top
|
|
||||||
// at UXSTACKTOP), and tell the kernel to call the assembly-language
|
|
||||||
// _pgfault_upcall routine when a page fault occurs.
|
|
||||||
//
|
|
||||||
void
|
|
||||||
set_pgfault_handler(void (*handler)(struct UTrapframe *utf))
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (_pgfault_handler == 0) {
|
|
||||||
if(sys_page_alloc(0, (void*) UXSTACKTOP - PGSIZE, PTE_U | PTE_P | PTE_W) < 0)
|
|
||||||
panic("set_pgfault_handler");
|
|
||||||
sys_env_set_pgfault_upcall(0, _pgfault_upcall);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save handler pointer for assembly to call.
|
|
||||||
_pgfault_handler = handler;
|
|
||||||
}
|
|
||||||
191
lib/pipe.c
191
lib/pipe.c
@@ -1,191 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
#define debug 0
|
|
||||||
|
|
||||||
static ssize_t devpipe_read(struct Fd *fd, void *buf, size_t n);
|
|
||||||
static ssize_t devpipe_write(struct Fd *fd, const void *buf, size_t n);
|
|
||||||
static int devpipe_stat(struct Fd *fd, struct Stat *stat);
|
|
||||||
static int devpipe_close(struct Fd *fd);
|
|
||||||
|
|
||||||
struct Dev devpipe =
|
|
||||||
{
|
|
||||||
.dev_id = 'p',
|
|
||||||
.dev_name = "pipe",
|
|
||||||
.dev_read = devpipe_read,
|
|
||||||
.dev_write = devpipe_write,
|
|
||||||
.dev_close = devpipe_close,
|
|
||||||
.dev_stat = devpipe_stat,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define PIPEBUFSIZ 32 // small to provoke races
|
|
||||||
|
|
||||||
struct Pipe {
|
|
||||||
off_t p_rpos; // read position
|
|
||||||
off_t p_wpos; // write position
|
|
||||||
uint8_t p_buf[PIPEBUFSIZ]; // data buffer
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
|
||||||
pipe(int pfd[2])
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Fd *fd0, *fd1;
|
|
||||||
void *va;
|
|
||||||
|
|
||||||
// allocate the file descriptor table entries
|
|
||||||
if ((r = fd_alloc(&fd0)) < 0
|
|
||||||
|| (r = sys_page_alloc(0, fd0, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if ((r = fd_alloc(&fd1)) < 0
|
|
||||||
|| (r = sys_page_alloc(0, fd1, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
|
||||||
goto err1;
|
|
||||||
|
|
||||||
// allocate the pipe structure as first data page in both
|
|
||||||
va = fd2data(fd0);
|
|
||||||
if ((r = sys_page_alloc(0, va, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
|
||||||
goto err2;
|
|
||||||
if ((r = sys_page_map(0, va, 0, fd2data(fd1), PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
|
||||||
goto err3;
|
|
||||||
|
|
||||||
// set up fd structures
|
|
||||||
fd0->fd_dev_id = devpipe.dev_id;
|
|
||||||
fd0->fd_omode = O_RDONLY;
|
|
||||||
|
|
||||||
fd1->fd_dev_id = devpipe.dev_id;
|
|
||||||
fd1->fd_omode = O_WRONLY;
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
cprintf("[%08x] pipecreate %08x\n", thisenv->env_id, uvpt[PGNUM(va)]);
|
|
||||||
|
|
||||||
pfd[0] = fd2num(fd0);
|
|
||||||
pfd[1] = fd2num(fd1);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err3:
|
|
||||||
sys_page_unmap(0, va);
|
|
||||||
err2:
|
|
||||||
sys_page_unmap(0, fd1);
|
|
||||||
err1:
|
|
||||||
sys_page_unmap(0, fd0);
|
|
||||||
err:
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
_pipeisclosed(struct Fd *fd, struct Pipe *p)
|
|
||||||
{
|
|
||||||
int n, nn, ret;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
n = thisenv->env_runs;
|
|
||||||
ret = pageref(fd) == pageref(p);
|
|
||||||
nn = thisenv->env_runs;
|
|
||||||
if (n == nn)
|
|
||||||
return ret;
|
|
||||||
if (n != nn && ret == 1)
|
|
||||||
cprintf("pipe race avoided\n", n, thisenv->env_runs, ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
pipeisclosed(int fdnum)
|
|
||||||
{
|
|
||||||
struct Fd *fd;
|
|
||||||
struct Pipe *p;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
|
||||||
return r;
|
|
||||||
p = (struct Pipe*) fd2data(fd);
|
|
||||||
return _pipeisclosed(fd, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
devpipe_read(struct Fd *fd, void *vbuf, size_t n)
|
|
||||||
{
|
|
||||||
uint8_t *buf;
|
|
||||||
size_t i;
|
|
||||||
struct Pipe *p;
|
|
||||||
|
|
||||||
p = (struct Pipe*)fd2data(fd);
|
|
||||||
if (debug)
|
|
||||||
cprintf("[%08x] devpipe_read %08x %d rpos %d wpos %d\n",
|
|
||||||
thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos);
|
|
||||||
|
|
||||||
buf = vbuf;
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
while (p->p_rpos == p->p_wpos) {
|
|
||||||
// pipe is empty
|
|
||||||
// if we got any data, return it
|
|
||||||
if (i > 0)
|
|
||||||
return i;
|
|
||||||
// if all the writers are gone, note eof
|
|
||||||
if (_pipeisclosed(fd, p))
|
|
||||||
return 0;
|
|
||||||
// yield and see what happens
|
|
||||||
if (debug)
|
|
||||||
cprintf("devpipe_read yield\n");
|
|
||||||
sys_yield();
|
|
||||||
}
|
|
||||||
// there's a byte. take it.
|
|
||||||
// wait to increment rpos until the byte is taken!
|
|
||||||
buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ];
|
|
||||||
p->p_rpos++;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
devpipe_write(struct Fd *fd, const void *vbuf, size_t n)
|
|
||||||
{
|
|
||||||
const uint8_t *buf;
|
|
||||||
size_t i;
|
|
||||||
struct Pipe *p;
|
|
||||||
|
|
||||||
p = (struct Pipe*) fd2data(fd);
|
|
||||||
if (debug)
|
|
||||||
cprintf("[%08x] devpipe_write %08x %d rpos %d wpos %d\n",
|
|
||||||
thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos);
|
|
||||||
|
|
||||||
buf = vbuf;
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
while (p->p_wpos >= p->p_rpos + sizeof(p->p_buf)) {
|
|
||||||
// pipe is full
|
|
||||||
// if all the readers are gone
|
|
||||||
// (it's only writers like us now),
|
|
||||||
// note eof
|
|
||||||
if (_pipeisclosed(fd, p))
|
|
||||||
return 0;
|
|
||||||
// yield and see what happens
|
|
||||||
if (debug)
|
|
||||||
cprintf("devpipe_write yield\n");
|
|
||||||
sys_yield();
|
|
||||||
}
|
|
||||||
// there's room for a byte. store it.
|
|
||||||
// wait to increment wpos until the byte is stored!
|
|
||||||
p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i];
|
|
||||||
p->p_wpos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
devpipe_stat(struct Fd *fd, struct Stat *stat)
|
|
||||||
{
|
|
||||||
struct Pipe *p = (struct Pipe*) fd2data(fd);
|
|
||||||
strcpy(stat->st_name, "<pipe>");
|
|
||||||
stat->st_size = p->p_wpos - p->p_rpos;
|
|
||||||
stat->st_isdir = 0;
|
|
||||||
stat->st_dev = &devpipe;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
devpipe_close(struct Fd *fd)
|
|
||||||
{
|
|
||||||
(void) sys_page_unmap(0, fd);
|
|
||||||
return sys_page_unmap(0, fd2data(fd));
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -26,15 +26,6 @@ static const char * const error_string[MAXERROR] =
|
|||||||
[E_NO_MEM] = "out of memory",
|
[E_NO_MEM] = "out of memory",
|
||||||
[E_NO_FREE_ENV] = "out of environments",
|
[E_NO_FREE_ENV] = "out of environments",
|
||||||
[E_FAULT] = "segmentation fault",
|
[E_FAULT] = "segmentation fault",
|
||||||
[E_IPC_NOT_RECV]= "env is not recving",
|
|
||||||
[E_EOF] = "unexpected end of file",
|
|
||||||
[E_NO_DISK] = "no free space on disk",
|
|
||||||
[E_MAX_OPEN] = "too many files are open",
|
|
||||||
[E_NOT_FOUND] = "file or block not found",
|
|
||||||
[E_BAD_PATH] = "invalid path",
|
|
||||||
[E_FILE_EXISTS] = "file already exists",
|
|
||||||
[E_NOT_EXEC] = "file is not a valid executable",
|
|
||||||
[E_NOT_SUPP] = "operation not supported",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -9,21 +9,15 @@ readline(const char *prompt)
|
|||||||
{
|
{
|
||||||
int i, c, echoing;
|
int i, c, echoing;
|
||||||
|
|
||||||
#if JOS_KERNEL
|
|
||||||
if (prompt != NULL)
|
if (prompt != NULL)
|
||||||
cprintf("%s", prompt);
|
cprintf("%s", prompt);
|
||||||
#else
|
|
||||||
if (prompt != NULL)
|
|
||||||
fprintf(1, "%s", prompt);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
echoing = iscons(0);
|
echoing = iscons(0);
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getchar();
|
c = getchar();
|
||||||
if (c < 0) {
|
if (c < 0) {
|
||||||
if (c != -E_EOF)
|
cprintf("read error: %e\n", c);
|
||||||
cprintf("read error: %e\n", c);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if ((c == '\b' || c == '\x7f') && i > 0) {
|
} else if ((c == '\b' || c == '\x7f') && i > 0) {
|
||||||
if (echoing)
|
if (echoing)
|
||||||
|
|||||||
322
lib/spawn.c
322
lib/spawn.c
@@ -1,322 +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.
|
|
||||||
int r;
|
|
||||||
for(int pde_i = 0; pde_i < PDX(UTOP); pde_i++) {
|
|
||||||
pde_t pde = uvpd[pde_i];
|
|
||||||
if(!(pde & PTE_P)) continue;
|
|
||||||
|
|
||||||
for(int pte_i = 0; pte_i < NPTENTRIES; pte_i++) {
|
|
||||||
int pn = pde_i * NPTENTRIES + pte_i;
|
|
||||||
pte_t pte = uvpt[pn];
|
|
||||||
|
|
||||||
if((pte & (PTE_P | PTE_SHARE)) != (PTE_P | PTE_SHARE)) continue;
|
|
||||||
void* addr = (void*) (pn * PGSIZE);
|
|
||||||
if((r = sys_page_map(0, addr, child, addr, pte & PTE_SYSCALL)) < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -55,13 +55,13 @@ fast_syscall(int num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4) {
|
|||||||
void
|
void
|
||||||
sys_cputs(const char *s, size_t len)
|
sys_cputs(const char *s, size_t len)
|
||||||
{
|
{
|
||||||
syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0);
|
fast_syscall(SYS_cputs, (uint32_t)s, len, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
sys_cgetc(void)
|
sys_cgetc(void)
|
||||||
{
|
{
|
||||||
return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0);
|
return fast_syscall(SYS_cgetc, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -76,59 +76,3 @@ sys_getenvid(void)
|
|||||||
return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0);
|
return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
sys_yield(void)
|
|
||||||
{
|
|
||||||
syscall(SYS_yield, 0, 0, 0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_page_alloc(envid_t envid, void *va, int perm)
|
|
||||||
{
|
|
||||||
return syscall(SYS_page_alloc, 1, envid, (uint32_t) va, perm, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_page_map(envid_t srcenv, void *srcva, envid_t dstenv, void *dstva, int perm)
|
|
||||||
{
|
|
||||||
return syscall(SYS_page_map, 1, srcenv, (uint32_t) srcva, dstenv, (uint32_t) dstva, perm);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_page_unmap(envid_t envid, void *va)
|
|
||||||
{
|
|
||||||
return syscall(SYS_page_unmap, 1, envid, (uint32_t) va, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sys_exofork is inlined in lib.h
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_env_set_status(envid_t envid, int status)
|
|
||||||
{
|
|
||||||
return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
|
|
||||||
{
|
|
||||||
return syscall(SYS_env_set_trapframe, 1, envid, (uint32_t) tf, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_env_set_pgfault_upcall(envid_t envid, void *upcall)
|
|
||||||
{
|
|
||||||
return syscall(SYS_env_set_pgfault_upcall, 1, envid, (uint32_t) upcall, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, int perm)
|
|
||||||
{
|
|
||||||
return syscall(SYS_ipc_try_send, 0, envid, value, (uint32_t) srcva, perm, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
sys_ipc_recv(void *dstva)
|
|
||||||
{
|
|
||||||
return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
13
lib/wait.c
13
lib/wait.c
@@ -1,13 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
// Waits until 'envid' exits.
|
|
||||||
void
|
|
||||||
wait(envid_t envid)
|
|
||||||
{
|
|
||||||
const volatile struct Env *e;
|
|
||||||
|
|
||||||
assert(envid != 0);
|
|
||||||
e = &envs[ENVX(envid)];
|
|
||||||
while (e->env_id == envid && e->env_status != ENV_FREE)
|
|
||||||
sys_yield();
|
|
||||||
}
|
|
||||||
@@ -10,8 +10,7 @@ $(OBJDIR)/user/%.o: user/%.c $(OBJDIR)/.vars.USER_CFLAGS
|
|||||||
|
|
||||||
$(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(USERLIBS:%=$(OBJDIR)/lib/lib%.a) user/user.ld
|
$(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(USERLIBS:%=$(OBJDIR)/lib/lib%.a) user/user.ld
|
||||||
@echo + ld $@
|
@echo + ld $@
|
||||||
$(V)$(LD) -o $@.debug $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
||||||
$(V)$(OBJDUMP) -S $@.debug > $@.asm
|
$(V)$(OBJDUMP) -S $@ > $@.asm
|
||||||
$(V)$(NM) -n $@.debug > $@.sym
|
$(V)$(NM) -n $@ > $@.sym
|
||||||
$(V)$(OBJCOPY) -R .stab -R .stabstr --add-gnu-debuglink=$(basename $@.debug) $@.debug $@
|
|
||||||
|
|
||||||
|
|||||||
36
user/cat.c
36
user/cat.c
@@ -1,36 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
char buf[8192];
|
|
||||||
|
|
||||||
void
|
|
||||||
cat(int f, char *s)
|
|
||||||
{
|
|
||||||
long n;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
while ((n = read(f, buf, (long)sizeof(buf))) > 0)
|
|
||||||
if ((r = write(1, buf, n)) != n)
|
|
||||||
panic("write error copying %s: %e", s, r);
|
|
||||||
if (n < 0)
|
|
||||||
panic("error reading %s: %e", s, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int f, i;
|
|
||||||
|
|
||||||
binaryname = "cat";
|
|
||||||
if (argc == 1)
|
|
||||||
cat(0, "<stdin>");
|
|
||||||
else
|
|
||||||
for (i = 1; i < argc; i++) {
|
|
||||||
f = open(argv[i], O_RDONLY);
|
|
||||||
if (f < 0)
|
|
||||||
printf("can't open %s: %e\n", argv[i], f);
|
|
||||||
else {
|
|
||||||
cat(f, argv[i]);
|
|
||||||
close(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
// Ping-pong a counter between two processes.
|
|
||||||
// Only need to start one of these -- splits into two, crudely.
|
|
||||||
|
|
||||||
#include <inc/string.h>
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
envid_t dumbfork(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
envid_t who;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// fork a child process
|
|
||||||
who = dumbfork();
|
|
||||||
|
|
||||||
// print a message and yield to the other a few times
|
|
||||||
for (i = 0; i < (who ? 10 : 20); i++) {
|
|
||||||
cprintf("%d: I am the %s!\n", i, who ? "parent" : "child");
|
|
||||||
sys_yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
duppage(envid_t dstenv, void *addr)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
// This is NOT what you should do in your fork.
|
|
||||||
if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0)
|
|
||||||
panic("sys_page_alloc: %e", r);
|
|
||||||
if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
|
||||||
panic("sys_page_map: %e", r);
|
|
||||||
memmove(UTEMP, addr, PGSIZE);
|
|
||||||
if ((r = sys_page_unmap(0, UTEMP)) < 0)
|
|
||||||
panic("sys_page_unmap: %e", r);
|
|
||||||
}
|
|
||||||
|
|
||||||
envid_t
|
|
||||||
dumbfork(void)
|
|
||||||
{
|
|
||||||
envid_t envid;
|
|
||||||
uint8_t *addr;
|
|
||||||
int r;
|
|
||||||
extern unsigned char end[];
|
|
||||||
|
|
||||||
// Allocate a new child environment.
|
|
||||||
// The kernel will initialize it with a copy of our register state,
|
|
||||||
// so that the child will appear to have called sys_exofork() too -
|
|
||||||
// except that in the child, this "fake" call to sys_exofork()
|
|
||||||
// will return 0 instead of the envid of the child.
|
|
||||||
envid = sys_exofork();
|
|
||||||
if (envid < 0)
|
|
||||||
panic("sys_exofork: %e", envid);
|
|
||||||
if (envid == 0) {
|
|
||||||
// We're the child.
|
|
||||||
// The copied value of the global variable 'thisenv'
|
|
||||||
// is no longer valid (it refers to the parent!).
|
|
||||||
// Fix it and return 0.
|
|
||||||
thisenv = &envs[ENVX(sys_getenvid())];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're the parent.
|
|
||||||
// Eagerly copy our entire address space into the child.
|
|
||||||
// This is NOT what you should do in your fork implementation.
|
|
||||||
for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE)
|
|
||||||
duppage(envid, addr);
|
|
||||||
|
|
||||||
// Also copy the stack we are currently running on.
|
|
||||||
duppage(envid, ROUNDDOWN(&addr, PGSIZE));
|
|
||||||
|
|
||||||
// Start the child environment running
|
|
||||||
if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0)
|
|
||||||
panic("sys_env_set_status: %e", r);
|
|
||||||
|
|
||||||
return envid;
|
|
||||||
}
|
|
||||||
|
|
||||||
21
user/echo.c
21
user/echo.c
@@ -1,21 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, nflag;
|
|
||||||
|
|
||||||
nflag = 0;
|
|
||||||
if (argc > 1 && strcmp(argv[1], "-n") == 0) {
|
|
||||||
nflag = 1;
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
}
|
|
||||||
for (i = 1; i < argc; i++) {
|
|
||||||
if (i > 1)
|
|
||||||
write(1, " ", 1);
|
|
||||||
write(1, argv[i], strlen(argv[i]));
|
|
||||||
}
|
|
||||||
if (!nflag)
|
|
||||||
write(1, "\n", 1);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// Demonstrate lack of fairness in IPC.
|
|
||||||
// Start three instances of this program as envs 1, 2, and 3.
|
|
||||||
// (user/idle is env 0).
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
envid_t who, id;
|
|
||||||
|
|
||||||
id = sys_getenvid();
|
|
||||||
|
|
||||||
if (thisenv == &envs[1]) {
|
|
||||||
while (1) {
|
|
||||||
ipc_recv(&who, 0, 0);
|
|
||||||
cprintf("%x recv from %x\n", id, who);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cprintf("%x loop sending to %x\n", id, envs[1].env_id);
|
|
||||||
while (1)
|
|
||||||
ipc_send(envs[1].env_id, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// test user-level fault handler -- alloc pages to fix faults
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
handler(struct UTrapframe *utf)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
void *addr = (void*)utf->utf_fault_va;
|
|
||||||
|
|
||||||
cprintf("fault %x\n", addr);
|
|
||||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
|
||||||
PTE_P|PTE_U|PTE_W)) < 0)
|
|
||||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
|
||||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
set_pgfault_handler(handler);
|
|
||||||
cprintf("%s\n", (char*)0xDeadBeef);
|
|
||||||
cprintf("%s\n", (char*)0xCafeBffe);
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// test user-level fault handler -- alloc pages to fix faults
|
|
||||||
// doesn't work because we sys_cputs instead of cprintf (exercise: why?)
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
handler(struct UTrapframe *utf)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
void *addr = (void*)utf->utf_fault_va;
|
|
||||||
|
|
||||||
cprintf("fault %x\n", addr);
|
|
||||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
|
||||||
PTE_P|PTE_U|PTE_W)) < 0)
|
|
||||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
|
||||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
set_pgfault_handler(handler);
|
|
||||||
sys_cputs((char*)0xDEADBEEF, 4);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// test bad pointer for user-level fault handler
|
|
||||||
// this is going to fault in the fault handler accessing eip (always!)
|
|
||||||
// so eventually the kernel kills it (PFM_KILL) because
|
|
||||||
// we outrun the stack with invocations of the user-level handler
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
|
||||||
sys_env_set_pgfault_upcall(0, (void*) 0xDeadBeef);
|
|
||||||
*(int*)0 = 0;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// test user-level fault handler -- just exit when we fault
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
handler(struct UTrapframe *utf)
|
|
||||||
{
|
|
||||||
void *addr = (void*)utf->utf_fault_va;
|
|
||||||
uint32_t err = utf->utf_err;
|
|
||||||
cprintf("i faulted at va %x, err %x\n", addr, err & 7);
|
|
||||||
sys_env_destroy(sys_getenvid());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
set_pgfault_handler(handler);
|
|
||||||
*(int*)0xDeadBeef = 0;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// test evil pointer for user-level fault handler
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
|
||||||
sys_env_set_pgfault_upcall(0, (void*) 0xF0100020);
|
|
||||||
*(int*)0 = 0;
|
|
||||||
}
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
// test user fault handler being called with no exception stack mapped
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void _pgfault_upcall();
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
sys_env_set_pgfault_upcall(0, (void*) _pgfault_upcall);
|
|
||||||
*(int*)0 = 0;
|
|
||||||
}
|
|
||||||
146
user/faultregs.c
146
user/faultregs.c
@@ -1,146 +0,0 @@
|
|||||||
// test register restore on user-level page fault return
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
struct regs
|
|
||||||
{
|
|
||||||
struct PushRegs regs;
|
|
||||||
uintptr_t eip;
|
|
||||||
uint32_t eflags;
|
|
||||||
uintptr_t esp;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SAVE_REGS(base) \
|
|
||||||
"\tmovl %%edi, "base"+0x00\n" \
|
|
||||||
"\tmovl %%esi, "base"+0x04\n" \
|
|
||||||
"\tmovl %%ebp, "base"+0x08\n" \
|
|
||||||
"\tmovl %%ebx, "base"+0x10\n" \
|
|
||||||
"\tmovl %%edx, "base"+0x14\n" \
|
|
||||||
"\tmovl %%ecx, "base"+0x18\n" \
|
|
||||||
"\tmovl %%eax, "base"+0x1c\n" \
|
|
||||||
"\tmovl %%esp, "base"+0x28\n"
|
|
||||||
|
|
||||||
#define LOAD_REGS(base) \
|
|
||||||
"\tmovl "base"+0x00, %%edi\n" \
|
|
||||||
"\tmovl "base"+0x04, %%esi\n" \
|
|
||||||
"\tmovl "base"+0x08, %%ebp\n" \
|
|
||||||
"\tmovl "base"+0x10, %%ebx\n" \
|
|
||||||
"\tmovl "base"+0x14, %%edx\n" \
|
|
||||||
"\tmovl "base"+0x18, %%ecx\n" \
|
|
||||||
"\tmovl "base"+0x1c, %%eax\n" \
|
|
||||||
"\tmovl "base"+0x28, %%esp\n"
|
|
||||||
|
|
||||||
static struct regs before, during, after;
|
|
||||||
|
|
||||||
static void
|
|
||||||
check_regs(struct regs* a, const char *an, struct regs* b, const char *bn,
|
|
||||||
const char *testname)
|
|
||||||
{
|
|
||||||
int mismatch = 0;
|
|
||||||
|
|
||||||
cprintf("%-6s %-8s %-8s\n", "", an, bn);
|
|
||||||
|
|
||||||
#define CHECK(name, field) \
|
|
||||||
do { \
|
|
||||||
cprintf("%-6s %08x %08x ", #name, a->field, b->field); \
|
|
||||||
if (a->field == b->field) \
|
|
||||||
cprintf("OK\n"); \
|
|
||||||
else { \
|
|
||||||
cprintf("MISMATCH\n"); \
|
|
||||||
mismatch = 1; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
CHECK(edi, regs.reg_edi);
|
|
||||||
CHECK(esi, regs.reg_esi);
|
|
||||||
CHECK(ebp, regs.reg_ebp);
|
|
||||||
CHECK(ebx, regs.reg_ebx);
|
|
||||||
CHECK(edx, regs.reg_edx);
|
|
||||||
CHECK(ecx, regs.reg_ecx);
|
|
||||||
CHECK(eax, regs.reg_eax);
|
|
||||||
CHECK(eip, eip);
|
|
||||||
CHECK(eflags, eflags);
|
|
||||||
CHECK(esp, esp);
|
|
||||||
|
|
||||||
#undef CHECK
|
|
||||||
|
|
||||||
cprintf("Registers %s ", testname);
|
|
||||||
if (!mismatch)
|
|
||||||
cprintf("OK\n");
|
|
||||||
else
|
|
||||||
cprintf("MISMATCH\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
pgfault(struct UTrapframe *utf)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (utf->utf_fault_va != (uint32_t)UTEMP)
|
|
||||||
panic("pgfault expected at UTEMP, got 0x%08x (eip %08x)",
|
|
||||||
utf->utf_fault_va, utf->utf_eip);
|
|
||||||
|
|
||||||
// Check registers in UTrapframe
|
|
||||||
during.regs = utf->utf_regs;
|
|
||||||
during.eip = utf->utf_eip;
|
|
||||||
during.eflags = utf->utf_eflags & ~FL_RF;
|
|
||||||
during.esp = utf->utf_esp;
|
|
||||||
check_regs(&before, "before", &during, "during", "in UTrapframe");
|
|
||||||
|
|
||||||
// Map UTEMP so the write succeeds
|
|
||||||
if ((r = sys_page_alloc(0, UTEMP, PTE_U|PTE_P|PTE_W)) < 0)
|
|
||||||
panic("sys_page_alloc: %e", r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
set_pgfault_handler(pgfault);
|
|
||||||
|
|
||||||
asm volatile(
|
|
||||||
// Light up eflags to catch more errors
|
|
||||||
"\tpushl %%eax\n"
|
|
||||||
"\tpushfl\n"
|
|
||||||
"\tpopl %%eax\n"
|
|
||||||
"\torl $0x8d5, %%eax\n"
|
|
||||||
"\tpushl %%eax\n"
|
|
||||||
"\tpopfl\n"
|
|
||||||
|
|
||||||
// Save before registers
|
|
||||||
// eflags
|
|
||||||
"\tmov %%eax, %0+0x24\n"
|
|
||||||
// eip
|
|
||||||
"\tleal 0f, %%eax\n"
|
|
||||||
"\tmovl %%eax, %0+0x20\n"
|
|
||||||
"\tpopl %%eax\n"
|
|
||||||
// others
|
|
||||||
SAVE_REGS("%0")
|
|
||||||
|
|
||||||
// Fault at UTEMP
|
|
||||||
"\t0: movl $42, 0x400000\n"
|
|
||||||
|
|
||||||
// Save after registers (except eip and eflags)
|
|
||||||
SAVE_REGS("%1")
|
|
||||||
// Restore registers (except eip and eflags). This
|
|
||||||
// way, the test will run even if EIP is the *only*
|
|
||||||
// thing restored correctly.
|
|
||||||
LOAD_REGS("%0")
|
|
||||||
// Save after eflags (now that stack is back); note
|
|
||||||
// that we were very careful not to modify eflags in
|
|
||||||
// since we saved it
|
|
||||||
"\tpushl %%eax\n"
|
|
||||||
"\tpushfl\n"
|
|
||||||
"\tpopl %%eax\n"
|
|
||||||
"\tmov %%eax, %1+0x24\n"
|
|
||||||
"\tpopl %%eax\n"
|
|
||||||
: : "m" (before), "m" (after) : "memory", "cc");
|
|
||||||
|
|
||||||
// Check UTEMP to roughly determine that EIP was restored
|
|
||||||
// correctly (of course, we probably wouldn't get this far if
|
|
||||||
// it weren't)
|
|
||||||
if (*(int*)UTEMP != 42)
|
|
||||||
cprintf("EIP after page-fault MISMATCH\n");
|
|
||||||
after.eip = before.eip;
|
|
||||||
|
|
||||||
check_regs(&before, "before", &after, "after", "after page-fault");
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// Fork a binary tree of processes and display their structure.
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
#define DEPTH 3
|
|
||||||
|
|
||||||
void forktree(const char *cur);
|
|
||||||
|
|
||||||
void
|
|
||||||
forkchild(const char *cur, char branch)
|
|
||||||
{
|
|
||||||
char nxt[DEPTH+1];
|
|
||||||
|
|
||||||
if (strlen(cur) >= DEPTH)
|
|
||||||
return;
|
|
||||||
|
|
||||||
snprintf(nxt, DEPTH+1, "%s%c", cur, branch);
|
|
||||||
if (fork() == 0) {
|
|
||||||
forktree(nxt);
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
forktree(const char *cur)
|
|
||||||
{
|
|
||||||
cprintf("%04x: I am '%s'\n", sys_getenvid(), cur);
|
|
||||||
|
|
||||||
forkchild(cur, '0');
|
|
||||||
forkchild(cur, '1');
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
forktree("");
|
|
||||||
}
|
|
||||||
|
|
||||||
29
user/icode.c
29
user/icode.c
@@ -1,29 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int fd, n, r;
|
|
||||||
char buf[512+1];
|
|
||||||
|
|
||||||
binaryname = "icode";
|
|
||||||
|
|
||||||
cprintf("icode startup\n");
|
|
||||||
|
|
||||||
cprintf("icode: open /motd\n");
|
|
||||||
if ((fd = open("/motd", O_RDONLY)) < 0)
|
|
||||||
panic("icode: open /motd: %e", fd);
|
|
||||||
|
|
||||||
cprintf("icode: read /motd\n");
|
|
||||||
while ((n = read(fd, buf, sizeof buf-1)) > 0)
|
|
||||||
sys_cputs(buf, n);
|
|
||||||
|
|
||||||
cprintf("icode: close /motd\n");
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
cprintf("icode: spawn /init\n");
|
|
||||||
if ((r = spawnl("/init", "init", "initarg1", "initarg2", (char*)0)) < 0)
|
|
||||||
panic("icode: spawn /init: %e", r);
|
|
||||||
|
|
||||||
cprintf("icode: exiting\n");
|
|
||||||
}
|
|
||||||
20
user/idle.c
20
user/idle.c
@@ -1,20 +0,0 @@
|
|||||||
// idle loop
|
|
||||||
|
|
||||||
#include <inc/x86.h>
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
binaryname = "idle";
|
|
||||||
|
|
||||||
// Loop forever, simply trying to yield to a different environment.
|
|
||||||
// Instead of busy-waiting like this,
|
|
||||||
// a better way would be to use the processor's HLT instruction
|
|
||||||
// to cause the processor to stop executing until the next interrupt -
|
|
||||||
// doing so allows the processor to conserve power more effectively.
|
|
||||||
while (1) {
|
|
||||||
sys_yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
69
user/init.c
69
user/init.c
@@ -1,69 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
struct {
|
|
||||||
char msg1[5000];
|
|
||||||
char msg2[1000];
|
|
||||||
} data = {
|
|
||||||
"this is initialized data",
|
|
||||||
"so is this"
|
|
||||||
};
|
|
||||||
|
|
||||||
char bss[6000];
|
|
||||||
|
|
||||||
int
|
|
||||||
sum(const char *s, int n)
|
|
||||||
{
|
|
||||||
int i, tot = 0;
|
|
||||||
for (i = 0; i < n; i++)
|
|
||||||
tot ^= i * s[i];
|
|
||||||
return tot;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, r, x, want;
|
|
||||||
char args[256];
|
|
||||||
|
|
||||||
cprintf("init: running\n");
|
|
||||||
|
|
||||||
want = 0xf989e;
|
|
||||||
if ((x = sum((char*)&data, sizeof data)) != want)
|
|
||||||
cprintf("init: data is not initialized: got sum %08x wanted %08x\n",
|
|
||||||
x, want);
|
|
||||||
else
|
|
||||||
cprintf("init: data seems okay\n");
|
|
||||||
if ((x = sum(bss, sizeof bss)) != 0)
|
|
||||||
cprintf("bss is not initialized: wanted sum 0 got %08x\n", x);
|
|
||||||
else
|
|
||||||
cprintf("init: bss seems okay\n");
|
|
||||||
|
|
||||||
// output in one syscall per line to avoid output interleaving
|
|
||||||
strcat(args, "init: args:");
|
|
||||||
for (i = 0; i < argc; i++) {
|
|
||||||
strcat(args, " '");
|
|
||||||
strcat(args, argv[i]);
|
|
||||||
strcat(args, "'");
|
|
||||||
}
|
|
||||||
cprintf("%s\n", args);
|
|
||||||
|
|
||||||
cprintf("init: running sh\n");
|
|
||||||
|
|
||||||
// being run directly from kernel, so no file descriptors open yet
|
|
||||||
close(0);
|
|
||||||
if ((r = opencons()) < 0)
|
|
||||||
panic("opencons: %e", r);
|
|
||||||
if (r != 0)
|
|
||||||
panic("first opencons used fd %d", r);
|
|
||||||
if ((r = dup(0, 1)) < 0)
|
|
||||||
panic("dup: %e", r);
|
|
||||||
while (1) {
|
|
||||||
cprintf("init: starting sh\n");
|
|
||||||
r = spawnl("/sh", "sh", (char*)0);
|
|
||||||
if (r < 0) {
|
|
||||||
cprintf("init: spawn sh: %e\n", r);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
wait(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, r, x, want;
|
|
||||||
|
|
||||||
cprintf("initsh: running sh\n");
|
|
||||||
|
|
||||||
// being run directly from kernel, so no file descriptors open yet
|
|
||||||
close(0);
|
|
||||||
if ((r = opencons()) < 0)
|
|
||||||
panic("opencons: %e", r);
|
|
||||||
if (r != 0)
|
|
||||||
panic("first opencons used fd %d", r);
|
|
||||||
if ((r = dup(0, 1)) < 0)
|
|
||||||
panic("dup: %e", r);
|
|
||||||
while (1) {
|
|
||||||
cprintf("init: starting sh\n");
|
|
||||||
r = spawnl("/sh", "sh", (char*)0);
|
|
||||||
if (r < 0) {
|
|
||||||
cprintf("init: spawn sh: %e\n", r);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
wait(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
91
user/ls.c
91
user/ls.c
@@ -1,91 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
int flag[256];
|
|
||||||
|
|
||||||
void lsdir(const char*, const char*);
|
|
||||||
void ls1(const char*, bool, off_t, const char*);
|
|
||||||
|
|
||||||
void
|
|
||||||
ls(const char *path, const char *prefix)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct Stat st;
|
|
||||||
|
|
||||||
if ((r = stat(path, &st)) < 0)
|
|
||||||
panic("stat %s: %e", path, r);
|
|
||||||
if (st.st_isdir && !flag['d'])
|
|
||||||
lsdir(path, prefix);
|
|
||||||
else
|
|
||||||
ls1(0, st.st_isdir, st.st_size, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
lsdir(const char *path, const char *prefix)
|
|
||||||
{
|
|
||||||
int fd, n;
|
|
||||||
struct File f;
|
|
||||||
|
|
||||||
if ((fd = open(path, O_RDONLY)) < 0)
|
|
||||||
panic("open %s: %e", path, fd);
|
|
||||||
while ((n = readn(fd, &f, sizeof f)) == sizeof f)
|
|
||||||
if (f.f_name[0])
|
|
||||||
ls1(prefix, f.f_type==FTYPE_DIR, f.f_size, f.f_name);
|
|
||||||
if (n > 0)
|
|
||||||
panic("short read in directory %s", path);
|
|
||||||
if (n < 0)
|
|
||||||
panic("error reading directory %s: %e", path, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ls1(const char *prefix, bool isdir, off_t size, const char *name)
|
|
||||||
{
|
|
||||||
const char *sep;
|
|
||||||
|
|
||||||
if(flag['l'])
|
|
||||||
printf("%11d %c ", size, isdir ? 'd' : '-');
|
|
||||||
if(prefix) {
|
|
||||||
if (prefix[0] && prefix[strlen(prefix)-1] != '/')
|
|
||||||
sep = "/";
|
|
||||||
else
|
|
||||||
sep = "";
|
|
||||||
printf("%s%s", prefix, sep);
|
|
||||||
}
|
|
||||||
printf("%s", name);
|
|
||||||
if(flag['F'] && isdir)
|
|
||||||
printf("/");
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage(void)
|
|
||||||
{
|
|
||||||
printf("usage: ls [-dFl] [file...]\n");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct Argstate args;
|
|
||||||
|
|
||||||
argstart(&argc, argv, &args);
|
|
||||||
while ((i = argnext(&args)) >= 0)
|
|
||||||
switch (i) {
|
|
||||||
case 'd':
|
|
||||||
case 'F':
|
|
||||||
case 'l':
|
|
||||||
flag[i]++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc == 1)
|
|
||||||
ls("/", "");
|
|
||||||
else {
|
|
||||||
for (i = 1; i < argc; i++)
|
|
||||||
ls(argv[i], argv[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
35
user/lsfd.c
35
user/lsfd.c
@@ -1,35 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
usage(void)
|
|
||||||
{
|
|
||||||
cprintf("usage: lsfd [-1]\n");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, usefprint = 0;
|
|
||||||
struct Stat st;
|
|
||||||
struct Argstate args;
|
|
||||||
|
|
||||||
argstart(&argc, argv, &args);
|
|
||||||
while ((i = argnext(&args)) >= 0)
|
|
||||||
if (i == '1')
|
|
||||||
usefprint = 1;
|
|
||||||
else
|
|
||||||
usage();
|
|
||||||
|
|
||||||
for (i = 0; i < 32; i++)
|
|
||||||
if (fstat(i, &st) >= 0) {
|
|
||||||
if (usefprint)
|
|
||||||
fprintf(1, "fd %d: name %s isdir %d size %d dev %s\n",
|
|
||||||
i, st.st_name, st.st_isdir,
|
|
||||||
st.st_size, st.st_dev->dev_name);
|
|
||||||
else
|
|
||||||
cprintf("fd %d: name %s isdir %d size %d dev %s\n",
|
|
||||||
i, st.st_name, st.st_isdir,
|
|
||||||
st.st_size, st.st_dev->dev_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
47
user/num.c
47
user/num.c
@@ -1,47 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
int bol = 1;
|
|
||||||
int line = 0;
|
|
||||||
|
|
||||||
void
|
|
||||||
num(int f, const char *s)
|
|
||||||
{
|
|
||||||
long n;
|
|
||||||
int r;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
while ((n = read(f, &c, 1)) > 0) {
|
|
||||||
if (bol) {
|
|
||||||
printf("%5d ", ++line);
|
|
||||||
bol = 0;
|
|
||||||
}
|
|
||||||
if ((r = write(1, &c, 1)) != 1)
|
|
||||||
panic("write error copying %s: %e", s, r);
|
|
||||||
if (c == '\n')
|
|
||||||
bol = 1;
|
|
||||||
}
|
|
||||||
if (n < 0)
|
|
||||||
panic("error reading %s: %e", s, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int f, i;
|
|
||||||
|
|
||||||
binaryname = "num";
|
|
||||||
if (argc == 1)
|
|
||||||
num(0, "<stdin>");
|
|
||||||
else
|
|
||||||
for (i = 1; i < argc; i++) {
|
|
||||||
f = open(argv[i], O_RDONLY);
|
|
||||||
if (f < 0)
|
|
||||||
panic("can't open %s: %e", argv[i], f);
|
|
||||||
else {
|
|
||||||
num(f, argv[i]);
|
|
||||||
close(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// Ping-pong a counter between two processes.
|
|
||||||
// Only need to start one of these -- splits into two with fork.
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
envid_t who;
|
|
||||||
|
|
||||||
if ((who = fork()) != 0) {
|
|
||||||
// get the ball rolling
|
|
||||||
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
|
|
||||||
ipc_send(who, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
uint32_t i = ipc_recv(&who, 0, 0);
|
|
||||||
cprintf("%x got %d from %x\n", sys_getenvid(), i, who);
|
|
||||||
if (i == 10)
|
|
||||||
return;
|
|
||||||
i++;
|
|
||||||
ipc_send(who, i, 0, 0);
|
|
||||||
if (i == 10)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// Ping-pong a counter between two shared-memory processes.
|
|
||||||
// Only need to start one of these -- splits into two with sfork.
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
uint32_t val;
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
envid_t who;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
if ((who = sfork()) != 0) {
|
|
||||||
cprintf("i am %08x; thisenv is %p\n", sys_getenvid(), thisenv);
|
|
||||||
// get the ball rolling
|
|
||||||
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
|
|
||||||
ipc_send(who, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
ipc_recv(&who, 0, 0);
|
|
||||||
cprintf("%x got %d from %x (thisenv is %p %x)\n", sys_getenvid(), val, who, thisenv, thisenv->env_id);
|
|
||||||
if (val == 10)
|
|
||||||
return;
|
|
||||||
++val;
|
|
||||||
ipc_send(who, 0, 0, 0);
|
|
||||||
if (val == 10)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
// Concurrent version of prime sieve of Eratosthenes.
|
|
||||||
// Invented by Doug McIlroy, inventor of Unix pipes.
|
|
||||||
// See http://swtch.com/~rsc/thread/.
|
|
||||||
// The picture halfway down the page and the text surrounding it
|
|
||||||
// explain what's going on here.
|
|
||||||
//
|
|
||||||
// Since NENV is 1024, we can print 1022 primes before running out.
|
|
||||||
// The remaining two environments are the integer generator at the bottom
|
|
||||||
// of main and user/idle.
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
primeproc(void)
|
|
||||||
{
|
|
||||||
int i, id, p;
|
|
||||||
envid_t envid;
|
|
||||||
|
|
||||||
// fetch a prime from our left neighbor
|
|
||||||
top:
|
|
||||||
p = ipc_recv(&envid, 0, 0);
|
|
||||||
cprintf("CPU %d: %d ", thisenv->env_cpunum, p);
|
|
||||||
|
|
||||||
// fork a right neighbor to continue the chain
|
|
||||||
if ((id = fork()) < 0)
|
|
||||||
panic("fork: %e", id);
|
|
||||||
if (id == 0)
|
|
||||||
goto top;
|
|
||||||
|
|
||||||
// filter out multiples of our prime
|
|
||||||
while (1) {
|
|
||||||
i = ipc_recv(&envid, 0, 0);
|
|
||||||
if (i % p)
|
|
||||||
ipc_send(id, i, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, id;
|
|
||||||
|
|
||||||
// fork the first prime process in the chain
|
|
||||||
if ((id = fork()) < 0)
|
|
||||||
panic("fork: %e", id);
|
|
||||||
if (id == 0)
|
|
||||||
primeproc();
|
|
||||||
|
|
||||||
// feed all the integers through
|
|
||||||
for (i = 2; ; i++)
|
|
||||||
ipc_send(id, i, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
// Concurrent version of prime sieve of Eratosthenes.
|
|
||||||
// Invented by Doug McIlroy, inventor of Unix pipes.
|
|
||||||
// See http://swtch.com/~rsc/thread/.
|
|
||||||
// The picture halfway down the page and the text surrounding it
|
|
||||||
// explain what's going on here.
|
|
||||||
//
|
|
||||||
// Since NENV is 1024, we can print 1022 primes before running out.
|
|
||||||
// The remaining two environments are the integer generator at the bottom
|
|
||||||
// of main and user/idle.
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
primeproc(int fd)
|
|
||||||
{
|
|
||||||
int i, id, p, pfd[2], wfd, r;
|
|
||||||
|
|
||||||
// fetch a prime from our left neighbor
|
|
||||||
top:
|
|
||||||
if ((r = readn(fd, &p, 4)) != 4)
|
|
||||||
panic("primeproc could not read initial prime: %d, %e", r, r >= 0 ? 0 : r);
|
|
||||||
|
|
||||||
cprintf("%d\n", p);
|
|
||||||
|
|
||||||
// fork a right neighbor to continue the chain
|
|
||||||
if ((i=pipe(pfd)) < 0)
|
|
||||||
panic("pipe: %e", i);
|
|
||||||
if ((id = fork()) < 0)
|
|
||||||
panic("fork: %e", id);
|
|
||||||
if (id == 0) {
|
|
||||||
close(fd);
|
|
||||||
close(pfd[1]);
|
|
||||||
fd = pfd[0];
|
|
||||||
goto top;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(pfd[0]);
|
|
||||||
wfd = pfd[1];
|
|
||||||
|
|
||||||
// filter out multiples of our prime
|
|
||||||
for (;;) {
|
|
||||||
if ((r=readn(fd, &i, 4)) != 4)
|
|
||||||
panic("primeproc %d readn %d %d %e", p, fd, r, r >= 0 ? 0 : r);
|
|
||||||
if (i%p)
|
|
||||||
if ((r=write(wfd, &i, 4)) != 4)
|
|
||||||
panic("primeproc %d write: %d %e", p, r, r >= 0 ? 0 : r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, id, p[2], r;
|
|
||||||
|
|
||||||
binaryname = "primespipe";
|
|
||||||
|
|
||||||
if ((i=pipe(p)) < 0)
|
|
||||||
panic("pipe: %e", i);
|
|
||||||
|
|
||||||
// fork the first prime process in the chain
|
|
||||||
if ((id=fork()) < 0)
|
|
||||||
panic("fork: %e", id);
|
|
||||||
|
|
||||||
if (id == 0) {
|
|
||||||
close(p[1]);
|
|
||||||
primeproc(p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(p[0]);
|
|
||||||
|
|
||||||
// feed all the integers through
|
|
||||||
for (i=2;; i++)
|
|
||||||
if ((r=write(p[1], &i, 4)) != 4)
|
|
||||||
panic("generator write: %d, %e", r, r >= 0 ? 0 : r);
|
|
||||||
}
|
|
||||||
|
|
||||||
329
user/sh.c
329
user/sh.c
@@ -1,329 +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.
|
|
||||||
if ((fd = open(t, O_RDONLY)) < 0) {
|
|
||||||
cprintf("open %s for read: %e", t, fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fd != 0) {
|
|
||||||
dup(fd, 0);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cprintf("i am parent environment %08x\n", thisenv->env_id);
|
|
||||||
if ((r = spawnl("faultio", "faultio", 0)) < 0)
|
|
||||||
panic("spawn(faultio) failed: %e", r);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cprintf("i am parent environment %08x\n", thisenv->env_id);
|
|
||||||
if ((r = spawnl("hello", "hello", 0)) < 0)
|
|
||||||
panic("spawn(hello) failed: %e", r);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cprintf("i am parent environment %08x\n", thisenv->env_id);
|
|
||||||
if ((r = spawnl("init", "init", "one", "two", 0)) < 0)
|
|
||||||
panic("spawnl(init) failed: %e", r);
|
|
||||||
}
|
|
||||||
31
user/spin.c
31
user/spin.c
@@ -1,31 +0,0 @@
|
|||||||
// Test preemption by forking off a child process that just spins forever.
|
|
||||||
// Let it run for a couple time slices, then kill it.
|
|
||||||
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
envid_t env;
|
|
||||||
|
|
||||||
cprintf("I am the parent. Forking the child...\n");
|
|
||||||
if ((env = fork()) == 0) {
|
|
||||||
cprintf("I am the child. Spinning...\n");
|
|
||||||
while (1)
|
|
||||||
/* do nothing */;
|
|
||||||
}
|
|
||||||
|
|
||||||
cprintf("I am the parent. Running the child...\n");
|
|
||||||
sys_yield();
|
|
||||||
sys_yield();
|
|
||||||
sys_yield();
|
|
||||||
sys_yield();
|
|
||||||
sys_yield();
|
|
||||||
sys_yield();
|
|
||||||
sys_yield();
|
|
||||||
sys_yield();
|
|
||||||
|
|
||||||
cprintf("I am the parent. Killing the child...\n");
|
|
||||||
sys_env_destroy(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
volatile int counter;
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
int seen;
|
|
||||||
envid_t parent = sys_getenvid();
|
|
||||||
|
|
||||||
// Fork several environments
|
|
||||||
for (i = 0; i < 20; i++)
|
|
||||||
if (fork() == 0)
|
|
||||||
break;
|
|
||||||
if (i == 20) {
|
|
||||||
sys_yield();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the parent to finish forking
|
|
||||||
while (envs[ENVX(parent)].env_status != ENV_FREE)
|
|
||||||
asm volatile("pause");
|
|
||||||
|
|
||||||
// Check that one environment doesn't run on two CPUs at once
|
|
||||||
for (i = 0; i < 10; i++) {
|
|
||||||
sys_yield();
|
|
||||||
for (j = 0; j < 10000; j++)
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter != 10*10000)
|
|
||||||
panic("ran on two CPUs at once (counter is %d)", counter);
|
|
||||||
|
|
||||||
// Check that we see environments running on different CPUs
|
|
||||||
cprintf("[%08x] stresssched on CPU %d\n", thisenv->env_id, thisenv->env_cpunum);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#include <inc/x86.h>
|
|
||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
char buf[512], buf2[512];
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int fd, r, n, n2;
|
|
||||||
|
|
||||||
if ((fd = open("motd", O_RDONLY)) < 0)
|
|
||||||
panic("open motd: %e", fd);
|
|
||||||
seek(fd, 0);
|
|
||||||
if ((n = readn(fd, buf, sizeof buf)) <= 0)
|
|
||||||
panic("readn: %e", n);
|
|
||||||
|
|
||||||
if ((r = fork()) < 0)
|
|
||||||
panic("fork: %e", r);
|
|
||||||
if (r == 0) {
|
|
||||||
seek(fd, 0);
|
|
||||||
cprintf("going to read in child (might page fault if your sharing is buggy)\n");
|
|
||||||
if ((n2 = readn(fd, buf2, sizeof buf2)) != n)
|
|
||||||
panic("read in parent got %d, read in child got %d", n, n2);
|
|
||||||
if (memcmp(buf, buf2, n) != 0)
|
|
||||||
panic("read in parent got different bytes from read in child");
|
|
||||||
cprintf("read in child succeeded\n");
|
|
||||||
seek(fd, 0);
|
|
||||||
close(fd);
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
wait(r);
|
|
||||||
if ((n2 = readn(fd, buf2, sizeof buf2)) != n)
|
|
||||||
panic("read in parent got %d, then got %d", n, n2);
|
|
||||||
cprintf("read in parent succeeded\n");
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
breakpoint();
|
|
||||||
}
|
|
||||||
128
user/testfile.c
128
user/testfile.c
@@ -1,128 +0,0 @@
|
|||||||
#include <inc/lib.h>
|
|
||||||
|
|
||||||
const char *msg = "This is the NEW message of the day!\n\n";
|
|
||||||
|
|
||||||
#define FVA ((struct Fd*)0xCCCCC000)
|
|
||||||
|
|
||||||
static int
|
|
||||||
xopen(const char *path, int mode)
|
|
||||||
{
|
|
||||||
extern union Fsipc fsipcbuf;
|
|
||||||
envid_t fsenv;
|
|
||||||
|
|
||||||
strcpy(fsipcbuf.open.req_path, path);
|
|
||||||
fsipcbuf.open.req_omode = mode;
|
|
||||||
|
|
||||||
fsenv = ipc_find_env(ENV_TYPE_FS);
|
|
||||||
ipc_send(fsenv, FSREQ_OPEN, &fsipcbuf, PTE_P | PTE_W | PTE_U);
|
|
||||||
return ipc_recv(NULL, FVA, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
umain(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int r, f, i;
|
|
||||||
struct Fd *fd;
|
|
||||||
struct Fd fdcopy;
|
|
||||||
struct Stat st;
|
|
||||||
char buf[512];
|
|
||||||
|
|
||||||
// We open files manually first, to avoid the FD layer
|
|
||||||
if ((r = xopen("/not-found", O_RDONLY)) < 0 && r != -E_NOT_FOUND)
|
|
||||||
panic("serve_open /not-found: %e", r);
|
|
||||||
else if (r >= 0)
|
|
||||||
panic("serve_open /not-found succeeded!");
|
|
||||||
|
|
||||||
if ((r = xopen("/newmotd", O_RDONLY)) < 0)
|
|
||||||
panic("serve_open /newmotd: %e", r);
|
|
||||||
if (FVA->fd_dev_id != 'f' || FVA->fd_offset != 0 || FVA->fd_omode != O_RDONLY)
|
|
||||||
panic("serve_open did not fill struct Fd correctly\n");
|
|
||||||
cprintf("serve_open is good\n");
|
|
||||||
|
|
||||||
if ((r = devfile.dev_stat(FVA, &st)) < 0)
|
|
||||||
panic("file_stat: %e", r);
|
|
||||||
if (strlen(msg) != st.st_size)
|
|
||||||
panic("file_stat returned size %d wanted %d\n", st.st_size, strlen(msg));
|
|
||||||
cprintf("file_stat is good\n");
|
|
||||||
|
|
||||||
memset(buf, 0, sizeof buf);
|
|
||||||
if ((r = devfile.dev_read(FVA, buf, sizeof buf)) < 0)
|
|
||||||
panic("file_read: %e", r);
|
|
||||||
if (strcmp(buf, msg) != 0)
|
|
||||||
panic("file_read returned wrong data");
|
|
||||||
cprintf("file_read is good\n");
|
|
||||||
|
|
||||||
if ((r = devfile.dev_close(FVA)) < 0)
|
|
||||||
panic("file_close: %e", r);
|
|
||||||
cprintf("file_close is good\n");
|
|
||||||
|
|
||||||
// We're about to unmap the FD, but still need a way to get
|
|
||||||
// the stale filenum to serve_read, so we make a local copy.
|
|
||||||
// The file server won't think it's stale until we unmap the
|
|
||||||
// FD page.
|
|
||||||
fdcopy = *FVA;
|
|
||||||
sys_page_unmap(0, FVA);
|
|
||||||
|
|
||||||
if ((r = devfile.dev_read(&fdcopy, buf, sizeof buf)) != -E_INVAL)
|
|
||||||
panic("serve_read does not handle stale fileids correctly: %e", r);
|
|
||||||
cprintf("stale fileid is good\n");
|
|
||||||
|
|
||||||
// Try writing
|
|
||||||
if ((r = xopen("/new-file", O_RDWR|O_CREAT)) < 0)
|
|
||||||
panic("serve_open /new-file: %e", r);
|
|
||||||
|
|
||||||
if ((r = devfile.dev_write(FVA, msg, strlen(msg))) != strlen(msg))
|
|
||||||
panic("file_write: %e", r);
|
|
||||||
cprintf("file_write is good\n");
|
|
||||||
|
|
||||||
FVA->fd_offset = 0;
|
|
||||||
memset(buf, 0, sizeof buf);
|
|
||||||
if ((r = devfile.dev_read(FVA, buf, sizeof buf)) < 0)
|
|
||||||
panic("file_read after file_write: %e", r);
|
|
||||||
if (r != strlen(msg))
|
|
||||||
panic("file_read after file_write returned wrong length: %d", r);
|
|
||||||
if (strcmp(buf, msg) != 0)
|
|
||||||
panic("file_read after file_write returned wrong data");
|
|
||||||
cprintf("file_read after file_write is good\n");
|
|
||||||
|
|
||||||
// Now we'll try out open
|
|
||||||
if ((r = open("/not-found", O_RDONLY)) < 0 && r != -E_NOT_FOUND)
|
|
||||||
panic("open /not-found: %e", r);
|
|
||||||
else if (r >= 0)
|
|
||||||
panic("open /not-found succeeded!");
|
|
||||||
|
|
||||||
if ((r = open("/newmotd", O_RDONLY)) < 0)
|
|
||||||
panic("open /newmotd: %e", r);
|
|
||||||
fd = (struct Fd*) (0xD0000000 + r*PGSIZE);
|
|
||||||
if (fd->fd_dev_id != 'f' || fd->fd_offset != 0 || fd->fd_omode != O_RDONLY)
|
|
||||||
panic("open did not fill struct Fd correctly\n");
|
|
||||||
cprintf("open is good\n");
|
|
||||||
|
|
||||||
// Try files with indirect blocks
|
|
||||||
if ((f = open("/big", O_WRONLY|O_CREAT)) < 0)
|
|
||||||
panic("creat /big: %e", f);
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
for (i = 0; i < (NDIRECT*3)*BLKSIZE; i += sizeof(buf)) {
|
|
||||||
*(int*)buf = i;
|
|
||||||
if ((r = write(f, buf, sizeof(buf))) < 0)
|
|
||||||
panic("write /big@%d: %e", i, r);
|
|
||||||
}
|
|
||||||
close(f);
|
|
||||||
|
|
||||||
if ((f = open("/big", O_RDONLY)) < 0)
|
|
||||||
panic("open /big: %e", f);
|
|
||||||
for (i = 0; i < (NDIRECT*3)*BLKSIZE; i += sizeof(buf)) {
|
|
||||||
*(int*)buf = i;
|
|
||||||
if ((r = readn(f, buf, sizeof(buf))) < 0)
|
|
||||||
panic("read /big@%d: %e", i, r);
|
|
||||||
if (r != sizeof(buf))
|
|
||||||
panic("read /big from %d returned %d < %d bytes",
|
|
||||||
i, r, sizeof(buf));
|
|
||||||
if (*(int*)buf != i)
|
|
||||||
panic("read /big from %d returned bad data %d",
|
|
||||||
i, *(int*)buf);
|
|
||||||
}
|
|
||||||
close(f);
|
|
||||||
cprintf("large file is good\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user