diff --git a/Makefile b/Makefile index 7141207fe41361eb698e041003179e28479aa5f4..2a9fcdb608b2a86bb2acd68dbf824b4d6066c34c 100644 --- a/Makefile +++ b/Makefile @@ -1,68 +1,13 @@ +KERNEL_DIR = ./kernel -OBJCOPY := $(GCCPREFIX)objcopy -OBJDUMP := $(GCCPREFIX)objdump - -objects_c := $(wildcard *.c) -objects_s := $(wildcard *.s) - -GCC := riscv64-unknown-elf-gcc -GCCFLAGS = -Wall -O0 -fno-omit-frame-pointer -ggdb -g -GCCFLAGS += -MD -GCCFLAGS += -mcmodel=medany -GCCFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax -GCCFLAGS += -I. -GCCFLAGS += -Werror - -LD := riscv64-unknown-elf-ld -LDFLAGS := -z max-page-size=4096 -T -OBJCOPY := riscv64-unknown-elf-objcopy -QEMU := qemu-system-riscv64 -QEMUFLAGS := -machine virt -m 128M -nographic -bios default -smp 1 -drive file=fat32.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -HEAD := *.h -KERNEL_FILE := core.img +modules := $(KERNEL_DIR) -user/usys.S : user/usys.pl - perl user/usys.pl > user/usys.S - -default: user/usys.S - - ${GCC} ${GCCFLAGS} -c start.S -o start.o - ${GCC} ${GCCFLAGS} -c trampoline.S -o trampoline.o - ${GCC} ${GCCFLAGS} -c kernel.c -o kernel.o - ${GCC} ${GCCFLAGS} -c console.c -o console.o - ${GCC} ${GCCFLAGS} -c sbi.c -o sbi.o - ${GCC} ${GCCFLAGS} -c stdio.c -o stdio.o - ${GCC} ${GCCFLAGS} -c trap.c -o trap.o - ${GCC} ${GCCFLAGS} -c kernel_trapentry.S -o kernel_trapentry.o - ${GCC} ${GCCFLAGS} -c string.c -o string.o - ${GCC} ${GCCFLAGS} -c memory.c -o memory.o - ${GCC} ${GCCFLAGS} -c vm.c -o vm.o - ${GCC} ${GCCFLAGS} -c process.c -o process.o - ${GCC} ${GCCFLAGS} -c spinlock.c -o spinlock.o - ${GCC} ${GCCFLAGS} -c hart.c -o hart.o - ${GCC} ${GCCFLAGS} -c switch.S -o switch.o - ${GCC} ${GCCFLAGS} -c schedule.c -o schedule.o - ${GCC} ${GCCFLAGS} -c virtio_disk.c -o virtio_disk.o - ${GCC} ${GCCFLAGS} -c buf.c -o buf.o - ${GCC} ${GCCFLAGS} -c fat32.c -o fat32.o - ${GCC} ${GCCFLAGS} -c file.c -o file.o - ${GCC} ${GCCFLAGS} -c sleeplock.c -o sleeplock.o - ${GCC} ${GCCFLAGS} -c disk.c -o disk.o - ${GCC} ${GCCFLAGS} -c execv.c -o execv.o - ${GCC} ${GCCFLAGS} -c syscall.c -o syscall.o - ${GCC} ${GCCFLAGS} -c syscall_arg.c -o syscall_arg.o -# ${GCC} ${GCCFLAGS} -c user/init.c -o user/init.o -# ${GCC} ${GCCFLAGS} -c user/usys.S -o user/usys.o - ${LD} ${LDFLAGS} linker.ld -o kernel.elf start.o \ - kernel.o sbi.o stdio.o console.o \ - trap.o kernel_trapentry.o string.o memory.o \ - vm.o process.o spinlock.o hart.o trampoline.o \ - switch.o schedule.o sleeplock.o virtio_disk.o buf.o fat32.o \ - disk.o file.o execv.o syscall.o syscall_arg.o -# user/usys.o user/init.o +build : + cd $(KERNEL_DIR) && $(MAKE) kernel - ${OBJCOPY} kernel.elf --strip-all -O binary $(KERNEL_FILE) +run: clean fat build + ${QEMU} -kernel $(KERNEL_FILE) ${QEMUFLAGS} fat: @-mkdir mnt @@ -72,15 +17,7 @@ fat: @cp -r ./sdcard_file/* ./mnt @umount ./mnt -run: clean fat default - ${QEMU} -kernel $(KERNEL_FILE) ${QEMUFLAGS} - - -debug: clean fat default -# @tmux new-session -d \ - ${QEMU} -kernel $(KERNEL_FILE) ${QEMUFLAGS} -s -S ${QEMUGDB}&& \ - tmux split-window "riscv64-unknown-elf-gdb -ex 'file kernel.elf' -ex 'set arch riscv:rv64' -ex 'target remote localhost:25000'" && \ - tmux -2 attach-session -d +debug: clean fat build @echo "*** Now run 'gdb' in another window." ${QEMU} -kernel $(KERNEL_FILE) ${QEMUFLAGS} -s -S @@ -88,6 +25,11 @@ debug: clean fat default # target remote localhost:1234 clean: - @echo "*** Now run 'gdb' in another window." 1>&2 - -rm -f *.o *.img *.elf *.d user/*.o user/*.d user/*.img user/*.elf - -rm -r ./mnt \ No newline at end of file + -rm -r ./mnt + + for d in $(modules); \ + do \ + $(MAKE) --directory=$$d clean; \ + done + -rm -rf *.o *.img + \ No newline at end of file diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e3e735f1bf81df83b3f4d61ba03a89d7bc6ede54 --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,49 @@ + +OBJCOPY := $(GCCPREFIX)objcopy +OBJDUMP := $(GCCPREFIX)objdump + + +GCC := riscv64-unknown-elf-gcc +GCCFLAGS = -Wall -O0 -fno-omit-frame-pointer -ggdb -g +GCCFLAGS += -MD +GCCFLAGS += -mcmodel=medany +GCCFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax +GCCFLAGS += -I. +GCCFLAGS += -Werror + +LD := riscv64-unknown-elf-ld +LDFLAGS := -z max-page-size=4096 -T +OBJCOPY := riscv64-unknown-elf-objcopy +QEMU := qemu-system-riscv64 +QEMUFLAGS := -machine virt -m 128M -nographic -bios default -smp 1 -drive file=fat32.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 +HEAD := *.h +KERNEL_FILE := core.img +KERNEL_ELF := kernel.elf + +OBJS = start.o trampoline.o kernel.o console.o sbi.o stdio.o \ + trap.o kernel_trapentry.o string.o memory.o vm.o \ + process.o spinlock.o hart.o switch.o schedule.o virtio_disk.o \ + buf.o fat32.o file.o sleeplock.o disk.o execv.o syscall.o \ + syscall_arg.o + +%.o: %.c + $(GCC) $(GCCFLAGS) -c $< + +%.o: %.S + $(GCC) $(GCCFLAGS) -c $< + + +build: $(OBJS) + +$(KERNEL_ELF): build + ${LD} ${LDFLAGS} linker.ld -o $(KERNEL_ELF) $(OBJS) + +kernel: $(KERNEL_ELF) + ${OBJCOPY} $(KERNEL_ELF) --strip-all -O binary ../$(KERNEL_FILE) + + +clean: + @echo "*** Now run 'gdb' in another window." 1>&2 + -rm -f *.o *.img *.elf *.d + +# -rm -r ./mnt \ No newline at end of file diff --git a/Type_defs.h b/kernel/Type_defs.h similarity index 93% rename from Type_defs.h rename to kernel/Type_defs.h index 292086e9fd8276f3d670f87d3e63f071667b5fb9..669bd9e07e9c69fcb6207746ab965031382d0ce2 100644 --- a/Type_defs.h +++ b/kernel/Type_defs.h @@ -17,5 +17,6 @@ typedef long int64_t; //risc-v64 long = 64bit typedef unsigned long uint64_t; typedef uint64_t size_t; typedef int32_t pid_t; +typedef unsigned int uint; #endif diff --git a/buf.c b/kernel/buf.c similarity index 100% rename from buf.c rename to kernel/buf.c diff --git a/buf.h b/kernel/buf.h similarity index 100% rename from buf.h rename to kernel/buf.h diff --git a/console.c b/kernel/console.c similarity index 100% rename from console.c rename to kernel/console.c diff --git a/console.h b/kernel/console.h similarity index 100% rename from console.h rename to kernel/console.h diff --git a/defs.h b/kernel/defs.h similarity index 100% rename from defs.h rename to kernel/defs.h diff --git a/disk.c b/kernel/disk.c similarity index 100% rename from disk.c rename to kernel/disk.c diff --git a/disk.h b/kernel/disk.h similarity index 100% rename from disk.h rename to kernel/disk.h diff --git a/elf.h b/kernel/elf.h similarity index 100% rename from elf.h rename to kernel/elf.h diff --git a/execv.c b/kernel/execv.c similarity index 100% rename from execv.c rename to kernel/execv.c diff --git a/execv.h b/kernel/execv.h similarity index 100% rename from execv.h rename to kernel/execv.h diff --git a/fat32.c b/kernel/fat32.c similarity index 100% rename from fat32.c rename to kernel/fat32.c diff --git a/fat32.h b/kernel/fat32.h similarity index 100% rename from fat32.h rename to kernel/fat32.h diff --git a/fcntl.h b/kernel/fcntl.h similarity index 100% rename from fcntl.h rename to kernel/fcntl.h diff --git a/file.c b/kernel/file.c similarity index 100% rename from file.c rename to kernel/file.c diff --git a/file.h b/kernel/file.h similarity index 100% rename from file.h rename to kernel/file.h diff --git a/hart.c b/kernel/hart.c similarity index 100% rename from hart.c rename to kernel/hart.c diff --git a/hart.h b/kernel/hart.h similarity index 100% rename from hart.h rename to kernel/hart.h diff --git a/kernel.c b/kernel/kernel.c similarity index 100% rename from kernel.c rename to kernel/kernel.c diff --git a/kernel_trapentry.S b/kernel/kernel_trapentry.S similarity index 100% rename from kernel_trapentry.S rename to kernel/kernel_trapentry.S diff --git a/linker.ld b/kernel/linker.ld similarity index 100% rename from linker.ld rename to kernel/linker.ld diff --git a/memlayout.h b/kernel/memlayout.h similarity index 100% rename from memlayout.h rename to kernel/memlayout.h diff --git a/memory.c b/kernel/memory.c similarity index 100% rename from memory.c rename to kernel/memory.c diff --git a/memory.h b/kernel/memory.h similarity index 100% rename from memory.h rename to kernel/memory.h diff --git a/process.c b/kernel/process.c similarity index 100% rename from process.c rename to kernel/process.c diff --git a/process.h b/kernel/process.h similarity index 100% rename from process.h rename to kernel/process.h diff --git a/register.h b/kernel/register.h similarity index 100% rename from register.h rename to kernel/register.h diff --git a/riscv.h b/kernel/riscv.h similarity index 100% rename from riscv.h rename to kernel/riscv.h diff --git a/sbi.c b/kernel/sbi.c similarity index 100% rename from sbi.c rename to kernel/sbi.c diff --git a/sbi.h b/kernel/sbi.h similarity index 100% rename from sbi.h rename to kernel/sbi.h diff --git a/schedule.c b/kernel/schedule.c similarity index 100% rename from schedule.c rename to kernel/schedule.c diff --git a/schedule.h b/kernel/schedule.h similarity index 100% rename from schedule.h rename to kernel/schedule.h diff --git a/sleeplock.c b/kernel/sleeplock.c similarity index 100% rename from sleeplock.c rename to kernel/sleeplock.c diff --git a/sleeplock.h b/kernel/sleeplock.h similarity index 100% rename from sleeplock.h rename to kernel/sleeplock.h diff --git a/spinlock.c b/kernel/spinlock.c similarity index 100% rename from spinlock.c rename to kernel/spinlock.c diff --git a/spinlock.h b/kernel/spinlock.h similarity index 100% rename from spinlock.h rename to kernel/spinlock.h diff --git a/start.S b/kernel/start.S similarity index 100% rename from start.S rename to kernel/start.S diff --git a/stat.h b/kernel/stat.h similarity index 100% rename from stat.h rename to kernel/stat.h diff --git a/stdio.c b/kernel/stdio.c similarity index 100% rename from stdio.c rename to kernel/stdio.c diff --git a/stdio.h b/kernel/stdio.h similarity index 100% rename from stdio.h rename to kernel/stdio.h diff --git a/string.c b/kernel/string.c similarity index 100% rename from string.c rename to kernel/string.c diff --git a/string.h b/kernel/string.h similarity index 100% rename from string.h rename to kernel/string.h diff --git a/switch.S b/kernel/switch.S similarity index 100% rename from switch.S rename to kernel/switch.S diff --git a/syscall.c b/kernel/syscall.c similarity index 100% rename from syscall.c rename to kernel/syscall.c diff --git a/syscall.h b/kernel/syscall.h similarity index 100% rename from syscall.h rename to kernel/syscall.h diff --git a/syscall_arg.c b/kernel/syscall_arg.c similarity index 100% rename from syscall_arg.c rename to kernel/syscall_arg.c diff --git a/syscall_arg.h b/kernel/syscall_arg.h similarity index 100% rename from syscall_arg.h rename to kernel/syscall_arg.h diff --git a/trampoline.S b/kernel/trampoline.S similarity index 100% rename from trampoline.S rename to kernel/trampoline.S diff --git a/trap.c b/kernel/trap.c similarity index 100% rename from trap.c rename to kernel/trap.c diff --git a/trap.h b/kernel/trap.h similarity index 100% rename from trap.h rename to kernel/trap.h diff --git a/virtio.h b/kernel/virtio.h similarity index 100% rename from virtio.h rename to kernel/virtio.h diff --git a/virtio_disk.c b/kernel/virtio_disk.c similarity index 100% rename from virtio_disk.c rename to kernel/virtio_disk.c diff --git a/vm.c b/kernel/vm.c similarity index 100% rename from vm.c rename to kernel/vm.c diff --git a/vm.h b/kernel/vm.h similarity index 100% rename from vm.h rename to kernel/vm.h diff --git a/user/printf.c b/user/printf.c index 7b705f4d5554269e0a6c32d34e801d0e313c1f28..1d38456fb44702898354a8ca0ab2d737e771cf75 100644 --- a/user/printf.c +++ b/user/printf.c @@ -39,12 +39,12 @@ printint(int fd, int xx, int base, int sgn) } static void -printptr(int fd, uint64 x) { +printptr(int fd, uint64_t x) { int i; putc(fd, '0'); putc(fd, 'x'); - for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) - putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]); + for (i = 0; i < (sizeof(uint64_t) * 2); i++, x <<= 4) + putc(fd, digits[x >> (sizeof(uint64_t) * 8 - 4)]); } // Print to the given fd. Only understands %d, %x, %p, %s. @@ -67,11 +67,11 @@ vprintf(int fd, const char *fmt, va_list ap) if(c == 'd'){ printint(fd, va_arg(ap, int), 10, 1); } else if(c == 'l') { - printint(fd, va_arg(ap, uint64), 10, 0); + printint(fd, va_arg(ap, uint64_t), 10, 0); } else if(c == 'x') { printint(fd, va_arg(ap, int), 16, 0); } else if(c == 'p') { - printptr(fd, va_arg(ap, uint64)); + printptr(fd, va_arg(ap, uint64_t)); } else if(c == 's'){ s = va_arg(ap, char*); if(s == 0) diff --git a/xv6_user/Makefile b/xv6_user/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7879717b0e8a513eb8d866446bcae9b032df7520 --- /dev/null +++ b/xv6_user/Makefile @@ -0,0 +1,41 @@ +BUILD=build +FSIMG=$(ROOT)/fsimg +$(shell mkdir -p $(BUILD)) +$(shell mkdir -p $(FSIMG)) + +# SRCS=$(shell find src -name "*.c" -o -name "*.S") +SRCS=src/sh.c src/init.c src/echo.c src/kalloctest.c src/mmaptest.c src/mkdir.c src/user_test.c src/ls.c src/rawcwd.c src/cat.c \ + src/kill.c src/wc.c src/rm.c src/shutdown.c +OBJS=$(addprefix $(BUILD)/, $(addsuffix .o, $(basename $(SRCS)))) +TARGET=$(addprefix $(FSIMG)/, $(basename $(notdir $(SRCS)))) +ARCHIVE=lib/lib.a +CFLAGS+=-I$(ROOT)/$(xv6U)/include + +all: $(OBJS) $(TARGET) + +export CFLAGS CC AS ASFLAGS OBJCOPY OBJDUMP LDFLAGS LD SCRIPTS +$(ARCHIVE): lib +lib: + @echo "$(YELLOW)build user lib:$(RESET)" + @make -C lib + +# forktest has less library code linked in - needs to be small +# in order to be able to max out the proc table. +# $(FSIMG)/forktest: $(BUILD)/src/forktest.o $(ARCHIVE) +# @$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $(FSIMG)/forktest $(BUILD)/src/forktest.o $(ARCHIVE) +# @$(OBJDUMP) -S $(FSIMG)/forktest > $(BUILD)/src/forktest.asm + +include $(SCRIPTS)/rules.mk +include $(SCRIPTS)/colors.mk + + +$(FSIMG)/%: $(BUILD)/src/%.o $(ARCHIVE) + @$(LD) $(LDFLAGS) -T src/user.ld -o $@ $^ + @$(OBJDUMP) -S $@ > $(BUILD)/src/$*.asm + @$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(BUILD)/src/$*.sym + +clean: + @-rm build/* -rf + -@make -C lib clean + +.PHONY: lib clean diff --git a/xv6_user/include/stddef.h b/xv6_user/include/stddef.h new file mode 100644 index 0000000000000000000000000000000000000000..8d7513b3c41b175e8719105e30c9eece74085ae6 --- /dev/null +++ b/xv6_user/include/stddef.h @@ -0,0 +1,135 @@ +#ifndef __STDDEF_H__ +#define __STDDEF_H__ + +/* Represents true-or-false values */ +typedef int bool; + +/* Explicitly-sized versions of integer types */ +typedef char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +typedef unsigned int uint; + +#define ULONG_MAX (0xffffffffffffffffULL) +#define LONG_MAX (0x7fffffffffffffffLL) +#define INTMAX_MAX LONG_MAX +#define UINT_MAX (0xffffffffU) +#define INT_MAX (0x7fffffff) +#define UCHAR_MAX (0xffU) +#define CHAR_MAX (0x7f) + +/* * + * Pointers and addresses are 32 bits long. + * We use pointer types to represent addresses, + * uintptr_t to represent the numerical values of addresses. + * */ +#if __riscv_xlen == 64 +typedef int64 intptr_t; +typedef uint64 uintptr_t; +#elif __riscv_xlen == 32 +typedef int32_t intptr_t; +typedef uint32 uintptr_t; +#endif + +/* size_t is used for memory object sizes */ +typedef uintptr_t size_t; +typedef intptr_t ssize_t; + +typedef int pid_t; + +#define NULL ((void *)0) + +#define SIGCHLD 17 + +#define va_start(ap, last) (__builtin_va_start(ap, last)) +#define va_arg(ap, type) (__builtin_va_arg(ap, type)) +#define va_end(ap) (__builtin_va_end(ap)) +#define va_copy(d, s) (__builtin_va_copy(d, s)) +typedef __builtin_va_list va_list; + +#define O_RDONLY 0x000 +#define O_WRONLY 0x001 +#define O_RDWR 0x002 // å¯è¯»å¯å†™ +#define O_TRUNC 0x400 +//#define O_CREATE 0x200 +#define O_CREATE 0x40 +#define O_DIRECTORY 0x0200000 + +#define DIR 0x040000 +#define FILE 0x100000 + +#define AT_FDCWD -100 + +typedef struct +{ + uint64 sec; // 自 Unix 纪元起的秒数 + uint64 usec; // 微秒数 +} TimeVal; + +typedef struct +{ + uint64 dev; // 文件所在ç£ç›˜é©±åЍ噍å·ï¼Œä¸è€ƒè™‘ + uint64 ino; // inode 文件所在 inode ç¼–å· + uint32 mode; // 文件类型 + uint32 nlink; // 硬链接数é‡ï¼Œåˆå§‹ä¸º1 + uint64 pad[7]; // æ— éœ€è€ƒè™‘ï¼Œä¸ºäº†å…¼å®¹æ€§è®¾è®¡ +} Stat; + +typedef unsigned int mode_t; +typedef long int off_t; + +struct kstat { + uint64 st_dev; + uint64 st_ino; + mode_t st_mode; + uint32 st_nlink; + uint32 st_uid; + uint32 st_gid; + uint64 st_rdev; + unsigned long __pad; + off_t st_size; + uint32 st_blksize; + int __pad2; + uint64 st_blocks; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + unsigned __unused[2]; +}; + + + +struct linux_dirent64 { + uint64 d_ino; + int64 d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[]; +}; + +// for mmap +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 +#define PROT_GROWSDOWN 0X01000000 +#define PROT_GROWSUP 0X02000000 + +#define MAP_FILE 0 +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0X02 +#define MAP_FAILED ((void *) -1) + +// add +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +#endif // __STDDEF_H__ diff --git a/xv6_user/include/stdio.h b/xv6_user/include/stdio.h new file mode 100644 index 0000000000000000000000000000000000000000..487c55a42f3a946dd091b20a617e41f11bd85e34 --- /dev/null +++ b/xv6_user/include/stdio.h @@ -0,0 +1,40 @@ +#ifndef __STDIO_H__ +#define __STDIO_H__ + +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +// file type +#define T_DIR 1 // Directory +#define T_FILE 2 // File +#define T_DEVICE 3 // Device + +//#define TEST_START(x) puts(x) +#define TEST_START(x) puts("========== START ");puts(x);puts(" ==========\n"); +#define TEST_END(x) puts("========== END ");puts(x);puts(" ==========\n"); + +#define stdin STDIN +#define stdout STDOUT +#define stderr STDERR + +#define va_start(ap, last) (__builtin_va_start(ap, last)) +#define va_arg(ap, type) (__builtin_va_arg(ap, type)) +#define va_end(ap) (__builtin_va_end(ap)) +#define va_copy(d, s) (__builtin_va_copy(d, s)) + +typedef __builtin_va_list va_list; +typedef unsigned long int uintmax_t; +typedef long int intmax_t; + +int getchar(); +int putchar(int); +int puts(const char *s); +// /* modify */ +void printf(const char *fmt, ...); + +/* add */ +void fprintf(int, const char*, ...); +char* gets(char*, int max); + +#endif // __STDIO_H__ diff --git a/xv6_user/include/stdlib.h b/xv6_user/include/stdlib.h new file mode 100644 index 0000000000000000000000000000000000000000..0de7359b3bc488bc877dab3cdf385d5ac1b16313 --- /dev/null +++ b/xv6_user/include/stdlib.h @@ -0,0 +1,30 @@ +#ifndef __STDLIB_H__ +#define __STDLIB_H__ + +#include "stddef.h" + +void panic(char *); + +#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) + +#ifndef assert +#define assert(f) \ + if (!(f)) \ + panic("\n --- Assert Fatal ! ---\n") +#endif + +/* add */ +void* malloc(uint); +void free(void*); + +struct stat { + int dev; // File system's disk device + uint ino; // Inode number + short type; // Type of file + short nlink; // Number of links to file + uint64 size; // Size of file in bytes +}; + +int xv6_stat(const char*, struct stat*); + +#endif //__STDLIB_H__ diff --git a/xv6_user/include/string.h b/xv6_user/include/string.h new file mode 100644 index 0000000000000000000000000000000000000000..4d9d5cda7937577ed79b33a923bc090c41598dca --- /dev/null +++ b/xv6_user/include/string.h @@ -0,0 +1,24 @@ +#ifndef __STRING_H__ +#define __STRING_H__ + +#include "stddef.h" + +int isspace(int c); +int isdigit(int c); +int atoi(const char *s); +void *memset(void *dest, int c, size_t n); +int strcmp(const char *l, const char *r); +size_t strlen(const char *); +size_t strnlen(const char *s, size_t n); +char *strncpy(char *restrict d, const char *restrict s, size_t n); +int strncmp(const char *_l, const char *_r, size_t n); + +/* add */ +char* strchr(const char*, char c); +char* strcpy(char*, const char*); +void *memmove(void*, const void*, int); +int memcmp(const void *, const void *, uint); +void *memcpy(void *, const void *, uint); +char* strcat(char* dest, const char* src); + +#endif // __STRING_H__ diff --git a/xv6_user/include/unistd.h b/xv6_user/include/unistd.h new file mode 100644 index 0000000000000000000000000000000000000000..bc55eae9421c7cd684280e482db2c94c5e99bc45 --- /dev/null +++ b/xv6_user/include/unistd.h @@ -0,0 +1,71 @@ +#ifndef __UNISTD_H__ +#define __UNISTD_H__ + +#include "stddef.h" + +extern int __clone(int (*func)(void *), void *stack, int flags, void *arg, ...); + +int open(const char *, int); +int openat(int, const char*, int); + +ssize_t read(int, void *, size_t); +ssize_t write(int, const void *, size_t); + +int close(int); +pid_t getpid(void); +pid_t getppid(void); +int sched_yield(void); +void exit(int); +pid_t fork(void); +pid_t clone(int (*fn)(void *arg), void *arg, void *stack, size_t stack_size, unsigned long flags); +int exec(char *); +int execve(const char *, char *const [], char *const []); +int waitpid(int, int *, int); +int64 get_time(); +int sys_get_time(TimeVal *ts, int tz); // syscall ID: 169; tz è¡¨ç¤ºæ—¶åŒºï¼Œè¿™é‡Œæ— éœ€è€ƒè™‘ï¼Œå§‹ç»ˆä¸º0; 返回值:æ£ç¡®è¿”回 0,错误返回 -1。 +int times(void *mytimes); +int sleep(unsigned long long); +int set_priority(int prio); +void *mmap(void *, size_t, int, int, int, off_t); +int munmap(void *start, size_t len); +int wait(int *); +int spawn(char *file); +int mailread(void *buf, int len); +int mailwrite(int pid, void *buf, int len); + +int fstat(int fd, struct kstat *st); +int sys_linkat(int olddirfd, char *oldpath, int newdirfd, char *newpath, unsigned int flags); +int sys_unlinkat(int dirfd, char *path, unsigned int flags); +int link(char *old_path, char *new_path); +// int unlink(char *path); +int uname(void *buf); +int time(unsigned long *tloc); +int brk(void *); + +char *getcwd(char *, size_t); +int chdir(const char *); +int mkdir(const char *, mode_t); +int getdents(int fd, struct linux_dirent64 *dirp64, unsigned long len); +int pipe(int [2]); +int dup(int); +int dup2(int, int); + +/* above is the oscomp's required syscall, don't modify, add new syscall below */ +/* ================================================================================= */ + +/* modify */ +// void exit(int) __attribute__((noreturn)); +int unlink(const char *path); + +/* add */ +int mknod(const char* path, short major, short minor); +char *sbrk(int); +int kill(int); +void shutdown(); + +/* debug */ +int print_pgtable(); +int print_vma(); +void print_rawfile(int fd, int print); + +#endif // __UNISTD_H__ diff --git a/xv6_user/lib/Makefile b/xv6_user/lib/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..20173fab1757b7604113e9aeef9bb0d3a9ad70bf --- /dev/null +++ b/xv6_user/lib/Makefile @@ -0,0 +1,21 @@ +BUILD=../build/lib +# CFLAGS = -Wall -Werror -O0 -fno-omit-frame-pointer -ggdb -gdwarf-2 +CFLAGS += -Wno-error +ULIB = $(BUILD)/ulib.o $(BUILD)/printf.o $(BUILD)/umalloc.o $(BUILD)/syscall.o $(BUILD)/clone.o +OBJS=$(ULIB) +ARCHIVE=lib.a + +all: $(ARCHIVE) + +include $(SCRIPTS)/rules.mk + +clean: + @-rm lib.a -f + +### Rule (archive): objects (`*.o`) -> `ARCHIVE.a` (ar) +$(ARCHIVE): $(ULIB) + @echo + AR "->" $(shell realpath $@ --relative-to .) + @ar rcsT $@ $(ULIB) + +# test: +# $(foreach var,$(.VARIABLES),$(info $(var) = $($(var)))) \ No newline at end of file diff --git a/xv6_user/lib/clone.S b/xv6_user/lib/clone.S new file mode 100644 index 0000000000000000000000000000000000000000..db908248cdcc7a1da7d1fbd3d79d573a9d54991f --- /dev/null +++ b/xv6_user/lib/clone.S @@ -0,0 +1,34 @@ +# __clone(func, stack, flags, arg, ptid, tls, ctid) +# a0, a1, a2, a3, a4, a5, a6 + +# syscall(SYS_clone, flags, stack, ptid, tls, ctid) +# a7 a0, a1, a2, a3, a4 + +.global __clone +.type __clone, %function +__clone: + # Save func and arg to stack + addi a1, a1, -16 + sd a0, 0(a1) + sd a3, 8(a1) + + # Call SYS_clone + mv a0, a2 + mv a2, a4 + mv a3, a5 + mv a4, a6 + li a7, 220 # SYS_clone + ecall + + beqz a0, 1f + # Parent + ret + + # Child +1: ld a1, 0(sp) + ld a0, 8(sp) + jalr a1 + + # Exit + li a7, 93 # SYS_exit + ecall diff --git a/xv6_user/lib/printf.c b/xv6_user/lib/printf.c new file mode 100644 index 0000000000000000000000000000000000000000..9ce4349d87ed23b5cd8c56e5c60730ab2e7b884a --- /dev/null +++ b/xv6_user/lib/printf.c @@ -0,0 +1,137 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + +#include <stdarg.h> + +static char digits[] = "0123456789ABCDEF"; + +int getchar() +{ + char byte = 0; + read(stdin, &byte, 1); + return byte; +} + +int putchar(int c) +{ + char byte = c; + return write(stdout, &byte, 1); +} + +int puts(const char *s) +{ + int r; + //r = -(write(stdout, s, strlen(s)) < 0 || putchar('\n') < 0); + r = -(write(stdout, s, strlen(s)) < 0); + return r; +} + +static void +putc(int fd, char c) +{ + write(fd, &c, 1); +} + +static void +printint(int fd, int xx, int base, int sgn) +{ + char buf[16]; + int i, neg; + uint x; + + neg = 0; + if(sgn && xx < 0){ + neg = 1; + x = -xx; + } else { + x = xx; + } + + i = 0; + do{ + buf[i++] = digits[x % base]; + }while((x /= base) != 0); + if(neg) + buf[i++] = '-'; + + while(--i >= 0) + putc(fd, buf[i]); +} + +static void +printptr(int fd, uint64 x) { + int i; + putc(fd, '0'); + putc(fd, 'x'); + for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) + putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]); +} + +// Print to the given fd. Only understands %d, %x, %p, %s. +void +vprintf(int fd, const char *fmt, va_list ap) +{ + char *s; + int c, i, state; + + state = 0; + 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, va_arg(ap, int), 10, 1); + } else if(c == 'l') { + printint(fd, va_arg(ap, uint64), 10, 0); + } else if(c == 'x') { + printint(fd, va_arg(ap, int), 16, 0); + } else if(c == 'p') { + printptr(fd, va_arg(ap, uint64)); + } else if(c == 's'){ + s = va_arg(ap, char*); + if(s == 0) + s = "(null)"; + while(*s != 0){ + putc(fd, *s); + s++; + } + } else if(c == 'c'){ + putc(fd, va_arg(ap, uint)); + } else if(c == '%'){ + putc(fd, c); + } else { + // Unknown % sequence. Print it to draw attention. + putc(fd, '%'); + putc(fd, c); + } + state = 0; + } + } +} + +void +fprintf(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fd, fmt, ap); +} + +void +printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(1, fmt, ap); +} diff --git a/xv6_user/lib/syscall.c b/xv6_user/lib/syscall.c new file mode 100644 index 0000000000000000000000000000000000000000..c23c24c70170fe2e4698e3648cfed6615b02d512 --- /dev/null +++ b/xv6_user/lib/syscall.c @@ -0,0 +1,255 @@ +#include "stddef.h" +#include "unistd.h" + +#include "syscall.h" + +int open(const char *path, int flags) +{ + return syscall(SYS_openat, AT_FDCWD, path, flags, O_RDWR); +} + +int openat(int dirfd,const char *path, int flags) +{ + return syscall(SYS_openat, dirfd, path, flags, 0600); +} + +int close(int fd) +{ + return syscall(SYS_close, fd); +} + +ssize_t read(int fd, void *buf, size_t len) +{ + return syscall(SYS_read, fd, buf, len); +} + +ssize_t write(int fd, const void *buf, size_t len) +{ + return syscall(SYS_write, fd, buf, len); +} + +pid_t getpid(void) +{ + return syscall(SYS_getpid); +} + +pid_t getppid(void) +{ + return syscall(SYS_getppid); +} + +int sched_yield(void) +{ + return syscall(SYS_sched_yield); +} + +pid_t fork(void) +{ + return syscall(SYS_clone, SIGCHLD, 0); +} + +pid_t clone(int (*fn)(void *arg), void *arg, void *stack, size_t stack_size, unsigned long flags) +{ + if (stack) + stack += stack_size; + + return __clone(fn, stack, flags, NULL, NULL, NULL); + //return syscall(SYS_clone, fn, stack, flags, NULL, NULL, NULL); +} +void exit(int code) +{ + syscall(SYS_exit, code); +} + +int waitpid(int pid, int *code, int options) +{ + return syscall(SYS_wait4, pid, code, options, 0); +} + +int exec(char *name) +{ + return syscall(SYS_execve, name); +} + +int execve(const char *name, char *const argv[], char *const argp[]) +{ + return syscall(SYS_execve, name, argv, argp); +} + +int times(void *mytimes) +{ + return syscall(SYS_times, mytimes); +} + +int64 get_time() +{ + TimeVal time; + int err = sys_get_time(&time, 0); + if (err == 0) + { + return ((time.sec & 0xffff) * 1000 + time.usec / 1000); + } + else + { + return -1; + } +} + +int sys_get_time(TimeVal *ts, int tz) +{ + return syscall(SYS_gettimeofday, ts, tz); +} + +int time(unsigned long *tloc) +{ + return syscall(SYS_time, tloc); +} + +int sleep(unsigned long long time) +{ + TimeVal tv = {.sec = time, .usec = 0}; + if (syscall(SYS_nanosleep, &tv, &tv)) return tv.sec; + return 0; +} + +int set_priority(int prio) +{ + return syscall(SYS_setpriority, prio); +} + +void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t off) +{ + return syscall(SYS_mmap, start, len, prot, flags, fd, off); +} + +int munmap(void *start, size_t len) +{ + return syscall(SYS_munmap, start, len); +} + +int wait(int *code) +{ + return waitpid((int)-1, code, 0); +} + +int spawn(char *file) +{ + return syscall(SYS_spawn, file); +} + +int mailread(void *buf, int len) +{ + return syscall(SYS_mailread, buf, len); +} + +int mailwrite(int pid, void *buf, int len) +{ + return syscall(SYS_mailwrite, pid, buf, len); +} + +int fstat(int fd, struct kstat *st) +{ + return syscall(SYS_fstat, fd, st); +} + +int sys_linkat(int olddirfd, char *oldpath, int newdirfd, char *newpath, unsigned int flags) +{ + return syscall(SYS_linkat, olddirfd, oldpath, newdirfd, newpath, flags); +} + +int sys_unlinkat(int dirfd, char *path, unsigned int flags) +{ + return syscall(SYS_unlinkat, dirfd, path, flags); +} + +int link(char *old_path, char *new_path) +{ + return sys_linkat(AT_FDCWD, old_path, AT_FDCWD, new_path, 0); +} + +int unlink(const char *path) +{ + return sys_unlinkat(AT_FDCWD, path, 0); +} + +int uname(void *buf) +{ + return syscall(SYS_uname, buf); +} + +int brk(void *addr) +{ + return syscall(SYS_brk, addr); +} + +char *getcwd(char *buf, size_t size){ + return syscall(SYS_getcwd, buf, size); +} + +int chdir(const char *path){ + return syscall(SYS_chdir, path); +} + +int mkdir(const char *path, mode_t mode){ + return syscall(SYS_mkdirat, AT_FDCWD, path, mode); +} + +int getdents(int fd, struct linux_dirent64 *dirp64, unsigned long len){ + //return syscall(SYS_getdents64, fd, dirp64, len); + return syscall(SYS_getdents64, fd, dirp64, len); +} + +int pipe(int fd[2]){ + return syscall(SYS_pipe2, fd, 0); +} + +int dup(int fd){ + return syscall(SYS_dup, fd); +} + +int dup2(int old, int new){ + return syscall(SYS_dup3, old, new, 0); +} + +int mount(const char *special, const char *dir, const char *fstype, unsigned long flags, const void *data) +{ + return syscall(SYS_mount, special, dir, fstype, flags, data); +} + +int umount(const char *special) +{ + return syscall(SYS_umount2, special, 0); +} + +/* above is the oscomp's required syscall, don't modify, add new syscall below */ +/* ================================================================================= */ + +/* add */ +int mknod(const char* path, short major, short minor) { + return syscall(SYS_mknod, path, major, minor); +} + +char *sbrk(int increment) { + return syscall(SYS_sbrk, increment); +} + +int kill(int pid) { + return syscall(SYS_kill, pid); +} + +void shutdown() { + return syscall(SYS_shutdown); +} + +/* debug */ +int print_pgtable() { + return syscall(SYS_print_pgtable); +} + +int print_vma() { + return syscall(SYS_print_vma); +} + +void print_rawfile(int fd, int printdir) { + return syscall(SYS_print_rawfile, fd, printdir); +} \ No newline at end of file diff --git a/xv6_user/lib/syscall.h b/xv6_user/lib/syscall.h new file mode 100644 index 0000000000000000000000000000000000000000..4f653e9df399c5b0ceb03e02856d9994235480da --- /dev/null +++ b/xv6_user/lib/syscall.h @@ -0,0 +1,30 @@ +#ifndef __SYSCALL_H__ +#define __SYSCALL_H__ + +#include "syscall_arch.h" +#include "syscall_ids.h" + +#ifndef __scc +#define __scc(X) ((long)(X)) +typedef long syscall_arg_t; +#endif + +#define __syscall1(n, a) __syscall1(n, __scc(a)) +#define __syscall2(n, a, b) __syscall2(n, __scc(a), __scc(b)) +#define __syscall3(n, a, b, c) __syscall3(n, __scc(a), __scc(b), __scc(c)) +#define __syscall4(n, a, b, c, d) __syscall4(n, __scc(a), __scc(b), __scc(c), __scc(d)) +#define __syscall5(n, a, b, c, d, e) __syscall5(n, __scc(a), __scc(b), __scc(c), __scc(d), __scc(e)) +#define __syscall6(n, a, b, c, d, e, f) __syscall6(n, __scc(a), __scc(b), __scc(c), __scc(d), __scc(e), __scc(f)) + +#define __SYSCALL_NARGS_X(a, b, c, d, e, f, g, h, n, ...) n +#define __SYSCALL_NARGS(...) __SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, ) +#define __SYSCALL_CONCAT_X(a, b) a##b +#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b) +#define __SYSCALL_DISP(b, ...) \ + __SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__)) \ + (__VA_ARGS__) + +#define __syscall(...) __SYSCALL_DISP(__syscall, __VA_ARGS__) +#define syscall(...) __syscall(__VA_ARGS__) + +#endif // __SYSCALL_H diff --git a/xv6_user/lib/syscall_arch.h b/xv6_user/lib/syscall_arch.h new file mode 100644 index 0000000000000000000000000000000000000000..042d7db1d698edd5cf76c00e95988bad50428420 --- /dev/null +++ b/xv6_user/lib/syscall_arch.h @@ -0,0 +1,80 @@ +#define __SYSCALL_LL_E(x) (x) +#define __SYSCALL_LL_O(x) (x) + +#define __asm_syscall(...) \ + __asm__ __volatile__("ecall\n\t" \ + : "=r"(a0) \ + : __VA_ARGS__ \ + : "memory"); \ + return a0; + +static inline long __syscall0(long n) +{ + register long a7 __asm__("a7") = n; + register long a0 __asm__("a0"); + __asm_syscall("r"(a7)) +} + +static inline long __syscall1(long n, long a) +{ + register long a7 __asm__("a7") = n; + register long a0 __asm__("a0") = a; + __asm_syscall("r"(a7), "0"(a0)) +} + +static inline long __syscall2(long n, long a, long b) +{ + register long a7 __asm__("a7") = n; + register long a0 __asm__("a0") = a; + register long a1 __asm__("a1") = b; + __asm_syscall("r"(a7), "0"(a0), "r"(a1)) +} + +static inline long __syscall3(long n, long a, long b, long c) +{ + register long a7 __asm__("a7") = n; + register long a0 __asm__("a0") = a; + register long a1 __asm__("a1") = b; + register long a2 __asm__("a2") = c; + __asm_syscall("r"(a7), "0"(a0), "r"(a1), "r"(a2)) +} + +static inline long __syscall4(long n, long a, long b, long c, long d) +{ + register long a7 __asm__("a7") = n; + register long a0 __asm__("a0") = a; + register long a1 __asm__("a1") = b; + register long a2 __asm__("a2") = c; + register long a3 __asm__("a3") = d; + __asm_syscall("r"(a7), "0"(a0), "r"(a1), "r"(a2), "r"(a3)) +} + +static inline long __syscall5(long n, long a, long b, long c, long d, long e) +{ + register long a7 __asm__("a7") = n; + register long a0 __asm__("a0") = a; + register long a1 __asm__("a1") = b; + register long a2 __asm__("a2") = c; + register long a3 __asm__("a3") = d; + register long a4 __asm__("a4") = e; + __asm_syscall("r"(a7), "0"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4)) +} + +static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f) +{ + register long a7 __asm__("a7") = n; + register long a0 __asm__("a0") = a; + register long a1 __asm__("a1") = b; + register long a2 __asm__("a2") = c; + register long a3 __asm__("a3") = d; + register long a4 __asm__("a4") = e; + register long a5 __asm__("a5") = f; + __asm_syscall("r"(a7), "0"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5)) +} + +#define VDSO_USEFUL +/* We don't have a clock_gettime function. +#define VDSO_CGT_SYM "__vdso_clock_gettime" +#define VDSO_CGT_VER "LINUX_2.6" */ + +#define IPC_64 0 diff --git a/xv6_user/lib/syscall_ids.h b/xv6_user/lib/syscall_ids.h new file mode 100644 index 0000000000000000000000000000000000000000..914d3f8449ca88e83a721a9accddeaca61020742 --- /dev/null +++ b/xv6_user/lib/syscall_ids.h @@ -0,0 +1,291 @@ +#define SYS_io_setup 0 +#define SYS_io_destroy 1 +#define SYS_io_submit 2 +#define SYS_io_cancel 3 +#define SYS_io_getevents 4 +#define SYS_setxattr 5 +#define SYS_lsetxattr 6 +#define SYS_fsetxattr 7 +#define SYS_getxattr 8 +#define SYS_lgetxattr 9 +#define SYS_fgetxattr 10 +#define SYS_listxattr 11 +#define SYS_llistxattr 12 +#define SYS_flistxattr 13 +#define SYS_removexattr 14 +#define SYS_lremovexattr 15 +#define SYS_fremovexattr 16 +#define SYS_getcwd 17 +#define SYS_lookup_dcookie 18 +#define SYS_eventfd2 19 +#define SYS_epoll_create1 20 +#define SYS_epoll_ctl 21 +#define SYS_epoll_pwait 22 +#define SYS_dup 23 +#define SYS_dup3 24 +#define SYS_fcntl 25 +#define SYS_inotify_init1 26 +#define SYS_inotify_add_watch 27 +#define SYS_inotify_rm_watch 28 +#define SYS_ioctl 29 +#define SYS_ioprio_set 30 +#define SYS_ioprio_get 31 +#define SYS_flock 32 +#define SYS_mknodat 33 +#define SYS_mkdirat 34 +#define SYS_unlinkat 35 +#define SYS_symlinkat 36 +#define SYS_linkat 37 +#define SYS_umount2 39 +#define SYS_mount 40 +#define SYS_pivot_root 41 +#define SYS_nfsservctl 42 +#define SYS_statfs 43 +#define SYS_fstatfs 44 +#define SYS_truncate 45 +#define SYS_ftruncate 46 +#define SYS_fallocate 47 +#define SYS_faccessat 48 +#define SYS_chdir 49 +#define SYS_fchdir 50 +#define SYS_chroot 51 +#define SYS_fchmod 52 +#define SYS_fchmodat 53 +#define SYS_fchownat 54 +#define SYS_fchown 55 +#define SYS_openat 56 +#define SYS_close 57 +#define SYS_vhangup 58 +#define SYS_pipe2 59 +#define SYS_quotactl 60 +#define SYS_getdents64 61 +#define SYS_lseek 62 +#define SYS_read 63 +#define SYS_write 64 +#define SYS_readv 65 +#define SYS_writev 66 +#define SYS_pread64 67 +#define SYS_pwrite64 68 +#define SYS_preadv 69 +#define SYS_pwritev 70 +#define SYS_sendfile 71 +#define SYS_pselect6 72 +#define SYS_ppoll 73 +#define SYS_signalfd4 74 +#define SYS_vmsplice 75 +#define SYS_splice 76 +#define SYS_tee 77 +#define SYS_readlinkat 78 +#define SYS_fstatat 79 +#define SYS_fstat 80 +#define SYS_sync 81 +#define SYS_fsync 82 +#define SYS_fdatasync 83 +#define SYS_sync_file_range 84 +#define SYS_timerfd_create 85 +#define SYS_timerfd_settime 86 +#define SYS_timerfd_gettime 87 +#define SYS_utimensat 88 +#define SYS_acct 89 +#define SYS_capget 90 +#define SYS_capset 91 +#define SYS_personality 92 +#define SYS_exit 93 +#define SYS_exit_group 94 +#define SYS_waitid 95 +#define SYS_set_tid_address 96 +#define SYS_unshare 97 +#define SYS_futex 98 +#define SYS_set_robust_list 99 +#define SYS_get_robust_list 100 +#define SYS_nanosleep 101 +#define SYS_getitimer 102 +#define SYS_setitimer 103 +#define SYS_kexec_load 104 +#define SYS_init_module 105 +#define SYS_delete_module 106 +#define SYS_timer_create 107 +#define SYS_timer_gettime 108 +#define SYS_timer_getoverrun 109 +#define SYS_timer_settime 110 +#define SYS_timer_delete 111 +#define SYS_clock_settime 112 +#define SYS_clock_gettime 113 +#define SYS_clock_getres 114 +#define SYS_clock_nanosleep 115 +#define SYS_syslog 116 +#define SYS_ptrace 117 +#define SYS_sched_setparam 118 +#define SYS_sched_setscheduler 119 +#define SYS_sched_getscheduler 120 +#define SYS_sched_getparam 121 +#define SYS_sched_setaffinity 122 +#define SYS_sched_getaffinity 123 +#define SYS_sched_yield 124 +#define SYS_sched_get_priority_max 125 +#define SYS_sched_get_priority_min 126 +#define SYS_sched_rr_get_interval 127 +#define SYS_restart_syscall 128 +#define SYS_kill 129 +#define SYS_tkill 130 +#define SYS_tgkill 131 +#define SYS_sigaltstack 132 +#define SYS_rt_sigsuspend 133 +#define SYS_rt_sigaction 134 +#define SYS_rt_sigprocmask 135 +#define SYS_rt_sigpending 136 +#define SYS_rt_sigtimedwait 137 +#define SYS_rt_sigqueueinfo 138 +#define SYS_rt_sigreturn 139 +#define SYS_setpriority 140 +#define SYS_getpriority 141 +#define SYS_reboot 142 +#define SYS_setregid 143 +#define SYS_setgid 144 +#define SYS_setreuid 145 +#define SYS_setuid 146 +#define SYS_setresuid 147 +#define SYS_getresuid 148 +#define SYS_setresgid 149 +#define SYS_getresgid 150 +#define SYS_setfsuid 151 +#define SYS_setfsgid 152 +#define SYS_times 153 +#define SYS_time 1062 +#define SYS_setpgid 154 +#define SYS_getpgid 155 +#define SYS_getsid 156 +#define SYS_setsid 157 +#define SYS_getgroups 158 +#define SYS_setgroups 159 +#define SYS_uname 160 +#define SYS_sethostname 161 +#define SYS_setdomainname 162 +#define SYS_getrlimit 163 +#define SYS_setrlimit 164 +#define SYS_getrusage 165 +#define SYS_umask 166 +#define SYS_prctl 167 +#define SYS_getcpu 168 +#define SYS_gettimeofday 169 +#define SYS_settimeofday 170 +#define SYS_adjtimex 171 +#define SYS_getpid 172 +#define SYS_getppid 173 +#define SYS_getuid 174 +#define SYS_geteuid 175 +#define SYS_getgid 176 +#define SYS_getegid 177 +#define SYS_gettid 178 +#define SYS_sysinfo 179 +#define SYS_mq_open 180 +#define SYS_mq_unlink 181 +#define SYS_mq_timedsend 182 +#define SYS_mq_timedreceive 183 +#define SYS_mq_notify 184 +#define SYS_mq_getsetattr 185 +#define SYS_msgget 186 +#define SYS_msgctl 187 +#define SYS_msgrcv 188 +#define SYS_msgsnd 189 +#define SYS_semget 190 +#define SYS_semctl 191 +#define SYS_semtimedop 192 +#define SYS_semop 193 +#define SYS_shmget 194 +#define SYS_shmctl 195 +#define SYS_shmat 196 +#define SYS_shmdt 197 +#define SYS_socket 198 +#define SYS_socketpair 199 +#define SYS_bind 200 +#define SYS_listen 201 +#define SYS_accept 202 +#define SYS_connect 203 +#define SYS_getsockname 204 +#define SYS_getpeername 205 +#define SYS_sendto 206 +#define SYS_recvfrom 207 +#define SYS_setsockopt 208 +#define SYS_getsockopt 209 +#define SYS_shutdown 210 +#define SYS_sendmsg 211 +#define SYS_recvmsg 212 +#define SYS_readahead 213 +#define SYS_brk 214 +#define SYS_munmap 215 +#define SYS_mremap 216 +#define SYS_add_key 217 +#define SYS_request_key 218 +#define SYS_keyctl 219 +#define SYS_clone 220 +#define SYS_execve 221 +#define SYS_mmap 222 +#define SYS_fadvise64 223 +#define SYS_swapon 224 +#define SYS_swapoff 225 +#define SYS_mprotect 226 +#define SYS_msync 227 +#define SYS_mlock 228 +#define SYS_munlock 229 +#define SYS_mlockall 230 +#define SYS_munlockall 231 +#define SYS_mincore 232 +#define SYS_madvise 233 +#define SYS_remap_file_pages 234 +#define SYS_mbind 235 +#define SYS_get_mempolicy 236 +#define SYS_set_mempolicy 237 +#define SYS_migrate_pages 238 +#define SYS_move_pages 239 +#define SYS_rt_tgsigqueueinfo 240 +#define SYS_perf_event_open 241 +#define SYS_accept4 242 +#define SYS_recvmmsg 243 +#define SYS_arch_specific_syscall 244 +#define SYS_wait4 260 +#define SYS_prlimit64 261 +#define SYS_fanotify_init 262 +#define SYS_fanotify_mark 263 +#define SYS_name_to_handle_at 264 +#define SYS_open_by_handle_at 265 +#define SYS_clock_adjtime 266 +#define SYS_syncfs 267 +#define SYS_setns 268 +#define SYS_sendmmsg 269 +#define SYS_process_vm_readv 270 +#define SYS_process_vm_writev 271 +#define SYS_kcmp 272 +#define SYS_finit_module 273 +#define SYS_sched_setattr 274 +#define SYS_sched_getattr 275 +#define SYS_renameat2 276 +#define SYS_seccomp 277 +#define SYS_getrandom 278 +#define SYS_memfd_create 279 +#define SYS_bpf 280 +#define SYS_execveat 281 +#define SYS_userfaultfd 282 +#define SYS_membarrier 283 +#define SYS_mlock2 284 +#define SYS_copy_file_range 285 +#define SYS_preadv2 286 +#define SYS_pwritev2 287 +#define SYS_pkey_mprotect 288 +#define SYS_pkey_alloc 289 +#define SYS_pkey_free 290 +#define SYS_statx 291 +#define SYS_io_pgetevents 292 +#define SYS_rseq 293 +#define SYS_kexec_file_load 294 +#define SYS_riscv_flush_icache 244 + 15 +#define SYS_spawn 400 +#define SYS_mailread 401 +#define SYS_mailwrite 402 + +#define SYS_mknod 16 +#define SYS_sbrk 11 +#define SYS_fork 0 +#define SYS_print_pgtable 500 +#define SYS_print_vma 501 +#define SYS_print_rawfile 502 \ No newline at end of file diff --git a/xv6_user/lib/ulib.c b/xv6_user/lib/ulib.c new file mode 100644 index 0000000000000000000000000000000000000000..66678b6e476015c3952f6fc6521983046f97c2c4 --- /dev/null +++ b/xv6_user/lib/ulib.c @@ -0,0 +1,160 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + +// +// wrapper so that it's OK if main() does not call exit(). +// +void +_main() +{ + extern int main(); + main(); + exit(0); +} + +char* +strcpy(char *s, const 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; +} + +size_t strlen(const char *s) +{ + int n; + + for(n = 0; s[n]; n++) + ; + return n; +} + +void* +memset(void *dst, int c, size_t n) +{ + char *cdst = (char *) dst; + int i; + for(i = 0; i < n; i++){ + cdst[i] = c; + } + return dst; +} + +char* +strchr(const char *s, char c) +{ + for(; *s; s++) + if(*s == c) + return (char*)s; + return 0; +} + +char* +gets(char *buf, int max) +{ + int i, cc; + char c; + + for(i=0; i+1 < max; ){ + cc = read(0, &c, 1); + if(cc < 1) + break; + buf[i++] = c; + if(c == '\n' || c == '\r') + break; + } + buf[i] = '\0'; + return buf; +} + +// int +// stat(const 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, const void *vsrc, int n) +{ + char *dst; + const char *src; + + dst = vdst; + src = vsrc; + if (src > dst) { + while(n-- > 0) + *dst++ = *src++; + } else { + dst += n; + src += n; + while(n-- > 0) + *--dst = *--src; + } + return vdst; +} + +int +memcmp(const void *s1, const void *s2, uint n) +{ + const char *p1 = s1, *p2 = s2; + while (n-- > 0) { + if (*p1 != *p2) { + return *p1 - *p2; + } + p1++; + p2++; + } + return 0; +} + +void * +memcpy(void *dst, const void *src, uint n) +{ + return memmove(dst, src, n); +} + +char* strcat(char* dest, const char* src) { + char* p = dest; + while (*p) { + ++p; + } + while (*src) { + *p++ = *src++; + } + *p = '\0'; + return dest; +} diff --git a/xv6_user/lib/umalloc.c b/xv6_user/lib/umalloc.c new file mode 100644 index 0000000000000000000000000000000000000000..b88ac03782e4b2d7f609242f1f64716de6412dc7 --- /dev/null +++ b/xv6_user/lib/umalloc.c @@ -0,0 +1,93 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.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; + } +} diff --git a/xv6_user/src/cat.c b/xv6_user/src/cat.c new file mode 100644 index 0000000000000000000000000000000000000000..c6befc116bb36a71e174431edb479f7cf622fcc1 --- /dev/null +++ b/xv6_user/src/cat.c @@ -0,0 +1,45 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" + + +char buf[512]; + +void cat(int fd) +{ + int n; + + while((n = read(fd, buf, sizeof(buf))) > 0) { + if (write(1, buf, n) != n) { + fprintf(2, "cat: write error\n"); + exit(1); + } + } + printf("\n"); + if(n < 0){ + fprintf(2, "cat: read error\n"); + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + int fd, i; + + if(argc <= 1){ + cat(0); + exit(0); + } + + for(i = 1; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + fprintf(2, "cat: cannot open %s\n", argv[i]); + exit(1); + } + cat(fd); + close(fd); + } + return 0; +} diff --git a/xv6_user/src/echo.c b/xv6_user/src/echo.c new file mode 100644 index 0000000000000000000000000000000000000000..9daa3f417b66aa16f8b1eedc2dc284cff29f3aed --- /dev/null +++ b/xv6_user/src/echo.c @@ -0,0 +1,20 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" + +int main(int argc, char *argv[]) +{ + int i; + for(i = 1; i < argc; i++){ + write(1, argv[i], strlen(argv[i])); + if(i + 1 < argc){ + write(1, " ", 1); + } else { + write(1, "\n", 1); + } + } + exit(0); + return 0; +} diff --git a/xv6_user/src/grep.c b/xv6_user/src/grep.c new file mode 100644 index 0000000000000000000000000000000000000000..a4c1262b56620d4004d786f40eeac955cb6c015b --- /dev/null +++ b/xv6_user/src/grep.c @@ -0,0 +1,108 @@ +// Simple grep. Only supports ^ . * $ operators. + +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.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-1)) > 0){ + m += n; + buf[m] = '\0'; + 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(m > 0){ + m -= p - buf; + memmove(buf, p, m); + } + } +} + +int +main(int argc, char *argv[]) +{ + int fd, i; + char *pattern; + + if(argc <= 1){ + fprintf(2, "usage: grep pattern [file ...]\n"); + exit(1); + } + pattern = argv[1]; + + if(argc <= 2){ + grep(pattern, 0); + exit(0); + } + + for(i = 2; i < argc; i++){ + if((fd = open(argv[i], 0)) < 0){ + printf("grep: cannot open %s\n", argv[i]); + exit(1); + } + grep(pattern, fd); + close(fd); + } + return 0; +} + +// Regexp matcher from Kernighan & Pike, +// The Practice of Programming, Chapter 9, or +// https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + +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; +} + diff --git a/xv6_user/src/grind.c b/xv6_user/src/grind.c new file mode 100644 index 0000000000000000000000000000000000000000..b60462545ba9ef3754ca0f50131cc02e53db39f9 --- /dev/null +++ b/xv6_user/src/grind.c @@ -0,0 +1,352 @@ +// +// run random system calls in parallel forever. +// + +#define USER +#include "param.h" +#include "types.h" +#include "fs/stat.h" +#include "user.h" + +#include "fs/fcntl.h" +#include "syscall_gen/syscall_num.h" +#include "../../memlayout.h" +#include "../../riscv.h" + +// from FreeBSD. +int +do_rand(unsigned long *ctx) +{ +/* + * Compute x = (7^5 * x) mod (2^31 - 1) + * without overflowing 31 bits: + * (2^31 - 1) = 127773 * (7^5) + 2836 + * From "Random number generators: good ones are hard to find", + * Park and Miller, Communications of the ACM, vol. 31, no. 10, + * October 1988, p. 1195. + */ + long hi, lo, x; + + /* Transform to [1, 0x7ffffffe] range. */ + x = (*ctx % 0x7ffffffe) + 1; + hi = x / 127773; + lo = x % 127773; + x = 16807 * lo - 2836 * hi; + if (x < 0) + x += 0x7fffffff; + /* Transform to [0, 0x7ffffffd] range. */ + x--; + *ctx = x; + return (x); +} + +unsigned long rand_next = 1; + +int +rand(void) +{ + return (do_rand(&rand_next)); +} + +void +go(int which_child) +{ + int fd = -1; + static char buf[999]; + char *break0 = sbrk(0); + uint64 iters = 0; + + mkdir("grindir"); + if(chdir("grindir") != 0){ + printf("grind: chdir grindir failed\n"); + exit(1); + } + chdir("/"); + + while(1){ + iters++; + if((iters % 500) == 0) + write(1, which_child?"B":"A", 1); + int what = rand() % 23; + if(what == 1){ + close(open("grindir/../a", O_CREATE|O_RDWR)); + } else if(what == 2){ + close(open("grindir/../grindir/../b", O_CREATE|O_RDWR)); + } else if(what == 3){ + unlink("grindir/../a"); + } else if(what == 4){ + if(chdir("grindir") != 0){ + printf("grind: chdir grindir failed\n"); + exit(1); + } + unlink("../b"); + chdir("/"); + } else if(what == 5){ + close(fd); + fd = open("/grindir/../a", O_CREATE|O_RDWR); + } else if(what == 6){ + close(fd); + fd = open("/./grindir/./../b", O_CREATE|O_RDWR); + } else if(what == 7){ + write(fd, buf, sizeof(buf)); + } else if(what == 8){ + read(fd, buf, sizeof(buf)); + } else if(what == 9){ + mkdir("grindir/../a"); + close(open("a/../a/./a", O_CREATE|O_RDWR)); + unlink("a/a"); + } else if(what == 10){ + mkdir("/../b"); + close(open("grindir/../b/b", O_CREATE|O_RDWR)); + unlink("b/b"); + } else if(what == 11){ + unlink("b"); + link("../grindir/./../a", "../b"); + } else if(what == 12){ + unlink("../grindir/../a"); + link(".././b", "/grindir/../a"); + } else if(what == 13){ + int pid = fork(); + if(pid == 0){ + exit(0); + } else if(pid < 0){ + printf("grind: fork failed\n"); + exit(1); + } + wait(0); + } else if(what == 14){ + int pid = fork(); + if(pid == 0){ + fork(); + fork(); + exit(0); + } else if(pid < 0){ + printf("grind: fork failed\n"); + exit(1); + } + wait(0); + } else if(what == 15){ + sbrk(6011); + } else if(what == 16){ + if(sbrk(0) > break0) + sbrk(-(sbrk(0) - break0)); + } else if(what == 17){ + int pid = fork(); + if(pid == 0){ + close(open("a", O_CREATE|O_RDWR)); + exit(0); + } else if(pid < 0){ + printf("grind: fork failed\n"); + exit(1); + } + if(chdir("../grindir/..") != 0){ + printf("grind: chdir failed\n"); + exit(1); + } + kill(pid); + wait(0); + } else if(what == 18){ + int pid = fork(); + if(pid == 0){ + kill(getpid()); + exit(0); + } else if(pid < 0){ + printf("grind: fork failed\n"); + exit(1); + } + wait(0); + } else if(what == 19){ + int fds[2]; + if(pipe(fds) < 0){ + printf("grind: pipe failed\n"); + exit(1); + } + int pid = fork(); + if(pid == 0){ + fork(); + fork(); + if(write(fds[1], "x", 1) != 1) + printf("grind: pipe write failed\n"); + char c; + if(read(fds[0], &c, 1) != 1) + printf("grind: pipe read failed\n"); + exit(0); + } else if(pid < 0){ + printf("grind: fork failed\n"); + exit(1); + } + close(fds[0]); + close(fds[1]); + wait(0); + } else if(what == 20){ + int pid = fork(); + if(pid == 0){ + unlink("a"); + mkdir("a"); + chdir("a"); + unlink("../a"); + fd = open("x", O_CREATE|O_RDWR); + unlink("x"); + exit(0); + } else if(pid < 0){ + printf("grind: fork failed\n"); + exit(1); + } + wait(0); + } else if(what == 21){ + unlink("c"); + // should always succeed. check that there are free i-nodes, + // file descriptors, blocks. + int fd1 = open("c", O_CREATE|O_RDWR); + if(fd1 < 0){ + printf("grind: create c failed\n"); + exit(1); + } + if(write(fd1, "x", 1) != 1){ + printf("grind: write c failed\n"); + exit(1); + } + struct stat st; + if(fstat(fd1, &st) != 0){ + printf("grind: fstat failed\n"); + exit(1); + } + if(st.size != 1){ + printf("grind: fstat reports wrong size %d\n", (int)st.size); + exit(1); + } + if(st.ino > 200){ + printf("grind: fstat reports crazy i-number %d\n", st.ino); + exit(1); + } + close(fd1); + unlink("c"); + } else if(what == 22){ + // echo hi | cat + int aa[2], bb[2]; + if(pipe(aa) < 0){ + fprintf(2, "grind: pipe failed\n"); + exit(1); + } + if(pipe(bb) < 0){ + fprintf(2, "grind: pipe failed\n"); + exit(1); + } + int pid1 = fork(); + if(pid1 == 0){ + close(bb[0]); + close(bb[1]); + close(aa[0]); + close(1); + if(dup(aa[1]) != 1){ + fprintf(2, "grind: dup failed\n"); + exit(1); + } + close(aa[1]); + char *args[3] = { "echo", "hi", 0 }; + exec("grindir/../echo", args); + fprintf(2, "grind: echo: not found\n"); + exit(2); + } else if(pid1 < 0){ + fprintf(2, "grind: fork failed\n"); + exit(3); + } + int pid2 = fork(); + if(pid2 == 0){ + close(aa[1]); + close(bb[0]); + close(0); + if(dup(aa[0]) != 0){ + fprintf(2, "grind: dup failed\n"); + exit(4); + } + close(aa[0]); + close(1); + if(dup(bb[1]) != 1){ + fprintf(2, "grind: dup failed\n"); + exit(5); + } + close(bb[1]); + char *args[2] = { "cat", 0 }; + exec("/cat", args); + fprintf(2, "grind: cat: not found\n"); + exit(6); + } else if(pid2 < 0){ + fprintf(2, "grind: fork failed\n"); + exit(7); + } + close(aa[0]); + close(aa[1]); + close(bb[1]); + char buf[4] = { 0, 0, 0, 0 }; + read(bb[0], buf+0, 1); + read(bb[0], buf+1, 1); + read(bb[0], buf+2, 1); + close(bb[0]); + int st1, st2; + wait(&st1); + wait(&st2); + if(st1 != 0 || st2 != 0 || strcmp(buf, "hi\n") != 0){ + printf("grind: exec pipeline failed %d %d \"%s\"\n", st1, st2, buf); + exit(1); + } + } + } +} + +void +iter() +{ + unlink("a"); + unlink("b"); + + int pid1 = fork(); + if(pid1 < 0){ + printf("grind: fork failed\n"); + exit(1); + } + if(pid1 == 0){ + rand_next ^= 31; + go(0); + exit(0); + } + + int pid2 = fork(); + if(pid2 < 0){ + printf("grind: fork failed\n"); + exit(1); + } + if(pid2 == 0){ + rand_next ^= 7177; + go(1); + exit(0); + } + + int st1 = -1; + wait(&st1); + if(st1 != 0){ + kill(pid1); + kill(pid2); + } + int st2 = -1; + wait(&st2); + + exit(0); +} + +int +main() +{ + while(1){ + int pid = fork(); + if(pid == 0){ + iter(); + exit(0); + } + if(pid > 0){ + wait(0); + } + sleep(20); + rand_next += 1; + } +} diff --git a/xv6_user/src/init.c b/xv6_user/src/init.c new file mode 100644 index 0000000000000000000000000000000000000000..525f2a72843c6bae4b9d9063674fa58ad449ef62 --- /dev/null +++ b/xv6_user/src/init.c @@ -0,0 +1,72 @@ +// init: The initial user-level program +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" + +char *argv[] = { "sh", 0 }; + +#define CONSOLE 1 +int +main(void) +{ + int pid, wpid; + + // if(open("console", O_RDWR) < 0){ + // // mknod("console", CONSOLE, 0); + // open("console", O_RDWR); + // } + + if(openat(AT_FDCWD,"console.dev", O_RDWR) < 0){ + mknod("console.dev", CONSOLE,0); + openat(AT_FDCWD,"console.dev", O_RDWR); + } + + // printf("\n\tNow you come here, welcome my dear friend.\n\n"); + dup(0); // stdout + dup(0); // stderr + + printf("\n"); + printf("██╗ ██████╗ ███████╗████████╗██╗ ██╗ █████╗ ██╗ ██╗███████╗██╗ ██╗██████╗ \n"); + printf("██║ ██╔â•â•â•██╗██╔â•â•â•â•â•╚â•â•██╔â•â•â•██║ ██║██╔â•â•██╗██║ ██╔â•██╔â•â•â•â•â•██║ ██║██╔â•â•██╗\n"); + printf("██║ ██║ ██║███████╗ ██║ ██║ █╗ ██║███████║█████╔╠█████╗ ██║ ██║██████╔â•\n"); + printf("██║ ██║ ██║╚â•â•â•â•██║ ██║ ██║███╗██║██╔â•â•██║██╔â•██╗ ██╔â•â•╠██║ ██║██╔â•â•â•â• \n"); + printf("███████╗╚██████╔â•███████║ ██║ ╚███╔███╔â•██║ ██║██║ ██╗███████╗╚██████╔â•██║ \n"); + printf("╚â•â•â•â•â•â•╠╚â•â•â•â•â•╠╚â•â•â•â•â•â•╠╚â•╠╚â•â•â•╚â•â•╠╚â•╠╚â•â•╚â•╠╚â•â•╚â•â•â•â•â•â•╠╚â•â•â•â•â•╠╚â•â• \n"); + printf("\n"); + + for (;;) { + printf("init: starting sh\n"); + + pid = fork(); + if (pid < 0) { + printf("init: fork failed\n"); + exit(1); + } + if (pid == 0) { + printf("about to start sh\n"); + execve("sh",argv,0); + printf("init: exec sh failed\n"); + exit(1); + } + + + for(;;){ + // this call to wait() returns if the shell exits, + // or if a parentless process exits. + wpid = wait((int *) 0); + if (wpid == pid) { + // the shell exited; restart it. + break; + } + else if (wpid < 0){ + printf("init: wait returned an error\n"); + exit(1); + } + else { + // it was a parentless process; do nothing. + } + } + } +} diff --git a/xv6_user/src/kalloctest.c b/xv6_user/src/kalloctest.c new file mode 100644 index 0000000000000000000000000000000000000000..4b0f726cba18a146e91ee07081ccfe0ddf1fad8a --- /dev/null +++ b/xv6_user/src/kalloctest.c @@ -0,0 +1,165 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "memory/memlayout.h" +#include "memory/buddy.h" + +#define NCHILD 2 +#define N 100000 +#define SZ 4096 + +// void test1(void); +void test2(void); +void test3(void); +char buf[SZ]; + +int +main(int argc, char *argv[]) +{ +// test1(); + test2(); + test3(); + exit(0); + return 0; +} + +// int ntas(int print) +// { +// int n; +// char *c; + +// if (statistics(buf, SZ) <= 0) { +// fprintf(2, "ntas: no stats\n"); +// } +// c = strchr(buf, '='); +// n = atoi(c+2); +// if(print) +// printf("%s", buf); +// return n; +// } + +// // Test concurrent kallocs and kfrees +// void test1(void) +// { +// void *a, *a1; +// int n, m; +// printf("start test1\n"); +// m = ntas(0); +// for(int i = 0; i < NCHILD; i++){ +// int pid = fork(); +// if(pid < 0){ +// printf("fork failed"); +// exit(-1); +// } +// if(pid == 0){ +// for(i = 0; i < N; i++) { +// a = sbrk(4096); +// *(int *)(a+4) = 1; +// a1 = sbrk(-4096); +// if (a1 != a + 4096) { +// printf("wrong sbrk\n"); +// exit(-1); +// } +// } +// exit(-1); +// } +// } + +// for(int i = 0; i < NCHILD; i++){ +// wait(0); +// } +// printf("test1 results:\n"); +// n = ntas(1); +// if(n-m < 10) +// printf("test1 OK\n"); +// else +// printf("test1 FAIL\n"); +// } + +// +// countfree() from usertests.c +// +int +countfree() +{ + uint64 sz0 = (uint64)sbrk(0); + int n = 0; + + while(1){ + uint64 a = (uint64) sbrk(4096); + if(a == 0xffffffffffffffff){ + break; + } + // modify the memory to make sure it's really allocated. + *(char *)(a + 4096 - 1) = 1; + n += 1; + } + sbrk(-((uint64)sbrk(0) - sz0)); + return n; +} + +// Test stealing +void test2() { + int free0 = countfree(); + int free1; + int n = (PHYSTOP-START_MEM)/PGSIZE; + printf("start test2\n"); + printf("total free number of pages: %d (out of %d)\n", free0, n); + if(n - free0 > 1000) { + printf("test2 FAILED: cannot allocate enough memory"); + exit(-1); + } + for (int i = 0; i < 50; i++) { + free1 = countfree(); + if(i % 10 == 9) + printf("."); + if(free1 != free0) { + printf("test2 FAIL: losing pages\n"); + exit(-1); + } + } + printf("\ntest2 OK\n"); +} + +// Test concurrent kalloc/kfree and stealing +void test3(void) +{ + void *a, *a1; + printf("start test3\n"); + for(int i = 0; i < NCHILD; i++){ + int pid = fork(); + if(pid < 0){ + printf("fork failed"); + exit(-1); + } + if(pid == 0){ + if (i == 0) { + for(i = 0; i < N; i++) { + a = sbrk(4096); + if ((uint64)a == -1) { + break; + } + *(int *)(a+4) = 1; + a1 = sbrk(-4096); + if (a1 != a + 4096) { + printf("wrong sbrk\n"); + exit(-1); + } + } + printf("child done %d\n", i); + exit(0); + } else { + countfree(); + printf("child done %d\n", i); + exit(0); + } + } + } + + for(int i = 0; i < NCHILD; i++){ + wait(0); + } + printf("test3 OK\n"); +} diff --git a/xv6_user/src/kill.c b/xv6_user/src/kill.c new file mode 100644 index 0000000000000000000000000000000000000000..2929b11d22a21c4ff438d2b94db9d7ac2d50e7f9 --- /dev/null +++ b/xv6_user/src/kill.c @@ -0,0 +1,19 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" + +int +main(int argc, char **argv) +{ + int i; + + if(argc < 2){ + fprintf(2, "usage: kill pid...\n"); + exit(1); + } + for(i=1; i<argc; i++) + kill(atoi(argv[i])); + return 0; +} diff --git a/xv6_user/src/ln.c b/xv6_user/src/ln.c new file mode 100644 index 0000000000000000000000000000000000000000..737bd0435c5549927e55cffd8dae63f637d38ae6 --- /dev/null +++ b/xv6_user/src/ln.c @@ -0,0 +1,16 @@ +#define USER +#include "types.h" +#include "fs/stat.h" +#include "user.h" + +int +main(int argc, char *argv[]) +{ + if(argc != 3){ + fprintf(2, "Usage: ln old new\n"); + exit(1); + } + if(link(argv[1], argv[2]) < 0) + fprintf(2, "link %s %s: failed\n", argv[1], argv[2]); + exit(0); +} diff --git a/xv6_user/src/ls.c b/xv6_user/src/ls.c new file mode 100644 index 0000000000000000000000000000000000000000..176ba202a46375937e441fd979e2417b74894c50 --- /dev/null +++ b/xv6_user/src/ls.c @@ -0,0 +1,81 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + +#define handle_error(msg) \ + do { printf("%s\n",msg); exit(-1); } while (0) + +#define BUF_SIZE 4096 + +int main(int argc, char *argv[]) { + int fd, nread; + // char buf[BUF_SIZE]; + char *buf = (char*)malloc(BUF_SIZE); + struct linux_dirent64 *d; + int bpos; + unsigned char d_type; + + // printf("hello ls; %d\n",argc); + fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY); + if (fd == -1) { + handle_error("getdents"); + } + + for (;;) { + nread = getdents(fd, (struct linux_dirent64 *)buf, BUF_SIZE); + if (nread == -1) + handle_error("getdents"); + + if (nread == 0) + break; + + // printf("--------------- nread=%d ---------------\n", nread); + // printf("inode# file type d_reclen d_off d_name\n"); + int ctr = 0; + for (bpos = 0; bpos < nread;) { + d = (struct linux_dirent64 *)(buf + bpos); + d_type = d->d_type; +/* + printf("inode: %d ", d->d_ino); + printf("dtype: %d ", d_type); + printf("%s ", (d_type == T_FILE) ? "regular" : + (d_type == T_DIR) ? "directory" : + // (d_type == DT_FIFO) ? "FIFO" : + // (d_type == DT_SOCK) ? "socket" : + // (d_type == DT_LNK) ? "symlink" : + (d_type == T_DEVICE) ? "dev" : + // (d_type == DT_CHR) ? "char dev" : + "???"); + printf("%d %d %s \n", d->d_reclen, + d->d_off, d->d_name); +*/ + switch (d_type) { + case T_DIR: + printf("\033[34;1m%s\t\033[0m",d->d_name); + break; + case T_DEVICE: + printf("\033[38;5;214m%s\t\033[0m",d->d_name); + break; + default: + printf("%s\t",d->d_name); + break; + } + + bpos += d->d_reclen; + if ( ++ctr == 6 ) { + write(STDOUT,"\n",1); + ctr = 0; + } + + // break; + } + write(STDOUT,"\n",1); + // write(1, argv[i], strlen(argv[i])); + } + free(buf); + return 0; + exit(0); +} \ No newline at end of file diff --git a/xv6_user/src/mkdir.c b/xv6_user/src/mkdir.c new file mode 100644 index 0000000000000000000000000000000000000000..3f4edb81daeadf253ff4b5e7b53e59ba264ef410 --- /dev/null +++ b/xv6_user/src/mkdir.c @@ -0,0 +1,27 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" + +int +main(int argc, char *argv[]) +{ + int i; + + if(argc != 2){ + fprintf(2, "Usage: mkdir file\n"); + exit(1); + } + + for(i = 1; i < argc; i++){ + // if(mkdirat(AT_FDCWD, argv[i], 0666) < 0){ + if(mkdir(argv[i], 0666) < 0){ + fprintf(2, "mkdirat: %s failed to create\n", argv[i]); + break; + } + } + + exit(0); + return 0; +} diff --git a/xv6_user/src/mmaptest.c b/xv6_user/src/mmaptest.c new file mode 100644 index 0000000000000000000000000000000000000000..1c80a61d74cc5037fe15b11c764b5b02d2946f23 --- /dev/null +++ b/xv6_user/src/mmaptest.c @@ -0,0 +1,356 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "riscv.h" +#include "stdlib.h" +#include "fs/vfs/fs_macro.h" + +void mmap_test(); +void fork_test(); +char buf[BSIZE]; + + +int +main(int argc, char *argv[]) +{ + mmap_test(); + fork_test(); + printf("mmaptest: all tests succeeded\n"); + exit(0); + return 0; +} + +char *testname = "???"; + +void +err(char *why) +{ + printf("mmaptest: %s failed: %s, pid=%d\n", testname, why, getpid()); + exit(1); +} + +// +// check the content of the two mapped pages. +// +void +_v1(char *p) +{ + int i; + for (i = 0; i < PGSIZE*2; i++) { + if (i < PGSIZE + (PGSIZE/2)) { + if (p[i] != 'A') { + printf("mismatch at %d, wanted 'A', got 0x%x\n", i, p[i]); + err("v1 mismatch (1)"); + } + } else { + if (p[i] != 0) { + printf("mismatch at %d, wanted zero, got 0x%x\n", i, p[i]); + err("v1 mismatch (2)"); + } + } + } +} + +// +// create a file to be mapped, containing +// 1.5 pages of 'A' and half a page of zeros. +// +void +makefile(const char *f) +{ + int i; + int n = PGSIZE/BSIZE; + + unlink(f); + int fd = open(f, O_WRONLY | O_CREATE); + if (fd == -1) + err("open"); + memset(buf, 'A', BSIZE); + // write 1.5 page + for (i = 0; i < n + n/2; i++) { + if (write(fd, buf, BSIZE) != BSIZE) + err("write 0 makefile"); + } + if (close(fd) == -1) + err("close"); +} + +void +mmap_test(void) +{ + int fd; + // int testfd; + int i; + const char * const f = "mmap.dur"; + printf("mmap_test starting\n"); + testname = "mmap_test"; + + // + // create a file with known content, map it into memory, check that + // the mapped memory has the same bytes as originally written to the + // file. + // + makefile(f); + // struct kstat stat; + // if ((testfd = open(f, O_RDWR)) == -1) + // err("open"); + // fstat(testfd, &stat); + + // printf("kstat is %d\n", stat.st_size); + // for (i = 0; i < stat.st_size; i += (PGSIZE / 2)){ + // // char b; + // char *buf = (char *)malloc(PGSIZE / 2 + 1); + // read(testfd, buf, (PGSIZE) / 2); + // buf[PGSIZE / 2 + 1] = '\0'; + // printf("buf content is :\n%s\n", buf); + // printf("i is %d\n", i); + // } + // close(testfd); + if ((fd = open(f, O_RDONLY)) == -1) + err("open"); + + printf("test mmap f\n"); + // + // this call to mmap() asks the kernel to map the content + // of open file fd into the address space. the first + // 0 argument indicates that the kernel should choose the + // virtual address. the second argument indicates how many + // bytes to map. the third argument indicates that the + // mapped memory should be read-only. the fourth argument + // indicates that, if the process modifies the mapped memory, + // that the modifications should not be written back to + // the file nor shared with other processes mapping the + // same file (of course in this case updates are prohibited + // due to PROT_READ). the fifth argument is the file descriptor + // of the file to be mapped. the last argument is the starting + // offset in the file. + // + char *p = mmap(0, PGSIZE*2, PROT_READ, MAP_PRIVATE, fd, 0); + if (p == MAP_FAILED) + err("mmap (1)"); + _v1(p); + if (munmap(p, PGSIZE*2) == -1) + err("munmap (1)"); + + printf("test mmap f: OK\n"); + + printf("test mmap private\n"); + // should be able to map file opened read-only with private writable + // mapping + p = mmap(0, PGSIZE*2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (p == MAP_FAILED) + err("mmap (2)"); + if (close(fd) == -1) + err("close"); + _v1(p); + for (i = 0; i < PGSIZE*2; i++) + p[i] = 'Z'; + if (munmap(p, PGSIZE*2) == -1) + err("munmap (2)"); + + printf("test mmap private: OK\n"); + + printf("test mmap read-only\n"); + + // check that mmap doesn't allow read/write mapping of a + // file opened read-only. + if ((fd = open(f, O_RDONLY)) == -1) + err("open"); + p = mmap(0, PGSIZE*3, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (p != MAP_FAILED) + err("mmap call should have failed"); + if (close(fd) == -1) + err("close"); + + printf("test mmap read-only: OK\n"); + + printf("test mmap read/write\n"); + + // check that mmap does allow read/write mapping of a + // file opened read/write. + if ((fd = open(f, O_RDWR)) == -1) + err("open"); + p = mmap(0, PGSIZE*3, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + err("mmap (3)"); + if (close(fd) == -1) + err("close"); + + // printf("hit 1\n"); + // check that the mapping still works after close(fd). + _v1(p); + + // write the mapped memory. + for (i = 0; i < PGSIZE*2; i++) + p[i] = 'Z'; + // printf("hit 2\n"); + + // printf("===============================================\n"); + // if ((testfd = open(f, O_RDWR)) == -1) + // err("open"); + // fstat(testfd, &stat); + + // printf("kstat is %d\n", stat.st_size); + // for (i = 0; i < stat.st_size; i += (PGSIZE / 2)){ + // // char b; + // char *buf = (char *)malloc(PGSIZE / 2 + 1); + // read(testfd, buf, (PGSIZE) / 2); + // buf[PGSIZE / 2 + 1] = '\0'; + // printf("buf content is :\n%s\n", buf); + // printf("i is %d\n", i); + // } + // close(testfd); + // unmap just the first two of three pages of mapped memory. + if (munmap(p, PGSIZE*2) == -1) + err("munmap (3)"); + + printf("test mmap read/write: OK\n"); + + printf("test mmap dirty\n"); + + // check that the writes to the mapped memory were + // written to the file. + if ((fd = open(f, O_RDWR)) == -1) + err("open"); + // printf("=======================================\n"); + // if ((testfd = open(f, O_RDWR)) == -1) + // err("open"); + // fstat(testfd, &stat); + + // printf("kstat is %d\n", stat.st_size); + // for (i = 0; i < PGSIZE + (PGSIZE/2); i += (PGSIZE / 2)){ + // // char b; + // char *buf = (char *)malloc(PGSIZE / 2 + 1); + // read(testfd, buf, (PGSIZE) / 2); + // buf[PGSIZE / 2 + 1] = '\0'; + // printf("buf content is :\n%s\n", buf); + // printf("i is %d\n", i); + // } + // close(testfd); + + for (i = 0; i < PGSIZE + (PGSIZE/2); i++){ + char b; + if (read(fd, &b, 1) != 1) + err("read (1)"); + if (b != 'Z') { + printf("%d\n", i); + err("file does not contain modifications"); + } + } + if (close(fd) == -1) + err("close"); + + printf("test mmap dirty: OK\n"); + + printf("test not-mapped unmap\n"); + + // unmap the rest of the mapped memory. + if (munmap(p+PGSIZE*2, PGSIZE) == -1) + err("munmap (4)"); + + printf("test not-mapped unmap: OK\n"); + + printf("test mmap two files\n"); + print_pgtable(); + + // + // mmap two files at the same time. + // + int fd1; + if((fd1 = open("mmap1", O_RDWR|O_CREATE)) < 0) + err("open mmap1"); + if(write(fd1, "12345", 5) != 5) + err("write mmap1"); + print_rawfile(fd1, 0); + printf("gap\n"); + char *p1 = mmap(0, PGSIZE, PROT_READ, MAP_PRIVATE, fd1, 0); + if(p1 == MAP_FAILED) + err("mmap mmap1"); + close(fd1); + unlink("mmap1"); + + // print_pgtable(); + int fd2; + if((fd2 = open("mmap2", O_RDWR|O_CREATE)) < 0) + err("open mmap2"); + if(write(fd2, "67890", 5) != 5) + err("write mmap2"); + char *p2 = mmap(0, PGSIZE, PROT_READ, MAP_PRIVATE, fd2, 0); + if(p2 == MAP_FAILED) + err("mmap mmap2"); + close(fd2); + unlink("mmap2"); + + if(memcmp(p1, "12345", 5) != 0) + err("mmap1 mismatch"); + if(memcmp(p2, "67890", 5) != 0) + err("mmap2 mismatch"); + + munmap(p1, PGSIZE); + if(memcmp(p2, "67890", 5) != 0) + err("mmap2 mismatch (2)"); + munmap(p2, PGSIZE); + + printf("test mmap two files: OK\n"); + + printf("mmap_test: ALL OK\n"); +} + +// +// mmap a file, then fork. +// check that the child sees the mapped file. +// +void +fork_test(void) +{ + int fd; + int pid; + const char * const f = "mmap.dur"; + + printf("fork_test starting\n"); + testname = "fork_test"; + + // mmap the file twice. + makefile(f); + if ((fd = open(f, O_RDONLY)) == -1) + err("open"); + unlink(f); + // print_vma(); + char *p1 = mmap(0, PGSIZE*2, PROT_READ, MAP_SHARED, fd, 0); + if (p1 == MAP_FAILED) + err("mmap (4)"); + char *p2 = mmap(0, PGSIZE*2, PROT_READ, MAP_SHARED, fd, 0); + if (p2 == MAP_FAILED) + err("mmap (5)"); + // print_vma(); + + // read just 2nd page. + if(*(p1+PGSIZE) != 'A') + err("fork mismatch (1)"); + + if((pid = fork()) < 0) + err("fork"); + if (pid == 0) { + _v1(p1); + munmap(p1, PGSIZE); // just the first page + // print_vma(); + exit(0); // tell the parent that the mapping looks OK. + } + + int status = -1; + wait(&status); + + if(status != 0){ + printf("fork_test failed\n"); + exit(1); + } + + // check that the parent's mappings are still there. + _v1(p1); + _v1(p2); + + printf("fork_test OK\n"); +} + diff --git a/xv6_user/src/rawcwd.c b/xv6_user/src/rawcwd.c new file mode 100644 index 0000000000000000000000000000000000000000..fe819c6889e4cf049a75d6775c2180e7c1027dc3 --- /dev/null +++ b/xv6_user/src/rawcwd.c @@ -0,0 +1,17 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" + +/* print the root content */ +int main() { + int fd; + fd = open(".", O_DIRECTORY); + if (fd < 0) { + return 0; + } else { + print_rawfile(fd, 1); + } + return 0; +} \ No newline at end of file diff --git a/xv6_user/src/rm.c b/xv6_user/src/rm.c new file mode 100644 index 0000000000000000000000000000000000000000..4e2a8e3aad05d51733ee9473793425a2566fae4b --- /dev/null +++ b/xv6_user/src/rm.c @@ -0,0 +1,26 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + + +int main(int argc, char *argv[]) { + int i; + + if (argc < 2) { + fprintf(2, "Usage: rm files...\n"); + exit(1); + } + + for (i = 1; i < argc; i++) { + if (unlink(argv[i]) < 0) { + fprintf(2, "rm: %s failed to delete\n", argv[i]); + break; + } + } + + exit(0); + return 0; +} diff --git a/xv6_user/src/sh.c b/xv6_user/src/sh.c new file mode 100644 index 0000000000000000000000000000000000000000..4e118afab2653a38a7cfd5ad4a03f3d8c51cf57a --- /dev/null +++ b/xv6_user/src/sh.c @@ -0,0 +1,517 @@ +// Shell. + +#define USER +#include "stddef.h" +#include "stdio.h" +#include "unistd.h" +#include "string.h" +#include "stdlib.h" +// #include "fs/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*); +void runcmd(struct cmd*); + +// 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(1); + + switch(cmd->type){ + default: + panic("runcmd"); + + case EXEC: + ecmd = (struct execcmd*)cmd; + if(ecmd->argv[0] == 0) + exit(1); + + char cmdpath[20] = "/"; + strcat(cmdpath,ecmd->argv[0]); + execve(cmdpath, ecmd->argv, NULL); + fprintf(2, "exec %s failed\n", ecmd->argv[0]); + break; + + case REDIR: + rcmd = (struct redircmd*)cmd; + close(rcmd->fd); + if(openat(AT_FDCWD, rcmd->file, rcmd->mode) < 0){ + fprintf(2, "openat %s failed\n", rcmd->file); + exit(1); + } + runcmd(rcmd->cmd); + break; + + case LIST: + lcmd = (struct listcmd*)cmd; + if(fork1() == 0) + runcmd(lcmd->left); + wait(0); + 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(0); + wait(0); + break; + + case BACK: + bcmd = (struct backcmd*)cmd; + if(fork1() == 0) + runcmd(bcmd->cmd); + break; + } + exit(0); +} + +char cwd[128]; +int +getcmd(char *buf, int nbuf) +{ + printf("%s",cwd); + write(2, "> ", 2); + memset(buf, 0, nbuf); + gets(buf, nbuf); + if(buf[0] == 0) // EOF + return -1; + return 0; +} + +int +main(void) +{ + + printf("We are in sh\n\n"); + // printf("██╗ ██████╗ ███████╗████████╗██╗ ██╗ █████╗ ██╗ ██╗███████╗██╗ ██╗██████╗ \n"); + // printf("██║ ██╔â•â•â•██╗██╔â•â•â•â•â•╚â•â•██╔â•â•â•██║ ██║██╔â•â•██╗██║ ██╔â•██╔â•â•â•â•â•██║ ██║██╔â•â•██╗\n"); + // printf("██║ ██║ ██║███████╗ ██║ ██║ █╗ ██║███████║█████╔╠█████╗ ██║ ██║██████╔â•\n"); + // printf("██║ ██║ ██║╚â•â•â•â•██║ ██║ ██║███╗██║██╔â•â•██║██╔â•██╗ ██╔â•â•╠██║ ██║██╔â•â•â•â• \n"); + // printf("███████╗╚██████╔â•███████║ ██║ ╚███╔███╔â•██║ ██║██║ ██╗███████╗╚██████╔â•██║ \n"); + // printf("╚â•â•â•â•â•â•╠╚â•â•â•â•â•╠╚â•â•â•â•â•â•╠╚â•╠╚â•â•â•╚â•â•╠╚â•╠╚â•â•╚â•╠╚â•â•╚â•â•â•â•â•â•╠╚â•â•â•â•â•╠╚â•â• \n"); + // printf("\n"); + static char buf[100]; + int fd; + getcwd(cwd,128); + // Ensure that three file descriptors are open. + while((fd = openat(AT_FDCWD,"console.dev", 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] == ' '){ + // Chdir must be called by the parent, not the child. + buf[strlen(buf)-1] = 0; // chop \n + if(chdir(buf+3) < 0){ + fprintf(2, "cannot cd %s\n", buf+3); + } + else { + getcwd(cwd,128); + } + continue; + } + if(fork1() == 0) + runcmd(parsecmd(buf)); + wait(0); + } + exit(0); + return 0; +} + +void +panic(char *s) +{ + fprintf(2, "%s\n", s); + exit(1); +} + +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){ + fprintf(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|O_TRUNC, 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; +} diff --git a/xv6_user/src/shutdown.c b/xv6_user/src/shutdown.c new file mode 100644 index 0000000000000000000000000000000000000000..a68725ad147dc67e12fcbf6c1ea5d94bcef10c33 --- /dev/null +++ b/xv6_user/src/shutdown.c @@ -0,0 +1,10 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" + +int main() { + shutdown(); + return 0; +} \ No newline at end of file diff --git a/xv6_user/src/user.ld b/xv6_user/src/user.ld new file mode 100644 index 0000000000000000000000000000000000000000..0ca922b66220c86c96336abc2b091c42c3197a30 --- /dev/null +++ b/xv6_user/src/user.ld @@ -0,0 +1,36 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY( _main ) + + +SECTIONS +{ + . = 0x0; + + .text : { + *(.text .text.*) + } + + .rodata : { + . = ALIGN(16); + *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ + . = ALIGN(16); + *(.rodata .rodata.*) + . = ALIGN(0x1000); + } + + .data : { + . = ALIGN(16); + *(.sdata .sdata.*) /* do not need to distinguish this from .data */ + . = ALIGN(16); + *(.data .data.*) + } + + .bss : { + . = ALIGN(16); + *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ + . = ALIGN(16); + *(.bss .bss.*) + } + + PROVIDE(end = .); +} diff --git a/xv6_user/src/user_test.c b/xv6_user/src/user_test.c new file mode 100644 index 0000000000000000000000000000000000000000..8db19f5f7fe38fd9dcbc49ad0cfa1c7dd31c0840 --- /dev/null +++ b/xv6_user/src/user_test.c @@ -0,0 +1,2802 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + +// Test that fork fails gracefully. +void forktest(void) { + int n, pid; + printf("==========fork test==========\n"); + int N = 100; + for (n = 0; n < 100; n++) { + pid = fork(); + if (pid < 0) + break; + if (pid == 0) + exit(0); + } + + if (n == N) { + printf("fork claimed to work N times!\n"); + exit(1); + } + + for (; n > 0; n--) { + if (wait(0) < 0) { + printf("wait stopped early\n"); + exit(1); + } + } + + if (wait(0) != -1) { + printf("wait got too many\n"); + exit(1); + } + + printf("fork test OK\n"); +} + +// concurrent forks to try to expose locking bugs. +void forkfork() { + int N = 2; + printf("==========forkfork test==========\n"); + for (int i = 0; i < N; i++) { + int pid = fork(); + if (pid < 0) { + printf("fork failed"); + exit(1); + } + if (pid == 0) { + for (int j = 0; j < 100; j++) { + int pid1 = fork(); + if (pid1 < 0) { + exit(1); + } + if (pid1 == 0) { + exit(0); + } + wait(0); + } + exit(0); + } + } + + int xstatus; + for (int i = 0; i < N; i++) { + wait(&xstatus); + if (xstatus != 0) { + printf("fork in child failed"); + exit(1); + } + } + printf("forkfork test OK\n"); +} + +// simple file system tests (open syscall) +void opentest() { + int fd; + printf("==========open test==========\n"); + + fd = open("echo", 0); + if (fd < 0) { + printf("open echo failed!\n"); + exit(1); + } + close(fd); + fd = open("doesnotexist", 0); + if (fd >= 0) { + printf("open doesnotexist succeeded!\n"); + exit(1); + } + printf("open test OK\n"); +} + +#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) +// try to find any races between exit and wait +void exitwait() { + int i, pid; + printf("==========exitwait test==========\n"); + + for (i = 0; i < 1000; i++) { + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid) { + int exit_state; + if (wait(&exit_state) != pid) { + printf("wait wrong pid\n"); + exit(1); + } + if ((i<<8) != exit_state) { + printf("%d %d %d\n", i, exit_state, WEXITSTATUS(pid)); + printf("wait wrong exit status\n"); + exit(1); + } + } else { + exit(i); + } + } + printf("exitwait test OK\n"); +} + +// what if two children exit() at the same time? +void twochildren() { + printf("==========twochildren test==========\n"); + for (int i = 0; i < 1000; i++) { + int pid1 = fork(); + if (pid1 < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid1 == 0) { + exit(0); + } else { + int pid2 = fork(); + if (pid2 < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid2 == 0) { + exit(0); + } else { + wait(0); + wait(0); + } + } + } + printf("twochildren test OK\n"); +} + +// try to find races in the reparenting +// code that handles a parent exiting +// when it still has live children. +void reparent() { + printf("==========reparent test==========\n"); + int master_pid = getpid(); + for (int i = 0; i < 200; i++) { + int pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid) { + if (wait(0) != pid) { + printf("wait wrong pid\n"); + exit(1); + } + } else { + int pid2 = fork(); + if (pid2 < 0) { + kill(master_pid); + exit(1); + } + exit(0); + } + } + printf("reparent test OK\n"); +} + +// regression test. does reparent() violate the parent-then-child +// locking order when giving away a child to init, so that exit() +// deadlocks against init's wait()? also used to trigger a "panic: +// release" due to exit() releasing a different p->parent->lock than +// it acquired. +void reparent2() { + printf("==========reparent2 test==========\n"); + for (int i = 0; i < 400; i++) { + int pid1 = fork(); + if (pid1 < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid1 == 0) { + fork(); + fork(); + exit(0); + } + wait(0); + } + printf("reparent2 test OK\n"); +} + +#define MAXOPBLOCKS 10 +#define BSIZE 512 +#define BUFSZ ((MAXOPBLOCKS + 2) * BSIZE) +char buf[BUFSZ]; + +// write syscall +void writetest() { + int fd; + int N = 100; + int SZ = 10; + printf("==========write test==========\n"); + fd = open("small", O_CREATE | O_RDWR); + if (fd < 0) { + printf("error: create small failed!\n"); + exit(1); + } + + for (int i = 0; i < N; i++) { + if (write(fd, "aaaaaaaaaa", SZ) != SZ) { + printf("error: write aa %d new file failed\n", i); + exit(1); + } + if (write(fd, "bbbbbbbbbb", SZ) != SZ) { + printf("error: write bb %d new file failed\n", i); + exit(1); + } + } + close(fd); + fd = open("small", O_RDONLY); + if (fd < 0) { + printf("error: open small failed!\n"); + exit(1); + } + int ret = read(fd, buf, N * SZ * 2); + if (ret != N * SZ * 2) { + printf("read failed\n"); + exit(1); + } + close(fd); + printf("write test OK\n"); +} + +void writebig() { + int i, fd; + int n; + int MAXFILE = 2048; + printf("==========writebig test==========\n"); + fd = open("big", O_CREATE | O_RDWR); + if (fd < 0) { + printf("error: creat big failed!\n"); + exit(1); + } + + for (i = 0; i < MAXFILE; i++) { + ((int *)buf)[0] = i; + if (write(fd, buf, BSIZE) != BSIZE) { + printf("error: write big file failed\n", i); + exit(1); + } + } + close(fd); + + fd = open("big", O_RDONLY); + if (fd < 0) { + printf("error: open big failed!\n"); + exit(1); + } + n = 0; + for (;;) { + i = read(fd, buf, BSIZE); + if (i == 0) { + if (n == MAXFILE - 1) { + printf("read only %d blocks from big", n); + exit(1); + } + break; + } else if (i != BSIZE) { + printf("read failed %d\n", i); + exit(1); + } + if (((int *)buf)[0] != n) { + printf("read content of block %d is %d\n", n, ((int *)buf)[0]); + exit(1); + } + n++; + } + close(fd); + if (unlink("big") < 0) { + printf("unlink big failed\n"); + exit(1); + } + printf("writebig test OK\n"); +} + +void openiputtest() { + int pid, xstatus; + printf("==========openiputtest test==========\n"); + + if (mkdir("oidir", 0666) < 0) { + printf("mkdir oidir failed\n"); + exit(1); + } + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + int fd = open("oidir", O_RDWR); + if (fd >= 0) { + printf("open directory for write succeeded\n"); + exit(1); + } + exit(0); + } + + sleep(1); + if (unlink("oidir") != 0) { + printf("unlink failed\n"); + exit(1); + } + + wait(&xstatus); + printf("openiputtest test OK\n"); +} + +void truncate1() { + char buf[32]; + printf("==========truncate1 test==========\n"); + + unlink("truncfile"); + int fd1 = open("truncfile", O_CREATE | O_WRONLY | O_TRUNC); + + write(fd1, "abcd", 4); + close(fd1); + + int fd2 = open("truncfile", O_RDONLY); + + memset(buf, 0, sizeof(buf)); + int n = read(fd2, buf, sizeof(buf)); + printf("%s\n", buf); + if (n != 4) { + printf("read %d bytes, wanted 4\n", n); + exit(1); + } + + fd1 = open("truncfile", O_WRONLY | O_TRUNC); + int fd3 = open("truncfile", O_RDONLY); + + memset(buf, 0, sizeof(buf)); + n = read(fd3, buf, sizeof(buf)); + printf("%s\n", buf); + + if (n != 0) { + printf("aaa fd3=%d\n", fd3); + printf("read %d bytes, wanted 0\n", n); + exit(1); + } + + // memset(buf, 0, sizeof(buf)); + n = read(fd2, buf, sizeof(buf)); + // printf("%s\n",buf); + if (n != 0) { + printf("bbb fd2=%d\n", fd2); + printf("read %d bytes, wanted 0\n", n); + exit(1); + } + + write(fd1, "abcdef", 6); + + memset(buf, 0, sizeof(buf)); + n = read(fd3, buf, sizeof(buf)); + printf("%s\n", buf); + + if (n != 6) { + printf("read %d bytes, wanted 6\n", n); + exit(1); + } + + memset(buf, 0, sizeof(buf)); + n = read(fd2, buf, sizeof(buf)); + printf("%s\n", buf); + + if (n != 2) { + printf("read %d bytes, wanted 2\n", n); + exit(1); + } + + unlink("truncfile"); + + close(fd1); + close(fd2); + close(fd3); + + printf("truncate1 test OK\n"); +} + +void forkforkfork() { + printf("==========forkforkfork test==========\n"); + unlink("stopforking"); + int pid = fork(); + if (pid < 0) { + printf("fork failed"); + exit(1); + } + if (pid == 0) { + while (1) { + int fd = open("stopforking", 0); + if (fd >= 0) { + exit(0); + } + if (fork() < 0) { + close(open("stopforking", O_CREATE | O_RDWR)); + } + } + exit(0); + } + + sleep(2); // two seconds + close(open("stopforking", O_CREATE | O_RDWR)); + wait(0); + sleep(1); // one second + + printf("forkforkfork test OK\n"); +} + +void preempt() { + printf("==========preempt test==========\n"); + int pid1, pid2, pid3; + int pfds[2]; + + pid1 = fork(); + if (pid1 < 0) { + printf("fork failed"); + exit(1); + } + if (pid1 == 0) + for (;;) + ; + + pid2 = fork(); + if (pid2 < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid2 == 0) + for (;;) + ; + + pipe(pfds); + pid3 = fork(); + if (pid3 < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid3 == 0) { + close(pfds[0]); + if (write(pfds[1], "x", 1) != 1) + printf("preempt write error"); + close(pfds[1]); + for (;;) + ; + } + + close(pfds[1]); + if (read(pfds[0], buf, sizeof(buf)) != 1) { + printf("preempt read error"); + return; + } + close(pfds[0]); + printf("kill... "); + kill(pid1); + kill(pid2); + kill(pid3); + printf("wait... "); + wait(0); + wait(0); + wait(0); + + printf("preempt test OK\n"); +} + +// test if child is killed (status = -1) +void killstatus() { + printf("==========killstatus test==========\n"); + int xst; + + for (int i = 0; i < 4; i++) { + int pid1 = fork(); + if (pid1 < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid1 == 0) { + while (1) { + getpid(); + } + exit(0); + } + sleep(1); + kill(pid1); + wait(&xst); + if (xst != (-1 << 8)) { + printf("%d %d\n", xst, i); + printf("status should be -1<<8\n"); + exit(1); + } + printf("%d\n", i); + } + printf("killstatus test OK\n"); +} + +// what if you pass ridiculous pointers to system calls +// that read user memory with copyin? +void copyin() { + printf("==========copyin test==========\n"); + // print_pgtable(); + uint64 addrs[] = {0x80000000LL, 0xffffffffffffffff}; + + for (int ai = 0; ai < 2; ai++) { + uint64 addr = addrs[ai]; + + int fd = open("copyin1", O_CREATE | O_WRONLY); + if (fd < 0) { + printf("open(copyin1) failed\n"); + exit(1); + } + int n = write(fd, (void *)addr, 8192); + if (n >= 0) { + printf("write(fd, %p, 8192) returned %d, not -1\n", addr, n); + exit(1); + } + close(fd); + unlink("copyin1"); + + n = write(1, (char *)addr, 8192); + if (n > 0) { + printf("write(1, %p, 8192) returned %d, not -1 or 0\n", addr, n); + exit(1); + } + + int fds[2]; + if (pipe(fds) < 0) { + printf("pipe() failed\n"); + exit(1); + } + n = write(fds[1], (char *)addr, 8192); + if (n > 0) { + printf("write(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n); + exit(1); + } + close(fds[0]); + close(fds[1]); + } + printf("copyin test OK\n"); +} + +// what if you pass ridiculous pointers to system calls +// that write user memory with copyout? +void copyout() { + printf("==========copyout test==========\n"); + uint64 addrs[] = {0x80000000LL, 0xffffffffffffffff}; + + for (int ai = 0; ai < 2; ai++) { + uint64 addr = addrs[ai]; + + int fd = open("README.md", 0); + if (fd < 0) { + printf("open(README.md) failed\n"); + exit(1); + } + int n = read(fd, (void *)addr, 8192); + if (n > 0) { + printf("read(fd, %p, 8192) returned %d, not -1 or 0\n", addr, n); + exit(1); + } + close(fd); + + int fds[2]; + if (pipe(fds) < 0) { + printf("pipe() failed\n"); + exit(1); + } + n = write(fds[1], "x", 1); + if (n != 1) { + printf("pipe write failed\n"); + exit(1); + } + n = read(fds[0], (void *)addr, 8192); + if (n > 0) { + printf("read(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n); + exit(1); + } + close(fds[0]); + close(fds[1]); + } + printf("copyout test OK\n"); +} + +// what if you pass ridiculous string pointers to system calls? +void copyinstr1() { + printf("==========copyinstr1 test==========\n"); + uint64 addrs[] = {0x80000000LL, 0xffffffffffffffff}; + + for (int ai = 0; ai < 2; ai++) { + uint64 addr = addrs[ai]; + + int fd = open((char *)addr, O_CREATE | O_WRONLY); + if (fd >= 0) { + printf("open(%p) returned %d, not -1\n", addr, fd); + exit(1); + } + } + printf("copyinstr1 test OK\n"); +} + +// See if the kernel refuses to read/write user memory that the +// application doesn't have anymore, because it returned it. +void rwsbrk() { + int fd, n; + printf("==========rwsbrk test==========\n"); + + uint64 a = (uint64)sbrk(8192); + + if (a == 0xffffffffffffffffLL) { + printf("sbrk(rwsbrk) failed\n"); + exit(1); + } + + if ((uint64)sbrk(-8192) == 0xffffffffffffffffLL) { + printf("sbrk(rwsbrk) shrink failed\n"); + exit(1); + } + + fd = open("rwsbrk", O_CREATE | O_WRONLY); + if (fd < 0) { + printf("open(rwsbrk) failed\n"); + exit(1); + } + n = write(fd, (void *)(a + 4096), 1024); + if (n >= 0) { + printf("write(fd, %p, 1024) returned %d, not -1\n", a + 4096, n); + exit(1); + } + close(fd); + unlink("rwsbrk"); + + fd = open("README.md", O_RDONLY); + if (fd < 0) { + printf("open(rwsbrk) failed\n"); + exit(1); + } + n = read(fd, (void *)(a + 4096), 10); + if (n >= 0) { + printf("read(fd, %p, 10) returned %d, not -1\n", a + 4096, n); + exit(1); + } + close(fd); + printf("rwsbrk test OK\n"); +} + +// write to an open FD whose file has just been truncated. +// this causes a write at an offset beyond the end of the file. +// such writes fail on xv6 (unlike POSIX) but at least +// they don't crash. +void truncate2() { + printf("==========truncate2 test==========\n"); + unlink("truncfile"); + + int fd1 = open("truncfile", O_CREATE | O_TRUNC | O_WRONLY); + write(fd1, "abcd", 4); + + int fd2 = open("truncfile", O_TRUNC | O_WRONLY); + + int n = write(fd1, "x", 1); + if (n != -1) { + printf("write returned %d, expected -1\n", n); + exit(1); + } + + unlink("truncfile"); + close(fd1); + close(fd2); + printf("truncate2 test OK\n"); +} + +void truncate3() { + printf("==========truncate3 test==========\n"); + int pid, xstatus; + + close(open("truncfile", O_CREATE | O_TRUNC | O_WRONLY)); + + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + + if (pid == 0) { + for (int i = 0; i < 100; i++) { + char buf[32]; + int fd = open("truncfile", O_WRONLY); + if (fd < 0) { + printf("open failed\n"); + exit(1); + } + int n = write(fd, "1234567890", 10); + if (n != 10) { + printf("write got %d, expected 10\n", n); + exit(1); + } + close(fd); + fd = open("truncfile", O_RDONLY); + read(fd, buf, sizeof(buf)); + close(fd); + } + exit(0); + } + + for (int i = 0; i < 150; i++) { + int fd = open("truncfile", O_CREATE | O_WRONLY | O_TRUNC); + if (fd < 0) { + printf("open failed\n"); + exit(1); + } + int n = write(fd, "xxx", 3); + if (n != 3) { + printf("write got %d, expected 3\n", n); + exit(1); + } + close(fd); + } + + wait(&xstatus); + unlink("truncfile"); + printf("truncate3 test OK\n"); +} + +// does chdir() call iput(p->cwd) in a transaction? +void iputtest() { + printf("==========iput test==========\n"); + if (mkdir("iputdir", 0666) < 0) { + printf("mkdir failed\n"); + exit(1); + } + if (chdir("iputdir") < 0) { + printf("chdir iputdir failed\n"); + exit(1); + } + if (unlink("../iputdir") < 0) { + printf("unlink ../iputdir failed\n"); + exit(1); + } + if (chdir("/") < 0) { + printf("chdir / failed\n"); + exit(1); + } + printf("iput test OK\n"); +} + +// does exit() call iput(p->cwd) in a transaction? +void exitiputtest() { + int pid, xstatus; + printf("==========exitiput test==========\n"); + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + if (mkdir("iputdir", 0666) < 0) { + printf("mkdir failed\n"); + exit(1); + } + printf("???\n"); + if (chdir("iputdir") < 0) { + printf("child chdir failed\n"); + exit(1); + } + if (unlink("../iputdir") < 0) { + printf("unlink ../iputdir failed\n"); + exit(1); + } + exit(0); + } + wait(&xstatus); + printf("exitiput test OK\n"); +} + +// many creates, followed by unlink test +void createtest() { + printf("==========createtest test==========\n"); + int i, fd; + int N = 50; + char name[3]; + name[0] = 'a'; + name[2] = '\0'; + for (i = 0; i < N; i++) { + name[1] = '0' + i; + fd = open(name, O_CREATE | O_RDWR); + close(fd); + } + name[0] = 'a'; + name[2] = '\0'; + for (i = 0; i < N; i++) { + name[1] = '0' + i; + unlink(name); + } + printf("createtest test OK\n"); +} + +// if process size was somewhat more than a page boundary, and then +// shrunk to be somewhat less than that page boundary, can the kernel +// still copyin() from addresses in the last page? +void sbrklast() { + printf("==========sbrklast test==========\n"); + uint64 top = (uint64)sbrk(0); + if ((top % 4096) != 0) + sbrk(4096 - (top % 4096)); + sbrk(4096); + sbrk(10); + sbrk(-20); + top = (uint64)sbrk(0); + char *p = (char *)(top - 64); + p[0] = 'x'; + p[1] = '\0'; + int fd = open(p, O_RDWR | O_CREATE); + write(fd, p, 1); + close(fd); + fd = open(p, O_RDWR); + p[0] = '\0'; + read(fd, p, 1); + if (p[0] != 'x') + exit(1); + printf("sbrklast test OK\n"); +} + +// // Section with tests that take a fair bit of time +// // directory that uses indirect blocks +// void bigdir() { +// printf("==========bigdir test==========\n"); +// enum { N = 500 }; +// int i, fd; +// char name[10]; + +// unlink("bd"); + +// fd = open("bd", O_CREATE); +// if (fd < 0) { +// printf("bigdir create failed\n"); +// exit(1); +// } +// close(fd); + +// for (i = 0; i < N; i++) { +// name[0] = 'x'; +// name[1] = '0' + (i / 64); +// name[2] = '0' + (i % 64); +// name[3] = '\0'; +// if (link("bd", name) != 0) { +// printf("bigdir link(bd, %s) failed\n", name); +// exit(1); +// } +// } + +// unlink("bd"); +// for (i = 0; i < N; i++) { +// name[0] = 'x'; +// name[1] = '0' + (i / 64); +// name[2] = '0' + (i % 64); +// name[3] = '\0'; +// if (unlink(name) != 0) { +// printf("bigdir unlink failed\n"); +// exit(1); +// } +// } +// printf("bigdir test OK\n"); +// } + +void dirtest() { + printf("==========dir test=========\n"); + if (mkdir("dir0", 0666) < 0) { + printf("mkdir failed\n"); + exit(1); + } + + if (chdir("dir0") < 0) { + printf("chdir dir0 failed\n"); + exit(1); + } + + if (chdir("..") < 0) { + printf("chdir .. failed\n"); + exit(1); + } + + if (unlink("dir0") < 0) { + printf("unlink dir0 failed\n"); + exit(1); + } + printf("dir test OK\n"); +} + +void execvetest() { + printf("==========execve test=========\n"); + int fd, xstatus, pid; + char *echoargv[] = {"echo", "OK", 0}; + char buf[3]; + + unlink("echo-ok"); + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + close(1); + fd = open("echo-ok", O_CREATE | O_WRONLY); + if (fd < 0) { + printf("create failed\n"); + exit(1); + } + if (fd != 1) { + printf("wrong fd\n"); + exit(1); + } + if (execve("echo", echoargv, NULL) < 0) { + printf("execve echo failed\n"); + exit(1); + } + // won't get to here + } + + if (wait(&xstatus) != pid) { + printf("wait failed!\n"); + } + if (xstatus != 0) + exit(xstatus); + + fd = open("echo-ok", O_RDONLY); + if (fd < 0) { + printf("open failed\n"); + exit(1); + } + if (read(fd, buf, 2) != 2) { + printf("read failed\n"); + exit(1); + } + unlink("echo-ok"); + if (buf[0] == 'O' && buf[1] == 'K') { + printf("execve test OK\n"); + } else { + printf("wrong output\n"); + exit(1); + } +} + +void uvmfree() { + printf("==========uvmfree test=========\n"); + enum { BIG = 100 * 1024 * 1024 }; + char *a, *p; + uint64 amt; + + int pid = fork(); + if (pid == 0) { + // oldbrk = sbrk(0); + + // can one grow address space to something big? + a = sbrk(0); + amt = BIG - (uint64)a; + p = sbrk(amt); + if (p != a) { + printf("sbrk test failed to grow big address space; enough phys mem?\n"); + exit(1); + } + printf("child done\n"); + exit(0); + } else if (pid > 0) { + wait((int *)0); + printf("uvmfree test OK\n"); + } else { + printf("fork failed"); + exit(1); + } +} + +// simple fork and pipe read/write +void pipe1() { + printf("==========pipe1 test=========\n"); + int fds[2], pid, xstatus; + int seq, i, n, cc, total; + enum { N = 5, + SZ = 1033 }; + + if (pipe(fds) != 0) { + printf("pipe() failed\n"); + exit(1); + } + pid = fork(); + seq = 0; + if (pid == 0) { + close(fds[0]); + for (n = 0; n < N; n++) { + for (i = 0; i < SZ; i++) + buf[i] = seq++; + if (write(fds[1], buf, SZ) != SZ) { + printf("pipe1 oops 1\n"); + exit(1); + } + } + exit(0); + } 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("pipe1 oops 2\n"); + return; + } + } + total += n; + cc = cc * 2; + if (cc > sizeof(buf)) + cc = sizeof(buf); + } + if (total != N * SZ) { + printf("pipe1 oops 3 total %d\n", total); + exit(1); + } + close(fds[0]); + wait(&xstatus); + printf("pipe1 test OK\n"); + } else { + printf("fork() failed\n"); + exit(1); + } +} + +// allocate all mem, free it, and allocate again +void mem() { + printf("==========mem test=========\n"); + void *m1, *m2; + int pid; + + 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("couldn't allocate mem?!!\n"); + exit(1); + } + free(m1); + exit(0); + } else { + int xstatus; + wait(&xstatus); + if (xstatus == -1) { + // probably page fault, so might be lazy lab, + // so OK. + exit(0); + } + printf("mem test OK\n"); + } +} + +// two processes write to the same file descriptor +// is the offset shared? does inode locking work? +void sharedfd() { + printf("==========sharedfd test=========\n"); + int fd, pid, i, n, nc, np; + enum { N = 1000, + SZ = 10 }; + char buf[SZ]; + + unlink("sharedfd"); + fd = open("sharedfd", O_CREATE | O_RDWR); + if (fd < 0) { + printf("cannot open sharedfd for writing"); + exit(1); + } + pid = fork(); + memset(buf, pid == 0 ? 'c' : 'p', sizeof(buf)); + for (i = 0; i < N; i++) { + if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { + printf("write sharedfd failed\n"); + exit(1); + } + } + if (pid == 0) { + exit(0); + } else { + int xstatus; + wait(&xstatus); + if (xstatus != 0) + exit(xstatus); + } + + close(fd); + fd = open("sharedfd", 0); + if (fd < 0) { + printf("cannot open sharedfd for reading\n"); + exit(1); + } + 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 == N * SZ && np == N * SZ) { + printf("sharedfd test OK\n"); + } else { + printf("nc/np test fails\n"); + exit(1); + } +} + +// four processes write different files at the same +// time, to test block allocation. +void fourfiles() { + printf("==========fourfiles test=========\n"); + int fd, pid, i, n, pi; + int total; + int j; + char *names[] = {"f0", "f1", "f2", "f3"}; + char *fname; + enum { N = 12, + NCHILD = 4, + SZ = 500 }; + + for (pi = 0; pi < NCHILD; pi++) { + fname = names[pi]; + unlink(fname); + + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + + if (pid == 0) { + fd = open(fname, O_CREATE | O_RDWR); + if (fd < 0) { + printf("create failed\n"); + exit(1); + } + + memset(buf, '0' + pi, SZ); + for (i = 0; i < N; i++) { + if ((n = write(fd, buf, SZ)) != SZ) { + printf("write failed %d\n", n); + exit(1); + } + } + exit(0); + } + } + + int xstatus; + for (pi = 0; pi < NCHILD; pi++) { + wait(&xstatus); + if (xstatus != 0) + exit(xstatus); + } + + for (i = 0; i < NCHILD; i++) { + fname = names[i]; + fd = open(fname, 0); + total = 0; + while ((n = read(fd, buf, sizeof(buf))) > 0) { + for (j = 0; j < n; j++) { + if (buf[j] != '0' + i) { + printf("wrong char\n"); + exit(1); + } + } + total += n; + } + close(fd); + if (total != N * SZ) { + printf("wrong length %d\n", total); + exit(1); + } + unlink(fname); + } + printf("fourfiles test OK\n"); +} + +// four processes create and delete different files in same directory +void createdelete() { + printf("==========createdelete test=========\n"); + enum { N = 20, + NCHILD = 4 }; + int pid, i, fd, pi; + char name[32]; + + for (pi = 0; pi < NCHILD; pi++) { + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + + if (pid == 0) { + name[0] = 'p' + pi; + name[2] = '\0'; + for (i = 0; i < N; i++) { + name[1] = '0' + i; + fd = open(name, O_CREATE | O_RDWR); + if (fd < 0) { + printf("create failed\n"); + exit(1); + } + close(fd); + if (i > 0 && (i % 2) == 0) { + name[1] = '0' + (i / 2); + if (unlink(name) < 0) { + printf("unlink failed\n"); + exit(1); + } + } + } + exit(0); + } + } + int xstatus; + for (pi = 0; pi < NCHILD; pi++) { + wait(&xstatus); + if (xstatus != 0) + exit(1); + } + + name[0] = name[1] = name[2] = 0; + for (i = 0; i < N; i++) { + for (pi = 0; pi < NCHILD; pi++) { + name[0] = 'p' + pi; + name[1] = '0' + i; + fd = open(name, 0); + if ((i == 0 || i >= N / 2) && fd < 0) { + printf("oops createdelete %s didn't exist\n", name); + exit(1); + } else if ((i >= 1 && i < N / 2) && fd >= 0) { + printf("oops createdelete %s did exist\n", name); + exit(1); + } + if (fd >= 0) + close(fd); + } + } + + for (i = 0; i < N; i++) { + for (pi = 0; pi < NCHILD; pi++) { + name[0] = 'p' + i; + name[1] = '0' + i; + unlink(name); + } + } + printf("createdelete test OK\n"); +} + +// can I unlink a file and still read it? +void unlinkread() { + printf("==========unlinkread test=========\n"); + enum { SZ = 5 }; + int fd; + + fd = open("unlinkread", O_CREATE | O_RDWR); + if (fd < 0) { + printf("create unlinkread failed\n"); + exit(1); + } + write(fd, "hello", SZ); + close(fd); + + fd = open("unlinkread", O_RDWR); + if (fd < 0) { + printf("open unlinkread failed\n"); + exit(1); + } + if (unlink("unlinkread") != 0) { + printf("unlink unlinkread failed\n"); + exit(1); + } + + int fd1 = open("unlinkread", O_CREATE | O_RDWR); + write(fd1, "yyy", 3); + close(fd1); + + if (read(fd, buf, sizeof(buf)) != SZ) { + printf("unlinkread read failed"); + exit(1); + } + + // printf("%s\n", buf); + if (buf[0] != 'h') { + printf("unlinkread wrong data\n"); + exit(1); + } + + // if (write(fd, buf, 10) != 10) { + // printf("unlinkread write failed\n"); + // exit(1); + // } + // close(fd); + // unlink("unlinkread"); + printf("unlinkread test OK\n"); +} + +// test writes that are larger than the log. +void bigwrite() { + int fd, sz; + printf("==========bigwrite test=========\n"); + unlink("bigwrite"); + for (sz = 499; sz < (MAXOPBLOCKS + 2) * BSIZE; sz += 471) { + fd = open("bigwrite", O_CREATE | O_RDWR); + if (fd < 0) { + printf("cannot create bigwrite\n"); + exit(1); + } + int i; + for (i = 0; i < 2; i++) { + int cc = write(fd, buf, sz); + if (cc != sz) { + printf("write(%d) ret %d\n", sz, cc); + exit(1); + } + } + close(fd); + unlink("bigwrite"); + } + printf("bigwrite test OK\n"); +} + +void bigfile() { + printf("==========bigfile test=========\n"); + enum { N = 20, + SZ = 600 }; + int fd, i, total, cc; + + unlink("bigfile.dat"); + fd = open("bigfile.dat", O_CREATE | O_RDWR); + if (fd < 0) { + printf("cannot create bigfile"); + exit(1); + } + for (i = 0; i < N; i++) { + memset(buf, i, SZ); + if (write(fd, buf, SZ) != SZ) { + printf("write bigfile failed\n"); + exit(1); + } + } + close(fd); + + fd = open("bigfile.dat", 0); + if (fd < 0) { + printf("cannot open bigfile\n"); + exit(1); + } + total = 0; + for (i = 0;; i++) { + cc = read(fd, buf, SZ / 2); + if (cc < 0) { + printf("read bigfile failed\n"); + exit(1); + } + if (cc == 0) + break; + if (cc != SZ / 2) { + printf("short read bigfile\n"); + exit(1); + } + if (buf[0] != i / 2 || buf[SZ / 2 - 1] != i / 2) { + printf("read bigfile wrong data\n"); + exit(1); + } + total += cc; + } + close(fd); + if (total != N * SZ) { + printf("read bigfile wrong total\n"); + exit(1); + } + unlink("bigfile.dat"); + printf("bigfile test OK\n"); +} + +void fourteen() { + printf("==========fourteen test=========\n"); + + // DIRSIZ is 14. + + if (mkdir("12345678901234", 0666) != 0) { + printf("mkdir 12345678901234 failed\n"); + exit(1); + } + if (mkdir("12345678901234/123456789012345", 0666) != 0) { + printf("mkdir 12345678901234/123456789012345 failed\n"); + exit(1); + } + int fd = open("123456789012345/123456789012345/123456789012345", O_CREATE); + if (fd < 0) { + printf("create 123456789012345/123456789012345/123456789012345 failed\n"); + exit(1); + } + + // close(fd); + // fd = open("12345678901234/12345678901234/12345678901234", 0); + // if (fd < 0) { + // printf("open 12345678901234/12345678901234/12345678901234 failed\n"); + // exit(1); + // } + // close(fd); + + // if (mkdir("12345678901234/12345678901234", 0666) == 0) { + // printf("mkdir 12345678901234/12345678901234 succeeded!\n"); + // exit(1); + // } + // if (mkdir("123456789012345/12345678901234", 0666) == 0) { + // printf("mkdir 12345678901234/123456789012345 succeeded!\n"); + // exit(1); + // } + + // // clean up + // unlink("123456789012345/12345678901234"); + // unlink("12345678901234/12345678901234"); + // unlink("12345678901234/12345678901234/12345678901234"); + // unlink("123456789012345/123456789012345/123456789012345"); + // unlink("12345678901234/123456789012345"); + // unlink("12345678901234"); + + printf("fourteen test OK\n"); +} + +void rmdot() { + printf("==========rmdom test=========\n"); + if (mkdir("dots", 066) != 0) { + printf("mkdir dots failed\n"); + exit(1); + } + if (chdir("dots") != 0) { + printf("chdir dots failed\n"); + exit(1); + } + if (unlink(".") == 0) { + printf("rm . worked!\n"); + exit(1); + } + if (unlink("..") == 0) { + printf("rm .. worked!\n"); + exit(1); + } + if (chdir("/") != 0) { + printf("chdir / failed\n"); + exit(1); + } + if (unlink("dots/.") == 0) { + printf("unlink dots/. worked!\n"); + exit(1); + } + if (unlink("dots/..") == 0) { + printf("unlink dots/.. worked!\n"); + exit(1); + } + if (unlink("dots") != 0) { + printf("unlink dots failed!\n"); + exit(1); + } + printf("rmdot test OK\n"); +} + +// regression test. test whether exec() leaks memory if one of the +// arguments is invalid. the test passes if the kernel doesn't panic. +void badarg() { + printf("==========badarg test=========\n"); + for (int i = 0; i < 50000; i++) { + char *argv[2]; + argv[0] = (char *)0xffffffff; + argv[1] = 0; + execve("echo", argv, NULL); + } + printf("badarg test OK\n"); +} + +// does sbrk handle signed int32 wrap-around with +// negative arguments? +void sbrk8000() { + printf("==========sbrk8000 test=========\n"); + sbrk(0x80000004); + volatile char *top = sbrk(0); + *(top - 1) = *(top - 1) + 1; + printf("sbrk8000 test OK\n"); +} + +// check that writes to text segment fault +void textwrite() { + printf("==========textwrite test=========\n"); + int pid; + int xstatus; + + pid = fork(); + if (pid == 0) { + volatile int *addr = (int *)0; + *addr = 10; + exit(1); + } else if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + wait(&xstatus); + if (xstatus == -1 << 8) // kernel killed child? + { + printf("textwrite test OK\n"); + } else + exit(xstatus); +} + +void outofinodes() { + printf("==========outofinodes test=========\n"); + int nzz = 32 * 32; + for (int i = 0; i < nzz; i++) { + char name[32]; + name[0] = 'z'; + name[1] = 'z'; + name[2] = '0' + (i / 32); + name[3] = '0' + (i % 32); + name[4] = '\0'; + unlink(name); + int fd = open(name, O_CREATE | O_RDWR | O_TRUNC); + if (fd < 0) { + // failure is eventually expected. + break; + } + close(fd); + } + + for (int i = 0; i < nzz; i++) { + char name[32]; + name[0] = 'z'; + name[1] = 'z'; + name[2] = '0' + (i / 32); + name[3] = '0' + (i % 32); + name[4] = '\0'; + unlink(name); + } + printf("outofinodes test OK\n"); +} + +// concurrent writes to try to provoke deadlock in the virtio disk +// driver. +void manywrites() { + printf("==========manywrites test=========\n"); + int nchildren = 4; + int howmany = 30; // increase to look for deadlock + + for (int ci = 0; ci < nchildren; ci++) { + int pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + + if (pid == 0) { + char name[3]; + name[0] = 'b'; + name[1] = 'a' + ci; + name[2] = '\0'; + unlink(name); + + for (int iters = 0; iters < howmany; iters++) { + for (int i = 0; i < ci + 1; i++) { + int fd = open(name, O_CREATE | O_RDWR); + if (fd < 0) { + printf("cannot create %s\n", name); + exit(1); + } + int sz = sizeof(buf); + int cc = write(fd, buf, sz); + if (cc != sz) { + printf("write(%d) ret %d\n", sz, cc); + exit(1); + } + close(fd); + } + unlink(name); + } + + unlink(name); + exit(0); + } + } + + for (int ci = 0; ci < nchildren; ci++) { + int st = 0; + wait(&st); + if (st != 0) + exit(st); + } + printf("manywrites test OK\n"); +} + +// regression test. does write() with an invalid buffer pointer cause +// a block to be allocated for a file that is then not freed when the +// file is deleted? if the kernel has this bug, it will panic: balloc: +// out of blocks. assumed_free may need to be raised to be more than +// the number of free blocks. this test takes a long time. +void badwrite() { + printf("==========badwrite test=========\n"); + int assumed_free = 600; + + unlink("junk"); + for (int i = 0; i < assumed_free; i++) { + int fd = open("junk", O_CREATE | O_WRONLY); + if (fd < 0) { + printf("open junk failed\n"); + exit(1); + } + write(fd, (char *)0xffffffffffL, 1); + close(fd); + unlink("junk"); + } + + int fd = open("junk", O_CREATE | O_WRONLY); + if (fd < 0) { + printf("open junk failed\n"); + exit(1); + } + if (write(fd, "x", 1) != 1) { + printf("write failed\n"); + exit(1); + } + close(fd); + unlink("junk"); + + printf("badwrite test OK\n"); +} + +// test the execveout() code that cleans up if it runs out +// of memory. it's really a test that such a condition +// doesn't cause a panic. +void execveout() { + printf("==========execveout test=========\n"); + for (int avail = 0; avail < 15; avail++) { + int pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } else if (pid == 0) { + // allocate all of memory. + while (1) { + uint64 a = (uint64)sbrk(4096); + if (a == 0xffffffffffffffffLL) + break; + *(char *)(a + 4096 - 1) = 1; + } + + // free a few pages, in order to let exec() make some + // progress. + for (int i = 0; i < avail; i++) + sbrk(-4096); + + close(1); + char *args[] = {"echo", "x", 0}; + execve("echo", args, NULL); + exit(0); + } else { + wait((int *)0); + } + } + printf("execveout test OK\n"); +} + +// can the kernel tolerate running out of disk space? +void diskfull() { + printf("==========diskfull test=========\n"); + int fi; + int done = 0; + int MAXFILE = 2000; + + unlink("diskfulldir"); + + for (fi = 0; done == 0; fi++) { + char name[32]; + name[0] = 'b'; + name[1] = 'i'; + name[2] = 'g'; + name[3] = '0' + fi; + name[4] = '\0'; + unlink(name); + int fd = open(name, O_CREATE | O_RDWR | O_TRUNC); + if (fd < 0) { + // oops, ran out of inodes before running out of blocks. + printf("could not create file %s\n", name); + done = 1; + break; + } + for (int i = 0; i < MAXFILE; i++) { + // char buf[BSIZE]; + if (write(fd, "hello", 5) != 5) { + done = 1; + close(fd); + break; + } + } + done = 1; + close(fd); + } + printf("disk is full\n"); + // now that there are no free blocks, test that dirlink() + // merely fails (doesn't panic) if it can't extend + // directory content. one of these file creations + // is expected to fail. + // int nzz = 128; + // for (int i = 0; i < nzz; i++) { + // char name[32]; + // name[0] = 'z'; + // name[1] = 'z'; + // name[2] = '0' + (i / 32); + // name[3] = '0' + (i % 32); + // name[4] = '\0'; + // unlink(name); + // int fd = open(name, O_CREATE | O_RDWR | O_TRUNC); + // if (fd < 0) + // break; + // close(fd); + // } + + // // this mkdir() is expected to fail. + // if (mkdir("diskfulldir", 0666) == 0) + // printf("mkdir(diskfulldir) unexpectedly succeeded!\n"); + + // unlink("diskfulldir"); + + // for (int i = 0; i < nzz; i++) { + // char name[32]; + // name[0] = 'z'; + // name[1] = 'z'; + // name[2] = '0' + (i / 32); + // name[3] = '0' + (i % 32); + // name[4] = '\0'; + // unlink(name); + // } + + // for (int i = 0; i < fi; i++) { + // char name[32]; + // name[0] = 'b'; + // name[1] = 'i'; + // name[2] = 'g'; + // name[3] = '0' + i; + // name[4] = '\0'; + // unlink(name); + // } + printf("diskfull test OK\n"); +} + +void sbrkbasic() { + printf("==========sbrkbasic test=========\n"); + enum { TOOMUCH = 1024 * 1024 * 1024 }; + int i, pid, xstatus; + char *c, *a, *b; + + // does sbrk() return the expected failure value? + pid = fork(); + if (pid < 0) { + printf("fork failed in sbrkbasic\n"); + exit(1); + } + if (pid == 0) { + a = sbrk(TOOMUCH); + if (a == (char *)0xffffffffffffffffL) { + // it's OK if this fails. + exit(0); + } + + for (b = a; b < a + TOOMUCH; b += 4096) { + *b = 99; + } + + // we should not get here! either sbrk(TOOMUCH) + // should have failed, or (with lazy allocation) + // a pagefault should have killed this process. + exit(1); + } + + wait(&xstatus); + if (xstatus == 1) { + printf("too much memory allocated!\n"); + exit(1); + } + + // can one sbrk() less than a page? + a = sbrk(0); + for (i = 0; i < 5000; i++) { + b = sbrk(1); + if (b != a) { + printf("sbrk test failed %d %x %x\n", i, a, b); + exit(1); + } + *b = 1; + a = b + 1; + } + pid = fork(); + if (pid < 0) { + printf("sbrk test fork failed\n"); + exit(1); + } + c = sbrk(1); + c = sbrk(1); + if (c != a + 1) { + printf("sbrk test failed post-fork\n"); + exit(1); + } + if (pid == 0) + exit(0); + wait(&xstatus); + printf("sbrkbasic test OK\n"); +} + +#define PGSIZE 4096 // bytes per page +void sbrkmuch() { + printf("==========sbrkmuch test=========\n"); + enum { BIG = 100 * 1024 * 1024 }; + char *c, *oldbrk, *a, *lastaddr, *p; + uint64 amt; + + oldbrk = sbrk(0); + + // can one grow address space to something big? + a = sbrk(0); + amt = BIG - (uint64)a; + p = sbrk(amt); + if (p != a) { + printf("sbrk test failed to grow big address space; enough phys mem?\n"); + exit(1); + } + + // touch each page to make sure it exists. + char *eee = sbrk(0); + // printf("eee is %x", eee); + // print_pgtable(); + // int cnt = 0; + for (char *pp = a; pp < eee; pp += 4096) { + // printf("%x\n", pp); + *pp = 1; + } + + lastaddr = (char *)(BIG - 1); + *lastaddr = 99; + + // can one de-allocate? + a = sbrk(0); + // printf("a is %x %d\n", a, a); + // print_pgtable(); + c = sbrk(-PGSIZE); + // print_pgtable(); + if (c == (char *)0xffffffffffffffffL) { + printf("sbrk could not deallocate\n"); + exit(1); + } + c = sbrk(0); + // printf("c is %x %d\n", c, c); + if (c != a - PGSIZE) { + printf("sbrk deallocation produced wrong address, a %x c %x\n", a, c); + exit(1); + } + + // can one re-allocate that page? + a = sbrk(0); + c = sbrk(PGSIZE); + if (c != a || sbrk(0) != a + PGSIZE) { + printf("sbrk re-allocation failed, a %x c %x\n", a, c); + exit(1); + } + if (*lastaddr == 99) { + // should be zero + printf("sbrk de-allocation didn't really deallocate\n"); + exit(1); + } + + a = sbrk(0); + c = sbrk(-(sbrk(0) - oldbrk)); + if (c != a) { + printf("sbrk downsize failed, a %x c %x\n", a, c); + exit(1); + } + printf("sbrkmuch test OK\n"); +} + +#define KERNBASE 0x80200000L +// can we read the kernel's memory? +void kernmem() { + printf("==========kernmem test=========\n"); + char *a; + int pid; + + for (a = (char *)(KERNBASE); a < (char *)(KERNBASE + 2000000); a += 50000) { + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + printf("oops could read %x = %x\n", a, *a); + exit(1); + } + int xstatus; + wait(&xstatus); + printf("%d\n", xstatus); + if (xstatus != -1 << 8) // did kernel kill child? + exit(1); + } + printf("kernmem test OK\n"); +} + +#define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) +// user code should not be able to write to addresses above MAXVA. +void MAXVAplus() { + printf("==========MAXVAplus test=========\n"); + volatile uint64 a = MAXVA; + for (; a != 0; a <<= 1) { + int pid; + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + *(char *)a = 99; + printf("oops wrote %x\n", a); + exit(1); + } + int xstatus; + wait(&xstatus); + if (xstatus != -1 << 8) // did kernel kill child? + exit(1); + } + printf("MAXVAplus test OK\n"); +} + +// if we run the system out of memory, does it clean up the last +// failed allocation? +void sbrkfail() { + printf("==========sbrkfail test=========\n"); + enum { BIG = 100 * 1024 * 1024 }; + int i, xstatus; + int fds[2]; + char scratch; + char *c, *a; + int pids[10]; + int pid; + + if (pipe(fds) != 0) { + printf("pipe() failed\n"); + exit(1); + } + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { + if ((pids[i] = fork()) == 0) { + // allocate a lot of memory + sbrk(BIG - (uint64)sbrk(0)); + write(fds[1], "x", 1); + // sit around until killed + for (;;) sleep(1000); + } + if (pids[i] != -1) + read(fds[0], &scratch, 1); + } + + // if those failed allocations freed up the pages they did allocate, + // we'll be able to allocate here + c = sbrk(PGSIZE); + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { + if (pids[i] == -1) + continue; + kill(pids[i]); + wait(0); + } + if (c == (char *)0xffffffffffffffffL) { + printf("failed sbrk leaked memory\n"); + exit(1); + } + + // test running fork with the above allocated page + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + // allocate a lot of memory. + // this should produce a page fault, + // and thus not complete. + a = sbrk(0); + sbrk(10 * BIG); + int n = 0; + for (i = 0; i < 10 * BIG; i += PGSIZE) { + n += *(a + i); + } + // print n so the compiler doesn't optimize away + // the for loop. + printf("allocate a lot of memory succeeded %d\n", n); + exit(1); + } + wait(&xstatus); + if (xstatus != -1 << 8 && xstatus != 2) + exit(1); + printf("sbrkfail test OK\n"); +} + +// test reads/writes from/to allocated memory +void sbrkarg() { + printf("==========sbrkarg test=========\n"); + char *a; + int fd, n; + + a = sbrk(PGSIZE); + fd = open("sbrk", O_CREATE | O_WRONLY); + unlink("sbrk"); + if (fd < 0) { + printf("open sbrk failed\n"); + exit(1); + } + if ((n = write(fd, a, PGSIZE)) < 0) { + printf("write sbrk failed\n"); + exit(1); + } + close(fd); + + // test writes to allocated memory + a = sbrk(PGSIZE); + if (pipe((int *)a) != 0) { + printf("pipe() failed\n"); + exit(1); + } + printf("sbrkarg test OK\n"); +} + +// does uninitialized data start out zero? +char uninit[10000]; +void bsstest() { + printf("==========bsstest test=========\n"); + int i; + for (i = 0; i < sizeof(uninit); i++) { + if (uninit[i] != '\0') { + printf("bss test failed\n"); + exit(1); + } + } + printf("bsstest 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? +#define MAXARG 32 // max exec arguments +void bigargtest() { + printf("==========bigargtest test=========\n"); + int pid, fd, xstatus; + + 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; + execve("echo", args, NULL); + fd = open("bigarg-ok", O_CREATE); + close(fd); + exit(0); + } else if (pid < 0) { + printf("bigargtest: fork failed\n"); + exit(1); + } + + wait(&xstatus); + if (xstatus != 0) + exit(xstatus); + fd = open("bigarg-ok", 0); + if (fd < 0) { + printf("bigarg test failed!\n"); + exit(1); + } + close(fd); + printf("bigargtest test OK\n"); +} + +// 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("==========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("writing %s\n", name); + int fd = open(name, O_CREATE | O_RDWR); + if (fd < 0) { + printf("open %s failed\n", name); + break; + } + int total = 0; + while (1) { + int cc = write(fd, buf, BSIZE); + if (cc < BSIZE) + break; + total += cc; + fsblocks++; + } + printf("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("fsfull test OK\n"); +} + +void argptest() { + printf("==========argptest test=========\n"); + int fd; + fd = open("init", O_RDONLY); + if (fd < 0) { + printf("open failed\n"); + exit(1); + } + read(fd, sbrk(0) - 1, -1); + close(fd); + printf("argptest test OK\n"); +} + +static inline uint64 +r_sp() { + uint64 x; + asm volatile("mv %0, sp" + : "=r"(x)); + return x; +} + +// check that there's an invalid page beneath +// the user stack, to catch stack overflow. +void stacktest() { + printf("==========stacktest test=========\n"); + int pid; + int xstatus; + + pid = fork(); + if (pid == 0) { + char *sp = (char *)r_sp(); + sp -= PGSIZE; + // the *sp should cause a trap. + printf("stacktest: read below stack %p\n", *sp); + exit(1); + } else if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + wait(&xstatus); + if (xstatus == -1 << 8) // kernel killed child? + { + printf("stacktest test OK\n"); + } else + exit(xstatus); +} + +// regression test. copyin(), copyout(), and copyinstr() used to cast +// the virtual page address to uint, which (with certain wild system +// call arguments) resulted in a kernel page faults. +void *big = (void *)0xeaeb0b5b00002f5e; +void pgbug() { + printf("==========pgbug test=========\n"); + char *argv[1]; + argv[0] = 0; + execve(big, argv, NULL); + pipe(big); + + printf("pgbug test OK\n"); +} + +// regression test. does the kernel panic if a process sbrk()s its +// size to be less than a page, or zero, or reduces the break by an +// amount too small to cause a page to be freed? +void sbrkbugs() { + printf("==========sbrkbugs test=========\n"); + int pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + int sz = (uint64)sbrk(0); + // free all user memory; there used to be a bug that + // would not adjust p->sz correctly in this case, + // causing exit() to panic. + sbrk(-sz); + // user page fault here. + exit(0); + } + wait(0); + + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + int sz = (uint64)sbrk(0); + // set the break to somewhere in the very first + // page; there used to be a bug that would incorrectly + // free the first page. + sbrk(-(sz - 3500)); + exit(0); + } + wait(0); + + pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(1); + } + if (pid == 0) { + // set the break in the middle of a page. + sbrk((10 * 4096 + 2048) - (uint64)sbrk(0)); + + // reduce the break a bit, but not enough to + // cause a page to be freed. this used to cause + // a panic. + sbrk(-10); + + exit(0); + } + wait(0); + + printf("sbrkbugs test OK\n"); +} + +// cow tests +// allocate more than half of physical memory, +// then fork. this will fail in the default +// kernel, which does not support copy-on-write. +#define PHYSTOP (0x80000000L + 130 * 1024 * 1024) +#define START_MEM 0x80a00000 +void simpletest() { + printf("==========simpletest test=========\n"); + uint64 phys_size = PHYSTOP - START_MEM; + int sz = (phys_size / 3) * 2; + + printf("simple: "); + + char *p = sbrk(sz); + if (p == (char *)0xffffffffffffffffL) { + printf("sbrk(%d) failed\n", sz); + exit(-1); + } + + for (char *q = p; q < p + sz; q += 4096) { + *(int *)q = getpid(); + } + + int pid = fork(); + if (pid < 0) { + printf("fork() failed\n"); + exit(-1); + } + + if (pid == 0) + exit(0); + + wait(0); + + if (sbrk(-sz) == (char *)0xffffffffffffffffL) { + printf("sbrk(-%d) failed\n", sz); + exit(-1); + } + + printf("simpletest test OK\n"); +} + +// three processes all write COW memory. +// this causes more than half of physical memory +// to be allocated, so it also checks whether +// copied pages are freed. +void threetest() { + printf("==========threetest test=========\n"); + uint64 phys_size = PHYSTOP - START_MEM; + int sz = phys_size / 4; + int pid1, pid2; + + printf("three: "); + + char *p = sbrk(sz); + if (p == (char *)0xffffffffffffffffL) { + printf("sbrk(%d) failed\n", sz); + exit(-1); + } + + pid1 = fork(); + if (pid1 < 0) { + printf("fork failed\n"); + exit(-1); + } + if (pid1 == 0) { + pid2 = fork(); + if (pid2 < 0) { + printf("fork failed"); + exit(-1); + } + if (pid2 == 0) { + for (char *q = p; q < p + (sz / 5) * 4; q += 4096) { + *(int *)q = getpid(); + } + for (char *q = p; q < p + (sz / 5) * 4; q += 4096) { + if (*(int *)q != getpid()) { + printf("wrong content\n"); + exit(-1); + } + } + exit(-1); + } + for (char *q = p; q < p + (sz / 2); q += 4096) { + *(int *)q = 9999; + } + exit(0); + } + + for (char *q = p; q < p + sz; q += 4096) { + *(int *)q = getpid(); + } + + wait(0); + + sleep(1); + + for (char *q = p; q < p + sz; q += 4096) { + if (*(int *)q != getpid()) { + printf("wrong content\n"); + exit(-1); + } + } + + if (sbrk(-sz) == (char *)0xffffffffffffffffL) { + printf("sbrk(-%d) failed\n", sz); + exit(-1); + } + + printf("threetest test OK\n"); +} + +char buf2[4096]; +int fds[2]; + +// test whether copyout() simulates COW faults. +void filetest() { + printf("==========filetest test==========\n"); + + buf2[0] = 99; + + for (int i = 0; i < 4; i++) { + if (pipe(fds) != 0) { + printf("pipe() failed\n"); + exit(-1); + } + int pid = fork(); + if (pid < 0) { + printf("fork failed\n"); + exit(-1); + } + if (pid == 0) { + sleep(1); + if (read(fds[0], buf2, sizeof(i)) != sizeof(i)) { + printf("error: read failed\n"); + exit(1); + } + sleep(1); + int j = *(int *)buf2; + if (j != i) { + printf("error: read the wrong value\n"); + exit(1); + } + exit(0); + } + if (write(fds[1], &i, sizeof(i)) != sizeof(i)) { + printf("error: write failed\n"); + exit(-1); + } + } + + int xstatus = 0; + for (int i = 0; i < 4; i++) { + wait(&xstatus); + if (xstatus != 0) { + exit(1); + } + } + + if (buf2[0] != 99) { + printf("error: child overwrote parent\n"); + exit(1); + } + + printf("filetest test OK\n"); +} + +void cowtest() { + simpletest(); + + // check that the first simpletest() freed the physical memory. + simpletest(); + + threetest(); + threetest(); + threetest(); + + filetest(); + + printf("ALL COW TESTS PASSED\n"); +} + + +void stressfs() { + int fd, i; + char path[] = "stressfs0"; + char data[512]; + + printf("==========stressfs test==========\n"); + memset(data, 'a', sizeof(data)); + + for (i = 0; i < 4; i++) + if (fork() > 0) + break; + + printf("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("read\n"); + + fd = open(path, O_RDONLY); + for (i = 0; i < 20; i++) + read(fd, data, sizeof(data)); + close(fd); + + wait(0); + printf("stressfs test OK\n"); +} + + +void copyinstr3() { + printf("==========copyinstr3 test==========\n"); + sbrk(8192); + uint64 top = (uint64)sbrk(0); + if ((top % PGSIZE) != 0) { + sbrk(PGSIZE - (top % PGSIZE)); + } + top = (uint64)sbrk(0); + if (top % PGSIZE) { + printf("oops\n"); + exit(1); + } + + char *b = (char *)(top - 1); + *b = 'x'; + + int ret = unlink(b); + if (ret != -1) { + printf("unlink(%s) returned %d, not -1\n", b, ret); + exit(1); + } + + int fd = open(b, O_CREATE | O_WRONLY); + if (fd != -1) { + printf("open(%s) returned %d, not -1\n", b, fd); + exit(1); + } + + char *args[] = {"xx", 0}; + ret = execve(b, args, NULL); + if (ret != -1) { + printf("execve(%s) returned %d, not -1\n", b, fd); + exit(1); + } + printf("copyinstr3 test OK\n"); +} + + +void subdir() { + int fd, cc; + printf("==========subdir test==========\n"); + unlink("ff"); + if (mkdir("dd", 0666) != 0) { + printf("mkdir dd failed\n"); + exit(1); + } + + fd = open("dd/ff", O_CREATE | O_RDWR); + if (fd < 0) { + printf("create dd/ff failed\n"); + exit(1); + } + write(fd, "ff", 2); + close(fd); + + if (unlink("dd") >= 0) { + printf("unlink dd (non-empty dir) succeeded!\n"); + exit(1); + } + + if (mkdir("/dd/dd", 0666) != 0) { + printf("subdir mkdir dd/dd failed\n"); + exit(1); + } + + fd = open("dd/dd/ff", O_CREATE | O_RDWR); + if (fd < 0) { + printf("create dd/dd/ff failed\n"); + exit(1); + } + write(fd, "FF", 2); + close(fd); + + fd = open("dd/dd/../ff", 0); + if (fd < 0) { + printf("open dd/dd/../ff failed\n"); + exit(1); + } + cc = read(fd, buf, sizeof(buf)); + printf("%s", buf); + if (cc != 2 || buf[0] != 'f') { + printf("dd/dd/../ff wrong content\n"); + exit(1); + } + close(fd); + + if (unlink("dd/dd/ff") != 0) { + printf("unlink dd/dd/ff failed\n"); + exit(1); + } + if (open("dd/dd/ff", O_RDONLY) >= 0) { + printf("open (unlinked) dd/dd/ff succeeded\n"); + exit(1); + } + + if (chdir("dd") != 0) { + printf("chdir dd failed\n"); + exit(1); + } + if (chdir("dd/../../dd") != 0) { + printf("chdir dd/../../dd failed\n"); + exit(1); + } + if (chdir("dd/../../../dd") != 0) { + printf("chdir dd/../../dd failed\n"); + exit(1); + } + if (chdir("./..") != 0) { + printf("chdir ./.. failed\n"); + exit(1); + } + + + if (open("dd/dd/ff", O_RDONLY) >= 0) { + printf("open (unlinked) dd/dd/ff succeeded!\n"); + exit(1); + } + + if (open("dd/ff/ff", O_CREATE | O_RDWR) >= 0) { + printf("create dd/ff/ff succeeded!\n"); + exit(1); + } + if (open("dd/xx/ff", O_CREATE | O_RDWR) >= 0) { + printf("create dd/xx/ff succeeded!\n"); + exit(1); + } + if (open("dd", O_CREATE) >= 0) { + printf("create dd succeeded!\n"); + exit(1); + } + if (open("dd", O_RDWR) >= 0) { + printf("open dd rdwr succeeded!\n"); + exit(1); + } + + if (open("dd", O_WRONLY) >= 0) { + printf("open dd wronly succeeded!\n"); + exit(1); + } + + // if (mkdir("dd/ff/ff", 066) == 0) { + // printf("mkdir dd/ff/ff succeeded!\n"); + // exit(1); + // } + // if (mkdir("dd/xx/ff", 066) == 0) { + // printf("mkdir dd/xx/ff succeeded!\n"); + // exit(1); + // } + // if (mkdir("dd/dd/ffff", 0666) == 0) { + // printf("mkdir dd/dd/ffff succeeded!\n"); + // exit(1); + // } + // if (unlink("dd/xx/ff") == 0) { + // printf("unlink dd/xx/ff succeeded!\n"); + // exit(1); + // } + // if (unlink("dd/ff/ff") == 0) { + // printf("unlink dd/ff/ff succeeded!\n"); + // exit(1); + // } + // if (chdir("dd/ff") == 0) { + // printf("chdir dd/ff succeeded!\n"); + // exit(1); + // } + // if (chdir("dd/xx") == 0) { + // printf("chdir dd/xx succeeded!\n"); + // exit(1); + // } + + // if (unlink("dd/dd/ffff") != 0) { + // printf("unlink dd/dd/ff failed\n"); + // exit(1); + // } + // if (unlink("dd/ff") != 0) { + // printf("unlink dd/ff failed\n"); + // exit(1); + // } + // if (unlink("dd") == 0) { + // printf("unlink non-empty dd succeeded!\n"); + // exit(1); + // } + // if (unlink("dd/dd") < 0) { + // printf("unlink dd/dd failed\n"); + // exit(1); + // } + // if (unlink("dd") < 0) { + // printf("unlink dd failed\n"); + // exit(1); + // } + printf("subdir test OK\n"); +} + +void dirfile() { + printf("==========dirfile test==========\n"); + int fd; + + fd = open("dirfile", O_CREATE); + if (fd < 0) { + printf("create dirfile failed\n"); + exit(1); + } + close(fd); + if (chdir("dirfile") == 0) { + printf("chdir dirfile succeeded!\n"); + exit(1); + } + fd = open("dirfile/xx", 0); + if (fd >= 0) { + printf("create dirfile/xx succeeded!\n"); + exit(1); + } + fd = open("dirfile/xx", O_CREATE); + if (fd >= 0) { + printf("create dirfile/xx succeeded!\n"); + exit(1); + } + if (mkdir("dirfile/xx", 0666) == 0) { + printf("mkdir dirfile/xx succeeded!\n"); + exit(1); + } + if (unlink("dirfile/xx") == 0) { + printf("unlink dirfile/xx succeeded!\n"); + exit(1); + } + + if (unlink("dirfile") != 0) { + printf("unlink dirfile failed!\n"); + exit(1); + } + + close(fd); + printf("dirfile test OK\n"); +} + + +int main(void) { + forktest(); + exitwait(); + forkfork(); + forkforkfork(); + twochildren(); + reparent(); + reparent2(); + killstatus(); + + + opentest(); + openiputtest(); + writetest(); + writebig(); + preempt(); + truncate1(); + copyin(); + copyout(); + copyinstr1(); + truncate2(); + truncate3(); + iputtest(); + exitiputtest(); + createtest(); + + sbrklast(); + dirtest(); + execvetest(); + uvmfree(); + pipe1(); + mem(); + sharedfd(); + createdelete(); + fourfiles(); + bigwrite(); + bigfile(); + rmdot(); + badarg(); + sbrk8000(); + textwrite(); + outofinodes(); + manywrites(); + badwrite(); + sbrkbasic(); + sbrkmuch(); + kernmem(); + MAXVAplus(); + sbrkfail(); + sbrkarg(); + bsstest(); + bigargtest(); + argptest(); + stacktest(); + pgbug(); + sbrkbugs(); + cowtest(); + copyinstr3(); + stressfs(); + + + // TODO : + // fsfull(); + // diskfull(); + // execveout(); + // fourteen(); + // unlinkread(); + // subdir(); + // dirfile(); + + printf("ALL TESTS PASSED\n"); + exit(0); + return 0; +} diff --git a/xv6_user/src/usertests.c b/xv6_user/src/usertests.c new file mode 100644 index 0000000000000000000000000000000000000000..e64760f07ff6d15b15b66df8ff5e87ddb47959cb --- /dev/null +++ b/xv6_user/src/usertests.c @@ -0,0 +1,711 @@ +#define USER +#include "param.h" +#include "types.h" +#include "fs/stat.h" +#include "user.h" + +#include "fs/fcntl.h" +#include "syscall_gen/syscall_num.h" +#include "memory/memlayout.h" +#include "riscv.h" +#include "memory/buddy.h" + +// +// Tests xv6 system calls. usertests without arguments runs them all +// and usertests <name> runs <name> test. The test runner creates for +// each test a process and based on the exit status of the process, +// the test runner reports "OK" or "FAILED". Some tests result in +// kernel printing usertrap messages, which can be ignored if test +// prints "OK". +// + +#define BUFSZ ((MAXOPBLOCKS + 2) * BSIZE) + +char buf[BUFSZ]; + + +char junk1[4096]; +int fds[2]; +char junk2[4096]; +char buf2[4096]; +char junk3[4096]; + + + + +// +// Section with tests that run fairly quickly. Use -q if you want to +// run just those. With -q usertests also runs the ones that take a +// fair of time. +// + +// what if a string argument crosses over the end of last user page? + + + + + + +// does the error path in open() for attempt to write a +// directory call iput() in a transaction? +// needs a hacked kernel that pauses just after the namei() +// call in sys_open(): +// if((ip = namei(path)) == 0) +// return -1; +// { +// int i; +// for(i = 0; i < 10000; i++) +// yield(); +// } + + +// More file system tests + +void linktest(char *s) { + enum { SZ = 5 }; + int fd; + + unlink("lf1"); + unlink("lf2"); + + fd = open("lf1", O_CREATE | O_RDWR); + if (fd < 0) { + printf("%s: create lf1 failed\n", s); + exit(1); + } + if (write(fd, "hello", SZ) != SZ) { + printf("%s: write lf1 failed\n", s); + exit(1); + } + close(fd); + + if (link("lf1", "lf2") < 0) { + printf("%s: link lf1 lf2 failed\n", s); + exit(1); + } + unlink("lf1"); + + if (open("lf1", 0) >= 0) { + printf("%s: unlinked lf1 but it is still there!\n", s); + exit(1); + } + + fd = open("lf2", 0); + if (fd < 0) { + printf("%s: open lf2 failed\n", s); + exit(1); + } + if (read(fd, buf, sizeof(buf)) != SZ) { + printf("%s: read lf2 failed\n", s); + exit(1); + } + close(fd); + + if (link("lf2", "lf2") >= 0) { + printf("%s: link lf2 lf2 succeeded! oops\n", s); + exit(1); + } + + unlink("lf2"); + if (link("lf2", "lf1") >= 0) { + printf("%s: link non-existent succeeded! oops\n", s); + exit(1); + } + + if (link(".", "lf1") >= 0) { + printf("%s: link . lf1 succeeded! oops\n", s); + exit(1); + } +} + +// test concurrent create/link/unlink of the same file +void concreate(char *s) { + enum { N = 40 }; + char file[3]; + int i, pid, n, fd; + char fa[N]; + struct { + ushort inum; + char name[DIRSIZ]; + } de; + + file[0] = 'C'; + file[2] = '\0'; + for (i = 0; i < N; 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("concreate create %s failed\n", file); + exit(1); + } + close(fd); + } + if (pid == 0) { + exit(0); + } else { + int xstatus; + wait(&xstatus); + if (xstatus != 0) + exit(1); + } + } + + 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("%s: concreate weird file %s\n", s, de.name); + exit(1); + } + if (fa[i]) { + printf("%s: concreate duplicate file %s\n", s, de.name); + exit(1); + } + fa[i] = 1; + n++; + } + } + close(fd); + + if (n != N) { + printf("%s: concreate not enough files in directory listing\n", s); + exit(1); + } + + for (i = 0; i < N; i++) { + file[1] = '0' + i; + pid = fork(); + if (pid < 0) { + printf("%s: fork failed\n", s); + exit(1); + } + 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)); + close(open(file, 0)); + close(open(file, 0)); + } else { + unlink(file); + unlink(file); + unlink(file); + unlink(file); + unlink(file); + unlink(file); + } + if (pid == 0) + exit(0); + else + wait(0); + } +} + +// another concurrent link/unlink/create test, +// to look for deadlocks. +void linkunlink(char *s) { + int pid, i; + + unlink("x"); + pid = fork(); + if (pid < 0) { + printf("%s: fork failed\n", s); + exit(1); + } + + 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(0); + else + exit(0); +} + +void subdir(char *s) { + int fd, cc; + + unlink("ff"); + if (mkdir("dd") != 0) { + printf("%s: mkdir dd failed\n", s); + exit(1); + } + + fd = open("dd/ff", O_CREATE | O_RDWR); + if (fd < 0) { + printf("%s: create dd/ff failed\n", s); + exit(1); + } + write(fd, "ff", 2); + close(fd); + + if (unlink("dd") >= 0) { + printf("%s: unlink dd (non-empty dir) succeeded!\n", s); + exit(1); + } + + if (mkdir("/dd/dd") != 0) { + printf("subdir mkdir dd/dd failed\n", s); + exit(1); + } + + fd = open("dd/dd/ff", O_CREATE | O_RDWR); + if (fd < 0) { + printf("%s: create dd/dd/ff failed\n", s); + exit(1); + } + write(fd, "FF", 2); + close(fd); + + fd = open("dd/dd/../ff", 0); + if (fd < 0) { + printf("%s: open dd/dd/../ff failed\n", s); + exit(1); + } + cc = read(fd, buf, sizeof(buf)); + if (cc != 2 || buf[0] != 'f') { + printf("%s: dd/dd/../ff wrong content\n", s); + exit(1); + } + close(fd); + + if (link("dd/dd/ff", "dd/dd/ffff") != 0) { + printf("link dd/dd/ff dd/dd/ffff failed\n", s); + exit(1); + } + + if (unlink("dd/dd/ff") != 0) { + printf("%s: unlink dd/dd/ff failed\n", s); + exit(1); + } + if (open("dd/dd/ff", O_RDONLY) >= 0) { + printf("%s: open (unlinked) dd/dd/ff succeeded\n", s); + exit(1); + } + + if (chdir("dd") != 0) { + printf("%s: chdir dd failed\n", s); + exit(1); + } + if (chdir("dd/../../dd") != 0) { + printf("%s: chdir dd/../../dd failed\n", s); + exit(1); + } + if (chdir("dd/../../../dd") != 0) { + printf("chdir dd/../../dd failed\n", s); + exit(1); + } + if (chdir("./..") != 0) { + printf("%s: chdir ./.. failed\n", s); + exit(1); + } + + fd = open("dd/dd/ffff", 0); + if (fd < 0) { + printf("%s: open dd/dd/ffff failed\n", s); + exit(1); + } + if (read(fd, buf, sizeof(buf)) != 2) { + printf("%s: read dd/dd/ffff wrong len\n", s); + exit(1); + } + close(fd); + + if (open("dd/dd/ff", O_RDONLY) >= 0) { + printf("%s: open (unlinked) dd/dd/ff succeeded!\n", s); + exit(1); + } + + if (open("dd/ff/ff", O_CREATE | O_RDWR) >= 0) { + printf("%s: create dd/ff/ff succeeded!\n", s); + exit(1); + } + if (open("dd/xx/ff", O_CREATE | O_RDWR) >= 0) { + printf("%s: create dd/xx/ff succeeded!\n", s); + exit(1); + } + if (open("dd", O_CREATE) >= 0) { + printf("%s: create dd succeeded!\n", s); + exit(1); + } + if (open("dd", O_RDWR) >= 0) { + printf("%s: open dd rdwr succeeded!\n", s); + exit(1); + } + if (open("dd", O_WRONLY) >= 0) { + printf("%s: open dd wronly succeeded!\n", s); + exit(1); + } + if (link("dd/ff/ff", "dd/dd/xx") == 0) { + printf("%s: link dd/ff/ff dd/dd/xx succeeded!\n", s); + exit(1); + } + if (link("dd/xx/ff", "dd/dd/xx") == 0) { + printf("%s: link dd/xx/ff dd/dd/xx succeeded!\n", s); + exit(1); + } + if (link("dd/ff", "dd/dd/ffff") == 0) { + printf("%s: link dd/ff dd/dd/ffff succeeded!\n", s); + exit(1); + } + if (mkdir("dd/ff/ff") == 0) { + printf("%s: mkdir dd/ff/ff succeeded!\n", s); + exit(1); + } + if (mkdir("dd/xx/ff") == 0) { + printf("%s: mkdir dd/xx/ff succeeded!\n", s); + exit(1); + } + if (mkdir("dd/dd/ffff") == 0) { + printf("%s: mkdir dd/dd/ffff succeeded!\n", s); + exit(1); + } + if (unlink("dd/xx/ff") == 0) { + printf("%s: unlink dd/xx/ff succeeded!\n", s); + exit(1); + } + if (unlink("dd/ff/ff") == 0) { + printf("%s: unlink dd/ff/ff succeeded!\n", s); + exit(1); + } + if (chdir("dd/ff") == 0) { + printf("%s: chdir dd/ff succeeded!\n", s); + exit(1); + } + if (chdir("dd/xx") == 0) { + printf("%s: chdir dd/xx succeeded!\n", s); + exit(1); + } + + if (unlink("dd/dd/ffff") != 0) { + printf("%s: unlink dd/dd/ff failed\n", s); + exit(1); + } + if (unlink("dd/ff") != 0) { + printf("%s: unlink dd/ff failed\n", s); + exit(1); + } + if (unlink("dd") == 0) { + printf("%s: unlink non-empty dd succeeded!\n", s); + exit(1); + } + if (unlink("dd/dd") < 0) { + printf("%s: unlink dd/dd failed\n", s); + exit(1); + } + if (unlink("dd") < 0) { + printf("%s: unlink dd failed\n", s); + exit(1); + } +} + + +// test that iput() is called at the end of _namei(). +// also tests empty file names. +void iref(char *s) { + int i, fd; + + for (i = 0; i < NINODE + 1; i++) { + if (mkdir("irefd") != 0) { + printf("%s: mkdir irefd failed\n", s); + exit(1); + } + if (chdir("irefd") != 0) { + printf("%s: chdir irefd failed\n", s); + exit(1); + } + + mkdir(""); + link("README.md", ""); + fd = open("", O_CREATE); + if (fd >= 0) + close(fd); + fd = open("xx", O_CREATE); + if (fd >= 0) + close(fd); + unlink("xx"); + } + + // clean up + for (i = 0; i < NINODE + 1; i++) { + chdir(".."); + unlink("irefd"); + } + + chdir("/"); +} + +void validatetest(char *s) { + int hi; + uint64 p; + + hi = 1100 * 1024; + for (p = 0; p <= (uint)hi; p += PGSIZE) { + // try to crash the kernel by passing in a bad string pointer + if (link("nosuchfile", (char *)p) != -1) { + printf("%s: link should not succeed\n", s); + exit(1); + } + } +} + + +struct test { + void (*f)(char *); + char *s; +} quicktests[] = { + {cowtest, "cowtest"}, + {copyin, "copyin"}, + {copyout, "copyout"}, + {copyinstr1, "copyinstr1"}, + {copyinstr2, "copyinstr2"}, + {copyinstr3, "copyinstr3"}, + {rwsbrk, "rwsbrk"}, + {truncate1, "truncate1"}, + {truncate2, "truncate2"}, + {truncate3, "truncate3"}, + {openiputtest, "openiput"}, + {exitiputtest, "exitiput"}, + {iputtest, "iput"}, + {opentest, "opentest"}, + {writetest, "writetest"}, + {writebig, "writebig"}, + {createtest, "createtest"}, + {dirtest, "dirtest"}, + {exectest, "exectest"}, + {pipe1, "pipe1"}, + {killstatus, "killstatus"}, + {preempt, "preempt"}, + {exitwait, "exitwait"}, + {reparent, "reparent"}, + {twochildren, "twochildren"}, + {forkfork, "forkfork"}, + {forkforkfork, "forkforkfork"}, + {reparent2, "reparent2"}, + {mem, "mem"}, + {sharedfd, "sharedfd"}, + {fourfiles, "fourfiles"}, + {createdelete, "createdelete"}, + {unlinkread, "unlinkread"}, + {linktest, "linktest"}, + {concreate, "concreate"}, + {linkunlink, "linkunlink"}, + {subdir, "subdir"}, + {bigwrite, "bigwrite"}, + {bigfile, "bigfile"}, + {fourteen, "fourteen"}, + {rmdot, "rmdot"}, + {dirfile, "dirfile"}, + {iref, "iref"}, + {forktest, "forktest"}, + {sbrkbasic, "sbrkbasic"}, + {sbrkmuch, "sbrkmuch"}, + {kernmem, "kernmem"}, + {MAXVAplus, "MAXVAplus"}, + {sbrkfail, "sbrkfail"}, + {sbrkarg, "sbrkarg"}, + {validatetest, "validatetest"}, + {bsstest, "bsstest"}, + {bigargtest, "bigargtest"}, + {argptest, "argptest"}, + {stacktest, "stacktest"}, + {textwrite, "textwrite"}, + {pgbug, "pgbug"}, + {sbrkbugs, "sbrkbugs"}, + {sbrklast, "sbrklast"}, + {sbrk8000, "sbrk8000"}, + {badarg, "badarg"}, + {uvmfree, "uvmfree"}, + + {0, 0}, +}; + +struct test slowtests[] = { + {bigdir, "bigdir"}, + {manywrites, "manywrites"}, + {badwrite, "badwrite"}, + {execout, "execout"}, + {diskfull, "diskfull"}, + {outofinodes, "outofinodes"}, + + {0, 0}, +}; + +// +// drive tests +// + +// run each test in its own process. run returns 1 if child's exit() +// indicates success. +int run(void f(char *), char *s) { + int pid; + int xstatus; + + printf("test %s: ", s); + if ((pid = fork()) < 0) { + printf("runtest: fork error\n"); + exit(1); + } + if (pid == 0) { + f(s); + exit(0); + } else { + wait(&xstatus); + if (xstatus != 0) + printf("FAILED\n"); + else + printf("OK\n"); + return xstatus == 0; + } +} + +int runtests(struct test *tests, char *justone) { + for (struct test *t = tests; t->s != 0; t++) { + if ((justone == 0) || strcmp(t->s, justone) == 0) { + if (!run(t->f, t->s)) { + printf("SOME TESTS FAILED\n"); + return 1; + } + } + } + return 0; +} + +// +// use sbrk() to count how many free physical memory pages there are. +// touches the pages to force allocation. +// because out of memory with lazy allocation results in the process +// taking a fault and being killed, fork and report back. +// +int countfree() { + int fds[2]; + + if (pipe(fds) < 0) { + printf("pipe() failed in countfree()\n"); + exit(1); + } + + int pid = fork(); + + if (pid < 0) { + printf("fork failed in countfree()\n"); + exit(1); + } + + if (pid == 0) { + close(fds[0]); + + while (1) { + uint64 a = (uint64)sbrk(4096); + if (a == 0xffffffffffffffff) { + break; + } + + // modify the memory to make sure it's really allocated. + *(char *)(a + 4096 - 1) = 1; + + // report back one more page. + if (write(fds[1], "x", 1) != 1) { + printf("write() failed in countfree()\n"); + exit(1); + } + } + + exit(0); + } + + close(fds[1]); + + int n = 0; + while (1) { + char c; + int cc = read(fds[0], &c, 1); + if (cc < 0) { + printf("read() failed in countfree()\n"); + exit(1); + } + if (cc == 0) + break; + n += 1; + } + + close(fds[0]); + wait((int *)0); + + return n; +} + +int drivetests(int quick, int continuous, char *justone) { + do { + printf("usertests starting\n"); + int free0 = countfree(); + int free1 = 0; + if (runtests(quicktests, justone)) { + if (continuous != 2) { + return 1; + } + } + if (!quick) { + if (justone == 0) + printf("usertests slow tests starting\n"); + if (runtests(slowtests, justone)) { + if (continuous != 2) { + return 1; + } + } + } + if ((free1 = countfree()) < free0) { + printf("FAILED -- lost some free pages %d (out of %d)\n", free1, free0); + if (continuous != 2) { + return 1; + } + } + } while (continuous); + return 0; +} + +int main(int argc, char *argv[]) { + int continuous = 0; + int quick = 0; + char *justone = 0; + + // print_pgtable(); + if (argc == 2 && strcmp(argv[1], "-q") == 0) { + quick = 1; + } else if (argc == 2 && strcmp(argv[1], "-c") == 0) { + continuous = 1; + } else if (argc == 2 && strcmp(argv[1], "-C") == 0) { + continuous = 2; + } else if (argc == 2 && argv[1][0] != '-') { + justone = argv[1]; + } else if (argc > 1) { + printf("Usage: usertests [-c] [-C] [-q] [testname]\n"); + exit(1); + } + if (drivetests(quick, continuous, justone)) { + exit(1); + } + printf("ALL TESTS PASSED\n"); + exit(0); +} diff --git a/xv6_user/src/wc.c b/xv6_user/src/wc.c new file mode 100644 index 0000000000000000000000000000000000000000..a568142e0d8ed8f8d93776465d47cf8627dfc94b --- /dev/null +++ b/xv6_user/src/wc.c @@ -0,0 +1,54 @@ +#define USER +#include "stddef.h" +#include "unistd.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.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("wc: read error\n"); + exit(1); + } + printf("%d %d %d %s\n", l, w, c, name); +} + +int main(int argc, char *argv[]) { + int fd, i; + + if (argc <= 1) { + wc(0, ""); + exit(0); + } + + for (i = 1; i < argc; i++) { + if ((fd = open(argv[i], 0)) < 0) { + printf("wc: cannot open %s\n", argv[i]); + exit(1); + } + wc(fd, argv[i]); + close(fd); + } + exit(0); + return 0; +}