Mercurial > hg > Members > mitsuki > xv6_rpi2_port
changeset 0:ed10291ff195
first commit
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,30 @@ +The xv6 RPI2 port software is: + +Copyright (c) 2017 +Zhiyi Huang, University of Otago +Alex Bradbury, University of Cambridge +Alex Chadwick, University of Cambridge +Theo Markettos, University of Cambridge +Robert Mullins, University of Cambridge +Robert N. M. Watson, University of Cambridge + + +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/Makefile Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,149 @@ +UNAME := $(shell uname -m) + +ifeq ($(UNAME), x86_64) + # set FVP as default on x86_64 systems + hw?=rpi2 + TOOLCHAIN?=~/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi- +else + # RPI reports 'uname -m' as: armv7l + hw?=rpi2 +endif + +# PHYSTART : start of memory (RAM) +# PHYSIZE : size of memory +# KERNBASE : address of high memory schema +# MMIO_PA : physical address of peripherals (MMIO) +# MMIO_VA : virtal address of peripherals +# MMIO_SIZE : size of peripherals +# PERIPHBASE: virtal address of PERIPHBASE[39:15] + +ifeq ($(hw), fvp) + PHYSTART = 0x80000000 + PHYSIZE = 0x08000000 + KERNBASE = 0xC0000000 + MMIO_PA = 0x1C000000 + MMIO_VA = 0xD0000000 + MMIO_SIZE = 0x04000000 + PERIPHBASE= 0xDF000000 + CFLAGS = -ffreestanding -nostdlib -nostartfiles -O0 -Wall -MD -ggdb -Wall -mcpu=cortex-a9 -mfloat-abi=hard -fno-short-enums -I include + #CFLAGS = -ffreestanding -nostdlib -nostartfiles -O2 -Wall -MD -ggdb -Wall -mcpu=cortex-a9 -mfloat-abi=hard -fno-short-enums -I include + TARGET = fvp.img + CC_OPTIONS = -DFVP +else ifeq ($(hw), rpi1) + PHYSTART = 0x00000000 + # PHYSIZE set to 128mb + PHYSIZE = 0x08000000 + KERNBASE = 0x80000000 + MMIO_PA = 0x20000000 + MMIO_VA = 0xD0000000 + MMIO_SIZE = 0x01000000 + PERIPHBASE= 0xDF000000 + #CFLAGS = -fno-pic -static -Wno-packed-bitfield-compat -fno-builtin -fno-strict-aliasing -fshort-wchar -O2 -Wall -MD -ggdb -Werror -fno-omit-frame-pointer -fno-stack-protector -Wa,-march=armv6 -Wa,-mcpu=arm1176jzf-s -mfloat-abi=hard -fno-short-enums -I include + CFLAGS = -ffreestanding -nostdlib -nostartfiles -O2 -Wall -MD -ggdb -Wall -mcpu=arm1176jzf-s -mfloat-abi=hard -fno-short-enums -I include + TARGET = kernel.img + CC_OPTIONS = -DRPI1 +else ifeq ($(hw), rpi2) + PHYSTART = 0x00000000 + # PHYSIZE set to 256mb + PHYSIZE = 0x10000000 + KERNBASE = 0x80000000 + MMIO_PA = 0x3F000000 + MMIO_VA = 0xD0000000 + MMIO_SIZE = 0x01000000 + PERIPHBASE= 0xDF000000 + CFLAGS = -ffreestanding -nostdlib -nostartfiles -O2 -Wall -MD -ggdb -Wall -mcpu=cortex-a7 -mfloat-abi=hard -fno-short-enums -I include + TARGET = kernel7.bin + CC_OPTIONS = -DRPI2 +else +$(error Hardware (hw) should be fvp, rpi1 or rpi2, eg. make hw=rpi2) +endif + + +K_PDX_BASE=$(shell printf "0x%X\n" $$(( $(PHYSTART) + 0x4000 )) ) +K_PTX_BASE=$(shell printf "0x%X\n" $$(( $(PHYSTART) + 0x3000 )) ) +OFFSET=0x8000 +PHYSOFFSET=$(shell printf "0x%X\n" $$(( $(PHYSTART) + $(OFFSET) )) ) +KERNOFFSET=$(shell printf "0x%X\n" $$(( $(KERNBASE) + $(OFFSET) )) ) + +CC_OPTIONS += -DPHYSTART=$(PHYSTART) -DPHYSIZE=$(PHYSIZE) -DKERNBASE=$(KERNBASE) -DMMIO_PA=$(MMIO_PA) -DMMIO_VA=$(MMIO_VA) -DMMIO_SIZE=$(MMIO_SIZE) -DPERIPHBASE=$(PERIPHBASE) +CC_OPTIONS += -DK_PDX_BASE=$(K_PDX_BASE) -DK_PTX_BASE=$(K_PTX_BASE) -DPHYSOFFSET=$(PHYSOFFSET) -DKERNOFFSET=$(KERNOFFSET) + +LD_OPTIONS = --defsym=PHYSTART=$(PHYSTART) --defsym=PHYSIZE=$(PHYSIZE) --defsym=KERNBASE=$(KERNBASE) --defsym=MMIO_PA=$(MMIO_PA) --defsym=MMIO_VA=$(MMIO_VA) --defsym=MMIO_SIZE=$(MMIO_SIZE) --defsym=PERIPHBASE=$(PERIPHBASE) +LD_OPTIONS += --defsym=K_PDX_BASE=$(K_PDX_BASE) --defsym=K_PTX_BASE=$(K_PTX_BASE) --defsym=PHYSOFFSET=$(PHYSOFFSET) --defsym=KERNOFFSET=$(KERNOFFSET) + +# Build directory +BUILD=build/ +# Sources directory. +SOURCE=source/ +# Generated listing file. +LIBRARIES= + +# The names of all object files that must be generated. Deduced from the +# assembly code files in source. +ASM_OBJECTS = $(patsubst $(SOURCE)%.S,$(BUILD)%.o,$(wildcard $(SOURCE)*.S)) + +C_OBJECTS = $(patsubst $(SOURCE)%.c,$(BUILD)%.o,$(wildcard $(SOURCE)*.c)) + +# Rule to make everything. +all : $(TARGET) + +# Rule to make the elf file. +$(TARGET): $(ASM_OBJECTS) $(C_OBJECTS) kernel.ld + $(TOOLCHAIN)ld $(ASM_OBJECTS) $(C_OBJECTS) -L. $(patsubst %,-l %,$(LIBRARIES)) $(LD_OPTIONS) -Map kernel.map -o $(BUILD)kernel.elf -T kernel.ld + $(TOOLCHAIN)objdump -d $(BUILD)kernel.elf > kernel.list + $(TOOLCHAIN)objcopy $(BUILD)kernel.elf -O binary $(TARGET) + +# Build ASM files +$(BUILD)%.o: $(SOURCE)%.S $(BUILD) + $(TOOLCHAIN)gcc -c $(CFLAGS) $(CC_OPTIONS) -I source $< -o $@ + +# Build C files +$(BUILD)%.o: $(SOURCE)%.c $(BUILD) + $(TOOLCHAIN)gcc -c $(CFLAGS) $(CC_OPTIONS) $< -o $@ + +loader: loader.S kernel7.bin + $(TOOLCHAIN)gcc -c loader.S -o loader.elf -fpic -ffreestanding -nostdlib -nostartfiles -O0 -Wall -ggdb -Wall -mcpu=cortex-a7 -mfloat-abi=hard -fno-short-enums -o loader.o + $(TOOLCHAIN)ld loader.o -o loader.elf -T loader.ld + #$(TOOLCHAIN)objdump -D loader.elf > loader.list + $(TOOLCHAIN)objcopy loader.elf -O binary kernel7.img + -rm loader.elf loader.o + +$(BUILD): + mkdir $@ + +.PHONY: report +report: + @echo 'Hardware :' $(hw) + @echo 'TARGET :' $(TARGET) + @echo 'PHYSTART :' $(PHYSTART) + @echo 'PHYSIZE :' $(PHYSIZE) + + @echo 'K_PDX_BASE:' $(K_PDX_BASE) + @echo 'K_PTX_BASE:' $(K_PTX_BASE) + @echo 'PHYSOFFSET:' $(PHYSOFFSET) + + @echo 'KERNBASE :' $(KERNBASE) + @echo 'KERNOFFSET:' $(KERNOFFSET) + + @echo 'MMIO_PA :' $(MMIO_PA) + @echo 'MMIO_SIZE :' $(MMIO_SIZE) + @echo 'MMIO_VA :' $(MMIO_VA) + @echo 'PERIPHBASE:' $(PERIPHBASE) + + @echo 'CFLAGS :' $(CFLAGS) + @echo 'CC_OPTIONS:' $(CC_OPTIONS) + @echo 'LD_OPTIONS:' $(LD_OPTIONS) + +.PHONY: install +install: + cp kernel*.img /media/$(USER)/boot/ + sync + umount /media/$(USER)/* + +# Rule to clean files. +clean : + -rm -rf $(BUILD) + -rm -f *.img + -rm -f *.bin + -rm -f kernel.list + -rm -f kernel.map
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,65 @@ +xv6_rpi2_port is based on MIT xv6 (http://pdos.csail.mit.edu/6.828/2012/v6.html). +It is ported from x86 to armv6 and then to armv7 in Raspberry Pi (RPI2/3). +The rpi port follows the coding style of xv6 as much as possible to hide the architectural +differences between x86 and armv6/armv7. The port is not for multiprocessor yet +though RPI2/3 has four cores. + +ACKNOWLEDGEMENTS + +xv6_rpi2_port is inspired by MIT xv6 and Alex Chadwick's Baking Pi +Tutorials (http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/). +Most architecture-independent code is directly from MIT xv6 though sometimes +minor adjustments were done to explicitly initialize data structures. +Some C code such as the GPU driver is based on the understanding of +Alex Chadwick's assembly code. + +Some code for mmu and trap handling is based on the understanding of the +Plan 9 bcm port (http://plan9.bell-labs.com/sources/plan9/sys/src/9/bcm/), +though the assembly code was completely rewritten. +David Welch's RPI code (https://github.com/dwelch67/raspberrypi) is also +inspiring for trap handling and uart driver. + +Mahdi Amiri Kordestany (mahdi@cs.otago.ac.nz) ported xv6 from RPI1 to +RPI2/3. + +If you spot errors or suggest improvements, please send email to +Zhiyi Huang (hzy@cs.otago.ac.nz). + +Building xv6_rpi2_port: + +Suppose you have checked out the source with: + +$ git clone https://github.com/zhiyihuang/xv6_rpi2_port.git + +On an RPI2/3 installed with Raspbian, type 'make loader' to make 'kernel7.img'. + +Copy 'kernel7.img' to /boot with a different name: +# cp kernel7.img /boot/kernel-xv6.img + +Comment out the old entry 'kernel=' and add a new entry +'kernel=kernel-xv6.img' to /boot/config.txt. + +Add the following into /boot/config.txt: + +kernel_old=1 +disable_commandline_tags=1 +enable_uart=1 + + +Reboot the machine. + +The USB keyboard is not working yet. The only way to input into +the console is to use the mini UART (serial port). You can use a +USB to TTL Serial Cable to connect the mini UART to a virtual terminal. +You may use minicom or CoolTerm to create the virtual terminal. +You have to open the lid to connect the cable to the GPIO pins (14 and 15) +of the Pi. + +Building xv6 user programs and FS (You don't need this step if you +don't change the user programs): + +cd uprogs +make + +copy 'initcode' and 'fs.img' to the directory 'source' +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/connect.gdb Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,2 @@ +symbol build/kernel.elf +target remote tcp::1234
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debug.sh Sun Jan 06 19:27:03 2019 +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/include/arm.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,72 @@ +/***************************************************************** +* arm.h +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + + +#define PSR_MODE_USR 0x00000010 +#define PSR_MODE_FIQ 0x00000011 +#define PSR_MODE_IRQ 0x00000012 +#define PSR_MODE_SVC 0x00000013 +#define PSR_MODE_MON 0x00000016 +#define PSR_MODE_ABT 0x00000017 +#define PSR_MODE_UND 0x0000001B +#define PSR_MODE_SYS 0x0000001F +#define PSR_MASK 0x0000001F +#define USER_MODE 0x0 + +#define PSR_DISABLE_IRQ 0x00000080 +#define PSR_DISABLE_FIQ 0x00000040 + +#define PSR_V 0x10000000 +#define PSR_C 0x20000000 +#define PSR_Z 0x40000000 +#define PSR_N 0x80000000 + + +static inline uint +inw(uint addr) +{ + uint data; + + asm volatile("ldr %0,[%1]" : "=r"(data) : "r"(addr)); + return data; +} + +static inline void +outw(uint addr, uint data) +{ + asm volatile("str %1,[%0]" : : "r"(addr), "r"(data)); +} + + +// Layout of the trap frame built on the stack +// by exception.s, and passed to trap(). +struct trapframe { + uint sp; // user mode sp + 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 r13; + uint r14; + uint trapno; + uint ifar; // Instruction Fault Address Register (IFAR) + uint cpsr; + uint spsr; // saved cpsr from the trapped/interrupted mode + uint pc; // return address of the interrupted code +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/buf.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,13 @@ +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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/defs.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,233 @@ +struct buf; +struct context; +struct file; +struct inode; +struct pipe; +struct proc; +struct spinlock; +struct stat; +struct superblock; + +void OkLoop(void); +void NotOkLoop(void); + +// mmu.c +void mmuinit0(void); +void mmuinit1(void); +void barriers(void); +void dsb_barrier(void); +void flush_tlb(void); +void flush_dcache_all(void); +void flush_dcache(uint va1, uint va2); +void flush_idcache(void); +void set_pgtbase(uint base); + +// bio.c +void binit(void); +struct buf* bread(uint, uint); +void brelse(struct buf*); +void bwrite(struct buf*); + +// console.c +void consoleinit(void); +void cprintf(char*, ...); +void consoleintr(int(*)(void)); +void panic(char*) __attribute__((noreturn)); +void drawcharacter(u8, uint, uint); +void gpuputc(uint); +void gpuinit(void); + + +// 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 ideintr(void); +void iderw(struct buf*); + +// 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); + +// kalloc.c +char* kalloc(void); +void kfree(char*); +void kinit1(void*, void*); +void kinit2(void*, void*); + + +// log.c +void initlog(void); +void log_write(struct buf*); +void begin_trans(); +void commit_trans(); + +// 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*); + +// 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); + +void kvmalloc(void); + + +int UsbInitialise(void); +void KeyboardUpdate(void); +char KeyboardGetChar(void); +uint KeyboardCount(void); +uint KeyboardGetAddress(uint); +struct KeyboardLeds KeyboardGetLedSupport(uint); + +// spinlock.c +void acquire(struct spinlock*); +void getcallerpcs(void*, uint*); +int holding(struct spinlock*); +void initlock(struct spinlock*, char*); +void release(struct spinlock*); +void pushcli(void); +void popcli(void); + +// 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); +uint div(uint n, uint d); + +// 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 timer3init(void); +void timer3intr(void); +unsigned long long getsystemtime(void); +void delay(uint); + +// trap.c +void tvinit(void); +void sti(void); +void cli(void); +void disable_intrs(void); +void enable_intrs(void); +extern uint ticks; +extern struct spinlock tickslock; +uint readcpsr(void); + +// uart.c +void uartinit(void); +void miniuartintr(void); +void uartputc(uint); +void setgpiofunc(uint, uint); +void setgpioval(uint, uint); + +// vm.c +void seginit(void); +void kvmalloc(void); +void vmenable(void); +pde_t* setupkvm(void); +char* uva2ka(pde_t*, char*); +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*); +void switchkvm(void); +int copyout(pde_t*, uint, void*, uint); +void clearpteu(pde_t *pgdir, char *uva); + +// mailbox.c +uint readmailbox(u8); +void writemailbox(uint *, u8); +void create_request(volatile uint *mbuf, uint tag, uint buflen, uint len, uint *data); +void mailboxinit(void); + + + +// number of elements in fixed-size array +#define NELEM(x) (sizeof(x)/sizeof((x)[0])) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/elf.h Sun Jan 06 19:27:03 2019 +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/include/fcntl.h Sun Jan 06 19:27:03 2019 +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/include/file.h Sun Jan 06 19:27:03 2019 +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/include/fs.h Sun Jan 06 19:27:03 2019 +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/include/fvp.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,41 @@ +/* + * fvp.h + * + * Created on: Dec 2, 2016 + * Author: Mahdi Amiri + */ + +#ifndef FVP_H +#define FVP_H +#define FVP_PL011_BASE (MMIO_VA + 0x90000) +#define FVP_PL011_UARTDR FVP_PL011_BASE // Data register +#define FVP_PL011_UARTFR (FVP_PL011_BASE+0x18) // Flag register +#define FVP_PL011_UARTFR_TXFF (1 << 5) // Transmit FIFO full @ UARTFR +#define FVP_PL011_UARTFR_RXFE (1 << 4) // Receive FIFO empty @ UARTFR +#define FVP_PL011_UARTCR (FVP_PL011_BASE+0x30) // Control register +#define FVP_PL011_UARTCR_UARTEN 1 // UART enable @ UARTCR +#define FVP_PL011_UARTCR_TXE (1<<8) // Transmit enable @ UARTCR +#define FVP_PL011_UARTCR_RXE (1<<9) // Receive enable @ UARTCR +#define FVP_PL011_UARTIMSC (FVP_PL011_BASE+0x38) // Interrupt mask set/clear register +#define FVP_PL011_UARTIMSC_RXIM (1<<4) // Receive interrupt mask. + +//#define FVP_GLOBAL_TIMER_BASE ((uint) &__va_private_start + 0x0200 ) +#define FVP_GLOBAL_TIMER_BASE (MMIO_VA + 0x110000) +#define FVP_TIMER1_LOAD FVP_GLOBAL_TIMER_BASE +#define FVP_TIMER1_VALUE ( FVP_GLOBAL_TIMER_BASE + 0x4 ) +#define FVP_TIMER1_CNTL ( FVP_GLOBAL_TIMER_BASE + 0x8 ) +#define FVP_TIMER1_INT_CTRL ( FVP_GLOBAL_TIMER_BASE + 0xC ) +#define FVP_TIMER1_RIS ( FVP_GLOBAL_TIMER_BASE + 0x10 ) +#define FVP_TIMER1_MIS ( FVP_GLOBAL_TIMER_BASE + 0x14 ) +#define FVP_TIMER1_BGL ( FVP_GLOBAL_TIMER_BASE + 0x18 ) + +#define ARM_GLOBAL_TIMER_BASE ((uint) &__va_private_start + 0x0200 ) +#define ARM_GLOBAL_TIMER_VALUE_LO ARM_GLOBAL_TIMER_BASE // Load Register +#define ARM_GLOBAL_TIMER_VALUE_HI ( ARM_GLOBAL_TIMER_BASE + 0x4 ) // Value +#define ARM_GLOBAL_TIMER_CNTL ( ARM_GLOBAL_TIMER_BASE + 0x8 ) // Value +#define ARM_GLOBAL_TIMER_INT_STATUS ( ARM_GLOBAL_TIMER_BASE + 0xC ) // Value +#define ARM_GLOBAL_TIMER_CMP_LO ( ARM_GLOBAL_TIMER_BASE + 0x10 ) // Value +#define ARM_GLOBAL_TIMER_CMP_HI ( ARM_GLOBAL_TIMER_BASE + 0x14 ) // Value +#define ARM_GLOBAL_TIMER_AUTO_INC ( ARM_GLOBAL_TIMER_BASE + 0x18 ) // Value + +#endif /* FVP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/mailbox.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,38 @@ +#define MAILBOX_BASE (MMIO_VA+0x00B880) + +#define MPI_REQUEST 0x00000000 +#define MPI_RESPONSE_OK 0x80000000 +#define MPI_RESPONSE_ERR 0x80000001 + +#define POS_OVERALL_LENGTH 0 +#define POS_RV 1 +#define POS_TAG 2 + +#define POS_TAG_ID 0 +#define POS_TAG_BUFLEN 1 +#define POS_TAG_DATALEN 2 +#define POS_TAG_DATA 3 + + +#define MB_HEADER_LENGTH 2 +#define TAG_HEADER_LENGTH 3 + +/* to be extended */ +#define MPI_TAG_GET_FIRMWARE 0x00000001 +#define MPI_TAG_GET_BOARD_MODEL 0x00010001 +#define MPI_TAG_GET_BOARD_REVISION 0x00010002 +#define MPI_TAG_GET_MAC_ADDRESS 0x00010003 +#define MPI_TAG_GET_BOARD_SERIAL 0x00010004 +#define MPI_TAG_GET_ARM_MEMORY 0x00010005 +#define MPI_TAG_GET_VC_MEMORY 0x00010006 +#define MPI_TAG_GET_CLOCKS 0x00010007 +#define MPI_TAG_GET_COMMANDLINE 0x00050001 +#define MPI_TAG_GET_DMA_CHANNELS 0x00060001 +#define MPI_TAG_GET_POWER_STATE 0x00020001 +#define MPI_TAG_SET_POWER_STATE 0x00028001 +#define MPI_TAG_GET_TIMING 0x00020002 +#define MPI_TAG_GET_FIRMWARE 0x00000001 +#define MPI_TAG_GET_CLOCK_STATE 0x00030001 +#define MPI_TAG_SET_CLOCK_STATE 0x00038001 + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/memlayout.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,28 @@ +/***************************************************************** +* memlayout.h +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +// Memory layout + +#define PHYSTOP (PHYSTART+PHYSIZE) + +#define USERBOUND 0x40000000 // maximum user space due to one page pgd +#define GPUMEMBASE 0x40000000 +#define GPUMEMSIZE (1024*MBYTE) + +#define TVSIZE 0x1000 + +static inline uint v2p(void *a) { return ((uint) (a)) - (KERNBASE-PHYSTART); } +static inline void *p2v(uint a) { return (void *) ((a) + (KERNBASE-PHYSTART)); } + +#define V2P(a) (((uint) (a)) - (KERNBASE-PHYSTART)) +#define P2V(a) (((void *) (a)) + (KERNBASE-PHYSTART)) + +#define V2P_WO(x) ((x) - (KERNBASE-PHYSTART)) // same as V2P, but without casts +#define P2V_WO(x) ((x) + (KERNBASE-PHYSTART)) // same as V2P, but without casts +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/mmu.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,86 @@ +/***************************************************************** +* mmu.h +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + +#define MBYTE 0x100000 +//Makefile #define K_PDX_BASE 0x4000 +//Makefile #define K_PTX_BASE 0x3000 + +#define CACHELINESIZE 32 + +/* + * page table entries. +*/ + +#define UNMAPPED 0x00000000 + +#define COARSE (0<<4|1) +#define SECTION (0<<4|2) + +#define LARGE 0x00000001 +#define SMALL 0x00000002 +#define BUFFERED 0x00000004 +#define CACHED 0x00000008 +#define APX 1 << 9 +#define SHAREABLE 1 << 10 +#define nG 1 << 11 // non-global +#define XN 0x1 // execute never +#define DOMAIN0 0 + +#define NOACCESS 0 +#define K_RW 1 +#define U_AP 2 +#define U_RW 3 + +#define ACCESS_PERM(n, v) (((v) & 3) << (((n) * 2) + 4)) +#define PDX_AP(ap) (ACCESS_PERM(3, (ap))) +#define PTX_AP(ap) (ACCESS_PERM(3, (ap)) | ACCESS_PERM(2, (ap)) \ + | ACCESS_PERM(1, (ap)) | ACCESS_PERM(0, (ap))) + +#define HVECTORS 0xffff0000 + +// A virtual address 'la' has a three-part structure as follows: +// +// +--------12------+-------8--------+---------12----------+ +// | Page Directory | Page Table | Offset within Page | +// | Index | Index | | +// +----------------+----------------+---------------------+ +// \--- PDX(va) --/ \--- PTX(va) --/ + +// page directory index +#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0xFFF) + +// page table index +#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0xFF) + +// construct virtual address from indexes and offset +#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) + +// Address in page table or page directory entry +#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF) +#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF) + +// Page directory and page table constants. +#define NPDENTRIES 1024 // # directory entries per page directory +#define NPTENTRIES 1024 // # PTEs per page table +#define PGSIZE 4096 // bytes mapped by a page + +#define PGSHIFT 12 // log2(PGSIZE) +#define PTXSHIFT 12 // offset of PTX in a linear address +#define PDXSHIFT 20 // offset of PDX in a linear address + + +#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) +#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1)) + +#define PGDIR_BASE P2V(K_PDX_BASE) + +#define KVMPDXATTR DOMAIN0|PDX_AP(U_RW)|SECTION|CACHED|BUFFERED + +#define UVMPDXATTR DOMAIN0|COARSE +#define UVMPTXATTR (PTX_AP(U_RW)^APX)|CACHED|BUFFERED|SMALL +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/param.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,12 @@ +#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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/proc.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,91 @@ +/***************************************************************** +* proc.h +* adapted from MIT xv6 by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +// Segments in proc->gdt. +#define NSEGS 7 + +// Per-CPU state +struct cpu { + uchar id; // Local APIC 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. +}; + +struct cpu cpus[NCPU]; +//extern int ncpu; + +// Per-CPU variables, holding pointers to the +// current cpu and to the current process. +// The asm suffix tells gcc to use "%gs:0" to refer to cpu +// and "%gs:4" to refer to proc. seginit sets up the +// %gs segment register so that %gs refers to the memory +// holding those two variables in the local cpu's struct cpu. +// This is similar to how thread-local variables are implemented +// in thread libraries such as Linux pthreads. +//extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()] +//extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc + +#define curr_cpu (&cpus[0]) +#define curr_proc (cpus[0].proc) + +//PAGEBREAK: 17 +// Saved registers for kernel context switches. +// Don't need to save all the segment registers (%cs, etc), +// because they are constant across kernel contexts. +// Don't need to save %eax, %ecx, %edx, because the +// x86 convention is that the caller has saved them. +// Contexts are stored at the bottom of the stack they +// describe; the stack pointer is the address of the context. +// The layout of the context matches the layout of the stack in swtch.S +// at the "Switch stacks" comment. Switch doesn't save eip explicitly, +// but it is on the stack and allocproc() manipulates it. +struct context { + uint r4; + uint r5; + uint r6; + uint r7; + uint r8; + uint r9; + uint r10; + uint r11; + uint r12; + uint lr; + uint pc; +}; + +enum procstate { UNUSED=0, 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/spinlock.h Sun Jan 06 19:27:03 2019 +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/include/stat.h Sun Jan 06 19:27:03 2019 +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/include/syscall.h Sun Jan 06 19:27:03 2019 +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/include/traps.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,21 @@ +/***************************************************************** +* traps.h +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +// These are arbitrarily chosen, but with care not to overlap +// processor defined exceptions or interrupt vectors. +#define T_SYSCALL 0x40 // system call +#define T_IRQ 0x80 // interrupt +#define T_UND 0x01 // undefined instruction +#define T_PABT 0x02 // prefetch abort +#define T_DABT 0x04 // data abort + +#define IRQ_TIMER3 3 +#define IRQ_MINIUART 29 + +#define INT_REGS_BASE (MMIO_VA+0xB200)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/types.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,53 @@ +/***************************************************************** +* types.h +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef unsigned long long u64; + +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef uint pde_t; +typedef uint pte_t; + + +/* trap vectors layout at virtual +address HVECTORS (and KZERO(0x80000000), doubled mapped).*/ +typedef struct Vpage0 { + void (*vectors[8])(void); + uint vtable[8]; +} Vpage0; + + +/* ARM interrupt control registers */ +typedef struct intctrlregs { + uint armpending; + uint gpupending[2]; + uint fiqctrl; + uint gpuenable[2]; + uint armenable; + uint gpudisable[2]; + uint armdisable; +} intctrlregs; + +struct framebufdescription { + uint width; //width + uint height; // height + uint v_width; // virtual width + uint v_height; // virtual height + uint pitch; // GPU pitch + uint depth; // bit depth + uint x; + uint y; + uint fbp; //GPU framebuffer pointer + uint fbs; // GPU framebuffer size +}; + +typedef struct framebufdescription FBI;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/user.h Sun Jan 06 19:27:03 2019 +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/kernel.ld Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,71 @@ +/****************************************************************************** +* kernel.ld +* by Alex Chadwick +* +* A linker script for generation of raspberry pi kernel images. +******************************************************************************/ +ENTRY(_start) + +PHYSOFFSET = DEFINED(PHYSOFFSET) ? PHYSOFFSET : 0x80008000; +KERNOFFSET = DEFINED(KERNOFFSET) ? KERNOFFSET : 0xC0008000; +SHIFT = KERNOFFSET - PHYSOFFSET; + +SECTIONS { + /* Link the kernel at this address: "." means the current address */ + /* Must be equal to KERNLINK */ + + /* + * First and formost we need the .init section, containing the code to + * be run first. We allow room for the ATAGs and stack and conform to + * the bootloader's expectation by putting this code at 0x8000. + */ + + .init PHYSOFFSET : { + *(.init) + } + + . = ALIGN(0x100); + INIT_END = .; + . = . + SHIFT; + + /* + * Next we put the rest of the code. + */ + + .text : AT (INIT_END) { + *.c.o(.text) + *(.text .stub .text.*) + } + + /* + * read-only data + */ + .rodata : { + *(.rodata .rodata.*) + } + + /* Adjust the address for the data segment to the next page */ + . = ALIGN(0x1000); + + /* + * Next we put the data. + */ + .data : { + *(.data) + *.c.o(*) + } + + .bss : { + *(.bss) + } + + PROVIDE(end = .); + + /* + * Finally comes everything else. A fun trick here is to put all other + * sections into this section, which will be discarded by default. + */ + /DISCARD/ : { + *(.eh_frame .note.GNU-stack) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/loader.S Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,43 @@ +/* + * loader.S + * + * Created on: Feb 11, 2017 + * Author: Mahdi Amiri + */ + + .text + .globl _start +_start: + b loader_start /* branch to the code */ + b loader_sleep // undefined + b loader_sleep // svc + b loader_sleep // prefetch + b loader_sleep // abort + b loader_sleep // hypervisor + b loader_sleep // irq + b loader_sleep // fiq + + .balign 4 +loader_sleep: + wfi + b loader_sleep + +loader_start: + // Switch to SVC mode, all interrupts disabled + .set PSR_MODE_SVC, 0x13 + .set PSR_MODE_IRQ_DISABLED, (1<<7) + .set PSR_MODE_FIQ_DISABLED, (1<<6) + msr cpsr_c, #(PSR_MODE_SVC + PSR_MODE_FIQ_DISABLED + PSR_MODE_IRQ_DISABLED) + + // Set all CPUs to wait except the primary CPU + mrc p15, 0, r0, c0, c0, 5 + ands r0, r0, #0x03 + bne loader_sleep + + mov pc, #0x8000 + + .data + .align 4 +_data_start: + .incbin "kernel7.bin" +_data_end:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/loader.ld Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,9 @@ +ENTRY(_start) +SECTIONS { + .text 0x0 : { + * (.text); + } + .data 0x8000 : { + * (.data); + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/makefile-armgcc Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,149 @@ +UNAME := $(shell uname -m) + +ifeq ($(UNAME), x86_64) + # set FVP as default on x86_64 systems + hw?=rpi2 + TOOLCHAIN?=/Users/mitsuki/workspace/gcc/cross/bin/arm-none-eabi- +else + # RPI reports 'uname -m' as: armv7l + hw?=rpi2 +endif + +# PHYSTART : start of memory (RAM) +# PHYSIZE : size of memory +# KERNBASE : address of high memory schema +# MMIO_PA : physical address of peripherals (MMIO) +# MMIO_VA : virtal address of peripherals +# MMIO_SIZE : size of peripherals +# PERIPHBASE: virtal address of PERIPHBASE[39:15] + +ifeq ($(hw), fvp) + PHYSTART = 0x80000000 + PHYSIZE = 0x08000000 + KERNBASE = 0xC0000000 + MMIO_PA = 0x1C000000 + MMIO_VA = 0xD0000000 + MMIO_SIZE = 0x04000000 + PERIPHBASE= 0xDF000000 + CFLAGS = -ffreestanding -nostdlib -nostartfiles -O0 -Wall -MD -ggdb -Wall -mcpu=cortex-a9 -mfloat-abi=hard -fno-short-enums -I include + #CFLAGS = -ffreestanding -nostdlib -nostartfiles -O2 -Wall -MD -ggdb -Wall -mcpu=cortex-a9 -mfloat-abi=hard -fno-short-enums -I include + TARGET = fvp.img + CC_OPTIONS = -DFVP +else ifeq ($(hw), rpi1) + PHYSTART = 0x00000000 + # PHYSIZE set to 128mb + PHYSIZE = 0x08000000 + KERNBASE = 0x80000000 + MMIO_PA = 0x20000000 + MMIO_VA = 0xD0000000 + MMIO_SIZE = 0x01000000 + PERIPHBASE= 0xDF000000 + #CFLAGS = -fno-pic -static -Wno-packed-bitfield-compat -fno-builtin -fno-strict-aliasing -fshort-wchar -O2 -Wall -MD -ggdb -Werror -fno-omit-frame-pointer -fno-stack-protector -Wa,-march=armv6 -Wa,-mcpu=arm1176jzf-s -mfloat-abi=hard -fno-short-enums -I include + CFLAGS = -ffreestanding -nostdlib -nostartfiles -O2 -Wall -MD -ggdb -Wall -mcpu=arm1176jzf-s -mfloat-abi=hard -fno-short-enums -I include + TARGET = kernel.img + CC_OPTIONS = -DRPI1 +else ifeq ($(hw), rpi2) + PHYSTART = 0x00000000 + # PHYSIZE set to 256mb + PHYSIZE = 0x10000000 + KERNBASE = 0x80000000 + MMIO_PA = 0x3F000000 + MMIO_VA = 0xD0000000 + MMIO_SIZE = 0x01000000 + PERIPHBASE= 0xDF000000 + CFLAGS = -ffreestanding -nostdlib -nostartfiles -O2 -Wall -MD -ggdb -Wall -mcpu=cortex-a7 -mfloat-abi=hard -fno-short-enums -I include + TARGET = kernel7.bin + CC_OPTIONS = -DRPI2 +else +$(error Hardware (hw) should be fvp, rpi1 or rpi2, eg. make hw=rpi2) +endif + + +K_PDX_BASE=$(shell printf "0x%X\n" $$(( $(PHYSTART) + 0x4000 )) ) +K_PTX_BASE=$(shell printf "0x%X\n" $$(( $(PHYSTART) + 0x3000 )) ) +OFFSET=0x8000 +PHYSOFFSET=$(shell printf "0x%X\n" $$(( $(PHYSTART) + $(OFFSET) )) ) +KERNOFFSET=$(shell printf "0x%X\n" $$(( $(KERNBASE) + $(OFFSET) )) ) + +CC_OPTIONS += -DPHYSTART=$(PHYSTART) -DPHYSIZE=$(PHYSIZE) -DKERNBASE=$(KERNBASE) -DMMIO_PA=$(MMIO_PA) -DMMIO_VA=$(MMIO_VA) -DMMIO_SIZE=$(MMIO_SIZE) -DPERIPHBASE=$(PERIPHBASE) +CC_OPTIONS += -DK_PDX_BASE=$(K_PDX_BASE) -DK_PTX_BASE=$(K_PTX_BASE) -DPHYSOFFSET=$(PHYSOFFSET) -DKERNOFFSET=$(KERNOFFSET) + +LD_OPTIONS = --defsym=PHYSTART=$(PHYSTART) --defsym=PHYSIZE=$(PHYSIZE) --defsym=KERNBASE=$(KERNBASE) --defsym=MMIO_PA=$(MMIO_PA) --defsym=MMIO_VA=$(MMIO_VA) --defsym=MMIO_SIZE=$(MMIO_SIZE) --defsym=PERIPHBASE=$(PERIPHBASE) +LD_OPTIONS += --defsym=K_PDX_BASE=$(K_PDX_BASE) --defsym=K_PTX_BASE=$(K_PTX_BASE) --defsym=PHYSOFFSET=$(PHYSOFFSET) --defsym=KERNOFFSET=$(KERNOFFSET) + +# Build directory +BUILD=build/ +# Sources directory. +SOURCE=source/ +# Generated listing file. +LIBRARIES= + +# The names of all object files that must be generated. Deduced from the +# assembly code files in source. +ASM_OBJECTS = $(patsubst $(SOURCE)%.S,$(BUILD)%.o,$(wildcard $(SOURCE)*.S)) + +C_OBJECTS = $(patsubst $(SOURCE)%.c,$(BUILD)%.o,$(wildcard $(SOURCE)*.c)) + +# Rule to make everything. +all : $(TARGET) + +# Rule to make the elf file. +$(TARGET): $(ASM_OBJECTS) $(C_OBJECTS) kernel.ld + $(TOOLCHAIN)ld $(ASM_OBJECTS) $(C_OBJECTS) -L. $(patsubst %,-l %,$(LIBRARIES)) $(LD_OPTIONS) -Map kernel.map -o $(BUILD)kernel.elf -T kernel.ld + $(TOOLCHAIN)objdump -d $(BUILD)kernel.elf > kernel.list + $(TOOLCHAIN)objcopy $(BUILD)kernel.elf -O binary $(TARGET) + +# Build ASM files +$(BUILD)%.o: $(SOURCE)%.S $(BUILD) + $(TOOLCHAIN)gcc -c $(CFLAGS) $(CC_OPTIONS) -I source $< -o $@ + +# Build C files +$(BUILD)%.o: $(SOURCE)%.c $(BUILD) + $(TOOLCHAIN)gcc -c $(CFLAGS) $(CC_OPTIONS) $< -o $@ + +loader: loader.S kernel7.bin + $(TOOLCHAIN)gcc -c loader.S -o loader.elf -fpic -ffreestanding -nostdlib -nostartfiles -O0 -Wall -ggdb -Wall -mcpu=cortex-a7 -mfloat-abi=hard -fno-short-enums -o loader.o + $(TOOLCHAIN)ld loader.o -o loader.elf -T loader.ld + #$(TOOLCHAIN)objdump -D loader.elf > loader.list + $(TOOLCHAIN)objcopy loader.elf -O binary kernel7.img + -rm loader.elf loader.o + +$(BUILD): + mkdir $@ + +.PHONY: report +report: + @echo 'Hardware :' $(hw) + @echo 'TARGET :' $(TARGET) + @echo 'PHYSTART :' $(PHYSTART) + @echo 'PHYSIZE :' $(PHYSIZE) + + @echo 'K_PDX_BASE:' $(K_PDX_BASE) + @echo 'K_PTX_BASE:' $(K_PTX_BASE) + @echo 'PHYSOFFSET:' $(PHYSOFFSET) + + @echo 'KERNBASE :' $(KERNBASE) + @echo 'KERNOFFSET:' $(KERNOFFSET) + + @echo 'MMIO_PA :' $(MMIO_PA) + @echo 'MMIO_SIZE :' $(MMIO_SIZE) + @echo 'MMIO_VA :' $(MMIO_VA) + @echo 'PERIPHBASE:' $(PERIPHBASE) + + @echo 'CFLAGS :' $(CFLAGS) + @echo 'CC_OPTIONS:' $(CC_OPTIONS) + @echo 'LD_OPTIONS:' $(LD_OPTIONS) + +.PHONY: install +install: + cp kernel*.img /media/$(USER)/boot/ + sync + umount /media/$(USER)/* + +# Rule to clean files. +clean : + -rm -rf $(BUILD) + -rm -f *.img + -rm -f *.bin + -rm -f kernel.list + -rm -f kernel.map
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/run-debug.sh Sun Jan 06 19:27:03 2019 +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 build/kernel.elf -s -S
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/LICENSE Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,24 @@ +The xv6 software is: + +Copyright (c) 2006-2009 Frans Kaashoek, Robert Morris, Russ Cox, + Massachusetts Institute of Technology + +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/source/bio.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,141 @@ +// 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; + + memset(&bcache, 0, sizeof(bcache)); + 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 0; +} + +// 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/source/console.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,535 @@ +/***************************************************************** + * console.c + * adapted from MIT xv6 by Zhiyi Huang, hzy@cs.otago.ac.nz + * University of Otago + * + ********************************************************************/ + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "traps.h" +#include "spinlock.h" +#include "fs.h" +#include "file.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "arm.h" + +#define BACKSPACE 0x100 + +static int panicked = 0; + +static struct { + struct spinlock lock; + int locking; +} cons; + + +uint cursor_x=0, cursor_y=0; +uint frameheight=1024, framewidth=1280, framedepth=16; +uint fontheight=16, fontwidth=8; +FBI fbinfo __attribute__ ((aligned (16), nocommon)); + +extern volatile uint *mailbuffer; +extern u8 font[]; +static uint gpucolour=0xffff; + +void setgpucolour(u16 c) +{ + gpucolour = c; +} + + + +uint initframebuf(uint width, uint height, uint depth) +{ + + + fbinfo.width = width; + fbinfo.height = height; + fbinfo.v_width = width; + fbinfo.v_height = height; + fbinfo.pitch = 0; + fbinfo.depth = depth; + fbinfo.x = 0; + fbinfo.y = 0; + fbinfo.fbp = 0; + fbinfo.fbs = 0; + writemailbox((uint *)&fbinfo, 1); + return readmailbox(1); +} + +#define INPUT_BUF 128 +struct { + struct spinlock lock; + char buf[INPUT_BUF]; + uint r; // Read index + uint w; // Write index + uint e; // Edit index +} input; + +int +consolewrite(struct inode *ip, char *buf, int n) +{ + int i; + + // cprintf("consolewrite is called: ip=%x buf=%x, n=%x", ip, buf, n); + iunlock(ip); + acquire(&cons.lock); + for(i = 0; i < n; i++){ +#if defined (RPI1) + gpuputc(buf[i] & 0xff); + uartputc(buf[i] & 0xff); +#elif defined (RPI2) + gpuputc(buf[i] & 0xff); + uartputc(buf[i] & 0xff); +#elif defined (FVP) + uartputc_fvp(buf[i] & 0xff); +#endif + + } + release(&cons.lock); + ilock(ip); + + return n; +} + + +void drawpixel(uint x, uint y) +{ + u16 *addr; + + if(x >= framewidth || y >= frameheight) return; + addr = (u16 *) fbinfo.fbp; + addr += y*framewidth + x; + *addr = gpucolour; + return; +} + + +void drawcursor(uint x, uint y) +{ + u8 row, bit; + + for(row=0; row<15; row++) + for(bit=0; bit<8; bit++) + drawpixel(x+bit, y+row); +} + +void drawcharacter(u8 c, uint x, uint y) +{ + u8 *faddr; + u8 row, bit, bits; + uint tv; + + if(c > 127) return; + tv = ((uint)c) << 4; + faddr = font + tv; + for(row=0; row<15; row++){ + bits = *(faddr+row); + for(bit=0; bit<8; bit++){ + if((bits>>bit) & 1) drawpixel(x+bit, y+row); + } + } + +} + +//static void +void +gpuputc(uint c) +{ + #if defined (RPI1) || defined (RPI2) + + if(fbinfo.fbp == 0) return; + + if(c=='\n'){ + cursor_x = 0; + cursor_y += fontheight; + if(cursor_y >= frameheight) { + memmove((u8 *)fbinfo.fbp, (u8 *)fbinfo.fbp+framewidth*fontheight*2, (frameheight - fontheight)*framewidth*2); + cursor_y = frameheight - fontheight; + setgpucolour(0); + while(cursor_x < framewidth) { + drawcursor(cursor_x, cursor_y); + cursor_x = cursor_x + fontwidth; + } + setgpucolour(0xffff); + cursor_x = 0; + } + } else if(c == BACKSPACE) { + if (cursor_x > 0) { + cursor_x -= fontwidth; + setgpucolour(0); + drawcursor(cursor_x, cursor_y); + setgpucolour(0xffff); + } + } else { + setgpucolour(0); + drawcursor(cursor_x, cursor_y); + setgpucolour(0xffff); + if(c!=' ') drawcharacter(c, cursor_x, cursor_y); + cursor_x = cursor_x + fontwidth; + if(cursor_x >= framewidth) { + cursor_x = 0; + cursor_y += fontheight; + if(cursor_y >= frameheight) { + memmove((u8 *)fbinfo.fbp, (u8 *)fbinfo.fbp+framewidth*fontheight*2, (frameheight - fontheight)*framewidth*2); + cursor_y = frameheight - fontheight; + setgpucolour(0); + while(cursor_x < framewidth) { + drawcursor(cursor_x, cursor_y); + cursor_x = cursor_x + fontwidth; + } + setgpucolour(0xffff); + cursor_x = 0; + } + } + } + #endif + +} + + +static void +printint(int xx, int base, int sign) +{ + static u8 digits[] = "0123456789abcdef"; + u8 buf[16]; + int i; + uint x, y, b; + + if(sign && (sign = xx < 0)) + x = -xx; + else + x = xx; + + b = base; + i = 0; + do{ + y = div(x, b); + buf[i++] = digits[x - y * b]; + }while((x = y) != 0); + + if(sign) + buf[i++] = '-'; + + while(--i >= 0){ + #if defined (RPI1) + gpuputc(buf[i]); + uartputc(buf[i]); + #elif defined (RPI2) + gpuputc(buf[i]); + uartputc(buf[i]); + #elif defined (FVP) + uartputc_fvp(buf[i]); + #endif + } +} + + +// Print to the console. only understands %d, %x, %p, %s. +void +cprintf(char *fmt, ...) +{ + int i, c; + int 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 != '%'){ + #if defined (RPI1) + gpuputc(c); + uartputc(c); + #elif defined (RPI2) + gpuputc(c); + uartputc(c); + #elif defined (FVP) + uartputc_fvp(c); + #endif + 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++){ + #if defined (RPI1) + gpuputc(*s); + uartputc(*s); + #elif defined (RPI2) + gpuputc(*s); + uartputc(*s); + #elif defined (FVP) + uartputc_fvp(*s); + #endif + } + break; + case '%': + #if defined (RPI1) + gpuputc('%'); + uartputc('%'); + #elif defined (RPI2) + gpuputc('%'); + uartputc('%'); + #elif defined (FVP) + uartputc_fvp('%'); + #endif + break; + default: + // Print unknown % sequence to draw attention. + #if defined (RPI1) + gpuputc('%'); + uartputc('%'); + gpuputc(c); + uartputc(c); + #elif defined (RPI2) + gpuputc('%'); + uartputc('%'); + gpuputc(c); + uartputc(c); + #elif defined (FVP) + uartputc_fvp('%'); + uartputc_fvp(c); + #endif + break; + } + } + if(locking) + release(&cons.lock); +} + +void +panic(char *s) +{ + int i; + uint pcs[10]; + + cprintf("cpu%d: panic: ", 0); + cprintf(s); + cprintf("\n"); + getcallerpcs(&s, pcs); + for(i=0; i<10; i++) + cprintf(" %p", pcs[i]); + panicked = 1; // freeze other CPU + + for(;;) + ; +} + +#define C(x) ((x)-'@') // Control-x + +void +consputc(int c) +{ + if(panicked){ + cli(); + for(;;) + ; + } + + if(c == BACKSPACE){ + //#if defined (RPI1) + gpuputc('\b'); gpuputc(' '); gpuputc('\b'); + //#endif + uartputc('\b'); uartputc(' '); uartputc('\b'); + } else if(c == C('D')) { + //#if defined (RPI1) + gpuputc('^'); gpuputc('D'); + //#endif + uartputc('^'); uartputc('D'); + } else { + //#if defined (RPI1) + gpuputc(c); + //#endif + uartputc(c); + } +} + + +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){ + if(c == 0xa) break; + c = (c == 0xd) ? '\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; + + //cprintf("inside consoleread\n"); + iunlock(ip); + target = n; + acquire(&input.lock); + while(n > 0){ + while(input.r == input.w){ + if(curr_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; +} + +void gpuinit() +{ + #if defined (RPI1) || defined (RPI2) + uint fbinforesp; + fbinforesp = initframebuf(framewidth, frameheight, framedepth); + if(fbinforesp != 0){ + fbinfo.fbp = 0; + cprintf("Failed to initialize GPU framebuffer!\n"); + return; + } + + // convert the address into ARM space and then to the ARM VM space for the whole physical address space + fbinfo.fbp = (fbinfo.fbp & 0x3fffffff) + 0x40000000; + //cprintf("The frame buffer pointer is %x\n", fbinfo.fbp); + #endif + + +/************************************************** +**** The following mailbox code for setting up GPU framebuffer also works *** +**** This may be a better way to set up the framebuffer but left for future extension ** + + create_request(mailbuffer, 0x40003, 8, 0, 0); //get physical buffer width/height + writemailbox((uint *)mailbuffer, 8); + readmailbox(8); + if(mailbuffer[1] != 0x80000000) cprintf("error readmailbox: %x\n", 0x40003); + cprintf("physical width/height are %d %d\n", mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH], mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH+1]); + + create_request(mailbuffer, 0x40005, 8, 0, 0); //get display depth + writemailbox((uint *)mailbuffer, 8); + readmailbox(8); + if(mailbuffer[1] != 0x80000000) cprintf("error readmailbox: %x\n", 0x40005); + cprintf("The depth of the display is %d\n", mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH]); + + mb_data[0] = 1280; mb_data[1] = 1024; + create_request(mailbuffer, 0x48004, 8, 2, mb_data); //set virtual buffer width/height + writemailbox((uint *)mailbuffer, 8); + readmailbox(8); + if(mailbuffer[1] != 0x80000000) cprintf("error readmailbox: %x\n", 0x48004); + cprintf("The virtual width/height are %d %d\n", mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH], mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH+1]); + + mb_data[0] = 0; mb_data[1] = 0; + create_request(mailbuffer, 0x48009, 8, 2, mb_data); //set virtual offset + writemailbox((uint *)mailbuffer, 8); + readmailbox(8); + if(mailbuffer[1] != 0x80000000) cprintf("error readmailbox: %x\n", 0x48009); + cprintf("The virtual offsets are %d %d\n", mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH], mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH+1]); + + mb_data[0] = 0; + create_request(mailbuffer, 0x40001, 8, 1, mb_data); //allocate buffer + writemailbox((uint *)mailbuffer, 8); + readmailbox(8); + if(mailbuffer[1] != 0x80000000) cprintf("error readmailbox: %x\n", 0x40001); + cprintf("The buffer base address and size are %x %x\n", mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH], mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH+1]); + fb = (u32 *)(0x40000000 + mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH]); + + create_request(mailbuffer, 0x40008, 8, 0, 0); //get pitch + writemailbox((uint *)mailbuffer, 8); + readmailbox(8); + if(mailbuffer[1] != 0x80000000) cprintf("error readmailbox: %x\n", 0x40008); + cprintf("The depth of the display is %d\n", mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH]); + +for (i=0; i< 2000; i++) fb[i] =0xffffffff; + +*****************/ + +} + + +void consoleinit(void) +{ + + fbinfo.fbp = 0; + initlock(&cons.lock, "console"); + memset(&input, 0, sizeof(input)); + initlock(&input.lock, "input"); + + memset(devsw, 0, sizeof(struct devsw)*NDEV); + devsw[CONSOLE].write = consolewrite; + devsw[CONSOLE].read = consoleread; + cons.locking = 1; + panicked = 0; // must initialize in code since the compiler does not + + cursor_x=cursor_y=0; + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/entry.S Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,312 @@ +/***************************************************************** +* entry.s +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + +.section .init, "ax" +.globl _start +_start: + b boot_reset + b boot_sleep // undefined + b boot_sleep // svc + b boot_sleep // prefetch + b boot_sleep // abort + b boot_sleep // hypervisor + b boot_sleep // irq + b boot_sleep // fiq + + .balign 4 +boot_sleep: + wfe + b boot_sleep + +boot_reset: + // Switch to SVC mode, all interrupts disabled + .set PSR_MODE_SVC, 0x13 + .set PSR_MODE_IRQ_DISABLED, (1<<7) + .set PSR_MODE_FIQ_DISABLED, (1<<6) + msr cpsr_c, #(PSR_MODE_SVC + PSR_MODE_FIQ_DISABLED + PSR_MODE_IRQ_DISABLED) + + // Disable caches, MMU, and flow prediction + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(0x1 << 12) // Disable instruction cache + bic r0, r0, #(0x1 << 11) // Disable flow prediction + bic r0, r0, #(0x1 << 2) // Disable data cache + bic r0, r0, #0x1 // Disable MMU + mcr p15, 0, r0, c1, c0, 0 + + // Enable ACTLR.SMP bit + mrc p15, 0, r0, c1, c0, 1 + orr r0, r0, #(1 << 6) + mcr p15, 0, r0, c1, c0, 1 + + // Invalidate TLB and branch prediction caches. + mov r0,#0 + mcr p15, 0, r0, c8, c7, 0 // Invalidate unified TLB + mcr p15, 0, r0, c7, c5, 6 // Invalidate BPIALL + + // Update ARM vector address (early binding for debug) + ldr r0, =_start + mcr p15, 0, r0, c12, c0, 0 // VBAR + + // Cache invalidation for older Cortex-A + // Note: Cortex-A7 (RPI2) does not need this part. + // Invalidate l1 instruction cache + mrc p15, 1, r0, c0, c0, 1 + tst r0, #0x3 + mov r0, #0 + mcrne p15, 0, r0, c7, c5, 0 + + // Invalidate data/unified caches + mrc p15, 1, r0, c0, c0, 1 + ands r3, r0, #0x07000000 + mov r3, r3, lsr #23 + beq finished + + mov r10, #0 +loop1: + add r2, r10, r10, lsr #1 + mov r1, r0, lsr r2 + and r1, r1, #7 + cmp r1, #2 + blt skip + + mcr p15, 2, r10, c0, c0, 0 + isb + mrc p15, 1, r1, c0, c0, 0 + and r2, r1, #7 + add r2, r2, #4 + ldr r4, =0x3ff + ands r4, r4, r1, lsr #3 + clz r5, r4 + ldr r7, =0x7fff + ands r7, r7, r1, lsr #13 +loop2: + mov r9, r4 + +loop3: + orr r11, r10, r9, lsl r5 + orr r11, r11, r7, lsl r2 + mcr p15, 0, r11, c7, c6,2 + subs r9, r9, #1 + bge loop3 + subs r7, r7, #1 + bge loop2 + +skip: + add r10, r10, #2 + cmp r3, r10 + bgt loop1 +finished: + + // MMU configurations + // Activate TTBR0 by TTBCR reg + mov r0,#0x0 + mcr p15, 0, r0, c2, c0, 2 + + // Set master translation table address (TTBR0) + ldr r0,=K_PDX_BASE + mov r1, #0x08 + orr r1,r1,#0x40 + orr r0,r0,r1 + mcr p15, 0, r0, c2, c0, 0 + + // Set depricated ARM domains + mrc p15, 0, r0, c3, c0, 0 + ldr r0, =0x55555555 + mcr p15, 0, r0, c3, c0, 0 + + // Set all CPUs to wait except the primary CPU + mrc p15, 0, r0, c0, c0, 5 + ands r0, r0, #0x03 + wfene + bne mp_continue + + // MMU Phase 1 + // Create master translation table (page directory index) +mmu_phase1: + + ldr r0,=K_PDX_BASE + ldr r1,=0xfff + ldr r2,=0 + +pagetable_invalidate: + str r2, [r0, r1, lsl#2] + subs r1, r1, #1 + bpl pagetable_invalidate + + // Page table attribute + // 0x14406= 0b0010 100 01 0 0000 0 01 10 + // 0x14c06= 0b0010 100 11 0 0000 0 01 10 + // 0x15c06= 0b0010 101 11 0 0000 0 01 10 + // ZGSA-TEX-AP-I-DOMN-X-CB-10 + + //ldr r2,=0x14c06 //Inner cache + //ldr r2,=0x15c06 //Outer cache + ldr r2,=0x14406 + + // Map __pa_init_start to __pa_init_start address + ldr r1,=PHYSTART + lsr r1, #20 + orr r3, r2, r1, lsl#20 + str r3, [r0, r1, lsl#2] + + // Map __va_kernel_start to __pa_init_start address + ldr r1,=PHYSTART + lsr r1, #20 + orr r3, r2, r1, lsl#20 + ldr r1,=KERNBASE + lsr r1, #20 + str r3, [r0, r1, lsl#2] + + // Map device MMIO (just GPIO for LED debug) + ldr r2,=0xc16 //device template + ldr r1,=(MMIO_PA+0x200000) + lsr r1, #20 + orr r3, r2, r1, lsl#20 + ldr r1,=(MMIO_VA+0x200000) + lsr r1, #20 + str r3, [r0, r1, lsl#2] + + // All processors will start from here after waiting: +mp_continue: + ldr sp, =(KERNBASE+0x3000) + // Enable I/D$, MMU, and flow prediction. + dsb + ldr r1,=_pagingstart + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(0x1 << 13) // High vector + //orr r0, r0, #(0x1 << 12) // Enable I$ + //orr r0, r0, #(0x1 << 11) // Enable flow prediction + //orr r0, r0, #(0x1 << 2) // Enable D$ + orr r0, r0, #0x1 // Enable MMU + mcr p15, 0, r0, c1, c0, 0 + bx r1 + + .section .text +.global _pagingstart +_pagingstart: + bl cmain /* call C functions now */ + bl NotOkLoop + +.global acknowledge +acknowledge: + //Turn on the LED + ldr r2,=MMIO_VA + add r2,r2,#0x200000 + //Function select + mov r3,#1 + + #ifdef RPI1 + lsl r3,#18 //Pi1 ACT LED: GPIO#16 (GPFSEL1) + str r3,[r2,#0x4] + mov r3,#1 + lsl r3,#16 + str r3,[r2,#0x28] //Pi1 (GPCLR0) + #endif + + #ifdef RPI2 + lsl r3,#21 //Pi2 ACT LED: GPIO#47 (GPFSEL4) + str r3,[r2,#0x10] + mov r3,#1 + lsl r3,#15 + str r3,[r2,#0x20] //Pi2 (GPSET1) + #endif + + bx lr + +.global dsb_barrier +dsb_barrier: + #ifdef RPI1 + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 + #else + dsb + isb + #endif + bx lr +.global flush_dcache_all +flush_dcache_all: + #ifdef RPI1 + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mov r0, #0 + mcr p15, 0, r0, c7, c14, 0 /* invalidate d-cache */ + #else + dsb + isb + #endif + bx lr +.global flush_idcache +flush_idcache: + #ifdef RPI1 + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mov r0, #0 + mcr p15, 0, r0, c7, c14, 0 /* invalidate d-cache */ + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 /* invalidate i-cache */ + #else + dsb + isb + #endif + bx lr +.global flush_tlb +flush_tlb: + #ifdef RPI1 + mov r0, #0 + mcr p15, 0, r0, c8, c7, 0 + mcr p15, 0, r0, c7, c10, 4 + #else + dsb + isb + mov r0,#0 + mcr p15, 0, r0, c8, c7, 0 // Invalidate unified TLB + mcr p15, 0, r0, c7, c5, 6 // Invalidate BPIALL + dsb + isb + #endif + bx lr +.global flush_dcache /* flush a range of data cache flush_dcache(va1, va2) */ +flush_dcache: + #ifdef RPI1 + mcrr p15, 0, r0, r1, c14 + #else + dsb + isb + #endif + bx lr + +.global set_pgtbase /* set the page table base set_pgtbase(base) */ +set_pgtbase: + mcr p15, 0, r0, c2, c0 + bx lr + +.global getsystemtime +getsystemtime: + ldr r0, =(MMIO_VA+0x003004) /* addr of the time-stamp lower 32 bits */ + ldrd r0, r1, [r0] + bx lr + +.section .data + +.align 4 +.globl font +font: + .incbin "/Users/mitsuki/workspace/os/xv6_rpi2_port/source/font1.bin" + +.align 4 +.global _binary_initcode_start +_binary_initcode_start: + .incbin "/Users/mitsuki/workspace/os/xv6_rpi2_port/source/initcode" +.global _binary_initcode_end +_binary_initcode_end: + +.align 4 +.global _binary_fs_img_start +_binary_fs_img_start: + .incbin "/Users/mitsuki/workspace/os/xv6_rpi2_port/source/fs.img" +.global _binary_fs_img_end +_binary_fs_img_end:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/exception.S Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,226 @@ +/***************************************************************** +* exception.s +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + +.align 4 +.section .text + +.global vectors +vectors: + ldr pc, reset_handler + ldr pc, undefintr_handler + ldr pc, swi_handler + ldr pc, prefetch_handler + ldr pc, data_handler + ldr pc, unused_handler + ldr pc, irq_handler + ldr pc, fiq_handler +reset_handler: + .word hang /* reset, in svc mode already */ +undefintr_handler: + .word do_und /* undefined instruction */ +swi_handler: + .word do_svc /* SWI & SVC */ +prefetch_handler: + .word do_pabt /* prefetch abort */ +data_handler: + .word do_dabt /* data abort */ +unused_handler: + .word hang /* reserved */ +irq_handler: + .word do_irq /* IRQ */ +fiq_handler: + .word hang /* FIQ */ + +hang: + bl NotOkLoop; + b hang +do_svc: + push {lr} + mrs lr, spsr + push {lr} + mrs lr, cpsr + push {lr} + mrc p15, 0, lr, c6, c0, 2 /* read Instruction Fault Address Register (IFAR) */ + push {lr} + mov lr, #0x40 + push {lr} + STMFD sp, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14} + sub sp, sp, #60 + mov r0, sp /* save sp */ + STMFD r0, {r13}^ /* save user mode sp */ + mov r1, r1 /* three nops after STM with user mode banked registers */ + mov r1, r1 + mov r1, r1 + mov sp, r0 /* restore sp */ + sub sp, sp, #4 + mov r0, sp + bl trap + +.global trapret +trapret: + mov r0, sp /* save sp in case it is changed to sp_usr after the following LDMFD instruction */ + LDMFD r0, {r13}^ /* restore user mode sp */ + mov r1, r1 /* three nops after LDMFD */ + mov r1, r1 + mov r1, r1 + mov sp, r0 /* restore sp */ + add sp, sp, #4 + LDMFD sp, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12} + add sp, sp, #72 + pop {lr} + msr spsr, lr + pop {lr} + movs pc, lr /* subs pc,lr,#0 */ + +do_und: + STMFD sp, {r0-r4} + mov r0, #0x01 + b _switchtosvc + +do_pabt: + STMFD sp, {r0-r4} + mov r0, #0x02 + b _switchtosvc + +do_dabt: + STMFD sp, {r0-r4} + mov r0, #0x04 + b _switchtosvc + +do_irq: + STMFD sp, {r0-r4} + mov r0, #0x80 + b _switchtosvc +_switchtosvc: + mrs r1, spsr + sub r2, lr, #4 + mov r3, sp + mrs lr, cpsr + bic lr, #0x0000001F /* PSR_MASK */ + orr lr, #0x00000080 /* PSR_DISABLE_IRQ */ + orr lr, #0x00000013 /* PSR_MODE_SVC */ + msr cpsr, lr /* switch to svc */ + push {r2} + push {r1} + mrs r1, cpsr + push {r1} + mrc p15, 0, r1, c6, c0, 2 /* read Instruction Fault Address Register (I +FAR) */ + push {r1} + push {r0} + sub r1, r3, #20 + LDMFD r1, {r0-r4} + STMFD sp, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14} + sub sp, sp, #60 + mov r0, sp /* save sp */ + STMFD r0, {r13}^ /* save user mode sp */ + mov r1, r1 /* three nops after STM with user mode banked registers */ + mov r1, r1 + mov r1, r1 + mov sp, r0 /* restore sp */ + sub sp, sp, #4 + mov r0, sp + + bl trap + + mov r0, sp + add r0, #76 + LDMIA r0, {r1} + mov r2, r1 + and r2, #0xf + cmp r2, #0 + beq _backtouser + msr cpsr, r1 + add sp, #4 + LDMFD sp, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12} + add sp, sp, #56 + pop {r14} + add sp, sp, #16 + pop {pc} + +_backtouser: + mov r0, sp /* save sp in case it is changed to sp_usr after the following LDMFD instruction */ + LDMFD r0, {r13}^ /* restore user mode sp */ + mov r1, r1 /* three nops after LDMFD */ + mov r1, r1 + mov r1, r1 + mov sp, r0 /* restore sp */ + add sp, sp, #4 + LDMIA sp, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12} + add sp, sp, #72 + pop {lr} + msr spsr, lr + pop {lr} + movs pc, lr /* subs pc,lr,#0 */ + + +.global set_mode_sp +set_mode_sp: + mrs r2, cpsr + msr cpsr_c,r1 + mov sp, r0 + mrs r0, cpsr + orr r0, #0x00000080 /* PSR_DISABLE_IRQ */ + orr r0, #0x00000040 /* PSR_DISABLE_FIQ */ + msr cpsr, r0 + msr cpsr_c, r2 + bx lr + +.global readcpsr +readcpsr: + mrs r0, cpsr + bx lr + +.global cli +cli: + mrs r0, cpsr + orr r0, #0x00000080 /* PSR_DISABLE_IRQ */ + msr cpsr, r0 + bx lr + +.global sti +sti: + mrs r0, cpsr + bic r0, r0, #0x00000080 /* PSR_DISABLE_IRQ */ + msr cpsr, r0 + bx lr + +.global swtch +swtch: + push {lr} /* save the return address */ + push {lr} + /* save old callee-save registers */ + push {r12} + push {r11} + push {r10} + push {r9} + push {r8} + push {r7} + push {r6} + push {r5} + push {r4} + + /* switch stacks */ + str sp, [r0] + mov sp, r1 + + /* load new callee-save registers */ + pop {r4} + pop {r5} + pop {r6} + pop {r7} + pop {r8} + pop {r9} + pop {r10} + pop {r11} + pop {r12} + + /* return to previously saved pc */ + pop {lr} + pop {pc} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/exec.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,104 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "defs.h" +#include "arm.h" +#include "elf.h" + +int +exec(char *path, char **argv) +{ + char *last; + int i, off; + uint argc, sz, sp, ustack[3+MAXARG+1]; + struct elfhdr elf; + struct inode *ip; + struct proghdr ph; + pde_t *pgdir, *oldpgdir; + + if((ip = namei(path)) == 0) + return -1; + ilock(ip); + pgdir = 0; + + // Check ELF header + if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) + goto bad; + if(elf.magic != ELF_MAGIC) + goto bad; + + if((pgdir = setupkvm()) == 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 = PGROUNDUP(sz); + if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0) + goto bad; + clearpteu(pgdir, (char*)(sz - 2*PGSIZE)); + 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[3+argc] = sp; + } + ustack[3+argc] = 0; + +//cprintf("Exec is called argc=%d sz=%x\n", argc, sz); + + ustack[0] = 0xffffffff; // fake return PC + ustack[1] = argc; + ustack[2] = sp - (argc+1)*4; // argv pointer + + sp -= (3+argc+1) * 4; + if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0) + goto bad; + + // Save program name for debugging. +/* for(last=s=path; *s; s++) + if(*s == '/') + last = s+1;*/ + last = argv[0]; + safestrcpy(curr_proc->name, last, sizeof(curr_proc->name)); + + // Commit to the user image. + oldpgdir = curr_proc->pgdir; + curr_proc->pgdir = pgdir; + curr_proc->sz = sz; + curr_proc->tf->pc = elf.entry; // main + curr_proc->tf->sp = sp; + curr_proc->tf->r0 = ustack[1]; + curr_proc->tf->r1 = ustack[2]; + switchuvm(curr_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/source/file.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,162 @@ +// +// 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) +{ + memset(&ftable, 0, sizeof(ftable)); + 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); +//cprintf("inside fileread\n"); + if((r = readi(f->ip, addr, f->off, n)) > 0) + f->off += r; +//cprintf("inside fileread: after readi rv=%x\n", r); + iunlock(f->ip); + return r; + } + panic("fileread"); + return -1; +} + +//PAGEBREAK! +// Write to file f. +int +filewrite(struct file *f, char *addr, int n) +{ + int r; + + if(f->writable == 0) + return -1; +//cprintf("inside filewrite\n"); + 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. + int max = ((LOGSIZE-1-1-2) / 2) * 512; + int i = 0; + while(i < n){ + int 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"); + return -1; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/fs.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,656 @@ +// 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"); + return -1; +} + +// 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) +{ + memset(&icache, 0, sizeof(icache)); + 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"); + return 0; +} + +// 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"); + return -1; +} + +// 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; +//cprintf("inside readi\n"); + 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; +//cprintf("inside writei: type=%x major=%x, func addr: %x\n", ip->type, ip->major, devsw[ip->major].write); + if(ip->type == T_DEV){ + if(ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write) + return -1; +//cprintf("before calling consolewrite: major=%x, func addr: %x\n", ip->major, devsw[ip->major].write); + 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(curr_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/source/kalloc.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,95 @@ +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "spinlock.h" + +void freerange(void *vstart, void *vend); +extern char end[]; // first address after kernel loaded from ELF file +extern unsigned int pm_size; + +struct run { + struct run *next; +}; + +struct { + struct spinlock lock; + int use_lock; + struct run *freelist; +} kmem; + +// 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) +{ + pm_size = 0x400*0x400*32; // assume at least 32 MB physical memory + initlock(&kmem.lock, "kmem"); + kmem.use_lock = 0; + kmem.freelist = 0; + 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*)PGROUNDUP((uint)vstart); + for(; p + PGSIZE <= (char*)vend; p += PGSIZE) + 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 % PGSIZE || v < end || v2p(v) >= pm_size) + panic("kfree"); + + // Fill with junk to catch dangling refs. + memset(v, 1, PGSIZE); + + 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/source/log.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,186 @@ +#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) +{ + if (sizeof(struct logheader) >= BSIZE) + panic("initlog: too big logheader"); + + struct superblock sb; + memset(&log, 0, sizeof(log)); + 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; + + for (tail = 0; tail < log.lh.n; tail++) { + struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block + struct buf *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 = bread(log.dev, log.start); + struct logheader *lh = (struct logheader *) (buf->data); + int i; + 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 = bread(log.dev, log.start); + struct logheader *hb = (struct logheader *) (buf->data); + int i; + 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) +{ + 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; + struct buf *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/source/mailbox.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,94 @@ +/***************************************************************** +* mailbox.c +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "traps.h" +#include "spinlock.h" +#include "fs.h" +#include "file.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "arm.h" +#include "mailbox.h" + +/* Note: for more tags refer to +https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ + + +void +create_request(volatile uint *mbuf, uint tag, uint buflen, uint len, uint *data) +{ + int i; + volatile uint *tag_info; + uint nw, tag_len, total_len; + + tag_info = mbuf + POS_TAG; + + tag_info[POS_TAG_ID] = tag; + tag_info[POS_TAG_BUFLEN] = buflen; + tag_info[POS_TAG_DATALEN] = len & 0x7FFFFFFF; + + nw = buflen >> 2; + + if (!data) + for (i = 0; i < nw; ++i) tag_info[POS_TAG_DATA + i] = 0; + else + for (i = 0; i < nw; ++i) tag_info[POS_TAG_DATA + i] = data[i]; + + tag_info[POS_TAG_DATA+nw] = 0; // indicate end of tag + + tag_len = mbuf[MB_HEADER_LENGTH + POS_TAG_BUFLEN]; + total_len = (MB_HEADER_LENGTH*4) + (TAG_HEADER_LENGTH*4) + tag_len + 4; + + mbuf[POS_OVERALL_LENGTH] = total_len; + mbuf[POS_RV] = MPI_REQUEST; + +} + +volatile uint *mailbuffer; + +void mailboxinit() +{ +mailbuffer = (uint *)kalloc(); +} + +uint +readmailbox(u8 channel) +{ + uint x, y, z; + +again: + while ((inw(MAILBOX_BASE+24) & 0x40000000) != 0); + x = inw(MAILBOX_BASE); + z = x & 0xf; y = (uint)(channel & 0xf); + if(z != y) goto again; + + return x&0xfffffff0; +} + +void +writemailbox(uint *addr, u8 channel) +{ + uint x, y, a; + + a = (uint)addr; + a -= KERNBASE; /* convert to ARM physical address */ + a += 0xc0000000; /* convert to VC address space */ + x = a & 0xfffffff0; + y = x | (uint)(channel & 0xf); + + flush_dcache_all(); + + while ((inw(MAILBOX_BASE+24) & 0x80000000) != 0); + //while ((inw(MAILBOX_BASE+0x38) & 0x80000000) != 0); + outw(MAILBOX_BASE+32, y); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/main.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,112 @@ +/***************************************************************** +* main.c +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "arm.h" +#include "mailbox.h" + +extern char end[]; // first address after kernel loaded from ELF file +extern pde_t *kpgdir; +extern volatile uint *mailbuffer; +extern unsigned int pm_size; + +void OkLoop() +{ + setgpiofunc(18, 1); // gpio 18 for Ok Led, set as an output + while(1){ + setgpioval(18, 0); + delay(2000000); + setgpioval(18, 1); + delay(2000000); + } +} + +void NotOkLoop() +{ + setgpiofunc(18, 1); // gpio 18 for Ok Led, set as an output + while(1){ + setgpioval(18, 0); + delay(500000); + setgpioval(18, 1); + delay(500000); + } +} + +unsigned int getpmsize() +{ + create_request(mailbuffer, MPI_TAG_GET_ARM_MEMORY, 8, 0, 0); + writemailbox((uint *)mailbuffer, 8); + readmailbox(8); + if(mailbuffer[1] != 0x80000000) cprintf("Error readmailbox: %x\n", MPI_TAG_GET_ARM_MEMORY); + return mailbuffer[MB_HEADER_LENGTH + TAG_HEADER_LENGTH+1]; +} + +void machinit(void) +{ + memset(cpus, 0, sizeof(struct cpu)*NCPU); +} + + +void enableirqminiuart(void); + +uint mb_data[10]; + +int cmain() +{ + mmuinit0(); + machinit(); + + #if defined (RPI1) || defined (RPI2) + uartinit(); + #elif defined (FVP) + uartinit_fvp(); + #endif + + dsb_barrier(); + + consoleinit(); + cprintf("\nHello World from xv6\n"); + + kinit1(end, P2V((8*1024*1024)+PHYSTART)); + // collect some free space (8 MB) for imminent use + // the physical space below 0x8000 is reserved for PGDIR and kernel stack + kpgdir=p2v(K_PDX_BASE); + + mailboxinit(); + + pm_size = getpmsize(); + cprintf("ARM memory is %x\n", pm_size); + + mmuinit1(); + gpuinit(); + pinit(); + tvinit(); + cprintf("it is ok after tvinit\n"); + binit(); +cprintf("it is ok after binit\n"); + fileinit(); +cprintf("it is ok after fileinit\n"); + iinit(); +cprintf("it is ok after iinit\n"); + ideinit(); +cprintf("it is ok after ideinit\n"); + kinit2(P2V((8*1024*1024)+PHYSTART), P2V(pm_size)); +cprintf("it is ok after kinit2\n"); + userinit(); +cprintf("it is ok after userinit\n"); + timer3init(); +cprintf("it is ok after timer3init\n"); + scheduler(); + NotOkLoop(); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/memide.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,58 @@ +// 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 "arm.h" +#include "traps.h" +#include "spinlock.h" +#include "buf.h" + +extern uchar _binary_fs_img_start[], _binary_fs_img_end[]; + +static int disksize; +static uchar *memdisk; + +void +ideinit(void) +{ + memdisk = _binary_fs_img_start; + disksize = div(((uint)_binary_fs_img_end - (uint)_binary_fs_img_start), 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/source/mmu.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,120 @@ +/***************************************************************** + * mmu.c + * by Zhiyi Huang, hzy@cs.otago.ac.nz + * University of Otago + * + ********************************************************************/ + + +#include "types.h" +#include "defs.h" +#include "memlayout.h" +#include "mmu.h" + +unsigned int pm_size; + +void mmuinit0(void) +{ + pde_t *l1; + pte_t *l2; + uint pa, va; + + // diable mmu + // use inline assembly here as there is a limit on + // branch distance after mmu is disabled + // asm volatile("mrc p15, 0, r1, c1, c0, 0\n\t" + // "bic r1,r1,#0x00000004\n\t" // 2: Disable data cache + // "bic r1,r1,#0x00001000\n\t" // 12: Disable instruction cache + // "bic r1,r1,#0x00000800\n\t" // 11: Disable branch prediction + // "bic r1,r1,#0x00000001\n\t" // 0: Disable MMU + // "mcr p15, 0, r1, c1, c0, 0\n\t" + // "mov r0, #0\n\t" + // "mcr p15, 0, r0, c7, c7, 0\n\t" // Invalidate Both Caches (only for ARM11) + // "mcr p15, 0, r0, c8, c7, 0\n\t" // Invalidate Unified TLB + // ::: "r0", "r1", "cc", "memory"); + + + //for(p=(uint *)0x2000; p<(uint *)0x8000; p++) *p = 0; + + l1 = (pde_t *) K_PDX_BASE; + l2 = (pte_t *) K_PTX_BASE; + + // map all of ram at KERNBASE + va = KERNBASE + MBYTE; + for(pa = PHYSTART + MBYTE; pa < PHYSTART+PHYSIZE; pa += MBYTE){ + l1[PDX(va)] = pa|DOMAIN0|PDX_AP(K_RW)|SECTION|CACHED|BUFFERED; + va += MBYTE; + } + + // identity map first MB of ram so mmu can be enabled + //l1[PDX(PHYSTART)] = PHYSTART|DOMAIN0|PDX_AP(K_RW)|SECTION|CACHED|BUFFERED; + + // map IO region + va = MMIO_VA; + for(pa = MMIO_PA; pa < MMIO_PA+MMIO_SIZE; pa += MBYTE){ + l1[PDX(va)] = pa|DOMAIN0|PDX_AP(K_RW)|SECTION; + va += MBYTE; + } + + // map GPU memory + va = GPUMEMBASE; + for(pa = 0; pa < (uint)GPUMEMSIZE; pa += MBYTE){ + l1[PDX(va)] = pa|DOMAIN0|PDX_AP(K_RW)|SECTION; + va += MBYTE; + } + + // double map exception vectors at top of virtual memory + va = HVECTORS; + l1[PDX(va)] = (uint)l2|DOMAIN0|COARSE; + l2[PTX(va)] = PHYSTART|PTX_AP(K_RW)|SMALL; + + // asm volatile("mov r1, #1\n\t" + // "mcr p15, 0, r1, c3, c0\n\t" + // "mov r1, #0x4000\n\t" + // "mcr p15, 0, r1, c2, c0\n\t" + // "mrc p15, 0, r0, c1, c0, 0\n\t" + // "mov r1, #0x00002000\n\t" // 13: Enable High exception vectors + // "orr r1, #0x00000004\n\t" // 2: Enable data cache + // "orr r1, #0x00001000\n\t" // 12: Enable instruction cache + // "orr r1, #0x00000001\n\t" // 0: Enable MMU + // "orr r0, r1\n\t" + // "mcr p15, 0, r0, c1, c0, 0\n\t" + // "mov r1, #1\n\t" + // "mcr p15, 0, r1, c15, c12, 0\n\t" // Read Performance Monitor Control Register (ARM11)? + // ::: "r0", "r1", "cc", "memory"); + +} + +void +mmuinit1(void) +{ + pde_t *l1; + uint va1, va2; + uint pa, va; + + l1 = (pde_t*)(K_PDX_BASE); + + + // map the rest of RAM after PHYSTART+PHYSIZE + va = KERNBASE + PHYSIZE; + for(pa = PHYSTART + PHYSIZE; pa < PHYSTART+pm_size; pa += MBYTE){ + l1[PDX(va)] = pa|DOMAIN0|PDX_AP(K_RW)|SECTION|CACHED|BUFFERED; + va += MBYTE; + } + + + // undo identity map of first MB of ram + l1[PDX(PHYSTART)] = 0; + + // drain write buffer; writeback data cache range [va, va+n] + va1 = (uint)&l1[PDX(PHYSTART)]; + va2 = va1 + sizeof(pde_t); + va1 = va1 & ~((uint)CACHELINESIZE-1); + va2 = va2 & ~((uint)CACHELINESIZE-1); + flush_dcache(va1, va2); + + // invalidate TLB; DSB barrier used + flush_tlb(); + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/pipe.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,121 @@ +#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 = (struct pipe*)kalloc()) == 0) + goto bad; + memset(p, 0, PGSIZE); + 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((char*)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((char*)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 || curr_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(curr_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/source/proc.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,453 @@ +/***************************************************************** +* proc.c +* adapted from MIT xv6 by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "arm.h" +#include "proc.h" +#include "spinlock.h" + +struct { + struct spinlock lock; + struct proc proc[NPROC]; +} ptable; + +static struct proc *initproc; + +int first_sched = 1; +int nextpid = 1; +extern void forkret(void); +extern void trapret(void); + +static void wakeup1(void *chan); + +void +pinit(void) +{ + memset(&ptable, 0, sizeof(ptable)); + 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 = kalloc()) == 0){ + p->state = UNUSED; + return 0; + } + memset(p->kstack, 0, PGSIZE); + 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 -= sizeof *p->context; + p->context = (struct context*)sp; + memset(p->context, 0, sizeof *p->context); + p->context->pc = (uint)forkret; + p->context->lr = (uint)trapret; + + return p; +} + +//PAGEBREAK: 32 +// Set up first user process. +void +userinit(void) +{ + struct proc *p; + extern char _binary_initcode_start[], _binary_initcode_end[]; + uint _binary_initcode_size; + + _binary_initcode_size = (uint)_binary_initcode_end - (uint)_binary_initcode_start; + p = allocproc(); +//cprintf("after allocproc: initcode start: %x end %x\n", _binary_initcode_start, _binary_initcode_end); + initproc = p; +//cprintf("initproc is %x\n", initproc); + if((p->pgdir = setupkvm()) == 0) + panic("userinit: out of memory?"); +//cprintf("after setupkvm\n"); + inituvm(p->pgdir, _binary_initcode_start, _binary_initcode_size); +//cprintf("after initkvm\n"); + p->sz = PGSIZE; + memset(p->tf, 0, sizeof(*p->tf)); + p->tf->spsr = 0x10; + p->tf->sp = PGSIZE; + 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 = curr_proc->sz; + if(n > 0){ + if((sz = allocuvm(curr_proc->pgdir, sz, sz + n)) == 0) + return -1; + } else if(n < 0){ + if((sz = deallocuvm(curr_proc->pgdir, sz, sz + n)) == 0) + return -1; + } + curr_proc->sz = sz; + switchuvm(curr_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(curr_proc->pgdir, curr_proc->sz)) == 0){ + kfree(np->kstack); + np->kstack = 0; + np->state = UNUSED; + return -1; + } + np->sz = curr_proc->sz; + np->parent = curr_proc; + *np->tf = *curr_proc->tf; + + // Clear r0 so that fork returns 0 in the child. + np->tf->r0 = 0; + + for(i = 0; i < NOFILE; i++) + if(curr_proc->ofile[i]) + np->ofile[i] = filedup(curr_proc->ofile[i]); + np->cwd = idup(curr_proc->cwd); + + pid = np->pid; + np->state = RUNNABLE; + safestrcpy(np->name, curr_proc->name, sizeof(curr_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(curr_proc == initproc) + panic("init exiting"); + + // Close all open files. + for(fd = 0; fd < NOFILE; fd++){ + if(curr_proc->ofile[fd]){ + fileclose(curr_proc->ofile[fd]); + curr_proc->ofile[fd] = 0; + } + } + + iput(curr_proc->cwd); + curr_proc->cwd = 0; + + acquire(&ptable.lock); + + // Parent might be sleeping in wait(). + wakeup1(curr_proc->parent); + + // Pass abandoned children to init. + for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ + if(p->parent == curr_proc){ + p->parent = initproc; + if(p->state == ZOMBIE) + wakeup1(initproc); + } + } + + // Jump into the scheduler, never to return. + curr_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 != curr_proc) + continue; + havekids = 1; + if(p->state == ZOMBIE){ + // Found one. + pid = p->pid; + kfree(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 || curr_proc->killed){ + release(&ptable.lock); + return -1; + } +//cprintf("inside wait before calling sleep\n"); + // Wait for children to exit. (See wakeup1 call in proc_exit.) + sleep(curr_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. + //cprintf("before enabling interrupts\n"); + if(first_sched) first_sched = 0; + else 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. + curr_proc = p; +//cprintf("before switching page table\n"); + switchuvm(p); + p->state = RUNNING; +//cprintf("after switching page table\n"); + + swtch(&curr_cpu->scheduler, curr_proc->context); + + switchkvm(); + + // Process is done running for now. + // It should have changed its p->state before coming back. + curr_proc = 0; + } + release(&ptable.lock); + + } +} + +// Enter scheduler. Must hold only ptable.lock +// and have changed proc->state. +void +sched(void) +{ + int intena; + + if(!holding(&ptable.lock)) + panic("sched ptable.lock"); + if(curr_cpu->ncli != 1) + panic("sched locks"); + if(curr_proc->state == RUNNING) + panic("sched running"); + if(!(readcpsr()&PSR_DISABLE_IRQ)) + panic("sched interruptible"); + intena = curr_cpu->intena; + swtch(&curr_proc->context, curr_cpu->scheduler); + curr_cpu->intena = intena; +} + +// Give up the CPU for one scheduling round. +void +yield(void) +{ + acquire(&ptable.lock); //DOC: yieldlock + curr_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(); + } +//cprintf("inside forkret\n"); + + // 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) +{ + if(curr_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. + curr_proc->chan = chan; + curr_proc->state = SLEEPING; +//cprintf("inside sleep before calling sched\n"); + sched(); + + // Tidy up. + curr_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) +{ +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/spinlock.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,105 @@ +/***************************************************************** +* spinlock.c +* adapted from MIT xv6 by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +#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; +} + +// 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. + if(holding(lk)){ + cprintf("lock name: %s, locked: %d, cpu: %x CPSR: %x\n", lk->name, lk->locked, lk->cpu, readcpsr()); + panic("acquire"); + } + + lk->locked = 1; + + // Record info about lock acquisition for debugging. + lk->cpu = curr_cpu; +} + +// Release the lock. +void +release(struct spinlock *lk) +{ + + if(!holding(lk)) + panic("release"); + + lk->pcs[0] = 0; + lk->cpu = 0; + + lk->locked = 0; + popcli(); +} + +// Record the current call stack in pcs[] by following the %ebp chain. +void +getcallerpcs(void *v, uint pcs[]) +{ +} + + +// Check whether this cpu is holding the lock. +int +holding(struct spinlock *lock) +{ +int rv; + rv = lock->locked && lock->cpu == curr_cpu; +/* if(rv){ + cprintf("The held lock: %s, locked: %d, cpu: %x\n", lock->name, lock->locked, lock->cpu); + }*/ + return rv; +} + + +// 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) +{ + uint cpsr; + cpsr = readcpsr(); + cli(); + if(curr_cpu->ncli++ == 0) + curr_cpu->intena = (cpsr & PSR_DISABLE_IRQ) ? 0: 1; +} + +void +popcli(void) +{ + if(!(readcpsr()&PSR_DISABLE_IRQ)) + panic("popcli - interruptible"); + if(--curr_cpu->ncli < 0) + panic("popcli"); + if(curr_cpu->ncli == 0 && curr_cpu->intena) + sti(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/string.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,148 @@ +/***************************************************************** +* string.c +* adapted from MIT xv6 by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + +#include "types.h" + +void* +memsetw(int *dst, int c, uint n) +{ + int *p=dst; + uint rc=n; + + while (rc-- > 0) *p++ = c; + return (void *)p; +} + +void* +memsetb(char *dst, int c, uint n) +{ + char *p=dst; + uint rc=n; + + while (rc-- > 0) *p++ = c; + return (void *)p; +} + + +void* +memset(void *dst, int c, uint n) +{ + if ((int)dst%4 == 0 && n%4 == 0){ + c &= 0xFF; + return memsetw((int *)dst, (c<<24)|(c<<16)|(c<<8)|c, n/4); + } else + return memsetb((char *)dst, c, n); +} + +int +memcmp(const void *v1, const void *v2, uint n) +{ + const u8 *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 (u8)*p - (u8)*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; +} + +uint div(uint n, uint d) // long division +{ + uint q=0, r=0; + int i; + + for(i=31;i>=0;i--){ + r = r << 1; + r = r | ((n >> i) & 1); + if(r >= d) { + r = r - d; + q = q | (1 << i); + } + } + return q; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/syscall.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,152 @@ +/***************************************************************** +* syscall.c +* adapted from MIT xv6 by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + +#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 %eax. +// Arguments on the stack, from the user call to the C +// library system call function. The saved user %esp points +// to a saved program counter, and then the first argument. + +// Fetch the int at addr from the current process. +int +fetchint(uint addr, int *ip) +{ + if(addr >= curr_proc->sz || addr+4 > curr_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 >= curr_proc->sz) + return -1; + *pp = (char*)addr; + ep = (char*)curr_proc->sz; + for(s = *pp; s < ep; s++) + if(*s == 0) + return s - *pp; + return -1; +} + +// Fetch the nth 32-bit system call argument. +int +argint(int n, int *ip) +{ + return fetchint(curr_proc->tf->sp + 4*n, ip); +} + +// 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 >= curr_proc->sz || (uint)i+size > curr_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; + + num = curr_proc->tf->r0; + if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { +// cprintf("\n%d %s: sys call %d syscall address %x\n", +// curr_proc->pid, curr_proc->name, num, syscalls[num]); + + if(num == SYS_exec) { + if(syscalls[num]() == -1) curr_proc->tf->r0 = -1; + } else curr_proc->tf->r0 = syscalls[num](); + } else { + cprintf("%d %s: unknown sys call %d\n", + curr_proc->pid, curr_proc->name, num); + curr_proc->tf->r0 = -1; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/sysfile.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,426 @@ +// +// 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=curr_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(curr_proc->ofile[fd] == 0){ + curr_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; +//cprintf("inside sys_write\n"); + return filewrite(f, p, n); +} + +int +sys_close(void) +{ + int fd; + struct file *f; + + if(argfd(0, &fd, &f) < 0) + return -1; + curr_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(curr_proc->cwd); + curr_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(char *)*MAXARG); + 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) + curr_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/source/sysproc.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,90 @@ +#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 curr_proc->pid; +} + +int +sys_sbrk(void) +{ + int addr; + int n; + + if(argint(0, &n) < 0) + return -1; + addr = curr_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(curr_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/source/timer.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,81 @@ +/***************************************************************** +* timer.c +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + +// The System Timer peripheral + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "proc.h" +#include "traps.h" +#include "arm.h" +#include "spinlock.h" + +#define TIMER_REGS_BASE (MMIO_VA+0x003000) +#define CONTROL_STATUS 0x0 // control/status +#define COUNTER_LO 0x4 // the time-stamp lower 32 bits +#define COUNTER_HI 0x8 // the time-stamp higher 32 bits +#define COMPARE0 0xc // compare 0 +#define COMPARE1 0x10 // compare 1 +#define COMPARE2 0x14 // compare 2 +#define COMPARE3 0x18 // compare 3 + +#define TIMER_FREQ 10000 // interrupt 100 times/sec. + +void +enabletimer3irq(void) +{ + intctrlregs *ip; + + ip = (intctrlregs *)INT_REGS_BASE; + ip->gpuenable[0] |= 1 << IRQ_TIMER3; // enable the system timer3 irq +} + + +void +timer3init(void) +{ +uint v; + + enabletimer3irq(); + + v = inw(TIMER_REGS_BASE+COUNTER_LO); + v += TIMER_FREQ; + + outw(TIMER_REGS_BASE+COMPARE3, v); + ticks = 0; +} + +void +timer3intr(void) +{ +uint v; +//cprintf("timer3 interrupt: %x\n", inw(TIMER_REGS_BASE+CONTROL_STATUS)); + outw(TIMER_REGS_BASE+CONTROL_STATUS, (1 << IRQ_TIMER3)); // clear timer3 irq + + ticks++; + wakeup(&ticks); + + // reset the value of compare3 + v=inw(TIMER_REGS_BASE+COUNTER_LO); + v += TIMER_FREQ; + outw(TIMER_REGS_BASE+COMPARE3, v); +} + +void +delay(uint m) +{ + unsigned long long t; + + if(m == 0) return; + + t = getsystemtime() + m; + while(t != getsystemtime()); + + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/trap.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,197 @@ +/***************************************************************** +* trap.c +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "arm.h" +#include "traps.h" +#include "spinlock.h" + +extern u8 *vectors; + +void cprintf(char*, ...); +void dsb_barrier(void); +void flush_idcache(void); +void *memmove(void *dst, const void *src, uint n); +void set_mode_sp(char *, uint); + +struct spinlock tickslock; +uint ticks; + +void enable_intrs(void) +{ + intctrlregs *ip; + + ip = (intctrlregs *)INT_REGS_BASE; + ip->gpuenable[0] |= 1 << 29; // enable the miniuart through Aux + //ip->gpuenable[1] |= 1 << 25; // enable uart + ip->armenable |= 1 << 0; // enable the system timer +} + + +void disable_intrs(void) +{ + intctrlregs *ip; + int disable; + + ip = (intctrlregs *)INT_REGS_BASE; + disable = ~0; + ip->gpudisable[0] = disable; + ip->gpudisable[1] = disable; + ip->armdisable = disable; + ip->fiqctrl = 0; +} + + +void tvinit(void) +{ + uint *d, *s; + char *ptr; + + /* initialize the exception vectors */ + d = (uint *)HVECTORS; + s = (uint *)&vectors; + memmove(d, s, sizeof(Vpage0)); + + /* cacheuwbinv(); drain write buffer and prefetch buffer + * writeback and invalidate data cache + * invalidate instruction cache + */ + dsb_barrier(); + flush_idcache(); + ptr = kalloc(); + memset(ptr, 0, PGSIZE); + set_mode_sp(ptr+4096, 0xD1);/* fiq mode, fiq and irq are disabled */ + + ptr = kalloc(); + memset(ptr, 0, PGSIZE); + set_mode_sp(ptr+4096, 0xD2);/* irq mode, fiq and irq are disabled */ + ptr = kalloc(); + memset(ptr, 0, PGSIZE); + set_mode_sp(ptr+4096, 0xDB);/* undefined mode, fiq and irq are disabled */ + ptr = kalloc(); + memset(ptr, 0, PGSIZE); + set_mode_sp(ptr+4096, 0xD7);/* abort mode, fiq and irq are disabled */ + ptr = kalloc(); + memset(ptr, 0, PGSIZE); + set_mode_sp(ptr+4096, 0xD6);/* secure monitor mode, fiq and irq are disabled */ + ptr = kalloc(); + memset(ptr, 0, PGSIZE); + set_mode_sp(ptr+4096, 0xDF);/* system mode, fiq and irq are disabled */ + + dsb_barrier(); +} + +void trap_oops(struct trapframe *tf) +{ + +cprintf("trapno: %x, spsr: %x, sp: %x, pc: %x cpsr: %x ifar: %x\n", tf->trapno, tf->spsr, tf->sp, tf->pc, tf->cpsr, tf->ifar); +cprintf("Saved registers: r0: %x, r1: %x, r2: %x, r3: %x, r4: %x, r5: %x\n", tf->r0, tf->r1, tf->r2, tf->r3, tf->r4, tf->r5); +cprintf("More registers: r6: %x, r7: %x, r8: %x, r9: %x, r10: %x, r11: %x, r12: %x\n", tf->r6, tf->r7, tf->r8, tf->r9, tf->r10, tf->r11, tf->r12); + +//NotOkLoop(); +} + +void handle_irq(struct trapframe *tf) +{ + intctrlregs *ip; + +/*cprintf("trapno: %x, spsr: %x, sp: %x, lr: %x cpsr: %x ifar: %x\n", tf->trapno, tf->spsr, tf->sp, tf->pc, tf->cpsr, tf->ifar); +cprintf("Saved registers: r0: %x, r1: %x, r2: %x, r3: %x, r4: %x, r5: %x, r6: %x\n", tf->r0, tf->r1, tf->r2, tf->r3, tf->r4, tf->r5, tf->r6); +cprintf("More registers: r6: %x, r7: %x, r8: %x, r9: %x, r10: %x, r11: %x, r12: %x, r13: %x, r14: %x\n", tf->r7, tf->r8, tf->r9, tf->r10, tf->r11, tf->r12, tf->r13, tf->r14); +*/ + ip = (intctrlregs *)INT_REGS_BASE; + while(ip->gpupending[0] || ip->gpupending[1] || ip->armpending){ + if(ip->gpupending[0] & (1 << 3)) { + timer3intr(); + } + if(ip->gpupending[0] & (1 << 29)) { + miniuartintr(); + } + } + +} + + +//PAGEBREAK: 41 +void +trap(struct trapframe *tf) +{ + intctrlregs *ip; + uint istimer; + +//cprintf("Trap %d from cpu %d eip %x (cr2=0x%x)\n", +// tf->trapno, curr_cpu->id, tf->eip, 0); + //trap_oops(tf); + if(tf->trapno == T_SYSCALL){ + if(curr_proc->killed) + exit(); + curr_proc->tf = tf; + syscall(); + if(curr_proc->killed) + exit(); + return; + } + + istimer = 0; + switch(tf->trapno){ + case T_IRQ: + ip = (intctrlregs *)INT_REGS_BASE; + while(ip->gpupending[0] || ip->gpupending[1] || ip->armpending){ + if(ip->gpupending[0] & (1 << IRQ_TIMER3)) { + istimer = 1; + timer3intr(); + } + if(ip->gpupending[0] & (1 << IRQ_MINIUART)) { + miniuartintr(); + } + } + + break; + default: + if(curr_proc == 0 || (tf->spsr & 0xF) != USER_MODE){ + // In kernel, it must be our mistake. + cprintf("unexpected trap %d from cpu %d addr %x spsr %x cpsr %x ifar %x\n", + tf->trapno, curr_cpu->id, tf->pc, tf->spsr, tf->cpsr, tf->ifar); + panic("trap"); + } + // In user space, assume process misbehaved. + cprintf("pid %d %s: trap %d on cpu %d " + "addr 0x%x spsr 0x%x cpsr 0x%x ifar 0x%x--kill proc\n", + curr_proc->pid, curr_proc->name, tf->trapno, curr_cpu->id, tf->pc, + tf->spsr, tf->cpsr, tf->ifar); + curr_proc->killed = 1; + } + + // Force process exit if it has been killed and is in user space. + // (If it is still executing in the kernel, let it keep running + // until it gets to the regular system call return.) + +//cprintf("Proc pointer: %d\n", curr_proc); + if(curr_proc){ + if(curr_proc->killed && (tf->spsr&0xF) == USER_MODE) + exit(); + + // Force process to give up CPU on clock tick. + // If interrupts were on while locks held, would need to check nlock. + if(curr_proc->state == RUNNING && istimer) + yield(); + + // Check if the process has been killed since we yielded + if(curr_proc->killed && (tf->spsr&0xF) == USER_MODE) + exit(); + } + +//cprintf("Proc pointer: %d after\n", curr_proc); + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/uart.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,137 @@ +/***************************************************************** +* uart.c +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +#include "types.h" +#include "defs.h" +#include "memlayout.h" +#include "traps.h" +#include "arm.h" + +#define GPFSEL0 (MMIO_VA+0x200000) +#define GPFSEL1 (MMIO_VA+0x200004) +#define GPFSEL2 (MMIO_VA+0x200008) +#define GPFSEL3 (MMIO_VA+0x20000C) +#define GPFSEL4 (MMIO_VA+0x200010) +#define GPFSEL5 (MMIO_VA+0x200014) +#define GPSET0 (MMIO_VA+0x20001C) +#define GPSET1 (MMIO_VA+0x200020) +#define GPCLR0 (MMIO_VA+0x200028) +#define GPCLR1 (MMIO_VA+0x20002C) +#define GPPUD (MMIO_VA+0x200094) +#define GPPUDCLK0 (MMIO_VA+0x200098) +#define GPPUDCLK1 (MMIO_VA+0x20009C) + +#define AUX_IRQ (MMIO_VA+0x215000) +#define AUX_ENABLES (MMIO_VA+0x215004) +#define AUX_MU_IO_REG (MMIO_VA+0x215040) +#define AUX_MU_IER_REG (MMIO_VA+0x215044) +#define AUX_MU_IIR_REG (MMIO_VA+0x215048) +#define AUX_MU_LCR_REG (MMIO_VA+0x21504C) +#define AUX_MU_MCR_REG (MMIO_VA+0x215050) +#define AUX_MU_LSR_REG (MMIO_VA+0x215054) +#define AUX_MU_MSR_REG (MMIO_VA+0x215058) +#define AUX_MU_SCRATCH (MMIO_VA+0x21505C) +#define AUX_MU_CNTL_REG (MMIO_VA+0x215060) +#define AUX_MU_STAT_REG (MMIO_VA+0x215064) +#define AUX_MU_BAUD_REG (MMIO_VA+0x215068) + +void +setgpioval(uint pin, uint val) +{ + uint sel, ssel, rsel, shift; + + if(pin > 53) return; + if(pin >= 32) sel = 1; else sel = 0; + ssel = GPSET0 + (sel << 2); + rsel = GPCLR0 + (sel << 2); + if(sel) shift = (pin - 32) & 0x1f; + else shift = pin & 0x1f; + if(val == 0) outw(rsel, 1<<shift); + else outw(ssel, 1<<shift); +} + + +void +setgpiofunc(uint pin, uint func) +{ + uint sel, data, shift; + + if(pin > 53) return; + sel = 0; + while (pin > 10) { + pin = pin - 10; + sel++; + } + sel = (sel << 2) + GPFSEL0; + data = inw(sel); + shift = pin + (pin << 1); + data &= ~(7 << shift); + outw(sel, data); + data |= func << shift; + outw(sel, data); +} + + +void +uartputc(uint c) +{ + if(c=='\n') { + while(1) if(inw(AUX_MU_LSR_REG) & 0x20) break; + outw(AUX_MU_IO_REG, 0x0d); // add CR before LF + } + while(1) if(inw(AUX_MU_LSR_REG) & 0x20) break; + outw(AUX_MU_IO_REG, c); +} + +static int +uartgetc(void) +{ + if(inw(AUX_MU_LSR_REG)&0x1) return inw(AUX_MU_IO_REG); + else return -1; +} + +void +enableirqminiuart(void) +{ + intctrlregs *ip; + + ip = (intctrlregs *)INT_REGS_BASE; + ip->gpuenable[0] |= (1 << 29); // enable the miniuart through Aux +} + + +void +miniuartintr(void) +{ + consoleintr(uartgetc); +} + +void +uartinit(void) +{ + outw(AUX_ENABLES, 1); + outw(AUX_MU_CNTL_REG, 0); + outw(AUX_MU_LCR_REG, 0x3); + outw(AUX_MU_MCR_REG, 0); + outw(AUX_MU_IER_REG, 0x1); + outw(AUX_MU_IIR_REG, 0xC7); + outw(AUX_MU_BAUD_REG, 270); // (250,000,000/(115200*8))-1 = 270 + + setgpiofunc(14, 2); // gpio 14, alt 5 + setgpiofunc(15, 2); // gpio 15, alt 5 + + outw(GPPUD, 0); + delay(10); + outw(GPPUDCLK0, (1 << 14) | (1 << 15) ); + delay(10); + outw(GPPUDCLK0, 0); + + outw(AUX_MU_CNTL_REG, 3); + enableirqminiuart(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/uart_pl011.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,49 @@ +/* + * pl011.c + * + * Created on: Nov 20, 2016 + * Author: Mahdi Amiri + */ +#include <fvp.h> +#include <types.h> +#include <defs.h> + +uint uart_lock; //Mutex lock + +void uartinit_fvp(){ + /* Enable pl011 interrupts */ + *(volatile uint*) FVP_PL011_UARTIMSC = FVP_PL011_UARTIMSC_RXIM; + + /* Enable pl011 controller */ + *(volatile uint*) FVP_PL011_UARTCR = + *(volatile uint*) FVP_PL011_UARTCR | FVP_PL011_UARTCR_UARTEN | FVP_PL011_UARTCR_TXE | FVP_PL011_UARTCR_RXE; + //outw(UARTCR,inw(UARTCR) | UARTCR_UARTEN | UARTCR_TXE | UARTCR_RXE); + //enable_irq(37,1); + uart_lock=0; // Open Mutex lock +} + +void uartputc_fvp(uint c) +{ + if(c=='\n') { + /* Wait until the buffer is empty */ + while (*(volatile uint*)(FVP_PL011_UARTFR) & (FVP_PL011_UARTFR_TXFF)); + //while (inw(UARTFR) & UARTFR_TXFF); + /* Put the character into the register */ + *(volatile uint*) FVP_PL011_UARTDR = 0x0d; + //outw(UARTDR , c); + } + while (*(volatile uint*)(FVP_PL011_UARTFR) & (FVP_PL011_UARTFR_TXFF)); + *(volatile uint*) FVP_PL011_UARTDR = c; + + +} + +uint uartgetc_fvp() +{ + /* Wait until the buffer is empty */ + while (*(volatile uint*)(FVP_PL011_UARTFR) & (FVP_PL011_UARTFR_RXFE)); + //while (inw(UARTFR) & UARTFR_RXFE); + /* Put the character into the register */ + return *(volatile uint*) FVP_PL011_UARTDR; + //outw(UARTDR , c); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/vm.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,419 @@ +/***************************************************************** +* vm.c +* adapted from MIT xv6 by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +#include "param.h" +#include "types.h" +#include "defs.h" +#include "arm.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "elf.h" + +extern char data[]; // defined by kernel.ld +extern char end[]; // defined by kernel.ld +extern unsigned int pm_size; + +pde_t *kpgdir; // for use in scheduler() + +// Return the address of the PTE in page table pgdir +// 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, uint l1attr, int alloc) +{ + pde_t *pde; + pte_t *pgtab; + + pde = &pgdir[PDX(va)]; + if((uint)*pde != 0){ + pgtab = (pte_t*)p2v(PTE_ADDR(*pde)); + } else { + if(!alloc || (pgtab = (pte_t*)kalloc()) == 0) + return 0; + // Make sure all those PTE_P bits are zero. + memset(pgtab, 0, PGSIZE); + // 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) | l1attr; +//cprintf("the pde value is %x\n", (uint)*pde); + } + return &pgtab[PTX(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, uint l1attr, uint l2attr) +{ + char *a, *last; + pte_t *pte; + + a = (char*)PGROUNDDOWN((uint)va); + last = (char*)PGROUNDDOWN(((uint)va) + size - 1); + +//cprintf("size= %x a=%x last= %x pa=%x\n", size, a, last, pa); + + if((SECTION & l1attr) != 0){// for 1 MB pages + for(;;){ + if(a > last) break; + if((uint)pgdir[PDX(a)] != 0) panic("remap"); + pgdir[PDX(a)] = pa | l1attr; +//cprintf("The pgdir entry: %x value: %x a=%x last= %x\n", PDX(a), pgdir[PDX(a)], a, last); + a += MBYTE; + pa += MBYTE; + } + } else if((COARSE & l1attr) != 0){// for 4kB pages + for(;;){ + //cprintf("The pgdir is %x value: %x a=%x last= %x\n", pgdir+PDX(a), pgdir[PDX(a)], a, last); + if((pte = walkpgdir(pgdir, a, l1attr, 1)) == 0) + return -1; + if((uint)*pte != 0) panic("remap"); + *pte = pa | l2attr; +//cprintf("The pte value is %x, the pde values is %x\n", (uint)*pte, pgdir[PDX(a)]); + if(a == last) break; + a += PGSIZE; + pa += PGSIZE; + } + } else panic("Unknown page attribute"); + return 0; +} + +// There is one page table per process, plus one that's used when +// a CPU is not running any process (kpgdir). The kernel uses the +// current process's page table during system calls and interrupts; +// page protection bits prevent user code from using the kernel's +// mappings. +// +// setupkvm() and exec() set up every page table like this: +// +// 0..KERNBASE: user memory (text+data+stack+heap), mapped to +// phys memory allocated by the kernel +// KERNBASE..KERNBASE+EXTMEM: mapped to 0..EXTMEM (for I/O space) +// KERNBASE+EXTMEM..data: mapped to EXTMEM..V2P(data) +// for the kernel's instructions and r/o data +// data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP, +// rw data + free physical memory +// 0xfe000000..0: mapped direct (devices such as ioapic) +// +// The kernel allocates physical memory for its heap and for user memory +// between V2P(end) and the end of physical memory (PHYSTOP) +// (directly addressable from end..P2V(PHYSTOP)). + +// This table defines the kernel's mappings, which are present in +// every process's page table. +static struct kmap { + void *virt; + uint phys_start; + uint phys_end; + uint l1attr; + uint l2attr; +} kmap[] = { + { (void*)KERNBASE, PHYSTART, PHYSTOP, DOMAIN0|PDX_AP(U_RW)|SECTION|CACHED|BUFFERED, 0}, + { (void*)MMIO_VA, MMIO_PA, MMIO_PA+MMIO_SIZE, DOMAIN0|PDX_AP(U_RW)|SECTION, 0}, + { (void*)HVECTORS, PHYSTART, PHYSTART+TVSIZE, DOMAIN0|COARSE, PTX_AP(K_RW)|SMALL}, +}; + +// Set up kernel part of a page table. +// However, since the kernel part is shared, only the user part +// of the pgd is allocated (one page only for simplicity, so user space +// is now limited to 1GB +pde_t* +setupkvm(void) +{ + pde_t *pgdir; + + if((pgdir = (pde_t*)kalloc()) == 0) + return 0; +//cprintf("inside setupkvm: pgdir=%x\n", pgdir); + memset(pgdir, 0, PGSIZE); +//cprintf("after memset\n", pgdir); + return pgdir; +} + + +// Set up kernel part of a page table. +pde_t* +setupkvm_new(void) +{ + pde_t *pgdir; + struct kmap *k; + +/* if((pgdir = (pde_t*)kalloc()) == 0) + return 0;*/ + + pgdir = kpgdir; + memset(pgdir, 0, 4*PGSIZE); + if (p2v(pm_size) > (void*)MMIO_VA) + panic("PHYSTOP (pm_size) too high"); + k = kmap; k->phys_end = pm_size; + for(k = kmap; k < &kmap[NELEM(kmap)]; k++) + if(mappages(pgdir, k->virt, k->phys_end - k->phys_start, + (uint)k->phys_start, k->l1attr, k->l2attr) < 0) + return 0; + return pgdir; +} + +// Allocate one page table for the machine for the kernel address +// space for scheduler processes. +void +kvmalloc(void) +{ + kpgdir = setupkvm_new(); + switchkvm(); +} + +// Switch h/w page table register to the kernel-only page table, +// for when no process is running. +void +switchkvm(void) +{ +// do nothing here as the same pgdir is shared between kernel and user; +// will see if the user portion of the pgdir should be removed. +} + +void +switchkvm_new(void) +{ + dsb_barrier(); + flush_idcache(); + //cprintf("The phy pgtbase address is %x\n", (uint)v2p(kpgdir)); + set_pgtbase((uint)v2p(kpgdir)); // switch to the kernel page table + //cprintf("after set_pgtbase\n"); + dsb_barrier(); + flush_tlb(); + //cprintf("after flush_tlb\n"); +} + +// Switch TSS and h/w page table to correspond to process p. +void +switchuvm(struct proc *p) +{ + pushcli(); + //cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE; + if(p->pgdir == 0) + panic("switchuvm: no pgdir"); +//cprintf("before copying uvm to kvm kpgdir=%x the first entry: %x\n", kpgdir, kpgdir[0]); + memmove((void *)kpgdir, (void *)p->pgdir, PGSIZE); // switch to new user address space + flush_idcache(); + 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 >= PGSIZE) + panic("inituvm: more than a page"); + mem = kalloc(); + memset(mem, 0, PGSIZE); +//cprintf("inituvm: page is allocated at %x\n", mem); + mappages(pgdir, 0, PGSIZE, v2p(mem), UVMPDXATTR, UVMPTXATTR); + //mappages(pgdir, 0, PGSIZE, v2p(mem), UVMPDXATTR, 0xdfe); + + 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 % PGSIZE != 0) + panic("loaduvm: addr must be page aligned"); + if((uint)addr + sz > USERBOUND) + panic("loaduvm: user address space exceeds the allowed space (> 0x80000000)"); + for(i = 0; i < sz; i += PGSIZE){ + if((pte = walkpgdir(pgdir, addr+i, UVMPDXATTR, 0)) == 0) + panic("loaduvm: address should exist"); + pa = PTE_ADDR(*pte); + if(sz - i < PGSIZE) + n = sz - i; + else + n = PGSIZE; + 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 >= USERBOUND) + return 0; + if(newsz < oldsz) + return oldsz; + + a = PGROUNDUP(oldsz); + for(; a < newsz; a += PGSIZE){ + mem = kalloc(); + if(mem == 0){ + cprintf("allocuvm out of memory\n"); + deallocuvm(pgdir, newsz, oldsz); + return 0; + } + memset(mem, 0, PGSIZE); + mappages(pgdir, (char*)a, PGSIZE, v2p(mem), UVMPDXATTR, UVMPTXATTR); + //mappages(pgdir, (char*)a, PGSIZE, v2p(mem), UVMPDXATTR, 0xdfe); + } + 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, pa; + + if(newsz >= oldsz) + return oldsz; + + a = PGROUNDUP(newsz); + for(; a < oldsz; a += PGSIZE){ + pte = walkpgdir(pgdir, (char*)a, UVMPDXATTR, 0); + if(!pte) + a += (NPTENTRIES - 1) * PGSIZE; + else if(*pte != 0){ + pa = PTE_ADDR(*pte); + if(pa == 0) + panic("kfree"); + char *v = p2v(pa); + kfree(v); + *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; + + if(pgdir == 0) + panic("freevm: no pgdir"); + deallocuvm(pgdir, USERBOUND, 0); + for(i = 0; i < NPDENTRIES; i++){ + if((uint)pgdir[i] != 0){ + char * v = p2v(PTE_ADDR(pgdir[i])); + kfree(v); + } + } + kfree((char*)pgdir); +} + +// Clear PTE_U on a page. Used to create an inaccessible +// page beneath the user stack. +void +clearpteu(pde_t *pgdir, char *uva) +{ + pte_t *pte; + + pte = walkpgdir(pgdir, uva, UVMPDXATTR, 0); + if(pte == 0) + panic("clearpteu"); + *pte &= ~PTX_AP(U_AP); +} + +// 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, flags; + char *mem; + + if((d = setupkvm()) == 0) + return 0; + for(i = 0; i < sz; i += PGSIZE){ + if((pte = walkpgdir(pgdir, (void *) i, UVMPDXATTR, 0)) == 0) + panic("copyuvm: pte should exist"); + if((uint)*pte == 0) + panic("copyuvm: page not present"); + pa = PTE_ADDR(*pte); + flags = PTE_FLAGS(*pte); + if((mem = kalloc()) == 0) + goto bad; + memmove(mem, (char*)p2v(pa), PGSIZE); + if(mappages(d, (void*)i, PGSIZE, v2p(mem), UVMPDXATTR, flags) < 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, UVMPDXATTR, 0); + if((uint)*pte == 0) + return 0; + if(((uint)*pte & PTX_AP(U_AP)) == 0) + 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 PTE_U 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 = (uint)PGROUNDDOWN(va); + pa0 = uva2ka(pgdir, (char*)va0); + if(pa0 == 0) + return -1; + n = PGSIZE - (va - va0); + if(n > len) + n = len; + memmove(pa0 + (va - va0), buf, n); + len -= n; + buf += n; + va = va0 + PGSIZE; + } + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/LICENSE Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,24 @@ +The xv6 software is: + +Copyright (c) 2006-2009 Frans Kaashoek, Robert Morris, Russ Cox, + Massachusetts Institute of Technology + +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/uprogs/Makefile Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,71 @@ +# Cross-compiling (e.g., on Mac OS X) +#TOOLPREFIX = i386-jos-elf- +# Using native tools (e.g., on X86 Linux) +#TOOLPREFIX = ~/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi- + + +# The intermediate directory for compiled object files. +BUILD = build/ + +CC = $(TOOLPREFIX)gcc +AS = $(TOOLPREFIX)as +LD = $(TOOLPREFIX)ld +OBJCOPY = $(TOOLPREFIX)objcopy +OBJDUMP = $(TOOLPREFIX)objdump +#CFLAGS := -fno-pic -static -fno-builtin -fno-strict-aliasing -fshort-wchar -O2 -Wall -MD -ggdb -Werror -fno-omit-frame-pointer -fno-stack-protector -Wa,-march=armv6 -Wa,-mcpu=arm1176jzf-s +CFLAGS := -fno-pic -static -fno-builtin -fno-strict-aliasing -fshort-wchar -O2 -Wall -MD -ggdb -Werror -fno-omit-frame-pointer -fno-stack-protector -march=armv7-a + +all: mkfs initcode fs.img + +initcode: initcode.S + $(CC) $(CFLAGS) -nostdinc -I. -c initcode.S + $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o + $(OBJCOPY) -S -O binary initcode.out initcode + $(OBJDUMP) -S initcode.o > initcode.asm + + +ULIB = ulib.o usys.o printf.o umalloc.o + +_%: %.o $(ULIB) + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ + $(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 + +mkfs: mkfs.c ../include/fs.h + gcc -o mkfs mkfs.c + #gcc -Werror -Wall -o mkfs mkfs.c + +# Prevent deletion of intermediate files, e.g. cat.o, after first build, so +# that disk image changes after first build are persistent until clean. More +# details: +# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html +.PRECIOUS: %.o + +UPROGS=\ + _cat\ + _echo\ + _forktest\ + _grep\ + _init\ + _kill\ + _ln\ + _ls\ + _mkdir\ + _rm\ + _sh\ + _stressfs\ + _usertests\ + _wc\ + _zombie\ + +fs.img: mkfs README $(UPROGS) + ./mkfs fs.img README $(UPROGS) + +clean: + rm -f *.o *.d *.asm *.sym fs.img mkfs initcode initcode.out $(UPROGS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/README Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,50 @@ +xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix +Version 6 (v6). xv6 loosely follows the structure and style of v6, +but is implemented for a modern x86-based multiprocessor using ANSI C. + +ACKNOWLEDGMENTS + +xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer +to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, +2000)). See also http://pdos.csail.mit.edu/6.828/2012/v6.html, which +provides pointers to on-line resources for v6. + +xv6 borrows code from the following sources: + JOS (asm.h, elf.h, mmu.h, bootasm.S, ide.c, console.c, and others) + Plan 9 (entryother.S, mp.h, mp.c, lapic.c) + FreeBSD (ioapic.c) + NetBSD (console.c) + +The following people have made contributions: + Russ Cox (context switching, locking) + Cliff Frey (MP) + Xiao Yu (MP) + Nickolai Zeldovich + Austin Clements + +In addition, we are grateful for the patches contributed by Greg +Price, Yandong Mao, and Hitoshi Mitake. + +The code in the files that constitute xv6 is +Copyright 2006-2012 Frans Kaashoek, Robert Morris, and Russ Cox. + +ERROR REPORTS + +If you spot errors or have suggestions for improvement, please send +email to Frans Kaashoek and Robert Morris (kaashoek,rtm@csail.mit.edu). + +BUILDING AND RUNNING XV6 + +To build xv6 on an x86 ELF machine (like Linux or FreeBSD), run "make". +On non-x86 or non-ELF machines (like OS X, even on x86), you will +need to install a cross-compiler gcc suite capable of producing x86 ELF +binaries. See http://pdos.csail.mit.edu/6.828/2012/tools.html. +Then run "make TOOLPREFIX=i386-jos-elf-". + +To run xv6, you can use the Bochs or QEMU PC simulators. Bochs makes +debugging easier, but QEMU is much faster. To run in Bochs, run "make +bochs" and then type "c" at the bochs prompt. To run in QEMU, run +"make qemu". + +To create a typeset version of the code, run "make xv6.pdf". This +requires the "mpage" utility. See http://www.mesa.nl/pub/mpage/.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/arm.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,25 @@ +//PAGEBREAK: 36 +// Layout of the trap frame built on the stack by the +// hardware and by trapasm.S, and passed to trap(). +struct trapframe { + uint trapno; + uint spsr; // saved cpsr from the trapped/interrupted mode + uint ifar; // Instruction Fault Address Register (IFAR) + uint cpsr; + uint sp; // user mode sp + uint lr; // return address of the interrupted code + uint r12; + uint r11; + uint r10; + uint r9; + uint r8; + uint r7; + uint r6; + uint r5; + uint r4; + uint r3; + uint r2; + uint r1; + uint r0; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/buf.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,13 @@ +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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/cat.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,38 @@ +#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/uprogs/defs.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,208 @@ +struct buf; +struct context; +struct file; +struct inode; +struct pipe; +struct proc; +struct spinlock; +struct stat; +struct superblock; + +void OkLoop(void); +void NotOkLoop(void); + +void uart_putc(u32); +void cprintf(char*, ...); +void SetGpio(u32, u32); +void SetGpioFunction(u32, u32); +void Wait(u32); +void consoleinit(void); +void uart_init(void); +void mmuinit1(void); +void barriers(void); +void dsb_barrier(void); +void flush_tlb(void); +void flush_dcache(u32 va1, u32 va2); +void flush_idcache(void); +void set_pgtbase(u32 base); + +// bio.c +void binit(void); +struct buf* bread(uint, uint); +void brelse(struct buf*); +void bwrite(struct buf*); + +// 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 ideintr(void); +void iderw(struct buf*); + +// 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); + +// kalloc.c +char* kalloc(void); +void kfree(char*); +void kinit1(void*, void*); +void kinit2(void*, void*); + + +// log.c +void initlog(void); +void log_write(struct buf*); +void begin_trans(); +void commit_trans(); + +// 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*); + +// 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); + +void kvmalloc(void); + + +void drawCharacter(u8, u32, u32); +int UsbInitialise(void); +void KeyboardUpdate(void); +char KeyboardGetChar(void); +void gpuputc(u32); +u32 KeyboardCount(void); +u32 KeyboardGetAddress(u32); +struct KeyboardLeds KeyboardGetLedSupport(u32); + +void panic(char *s); + +// spinlock.c +void acquire(struct spinlock*); +void getcallerpcs(void*, uint*); +int holding(struct spinlock*); +void initlock(struct spinlock*, char*); +void release(struct spinlock*); +void pushcli(void); +void popcli(void); + +// 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); +u32 div(u32 n, u32 d); + +// 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); + +// trap.c +void trap_init(void); +extern uint ticks; +extern struct spinlock tickslock; + + +// vm.c +void seginit(void); +void kvmalloc(void); +void vmenable(void); +pde_t* setupkvm(void); +char* uva2ka(pde_t*, char*); +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*); +void switchkvm(void); +int copyout(pde_t*, uint, void*, uint); +void clearpteu(pde_t *pgdir, char *uva); + + + +// number of elements in fixed-size array +#define NELEM(x) (sizeof(x)/sizeof((x)[0])) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/echo.c Sun Jan 06 19:27:03 2019 +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/uprogs/elf.h Sun Jan 06 19:27:03 2019 +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/uprogs/fcntl.h Sun Jan 06 19:27:03 2019 +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/uprogs/file.h Sun Jan 06 19:27:03 2019 +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/uprogs/forktest.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,56 @@ +// 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) + 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/uprogs/fs.h Sun Jan 06 19:27:03 2019 +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/uprogs/grep.c Sun Jan 06 19:27:03 2019 +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/uprogs/init.c Sun Jan 06 19:27:03 2019 +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/uprogs/initcode.S Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,37 @@ +# Initial process execs /init. + +#include "syscall.h" +#include "traps.h" + + +# exec(init, argv) +.globl start +start: + push {lr} + ldr r0, =argv + push {r0} + ldr r0, =init + push {r0} + mov r0, #SYS_exec + swi #T_SYSCALL + pop {lr} + pop {lr} + pop {lr} + bx lr + +# for(;;) exit(); +exit: + mov r11, #SYS_exit + swi #T_SYSCALL + bl exit + +# char init[] = "/init\0"; +init: + .string "/init\0" + +# char *argv[] = { init, 0 }; +.p2align 2 +argv: + .long init + .long 0 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/kill.c Sun Jan 06 19:27:03 2019 +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/uprogs/ln.c Sun Jan 06 19:27:03 2019 +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/uprogs/ls.c Sun Jan 06 19:27:03 2019 +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/uprogs/memlayout.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,18 @@ +// Memory layout + +// Key addresses for address space layout (see kmap in vm.c for layout) +#define KERNBASE 0x80000000 // First kernel virtual address +#define USERBOUND 0x40000000 // maximum user space due to one page pgd + +#define MACHADDR (KERNBASE+0x2000) +#define TVSIZE 0x1000 + +static inline uint v2p(void *a) { return ((uint) (a)) - KERNBASE; } +static inline void *p2v(uint a) { return (void *) ((a) + KERNBASE); } + +#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/uprogs/mkdir.c Sun Jan 06 19:27:03 2019 +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/uprogs/mkfs.c Sun Jan 06 19:27:03 2019 +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/uprogs/mmu.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,86 @@ +#define MByte 0x100000 +#define L1 0x4000 +#define L2 0x3000 + +#define CACHELINESIZE 32 + +#define FEXT(d, o, w) (((d)>>(o)) & ((1<<(w))-1)) +#define L1X(va) FEXT((va), 20, 12) +#define L2X(va) FEXT((va), 12, 8) + +/* + * page table entries. +*/ + +#define Mbz (0<<4) +#define Fault 0x00000000 /* L[12] pte: unmapped */ + +#define Coarse (Mbz|1) /* L1 */ +#define Section (Mbz|2) /* L1 1MB */ +#define Fine (Mbz|3) /* L1 */ + +#define Large 0x00000001 /* L2 64KB */ +#define Small 0x00000002 /* L2 4KB */ +#define Tiny 0x00000003 /* L2 1KB: not in v7 */ +#define Buffered 0x00000004 /* L[12]: write-back not -thru */ +#define Cached 0x00000008 /* L[12] */ +#define Dom0 0 + +#define Noaccess 0 /* AP, DAC */ +#define Krw 1 /* AP */ +/* armv7 deprecates AP[2] == 1 & AP[1:0] == 2 (Uro), prefers 3 (new in v7) */ +#define Uro 2 /* AP */ +#define Urw 3 /* AP */ +#define Client 1 /* DAC */ +#define Manager 3 /* DAC */ + +#define F(v, o, w) (((v) & ((1<<(w))-1))<<(o)) +#define AP(n, v) F((v), ((n)*2)+4, 2) +#define L1AP(ap) (AP(3, (ap))) +#define L2AP(ap) (AP(3, (ap))|AP(2, (ap))|AP(1, (ap))|AP(0, (ap))) /* pre-armv7 */ +#define DAC(n, v) F((v), (n)*2, 2) + +#define HVECTORS 0xffff0000 + +// A virtual address 'la' has a three-part structure as follows: +// +// +--------12------+-------8--------+---------12----------+ +// | Page Directory | Page Table | Offset within Page | +// | Index | Index | | +// +----------------+----------------+---------------------+ +// \--- PDX(va) --/ \--- PTX(va) --/ + +// page directory index +#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0xFFF) + +// page table index +#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0xFF) + +// construct virtual address from indexes and offset +#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) + +// Address in page table or page directory entry +#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF) +#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF) + +// Page directory and page table constants. +#define NPDENTRIES 1024 // # directory entries per page directory +#define NPTENTRIES 1024 // # PTEs per page table +#define PGSIZE 4096 // bytes mapped by a page + +#define PGSHIFT 12 // log2(PGSIZE) +#define PTXSHIFT 12 // offset of PTX in a linear address +#define PDXSHIFT 20 // offset of PDX in a linear address + + +#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) +#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1)) + +#define PGDIR_BASE P2V(L1) + +#define KVML1ATTR Dom0|L1AP(Urw)|Section|Cached|Buffered + +#define UVML1ATTR Dom0|Coarse +#define UVML2ATTR L2AP(Urw)|Cached|Buffered|Small + +#define USER_MODE 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/param.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,12 @@ +#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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/printf.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,103 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +static void +putc(int fd, char c) +{ + write(fd, &c, 1); +} + +u32 div(u32 n, u32 d) // long division +{ + u32 q=0, r=0; + int i; + + for(i=31;i>=0;i--){ + r = r << 1; + r = r | ((n >> i) & 1); + if(r >= d) { + r = r - d; + q = q | (1 << i); + } + } + return q; +} + +static void +printint(int fd, int xx, int base, int sgn) +{ + static char digits[] = "0123456789ABCDEF"; + char buf[16]; + int i, neg; + uint x, y, b; + + neg = 0; + if(sgn && xx < 0){ + neg = 1; + x = -xx; + } else { + x = xx; + } + + b = base; + i = 0; + do{ + y = div(x, b); + buf[i++] = digits[x - y * b]; + }while((x = y) != 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/uprogs/proc.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,82 @@ +// Segments in proc->gdt. +#define NSEGS 7 + +// Per-CPU state +struct cpu { + uchar id; // Local APIC 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. +}; + +struct cpu cpus[NCPU]; +//extern int ncpu; + +// Per-CPU variables, holding pointers to the +// current cpu and to the current process. +// The asm suffix tells gcc to use "%gs:0" to refer to cpu +// and "%gs:4" to refer to proc. seginit sets up the +// %gs segment register so that %gs refers to the memory +// holding those two variables in the local cpu's struct cpu. +// This is similar to how thread-local variables are implemented +// in thread libraries such as Linux pthreads. +//extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()] +//extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc + +#define curr_cpu (&cpus[0]) +#define curr_proc (cpus[0].proc) + +//PAGEBREAK: 17 +// Saved registers for kernel context switches. +// Don't need to save all the segment registers (%cs, etc), +// because they are constant across kernel contexts. +// Don't need to save %eax, %ecx, %edx, because the +// x86 convention is that the caller has saved them. +// Contexts are stored at the bottom of the stack they +// describe; the stack pointer is the address of the context. +// The layout of the context matches the layout of the stack in swtch.S +// at the "Switch stacks" comment. Switch doesn't save eip explicitly, +// but it is on the stack and allocproc() manipulates it. +struct context { + uint r4; + uint r5; + uint r6; + uint r7; + uint r8; + uint r9; + uint r10; + uint r11; + uint r12; + uint lr; + uint pc; +}; + +enum procstate { UNUSED=0, 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/rm.c Sun Jan 06 19:27:03 2019 +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/uprogs/sh.c Sun Jan 06 19:27:03 2019 +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/uprogs/spinlock.h Sun Jan 06 19:27:03 2019 +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/uprogs/stat.h Sun Jan 06 19:27:03 2019 +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/uprogs/stressfs.c Sun Jan 06 19:27:03 2019 +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/uprogs/syscall.h Sun Jan 06 19:27:03 2019 +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/uprogs/traps.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,15 @@ +// These are arbitrarily chosen, but with care not to overlap +// processor defined exceptions or interrupt vectors. +#define T_SYSCALL 64 // system call +#define T_DEFAULT 500 // catchall + +#define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ + +#define IRQ_TIMER 0 +#define IRQ_KBD 1 +#define IRQ_COM1 4 +#define IRQ_IDE 14 +#define IRQ_ERROR 19 +#define IRQ_SPURIOUS 31 + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/types.h Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,86 @@ +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef unsigned long long u64; + +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef uint pde_t; +typedef uint pte_t; + + +/* trap vectors layout at virtual +address HVECTORS (and KZERO(0x80000000), doubled mapped).*/ +typedef struct Vpage0 { + void (*vectors[8])(void); + u32 vtable[8]; +} Vpage0; + + +/* interrupt control registers */ +typedef struct Intregs { + u32 ARMpending; + u32 GPUpending[2]; + u32 FIQctl; + u32 GPUenable[2]; + u32 ARMenable; + u32 GPUdisable[2]; + u32 ARMdisable; +} Intregs; + + +typedef struct Mach +{ + int machno; /* physical id of processor */ + + int flushmmu; /* flush current proc mmu state */ + + /* stats */ + int tlbfault; + int tlbpurge; + int pfault; + int cs; + int syscall; + int load; + int intr; + int lastintr; + int ilockdepth; + + + int cpumhz; + /* vfp2 or vfp3 fpu */ + int havefp; + int havefpvalid; + int fpon; + int fpconfiged; + int fpnregs; + int fppid; /* pid of last fault */ + int fpcnt; /* how many consecutive at that addr */ + + /* save areas for exceptions, hold R0-R4 */ + u32 sfiq[5]; + u32 sirq[5]; + u32 sund[5]; + u32 sabt[5]; + u32 smon[5]; /* probably not needed */ + u32 ssys[5]; + + int stack[1]; +} Mach; + + +struct framebufferdescription { + u32 width; //width + u32 height; // height + u32 v_width; // virtual width + u32 v_height; // virtual height + u32 pitch; // GPU pitch + u32 depth; // bit depth + u32 x; + u32 y; + u32 fbp; //GPU framebuffer pointer + u32 fbs; // GPU framebuffer size +}; + +typedef struct framebufferdescription FBI;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uprogs/ulib.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,108 @@ +#include "types.h" +#include "stat.h" +#include "fcntl.h" +#include "user.h" +#include "arm.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 c, uint n) +{ + char *p=dst; + u32 rc=n; + + while (rc-- > 0) *p++ = c; + return (void *)p; +} + +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/uprogs/umalloc.c Sun Jan 06 19:27:03 2019 +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/uprogs/user.h Sun Jan 06 19:27:03 2019 +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/uprogs/usertests.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,1678 @@ +#include "param.h" +#include "types.h" +#include "stat.h" +#include "user.h" +#include "fs.h" +#include "fcntl.h" +#include "syscall.h" +#include "traps.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; + + 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; + while((m2 = malloc(10001)) != 0){ + *(char**)m2 = m1; + m1 = m2; + } + while(m1){ + m2 = *(char**)m1; + free(m1); + m1 = m2; + } + 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 { + 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[5], 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? + printf(stdout, "test #1 sbrk() less than a page?\n"); + 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; + } + printf(stdout, "test #1 done.\n"); + + printf(stdout, "test #2 test fork?\n"); + pid = fork(); + if(pid < 0){ + printf(stdout, "sbrk test fork failed\n"); + exit(); + } + printf(stdout, "test #2 done.\n"); + + printf(stdout, "test #3 post-fork.\n"); + c = sbrk(1); + c = sbrk(1); + if(c != a + 1){ + printf(stdout, "sbrk test failed post-fork\n"); + exit(); + } + if(pid == 0) + exit(); + wait(); + printf(stdout, "test #3 done.\n"); + + printf(stdout, "test #4 grow address space to something big.\n"); + // 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; + printf(stdout, "test #4 done.\n"); + + printf(stdout, "test #5 de-allocate\n"); + // 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(); + } + printf(stdout, "test #5 done.\n"); + + printf(stdout, "test #6 re-allocate page.\n"); + // 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(); + } + printf(stdout, "test #6 done.\n"); + + printf(stdout, "test #7 downsize.\n"); + a = sbrk(0); + c = sbrk(-(sbrk(0) - oldbrk)); + if(c != a){ + printf(stdout, "sbrk downsize failed, a %x c %x\n", a, c); + exit(); + } + printf(stdout, "test #7 done.\n"); + + printf(stdout, "test #8 read the kernel's memory.\n"); + // 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(); + } + printf(stdout, "test #8 done.\n"); + + printf(stdout, "test #9 clean up the last failed allocation\n"); + // 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(); + } + printf(stdout, "test #9 done.\n"); + + printf(stdout, "test #10 memory utilization 1.\n"); + for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ + if((pids[i] = fork()) == 0){ + printf(stdout, "create pid:%d.\n",i); + // 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); + } + printf(stdout, "test #10 done.\n"); + + printf(stdout, "test #11 memory utilization 2.\n"); + // 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(); + } + printf(stdout, "test #11 done.\n"); + + printf(stdout, "test #12 leaked memory.\n"); + if(c == (char*)0xffffffff){ + printf(stdout, "failed sbrk leaked memory\n"); + exit(); + } + printf(stdout, "test #12 done.\n"); + + if(sbrk(0) > oldbrk) + sbrk(-(sbrk(0) - oldbrk)); + + printf(stdout, "sbrk test OK\n"); +} + +void +validateint(int *p) +{ + sleep(*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/uprogs/usys.S Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,374 @@ +/***************************************************************** +* usys.S +* by Zhiyi Huang, hzy@cs.otago.ac.nz +* University of Otago +* +********************************************************************/ + + + +#include "syscall.h" +#include "traps.h" + +/* This is clumsy, but don't know how to make it smart */ +.globl fork +fork: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_fork + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl exit +exit: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_exit + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl wait +wait: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_wait + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl pipe +pipe: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_pipe + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl read +read: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_read + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl write +write: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_write + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl close +close: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_close + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + + +.globl kill +kill: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_kill + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl exec +exec: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_exec + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl open +open: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_open + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl mknod +mknod: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_mknod + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl unlink +unlink: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_unlink + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl fstat +fstat: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_fstat + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl link +link: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_link + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl mkdir +mkdir: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_mkdir + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl chdir +chdir: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_chdir + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl dup +dup: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_dup + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl getpid +getpid: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_getpid + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl sbrk +sbrk: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_sbrk + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl sleep +sleep: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_sleep + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + bx lr + +.globl uptime +uptime: + push {lr} + push {r3} + push {r2} + push {r1} + push {r0} + mov r0, #SYS_uptime + swi #T_SYSCALL + pop {r1} /* to avoid overwrite of r0 */ + pop {r1} + pop {r2} + pop {r3} + pop {lr} + 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/uprogs/wc.c Sun Jan 06 19:27:03 2019 +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/uprogs/zombie.c Sun Jan 06 19:27:03 2019 +0900 @@ -0,0 +1,18 @@ +// 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. + printf(2, "Parent exits!\n"); + exit(); + } + printf(2, "Child exits!\n"); + exit(); +}