Compare commits
21 Commits
c3e2a92b08
...
lab5
| Author | SHA1 | Date | |
|---|---|---|---|
| 4199ce9c37 | |||
| e9f683f6d6 | |||
| 9acc7c80f7 | |||
| 721a113c93 | |||
| 86c4aa03ed | |||
| f1980d32ca | |||
| 46e7d21fbf | |||
| d943a2be37 | |||
| 1e5c0639ee | |||
| 34e9433d15 | |||
| 9bec3d83bd | |||
| 0289ec3b3e | |||
| 46cc5c9478 | |||
| 4e4de7b836 | |||
| 03b296f7e1 | |||
| 74b1c2c69d | |||
| 3449b8c566 | |||
| 7d76204053 | |||
|
|
ed614a36f0 | ||
|
|
adbb69750a | ||
|
|
c67463e23c |
@@ -141,6 +141,7 @@ 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
|
CPUS ?= 1
|
||||||
@@ -149,6 +150,8 @@ QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -
|
|||||||
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 += -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
|
||||||
|
|||||||
22
answers-lab2.txt
Normal file
22
answers-lab2.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
1. The entry given by PDX(UVPT) is mapped to the page directory itself,
|
||||||
|
to allow programs to read existing memory mappings. The entry at PDX(UPAGES)
|
||||||
|
is mapped to the pages variable in memory so that the kernel (and potentially other ring 0 programs) can access it. The entry pointed to by PDX(KSTACKTOP-KSTACKSIZE) is mapped
|
||||||
|
to the bootstack location. Finally, both the memory pointed to by 0 and PDX(KERNBASE) are mapped to kernel memory. However, the mappings at VA 0 are read-only, so user programs can't touch them, while the mappings at PDX(KERNBASE) are kernel-private and RW.
|
||||||
|
|
||||||
|
A table could be as follows:
|
||||||
|
|
||||||
|
400 - Kernel memory
|
||||||
|
... - Kernel memory
|
||||||
|
3c0 - Kernel memory
|
||||||
|
3bf - Kernel Stack
|
||||||
|
3bd - UVPT
|
||||||
|
3bc - UPAGES
|
||||||
|
|
||||||
|
|
||||||
|
2. The kernel memory is mapped as kernel read write. This means there are several flags set in the kernel page directory and page table entries, which indicate that this is restricted memory. The lower 3 bits of the CS register will be checked when an access to this memory is made, and, if they are not 0 or 1 (indicating kernel code), the CPU generates a fault, and the user program gives up control back to the OS. Thus, unless a program is started in ring 0, it will not be able to read or write kernel memory, as it should.
|
||||||
|
|
||||||
|
3. The absolute maximum is 4GB, since we use 32-bit integers for referencing memory. Some of this memory is used for the kernel itself, as well as for the page directory and tables.
|
||||||
|
|
||||||
|
4. The page directory and the corresponding pages are all 4kb. The page directory can have 1024 entries, and each of these point to a page table. Thus, we use approximatey 4MB (slightly more than that, actually, due to the size of the page directory itself) of memory. Additionally, the "pages" structs (which are used to keep track of available physical pages), will require ~1000000 entries, each of which is between 6 and 8 bytes (depending on whether GCC aligns struct sizes). This means another 8MB is used to keep track of free pages, to a total of around 12MB.
|
||||||
|
|
||||||
|
5. We switch to high EIP when we jump to "relocated". Relocated is a label, and a symbol that's inserted by the linker. Since the linker is configured to link the kernel high, relocated points to the upper portion of memory, where KERNBASE is. However, the entry page directory, just like our full page directory later on, sets up two mappings, one starting at 0 (creating a one to one mapping between some of the virtual addresses and their physical counterparts), and one starting at KERNBASE. Thus, we can continue to run at a low EIP. The only reason I can think of as to why we NEED to make the switch, besides the elementary "the kernel links high", is that we need to be able to write to various symbols, also linked above KERNBASE.
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
LAB=4
|
LAB=5
|
||||||
PACKAGEDATE=Mon Oct 8 21:31:51 PDT 2018
|
PACKAGEDATE=Wed Oct 24 20:44:37 EDT 2018
|
||||||
|
|||||||
77
fs/Makefrag
Normal file
77
fs/Makefrag
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
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
Normal file
166
fs/bc.c
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
|
||||||
|
#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
Normal file
490
fs/fs.c
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
#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
Normal file
49
fs/fs.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#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
Normal file
244
fs/fsformat.c
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* 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
Normal file
112
fs/ide.c
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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
Normal file
12
fs/lorem
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
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
Normal file
4
fs/motd
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
This is /motd, the message of the day.
|
||||||
|
|
||||||
|
Welcome to the JOS kernel, now with a file system!
|
||||||
|
|
||||||
2
fs/newmotd
Normal file
2
fs/newmotd
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
This is the NEW message of the day!
|
||||||
|
|
||||||
5
fs/script
Normal file
5
fs/script
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
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
Normal file
361
fs/serv.c
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
7
fs/testshell.sh
Normal file
7
fs/testshell.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
echo hello world | cat
|
||||||
|
cat lorem
|
||||||
|
cat lorem |num
|
||||||
|
cat lorem |num |num |num |num |num
|
||||||
|
lsfd -1
|
||||||
|
cat script
|
||||||
|
sh <script
|
||||||
112
grade-lab5
Executable file
112
grade-lab5
Executable file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/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
Normal file
82
inc/args.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#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
|
||||||
@@ -41,6 +41,7 @@ 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 {
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ enum {
|
|||||||
E_IPC_NOT_RECV , // Attempt to send to env that is not recving
|
E_IPC_NOT_RECV , // Attempt to send to env that is not recving
|
||||||
E_EOF , // Unexpected end of file
|
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
Normal file
58
inc/fd.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// 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
Normal file
116
inc/fs.h
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// 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 */
|
||||||
40
inc/lib.h
40
inc/lib.h
@@ -17,6 +17,9 @@
|
|||||||
#include <inc/memlayout.h>
|
#include <inc/memlayout.h>
|
||||||
#include <inc/syscall.h>
|
#include <inc/syscall.h>
|
||||||
#include <inc/trap.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)
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ int sys_env_destroy(envid_t);
|
|||||||
void sys_yield(void);
|
void sys_yield(void);
|
||||||
static envid_t sys_exofork(void);
|
static envid_t sys_exofork(void);
|
||||||
int sys_env_set_status(envid_t env, int status);
|
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_env_set_pgfault_upcall(envid_t env, void *upcall);
|
||||||
int sys_page_alloc(envid_t env, void *pg, int perm);
|
int sys_page_alloc(envid_t env, void *pg, int perm);
|
||||||
int sys_page_map(envid_t src_env, void *src_pg,
|
int sys_page_map(envid_t src_env, void *src_pg,
|
||||||
@@ -75,7 +79,43 @@ envid_t ipc_find_env(enum EnvType type);
|
|||||||
envid_t fork(void);
|
envid_t fork(void);
|
||||||
envid_t sfork(void); // Challenge!
|
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 */
|
||||||
|
|||||||
33
inc/partition.h
Normal file
33
inc/partition.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#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
|
||||||
@@ -12,6 +12,7 @@ enum {
|
|||||||
SYS_page_unmap,
|
SYS_page_unmap,
|
||||||
SYS_exofork,
|
SYS_exofork,
|
||||||
SYS_env_set_status,
|
SYS_env_set_status,
|
||||||
|
SYS_env_set_trapframe,
|
||||||
SYS_env_set_pgfault_upcall,
|
SYS_env_set_pgfault_upcall,
|
||||||
SYS_yield,
|
SYS_yield,
|
||||||
SYS_ipc_try_send,
|
SYS_ipc_try_send,
|
||||||
|
|||||||
19
inc/x86.h
19
inc/x86.h
@@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include <inc/types.h>
|
#include <inc/types.h>
|
||||||
|
|
||||||
|
#define MSR_IA32_SYSENTER_CS 0x174
|
||||||
|
#define MSR_IA32_SYSENTER_EIP 0x176
|
||||||
|
#define MSR_IA32_SYSENTER_ESP 0x175
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
breakpoint(void)
|
breakpoint(void)
|
||||||
{
|
{
|
||||||
@@ -261,4 +265,19 @@ xchg(volatile uint32_t *addr, uint32_t newval)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
write_msr(uint32_t reg, uint32_t low, uint32_t high) {
|
||||||
|
asm volatile("wrmsr\n\t"
|
||||||
|
:: "c" (reg), "a" (low), "d" (high));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
read_msr(uint32_t reg, uint32_t* low, uint32_t* high) {
|
||||||
|
uint32_t eax, edx;
|
||||||
|
asm volatile("rdmsr\n\t"
|
||||||
|
: "=a" (eax), "=d" (edx) : "c" (reg));
|
||||||
|
*low = eax;
|
||||||
|
*high = edx;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* !JOS_INC_X86_H */
|
#endif /* !JOS_INC_X86_H */
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ KERN_BINFILES := user/hello \
|
|||||||
user/faultread \
|
user/faultread \
|
||||||
user/faultreadkernel \
|
user/faultreadkernel \
|
||||||
user/faultwrite \
|
user/faultwrite \
|
||||||
user/faultwritekernel
|
user/faultwritekernel \
|
||||||
|
user/getc
|
||||||
|
|
||||||
# Binary files for LAB4
|
# Binary files for LAB4
|
||||||
KERN_BINFILES += user/idle \
|
KERN_BINFILES += user/idle \
|
||||||
@@ -77,6 +78,24 @@ KERN_BINFILES += user/idle \
|
|||||||
user/pingpong \
|
user/pingpong \
|
||||||
user/pingpongs \
|
user/pingpongs \
|
||||||
user/primes
|
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))
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
18
kern/env.c
18
kern/env.c
@@ -255,6 +255,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
|
|||||||
|
|
||||||
// Enable interrupts while in user mode.
|
// Enable interrupts while in user mode.
|
||||||
// LAB 4: Your code here.
|
// LAB 4: Your code here.
|
||||||
|
e->env_tf.tf_eflags |= FL_IF;
|
||||||
|
|
||||||
// Clear the page fault handler until user installs one.
|
// Clear the page fault handler until user installs one.
|
||||||
e->env_pgfault_upcall = 0;
|
e->env_pgfault_upcall = 0;
|
||||||
@@ -266,7 +267,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +289,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;
|
size_t count = ROUNDUP(len, PGSIZE) / PGSIZE + 1;
|
||||||
struct PageInfo* p;
|
struct PageInfo* p;
|
||||||
|
|
||||||
while(count--) {
|
while(count--) {
|
||||||
@@ -356,6 +357,7 @@ 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;
|
||||||
@@ -363,11 +365,10 @@ 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
|
||||||
@@ -386,10 +387,16 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +417,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);
|
||||||
@@ -528,6 +535,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
kern/init.c
17
kern/init.c
@@ -3,6 +3,7 @@
|
|||||||
#include <inc/stdio.h>
|
#include <inc/stdio.h>
|
||||||
#include <inc/string.h>
|
#include <inc/string.h>
|
||||||
#include <inc/assert.h>
|
#include <inc/assert.h>
|
||||||
|
#include <inc/x86.h>
|
||||||
|
|
||||||
#include <kern/monitor.h>
|
#include <kern/monitor.h>
|
||||||
#include <kern/console.h>
|
#include <kern/console.h>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
|
|
||||||
static void boot_aps(void);
|
static void boot_aps(void);
|
||||||
|
|
||||||
|
void sysenter_handler();
|
||||||
|
|
||||||
void
|
void
|
||||||
i386_init(void)
|
i386_init(void)
|
||||||
@@ -33,6 +35,10 @@ i386_init(void)
|
|||||||
"\33[34m" "r"
|
"\33[34m" "r"
|
||||||
"\33[0m" " Works!" "\n");
|
"\33[0m" " Works!" "\n");
|
||||||
|
|
||||||
|
write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) sysenter_handler, 0);
|
||||||
|
write_msr(MSR_IA32_SYSENTER_ESP, KSTACKTOP, 0);
|
||||||
|
write_msr(MSR_IA32_SYSENTER_CS, GD_KT, 0);
|
||||||
|
|
||||||
// Lab 2 memory management initialization functions
|
// Lab 2 memory management initialization functions
|
||||||
mem_init();
|
mem_init();
|
||||||
|
|
||||||
@@ -49,18 +55,25 @@ i386_init(void)
|
|||||||
|
|
||||||
// Acquire the big kernel lock before waking up APs
|
// Acquire the big kernel lock before waking up APs
|
||||||
// Your code here:
|
// Your code here:
|
||||||
|
lock_kernel();
|
||||||
|
|
||||||
// Starting non-boot CPUs
|
// Starting non-boot CPUs
|
||||||
boot_aps();
|
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_primes, ENV_TYPE_USER);
|
ENV_CREATE(user_icode, ENV_TYPE_USER);
|
||||||
#endif // TEST*
|
#endif // TEST*
|
||||||
|
|
||||||
|
// Should not be necessary - drains keyboard because interrupt has given up.
|
||||||
|
kbd_intr();
|
||||||
|
|
||||||
// Schedule and run the first user environment!
|
// Schedule and run the first user environment!
|
||||||
sched_yield();
|
sched_yield();
|
||||||
}
|
}
|
||||||
@@ -115,6 +128,8 @@ mp_main(void)
|
|||||||
// only one CPU can enter the scheduler at a time!
|
// only one CPU can enter the scheduler at a time!
|
||||||
//
|
//
|
||||||
// Your code here:
|
// Your code here:
|
||||||
|
lock_kernel();
|
||||||
|
sched_yield();
|
||||||
|
|
||||||
// Remove this after you finish Exercise 6
|
// Remove this after you finish Exercise 6
|
||||||
for (;;);
|
for (;;);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <inc/assert.h>
|
#include <inc/assert.h>
|
||||||
#include <inc/x86.h>
|
#include <inc/x86.h>
|
||||||
|
|
||||||
|
#include <kern/env.h>
|
||||||
#include <kern/ansi.h>
|
#include <kern/ansi.h>
|
||||||
#include <kern/console.h>
|
#include <kern/console.h>
|
||||||
#include <kern/monitor.h>
|
#include <kern/monitor.h>
|
||||||
@@ -31,7 +32,9 @@ static struct Command commands[] = {
|
|||||||
{ "kerninfo", "Display information about the kernel", mon_kerninfo },
|
{ "kerninfo", "Display information about the kernel", mon_kerninfo },
|
||||||
{ "backtrace", "Display current backtrace", mon_backtrace },
|
{ "backtrace", "Display current backtrace", mon_backtrace },
|
||||||
{ "showmappings", "Display the physical mappings for range", mon_showmappings },
|
{ "showmappings", "Display the physical mappings for range", mon_showmappings },
|
||||||
{ "mperms", "Change the permissions of a memory range", mon_mperms }
|
{ "mperms", "Change the permissions of a memory range", mon_mperms },
|
||||||
|
{ "resume", "Resume from a breakpoint", mon_resume },
|
||||||
|
{ "step", "Step to next instruction", mon_step }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -188,6 +191,43 @@ int mon_mperms(int argc, char** argv, struct Trapframe* tf) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a nice thought and all...
|
||||||
|
// But we should modify the trap frame.
|
||||||
|
// I feel stupid.
|
||||||
|
static inline void
|
||||||
|
set_eflag(uint16_t flagno, int value) {
|
||||||
|
uint32_t temp;
|
||||||
|
uint32_t mask = ~(1 << flagno);
|
||||||
|
uint32_t regv = value << flagno;
|
||||||
|
asm volatile("pushfl\n\t"
|
||||||
|
"pop %w0\n\t"
|
||||||
|
"and %w1, %w0\n\t"
|
||||||
|
"or %w2, %w0\n\t"
|
||||||
|
"push %w0\n\t"
|
||||||
|
"popfl\n\t"
|
||||||
|
: "=r" (temp)
|
||||||
|
: "r" (mask), "r" (regv));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXPECT_BRKPT if(!(tf->tf_trapno == T_BRKPT || tf->tf_trapno == T_DEBUG)) { \
|
||||||
|
cprintf(ACOL_ERR("I don't think I should resume from this.\n")); \
|
||||||
|
return 0; }
|
||||||
|
#define EFLAGS_TF 0x8
|
||||||
|
|
||||||
|
int mon_resume(int argc, char** argv, struct Trapframe* tf) {
|
||||||
|
EXPECT_BRKPT;
|
||||||
|
tf->tf_eflags &= ~(1 << EFLAGS_TF);
|
||||||
|
env_pop_tf(tf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mon_step(int argc, char** argv, struct Trapframe* tf) {
|
||||||
|
EXPECT_BRKPT;
|
||||||
|
tf->tf_eflags |= 1 << EFLAGS_TF;
|
||||||
|
env_pop_tf(tf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/***** Kernel monitor command interpreter *****/
|
/***** Kernel monitor command interpreter *****/
|
||||||
|
|
||||||
#define WHITESPACE "\t\r\n "
|
#define WHITESPACE "\t\r\n "
|
||||||
|
|||||||
@@ -17,5 +17,7 @@ int mon_kerninfo(int argc, char **argv, struct Trapframe *tf);
|
|||||||
int mon_backtrace(int argc, char **argv, struct Trapframe *tf);
|
int mon_backtrace(int argc, char **argv, struct Trapframe *tf);
|
||||||
int mon_showmappings(int argc, char **argv, struct Trapframe *tf);
|
int mon_showmappings(int argc, char **argv, struct Trapframe *tf);
|
||||||
int mon_mperms(int argc, char** argv, struct Trapframe* tf);
|
int mon_mperms(int argc, char** argv, struct Trapframe* tf);
|
||||||
|
int mon_resume(int argc, char** argv, struct Trapframe* tf);
|
||||||
|
int mon_step(int argc, char** argv, struct Trapframe* tf);
|
||||||
|
|
||||||
#endif // !JOS_KERN_MONITOR_H
|
#endif // !JOS_KERN_MONITOR_H
|
||||||
|
|||||||
28
kern/pmap.c
28
kern/pmap.c
@@ -85,6 +85,8 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -187,8 +189,6 @@ mem_init(void)
|
|||||||
boot_map_region(kern_pgdir,
|
boot_map_region(kern_pgdir,
|
||||||
UPAGES, ROUNDUP(pages_size, PGSIZE),
|
UPAGES, ROUNDUP(pages_size, PGSIZE),
|
||||||
PADDR(pages), PTE_U);
|
PADDR(pages), PTE_U);
|
||||||
kern_pgdir[PDX(UPAGES)] |= PTE_U;
|
|
||||||
kern_pgdir[PDX(UPAGES)] &= ~PTE_W;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Map the 'envs' array read-only by the user at linear address UENVS
|
// Map the 'envs' array read-only by the user at linear address UENVS
|
||||||
@@ -197,11 +197,10 @@ 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);
|
||||||
kern_pgdir[PDX(UENVS)] |= PTE_U;
|
|
||||||
kern_pgdir[PDX(UPAGES)] &= ~PTE_W;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Use the physical memory that 'bootstack' refers to as the kernel
|
// Use the physical memory that 'bootstack' refers to as the kernel
|
||||||
@@ -217,8 +216,6 @@ mem_init(void)
|
|||||||
boot_map_region(kern_pgdir,
|
boot_map_region(kern_pgdir,
|
||||||
KSTACKTOP-KSTKSIZE, KSTKSIZE,
|
KSTACKTOP-KSTKSIZE, KSTKSIZE,
|
||||||
PADDR(bootstack), PTE_W);
|
PADDR(bootstack), PTE_W);
|
||||||
kern_pgdir[PDX(KSTACKTOP-KSTKSIZE)] |= PTE_W;
|
|
||||||
kern_pgdir[PDX(KSTACKTOP-KSTKSIZE)] &= ~PTE_U;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Map all of physical memory at KERNBASE.
|
// Map all of physical memory at KERNBASE.
|
||||||
@@ -231,9 +228,6 @@ mem_init(void)
|
|||||||
boot_map_region(kern_pgdir,
|
boot_map_region(kern_pgdir,
|
||||||
KERNBASE, 0x100000000 - KERNBASE,
|
KERNBASE, 0x100000000 - KERNBASE,
|
||||||
0, PTE_W);
|
0, PTE_W);
|
||||||
kern_pgdir[PDX(KERNBASE)] |= PTE_W | PTE_P;
|
|
||||||
kern_pgdir[PDX(KERNBASE)] &= ~PTE_U;
|
|
||||||
|
|
||||||
|
|
||||||
// Initialize the SMP-related parts of the memory map
|
// Initialize the SMP-related parts of the memory map
|
||||||
mem_init_mp();
|
mem_init_mp();
|
||||||
@@ -285,6 +279,11 @@ mem_init_mp(void)
|
|||||||
// Permissions: kernel RW, user NONE
|
// Permissions: kernel RW, user NONE
|
||||||
//
|
//
|
||||||
// LAB 4: Your code here:
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +297,7 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -605,7 +605,15 @@ mmio_map_region(physaddr_t pa, size_t size)
|
|||||||
// Hint: The staff solution uses boot_map_region.
|
// Hint: The staff solution uses boot_map_region.
|
||||||
//
|
//
|
||||||
// Your code here:
|
// Your code here:
|
||||||
panic("mmio_map_region not implemented");
|
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;
|
||||||
|
|||||||
22
kern/sched.c
22
kern/sched.c
@@ -21,7 +21,8 @@ sched_yield(void)
|
|||||||
//
|
//
|
||||||
// If no envs are runnable, but the environment previously
|
// If no envs are runnable, but the environment previously
|
||||||
// running on this CPU is still ENV_RUNNING, it's okay to
|
// running on this CPU is still ENV_RUNNING, it's okay to
|
||||||
// choose that environment.
|
// choose that environment. Make sure curenv is not null before
|
||||||
|
// dereferencing it.
|
||||||
//
|
//
|
||||||
// Never choose an environment that's currently running on
|
// Never choose an environment that's currently running on
|
||||||
// another CPU (env_status == ENV_RUNNING). If there are
|
// another CPU (env_status == ENV_RUNNING). If there are
|
||||||
@@ -29,6 +30,23 @@ sched_yield(void)
|
|||||||
// below to halt the cpu.
|
// below to halt the cpu.
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
// 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 never returns
|
||||||
sched_halt();
|
sched_halt();
|
||||||
@@ -76,7 +94,7 @@ sched_halt(void)
|
|||||||
"pushl $0\n"
|
"pushl $0\n"
|
||||||
// LAB 4:
|
// LAB 4:
|
||||||
// Uncomment the following line after completing exercise 13
|
// Uncomment the following line after completing exercise 13
|
||||||
//"sti\n"
|
"sti\n"
|
||||||
"1:\n"
|
"1:\n"
|
||||||
"hlt\n"
|
"hlt\n"
|
||||||
"jmp 1b\n"
|
"jmp 1b\n"
|
||||||
|
|||||||
181
kern/syscall.c
181
kern/syscall.c
@@ -53,10 +53,6 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -80,9 +76,16 @@ sys_exofork(void)
|
|||||||
// status is set to ENV_NOT_RUNNABLE, and the register set is copied
|
// status is set to ENV_NOT_RUNNABLE, and the register set is copied
|
||||||
// from the current environment -- but tweaked so sys_exofork
|
// from the current environment -- but tweaked so sys_exofork
|
||||||
// will appear to return 0.
|
// will appear to return 0.
|
||||||
|
struct Env* new_env;
|
||||||
|
int error_code;
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
error_code = env_alloc(&new_env, curenv->env_id);
|
||||||
panic("sys_exofork not implemented");
|
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
|
// Set envid's env_status to status, which must be ENV_RUNNABLE
|
||||||
@@ -100,9 +103,50 @@ sys_env_set_status(envid_t envid, int status)
|
|||||||
// You should set envid2env's third argument to 1, which will
|
// You should set envid2env's third argument to 1, which will
|
||||||
// check whether the current environment has permission to set
|
// check whether the current environment has permission to set
|
||||||
// envid's status.
|
// envid's status.
|
||||||
|
struct Env* env;
|
||||||
|
int error_code;
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
error_code = envid2env(envid, &env, 1);
|
||||||
panic("sys_env_set_status not implemented");
|
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
|
// Set the page fault upcall for 'envid' by modifying the corresponding struct
|
||||||
@@ -116,8 +160,11 @@ sys_env_set_status(envid_t envid, int status)
|
|||||||
static int
|
static int
|
||||||
sys_env_set_pgfault_upcall(envid_t envid, void *func)
|
sys_env_set_pgfault_upcall(envid_t envid, void *func)
|
||||||
{
|
{
|
||||||
// LAB 4: Your code here.
|
struct Env* env;
|
||||||
panic("sys_env_set_pgfault_upcall not implemented");
|
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
|
// Allocate a page of memory and map it at 'va' with permission
|
||||||
@@ -145,9 +192,22 @@ sys_page_alloc(envid_t envid, void *va, int perm)
|
|||||||
// parameters for correctness.
|
// parameters for correctness.
|
||||||
// If page_insert() fails, remember to free the page you
|
// If page_insert() fails, remember to free the page you
|
||||||
// allocated!
|
// allocated!
|
||||||
|
struct Env* env;
|
||||||
|
int return_code;
|
||||||
|
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
|
||||||
panic("sys_page_alloc not implemented");
|
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
|
// Map the page of memory at 'srcva' in srcenvid's address space
|
||||||
@@ -176,9 +236,24 @@ sys_page_map(envid_t srcenvid, void *srcva,
|
|||||||
// parameters for correctness.
|
// parameters for correctness.
|
||||||
// Use the third argument to page_lookup() to
|
// Use the third argument to page_lookup() to
|
||||||
// check the current permissions on the page.
|
// check the current permissions on the page.
|
||||||
|
struct Env *srcenv, *dstenv;
|
||||||
|
pte_t* srcpte;
|
||||||
|
int return_code;
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
if((return_code = envid2env(srcenvid, &srcenv, 1)) < 0) return return_code;
|
||||||
panic("sys_page_map not implemented");
|
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'.
|
// Unmap the page of memory at 'va' in the address space of 'envid'.
|
||||||
@@ -192,9 +267,14 @@ static int
|
|||||||
sys_page_unmap(envid_t envid, void *va)
|
sys_page_unmap(envid_t envid, void *va)
|
||||||
{
|
{
|
||||||
// Hint: This function is a wrapper around page_remove().
|
// Hint: This function is a wrapper around page_remove().
|
||||||
|
struct Env* env;
|
||||||
|
int return_code;
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
||||||
panic("sys_page_unmap not implemented");
|
if(!SYS_CHECKADDR(va)) return -E_INVAL;
|
||||||
|
|
||||||
|
page_remove(env->env_pgdir, va);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to send 'value' to the target env 'envid'.
|
// Try to send 'value' to the target env 'envid'.
|
||||||
@@ -238,8 +318,38 @@ sys_page_unmap(envid_t envid, void *va)
|
|||||||
static int
|
static int
|
||||||
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
|
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
|
||||||
{
|
{
|
||||||
// LAB 4: Your code here.
|
struct Env* dest_env;
|
||||||
panic("sys_ipc_try_send not implemented");
|
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
|
// Block until a value is ready. Record that you want to receive
|
||||||
@@ -256,8 +366,20 @@ sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
|
|||||||
static int
|
static int
|
||||||
sys_ipc_recv(void *dstva)
|
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.
|
// LAB 4: Your code here.
|
||||||
panic("sys_ipc_recv not implemented");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +401,27 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
73
kern/trap.c
73
kern/trap.c
@@ -89,6 +89,13 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -125,6 +132,13 @@ 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();
|
||||||
}
|
}
|
||||||
@@ -160,18 +174,18 @@ trap_init_percpu(void)
|
|||||||
|
|
||||||
// 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.
|
||||||
ts.ts_esp0 = KSTACKTOP;
|
thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - thiscpu->cpu_id * (KSTKSIZE + KSTKGAP);
|
||||||
ts.ts_ss0 = GD_KD;
|
thiscpu->cpu_ts.ts_ss0 = GD_KD;
|
||||||
ts.ts_iomb = sizeof(struct Taskstate);
|
thiscpu->cpu_ts.ts_iomb = sizeof(struct Taskstate);
|
||||||
|
|
||||||
// Initialize the TSS slot of the gdt.
|
// Initialize the TSS slot of the gdt.
|
||||||
gdt[GD_TSS0 >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
|
gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts),
|
||||||
sizeof(struct Taskstate) - 1, 0);
|
sizeof(struct Taskstate) - 1, 0);
|
||||||
gdt[GD_TSS0 >> 3].sd_s = 0;
|
gdt[(GD_TSS0 >> 3) + cpunum()].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);
|
ltr(GD_TSS0 + (cpunum() << 3));
|
||||||
|
|
||||||
// Load the IDT
|
// Load the IDT
|
||||||
lidt(&idt_pd);
|
lidt(&idt_pd);
|
||||||
@@ -231,7 +245,7 @@ trap_dispatch(struct Trapframe *tf)
|
|||||||
if (tf->tf_trapno == T_PGFLT) {
|
if (tf->tf_trapno == T_PGFLT) {
|
||||||
page_fault_handler(tf);
|
page_fault_handler(tf);
|
||||||
return;
|
return;
|
||||||
} else if (tf->tf_trapno == T_BRKPT) {
|
} else if (tf->tf_trapno == T_BRKPT || tf->tf_trapno == T_DEBUG) {
|
||||||
monitor(tf);
|
monitor(tf);
|
||||||
return;
|
return;
|
||||||
} else if (tf->tf_trapno == T_SYSCALL) {
|
} else if (tf->tf_trapno == T_SYSCALL) {
|
||||||
@@ -243,6 +257,15 @@ 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
|
// Handle spurious interrupts
|
||||||
@@ -258,6 +281,9 @@ trap_dispatch(struct Trapframe *tf)
|
|||||||
// interrupt using lapic_eoi() before calling the scheduler!
|
// interrupt using lapic_eoi() before calling the scheduler!
|
||||||
// LAB 4: Your code here.
|
// 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)
|
||||||
@@ -294,6 +320,7 @@ trap(struct Trapframe *tf)
|
|||||||
// Acquire the big kernel lock before doing any
|
// Acquire the big kernel lock before doing any
|
||||||
// serious kernel work.
|
// serious kernel work.
|
||||||
// LAB 4: Your code here.
|
// LAB 4: Your code here.
|
||||||
|
lock_kernel();
|
||||||
assert(curenv);
|
assert(curenv);
|
||||||
|
|
||||||
// Garbage collect if current enviroment is a zombie
|
// Garbage collect if current enviroment is a zombie
|
||||||
@@ -339,6 +366,8 @@ 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.
|
||||||
@@ -373,11 +402,39 @@ page_fault_handler(struct Trapframe *tf)
|
|||||||
// (the 'tf' variable points at 'curenv->env_tf').
|
// (the 'tf' variable points at 'curenv->env_tf').
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
// LAB 4: Your code here.
|
||||||
|
if(!curenv->env_pgfault_upcall) {
|
||||||
// Destroy the environment that caused the fault.
|
// Destroy the environment that caused the fault.
|
||||||
cprintf("[%08x] user fault va %08x ip %08x\n",
|
cprintf("[%08x] user fault va %08x ip %08x\n",
|
||||||
curenv->env_id, fault_va, tf->tf_eip);
|
curenv->env_id, fault_va, tf->tf_eip);
|
||||||
print_trapframe(tf);
|
print_trapframe(tf);
|
||||||
env_destroy(curenv);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,21 @@
|
|||||||
|
|
||||||
.text
|
.text
|
||||||
|
|
||||||
|
.globl sysenter_handler
|
||||||
|
sysenter_handler:
|
||||||
|
push %ebp // holds env's stack pointer
|
||||||
|
push %esi // holds the env's return addr
|
||||||
|
push %edi
|
||||||
|
push %ebx
|
||||||
|
push %ecx
|
||||||
|
push %edx
|
||||||
|
push %eax
|
||||||
|
call syscall
|
||||||
|
add $0x14, %esp
|
||||||
|
pop %edx
|
||||||
|
pop %ecx
|
||||||
|
sysexit
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lab 3: Your code here for generating entry points for the different traps.
|
* Lab 3: Your code here for generating entry points for the different traps.
|
||||||
*/
|
*/
|
||||||
@@ -67,6 +82,12 @@ 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
|
||||||
|
|||||||
10
lib/Makefrag
10
lib/Makefrag
@@ -16,7 +16,17 @@ LIB_SRCFILES := $(LIB_SRCFILES) \
|
|||||||
lib/fork.c \
|
lib/fork.c \
|
||||||
lib/ipc.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
Normal file
73
lib/args.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
109
lib/console.c
109
lib/console.c
@@ -15,11 +15,114 @@ cputchar(int ch)
|
|||||||
int
|
int
|
||||||
getchar(void)
|
getchar(void)
|
||||||
{
|
{
|
||||||
|
unsigned char c;
|
||||||
int r;
|
int r;
|
||||||
// sys_cgetc does not block, but getchar should.
|
|
||||||
while ((r = sys_cgetc()) == 0)
|
// JOS does, however, support standard _input_ redirection,
|
||||||
sys_yield();
|
// allowing the user to redirect script files to the shell and such.
|
||||||
|
// getchar() reads a character from file descriptor 0.
|
||||||
|
r = read(0, &c, 1);
|
||||||
|
if (r < 0)
|
||||||
return r;
|
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,6 +4,7 @@
|
|||||||
void
|
void
|
||||||
exit(void)
|
exit(void)
|
||||||
{
|
{
|
||||||
|
close_all();
|
||||||
sys_env_destroy(0);
|
sys_env_destroy(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
320
lib/fd.c
Normal file
320
lib/fd.c
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
#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
Normal file
186
lib/file.c
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
75
lib/fork.c
75
lib/fork.c
@@ -23,18 +23,22 @@ pgfault(struct UTrapframe *utf)
|
|||||||
// Hint:
|
// Hint:
|
||||||
// Use the read-only page table mappings at uvpt
|
// Use the read-only page table mappings at uvpt
|
||||||
// (see <inc/memlayout.h>).
|
// (see <inc/memlayout.h>).
|
||||||
|
if(!((err & FEC_WR) && (uvpt[(uintptr_t) addr >> PGSHIFT] & PTE_COW)))
|
||||||
// LAB 4: Your code here.
|
panic("page fault (addr %p)! %c", addr, (err & FEC_WR) ? 'w' : 'r');
|
||||||
|
|
||||||
// Allocate a new page, map it at a temporary location (PFTEMP),
|
// 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
|
// copy the data from the old page to the new page, then move the new
|
||||||
// page to the old page's address.
|
// page to the old page's address.
|
||||||
// Hint:
|
// Hint:
|
||||||
// You should make three system calls.
|
// 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");
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
memcpy(temp_addr, fault_addr, PGSIZE);
|
||||||
|
sys_page_map(0, temp_addr, 0, fault_addr, PTE_P | PTE_U | PTE_W);
|
||||||
panic("pgfault not implemented");
|
sys_page_unmap(0, temp_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -52,9 +56,27 @@ static int
|
|||||||
duppage(envid_t envid, unsigned pn)
|
duppage(envid_t envid, unsigned pn)
|
||||||
{
|
{
|
||||||
int r;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// LAB 4: Your code here.
|
|
||||||
panic("duppage not implemented");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +99,43 @@ duppage(envid_t envid, unsigned pn)
|
|||||||
envid_t
|
envid_t
|
||||||
fork(void)
|
fork(void)
|
||||||
{
|
{
|
||||||
// LAB 4: Your code here.
|
set_pgfault_handler(pgfault);
|
||||||
panic("fork not implemented");
|
|
||||||
|
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!
|
// Challenge!
|
||||||
|
|||||||
81
lib/fprintf.c
Normal file
81
lib/fprintf.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
19
lib/ipc.c
19
lib/ipc.c
@@ -22,9 +22,14 @@
|
|||||||
int32_t
|
int32_t
|
||||||
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
|
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
|
||||||
{
|
{
|
||||||
// LAB 4: Your code here.
|
int return_code;
|
||||||
panic("ipc_recv not implemented");
|
if((return_code = sys_ipc_recv(pg ? pg : (void*) ~0)) < 0)
|
||||||
return 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'.
|
// Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'.
|
||||||
@@ -38,8 +43,12 @@ ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
|
|||||||
void
|
void
|
||||||
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
|
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
|
||||||
{
|
{
|
||||||
// LAB 4: Your code here.
|
int return_code = -E_IPC_NOT_RECV;
|
||||||
panic("ipc_send not implemented");
|
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 the first environment of the given type. We'll use this to
|
||||||
|
|||||||
14
lib/pageref.c
Normal file
14
lib/pageref.c
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -65,18 +65,29 @@ _pgfault_upcall:
|
|||||||
// ways as registers become unavailable as scratch space.
|
// ways as registers become unavailable as scratch space.
|
||||||
//
|
//
|
||||||
// LAB 4: Your code here.
|
// 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
|
// Restore the trap-time registers. After you do this, you
|
||||||
// can no longer modify any general-purpose registers.
|
// can no longer modify any general-purpose registers.
|
||||||
// LAB 4: Your code here.
|
// LAB 4: Your code here.
|
||||||
|
add $0x8, %esp
|
||||||
|
popal
|
||||||
|
|
||||||
// Restore eflags from the stack. After you do this, you can
|
// Restore eflags from the stack. After you do this, you can
|
||||||
// no longer use arithmetic operations or anything else that
|
// no longer use arithmetic operations or anything else that
|
||||||
// modifies eflags.
|
// modifies eflags.
|
||||||
// LAB 4: Your code here.
|
// LAB 4: Your code here.
|
||||||
|
add $0x4, %esp
|
||||||
|
popfl
|
||||||
|
|
||||||
// Switch back to the adjusted trap-time stack.
|
// Switch back to the adjusted trap-time stack.
|
||||||
// LAB 4: Your code here.
|
// LAB 4: Your code here.
|
||||||
|
pop %esp
|
||||||
|
|
||||||
// Return to re-execute the instruction that faulted.
|
// Return to re-execute the instruction that faulted.
|
||||||
// LAB 4: Your code here.
|
// LAB 4: Your code here.
|
||||||
|
ret
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ set_pgfault_handler(void (*handler)(struct UTrapframe *utf))
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (_pgfault_handler == 0) {
|
if (_pgfault_handler == 0) {
|
||||||
// First time through!
|
if(sys_page_alloc(0, (void*) UXSTACKTOP - PGSIZE, PTE_U | PTE_P | PTE_W) < 0)
|
||||||
// LAB 4: Your code here.
|
panic("set_pgfault_handler");
|
||||||
panic("set_pgfault_handler not implemented");
|
sys_env_set_pgfault_upcall(0, _pgfault_upcall);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save handler pointer for assembly to call.
|
// Save handler pointer for assembly to call.
|
||||||
|
|||||||
191
lib/pipe.c
Normal file
191
lib/pipe.c
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
#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));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -28,6 +28,13 @@ static const char * const error_string[MAXERROR] =
|
|||||||
[E_FAULT] = "segmentation fault",
|
[E_FAULT] = "segmentation fault",
|
||||||
[E_IPC_NOT_RECV]= "env is not recving",
|
[E_IPC_NOT_RECV]= "env is not recving",
|
||||||
[E_EOF] = "unexpected end of file",
|
[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,14 +9,20 @@ 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) {
|
||||||
|
|||||||
322
lib/spawn.c
Normal file
322
lib/spawn.c
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <inc/syscall.h>
|
#include <inc/syscall.h>
|
||||||
#include <inc/lib.h>
|
#include <inc/lib.h>
|
||||||
|
#include <inc/x86.h>
|
||||||
|
|
||||||
static inline int32_t
|
static inline int32_t
|
||||||
syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
||||||
@@ -37,6 +38,20 @@ syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
fast_syscall(int num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4) {
|
||||||
|
asm volatile(
|
||||||
|
"push %%ebp\n\t"
|
||||||
|
"mov %%esp, %%ebp\n\t"
|
||||||
|
"lea syscall_ret_%=, %%esi\n\t"
|
||||||
|
"sysenter\n\t"
|
||||||
|
"syscall_ret_%=: pop %%ebp\n\t"
|
||||||
|
: "+a" (num)
|
||||||
|
: "d" (a1), "c" (a2), "b" (a3), "D" (a4)
|
||||||
|
: "esi");
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sys_cputs(const char *s, size_t len)
|
sys_cputs(const char *s, size_t len)
|
||||||
{
|
{
|
||||||
@@ -93,6 +108,12 @@ sys_env_set_status(envid_t envid, int status)
|
|||||||
return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0);
|
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
|
int
|
||||||
sys_env_set_pgfault_upcall(envid_t envid, void *upcall)
|
sys_env_set_pgfault_upcall(envid_t envid, void *upcall)
|
||||||
{
|
{
|
||||||
|
|||||||
13
lib/wait.c
Normal file
13
lib/wait.c
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#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,7 +10,8 @@ $(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 $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
$(V)$(LD) -o $@.debug $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
||||||
$(V)$(OBJDUMP) -S $@ > $@.asm
|
$(V)$(OBJDUMP) -S $@.debug > $@.asm
|
||||||
$(V)$(NM) -n $@ > $@.sym
|
$(V)$(NM) -n $@.debug > $@.sym
|
||||||
|
$(V)$(OBJCOPY) -R .stab -R .stabstr --add-gnu-debuglink=$(basename $@.debug) $@.debug $@
|
||||||
|
|
||||||
|
|||||||
36
user/cat.c
Normal file
36
user/cat.c
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
user/echo.c
Normal file
21
user/echo.c
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
22
user/faultio.c
Normal file
22
user/faultio.c
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// test user-level fault handler -- alloc pages to fix faults
|
||||||
|
|
||||||
|
#include <inc/lib.h>
|
||||||
|
#include <inc/x86.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int x, r;
|
||||||
|
int nsecs = 1;
|
||||||
|
int secno = 0;
|
||||||
|
int diskno = 1;
|
||||||
|
|
||||||
|
if (read_eflags() & FL_IOPL_3)
|
||||||
|
cprintf("eflags wrong\n");
|
||||||
|
|
||||||
|
// this outb to select disk 1 should result in a general protection
|
||||||
|
// fault, because user-level code shouldn't be able to use the io space.
|
||||||
|
outb(0x1F6, 0xE0 | (1<<4));
|
||||||
|
|
||||||
|
cprintf("%s: made it here --- bug\n");
|
||||||
|
}
|
||||||
9
user/getc.c
Normal file
9
user/getc.c
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
while(!(c = sys_cgetc()));
|
||||||
|
cprintf("got character %c\n", c);
|
||||||
|
}
|
||||||
29
user/icode.c
Normal file
29
user/icode.c
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#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");
|
||||||
|
}
|
||||||
69
user/init.c
Normal file
69
user/init.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
user/initsh.c
Normal file
27
user/initsh.c
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#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
Normal file
91
user/ls.c
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#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
Normal file
35
user/lsfd.c
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#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
Normal file
47
user/num.c
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
76
user/primespipe.c
Normal file
76
user/primespipe.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// 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
Normal file
329
user/sh.c
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
10
user/spawnfaultio.c
Normal file
10
user/spawnfaultio.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
10
user/spawnhello.c
Normal file
10
user/spawnhello.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
10
user/spawninit.c
Normal file
10
user/spawninit.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
38
user/testfdsharing.c
Normal file
38
user/testfdsharing.c
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#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
Normal file
128
user/testfile.c
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#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");
|
||||||
|
}
|
||||||
|
|
||||||
30
user/testkbd.c
Normal file
30
user/testkbd.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
|
||||||
|
// Spin for a bit to let the console quiet
|
||||||
|
for (i = 0; i < 10; ++i)
|
||||||
|
sys_yield();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for(;;){
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = readline("Type a line: ");
|
||||||
|
if (buf != NULL)
|
||||||
|
fprintf(1, "%s\n", buf);
|
||||||
|
else
|
||||||
|
fprintf(1, "(end of file received)\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
24
user/testmalloc.c
Normal file
24
user/testmalloc.c
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
int n;
|
||||||
|
void *v;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
buf = readline("> ");
|
||||||
|
if (buf == 0)
|
||||||
|
exit();
|
||||||
|
if (memcmp(buf, "free ", 5) == 0) {
|
||||||
|
v = (void*) strtol(buf + 5, 0, 0);
|
||||||
|
free(v);
|
||||||
|
} else if (memcmp(buf, "malloc ", 7) == 0) {
|
||||||
|
n = strtol(buf + 7, 0, 0);
|
||||||
|
v = malloc(n);
|
||||||
|
printf("\t0x%x\n", (uintptr_t) v);
|
||||||
|
} else
|
||||||
|
printf("?unknown command\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
64
user/testpipe.c
Normal file
64
user/testpipe.c
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
char *msg = "Now is the time for all good men to come to the aid of their party.";
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char buf[100];
|
||||||
|
int i, pid, p[2];
|
||||||
|
|
||||||
|
binaryname = "pipereadeof";
|
||||||
|
|
||||||
|
if ((i = pipe(p)) < 0)
|
||||||
|
panic("pipe: %e", i);
|
||||||
|
|
||||||
|
if ((pid = fork()) < 0)
|
||||||
|
panic("fork: %e", i);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
cprintf("[%08x] pipereadeof close %d\n", thisenv->env_id, p[1]);
|
||||||
|
close(p[1]);
|
||||||
|
cprintf("[%08x] pipereadeof readn %d\n", thisenv->env_id, p[0]);
|
||||||
|
i = readn(p[0], buf, sizeof buf-1);
|
||||||
|
if (i < 0)
|
||||||
|
panic("read: %e", i);
|
||||||
|
buf[i] = 0;
|
||||||
|
if (strcmp(buf, msg) == 0)
|
||||||
|
cprintf("\npipe read closed properly\n");
|
||||||
|
else
|
||||||
|
cprintf("\ngot %d bytes: %s\n", i, buf);
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
cprintf("[%08x] pipereadeof close %d\n", thisenv->env_id, p[0]);
|
||||||
|
close(p[0]);
|
||||||
|
cprintf("[%08x] pipereadeof write %d\n", thisenv->env_id, p[1]);
|
||||||
|
if ((i = write(p[1], msg, strlen(msg))) != strlen(msg))
|
||||||
|
panic("write: %e", i);
|
||||||
|
close(p[1]);
|
||||||
|
}
|
||||||
|
wait(pid);
|
||||||
|
|
||||||
|
binaryname = "pipewriteeof";
|
||||||
|
if ((i = pipe(p)) < 0)
|
||||||
|
panic("pipe: %e", i);
|
||||||
|
|
||||||
|
if ((pid = fork()) < 0)
|
||||||
|
panic("fork: %e", i);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
close(p[0]);
|
||||||
|
while (1) {
|
||||||
|
cprintf(".");
|
||||||
|
if (write(p[1], "x", 1) != 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cprintf("\npipe write closed properly\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
close(p[0]);
|
||||||
|
close(p[1]);
|
||||||
|
wait(pid);
|
||||||
|
|
||||||
|
cprintf("pipe tests passed\n");
|
||||||
|
}
|
||||||
66
user/testpiperace.c
Normal file
66
user/testpiperace.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int p[2], r, pid, i, max;
|
||||||
|
void *va;
|
||||||
|
struct Fd *fd;
|
||||||
|
const volatile struct Env *kid;
|
||||||
|
|
||||||
|
cprintf("testing for dup race...\n");
|
||||||
|
if ((r = pipe(p)) < 0)
|
||||||
|
panic("pipe: %e", r);
|
||||||
|
max = 200;
|
||||||
|
if ((r = fork()) < 0)
|
||||||
|
panic("fork: %e", r);
|
||||||
|
if (r == 0) {
|
||||||
|
close(p[1]);
|
||||||
|
//
|
||||||
|
// Now the ref count for p[0] will toggle between 2 and 3
|
||||||
|
// as the parent dups and closes it (there's a close implicit in dup).
|
||||||
|
//
|
||||||
|
// The ref count for p[1] is 1.
|
||||||
|
// Thus the ref count for the underlying pipe structure
|
||||||
|
// will toggle between 3 and 4.
|
||||||
|
//
|
||||||
|
// If a clock interrupt catches close between unmapping
|
||||||
|
// the pipe structure and unmapping the fd, we'll have
|
||||||
|
// a ref count for p[0] of 3, a ref count for p[1] of 1,
|
||||||
|
// and a ref count for the pipe structure of 3, which is
|
||||||
|
// a no-no.
|
||||||
|
//
|
||||||
|
// If a clock interrupt catches dup between mapping the
|
||||||
|
// fd and mapping the pipe structure, we'll have the same
|
||||||
|
// ref counts, still a no-no.
|
||||||
|
//
|
||||||
|
for (i=0; i<max; i++) {
|
||||||
|
if(pipeisclosed(p[0])){
|
||||||
|
cprintf("RACE: pipe appears closed\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
sys_yield();
|
||||||
|
}
|
||||||
|
// do something to be not runnable besides exiting
|
||||||
|
ipc_recv(0,0,0);
|
||||||
|
}
|
||||||
|
pid = r;
|
||||||
|
cprintf("pid is %d\n", pid);
|
||||||
|
va = 0;
|
||||||
|
kid = &envs[ENVX(pid)];
|
||||||
|
cprintf("kid is %d\n", kid-envs);
|
||||||
|
dup(p[0], 10);
|
||||||
|
while (kid->env_status == ENV_RUNNABLE)
|
||||||
|
dup(p[0], 10);
|
||||||
|
|
||||||
|
cprintf("child done with loop\n");
|
||||||
|
if (pipeisclosed(p[0]))
|
||||||
|
panic("somehow the other end of p[0] got closed!");
|
||||||
|
if ((r = fd_lookup(p[0], &fd)) < 0)
|
||||||
|
panic("cannot look up p[0]: %e", r);
|
||||||
|
va = fd2data(fd);
|
||||||
|
if (pageref(va) != 3+1)
|
||||||
|
cprintf("\nchild detected race\n");
|
||||||
|
else
|
||||||
|
cprintf("\nrace didn't happen\n", max);
|
||||||
|
}
|
||||||
69
user/testpiperace2.c
Normal file
69
user/testpiperace2.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int p[2], r, i;
|
||||||
|
struct Fd *fd;
|
||||||
|
const volatile struct Env *kid;
|
||||||
|
|
||||||
|
cprintf("testing for pipeisclosed race...\n");
|
||||||
|
if ((r = pipe(p)) < 0)
|
||||||
|
panic("pipe: %e", r);
|
||||||
|
if ((r = fork()) < 0)
|
||||||
|
panic("fork: %e", r);
|
||||||
|
if (r == 0) {
|
||||||
|
// child just dups and closes repeatedly,
|
||||||
|
// yielding so the parent can see
|
||||||
|
// the fd state between the two.
|
||||||
|
close(p[1]);
|
||||||
|
for (i = 0; i < 200; i++) {
|
||||||
|
if (i % 10 == 0)
|
||||||
|
cprintf("%d.", i);
|
||||||
|
// dup, then close. yield so that other guy will
|
||||||
|
// see us while we're between them.
|
||||||
|
dup(p[0], 10);
|
||||||
|
sys_yield();
|
||||||
|
close(10);
|
||||||
|
sys_yield();
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We hold both p[0] and p[1] open, so pipeisclosed should
|
||||||
|
// never return false.
|
||||||
|
//
|
||||||
|
// Now the ref count for p[0] will toggle between 2 and 3
|
||||||
|
// as the child dups and closes it.
|
||||||
|
// The ref count for p[1] is 1.
|
||||||
|
// Thus the ref count for the underlying pipe structure
|
||||||
|
// will toggle between 3 and 4.
|
||||||
|
//
|
||||||
|
// If pipeisclosed checks pageref(p[0]) and gets 3, and
|
||||||
|
// then the child closes, and then pipeisclosed checks
|
||||||
|
// pageref(pipe structure) and gets 3, then it will return true
|
||||||
|
// when it shouldn't.
|
||||||
|
//
|
||||||
|
// If pipeisclosed checks pageref(pipe structure) and gets 3,
|
||||||
|
// and then the child dups, and then pipeisclosed checks
|
||||||
|
// pageref(p[0]) and gets 3, then it will return true when
|
||||||
|
// it shouldn't.
|
||||||
|
//
|
||||||
|
// So either way, pipeisclosed is going give a wrong answer.
|
||||||
|
//
|
||||||
|
kid = &envs[ENVX(r)];
|
||||||
|
while (kid->env_status == ENV_RUNNABLE)
|
||||||
|
if (pipeisclosed(p[0]) != 0) {
|
||||||
|
cprintf("\nRACE: pipe appears closed\n");
|
||||||
|
sys_env_destroy(r);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
cprintf("child done with loop\n");
|
||||||
|
if (pipeisclosed(p[0]))
|
||||||
|
panic("somehow the other end of p[0] got closed!");
|
||||||
|
if ((r = fd_lookup(p[0], &fd)) < 0)
|
||||||
|
panic("cannot look up p[0]: %e", r);
|
||||||
|
(void) fd2data(fd);
|
||||||
|
cprintf("race didn't happen\n");
|
||||||
|
}
|
||||||
42
user/testptelibrary.c
Normal file
42
user/testptelibrary.c
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
#define VA ((char *) 0xA0000000)
|
||||||
|
const char *msg = "hello, world\n";
|
||||||
|
const char *msg2 = "goodbye, world\n";
|
||||||
|
|
||||||
|
void childofspawn(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (argc != 0)
|
||||||
|
childofspawn();
|
||||||
|
|
||||||
|
if ((r = sys_page_alloc(0, VA, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||||
|
panic("sys_page_alloc: %e", r);
|
||||||
|
|
||||||
|
// check fork
|
||||||
|
if ((r = fork()) < 0)
|
||||||
|
panic("fork: %e", r);
|
||||||
|
if (r == 0) {
|
||||||
|
strcpy(VA, msg);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
wait(r);
|
||||||
|
cprintf("fork handles PTE_SHARE %s\n", strcmp(VA, msg) == 0 ? "right" : "wrong");
|
||||||
|
|
||||||
|
// check spawn
|
||||||
|
if ((r = spawnl("/testptelibrary", "testptelibrary", "arg", 0)) < 0)
|
||||||
|
panic("spawn: %e", r);
|
||||||
|
wait(r);
|
||||||
|
cprintf("spawn handles PTE_SHARE %s\n", strcmp(VA, msg2) == 0 ? "right" : "wrong");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
childofspawn(void)
|
||||||
|
{
|
||||||
|
strcpy(VA, msg2);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
45
user/testpteshare.c
Normal file
45
user/testpteshare.c
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include <inc/x86.h>
|
||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
#define VA ((char *) 0xA0000000)
|
||||||
|
const char *msg = "hello, world\n";
|
||||||
|
const char *msg2 = "goodbye, world\n";
|
||||||
|
|
||||||
|
void childofspawn(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (argc != 0)
|
||||||
|
childofspawn();
|
||||||
|
|
||||||
|
if ((r = sys_page_alloc(0, VA, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||||
|
panic("sys_page_alloc: %e", r);
|
||||||
|
|
||||||
|
// check fork
|
||||||
|
if ((r = fork()) < 0)
|
||||||
|
panic("fork: %e", r);
|
||||||
|
if (r == 0) {
|
||||||
|
strcpy(VA, msg);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
wait(r);
|
||||||
|
cprintf("fork handles PTE_SHARE %s\n", strcmp(VA, msg) == 0 ? "right" : "wrong");
|
||||||
|
|
||||||
|
// check spawn
|
||||||
|
if ((r = spawnl("/testpteshare", "testpteshare", "arg", 0)) < 0)
|
||||||
|
panic("spawn: %e", r);
|
||||||
|
wait(r);
|
||||||
|
cprintf("spawn handles PTE_SHARE %s\n", strcmp(VA, msg2) == 0 ? "right" : "wrong");
|
||||||
|
|
||||||
|
breakpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
childofspawn(void)
|
||||||
|
{
|
||||||
|
strcpy(VA, msg2);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
85
user/testshell.c
Normal file
85
user/testshell.c
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#include <inc/x86.h>
|
||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
void wrong(int, int, int);
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char c1, c2;
|
||||||
|
int r, rfd, wfd, kfd, n1, n2, off, nloff;
|
||||||
|
int pfds[2];
|
||||||
|
|
||||||
|
close(0);
|
||||||
|
close(1);
|
||||||
|
opencons();
|
||||||
|
opencons();
|
||||||
|
|
||||||
|
if ((rfd = open("testshell.sh", O_RDONLY)) < 0)
|
||||||
|
panic("open testshell.sh: %e", rfd);
|
||||||
|
if ((wfd = pipe(pfds)) < 0)
|
||||||
|
panic("pipe: %e", wfd);
|
||||||
|
wfd = pfds[1];
|
||||||
|
|
||||||
|
cprintf("running sh -x < testshell.sh | cat\n");
|
||||||
|
if ((r = fork()) < 0)
|
||||||
|
panic("fork: %e", r);
|
||||||
|
if (r == 0) {
|
||||||
|
dup(rfd, 0);
|
||||||
|
dup(wfd, 1);
|
||||||
|
close(rfd);
|
||||||
|
close(wfd);
|
||||||
|
if ((r = spawnl("/sh", "sh", "-x", 0)) < 0)
|
||||||
|
panic("spawn: %e", r);
|
||||||
|
close(0);
|
||||||
|
close(1);
|
||||||
|
wait(r);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
close(rfd);
|
||||||
|
close(wfd);
|
||||||
|
|
||||||
|
rfd = pfds[0];
|
||||||
|
if ((kfd = open("testshell.key", O_RDONLY)) < 0)
|
||||||
|
panic("open testshell.key for reading: %e", kfd);
|
||||||
|
|
||||||
|
nloff = 0;
|
||||||
|
for (off=0;; off++) {
|
||||||
|
n1 = read(rfd, &c1, 1);
|
||||||
|
n2 = read(kfd, &c2, 1);
|
||||||
|
if (n1 < 0)
|
||||||
|
panic("reading testshell.out: %e", n1);
|
||||||
|
if (n2 < 0)
|
||||||
|
panic("reading testshell.key: %e", n2);
|
||||||
|
if (n1 == 0 && n2 == 0)
|
||||||
|
break;
|
||||||
|
if (n1 != 1 || n2 != 1 || c1 != c2)
|
||||||
|
wrong(rfd, kfd, nloff);
|
||||||
|
if (c1 == '\n')
|
||||||
|
nloff = off+1;
|
||||||
|
}
|
||||||
|
cprintf("shell ran correctly\n");
|
||||||
|
|
||||||
|
breakpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wrong(int rfd, int kfd, int off)
|
||||||
|
{
|
||||||
|
char buf[100];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
seek(rfd, off);
|
||||||
|
seek(kfd, off);
|
||||||
|
|
||||||
|
cprintf("shell produced incorrect output.\n");
|
||||||
|
cprintf("expected:\n===\n");
|
||||||
|
while ((n = read(kfd, buf, sizeof buf-1)) > 0)
|
||||||
|
sys_cputs(buf, n);
|
||||||
|
cprintf("===\ngot:\n===\n");
|
||||||
|
while ((n = read(rfd, buf, sizeof buf-1)) > 0)
|
||||||
|
sys_cputs(buf, n);
|
||||||
|
cprintf("===\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
40
user/writemotd.c
Normal file
40
user/writemotd.c
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include <inc/lib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
umain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int rfd, wfd;
|
||||||
|
char buf[512];
|
||||||
|
int n, r;
|
||||||
|
|
||||||
|
if ((rfd = open("/newmotd", O_RDONLY)) < 0)
|
||||||
|
panic("open /newmotd: %e", rfd);
|
||||||
|
if ((wfd = open("/motd", O_RDWR)) < 0)
|
||||||
|
panic("open /motd: %e", wfd);
|
||||||
|
cprintf("file descriptors %d %d\n", rfd, wfd);
|
||||||
|
if (rfd == wfd)
|
||||||
|
panic("open /newmotd and /motd give same file descriptor");
|
||||||
|
|
||||||
|
cprintf("OLD MOTD\n===\n");
|
||||||
|
while ((n = read(wfd, buf, sizeof buf-1)) > 0)
|
||||||
|
sys_cputs(buf, n);
|
||||||
|
cprintf("===\n");
|
||||||
|
seek(wfd, 0);
|
||||||
|
|
||||||
|
if ((r = ftruncate(wfd, 0)) < 0)
|
||||||
|
panic("truncate /motd: %e", r);
|
||||||
|
|
||||||
|
cprintf("NEW MOTD\n===\n");
|
||||||
|
while ((n = read(rfd, buf, sizeof buf-1)) > 0) {
|
||||||
|
sys_cputs(buf, n);
|
||||||
|
if ((r = write(wfd, buf, n)) != n)
|
||||||
|
panic("write /motd: %e", r);
|
||||||
|
}
|
||||||
|
cprintf("===\n");
|
||||||
|
|
||||||
|
if (n < 0)
|
||||||
|
panic("read /newmotd: %e", n);
|
||||||
|
|
||||||
|
close(rfd);
|
||||||
|
close(wfd);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user