Mercurial > hg > Members > anatofuz > CbC_xv6
changeset 0:83c23a36980d
Init
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,40 @@ +xv6-rpi +======= + +Project description +------- + +[Xv6](http://pdos.csail.mit.edu/6.828/2012/xv6.html) is a simple Unix-like teaching operating system developed in 2006 by MIT for its operating system course. Xv6 is inspired by and modeled after the Sixth Edition Unix (aka V6). Xv6 is fairly complete educational operating system while remaining simply and manageable. It has most components in an early Unix system, such as processes, virtual memory, kernel-user separation, interrupt, file system... + +The original xv6 is implemented for the x86 architecture. This is an effort to port xv6 to ARM, particularly [Raspberry Pi](http://www.raspberrypi.org/). The initial porting of xv6 to ARM (QEMU/ARMv6) has been completed by people in the department of [computer science](http://www.cs.fsu.edu/) in [Florida State University](http://www.fsu.edu/). + +Debug +------- +1. use QEMU to dump a execution trace +`qemu-system-arm -M versatilepb -m 128 -cpu arm1176 -nographic -singlestep \ + -d exec,cpu,guest_errors -D qemu.log -kernel kernel.elf` + +2. insert `show_callstk` in the kernel to dump current call stacks. + +License +------- +The MIT License (MIT) + +Copyright (c) 2013 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Makefile Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,75 @@ +# specify path to QEMU, installed with MacPorts +QEMU = qemu-system-arm + +include makefile.inc + +# link the libgcc.a for __aeabi_idiv. ARM has no native support for div +LIBS = $(LIBGCC) + +OBJS = \ + lib/string.o \ + \ + arm.o\ + asm.o\ + bio.o\ + buddy.o\ + console.o\ + exec.o\ + file.o\ + fs.o\ + log.o\ + main.o\ + memide.o\ + pipe.o\ + proc.o\ + spinlock.o\ + start.o\ + swtch.o\ + syscall.o\ + sysfile.o\ + sysproc.o\ + trap_asm.o\ + trap.o\ + vm.o \ + \ + device/picirq.o \ + device/timer.o \ + device/uart.o + +KERN_OBJS = $(OBJS) entry.o +kernel.elf: $(addprefix build/,$(KERN_OBJS)) kernel.ld build/initcode build/fs.img + cp -f build/initcode initcode + cp -f build/fs.img fs.img + $(call LINK_BIN, kernel.ld, kernel.elf, \ + $(addprefix build/,$(KERN_OBJS)), \ + initcode fs.img) + $(OBJDUMP) -S kernel.elf > kernel.asm + $(OBJDUMP) -t kernel.elf | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym + rm -f initcode fs.img + +qemu: kernel.elf + @clear + @echo "Press Ctrl-A and then X to terminate QEMU session\n" + $(QEMU) -M versatilepb -m 128 -cpu arm1176 -nographic -kernel kernel.elf + +INITCODE_OBJ = initcode.o +$(addprefix build/,$(INITCODE_OBJ)): initcode.S + $(call build-directory) + $(call AS_WITH, -nostdinc -I.) + +#initcode is linked into the kernel, it will be used to craft the first process +build/initcode: $(addprefix build/,$(INITCODE_OBJ)) + $(call LINK_INIT, -N -e start -Ttext 0) + $(call OBJCOPY_INIT) + $(OBJDUMP) -S $< > initcode.asm + +build/fs.img: + make -C tools + make -C usr + +clean: + rm -rf build + rm -f *.o *.d *.asm *.sym vectors.S bootblock entryother \ + initcode initcode.out kernel xv6.img fs.img kernel.elf memfs + make -C tools clean + make -C usr clean
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/arm.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,127 @@ +// BSP support routine +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "proc.h" +#include "arm.h" +#include "mmu.h" + +void cli (void) +{ + uint val; + + // ok, enable paging using read/modify/write + asm("MRS %[v], cpsr": [v]"=r" (val)::); + val |= DIS_INT; + asm("MSR cpsr_cxsf, %[v]": :[v]"r" (val):); +} + +void sti (void) +{ + uint val; + + // ok, enable paging using read/modify/write + asm("MRS %[v], cpsr": [v]"=r" (val)::); + val &= ~DIS_INT; + asm("MSR cpsr_cxsf, %[v]": :[v]"r" (val):); +} + +// return the cpsr used for user program +uint spsr_usr () +{ + uint val; + + // ok, enable paging using read/modify/write + asm("MRS %[v], cpsr": [v]"=r" (val)::); + val &= ~MODE_MASK; + val |= USR_MODE; + + return val; +} + +// return whether interrupt is currently enabled +int int_enabled () +{ + uint val; + + // ok, enable paging using read/modify/write + asm("MRS %[v], cpsr": [v]"=r" (val)::); + + return !(val & DIS_INT); +} + +// Pushcli/popcli are like cli/sti except that they are matched: +// it takes two popcli to undo two pushcli. Also, if interrupts +// are off, then pushcli, popcli leaves them off. + +void pushcli (void) +{ + int enabled; + + enabled = int_enabled(); + + cli(); + + if (cpu->ncli++ == 0) { + cpu->intena = enabled; + } +} + +void popcli (void) +{ + if (int_enabled()) { + panic("popcli - interruptible"); + } + + if (--cpu->ncli < 0) { + cprintf("cpu (%d)->ncli: %d\n", cpu, cpu->ncli); + panic("popcli -- ncli < 0"); + } + + if ((cpu->ncli == 0) && cpu->intena) { + sti(); + } +} + +// Record the current call stack in pcs[] by following the call chain. +// In ARM ABI, the function prologue is as: +// push {fp, lr} +// add fp, sp, #4 +// so, fp points to lr, the return address +void getcallerpcs (void * v, uint pcs[]) +{ + uint *fp; + int i; + + fp = (uint*) v; + + for (i = 0; i < N_CALLSTK; i++) { + if ((fp == 0) || (fp < (uint*) KERNBASE) || (fp == (uint*) 0xffffffff)) { + break; + } + + fp = fp - 1; // points fp to the saved fp + pcs[i] = fp[1]; // saved lr + fp = (uint*) fp[0]; // saved fp + } + + for (; i < N_CALLSTK; i++) { + pcs[i] = 0; + } +} + +void show_callstk (char *s) +{ + int i; + uint pcs[N_CALLSTK]; + + cprintf("%s\n", s); + + getcallerpcs(get_fp(), pcs); + + for (i = N_CALLSTK - 1; i >= 0; i--) { + cprintf("%d: 0x%x\n", i + 1, pcs[i]); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/arm.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,69 @@ +#ifndef ARM_INCLUDE +#define ARM_INCLUDE + +#include "device/versatile_pb.h" + +// trap frame: in ARM, there are seven modes. Among the 16 regular registers, +// r13 (sp), r14(lr), r15(pc) are banked in all modes. +// 1. In xv6_a, all kernel level activities (e.g., Syscall and IRQ) happens +// in the SVC mode. CPU is put in different modes by different events. We +// switch them to the SVC mode, by shoving the trapframe to the kernel stack. +// 2. during the context switched, the banked user space registers should also +// be saved/restored. +// +// Here is an example: +// 1. a user app issues a syscall (via SWI), its user-space registers are +// saved on its kernel stack, syscall is being served. +// 2. an interrupt happens, it preempted the syscall. the app's kernel-space +// registers are again saved on its stack. +// 3. interrupt service ended, and execution returns to the syscall. +// 4. kernel decides to reschedule (context switch), it saves the kernel states +// and switches to a new process (including user-space banked registers) +#ifndef __ASSEMBLER__ +struct trapframe { + uint sp_usr; // user mode sp + uint lr_usr; // user mode lr + uint r14_svc; // r14_svc (r14_svc == pc if SWI) + uint spsr; + uint r0; + uint r1; + uint r2; + uint r3; + uint r4; + uint r5; + uint r6; + uint r7; + uint r8; + uint r9; + uint r10; + uint r11; + uint r12; + uint pc; // (lr on entry) instruction to resume execution +}; +#endif + +// cpsr/spsr bits +#define NO_INT 0xc0 +#define DIS_INT 0x80 + +// ARM has 7 modes and banked registers +#define MODE_MASK 0x1f +#define USR_MODE 0x10 +#define FIQ_MODE 0x11 +#define IRQ_MODE 0x12 +#define SVC_MODE 0x13 +#define ABT_MODE 0x17 +#define UND_MODE 0x1b +#define SYS_MODE 0x1f + +// vector table +#define TRAP_RESET 0 +#define TRAP_UND 1 +#define TRAP_SWI 2 +#define TRAP_IABT 3 +#define TRAP_DABT 4 +#define TRAP_NA 5 +#define TRAP_IRQ 6 +#define TRAP_FIQ 7 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/asm.S Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,32 @@ +#include "arm.h" + +.text +.code 32 + +.global set_stk +.global get_fp + +# set the stack for IRQ mode, we can use r0-r3 for free +# input: r0 - #mode, r1 - stk +set_stk: + # switch to the IRQ mode + MRS r2, cpsr + BIC r2, r2, #MODE_MASK + ORR r2, r2, r0 + MSR cpsr_cxsf, r2 + + # set the stack pointer, sp register is banked, so we need to switch mode + MOV sp, r1 + + # switch back to the SVC mode + BIC r2, r2, #MODE_MASK + ORR r2, r2, #SVC_MODE + MSR cpsr_cxsf, r2 + + # return + bx lr + +# return the frame pointer for the current function +get_fp: + MOV r0, fp + bx lr \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/bio.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,143 @@ +// Buffer cache. +// +// The buffer cache is a linked list of buf structures holding +// cached copies of disk block contents. Caching disk blocks +// in memory reduces the number of disk reads and also provides +// a synchronization point for disk blocks used by multiple processes. +// +// Interface: +// * To get a buffer for a particular disk block, call bread. +// * After changing buffer data, call bwrite to write it to disk. +// * When done with the buffer, call brelse. +// * Do not use the buffer after calling brelse. +// * Only one process at a time can use a buffer, +// so do not keep them longer than necessary. +// +// The implementation uses three state flags internally: +// * B_BUSY: the block has been returned from bread +// and has not been passed back to brelse. +// * B_VALID: the buffer data has been read from the disk. +// * B_DIRTY: the buffer data has been modified +// and needs to be written to disk. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "spinlock.h" +#include "buf.h" + +struct { + struct spinlock lock; + struct buf buf[NBUF]; + + // Linked list of all buffers, through prev/next. + // head.next is most recently used. + struct buf head; +} bcache; + +void binit (void) +{ + struct buf *b; + + initlock(&bcache.lock, "bcache"); + + //PAGEBREAK! + // Create linked list of buffers + bcache.head.prev = &bcache.head; + bcache.head.next = &bcache.head; + + for (b = bcache.buf; b < bcache.buf + NBUF; b++) { + b->next = bcache.head.next; + b->prev = &bcache.head; + b->dev = -1; + bcache.head.next->prev = b; + bcache.head.next = b; + } +} + +// Look through buffer cache for sector on device dev. +// If not found, allocate fresh block. +// In either case, return B_BUSY buffer. +static struct buf* bget (uint dev, uint sector) +{ + struct buf *b; + + acquire(&bcache.lock); + + loop: + // Is the sector already cached? + for (b = bcache.head.next; b != &bcache.head; b = b->next) { + if (b->dev == dev && b->sector == sector) { + if (!(b->flags & B_BUSY)) { + b->flags |= B_BUSY; + release(&bcache.lock); + return b; + } + + sleep(b, &bcache.lock); + goto loop; + } + } + + // Not cached; recycle some non-busy and clean buffer. + for (b = bcache.head.prev; b != &bcache.head; b = b->prev) { + if ((b->flags & B_BUSY) == 0 && (b->flags & B_DIRTY) == 0) { + b->dev = dev; + b->sector = sector; + b->flags = B_BUSY; + release(&bcache.lock); + return b; + } + } + + panic("bget: no buffers"); +} + +// Return a B_BUSY buf with the contents of the indicated disk sector. +struct buf* bread (uint dev, uint sector) +{ + struct buf *b; + + b = bget(dev, sector); + + if (!(b->flags & B_VALID)) { + iderw(b); + } + + return b; +} + +// Write b's contents to disk. Must be B_BUSY. +void bwrite (struct buf *b) +{ + if ((b->flags & B_BUSY) == 0) { + panic("bwrite"); + } + + b->flags |= B_DIRTY; + iderw(b); +} + +// Release a B_BUSY buffer. +// Move to the head of the MRU list. +void brelse (struct buf *b) +{ + if ((b->flags & B_BUSY) == 0) { + panic("brelse"); + } + + acquire(&bcache.lock); + + b->next->prev = b->prev; + b->prev->next = b->next; + b->next = bcache.head.next; + b->prev = &bcache.head; + bcache.head.next->prev = b; + bcache.head.next = b; + + b->flags &= ~B_BUSY; + wakeup(b); + + release(&bcache.lock); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/buddy.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,337 @@ +// Buddy memory allocator +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "spinlock.h" +#include "arm.h" + + +// this file implement the buddy memory allocator. Each order divides +// the memory pool into equal-sized blocks (2^n). We use bitmap to record +// allocation status for each block. This allows for efficient merging +// when blocks are freed. We also use double-linked list to chain together +// free blocks (for each order), thus allowing fast allocation. There is +// about 8% overhead (maximum) for this structure. + +#define MAX_ORD 12 +#define MIN_ORD 6 +#define N_ORD (MAX_ORD - MIN_ORD +1) + +struct mark { + uint32 lnks; // double links (actually indexes) + uint32 bitmap; // bitmap, whether the block is available (1=available) +}; + +// lnks is a combination of previous link (index) and next link (index) +#define PRE_LNK(lnks) ((lnks) >> 16) +#define NEXT_LNK(lnks) ((lnks) & 0xFFFF) +#define LNKS(pre, next) (((pre) << 16) | ((next) & 0xFFFF)) +#define NIL ((uint16)0xFFFF) + +struct order { + uint32 head; // the first non-empty mark + uint32 offset; // the first mark +}; + +struct kmem { + struct spinlock lock; + uint start; // start of memory for marks + uint start_heap; // start of allocatable memory + uint end; + struct order orders[N_ORD]; // orders used for buddy systems +}; + +static struct kmem kmem; + +// coversion between block id to mark and memory address +static inline struct mark* get_mark (int order, int idx) +{ + return (struct mark*)kmem.start + (kmem.orders[order - MIN_ORD].offset + idx); +} + +static inline void* blkid2mem (int order, int blkid) +{ + return (void*)(kmem.start_heap + (1 << order) * blkid); +} + +static inline int mem2blkid (int order, void *mem) +{ + return ((uint)mem - kmem.start_heap) >> order; +} + +static inline int available (uint bitmap, int blk_id) +{ + return bitmap & (1 << (blk_id & 0x1F)); +} + +void kmem_init (void) +{ + initlock(&kmem.lock, "kmem"); +} + +void kmem_init2(void *vstart, void *vend) +{ + int i, j; + uint32 total, n; + uint len; + struct order *ord; + struct mark *mk; + + kmem.start = (uint)vstart; + kmem.end = (uint)vend; + len = kmem.end - kmem.start; + + // reserved memory at vstart for an array of marks (for all the orders) + n = (len >> (MAX_ORD + 5)) + 1; // estimated # of marks for max order + total = 0; + + for (i = N_ORD - 1; i >= 0; i--) { + ord = kmem.orders + i; + ord->offset = total; + ord->head = NIL; + + // set the bitmaps to mark all blocks not available + for (j = 0; j < n; j++) { + mk = get_mark(i + MIN_ORD, j); + mk->lnks = LNKS(NIL, NIL); + mk->bitmap = 0; + } + + total += n; + n <<= 1; // each order doubles required marks + } + + // add all available memory to the highest order bucket + kmem.start_heap = align_up(kmem.start + total * sizeof(*mk), 1 << MAX_ORD); + + for (i = kmem.start_heap; i < kmem.end; i += (1 << MAX_ORD)){ + kfree ((void*)i, MAX_ORD); + } +} + +// mark a block as unavailable +static void unmark_blk (int order, int blk_id) +{ + struct mark *mk, *p; + struct order *ord; + int prev, next; + + ord = &kmem.orders[order - MIN_ORD]; + mk = get_mark (order, blk_id >> 5); + + // clear the bit in the bitmap + if (!available(mk->bitmap, blk_id)) { + panic ("double alloc\n"); + } + + mk->bitmap &= ~(1 << (blk_id & 0x1F)); + + // if it's the last block in the bitmap, delete from the list + if (mk->bitmap == 0) { + blk_id >>= 5; + + prev = PRE_LNK(mk->lnks); + next = NEXT_LNK(mk->lnks); + + if (prev != NIL) { + p = get_mark(order, prev); + p->lnks = LNKS(PRE_LNK(p->lnks), next); + + } else if (ord->head == blk_id) { + // if we are the first in the link + ord->head = next; + } + + if (next != NIL) { + p = get_mark(order, next); + p->lnks = LNKS(prev, NEXT_LNK(p->lnks)); + } + + mk->lnks = LNKS(NIL, NIL); + } +} + +// mark a block as available +static void mark_blk (int order, int blk_id) +{ + struct mark *mk, *p; + struct order *ord; + int insert; + + ord = &kmem.orders[order - MIN_ORD]; + mk = get_mark (order, blk_id >> 5); + + // whether we need to insert it into the list + insert = (mk->bitmap == 0); + + // clear the bit map + if (available(mk->bitmap, blk_id)) { + panic ("double free\n"); + } + + mk->bitmap |= (1 << (blk_id & 0x1F)); + + // just insert it to the head, no need to keep the list ordered + if (insert) { + blk_id >>= 5; + mk->lnks = LNKS(NIL, ord->head); + + // fix the pre pointer of the next mark + if (ord->head != NIL) { + p = get_mark(order, ord->head); + p->lnks = LNKS(blk_id, NEXT_LNK(p->lnks)); + } + + ord->head = blk_id; + } +} + +// get a block +static void* get_blk (int order) +{ + struct mark *mk; + int blk_id; + int i; + struct order *ord; + + ord = &kmem.orders[order - MIN_ORD]; + mk = get_mark(order, ord->head); + + if (mk->bitmap == 0) { + panic ("empty mark in the list\n"); + } + + for (i = 0; i < 32; i++) { + if (mk->bitmap & (1 << i)) { + blk_id = ord->head * 32 + i; + unmark_blk(order, blk_id); + + return blkid2mem(order, blk_id); + } + } + + return NULL; +} + +void _kfree (void *mem, int order); + + +static void *_kmalloc (int order) +{ + struct order *ord; + uint8 *up; + + ord = &kmem.orders[order - MIN_ORD]; + up = NULL; + + if (ord->head != NIL) { + up = get_blk(order); + + } else if (order < MAX_ORD){ + // if currently no block available, try to split a parent + up = _kmalloc (order + 1); + + if (up != NULL) { + _kfree (up + (1 << order), order); + } + } + + return up; +} + +// allocate memory that has the size of (1 << order) +void *kmalloc (int order) +{ + uint8 *up; + + if ((order > MAX_ORD) || (order < MIN_ORD)) { + panic("kmalloc: order out of range\n"); + } + + acquire(&kmem.lock); + up = _kmalloc(order); + release(&kmem.lock); + + return up; +} + +void _kfree (void *mem, int order) +{ + int blk_id, buddy_id; + struct mark *mk; + + blk_id = mem2blkid(order, mem); + mk = get_mark(order, blk_id >> 5); + + if (available(mk->bitmap, blk_id)) { + panic ("kfree: double free"); + } + + buddy_id = blk_id ^ 0x0001; // blk_id and buddy_id differs in the last bit + // buddy must be in the same bit map + if (!available(mk->bitmap, buddy_id) || (order == MAX_ORD)) { + mark_blk(order, blk_id); + } else { + // our buddy is also free, merge it + unmark_blk (order, buddy_id); + _kfree (blkid2mem(order, blk_id & ~0x0001), order+1); + } +} + +// free kernel memory, we require order parameter here to avoid +// storing size info somewhere which might break the alignment +void kfree (void *mem, int order) +{ + if ((order > MAX_ORD) || (order < MIN_ORD) || (uint)mem & ((1<<order) -1)) { + panic("kfree: order out of range or memory unaligned\n"); + } + + acquire(&kmem.lock); + _kfree(mem, order); + release(&kmem.lock); +} + +// free a page +void free_page(void *v) +{ + kfree (v, PTE_SHIFT); +} + +// allocate a page +void* alloc_page (void) +{ + return kmalloc (PTE_SHIFT); +} + +// round up power of 2, then get the order +// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +int get_order (uint32 v) +{ + uint32 ord; + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + for (ord = 0; ord < 32; ord++) { + if (v & (1 << ord)) { + break; + } + } + + if (ord < MIN_ORD) { + ord = MIN_ORD; + } else if (ord > MAX_ORD) { + panic ("order too big!"); + } + + return ord; + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/buf.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,18 @@ +#ifndef INCLUDE_BUF_H +#define INCLUDE_BUF_H + +struct buf { + int flags; + uint dev; + uint sector; + struct buf *prev; // LRU cache list + struct buf *next; + struct buf *qnext; // disk queue + uchar data[512]; +}; + +#define B_BUSY 0x1 // buffer is locked by some process +#define B_VALID 0x2 // buffer has been read from disk +#define B_DIRTY 0x4 // buffer needs to be written to disk + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/connect.gdb Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,1 @@ +arm-none-eabi-gdb -x connect.gdb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,294 @@ +// Console input and output. +// Input is from the keyboard or serial port. +// Output is written to the screen and serial port. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "spinlock.h" +#include "fs.h" +#include "file.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" + +static void consputc (int); + +static int panicked = 0; + +static struct { + struct spinlock lock; + int locking; +} cons; + +static void printint (int xx, int base, int sign) +{ + static char digits[] = "0123456789abcdef"; + char buf[16]; + int i; + uint x; + + if (sign && (sign = xx < 0)) { + x = -xx; + } else { + x = xx; + } + + i = 0; + + do { + buf[i++] = digits[x % base]; + } while ((x /= base) != 0); + + if (sign) { + buf[i++] = '-'; + } + + while (--i >= 0) { + consputc(buf[i]); + } +} +//PAGEBREAK: 50 + +// Print to the console. only understands %d, %x, %p, %s. +void cprintf (char *fmt, ...) +{ + int i, c, locking; + uint *argp; + char *s; + + locking = cons.locking; + + if (locking) { + acquire(&cons.lock); + } + + if (fmt == 0) { + panic("null fmt"); + } + + argp = (uint*) (void*) (&fmt + 1); + + for (i = 0; (c = fmt[i] & 0xff) != 0; i++) { + if (c != '%') { + consputc(c); + continue; + } + + c = fmt[++i] & 0xff; + + if (c == 0) { + break; + } + + switch (c) { + case 'd': + printint(*argp++, 10, 1); + break; + + case 'x': + case 'p': + printint(*argp++, 16, 0); + break; + + case 's': + if ((s = (char*) *argp++) == 0) { + s = "(null)"; + } + + for (; *s; s++) { + consputc(*s); + } + break; + + case '%': + consputc('%'); + break; + + default: + // Print unknown % sequence to draw attention. + consputc('%'); + consputc(c); + break; + } + } + + if (locking) { + release(&cons.lock); + } +} + +void panic (char *s) +{ + cli(); + + cons.locking = 0; + + cprintf("cpu%d: panic: ", cpu->id); + + show_callstk(s); + panicked = 1; // freeze other CPU + + while (1) + ; +} + +//PAGEBREAK: 50 +#define BACKSPACE 0x100 +#define CRTPORT 0x3d4 + +void consputc (int c) +{ + if (panicked) { + cli(); + while (1) + ; + } + + if (c == BACKSPACE) { + uartputc('\b'); + uartputc(' '); + uartputc('\b'); + } else { + uartputc(c); + } + + // cgaputc(c); +} + +#define INPUT_BUF 512 +struct { + struct spinlock lock; + char buf[INPUT_BUF]; + uint r; // Read index + uint w; // Write index + uint e; // Edit index +} input; + +#define C(x) ((x)-'@') // Control-x +void consoleintr (int (*getc) (void)) +{ + int c; + + acquire(&input.lock); + + while ((c = getc()) >= 0) { + switch (c) { + case C('P'): // Process listing. + procdump(); + break; + + case C('U'): // Kill line. + while ((input.e != input.w) && (input.buf[(input.e - 1) % INPUT_BUF] != '\n')) { + input.e--; + consputc(BACKSPACE); + } + + break; + + case C('H'): + case '\x7f': // Backspace + if (input.e != input.w) { + input.e--; + consputc(BACKSPACE); + } + + break; + + default: + if ((c != 0) && (input.e - input.r < INPUT_BUF)) { + c = (c == '\r') ? '\n' : c; + + input.buf[input.e++ % INPUT_BUF] = c; + consputc(c); + + if (c == '\n' || c == C('D') || input.e == input.r + INPUT_BUF) { + input.w = input.e; + wakeup(&input.r); + } + } + + break; + } + } + + release(&input.lock); +} + +int consoleread (struct inode *ip, char *dst, int n) +{ + uint target; + int c; + + iunlock(ip); + + target = n; + acquire(&input.lock); + + while (n > 0) { + while (input.r == input.w) { + if (proc->killed) { + release(&input.lock); + ilock(ip); + return -1; + } + + sleep(&input.r, &input.lock); + } + + c = input.buf[input.r++ % INPUT_BUF]; + + if (c == C('D')) { // EOF + if (n < target) { + // Save ^D for next time, to make sure + // caller gets a 0-byte result. + input.r--; + } + + break; + } + + *dst++ = c; + --n; + + if (c == '\n') { + break; + } + } + + release(&input.lock); + ilock(ip); + + return target - n; +} + +int consolewrite (struct inode *ip, char *buf, int n) +{ + int i; + + iunlock(ip); + + acquire(&cons.lock); + + for (i = 0; i < n; i++) { + consputc(buf[i] & 0xff); + } + + release(&cons.lock); + + ilock(ip); + + return n; +} + +void consoleinit (void) +{ + initlock(&cons.lock, "console"); + initlock(&input.lock, "input"); + + devsw[CONSOLE].write = consolewrite; + devsw[CONSOLE].read = consoleread; + + cons.locking = 1; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/debug.sh Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,2 @@ +#!/bin/sh +arm-none-eabi-gdb -x connect.gdb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/defs.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,206 @@ +// +// a definition of data structure/methods +// +#ifndef INCLUDE_DEFS_H +#define INCLUDE_DEFS_H + +#define UMAX(a, b) ((uint)(a) > (uint)(b) ? (uint)(a):(uint)(b)) +#define UMIN(a, b) ((uint)(a) > (uint)(b) ? (uint)(b):(uint)(a)) + +// number of elements in fixed-size array +#define NELEM(x) (sizeof(x)/sizeof((x)[0])) + +struct buf; +struct context; +struct file; +struct inode; +struct pipe; +struct proc; +struct spinlock; +struct stat; +struct superblock; +struct trapframe; + +typedef uint32 pte_t; +typedef uint32 pde_t; +extern uint32 _kernel_pgtbl; +typedef void (*ISR) (struct trapframe *tf, int n); + +// arm.c +void set_stk(uint mode, uint addr); +void cli (void); +void sti (void); +uint spsr_usr(); +int int_enabled(); +void pushcli(void); +void popcli(void); +void getcallerpcs(void *, uint*); +void* get_fp (void); +void show_callstk (char *); + + +// bio.c +void binit(void); +struct buf* bread(uint, uint); +void brelse(struct buf*); +void bwrite(struct buf*); + +// buddy.c +void kmem_init (void); +void kmem_init2(void *vstart, void *vend); +void* kmalloc (int order); +void kfree (void *mem, int order); +void free_page(void *v); +void* alloc_page (void); +void kmem_test_b (void); +int get_order (uint32 v); + +// console.c +void consoleinit(void); +void cprintf(char*, ...); +void consoleintr(int(*)(void)); +void panic(char*) __attribute__((noreturn)); + +// exec.c +int exec(char*, char**); + +// file.c +struct file* filealloc(void); +void fileclose(struct file*); +struct file* filedup(struct file*); +void fileinit(void); +int fileread(struct file*, char*, int n); +int filestat(struct file*, struct stat*); +int filewrite(struct file*, char*, int n); + +// fs.c +void readsb(int dev, struct superblock *sb); +int dirlink(struct inode*, char*, uint); +struct inode* dirlookup(struct inode*, char*, uint*); +struct inode* ialloc(uint, short); +struct inode* idup(struct inode*); +void iinit(void); +void ilock(struct inode*); +void iput(struct inode*); +void iunlock(struct inode*); +void iunlockput(struct inode*); +void iupdate(struct inode*); +int namecmp(const char*, const char*); +struct inode* namei(char*); +struct inode* nameiparent(char*, char*); +int readi(struct inode*, char*, uint, uint); +void stati(struct inode*, struct stat*); +int writei(struct inode*, char*, uint, uint); + +// ide.c +void ideinit(void); +void iderw(struct buf*); + +// kalloc.c +/*char* kalloc(void); +void kfree(char*); +void kinit1(void*, void*); +void kinit2(void*, void*); +void kmem_init (void);*/ + +// log.c +void initlog(void); +void log_write(struct buf*); +void begin_trans(); +void commit_trans(); + +// picirq.c +void pic_enable(int, ISR); +void pic_init(void*); +void pic_dispatch (struct trapframe *tp); + +// pipe.c +int pipealloc(struct file**, struct file**); +void pipeclose(struct pipe*, int); +int piperead(struct pipe*, char*, int); +int pipewrite(struct pipe*, char*, int); + +//PAGEBREAK: 16 +// proc.c +struct proc* copyproc(struct proc*); +void exit(void); +int fork(void); +int growproc(int); +int kill(int); +void pinit(void); +void procdump(void); +void scheduler(void) __attribute__((noreturn)); +void sched(void); +void sleep(void*, struct spinlock*); +void userinit(void); +int wait(void); +void wakeup(void*); +void yield(void); + +// swtch.S +void swtch(struct context**, struct context*); + +// spinlock.c +void acquire(struct spinlock*); +int holding(struct spinlock*); +void initlock(struct spinlock*, char*); +void release(struct spinlock*); + +// string.c +int memcmp(const void*, const void*, uint); +void* memmove(void*, const void*, uint); +void* memset(void*, int, uint); +char* safestrcpy(char*, const char*, int); +int strlen(const char*); +int strncmp(const char*, const char*, uint); +char* strncpy(char*, const char*, int); + +// syscall.c +int argint(int, int*); +int argptr(int, char**, int); +int argstr(int, char**); +int fetchint(uint, int*); +int fetchstr(uint, char**); +void syscall(void); + +// timer.c +void timer_init(int hz); +extern struct spinlock tickslock; + +// trap.c +extern uint ticks; +void trap_init(void); +void dump_trapframe (struct trapframe *tf); + +// trap_asm.S +void trap_reset(void); +void trap_und(void); +void trap_swi(void); +void trap_iabort(void); +void trap_dabort(void); +void trap_na(void); +void trap_irq(void); +void trap_fiq(void); + +// uart.c +void uart_init(void*); +void uartputc(int); +int uartgetc(void); +void micro_delay(int us); +void uart_enable_rx(); + +// vm.c +int allocuvm(pde_t*, uint, uint); +int deallocuvm(pde_t*, uint, uint); +void freevm(pde_t*); +void inituvm(pde_t*, char*, uint); +int loaduvm(pde_t*, char*, struct inode*, uint, uint); +pde_t* copyuvm(pde_t*, uint); +void switchuvm(struct proc*); +int copyout(pde_t*, uint, void*, uint); +void clearpteu(pde_t *pgdir, char *uva); +void* kpt_alloc(void); +void init_vmm (void); +void kpt_freerange (uint32 low, uint32 hi); +void paging_init (uint phy_low, uint phy_hi); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/device/picirq.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,96 @@ +// Support of ARM PrimeCell Vectored Interrrupt Controller (PL190) +#include "types.h" +#include "defs.h" +#include "param.h" +#include "arm.h" +#include "memlayout.h" +#include "mmu.h" + +// PL190 supports the vectored interrupts and non-vectored interrupts. +// In this code, we use non-vected interrupts (aka. simple interrupt). +// The flow to handle simple interrupts is as the following: +// 1. an interrupt (IRQ) occurs, trap.c branches to our IRQ handler +// 2. read the VICIRQStatus register, for each source generating +// the interrrupt: +// 2.1 locate the correct ISR +// 2.2 execute the ISR +// 2.3 clear the interrupt +// 3 return to trap.c, which will resume interrupted routines +// Note: must not read VICVectorAddr + + +// define the register offsets (in the unit of 4 bytes). The base address +// of the VIC depends on the board +static volatile uint* vic_base; + +#define VIC_IRQSTATUS 0 // status of interrupts after masking by ENABLE and SEL +#define VIC_FIQSTATUS 1 // status of interrupts after masking +#define VIC_RAWINTR 2 // status of interrupts before masking +#define VIC_INTSEL 3 // interrupt select (IRQ or FIQ), by default IRQ +#define VIC_INTENABLE 4 // enable interrupts (1 - enabled, 0 - disabled) +#define VIC_INTCLEAR 5 // clear bits in ENABLE register (1 - clear it) +#define VIC_PROTECTIOIN 8 // who can access: user or privileged + +#define NUM_INTSRC 32 // numbers of interrupt source supported + +static ISR isrs[NUM_INTSRC]; + +static void default_isr (struct trapframe *tf, int n) +{ + cprintf ("unhandled interrupt: %d\n", n); +} + +// initialize the PL190 VIC +void pic_init (void * base) +{ + int i; + + // set the base for the controller and disable all interrupts + vic_base = base; + vic_base[VIC_INTCLEAR] = 0xFFFFFFFF; + + for (i = 0; i < NUM_INTSRC; i++) { + isrs[i] = default_isr; + } +} + +// enable an interrupt (with the ISR) +void pic_enable (int n, ISR isr) +{ + if ((n<0) || (n >= NUM_INTSRC)) { + panic ("invalid interrupt source"); + } + + // write 1 bit enable the interrupt, 0 bit has no effect + isrs[n] = isr; + vic_base[VIC_INTENABLE] = (1 << n); +} + +// disable an interrupt +void pic_disable (int n) +{ + if ((n<0) || (n >= NUM_INTSRC)) { + panic ("invalid interrupt source"); + } + + vic_base[VIC_INTCLEAR] = (1 << n); + isrs[n] = default_isr; +} + +// dispatch the interrupt +void pic_dispatch (struct trapframe *tp) +{ + uint intstatus; + int i; + + intstatus = vic_base[VIC_IRQSTATUS]; + + for (i = 0; i < NUM_INTSRC; i++) { + if (intstatus & (1<<i)) { + isrs[i](tp, i); + } + } + + intstatus = vic_base[VIC_IRQSTATUS]; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/device/timer.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,77 @@ +// ARM dual-timer module support (SP804) +#include "types.h" +#include "param.h" +#include "arm.h" +#include "mmu.h" +#include "defs.h" +#include "memlayout.h" +#include "spinlock.h" + +// A SP804 has two timers, we only use the first one, and as perodic timer + +// define registers (in units of 4-bytes) +#define TIMER_LOAD 0 // load register, for perodic timer +#define TIMER_CURVAL 1 // current value of the counter +#define TIMER_CONTROL 2 // control register +#define TIMER_INTCLR 3 // clear (ack) the interrupt (any write clear it) +#define TIMER_MIS 5 // masked interrupt status + +// control register bit definitions +#define TIMER_ONESHOT 0x01 // wrap or one shot +#define TIMER_32BIT 0x02 // 16-bit/32-bit counter +#define TIMER_INTEN 0x20 // enable/disable interrupt +#define TIMER_PERIODIC 0x40 // enable periodic mode +#define TIMER_EN 0x80 // enable the timer + +void isr_timer (struct trapframe *tp, int irq_idx); + +struct spinlock tickslock; +uint ticks; + +// acknowledge the timer, write any value to TIMER_INTCLR should do +static void ack_timer () +{ + volatile uint * timer0 = P2V(TIMER0); + timer0[TIMER_INTCLR] = 1; +} + +// initialize the timer: perodical and interrupt based +void timer_init(int hz) +{ + volatile uint * timer0 = P2V(TIMER0); + + initlock(&tickslock, "time"); + + timer0[TIMER_LOAD] = CLK_HZ / hz; + timer0[TIMER_CONTROL] = TIMER_EN|TIMER_PERIODIC|TIMER_32BIT|TIMER_INTEN; + + pic_enable (PIC_TIMER01, isr_timer); +} + +// interrupt service routine for the timer +void isr_timer (struct trapframe *tp, int irq_idx) +{ + acquire(&tickslock); + ticks++; + wakeup(&ticks); + release(&tickslock); + ack_timer(); +} + +// a short delay, use timer 1 as the source +void micro_delay (int us) +{ + volatile uint * timer1 = P2V(TIMER1); + + // load the initial value to timer1, and configure it to be freerun + timer1[TIMER_CONTROL] = TIMER_EN | TIMER_32BIT; + timer1[TIMER_LOAD] = us; + + // the register will wrap to 0xFFFFFFFF after decrement to 0 + while ((int)timer1[TIMER_CURVAL] > 0) { + + } + + // disable timer + timer1[TIMER_CONTROL] = 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/device/uart.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,87 @@ +// driver for ARM PrimeCell UART (PL011) +#include "types.h" +#include "defs.h" +#include "param.h" +#include "arm.h" +#include "memlayout.h" + +static volatile uint *uart_base; +void isr_uart (struct trapframe *tf, int idx); + +#define UART_DR 0 // data register +#define UART_RSR 1 // receive status register/error clear register +#define UART_FR 6 // flag register +#define UART_IBRD 9 // integer baud rate register +#define UART_FBRD 10 // Fractional baud rate register +#define UART_LCR 11 // line control register +#define UART_CR 12 // control register +#define UART_IMSC 14 // interrupt mask set/clear register +#define UART_MIS 16 // masked interrupt status register +#define UART_ICR 17 // interrupt clear register +// bits in registers +#define UARTFR_TXFF (1 << 5) // tramit FIFO full +#define UARTFR_RXFE (1 << 4) // receive FIFO empty +#define UARTCR_RXE (1 << 9) // enable receive +#define UARTCR_TXE (1 << 8) // enable transmit +#define UARTCR_EN (1 << 0) // enable UART +#define UARTLCR_FEN (1 << 4) // enable FIFO +#define UART_RXI (1 << 4) // receive interrupt +#define UART_TXI (1 << 5) // transmit interrupt +#define UART_BITRATE 19200 + +// enable uart +void uart_init (void *addr) +{ + uint left; + + uart_base = addr; + + // set the bit rate: integer/fractional baud rate registers + uart_base[UART_IBRD] = UART_CLK / (16 * UART_BITRATE); + + left = UART_CLK % (16 * UART_BITRATE); + uart_base[UART_FBRD] = (left * 4 + UART_BITRATE / 2) / UART_BITRATE; + + // enable trasmit and receive + uart_base[UART_CR] |= (UARTCR_EN | UARTCR_RXE | UARTCR_TXE); + + // enable FIFO + uart_base[UART_LCR] |= UARTLCR_FEN; +} + +// enable the receive (interrupt) for uart (after PIC has initialized) +void uart_enable_rx () +{ + uart_base[UART_IMSC] = UART_RXI; + pic_enable(PIC_UART0, isr_uart); +} + +void uartputc (int c) +{ + // wait a short period if the transmit FIFO is full + while (uart_base[UART_FR] & UARTFR_TXFF) { + micro_delay(10); + } + + uart_base[UART_DR] = c; +} + +//poll the UART for data +int uartgetc (void) +{ + if (uart_base[UART_FR] & UARTFR_RXFE) { + return -1; + } + + return uart_base[UART_DR]; +} + +void isr_uart (struct trapframe *tf, int idx) +{ + if (uart_base[UART_MIS] & UART_RXI) { + consoleintr(uartgetc); + } + + // clear the interrupt + uart_base[UART_ICR] = UART_RXI | UART_TXI; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/device/versatile_pb.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,35 @@ +// +// Board specific information for the VersatilePB board +// +#ifndef VERSATILEPB +#define VERSATILEPB + + +// the VerstatilePB board can support up to 256MB memory. +// but we assume it has 128MB instead. During boot, the lower +// 64MB memory is mapped to the flash, needs to be remapped +// the the SDRAM. We skip this for QEMU +#define PHYSTOP 0x08000000 +#define BSP_MEMREMAP 0x04000000 + +#define DEVBASE 0x10000000 +#define DEV_MEM_SZ 0x08000000 +#define VEC_TBL 0xFFFF0000 + + +#define STACK_FILL 0xdeadbeef + +#define UART0 0x101f1000 +#define UART_CLK 24000000 // Clock rate for UART + +#define TIMER0 0x101E2000 +#define TIMER1 0x101E2020 +#define CLK_HZ 1000000 // the clock is 1MHZ + +#define VIC_BASE 0x10140000 +#define PIC_TIMER01 4 +#define PIC_TIMER23 5 +#define PIC_UART0 12 +#define PIC_GRAPHIC 19 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/elf.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,42 @@ +// Format of an ELF executable file + +#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian + +// File header +struct elfhdr { + uint magic; // must equal ELF_MAGIC + uchar elf[12]; + ushort type; + ushort machine; + uint version; + uint entry; + uint phoff; + uint shoff; + uint flags; + ushort ehsize; + ushort phentsize; + ushort phnum; + ushort shentsize; + ushort shnum; + ushort shstrndx; +}; + +// Program section header +struct proghdr { + uint type; + uint off; + uint vaddr; + uint paddr; + uint filesz; + uint memsz; + uint flags; + uint align; +}; + +// Values for Proghdr type +#define ELF_PROG_LOAD 1 + +// Flag bits for Proghdr flags +#define ELF_PROG_FLAG_EXEC 1 +#define ELF_PROG_FLAG_WRITE 2 +#define ELF_PROG_FLAG_READ 4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/entry.S Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,33 @@ +#include "arm.h" +#include "memlayout.h" + +.text +.code 32 + +.global _start + +_start: + # clear the entry bss section, the svc stack, and kernel page table + LDR r1, =edata_entry + LDR r2, =end_entry + MOV r3, #0x00 + +1: + CMP r1, r2 + STMLTIA r1!, {r3} + BLT 1b + + # initialize stack pointers for svc modes + MSR CPSR_cxsf, #(SVC_MODE|NO_INT) + LDR sp, =svc_stktop + + BL start + B . + +# during startup, kernel stack uses user address, now switch it to kernel addr +.global jump_stack +jump_stack: + MOV r0, sp + ADD r0, r0, #KERNBASE + MOV sp, r0 + MOV pc, lr \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/exec.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,143 @@ +#include "types.h" +#include "param.h" +#include "defs.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "elf.h" +#include "arm.h" + +// load a user program for execution +int exec (char *path, char **argv) +{ + struct elfhdr elf; + struct inode *ip; + struct proghdr ph; + pde_t *pgdir; + pde_t *oldpgdir; + char *s; + char *last; + int i; + int off; + uint argc; + uint sz; + uint sp; + uint ustack[3 + MAXARG + 1]; + + if ((ip = namei(path)) == 0) { + return -1; + } + + ilock(ip); + + // Check ELF header + if (readi(ip, (char*) &elf, 0, sizeof(elf)) < sizeof(elf)) { + goto bad; + } + + if (elf.magic != ELF_MAGIC) { + goto bad; + } + + pgdir = 0; + + if ((pgdir = kpt_alloc()) == 0) { + goto bad; + } + + // Load program into memory. + sz = 0; + + for (i = 0, off = elf.phoff; i < elf.phnum; i++, off += sizeof(ph)) { + if (readi(ip, (char*) &ph, off, sizeof(ph)) != sizeof(ph)) { + goto bad; + } + + if (ph.type != ELF_PROG_LOAD) { + continue; + } + + if (ph.memsz < ph.filesz) { + goto bad; + } + + if ((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0) { + goto bad; + } + + if (loaduvm(pgdir, (char*) ph.vaddr, ip, ph.off, ph.filesz) < 0) { + goto bad; + } + } + + iunlockput(ip); + ip = 0; + + // Allocate two pages at the next page boundary. + // Make the first inaccessible. Use the second as the user stack. + sz = align_up (sz, PTE_SZ); + + if ((sz = allocuvm(pgdir, sz, sz + 2 * PTE_SZ)) == 0) { + goto bad; + } + + clearpteu(pgdir, (char*) (sz - 2 * PTE_SZ)); + + sp = sz; + + // Push argument strings, prepare rest of stack in ustack. + for (argc = 0; argv[argc]; argc++) { + if (argc >= MAXARG) { + goto bad; + } + + sp = (sp - (strlen(argv[argc]) + 1)) & ~3; + + if (copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) { + goto bad; + } + + ustack[argc] = sp; + } + + ustack[argc] = 0; + + // in ARM, parameters are passed in r0 and r1 + proc->tf->r0 = argc; + proc->tf->r1 = sp - (argc + 1) * 4; + + sp -= (argc + 1) * 4; + + if (copyout(pgdir, sp, ustack, (argc + 1) * 4) < 0) { + goto bad; + } + + // Save program name for debugging. + for (last = s = path; *s; s++) { + if (*s == '/') { + last = s + 1; + } + } + + safestrcpy(proc->name, last, sizeof(proc->name)); + + // Commit to the user image. + oldpgdir = proc->pgdir; + proc->pgdir = pgdir; + proc->sz = sz; + proc->tf->pc = elf.entry; + proc->tf->sp_usr = sp; + + switchuvm(proc); + freevm(oldpgdir); + return 0; + + bad: if (pgdir) { + freevm(pgdir); + } + + if (ip) { + iunlockput(ip); + } + return -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fcntl.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,4 @@ +#define O_RDONLY 0x000 +#define O_WRONLY 0x001 +#define O_RDWR 0x002 +#define O_CREATE 0x200
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/file.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,189 @@ +// +// File descriptors +// + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "fs.h" +#include "file.h" +#include "spinlock.h" + +struct devsw devsw[NDEV]; +struct { + struct spinlock lock; + struct file file[NFILE]; +} ftable; + +void fileinit (void) +{ + initlock(&ftable.lock, "ftable"); +} + +// Allocate a file structure. +struct file* filealloc (void) +{ + struct file *f; + + acquire(&ftable.lock); + + for (f = ftable.file; f < ftable.file + NFILE; f++) { + if (f->ref == 0) { + f->ref = 1; + release(&ftable.lock); + return f; + } + } + + release(&ftable.lock); + return 0; +} + +// Increment ref count for file f. +struct file* filedup (struct file *f) +{ + acquire(&ftable.lock); + + if (f->ref < 1) { + panic("filedup"); + } + + f->ref++; + release(&ftable.lock); + return f; +} + +// Close file f. (Decrement ref count, close when reaches 0.) +void fileclose (struct file *f) +{ + struct file ff; + + acquire(&ftable.lock); + + if (f->ref < 1) { + panic("fileclose"); + } + + if (--f->ref > 0) { + release(&ftable.lock); + return; + } + + ff = *f; + f->ref = 0; + f->type = FD_NONE; + release(&ftable.lock); + + if (ff.type == FD_PIPE) { + pipeclose(ff.pipe, ff.writable); + + } else if (ff.type == FD_INODE) { + begin_trans(); + iput(ff.ip); + commit_trans(); + } +} + +// Get metadata about file f. +int filestat (struct file *f, struct stat *st) +{ + if (f->type == FD_INODE) { + ilock(f->ip); + stati(f->ip, st); + iunlock(f->ip); + + return 0; + } + + return -1; +} + +// Read from file f. +int fileread (struct file *f, char *addr, int n) +{ + int r; + + if (f->readable == 0) { + return -1; + } + + if (f->type == FD_PIPE) { + return piperead(f->pipe, addr, n); + } + + if (f->type == FD_INODE) { + ilock(f->ip); + + if ((r = readi(f->ip, addr, f->off, n)) > 0) { + f->off += r; + } + + iunlock(f->ip); + + return r; + } + + panic("fileread"); +} + +//PAGEBREAK! +// Write to file f. +int filewrite (struct file *f, char *addr, int n) +{ + int r; + int i; + int max; + int n1; + + if (f->writable == 0) { + return -1; + } + + if (f->type == FD_PIPE) { + return pipewrite(f->pipe, addr, n); + } + + if (f->type == FD_INODE) { + // write a few blocks at a time to avoid exceeding + // the maximum log transaction size, including + // i-node, indirect block, allocation blocks, + // and 2 blocks of slop for non-aligned writes. + // this really belongs lower down, since writei() + // might be writing a device like the console. + max = ((LOGSIZE - 1 - 1 - 2) / 2) * 512; + i = 0; + + while (i < n) { + n1 = n - i; + + if (n1 > max) { + n1 = max; + } + + begin_trans(); + ilock(f->ip); + + if ((r = writei(f->ip, addr + i, f->off, n1)) > 0) { + f->off += r; + } + + iunlock(f->ip); + commit_trans(); + + if (r < 0) { + break; + } + + if (r != n1) { + panic("short filewrite"); + } + + i += r; + } + + return i == n ? n : -1; + } + + panic("filewrite"); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/file.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,38 @@ +struct file { + enum { FD_NONE, FD_PIPE, FD_INODE } type; + int ref; // reference count + char readable; + char writable; + struct pipe *pipe; + struct inode *ip; + uint off; +}; + + +// in-memory copy of an inode +struct inode { + uint dev; // Device number + uint inum; // Inode number + int ref; // Reference count + int flags; // I_BUSY, I_VALID + + short type; // copy of disk inode + short major; + short minor; + short nlink; + uint size; + uint addrs[NDIRECT+1]; +}; +#define I_BUSY 0x1 +#define I_VALID 0x2 + +// table mapping major device number to +// device functions +struct devsw { + int (*read) (struct inode*, char*, int); + int (*write)(struct inode*, char*, int); +}; + +extern struct devsw devsw[]; + +#define CONSOLE 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fs.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,707 @@ +// File system implementation. Five layers: +// + Blocks: allocator for raw disk blocks. +// + Log: crash recovery for multi-step updates. +// + Files: inode allocator, reading, writing, metadata. +// + Directories: inode with special contents (list of other inodes!) +// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming. +// +// This file contains the low-level file system manipulation +// routines. The (higher-level) system call implementations +// are in sysfile.c. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "stat.h" +#include "mmu.h" +#include "proc.h" +#include "spinlock.h" +#include "buf.h" +#include "fs.h" +#include "file.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) +static void itrunc (struct inode*); + +// Read the super block. +void readsb (int dev, struct superblock *sb) +{ + struct buf *bp; + + bp = bread(dev, 1); + memmove(sb, bp->data, sizeof(*sb)); + brelse(bp); +} + +// Zero a block. +static void bzero (int dev, int bno) +{ + struct buf *bp; + + bp = bread(dev, bno); + memset(bp->data, 0, BSIZE); + log_write(bp); + brelse(bp); +} + +// Blocks. + +// Allocate a zeroed disk block. +static uint balloc (uint dev) +{ + int b, bi, m; + struct buf *bp; + struct superblock sb; + + bp = 0; + readsb(dev, &sb); + + for (b = 0; b < sb.size; b += BPB) { + bp = bread(dev, BBLOCK(b, sb.ninodes)); + + for (bi = 0; bi < BPB && b + bi < sb.size; bi++) { + m = 1 << (bi % 8); + + if ((bp->data[bi / 8] & m) == 0) { // Is block free? + bp->data[bi / 8] |= m; // Mark block in use. + log_write(bp); + brelse(bp); + bzero(dev, b + bi); + return b + bi; + } + } + + brelse(bp); + } + + panic("balloc: out of blocks"); +} + +// Free a disk block. +static void bfree (int dev, uint b) +{ + struct buf *bp; + struct superblock sb; + int bi, m; + + readsb(dev, &sb); + bp = bread(dev, BBLOCK(b, sb.ninodes)); + bi = b % BPB; + m = 1 << (bi % 8); + + if ((bp->data[bi / 8] & m) == 0) { + panic("freeing free block"); + } + + bp->data[bi / 8] &= ~m; + log_write(bp); + brelse(bp); +} + +// Inodes. +// +// An inode describes a single unnamed file. +// The inode disk structure holds metadata: the file's type, +// its size, the number of links referring to it, and the +// list of blocks holding the file's content. +// +// The inodes are laid out sequentially on disk immediately after +// the superblock. Each inode has a number, indicating its +// position on the disk. +// +// The kernel keeps a cache of in-use inodes in memory +// to provide a place for synchronizing access +// to inodes used by multiple processes. The cached +// inodes include book-keeping information that is +// not stored on disk: ip->ref and ip->flags. +// +// An inode and its in-memory represtative go through a +// sequence of states before they can be used by the +// rest of the file system code. +// +// * Allocation: an inode is allocated if its type (on disk) +// is non-zero. ialloc() allocates, iput() frees if +// the link count has fallen to zero. +// +// * Referencing in cache: an entry in the inode cache +// is free if ip->ref is zero. Otherwise ip->ref tracks +// the number of in-memory pointers to the entry (open +// files and current directories). iget() to find or +// create a cache entry and increment its ref, iput() +// to decrement ref. +// +// * Valid: the information (type, size, &c) in an inode +// cache entry is only correct when the I_VALID bit +// is set in ip->flags. ilock() reads the inode from +// the disk and sets I_VALID, while iput() clears +// I_VALID if ip->ref has fallen to zero. +// +// * Locked: file system code may only examine and modify +// the information in an inode and its content if it +// has first locked the inode. The I_BUSY flag indicates +// that the inode is locked. ilock() sets I_BUSY, +// while iunlock clears it. +// +// Thus a typical sequence is: +// ip = iget(dev, inum) +// ilock(ip) +// ... examine and modify ip->xxx ... +// iunlock(ip) +// iput(ip) +// +// ilock() is separate from iget() so that system calls can +// get a long-term reference to an inode (as for an open file) +// and only lock it for short periods (e.g., in read()). +// The separation also helps avoid deadlock and races during +// pathname lookup. iget() increments ip->ref so that the inode +// stays cached and pointers to it remain valid. +// +// Many internal file system functions expect the caller to +// have locked the inodes involved; this lets callers create +// multi-step atomic operations. + +struct { + struct spinlock lock; + struct inode inode[NINODE]; +} icache; + +void iinit (void) +{ + initlock(&icache.lock, "icache"); +} + +static struct inode* iget (uint dev, uint inum); + +//PAGEBREAK! +// Allocate a new inode with the given type on device dev. +// A free inode has a type of zero. +struct inode* ialloc (uint dev, short type) +{ + int inum; + struct buf *bp; + struct dinode *dip; + struct superblock sb; + + readsb(dev, &sb); + + for (inum = 1; inum < sb.ninodes; inum++) { + bp = bread(dev, IBLOCK(inum)); + dip = (struct dinode*) bp->data + inum % IPB; + + if (dip->type == 0) { // a free inode + memset(dip, 0, sizeof(*dip)); + dip->type = type; + log_write(bp); // mark it allocated on the disk + brelse(bp); + return iget(dev, inum); + } + + brelse(bp); + } + + panic("ialloc: no inodes"); +} + +// Copy a modified in-memory inode to disk. +void iupdate (struct inode *ip) +{ + struct buf *bp; + struct dinode *dip; + + bp = bread(ip->dev, IBLOCK(ip->inum)); + + dip = (struct dinode*) bp->data + ip->inum % IPB; + dip->type = ip->type; + dip->major = ip->major; + dip->minor = ip->minor; + dip->nlink = ip->nlink; + dip->size = ip->size; + + memmove(dip->addrs, ip->addrs, sizeof(ip->addrs)); + log_write(bp); + brelse(bp); +} + +// Find the inode with number inum on device dev +// and return the in-memory copy. Does not lock +// the inode and does not read it from disk. +static struct inode* iget (uint dev, uint inum) +{ + struct inode *ip, *empty; + + acquire(&icache.lock); + + // Is the inode already cached? + empty = 0; + + for (ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++) { + if (ip->ref > 0 && ip->dev == dev && ip->inum == inum) { + ip->ref++; + release(&icache.lock); + return ip; + } + + if (empty == 0 && ip->ref == 0) { // Remember empty slot. + empty = ip; + } + } + + // Recycle an inode cache entry. + if (empty == 0) { + panic("iget: no inodes"); + } + + ip = empty; + ip->dev = dev; + ip->inum = inum; + ip->ref = 1; + ip->flags = 0; + release(&icache.lock); + + return ip; +} + +// Increment reference count for ip. +// Returns ip to enable ip = idup(ip1) idiom. +struct inode* idup (struct inode *ip) +{ + acquire(&icache.lock); + ip->ref++; + release(&icache.lock); + return ip; +} + +// Lock the given inode. +// Reads the inode from disk if necessary. +void ilock (struct inode *ip) +{ + struct buf *bp; + struct dinode *dip; + + if (ip == 0 || ip->ref < 1) { + panic("ilock"); + } + + acquire(&icache.lock); + while (ip->flags & I_BUSY) { + sleep(ip, &icache.lock); + } + + ip->flags |= I_BUSY; + release(&icache.lock); + + if (!(ip->flags & I_VALID)) { + bp = bread(ip->dev, IBLOCK(ip->inum)); + + dip = (struct dinode*) bp->data + ip->inum % IPB; + ip->type = dip->type; + ip->major = dip->major; + ip->minor = dip->minor; + ip->nlink = dip->nlink; + ip->size = dip->size; + + memmove(ip->addrs, dip->addrs, sizeof(ip->addrs)); + brelse(bp); + ip->flags |= I_VALID; + + if (ip->type == 0) { + panic("ilock: no type"); + } + } +} + +// Unlock the given inode. +void iunlock (struct inode *ip) +{ + if (ip == 0 || !(ip->flags & I_BUSY) || ip->ref < 1) { + panic("iunlock"); + } + + acquire(&icache.lock); + ip->flags &= ~I_BUSY; + wakeup(ip); + release(&icache.lock); +} + +// Drop a reference to an in-memory inode. +// If that was the last reference, the inode cache entry can +// be recycled. +// If that was the last reference and the inode has no links +// to it, free the inode (and its content) on disk. +void iput (struct inode *ip) +{ + acquire(&icache.lock); + + if (ip->ref == 1 && (ip->flags & I_VALID) && ip->nlink == 0) { + // inode has no links: truncate and free inode. + if (ip->flags & I_BUSY) { + panic("iput busy"); + } + + ip->flags |= I_BUSY; + release(&icache.lock); + itrunc(ip); + ip->type = 0; + iupdate(ip); + + acquire(&icache.lock); + ip->flags = 0; + wakeup(ip); + } + + ip->ref--; + release(&icache.lock); +} + +// Common idiom: unlock, then put. +void iunlockput (struct inode *ip) +{ + iunlock(ip); + iput(ip); +} + +//PAGEBREAK! +// Inode content +// +// The content (data) associated with each inode is stored +// in blocks on the disk. The first NDIRECT block numbers +// are listed in ip->addrs[]. The next NINDIRECT blocks are +// listed in block ip->addrs[NDIRECT]. + +// Return the disk block address of the nth block in inode ip. +// If there is no such block, bmap allocates one. +static uint bmap (struct inode *ip, uint bn) +{ + uint addr, *a; + struct buf *bp; + + if (bn < NDIRECT) { + if ((addr = ip->addrs[bn]) == 0) { + ip->addrs[bn] = addr = balloc(ip->dev); + } + + return addr; + } + + bn -= NDIRECT; + + if (bn < NINDIRECT) { + // Load indirect block, allocating if necessary. + if ((addr = ip->addrs[NDIRECT]) == 0) { + ip->addrs[NDIRECT] = addr = balloc(ip->dev); + } + + bp = bread(ip->dev, addr); + a = (uint*) bp->data; + + if ((addr = a[bn]) == 0) { + a[bn] = addr = balloc(ip->dev); + log_write(bp); + } + + brelse(bp); + return addr; + } + + panic("bmap: out of range"); +} + +// Truncate inode (discard contents). +// Only called when the inode has no links +// to it (no directory entries referring to it) +// and has no in-memory reference to it (is +// not an open file or current directory). +static void itrunc (struct inode *ip) +{ + int i, j; + struct buf *bp; + uint *a; + + for (i = 0; i < NDIRECT; i++) { + if (ip->addrs[i]) { + bfree(ip->dev, ip->addrs[i]); + ip->addrs[i] = 0; + } + } + + if (ip->addrs[NDIRECT]) { + bp = bread(ip->dev, ip->addrs[NDIRECT]); + a = (uint*) bp->data; + + for (j = 0; j < NINDIRECT; j++) { + if (a[j]) { + bfree(ip->dev, a[j]); + } + } + + brelse(bp); + bfree(ip->dev, ip->addrs[NDIRECT]); + ip->addrs[NDIRECT] = 0; + } + + ip->size = 0; + iupdate(ip); +} + +// Copy stat information from inode. +void stati (struct inode *ip, struct stat *st) +{ + st->dev = ip->dev; + st->ino = ip->inum; + st->type = ip->type; + st->nlink = ip->nlink; + st->size = ip->size; +} + +//PAGEBREAK! +// Read data from inode. +int readi (struct inode *ip, char *dst, uint off, uint n) +{ + uint tot, m; + struct buf *bp; + + if (ip->type == T_DEV) { + if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read) { + return -1; + } + + return devsw[ip->major].read(ip, dst, n); + } + + if (off > ip->size || off + n < off) { + return -1; + } + + if (off + n > ip->size) { + n = ip->size - off; + } + + for (tot = 0; tot < n; tot += m, off += m, dst += m) { + bp = bread(ip->dev, bmap(ip, off / BSIZE)); + m = min(n - tot, BSIZE - off%BSIZE); + memmove(dst, bp->data + off % BSIZE, m); + brelse(bp); + } + + return n; +} + +// PAGEBREAK! +// Write data to inode. +int writei (struct inode *ip, char *src, uint off, uint n) +{ + uint tot, m; + struct buf *bp; + + if (ip->type == T_DEV) { + if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write) { + return -1; + } + + return devsw[ip->major].write(ip, src, n); + } + + if (off > ip->size || off + n < off) { + return -1; + } + + if (off + n > MAXFILE * BSIZE) { + return -1; + } + + for (tot = 0; tot < n; tot += m, off += m, src += m) { + bp = bread(ip->dev, bmap(ip, off / BSIZE)); + m = min(n - tot, BSIZE - off%BSIZE); + memmove(bp->data + off % BSIZE, src, m); + log_write(bp); + brelse(bp); + } + + if (n > 0 && off > ip->size) { + ip->size = off; + iupdate(ip); + } + + return n; +} + +//PAGEBREAK! +// Directories + +int namecmp (const char *s, const char *t) +{ + return strncmp(s, t, DIRSIZ); +} + +// Look for a directory entry in a directory. +// If found, set *poff to byte offset of entry. +struct inode* dirlookup (struct inode *dp, char *name, uint *poff) +{ + uint off, inum; + struct dirent de; + + if (dp->type != T_DIR) { + panic("dirlookup not DIR"); + } + + for (off = 0; off < dp->size; off += sizeof(de)) { + if (readi(dp, (char*) &de, off, sizeof(de)) != sizeof(de)) { + panic("dirlink read"); + } + + if (de.inum == 0) { + continue; + } + + if (namecmp(name, de.name) == 0) { + // entry matches path element + if (poff) { + *poff = off; + } + + inum = de.inum; + return iget(dp->dev, inum); + } + } + + return 0; +} + +// Write a new directory entry (name, inum) into the directory dp. +int dirlink (struct inode *dp, char *name, uint inum) +{ + int off; + struct dirent de; + struct inode *ip; + + // Check that name is not present. + if ((ip = dirlookup(dp, name, 0)) != 0) { + iput(ip); + return -1; + } + + // Look for an empty dirent. + for (off = 0; off < dp->size; off += sizeof(de)) { + if (readi(dp, (char*) &de, off, sizeof(de)) != sizeof(de)) { + panic("dirlink read"); + } + + if (de.inum == 0) { + break; + } + } + + strncpy(de.name, name, DIRSIZ); + de.inum = inum; + + if (writei(dp, (char*) &de, off, sizeof(de)) != sizeof(de)) { + panic("dirlink"); + } + + return 0; +} + +//PAGEBREAK! +// Paths + +// Copy the next path element from path into name. +// Return a pointer to the element following the copied one. +// The returned path has no leading slashes, +// so the caller can check *path=='\0' to see if the name is the last one. +// If no name to remove, return 0. +// +// Examples: +// skipelem("a/bb/c", name) = "bb/c", setting name = "a" +// skipelem("///a//bb", name) = "bb", setting name = "a" +// skipelem("a", name) = "", setting name = "a" +// skipelem("", name) = skipelem("////", name) = 0 +// +static char* skipelem (char *path, char *name) +{ + char *s; + int len; + + while (*path == '/') { + path++; + } + + if (*path == 0) { + return 0; + } + + s = path; + + while (*path != '/' && *path != 0) { + path++; + } + + len = path - s; + + if (len >= DIRSIZ) { + memmove(name, s, DIRSIZ); + } else { + memmove(name, s, len); + name[len] = 0; + } + + while (*path == '/') { + path++; + } + + return path; +} + +// Look up and return the inode for a path name. +// If parent != 0, return the inode for the parent and copy the final +// path element into name, which must have room for DIRSIZ bytes. +static struct inode* namex (char *path, int nameiparent, char *name) +{ + struct inode *ip, *next; + + if (*path == '/') { + ip = iget(ROOTDEV, ROOTINO); + } else { + ip = idup(proc->cwd); + } + + while ((path = skipelem(path, name)) != 0) { + ilock(ip); + + if (ip->type != T_DIR) { + iunlockput(ip); + return 0; + } + + if (nameiparent && *path == '\0') { + // Stop one level early. + iunlock(ip); + return ip; + } + + if ((next = dirlookup(ip, name, 0)) == 0) { + iunlockput(ip); + return 0; + } + + iunlockput(ip); + ip = next; + } + + if (nameiparent) { + iput(ip); + return 0; + } + + return ip; +} + +struct inode* namei (char *path) +{ + char name[DIRSIZ]; + return namex(path, 0, name); +} + +struct inode* nameiparent (char *path, char *name) +{ + return namex(path, 1, name); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fs.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,55 @@ +// On-disk file system format. +// Both the kernel and user programs use this header file. + +// Block 0 is unused. +// Block 1 is super block. +// Blocks 2 through sb.ninodes/IPB hold inodes. +// Then free bitmap blocks holding sb.size bits. +// Then sb.nblocks data blocks. +// Then sb.nlog log blocks. + +#define ROOTINO 1 // root i-number +#define BSIZE 512 // block size + +// File system super block +struct superblock { + uint size; // Size of file system image (blocks) + uint nblocks; // Number of data blocks + uint ninodes; // Number of inodes. + uint nlog; // Number of log blocks +}; + +#define NDIRECT 12 +#define NINDIRECT (BSIZE / sizeof(uint)) +#define MAXFILE (NDIRECT + NINDIRECT) + +// On-disk inode structure +struct dinode { + short type; // File type + short major; // Major device number (T_DEV only) + short minor; // Minor device number (T_DEV only) + short nlink; // Number of links to inode in file system + uint size; // Size of file (bytes) + uint addrs[NDIRECT+1]; // Data block addresses +}; + +// Inodes per block. +#define IPB (BSIZE / sizeof(struct dinode)) + +// Block containing inode i +#define IBLOCK(i) ((i) / IPB + 2) + +// Bitmap bits per block +#define BPB (BSIZE*8) + +// Block containing bit for block b +#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) + +// Directory is a file containing a sequence of dirent structures. +#define DIRSIZ 14 + +struct dirent { + ushort inum; + char name[DIRSIZ]; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/initcode.S Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,28 @@ +# Initial process execs /init. + +#include "syscall.h" + +.globl start + +# exec(init, argv) +start: + LDR r1, =init + LDR r2, =argv + MOV r0, #SYS_exec + SWI 0x00 + +exit: + MOV r0, #SYS_exit + SWI 0x00 + B exit + +# char init[] = "/init\0"; +init: + .string "/init\0" + +# char *argv[] = { init, 0 }; +.p2align 2 +argv: + .word init + .word 0 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/kalloc.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,111 @@ +// Physical memory allocator, intended to allocate +// memory for user processes, kernel stacks, page table pages, +// and pipe buffers. Allocates 4096-byte pages. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "spinlock.h" +#include "arm.h" + +void freerange(void *vstart, void *vend); +extern char end[]; // first address after kernel loaded from ELF file + +struct run { + struct run *next; +}; + +static struct { + struct spinlock lock; + int use_lock; + struct run *freelist; +} kmem; + +void kmem_init (void) +{ + initlock(&kmem.lock, "kmem"); + kmem.use_lock = 0; +} + +// Initialization happens in two phases. +// 1. main() calls kinit1() while still using entrypgdir to place just +// the pages mapped by entrypgdir on free list. +// 2. main() calls kinit2() with the rest of the physical pages +// after installing a full page table that maps them on all cores. +void kinit1(void *vstart, void *vend) +{ + freerange(vstart, vend); +} + +void kinit2(void *vstart, void *vend) +{ + freerange(vstart, vend); + kmem.use_lock = 1; +} + +void freerange(void *vstart, void *vend) +{ + char *p; + + p = (char*)align_up (vstart, PTE_SZ); + + for(; p + PTE_SZ <= (char*)vend; p += PTE_SZ) { + kfree(p); + } +} + +//PAGEBREAK: 21 +// Free the page of physical memory pointed at by v, +// which normally should have been returned by a +// call to kalloc(). (The exception is when +// initializing the allocator; see kinit above.) +void kfree(char *v) +{ + struct run *r; + + if((uint)v % PTE_SZ || v < end || v2p(v) >= PHYSTOP) { + cprintf("kfree(0x%x)\n", v); + panic("kfree"); + } + + // Fill with junk to catch dangling refs. + //memset(v, 0x00, PG_SIZE); + + if(kmem.use_lock) { + acquire(&kmem.lock); + } + + r = (struct run*)v; + r->next = kmem.freelist; + kmem.freelist = r; + + if(kmem.use_lock) { + release(&kmem.lock); + } +} + +// Allocate one 4096-byte page of physical memory. +// Returns a pointer that the kernel can use. +// Returns 0 if the memory cannot be allocated. +char* kalloc(void) +{ + struct run *r; + + if(kmem.use_lock) { + acquire(&kmem.lock); + } + + r = kmem.freelist; + + if(r) { + kmem.freelist = r->next; + } + + if(kmem.use_lock) { + release(&kmem.lock); + } + + return (char*)r; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/kernel.ld Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,78 @@ +OUTPUT_FORMAT("elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +ENTRY_SVC_STACK_SIZE = 0x1000; + +SECTIONS +{ + /* the entry point, before enabling paging. The code to enable paing + needs to have the same virtual/physical address. entry.S and start.c + run in this initial setting.*/ + . = 0x10000; + .start_sec : { + build/entry.o(.text) + build/start.o(.text .text.*) + + build/entry.o(.rodata .rodata.*) + build/start.o(.rodata .rodata.*) + + build/entry.o(.data .data.*) + build/start.o(.data .data.*) + + PROVIDE(edata_entry = .); + + build/entry.o(.bss .bss.* COMMON) + build/start.o(.bss .bss.* COMMON) + + /*define a stack for the entry*/ + . = ALIGN(0x1000); + . += ENTRY_SVC_STACK_SIZE; + + PROVIDE (svc_stktop = .); + + /* define the kernel page table, must be 16K and 16K-aligned*/ + . = ALIGN(0x4000); + PROVIDE (_kernel_pgtbl = .); + . += 0x4000; + + /* we also need a user page table*/ + PROVIDE (_user_pgtbl = .); + . += 0x1000; + + PROVIDE(end_entry = .); + } + + /*the kernel executes at the higher 2GB address space, but loaded + at the lower memory (0x20000)*/ + . = 0x80020000; + + .text : AT(0x20000){ + *(.text .text.* .gnu.linkonce.t.*) + } + + PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ + + .rodata : { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + /* aligned the data to a (4K) page, so it can be assigned + different protection than the code*/ + . = ALIGN(0x1000); + + PROVIDE (data_start = .); + + .data : { + *(.data .data.*) + } + + PROVIDE (edata = .); + + .bss : { + *(.bss .bss.* COMMON) + } + + . = ALIGN(0x1000); + PROVIDE (end = .); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/string.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,144 @@ +#include "types.h" +#include "arm.h" + + +void* memset(void *dst, int v, int n) +{ + uint8 *p; + uint8 c; + uint32 val; + uint32 *p4; + + p = dst; + c = v & 0xff; + val = (c << 24) | (c << 16) | (c << 8) | c; + + // set bytes before whole uint32 + for (; (n > 0) && ((uint)p % 4); n--, p++){ + *p = c; + } + + // set memory 4 bytes a time + p4 = (uint*)p; + + for (; n >= 4; n -= 4, p4++) { + *p4 = val; + } + + // set leftover one byte a time + p = (uint8*)p4; + + for (; n > 0; n--, p++) { + *p = c; + } + + return dst; +} + + +int memcmp(const void *v1, const void *v2, uint n) +{ + const uchar *s1, *s2; + + s1 = v1; + s2 = v2; + + while(n-- > 0){ + if(*s1 != *s2) { + return *s1 - *s2; + } + + s1++, s2++; + } + + return 0; +} + +void* memmove(void *dst, const void *src, uint n) +{ + const char *s; + char *d; + + s = src; + d = dst; + + if(s < d && s + n > d){ + s += n; + d += n; + + while(n-- > 0) { + *--d = *--s; + } + + } else { + while(n-- > 0) { + *d++ = *s++; + } + } + + return dst; +} + +// memcpy exists to placate GCC. Use memmove. +void* memcpy(void *dst, const void *src, uint n) +{ + return memmove(dst, src, n); +} + +int strncmp(const char *p, const char *q, uint n) +{ + while(n > 0 && *p && *p == *q) { + n--, p++, q++; + } + + if(n == 0) { + return 0; + } + + return (uchar)*p - (uchar)*q; +} + +char* strncpy(char *s, const char *t, int n) +{ + char *os; + + os = s; + + while(n-- > 0 && (*s++ = *t++) != 0) + ; + + while(n-- > 0) { + *s++ = 0; + } + + return os; +} + +// Like strncpy but guaranteed to NUL-terminate. +char* safestrcpy(char *s, const char *t, int n) +{ + char *os; + + os = s; + + if(n <= 0) { + return os; + } + + while(--n > 0 && (*s++ = *t++) != 0) + ; + + *s = 0; + return os; +} + +int strlen(const char *s) +{ + int n; + + for(n = 0; s[n]; n++) + ; + + return n; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/log.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,206 @@ +#include "types.h" +#include "defs.h" +#include "param.h" +#include "spinlock.h" +#include "fs.h" +#include "buf.h" + +// Simple logging. Each system call that might write the file system +// should be surrounded with begin_trans() and commit_trans() calls. +// +// The log holds at most one transaction at a time. Commit forces +// the log (with commit record) to disk, then installs the affected +// blocks to disk, then erases the log. begin_trans() ensures that +// only one system call can be in a transaction; others must wait. +// +// Allowing only one transaction at a time means that the file +// system code doesn't have to worry about the possibility of +// one transaction reading a block that another one has modified, +// for example an i-node block. +// +// Read-only system calls don't need to use transactions, though +// this means that they may observe uncommitted data. I-node and +// buffer locks prevent read-only calls from seeing inconsistent data. +// +// The log is a physical re-do log containing disk blocks. +// The on-disk log format: +// header block, containing sector #s for block A, B, C, ... +// block A +// block B +// block C +// ... +// Log appends are synchronous. + +// Contents of the header block, used for both the on-disk header block +// and to keep track in memory of logged sector #s before commit. +struct logheader { + int n; + int sector[LOGSIZE]; +}; + +struct log { + struct spinlock lock; + int start; + int size; + int busy; // a transaction is active + int dev; + struct logheader lh; +}; +struct log log; + +static void recover_from_log(void); + +void initlog(void) +{ + struct superblock sb; + + if (sizeof(struct logheader) >= BSIZE) { + panic("initlog: too big logheader"); + } + + initlock(&log.lock, "log"); + readsb(ROOTDEV, &sb); + log.start = sb.size - sb.nlog; + log.size = sb.nlog; + log.dev = ROOTDEV; + recover_from_log(); +} + +// Copy committed blocks from log to their home location +static void install_trans(void) +{ + int tail; + struct buf *lbuf; + struct buf *dbuf; + + for (tail = 0; tail < log.lh.n; tail++) { + lbuf = bread(log.dev, log.start+tail+1); // read log block + dbuf = bread(log.dev, log.lh.sector[tail]); // read dst + + memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst + + bwrite(dbuf); // write dst to disk + brelse(lbuf); + brelse(dbuf); + } +} + +// Read the log header from disk into the in-memory log header +static void read_head(void) +{ + struct buf *buf; + struct logheader *lh; + int i; + + buf = bread(log.dev, log.start); + lh = (struct logheader *) (buf->data); + log.lh.n = lh->n; + + for (i = 0; i < log.lh.n; i++) { + log.lh.sector[i] = lh->sector[i]; + } + + brelse(buf); +} + +// Write in-memory log header to disk. +// This is the true point at which the +// current transaction commits. +static void write_head(void) +{ + struct buf *buf; + struct logheader *hb; + int i; + + buf = bread(log.dev, log.start); + hb = (struct logheader *) (buf->data); + + hb->n = log.lh.n; + + for (i = 0; i < log.lh.n; i++) { + hb->sector[i] = log.lh.sector[i]; + } + + bwrite(buf); + brelse(buf); +} + +static void recover_from_log(void) +{ + read_head(); + install_trans(); // if committed, copy from log to disk + log.lh.n = 0; + write_head(); // clear the log +} + +void begin_trans(void) +{ + acquire(&log.lock); + + while (log.busy) { + sleep(&log, &log.lock); + } + + log.busy = 1; + release(&log.lock); +} + +void commit_trans(void) +{ + if (log.lh.n > 0) { + write_head(); // Write header to disk -- the real commit + install_trans(); // Now install writes to home locations + log.lh.n = 0; + write_head(); // Erase the transaction from the log + } + + acquire(&log.lock); + log.busy = 0; + wakeup(&log); + release(&log.lock); +} + +// Caller has modified b->data and is done with the buffer. +// Append the block to the log and record the block number, +// but don't write the log header (which would commit the write). +// log_write() replaces bwrite(); a typical use is: +// bp = bread(...) +// modify bp->data[] +// log_write(bp) +// brelse(bp) +void log_write(struct buf *b) +{ + struct buf *lbuf; + int i; + + if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) { + panic("too big a transaction"); + } + + if (!log.busy) { + panic("write outside of trans"); + } + + for (i = 0; i < log.lh.n; i++) { + if (log.lh.sector[i] == b->sector) { // log absorbtion? + break; + } + } + + log.lh.sector[i] = b->sector; + lbuf = bread(b->dev, log.start+i+1); + + memmove(lbuf->data, b->data, BSIZE); + bwrite(lbuf); + brelse(lbuf); + + if (i == log.lh.n) { + log.lh.n++; + } + + b->flags |= B_DIRTY; // XXX prevent eviction +} + +//PAGEBREAK! +// Blank page. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,54 @@ +// BSP support routine +#include "types.h" +#include "defs.h" +#include "param.h" +#include "arm.h" +#include "proc.h" +#include "memlayout.h" +#include "mmu.h" + +extern void* end; + +struct cpu cpus[NCPU]; +struct cpu *cpu; + +#define MB (1024*1024) + +void kmain (void) +{ + uint vectbl; + + cpu = &cpus[0]; + + uart_init (P2V(UART0)); + + // interrrupt vector table is in the middle of first 1MB. We use the left + // over for page tables + vectbl = P2V_WO (VEC_TBL & PDE_MASK); + + init_vmm (); + kpt_freerange (align_up(&end, PT_SZ), vectbl); + kpt_freerange (vectbl + PT_SZ, P2V_WO(INIT_KERNMAP)); + paging_init (INIT_KERNMAP, PHYSTOP); + + kmem_init (); + kmem_init2(P2V(INIT_KERNMAP), P2V(PHYSTOP)); + + trap_init (); // vector table and stacks for models + pic_init (P2V(VIC_BASE)); // interrupt controller + uart_enable_rx (); // interrupt for uart + consoleinit (); // console + pinit (); // process (locks) + + binit (); // buffer cache + fileinit (); // file table + iinit (); // inode cache + ideinit (); // ide (memory block device) + timer_init (HZ); // the timer (ticker) + + + sti (); + + userinit(); // first user process + scheduler(); // start running processes +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/makefile.inc Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,46 @@ +# Cross-compiling (e.g., on Mac OS X, install arm-none-eabi-gcc with MacPorts) +CROSSCOMPILE := arm-none-eabi- + +CC = $(CROSSCOMPILE)gcc +AS = $(CROSSCOMPILE)as +LD = $(CROSSCOMPILE)ld +OBJCOPY = $(CROSSCOMPILE)objcopy +OBJDUMP = $(CROSSCOMPILE)objdump + +CFLAGS = -march=armv6 -fno-pic -static -fno-builtin -fno-strict-aliasing -Wall -Werror -I. -g -O0 +LDFLAGS = -L. +ASFLAGS = -march=armv6 + +LIBGCC = $(shell $(CC) -print-libgcc-file-name) + +# host compiler +HOSTCC_preferred = gcc +define get_hostcc + $(if $(shell which $(HOSTCC_preferred)),$(HOSTCC_preferred),"cc") +endef +HOSTCC := $(call get_hostcc) + +# general rules +quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1)) + +LINK_BIN = $(call quiet-command,$(LD) $(LDFLAGS) \ + -T $(1) -o $(2) $(3) $(LIBS) -b binary $(4), " LINK $(TARGET_DIR)$@") + +LINK_INIT = $(call quiet-command,$(LD) $(LDFLAGS) \ + $(1) -o $@.out $<, " LINK $(TARGET_DIR)$@") +OBJCOPY_INIT = $(call quiet-command,$(OBJCOPY) \ + -S -O binary --prefix-symbols="_binary_$@" $@.out $@, " OBJCOPY $(TARGET_DIR)$@") + +build-directory = $(shell mkdir -p build build/device build/lib) + +build/%.o: %.c + $(call build-directory) + $(call quiet-command,$(CC) $(CFLAGS) \ + -c -o $@ $<," CC $(TARGET_DIR)$@") + +AS_WITH = $(call quiet-command,$(CC) $(ASFLAGS) \ + $(1) -c -o $@ $<," AS $(TARGET_DIR)$@") + +build/%.o: %.S + $(call build-directory) + $(call AS_WITH, )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/memide.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,63 @@ +// Fake IDE disk; stores blocks in memory. +// Useful for running kernel without scratch disk. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "mmu.h" +#include "proc.h" +#include "spinlock.h" +#include "buf.h" + +// a file system image, embeded +extern uchar _binary_fs_img_start[], _binary_fs_img_size[]; + +static int disksize; +static uchar *memdisk; + +void ideinit(void) +{ + memdisk = _binary_fs_img_start; + disksize = (uint)_binary_fs_img_size/512; +} + +// Interrupt handler. +void ideintr(void) +{ + // no-op +} + +// Sync buf with disk. +// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. +// Else if B_VALID is not set, read buf from disk, set B_VALID. +void iderw(struct buf *b) +{ + uchar *p; + + if(!(b->flags & B_BUSY)) { + panic("iderw: buf not busy"); + } + + if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) { + panic("iderw: nothing to do"); + } + + if(b->dev != 1) { + panic("iderw: request not for disk 1"); + } + + if(b->sector >= disksize) { + panic("iderw: sector out of range"); + } + + p = memdisk + b->sector*512; + + if(b->flags & B_DIRTY){ + b->flags &= ~B_DIRTY; + memmove(p, b->data, 512); + } else { + memmove(b->data, p, 512); + } + + b->flags |= B_VALID; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/memlayout.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,22 @@ +// Memory layout + +// Key addresses for address space layout (see kmap in vm.c for layout) +#define EXTMEM 0x20000 +#define KERNBASE 0x80000000 // First kernel virtual address +#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked + +// we first map 1MB low memory containing kernel code. +#define INIT_KERNMAP 0x100000 + +#ifndef __ASSEMBLER__ + +static inline uint v2p(void *a) { return ((uint) (a)) - KERNBASE; } +static inline void *p2v(uint a) { return (void *) ((a) + KERNBASE); } + +#endif + +#define V2P(a) (((uint) (a)) - KERNBASE) +#define P2V(a) (((void *) (a)) + KERNBASE) + +#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts +#define P2V_WO(x) ((x) + KERNBASE) // same as V2P, but without casts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mmu.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,63 @@ +// Definition for ARM MMU +#ifndef MMU_INCLUDE +#define MMU_INCLUDE + +// align_up/down: al must be of power of 2 +#define align_up(sz, al) (((uint)(sz)+ (uint)(al)-1) & ~((uint)(al)-1)) +#define align_dn(sz, al) ((uint)(sz) & ~((uint)(al)-1)) +// +// Since ARMv6, you may use two page tables, one for kernel pages (TTBR1), +// and one for user pages (TTBR0). We use this architecture. Memory address +// lower than UVIR_BITS^2 is translated by TTBR0, while higher memory is +// translated by TTBR1. +// Kernel pages are create statically during system initialization. It use +// 1MB page mapping. User pages use 4K pages. +// + + +// access permission for page directory/page table entries. +#define AP_NA 0x00 // no access +#define AP_KO 0x01 // privilaged access, kernel: RW, user: no access +#define AP_KUR 0x02 // no write access from user, read allowed +#define AP_KU 0x03 // full access + +// domain definition for page table entries +#define DM_NA 0x00 // any access causing a domain fault +#define DM_CLIENT 0x01 // any access checked against TLB (page table) +#define DM_RESRVED 0x02 // reserved +#define DM_MANAGER 0x03 // no access check + +#define PE_CACHE (1 << 3)// cachable +#define PE_BUF (1 << 2)// bufferable + +#define PE_TYPES 0x03 // mask for page type +#define KPDE_TYPE 0x02 // use "section" type for kernel page directory +#define UPDE_TYPE 0x01 // use "coarse page table" for user page directory +#define PTE_TYPE 0x02 // executable user page(subpage disable) + +// 1st-level or large (1MB) page directory (always maps 1MB memory) +#define PDE_SHIFT 20 // shift how many bits to get PDE index +#define PDE_SZ (1 << PDE_SHIFT) +#define PDE_MASK (PDE_SZ - 1) // offset for page directory entries +#define PDE_IDX(v) ((uint)(v) >> PDE_SHIFT) // index for page table entry + +// 2nd-level page table +#define PTE_SHIFT 12 // shift how many bits to get PTE index +#define PTE_IDX(v) (((uint)(v) >> PTE_SHIFT) & (NUM_PTE - 1)) +#define PTE_SZ (1 << PTE_SHIFT) +#define PTE_ADDR(v) align_dn (v, PTE_SZ) +#define PTE_AP(pte) (((pte) >> 4) & 0x03) + +// size of two-level page tables +#define UADDR_BITS 28 // maximum user-application memory, 256MB +#define UADDR_SZ (1 << UADDR_BITS) // maximum user address space size + +// must have NUM_UPDE == NUM_PTE +#define NUM_UPDE (1 << (UADDR_BITS - PDE_SHIFT)) // # of PDE for user space +#define NUM_PTE (1 << (PDE_SHIFT - PTE_SHIFT)) // how many PTE in a PT + +#define PT_SZ (NUM_PTE << 2) // user page table size (1K) +#define PT_ADDR(v) align_dn(v, PT_SZ) // physical address of the PT +#define PT_ORDER 10 + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/param.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,20 @@ +#ifndef PARAM_INCLUDE +#define PARAM_INCLUDE + + +#define NPROC 64 // maximum number of processes +#define KSTACKSIZE 4096 // size of per-process kernel stack +#define NCPU 8 // maximum number of CPUs +#define NOFILE 16 // open files per process +#define NFILE 100 // open files per system +#define NBUF 10 // size of disk block cache +#define NINODE 50 // maximum number of active i-nodes +#define NDEV 10 // maximum major device number +#define ROOTDEV 1 // device number of file system root disk +#define MAXARG 32 // max exec arguments +#define LOGSIZE 10 // max data sectors in on-disk log + +#define HZ 10 + +#define N_CALLSTK 15 +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pipe.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,146 @@ +#include "types.h" +#include "defs.h" +#include "param.h" +#include "mmu.h" +#include "proc.h" +#include "fs.h" +#include "file.h" +#include "spinlock.h" + +#define PIPESIZE 512 + +struct pipe { + struct spinlock lock; + char data[PIPESIZE]; + uint nread; // number of bytes read + uint nwrite; // number of bytes written + int readopen; // read fd is still open + int writeopen; // write fd is still open +}; + +int pipealloc(struct file **f0, struct file **f1) +{ + struct pipe *p; + + p = 0; + *f0 = *f1 = 0; + + if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) { + goto bad; + } + + if((p = kmalloc (get_order(sizeof(*p)))) == 0) { + goto bad; + } + + p->readopen = 1; + p->writeopen = 1; + p->nwrite = 0; + p->nread = 0; + + initlock(&p->lock, "pipe"); + + (*f0)->type = FD_PIPE; + (*f0)->readable = 1; + (*f0)->writable = 0; + (*f0)->pipe = p; + (*f1)->type = FD_PIPE; + (*f1)->readable = 0; + (*f1)->writable = 1; + (*f1)->pipe = p; + + return 0; + + //PAGEBREAK: 20 + bad: + if(p) { + kfree (p, get_order(sizeof*p)); + } + + if(*f0) { + fileclose(*f0); + } + + if(*f1) { + fileclose(*f1); + } + + return -1; +} + +void pipeclose(struct pipe *p, int writable) +{ + acquire(&p->lock); + + if(writable){ + p->writeopen = 0; + wakeup(&p->nread); + + } else { + p->readopen = 0; + wakeup(&p->nwrite); + } + + if(p->readopen == 0 && p->writeopen == 0){ + release(&p->lock); + kfree (p, get_order(sizeof(*p))); + + } else { + release(&p->lock); + } +} + +//PAGEBREAK: 40 +int pipewrite(struct pipe *p, char *addr, int n) +{ + int i; + + acquire(&p->lock); + + for(i = 0; i < n; i++){ + while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full + if(p->readopen == 0 /*|| proc->killed*/){ + release(&p->lock); + return -1; + } + + wakeup(&p->nread); + sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep + } + + p->data[p->nwrite++ % PIPESIZE] = addr[i]; + } + + wakeup(&p->nread); //DOC: pipewrite-wakeup1 + release(&p->lock); + return n; +} + +int piperead(struct pipe *p, char *addr, int n) +{ + int i; + + acquire(&p->lock); + + while(p->nread == p->nwrite && p->writeopen){ //DOC: pipe-empty + if(proc->killed){ + release(&p->lock); + return -1; + } + + sleep(&p->nread, &p->lock); //DOC: piperead-sleep*/ + } + + for(i = 0; i < n; i++){ //DOC: piperead-copy + if(p->nread == p->nwrite) { + break; + } + + addr[i] = p->data[p->nread++ % PIPESIZE]; + } + + wakeup(&p->nwrite); //DOC: piperead-wakeup + release(&p->lock); + + return i; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/proc.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,532 @@ +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "arm.h" +#include "proc.h" +#include "spinlock.h" + +// +// Process initialization: +// process initialize is somewhat tricky. +// 1. We need to fake the kernel stack of a new process as if the process +// has been interrupt (a trapframe on the stack), this would allow us +// to "return" to the correct user instruction. +// 2. We also need to fake the kernel execution for this new process. When +// swtch switches to this (new) process, it will switch to its stack, +// and reload registers with the saved context. We use forkret as the +// return address (in lr register). (In x86, it will be the return address +// pushed on the stack by the process.) +// +// The design of context switch in xv6 is interesting: after initialization, +// each CPU executes in the scheduler() function. The context switch is not +// between two processes, but instead, between the scheduler. Think of scheduler +// as the idle process. +// +struct { + struct spinlock lock; + struct proc proc[NPROC]; +} ptable; + +static struct proc *initproc; +struct proc *proc; + +int nextpid = 1; +extern void forkret(void); +extern void trapret(void); + +static void wakeup1(void *chan); + +void pinit(void) +{ + initlock(&ptable.lock, "ptable"); +} + +//PAGEBREAK: 32 +// Look in the process table for an UNUSED proc. +// If found, change state to EMBRYO and initialize +// state required to run in the kernel. +// Otherwise return 0. +static struct proc* allocproc(void) +{ + struct proc *p; + char *sp; + + acquire(&ptable.lock); + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if(p->state == UNUSED) { + goto found; + } + + } + + release(&ptable.lock); + return 0; + + found: + p->state = EMBRYO; + p->pid = nextpid++; + release(&ptable.lock); + + // Allocate kernel stack. + if((p->kstack = alloc_page ()) == 0){ + p->state = UNUSED; + return 0; + } + + sp = p->kstack + KSTACKSIZE; + + // Leave room for trap frame. + sp -= sizeof (*p->tf); + p->tf = (struct trapframe*)sp; + + // Set up new context to start executing at forkret, + // which returns to trapret. + sp -= 4; + *(uint*)sp = (uint)trapret; + + sp -= 4; + *(uint*)sp = (uint)p->kstack + KSTACKSIZE; + + sp -= sizeof (*p->context); + p->context = (struct context*)sp; + memset(p->context, 0, sizeof(*p->context)); + + // skip the push {fp, lr} instruction in the prologue of forkret. + // This is different from x86, in which the harderware pushes return + // address before executing the callee. In ARM, return address is + // loaded into the lr register, and push to the stack by the callee + // (if and when necessary). We need to skip that instruction and let + // it use our implementation. + p->context->lr = (uint)forkret+4; + + return p; +} + +void error_init () +{ + panic ("failed to craft first process\n"); +} + + +//PAGEBREAK: 32 +// hand-craft the first user process. We link initcode.S into the kernel +// as a binary, the linker will generate __binary_initcode_start/_size +void userinit(void) +{ + struct proc *p; + extern char _binary_initcode_start[], _binary_initcode_size[]; + + p = allocproc(); + initproc = p; + + if((p->pgdir = kpt_alloc()) == NULL) { + panic("userinit: out of memory?"); + } + + inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); + + p->sz = PTE_SZ; + + // craft the trapframe as if + memset(p->tf, 0, sizeof(*p->tf)); + + p->tf->r14_svc = (uint)error_init; + p->tf->spsr = spsr_usr (); + p->tf->sp_usr = PTE_SZ; // set the user stack + p->tf->lr_usr = 0; + + // set the user pc. The actual pc loaded into r15_usr is in + // p->tf, the trapframe. + p->tf->pc = 0; // beginning of initcode.S + + safestrcpy(p->name, "initcode", sizeof(p->name)); + p->cwd = namei("/"); + + p->state = RUNNABLE; +} + +// Grow current process's memory by n bytes. +// Return 0 on success, -1 on failure. +int growproc(int n) +{ + uint sz; + + sz = proc->sz; + + if(n > 0){ + if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0) { + return -1; + } + + } else if(n < 0){ + if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0) { + return -1; + } + } + + proc->sz = sz; + switchuvm(proc); + + return 0; +} + +// Create a new process copying p as the parent. +// Sets up stack to return as if from system call. +// Caller must set state of returned proc to RUNNABLE. +int fork(void) +{ + int i, pid; + struct proc *np; + + // Allocate process. + if((np = allocproc()) == 0) { + return -1; + } + + // Copy process state from p. + if((np->pgdir = copyuvm(proc->pgdir, proc->sz)) == 0){ + free_page(np->kstack); + np->kstack = 0; + np->state = UNUSED; + return -1; + } + + np->sz = proc->sz; + np->parent = proc; + *np->tf = *proc->tf; + + // Clear r0 so that fork returns 0 in the child. + np->tf->r0 = 0; + + for(i = 0; i < NOFILE; i++) { + if(proc->ofile[i]) { + np->ofile[i] = filedup(proc->ofile[i]); + } + } + + np->cwd = idup(proc->cwd); + + pid = np->pid; + np->state = RUNNABLE; + safestrcpy(np->name, proc->name, sizeof(proc->name)); + + return pid; +} + +// Exit the current process. Does not return. +// An exited process remains in the zombie state +// until its parent calls wait() to find out it exited. +void exit(void) +{ + struct proc *p; + int fd; + + if(proc == initproc) { + panic("init exiting"); + } + + // Close all open files. + for(fd = 0; fd < NOFILE; fd++){ + if(proc->ofile[fd]){ + fileclose(proc->ofile[fd]); + proc->ofile[fd] = 0; + } + } + + iput(proc->cwd); + proc->cwd = 0; + + acquire(&ptable.lock); + + // Parent might be sleeping in wait(). + wakeup1(proc->parent); + + // Pass abandoned children to init. + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->parent == proc){ + p->parent = initproc; + + if(p->state == ZOMBIE) { + wakeup1(initproc); + } + } + } + + // Jump into the scheduler, never to return. + proc->state = ZOMBIE; + sched(); + + panic("zombie exit"); +} + +// Wait for a child process to exit and return its pid. +// Return -1 if this process has no children. +int wait(void) +{ + struct proc *p; + int havekids, pid; + + acquire(&ptable.lock); + + for(;;){ + // Scan through table looking for zombie children. + havekids = 0; + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->parent != proc) { + continue; + } + + havekids = 1; + + if(p->state == ZOMBIE){ + // Found one. + pid = p->pid; + free_page(p->kstack); + p->kstack = 0; + freevm(p->pgdir); + p->state = UNUSED; + p->pid = 0; + p->parent = 0; + p->name[0] = 0; + p->killed = 0; + release(&ptable.lock); + + return pid; + } + } + + // No point waiting if we don't have any children. + if(!havekids || proc->killed){ + release(&ptable.lock); + return -1; + } + + // Wait for children to exit. (See wakeup1 call in proc_exit.) + sleep(proc, &ptable.lock); //DOC: wait-sleep + } +} + +//PAGEBREAK: 42 +// Per-CPU process scheduler. +// Each CPU calls scheduler() after setting itself up. +// Scheduler never returns. It loops, doing: +// - choose a process to run +// - swtch to start running that process +// - eventually that process transfers control +// via swtch back to the scheduler. +void scheduler(void) +{ + struct proc *p; + + for(;;){ + // Enable interrupts on this processor. + sti(); + + // Loop over process table looking for process to run. + acquire(&ptable.lock); + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->state != RUNNABLE) { + continue; + } + + // Switch to chosen process. It is the process's job + // to release ptable.lock and then reacquire it + // before jumping back to us. + proc = p; + switchuvm(p); + + p->state = RUNNING; + + swtch(&cpu->scheduler, proc->context); + // Process is done running for now. + // It should have changed its p->state before coming back. + proc = 0; + } + + release(&ptable.lock); + } +} + +// Enter scheduler. Must hold only ptable.lock +// and have changed proc->state. +void sched(void) +{ + int intena; + + //show_callstk ("sched"); + + if(!holding(&ptable.lock)) { + panic("sched ptable.lock"); + } + + if(cpu->ncli != 1) { + panic("sched locks"); + } + + if(proc->state == RUNNING) { + panic("sched running"); + } + + if(int_enabled ()) { + panic("sched interruptible"); + } + + intena = cpu->intena; + swtch(&proc->context, cpu->scheduler); + cpu->intena = intena; +} + +// Give up the CPU for one scheduling round. +void yield(void) +{ + acquire(&ptable.lock); //DOC: yieldlock + proc->state = RUNNABLE; + sched(); + release(&ptable.lock); +} + +// A fork child's very first scheduling by scheduler() +// will swtch here. "Return" to user space. +void forkret(void) +{ + static int first = 1; + + // Still holding ptable.lock from scheduler. + release(&ptable.lock); + + if (first) { + // Some initialization functions must be run in the context + // of a regular process (e.g., they call sleep), and thus cannot + // be run from main(). + first = 0; + initlog(); + } + + // Return to "caller", actually trapret (see allocproc). +} + +// Atomically release lock and sleep on chan. +// Reacquires lock when awakened. +void sleep(void *chan, struct spinlock *lk) +{ + //show_callstk("sleep"); + + if(proc == 0) { + panic("sleep"); + } + + if(lk == 0) { + panic("sleep without lk"); + } + + // Must acquire ptable.lock in order to change p->state and then call + // sched. Once we hold ptable.lock, we can be guaranteed that we won't + // miss any wakeup (wakeup runs with ptable.lock locked), so it's okay + // to release lk. + if(lk != &ptable.lock){ //DOC: sleeplock0 + acquire(&ptable.lock); //DOC: sleeplock1 + release(lk); + } + + // Go to sleep. + proc->chan = chan; + proc->state = SLEEPING; + sched(); + + // Tidy up. + proc->chan = 0; + + // Reacquire original lock. + if(lk != &ptable.lock){ //DOC: sleeplock2 + release(&ptable.lock); + acquire(lk); + } +} + +//PAGEBREAK! +// Wake up all processes sleeping on chan. The ptable lock must be held. +static void wakeup1(void *chan) +{ + struct proc *p; + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if(p->state == SLEEPING && p->chan == chan) { + p->state = RUNNABLE; + } + } +} + +// Wake up all processes sleeping on chan. +void wakeup(void *chan) +{ + acquire(&ptable.lock); + wakeup1(chan); + release(&ptable.lock); +} + +// Kill the process with the given pid. Process won't exit until it returns +// to user space (see trap in trap.c). +int kill(int pid) +{ + struct proc *p; + + acquire(&ptable.lock); + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->pid == pid){ + p->killed = 1; + + // Wake process from sleep if necessary. + if(p->state == SLEEPING) { + p->state = RUNNABLE; + } + + release(&ptable.lock); + return 0; + } + } + + release(&ptable.lock); + return -1; +} + +//PAGEBREAK: 36 +// Print a process listing to console. For debugging. Runs when user +// types ^P on console. No lock to avoid wedging a stuck machine further. +void procdump(void) +{ + static char *states[] = { + [UNUSED] "unused", + [EMBRYO] "embryo", + [SLEEPING] "sleep ", + [RUNNABLE] "runble", + [RUNNING] "run ", + [ZOMBIE] "zombie" + }; + + struct proc *p; + char *state; + + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->state == UNUSED) { + continue; + } + + if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) { + state = states[p->state]; + } else { + state = "???"; + } + + cprintf("%d %s %d:%s %d\n", p->pid, state, p->pid, p->name, p->parent->pid); + } + + show_callstk("procdump: \n"); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/proc.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,77 @@ +#ifndef PROC_INCLUDE_ +#define PROC_INCLUDE_ + +// Per-CPU state, now we only support one CPU +struct cpu { + uchar id; // index into cpus[] below + struct context* scheduler; // swtch() here to enter scheduler + volatile uint started; // Has the CPU started? + + int ncli; // Depth of pushcli nesting. + int intena; // Were interrupts enabled before pushcli? + + // Cpu-local storage variables; see below + struct cpu* cpu; + struct proc* proc; // The currently-running process. +}; + +extern struct cpu cpus[NCPU]; +extern int ncpu; + + +extern struct cpu* cpu; +extern struct proc* proc; + +//PAGEBREAK: 17 +// Saved registers for kernel context switches. The context switcher +// needs to save the callee save register, as usually. For ARM, it is +// also necessary to save the banked sp (r13) and lr (r14) registers. +// There is, however, no need to save the user space pc (r15) because +// pc has been saved on the stack somewhere. We only include it here +// for debugging purpose. It will not be restored for the next process. +// According to ARM calling convension, r0-r3 is caller saved. We do +// not need to save sp_svc, as it will be saved in the pcb, neither +// pc_svc, as it will be always be the same value. +// +// Keep it in sync with swtch.S +// +struct context { + // svc mode registers + uint r4; + uint r5; + uint r6; + uint r7; + uint r8; + uint r9; + uint r10; + uint r11; + uint r12; + uint lr; +}; + + +enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; + +// Per-process state +struct proc { + uint sz; // Size of process memory (bytes) + pde_t* pgdir; // Page table + char* kstack; // Bottom of kernel stack for this process + enum procstate state; // Process state + volatile int pid; // Process ID + struct proc* parent; // Parent process + struct trapframe* tf; // Trap frame for current syscall + struct context* context; // swtch() here to run process + void* chan; // If non-zero, sleeping on chan + int killed; // If non-zero, have been killed + struct file* ofile[NOFILE]; // Open files + struct inode* cwd; // Current directory + char name[16]; // Process name (debugging) +}; + +// Process memory is laid out contiguously, low addresses first: +// text +// original data and bss +// fixed-size stack +// expandable heap +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/run-debug.sh Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,2 @@ +#!/bin/sh +qemu-system-arm -M versatilepb -m 128 -cpu arm1176 -nographic -singlestep -d exec,cpu,guest_errors -D qemu.log -kernel kernel.elf -s -S
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/spinlock.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,81 @@ +// Mutual exclusion spin locks. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "arm.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "spinlock.h" + +void initlock(struct spinlock *lk, char *name) +{ + lk->name = name; + lk->locked = 0; + lk->cpu = 0; +} + +// For single CPU systems, there is no need for spinlock. +// Add the support when multi-processor is supported. + + +// Acquire the lock. +// Loops (spins) until the lock is acquired. +// Holding a lock for a long time may cause +// other CPUs to waste time spinning to acquire it. +void acquire(struct spinlock *lk) +{ + pushcli(); // disable interrupts to avoid deadlock. + lk->locked = 1; // set the lock status to make the kernel happy + +#if 0 + if(holding(lk)) + panic("acquire"); + + // The xchg is atomic. + // It also serializes, so that reads after acquire are not + // reordered before it. + while(xchg(&lk->locked, 1) != 0) + ; + + // Record info about lock acquisition for debugging. + lk->cpu = cpu; + getcallerpcs(get_fp(), lk->pcs); + +#endif +} + +// Release the lock. +void release(struct spinlock *lk) +{ +#if 0 + if(!holding(lk)) + panic("release"); + + lk->pcs[0] = 0; + lk->cpu = 0; + + // The xchg serializes, so that reads before release are + // not reordered after it. The 1996 PentiumPro manual (Volume 3, + // 7.2) says reads can be carried out speculatively and in + // any order, which implies we need to serialize here. + // But the 2007 Intel 64 Architecture Memory Ordering White + // Paper says that Intel 64 and IA-32 will not move a load + // after a store. So lock->locked = 0 would work here. + // The xchg being asm volatile ensures gcc emits it after + // the above assignments (and after the critical section). + xchg(&lk->locked, 0); +#endif + + lk->locked = 0; // set the lock state to keep the kernel happy + popcli(); +} + + +// Check whether this cpu is holding the lock. +int holding(struct spinlock *lock) +{ + return lock->locked; // && lock->cpu == cpus; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/spinlock.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,11 @@ +// Mutual exclusion lock. +struct spinlock { + uint locked; // Is the lock held? + + // For debugging: + char *name; // Name of lock. + struct cpu *cpu; // The cpu holding the lock. + uint pcs[10]; // The call stack (an array of program counters) + // that locked the lock. +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/start.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,193 @@ +// initialize section +#include "types.h" +#include "param.h" +#include "arm.h" +#include "mmu.h" +#include "defs.h" +#include "memlayout.h" + +void _uart_putc(int c) +{ + volatile uint8 * uart0 = (uint8*)UART0; + *uart0 = c; +} + + +void _puts (char *s) +{ + while (*s != '\0') { + _uart_putc(*s); + s++; + } +} + +void _putint (char *prefix, uint val, char* suffix) +{ + char* arr = "0123456789ABCDEF"; + int idx; + + if (prefix) { + _puts(prefix); + } + + for (idx = sizeof(val) * 8 - 4; idx >= 0; idx -= 4) { + _uart_putc(arr[(val >> idx) & 0x0F]); + } + + if (suffix) { + _puts(suffix); + } +} + + +// kernel page table, reserved in the kernel.ld +extern uint32 _kernel_pgtbl; +extern uint32 _user_pgtbl; + +uint32 *kernel_pgtbl = &_kernel_pgtbl; +uint32 *user_pgtbl = &_user_pgtbl; + +#define PDE_SHIFT 20 + +uint32 get_pde (uint32 virt) +{ + virt >>= PDE_SHIFT; + return kernel_pgtbl[virt]; +} + +// setup the boot page table: dev_mem whether it is device memory +void set_bootpgtbl (uint32 virt, uint32 phy, uint len, int dev_mem ) +{ + uint32 pde; + int idx; + + // convert all the parameters to indexes + virt >>= PDE_SHIFT; + phy >>= PDE_SHIFT; + len >>= PDE_SHIFT; + + for (idx = 0; idx < len; idx++) { + pde = (phy << PDE_SHIFT); + + if (!dev_mem) { + // normal memory, make it kernel-only, cachable, bufferable + pde |= (AP_KO << 10) | PE_CACHE | PE_BUF | KPDE_TYPE; + } else { + // device memory, make it non-cachable and non-bufferable + pde |= (AP_KO << 10) | KPDE_TYPE; + } + + // use different page table for user/kernel space + if (virt < NUM_UPDE) { + user_pgtbl[virt] = pde; + } else { + kernel_pgtbl[virt] = pde; + } + + virt++; + phy++; + } +} + +static void _flush_all (void) +{ + uint val = 0; + + // flush all TLB + asm("MCR p15, 0, %[r], c8, c7, 0" : :[r]"r" (val):); + + // invalid entire data and instruction cache + // asm ("MCR p15,0,%[r],c7,c5,0": :[r]"r" (val):); + // asm ("MCR p15,0,%[r],c7,c6,0": :[r]"r" (val):); +} + +void load_pgtlb (uint32* kern_pgtbl, uint32* user_pgtbl) +{ + uint ret; + char arch; + uint val; + + // read the main id register to make sure we are running on ARMv6 + asm("MRC p15, 0, %[r], c0, c0, 0": [r]"=r" (ret)::); + + if (ret >> 24 == 0x41) { + //_puts ("ARM-based CPU\n"); + } + + arch = (ret >> 16) & 0x0F; + + if ((arch != 7) && (arch != 0xF)) { + _puts ("need AARM v6 or higher\n"); + } + + // we need to check the cache/tlb etc., but let's skip it for now + + // set domain access control: all domain will be checked for permission + val = 0x55555555; + asm("MCR p15, 0, %[v], c3, c0, 0": :[v]"r" (val):); + + // set the page table base registers. We use two page tables: TTBR0 + // for user space and TTBR1 for kernel space + val = 32 - UADDR_BITS; + asm("MCR p15, 0, %[v], c2, c0, 2": :[v]"r" (val):); + + // set the kernel page table + val = (uint)kernel_pgtbl | 0x00; + asm("MCR p15, 0, %[v], c2, c0, 1": :[v]"r" (val):); + + // set the user page table + val = (uint)user_pgtbl | 0x00; + asm("MCR p15, 0, %[v], c2, c0, 0": :[v]"r" (val):); + + // ok, enable paging using read/modify/write + asm("MRC p15, 0, %[r], c1, c0, 0": [r]"=r" (val)::); + + val |= 0x80300D; // enable MMU, cache, write buffer, high vector tbl, + // disable subpage + asm("MCR p15, 0, %[r], c1, c0, 0": :[r]"r" (val):); + + _flush_all(); +} + +extern void * edata_entry; +extern void * svc_stktop; +extern void kmain (void); +extern void jump_stack (void); + +extern void * edata; +extern void * end; + +// clear the BSS section for the main kernel, see kernel.ld +void clear_bss (void) +{ + memset(&edata, 0x00, (uint)&end-(uint)&edata); +} + +void start (void) +{ + uint32 vectbl; + _puts("starting xv6 for ARM...\n"); + + // double map the low memory, required to enable paging + // we do not map all the physical memory + set_bootpgtbl(0, 0, INIT_KERNMAP, 0); + set_bootpgtbl(KERNBASE, 0, INIT_KERNMAP, 0); + + // vector table is in the middle of first 1MB (0xF000) + vectbl = P2V_WO (VEC_TBL & PDE_MASK); + + if (vectbl <= (uint)&end) { + cprintf ("error: vector table overlaps kernel\n"); + } + + set_bootpgtbl(VEC_TBL, 0, 1 << PDE_SHIFT, 0); + set_bootpgtbl(KERNBASE+DEVBASE, DEVBASE, DEV_MEM_SZ, 1); + + load_pgtlb (kernel_pgtbl, user_pgtbl); + jump_stack (); + + // We can now call normal kernel functions at high memory + clear_bss (); + + kmain (); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/stat.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,11 @@ +#define T_DIR 1 // Directory +#define T_FILE 2 // File +#define T_DEV 3 // Device + +struct stat { + short type; // Type of file + int dev; // File system's disk device + uint ino; // Inode number + short nlink; // Number of links to file + uint size; // Size of file in bytes +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/swtch.S Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,26 @@ +# Context switch +# +# void swtch(struct context **old, struct context *new); +# +# Save current register context in old +# and then load register context from new. +# The stack is as r4_svc-r12_svc, lr_svc, sp_usr, lr_usr, and pc_usr +.global swtch + +swtch: + STMFD r13!, {r4-r12, lr} // push svc r4-r12, lr to the stack + + # switch the stack + STR r13, [r0] // save current sp to the old PCB (**old) + MOV r13, r1 // load the next stack + + # load the new registers. pc_usr is not restored here because + # LDMFD^ will switch mode if pc_usr is loaded. We just simply + # pop it out as pc_usr is saved on the stack, and will be loaded + # when we return from kernel to user space (swi or interrupt return) + + LDMFD r13!, {r4-r12, lr} // pop svc r4-r12, lr + + # return to the caller + bx lr +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/syscall.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,164 @@ +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "arm.h" +#include "syscall.h" + +// User code makes a system call with INT T_SYSCALL. System call number +// in r0. Arguments on the stack, from the user call to the C library +// system call function. The saved user sp points to the first argument. + +// Fetch the int at addr from the current process. +int fetchint(uint addr, int *ip) +{ + if(addr >= proc->sz || addr+4 > proc->sz) { + return -1; + } + + *ip = *(int*)(addr); + return 0; +} + +// Fetch the nul-terminated string at addr from the current process. +// Doesn't actually copy the string - just sets *pp to point at it. +// Returns length of string, not including nul. +int fetchstr(uint addr, char **pp) +{ + char *s, *ep; + + if(addr >= proc->sz) { + return -1; + } + + *pp = (char*)addr; + ep = (char*)proc->sz; + + for(s = *pp; s < ep; s++) { + if(*s == 0) { + return s - *pp; + } + } + + return -1; +} + +// Fetch the nth (starting from 0) 32-bit system call argument. +// In our ABI, r0 contains system call index, r1-r4 contain parameters. +// now we support system calls with at most 4 parameters. +int argint(int n, int *ip) +{ + if (n > 3) { + panic ("too many system call parameters\n"); + } + + *ip = *(&proc->tf->r1 + n); + + return 0; +} + +// Fetch the nth word-sized system call argument as a pointer +// to a block of memory of size n bytes. Check that the pointer +// lies within the process address space. +int argptr(int n, char **pp, int size) +{ + int i; + + if(argint(n, &i) < 0) { + return -1; + } + + if((uint)i >= proc->sz || (uint)i+size > proc->sz) { + return -1; + } + + *pp = (char*)i; + return 0; +} + +// Fetch the nth word-sized system call argument as a string pointer. +// Check that the pointer is valid and the string is nul-terminated. +// (There is no shared writable memory, so the string can't change +// between this check and being used by the kernel.) +int argstr(int n, char **pp) +{ + int addr; + + if(argint(n, &addr) < 0) { + return -1; + } + + return fetchstr(addr, pp); +} + +extern int sys_chdir(void); +extern int sys_close(void); +extern int sys_dup(void); +extern int sys_exec(void); +extern int sys_exit(void); +extern int sys_fork(void); +extern int sys_fstat(void); +extern int sys_getpid(void); +extern int sys_kill(void); +extern int sys_link(void); +extern int sys_mkdir(void); +extern int sys_mknod(void); +extern int sys_open(void); +extern int sys_pipe(void); +extern int sys_read(void); +extern int sys_sbrk(void); +extern int sys_sleep(void); +extern int sys_unlink(void); +extern int sys_wait(void); +extern int sys_write(void); +extern int sys_uptime(void); + +static int (*syscalls[])(void) = { + [SYS_fork] sys_fork, + [SYS_exit] sys_exit, + [SYS_wait] sys_wait, + [SYS_pipe] sys_pipe, + [SYS_read] sys_read, + [SYS_kill] sys_kill, + [SYS_exec] sys_exec, + [SYS_fstat] sys_fstat, + [SYS_chdir] sys_chdir, + [SYS_dup] sys_dup, + [SYS_getpid] sys_getpid, + [SYS_sbrk] sys_sbrk, + [SYS_sleep] sys_sleep, + [SYS_uptime] sys_uptime, + [SYS_open] sys_open, + [SYS_write] sys_write, + [SYS_mknod] sys_mknod, + [SYS_unlink] sys_unlink, + [SYS_link] sys_link, + [SYS_mkdir] sys_mkdir, + [SYS_close] sys_close, +}; + +void syscall(void) +{ + int num; + int ret; + + num = proc->tf->r0; + + //cprintf ("syscall(%d) from %s(%d)\n", num, proc->name, proc->pid); + + if((num > 0) && (num <= NELEM(syscalls)) && syscalls[num]) { + ret = syscalls[num](); + + // in ARM, parameters to main (argc, argv) are passed in r0 and r1 + // do not set the return value if it is SYS_exec (the user program + // anyway does not expect us to return anything). + if (num != SYS_exec) { + proc->tf->r0 = ret; + } + } else { + cprintf("%d %s: unknown sys call %d\n", proc->pid, proc->name, num); + proc->tf->r0 = -1; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/syscall.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,22 @@ +// System call numbers +#define SYS_fork 1 +#define SYS_exit 2 +#define SYS_wait 3 +#define SYS_pipe 4 +#define SYS_read 5 +#define SYS_kill 6 +#define SYS_exec 7 +#define SYS_fstat 8 +#define SYS_chdir 9 +#define SYS_dup 10 +#define SYS_getpid 11 +#define SYS_sbrk 12 +#define SYS_sleep 13 +#define SYS_uptime 14 +#define SYS_open 15 +#define SYS_write 16 +#define SYS_mknod 17 +#define SYS_unlink 18 +#define SYS_link 19 +#define SYS_mkdir 20 +#define SYS_close 21
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sysfile.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,507 @@ +// +// File-system system calls. +// Mostly argument checking, since we don't trust +// user code, and calls into file.c and fs.c. +// + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "stat.h" +#include "mmu.h" +#include "proc.h" +#include "fs.h" +#include "file.h" +#include "fcntl.h" + +// Fetch the nth word-sized system call argument as a file descriptor +// and return both the descriptor and the corresponding struct file. +static int argfd(int n, int *pfd, struct file **pf) +{ + int fd; + struct file *f; + + if(argint(n, &fd) < 0) { + return -1; + } + + if(fd < 0 || fd >= NOFILE || (f=proc->ofile[fd]) == 0) { + return -1; + } + + if(pfd) { + *pfd = fd; + } + + if(pf) { + *pf = f; + } + + return 0; +} + +// Allocate a file descriptor for the given file. +// Takes over file reference from caller on success. +static int fdalloc(struct file *f) +{ + int fd; + + for(fd = 0; fd < NOFILE; fd++){ + if(proc->ofile[fd] == 0){ + proc->ofile[fd] = f; + return fd; + } + } + + return -1; +} + +int sys_dup(void) +{ + struct file *f; + int fd; + + if(argfd(0, 0, &f) < 0) { + return -1; + } + + if((fd=fdalloc(f)) < 0) { + return -1; + } + + filedup(f); + + return fd; +} + +int sys_read(void) +{ + struct file *f; + int n; + char *p; + + if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) { + return -1; + } + + return fileread(f, p, n); +} + +int sys_write(void) +{ + struct file *f; + int n; + char *p; + + if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) { + return -1; + } + + return filewrite(f, p, n); +} + +int sys_close(void) +{ + int fd; + struct file *f; + + if(argfd(0, &fd, &f) < 0) { + return -1; + } + + proc->ofile[fd] = 0; + fileclose(f); + + return 0; +} + +int sys_fstat(void) +{ + struct file *f; + struct stat *st; + + if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0) { + return -1; + } + + return filestat(f, st); +} + +// Create the path new as a link to the same inode as old. +int sys_link(void) +{ + char name[DIRSIZ], *new, *old; + struct inode *dp, *ip; + + if(argstr(0, &old) < 0 || argstr(1, &new) < 0) { + return -1; + } + + if((ip = namei(old)) == 0) { + return -1; + } + + begin_trans(); + + ilock(ip); + + if(ip->type == T_DIR){ + iunlockput(ip); + commit_trans(); + return -1; + } + + ip->nlink++; + iupdate(ip); + iunlock(ip); + + if((dp = nameiparent(new, name)) == 0) { + goto bad; + } + + ilock(dp); + + if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){ + iunlockput(dp); + goto bad; + } + + iunlockput(dp); + iput(ip); + + commit_trans(); + + return 0; + + bad: + ilock(ip); + ip->nlink--; + iupdate(ip); + iunlockput(ip); + commit_trans(); + return -1; +} + +// Is the directory dp empty except for "." and ".." ? +static int isdirempty(struct inode *dp) +{ + int off; + struct dirent de; + + for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){ + if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) { + panic("isdirempty: readi"); + } + + if(de.inum != 0) { + return 0; + } + } + return 1; +} + +//PAGEBREAK! +int sys_unlink(void) +{ + struct inode *ip, *dp; + struct dirent de; + char name[DIRSIZ], *path; + uint off; + + if(argstr(0, &path) < 0) { + return -1; + } + + if((dp = nameiparent(path, name)) == 0) { + return -1; + } + + begin_trans(); + + ilock(dp); + + // Cannot unlink "." or "..". + if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0) { + goto bad; + } + + if((ip = dirlookup(dp, name, &off)) == 0) { + goto bad; + } + + ilock(ip); + + if(ip->nlink < 1) { + panic("unlink: nlink < 1"); + } + + if(ip->type == T_DIR && !isdirempty(ip)){ + iunlockput(ip); + goto bad; + } + + memset(&de, 0, sizeof(de)); + + if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) { + panic("unlink: writei"); + } + + if(ip->type == T_DIR){ + dp->nlink--; + iupdate(dp); + } + + iunlockput(dp); + + ip->nlink--; + iupdate(ip); + iunlockput(ip); + + commit_trans(); + + return 0; + + bad: + iunlockput(dp); + commit_trans(); + return -1; +} + +static struct inode* create(char *path, short type, short major, short minor) +{ + uint off; + struct inode *ip, *dp; + char name[DIRSIZ]; + + if((dp = nameiparent(path, name)) == 0) { + return 0; + } + + ilock(dp); + + if((ip = dirlookup(dp, name, &off)) != 0){ + iunlockput(dp); + ilock(ip); + + if(type == T_FILE && ip->type == T_FILE) { + return ip; + } + + iunlockput(ip); + + return 0; + } + + if((ip = ialloc(dp->dev, type)) == 0) { + panic("create: ialloc"); + } + + ilock(ip); + ip->major = major; + ip->minor = minor; + ip->nlink = 1; + iupdate(ip); + + if(type == T_DIR){ // Create . and .. entries. + dp->nlink++; // for ".." + iupdate(dp); + + // No ip->nlink++ for ".": avoid cyclic ref count. + if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) { + panic("create dots"); + } + } + + if(dirlink(dp, name, ip->inum) < 0) { + panic("create: dirlink"); + } + + iunlockput(dp); + + return ip; +} + +int sys_open(void) +{ + char *path; + int fd, omode; + struct file *f; + struct inode *ip; + + if(argstr(0, &path) < 0 || argint(1, &omode) < 0) { + return -1; + } + + if(omode & O_CREATE){ + begin_trans(); + ip = create(path, T_FILE, 0, 0); + commit_trans(); + + if(ip == 0) { + return -1; + } + + } else { + if((ip = namei(path)) == 0) { + return -1; + } + + ilock(ip); + + if(ip->type == T_DIR && omode != O_RDONLY){ + iunlockput(ip); + return -1; + } + } + + if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){ + if(f) { + fileclose(f); + } + + iunlockput(ip); + return -1; + } + + iunlock(ip); + + f->type = FD_INODE; + f->ip = ip; + f->off = 0; + f->readable = !(omode & O_WRONLY); + f->writable = (omode & O_WRONLY) || (omode & O_RDWR); + + return fd; +} + +int sys_mkdir(void) +{ + char *path; + struct inode *ip; + + begin_trans(); + + if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){ + commit_trans(); + return -1; + } + + iunlockput(ip); + commit_trans(); + + return 0; +} + +int sys_mknod(void) +{ + struct inode *ip; + char *path; + int len; + int major, minor; + + begin_trans(); + + if((len=argstr(0, &path)) < 0 || + argint(1, &major) < 0 || argint(2, &minor) < 0 || + (ip = create(path, T_DEV, major, minor)) == 0){ + + commit_trans(); + return -1; + } + + iunlockput(ip); + commit_trans(); + + return 0; +} + +int sys_chdir(void) +{ + char *path; + struct inode *ip; + + if(argstr(0, &path) < 0 || (ip = namei(path)) == 0) { + return -1; + } + + ilock(ip); + + if(ip->type != T_DIR){ + iunlockput(ip); + return -1; + } + + iunlock(ip); + + iput(proc->cwd); + proc->cwd = ip; + + return 0; +} + +int sys_exec(void) +{ + char *path, *argv[MAXARG]; + int i; + uint uargv, uarg; + + if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){ + return -1; + } + + memset(argv, 0, sizeof(argv)); + + for(i=0;; i++){ + if(i >= NELEM(argv)) { + return -1; + } + + if(fetchint(uargv+4*i, (int*)&uarg) < 0) { + return -1; + } + + if(uarg == 0){ + argv[i] = 0; + break; + } + + if(fetchstr(uarg, &argv[i]) < 0) { + return -1; + } + } + + return exec(path, argv); +} + +int sys_pipe(void) +{ + int *fd; + struct file *rf, *wf; + int fd0, fd1; + + if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0) { + return -1; + } + + if(pipealloc(&rf, &wf) < 0) { + return -1; + } + + fd0 = -1; + + if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){ + if(fd0 >= 0) { + proc->ofile[fd0] = 0; + } + + fileclose(rf); + fileclose(wf); + + return -1; + } + + fd[0] = fd0; + fd[1] = fd1; + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sysproc.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,96 @@ +#include "types.h" +#include "arm.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" + +int sys_fork(void) +{ + return fork(); +} + +int sys_exit(void) +{ + exit(); + return 0; // not reached +} + +int sys_wait(void) +{ + return wait(); +} + +int sys_kill(void) +{ + int pid; + + if(argint(0, &pid) < 0) { + return -1; + } + + return kill(pid); +} + +int sys_getpid(void) +{ + return proc->pid; +} + +int sys_sbrk(void) +{ + int addr; + int n; + + if(argint(0, &n) < 0) { + return -1; + } + + addr = proc->sz; + + if(growproc(n) < 0) { + return -1; + } + + return addr; +} + +int sys_sleep(void) +{ + int n; + uint ticks0; + + if(argint(0, &n) < 0) { + return -1; + } + + acquire(&tickslock); + + ticks0 = ticks; + + while(ticks - ticks0 < n){ + if(proc->killed){ + release(&tickslock); + return -1; + } + + sleep(&ticks, &tickslock); + } + + release(&tickslock); + return 0; +} + +// return how many clock tick interrupts have occurred +// since start. +int sys_uptime(void) +{ + uint xticks; + + acquire(&tickslock); + xticks = ticks; + release(&tickslock); + + return xticks; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tools/Makefile Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,12 @@ +include ../makefile.inc + +CFLAGS = -Werror -Wall +CFLAGS += -iquote ../ + +all: mkfs + +mkfs: mkfs.c + $(HOSTCC) $(CFLAGS) -o $@ $^ + +clean: + rm -f mkfs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tools/mkfs.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,298 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> + +#define stat xv6_stat // avoid clash with host struct stat +#include "types.h" +#include "fs.h" +#include "stat.h" +#include "param.h" + +#define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) + +int nblocks = 985; +int nlog = LOGSIZE; +int ninodes = 200; +int size = 1024; + +int fsfd; +struct superblock sb; +char zeroes[512]; +uint freeblock; +uint usedblocks; +uint bitblocks; +uint freeinode = 1; + +void balloc(int); +void wsect(uint, void*); +void winode(uint, struct dinode*); +void rinode(uint inum, struct dinode *ip); +void rsect(uint sec, void *buf); +uint ialloc(ushort type); +void iappend(uint inum, void *p, int n); + +// convert to intel byte order +ushort +xshort(ushort x) +{ + ushort y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + return y; +} + +uint +xint(uint x) +{ + uint y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + a[2] = x >> 16; + a[3] = x >> 24; + return y; +} + +int +main(int argc, char *argv[]) +{ + int i, cc, fd; + uint rootino, inum, off; + struct dirent de; + char buf[512]; + struct dinode din; + + + static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); + + if(argc < 2){ + fprintf(stderr, "Usage: mkfs fs.img files...\n"); + exit(1); + } + + assert((512 % sizeof(struct dinode)) == 0); + assert((512 % sizeof(struct dirent)) == 0); + + fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); + if(fsfd < 0){ + perror(argv[1]); + exit(1); + } + + sb.size = xint(size); + sb.nblocks = xint(nblocks); // so whole disk is size sectors + sb.ninodes = xint(ninodes); + sb.nlog = xint(nlog); + + bitblocks = size/(512*8) + 1; + usedblocks = ninodes / IPB + 3 + bitblocks; + freeblock = usedblocks; + + printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks, + bitblocks, ninodes/IPB + 1, freeblock, nlog, nblocks+usedblocks+nlog); + + assert(nblocks + usedblocks + nlog == size); + + for(i = 0; i < nblocks + usedblocks + nlog; i++) + wsect(i, zeroes); + + memset(buf, 0, sizeof(buf)); + memmove(buf, &sb, sizeof(sb)); + wsect(1, buf); + + rootino = ialloc(T_DIR); + assert(rootino == ROOTINO); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, "."); + iappend(rootino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, ".."); + iappend(rootino, &de, sizeof(de)); + + for(i = 2; i < argc; i++){ + assert(index(argv[i], '/') == 0); + + if((fd = open(argv[i], 0)) < 0){ + perror(argv[i]); + exit(1); + } + + // Skip leading _ in name when writing to file system. + // The binaries are named _rm, _cat, etc. to keep the + // build operating system from trying to execute them + // in place of system binaries like rm and cat. + if(argv[i][0] == '_') + ++argv[i]; + + inum = ialloc(T_FILE); + + bzero(&de, sizeof(de)); + de.inum = xshort(inum); + strncpy(de.name, argv[i], DIRSIZ); + iappend(rootino, &de, sizeof(de)); + + while((cc = read(fd, buf, sizeof(buf))) > 0) + iappend(inum, buf, cc); + + close(fd); + } + + // fix size of root inode dir + rinode(rootino, &din); + off = xint(din.size); + off = ((off/BSIZE) + 1) * BSIZE; + din.size = xint(off); + winode(rootino, &din); + + balloc(usedblocks); + + exit(0); +} + +void +wsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * 512L, 0) != sec * 512L){ + perror("lseek"); + exit(1); + } + if(write(fsfd, buf, 512) != 512){ + perror("write"); + exit(1); + } +} + +uint +i2b(uint inum) +{ + return (inum / IPB) + 2; +} + +void +winode(uint inum, struct dinode *ip) +{ + char buf[512]; + uint bn; + struct dinode *dip; + + bn = i2b(inum); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *dip = *ip; + wsect(bn, buf); +} + +void +rinode(uint inum, struct dinode *ip) +{ + char buf[512]; + uint bn; + struct dinode *dip; + + bn = i2b(inum); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *ip = *dip; +} + +void +rsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * 512L, 0) != sec * 512L){ + perror("lseek"); + exit(1); + } + if(read(fsfd, buf, 512) != 512){ + perror("read"); + exit(1); + } +} + +uint +ialloc(ushort type) +{ + uint inum = freeinode++; + struct dinode din; + + bzero(&din, sizeof(din)); + din.type = xshort(type); + din.nlink = xshort(1); + din.size = xint(0); + winode(inum, &din); + return inum; +} + +void +balloc(int used) +{ + uchar buf[512]; + int i; + + printf("balloc: first %d blocks have been allocated\n", used); + assert(used < 512*8); + bzero(buf, 512); + for(i = 0; i < used; i++){ + buf[i/8] = buf[i/8] | (0x1 << (i%8)); + } + printf("balloc: write bitmap block at sector %zu\n", ninodes/IPB + 3); + wsect(ninodes / IPB + 3, buf); +} + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +void +iappend(uint inum, void *xp, int n) +{ + char *p = (char*)xp; + uint fbn, off, n1; + struct dinode din; + char buf[512]; + uint indirect[NINDIRECT]; + uint x; + + rinode(inum, &din); + + off = xint(din.size); + while(n > 0){ + fbn = off / 512; + assert(fbn < MAXFILE); + if(fbn < NDIRECT){ + if(xint(din.addrs[fbn]) == 0){ + din.addrs[fbn] = xint(freeblock++); + usedblocks++; + } + x = xint(din.addrs[fbn]); + } else { + if(xint(din.addrs[NDIRECT]) == 0){ + // printf("allocate indirect block\n"); + din.addrs[NDIRECT] = xint(freeblock++); + usedblocks++; + } + // printf("read indirect block\n"); + rsect(xint(din.addrs[NDIRECT]), (char*)indirect); + if(indirect[fbn - NDIRECT] == 0){ + indirect[fbn - NDIRECT] = xint(freeblock++); + usedblocks++; + wsect(xint(din.addrs[NDIRECT]), (char*)indirect); + } + x = xint(indirect[fbn-NDIRECT]); + } + n1 = min(n, (fbn + 1) * 512 - off); + rsect(x, buf); + bcopy(p, buf + off - (fbn * 512), n1); + wsect(x, buf); + n -= n1; + off += n1; + p += n1; + } + din.size = xint(off); + winode(inum, &din); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/trap.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,154 @@ +// The ARM UART, a memory mapped device +#include "types.h" +#include "defs.h" +#include "param.h" +#include "arm.h" +#include "proc.h" + +// trap routine +void swi_handler (struct trapframe *r) +{ + if (proc->killed) + exit(); + proc->tf = r; + syscall (); + if (proc->killed) + exit(); +} + +// trap routine +void irq_handler (struct trapframe *r) +{ + // proc points to the current process. If the kernel is + // running scheduler, proc is NULL. + if (proc != NULL) { + proc->tf = r; + } + + pic_dispatch (r); +} + +// trap routine +void reset_handler (struct trapframe *r) +{ + cli(); + cprintf ("reset at: 0x%x \n", r->pc); +} + +// trap routine +void und_handler (struct trapframe *r) +{ + cli(); + cprintf ("und at: 0x%x \n", r->pc); +} + +// trap routine +void dabort_handler (struct trapframe *r) +{ + uint dfs, fa; + + cli(); + + // read data fault status register + asm("MRC p15, 0, %[r], c5, c0, 0": [r]"=r" (dfs)::); + + // read the fault address register + asm("MRC p15, 0, %[r], c6, c0, 0": [r]"=r" (fa)::); + + cprintf ("data abort: instruction 0x%x, fault addr 0x%x, reason 0x%x \n", + r->pc, fa, dfs); + + dump_trapframe (r); +} + +// trap routine +void iabort_handler (struct trapframe *r) +{ + uint ifs; + + // read fault status register + asm("MRC p15, 0, %[r], c5, c0, 0": [r]"=r" (ifs)::); + + cli(); + cprintf ("prefetch abort at: 0x%x (reason: 0x%x)\n", r->pc, ifs); + dump_trapframe (r); +} + +// trap routine +void na_handler (struct trapframe *r) +{ + cli(); + cprintf ("n/a at: 0x%x \n", r->pc); +} + +// trap routine +void fiq_handler (struct trapframe *r) +{ + cli(); + cprintf ("fiq at: 0x%x \n", r->pc); +} + +// low-level init code: in real hardware, lower memory is usually mapped +// to flash during startup, we need to remap it to SDRAM +void trap_init ( ) +{ + volatile uint32 *ram_start; + char *stk; + int i; + uint modes[] = {FIQ_MODE, IRQ_MODE, ABT_MODE, UND_MODE}; + + // the opcode of PC relative load (to PC) instruction LDR pc, [pc,...] + static uint32 const LDR_PCPC = 0xE59FF000U; + + // create the excpetion vectors + ram_start = (uint32*)VEC_TBL; + + ram_start[0] = LDR_PCPC | 0x18; // Reset (SVC) + ram_start[1] = LDR_PCPC | 0x18; // Undefine Instruction (UND) + ram_start[2] = LDR_PCPC | 0x18; // Software interrupt (SVC) + ram_start[3] = LDR_PCPC | 0x18; // Prefetch abort (ABT) + ram_start[4] = LDR_PCPC | 0x18; // Data abort (ABT) + ram_start[5] = LDR_PCPC | 0x18; // Not assigned (-) + ram_start[6] = LDR_PCPC | 0x18; // IRQ (IRQ) + ram_start[7] = LDR_PCPC | 0x18; // FIQ (FIQ) + + ram_start[8] = (uint32)trap_reset; + ram_start[9] = (uint32)trap_und; + ram_start[10] = (uint32)trap_swi; + ram_start[11] = (uint32)trap_iabort; + ram_start[12] = (uint32)trap_dabort; + ram_start[13] = (uint32)trap_na; + ram_start[14] = (uint32)trap_irq; + ram_start[15] = (uint32)trap_fiq; + + // initialize the stacks for different mode + for (i = 0; i < sizeof(modes)/sizeof(uint); i++) { + stk = alloc_page (); + + if (stk == NULL) { + panic("failed to alloc memory for irq stack"); + } + + set_stk (modes[i], (uint)stk); + } +} + +void dump_trapframe (struct trapframe *tf) +{ + cprintf ("r14_svc: 0x%x\n", tf->r14_svc); + cprintf (" spsr: 0x%x\n", tf->spsr); + cprintf (" r0: 0x%x\n", tf->r0); + cprintf (" r1: 0x%x\n", tf->r1); + cprintf (" r2: 0x%x\n", tf->r2); + cprintf (" r3: 0x%x\n", tf->r3); + cprintf (" r4: 0x%x\n", tf->r4); + cprintf (" r5: 0x%x\n", tf->r5); + cprintf (" r6: 0x%x\n", tf->r6); + cprintf (" r7: 0x%x\n", tf->r7); + cprintf (" r8: 0x%x\n", tf->r8); + cprintf (" r9: 0x%x\n", tf->r9); + cprintf (" r10: 0x%x\n", tf->r10); + cprintf (" r11: 0x%x\n", tf->r11); + cprintf (" r12: 0x%x\n", tf->r12); + cprintf (" pc: 0x%x\n", tf->pc); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/trap_asm.S Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,165 @@ +//low-level trap handler glue code +#include "arm.h" + +.text +.code 32 + +.global trap_swi +.global trap_irq +.global trap_reset +.global trap_und +.global trap_iabort +.global trap_dabort +.global trap_na +.global trap_fiq +.global trapret + + +# handle SWI, we allow nested SWI +trap_swi: + # build trapframe on the stack + STMFD sp!, {r0-r12, r14} // save context + MRS r2, spsr // copy spsr to r2 + STMFD r13!, {r2} // save r2(spsr) to the stack + STMFD r13!, {r14} // save r14 again to have one uniform trapframe + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # call traps (trapframe *fp) + MOV r0, r13 // copy r13_svc to r0 + BL swi_handler // branch to the isr_swi + + # restore states +trapret: + LDMFD r13, {sp, lr}^ // restore user mode sp and lr + ADD r13, r13, #8 + LDMFD r13!, {r14} // restore r14 + LDMFD r13!, {r2} // restore spsr + MSR spsr_cxsf, r2 + LDMFD r13!,{r0-r12, pc}^ // restore context and return + + +# handle IRQ, we allow nested IRQs +trap_irq: + # save a few registers to the irq stack to provide scratch regs. + # r14 (lr_irq) contains the instruction (pc) to return to, need to + # save it on the stack as r14 is banked + SUB r14, r14, #4 // r14 (lr) contains the interrupted PC + STMFD r13!, {r0-r2, r14} // + MRS r1, spsr // save spsr_irq + MOV r0, r13 // save stack stop (r13_irq) + ADD r13, r13, #16 // reset the IRQ stack + + # switch to the SVC mode + MRS r2, cpsr + BIC r2, r2, #MODE_MASK + ORR r2, r2, #SVC_MODE + MSR cpsr_cxsf, r2 + + # now, in SVC mode, sp, lr, pc (r13, r14, r15) are all banked + # build the trap frame + LDR r2, [r0, #12] // read the r14_irq, then save it + STMFD r13!, {r2} + STMFD r13!, {r3-r12} // r4-r12 are preserved (non-banked) + LDMFD r0, {r3-r5} // copy r0-r2 over from irq stack + STMFD r13!, {r3-r5} + STMFD r13!, {r1} // save spsr + STMFD r13!, {lr} // save r14_svc + + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # get the parameters, then call the handler + MOV r0, r13 // points to + BL irq_handler + + # restore the previous status + B trapret + +# handle reset/undefine instruction/abort/not-assigned/fiq +# these handler does not allow nested handling +trap_reset: + MOV r14, #0 // lr: not defined on reset + STMFD r13!, {r0-r12, r14} + MRS r2, spsr // copy spsr to r2 + STMFD r13!, {r2} // save r2(spsr) to the stack + STMFD r13!, {r14} // save r14 again (it is not really correct) + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # call traps (trapframe *fp) + MOV r0, r13 // copy r13_svc to r0 + BL reset_handler + B . + +trap_und: + STMFD r13!, {r0-r12, r14} // lr: instruction after the undefined + MRS r2, spsr // copy spsr to r2 + STMFD r13!, {r2} // save r2(spsr) to the stack + STMFD r13!, {r14} // save r14 again (it is not really correct) + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # call traps (trapframe *fp) + MOV r0, r13 // save trapframe as the first parameter + BL und_handler + B . + +trap_iabort: + SUB r14, r14, #4 // lr: instruction causing the abort + STMFD r13!, {r0-r12, r14} + MRS r2, spsr // copy spsr to r2 + STMFD r13!, {r2} // save r2(spsr) to the stack + STMFD r13!, {r14} // save r14 again (it is not really correct) + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # call traps (trapframe *fp) + MOV r0, r13 // save trapframe as the first parameter + BL iabort_handler + B . + +trap_dabort: + SUB r14, r14, #8 // lr: instruction causing the abort + STMFD r13!, {r0-r12, r14} + MRS r2, spsr // copy spsr to r2 + STMFD r13!, {r2} // save r2(spsr) to the stack + STMFD r13!, {r14} // save r14 again (it is not really correct) + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # call traps (trapframe *fp) + MOV r0, r13 // save trapframe as the first parameter + BL dabort_handler + MOV r0, #2 // #SYS_exit + SWI 0x00 + B exit + #B . // trapret + +trap_na: + STMFD r13!, {r0-r12, r14} // should never happen, hardware error + MRS r2, spsr // copy spsr to r2 + STMFD r13!, {r2} // save r2(spsr) to the stack + STMFD r13!, {r14} // save r14 again (it is not really correct) + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # call traps (trapframe *fp) + MOV r0, r13 // save trapframe as the first parameter + BL na_handler + B . + +trap_fiq: + SUB r14, r14, #4 // lr: return address after the fiq handler + STMFD r13!, {r0-r12, r14} + MRS r2, spsr // copy spsr to r2 + STMFD r13!, {r2} // save r2(spsr) to the stack + STMFD r13!, {r14} // save r14 again (it is not really correct) + STMFD r13, {sp, lr}^ // save user mode sp and lr + SUB r13, r13, #8 + + # call traps (trapframe *fp) + MOV r0, r13 // save trapframe as the first parameter + BL fiq_handler + B . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/types.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,10 @@ +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +#ifndef NULL +#define NULL ((void*)0) +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/Makefile Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,49 @@ +include ../makefile.inc + +CFLAGS += -iquote ../ +ASFLAGS += -I ../ +ULIB = ulib.o usys.o printf.o umalloc.o + +MKFS = ../tools/mkfs +FS_IMAGE = ../build/fs.img + +UPROGS=\ + _cat\ + _echo\ + _grep\ + _init\ + _kill\ + _ln\ + _ls\ + _mkdir\ + _rm\ + _sh\ + _stressfs\ + _usertests\ + _wc\ + _zombie\ + _hello\ + _forktest\ + + +all: $(FS_IMAGE) + +_%: %.o $(ULIB) + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ -L ../ $(LIBGCC) + $(OBJDUMP) -S $@ > $*.asm + $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym + +_forktest: forktest.o $(ULIB) + # forktest has less library code linked in - needs to be small + # in order to be able to max out the proc table. + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o + $(OBJDUMP) -S _forktest > forktest.asm + +$(FS_IMAGE): $(MKFS) $(UPROGS) + $(MKFS) $@ $(UPROGS) UNIX + $(OBJDUMP) -S usys.o > usys.asm + +clean: + rm -f *.o *.d *.asm *.sym $(FS_IMAGE) \ + .gdbinit \ + $(UPROGS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/UNIX Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,46 @@ + History and Timeline + "...the number of UNIX installations has grown to 10, with more expected..." + - Dennis Ritchie and Ken Thompson, June 1972 + + "... When BTL withdrew from the project, they needed to rewrite an operating + system (OS) in order to play space war on another smaller machine (a DEC + PDP-7 [Programmed Data Processor] with 4K memory for user + programs). The result was a system which a punning colleague called + UNICS (UNiplexed Information and Computing Service)--an + 'emasculated Multics'; no one recalls whose idea the change to UNIX was" + Source: A brief look at the early history + + +Since it began to escape from AT&T's Bell Laboratories in the early 1970's, the success of the UNIX operating system has led to many different versions: recipients of the (at that time free) UNIX system code all began developing their own different versions in their own, different, ways for use and sale. Universities, research institutes, government bodies and computer companies all began using the powerful UNIX system to develop many of the technologies which today are part of a UNIX system. + +Computer aided design, manufacturing control systems, laboratory simulations, even the Internet itself, all began life with and because of UNIX systems. Today, without UNIX systems, the Internet would come to a screeching halt. Most telephone calls could not be made, electronic commerce would grind to a halt and there would have never been "Jurassic Park"! + +By the late 1970's, a ripple effect had come into play. By now the under- and post-graduate students whose lab work had pioneered these new applications of technology were attaining management and decision-making positions inside the computer system suppliers and among its customers. And they wanted to continue using UNIX systems. + +Soon all the large vendors, and many smaller ones, were marketing their own, diverging, versions of the UNIX system optimized for their own computer architectures and boasting many different strengths and features. Customers found that, although UNIX systems were available everywhere, they seldom were able to interwork or co-exist without significant investment of time and effort to make them work effectively. The trade mark UNIX was ubiquitous, but it was applied to a multitude of different, incompatible products. + +In the early 1980's, the market for UNIX systems had grown enough to be noticed by industry analysts and researchers. Now the question was no longer "What is a UNIX system?" but "Is a UNIX system suitable for business and commerce?" + +Throughout the early and mid-1980's, the debate about the strengths and weaknesses of UNIX systems raged, often fuelled by the utterances of the vendors themselves who sought to protect their profitable proprietary system sales by talking UNIX systems down. And, in an effort to further differentiate their competing UNIX system products, they kept developing and adding features of their own. + +In 1984, another factor brought added attention to UNIX systems. A group of vendors concerned about the continuing encroachment into their markets and control of system interfaces by the larger companies, developed the concept of "open systems." + +Open systems were those that would meet agreed specifications or standards. This resulted in the formation of X/Open Company Ltd whose remit was, and today in the guise of The Open Group remains, to define a comprehensive open systems environment. Open systems, they declared, would save on costs, attract a wider portfolio of applications and competition on equal terms. X/Open chose the UNIX system as the platform for the basis of open systems. + +Although UNIX was still owned by AT&T, the company did little commercially with it until the mid-1980's. Then the spotlight of X/Open showed clearly that a single, standard version of the UNIX system would be in the wider interests of the industry and its customers. The question now was, "which version?". + +In a move intended to unify the market in 1987, AT&T announced a pact with Sun Microsystems, the leading proponent of the Berkeley derived strain of UNIX. However, the rest of the industry viewed the development with considerable concern. Believing that their own markets were under threat they clubbed together to develop their own "new" open systems operating system. Their new organization was called the Open Software Foundation (OSF). In response to this, the AT&T/Sun faction formed UNIX International. + +The ensuing "UNIX wars" divided the system vendors between these two camps clustered around the two dominant UNIX system technologies: AT&T's System V and the OSF system called OSF/1. In the meantime, X/Open Company held the center ground. It continued the process of standardizing the APIs necessary for an open operating system specification. + +In addition, it looked at areas of the system beyond the operating system level where a standard approach would add value for supplier and customer alike, developing or adopting specifications for languages, database connectivity, networking and mainframe interworking. The results of this work were published in successive X/Open Portability Guides. + +XPG 4 was released in October 1992. During this time, X/Open had put in place a brand program based on vendor guarantees and supported by testing. Since the publication of XPG4, X/Open has continued to broaden the scope of open systems specifications in line with market requirements. As the benefits of the X/Open brand became known and understood, many large organizations began using X/Open as the basis for system design and procurement. By 1993, over $7 billion had been spent on X/Open branded systems. By the start of 1997 that figure has risen to over $23 billion. To date, procurements referencing the Single UNIX Specification amount to over $5.2 billion. + +In early 1993, AT&T sold it UNIX System Laboratories to Novell which was looking for a heavyweight operating system to link to its NetWare product range. At the same time, the company recognized that vesting control of the definition (specification) and trademark with a vendor-neutral organization would further facilitate the value of UNIX as a foundation of open systems. So the constituent parts of the UNIX System (source code/technology and specification/trademark), previously owned by a single entity are now quite separate + +In 1995 X/Open introduced the UNIX 95 brand for computer systems guaranteed to meet the Single UNIX Specification. The Single UNIX Specification brand program has now achieved critical mass: vendors whose products have met the demanding criteria now account for the majority of UNIX systems by value. + +For over twenty years, since the inception of X/Open, UNIX had been closely linked with open systems. X/Open, now The Open Group, continues to develop and evolve the Single UNIX Specification and associated brand program on behalf of the IT community. The freeing of the specification of the interfaces from the technology is allowing many systems to support the UNIX philosophy of small, often simple tools , that can be combined in many ways to perform often complex tasks. The stability of the core interfaces preserves existing investment, and is allowing development of a rich set of software tools. The Open Source movement is building on this stable foundation and is creating a resurgence of enthusiasm for the UNIX philosophy. In many ways Open Source can be seen as the true delivery of Open Systems that will ensure it continues to go from strength to strength. + +Source: http://www.unix.org/what_is_unix/history_timeline.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/cat.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,39 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +char buf[512]; + +void +cat(int fd) +{ + int n; + + while((n = read(fd, buf, sizeof(buf))) > 0) + write(1, buf, n); + if(n < 0){ + printf(1, "cat: read error\n"); + exit(); + } +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + + if(argc <= 1){ + cat(0); + exit(); + } + + for(i = 1; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf(1, "cat: cannot open %s\n", argv[i]); + exit(); + } + cat(fd); + close(fd); + } + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/echo.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,13 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + int i; + + for(i = 1; i < argc; i++) + printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/forktest.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,59 @@ +// Test that fork fails gracefully. +// Tiny executable so that the limit can be filling the proc table. + +#include "types.h" +#include "stat.h" +#include "user.h" + +#define N 1000 + +void +printf(int fd, char *s, ...) +{ + write(fd, s, strlen(s)); +} + +void +forktest(void) +{ + int n, pid; + + printf(1, "fork test\n"); + + for(n=0; n<N; n++){ + pid = fork(); + if(pid < 0) + { + printf(1, "fork failed!\n"); + break; + } + if(pid == 0) + exit(); + } + + if(n == N){ + printf(1, "fork claimed to work N times!\n", N); + exit(); + } + + for(; n > 0; n--){ + if(wait() < 0){ + printf(1, "wait stopped early\n"); + exit(); + } + } + + if(wait() != -1){ + printf(1, "wait got too many\n"); + exit(); + } + + printf(1, "fork test OK\n"); +} + +int +main(void) +{ + forktest(); + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/grep.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,106 @@ +// Simple grep. Only supports ^ . * $ operators. + +#include "types.h" +#include "stat.h" +#include "user.h" + +char buf[1024]; +int match(char*, char*); + +void +grep(char *pattern, int fd) +{ + int n, m; + char *p, *q; + + m = 0; + while((n = read(fd, buf+m, sizeof(buf)-m)) > 0){ + m += n; + p = buf; + while((q = strchr(p, '\n')) != 0){ + *q = 0; + if(match(pattern, p)){ + *q = '\n'; + write(1, p, q+1 - p); + } + p = q+1; + } + if(p == buf) + m = 0; + if(m > 0){ + m -= p - buf; + memmove(buf, p, m); + } + } +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + char *pattern; + + if(argc <= 1){ + printf(2, "usage: grep pattern [file ...]\n"); + exit(); + } + pattern = argv[1]; + + if(argc <= 2){ + grep(pattern, 0); + exit(); + } + + for(i = 2; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf(1, "grep: cannot open %s\n", argv[i]); + exit(); + } + grep(pattern, fd); + close(fd); + } + exit(); +} + +// Regexp matcher from Kernighan & Pike, +// The Practice of Programming, Chapter 9. + +int matchhere(char*, char*); +int matchstar(int, char*, char*); + +int +match(char *re, char *text) +{ + if(re[0] == '^') + return matchhere(re+1, text); + do{ // must look at empty string + if(matchhere(re, text)) + return 1; + }while(*text++ != '\0'); + return 0; +} + +// matchhere: search for re at beginning of text +int matchhere(char *re, char *text) +{ + if(re[0] == '\0') + return 1; + if(re[1] == '*') + return matchstar(re[0], re+2, text); + if(re[0] == '$' && re[1] == '\0') + return *text == '\0'; + if(*text!='\0' && (re[0]=='.' || re[0]==*text)) + return matchhere(re+1, text+1); + return 0; +} + +// matchstar: search for c*re at beginning of text +int matchstar(int c, char *re, char *text) +{ + do{ // a * matches zero or more instances + if(matchhere(re, text)) + return 1; + }while(*text!='\0' && (*text++==c || c=='.')); + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/hello.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,13 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + int i = 0; + for (i = fork(); i > 0; i = fork()) { + printf(1, "Hello, world! %d\n", i); + } + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/init.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,37 @@ +// init: The initial user-level program + +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fcntl.h" + +char *argv[] = { "sh", 0 }; + +int +main(void) +{ + int pid, wpid; + + if(open("console", O_RDWR) < 0){ + mknod("console", 1, 1); + open("console", O_RDWR); + } + dup(0); // stdout + dup(0); // stderr + + for(;;){ + printf(1, "init: starting sh\n"); + pid = fork(); + if(pid < 0){ + printf(1, "init: fork failed\n"); + exit(); + } + if(pid == 0){ + exec("sh", argv); + printf(1, "init: exec sh failed\n"); + exit(); + } + while((wpid=wait()) >= 0 && wpid != pid) + printf(1, "zombie!\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/kill.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,17 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char **argv) +{ + int i; + + if(argc < 1){ + printf(2, "usage: kill pid...\n"); + exit(); + } + for(i=1; i<argc; i++) + kill(atoi(argv[i])); + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/ln.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,15 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + if(argc != 3){ + printf(2, "Usage: ln old new\n"); + exit(); + } + if(link(argv[1], argv[2]) < 0) + printf(2, "link %s %s: failed\n", argv[1], argv[2]); + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/ls.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,85 @@ +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fs.h" + +char* +fmtname(char *path) +{ + static char buf[DIRSIZ+1]; + char *p; + + // Find first character after last slash. + for(p=path+strlen(path); p >= path && *p != '/'; p--) + ; + p++; + + // Return blank-padded name. + if(strlen(p) >= DIRSIZ) + return p; + memmove(buf, p, strlen(p)); + memset(buf+strlen(p), ' ', DIRSIZ-strlen(p)); + return buf; +} + +void +ls(char *path) +{ + char buf[512], *p; + int fd; + struct dirent de; + struct stat st; + + if((fd = open(path, 0)) < 0){ + printf(2, "ls: cannot open %s\n", path); + return; + } + + if(fstat(fd, &st) < 0){ + printf(2, "ls: cannot stat %s\n", path); + close(fd); + return; + } + + switch(st.type){ + case T_FILE: + printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size); + break; + + case T_DIR: + if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ + printf(1, "ls: path too long\n"); + break; + } + strcpy(buf, path); + p = buf+strlen(buf); + *p++ = '/'; + while(read(fd, &de, sizeof(de)) == sizeof(de)){ + if(de.inum == 0) + continue; + memmove(p, de.name, DIRSIZ); + p[DIRSIZ] = 0; + if(stat(buf, &st) < 0){ + printf(1, "ls: cannot stat %s\n", buf); + continue; + } + printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); + } + break; + } + close(fd); +} + +int +main(int argc, char *argv[]) +{ + int i; + + if(argc < 2){ + ls("."); + exit(); + } + for(i=1; i<argc; i++) + ls(argv[i]); + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/mkdir.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,23 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + int i; + + if(argc < 2){ + printf(2, "Usage: mkdir files...\n"); + exit(); + } + + for(i = 1; i < argc; i++){ + if(mkdir(argv[i]) < 0){ + printf(2, "mkdir: %s failed to create\n", argv[i]); + break; + } + } + + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/printf.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,85 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +static void +putc(int fd, char c) +{ + write(fd, &c, 1); +} + +static void +printint(int fd, int xx, int base, int sgn) +{ + static char digits[] = "0123456789ABCDEF"; + char buf[16]; + int i, neg; + uint x; + + neg = 0; + if(sgn && xx < 0){ + neg = 1; + x = -xx; + } else { + x = xx; + } + + i = 0; + do{ + buf[i++] = digits[x % base]; + }while((x /= base) != 0); + if(neg) + buf[i++] = '-'; + + while(--i >= 0) + putc(fd, buf[i]); +} + +// Print to the given fd. Only understands %d, %x, %p, %s. +void +printf(int fd, char *fmt, ...) +{ + char *s; + int c, i, state; + uint *ap; + + state = 0; + ap = (uint*)(void*)&fmt + 1; + for(i = 0; fmt[i]; i++){ + c = fmt[i] & 0xff; + if(state == 0){ + if(c == '%'){ + state = '%'; + } else { + putc(fd, c); + } + } else if(state == '%'){ + if(c == 'd'){ + printint(fd, *ap, 10, 1); + ap++; + } else if(c == 'x' || c == 'p'){ + printint(fd, *ap, 16, 0); + ap++; + } else if(c == 's'){ + s = (char*)*ap; + ap++; + if(s == 0) + s = "(null)"; + while(*s != 0){ + putc(fd, *s); + s++; + } + } else if(c == 'c'){ + putc(fd, *ap); + ap++; + } else if(c == '%'){ + putc(fd, c); + } else { + // Unknown % sequence. Print it to draw attention. + putc(fd, '%'); + putc(fd, c); + } + state = 0; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/rm.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,23 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + int i; + + if(argc < 2){ + printf(2, "Usage: rm files...\n"); + exit(); + } + + for(i = 1; i < argc; i++){ + if(unlink(argv[i]) < 0){ + printf(2, "rm: %s failed to delete\n", argv[i]); + break; + } + } + + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/sh.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,494 @@ +// Shell. + +#include "types.h" +#include "user.h" +#include "fcntl.h" + +// Parsed command representation +#define EXEC 1 +#define REDIR 2 +#define PIPE 3 +#define LIST 4 +#define BACK 5 + +#define MAXARGS 10 + +struct cmd { + int type; +}; + +struct execcmd { + int type; + char *argv[MAXARGS]; + char *eargv[MAXARGS]; +}; + +struct redircmd { + int type; + struct cmd *cmd; + char *file; + char *efile; + int mode; + int fd; +}; + +struct pipecmd { + int type; + struct cmd *left; + struct cmd *right; +}; + +struct listcmd { + int type; + struct cmd *left; + struct cmd *right; +}; + +struct backcmd { + int type; + struct cmd *cmd; +}; + +int fork1(void); // Fork but panics on failure. +void panic(char*); +struct cmd *parsecmd(char*); + +// Execute cmd. Never returns. +void +runcmd(struct cmd *cmd) +{ + int p[2]; + struct backcmd *bcmd; + struct execcmd *ecmd; + struct listcmd *lcmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; + + if(cmd == 0) + exit(); + + switch(cmd->type){ + default: + panic("runcmd"); + + case EXEC: + ecmd = (struct execcmd*)cmd; + if(ecmd->argv[0] == 0) + exit(); + exec(ecmd->argv[0], ecmd->argv); + printf(2, "exec %s failed\n", ecmd->argv[0]); + break; + + case REDIR: + rcmd = (struct redircmd*)cmd; + close(rcmd->fd); + if(open(rcmd->file, rcmd->mode) < 0){ + printf(2, "open %s failed\n", rcmd->file); + exit(); + } + runcmd(rcmd->cmd); + break; + + case LIST: + lcmd = (struct listcmd*)cmd; + if(fork1() == 0) + runcmd(lcmd->left); + wait(); + runcmd(lcmd->right); + break; + + case PIPE: + pcmd = (struct pipecmd*)cmd; + if(pipe(p) < 0) + panic("pipe"); + if(fork1() == 0){ + close(1); + dup(p[1]); + close(p[0]); + close(p[1]); + runcmd(pcmd->left); + } + if(fork1() == 0){ + close(0); + dup(p[0]); + close(p[0]); + close(p[1]); + runcmd(pcmd->right); + } + close(p[0]); + close(p[1]); + wait(); + wait(); + break; + + case BACK: + bcmd = (struct backcmd*)cmd; + if(fork1() == 0) + runcmd(bcmd->cmd); + break; + } + exit(); +} + +int +getcmd(char *buf, int nbuf) +{ + printf(2, "$ "); + memset(buf, 0, nbuf); + gets(buf, nbuf); + if(buf[0] == 0) // EOF + return -1; + return 0; +} + +int +main(void) +{ + static char buf[100]; + int fd; + + // Assumes three file descriptors open. + while((fd = open("console", O_RDWR)) >= 0){ + if(fd >= 3){ + close(fd); + break; + } + } + + // Read and run input commands. + while(getcmd(buf, sizeof(buf)) >= 0){ + if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ + // Clumsy but will have to do for now. + // Chdir has no effect on the parent if run in the child. + buf[strlen(buf)-1] = 0; // chop \n + if(chdir(buf+3) < 0) + printf(2, "cannot cd %s\n", buf+3); + continue; + } + if(fork1() == 0) + runcmd(parsecmd(buf)); + wait(); + } + exit(); +} + +void +panic(char *s) +{ + printf(2, "%s\n", s); + exit(); +} + +int +fork1(void) +{ + int pid; + + pid = fork(); + if(pid == -1) + panic("fork"); + return pid; +} + +//PAGEBREAK! +// Constructors + +struct cmd* +execcmd(void) +{ + struct execcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = EXEC; + return (struct cmd*)cmd; +} + +struct cmd* +redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) +{ + struct redircmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = REDIR; + cmd->cmd = subcmd; + cmd->file = file; + cmd->efile = efile; + cmd->mode = mode; + cmd->fd = fd; + return (struct cmd*)cmd; +} + +struct cmd* +pipecmd(struct cmd *left, struct cmd *right) +{ + struct pipecmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = PIPE; + cmd->left = left; + cmd->right = right; + return (struct cmd*)cmd; +} + +struct cmd* +listcmd(struct cmd *left, struct cmd *right) +{ + struct listcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = LIST; + cmd->left = left; + cmd->right = right; + return (struct cmd*)cmd; +} + +struct cmd* +backcmd(struct cmd *subcmd) +{ + struct backcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = BACK; + cmd->cmd = subcmd; + return (struct cmd*)cmd; +} +//PAGEBREAK! +// Parsing + +char whitespace[] = " \t\r\n\v"; +char symbols[] = "<|>&;()"; + +int +gettoken(char **ps, char *es, char **q, char **eq) +{ + char *s; + int ret; + + s = *ps; + while(s < es && strchr(whitespace, *s)) + s++; + if(q) + *q = s; + ret = *s; + switch(*s){ + case 0: + break; + case '|': + case '(': + case ')': + case ';': + case '&': + case '<': + s++; + break; + case '>': + s++; + if(*s == '>'){ + ret = '+'; + s++; + } + break; + default: + ret = 'a'; + while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) + s++; + break; + } + if(eq) + *eq = s; + + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return ret; +} + +int +peek(char **ps, char *es, char *toks) +{ + char *s; + + s = *ps; + while(s < es && strchr(whitespace, *s)) + s++; + *ps = s; + return *s && strchr(toks, *s); +} + +struct cmd *parseline(char**, char*); +struct cmd *parsepipe(char**, char*); +struct cmd *parseexec(char**, char*); +struct cmd *nulterminate(struct cmd*); + +struct cmd* +parsecmd(char *s) +{ + char *es; + struct cmd *cmd; + + es = s + strlen(s); + cmd = parseline(&s, es); + peek(&s, es, ""); + if(s != es){ + printf(2, "leftovers: %s\n", s); + panic("syntax"); + } + nulterminate(cmd); + return cmd; +} + +struct cmd* +parseline(char **ps, char *es) +{ + struct cmd *cmd; + + cmd = parsepipe(ps, es); + while(peek(ps, es, "&")){ + gettoken(ps, es, 0, 0); + cmd = backcmd(cmd); + } + if(peek(ps, es, ";")){ + gettoken(ps, es, 0, 0); + cmd = listcmd(cmd, parseline(ps, es)); + } + return cmd; +} + +struct cmd* +parsepipe(char **ps, char *es) +{ + struct cmd *cmd; + + cmd = parseexec(ps, es); + if(peek(ps, es, "|")){ + gettoken(ps, es, 0, 0); + cmd = pipecmd(cmd, parsepipe(ps, es)); + } + return cmd; +} + +struct cmd* +parseredirs(struct cmd *cmd, char **ps, char *es) +{ + int tok; + char *q, *eq; + + while(peek(ps, es, "<>")){ + tok = gettoken(ps, es, 0, 0); + if(gettoken(ps, es, &q, &eq) != 'a') + panic("missing file for redirection"); + switch(tok){ + case '<': + cmd = redircmd(cmd, q, eq, O_RDONLY, 0); + break; + case '>': + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + break; + case '+': // >> + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + break; + } + } + return cmd; +} + +struct cmd* +parseblock(char **ps, char *es) +{ + struct cmd *cmd; + + if(!peek(ps, es, "(")) + panic("parseblock"); + gettoken(ps, es, 0, 0); + cmd = parseline(ps, es); + if(!peek(ps, es, ")")) + panic("syntax - missing )"); + gettoken(ps, es, 0, 0); + cmd = parseredirs(cmd, ps, es); + return cmd; +} + +struct cmd* +parseexec(char **ps, char *es) +{ + char *q, *eq; + int tok, argc; + struct execcmd *cmd; + struct cmd *ret; + + if(peek(ps, es, "(")) + return parseblock(ps, es); + + ret = execcmd(); + cmd = (struct execcmd*)ret; + + argc = 0; + ret = parseredirs(ret, ps, es); + while(!peek(ps, es, "|)&;")){ + if((tok=gettoken(ps, es, &q, &eq)) == 0) + break; + if(tok != 'a') + panic("syntax"); + cmd->argv[argc] = q; + cmd->eargv[argc] = eq; + argc++; + if(argc >= MAXARGS) + panic("too many args"); + ret = parseredirs(ret, ps, es); + } + cmd->argv[argc] = 0; + cmd->eargv[argc] = 0; + return ret; +} + +// NUL-terminate all the counted strings. +struct cmd* +nulterminate(struct cmd *cmd) +{ + int i; + struct backcmd *bcmd; + struct execcmd *ecmd; + struct listcmd *lcmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; + + if(cmd == 0) + return 0; + + switch(cmd->type){ + case EXEC: + ecmd = (struct execcmd*)cmd; + for(i=0; ecmd->argv[i]; i++) + *ecmd->eargv[i] = 0; + break; + + case REDIR: + rcmd = (struct redircmd*)cmd; + nulterminate(rcmd->cmd); + *rcmd->efile = 0; + break; + + case PIPE: + pcmd = (struct pipecmd*)cmd; + nulterminate(pcmd->left); + nulterminate(pcmd->right); + break; + + case LIST: + lcmd = (struct listcmd*)cmd; + nulterminate(lcmd->left); + nulterminate(lcmd->right); + break; + + case BACK: + bcmd = (struct backcmd*)cmd; + nulterminate(bcmd->cmd); + break; + } + return cmd; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/stressfs.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,49 @@ +// Demonstrate that moving the "acquire" in iderw after the loop that +// appends to the idequeue results in a race. + +// For this to work, you should also add a spin within iderw's +// idequeue traversal loop. Adding the following demonstrated a panic +// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU: +// for (i = 0; i < 40000; i++) +// asm volatile(""); + +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fs.h" +#include "fcntl.h" + +int +main(int argc, char *argv[]) +{ + int fd, i; + char path[] = "stressfs0"; + char data[512]; + + printf(1, "stressfs starting\n"); + memset(data, 'a', sizeof(data)); + + for(i = 0; i < 4; i++) + if(fork() > 0) + break; + + printf(1, "write %d\n", i); + + path[8] += i; + fd = open(path, O_CREATE | O_RDWR); + for(i = 0; i < 20; i++) + // printf(fd, "%d\n", i); + write(fd, data, sizeof(data)); + close(fd); + + printf(1, "read\n"); + + fd = open(path, O_RDONLY); + for (i = 0; i < 20; i++) + read(fd, data, sizeof(data)); + close(fd); + + wait(); + + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/ulib.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,131 @@ +#include "types.h" +#include "stat.h" +#include "fcntl.h" +#include "user.h" + +char* +strcpy(char *s, char *t) +{ + char *os; + + os = s; + while((*s++ = *t++) != 0) + ; + return os; +} + +int +strcmp(const char *p, const char *q) +{ + while(*p && *p == *q) + p++, q++; + return (uchar)*p - (uchar)*q; +} + +uint +strlen(char *s) +{ + int n; + + for(n = 0; s[n]; n++) + ; + return n; +} + +void* +memset(void *dst, int v, uint n) +{ + uint8 *p; + uint8 c; + uint32 val; + uint32 *p4; + + p = dst; + c = v & 0xff; + val = (c << 24) | (c << 16) | (c << 8) | c; + + // set bytes before whole uint32 + for (; (n > 0) && ((uint)p % 4); n--, p++){ + *p = c; + } + + // set memory 4 bytes a time + p4 = (uint*)p; + + for (; n >= 4; n -= 4, p4++) { + *p4 = val; + } + + // set leftover one byte a time + p = (uint8*)p4; + + for (; n > 0; n--, p++) { + *p = c; + } + + return dst; +} + +char* +strchr(const char *s, char c) +{ + for(; *s; s++) + if(*s == c) + return (char*)s; + return 0; +} + +char* +gets(char *buf, int max) +{ + int i, cc; + char c; + + for(i=0; i+1 < max; ){ + cc = read(0, &c, 1); + if(cc < 1) + break; + buf[i++] = c; + if(c == '\n' || c == '\r') + break; + } + buf[i] = '\0'; + return buf; +} + +int +stat(char *n, struct stat *st) +{ + int fd; + int r; + + fd = open(n, O_RDONLY); + if(fd < 0) + return -1; + r = fstat(fd, st); + close(fd); + return r; +} + +int +atoi(const char *s) +{ + int n; + + n = 0; + while('0' <= *s && *s <= '9') + n = n*10 + *s++ - '0'; + return n; +} + +void* +memmove(void *vdst, void *vsrc, int n) +{ + char *dst, *src; + + dst = vdst; + src = vsrc; + while(n-- > 0) + *dst++ = *src++; + return vdst; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/umalloc.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,90 @@ +#include "types.h" +#include "stat.h" +#include "user.h" +#include "param.h" + +// Memory allocator by Kernighan and Ritchie, +// The C programming Language, 2nd ed. Section 8.7. + +typedef long Align; + +union header { + struct { + union header *ptr; + uint size; + } s; + Align x; +}; + +typedef union header Header; + +static Header base; +static Header *freep; + +void +free(void *ap) +{ + Header *bp, *p; + + bp = (Header*)ap - 1; + for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) + if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) + break; + if(bp + bp->s.size == p->s.ptr){ + bp->s.size += p->s.ptr->s.size; + bp->s.ptr = p->s.ptr->s.ptr; + } else + bp->s.ptr = p->s.ptr; + if(p + p->s.size == bp){ + p->s.size += bp->s.size; + p->s.ptr = bp->s.ptr; + } else + p->s.ptr = bp; + freep = p; +} + +static Header* +morecore(uint nu) +{ + char *p; + Header *hp; + + if(nu < 4096) + nu = 4096; + p = sbrk(nu * sizeof(Header)); + if(p == (char*)-1) + return 0; + hp = (Header*)p; + hp->s.size = nu; + free((void*)(hp + 1)); + return freep; +} + +void* +malloc(uint nbytes) +{ + Header *p, *prevp; + uint nunits; + + nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; + if((prevp = freep) == 0){ + base.s.ptr = freep = prevp = &base; + base.s.size = 0; + } + for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ + if(p->s.size >= nunits){ + if(p->s.size == nunits) + prevp->s.ptr = p->s.ptr; + else { + p->s.size -= nunits; + p += p->s.size; + p->s.size = nunits; + } + freep = prevp; + return (void*)(p + 1); + } + if(p == freep) + if((p = morecore(nunits)) == 0) + return 0; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/user.h Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,38 @@ +struct stat; + +// system calls +int fork(void); +int exit(void) __attribute__((noreturn)); +int wait(void); +int pipe(int*); +int write(int, void*, int); +int read(int, void*, int); +int close(int); +int kill(int); +int exec(char*, char**); +int open(char*, int); +int mknod(char*, short, short); +int unlink(char*); +int fstat(int fd, struct stat*); +int link(char*, char*); +int mkdir(char*); +int chdir(char*); +int dup(int); +int getpid(void); +char* sbrk(int); +int sleep(int); +int uptime(void); + +// ulib.c +int stat(char*, struct stat*); +char* strcpy(char*, char*); +void *memmove(void*, void*, int); +char* strchr(const char*, char c); +int strcmp(const char*, const char*); +void printf(int, char*, ...); +char* gets(char*, int max); +uint strlen(char*); +void* memset(void*, int, uint); +void* malloc(uint); +void free(void*); +int atoi(const char*);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/usertests.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,1651 @@ +#include "param.h" +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fs.h" +#include "fcntl.h" +#include "syscall.h" +#include "memlayout.h" + +char buf[8192]; +char name[3]; +char *echoargv[] = { "echo", "ALL", "TESTS", "PASSED", 0 }; +int stdout = 1; + +// simple file system tests + +void +opentest(void) +{ + int fd; + + printf(stdout, "open test\n"); + fd = open("echo", 0); + if(fd < 0){ + printf(stdout, "open echo failed!\n"); + exit(); + } + close(fd); + fd = open("doesnotexist", 0); + if(fd >= 0){ + printf(stdout, "open doesnotexist succeeded!\n"); + exit(); + } + printf(stdout, "open test ok\n"); +} + +void +writetest(void) +{ + int fd; + int i; + + printf(stdout, "small file test\n"); + fd = open("small", O_CREATE|O_RDWR); + if(fd >= 0){ + printf(stdout, "creat small succeeded; ok\n"); + } else { + printf(stdout, "error: creat small failed!\n"); + exit(); + } + for(i = 0; i < 100; i++){ + if(write(fd, "aaaaaaaaaa", 10) != 10){ + printf(stdout, "error: write aa %d new file failed\n", i); + exit(); + } + if(write(fd, "bbbbbbbbbb", 10) != 10){ + printf(stdout, "error: write bb %d new file failed\n", i); + exit(); + } + } + printf(stdout, "writes ok\n"); + close(fd); + fd = open("small", O_RDONLY); + if(fd >= 0){ + printf(stdout, "open small succeeded ok\n"); + } else { + printf(stdout, "error: open small failed!\n"); + exit(); + } + i = read(fd, buf, 2000); + if(i == 2000){ + printf(stdout, "read succeeded ok\n"); + } else { + printf(stdout, "read failed\n"); + exit(); + } + close(fd); + + if(unlink("small") < 0){ + printf(stdout, "unlink small failed\n"); + exit(); + } + printf(stdout, "small file test ok\n"); +} + +void +writetest1(void) +{ + int i, fd, n; + + printf(stdout, "big files test\n"); + + fd = open("big", O_CREATE|O_RDWR); + if(fd < 0){ + printf(stdout, "error: creat big failed!\n"); + exit(); + } + + for(i = 0; i < MAXFILE; i++){ + ((int*)buf)[0] = i; + if(write(fd, buf, 512) != 512){ + printf(stdout, "error: write big file failed\n", i); + exit(); + } + } + + close(fd); + + fd = open("big", O_RDONLY); + if(fd < 0){ + printf(stdout, "error: open big failed!\n"); + exit(); + } + + n = 0; + for(;;){ + i = read(fd, buf, 512); + if(i == 0){ + if(n == MAXFILE - 1){ + printf(stdout, "read only %d blocks from big", n); + exit(); + } + break; + } else if(i != 512){ + printf(stdout, "read failed %d\n", i); + exit(); + } + if(((int*)buf)[0] != n){ + printf(stdout, "read content of block %d is %d\n", + n, ((int*)buf)[0]); + exit(); + } + n++; + } + close(fd); + if(unlink("big") < 0){ + printf(stdout, "unlink big failed\n"); + exit(); + } + printf(stdout, "big files ok\n"); +} + +void +createtest(void) +{ + int i, fd; + + printf(stdout, "many creates, followed by unlink test\n"); + + name[0] = 'a'; + name[2] = '\0'; + for(i = 0; i < 52; i++){ + name[1] = '0' + i; + fd = open(name, O_CREATE|O_RDWR); + close(fd); + } + name[0] = 'a'; + name[2] = '\0'; + for(i = 0; i < 52; i++){ + name[1] = '0' + i; + unlink(name); + } + printf(stdout, "many creates, followed by unlink; ok\n"); +} + +void dirtest(void) +{ + printf(stdout, "mkdir test\n"); + + if(mkdir("dir0") < 0){ + printf(stdout, "mkdir failed\n"); + exit(); + } + + if(chdir("dir0") < 0){ + printf(stdout, "chdir dir0 failed\n"); + exit(); + } + + if(chdir("..") < 0){ + printf(stdout, "chdir .. failed\n"); + exit(); + } + + if(unlink("dir0") < 0){ + printf(stdout, "unlink dir0 failed\n"); + exit(); + } + printf(stdout, "mkdir test\n"); +} + +void +exectest(void) +{ + printf(stdout, "exec test\n"); + if(exec("echo", echoargv) < 0){ + printf(stdout, "exec echo failed\n"); + exit(); + } +} + +// simple fork and pipe read/write + +void +pipe1(void) +{ + int fds[2], pid; + int seq, i, n, cc, total; + + printf(1, "pipe test\n"); + if(pipe(fds) != 0){ + printf(1, "pipe() failed\n"); + exit(); + } + pid = fork(); + seq = 0; + if(pid == 0){ + close(fds[0]); + for(n = 0; n < 5; n++){ + for(i = 0; i < 1033; i++) + buf[i] = seq++; + if(write(fds[1], buf, 1033) != 1033){ + printf(1, "pipe1 oops 1\n"); + exit(); + } + } + exit(); + } else if(pid > 0){ + close(fds[1]); + total = 0; + cc = 1; + while((n = read(fds[0], buf, cc)) > 0){ + for(i = 0; i < n; i++){ + if((buf[i] & 0xff) != (seq++ & 0xff)){ + printf(1, "pipe1 oops 2\n"); + return; + } + } + total += n; + cc = cc * 2; + if(cc > sizeof(buf)) + cc = sizeof(buf); + } + if(total != 5 * 1033){ + printf(1, "pipe1 oops 3 total %d\n", total); + exit(); + } + close(fds[0]); + wait(); + } else { + printf(1, "fork() failed\n"); + exit(); + } + printf(1, "pipe1 ok\n"); +} + +// meant to be run w/ at most two CPUs +void +preempt(void) +{ + int pid1, pid2, pid3; + int pfds[2]; + + printf(1, "preempt: "); + pid1 = fork(); + if(pid1 == 0) + for(;;) + ; + + pid2 = fork(); + if(pid2 == 0) + for(;;) + ; + + pipe(pfds); + pid3 = fork(); + if(pid3 == 0){ + close(pfds[0]); + if(write(pfds[1], "x", 1) != 1) + printf(1, "preempt write error"); + close(pfds[1]); + for(;;) + ; + } + + close(pfds[1]); + if(read(pfds[0], buf, sizeof(buf)) != 1){ + printf(1, "preempt read error"); + return; + } + close(pfds[0]); + printf(1, "kill... "); + kill(pid1); + kill(pid2); + kill(pid3); + printf(1, "wait... "); + wait(); + wait(); + wait(); + printf(1, "preempt ok\n"); +} + +// try to find any races between exit and wait +void +exitwait(void) +{ + int i, pid; + + for(i = 0; i < 100; i++){ + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + return; + } + if(pid){ + if(wait() != pid){ + printf(1, "wait wrong pid\n"); + return; + } + } else { + exit(); + } + } + printf(1, "exitwait ok\n"); +} + +void +mem(void) +{ + void *m1, *m2; + int pid, ppid; + + printf(1, "mem test\n"); + ppid = getpid(); + if((pid = fork()) == 0){ + m1 = 0; + printf(1, "mem test alloc to full\n"); + while((m2 = malloc(10001)) != 0){ + *(char**)m2 = m1; + m1 = m2; + } + while(m1){ + m2 = *(char**)m1; + free(m1); + m1 = m2; + } + printf(1, "mem test alloc to full ok\n"); + m1 = malloc(1024*20); + if(m1 == 0){ + printf(1, "couldn't allocate mem?!!\n"); + kill(ppid); + exit(); + } + free(m1); + printf(1, "mem ok\n"); + exit(); + } else if (pid < 0) { + printf(1, "fork failed!\n"); + } else { + wait(); + } +} + +// More file system tests + +// two processes write to the same file descriptor +// is the offset shared? does inode locking work? +void +sharedfd(void) +{ + int fd, pid, i, n, nc, np; + char buf[10]; + + printf(1, "sharedfd test\n"); + + unlink("sharedfd"); + fd = open("sharedfd", O_CREATE|O_RDWR); + if(fd < 0){ + printf(1, "fstests: cannot open sharedfd for writing"); + return; + } + pid = fork(); + memset(buf, pid==0?'c':'p', sizeof(buf)); + for(i = 0; i < 1000; i++){ + if(write(fd, buf, sizeof(buf)) != sizeof(buf)){ + printf(1, "fstests: write sharedfd failed\n"); + break; + } + } + if(pid == 0) + exit(); + else + wait(); + close(fd); + fd = open("sharedfd", 0); + if(fd < 0){ + printf(1, "fstests: cannot open sharedfd for reading\n"); + return; + } + nc = np = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(i = 0; i < sizeof(buf); i++){ + if(buf[i] == 'c') + nc++; + if(buf[i] == 'p') + np++; + } + } + close(fd); + unlink("sharedfd"); + if(nc == 10000 && np == 10000){ + printf(1, "sharedfd ok\n"); + } else { + printf(1, "sharedfd oops %d %d\n", nc, np); + exit(); + } +} + +// two processes write two different files at the same +// time, to test block allocation. +void +twofiles(void) +{ + int fd, pid, i, j, n, total; + char *fname; + + printf(1, "twofiles test\n"); + + unlink("f1"); + unlink("f2"); + + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + + fname = pid ? "f1" : "f2"; + fd = open(fname, O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create failed\n"); + exit(); + } + + memset(buf, pid?'p':'c', 512); + for(i = 0; i < 12; i++){ + if((n = write(fd, buf, 500)) != 500){ + printf(1, "write failed %d\n", n); + exit(); + } + } + close(fd); + if(pid) + wait(); + else + exit(); + + for(i = 0; i < 2; i++){ + fd = open(i?"f1":"f2", 0); + total = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(j = 0; j < n; j++){ + if(buf[j] != (i?'p':'c')){ + printf(1, "wrong char\n"); + exit(); + } + } + total += n; + } + close(fd); + if(total != 12*500){ + printf(1, "wrong length %d\n", total); + exit(); + } + } + + unlink("f1"); + unlink("f2"); + + printf(1, "twofiles ok\n"); +} + +// two processes create and delete different files in same directory +void +createdelete(void) +{ + enum { N = 20 }; + int pid, i, fd; + char name[32]; + + printf(1, "createdelete test\n"); + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + + name[0] = pid ? 'p' : 'c'; + name[2] = '\0'; + for(i = 0; i < N; i++){ + name[1] = '0' + i; + fd = open(name, O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create failed\n"); + exit(); + } + close(fd); + if(i > 0 && (i % 2 ) == 0){ + name[1] = '0' + (i / 2); + if(unlink(name) < 0){ + printf(1, "unlink failed\n"); + exit(); + } + } + } + + if(pid==0) + exit(); + else + wait(); + + for(i = 0; i < N; i++){ + name[0] = 'p'; + name[1] = '0' + i; + fd = open(name, 0); + if((i == 0 || i >= N/2) && fd < 0){ + printf(1, "oops createdelete %s didn't exist\n", name); + exit(); + } else if((i >= 1 && i < N/2) && fd >= 0){ + printf(1, "oops createdelete %s did exist\n", name); + exit(); + } + if(fd >= 0) + close(fd); + + name[0] = 'c'; + name[1] = '0' + i; + fd = open(name, 0); + if((i == 0 || i >= N/2) && fd < 0){ + printf(1, "oops createdelete %s didn't exist\n", name); + exit(); + } else if((i >= 1 && i < N/2) && fd >= 0){ + printf(1, "oops createdelete %s did exist\n", name); + exit(); + } + if(fd >= 0) + close(fd); + } + + for(i = 0; i < N; i++){ + name[0] = 'p'; + name[1] = '0' + i; + unlink(name); + name[0] = 'c'; + unlink(name); + } + + printf(1, "createdelete ok\n"); +} + +// can I unlink a file and still read it? +void +unlinkread(void) +{ + int fd, fd1; + + printf(1, "unlinkread test\n"); + fd = open("unlinkread", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create unlinkread failed\n"); + exit(); + } + write(fd, "hello", 5); + close(fd); + + fd = open("unlinkread", O_RDWR); + if(fd < 0){ + printf(1, "open unlinkread failed\n"); + exit(); + } + if(unlink("unlinkread") != 0){ + printf(1, "unlink unlinkread failed\n"); + exit(); + } + + fd1 = open("unlinkread", O_CREATE | O_RDWR); + write(fd1, "yyy", 3); + close(fd1); + + if(read(fd, buf, sizeof(buf)) != 5){ + printf(1, "unlinkread read failed"); + exit(); + } + if(buf[0] != 'h'){ + printf(1, "unlinkread wrong data\n"); + exit(); + } + if(write(fd, buf, 10) != 10){ + printf(1, "unlinkread write failed\n"); + exit(); + } + close(fd); + unlink("unlinkread"); + printf(1, "unlinkread ok\n"); +} + +void +linktest(void) +{ + int fd; + + printf(1, "linktest\n"); + + unlink("lf1"); + unlink("lf2"); + + fd = open("lf1", O_CREATE|O_RDWR); + if(fd < 0){ + printf(1, "create lf1 failed\n"); + exit(); + } + if(write(fd, "hello", 5) != 5){ + printf(1, "write lf1 failed\n"); + exit(); + } + close(fd); + + if(link("lf1", "lf2") < 0){ + printf(1, "link lf1 lf2 failed\n"); + exit(); + } + unlink("lf1"); + + if(open("lf1", 0) >= 0){ + printf(1, "unlinked lf1 but it is still there!\n"); + exit(); + } + + fd = open("lf2", 0); + if(fd < 0){ + printf(1, "open lf2 failed\n"); + exit(); + } + if(read(fd, buf, sizeof(buf)) != 5){ + printf(1, "read lf2 failed\n"); + exit(); + } + close(fd); + + if(link("lf2", "lf2") >= 0){ + printf(1, "link lf2 lf2 succeeded! oops\n"); + exit(); + } + + unlink("lf2"); + if(link("lf2", "lf1") >= 0){ + printf(1, "link non-existant succeeded! oops\n"); + exit(); + } + + if(link(".", "lf1") >= 0){ + printf(1, "link . lf1 succeeded! oops\n"); + exit(); + } + + printf(1, "linktest ok\n"); +} + +// test concurrent create/link/unlink of the same file +void +concreate(void) +{ + char file[3]; + int i, pid, n, fd; + char fa[40]; + struct { + ushort inum; + char name[14]; + } de; + + printf(1, "concreate test\n"); + file[0] = 'C'; + file[2] = '\0'; + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + unlink(file); + pid = fork(); + if(pid && (i % 3) == 1){ + link("C0", file); + } else if(pid == 0 && (i % 5) == 1){ + link("C0", file); + } else { + fd = open(file, O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "concreate create %s failed\n", file); + exit(); + } + close(fd); + } + if(pid == 0) + exit(); + else + wait(); + } + + memset(fa, 0, sizeof(fa)); + fd = open(".", 0); + n = 0; + while(read(fd, &de, sizeof(de)) > 0){ + if(de.inum == 0) + continue; + if(de.name[0] == 'C' && de.name[2] == '\0'){ + i = de.name[1] - '0'; + if(i < 0 || i >= sizeof(fa)){ + printf(1, "concreate weird file %s\n", de.name); + exit(); + } + if(fa[i]){ + printf(1, "concreate duplicate file %s\n", de.name); + exit(); + } + fa[i] = 1; + n++; + } + } + close(fd); + + if(n != 40){ + printf(1, "concreate not enough files in directory listing\n"); + exit(); + } + + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + if(((i % 3) == 0 && pid == 0) || + ((i % 3) == 1 && pid != 0)){ + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); + } else { + unlink(file); + unlink(file); + unlink(file); + unlink(file); + } + if(pid == 0) + exit(); + else + wait(); + } + + printf(1, "concreate ok\n"); +} + +// another concurrent link/unlink/create test, +// to look for deadlocks. +void +linkunlink() +{ + int pid, i; + + printf(1, "linkunlink test\n"); + + unlink("x"); + pid = fork(); + if(pid < 0){ + printf(1, "fork failed\n"); + exit(); + } + + unsigned int x = (pid ? 1 : 97); + for(i = 0; i < 100; i++){ + x = x * 1103515245 + 12345; + if((x % 3) == 0){ + close(open("x", O_RDWR | O_CREATE)); + } else if((x % 3) == 1){ + link("cat", "x"); + } else { + unlink("x"); + } + } + + if(pid) + wait(); + else + exit(); + + printf(1, "linkunlink ok\n"); +} + +// directory that uses indirect blocks +void +bigdir(void) +{ + int i, fd; + char name[10]; + + printf(1, "bigdir test\n"); + unlink("bd"); + + fd = open("bd", O_CREATE); + if(fd < 0){ + printf(1, "bigdir create failed\n"); + exit(); + } + close(fd); + + for(i = 0; i < 500; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(link("bd", name) != 0){ + printf(1, "bigdir link failed\n"); + exit(); + } + } + + unlink("bd"); + for(i = 0; i < 500; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(unlink(name) != 0){ + printf(1, "bigdir unlink failed"); + exit(); + } + } + + printf(1, "bigdir ok\n"); +} + +void +subdir(void) +{ + int fd, cc; + + printf(1, "subdir test\n"); + + unlink("ff"); + if(mkdir("dd") != 0){ + printf(1, "subdir mkdir dd failed\n"); + exit(); + } + + fd = open("dd/ff", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create dd/ff failed\n"); + exit(); + } + write(fd, "ff", 2); + close(fd); + + if(unlink("dd") >= 0){ + printf(1, "unlink dd (non-empty dir) succeeded!\n"); + exit(); + } + + if(mkdir("/dd/dd") != 0){ + printf(1, "subdir mkdir dd/dd failed\n"); + exit(); + } + + fd = open("dd/dd/ff", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "create dd/dd/ff failed\n"); + exit(); + } + write(fd, "FF", 2); + close(fd); + + fd = open("dd/dd/../ff", 0); + if(fd < 0){ + printf(1, "open dd/dd/../ff failed\n"); + exit(); + } + cc = read(fd, buf, sizeof(buf)); + if(cc != 2 || buf[0] != 'f'){ + printf(1, "dd/dd/../ff wrong content\n"); + exit(); + } + close(fd); + + if(link("dd/dd/ff", "dd/dd/ffff") != 0){ + printf(1, "link dd/dd/ff dd/dd/ffff failed\n"); + exit(); + } + + if(unlink("dd/dd/ff") != 0){ + printf(1, "unlink dd/dd/ff failed\n"); + exit(); + } + if(open("dd/dd/ff", O_RDONLY) >= 0){ + printf(1, "open (unlinked) dd/dd/ff succeeded\n"); + exit(); + } + + if(chdir("dd") != 0){ + printf(1, "chdir dd failed\n"); + exit(); + } + if(chdir("dd/../../dd") != 0){ + printf(1, "chdir dd/../../dd failed\n"); + exit(); + } + if(chdir("dd/../../../dd") != 0){ + printf(1, "chdir dd/../../dd failed\n"); + exit(); + } + if(chdir("./..") != 0){ + printf(1, "chdir ./.. failed\n"); + exit(); + } + + fd = open("dd/dd/ffff", 0); + if(fd < 0){ + printf(1, "open dd/dd/ffff failed\n"); + exit(); + } + if(read(fd, buf, sizeof(buf)) != 2){ + printf(1, "read dd/dd/ffff wrong len\n"); + exit(); + } + close(fd); + + if(open("dd/dd/ff", O_RDONLY) >= 0){ + printf(1, "open (unlinked) dd/dd/ff succeeded!\n"); + exit(); + } + + if(open("dd/ff/ff", O_CREATE|O_RDWR) >= 0){ + printf(1, "create dd/ff/ff succeeded!\n"); + exit(); + } + if(open("dd/xx/ff", O_CREATE|O_RDWR) >= 0){ + printf(1, "create dd/xx/ff succeeded!\n"); + exit(); + } + if(open("dd", O_CREATE) >= 0){ + printf(1, "create dd succeeded!\n"); + exit(); + } + if(open("dd", O_RDWR) >= 0){ + printf(1, "open dd rdwr succeeded!\n"); + exit(); + } + if(open("dd", O_WRONLY) >= 0){ + printf(1, "open dd wronly succeeded!\n"); + exit(); + } + if(link("dd/ff/ff", "dd/dd/xx") == 0){ + printf(1, "link dd/ff/ff dd/dd/xx succeeded!\n"); + exit(); + } + if(link("dd/xx/ff", "dd/dd/xx") == 0){ + printf(1, "link dd/xx/ff dd/dd/xx succeeded!\n"); + exit(); + } + if(link("dd/ff", "dd/dd/ffff") == 0){ + printf(1, "link dd/ff dd/dd/ffff succeeded!\n"); + exit(); + } + if(mkdir("dd/ff/ff") == 0){ + printf(1, "mkdir dd/ff/ff succeeded!\n"); + exit(); + } + if(mkdir("dd/xx/ff") == 0){ + printf(1, "mkdir dd/xx/ff succeeded!\n"); + exit(); + } + if(mkdir("dd/dd/ffff") == 0){ + printf(1, "mkdir dd/dd/ffff succeeded!\n"); + exit(); + } + if(unlink("dd/xx/ff") == 0){ + printf(1, "unlink dd/xx/ff succeeded!\n"); + exit(); + } + if(unlink("dd/ff/ff") == 0){ + printf(1, "unlink dd/ff/ff succeeded!\n"); + exit(); + } + if(chdir("dd/ff") == 0){ + printf(1, "chdir dd/ff succeeded!\n"); + exit(); + } + if(chdir("dd/xx") == 0){ + printf(1, "chdir dd/xx succeeded!\n"); + exit(); + } + + if(unlink("dd/dd/ffff") != 0){ + printf(1, "unlink dd/dd/ff failed\n"); + exit(); + } + if(unlink("dd/ff") != 0){ + printf(1, "unlink dd/ff failed\n"); + exit(); + } + if(unlink("dd") == 0){ + printf(1, "unlink non-empty dd succeeded!\n"); + exit(); + } + if(unlink("dd/dd") < 0){ + printf(1, "unlink dd/dd failed\n"); + exit(); + } + if(unlink("dd") < 0){ + printf(1, "unlink dd failed\n"); + exit(); + } + + printf(1, "subdir ok\n"); +} + +// test writes that are larger than the log. +void +bigwrite(void) +{ + int fd, sz; + + printf(1, "bigwrite test\n"); + + unlink("bigwrite"); + for(sz = 499; sz < 12*512; sz += 471){ + fd = open("bigwrite", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "cannot create bigwrite\n"); + exit(); + } + int i; + for(i = 0; i < 2; i++){ + int cc = write(fd, buf, sz); + if(cc != sz){ + printf(1, "write(%d) ret %d\n", sz, cc); + exit(); + } + } + close(fd); + unlink("bigwrite"); + } + + printf(1, "bigwrite ok\n"); +} + +void +bigfile(void) +{ + int fd, i, total, cc; + + printf(1, "bigfile test\n"); + + unlink("bigfile"); + fd = open("bigfile", O_CREATE | O_RDWR); + if(fd < 0){ + printf(1, "cannot create bigfile"); + exit(); + } + for(i = 0; i < 20; i++){ + memset(buf, i, 600); + if(write(fd, buf, 600) != 600){ + printf(1, "write bigfile failed\n"); + exit(); + } + } + close(fd); + + fd = open("bigfile", 0); + if(fd < 0){ + printf(1, "cannot open bigfile\n"); + exit(); + } + total = 0; + for(i = 0; ; i++){ + cc = read(fd, buf, 300); + if(cc < 0){ + printf(1, "read bigfile failed\n"); + exit(); + } + if(cc == 0) + break; + if(cc != 300){ + printf(1, "short read bigfile\n"); + exit(); + } + if(buf[0] != i/2 || buf[299] != i/2){ + printf(1, "read bigfile wrong data\n"); + exit(); + } + total += cc; + } + close(fd); + if(total != 20*600){ + printf(1, "read bigfile wrong total\n"); + exit(); + } + unlink("bigfile"); + + printf(1, "bigfile test ok\n"); +} + +void +fourteen(void) +{ + int fd; + + // DIRSIZ is 14. + printf(1, "fourteen test\n"); + + if(mkdir("12345678901234") != 0){ + printf(1, "mkdir 12345678901234 failed\n"); + exit(); + } + if(mkdir("12345678901234/123456789012345") != 0){ + printf(1, "mkdir 12345678901234/123456789012345 failed\n"); + exit(); + } + fd = open("123456789012345/123456789012345/123456789012345", O_CREATE); + if(fd < 0){ + printf(1, "create 123456789012345/123456789012345/123456789012345 failed\n"); + exit(); + } + close(fd); + fd = open("12345678901234/12345678901234/12345678901234", 0); + if(fd < 0){ + printf(1, "open 12345678901234/12345678901234/12345678901234 failed\n"); + exit(); + } + close(fd); + + if(mkdir("12345678901234/12345678901234") == 0){ + printf(1, "mkdir 12345678901234/12345678901234 succeeded!\n"); + exit(); + } + if(mkdir("123456789012345/12345678901234") == 0){ + printf(1, "mkdir 12345678901234/123456789012345 succeeded!\n"); + exit(); + } + + printf(1, "fourteen ok\n"); +} + +void +rmdot(void) +{ + printf(1, "rmdot test\n"); + if(mkdir("dots") != 0){ + printf(1, "mkdir dots failed\n"); + exit(); + } + if(chdir("dots") != 0){ + printf(1, "chdir dots failed\n"); + exit(); + } + if(unlink(".") == 0){ + printf(1, "rm . worked!\n"); + exit(); + } + if(unlink("..") == 0){ + printf(1, "rm .. worked!\n"); + exit(); + } + if(chdir("/") != 0){ + printf(1, "chdir / failed\n"); + exit(); + } + if(unlink("dots/.") == 0){ + printf(1, "unlink dots/. worked!\n"); + exit(); + } + if(unlink("dots/..") == 0){ + printf(1, "unlink dots/.. worked!\n"); + exit(); + } + if(unlink("dots") != 0){ + printf(1, "unlink dots failed!\n"); + exit(); + } + printf(1, "rmdot ok\n"); +} + +void +dirfile(void) +{ + int fd; + + printf(1, "dir vs file\n"); + + fd = open("dirfile", O_CREATE); + if(fd < 0){ + printf(1, "create dirfile failed\n"); + exit(); + } + close(fd); + if(chdir("dirfile") == 0){ + printf(1, "chdir dirfile succeeded!\n"); + exit(); + } + fd = open("dirfile/xx", 0); + if(fd >= 0){ + printf(1, "create dirfile/xx succeeded!\n"); + exit(); + } + fd = open("dirfile/xx", O_CREATE); + if(fd >= 0){ + printf(1, "create dirfile/xx succeeded!\n"); + exit(); + } + if(mkdir("dirfile/xx") == 0){ + printf(1, "mkdir dirfile/xx succeeded!\n"); + exit(); + } + if(unlink("dirfile/xx") == 0){ + printf(1, "unlink dirfile/xx succeeded!\n"); + exit(); + } + if(link("README", "dirfile/xx") == 0){ + printf(1, "link to dirfile/xx succeeded!\n"); + exit(); + } + if(unlink("dirfile") != 0){ + printf(1, "unlink dirfile failed!\n"); + exit(); + } + + fd = open(".", O_RDWR); + if(fd >= 0){ + printf(1, "open . for writing succeeded!\n"); + exit(); + } + fd = open(".", 0); + if(write(fd, "x", 1) > 0){ + printf(1, "write . succeeded!\n"); + exit(); + } + close(fd); + + printf(1, "dir vs file OK\n"); +} + +// test that iput() is called at the end of _namei() +void +iref(void) +{ + int i, fd; + + printf(1, "empty file name\n"); + + // the 50 is NINODE + for(i = 0; i < 50 + 1; i++){ + if(mkdir("irefd") != 0){ + printf(1, "mkdir irefd failed\n"); + exit(); + } + if(chdir("irefd") != 0){ + printf(1, "chdir irefd failed\n"); + exit(); + } + + mkdir(""); + link("README", ""); + fd = open("", O_CREATE); + if(fd >= 0) + close(fd); + fd = open("xx", O_CREATE); + if(fd >= 0) + close(fd); + unlink("xx"); + } + + chdir("/"); + printf(1, "empty file name OK\n"); +} + +// test that fork fails gracefully +// the forktest binary also does this, but it runs out of proc entries first. +// inside the bigger usertests binary, we run out of memory first. +void +forktest(void) +{ + int n, pid; + + printf(1, "fork test\n"); + + for(n=0; n<1000; n++){ + pid = fork(); + if(pid < 0) + break; + if(pid == 0) + exit(); + } + + if(n == 1000){ + printf(1, "fork claimed to work 1000 times!\n"); + exit(); + } + + for(; n > 0; n--){ + if(wait() < 0){ + printf(1, "wait stopped early\n"); + exit(); + } + } + + if(wait() != -1){ + printf(1, "wait got too many\n"); + exit(); + } + + printf(1, "fork test OK\n"); +} + +void +sbrktest(void) +{ + int fds[2], pid, pids[1], ppid; + char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch; + uint amt; + + printf(stdout, "sbrk test\n"); + oldbrk = sbrk(0); + + // can one sbrk() less than a page? + a = sbrk(0); + int i; + for(i = 0; i < 5000; i++){ + b = sbrk(1); + if(b != a){ + printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); + exit(); + } + *b = 1; + a = b + 1; + } + pid = fork(); + if(pid < 0){ + printf(stdout, "sbrk test fork failed\n"); + exit(); + } + c = sbrk(1); + c = sbrk(1); + if(c != a + 1){ + printf(stdout, "sbrk test failed post-fork\n"); + exit(); + } + if(pid == 0) + exit(); + wait(); + + // can one grow address space to something big? +#define BIG (100*1024*1024) + a = sbrk(0); + amt = (BIG) - (uint)a; + p = sbrk(amt); + if (p != a) { + printf(stdout, "sbrk test failed to grow big address space; enough phys mem?\n"); + exit(); + } + lastaddr = (char*) (BIG-1); + *lastaddr = 99; + + // can one de-allocate? + a = sbrk(0); + c = sbrk(-4096); + if(c == (char*)0xffffffff){ + printf(stdout, "sbrk could not deallocate\n"); + exit(); + } + c = sbrk(0); + if(c != a - 4096){ + printf(stdout, "sbrk deallocation produced wrong address, a %x c %x\n", a, c); + exit(); + } + + // can one re-allocate that page? + a = sbrk(0); + c = sbrk(4096); + if(c != a || sbrk(0) != a + 4096){ + printf(stdout, "sbrk re-allocation failed, a %x c %x\n", a, c); + exit(); + } + if(*lastaddr == 99){ + // should be zero + printf(stdout, "sbrk de-allocation didn't really deallocate\n"); + exit(); + } + + a = sbrk(0); + c = sbrk(-(sbrk(0) - oldbrk)); + if(c != a){ + printf(stdout, "sbrk downsize failed, a %x c %x\n", a, c); + exit(); + } + + // can we read the kernel's memory? + for(a = (char*)(KERNBASE); a < (char*) (KERNBASE+2000000); a += 50000){ + ppid = getpid(); + pid = fork(); + if(pid < 0){ + printf(stdout, "fork failed\n"); + exit(); + } + if(pid == 0){ + printf(stdout, "oops could read %x = %x\n", a, *a); + kill(ppid); + exit(); + } + wait(); + } + + // if we run the system out of memory, does it clean up the last + // failed allocation? + if(pipe(fds) != 0){ + printf(1, "pipe() failed\n"); + exit(); + } + for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if((pids[i] = fork()) == 0){ + // allocate a lot of memory + sbrk(BIG - (uint)sbrk(0)); + write(fds[1], "x", 1); + // sit around until killed + for(;;) sleep(1000); + } + if(pids[i] != -1) + read(fds[0], &scratch, 1); + } + // if those failed allocations freed up the pages they did allocate, + // we'll be able to allocate here + c = sbrk(4096); + for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if(pids[i] == -1) + continue; + kill(pids[i]); + wait(); + } + if(c == (char*)0xffffffff){ + printf(stdout, "failed sbrk leaked memory\n"); + exit(); + } + + if(sbrk(0) > oldbrk) + sbrk(-(sbrk(0) - oldbrk)); + + printf(stdout, "sbrk test OK\n"); +} + +void +validateint(int *p) +{ +} + +void +validatetest(void) +{ + int hi, pid; + uint p; + + printf(stdout, "validate test\n"); + hi = 1100*1024; + + for(p = 0; p <= (uint)hi; p += 4096){ + if((pid = fork()) == 0){ + // try to crash the kernel by passing in a badly placed integer + validateint((int*)p); + exit(); + } + sleep(0); + sleep(0); + kill(pid); + wait(); + + // try to crash the kernel by passing in a bad string pointer + if(link("nosuchfile", (char*)p) != -1){ + printf(stdout, "link should not succeed\n"); + exit(); + } + } + + printf(stdout, "validate ok\n"); +} + +// does unintialized data start out zero? +char uninit[10000]; +void +bsstest(void) +{ + int i; + + printf(stdout, "bss test\n"); + for(i = 0; i < sizeof(uninit); i++){ + if(uninit[i] != '\0'){ + printf(stdout, "bss test failed\n"); + exit(); + } + } + printf(stdout, "bss test ok\n"); +} + +// does exec return an error if the arguments +// are larger than a page? or does it write +// below the stack and wreck the instructions/data? +void +bigargtest(void) +{ + int pid, fd; + + unlink("bigarg-ok"); + pid = fork(); + if(pid == 0){ + static char *args[MAXARG]; + int i; + for(i = 0; i < MAXARG-1; i++) + args[i] = "bigargs test: failed\n "; + args[MAXARG-1] = 0; + printf(stdout, "bigarg test\n"); + exec("echo", args); + printf(stdout, "bigarg test ok\n"); + fd = open("bigarg-ok", O_CREATE); + close(fd); + exit(); + } else if(pid < 0){ + printf(stdout, "bigargtest: fork failed\n"); + exit(); + } + wait(); + fd = open("bigarg-ok", 0); + if(fd < 0){ + printf(stdout, "bigarg test failed!\n"); + exit(); + } + close(fd); + unlink("bigarg-ok"); +} + +// what happens when the file system runs out of blocks? +// answer: balloc panics, so this test is not useful. +void +fsfull() +{ + int nfiles; + int fsblocks = 0; + + printf(1, "fsfull test\n"); + + for(nfiles = 0; ; nfiles++){ + char name[64]; + name[0] = 'f'; + name[1] = '0' + nfiles / 1000; + name[2] = '0' + (nfiles % 1000) / 100; + name[3] = '0' + (nfiles % 100) / 10; + name[4] = '0' + (nfiles % 10); + name[5] = '\0'; + printf(1, "writing %s\n", name); + int fd = open(name, O_CREATE|O_RDWR); + if(fd < 0){ + printf(1, "open %s failed\n", name); + break; + } + int total = 0; + while(1){ + int cc = write(fd, buf, 512); + if(cc < 512) + break; + total += cc; + fsblocks++; + } + printf(1, "wrote %d bytes\n", total); + close(fd); + if(total == 0) + break; + } + + while(nfiles >= 0){ + char name[64]; + name[0] = 'f'; + name[1] = '0' + nfiles / 1000; + name[2] = '0' + (nfiles % 1000) / 100; + name[3] = '0' + (nfiles % 100) / 10; + name[4] = '0' + (nfiles % 10); + name[5] = '\0'; + unlink(name); + nfiles--; + } + + printf(1, "fsfull test finished\n"); +} + +unsigned long randstate = 1; +unsigned int +rand() +{ + randstate = randstate * 1664525 + 1013904223; + return randstate; +} + +int +main(int argc, char *argv[]) +{ + printf(1, "usertests starting\n"); + + if(open("usertests.ran", 0) >= 0){ + printf(1, "already ran user tests -- rebuild fs.img\n"); + exit(); + } + close(open("usertests.ran", O_CREATE)); + + bigargtest(); + bigwrite(); + bigargtest(); + bsstest(); + sbrktest(); + validatetest(); + + opentest(); + writetest(); + writetest1(); + createtest(); + + mem(); + pipe1(); + //preempt(); + exitwait(); + + rmdot(); + fourteen(); + bigfile(); + subdir(); + concreate(); + linkunlink(); + linktest(); + unlinkread(); + createdelete(); + twofiles(); + sharedfd(); + dirfile(); + iref(); + forktest(); + bigdir(); // slow + + exectest(); + + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/usys.S Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,36 @@ +#include "syscall.h" + +#define SYSCALL(name) \ +.globl name; \ +name: \ + PUSH {r4};\ + MOV r4, r3;\ + MOV r3, r2;\ + MOV r2, r1;\ + MOV r1, r0;\ + MOV r0, #SYS_ ## name;\ + swi 0x00;\ + POP {r4};\ + bx lr; + +SYSCALL(fork) +SYSCALL(exit) +SYSCALL(wait) +SYSCALL(pipe) +SYSCALL(read) +SYSCALL(write) +SYSCALL(close) +SYSCALL(kill) +SYSCALL(exec) +SYSCALL(open) +SYSCALL(mknod) +SYSCALL(unlink) +SYSCALL(fstat) +SYSCALL(link) +SYSCALL(mkdir) +SYSCALL(chdir) +SYSCALL(dup) +SYSCALL(getpid) +SYSCALL(sbrk) +SYSCALL(sleep) +SYSCALL(uptime)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/wc.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,54 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +char buf[512]; + +void +wc(int fd, char *name) +{ + int i, n; + int l, w, c, inword; + + l = w = c = 0; + inword = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(i=0; i<n; i++){ + c++; + if(buf[i] == '\n') + l++; + if(strchr(" \r\t\n\v", buf[i])) + inword = 0; + else if(!inword){ + w++; + inword = 1; + } + } + } + if(n < 0){ + printf(1, "wc: read error\n"); + exit(); + } + printf(1, "%d %d %d %s\n", l, w, c, name); +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + + if(argc <= 1){ + wc(0, ""); + exit(); + } + + for(i = 1; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf(1, "wc: cannot open %s\n", argv[i]); + exit(); + } + wc(fd, argv[i]); + close(fd); + } + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/usr/zombie.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,14 @@ +// Create a zombie process that +// must be reparented at exit. + +#include "types.h" +#include "stat.h" +#include "user.h" + +int +main(void) +{ + if(fork() > 0) + sleep(5); // Let child exit before parent. + exit(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vm.c Fri May 26 23:11:05 2017 +0900 @@ -0,0 +1,447 @@ +#include "param.h" +#include "types.h" +#include "defs.h" +#include "arm.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "spinlock.h" +#include "elf.h" + +extern char data[]; // defined by kernel.ld +pde_t *kpgdir; // for use in scheduler() + +// Xv6 can only allocate memory in 4KB blocks. This is fine +// for x86. ARM's page table and page directory (for 28-bit +// user address) have a size of 1KB. kpt_alloc/free is used +// as a wrapper to support allocating page tables during boot +// (use the initial kernel map, and during runtime, use buddy +// memory allocator. +struct run { + struct run *next; +}; + +struct { + struct spinlock lock; + struct run *freelist; +} kpt_mem; + +void init_vmm (void) +{ + initlock(&kpt_mem.lock, "vm"); + kpt_mem.freelist = NULL; +} + +static void _kpt_free (char *v) +{ + struct run *r; + + r = (struct run*) v; + r->next = kpt_mem.freelist; + kpt_mem.freelist = r; +} + + +static void kpt_free (char *v) +{ + if (v >= (char*)P2V(INIT_KERNMAP)) { + kfree(v, PT_ORDER); + return; + } + + acquire(&kpt_mem.lock); + _kpt_free (v); + release(&kpt_mem.lock); +} + +// add some memory used for page tables (initialization code) +void kpt_freerange (uint32 low, uint32 hi) +{ + while (low < hi) { + _kpt_free ((char*)low); + low += PT_SZ; + } +} + +void* kpt_alloc (void) +{ + struct run *r; + + acquire(&kpt_mem.lock); + + if ((r = kpt_mem.freelist) != NULL ) { + kpt_mem.freelist = r->next; + } + + release(&kpt_mem.lock); + + // Allocate a PT page if no inital pages is available + if ((r == NULL) && ((r = kmalloc (PT_ORDER)) == NULL)) { + panic("oom: kpt_alloc"); + } + + memset(r, 0, PT_SZ); + return (char*) r; +} + +// Return the address of the PTE in page directory that corresponds to +// virtual address va. If alloc!=0, create any required page table pages. +static pte_t* walkpgdir (pde_t *pgdir, const void *va, int alloc) +{ + pde_t *pde; + pte_t *pgtab; + + // pgdir points to the page directory, get the page direcotry entry (pde) + pde = &pgdir[PDE_IDX(va)]; + + if (*pde & PE_TYPES) { + pgtab = (pte_t*) p2v(PT_ADDR(*pde)); + + } else { + if (!alloc || (pgtab = (pte_t*) kpt_alloc()) == 0) { + return 0; + } + + // Make sure all those PTE_P bits are zero. + memset(pgtab, 0, PT_SZ); + + // The permissions here are overly generous, but they can + // be further restricted by the permissions in the page table + // entries, if necessary. + *pde = v2p(pgtab) | UPDE_TYPE; + } + + return &pgtab[PTE_IDX(va)]; +} + +// Create PTEs for virtual addresses starting at va that refer to +// physical addresses starting at pa. va and size might not +// be page-aligned. +static int mappages (pde_t *pgdir, void *va, uint size, uint pa, int ap) +{ + char *a, *last; + pte_t *pte; + + a = (char*) align_dn(va, PTE_SZ); + last = (char*) align_dn((uint)va + size - 1, PTE_SZ); + + for (;;) { + if ((pte = walkpgdir(pgdir, a, 1)) == 0) { + return -1; + } + + if (*pte & PE_TYPES) { + panic("remap"); + } + + *pte = pa | ((ap & 0x3) << 4) | PE_CACHE | PE_BUF | PTE_TYPE; + + if (a == last) { + break; + } + + a += PTE_SZ; + pa += PTE_SZ; + } + + return 0; +} + +// flush all TLB +static void flush_tlb (void) +{ + uint val = 0; + asm("MCR p15, 0, %[r], c8, c7, 0" : :[r]"r" (val):); + + // invalid entire data and instruction cache + asm ("MCR p15,0,%[r],c7,c10,0": :[r]"r" (val):); + asm ("MCR p15,0,%[r],c7,c11,0": :[r]"r" (val):); +} + +// Switch to the user page table (TTBR0) +void switchuvm (struct proc *p) +{ + uint val; + + pushcli(); + + if (p->pgdir == 0) { + panic("switchuvm: no pgdir"); + } + + val = (uint) V2P(p->pgdir) | 0x00; + + asm("MCR p15, 0, %[v], c2, c0, 0": :[v]"r" (val):); + flush_tlb(); + + popcli(); +} + +// Load the initcode into address 0 of pgdir. sz must be less than a page. +void inituvm (pde_t *pgdir, char *init, uint sz) +{ + char *mem; + + if (sz >= PTE_SZ) { + panic("inituvm: more than a page"); + } + + mem = alloc_page(); + memset(mem, 0, PTE_SZ); + mappages(pgdir, 0, PTE_SZ, v2p(mem), AP_KU); + memmove(mem, init, sz); +} + +// Load a program segment into pgdir. addr must be page-aligned +// and the pages from addr to addr+sz must already be mapped. +int loaduvm (pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) +{ + uint i, pa, n; + pte_t *pte; + + if ((uint) addr % PTE_SZ != 0) { + panic("loaduvm: addr must be page aligned"); + } + + for (i = 0; i < sz; i += PTE_SZ) { + if ((pte = walkpgdir(pgdir, addr + i, 0)) == 0) { + panic("loaduvm: address should exist"); + } + + pa = PTE_ADDR(*pte); + + if (sz - i < PTE_SZ) { + n = sz - i; + } else { + n = PTE_SZ; + } + + if (readi(ip, p2v(pa), offset + i, n) != n) { + return -1; + } + } + + return 0; +} + +// Allocate page tables and physical memory to grow process from oldsz to +// newsz, which need not be page aligned. Returns new size or 0 on error. +int allocuvm (pde_t *pgdir, uint oldsz, uint newsz) +{ + char *mem; + uint a; + + if (newsz >= UADDR_SZ) { + return 0; + } + + if (newsz < oldsz) { + return oldsz; + } + + a = align_up(oldsz, PTE_SZ); + + for (; a < newsz; a += PTE_SZ) { + mem = alloc_page(); + + if (mem == 0) { + cprintf("allocuvm out of memory\n"); + deallocuvm(pgdir, newsz, oldsz); + return 0; + } + + memset(mem, 0, PTE_SZ); + mappages(pgdir, (char*) a, PTE_SZ, v2p(mem), AP_KU); + } + + return newsz; +} + +// Deallocate user pages to bring the process size from oldsz to +// newsz. oldsz and newsz need not be page-aligned, nor does newsz +// need to be less than oldsz. oldsz can be larger than the actual +// process size. Returns the new process size. +int deallocuvm (pde_t *pgdir, uint oldsz, uint newsz) +{ + pte_t *pte; + uint a; + uint pa; + + if (newsz >= oldsz) { + return oldsz; + } + + for (a = align_up(newsz, PTE_SZ); a < oldsz; a += PTE_SZ) { + pte = walkpgdir(pgdir, (char*) a, 0); + + if (!pte) { + // pte == 0 --> no page table for this entry + // round it up to the next page directory + a = align_up (a, PDE_SZ); + + } else if ((*pte & PE_TYPES) != 0) { + pa = PTE_ADDR(*pte); + + if (pa == 0) { + panic("deallocuvm"); + } + + free_page(p2v(pa)); + *pte = 0; + } + } + + return newsz; +} + +// Free a page table and all the physical memory pages +// in the user part. +void freevm (pde_t *pgdir) +{ + uint i; + char *v; + + if (pgdir == 0) { + panic("freevm: no pgdir"); + } + + // release the user space memroy, but not page tables + deallocuvm(pgdir, UADDR_SZ, 0); + + // release the page tables + for (i = 0; i < NUM_UPDE; i++) { + if (pgdir[i] & PE_TYPES) { + v = p2v(PT_ADDR(pgdir[i])); + kpt_free(v); + } + } + + kpt_free((char*) pgdir); +} + +// Clear PTE_U on a page. Used to create an inaccessible page beneath +// the user stack (to trap stack underflow). +void clearpteu (pde_t *pgdir, char *uva) +{ + pte_t *pte; + + pte = walkpgdir(pgdir, uva, 0); + if (pte == 0) { + panic("clearpteu"); + } + + // in ARM, we change the AP field (ap & 0x3) << 4) + *pte = (*pte & ~(0x03 << 4)) | AP_KO << 4; +} + +// Given a parent process's page table, create a copy +// of it for a child. +pde_t* copyuvm (pde_t *pgdir, uint sz) +{ + pde_t *d; + pte_t *pte; + uint pa, i, ap; + char *mem; + + // allocate a new first level page directory + d = kpt_alloc(); + if (d == NULL ) { + return NULL ; + } + + // copy the whole address space over (no COW) + for (i = 0; i < sz; i += PTE_SZ) { + if ((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) { + panic("copyuvm: pte should exist"); + } + + if (!(*pte & PE_TYPES)) { + panic("copyuvm: page not present"); + } + + pa = PTE_ADDR (*pte); + ap = PTE_AP (*pte); + + if ((mem = alloc_page()) == 0) { + goto bad; + } + + memmove(mem, (char*) p2v(pa), PTE_SZ); + + if (mappages(d, (void*) i, PTE_SZ, v2p(mem), ap) < 0) { + goto bad; + } + } + return d; + +bad: freevm(d); + return 0; +} + +//PAGEBREAK! +// Map user virtual address to kernel address. +char* uva2ka (pde_t *pgdir, char *uva) +{ + pte_t *pte; + + pte = walkpgdir(pgdir, uva, 0); + + // make sure it exists + if ((*pte & PE_TYPES) == 0) { + return 0; + } + + // make sure it is a user page + if (PTE_AP(*pte) != AP_KU) { + return 0; + } + + return (char*) p2v(PTE_ADDR(*pte)); +} + +// Copy len bytes from p to user address va in page table pgdir. +// Most useful when pgdir is not the current page table. +// uva2ka ensures this only works for user pages. +int copyout (pde_t *pgdir, uint va, void *p, uint len) +{ + char *buf, *pa0; + uint n, va0; + + buf = (char*) p; + + while (len > 0) { + va0 = align_dn(va, PTE_SZ); + pa0 = uva2ka(pgdir, (char*) va0); + + if (pa0 == 0) { + return -1; + } + + n = PTE_SZ - (va - va0); + + if (n > len) { + n = len; + } + + memmove(pa0 + (va - va0), buf, n); + + len -= n; + buf += n; + va = va0 + PTE_SZ; + } + + return 0; +} + + +// 1:1 map the memory [phy_low, phy_hi] in kernel. We need to +// use 2-level mapping for this block of memory. The rumor has +// it that ARMv6's small brain cannot handle the case that memory +// be mapped in both 1-level page table and 2-level page. For +// initial kernel, we use 1MB mapping, other memory needs to be +// mapped as 4KB pages +void paging_init (uint phy_low, uint phy_hi) +{ + mappages (P2V(&_kernel_pgtbl), P2V(phy_low), phy_hi - phy_low, phy_low, AP_KU); + flush_tlb (); +}