| @ -0,0 +1,328 @@ | |||
| PROJ := 5 | |||
| EMPTY := | |||
| SPACE := $(EMPTY) $(EMPTY) | |||
| SLASH := / | |||
| V := @ | |||
| # try to infer the correct GCCPREFX | |||
| ifndef GCCPREFIX | |||
| GCCPREFIX := $(shell if i386-ucore-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ | |||
| then echo 'i386-ucore-elf-'; \ | |||
| elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ | |||
| then echo ''; \ | |||
| else echo "***" 1>&2; \ | |||
| echo "*** Error: Couldn't find an i386-ucore-elf version of GCC/binutils." 1>&2; \ | |||
| echo "*** Is the directory with i386-ucore-elf-gcc in your PATH?" 1>&2; \ | |||
| echo "*** If your i386-ucore-elf toolchain is installed with a command" 1>&2; \ | |||
| echo "*** prefix other than 'i386-ucore-elf-', set your GCCPREFIX" 1>&2; \ | |||
| echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ | |||
| echo "*** To turn off this error, run 'gmake GCCPREFIX= ...'." 1>&2; \ | |||
| echo "***" 1>&2; exit 1; fi) | |||
| endif | |||
| # try to infer the correct QEMU | |||
| ifndef QEMU | |||
| QEMU := $(shell if which qemu-system-i386 > /dev/null; \ | |||
| then echo 'qemu-system-i386'; exit; \ | |||
| elif which i386-ucore-elf-qemu > /dev/null; \ | |||
| then echo 'i386-ucore-elf-qemu'; exit; \ | |||
| else \ | |||
| echo "***" 1>&2; \ | |||
| echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ | |||
| echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ | |||
| echo "***" 1>&2; exit 1; fi) | |||
| endif | |||
| # eliminate default suffix rules | |||
| .SUFFIXES: .c .S .h | |||
| # delete target files if there is an error (or make is interrupted) | |||
| .DELETE_ON_ERROR: | |||
| # define compiler and flags | |||
| HOSTCC := gcc | |||
| HOSTCFLAGS := -g -Wall -O2 | |||
| GDB := $(GCCPREFIX)gdb | |||
| CC ?= $(GCCPREFIX)gcc | |||
| CFLAGS := -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc $(DEFS) | |||
| CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) | |||
| CTYPE := c S | |||
| LD := $(GCCPREFIX)ld | |||
| LDFLAGS := -m $(shell $(LD) -V | grep elf_i386 2>/dev/null) | |||
| LDFLAGS += -nostdlib | |||
| OBJCOPY := $(GCCPREFIX)objcopy | |||
| OBJDUMP := $(GCCPREFIX)objdump | |||
| COPY := cp | |||
| MKDIR := mkdir -p | |||
| MV := mv | |||
| RM := rm -f | |||
| AWK := awk | |||
| SED := sed | |||
| SH := sh | |||
| TR := tr | |||
| TOUCH := touch -c | |||
| OBJDIR := obj | |||
| BINDIR := bin | |||
| ALLOBJS := | |||
| ALLDEPS := | |||
| TARGETS := | |||
| include tools/function.mk | |||
| listf_cc = $(call listf,$(1),$(CTYPE)) | |||
| USER_PREFIX := __user_ | |||
| # for cc | |||
| add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4)) | |||
| create_target_cc = $(call create_target,$(1),$(2),$(3),$(CC),$(CFLAGS)) | |||
| # for hostcc | |||
| add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3)) | |||
| create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS)) | |||
| cgtype = $(patsubst %.$(2),%.$(3),$(1)) | |||
| objfile = $(call toobj,$(1)) | |||
| asmfile = $(call cgtype,$(call toobj,$(1)),o,asm) | |||
| outfile = $(call cgtype,$(call toobj,$(1)),o,out) | |||
| symfile = $(call cgtype,$(call toobj,$(1)),o,sym) | |||
| filename = $(basename $(notdir $(1))) | |||
| ubinfile = $(call outfile,$(addprefix $(USER_PREFIX),$(call filename,$(1)))) | |||
| # for match pattern | |||
| match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?) | |||
| # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||
| # include kernel/user | |||
| INCLUDE += libs/ | |||
| CFLAGS += $(addprefix -I,$(INCLUDE)) | |||
| LIBDIR += libs | |||
| $(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,) | |||
| # ------------------------------------------------------------------- | |||
| # user programs | |||
| UINCLUDE += user/include/ \ | |||
| user/libs/ | |||
| USRCDIR += user | |||
| ULIBDIR += user/libs | |||
| UCFLAGS += $(addprefix -I,$(UINCLUDE)) | |||
| USER_BINS := | |||
| $(call add_files_cc,$(call listf_cc,$(ULIBDIR)),ulibs,$(UCFLAGS)) | |||
| $(call add_files_cc,$(call listf_cc,$(USRCDIR)),uprog,$(UCFLAGS)) | |||
| UOBJS := $(call read_packet,ulibs libs) | |||
| define uprog_ld | |||
| __user_bin__ := $$(call ubinfile,$(1)) | |||
| USER_BINS += $$(__user_bin__) | |||
| $$(__user_bin__): tools/user.ld | |||
| $$(__user_bin__): $$(UOBJS) | |||
| $$(__user_bin__): $(1) | $$$$(dir $$$$@) | |||
| $(V)$(LD) $(LDFLAGS) -T tools/user.ld -o $$@ $$(UOBJS) $(1) | |||
| @$(OBJDUMP) -S $$@ > $$(call cgtype,$$<,o,asm) | |||
| @$(OBJDUMP) -t $$@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$$$/d' > $$(call cgtype,$$<,o,sym) | |||
| endef | |||
| $(foreach p,$(call read_packet,uprog),$(eval $(call uprog_ld,$(p)))) | |||
| # ------------------------------------------------------------------- | |||
| # kernel | |||
| KINCLUDE += kern/debug/ \ | |||
| kern/driver/ \ | |||
| kern/trap/ \ | |||
| kern/mm/ \ | |||
| kern/libs/ \ | |||
| kern/sync/ \ | |||
| kern/fs/ \ | |||
| kern/process \ | |||
| kern/schedule \ | |||
| kern/syscall | |||
| KSRCDIR += kern/init \ | |||
| kern/libs \ | |||
| kern/debug \ | |||
| kern/driver \ | |||
| kern/trap \ | |||
| kern/mm \ | |||
| kern/sync \ | |||
| kern/fs \ | |||
| kern/process \ | |||
| kern/schedule \ | |||
| kern/syscall | |||
| KCFLAGS += $(addprefix -I,$(KINCLUDE)) | |||
| $(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS)) | |||
| KOBJS = $(call read_packet,kernel libs) | |||
| # create kernel target | |||
| kernel = $(call totarget,kernel) | |||
| $(kernel): tools/kernel.ld | |||
| $(kernel): $(KOBJS) $(USER_BINS) | |||
| @echo + ld $@ | |||
| $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) -b binary $(USER_BINS) | |||
| @$(OBJDUMP) -S $@ > $(call asmfile,kernel) | |||
| @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel) | |||
| $(call create_target,kernel) | |||
| # ------------------------------------------------------------------- | |||
| # create bootblock | |||
| bootfiles = $(call listf_cc,boot) | |||
| $(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc)) | |||
| bootblock = $(call totarget,bootblock) | |||
| $(bootblock): $(call toobj,boot/bootasm.S) $(call toobj,$(bootfiles)) | $(call totarget,sign) | |||
| @echo + ld $@ | |||
| $(V)$(LD) $(LDFLAGS) -N -T tools/boot.ld $^ -o $(call toobj,bootblock) | |||
| @$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock) | |||
| @$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock) | |||
| @$(call totarget,sign) $(call outfile,bootblock) $(bootblock) | |||
| $(call create_target,bootblock) | |||
| # ------------------------------------------------------------------- | |||
| # create 'sign' tools | |||
| $(call add_files_host,tools/sign.c,sign,sign) | |||
| $(call create_target_host,sign,sign) | |||
| # ------------------------------------------------------------------- | |||
| # create ucore.img | |||
| UCOREIMG := $(call totarget,ucore.img) | |||
| $(UCOREIMG): $(kernel) $(bootblock) | |||
| $(V)dd if=/dev/zero of=$@ count=10000 | |||
| $(V)dd if=$(bootblock) of=$@ conv=notrunc | |||
| $(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc | |||
| $(call create_target,ucore.img) | |||
| # ------------------------------------------------------------------- | |||
| # create swap.img | |||
| SWAPIMG := $(call totarget,swap.img) | |||
| $(SWAPIMG): | |||
| $(V)dd if=/dev/zero of=$@ bs=1M count=128 | |||
| $(call create_target,swap.img) | |||
| # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||
| $(call finish_all) | |||
| IGNORE_ALLDEPS = clean \ | |||
| dist-clean \ | |||
| grade \ | |||
| touch \ | |||
| print-.+ \ | |||
| run-.+ \ | |||
| build-.+ \ | |||
| handin | |||
| ifeq ($(call match,$(MAKECMDGOALS),$(IGNORE_ALLDEPS)),0) | |||
| -include $(ALLDEPS) | |||
| endif | |||
| # files for grade script | |||
| TARGETS: $(TARGETS) | |||
| .DEFAULT_GOAL := TARGETS | |||
| QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback | |||
| .PHONY: qemu qemu-nox debug debug-nox | |||
| qemu-mon: $(UCOREIMG) $(SWAPIMG) | |||
| $(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null | |||
| qemu: $(UCOREIMG) $(SWAPIMG) | |||
| $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null | |||
| qemu-nox: $(UCOREIMG) $(SWAPIMG) | |||
| $(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic | |||
| TERMINAL := gnome-terminal | |||
| debug: $(UCOREIMG) $(SWAPIMG) | |||
| $(V)$(QEMU) -S -s -parallel stdio $(QEMUOPTS) -serial null & | |||
| $(V)sleep 2 | |||
| $(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit" | |||
| debug-nox: $(UCOREIMG) $(SWAPIMG) | |||
| $(V)$(QEMU) -S -s -serial mon:stdio $(QEMUOPTS) -nographic & | |||
| $(V)sleep 2 | |||
| $(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit" | |||
| RUN_PREFIX := _binary_$(OBJDIR)_$(USER_PREFIX) | |||
| MAKEOPTS := --quiet --no-print-directory | |||
| run-%: build-% | |||
| $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null | |||
| run-nox-%: build-% | |||
| $(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic | |||
| build-%: touch | |||
| $(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=$* -DTESTSTART=$(RUN_PREFIX)$*_out_start -DTESTSIZE=$(RUN_PREFIX)$*_out_size" | |||
| .PHONY: grade touch | |||
| GRADE_GDB_IN := .gdb.in | |||
| GRADE_QEMU_OUT := .qemu.out | |||
| HANDIN := proj$(PROJ)-handin.tar.gz | |||
| TOUCH_FILES := kern/process/proc.c | |||
| MAKEOPTS := --quiet --no-print-directory | |||
| grade: | |||
| $(V)$(MAKE) $(MAKEOPTS) clean | |||
| $(V)$(SH) tools/grade.sh | |||
| touch: | |||
| $(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f)) | |||
| print-%: | |||
| @echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z])) | |||
| .PHONY: clean dist-clean handin packall | |||
| clean: | |||
| $(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) | |||
| -$(RM) -r $(OBJDIR) $(BINDIR) | |||
| dist-clean: clean | |||
| -$(RM) $(HANDIN) | |||
| handin: packall | |||
| @echo Please visit http://learn.tsinghua.edu.cn and upload $(HANDIN). Thanks! | |||
| packall: clean | |||
| @$(RM) -f $(HANDIN) | |||
| @tar -czf $(HANDIN) `find . -type f -o -type d | grep -v '^\.*$$' | grep -vF '$(HANDIN)'` | |||
| @ -0,0 +1,26 @@ | |||
| #ifndef __BOOT_ASM_H__ | |||
| #define __BOOT_ASM_H__ | |||
| /* Assembler macros to create x86 segments */ | |||
| /* Normal segment */ | |||
| #define SEG_NULLASM \ | |||
| .word 0, 0; \ | |||
| .byte 0, 0, 0, 0 | |||
| #define SEG_ASM(type,base,lim) \ | |||
| .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ | |||
| .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ | |||
| (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) | |||
| /* Application segment type bits */ | |||
| #define STA_X 0x8 // Executable segment | |||
| #define STA_E 0x4 // Expand down (non-executable segments) | |||
| #define STA_C 0x4 // Conforming code segment (executable only) | |||
| #define STA_W 0x2 // Writeable (non-executable segments) | |||
| #define STA_R 0x2 // Readable (executable segments) | |||
| #define STA_A 0x1 // Accessed | |||
| #endif /* !__BOOT_ASM_H__ */ | |||
| @ -0,0 +1,107 @@ | |||
| #include <asm.h> | |||
| # Start the CPU: switch to 32-bit protected mode, jump into C. | |||
| # The BIOS loads this code from the first sector of the hard disk into | |||
| # memory at physical address 0x7c00 and starts executing in real mode | |||
| # with %cs=0 %ip=7c00. | |||
| .set PROT_MODE_CSEG, 0x8 # kernel code segment selector | |||
| .set PROT_MODE_DSEG, 0x10 # kernel data segment selector | |||
| .set CR0_PE_ON, 0x1 # protected mode enable flag | |||
| .set SMAP, 0x534d4150 | |||
| # start address should be 0:7c00, in real mode, the beginning address of the running bootloader | |||
| .globl start | |||
| start: | |||
| .code16 # Assemble for 16-bit mode | |||
| cli # Disable interrupts | |||
| cld # String operations increment | |||
| # Set up the important data segment registers (DS, ES, SS). | |||
| xorw %ax, %ax # Segment number zero | |||
| movw %ax, %ds # -> Data Segment | |||
| movw %ax, %es # -> Extra Segment | |||
| movw %ax, %ss # -> Stack Segment | |||
| # Enable A20: | |||
| # For backwards compatibility with the earliest PCs, physical | |||
| # address line 20 is tied low, so that addresses higher than | |||
| # 1MB wrap around to zero by default. This code undoes this. | |||
| seta20.1: | |||
| inb $0x64, %al # Wait for not busy | |||
| testb $0x2, %al | |||
| jnz seta20.1 | |||
| movb $0xd1, %al # 0xd1 -> port 0x64 | |||
| outb %al, $0x64 | |||
| seta20.2: | |||
| inb $0x64, %al # Wait for not busy | |||
| testb $0x2, %al | |||
| jnz seta20.2 | |||
| movb $0xdf, %al # 0xdf -> port 0x60 | |||
| outb %al, $0x60 | |||
| probe_memory: | |||
| movl $0, 0x8000 | |||
| xorl %ebx, %ebx | |||
| movw $0x8004, %di | |||
| start_probe: | |||
| movl $0xE820, %eax | |||
| movl $20, %ecx | |||
| movl $SMAP, %edx | |||
| int $0x15 | |||
| jnc cont | |||
| movw $12345, 0x8000 | |||
| jmp finish_probe | |||
| cont: | |||
| addw $20, %di | |||
| incl 0x8000 | |||
| cmpl $0, %ebx | |||
| jnz start_probe | |||
| finish_probe: | |||
| # Switch from real to protected mode, using a bootstrap GDT | |||
| # and segment translation that makes virtual addresses | |||
| # identical to physical addresses, so that the | |||
| # effective memory map does not change during the switch. | |||
| lgdt gdtdesc | |||
| movl %cr0, %eax | |||
| orl $CR0_PE_ON, %eax | |||
| movl %eax, %cr0 | |||
| # Jump to next instruction, but in 32-bit code segment. | |||
| # Switches processor into 32-bit mode. | |||
| ljmp $PROT_MODE_CSEG, $protcseg | |||
| .code32 # Assemble for 32-bit mode | |||
| protcseg: | |||
| # Set up the protected-mode data segment registers | |||
| movw $PROT_MODE_DSEG, %ax # Our data segment selector | |||
| movw %ax, %ds # -> DS: Data Segment | |||
| movw %ax, %es # -> ES: Extra Segment | |||
| movw %ax, %fs # -> FS | |||
| movw %ax, %gs # -> GS | |||
| movw %ax, %ss # -> SS: Stack Segment | |||
| # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00) | |||
| movl $0x0, %ebp | |||
| movl $start, %esp | |||
| call bootmain | |||
| # If bootmain returns (it shouldn't), loop. | |||
| spin: | |||
| jmp spin | |||
| .data | |||
| # Bootstrap GDT | |||
| .p2align 2 # force 4 byte alignment | |||
| gdt: | |||
| SEG_NULLASM # null seg | |||
| SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel | |||
| SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel | |||
| gdtdesc: | |||
| .word 0x17 # sizeof(gdt) - 1 | |||
| .long gdt # address gdt | |||
| @ -0,0 +1,116 @@ | |||
| #include <defs.h> | |||
| #include <x86.h> | |||
| #include <elf.h> | |||
| /* ********************************************************************* | |||
| * This a dirt simple boot loader, whose sole job is to boot | |||
| * an ELF kernel image from the first IDE hard disk. | |||
| * | |||
| * DISK LAYOUT | |||
| * * This program(bootasm.S and bootmain.c) is the bootloader. | |||
| * It should be stored in the first sector of the disk. | |||
| * | |||
| * * The 2nd sector onward holds the kernel image. | |||
| * | |||
| * * The kernel image must be in ELF format. | |||
| * | |||
| * BOOT UP STEPS | |||
| * * when the CPU boots it loads the BIOS into memory and executes it | |||
| * | |||
| * * the BIOS intializes devices, sets of the interrupt routines, and | |||
| * reads the first sector of the boot device(e.g., hard-drive) | |||
| * into memory and jumps to it. | |||
| * | |||
| * * Assuming this boot loader is stored in the first sector of the | |||
| * hard-drive, this code takes over... | |||
| * | |||
| * * control starts in bootasm.S -- which sets up protected mode, | |||
| * and a stack so C code then run, then calls bootmain() | |||
| * | |||
| * * bootmain() in this file takes over, reads in the kernel and jumps to it. | |||
| * */ | |||
| #define SECTSIZE 512 | |||
| #define ELFHDR ((struct elfhdr *)0x10000) // scratch space | |||
| /* waitdisk - wait for disk ready */ | |||
| static void | |||
| waitdisk(void) { | |||
| while ((inb(0x1F7) & 0xC0) != 0x40) | |||
| /* do nothing */; | |||
| } | |||
| /* readsect - read a single sector at @secno into @dst */ | |||
| static void | |||
| readsect(void *dst, uint32_t secno) { | |||
| // wait for disk to be ready | |||
| waitdisk(); | |||
| outb(0x1F2, 1); // count = 1 | |||
| outb(0x1F3, secno & 0xFF); | |||
| outb(0x1F4, (secno >> 8) & 0xFF); | |||
| outb(0x1F5, (secno >> 16) & 0xFF); | |||
| outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0); | |||
| outb(0x1F7, 0x20); // cmd 0x20 - read sectors | |||
| // wait for disk to be ready | |||
| waitdisk(); | |||
| // read a sector | |||
| insl(0x1F0, dst, SECTSIZE / 4); | |||
| } | |||
| /* * | |||
| * readseg - read @count bytes at @offset from kernel into virtual address @va, | |||
| * might copy more than asked. | |||
| * */ | |||
| static void | |||
| readseg(uintptr_t va, uint32_t count, uint32_t offset) { | |||
| uintptr_t end_va = va + count; | |||
| // round down to sector boundary | |||
| va -= offset % SECTSIZE; | |||
| // translate from bytes to sectors; kernel starts at sector 1 | |||
| uint32_t secno = (offset / SECTSIZE) + 1; | |||
| // If this is too slow, we could read lots of sectors at a time. | |||
| // We'd write more to memory than asked, but it doesn't matter -- | |||
| // we load in increasing order. | |||
| for (; va < end_va; va += SECTSIZE, secno ++) { | |||
| readsect((void *)va, secno); | |||
| } | |||
| } | |||
| /* bootmain - the entry of bootloader */ | |||
| void | |||
| bootmain(void) { | |||
| // read the 1st page off disk | |||
| readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0); | |||
| // is this a valid ELF? | |||
| if (ELFHDR->e_magic != ELF_MAGIC) { | |||
| goto bad; | |||
| } | |||
| struct proghdr *ph, *eph; | |||
| // load each program segment (ignores ph flags) | |||
| ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff); | |||
| eph = ph + ELFHDR->e_phnum; | |||
| for (; ph < eph; ph ++) { | |||
| readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset); | |||
| } | |||
| // call the entry point from the ELF header | |||
| // note: does not return | |||
| ((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))(); | |||
| bad: | |||
| outw(0x8A00, 0x8A00); | |||
| outw(0x8A00, 0x8E00); | |||
| /* do nothing */ | |||
| while (1); | |||
| } | |||
| @ -0,0 +1,27 @@ | |||
| #ifndef __KERN_DEBUG_ASSERT_H__ | |||
| #define __KERN_DEBUG_ASSERT_H__ | |||
| #include <defs.h> | |||
| void __warn(const char *file, int line, const char *fmt, ...); | |||
| void __noreturn __panic(const char *file, int line, const char *fmt, ...); | |||
| #define warn(...) \ | |||
| __warn(__FILE__, __LINE__, __VA_ARGS__) | |||
| #define panic(...) \ | |||
| __panic(__FILE__, __LINE__, __VA_ARGS__) | |||
| #define assert(x) \ | |||
| do { \ | |||
| if (!(x)) { \ | |||
| panic("assertion failed: %s", #x); \ | |||
| } \ | |||
| } while (0) | |||
| // static_assert(x) will generate a compile-time error if 'x' is false. | |||
| #define static_assert(x) \ | |||
| switch (x) { case 0: case (x): ; } | |||
| #endif /* !__KERN_DEBUG_ASSERT_H__ */ | |||
| @ -0,0 +1,365 @@ | |||
| #include <defs.h> | |||
| #include <x86.h> | |||
| #include <stab.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <memlayout.h> | |||
| #include <sync.h> | |||
| #include <vmm.h> | |||
| #include <proc.h> | |||
| #include <kdebug.h> | |||
| #include <kmonitor.h> | |||
| #include <assert.h> | |||
| #define STACKFRAME_DEPTH 20 | |||
| extern const struct stab __STAB_BEGIN__[]; // beginning of stabs table | |||
| extern const struct stab __STAB_END__[]; // end of stabs table | |||
| extern const char __STABSTR_BEGIN__[]; // beginning of string table | |||
| extern const char __STABSTR_END__[]; // end of string table | |||
| /* debug information about a particular instruction pointer */ | |||
| struct eipdebuginfo { | |||
| const char *eip_file; // source code filename for eip | |||
| int eip_line; // source code line number for eip | |||
| const char *eip_fn_name; // name of function containing eip | |||
| int eip_fn_namelen; // length of function's name | |||
| uintptr_t eip_fn_addr; // start address of function | |||
| int eip_fn_narg; // number of function arguments | |||
| }; | |||
| /* user STABS data structure */ | |||
| struct userstabdata { | |||
| const struct stab *stabs; | |||
| const struct stab *stab_end; | |||
| const char *stabstr; | |||
| const char *stabstr_end; | |||
| }; | |||
| /* * | |||
| * stab_binsearch - according to the input, the initial value of | |||
| * range [*@region_left, *@region_right], find a single stab entry | |||
| * that includes the address @addr and matches the type @type, | |||
| * and then save its boundary to the locations that pointed | |||
| * by @region_left and @region_right. | |||
| * | |||
| * Some stab types are arranged in increasing order by instruction address. | |||
| * For example, N_FUN stabs (stab entries with n_type == N_FUN), which | |||
| * mark functions, and N_SO stabs, which mark source files. | |||
| * | |||
| * Given an instruction address, this function finds the single stab entry | |||
| * of type @type that contains that address. | |||
| * | |||
| * The search takes place within the range [*@region_left, *@region_right]. | |||
| * Thus, to search an entire set of N stabs, you might do: | |||
| * | |||
| * left = 0; | |||
| * right = N - 1; (rightmost stab) | |||
| * stab_binsearch(stabs, &left, &right, type, addr); | |||
| * | |||
| * The search modifies *region_left and *region_right to bracket the @addr. | |||
| * *@region_left points to the matching stab that contains @addr, | |||
| * and *@region_right points just before the next stab. | |||
| * If *@region_left > *region_right, then @addr is not contained in any | |||
| * matching stab. | |||
| * | |||
| * For example, given these N_SO stabs: | |||
| * Index Type Address | |||
| * 0 SO f0100000 | |||
| * 13 SO f0100040 | |||
| * 117 SO f0100176 | |||
| * 118 SO f0100178 | |||
| * 555 SO f0100652 | |||
| * 556 SO f0100654 | |||
| * 657 SO f0100849 | |||
| * this code: | |||
| * left = 0, right = 657; | |||
| * stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184); | |||
| * will exit setting left = 118, right = 554. | |||
| * */ | |||
| static void | |||
| stab_binsearch(const struct stab *stabs, int *region_left, int *region_right, | |||
| int type, uintptr_t addr) { | |||
| int l = *region_left, r = *region_right, any_matches = 0; | |||
| while (l <= r) { | |||
| int true_m = (l + r) / 2, m = true_m; | |||
| // search for earliest stab with right type | |||
| while (m >= l && stabs[m].n_type != type) { | |||
| m --; | |||
| } | |||
| if (m < l) { // no match in [l, m] | |||
| l = true_m + 1; | |||
| continue; | |||
| } | |||
| // actual binary search | |||
| any_matches = 1; | |||
| if (stabs[m].n_value < addr) { | |||
| *region_left = m; | |||
| l = true_m + 1; | |||
| } else if (stabs[m].n_value > addr) { | |||
| *region_right = m - 1; | |||
| r = m - 1; | |||
| } else { | |||
| // exact match for 'addr', but continue loop to find | |||
| // *region_right | |||
| *region_left = m; | |||
| l = m; | |||
| addr ++; | |||
| } | |||
| } | |||
| if (!any_matches) { | |||
| *region_right = *region_left - 1; | |||
| } | |||
| else { | |||
| // find rightmost region containing 'addr' | |||
| l = *region_right; | |||
| for (; l > *region_left && stabs[l].n_type != type; l --) | |||
| /* do nothing */; | |||
| *region_left = l; | |||
| } | |||
| } | |||
| /* * | |||
| * debuginfo_eip - Fill in the @info structure with information about | |||
| * the specified instruction address, @addr. Returns 0 if information | |||
| * was found, and negative if not. But even if it returns negative it | |||
| * has stored some information into '*info'. | |||
| * */ | |||
| int | |||
| debuginfo_eip(uintptr_t addr, struct eipdebuginfo *info) { | |||
| const struct stab *stabs, *stab_end; | |||
| const char *stabstr, *stabstr_end; | |||
| info->eip_file = "<unknown>"; | |||
| info->eip_line = 0; | |||
| info->eip_fn_name = "<unknown>"; | |||
| info->eip_fn_namelen = 9; | |||
| info->eip_fn_addr = addr; | |||
| info->eip_fn_narg = 0; | |||
| // find the relevant set of stabs | |||
| if (addr >= KERNBASE) { | |||
| stabs = __STAB_BEGIN__; | |||
| stab_end = __STAB_END__; | |||
| stabstr = __STABSTR_BEGIN__; | |||
| stabstr_end = __STABSTR_END__; | |||
| } | |||
| else { | |||
| // user-program linker script, tools/user.ld puts the information about the | |||
| // program's stabs (included __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, | |||
| // and __STABSTR_END__) in a structure located at virtual address USTAB. | |||
| const struct userstabdata *usd = (struct userstabdata *)USTAB; | |||
| // make sure that debugger (current process) can access this memory | |||
| struct mm_struct *mm; | |||
| if (current == NULL || (mm = current->mm) == NULL) { | |||
| return -1; | |||
| } | |||
| if (!user_mem_check(mm, (uintptr_t)usd, sizeof(struct userstabdata), 0)) { | |||
| return -1; | |||
| } | |||
| stabs = usd->stabs; | |||
| stab_end = usd->stab_end; | |||
| stabstr = usd->stabstr; | |||
| stabstr_end = usd->stabstr_end; | |||
| // make sure the STABS and string table memory is valid | |||
| if (!user_mem_check(mm, (uintptr_t)stabs, (uintptr_t)stab_end - (uintptr_t)stabs, 0)) { | |||
| return -1; | |||
| } | |||
| if (!user_mem_check(mm, (uintptr_t)stabstr, stabstr_end - stabstr, 0)) { | |||
| return -1; | |||
| } | |||
| } | |||
| // String table validity checks | |||
| if (stabstr_end <= stabstr || stabstr_end[-1] != 0) { | |||
| return -1; | |||
| } | |||
| // Now we find the right stabs that define the function containing | |||
| // 'eip'. First, we find the basic source file containing 'eip'. | |||
| // Then, we look in that source file for the function. Then we look | |||
| // for the line number. | |||
| // Search the entire set of stabs for the source file (type N_SO). | |||
| int lfile = 0, rfile = (stab_end - stabs) - 1; | |||
| stab_binsearch(stabs, &lfile, &rfile, N_SO, addr); | |||
| if (lfile == 0) | |||
| return -1; | |||
| // Search within that file's stabs for the function definition | |||
| // (N_FUN). | |||
| int lfun = lfile, rfun = rfile; | |||
| int lline, rline; | |||
| stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr); | |||
| if (lfun <= rfun) { | |||
| // stabs[lfun] points to the function name | |||
| // in the string table, but check bounds just in case. | |||
| if (stabs[lfun].n_strx < stabstr_end - stabstr) { | |||
| info->eip_fn_name = stabstr + stabs[lfun].n_strx; | |||
| } | |||
| info->eip_fn_addr = stabs[lfun].n_value; | |||
| addr -= info->eip_fn_addr; | |||
| // Search within the function definition for the line number. | |||
| lline = lfun; | |||
| rline = rfun; | |||
| } else { | |||
| // Couldn't find function stab! Maybe we're in an assembly | |||
| // file. Search the whole file for the line number. | |||
| info->eip_fn_addr = addr; | |||
| lline = lfile; | |||
| rline = rfile; | |||
| } | |||
| info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name; | |||
| // Search within [lline, rline] for the line number stab. | |||
| // If found, set info->eip_line to the right line number. | |||
| // If not found, return -1. | |||
| stab_binsearch(stabs, &lline, &rline, N_SLINE, addr); | |||
| if (lline <= rline) { | |||
| info->eip_line = stabs[rline].n_desc; | |||
| } else { | |||
| return -1; | |||
| } | |||
| // Search backwards from the line number for the relevant filename stab. | |||
| // We can't just use the "lfile" stab because inlined functions | |||
| // can interpolate code from a different file! | |||
| // Such included source files use the N_SOL stab type. | |||
| while (lline >= lfile | |||
| && stabs[lline].n_type != N_SOL | |||
| && (stabs[lline].n_type != N_SO || !stabs[lline].n_value)) { | |||
| lline --; | |||
| } | |||
| if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr) { | |||
| info->eip_file = stabstr + stabs[lline].n_strx; | |||
| } | |||
| // Set eip_fn_narg to the number of arguments taken by the function, | |||
| // or 0 if there was no containing function. | |||
| if (lfun < rfun) { | |||
| for (lline = lfun + 1; | |||
| lline < rfun && stabs[lline].n_type == N_PSYM; | |||
| lline ++) { | |||
| info->eip_fn_narg ++; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| /* * | |||
| * print_kerninfo - print the information about kernel, including the location | |||
| * of kernel entry, the start addresses of data and text segements, the start | |||
| * address of free memory and how many memory that kernel has used. | |||
| * */ | |||
| void | |||
| print_kerninfo(void) { | |||
| extern char etext[], edata[], end[], kern_init[]; | |||
| cprintf("Special kernel symbols:\n"); | |||
| cprintf(" entry 0x%08x (phys)\n", kern_init); | |||
| cprintf(" etext 0x%08x (phys)\n", etext); | |||
| cprintf(" edata 0x%08x (phys)\n", edata); | |||
| cprintf(" end 0x%08x (phys)\n", end); | |||
| cprintf("Kernel executable memory footprint: %dKB\n", (end - kern_init + 1023)/1024); | |||
| } | |||
| /* * | |||
| * print_debuginfo - read and print the stat information for the address @eip, | |||
| * and info.eip_fn_addr should be the first address of the related function. | |||
| * */ | |||
| void | |||
| print_debuginfo(uintptr_t eip) { | |||
| struct eipdebuginfo info; | |||
| if (debuginfo_eip(eip, &info) != 0) { | |||
| cprintf(" <unknow>: -- 0x%08x --\n", eip); | |||
| } | |||
| else { | |||
| char fnname[256]; | |||
| int j; | |||
| for (j = 0; j < info.eip_fn_namelen; j ++) { | |||
| fnname[j] = info.eip_fn_name[j]; | |||
| } | |||
| fnname[j] = '\0'; | |||
| cprintf(" %s:%d: %s+%d\n", info.eip_file, info.eip_line, | |||
| fnname, eip - info.eip_fn_addr); | |||
| } | |||
| } | |||
| static __noinline uint32_t | |||
| read_eip(void) { | |||
| uint32_t eip; | |||
| asm volatile("movl 4(%%ebp), %0" : "=r" (eip)); | |||
| return eip; | |||
| } | |||
| /* * | |||
| * print_stackframe - print a list of the saved eip values from the nested 'call' | |||
| * instructions that led to the current point of execution | |||
| * | |||
| * The x86 stack pointer, namely esp, points to the lowest location on the stack | |||
| * that is currently in use. Everything below that location in stack is free. Pushing | |||
| * a value onto the stack will invole decreasing the stack pointer and then writing | |||
| * the value to the place that stack pointer pointes to. And popping a value do the | |||
| * opposite. | |||
| * | |||
| * The ebp (base pointer) register, in contrast, is associated with the stack | |||
| * primarily by software convention. On entry to a C function, the function's | |||
| * prologue code normally saves the previous function's base pointer by pushing | |||
| * it onto the stack, and then copies the current esp value into ebp for the duration | |||
| * of the function. If all the functions in a program obey this convention, | |||
| * then at any given point during the program's execution, it is possible to trace | |||
| * back through the stack by following the chain of saved ebp pointers and determining | |||
| * exactly what nested sequence of function calls caused this particular point in the | |||
| * program to be reached. This capability can be particularly useful, for example, | |||
| * when a particular function causes an assert failure or panic because bad arguments | |||
| * were passed to it, but you aren't sure who passed the bad arguments. A stack | |||
| * backtrace lets you find the offending function. | |||
| * | |||
| * The inline function read_ebp() can tell us the value of current ebp. And the | |||
| * non-inline function read_eip() is useful, it can read the value of current eip, | |||
| * since while calling this function, read_eip() can read the caller's eip from | |||
| * stack easily. | |||
| * | |||
| * In print_debuginfo(), the function debuginfo_eip() can get enough information about | |||
| * calling-chain. Finally print_stackframe() will trace and print them for debugging. | |||
| * | |||
| * Note that, the length of ebp-chain is limited. In boot/bootasm.S, before jumping | |||
| * to the kernel entry, the value of ebp has been set to zero, that's the boundary. | |||
| * */ | |||
| void | |||
| print_stackframe(void) { | |||
| /* LAB1 YOUR CODE : STEP 1 */ | |||
| /* (1) call read_ebp() to get the value of ebp. the type is (uint32_t); | |||
| * (2) call read_eip() to get the value of eip. the type is (uint32_t); | |||
| * (3) from 0 .. STACKFRAME_DEPTH | |||
| * (3.1) printf value of ebp, eip | |||
| * (3.2) (uint32_t)calling arguments [0..4] = the contents in address (unit32_t)ebp +2 [0..4] | |||
| * (3.3) cprintf("\n"); | |||
| * (3.4) call print_debuginfo(eip-1) to print the C calling function name and line number, etc. | |||
| * (3.5) popup a calling stackframe | |||
| * NOTICE: the calling funciton's return addr eip = ss:[ebp+4] | |||
| * the calling funciton's ebp = ss:[ebp] | |||
| */ | |||
| uint32_t ebp = read_ebp(), eip = read_eip(); | |||
| int i, j; | |||
| for (i = 0; ebp != 0 && i < STACKFRAME_DEPTH; i ++) { | |||
| cprintf("ebp:0x%08x eip:0x%08x args:", ebp, eip); | |||
| uint32_t *args = (uint32_t *)ebp + 2; | |||
| for (j = 0; j < 4; j ++) { | |||
| cprintf("0x%08x ", args[j]); | |||
| } | |||
| cprintf("\n"); | |||
| print_debuginfo(eip - 1); | |||
| eip = ((uint32_t *)ebp)[1]; | |||
| ebp = ((uint32_t *)ebp)[0]; | |||
| } | |||
| } | |||
| @ -0,0 +1,12 @@ | |||
| #ifndef __KERN_DEBUG_KDEBUG_H__ | |||
| #define __KERN_DEBUG_KDEBUG_H__ | |||
| #include <defs.h> | |||
| #include <trap.h> | |||
| void print_kerninfo(void); | |||
| void print_stackframe(void); | |||
| void print_debuginfo(uintptr_t eip); | |||
| #endif /* !__KERN_DEBUG_KDEBUG_H__ */ | |||
| @ -0,0 +1,132 @@ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <mmu.h> | |||
| #include <trap.h> | |||
| #include <kmonitor.h> | |||
| #include <kdebug.h> | |||
| /* * | |||
| * Simple command-line kernel monitor useful for controlling the | |||
| * kernel and exploring the system interactively. | |||
| * */ | |||
| struct command { | |||
| const char *name; | |||
| const char *desc; | |||
| // return -1 to force monitor to exit | |||
| int(*func)(int argc, char **argv, struct trapframe *tf); | |||
| }; | |||
| static struct command commands[] = { | |||
| {"help", "Display this list of commands.", mon_help}, | |||
| {"kerninfo", "Display information about the kernel.", mon_kerninfo}, | |||
| {"backtrace", "Print backtrace of stack frame.", mon_backtrace}, | |||
| }; | |||
| /* return if kernel is panic, in kern/debug/panic.c */ | |||
| bool is_kernel_panic(void); | |||
| #define NCOMMANDS (sizeof(commands)/sizeof(struct command)) | |||
| /***** Kernel monitor command interpreter *****/ | |||
| #define MAXARGS 16 | |||
| #define WHITESPACE " \t\n\r" | |||
| /* parse - parse the command buffer into whitespace-separated arguments */ | |||
| static int | |||
| parse(char *buf, char **argv) { | |||
| int argc = 0; | |||
| while (1) { | |||
| // find global whitespace | |||
| while (*buf != '\0' && strchr(WHITESPACE, *buf) != NULL) { | |||
| *buf ++ = '\0'; | |||
| } | |||
| if (*buf == '\0') { | |||
| break; | |||
| } | |||
| // save and scan past next arg | |||
| if (argc == MAXARGS - 1) { | |||
| cprintf("Too many arguments (max %d).\n", MAXARGS); | |||
| } | |||
| argv[argc ++] = buf; | |||
| while (*buf != '\0' && strchr(WHITESPACE, *buf) == NULL) { | |||
| buf ++; | |||
| } | |||
| } | |||
| return argc; | |||
| } | |||
| /* * | |||
| * runcmd - parse the input string, split it into separated arguments | |||
| * and then lookup and invoke some related commands/ | |||
| * */ | |||
| static int | |||
| runcmd(char *buf, struct trapframe *tf) { | |||
| char *argv[MAXARGS]; | |||
| int argc = parse(buf, argv); | |||
| if (argc == 0) { | |||
| return 0; | |||
| } | |||
| int i; | |||
| for (i = 0; i < NCOMMANDS; i ++) { | |||
| if (strcmp(commands[i].name, argv[0]) == 0) { | |||
| return commands[i].func(argc - 1, argv + 1, tf); | |||
| } | |||
| } | |||
| cprintf("Unknown command '%s'\n", argv[0]); | |||
| return 0; | |||
| } | |||
| /***** Implementations of basic kernel monitor commands *****/ | |||
| void | |||
| kmonitor(struct trapframe *tf) { | |||
| cprintf("Welcome to the kernel debug monitor!!\n"); | |||
| cprintf("Type 'help' for a list of commands.\n"); | |||
| if (tf != NULL) { | |||
| print_trapframe(tf); | |||
| } | |||
| char *buf; | |||
| while (1) { | |||
| if ((buf = readline("K> ")) != NULL) { | |||
| if (runcmd(buf, tf) < 0) { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /* mon_help - print the information about mon_* functions */ | |||
| int | |||
| mon_help(int argc, char **argv, struct trapframe *tf) { | |||
| int i; | |||
| for (i = 0; i < NCOMMANDS; i ++) { | |||
| cprintf("%s - %s\n", commands[i].name, commands[i].desc); | |||
| } | |||
| return 0; | |||
| } | |||
| /* * | |||
| * mon_kerninfo - call print_kerninfo in kern/debug/kdebug.c to | |||
| * print the memory occupancy in kernel. | |||
| * */ | |||
| int | |||
| mon_kerninfo(int argc, char **argv, struct trapframe *tf) { | |||
| print_kerninfo(); | |||
| return 0; | |||
| } | |||
| /* * | |||
| * mon_backtrace - call print_stackframe in kern/debug/kdebug.c to | |||
| * print a backtrace of the stack. | |||
| * */ | |||
| int | |||
| mon_backtrace(int argc, char **argv, struct trapframe *tf) { | |||
| print_stackframe(); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,19 @@ | |||
| #ifndef __KERN_DEBUG_MONITOR_H__ | |||
| #define __KERN_DEBUG_MONITOR_H__ | |||
| #include <trap.h> | |||
| void kmonitor(struct trapframe *tf); | |||
| int mon_help(int argc, char **argv, struct trapframe *tf); | |||
| int mon_kerninfo(int argc, char **argv, struct trapframe *tf); | |||
| int mon_backtrace(int argc, char **argv, struct trapframe *tf); | |||
| int mon_continue(int argc, char **argv, struct trapframe *tf); | |||
| int mon_step(int argc, char **argv, struct trapframe *tf); | |||
| int mon_breakpoint(int argc, char **argv, struct trapframe *tf); | |||
| int mon_watchpoint(int argc, char **argv, struct trapframe *tf); | |||
| int mon_delete_dr(int argc, char **argv, struct trapframe *tf); | |||
| int mon_list_dr(int argc, char **argv, struct trapframe *tf); | |||
| #endif /* !__KERN_DEBUG_MONITOR_H__ */ | |||
| @ -0,0 +1,49 @@ | |||
| #include <defs.h> | |||
| #include <stdio.h> | |||
| #include <intr.h> | |||
| #include <kmonitor.h> | |||
| static bool is_panic = 0; | |||
| /* * | |||
| * __panic - __panic is called on unresolvable fatal errors. it prints | |||
| * "panic: 'message'", and then enters the kernel monitor. | |||
| * */ | |||
| void | |||
| __panic(const char *file, int line, const char *fmt, ...) { | |||
| if (is_panic) { | |||
| goto panic_dead; | |||
| } | |||
| is_panic = 1; | |||
| // print the 'message' | |||
| va_list ap; | |||
| va_start(ap, fmt); | |||
| cprintf("kernel panic at %s:%d:\n ", file, line); | |||
| vcprintf(fmt, ap); | |||
| cprintf("\n"); | |||
| va_end(ap); | |||
| panic_dead: | |||
| intr_disable(); | |||
| while (1) { | |||
| kmonitor(NULL); | |||
| } | |||
| } | |||
| /* __warn - like panic, but don't */ | |||
| void | |||
| __warn(const char *file, int line, const char *fmt, ...) { | |||
| va_list ap; | |||
| va_start(ap, fmt); | |||
| cprintf("kernel warning at %s:%d:\n ", file, line); | |||
| vcprintf(fmt, ap); | |||
| cprintf("\n"); | |||
| va_end(ap); | |||
| } | |||
| bool | |||
| is_kernel_panic(void) { | |||
| return is_panic; | |||
| } | |||
| @ -0,0 +1,54 @@ | |||
| #ifndef __KERN_DEBUG_STAB_H__ | |||
| #define __KERN_DEBUG_STAB_H__ | |||
| #include <defs.h> | |||
| /* * | |||
| * STABS debugging info | |||
| * | |||
| * The kernel debugger can understand some debugging information in | |||
| * the STABS format. For more information on this format, see | |||
| * http://sources.redhat.com/gdb/onlinedocs/stabs_toc.html | |||
| * | |||
| * The constants below define some symbol types used by various debuggers | |||
| * and compilers. Kernel uses the N_SO, N_SOL, N_FUN, and N_SLINE types. | |||
| * */ | |||
| #define N_GSYM 0x20 // global symbol | |||
| #define N_FNAME 0x22 // F77 function name | |||
| #define N_FUN 0x24 // procedure name | |||
| #define N_STSYM 0x26 // data segment variable | |||
| #define N_LCSYM 0x28 // bss segment variable | |||
| #define N_MAIN 0x2a // main function name | |||
| #define N_PC 0x30 // global Pascal symbol | |||
| #define N_RSYM 0x40 // register variable | |||
| #define N_SLINE 0x44 // text segment line number | |||
| #define N_DSLINE 0x46 // data segment line number | |||
| #define N_BSLINE 0x48 // bss segment line number | |||
| #define N_SSYM 0x60 // structure/union element | |||
| #define N_SO 0x64 // main source file name | |||
| #define N_LSYM 0x80 // stack variable | |||
| #define N_BINCL 0x82 // include file beginning | |||
| #define N_SOL 0x84 // included source file name | |||
| #define N_PSYM 0xa0 // parameter variable | |||
| #define N_EINCL 0xa2 // include file end | |||
| #define N_ENTRY 0xa4 // alternate entry point | |||
| #define N_LBRAC 0xc0 // left bracket | |||
| #define N_EXCL 0xc2 // deleted include file | |||
| #define N_RBRAC 0xe0 // right bracket | |||
| #define N_BCOMM 0xe2 // begin common | |||
| #define N_ECOMM 0xe4 // end common | |||
| #define N_ECOML 0xe8 // end common (local name) | |||
| #define N_LENG 0xfe // length of preceding entry | |||
| /* Entries in the STABS table are formatted as follows. */ | |||
| struct stab { | |||
| uint32_t n_strx; // index into string table of name | |||
| uint8_t n_type; // type of symbol | |||
| uint8_t n_other; // misc info (usually empty) | |||
| uint16_t n_desc; // description field | |||
| uintptr_t n_value; // value of symbol | |||
| }; | |||
| #endif /* !__KERN_DEBUG_STAB_H__ */ | |||
| @ -0,0 +1,45 @@ | |||
| #include <x86.h> | |||
| #include <trap.h> | |||
| #include <stdio.h> | |||
| #include <picirq.h> | |||
| /* * | |||
| * Support for time-related hardware gadgets - the 8253 timer, | |||
| * which generates interruptes on IRQ-0. | |||
| * */ | |||
| #define IO_TIMER1 0x040 // 8253 Timer #1 | |||
| /* * | |||
| * Frequency of all three count-down timers; (TIMER_FREQ/freq) | |||
| * is the appropriate count to generate a frequency of freq Hz. | |||
| * */ | |||
| #define TIMER_FREQ 1193182 | |||
| #define TIMER_DIV(x) ((TIMER_FREQ + (x) / 2) / (x)) | |||
| #define TIMER_MODE (IO_TIMER1 + 3) // timer mode port | |||
| #define TIMER_SEL0 0x00 // select counter 0 | |||
| #define TIMER_RATEGEN 0x04 // mode 2, rate generator | |||
| #define TIMER_16BIT 0x30 // r/w counter 16 bits, LSB first | |||
| volatile size_t ticks; | |||
| /* * | |||
| * clock_init - initialize 8253 clock to interrupt 100 times per second, | |||
| * and then enable IRQ_TIMER. | |||
| * */ | |||
| void | |||
| clock_init(void) { | |||
| // set 8253 timer-chip | |||
| outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); | |||
| outb(IO_TIMER1, TIMER_DIV(100) % 256); | |||
| outb(IO_TIMER1, TIMER_DIV(100) / 256); | |||
| // initialize time counter 'ticks' to zero | |||
| ticks = 0; | |||
| cprintf("++ setup timer interrupts\n"); | |||
| pic_enable(IRQ_TIMER); | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| #ifndef __KERN_DRIVER_CLOCK_H__ | |||
| #define __KERN_DRIVER_CLOCK_H__ | |||
| #include <defs.h> | |||
| extern volatile size_t ticks; | |||
| void clock_init(void); | |||
| #endif /* !__KERN_DRIVER_CLOCK_H__ */ | |||
| @ -0,0 +1,465 @@ | |||
| #include <defs.h> | |||
| #include <x86.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <kbdreg.h> | |||
| #include <picirq.h> | |||
| #include <trap.h> | |||
| #include <memlayout.h> | |||
| #include <sync.h> | |||
| /* stupid I/O delay routine necessitated by historical PC design flaws */ | |||
| static void | |||
| delay(void) { | |||
| inb(0x84); | |||
| inb(0x84); | |||
| inb(0x84); | |||
| inb(0x84); | |||
| } | |||
| /***** Serial I/O code *****/ | |||
| #define COM1 0x3F8 | |||
| #define COM_RX 0 // In: Receive buffer (DLAB=0) | |||
| #define COM_TX 0 // Out: Transmit buffer (DLAB=0) | |||
| #define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1) | |||
| #define COM_DLM 1 // Out: Divisor Latch High (DLAB=1) | |||
| #define COM_IER 1 // Out: Interrupt Enable Register | |||
| #define COM_IER_RDI 0x01 // Enable receiver data interrupt | |||
| #define COM_IIR 2 // In: Interrupt ID Register | |||
| #define COM_FCR 2 // Out: FIFO Control Register | |||
| #define COM_LCR 3 // Out: Line Control Register | |||
| #define COM_LCR_DLAB 0x80 // Divisor latch access bit | |||
| #define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits | |||
| #define COM_MCR 4 // Out: Modem Control Register | |||
| #define COM_MCR_RTS 0x02 // RTS complement | |||
| #define COM_MCR_DTR 0x01 // DTR complement | |||
| #define COM_MCR_OUT2 0x08 // Out2 complement | |||
| #define COM_LSR 5 // In: Line Status Register | |||
| #define COM_LSR_DATA 0x01 // Data available | |||
| #define COM_LSR_TXRDY 0x20 // Transmit buffer avail | |||
| #define COM_LSR_TSRE 0x40 // Transmitter off | |||
| #define MONO_BASE 0x3B4 | |||
| #define MONO_BUF 0xB0000 | |||
| #define CGA_BASE 0x3D4 | |||
| #define CGA_BUF 0xB8000 | |||
| #define CRT_ROWS 25 | |||
| #define CRT_COLS 80 | |||
| #define CRT_SIZE (CRT_ROWS * CRT_COLS) | |||
| #define LPTPORT 0x378 | |||
| static uint16_t *crt_buf; | |||
| static uint16_t crt_pos; | |||
| static uint16_t addr_6845; | |||
| /* TEXT-mode CGA/VGA display output */ | |||
| static void | |||
| cga_init(void) { | |||
| volatile uint16_t *cp = (uint16_t *)(CGA_BUF + KERNBASE); | |||
| uint16_t was = *cp; | |||
| *cp = (uint16_t) 0xA55A; | |||
| if (*cp != 0xA55A) { | |||
| cp = (uint16_t*)(MONO_BUF + KERNBASE); | |||
| addr_6845 = MONO_BASE; | |||
| } else { | |||
| *cp = was; | |||
| addr_6845 = CGA_BASE; | |||
| } | |||
| // Extract cursor location | |||
| uint32_t pos; | |||
| outb(addr_6845, 14); | |||
| pos = inb(addr_6845 + 1) << 8; | |||
| outb(addr_6845, 15); | |||
| pos |= inb(addr_6845 + 1); | |||
| crt_buf = (uint16_t*) cp; | |||
| crt_pos = pos; | |||
| } | |||
| static bool serial_exists = 0; | |||
| static void | |||
| serial_init(void) { | |||
| // Turn off the FIFO | |||
| outb(COM1 + COM_FCR, 0); | |||
| // Set speed; requires DLAB latch | |||
| outb(COM1 + COM_LCR, COM_LCR_DLAB); | |||
| outb(COM1 + COM_DLL, (uint8_t) (115200 / 9600)); | |||
| outb(COM1 + COM_DLM, 0); | |||
| // 8 data bits, 1 stop bit, parity off; turn off DLAB latch | |||
| outb(COM1 + COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB); | |||
| // No modem controls | |||
| outb(COM1 + COM_MCR, 0); | |||
| // Enable rcv interrupts | |||
| outb(COM1 + COM_IER, COM_IER_RDI); | |||
| // Clear any preexisting overrun indications and interrupts | |||
| // Serial port doesn't exist if COM_LSR returns 0xFF | |||
| serial_exists = (inb(COM1 + COM_LSR) != 0xFF); | |||
| (void) inb(COM1+COM_IIR); | |||
| (void) inb(COM1+COM_RX); | |||
| if (serial_exists) { | |||
| pic_enable(IRQ_COM1); | |||
| } | |||
| } | |||
| static void | |||
| lpt_putc_sub(int c) { | |||
| int i; | |||
| for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) { | |||
| delay(); | |||
| } | |||
| outb(LPTPORT + 0, c); | |||
| outb(LPTPORT + 2, 0x08 | 0x04 | 0x01); | |||
| outb(LPTPORT + 2, 0x08); | |||
| } | |||
| /* lpt_putc - copy console output to parallel port */ | |||
| static void | |||
| lpt_putc(int c) { | |||
| if (c != '\b') { | |||
| lpt_putc_sub(c); | |||
| } | |||
| else { | |||
| lpt_putc_sub('\b'); | |||
| lpt_putc_sub(' '); | |||
| lpt_putc_sub('\b'); | |||
| } | |||
| } | |||
| /* cga_putc - print character to console */ | |||
| static void | |||
| cga_putc(int c) { | |||
| // set black on white | |||
| if (!(c & ~0xFF)) { | |||
| c |= 0x0700; | |||
| } | |||
| switch (c & 0xff) { | |||
| case '\b': | |||
| if (crt_pos > 0) { | |||
| crt_pos --; | |||
| crt_buf[crt_pos] = (c & ~0xff) | ' '; | |||
| } | |||
| break; | |||
| case '\n': | |||
| crt_pos += CRT_COLS; | |||
| case '\r': | |||
| crt_pos -= (crt_pos % CRT_COLS); | |||
| break; | |||
| default: | |||
| crt_buf[crt_pos ++] = c; // write the character | |||
| break; | |||
| } | |||
| // What is the purpose of this? | |||
| if (crt_pos >= CRT_SIZE) { | |||
| int i; | |||
| memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t)); | |||
| for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i ++) { | |||
| crt_buf[i] = 0x0700 | ' '; | |||
| } | |||
| crt_pos -= CRT_COLS; | |||
| } | |||
| // move that little blinky thing | |||
| outb(addr_6845, 14); | |||
| outb(addr_6845 + 1, crt_pos >> 8); | |||
| outb(addr_6845, 15); | |||
| outb(addr_6845 + 1, crt_pos); | |||
| } | |||
| static void | |||
| serial_putc_sub(int c) { | |||
| int i; | |||
| for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) { | |||
| delay(); | |||
| } | |||
| outb(COM1 + COM_TX, c); | |||
| } | |||
| /* serial_putc - print character to serial port */ | |||
| static void | |||
| serial_putc(int c) { | |||
| if (c != '\b') { | |||
| serial_putc_sub(c); | |||
| } | |||
| else { | |||
| serial_putc_sub('\b'); | |||
| serial_putc_sub(' '); | |||
| serial_putc_sub('\b'); | |||
| } | |||
| } | |||
| /* * | |||
| * Here we manage the console input buffer, where we stash characters | |||
| * received from the keyboard or serial port whenever the corresponding | |||
| * interrupt occurs. | |||
| * */ | |||
| #define CONSBUFSIZE 512 | |||
| static struct { | |||
| uint8_t buf[CONSBUFSIZE]; | |||
| uint32_t rpos; | |||
| uint32_t wpos; | |||
| } cons; | |||
| /* * | |||
| * cons_intr - called by device interrupt routines to feed input | |||
| * characters into the circular console input buffer. | |||
| * */ | |||
| static void | |||
| cons_intr(int (*proc)(void)) { | |||
| int c; | |||
| while ((c = (*proc)()) != -1) { | |||
| if (c != 0) { | |||
| cons.buf[cons.wpos ++] = c; | |||
| if (cons.wpos == CONSBUFSIZE) { | |||
| cons.wpos = 0; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /* serial_proc_data - get data from serial port */ | |||
| static int | |||
| serial_proc_data(void) { | |||
| if (!(inb(COM1 + COM_LSR) & COM_LSR_DATA)) { | |||
| return -1; | |||
| } | |||
| int c = inb(COM1 + COM_RX); | |||
| if (c == 127) { | |||
| c = '\b'; | |||
| } | |||
| return c; | |||
| } | |||
| /* serial_intr - try to feed input characters from serial port */ | |||
| void | |||
| serial_intr(void) { | |||
| if (serial_exists) { | |||
| cons_intr(serial_proc_data); | |||
| } | |||
| } | |||
| /***** Keyboard input code *****/ | |||
| #define NO 0 | |||
| #define SHIFT (1<<0) | |||
| #define CTL (1<<1) | |||
| #define ALT (1<<2) | |||
| #define CAPSLOCK (1<<3) | |||
| #define NUMLOCK (1<<4) | |||
| #define SCROLLLOCK (1<<5) | |||
| #define E0ESC (1<<6) | |||
| static uint8_t shiftcode[256] = { | |||
| [0x1D] CTL, | |||
| [0x2A] SHIFT, | |||
| [0x36] SHIFT, | |||
| [0x38] ALT, | |||
| [0x9D] CTL, | |||
| [0xB8] ALT | |||
| }; | |||
| static uint8_t togglecode[256] = { | |||
| [0x3A] CAPSLOCK, | |||
| [0x45] NUMLOCK, | |||
| [0x46] SCROLLLOCK | |||
| }; | |||
| static uint8_t normalmap[256] = { | |||
| NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 | |||
| '7', '8', '9', '0', '-', '=', '\b', '\t', | |||
| 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 | |||
| 'o', 'p', '[', ']', '\n', NO, 'a', 's', | |||
| 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 | |||
| '\'', '`', NO, '\\', 'z', 'x', 'c', 'v', | |||
| 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30 | |||
| NO, ' ', NO, NO, NO, NO, NO, NO, | |||
| NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 | |||
| '8', '9', '-', '4', '5', '6', '+', '1', | |||
| '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 | |||
| [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/, | |||
| [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP, | |||
| [0xC9] KEY_PGUP, [0xCB] KEY_LF, | |||
| [0xCD] KEY_RT, [0xCF] KEY_END, | |||
| [0xD0] KEY_DN, [0xD1] KEY_PGDN, | |||
| [0xD2] KEY_INS, [0xD3] KEY_DEL | |||
| }; | |||
| static uint8_t shiftmap[256] = { | |||
| NO, 033, '!', '@', '#', '$', '%', '^', // 0x00 | |||
| '&', '*', '(', ')', '_', '+', '\b', '\t', | |||
| 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10 | |||
| 'O', 'P', '{', '}', '\n', NO, 'A', 'S', | |||
| 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20 | |||
| '"', '~', NO, '|', 'Z', 'X', 'C', 'V', | |||
| 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30 | |||
| NO, ' ', NO, NO, NO, NO, NO, NO, | |||
| NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 | |||
| '8', '9', '-', '4', '5', '6', '+', '1', | |||
| '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 | |||
| [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/, | |||
| [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP, | |||
| [0xC9] KEY_PGUP, [0xCB] KEY_LF, | |||
| [0xCD] KEY_RT, [0xCF] KEY_END, | |||
| [0xD0] KEY_DN, [0xD1] KEY_PGDN, | |||
| [0xD2] KEY_INS, [0xD3] KEY_DEL | |||
| }; | |||
| #define C(x) (x - '@') | |||
| static uint8_t ctlmap[256] = { | |||
| NO, NO, NO, NO, NO, NO, NO, NO, | |||
| NO, NO, NO, NO, NO, NO, NO, NO, | |||
| C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'), | |||
| C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'), | |||
| C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO, | |||
| NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'), | |||
| C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO, | |||
| [0x97] KEY_HOME, | |||
| [0xB5] C('/'), [0xC8] KEY_UP, | |||
| [0xC9] KEY_PGUP, [0xCB] KEY_LF, | |||
| [0xCD] KEY_RT, [0xCF] KEY_END, | |||
| [0xD0] KEY_DN, [0xD1] KEY_PGDN, | |||
| [0xD2] KEY_INS, [0xD3] KEY_DEL | |||
| }; | |||
| static uint8_t *charcode[4] = { | |||
| normalmap, | |||
| shiftmap, | |||
| ctlmap, | |||
| ctlmap | |||
| }; | |||
| /* * | |||
| * kbd_proc_data - get data from keyboard | |||
| * | |||
| * The kbd_proc_data() function gets data from the keyboard. | |||
| * If we finish a character, return it, else 0. And return -1 if no data. | |||
| * */ | |||
| static int | |||
| kbd_proc_data(void) { | |||
| int c; | |||
| uint8_t data; | |||
| static uint32_t shift; | |||
| if ((inb(KBSTATP) & KBS_DIB) == 0) { | |||
| return -1; | |||
| } | |||
| data = inb(KBDATAP); | |||
| if (data == 0xE0) { | |||
| // E0 escape character | |||
| shift |= E0ESC; | |||
| return 0; | |||
| } else if (data & 0x80) { | |||
| // Key released | |||
| data = (shift & E0ESC ? data : data & 0x7F); | |||
| shift &= ~(shiftcode[data] | E0ESC); | |||
| return 0; | |||
| } else if (shift & E0ESC) { | |||
| // Last character was an E0 escape; or with 0x80 | |||
| data |= 0x80; | |||
| shift &= ~E0ESC; | |||
| } | |||
| shift |= shiftcode[data]; | |||
| shift ^= togglecode[data]; | |||
| c = charcode[shift & (CTL | SHIFT)][data]; | |||
| if (shift & CAPSLOCK) { | |||
| if ('a' <= c && c <= 'z') | |||
| c += 'A' - 'a'; | |||
| else if ('A' <= c && c <= 'Z') | |||
| c += 'a' - 'A'; | |||
| } | |||
| // Process special keys | |||
| // Ctrl-Alt-Del: reboot | |||
| if (!(~shift & (CTL | ALT)) && c == KEY_DEL) { | |||
| cprintf("Rebooting!\n"); | |||
| outb(0x92, 0x3); // courtesy of Chris Frost | |||
| } | |||
| return c; | |||
| } | |||
| /* kbd_intr - try to feed input characters from keyboard */ | |||
| static void | |||
| kbd_intr(void) { | |||
| cons_intr(kbd_proc_data); | |||
| } | |||
| static void | |||
| kbd_init(void) { | |||
| // drain the kbd buffer | |||
| kbd_intr(); | |||
| pic_enable(IRQ_KBD); | |||
| } | |||
| /* cons_init - initializes the console devices */ | |||
| void | |||
| cons_init(void) { | |||
| cga_init(); | |||
| serial_init(); | |||
| kbd_init(); | |||
| if (!serial_exists) { | |||
| cprintf("serial port does not exist!!\n"); | |||
| } | |||
| } | |||
| /* cons_putc - print a single character @c to console devices */ | |||
| void | |||
| cons_putc(int c) { | |||
| bool intr_flag; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| lpt_putc(c); | |||
| cga_putc(c); | |||
| serial_putc(c); | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| } | |||
| /* * | |||
| * cons_getc - return the next input character from console, | |||
| * or 0 if none waiting. | |||
| * */ | |||
| int | |||
| cons_getc(void) { | |||
| int c = 0; | |||
| bool intr_flag; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| // poll for any pending input characters, | |||
| // so that this function works even when interrupts are disabled | |||
| // (e.g., when called from the kernel monitor). | |||
| serial_intr(); | |||
| kbd_intr(); | |||
| // grab the next character from the input buffer. | |||
| if (cons.rpos != cons.wpos) { | |||
| c = cons.buf[cons.rpos ++]; | |||
| if (cons.rpos == CONSBUFSIZE) { | |||
| cons.rpos = 0; | |||
| } | |||
| } | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| return c; | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| #ifndef __KERN_DRIVER_CONSOLE_H__ | |||
| #define __KERN_DRIVER_CONSOLE_H__ | |||
| void cons_init(void); | |||
| void cons_putc(int c); | |||
| int cons_getc(void); | |||
| void serial_intr(void); | |||
| void kbd_intr(void); | |||
| #endif /* !__KERN_DRIVER_CONSOLE_H__ */ | |||
| @ -0,0 +1,214 @@ | |||
| #include <defs.h> | |||
| #include <stdio.h> | |||
| #include <trap.h> | |||
| #include <picirq.h> | |||
| #include <fs.h> | |||
| #include <ide.h> | |||
| #include <x86.h> | |||
| #include <assert.h> | |||
| #define ISA_DATA 0x00 | |||
| #define ISA_ERROR 0x01 | |||
| #define ISA_PRECOMP 0x01 | |||
| #define ISA_CTRL 0x02 | |||
| #define ISA_SECCNT 0x02 | |||
| #define ISA_SECTOR 0x03 | |||
| #define ISA_CYL_LO 0x04 | |||
| #define ISA_CYL_HI 0x05 | |||
| #define ISA_SDH 0x06 | |||
| #define ISA_COMMAND 0x07 | |||
| #define ISA_STATUS 0x07 | |||
| #define IDE_BSY 0x80 | |||
| #define IDE_DRDY 0x40 | |||
| #define IDE_DF 0x20 | |||
| #define IDE_DRQ 0x08 | |||
| #define IDE_ERR 0x01 | |||
| #define IDE_CMD_READ 0x20 | |||
| #define IDE_CMD_WRITE 0x30 | |||
| #define IDE_CMD_IDENTIFY 0xEC | |||
| #define IDE_IDENT_SECTORS 20 | |||
| #define IDE_IDENT_MODEL 54 | |||
| #define IDE_IDENT_CAPABILITIES 98 | |||
| #define IDE_IDENT_CMDSETS 164 | |||
| #define IDE_IDENT_MAX_LBA 120 | |||
| #define IDE_IDENT_MAX_LBA_EXT 200 | |||
| #define IO_BASE0 0x1F0 | |||
| #define IO_BASE1 0x170 | |||
| #define IO_CTRL0 0x3F4 | |||
| #define IO_CTRL1 0x374 | |||
| #define MAX_IDE 4 | |||
| #define MAX_NSECS 128 | |||
| #define MAX_DISK_NSECS 0x10000000U | |||
| #define VALID_IDE(ideno) (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid)) | |||
| static const struct { | |||
| unsigned short base; // I/O Base | |||
| unsigned short ctrl; // Control Base | |||
| } channels[2] = { | |||
| {IO_BASE0, IO_CTRL0}, | |||
| {IO_BASE1, IO_CTRL1}, | |||
| }; | |||
| #define IO_BASE(ideno) (channels[(ideno) >> 1].base) | |||
| #define IO_CTRL(ideno) (channels[(ideno) >> 1].ctrl) | |||
| static struct ide_device { | |||
| unsigned char valid; // 0 or 1 (If Device Really Exists) | |||
| unsigned int sets; // Commend Sets Supported | |||
| unsigned int size; // Size in Sectors | |||
| unsigned char model[41]; // Model in String | |||
| } ide_devices[MAX_IDE]; | |||
| static int | |||
| ide_wait_ready(unsigned short iobase, bool check_error) { | |||
| int r; | |||
| while ((r = inb(iobase + ISA_STATUS)) & IDE_BSY) | |||
| /* nothing */; | |||
| if (check_error && (r & (IDE_DF | IDE_ERR)) != 0) { | |||
| return -1; | |||
| } | |||
| return 0; | |||
| } | |||
| void | |||
| ide_init(void) { | |||
| static_assert((SECTSIZE % 4) == 0); | |||
| unsigned short ideno, iobase; | |||
| for (ideno = 0; ideno < MAX_IDE; ideno ++) { | |||
| /* assume that no device here */ | |||
| ide_devices[ideno].valid = 0; | |||
| iobase = IO_BASE(ideno); | |||
| /* wait device ready */ | |||
| ide_wait_ready(iobase, 0); | |||
| /* step1: select drive */ | |||
| outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4)); | |||
| ide_wait_ready(iobase, 0); | |||
| /* step2: send ATA identify command */ | |||
| outb(iobase + ISA_COMMAND, IDE_CMD_IDENTIFY); | |||
| ide_wait_ready(iobase, 0); | |||
| /* step3: polling */ | |||
| if (inb(iobase + ISA_STATUS) == 0 || ide_wait_ready(iobase, 1) != 0) { | |||
| continue ; | |||
| } | |||
| /* device is ok */ | |||
| ide_devices[ideno].valid = 1; | |||
| /* read identification space of the device */ | |||
| unsigned int buffer[128]; | |||
| insl(iobase + ISA_DATA, buffer, sizeof(buffer) / sizeof(unsigned int)); | |||
| unsigned char *ident = (unsigned char *)buffer; | |||
| unsigned int sectors; | |||
| unsigned int cmdsets = *(unsigned int *)(ident + IDE_IDENT_CMDSETS); | |||
| /* device use 48-bits or 28-bits addressing */ | |||
| if (cmdsets & (1 << 26)) { | |||
| sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA_EXT); | |||
| } | |||
| else { | |||
| sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA); | |||
| } | |||
| ide_devices[ideno].sets = cmdsets; | |||
| ide_devices[ideno].size = sectors; | |||
| /* check if supports LBA */ | |||
| assert((*(unsigned short *)(ident + IDE_IDENT_CAPABILITIES) & 0x200) != 0); | |||
| unsigned char *model = ide_devices[ideno].model, *data = ident + IDE_IDENT_MODEL; | |||
| unsigned int i, length = 40; | |||
| for (i = 0; i < length; i += 2) { | |||
| model[i] = data[i + 1], model[i + 1] = data[i]; | |||
| } | |||
| do { | |||
| model[i] = '\0'; | |||
| } while (i -- > 0 && model[i] == ' '); | |||
| cprintf("ide %d: %10u(sectors), '%s'.\n", ideno, ide_devices[ideno].size, ide_devices[ideno].model); | |||
| } | |||
| // enable ide interrupt | |||
| pic_enable(IRQ_IDE1); | |||
| pic_enable(IRQ_IDE2); | |||
| } | |||
| bool | |||
| ide_device_valid(unsigned short ideno) { | |||
| return VALID_IDE(ideno); | |||
| } | |||
| size_t | |||
| ide_device_size(unsigned short ideno) { | |||
| if (ide_device_valid(ideno)) { | |||
| return ide_devices[ideno].size; | |||
| } | |||
| return 0; | |||
| } | |||
| int | |||
| ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs) { | |||
| assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); | |||
| assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); | |||
| unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno); | |||
| ide_wait_ready(iobase, 0); | |||
| // generate interrupt | |||
| outb(ioctrl + ISA_CTRL, 0); | |||
| outb(iobase + ISA_SECCNT, nsecs); | |||
| outb(iobase + ISA_SECTOR, secno & 0xFF); | |||
| outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF); | |||
| outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF); | |||
| outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF)); | |||
| outb(iobase + ISA_COMMAND, IDE_CMD_READ); | |||
| int ret = 0; | |||
| for (; nsecs > 0; nsecs --, dst += SECTSIZE) { | |||
| if ((ret = ide_wait_ready(iobase, 1)) != 0) { | |||
| goto out; | |||
| } | |||
| insl(iobase, dst, SECTSIZE / sizeof(uint32_t)); | |||
| } | |||
| out: | |||
| return ret; | |||
| } | |||
| int | |||
| ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs) { | |||
| assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); | |||
| assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); | |||
| unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno); | |||
| ide_wait_ready(iobase, 0); | |||
| // generate interrupt | |||
| outb(ioctrl + ISA_CTRL, 0); | |||
| outb(iobase + ISA_SECCNT, nsecs); | |||
| outb(iobase + ISA_SECTOR, secno & 0xFF); | |||
| outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF); | |||
| outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF); | |||
| outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF)); | |||
| outb(iobase + ISA_COMMAND, IDE_CMD_WRITE); | |||
| int ret = 0; | |||
| for (; nsecs > 0; nsecs --, src += SECTSIZE) { | |||
| if ((ret = ide_wait_ready(iobase, 1)) != 0) { | |||
| goto out; | |||
| } | |||
| outsl(iobase, src, SECTSIZE / sizeof(uint32_t)); | |||
| } | |||
| out: | |||
| return ret; | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| #ifndef __KERN_DRIVER_IDE_H__ | |||
| #define __KERN_DRIVER_IDE_H__ | |||
| #include <defs.h> | |||
| void ide_init(void); | |||
| bool ide_device_valid(unsigned short ideno); | |||
| size_t ide_device_size(unsigned short ideno); | |||
| int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs); | |||
| int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs); | |||
| #endif /* !__KERN_DRIVER_IDE_H__ */ | |||
| @ -0,0 +1,15 @@ | |||
| #include <x86.h> | |||
| #include <intr.h> | |||
| /* intr_enable - enable irq interrupt */ | |||
| void | |||
| intr_enable(void) { | |||
| sti(); | |||
| } | |||
| /* intr_disable - disable irq interrupt */ | |||
| void | |||
| intr_disable(void) { | |||
| cli(); | |||
| } | |||
| @ -0,0 +1,8 @@ | |||
| #ifndef __KERN_DRIVER_INTR_H__ | |||
| #define __KERN_DRIVER_INTR_H__ | |||
| void intr_enable(void); | |||
| void intr_disable(void); | |||
| #endif /* !__KERN_DRIVER_INTR_H__ */ | |||
| @ -0,0 +1,84 @@ | |||
| #ifndef __KERN_DRIVER_KBDREG_H__ | |||
| #define __KERN_DRIVER_KBDREG_H__ | |||
| // Special keycodes | |||
| #define KEY_HOME 0xE0 | |||
| #define KEY_END 0xE1 | |||
| #define KEY_UP 0xE2 | |||
| #define KEY_DN 0xE3 | |||
| #define KEY_LF 0xE4 | |||
| #define KEY_RT 0xE5 | |||
| #define KEY_PGUP 0xE6 | |||
| #define KEY_PGDN 0xE7 | |||
| #define KEY_INS 0xE8 | |||
| #define KEY_DEL 0xE9 | |||
| /* This is i8042reg.h + kbdreg.h from NetBSD. */ | |||
| #define KBSTATP 0x64 // kbd controller status port(I) | |||
| #define KBS_DIB 0x01 // kbd data in buffer | |||
| #define KBS_IBF 0x02 // kbd input buffer low | |||
| #define KBS_WARM 0x04 // kbd input buffer low | |||
| #define BS_OCMD 0x08 // kbd output buffer has command | |||
| #define KBS_NOSEC 0x10 // kbd security lock not engaged | |||
| #define KBS_TERR 0x20 // kbd transmission error | |||
| #define KBS_RERR 0x40 // kbd receive error | |||
| #define KBS_PERR 0x80 // kbd parity error | |||
| #define KBCMDP 0x64 // kbd controller port(O) | |||
| #define KBC_RAMREAD 0x20 // read from RAM | |||
| #define KBC_RAMWRITE 0x60 // write to RAM | |||
| #define KBC_AUXDISABLE 0xa7 // disable auxiliary port | |||
| #define KBC_AUXENABLE 0xa8 // enable auxiliary port | |||
| #define KBC_AUXTEST 0xa9 // test auxiliary port | |||
| #define KBC_KBDECHO 0xd2 // echo to keyboard port | |||
| #define KBC_AUXECHO 0xd3 // echo to auxiliary port | |||
| #define KBC_AUXWRITE 0xd4 // write to auxiliary port | |||
| #define KBC_SELFTEST 0xaa // start self-test | |||
| #define KBC_KBDTEST 0xab // test keyboard port | |||
| #define KBC_KBDDISABLE 0xad // disable keyboard port | |||
| #define KBC_KBDENABLE 0xae // enable keyboard port | |||
| #define KBC_PULSE0 0xfe // pulse output bit 0 | |||
| #define KBC_PULSE1 0xfd // pulse output bit 1 | |||
| #define KBC_PULSE2 0xfb // pulse output bit 2 | |||
| #define KBC_PULSE3 0xf7 // pulse output bit 3 | |||
| #define KBDATAP 0x60 // kbd data port(I) | |||
| #define KBOUTP 0x60 // kbd data port(O) | |||
| #define K_RDCMDBYTE 0x20 | |||
| #define K_LDCMDBYTE 0x60 | |||
| #define KC8_TRANS 0x40 // convert to old scan codes | |||
| #define KC8_MDISABLE 0x20 // disable mouse | |||
| #define KC8_KDISABLE 0x10 // disable keyboard | |||
| #define KC8_IGNSEC 0x08 // ignore security lock | |||
| #define KC8_CPU 0x04 // exit from protected mode reset | |||
| #define KC8_MENABLE 0x02 // enable mouse interrupt | |||
| #define KC8_KENABLE 0x01 // enable keyboard interrupt | |||
| #define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE) | |||
| /* keyboard commands */ | |||
| #define KBC_RESET 0xFF // reset the keyboard | |||
| #define KBC_RESEND 0xFE // request the keyboard resend the last byte | |||
| #define KBC_SETDEFAULT 0xF6 // resets keyboard to its power-on defaults | |||
| #define KBC_DISABLE 0xF5 // as per KBC_SETDEFAULT, but also disable key scanning | |||
| #define KBC_ENABLE 0xF4 // enable key scanning | |||
| #define KBC_TYPEMATIC 0xF3 // set typematic rate and delay | |||
| #define KBC_SETTABLE 0xF0 // set scancode translation table | |||
| #define KBC_MODEIND 0xED // set mode indicators(i.e. LEDs) | |||
| #define KBC_ECHO 0xEE // request an echo from the keyboard | |||
| /* keyboard responses */ | |||
| #define KBR_EXTENDED 0xE0 // extended key sequence | |||
| #define KBR_RESEND 0xFE // needs resend of command | |||
| #define KBR_ACK 0xFA // received a valid command | |||
| #define KBR_OVERRUN 0x00 // flooded | |||
| #define KBR_FAILURE 0xFD // diagnosic failure | |||
| #define KBR_BREAK 0xF0 // break code prefix - sent on key release | |||
| #define KBR_RSTDONE 0xAA // reset complete | |||
| #define KBR_ECHO 0xEE // echo response | |||
| #endif /* !__KERN_DRIVER_KBDREG_H__ */ | |||
| @ -0,0 +1,86 @@ | |||
| #include <defs.h> | |||
| #include <x86.h> | |||
| #include <picirq.h> | |||
| // I/O Addresses of the two programmable interrupt controllers | |||
| #define IO_PIC1 0x20 // Master (IRQs 0-7) | |||
| #define IO_PIC2 0xA0 // Slave (IRQs 8-15) | |||
| #define IRQ_SLAVE 2 // IRQ at which slave connects to master | |||
| // Current IRQ mask. | |||
| // Initial IRQ mask has interrupt 2 enabled (for slave 8259A). | |||
| static uint16_t irq_mask = 0xFFFF & ~(1 << IRQ_SLAVE); | |||
| static bool did_init = 0; | |||
| static void | |||
| pic_setmask(uint16_t mask) { | |||
| irq_mask = mask; | |||
| if (did_init) { | |||
| outb(IO_PIC1 + 1, mask); | |||
| outb(IO_PIC2 + 1, mask >> 8); | |||
| } | |||
| } | |||
| void | |||
| pic_enable(unsigned int irq) { | |||
| pic_setmask(irq_mask & ~(1 << irq)); | |||
| } | |||
| /* pic_init - initialize the 8259A interrupt controllers */ | |||
| void | |||
| pic_init(void) { | |||
| did_init = 1; | |||
| // mask all interrupts | |||
| outb(IO_PIC1 + 1, 0xFF); | |||
| outb(IO_PIC2 + 1, 0xFF); | |||
| // Set up master (8259A-1) | |||
| // ICW1: 0001g0hi | |||
| // g: 0 = edge triggering, 1 = level triggering | |||
| // h: 0 = cascaded PICs, 1 = master only | |||
| // i: 0 = no ICW4, 1 = ICW4 required | |||
| outb(IO_PIC1, 0x11); | |||
| // ICW2: Vector offset | |||
| outb(IO_PIC1 + 1, IRQ_OFFSET); | |||
| // ICW3: (master PIC) bit mask of IR lines connected to slaves | |||
| // (slave PIC) 3-bit # of slave's connection to master | |||
| outb(IO_PIC1 + 1, 1 << IRQ_SLAVE); | |||
| // ICW4: 000nbmap | |||
| // n: 1 = special fully nested mode | |||
| // b: 1 = buffered mode | |||
| // m: 0 = slave PIC, 1 = master PIC | |||
| // (ignored when b is 0, as the master/slave role | |||
| // can be hardwired). | |||
| // a: 1 = Automatic EOI mode | |||
| // p: 0 = MCS-80/85 mode, 1 = intel x86 mode | |||
| outb(IO_PIC1 + 1, 0x3); | |||
| // Set up slave (8259A-2) | |||
| outb(IO_PIC2, 0x11); // ICW1 | |||
| outb(IO_PIC2 + 1, IRQ_OFFSET + 8); // ICW2 | |||
| outb(IO_PIC2 + 1, IRQ_SLAVE); // ICW3 | |||
| // NB Automatic EOI mode doesn't tend to work on the slave. | |||
| // Linux source code says it's "to be investigated". | |||
| outb(IO_PIC2 + 1, 0x3); // ICW4 | |||
| // OCW3: 0ef01prs | |||
| // ef: 0x = NOP, 10 = clear specific mask, 11 = set specific mask | |||
| // p: 0 = no polling, 1 = polling mode | |||
| // rs: 0x = NOP, 10 = read IRR, 11 = read ISR | |||
| outb(IO_PIC1, 0x68); // clear specific mask | |||
| outb(IO_PIC1, 0x0a); // read IRR by default | |||
| outb(IO_PIC2, 0x68); // OCW3 | |||
| outb(IO_PIC2, 0x0a); // OCW3 | |||
| if (irq_mask != 0xFFFF) { | |||
| pic_setmask(irq_mask); | |||
| } | |||
| } | |||
| @ -0,0 +1,10 @@ | |||
| #ifndef __KERN_DRIVER_PICIRQ_H__ | |||
| #define __KERN_DRIVER_PICIRQ_H__ | |||
| void pic_init(void); | |||
| void pic_enable(unsigned int irq); | |||
| #define IRQ_OFFSET 32 | |||
| #endif /* !__KERN_DRIVER_PICIRQ_H__ */ | |||
| @ -0,0 +1,12 @@ | |||
| #ifndef __KERN_FS_FS_H__ | |||
| #define __KERN_FS_FS_H__ | |||
| #include <mmu.h> | |||
| #define SECTSIZE 512 | |||
| #define PAGE_NSECT (PGSIZE / SECTSIZE) | |||
| #define SWAP_DEV_NO 1 | |||
| #endif /* !__KERN_FS_FS_H__ */ | |||
| @ -0,0 +1,27 @@ | |||
| #include <swap.h> | |||
| #include <swapfs.h> | |||
| #include <mmu.h> | |||
| #include <fs.h> | |||
| #include <ide.h> | |||
| #include <pmm.h> | |||
| #include <assert.h> | |||
| void | |||
| swapfs_init(void) { | |||
| static_assert((PGSIZE % SECTSIZE) == 0); | |||
| if (!ide_device_valid(SWAP_DEV_NO)) { | |||
| panic("swap fs isn't available.\n"); | |||
| } | |||
| max_swap_offset = ide_device_size(SWAP_DEV_NO) / (PGSIZE / SECTSIZE); | |||
| } | |||
| int | |||
| swapfs_read(swap_entry_t entry, struct Page *page) { | |||
| return ide_read_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT); | |||
| } | |||
| int | |||
| swapfs_write(swap_entry_t entry, struct Page *page) { | |||
| return ide_write_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT); | |||
| } | |||
| @ -0,0 +1,12 @@ | |||
| #ifndef __KERN_FS_SWAPFS_H__ | |||
| #define __KERN_FS_SWAPFS_H__ | |||
| #include <memlayout.h> | |||
| #include <swap.h> | |||
| void swapfs_init(void); | |||
| int swapfs_read(swap_entry_t entry, struct Page *page); | |||
| int swapfs_write(swap_entry_t entry, struct Page *page); | |||
| #endif /* !__KERN_FS_SWAPFS_H__ */ | |||
| @ -0,0 +1,49 @@ | |||
| #include <mmu.h> | |||
| #include <memlayout.h> | |||
| #define REALLOC(x) (x - KERNBASE) | |||
| .text | |||
| .globl kern_entry | |||
| kern_entry: | |||
| # reload temperate gdt (second time) to remap all physical memory | |||
| # virtual_addr 0~4G=linear_addr&physical_addr -KERNBASE~4G-KERNBASE | |||
| lgdt REALLOC(__gdtdesc) | |||
| movl $KERNEL_DS, %eax | |||
| movw %ax, %ds | |||
| movw %ax, %es | |||
| movw %ax, %ss | |||
| ljmp $KERNEL_CS, $relocated | |||
| relocated: | |||
| # set ebp, esp | |||
| movl $0x0, %ebp | |||
| # the kernel stack region is from bootstack -- bootstacktop, | |||
| # the kernel stack size is KSTACKSIZE (8KB)defined in memlayout.h | |||
| movl $bootstacktop, %esp | |||
| # now kernel stack is ready , call the first C function | |||
| call kern_init | |||
| # should never get here | |||
| spin: | |||
| jmp spin | |||
| .data | |||
| .align PGSIZE | |||
| .globl bootstack | |||
| bootstack: | |||
| .space KSTACKSIZE | |||
| .globl bootstacktop | |||
| bootstacktop: | |||
| .align 4 | |||
| __gdt: | |||
| SEG_NULL | |||
| SEG_ASM(STA_X | STA_R, - KERNBASE, 0xFFFFFFFF) # code segment | |||
| SEG_ASM(STA_W, - KERNBASE, 0xFFFFFFFF) # data segment | |||
| __gdtdesc: | |||
| .word 0x17 # sizeof(__gdt) - 1 | |||
| .long REALLOC(__gdt) | |||
| @ -0,0 +1,113 @@ | |||
| #include <defs.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <console.h> | |||
| #include <kdebug.h> | |||
| #include <picirq.h> | |||
| #include <trap.h> | |||
| #include <clock.h> | |||
| #include <intr.h> | |||
| #include <pmm.h> | |||
| #include <vmm.h> | |||
| #include <ide.h> | |||
| #include <swap.h> | |||
| #include <proc.h> | |||
| int kern_init(void) __attribute__((noreturn)); | |||
| static void lab1_switch_test(void); | |||
| int | |||
| kern_init(void) { | |||
| extern char edata[], end[]; | |||
| memset(edata, 0, end - edata); | |||
| cons_init(); // init the console | |||
| const char *message = "(THU.CST) os is loading ..."; | |||
| cprintf("%s\n\n", message); | |||
| print_kerninfo(); | |||
| grade_backtrace(); | |||
| pmm_init(); // init physical memory management | |||
| pic_init(); // init interrupt controller | |||
| idt_init(); // init interrupt descriptor table | |||
| vmm_init(); // init virtual memory management | |||
| proc_init(); // init process table | |||
| ide_init(); // init ide devices | |||
| swap_init(); // init swap | |||
| clock_init(); // init clock interrupt | |||
| intr_enable(); // enable irq interrupt | |||
| //LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test() | |||
| // user/kernel mode switch test | |||
| //lab1_switch_test(); | |||
| cpu_idle(); // run idle process | |||
| } | |||
| void __attribute__((noinline)) | |||
| grade_backtrace2(int arg0, int arg1, int arg2, int arg3) { | |||
| mon_backtrace(0, NULL, NULL); | |||
| } | |||
| void __attribute__((noinline)) | |||
| grade_backtrace1(int arg0, int arg1) { | |||
| grade_backtrace2(arg0, (int)&arg0, arg1, (int)&arg1); | |||
| } | |||
| void __attribute__((noinline)) | |||
| grade_backtrace0(int arg0, int arg1, int arg2) { | |||
| grade_backtrace1(arg0, arg2); | |||
| } | |||
| void | |||
| grade_backtrace(void) { | |||
| grade_backtrace0(0, (int)kern_init, 0xffff0000); | |||
| } | |||
| static void | |||
| lab1_print_cur_status(void) { | |||
| static int round = 0; | |||
| uint16_t reg1, reg2, reg3, reg4; | |||
| asm volatile ( | |||
| "mov %%cs, %0;" | |||
| "mov %%ds, %1;" | |||
| "mov %%es, %2;" | |||
| "mov %%ss, %3;" | |||
| : "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4)); | |||
| cprintf("%d: @ring %d\n", round, reg1 & 3); | |||
| cprintf("%d: cs = %x\n", round, reg1); | |||
| cprintf("%d: ds = %x\n", round, reg2); | |||
| cprintf("%d: es = %x\n", round, reg3); | |||
| cprintf("%d: ss = %x\n", round, reg4); | |||
| round ++; | |||
| } | |||
| static void | |||
| lab1_switch_to_user(void) { | |||
| //LAB1 CHALLENGE 1 : TODO | |||
| } | |||
| static void | |||
| lab1_switch_to_kernel(void) { | |||
| //LAB1 CHALLENGE 1 : TODO | |||
| } | |||
| static void | |||
| lab1_switch_test(void) { | |||
| lab1_print_cur_status(); | |||
| cprintf("+++ switch to user mode +++\n"); | |||
| lab1_switch_to_user(); | |||
| lab1_print_cur_status(); | |||
| cprintf("+++ switch to kernel mode +++\n"); | |||
| lab1_switch_to_kernel(); | |||
| lab1_print_cur_status(); | |||
| } | |||
| @ -0,0 +1,528 @@ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <kmalloc.h> | |||
| #include <rb_tree.h> | |||
| #include <assert.h> | |||
| /* rb_node_create - create a new rb_node */ | |||
| static inline rb_node * | |||
| rb_node_create(void) { | |||
| return kmalloc(sizeof(rb_node)); | |||
| } | |||
| /* rb_tree_empty - tests if tree is empty */ | |||
| static inline bool | |||
| rb_tree_empty(rb_tree *tree) { | |||
| rb_node *nil = tree->nil, *root = tree->root; | |||
| return root->left == nil; | |||
| } | |||
| /* * | |||
| * rb_tree_create - creates a new red-black tree, the 'compare' function | |||
| * is required and returns 'NULL' if failed. | |||
| * | |||
| * Note that, root->left should always point to the node that is the root | |||
| * of the tree. And nil points to a 'NULL' node which should always be | |||
| * black and may have arbitrary children and parent node. | |||
| * */ | |||
| rb_tree * | |||
| rb_tree_create(int (*compare)(rb_node *node1, rb_node *node2)) { | |||
| assert(compare != NULL); | |||
| rb_tree *tree; | |||
| rb_node *nil, *root; | |||
| if ((tree = kmalloc(sizeof(rb_tree))) == NULL) { | |||
| goto bad_tree; | |||
| } | |||
| tree->compare = compare; | |||
| if ((nil = rb_node_create()) == NULL) { | |||
| goto bad_node_cleanup_tree; | |||
| } | |||
| nil->parent = nil->left = nil->right = nil; | |||
| nil->red = 0; | |||
| tree->nil = nil; | |||
| if ((root = rb_node_create()) == NULL) { | |||
| goto bad_node_cleanup_nil; | |||
| } | |||
| root->parent = root->left = root->right = nil; | |||
| root->red = 0; | |||
| tree->root = root; | |||
| return tree; | |||
| bad_node_cleanup_nil: | |||
| kfree(nil); | |||
| bad_node_cleanup_tree: | |||
| kfree(tree); | |||
| bad_tree: | |||
| return NULL; | |||
| } | |||
| /* * | |||
| * FUNC_ROTATE - rotates as described in "Introduction to Algorithm". | |||
| * | |||
| * For example, FUNC_ROTATE(rb_left_rotate, left, right) can be expaned to a | |||
| * left-rotate function, which requires an red-black 'tree' and a node 'x' | |||
| * to be rotated on. Basically, this function, named rb_left_rotate, makes the | |||
| * parent of 'x' be the left child of 'x', 'x' the parent of its parent before | |||
| * rotation and finally fixes other nodes accordingly. | |||
| * | |||
| * FUNC_ROTATE(xx, left, right) means left-rotate, | |||
| * and FUNC_ROTATE(xx, right, left) means right-rotate. | |||
| * */ | |||
| #define FUNC_ROTATE(func_name, _left, _right) \ | |||
| static void \ | |||
| func_name(rb_tree *tree, rb_node *x) { \ | |||
| rb_node *nil = tree->nil, *y = x->_right; \ | |||
| assert(x != tree->root && x != nil && y != nil); \ | |||
| x->_right = y->_left; \ | |||
| if (y->_left != nil) { \ | |||
| y->_left->parent = x; \ | |||
| } \ | |||
| y->parent = x->parent; \ | |||
| if (x == x->parent->_left) { \ | |||
| x->parent->_left = y; \ | |||
| } \ | |||
| else { \ | |||
| x->parent->_right = y; \ | |||
| } \ | |||
| y->_left = x; \ | |||
| x->parent = y; \ | |||
| assert(!(nil->red)); \ | |||
| } | |||
| FUNC_ROTATE(rb_left_rotate, left, right); | |||
| FUNC_ROTATE(rb_right_rotate, right, left); | |||
| #undef FUNC_ROTATE | |||
| #define COMPARE(tree, node1, node2) \ | |||
| ((tree))->compare((node1), (node2)) | |||
| /* * | |||
| * rb_insert_binary - insert @node to red-black @tree as if it were | |||
| * a regular binary tree. This function is only intended to be called | |||
| * by function rb_insert. | |||
| * */ | |||
| static inline void | |||
| rb_insert_binary(rb_tree *tree, rb_node *node) { | |||
| rb_node *x, *y, *z = node, *nil = tree->nil, *root = tree->root; | |||
| z->left = z->right = nil; | |||
| y = root, x = y->left; | |||
| while (x != nil) { | |||
| y = x; | |||
| x = (COMPARE(tree, x, node) > 0) ? x->left : x->right; | |||
| } | |||
| z->parent = y; | |||
| if (y == root || COMPARE(tree, y, z) > 0) { | |||
| y->left = z; | |||
| } | |||
| else { | |||
| y->right = z; | |||
| } | |||
| } | |||
| /* rb_insert - insert a node to red-black tree */ | |||
| void | |||
| rb_insert(rb_tree *tree, rb_node *node) { | |||
| rb_insert_binary(tree, node); | |||
| node->red = 1; | |||
| rb_node *x = node, *y; | |||
| #define RB_INSERT_SUB(_left, _right) \ | |||
| do { \ | |||
| y = x->parent->parent->_right; \ | |||
| if (y->red) { \ | |||
| x->parent->red = 0; \ | |||
| y->red = 0; \ | |||
| x->parent->parent->red = 1; \ | |||
| x = x->parent->parent; \ | |||
| } \ | |||
| else { \ | |||
| if (x == x->parent->_right) { \ | |||
| x = x->parent; \ | |||
| rb_##_left##_rotate(tree, x); \ | |||
| } \ | |||
| x->parent->red = 0; \ | |||
| x->parent->parent->red = 1; \ | |||
| rb_##_right##_rotate(tree, x->parent->parent); \ | |||
| } \ | |||
| } while (0) | |||
| while (x->parent->red) { | |||
| if (x->parent == x->parent->parent->left) { | |||
| RB_INSERT_SUB(left, right); | |||
| } | |||
| else { | |||
| RB_INSERT_SUB(right, left); | |||
| } | |||
| } | |||
| tree->root->left->red = 0; | |||
| assert(!(tree->nil->red) && !(tree->root->red)); | |||
| #undef RB_INSERT_SUB | |||
| } | |||
| /* * | |||
| * rb_tree_successor - returns the successor of @node, or nil | |||
| * if no successor exists. Make sure that @node must belong to @tree, | |||
| * and this function should only be called by rb_node_prev. | |||
| * */ | |||
| static inline rb_node * | |||
| rb_tree_successor(rb_tree *tree, rb_node *node) { | |||
| rb_node *x = node, *y, *nil = tree->nil; | |||
| if ((y = x->right) != nil) { | |||
| while (y->left != nil) { | |||
| y = y->left; | |||
| } | |||
| return y; | |||
| } | |||
| else { | |||
| y = x->parent; | |||
| while (x == y->right) { | |||
| x = y, y = y->parent; | |||
| } | |||
| if (y == tree->root) { | |||
| return nil; | |||
| } | |||
| return y; | |||
| } | |||
| } | |||
| /* * | |||
| * rb_tree_predecessor - returns the predecessor of @node, or nil | |||
| * if no predecessor exists, likes rb_tree_successor. | |||
| * */ | |||
| static inline rb_node * | |||
| rb_tree_predecessor(rb_tree *tree, rb_node *node) { | |||
| rb_node *x = node, *y, *nil = tree->nil; | |||
| if ((y = x->left) != nil) { | |||
| while (y->right != nil) { | |||
| y = y->right; | |||
| } | |||
| return y; | |||
| } | |||
| else { | |||
| y = x->parent; | |||
| while (x == y->left) { | |||
| if (y == tree->root) { | |||
| return nil; | |||
| } | |||
| x = y, y = y->parent; | |||
| } | |||
| return y; | |||
| } | |||
| } | |||
| /* * | |||
| * rb_search - returns a node with value 'equal' to @key (according to | |||
| * function @compare). If there're multiple nodes with value 'equal' to @key, | |||
| * the functions returns the one highest in the tree. | |||
| * */ | |||
| rb_node * | |||
| rb_search(rb_tree *tree, int (*compare)(rb_node *node, void *key), void *key) { | |||
| rb_node *nil = tree->nil, *node = tree->root->left; | |||
| int r; | |||
| while (node != nil && (r = compare(node, key)) != 0) { | |||
| node = (r > 0) ? node->left : node->right; | |||
| } | |||
| return (node != nil) ? node : NULL; | |||
| } | |||
| /* * | |||
| * rb_delete_fixup - performs rotations and changes colors to restore | |||
| * red-black properties after a node is deleted. | |||
| * */ | |||
| static void | |||
| rb_delete_fixup(rb_tree *tree, rb_node *node) { | |||
| rb_node *x = node, *w, *root = tree->root->left; | |||
| #define RB_DELETE_FIXUP_SUB(_left, _right) \ | |||
| do { \ | |||
| w = x->parent->_right; \ | |||
| if (w->red) { \ | |||
| w->red = 0; \ | |||
| x->parent->red = 1; \ | |||
| rb_##_left##_rotate(tree, x->parent); \ | |||
| w = x->parent->_right; \ | |||
| } \ | |||
| if (!w->_left->red && !w->_right->red) { \ | |||
| w->red = 1; \ | |||
| x = x->parent; \ | |||
| } \ | |||
| else { \ | |||
| if (!w->_right->red) { \ | |||
| w->_left->red = 0; \ | |||
| w->red = 1; \ | |||
| rb_##_right##_rotate(tree, w); \ | |||
| w = x->parent->_right; \ | |||
| } \ | |||
| w->red = x->parent->red; \ | |||
| x->parent->red = 0; \ | |||
| w->_right->red = 0; \ | |||
| rb_##_left##_rotate(tree, x->parent); \ | |||
| x = root; \ | |||
| } \ | |||
| } while (0) | |||
| while (x != root && !x->red) { | |||
| if (x == x->parent->left) { | |||
| RB_DELETE_FIXUP_SUB(left, right); | |||
| } | |||
| else { | |||
| RB_DELETE_FIXUP_SUB(right, left); | |||
| } | |||
| } | |||
| x->red = 0; | |||
| #undef RB_DELETE_FIXUP_SUB | |||
| } | |||
| /* * | |||
| * rb_delete - deletes @node from @tree, and calls rb_delete_fixup to | |||
| * restore red-black properties. | |||
| * */ | |||
| void | |||
| rb_delete(rb_tree *tree, rb_node *node) { | |||
| rb_node *x, *y, *z = node; | |||
| rb_node *nil = tree->nil, *root = tree->root; | |||
| y = (z->left == nil || z->right == nil) ? z : rb_tree_successor(tree, z); | |||
| x = (y->left != nil) ? y->left : y->right; | |||
| assert(y != root && y != nil); | |||
| x->parent = y->parent; | |||
| if (y == y->parent->left) { | |||
| y->parent->left = x; | |||
| } | |||
| else { | |||
| y->parent->right = x; | |||
| } | |||
| bool need_fixup = !(y->red); | |||
| if (y != z) { | |||
| if (z == z->parent->left) { | |||
| z->parent->left = y; | |||
| } | |||
| else { | |||
| z->parent->right = y; | |||
| } | |||
| z->left->parent = z->right->parent = y; | |||
| *y = *z; | |||
| } | |||
| if (need_fixup) { | |||
| rb_delete_fixup(tree, x); | |||
| } | |||
| } | |||
| /* rb_tree_destroy - destroy a tree and free memory */ | |||
| void | |||
| rb_tree_destroy(rb_tree *tree) { | |||
| kfree(tree->root); | |||
| kfree(tree->nil); | |||
| kfree(tree); | |||
| } | |||
| /* * | |||
| * rb_node_prev - returns the predecessor node of @node in @tree, | |||
| * or 'NULL' if no predecessor exists. | |||
| * */ | |||
| rb_node * | |||
| rb_node_prev(rb_tree *tree, rb_node *node) { | |||
| rb_node *prev = rb_tree_predecessor(tree, node); | |||
| return (prev != tree->nil) ? prev : NULL; | |||
| } | |||
| /* * | |||
| * rb_node_next - returns the successor node of @node in @tree, | |||
| * or 'NULL' if no successor exists. | |||
| * */ | |||
| rb_node * | |||
| rb_node_next(rb_tree *tree, rb_node *node) { | |||
| rb_node *next = rb_tree_successor(tree, node); | |||
| return (next != tree->nil) ? next : NULL; | |||
| } | |||
| /* rb_node_root - returns the root node of a @tree, or 'NULL' if tree is empty */ | |||
| rb_node * | |||
| rb_node_root(rb_tree *tree) { | |||
| rb_node *node = tree->root->left; | |||
| return (node != tree->nil) ? node : NULL; | |||
| } | |||
| /* rb_node_left - gets the left child of @node, or 'NULL' if no such node */ | |||
| rb_node * | |||
| rb_node_left(rb_tree *tree, rb_node *node) { | |||
| rb_node *left = node->left; | |||
| return (left != tree->nil) ? left : NULL; | |||
| } | |||
| /* rb_node_right - gets the right child of @node, or 'NULL' if no such node */ | |||
| rb_node * | |||
| rb_node_right(rb_tree *tree, rb_node *node) { | |||
| rb_node *right = node->right; | |||
| return (right != tree->nil) ? right : NULL; | |||
| } | |||
| int | |||
| check_tree(rb_tree *tree, rb_node *node) { | |||
| rb_node *nil = tree->nil; | |||
| if (node == nil) { | |||
| assert(!node->red); | |||
| return 1; | |||
| } | |||
| if (node->left != nil) { | |||
| assert(COMPARE(tree, node, node->left) >= 0); | |||
| assert(node->left->parent == node); | |||
| } | |||
| if (node->right != nil) { | |||
| assert(COMPARE(tree, node, node->right) <= 0); | |||
| assert(node->right->parent == node); | |||
| } | |||
| if (node->red) { | |||
| assert(!node->left->red && !node->right->red); | |||
| } | |||
| int hb_left = check_tree(tree, node->left); | |||
| int hb_right = check_tree(tree, node->right); | |||
| assert(hb_left == hb_right); | |||
| int hb = hb_left; | |||
| if (!node->red) { | |||
| hb ++; | |||
| } | |||
| return hb; | |||
| } | |||
| static void * | |||
| check_safe_kmalloc(size_t size) { | |||
| void *ret = kmalloc(size); | |||
| assert(ret != NULL); | |||
| return ret; | |||
| } | |||
| struct check_data { | |||
| long data; | |||
| rb_node rb_link; | |||
| }; | |||
| #define rbn2data(node) \ | |||
| (to_struct(node, struct check_data, rb_link)) | |||
| static inline int | |||
| check_compare1(rb_node *node1, rb_node *node2) { | |||
| return rbn2data(node1)->data - rbn2data(node2)->data; | |||
| } | |||
| static inline int | |||
| check_compare2(rb_node *node, void *key) { | |||
| return rbn2data(node)->data - (long)key; | |||
| } | |||
| void | |||
| check_rb_tree(void) { | |||
| rb_tree *tree = rb_tree_create(check_compare1); | |||
| assert(tree != NULL); | |||
| rb_node *nil = tree->nil, *root = tree->root; | |||
| assert(!nil->red && root->left == nil); | |||
| int total = 1000; | |||
| struct check_data **all = check_safe_kmalloc(sizeof(struct check_data *) * total); | |||
| long i; | |||
| for (i = 0; i < total; i ++) { | |||
| all[i] = check_safe_kmalloc(sizeof(struct check_data)); | |||
| all[i]->data = i; | |||
| } | |||
| int *mark = check_safe_kmalloc(sizeof(int) * total); | |||
| memset(mark, 0, sizeof(int) * total); | |||
| for (i = 0; i < total; i ++) { | |||
| mark[all[i]->data] = 1; | |||
| } | |||
| for (i = 0; i < total; i ++) { | |||
| assert(mark[i] == 1); | |||
| } | |||
| for (i = 0; i < total; i ++) { | |||
| int j = (rand() % (total - i)) + i; | |||
| struct check_data *z = all[i]; | |||
| all[i] = all[j]; | |||
| all[j] = z; | |||
| } | |||
| memset(mark, 0, sizeof(int) * total); | |||
| for (i = 0; i < total; i ++) { | |||
| mark[all[i]->data] = 1; | |||
| } | |||
| for (i = 0; i < total; i ++) { | |||
| assert(mark[i] == 1); | |||
| } | |||
| for (i = 0; i < total; i ++) { | |||
| rb_insert(tree, &(all[i]->rb_link)); | |||
| check_tree(tree, root->left); | |||
| } | |||
| rb_node *node; | |||
| for (i = 0; i < total; i ++) { | |||
| node = rb_search(tree, check_compare2, (void *)(all[i]->data)); | |||
| assert(node != NULL && node == &(all[i]->rb_link)); | |||
| } | |||
| for (i = 0; i < total; i ++) { | |||
| node = rb_search(tree, check_compare2, (void *)i); | |||
| assert(node != NULL && rbn2data(node)->data == i); | |||
| rb_delete(tree, node); | |||
| check_tree(tree, root->left); | |||
| } | |||
| assert(!nil->red && root->left == nil); | |||
| long max = 32; | |||
| if (max > total) { | |||
| max = total; | |||
| } | |||
| for (i = 0; i < max; i ++) { | |||
| all[i]->data = max; | |||
| rb_insert(tree, &(all[i]->rb_link)); | |||
| check_tree(tree, root->left); | |||
| } | |||
| for (i = 0; i < max; i ++) { | |||
| node = rb_search(tree, check_compare2, (void *)max); | |||
| assert(node != NULL && rbn2data(node)->data == max); | |||
| rb_delete(tree, node); | |||
| check_tree(tree, root->left); | |||
| } | |||
| assert(rb_tree_empty(tree)); | |||
| for (i = 0; i < total; i ++) { | |||
| rb_insert(tree, &(all[i]->rb_link)); | |||
| check_tree(tree, root->left); | |||
| } | |||
| rb_tree_destroy(tree); | |||
| for (i = 0; i < total; i ++) { | |||
| kfree(all[i]); | |||
| } | |||
| kfree(mark); | |||
| kfree(all); | |||
| } | |||
| @ -0,0 +1,32 @@ | |||
| #ifndef __KERN_LIBS_RB_TREE_H__ | |||
| #define __KERN_LIBS_RB_TREE_H__ | |||
| #include <defs.h> | |||
| typedef struct rb_node { | |||
| bool red; // if red = 0, it's a black node | |||
| struct rb_node *parent; | |||
| struct rb_node *left, *right; | |||
| } rb_node; | |||
| typedef struct rb_tree { | |||
| // compare function should return -1 if *node1 < *node2, 1 if *node1 > *node2, and 0 otherwise | |||
| int (*compare)(rb_node *node1, rb_node *node2); | |||
| struct rb_node *nil, *root; | |||
| } rb_tree; | |||
| rb_tree *rb_tree_create(int (*compare)(rb_node *node1, rb_node *node2)); | |||
| void rb_tree_destroy(rb_tree *tree); | |||
| void rb_insert(rb_tree *tree, rb_node *node); | |||
| void rb_delete(rb_tree *tree, rb_node *node); | |||
| rb_node *rb_search(rb_tree *tree, int (*compare)(rb_node *node, void *key), void *key); | |||
| rb_node *rb_node_prev(rb_tree *tree, rb_node *node); | |||
| rb_node *rb_node_next(rb_tree *tree, rb_node *node); | |||
| rb_node *rb_node_root(rb_tree *tree); | |||
| rb_node *rb_node_left(rb_tree *tree, rb_node *node); | |||
| rb_node *rb_node_right(rb_tree *tree, rb_node *node); | |||
| void check_rb_tree(void); | |||
| #endif /* !__KERN_LIBS_RBTREE_H__ */ | |||
| @ -0,0 +1,50 @@ | |||
| #include <stdio.h> | |||
| #define BUFSIZE 1024 | |||
| static char buf[BUFSIZE]; | |||
| /* * | |||
| * readline - get a line from stdin | |||
| * @prompt: the string to be written to stdout | |||
| * | |||
| * The readline() function will write the input string @prompt to | |||
| * stdout first. If the @prompt is NULL or the empty string, | |||
| * no prompt is issued. | |||
| * | |||
| * This function will keep on reading characters and saving them to buffer | |||
| * 'buf' until '\n' or '\r' is encountered. | |||
| * | |||
| * Note that, if the length of string that will be read is longer than | |||
| * buffer size, the end of string will be discarded. | |||
| * | |||
| * The readline() function returns the text of the line read. If some errors | |||
| * are happened, NULL is returned. The return value is a global variable, | |||
| * thus it should be copied before it is used. | |||
| * */ | |||
| char * | |||
| readline(const char *prompt) { | |||
| if (prompt != NULL) { | |||
| cprintf("%s", prompt); | |||
| } | |||
| int i = 0, c; | |||
| while (1) { | |||
| c = getchar(); | |||
| if (c < 0) { | |||
| return NULL; | |||
| } | |||
| else if (c >= ' ' && i < BUFSIZE - 1) { | |||
| cputchar(c); | |||
| buf[i ++] = c; | |||
| } | |||
| else if (c == '\b' && i > 0) { | |||
| cputchar(c); | |||
| i --; | |||
| } | |||
| else if (c == '\n' || c == '\r') { | |||
| cputchar(c); | |||
| buf[i] = '\0'; | |||
| return buf; | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,78 @@ | |||
| #include <defs.h> | |||
| #include <stdio.h> | |||
| #include <console.h> | |||
| /* HIGH level console I/O */ | |||
| /* * | |||
| * cputch - writes a single character @c to stdout, and it will | |||
| * increace the value of counter pointed by @cnt. | |||
| * */ | |||
| static void | |||
| cputch(int c, int *cnt) { | |||
| cons_putc(c); | |||
| (*cnt) ++; | |||
| } | |||
| /* * | |||
| * vcprintf - format a string and writes it to stdout | |||
| * | |||
| * The return value is the number of characters which would be | |||
| * written to stdout. | |||
| * | |||
| * Call this function if you are already dealing with a va_list. | |||
| * Or you probably want cprintf() instead. | |||
| * */ | |||
| int | |||
| vcprintf(const char *fmt, va_list ap) { | |||
| int cnt = 0; | |||
| vprintfmt((void*)cputch, &cnt, fmt, ap); | |||
| return cnt; | |||
| } | |||
| /* * | |||
| * cprintf - formats a string and writes it to stdout | |||
| * | |||
| * The return value is the number of characters which would be | |||
| * written to stdout. | |||
| * */ | |||
| int | |||
| cprintf(const char *fmt, ...) { | |||
| va_list ap; | |||
| int cnt; | |||
| va_start(ap, fmt); | |||
| cnt = vcprintf(fmt, ap); | |||
| va_end(ap); | |||
| return cnt; | |||
| } | |||
| /* cputchar - writes a single character to stdout */ | |||
| void | |||
| cputchar(int c) { | |||
| cons_putc(c); | |||
| } | |||
| /* * | |||
| * cputs- writes the string pointed by @str to stdout and | |||
| * appends a newline character. | |||
| * */ | |||
| int | |||
| cputs(const char *str) { | |||
| int cnt = 0; | |||
| char c; | |||
| while ((c = *str ++) != '\0') { | |||
| cputch(c, &cnt); | |||
| } | |||
| cputch('\n', &cnt); | |||
| return cnt; | |||
| } | |||
| /* getchar - reads a single non-zero character from stdin */ | |||
| int | |||
| getchar(void) { | |||
| int c; | |||
| while ((c = cons_getc()) == 0) | |||
| /* do nothing */; | |||
| return c; | |||
| } | |||
| @ -0,0 +1,294 @@ | |||
| #include <pmm.h> | |||
| #include <list.h> | |||
| #include <string.h> | |||
| #include <default_pmm.h> | |||
| /* In the first fit algorithm, the allocator keeps a list of free blocks (known as the free list) and, | |||
| on receiving a request for memory, scans along the list for the first block that is large enough to | |||
| satisfy the request. If the chosen block is significantly larger than that requested, then it is | |||
| usually split, and the remainder added to the list as another free block. | |||
| Please see Page 196~198, Section 8.2 of Yan Wei Ming's chinese book "Data Structure -- C programming language" | |||
| */ | |||
| // LAB2 EXERCISE 1: YOUR CODE | |||
| // you should rewrite functions: default_init,default_init_memmap,default_alloc_pages, default_free_pages. | |||
| /* | |||
| * Details of FFMA | |||
| * (1) Prepare: In order to implement the First-Fit Mem Alloc (FFMA), we should manage the free mem block use some list. | |||
| * The struct free_area_t is used for the management of free mem blocks. At first you should | |||
| * be familiar to the struct list in list.h. struct list is a simple doubly linked list implementation. | |||
| * You should know howto USE: list_init, list_add(list_add_after), list_add_before, list_del, list_next, list_prev | |||
| * Another tricky method is to transform a general list struct to a special struct (such as struct page): | |||
| * you can find some MACRO: le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.) | |||
| * (2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0. | |||
| * free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks. | |||
| * (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap | |||
| * This fun is used to init a free block (with parameter: addr_base, page_number). | |||
| * First you should init each page (in memlayout.h) in this free block, include: | |||
| * p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c), | |||
| * the bit PG_reserved is setted in p->flags) | |||
| * if this page is free and is not the first page of free block, p->property should be set to 0. | |||
| * if this page is free and is the first page of free block, p->property should be set to total num of block. | |||
| * p->ref should be 0, because now p is free and no reference. | |||
| * We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); ) | |||
| * Finally, we should sum the number of free mem block: nr_free+=n | |||
| * (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr | |||
| * of malloced block. | |||
| * (4.1) So you should search freelist like this: | |||
| * list_entry_t le = &free_list; | |||
| * while((le=list_next(le)) != &free_list) { | |||
| * .... | |||
| * (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n? | |||
| * struct Page *p = le2page(le, page_link); | |||
| * if(p->property >= n){ ... | |||
| * (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced. | |||
| * Some flag bits of this page should be setted: PG_reserved =1, PG_property =0 | |||
| * unlink the pages from free_list | |||
| * (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block, | |||
| * (such as: le2page(le,page_link))->property = p->property - n;) | |||
| * (4.1.3) re-caluclate nr_free (number of the the rest of all free block) | |||
| * (4.1.4) return p | |||
| * (4.2) If we can not find a free block (block size >=n), then return NULL | |||
| * (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks. | |||
| * (5.1) according the base addr of withdrawed blocks, search free list, find the correct position | |||
| * (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before) | |||
| * (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty) | |||
| * (5.3) try to merge low addr or high addr blocks. Notice: should change some pages's p->property correctly. | |||
| */ | |||
| free_area_t free_area; | |||
| #define free_list (free_area.free_list) | |||
| #define nr_free (free_area.nr_free) | |||
| static void | |||
| default_init(void) { | |||
| list_init(&free_list); | |||
| nr_free = 0; | |||
| } | |||
| static void | |||
| default_init_memmap(struct Page *base, size_t n) { | |||
| assert(n > 0); | |||
| struct Page *p = base; | |||
| for (; p != base + n; p ++) { | |||
| assert(PageReserved(p)); | |||
| p->flags = 0; | |||
| SetPageProperty(p); | |||
| p->property = 0; | |||
| set_page_ref(p, 0); | |||
| list_add_before(&free_list, &(p->page_link)); | |||
| } | |||
| nr_free += n; | |||
| //first block | |||
| base->property = n; | |||
| } | |||
| static struct Page * | |||
| default_alloc_pages(size_t n) { | |||
| assert(n > 0); | |||
| if (n > nr_free) { | |||
| return NULL; | |||
| } | |||
| list_entry_t *le, *len; | |||
| le = &free_list; | |||
| while((le=list_next(le)) != &free_list) { | |||
| struct Page *p = le2page(le, page_link); | |||
| if(p->property >= n){ | |||
| int i; | |||
| for(i=0;i<n;i++){ | |||
| len = list_next(le); | |||
| struct Page *pp = le2page(le, page_link); | |||
| SetPageReserved(pp); | |||
| ClearPageProperty(pp); | |||
| list_del(le); | |||
| le = len; | |||
| } | |||
| if(p->property>n){ | |||
| (le2page(le,page_link))->property = p->property - n; | |||
| } | |||
| ClearPageProperty(p); | |||
| SetPageReserved(p); | |||
| nr_free -= n; | |||
| return p; | |||
| } | |||
| } | |||
| return NULL; | |||
| } | |||
| static void | |||
| default_free_pages(struct Page *base, size_t n) { | |||
| assert(n > 0); | |||
| assert(PageReserved(base)); | |||
| list_entry_t *le = &free_list; | |||
| struct Page * p; | |||
| while((le=list_next(le)) != &free_list) { | |||
| p = le2page(le, page_link); | |||
| if(p>base){ | |||
| break; | |||
| } | |||
| } | |||
| //list_add_before(le, base->page_link); | |||
| for(p=base;p<base+n;p++){ | |||
| list_add_before(le, &(p->page_link)); | |||
| } | |||
| base->flags = 0; | |||
| set_page_ref(base, 0); | |||
| ClearPageProperty(base); | |||
| SetPageProperty(base); | |||
| base->property = n; | |||
| p = le2page(le,page_link) ; | |||
| if( base+n == p ){ | |||
| base->property += p->property; | |||
| p->property = 0; | |||
| } | |||
| le = list_prev(&(base->page_link)); | |||
| p = le2page(le, page_link); | |||
| if(le!=&free_list && p==base-1){ | |||
| while(le!=&free_list){ | |||
| if(p->property){ | |||
| p->property += base->property; | |||
| base->property = 0; | |||
| break; | |||
| } | |||
| le = list_prev(le); | |||
| p = le2page(le,page_link); | |||
| } | |||
| } | |||
| nr_free += n; | |||
| return ; | |||
| } | |||
| static size_t | |||
| default_nr_free_pages(void) { | |||
| return nr_free; | |||
| } | |||
| static void | |||
| basic_check(void) { | |||
| struct Page *p0, *p1, *p2; | |||
| p0 = p1 = p2 = NULL; | |||
| assert((p0 = alloc_page()) != NULL); | |||
| assert((p1 = alloc_page()) != NULL); | |||
| assert((p2 = alloc_page()) != NULL); | |||
| assert(p0 != p1 && p0 != p2 && p1 != p2); | |||
| assert(page_ref(p0) == 0 && page_ref(p1) == 0 && page_ref(p2) == 0); | |||
| assert(page2pa(p0) < npage * PGSIZE); | |||
| assert(page2pa(p1) < npage * PGSIZE); | |||
| assert(page2pa(p2) < npage * PGSIZE); | |||
| list_entry_t free_list_store = free_list; | |||
| list_init(&free_list); | |||
| assert(list_empty(&free_list)); | |||
| unsigned int nr_free_store = nr_free; | |||
| nr_free = 0; | |||
| assert(alloc_page() == NULL); | |||
| free_page(p0); | |||
| free_page(p1); | |||
| free_page(p2); | |||
| assert(nr_free == 3); | |||
| assert((p0 = alloc_page()) != NULL); | |||
| assert((p1 = alloc_page()) != NULL); | |||
| assert((p2 = alloc_page()) != NULL); | |||
| assert(alloc_page() == NULL); | |||
| free_page(p0); | |||
| assert(!list_empty(&free_list)); | |||
| struct Page *p; | |||
| assert((p = alloc_page()) == p0); | |||
| assert(alloc_page() == NULL); | |||
| assert(nr_free == 0); | |||
| free_list = free_list_store; | |||
| nr_free = nr_free_store; | |||
| free_page(p); | |||
| free_page(p1); | |||
| free_page(p2); | |||
| } | |||
| // LAB2: below code is used to check the first fit allocation algorithm (your EXERCISE 1) | |||
| // NOTICE: You SHOULD NOT CHANGE basic_check, default_check functions! | |||
| static void | |||
| default_check(void) { | |||
| int count = 0, total = 0; | |||
| list_entry_t *le = &free_list; | |||
| while ((le = list_next(le)) != &free_list) { | |||
| struct Page *p = le2page(le, page_link); | |||
| assert(PageProperty(p)); | |||
| count ++, total += p->property; | |||
| } | |||
| assert(total == nr_free_pages()); | |||
| basic_check(); | |||
| struct Page *p0 = alloc_pages(5), *p1, *p2; | |||
| assert(p0 != NULL); | |||
| assert(!PageProperty(p0)); | |||
| list_entry_t free_list_store = free_list; | |||
| list_init(&free_list); | |||
| assert(list_empty(&free_list)); | |||
| assert(alloc_page() == NULL); | |||
| unsigned int nr_free_store = nr_free; | |||
| nr_free = 0; | |||
| free_pages(p0 + 2, 3); | |||
| assert(alloc_pages(4) == NULL); | |||
| assert(PageProperty(p0 + 2) && p0[2].property == 3); | |||
| assert((p1 = alloc_pages(3)) != NULL); | |||
| assert(alloc_page() == NULL); | |||
| assert(p0 + 2 == p1); | |||
| p2 = p0 + 1; | |||
| free_page(p0); | |||
| free_pages(p1, 3); | |||
| assert(PageProperty(p0) && p0->property == 1); | |||
| assert(PageProperty(p1) && p1->property == 3); | |||
| assert((p0 = alloc_page()) == p2 - 1); | |||
| free_page(p0); | |||
| assert((p0 = alloc_pages(2)) == p2 + 1); | |||
| free_pages(p0, 2); | |||
| free_page(p2); | |||
| assert((p0 = alloc_pages(5)) != NULL); | |||
| assert(alloc_page() == NULL); | |||
| assert(nr_free == 0); | |||
| nr_free = nr_free_store; | |||
| free_list = free_list_store; | |||
| free_pages(p0, 5); | |||
| le = &free_list; | |||
| while ((le = list_next(le)) != &free_list) { | |||
| struct Page *p = le2page(le, page_link); | |||
| count --, total -= p->property; | |||
| } | |||
| assert(count == 0); | |||
| assert(total == 0); | |||
| } | |||
| const struct pmm_manager default_pmm_manager = { | |||
| .name = "default_pmm_manager", | |||
| .init = default_init, | |||
| .init_memmap = default_init_memmap, | |||
| .alloc_pages = default_alloc_pages, | |||
| .free_pages = default_free_pages, | |||
| .nr_free_pages = default_nr_free_pages, | |||
| .check = default_check, | |||
| }; | |||
| @ -0,0 +1,9 @@ | |||
| #ifndef __KERN_MM_DEFAULT_PMM_H__ | |||
| #define __KERN_MM_DEFAULT_PMM_H__ | |||
| #include <pmm.h> | |||
| extern const struct pmm_manager default_pmm_manager; | |||
| extern free_area_t free_area; | |||
| #endif /* ! __KERN_MM_DEFAULT_PMM_H__ */ | |||
| @ -0,0 +1,310 @@ | |||
| #include <defs.h> | |||
| #include <list.h> | |||
| #include <memlayout.h> | |||
| #include <assert.h> | |||
| #include <kmalloc.h> | |||
| #include <sync.h> | |||
| #include <pmm.h> | |||
| #include <stdio.h> | |||
| /* | |||
| * SLOB Allocator: Simple List Of Blocks | |||
| * | |||
| * Matt Mackall <mpm@selenic.com> 12/30/03 | |||
| * | |||
| * How SLOB works: | |||
| * | |||
| * The core of SLOB is a traditional K&R style heap allocator, with | |||
| * support for returning aligned objects. The granularity of this | |||
| * allocator is 8 bytes on x86, though it's perhaps possible to reduce | |||
| * this to 4 if it's deemed worth the effort. The slob heap is a | |||
| * singly-linked list of pages from __get_free_page, grown on demand | |||
| * and allocation from the heap is currently first-fit. | |||
| * | |||
| * Above this is an implementation of kmalloc/kfree. Blocks returned | |||
| * from kmalloc are 8-byte aligned and prepended with a 8-byte header. | |||
| * If kmalloc is asked for objects of PAGE_SIZE or larger, it calls | |||
| * __get_free_pages directly so that it can return page-aligned blocks | |||
| * and keeps a linked list of such pages and their orders. These | |||
| * objects are detected in kfree() by their page alignment. | |||
| * | |||
| * SLAB is emulated on top of SLOB by simply calling constructors and | |||
| * destructors for every SLAB allocation. Objects are returned with | |||
| * the 8-byte alignment unless the SLAB_MUST_HWCACHE_ALIGN flag is | |||
| * set, in which case the low-level allocator will fragment blocks to | |||
| * create the proper alignment. Again, objects of page-size or greater | |||
| * are allocated by calling __get_free_pages. As SLAB objects know | |||
| * their size, no separate size bookkeeping is necessary and there is | |||
| * essentially no allocation space overhead. | |||
| */ | |||
| //some helper | |||
| #define spin_lock_irqsave(l, f) local_intr_save(f) | |||
| #define spin_unlock_irqrestore(l, f) local_intr_restore(f) | |||
| typedef unsigned int gfp_t; | |||
| #ifndef PAGE_SIZE | |||
| #define PAGE_SIZE PGSIZE | |||
| #endif | |||
| #ifndef L1_CACHE_BYTES | |||
| #define L1_CACHE_BYTES 64 | |||
| #endif | |||
| #ifndef ALIGN | |||
| #define ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1))) | |||
| #endif | |||
| struct slob_block { | |||
| int units; | |||
| struct slob_block *next; | |||
| }; | |||
| typedef struct slob_block slob_t; | |||
| #define SLOB_UNIT sizeof(slob_t) | |||
| #define SLOB_UNITS(size) (((size) + SLOB_UNIT - 1)/SLOB_UNIT) | |||
| #define SLOB_ALIGN L1_CACHE_BYTES | |||
| struct bigblock { | |||
| int order; | |||
| void *pages; | |||
| struct bigblock *next; | |||
| }; | |||
| typedef struct bigblock bigblock_t; | |||
| static slob_t arena = { .next = &arena, .units = 1 }; | |||
| static slob_t *slobfree = &arena; | |||
| static bigblock_t *bigblocks; | |||
| static void* __slob_get_free_pages(gfp_t gfp, int order) | |||
| { | |||
| struct Page * page = alloc_pages(1 << order); | |||
| if(!page) | |||
| return NULL; | |||
| return page2kva(page); | |||
| } | |||
| #define __slob_get_free_page(gfp) __slob_get_free_pages(gfp, 0) | |||
| static inline void __slob_free_pages(unsigned long kva, int order) | |||
| { | |||
| free_pages(kva2page(kva), 1 << order); | |||
| } | |||
| static void slob_free(void *b, int size); | |||
| static void *slob_alloc(size_t size, gfp_t gfp, int align) | |||
| { | |||
| assert( (size + SLOB_UNIT) < PAGE_SIZE ); | |||
| slob_t *prev, *cur, *aligned = 0; | |||
| int delta = 0, units = SLOB_UNITS(size); | |||
| unsigned long flags; | |||
| spin_lock_irqsave(&slob_lock, flags); | |||
| prev = slobfree; | |||
| for (cur = prev->next; ; prev = cur, cur = cur->next) { | |||
| if (align) { | |||
| aligned = (slob_t *)ALIGN((unsigned long)cur, align); | |||
| delta = aligned - cur; | |||
| } | |||
| if (cur->units >= units + delta) { /* room enough? */ | |||
| if (delta) { /* need to fragment head to align? */ | |||
| aligned->units = cur->units - delta; | |||
| aligned->next = cur->next; | |||
| cur->next = aligned; | |||
| cur->units = delta; | |||
| prev = cur; | |||
| cur = aligned; | |||
| } | |||
| if (cur->units == units) /* exact fit? */ | |||
| prev->next = cur->next; /* unlink */ | |||
| else { /* fragment */ | |||
| prev->next = cur + units; | |||
| prev->next->units = cur->units - units; | |||
| prev->next->next = cur->next; | |||
| cur->units = units; | |||
| } | |||
| slobfree = prev; | |||
| spin_unlock_irqrestore(&slob_lock, flags); | |||
| return cur; | |||
| } | |||
| if (cur == slobfree) { | |||
| spin_unlock_irqrestore(&slob_lock, flags); | |||
| if (size == PAGE_SIZE) /* trying to shrink arena? */ | |||
| return 0; | |||
| cur = (slob_t *)__slob_get_free_page(gfp); | |||
| if (!cur) | |||
| return 0; | |||
| slob_free(cur, PAGE_SIZE); | |||
| spin_lock_irqsave(&slob_lock, flags); | |||
| cur = slobfree; | |||
| } | |||
| } | |||
| } | |||
| static void slob_free(void *block, int size) | |||
| { | |||
| slob_t *cur, *b = (slob_t *)block; | |||
| unsigned long flags; | |||
| if (!block) | |||
| return; | |||
| if (size) | |||
| b->units = SLOB_UNITS(size); | |||
| /* Find reinsertion point */ | |||
| spin_lock_irqsave(&slob_lock, flags); | |||
| for (cur = slobfree; !(b > cur && b < cur->next); cur = cur->next) | |||
| if (cur >= cur->next && (b > cur || b < cur->next)) | |||
| break; | |||
| if (b + b->units == cur->next) { | |||
| b->units += cur->next->units; | |||
| b->next = cur->next->next; | |||
| } else | |||
| b->next = cur->next; | |||
| if (cur + cur->units == b) { | |||
| cur->units += b->units; | |||
| cur->next = b->next; | |||
| } else | |||
| cur->next = b; | |||
| slobfree = cur; | |||
| spin_unlock_irqrestore(&slob_lock, flags); | |||
| } | |||
| void check_slab(void) { | |||
| cprintf("check_slab() success\n"); | |||
| } | |||
| void | |||
| slab_init(void) { | |||
| cprintf("use SLOB allocator\n"); | |||
| check_slab(); | |||
| } | |||
| inline void | |||
| kmalloc_init(void) { | |||
| slab_init(); | |||
| cprintf("kmalloc_init() succeeded!\n"); | |||
| } | |||
| size_t | |||
| slab_allocated(void) { | |||
| return 0; | |||
| } | |||
| size_t | |||
| kallocated(void) { | |||
| return slab_allocated(); | |||
| } | |||
| static int find_order(int size) | |||
| { | |||
| int order = 0; | |||
| for ( ; size > 4096 ; size >>=1) | |||
| order++; | |||
| return order; | |||
| } | |||
| static void *__kmalloc(size_t size, gfp_t gfp) | |||
| { | |||
| slob_t *m; | |||
| bigblock_t *bb; | |||
| unsigned long flags; | |||
| if (size < PAGE_SIZE - SLOB_UNIT) { | |||
| m = slob_alloc(size + SLOB_UNIT, gfp, 0); | |||
| return m ? (void *)(m + 1) : 0; | |||
| } | |||
| bb = slob_alloc(sizeof(bigblock_t), gfp, 0); | |||
| if (!bb) | |||
| return 0; | |||
| bb->order = find_order(size); | |||
| bb->pages = (void *)__slob_get_free_pages(gfp, bb->order); | |||
| if (bb->pages) { | |||
| spin_lock_irqsave(&block_lock, flags); | |||
| bb->next = bigblocks; | |||
| bigblocks = bb; | |||
| spin_unlock_irqrestore(&block_lock, flags); | |||
| return bb->pages; | |||
| } | |||
| slob_free(bb, sizeof(bigblock_t)); | |||
| return 0; | |||
| } | |||
| void * | |||
| kmalloc(size_t size) | |||
| { | |||
| return __kmalloc(size, 0); | |||
| } | |||
| void kfree(void *block) | |||
| { | |||
| bigblock_t *bb, **last = &bigblocks; | |||
| unsigned long flags; | |||
| if (!block) | |||
| return; | |||
| if (!((unsigned long)block & (PAGE_SIZE-1))) { | |||
| /* might be on the big block list */ | |||
| spin_lock_irqsave(&block_lock, flags); | |||
| for (bb = bigblocks; bb; last = &bb->next, bb = bb->next) { | |||
| if (bb->pages == block) { | |||
| *last = bb->next; | |||
| spin_unlock_irqrestore(&block_lock, flags); | |||
| __slob_free_pages((unsigned long)block, bb->order); | |||
| slob_free(bb, sizeof(bigblock_t)); | |||
| return; | |||
| } | |||
| } | |||
| spin_unlock_irqrestore(&block_lock, flags); | |||
| } | |||
| slob_free((slob_t *)block - 1, 0); | |||
| return; | |||
| } | |||
| unsigned int ksize(const void *block) | |||
| { | |||
| bigblock_t *bb; | |||
| unsigned long flags; | |||
| if (!block) | |||
| return 0; | |||
| if (!((unsigned long)block & (PAGE_SIZE-1))) { | |||
| spin_lock_irqsave(&block_lock, flags); | |||
| for (bb = bigblocks; bb; bb = bb->next) | |||
| if (bb->pages == block) { | |||
| spin_unlock_irqrestore(&slob_lock, flags); | |||
| return PAGE_SIZE << bb->order; | |||
| } | |||
| spin_unlock_irqrestore(&block_lock, flags); | |||
| } | |||
| return ((slob_t *)block - 1)->units * SLOB_UNIT; | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| #ifndef __KERN_MM_SLAB_H__ | |||
| #define __KERN_MM_SLAB_H__ | |||
| #include <defs.h> | |||
| #define KMALLOC_MAX_ORDER 10 | |||
| void kmalloc_init(void); | |||
| void *kmalloc(size_t n); | |||
| void kfree(void *objp); | |||
| size_t kallocated(void); | |||
| #endif /* !__KERN_MM_SLAB_H__ */ | |||
| @ -0,0 +1,169 @@ | |||
| #ifndef __KERN_MM_MEMLAYOUT_H__ | |||
| #define __KERN_MM_MEMLAYOUT_H__ | |||
| /* This file contains the definitions for memory management in our OS. */ | |||
| /* global segment number */ | |||
| #define SEG_KTEXT 1 | |||
| #define SEG_KDATA 2 | |||
| #define SEG_UTEXT 3 | |||
| #define SEG_UDATA 4 | |||
| #define SEG_TSS 5 | |||
| /* global descrptor numbers */ | |||
| #define GD_KTEXT ((SEG_KTEXT) << 3) // kernel text | |||
| #define GD_KDATA ((SEG_KDATA) << 3) // kernel data | |||
| #define GD_UTEXT ((SEG_UTEXT) << 3) // user text | |||
| #define GD_UDATA ((SEG_UDATA) << 3) // user data | |||
| #define GD_TSS ((SEG_TSS) << 3) // task segment selector | |||
| #define DPL_KERNEL (0) | |||
| #define DPL_USER (3) | |||
| #define KERNEL_CS ((GD_KTEXT) | DPL_KERNEL) | |||
| #define KERNEL_DS ((GD_KDATA) | DPL_KERNEL) | |||
| #define USER_CS ((GD_UTEXT) | DPL_USER) | |||
| #define USER_DS ((GD_UDATA) | DPL_USER) | |||
| /* * | |||
| * Virtual memory map: Permissions | |||
| * kernel/user | |||
| * | |||
| * 4G ------------------> +---------------------------------+ | |||
| * | | | |||
| * | Empty Memory (*) | | |||
| * | | | |||
| * +---------------------------------+ 0xFB000000 | |||
| * | Cur. Page Table (Kern, RW) | RW/-- PTSIZE | |||
| * VPT -----------------> +---------------------------------+ 0xFAC00000 | |||
| * | Invalid Memory (*) | --/-- | |||
| * KERNTOP -------------> +---------------------------------+ 0xF8000000 | |||
| * | | | |||
| * | Remapped Physical Memory | RW/-- KMEMSIZE | |||
| * | | | |||
| * KERNBASE ------------> +---------------------------------+ 0xC0000000 | |||
| * | Invalid Memory (*) | --/-- | |||
| * USERTOP -------------> +---------------------------------+ 0xB0000000 | |||
| * | User stack | | |||
| * +---------------------------------+ | |||
| * | | | |||
| * : : | |||
| * | ~~~~~~~~~~~~~~~~ | | |||
| * : : | |||
| * | | | |||
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| * | User Program & Heap | | |||
| * UTEXT ---------------> +---------------------------------+ 0x00800000 | |||
| * | Invalid Memory (*) | --/-- | |||
| * | - - - - - - - - - - - - - - - | | |||
| * | User STAB Data (optional) | | |||
| * USERBASE, USTAB------> +---------------------------------+ 0x00200000 | |||
| * | Invalid Memory (*) | --/-- | |||
| * 0 -------------------> +---------------------------------+ 0x00000000 | |||
| * (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped. | |||
| * "Empty Memory" is normally unmapped, but user programs may map pages | |||
| * there if desired. | |||
| * | |||
| * */ | |||
| /* All physical memory mapped at this address */ | |||
| #define KERNBASE 0xC0000000 | |||
| #define KMEMSIZE 0x38000000 // the maximum amount of physical memory | |||
| #define KERNTOP (KERNBASE + KMEMSIZE) | |||
| /* * | |||
| * Virtual page table. Entry PDX[VPT] in the PD (Page Directory) contains | |||
| * a pointer to the page directory itself, thereby turning the PD into a page | |||
| * table, which maps all the PTEs (Page Table Entry) containing the page mappings | |||
| * for the entire virtual address space into that 4 Meg region starting at VPT. | |||
| * */ | |||
| #define VPT 0xFAC00000 | |||
| #define KSTACKPAGE 2 // # of pages in kernel stack | |||
| #define KSTACKSIZE (KSTACKPAGE * PGSIZE) // sizeof kernel stack | |||
| #define USERTOP 0xB0000000 | |||
| #define USTACKTOP USERTOP | |||
| #define USTACKPAGE 256 // # of pages in user stack | |||
| #define USTACKSIZE (USTACKPAGE * PGSIZE) // sizeof user stack | |||
| #define USERBASE 0x00200000 | |||
| #define UTEXT 0x00800000 // where user programs generally begin | |||
| #define USTAB USERBASE // the location of the user STABS data structure | |||
| #define USER_ACCESS(start, end) \ | |||
| (USERBASE <= (start) && (start) < (end) && (end) <= USERTOP) | |||
| #define KERN_ACCESS(start, end) \ | |||
| (KERNBASE <= (start) && (start) < (end) && (end) <= KERNTOP) | |||
| #ifndef __ASSEMBLER__ | |||
| #include <defs.h> | |||
| #include <atomic.h> | |||
| #include <list.h> | |||
| typedef uintptr_t pte_t; | |||
| typedef uintptr_t pde_t; | |||
| typedef pte_t swap_entry_t; //the pte can also be a swap entry | |||
| // some constants for bios interrupt 15h AX = 0xE820 | |||
| #define E820MAX 20 // number of entries in E820MAP | |||
| #define E820_ARM 1 // address range memory | |||
| #define E820_ARR 2 // address range reserved | |||
| struct e820map { | |||
| int nr_map; | |||
| struct { | |||
| uint64_t addr; | |||
| uint64_t size; | |||
| uint32_t type; | |||
| } __attribute__((packed)) map[E820MAX]; | |||
| }; | |||
| /* * | |||
| * struct Page - Page descriptor structures. Each Page describes one | |||
| * physical page. In kern/mm/pmm.h, you can find lots of useful functions | |||
| * that convert Page to other data types, such as phyical address. | |||
| * */ | |||
| struct Page { | |||
| int ref; // page frame's reference counter | |||
| uint32_t flags; // array of flags that describe the status of the page frame | |||
| unsigned int property; // used in buddy system, stores the order (the X in 2^X) of the continuous memory block | |||
| int zone_num; // used in buddy system, the No. of zone which the page belongs to | |||
| list_entry_t page_link; // free list link | |||
| list_entry_t pra_page_link; // used for pra (page replace algorithm) | |||
| uintptr_t pra_vaddr; // used for pra (page replace algorithm) | |||
| }; | |||
| /* Flags describing the status of a page frame */ | |||
| #define PG_reserved 0 // the page descriptor is reserved for kernel or unusable | |||
| #define PG_property 1 // the member 'property' is valid | |||
| #define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags)) | |||
| #define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags)) | |||
| #define PageReserved(page) test_bit(PG_reserved, &((page)->flags)) | |||
| #define SetPageProperty(page) set_bit(PG_property, &((page)->flags)) | |||
| #define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags)) | |||
| #define PageProperty(page) test_bit(PG_property, &((page)->flags)) | |||
| // convert list entry to page | |||
| #define le2page(le, member) \ | |||
| to_struct((le), struct Page, member) | |||
| /* free_area_t - maintains a doubly linked list to record free (unused) pages */ | |||
| typedef struct { | |||
| list_entry_t free_list; // the list header | |||
| unsigned int nr_free; // # of free pages in this free list | |||
| } free_area_t; | |||
| /* for slab style kmalloc */ | |||
| #define PG_slab 2 // page frame is included in a slab | |||
| #define SetPageSlab(page) set_bit(PG_slab, &((page)->flags)) | |||
| #define ClearPageSlab(page) clear_bit(PG_slab, &((page)->flags)) | |||
| #define PageSlab(page) test_bit(PG_slab, &((page)->flags)) | |||
| #endif /* !__ASSEMBLER__ */ | |||
| #endif /* !__KERN_MM_MEMLAYOUT_H__ */ | |||
| @ -0,0 +1,272 @@ | |||
| #ifndef __KERN_MM_MMU_H__ | |||
| #define __KERN_MM_MMU_H__ | |||
| /* Eflags register */ | |||
| #define FL_CF 0x00000001 // Carry Flag | |||
| #define FL_PF 0x00000004 // Parity Flag | |||
| #define FL_AF 0x00000010 // Auxiliary carry Flag | |||
| #define FL_ZF 0x00000040 // Zero Flag | |||
| #define FL_SF 0x00000080 // Sign Flag | |||
| #define FL_TF 0x00000100 // Trap Flag | |||
| #define FL_IF 0x00000200 // Interrupt Flag | |||
| #define FL_DF 0x00000400 // Direction Flag | |||
| #define FL_OF 0x00000800 // Overflow Flag | |||
| #define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask | |||
| #define FL_IOPL_0 0x00000000 // IOPL == 0 | |||
| #define FL_IOPL_1 0x00001000 // IOPL == 1 | |||
| #define FL_IOPL_2 0x00002000 // IOPL == 2 | |||
| #define FL_IOPL_3 0x00003000 // IOPL == 3 | |||
| #define FL_NT 0x00004000 // Nested Task | |||
| #define FL_RF 0x00010000 // Resume Flag | |||
| #define FL_VM 0x00020000 // Virtual 8086 mode | |||
| #define FL_AC 0x00040000 // Alignment Check | |||
| #define FL_VIF 0x00080000 // Virtual Interrupt Flag | |||
| #define FL_VIP 0x00100000 // Virtual Interrupt Pending | |||
| #define FL_ID 0x00200000 // ID flag | |||
| /* Application segment type bits */ | |||
| #define STA_X 0x8 // Executable segment | |||
| #define STA_E 0x4 // Expand down (non-executable segments) | |||
| #define STA_C 0x4 // Conforming code segment (executable only) | |||
| #define STA_W 0x2 // Writeable (non-executable segments) | |||
| #define STA_R 0x2 // Readable (executable segments) | |||
| #define STA_A 0x1 // Accessed | |||
| /* System segment type bits */ | |||
| #define STS_T16A 0x1 // Available 16-bit TSS | |||
| #define STS_LDT 0x2 // Local Descriptor Table | |||
| #define STS_T16B 0x3 // Busy 16-bit TSS | |||
| #define STS_CG16 0x4 // 16-bit Call Gate | |||
| #define STS_TG 0x5 // Task Gate / Coum Transmitions | |||
| #define STS_IG16 0x6 // 16-bit Interrupt Gate | |||
| #define STS_TG16 0x7 // 16-bit Trap Gate | |||
| #define STS_T32A 0x9 // Available 32-bit TSS | |||
| #define STS_T32B 0xB // Busy 32-bit TSS | |||
| #define STS_CG32 0xC // 32-bit Call Gate | |||
| #define STS_IG32 0xE // 32-bit Interrupt Gate | |||
| #define STS_TG32 0xF // 32-bit Trap Gate | |||
| #ifdef __ASSEMBLER__ | |||
| #define SEG_NULL \ | |||
| .word 0, 0; \ | |||
| .byte 0, 0, 0, 0 | |||
| #define SEG_ASM(type,base,lim) \ | |||
| .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ | |||
| .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ | |||
| (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) | |||
| #else /* not __ASSEMBLER__ */ | |||
| #include <defs.h> | |||
| /* Gate descriptors for interrupts and traps */ | |||
| struct gatedesc { | |||
| unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment | |||
| unsigned gd_ss : 16; // segment selector | |||
| unsigned gd_args : 5; // # args, 0 for interrupt/trap gates | |||
| unsigned gd_rsv1 : 3; // reserved(should be zero I guess) | |||
| unsigned gd_type : 4; // type(STS_{TG,IG32,TG32}) | |||
| unsigned gd_s : 1; // must be 0 (system) | |||
| unsigned gd_dpl : 2; // descriptor(meaning new) privilege level | |||
| unsigned gd_p : 1; // Present | |||
| unsigned gd_off_31_16 : 16; // high bits of offset in segment | |||
| }; | |||
| /* * | |||
| * Set up a normal interrupt/trap gate descriptor | |||
| * - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate | |||
| * - sel: Code segment selector for interrupt/trap handler | |||
| * - off: Offset in code segment for interrupt/trap handler | |||
| * - dpl: Descriptor Privilege Level - the privilege level required | |||
| * for software to invoke this interrupt/trap gate explicitly | |||
| * using an int instruction. | |||
| * */ | |||
| #define SETGATE(gate, istrap, sel, off, dpl) { \ | |||
| (gate).gd_off_15_0 = (uint32_t)(off) & 0xffff; \ | |||
| (gate).gd_ss = (sel); \ | |||
| (gate).gd_args = 0; \ | |||
| (gate).gd_rsv1 = 0; \ | |||
| (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \ | |||
| (gate).gd_s = 0; \ | |||
| (gate).gd_dpl = (dpl); \ | |||
| (gate).gd_p = 1; \ | |||
| (gate).gd_off_31_16 = (uint32_t)(off) >> 16; \ | |||
| } | |||
| /* Set up a call gate descriptor */ | |||
| #define SETCALLGATE(gate, ss, off, dpl) { \ | |||
| (gate).gd_off_15_0 = (uint32_t)(off) & 0xffff; \ | |||
| (gate).gd_ss = (ss); \ | |||
| (gate).gd_args = 0; \ | |||
| (gate).gd_rsv1 = 0; \ | |||
| (gate).gd_type = STS_CG32; \ | |||
| (gate).gd_s = 0; \ | |||
| (gate).gd_dpl = (dpl); \ | |||
| (gate).gd_p = 1; \ | |||
| (gate).gd_off_31_16 = (uint32_t)(off) >> 16; \ | |||
| } | |||
| /* segment descriptors */ | |||
| struct segdesc { | |||
| unsigned sd_lim_15_0 : 16; // low bits of segment limit | |||
| unsigned sd_base_15_0 : 16; // low bits of segment base address | |||
| unsigned sd_base_23_16 : 8; // middle bits of segment base address | |||
| unsigned sd_type : 4; // segment type (see STS_ constants) | |||
| unsigned sd_s : 1; // 0 = system, 1 = application | |||
| unsigned sd_dpl : 2; // descriptor Privilege Level | |||
| unsigned sd_p : 1; // present | |||
| unsigned sd_lim_19_16 : 4; // high bits of segment limit | |||
| unsigned sd_avl : 1; // unused (available for software use) | |||
| unsigned sd_rsv1 : 1; // reserved | |||
| unsigned sd_db : 1; // 0 = 16-bit segment, 1 = 32-bit segment | |||
| unsigned sd_g : 1; // granularity: limit scaled by 4K when set | |||
| unsigned sd_base_31_24 : 8; // high bits of segment base address | |||
| }; | |||
| #define SEG_NULL \ | |||
| (struct segdesc) {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} | |||
| #define SEG(type, base, lim, dpl) \ | |||
| (struct segdesc) { \ | |||
| ((lim) >> 12) & 0xffff, (base) & 0xffff, \ | |||
| ((base) >> 16) & 0xff, type, 1, dpl, 1, \ | |||
| (unsigned)(lim) >> 28, 0, 0, 1, 1, \ | |||
| (unsigned) (base) >> 24 \ | |||
| } | |||
| #define SEGTSS(type, base, lim, dpl) \ | |||
| (struct segdesc) { \ | |||
| (lim) & 0xffff, (base) & 0xffff, \ | |||
| ((base) >> 16) & 0xff, type, 0, dpl, 1, \ | |||
| (unsigned) (lim) >> 16, 0, 0, 1, 0, \ | |||
| (unsigned) (base) >> 24 \ | |||
| } | |||
| /* task state segment format (as described by the Pentium architecture book) */ | |||
| struct taskstate { | |||
| uint32_t ts_link; // old ts selector | |||
| uintptr_t ts_esp0; // stack pointers and segment selectors | |||
| uint16_t ts_ss0; // after an increase in privilege level | |||
| uint16_t ts_padding1; | |||
| uintptr_t ts_esp1; | |||
| uint16_t ts_ss1; | |||
| uint16_t ts_padding2; | |||
| uintptr_t ts_esp2; | |||
| uint16_t ts_ss2; | |||
| uint16_t ts_padding3; | |||
| uintptr_t ts_cr3; // page directory base | |||
| uintptr_t ts_eip; // saved state from last task switch | |||
| uint32_t ts_eflags; | |||
| uint32_t ts_eax; // more saved state (registers) | |||
| uint32_t ts_ecx; | |||
| uint32_t ts_edx; | |||
| uint32_t ts_ebx; | |||
| uintptr_t ts_esp; | |||
| uintptr_t ts_ebp; | |||
| uint32_t ts_esi; | |||
| uint32_t ts_edi; | |||
| uint16_t ts_es; // even more saved state (segment selectors) | |||
| uint16_t ts_padding4; | |||
| uint16_t ts_cs; | |||
| uint16_t ts_padding5; | |||
| uint16_t ts_ss; | |||
| uint16_t ts_padding6; | |||
| uint16_t ts_ds; | |||
| uint16_t ts_padding7; | |||
| uint16_t ts_fs; | |||
| uint16_t ts_padding8; | |||
| uint16_t ts_gs; | |||
| uint16_t ts_padding9; | |||
| uint16_t ts_ldt; | |||
| uint16_t ts_padding10; | |||
| uint16_t ts_t; // trap on task switch | |||
| uint16_t ts_iomb; // i/o map base address | |||
| } __attribute__((packed)); | |||
| #endif /* !__ASSEMBLER__ */ | |||
| // A linear address 'la' has a three-part structure as follows: | |||
| // | |||
| // +--------10------+-------10-------+---------12----------+ | |||
| // | Page Directory | Page Table | Offset within Page | | |||
| // | Index | Index | | | |||
| // +----------------+----------------+---------------------+ | |||
| // \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/ | |||
| // \----------- PPN(la) -----------/ | |||
| // | |||
| // The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown. | |||
| // To construct a linear address la from PDX(la), PTX(la), and PGOFF(la), | |||
| // use PGADDR(PDX(la), PTX(la), PGOFF(la)). | |||
| // page directory index | |||
| #define PDX(la) ((((uintptr_t)(la)) >> PDXSHIFT) & 0x3FF) | |||
| // page table index | |||
| #define PTX(la) ((((uintptr_t)(la)) >> PTXSHIFT) & 0x3FF) | |||
| // page number field of address | |||
| #define PPN(la) (((uintptr_t)(la)) >> PTXSHIFT) | |||
| // offset in page | |||
| #define PGOFF(la) (((uintptr_t)(la)) & 0xFFF) | |||
| // construct linear address from indexes and offset | |||
| #define PGADDR(d, t, o) ((uintptr_t)((d) << PDXSHIFT | (t) << PTXSHIFT | (o))) | |||
| // address in page table or page directory entry | |||
| #define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF) | |||
| #define PDE_ADDR(pde) PTE_ADDR(pde) | |||
| /* page directory and page table constants */ | |||
| #define NPDEENTRY 1024 // page directory entries per page directory | |||
| #define NPTEENTRY 1024 // page table entries per page table | |||
| #define PGSIZE 4096 // bytes mapped by a page | |||
| #define PGSHIFT 12 // log2(PGSIZE) | |||
| #define PTSIZE (PGSIZE * NPTEENTRY) // bytes mapped by a page directory entry | |||
| #define PTSHIFT 22 // log2(PTSIZE) | |||
| #define PTXSHIFT 12 // offset of PTX in a linear address | |||
| #define PDXSHIFT 22 // offset of PDX in a linear address | |||
| /* page table/directory entry flags */ | |||
| #define PTE_P 0x001 // Present | |||
| #define PTE_W 0x002 // Writeable | |||
| #define PTE_U 0x004 // User | |||
| #define PTE_PWT 0x008 // Write-Through | |||
| #define PTE_PCD 0x010 // Cache-Disable | |||
| #define PTE_A 0x020 // Accessed | |||
| #define PTE_D 0x040 // Dirty | |||
| #define PTE_PS 0x080 // Page Size | |||
| #define PTE_MBZ 0x180 // Bits must be zero | |||
| #define PTE_AVAIL 0xE00 // Available for software use | |||
| // The PTE_AVAIL bits aren't used by the kernel or interpreted by the | |||
| // hardware, so user processes are allowed to set them arbitrarily. | |||
| #define PTE_USER (PTE_U | PTE_W | PTE_P) | |||
| /* Control Register flags */ | |||
| #define CR0_PE 0x00000001 // Protection Enable | |||
| #define CR0_MP 0x00000002 // Monitor coProcessor | |||
| #define CR0_EM 0x00000004 // Emulation | |||
| #define CR0_TS 0x00000008 // Task Switched | |||
| #define CR0_ET 0x00000010 // Extension Type | |||
| #define CR0_NE 0x00000020 // Numeric Errror | |||
| #define CR0_WP 0x00010000 // Write Protect | |||
| #define CR0_AM 0x00040000 // Alignment Mask | |||
| #define CR0_NW 0x20000000 // Not Writethrough | |||
| #define CR0_CD 0x40000000 // Cache Disable | |||
| #define CR0_PG 0x80000000 // Paging | |||
| #define CR4_PCE 0x00000100 // Performance counter enable | |||
| #define CR4_MCE 0x00000040 // Machine Check Enable | |||
| #define CR4_PSE 0x00000010 // Page Size Extensions | |||
| #define CR4_DE 0x00000008 // Debugging Extensions | |||
| #define CR4_TSD 0x00000004 // Time Stamp Disable | |||
| #define CR4_PVI 0x00000002 // Protected-Mode Virtual Interrupts | |||
| #define CR4_VME 0x00000001 // V86 Mode Extensions | |||
| #endif /* !__KERN_MM_MMU_H__ */ | |||
| @ -0,0 +1,785 @@ | |||
| #include <defs.h> | |||
| #include <x86.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <mmu.h> | |||
| #include <memlayout.h> | |||
| #include <pmm.h> | |||
| #include <default_pmm.h> | |||
| #include <sync.h> | |||
| #include <error.h> | |||
| #include <swap.h> | |||
| #include <vmm.h> | |||
| #include <kmalloc.h> | |||
| /* * | |||
| * Task State Segment: | |||
| * | |||
| * The TSS may reside anywhere in memory. A special segment register called | |||
| * the Task Register (TR) holds a segment selector that points a valid TSS | |||
| * segment descriptor which resides in the GDT. Therefore, to use a TSS | |||
| * the following must be done in function gdt_init: | |||
| * - create a TSS descriptor entry in GDT | |||
| * - add enough information to the TSS in memory as needed | |||
| * - load the TR register with a segment selector for that segment | |||
| * | |||
| * There are several fileds in TSS for specifying the new stack pointer when a | |||
| * privilege level change happens. But only the fields SS0 and ESP0 are useful | |||
| * in our os kernel. | |||
| * | |||
| * The field SS0 contains the stack segment selector for CPL = 0, and the ESP0 | |||
| * contains the new ESP value for CPL = 0. When an interrupt happens in protected | |||
| * mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value | |||
| * into SS and ESP respectively. | |||
| * */ | |||
| static struct taskstate ts = {0}; | |||
| // virtual address of physicall page array | |||
| struct Page *pages; | |||
| // amount of physical memory (in pages) | |||
| size_t npage = 0; | |||
| // virtual address of boot-time page directory | |||
| pde_t *boot_pgdir = NULL; | |||
| // physical address of boot-time page directory | |||
| uintptr_t boot_cr3; | |||
| // physical memory management | |||
| const struct pmm_manager *pmm_manager; | |||
| /* * | |||
| * The page directory entry corresponding to the virtual address range | |||
| * [VPT, VPT + PTSIZE) points to the page directory itself. Thus, the page | |||
| * directory is treated as a page table as well as a page directory. | |||
| * | |||
| * One result of treating the page directory as a page table is that all PTEs | |||
| * can be accessed though a "virtual page table" at virtual address VPT. And the | |||
| * PTE for number n is stored in vpt[n]. | |||
| * | |||
| * A second consequence is that the contents of the current page directory will | |||
| * always available at virtual address PGADDR(PDX(VPT), PDX(VPT), 0), to which | |||
| * vpd is set bellow. | |||
| * */ | |||
| pte_t * const vpt = (pte_t *)VPT; | |||
| pde_t * const vpd = (pde_t *)PGADDR(PDX(VPT), PDX(VPT), 0); | |||
| /* * | |||
| * Global Descriptor Table: | |||
| * | |||
| * The kernel and user segments are identical (except for the DPL). To load | |||
| * the %ss register, the CPL must equal the DPL. Thus, we must duplicate the | |||
| * segments for the user and the kernel. Defined as follows: | |||
| * - 0x0 : unused (always faults -- for trapping NULL far pointers) | |||
| * - 0x8 : kernel code segment | |||
| * - 0x10: kernel data segment | |||
| * - 0x18: user code segment | |||
| * - 0x20: user data segment | |||
| * - 0x28: defined for tss, initialized in gdt_init | |||
| * */ | |||
| static struct segdesc gdt[] = { | |||
| SEG_NULL, | |||
| [SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL), | |||
| [SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL), | |||
| [SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER), | |||
| [SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER), | |||
| [SEG_TSS] = SEG_NULL, | |||
| }; | |||
| static struct pseudodesc gdt_pd = { | |||
| sizeof(gdt) - 1, (uintptr_t)gdt | |||
| }; | |||
| static void check_alloc_page(void); | |||
| static void check_pgdir(void); | |||
| static void check_boot_pgdir(void); | |||
| /* * | |||
| * lgdt - load the global descriptor table register and reset the | |||
| * data/code segement registers for kernel. | |||
| * */ | |||
| static inline void | |||
| lgdt(struct pseudodesc *pd) { | |||
| asm volatile ("lgdt (%0)" :: "r" (pd)); | |||
| asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS)); | |||
| asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS)); | |||
| asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS)); | |||
| asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS)); | |||
| asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS)); | |||
| // reload cs | |||
| asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS)); | |||
| } | |||
| /* * | |||
| * load_esp0 - change the ESP0 in default task state segment, | |||
| * so that we can use different kernel stack when we trap frame | |||
| * user to kernel. | |||
| * */ | |||
| void | |||
| load_esp0(uintptr_t esp0) { | |||
| ts.ts_esp0 = esp0; | |||
| } | |||
| /* gdt_init - initialize the default GDT and TSS */ | |||
| static void | |||
| gdt_init(void) { | |||
| // set boot kernel stack and default SS0 | |||
| load_esp0((uintptr_t)bootstacktop); | |||
| ts.ts_ss0 = KERNEL_DS; | |||
| // initialize the TSS filed of the gdt | |||
| gdt[SEG_TSS] = SEGTSS(STS_T32A, (uintptr_t)&ts, sizeof(ts), DPL_KERNEL); | |||
| // reload all segment registers | |||
| lgdt(&gdt_pd); | |||
| // load the TSS | |||
| ltr(GD_TSS); | |||
| } | |||
| //init_pmm_manager - initialize a pmm_manager instance | |||
| static void | |||
| init_pmm_manager(void) { | |||
| pmm_manager = &default_pmm_manager; | |||
| cprintf("memory management: %s\n", pmm_manager->name); | |||
| pmm_manager->init(); | |||
| } | |||
| //init_memmap - call pmm->init_memmap to build Page struct for free memory | |||
| static void | |||
| init_memmap(struct Page *base, size_t n) { | |||
| pmm_manager->init_memmap(base, n); | |||
| } | |||
| //alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory | |||
| struct Page * | |||
| alloc_pages(size_t n) { | |||
| struct Page *page=NULL; | |||
| bool intr_flag; | |||
| while (1) | |||
| { | |||
| local_intr_save(intr_flag); | |||
| { | |||
| page = pmm_manager->alloc_pages(n); | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| if (page != NULL || n > 1 || swap_init_ok == 0) break; | |||
| extern struct mm_struct *check_mm_struct; | |||
| //cprintf("page %x, call swap_out in alloc_pages %d\n",page, n); | |||
| swap_out(check_mm_struct, n, 0); | |||
| } | |||
| //cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages)); | |||
| return page; | |||
| } | |||
| //free_pages - call pmm->free_pages to free a continuous n*PAGESIZE memory | |||
| void | |||
| free_pages(struct Page *base, size_t n) { | |||
| bool intr_flag; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| pmm_manager->free_pages(base, n); | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| } | |||
| //nr_free_pages - call pmm->nr_free_pages to get the size (nr*PAGESIZE) | |||
| //of current free memory | |||
| size_t | |||
| nr_free_pages(void) { | |||
| size_t ret; | |||
| bool intr_flag; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| ret = pmm_manager->nr_free_pages(); | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| return ret; | |||
| } | |||
| /* pmm_init - initialize the physical memory management */ | |||
| static void | |||
| page_init(void) { | |||
| struct e820map *memmap = (struct e820map *)(0x8000 + KERNBASE); | |||
| uint64_t maxpa = 0; | |||
| cprintf("e820map:\n"); | |||
| int i; | |||
| for (i = 0; i < memmap->nr_map; i ++) { | |||
| uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size; | |||
| cprintf(" memory: %08llx, [%08llx, %08llx], type = %d.\n", | |||
| memmap->map[i].size, begin, end - 1, memmap->map[i].type); | |||
| if (memmap->map[i].type == E820_ARM) { | |||
| if (maxpa < end && begin < KMEMSIZE) { | |||
| maxpa = end; | |||
| } | |||
| } | |||
| } | |||
| if (maxpa > KMEMSIZE) { | |||
| maxpa = KMEMSIZE; | |||
| } | |||
| extern char end[]; | |||
| npage = maxpa / PGSIZE; | |||
| pages = (struct Page *)ROUNDUP((void *)end, PGSIZE); | |||
| for (i = 0; i < npage; i ++) { | |||
| SetPageReserved(pages + i); | |||
| } | |||
| uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage); | |||
| for (i = 0; i < memmap->nr_map; i ++) { | |||
| uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size; | |||
| if (memmap->map[i].type == E820_ARM) { | |||
| if (begin < freemem) { | |||
| begin = freemem; | |||
| } | |||
| if (end > KMEMSIZE) { | |||
| end = KMEMSIZE; | |||
| } | |||
| if (begin < end) { | |||
| begin = ROUNDUP(begin, PGSIZE); | |||
| end = ROUNDDOWN(end, PGSIZE); | |||
| if (begin < end) { | |||
| init_memmap(pa2page(begin), (end - begin) / PGSIZE); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| static void | |||
| enable_paging(void) { | |||
| lcr3(boot_cr3); | |||
| // turn on paging | |||
| uint32_t cr0 = rcr0(); | |||
| cr0 |= CR0_PE | CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_TS | CR0_EM | CR0_MP; | |||
| cr0 &= ~(CR0_TS | CR0_EM); | |||
| lcr0(cr0); | |||
| } | |||
| //boot_map_segment - setup&enable the paging mechanism | |||
| // parameters | |||
| // la: linear address of this memory need to map (after x86 segment map) | |||
| // size: memory size | |||
| // pa: physical address of this memory | |||
| // perm: permission of this memory | |||
| static void | |||
| boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, uintptr_t pa, uint32_t perm) { | |||
| assert(PGOFF(la) == PGOFF(pa)); | |||
| size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE; | |||
| la = ROUNDDOWN(la, PGSIZE); | |||
| pa = ROUNDDOWN(pa, PGSIZE); | |||
| for (; n > 0; n --, la += PGSIZE, pa += PGSIZE) { | |||
| pte_t *ptep = get_pte(pgdir, la, 1); | |||
| assert(ptep != NULL); | |||
| *ptep = pa | PTE_P | perm; | |||
| } | |||
| } | |||
| //boot_alloc_page - allocate one page using pmm->alloc_pages(1) | |||
| // return value: the kernel virtual address of this allocated page | |||
| //note: this function is used to get the memory for PDT(Page Directory Table)&PT(Page Table) | |||
| static void * | |||
| boot_alloc_page(void) { | |||
| struct Page *p = alloc_page(); | |||
| if (p == NULL) { | |||
| panic("boot_alloc_page failed.\n"); | |||
| } | |||
| return page2kva(p); | |||
| } | |||
| //pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup paging mechanism | |||
| // - check the correctness of pmm & paging mechanism, print PDT&PT | |||
| void | |||
| pmm_init(void) { | |||
| //We need to alloc/free the physical memory (granularity is 4KB or other size). | |||
| //So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h | |||
| //First we should init a physical memory manager(pmm) based on the framework. | |||
| //Then pmm can alloc/free the physical memory. | |||
| //Now the first_fit/best_fit/worst_fit/buddy_system pmm are available. | |||
| init_pmm_manager(); | |||
| // detect physical memory space, reserve already used memory, | |||
| // then use pmm->init_memmap to create free page list | |||
| page_init(); | |||
| //use pmm->check to verify the correctness of the alloc/free function in a pmm | |||
| check_alloc_page(); | |||
| // create boot_pgdir, an initial page directory(Page Directory Table, PDT) | |||
| boot_pgdir = boot_alloc_page(); | |||
| memset(boot_pgdir, 0, PGSIZE); | |||
| boot_cr3 = PADDR(boot_pgdir); | |||
| check_pgdir(); | |||
| static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0); | |||
| // recursively insert boot_pgdir in itself | |||
| // to form a virtual page table at virtual address VPT | |||
| boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_P | PTE_W; | |||
| // map all physical memory to linear memory with base linear addr KERNBASE | |||
| //linear_addr KERNBASE~KERNBASE+KMEMSIZE = phy_addr 0~KMEMSIZE | |||
| //But shouldn't use this map until enable_paging() & gdt_init() finished. | |||
| boot_map_segment(boot_pgdir, KERNBASE, KMEMSIZE, 0, PTE_W); | |||
| //temporary map: | |||
| //virtual_addr 3G~3G+4M = linear_addr 0~4M = linear_addr 3G~3G+4M = phy_addr 0~4M | |||
| boot_pgdir[0] = boot_pgdir[PDX(KERNBASE)]; | |||
| enable_paging(); | |||
| //reload gdt(third time,the last time) to map all physical memory | |||
| //virtual_addr 0~4G=liear_addr 0~4G | |||
| //then set kernel stack(ss:esp) in TSS, setup TSS in gdt, load TSS | |||
| gdt_init(); | |||
| //disable the map of virtual_addr 0~4M | |||
| boot_pgdir[0] = 0; | |||
| //now the basic virtual memory map(see memalyout.h) is established. | |||
| //check the correctness of the basic virtual memory map. | |||
| check_boot_pgdir(); | |||
| print_pgdir(); | |||
| kmalloc_init(); | |||
| } | |||
| //get_pte - get pte and return the kernel virtual address of this pte for la | |||
| // - if the PT contians this pte didn't exist, alloc a page for PT | |||
| // parameter: | |||
| // pgdir: the kernel virtual base address of PDT | |||
| // la: the linear address need to map | |||
| // create: a logical value to decide if alloc a page for PT | |||
| // return vaule: the kernel virtual address of this pte | |||
| pte_t * | |||
| get_pte(pde_t *pgdir, uintptr_t la, bool create) { | |||
| /* LAB2 EXERCISE 2: YOUR CODE | |||
| * | |||
| * If you need to visit a physical address, please use KADDR() | |||
| * please read pmm.h for useful macros | |||
| * | |||
| * Maybe you want help comment, BELOW comments can help you finish the code | |||
| * | |||
| * Some Useful MACROs and DEFINEs, you can use them in below implementation. | |||
| * MACROs or Functions: | |||
| * PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la. | |||
| * KADDR(pa) : takes a physical address and returns the corresponding kernel virtual address. | |||
| * set_page_ref(page,1) : means the page be referenced by one time | |||
| * page2pa(page): get the physical address of memory which this (struct Page *) page manages | |||
| * struct Page * alloc_page() : allocation a page | |||
| * memset(void *s, char c, size_t n) : sets the first n bytes of the memory area pointed by s | |||
| * to the specified value c. | |||
| * DEFINEs: | |||
| * PTE_P 0x001 // page table/directory entry flags bit : Present | |||
| * PTE_W 0x002 // page table/directory entry flags bit : Writeable | |||
| * PTE_U 0x004 // page table/directory entry flags bit : User can access | |||
| */ | |||
| #if 0 | |||
| pde_t *pdep = NULL; // (1) find page directory entry | |||
| if (0) { // (2) check if entry is not present | |||
| // (3) check if creating is needed, then alloc page for page table | |||
| // CAUTION: this page is used for page table, not for common data page | |||
| // (4) set page reference | |||
| uintptr_t pa = 0; // (5) get linear address of page | |||
| // (6) clear page content using memset | |||
| // (7) set page directory entry's permission | |||
| } | |||
| return NULL; // (8) return page table entry | |||
| #endif | |||
| pde_t *pdep = &pgdir[PDX(la)]; | |||
| if (!(*pdep & PTE_P)) { | |||
| struct Page *page; | |||
| if (!create || (page = alloc_page()) == NULL) { | |||
| return NULL; | |||
| } | |||
| set_page_ref(page, 1); | |||
| uintptr_t pa = page2pa(page); | |||
| memset(KADDR(pa), 0, PGSIZE); | |||
| *pdep = pa | PTE_U | PTE_W | PTE_P; | |||
| } | |||
| return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)]; | |||
| } | |||
| //get_page - get related Page struct for linear address la using PDT pgdir | |||
| struct Page * | |||
| get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store) { | |||
| pte_t *ptep = get_pte(pgdir, la, 0); | |||
| if (ptep_store != NULL) { | |||
| *ptep_store = ptep; | |||
| } | |||
| if (ptep != NULL && *ptep & PTE_P) { | |||
| return pa2page(*ptep); | |||
| } | |||
| return NULL; | |||
| } | |||
| //page_remove_pte - free an Page sturct which is related linear address la | |||
| // - and clean(invalidate) pte which is related linear address la | |||
| //note: PT is changed, so the TLB need to be invalidate | |||
| static inline void | |||
| page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) { | |||
| /* LAB2 EXERCISE 3: YOUR CODE | |||
| * | |||
| * Please check if ptep is valid, and tlb must be manually updated if mapping is updated | |||
| * | |||
| * Maybe you want help comment, BELOW comments can help you finish the code | |||
| * | |||
| * Some Useful MACROs and DEFINEs, you can use them in below implementation. | |||
| * MACROs or Functions: | |||
| * struct Page *page pte2page(*ptep): get the according page from the value of a ptep | |||
| * free_page : free a page | |||
| * page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , then this page should be free. | |||
| * tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, but only if the page tables being | |||
| * edited are the ones currently in use by the processor. | |||
| * DEFINEs: | |||
| * PTE_P 0x001 // page table/directory entry flags bit : Present | |||
| */ | |||
| #if 0 | |||
| if (0) { //(1) check if page directory is present | |||
| struct Page *page = NULL; //(2) find corresponding page to pte | |||
| //(3) decrease page reference | |||
| //(4) and free this page when page reference reachs 0 | |||
| //(5) clear second page table entry | |||
| //(6) flush tlb | |||
| } | |||
| #endif | |||
| if (*ptep & PTE_P) { | |||
| struct Page *page = pte2page(*ptep); | |||
| if (page_ref_dec(page) == 0) { | |||
| free_page(page); | |||
| } | |||
| *ptep = 0; | |||
| tlb_invalidate(pgdir, la); | |||
| } | |||
| } | |||
| void | |||
| unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end) { | |||
| assert(start % PGSIZE == 0 && end % PGSIZE == 0); | |||
| assert(USER_ACCESS(start, end)); | |||
| do { | |||
| pte_t *ptep = get_pte(pgdir, start, 0); | |||
| if (ptep == NULL) { | |||
| start = ROUNDDOWN(start + PTSIZE, PTSIZE); | |||
| continue ; | |||
| } | |||
| if (*ptep != 0) { | |||
| page_remove_pte(pgdir, start, ptep); | |||
| } | |||
| start += PGSIZE; | |||
| } while (start != 0 && start < end); | |||
| } | |||
| void | |||
| exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end) { | |||
| assert(start % PGSIZE == 0 && end % PGSIZE == 0); | |||
| assert(USER_ACCESS(start, end)); | |||
| start = ROUNDDOWN(start, PTSIZE); | |||
| do { | |||
| int pde_idx = PDX(start); | |||
| if (pgdir[pde_idx] & PTE_P) { | |||
| free_page(pde2page(pgdir[pde_idx])); | |||
| pgdir[pde_idx] = 0; | |||
| } | |||
| start += PTSIZE; | |||
| } while (start != 0 && start < end); | |||
| } | |||
| /* copy_range - copy content of memory (start, end) of one process A to another process B | |||
| * @to: the addr of process B's Page Directory | |||
| * @from: the addr of process A's Page Directory | |||
| * @share: flags to indicate to dup OR share. We just use dup method, so it didn't be used. | |||
| * | |||
| * CALL GRAPH: copy_mm-->dup_mmap-->copy_range | |||
| */ | |||
| int | |||
| copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share) { | |||
| assert(start % PGSIZE == 0 && end % PGSIZE == 0); | |||
| assert(USER_ACCESS(start, end)); | |||
| // copy content by page unit. | |||
| do { | |||
| //call get_pte to find process A's pte according to the addr start | |||
| pte_t *ptep = get_pte(from, start, 0), *nptep; | |||
| if (ptep == NULL) { | |||
| start = ROUNDDOWN(start + PTSIZE, PTSIZE); | |||
| continue ; | |||
| } | |||
| //call get_pte to find process B's pte according to the addr start. If pte is NULL, just alloc a PT | |||
| if (*ptep & PTE_P) { | |||
| if ((nptep = get_pte(to, start, 1)) == NULL) { | |||
| return -E_NO_MEM; | |||
| } | |||
| uint32_t perm = (*ptep & PTE_USER); | |||
| //get page from ptep | |||
| struct Page *page = pte2page(*ptep); | |||
| // alloc a page for process B | |||
| struct Page *npage=alloc_page(); | |||
| assert(page!=NULL); | |||
| assert(npage!=NULL); | |||
| int ret=0; | |||
| /* LAB5:EXERCISE2 YOUR CODE | |||
| * replicate content of page to npage, build the map of phy addr of nage with the linear addr start | |||
| * | |||
| * Some Useful MACROs and DEFINEs, you can use them in below implementation. | |||
| * MACROs or Functions: | |||
| * page2kva(struct Page *page): return the kernel vritual addr of memory which page managed (SEE pmm.h) | |||
| * page_insert: build the map of phy addr of an Page with the linear addr la | |||
| * memcpy: typical memory copy function | |||
| * | |||
| * (1) find src_kvaddr: the kernel virtual address of page | |||
| * (2) find dst_kvaddr: the kernel virtual address of npage | |||
| * (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE | |||
| * (4) build the map of phy addr of nage with the linear addr start | |||
| */ | |||
| void * kva_src = page2kva(page); | |||
| void * kva_dst = page2kva(npage); | |||
| memcpy(kva_dst, kva_src, PGSIZE); | |||
| ret = page_insert(to, npage, start, perm); | |||
| assert(ret == 0); | |||
| } | |||
| start += PGSIZE; | |||
| } while (start != 0 && start < end); | |||
| return 0; | |||
| } | |||
| //page_remove - free an Page which is related linear address la and has an validated pte | |||
| void | |||
| page_remove(pde_t *pgdir, uintptr_t la) { | |||
| pte_t *ptep = get_pte(pgdir, la, 0); | |||
| if (ptep != NULL) { | |||
| page_remove_pte(pgdir, la, ptep); | |||
| } | |||
| } | |||
| //page_insert - build the map of phy addr of an Page with the linear addr la | |||
| // paramemters: | |||
| // pgdir: the kernel virtual base address of PDT | |||
| // page: the Page which need to map | |||
| // la: the linear address need to map | |||
| // perm: the permission of this Page which is setted in related pte | |||
| // return value: always 0 | |||
| //note: PT is changed, so the TLB need to be invalidate | |||
| int | |||
| page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) { | |||
| pte_t *ptep = get_pte(pgdir, la, 1); | |||
| if (ptep == NULL) { | |||
| return -E_NO_MEM; | |||
| } | |||
| page_ref_inc(page); | |||
| if (*ptep & PTE_P) { | |||
| struct Page *p = pte2page(*ptep); | |||
| if (p == page) { | |||
| page_ref_dec(page); | |||
| } | |||
| else { | |||
| page_remove_pte(pgdir, la, ptep); | |||
| } | |||
| } | |||
| *ptep = page2pa(page) | PTE_P | perm; | |||
| tlb_invalidate(pgdir, la); | |||
| return 0; | |||
| } | |||
| // invalidate a TLB entry, but only if the page tables being | |||
| // edited are the ones currently in use by the processor. | |||
| void | |||
| tlb_invalidate(pde_t *pgdir, uintptr_t la) { | |||
| if (rcr3() == PADDR(pgdir)) { | |||
| invlpg((void *)la); | |||
| } | |||
| } | |||
| // pgdir_alloc_page - call alloc_page & page_insert functions to | |||
| // - allocate a page size memory & setup an addr map | |||
| // - pa<->la with linear address la and the PDT pgdir | |||
| struct Page * | |||
| pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) { | |||
| struct Page *page = alloc_page(); | |||
| if (page != NULL) { | |||
| if (page_insert(pgdir, page, la, perm) != 0) { | |||
| free_page(page); | |||
| return NULL; | |||
| } | |||
| if (swap_init_ok){ | |||
| if(check_mm_struct!=NULL) { | |||
| swap_map_swappable(check_mm_struct, la, page, 0); | |||
| page->pra_vaddr=la; | |||
| assert(page_ref(page) == 1); | |||
| //cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, pra_link_next %x in pgdir_alloc_page\n", (page-pages), page->pra_vaddr,page->pra_page_link.prev, page->pra_page_link.next); | |||
| } | |||
| else { //now current is existed, should fix it in the future | |||
| //swap_map_swappable(current->mm, la, page, 0); | |||
| //page->pra_vaddr=la; | |||
| //assert(page_ref(page) == 1); | |||
| //panic("pgdir_alloc_page: no pages. now current is existed, should fix it in the future\n"); | |||
| } | |||
| } | |||
| } | |||
| return page; | |||
| } | |||
| static void | |||
| check_alloc_page(void) { | |||
| pmm_manager->check(); | |||
| cprintf("check_alloc_page() succeeded!\n"); | |||
| } | |||
| static void | |||
| check_pgdir(void) { | |||
| assert(npage <= KMEMSIZE / PGSIZE); | |||
| assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0); | |||
| assert(get_page(boot_pgdir, 0x0, NULL) == NULL); | |||
| struct Page *p1, *p2; | |||
| p1 = alloc_page(); | |||
| assert(page_insert(boot_pgdir, p1, 0x0, 0) == 0); | |||
| pte_t *ptep; | |||
| assert((ptep = get_pte(boot_pgdir, 0x0, 0)) != NULL); | |||
| assert(pa2page(*ptep) == p1); | |||
| assert(page_ref(p1) == 1); | |||
| ptep = &((pte_t *)KADDR(PDE_ADDR(boot_pgdir[0])))[1]; | |||
| assert(get_pte(boot_pgdir, PGSIZE, 0) == ptep); | |||
| p2 = alloc_page(); | |||
| assert(page_insert(boot_pgdir, p2, PGSIZE, PTE_U | PTE_W) == 0); | |||
| assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL); | |||
| assert(*ptep & PTE_U); | |||
| assert(*ptep & PTE_W); | |||
| assert(boot_pgdir[0] & PTE_U); | |||
| assert(page_ref(p2) == 1); | |||
| assert(page_insert(boot_pgdir, p1, PGSIZE, 0) == 0); | |||
| assert(page_ref(p1) == 2); | |||
| assert(page_ref(p2) == 0); | |||
| assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL); | |||
| assert(pa2page(*ptep) == p1); | |||
| assert((*ptep & PTE_U) == 0); | |||
| page_remove(boot_pgdir, 0x0); | |||
| assert(page_ref(p1) == 1); | |||
| assert(page_ref(p2) == 0); | |||
| page_remove(boot_pgdir, PGSIZE); | |||
| assert(page_ref(p1) == 0); | |||
| assert(page_ref(p2) == 0); | |||
| assert(page_ref(pa2page(boot_pgdir[0])) == 1); | |||
| free_page(pa2page(boot_pgdir[0])); | |||
| boot_pgdir[0] = 0; | |||
| cprintf("check_pgdir() succeeded!\n"); | |||
| } | |||
| static void | |||
| check_boot_pgdir(void) { | |||
| pte_t *ptep; | |||
| int i; | |||
| for (i = 0; i < npage; i += PGSIZE) { | |||
| assert((ptep = get_pte(boot_pgdir, (uintptr_t)KADDR(i), 0)) != NULL); | |||
| assert(PTE_ADDR(*ptep) == i); | |||
| } | |||
| assert(PDE_ADDR(boot_pgdir[PDX(VPT)]) == PADDR(boot_pgdir)); | |||
| assert(boot_pgdir[0] == 0); | |||
| struct Page *p; | |||
| p = alloc_page(); | |||
| assert(page_insert(boot_pgdir, p, 0x100, PTE_W) == 0); | |||
| assert(page_ref(p) == 1); | |||
| assert(page_insert(boot_pgdir, p, 0x100 + PGSIZE, PTE_W) == 0); | |||
| assert(page_ref(p) == 2); | |||
| const char *str = "ucore: Hello world!!"; | |||
| strcpy((void *)0x100, str); | |||
| assert(strcmp((void *)0x100, (void *)(0x100 + PGSIZE)) == 0); | |||
| *(char *)(page2kva(p) + 0x100) = '\0'; | |||
| assert(strlen((const char *)0x100) == 0); | |||
| free_page(p); | |||
| free_page(pa2page(PDE_ADDR(boot_pgdir[0]))); | |||
| boot_pgdir[0] = 0; | |||
| cprintf("check_boot_pgdir() succeeded!\n"); | |||
| } | |||
| //perm2str - use string 'u,r,w,-' to present the permission | |||
| static const char * | |||
| perm2str(int perm) { | |||
| static char str[4]; | |||
| str[0] = (perm & PTE_U) ? 'u' : '-'; | |||
| str[1] = 'r'; | |||
| str[2] = (perm & PTE_W) ? 'w' : '-'; | |||
| str[3] = '\0'; | |||
| return str; | |||
| } | |||
| //get_pgtable_items - In [left, right] range of PDT or PT, find a continuous linear addr space | |||
| // - (left_store*X_SIZE~right_store*X_SIZE) for PDT or PT | |||
| // - X_SIZE=PTSIZE=4M, if PDT; X_SIZE=PGSIZE=4K, if PT | |||
| // paramemters: | |||
| // left: no use ??? | |||
| // right: the high side of table's range | |||
| // start: the low side of table's range | |||
| // table: the beginning addr of table | |||
| // left_store: the pointer of the high side of table's next range | |||
| // right_store: the pointer of the low side of table's next range | |||
| // return value: 0 - not a invalid item range, perm - a valid item range with perm permission | |||
| static int | |||
| get_pgtable_items(size_t left, size_t right, size_t start, uintptr_t *table, size_t *left_store, size_t *right_store) { | |||
| if (start >= right) { | |||
| return 0; | |||
| } | |||
| while (start < right && !(table[start] & PTE_P)) { | |||
| start ++; | |||
| } | |||
| if (start < right) { | |||
| if (left_store != NULL) { | |||
| *left_store = start; | |||
| } | |||
| int perm = (table[start ++] & PTE_USER); | |||
| while (start < right && (table[start] & PTE_USER) == perm) { | |||
| start ++; | |||
| } | |||
| if (right_store != NULL) { | |||
| *right_store = start; | |||
| } | |||
| return perm; | |||
| } | |||
| return 0; | |||
| } | |||
| //print_pgdir - print the PDT&PT | |||
| void | |||
| print_pgdir(void) { | |||
| cprintf("-------------------- BEGIN --------------------\n"); | |||
| size_t left, right = 0, perm; | |||
| while ((perm = get_pgtable_items(0, NPDEENTRY, right, vpd, &left, &right)) != 0) { | |||
| cprintf("PDE(%03x) %08x-%08x %08x %s\n", right - left, | |||
| left * PTSIZE, right * PTSIZE, (right - left) * PTSIZE, perm2str(perm)); | |||
| size_t l, r = left * NPTEENTRY; | |||
| while ((perm = get_pgtable_items(left * NPTEENTRY, right * NPTEENTRY, r, vpt, &l, &r)) != 0) { | |||
| cprintf(" |-- PTE(%05x) %08x-%08x %08x %s\n", r - l, | |||
| l * PGSIZE, r * PGSIZE, (r - l) * PGSIZE, perm2str(perm)); | |||
| } | |||
| } | |||
| cprintf("--------------------- END ---------------------\n"); | |||
| } | |||
| @ -0,0 +1,147 @@ | |||
| #ifndef __KERN_MM_PMM_H__ | |||
| #define __KERN_MM_PMM_H__ | |||
| #include <defs.h> | |||
| #include <mmu.h> | |||
| #include <memlayout.h> | |||
| #include <atomic.h> | |||
| #include <assert.h> | |||
| // pmm_manager is a physical memory management class. A special pmm manager - XXX_pmm_manager | |||
| // only needs to implement the methods in pmm_manager class, then XXX_pmm_manager can be used | |||
| // by ucore to manage the total physical memory space. | |||
| struct pmm_manager { | |||
| const char *name; // XXX_pmm_manager's name | |||
| void (*init)(void); // initialize internal description&management data structure | |||
| // (free block list, number of free block) of XXX_pmm_manager | |||
| void (*init_memmap)(struct Page *base, size_t n); // setup description&management data structcure according to | |||
| // the initial free physical memory space | |||
| struct Page *(*alloc_pages)(size_t n); // allocate >=n pages, depend on the allocation algorithm | |||
| void (*free_pages)(struct Page *base, size_t n); // free >=n pages with "base" addr of Page descriptor structures(memlayout.h) | |||
| size_t (*nr_free_pages)(void); // return the number of free pages | |||
| void (*check)(void); // check the correctness of XXX_pmm_manager | |||
| }; | |||
| extern const struct pmm_manager *pmm_manager; | |||
| extern pde_t *boot_pgdir; | |||
| extern uintptr_t boot_cr3; | |||
| void pmm_init(void); | |||
| struct Page *alloc_pages(size_t n); | |||
| void free_pages(struct Page *base, size_t n); | |||
| size_t nr_free_pages(void); | |||
| #define alloc_page() alloc_pages(1) | |||
| #define free_page(page) free_pages(page, 1) | |||
| pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create); | |||
| struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store); | |||
| void page_remove(pde_t *pgdir, uintptr_t la); | |||
| int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm); | |||
| void load_esp0(uintptr_t esp0); | |||
| void tlb_invalidate(pde_t *pgdir, uintptr_t la); | |||
| struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm); | |||
| void unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end); | |||
| void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end); | |||
| int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share); | |||
| void print_pgdir(void); | |||
| /* * | |||
| * PADDR - takes a kernel virtual address (an address that points above KERNBASE), | |||
| * where the machine's maximum 256MB of physical memory is mapped and returns the | |||
| * corresponding physical address. It panics if you pass it a non-kernel virtual address. | |||
| * */ | |||
| #define PADDR(kva) ({ \ | |||
| uintptr_t __m_kva = (uintptr_t)(kva); \ | |||
| if (__m_kva < KERNBASE) { \ | |||
| panic("PADDR called with invalid kva %08lx", __m_kva); \ | |||
| } \ | |||
| __m_kva - KERNBASE; \ | |||
| }) | |||
| /* * | |||
| * KADDR - takes a physical address and returns the corresponding kernel virtual | |||
| * address. It panics if you pass an invalid physical address. | |||
| * */ | |||
| #define KADDR(pa) ({ \ | |||
| uintptr_t __m_pa = (pa); \ | |||
| size_t __m_ppn = PPN(__m_pa); \ | |||
| if (__m_ppn >= npage) { \ | |||
| panic("KADDR called with invalid pa %08lx", __m_pa); \ | |||
| } \ | |||
| (void *) (__m_pa + KERNBASE); \ | |||
| }) | |||
| extern struct Page *pages; | |||
| extern size_t npage; | |||
| static inline ppn_t | |||
| page2ppn(struct Page *page) { | |||
| return page - pages; | |||
| } | |||
| static inline uintptr_t | |||
| page2pa(struct Page *page) { | |||
| return page2ppn(page) << PGSHIFT; | |||
| } | |||
| static inline struct Page * | |||
| pa2page(uintptr_t pa) { | |||
| if (PPN(pa) >= npage) { | |||
| panic("pa2page called with invalid pa"); | |||
| } | |||
| return &pages[PPN(pa)]; | |||
| } | |||
| static inline void * | |||
| page2kva(struct Page *page) { | |||
| return KADDR(page2pa(page)); | |||
| } | |||
| static inline struct Page * | |||
| kva2page(void *kva) { | |||
| return pa2page(PADDR(kva)); | |||
| } | |||
| static inline struct Page * | |||
| pte2page(pte_t pte) { | |||
| if (!(pte & PTE_P)) { | |||
| panic("pte2page called with invalid pte"); | |||
| } | |||
| return pa2page(PTE_ADDR(pte)); | |||
| } | |||
| static inline struct Page * | |||
| pde2page(pde_t pde) { | |||
| return pa2page(PDE_ADDR(pde)); | |||
| } | |||
| static inline int | |||
| page_ref(struct Page *page) { | |||
| return page->ref; | |||
| } | |||
| static inline void | |||
| set_page_ref(struct Page *page, int val) { | |||
| page->ref = val; | |||
| } | |||
| static inline int | |||
| page_ref_inc(struct Page *page) { | |||
| page->ref += 1; | |||
| return page->ref; | |||
| } | |||
| static inline int | |||
| page_ref_dec(struct Page *page) { | |||
| page->ref -= 1; | |||
| return page->ref; | |||
| } | |||
| extern char bootstack[], bootstacktop[]; | |||
| #endif /* !__KERN_MM_PMM_H__ */ | |||
| @ -0,0 +1,284 @@ | |||
| #include <swap.h> | |||
| #include <swapfs.h> | |||
| #include <swap_fifo.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <memlayout.h> | |||
| #include <pmm.h> | |||
| #include <mmu.h> | |||
| #include <default_pmm.h> | |||
| #include <kdebug.h> | |||
| // the valid vaddr for check is between 0~CHECK_VALID_VADDR-1 | |||
| #define CHECK_VALID_VIR_PAGE_NUM 5 | |||
| #define BEING_CHECK_VALID_VADDR 0X1000 | |||
| #define CHECK_VALID_VADDR (CHECK_VALID_VIR_PAGE_NUM+1)*0x1000 | |||
| // the max number of valid physical page for check | |||
| #define CHECK_VALID_PHY_PAGE_NUM 4 | |||
| // the max access seq number | |||
| #define MAX_SEQ_NO 10 | |||
| static struct swap_manager *sm; | |||
| size_t max_swap_offset; | |||
| volatile int swap_init_ok = 0; | |||
| unsigned int swap_page[CHECK_VALID_VIR_PAGE_NUM]; | |||
| unsigned int swap_in_seq_no[MAX_SEQ_NO],swap_out_seq_no[MAX_SEQ_NO]; | |||
| static void check_swap(void); | |||
| int | |||
| swap_init(void) | |||
| { | |||
| swapfs_init(); | |||
| if (!(1024 <= max_swap_offset && max_swap_offset < MAX_SWAP_OFFSET_LIMIT)) | |||
| { | |||
| panic("bad max_swap_offset %08x.\n", max_swap_offset); | |||
| } | |||
| sm = &swap_manager_fifo; | |||
| int r = sm->init(); | |||
| if (r == 0) | |||
| { | |||
| swap_init_ok = 1; | |||
| cprintf("SWAP: manager = %s\n", sm->name); | |||
| check_swap(); | |||
| } | |||
| return r; | |||
| } | |||
| int | |||
| swap_init_mm(struct mm_struct *mm) | |||
| { | |||
| return sm->init_mm(mm); | |||
| } | |||
| int | |||
| swap_tick_event(struct mm_struct *mm) | |||
| { | |||
| return sm->tick_event(mm); | |||
| } | |||
| int | |||
| swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) | |||
| { | |||
| return sm->map_swappable(mm, addr, page, swap_in); | |||
| } | |||
| int | |||
| swap_set_unswappable(struct mm_struct *mm, uintptr_t addr) | |||
| { | |||
| return sm->set_unswappable(mm, addr); | |||
| } | |||
| volatile unsigned int swap_out_num=0; | |||
| int | |||
| swap_out(struct mm_struct *mm, int n, int in_tick) | |||
| { | |||
| int i; | |||
| for (i = 0; i != n; ++ i) | |||
| { | |||
| uintptr_t v; | |||
| //struct Page **ptr_page=NULL; | |||
| struct Page *page; | |||
| // cprintf("i %d, SWAP: call swap_out_victim\n",i); | |||
| int r = sm->swap_out_victim(mm, &page, in_tick); | |||
| if (r != 0) { | |||
| cprintf("i %d, swap_out: call swap_out_victim failed\n",i); | |||
| break; | |||
| } | |||
| //assert(!PageReserved(page)); | |||
| //cprintf("SWAP: choose victim page 0x%08x\n", page); | |||
| v=page->pra_vaddr; | |||
| pte_t *ptep = get_pte(mm->pgdir, v, 0); | |||
| assert((*ptep & PTE_P) != 0); | |||
| if (swapfs_write( (page->pra_vaddr/PGSIZE+1)<<8, page) != 0) { | |||
| cprintf("SWAP: failed to save\n"); | |||
| sm->map_swappable(mm, v, page, 0); | |||
| continue; | |||
| } | |||
| else { | |||
| cprintf("swap_out: i %d, store page in vaddr 0x%x to disk swap entry %d\n", i, v, page->pra_vaddr/PGSIZE+1); | |||
| *ptep = (page->pra_vaddr/PGSIZE+1)<<8; | |||
| free_page(page); | |||
| } | |||
| tlb_invalidate(mm->pgdir, v); | |||
| } | |||
| return i; | |||
| } | |||
| int | |||
| swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result) | |||
| { | |||
| struct Page *result = alloc_page(); | |||
| assert(result!=NULL); | |||
| pte_t *ptep = get_pte(mm->pgdir, addr, 0); | |||
| // cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages)); | |||
| int r; | |||
| if ((r = swapfs_read((*ptep), result)) != 0) | |||
| { | |||
| assert(r!=0); | |||
| } | |||
| cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr); | |||
| *ptr_result=result; | |||
| return 0; | |||
| } | |||
| static inline void | |||
| check_content_set(void) | |||
| { | |||
| *(unsigned char *)0x1000 = 0x0a; | |||
| assert(pgfault_num==1); | |||
| *(unsigned char *)0x1010 = 0x0a; | |||
| assert(pgfault_num==1); | |||
| *(unsigned char *)0x2000 = 0x0b; | |||
| assert(pgfault_num==2); | |||
| *(unsigned char *)0x2010 = 0x0b; | |||
| assert(pgfault_num==2); | |||
| *(unsigned char *)0x3000 = 0x0c; | |||
| assert(pgfault_num==3); | |||
| *(unsigned char *)0x3010 = 0x0c; | |||
| assert(pgfault_num==3); | |||
| *(unsigned char *)0x4000 = 0x0d; | |||
| assert(pgfault_num==4); | |||
| *(unsigned char *)0x4010 = 0x0d; | |||
| assert(pgfault_num==4); | |||
| } | |||
| static inline int | |||
| check_content_access(void) | |||
| { | |||
| int ret = sm->check_swap(); | |||
| return ret; | |||
| } | |||
| struct Page * check_rp[CHECK_VALID_PHY_PAGE_NUM]; | |||
| pte_t * check_ptep[CHECK_VALID_PHY_PAGE_NUM]; | |||
| unsigned int check_swap_addr[CHECK_VALID_VIR_PAGE_NUM]; | |||
| extern free_area_t free_area; | |||
| #define free_list (free_area.free_list) | |||
| #define nr_free (free_area.nr_free) | |||
| static void | |||
| check_swap(void) | |||
| { | |||
| //backup mem env | |||
| int ret, count = 0, total = 0, i; | |||
| list_entry_t *le = &free_list; | |||
| while ((le = list_next(le)) != &free_list) { | |||
| struct Page *p = le2page(le, page_link); | |||
| assert(PageProperty(p)); | |||
| count ++, total += p->property; | |||
| } | |||
| assert(total == nr_free_pages()); | |||
| cprintf("BEGIN check_swap: count %d, total %d\n",count,total); | |||
| //now we set the phy pages env | |||
| struct mm_struct *mm = mm_create(); | |||
| assert(mm != NULL); | |||
| extern struct mm_struct *check_mm_struct; | |||
| assert(check_mm_struct == NULL); | |||
| check_mm_struct = mm; | |||
| pde_t *pgdir = mm->pgdir = boot_pgdir; | |||
| assert(pgdir[0] == 0); | |||
| struct vma_struct *vma = vma_create(BEING_CHECK_VALID_VADDR, CHECK_VALID_VADDR, VM_WRITE | VM_READ); | |||
| assert(vma != NULL); | |||
| insert_vma_struct(mm, vma); | |||
| //setup the temp Page Table vaddr 0~4MB | |||
| cprintf("setup Page Table for vaddr 0X1000, so alloc a page\n"); | |||
| pte_t *temp_ptep=NULL; | |||
| temp_ptep = get_pte(mm->pgdir, BEING_CHECK_VALID_VADDR, 1); | |||
| assert(temp_ptep!= NULL); | |||
| cprintf("setup Page Table vaddr 0~4MB OVER!\n"); | |||
| for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) { | |||
| check_rp[i] = alloc_page(); | |||
| assert(check_rp[i] != NULL ); | |||
| assert(!PageProperty(check_rp[i])); | |||
| } | |||
| list_entry_t free_list_store = free_list; | |||
| list_init(&free_list); | |||
| assert(list_empty(&free_list)); | |||
| //assert(alloc_page() == NULL); | |||
| unsigned int nr_free_store = nr_free; | |||
| nr_free = 0; | |||
| for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) { | |||
| free_pages(check_rp[i],1); | |||
| } | |||
| assert(nr_free==CHECK_VALID_PHY_PAGE_NUM); | |||
| cprintf("set up init env for check_swap begin!\n"); | |||
| //setup initial vir_page<->phy_page environment for page relpacement algorithm | |||
| pgfault_num=0; | |||
| check_content_set(); | |||
| assert( nr_free == 0); | |||
| for(i = 0; i<MAX_SEQ_NO ; i++) | |||
| swap_out_seq_no[i]=swap_in_seq_no[i]=-1; | |||
| for (i= 0;i<CHECK_VALID_PHY_PAGE_NUM;i++) { | |||
| check_ptep[i]=0; | |||
| check_ptep[i] = get_pte(pgdir, (i+1)*0x1000, 0); | |||
| //cprintf("i %d, check_ptep addr %x, value %x\n", i, check_ptep[i], *check_ptep[i]); | |||
| assert(check_ptep[i] != NULL); | |||
| assert(pte2page(*check_ptep[i]) == check_rp[i]); | |||
| assert((*check_ptep[i] & PTE_P)); | |||
| } | |||
| cprintf("set up init env for check_swap over!\n"); | |||
| // now access the virt pages to test page relpacement algorithm | |||
| ret=check_content_access(); | |||
| assert(ret==0); | |||
| //restore kernel mem env | |||
| for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) { | |||
| free_pages(check_rp[i],1); | |||
| } | |||
| //free_page(pte2page(*temp_ptep)); | |||
| free_page(pa2page(pgdir[0])); | |||
| pgdir[0] = 0; | |||
| mm->pgdir = NULL; | |||
| mm_destroy(mm); | |||
| check_mm_struct = NULL; | |||
| nr_free = nr_free_store; | |||
| free_list = free_list_store; | |||
| le = &free_list; | |||
| while ((le = list_next(le)) != &free_list) { | |||
| struct Page *p = le2page(le, page_link); | |||
| count --, total -= p->property; | |||
| } | |||
| cprintf("count is %d, total is %d\n",count,total); | |||
| //assert(count == 0); | |||
| cprintf("check_swap() succeeded!\n"); | |||
| } | |||
| @ -0,0 +1,65 @@ | |||
| #ifndef __KERN_MM_SWAP_H__ | |||
| #define __KERN_MM_SWAP_H__ | |||
| #include <defs.h> | |||
| #include <memlayout.h> | |||
| #include <pmm.h> | |||
| #include <vmm.h> | |||
| /* * | |||
| * swap_entry_t | |||
| * -------------------------------------------- | |||
| * | offset | reserved | 0 | | |||
| * -------------------------------------------- | |||
| * 24 bits 7 bits 1 bit | |||
| * */ | |||
| #define MAX_SWAP_OFFSET_LIMIT (1 << 24) | |||
| extern size_t max_swap_offset; | |||
| /* * | |||
| * swap_offset - takes a swap_entry (saved in pte), and returns | |||
| * the corresponding offset in swap mem_map. | |||
| * */ | |||
| #define swap_offset(entry) ({ \ | |||
| size_t __offset = (entry >> 8); \ | |||
| if (!(__offset > 0 && __offset < max_swap_offset)) { \ | |||
| panic("invalid swap_entry_t = %08x.\n", entry); \ | |||
| } \ | |||
| __offset; \ | |||
| }) | |||
| struct swap_manager | |||
| { | |||
| const char *name; | |||
| /* Global initialization for the swap manager */ | |||
| int (*init) (void); | |||
| /* Initialize the priv data inside mm_struct */ | |||
| int (*init_mm) (struct mm_struct *mm); | |||
| /* Called when tick interrupt occured */ | |||
| int (*tick_event) (struct mm_struct *mm); | |||
| /* Called when map a swappable page into the mm_struct */ | |||
| int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in); | |||
| /* When a page is marked as shared, this routine is called to | |||
| * delete the addr entry from the swap manager */ | |||
| int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr); | |||
| /* Try to swap out a page, return then victim */ | |||
| int (*swap_out_victim) (struct mm_struct *mm, struct Page **ptr_page, int in_tick); | |||
| /* check the page relpacement algorithm */ | |||
| int (*check_swap)(void); | |||
| }; | |||
| extern volatile int swap_init_ok; | |||
| int swap_init(void); | |||
| int swap_init_mm(struct mm_struct *mm); | |||
| int swap_tick_event(struct mm_struct *mm); | |||
| int swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in); | |||
| int swap_set_unswappable(struct mm_struct *mm, uintptr_t addr); | |||
| int swap_out(struct mm_struct *mm, int n, int in_tick); | |||
| int swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result); | |||
| //#define MEMBER_OFFSET(m,t) ((int)(&((t *)0)->m)) | |||
| //#define FROM_MEMBER(m,t,a) ((t *)((char *)(a) - MEMBER_OFFSET(m,t))) | |||
| #endif | |||
| @ -0,0 +1,144 @@ | |||
| #include <defs.h> | |||
| #include <x86.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <swap.h> | |||
| #include <swap_fifo.h> | |||
| #include <list.h> | |||
| /* [wikipedia]The simplest Page Replacement Algorithm(PRA) is a FIFO algorithm. The first-in, first-out | |||
| * page replacement algorithm is a low-overhead algorithm that requires little book-keeping on | |||
| * the part of the operating system. The idea is obvious from the name - the operating system | |||
| * keeps track of all the pages in memory in a queue, with the most recent arrival at the back, | |||
| * and the earliest arrival in front. When a page needs to be replaced, the page at the front | |||
| * of the queue (the oldest page) is selected. While FIFO is cheap and intuitive, it performs | |||
| * poorly in practical application. Thus, it is rarely used in its unmodified form. This | |||
| * algorithm experiences Belady's anomaly. | |||
| * | |||
| * Details of FIFO PRA | |||
| * (1) Prepare: In order to implement FIFO PRA, we should manage all swappable pages, so we can | |||
| * link these pages into pra_list_head according the time order. At first you should | |||
| * be familiar to the struct list in list.h. struct list is a simple doubly linked list | |||
| * implementation. You should know howto USE: list_init, list_add(list_add_after), | |||
| * list_add_before, list_del, list_next, list_prev. Another tricky method is to transform | |||
| * a general list struct to a special struct (such as struct page). You can find some MACRO: | |||
| * le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc. | |||
| */ | |||
| list_entry_t pra_list_head; | |||
| /* | |||
| * (2) _fifo_init_mm: init pra_list_head and let mm->sm_priv point to the addr of pra_list_head. | |||
| * Now, From the memory control struct mm_struct, we can access FIFO PRA | |||
| */ | |||
| static int | |||
| _fifo_init_mm(struct mm_struct *mm) | |||
| { | |||
| list_init(&pra_list_head); | |||
| mm->sm_priv = &pra_list_head; | |||
| //cprintf(" mm->sm_priv %x in fifo_init_mm\n",mm->sm_priv); | |||
| return 0; | |||
| } | |||
| /* | |||
| * (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue | |||
| */ | |||
| static int | |||
| _fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) | |||
| { | |||
| list_entry_t *head=(list_entry_t*) mm->sm_priv; | |||
| list_entry_t *entry=&(page->pra_page_link); | |||
| assert(entry != NULL && head != NULL); | |||
| //record the page access situlation | |||
| /*LAB3 EXERCISE 2: YOUR CODE*/ | |||
| //(1)link the most recent arrival page at the back of the pra_list_head qeueue. | |||
| list_add(head, entry); | |||
| return 0; | |||
| } | |||
| /* | |||
| * (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue, | |||
| * then set the addr of addr of this page to ptr_page. | |||
| */ | |||
| static int | |||
| _fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick) | |||
| { | |||
| list_entry_t *head=(list_entry_t*) mm->sm_priv; | |||
| assert(head != NULL); | |||
| assert(in_tick==0); | |||
| /* Select the victim */ | |||
| /*LAB3 EXERCISE 2: YOUR CODE*/ | |||
| //(1) unlink the earliest arrival page in front of pra_list_head qeueue | |||
| //(2) set the addr of addr of this page to ptr_page | |||
| /* Select the tail */ | |||
| list_entry_t *le = head->prev; | |||
| assert(head!=le); | |||
| struct Page *p = le2page(le, pra_page_link); | |||
| list_del(le); | |||
| assert(p !=NULL); | |||
| *ptr_page = p; | |||
| return 0; | |||
| } | |||
| static int | |||
| _fifo_check_swap(void) { | |||
| cprintf("write Virt Page c in fifo_check_swap\n"); | |||
| *(unsigned char *)0x3000 = 0x0c; | |||
| assert(pgfault_num==4); | |||
| cprintf("write Virt Page a in fifo_check_swap\n"); | |||
| *(unsigned char *)0x1000 = 0x0a; | |||
| assert(pgfault_num==4); | |||
| cprintf("write Virt Page d in fifo_check_swap\n"); | |||
| *(unsigned char *)0x4000 = 0x0d; | |||
| assert(pgfault_num==4); | |||
| cprintf("write Virt Page b in fifo_check_swap\n"); | |||
| *(unsigned char *)0x2000 = 0x0b; | |||
| assert(pgfault_num==4); | |||
| cprintf("write Virt Page e in fifo_check_swap\n"); | |||
| *(unsigned char *)0x5000 = 0x0e; | |||
| assert(pgfault_num==5); | |||
| cprintf("write Virt Page b in fifo_check_swap\n"); | |||
| *(unsigned char *)0x2000 = 0x0b; | |||
| assert(pgfault_num==5); | |||
| cprintf("write Virt Page a in fifo_check_swap\n"); | |||
| *(unsigned char *)0x1000 = 0x0a; | |||
| assert(pgfault_num==6); | |||
| cprintf("write Virt Page b in fifo_check_swap\n"); | |||
| *(unsigned char *)0x2000 = 0x0b; | |||
| assert(pgfault_num==7); | |||
| cprintf("write Virt Page c in fifo_check_swap\n"); | |||
| *(unsigned char *)0x3000 = 0x0c; | |||
| assert(pgfault_num==8); | |||
| cprintf("write Virt Page d in fifo_check_swap\n"); | |||
| *(unsigned char *)0x4000 = 0x0d; | |||
| assert(pgfault_num==9); | |||
| return 0; | |||
| } | |||
| static int | |||
| _fifo_init(void) | |||
| { | |||
| return 0; | |||
| } | |||
| static int | |||
| _fifo_set_unswappable(struct mm_struct *mm, uintptr_t addr) | |||
| { | |||
| return 0; | |||
| } | |||
| static int | |||
| _fifo_tick_event(struct mm_struct *mm) | |||
| { return 0; } | |||
| struct swap_manager swap_manager_fifo = | |||
| { | |||
| .name = "fifo swap manager", | |||
| .init = &_fifo_init, | |||
| .init_mm = &_fifo_init_mm, | |||
| .tick_event = &_fifo_tick_event, | |||
| .map_swappable = &_fifo_map_swappable, | |||
| .set_unswappable = &_fifo_set_unswappable, | |||
| .swap_out_victim = &_fifo_swap_out_victim, | |||
| .check_swap = &_fifo_check_swap, | |||
| }; | |||
| @ -0,0 +1,7 @@ | |||
| #ifndef __KERN_MM_SWAP_FIFO_H__ | |||
| #define __KERN_MM_SWAP_FIFO_H__ | |||
| #include <swap.h> | |||
| extern struct swap_manager swap_manager_fifo; | |||
| #endif | |||
| @ -0,0 +1,562 @@ | |||
| #include <vmm.h> | |||
| #include <sync.h> | |||
| #include <string.h> | |||
| #include <assert.h> | |||
| #include <stdio.h> | |||
| #include <error.h> | |||
| #include <pmm.h> | |||
| #include <x86.h> | |||
| #include <swap.h> | |||
| #include <kmalloc.h> | |||
| /* | |||
| vmm design include two parts: mm_struct (mm) & vma_struct (vma) | |||
| mm is the memory manager for the set of continuous virtual memory | |||
| area which have the same PDT. vma is a continuous virtual memory area. | |||
| There a linear link list for vma & a redblack link list for vma in mm. | |||
| --------------- | |||
| mm related functions: | |||
| golbal functions | |||
| struct mm_struct * mm_create(void) | |||
| void mm_destroy(struct mm_struct *mm) | |||
| int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) | |||
| -------------- | |||
| vma related functions: | |||
| global functions | |||
| struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...) | |||
| void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) | |||
| struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr) | |||
| local functions | |||
| inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) | |||
| --------------- | |||
| check correctness functions | |||
| void check_vmm(void); | |||
| void check_vma_struct(void); | |||
| void check_pgfault(void); | |||
| */ | |||
| static void check_vmm(void); | |||
| static void check_vma_struct(void); | |||
| static void check_pgfault(void); | |||
| // mm_create - alloc a mm_struct & initialize it. | |||
| struct mm_struct * | |||
| mm_create(void) { | |||
| struct mm_struct *mm = kmalloc(sizeof(struct mm_struct)); | |||
| if (mm != NULL) { | |||
| list_init(&(mm->mmap_list)); | |||
| mm->mmap_cache = NULL; | |||
| mm->pgdir = NULL; | |||
| mm->map_count = 0; | |||
| if (swap_init_ok) swap_init_mm(mm); | |||
| else mm->sm_priv = NULL; | |||
| set_mm_count(mm, 0); | |||
| lock_init(&(mm->mm_lock)); | |||
| } | |||
| return mm; | |||
| } | |||
| // vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end) | |||
| struct vma_struct * | |||
| vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) { | |||
| struct vma_struct *vma = kmalloc(sizeof(struct vma_struct)); | |||
| if (vma != NULL) { | |||
| vma->vm_start = vm_start; | |||
| vma->vm_end = vm_end; | |||
| vma->vm_flags = vm_flags; | |||
| } | |||
| return vma; | |||
| } | |||
| // find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end) | |||
| struct vma_struct * | |||
| find_vma(struct mm_struct *mm, uintptr_t addr) { | |||
| struct vma_struct *vma = NULL; | |||
| if (mm != NULL) { | |||
| vma = mm->mmap_cache; | |||
| if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) { | |||
| bool found = 0; | |||
| list_entry_t *list = &(mm->mmap_list), *le = list; | |||
| while ((le = list_next(le)) != list) { | |||
| vma = le2vma(le, list_link); | |||
| if (vma->vm_start<=addr && addr < vma->vm_end) { | |||
| found = 1; | |||
| break; | |||
| } | |||
| } | |||
| if (!found) { | |||
| vma = NULL; | |||
| } | |||
| } | |||
| if (vma != NULL) { | |||
| mm->mmap_cache = vma; | |||
| } | |||
| } | |||
| return vma; | |||
| } | |||
| // check_vma_overlap - check if vma1 overlaps vma2 ? | |||
| static inline void | |||
| check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) { | |||
| assert(prev->vm_start < prev->vm_end); | |||
| assert(prev->vm_end <= next->vm_start); | |||
| assert(next->vm_start < next->vm_end); | |||
| } | |||
| // insert_vma_struct -insert vma in mm's list link | |||
| void | |||
| insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) { | |||
| assert(vma->vm_start < vma->vm_end); | |||
| list_entry_t *list = &(mm->mmap_list); | |||
| list_entry_t *le_prev = list, *le_next; | |||
| list_entry_t *le = list; | |||
| while ((le = list_next(le)) != list) { | |||
| struct vma_struct *mmap_prev = le2vma(le, list_link); | |||
| if (mmap_prev->vm_start > vma->vm_start) { | |||
| break; | |||
| } | |||
| le_prev = le; | |||
| } | |||
| le_next = list_next(le_prev); | |||
| /* check overlap */ | |||
| if (le_prev != list) { | |||
| check_vma_overlap(le2vma(le_prev, list_link), vma); | |||
| } | |||
| if (le_next != list) { | |||
| check_vma_overlap(vma, le2vma(le_next, list_link)); | |||
| } | |||
| vma->vm_mm = mm; | |||
| list_add_after(le_prev, &(vma->list_link)); | |||
| mm->map_count ++; | |||
| } | |||
| // mm_destroy - free mm and mm internal fields | |||
| void | |||
| mm_destroy(struct mm_struct *mm) { | |||
| assert(mm_count(mm) == 0); | |||
| list_entry_t *list = &(mm->mmap_list), *le; | |||
| while ((le = list_next(list)) != list) { | |||
| list_del(le); | |||
| kfree(le2vma(le, list_link)); //kfree vma | |||
| } | |||
| kfree(mm); //kfree mm | |||
| mm=NULL; | |||
| } | |||
| int | |||
| mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags, | |||
| struct vma_struct **vma_store) { | |||
| uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE); | |||
| if (!USER_ACCESS(start, end)) { | |||
| return -E_INVAL; | |||
| } | |||
| assert(mm != NULL); | |||
| int ret = -E_INVAL; | |||
| struct vma_struct *vma; | |||
| if ((vma = find_vma(mm, start)) != NULL && end > vma->vm_start) { | |||
| goto out; | |||
| } | |||
| ret = -E_NO_MEM; | |||
| if ((vma = vma_create(start, end, vm_flags)) == NULL) { | |||
| goto out; | |||
| } | |||
| insert_vma_struct(mm, vma); | |||
| if (vma_store != NULL) { | |||
| *vma_store = vma; | |||
| } | |||
| ret = 0; | |||
| out: | |||
| return ret; | |||
| } | |||
| int | |||
| dup_mmap(struct mm_struct *to, struct mm_struct *from) { | |||
| assert(to != NULL && from != NULL); | |||
| list_entry_t *list = &(from->mmap_list), *le = list; | |||
| while ((le = list_prev(le)) != list) { | |||
| struct vma_struct *vma, *nvma; | |||
| vma = le2vma(le, list_link); | |||
| nvma = vma_create(vma->vm_start, vma->vm_end, vma->vm_flags); | |||
| if (nvma == NULL) { | |||
| return -E_NO_MEM; | |||
| } | |||
| insert_vma_struct(to, nvma); | |||
| bool share = 0; | |||
| if (copy_range(to->pgdir, from->pgdir, vma->vm_start, vma->vm_end, share) != 0) { | |||
| return -E_NO_MEM; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| void | |||
| exit_mmap(struct mm_struct *mm) { | |||
| assert(mm != NULL && mm_count(mm) == 0); | |||
| pde_t *pgdir = mm->pgdir; | |||
| list_entry_t *list = &(mm->mmap_list), *le = list; | |||
| while ((le = list_next(le)) != list) { | |||
| struct vma_struct *vma = le2vma(le, list_link); | |||
| unmap_range(pgdir, vma->vm_start, vma->vm_end); | |||
| } | |||
| while ((le = list_next(le)) != list) { | |||
| struct vma_struct *vma = le2vma(le, list_link); | |||
| exit_range(pgdir, vma->vm_start, vma->vm_end); | |||
| } | |||
| } | |||
| bool | |||
| copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable) { | |||
| if (!user_mem_check(mm, (uintptr_t)src, len, writable)) { | |||
| return 0; | |||
| } | |||
| memcpy(dst, src, len); | |||
| return 1; | |||
| } | |||
| bool | |||
| copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len) { | |||
| if (!user_mem_check(mm, (uintptr_t)dst, len, 1)) { | |||
| return 0; | |||
| } | |||
| memcpy(dst, src, len); | |||
| return 1; | |||
| } | |||
| // vmm_init - initialize virtual memory management | |||
| // - now just call check_vmm to check correctness of vmm | |||
| void | |||
| vmm_init(void) { | |||
| check_vmm(); | |||
| } | |||
| // check_vmm - check correctness of vmm | |||
| static void | |||
| check_vmm(void) { | |||
| size_t nr_free_pages_store = nr_free_pages(); | |||
| check_vma_struct(); | |||
| check_pgfault(); | |||
| // assert(nr_free_pages_store == nr_free_pages()); | |||
| cprintf("check_vmm() succeeded.\n"); | |||
| } | |||
| static void | |||
| check_vma_struct(void) { | |||
| size_t nr_free_pages_store = nr_free_pages(); | |||
| struct mm_struct *mm = mm_create(); | |||
| assert(mm != NULL); | |||
| int step1 = 10, step2 = step1 * 10; | |||
| int i; | |||
| for (i = step1; i >= 1; i --) { | |||
| struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); | |||
| assert(vma != NULL); | |||
| insert_vma_struct(mm, vma); | |||
| } | |||
| for (i = step1 + 1; i <= step2; i ++) { | |||
| struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0); | |||
| assert(vma != NULL); | |||
| insert_vma_struct(mm, vma); | |||
| } | |||
| list_entry_t *le = list_next(&(mm->mmap_list)); | |||
| for (i = 1; i <= step2; i ++) { | |||
| assert(le != &(mm->mmap_list)); | |||
| struct vma_struct *mmap = le2vma(le, list_link); | |||
| assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2); | |||
| le = list_next(le); | |||
| } | |||
| for (i = 5; i <= 5 * step2; i +=5) { | |||
| struct vma_struct *vma1 = find_vma(mm, i); | |||
| assert(vma1 != NULL); | |||
| struct vma_struct *vma2 = find_vma(mm, i+1); | |||
| assert(vma2 != NULL); | |||
| struct vma_struct *vma3 = find_vma(mm, i+2); | |||
| assert(vma3 == NULL); | |||
| struct vma_struct *vma4 = find_vma(mm, i+3); | |||
| assert(vma4 == NULL); | |||
| struct vma_struct *vma5 = find_vma(mm, i+4); | |||
| assert(vma5 == NULL); | |||
| assert(vma1->vm_start == i && vma1->vm_end == i + 2); | |||
| assert(vma2->vm_start == i && vma2->vm_end == i + 2); | |||
| } | |||
| for (i =4; i>=0; i--) { | |||
| struct vma_struct *vma_below_5= find_vma(mm,i); | |||
| if (vma_below_5 != NULL ) { | |||
| cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end); | |||
| } | |||
| assert(vma_below_5 == NULL); | |||
| } | |||
| mm_destroy(mm); | |||
| // assert(nr_free_pages_store == nr_free_pages()); | |||
| cprintf("check_vma_struct() succeeded!\n"); | |||
| } | |||
| struct mm_struct *check_mm_struct; | |||
| // check_pgfault - check correctness of pgfault handler | |||
| static void | |||
| check_pgfault(void) { | |||
| size_t nr_free_pages_store = nr_free_pages(); | |||
| check_mm_struct = mm_create(); | |||
| assert(check_mm_struct != NULL); | |||
| struct mm_struct *mm = check_mm_struct; | |||
| pde_t *pgdir = mm->pgdir = boot_pgdir; | |||
| assert(pgdir[0] == 0); | |||
| struct vma_struct *vma = vma_create(0, PTSIZE, VM_WRITE); | |||
| assert(vma != NULL); | |||
| insert_vma_struct(mm, vma); | |||
| uintptr_t addr = 0x100; | |||
| assert(find_vma(mm, addr) == vma); | |||
| int i, sum = 0; | |||
| for (i = 0; i < 100; i ++) { | |||
| *(char *)(addr + i) = i; | |||
| sum += i; | |||
| } | |||
| for (i = 0; i < 100; i ++) { | |||
| sum -= *(char *)(addr + i); | |||
| } | |||
| assert(sum == 0); | |||
| page_remove(pgdir, ROUNDDOWN(addr, PGSIZE)); | |||
| free_page(pa2page(pgdir[0])); | |||
| pgdir[0] = 0; | |||
| mm->pgdir = NULL; | |||
| mm_destroy(mm); | |||
| check_mm_struct = NULL; | |||
| assert(nr_free_pages_store == nr_free_pages()); | |||
| cprintf("check_pgfault() succeeded!\n"); | |||
| } | |||
| //page fault number | |||
| volatile unsigned int pgfault_num=0; | |||
| /* do_pgfault - interrupt handler to process the page fault execption | |||
| * @mm : the control struct for a set of vma using the same PDT | |||
| * @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware | |||
| * @addr : the addr which causes a memory access exception, (the contents of the CR2 register) | |||
| * | |||
| * CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault | |||
| * The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing | |||
| * the exception and recovering from it. | |||
| * (1) The contents of the CR2 register. The processor loads the CR2 register with the | |||
| * 32-bit linear address that generated the exception. The do_pgfault fun can | |||
| * use this address to locate the corresponding page directory and page-table | |||
| * entries. | |||
| * (2) An error code on the kernel stack. The error code for a page fault has a format different from | |||
| * that for other exceptions. The error code tells the exception handler three things: | |||
| * -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0) | |||
| * or to either an access rights violation or the use of a reserved bit (1). | |||
| * -- The W/R flag (bit 1) indicates whether the memory access that caused the exception | |||
| * was a read (0) or write (1). | |||
| * -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1) | |||
| * or supervisor mode (0) at the time of the exception. | |||
| */ | |||
| int | |||
| do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) { | |||
| int ret = -E_INVAL; | |||
| //try to find a vma which include addr | |||
| struct vma_struct *vma = find_vma(mm, addr); | |||
| pgfault_num++; | |||
| //If the addr is in the range of a mm's vma? | |||
| if (vma == NULL || vma->vm_start > addr) { | |||
| cprintf("not valid addr %x, and can not find it in vma\n", addr); | |||
| goto failed; | |||
| } | |||
| //check the error_code | |||
| switch (error_code & 3) { | |||
| default: | |||
| /* error code flag : default is 3 ( W/R=1, P=1): write, present */ | |||
| case 2: /* error code flag : (W/R=1, P=0): write, not present */ | |||
| if (!(vma->vm_flags & VM_WRITE)) { | |||
| cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n"); | |||
| goto failed; | |||
| } | |||
| break; | |||
| case 1: /* error code flag : (W/R=0, P=1): read, present */ | |||
| cprintf("do_pgfault failed: error code flag = read AND present\n"); | |||
| goto failed; | |||
| case 0: /* error code flag : (W/R=0, P=0): read, not present */ | |||
| if (!(vma->vm_flags & (VM_READ | VM_EXEC))) { | |||
| cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n"); | |||
| goto failed; | |||
| } | |||
| } | |||
| /* IF (write an existed addr ) OR | |||
| * (write an non_existed addr && addr is writable) OR | |||
| * (read an non_existed addr && addr is readable) | |||
| * THEN | |||
| * continue process | |||
| */ | |||
| uint32_t perm = PTE_U; | |||
| if (vma->vm_flags & VM_WRITE) { | |||
| perm |= PTE_W; | |||
| } | |||
| addr = ROUNDDOWN(addr, PGSIZE); | |||
| ret = -E_NO_MEM; | |||
| pte_t *ptep=NULL; | |||
| /*LAB3 EXERCISE 1: YOUR CODE | |||
| * Maybe you want help comment, BELOW comments can help you finish the code | |||
| * | |||
| * Some Useful MACROs and DEFINEs, you can use them in below implementation. | |||
| * MACROs or Functions: | |||
| * get_pte : get an pte and return the kernel virtual address of this pte for la | |||
| * if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1') | |||
| * pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup | |||
| * an addr map pa<--->la with linear address la and the PDT pgdir | |||
| * DEFINES: | |||
| * VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable | |||
| * PTE_W 0x002 // page table/directory entry flags bit : Writeable | |||
| * PTE_U 0x004 // page table/directory entry flags bit : User can access | |||
| * VARIABLES: | |||
| * mm->pgdir : the PDT of these vma | |||
| * | |||
| */ | |||
| #if 0 | |||
| /*LAB3 EXERCISE 1: YOUR CODE*/ | |||
| ptep = ??? //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT. | |||
| if (*ptep == 0) { | |||
| //(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr | |||
| } | |||
| else { | |||
| /*LAB3 EXERCISE 2: YOUR CODE | |||
| * Now we think this pte is a swap entry, we should load data from disk to a page with phy addr, | |||
| * and map the phy addr with logical addr, trigger swap manager to record the access situation of this page. | |||
| * | |||
| * Some Useful MACROs and DEFINEs, you can use them in below implementation. | |||
| * MACROs or Functions: | |||
| * swap_in(mm, addr, &page) : alloc a memory page, then according to the swap entry in PTE for addr, | |||
| * find the addr of disk page, read the content of disk page into this memroy page | |||
| * page_insert : build the map of phy addr of an Page with the linear addr la | |||
| * swap_map_swappable : set the page swappable | |||
| */ | |||
| if(swap_init_ok) { | |||
| struct Page *page=NULL; | |||
| //(1)According to the mm AND addr, try to load the content of right disk page | |||
| // into the memory which page managed. | |||
| //(2) According to the mm, addr AND page, setup the map of phy addr <---> logical addr | |||
| //(3) make the page swappable. | |||
| //(4) [NOTICE]: you myabe need to update your lab3's implementation for LAB5's normal execution. | |||
| } | |||
| else { | |||
| cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep); | |||
| goto failed; | |||
| } | |||
| } | |||
| #endif | |||
| // try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT. | |||
| // (notice the 3th parameter '1') | |||
| if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) { | |||
| cprintf("get_pte in do_pgfault failed\n"); | |||
| goto failed; | |||
| } | |||
| if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr | |||
| if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { | |||
| cprintf("pgdir_alloc_page in do_pgfault failed\n"); | |||
| goto failed; | |||
| } | |||
| } | |||
| else { | |||
| struct Page *page=NULL; | |||
| cprintf("do pgfault: ptep %x, pte %x\n",ptep, *ptep); | |||
| if (*ptep & PTE_P) { | |||
| //if process write to this existed readonly page (PTE_P means existed), then should be here now. | |||
| //we can implement the delayed memory space copy for fork child process (AKA copy on write, COW). | |||
| //we didn't implement now, we will do it in future. | |||
| panic("error write a non-writable pte"); | |||
| //page = pte2page(*ptep); | |||
| } else{ | |||
| // if this pte is a swap entry, then load data from disk to a page with phy addr | |||
| // and call page_insert to map the phy addr with logical addr | |||
| if(swap_init_ok) { | |||
| if ((ret = swap_in(mm, addr, &page)) != 0) { | |||
| cprintf("swap_in in do_pgfault failed\n"); | |||
| goto failed; | |||
| } | |||
| } | |||
| else { | |||
| cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep); | |||
| goto failed; | |||
| } | |||
| } | |||
| page_insert(mm->pgdir, page, addr, perm); | |||
| swap_map_swappable(mm, addr, page, 1); | |||
| } | |||
| ret = 0; | |||
| failed: | |||
| return ret; | |||
| } | |||
| bool | |||
| user_mem_check(struct mm_struct *mm, uintptr_t addr, size_t len, bool write) { | |||
| if (mm != NULL) { | |||
| if (!USER_ACCESS(addr, addr + len)) { | |||
| return 0; | |||
| } | |||
| struct vma_struct *vma; | |||
| uintptr_t start = addr, end = addr + len; | |||
| while (start < end) { | |||
| if ((vma = find_vma(mm, start)) == NULL || start < vma->vm_start) { | |||
| return 0; | |||
| } | |||
| if (!(vma->vm_flags & ((write) ? VM_WRITE : VM_READ))) { | |||
| return 0; | |||
| } | |||
| if (write && (vma->vm_flags & VM_STACK)) { | |||
| if (start < vma->vm_start + PGSIZE) { //check stack start & size | |||
| return 0; | |||
| } | |||
| } | |||
| start = vma->vm_end; | |||
| } | |||
| return 1; | |||
| } | |||
| return KERN_ACCESS(addr, addr + len); | |||
| } | |||
| @ -0,0 +1,102 @@ | |||
| #ifndef __KERN_MM_VMM_H__ | |||
| #define __KERN_MM_VMM_H__ | |||
| #include <defs.h> | |||
| #include <list.h> | |||
| #include <memlayout.h> | |||
| #include <sync.h> | |||
| //pre define | |||
| struct mm_struct; | |||
| // the virtual continuous memory area(vma) | |||
| struct vma_struct { | |||
| struct mm_struct *vm_mm; // the set of vma using the same PDT | |||
| uintptr_t vm_start; // start addr of vma | |||
| uintptr_t vm_end; // end addr of vma | |||
| uint32_t vm_flags; // flags of vma | |||
| list_entry_t list_link; // linear list link which sorted by start addr of vma | |||
| }; | |||
| #define le2vma(le, member) \ | |||
| to_struct((le), struct vma_struct, member) | |||
| #define VM_READ 0x00000001 | |||
| #define VM_WRITE 0x00000002 | |||
| #define VM_EXEC 0x00000004 | |||
| #define VM_STACK 0x00000008 | |||
| // the control struct for a set of vma using the same PDT | |||
| struct mm_struct { | |||
| list_entry_t mmap_list; // linear list link which sorted by start addr of vma | |||
| struct vma_struct *mmap_cache; // current accessed vma, used for speed purpose | |||
| pde_t *pgdir; // the PDT of these vma | |||
| int map_count; // the count of these vma | |||
| void *sm_priv; // the private data for swap manager | |||
| int mm_count; // the number ofprocess which shared the mm | |||
| lock_t mm_lock; // mutex for using dup_mmap fun to duplicat the mm | |||
| }; | |||
| struct vma_struct *find_vma(struct mm_struct *mm, uintptr_t addr); | |||
| struct vma_struct *vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags); | |||
| void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma); | |||
| struct mm_struct *mm_create(void); | |||
| void mm_destroy(struct mm_struct *mm); | |||
| void vmm_init(void); | |||
| int mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags, | |||
| struct vma_struct **vma_store); | |||
| int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr); | |||
| int mm_unmap(struct mm_struct *mm, uintptr_t addr, size_t len); | |||
| int dup_mmap(struct mm_struct *to, struct mm_struct *from); | |||
| void exit_mmap(struct mm_struct *mm); | |||
| uintptr_t get_unmapped_area(struct mm_struct *mm, size_t len); | |||
| int mm_brk(struct mm_struct *mm, uintptr_t addr, size_t len); | |||
| extern volatile unsigned int pgfault_num; | |||
| extern struct mm_struct *check_mm_struct; | |||
| bool user_mem_check(struct mm_struct *mm, uintptr_t start, size_t len, bool write); | |||
| bool copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable); | |||
| bool copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len); | |||
| static inline int | |||
| mm_count(struct mm_struct *mm) { | |||
| return mm->mm_count; | |||
| } | |||
| static inline void | |||
| set_mm_count(struct mm_struct *mm, int val) { | |||
| mm->mm_count = val; | |||
| } | |||
| static inline int | |||
| mm_count_inc(struct mm_struct *mm) { | |||
| mm->mm_count += 1; | |||
| return mm->mm_count; | |||
| } | |||
| static inline int | |||
| mm_count_dec(struct mm_struct *mm) { | |||
| mm->mm_count -= 1; | |||
| return mm->mm_count; | |||
| } | |||
| static inline void | |||
| lock_mm(struct mm_struct *mm) { | |||
| if (mm != NULL) { | |||
| lock(&(mm->mm_lock)); | |||
| } | |||
| } | |||
| static inline void | |||
| unlock_mm(struct mm_struct *mm) { | |||
| if (mm != NULL) { | |||
| unlock(&(mm->mm_lock)); | |||
| } | |||
| } | |||
| #endif /* !__KERN_MM_VMM_H__ */ | |||
| @ -0,0 +1,10 @@ | |||
| .text | |||
| .globl kernel_thread_entry | |||
| kernel_thread_entry: # void kernel_thread(void) | |||
| pushl %edx # push arg | |||
| call *%ebx # call fn | |||
| pushl %eax # save the return value of fn(arg) | |||
| call do_exit # call do_exit to terminate current thread | |||
| @ -0,0 +1,888 @@ | |||
| #include <proc.h> | |||
| #include <kmalloc.h> | |||
| #include <string.h> | |||
| #include <sync.h> | |||
| #include <pmm.h> | |||
| #include <error.h> | |||
| #include <sched.h> | |||
| #include <elf.h> | |||
| #include <vmm.h> | |||
| #include <trap.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <assert.h> | |||
| #include <unistd.h> | |||
| /* ------------- process/thread mechanism design&implementation ------------- | |||
| (an simplified Linux process/thread mechanism ) | |||
| introduction: | |||
| ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads | |||
| for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to | |||
| manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory). | |||
| ------------------------------ | |||
| process state : meaning -- reason | |||
| PROC_UNINIT : uninitialized -- alloc_proc | |||
| PROC_SLEEPING : sleeping -- try_free_pages, do_wait, do_sleep | |||
| PROC_RUNNABLE : runnable(maybe running) -- proc_init, wakeup_proc, | |||
| PROC_ZOMBIE : almost dead -- do_exit | |||
| ----------------------------- | |||
| process state changing: | |||
| alloc_proc RUNNING | |||
| + +--<----<--+ | |||
| + + proc_run + | |||
| V +-->---->--+ | |||
| PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING -- | |||
| A + + | |||
| | +--- do_exit --> PROC_ZOMBIE + | |||
| + + | |||
| -----------------------wakeup_proc---------------------------------- | |||
| ----------------------------- | |||
| process relations | |||
| parent: proc->parent (proc is children) | |||
| children: proc->cptr (proc is parent) | |||
| older sibling: proc->optr (proc is younger sibling) | |||
| younger sibling: proc->yptr (proc is older sibling) | |||
| ----------------------------- | |||
| related syscall for process: | |||
| SYS_exit : process exit, -->do_exit | |||
| SYS_fork : create child process, dup mm -->do_fork-->wakeup_proc | |||
| SYS_wait : wait process -->do_wait | |||
| SYS_exec : after fork, process execute a program -->load a program and refresh the mm | |||
| SYS_clone : create child thread -->do_fork-->wakeup_proc | |||
| SYS_yield : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process | |||
| SYS_sleep : process sleep -->do_sleep | |||
| SYS_kill : kill process -->do_kill-->proc->flags |= PF_EXITING | |||
| -->wakeup_proc-->do_wait-->do_exit | |||
| SYS_getpid : get the process's pid | |||
| */ | |||
| // the process set's list | |||
| list_entry_t proc_list; | |||
| #define HASH_SHIFT 10 | |||
| #define HASH_LIST_SIZE (1 << HASH_SHIFT) | |||
| #define pid_hashfn(x) (hash32(x, HASH_SHIFT)) | |||
| // has list for process set based on pid | |||
| static list_entry_t hash_list[HASH_LIST_SIZE]; | |||
| // idle proc | |||
| struct proc_struct *idleproc = NULL; | |||
| // init proc | |||
| struct proc_struct *initproc = NULL; | |||
| // current proc | |||
| struct proc_struct *current = NULL; | |||
| static int nr_process = 0; | |||
| void kernel_thread_entry(void); | |||
| void forkrets(struct trapframe *tf); | |||
| void switch_to(struct context *from, struct context *to); | |||
| // alloc_proc - alloc a proc_struct and init all fields of proc_struct | |||
| static struct proc_struct * | |||
| alloc_proc(void) { | |||
| struct proc_struct *proc = kmalloc(sizeof(struct proc_struct)); | |||
| if (proc != NULL) { | |||
| //LAB4:EXERCISE1 YOUR CODE | |||
| /* | |||
| * below fields in proc_struct need to be initialized | |||
| * enum proc_state state; // Process state | |||
| * int pid; // Process ID | |||
| * int runs; // the running times of Proces | |||
| * uintptr_t kstack; // Process kernel stack | |||
| * volatile bool need_resched; // bool value: need to be rescheduled to release CPU? | |||
| * struct proc_struct *parent; // the parent process | |||
| * struct mm_struct *mm; // Process's memory management field | |||
| * struct context context; // Switch here to run process | |||
| * struct trapframe *tf; // Trap frame for current interrupt | |||
| * uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT) | |||
| * uint32_t flags; // Process flag | |||
| * char name[PROC_NAME_LEN + 1]; // Process name | |||
| */ | |||
| proc->state = PROC_UNINIT; | |||
| proc->pid = -1; | |||
| proc->runs = 0; | |||
| proc->kstack = 0; | |||
| proc->need_resched = 0; | |||
| proc->parent = NULL; | |||
| proc->mm = NULL; | |||
| memset(&(proc->context), 0, sizeof(struct context)); | |||
| proc->tf = NULL; | |||
| proc->cr3 = boot_cr3; | |||
| proc->flags = 0; | |||
| memset(proc->name, 0, PROC_NAME_LEN); | |||
| proc->wait_state = 0; | |||
| proc->cptr = proc->optr = proc->yptr = NULL; | |||
| } | |||
| return proc; | |||
| } | |||
| // set_proc_name - set the name of proc | |||
| char * | |||
| set_proc_name(struct proc_struct *proc, const char *name) { | |||
| memset(proc->name, 0, sizeof(proc->name)); | |||
| return memcpy(proc->name, name, PROC_NAME_LEN); | |||
| } | |||
| // get_proc_name - get the name of proc | |||
| char * | |||
| get_proc_name(struct proc_struct *proc) { | |||
| static char name[PROC_NAME_LEN + 1]; | |||
| memset(name, 0, sizeof(name)); | |||
| return memcpy(name, proc->name, PROC_NAME_LEN); | |||
| } | |||
| // set_links - set the relation links of process | |||
| static void | |||
| set_links(struct proc_struct *proc) { | |||
| list_add(&proc_list, &(proc->list_link)); | |||
| proc->yptr = NULL; | |||
| if ((proc->optr = proc->parent->cptr) != NULL) { | |||
| proc->optr->yptr = proc; | |||
| } | |||
| proc->parent->cptr = proc; | |||
| nr_process ++; | |||
| } | |||
| // remove_links - clean the relation links of process | |||
| static void | |||
| remove_links(struct proc_struct *proc) { | |||
| list_del(&(proc->list_link)); | |||
| if (proc->optr != NULL) { | |||
| proc->optr->yptr = proc->yptr; | |||
| } | |||
| if (proc->yptr != NULL) { | |||
| proc->yptr->optr = proc->optr; | |||
| } | |||
| else { | |||
| proc->parent->cptr = proc->optr; | |||
| } | |||
| nr_process --; | |||
| } | |||
| // get_pid - alloc a unique pid for process | |||
| static int | |||
| get_pid(void) { | |||
| static_assert(MAX_PID > MAX_PROCESS); | |||
| struct proc_struct *proc; | |||
| list_entry_t *list = &proc_list, *le; | |||
| static int next_safe = MAX_PID, last_pid = MAX_PID; | |||
| if (++ last_pid >= MAX_PID) { | |||
| last_pid = 1; | |||
| goto inside; | |||
| } | |||
| if (last_pid >= next_safe) { | |||
| inside: | |||
| next_safe = MAX_PID; | |||
| repeat: | |||
| le = list; | |||
| while ((le = list_next(le)) != list) { | |||
| proc = le2proc(le, list_link); | |||
| if (proc->pid == last_pid) { | |||
| if (++ last_pid >= next_safe) { | |||
| if (last_pid >= MAX_PID) { | |||
| last_pid = 1; | |||
| } | |||
| next_safe = MAX_PID; | |||
| goto repeat; | |||
| } | |||
| } | |||
| else if (proc->pid > last_pid && next_safe > proc->pid) { | |||
| next_safe = proc->pid; | |||
| } | |||
| } | |||
| } | |||
| return last_pid; | |||
| } | |||
| // proc_run - make process "proc" running on cpu | |||
| // NOTE: before call switch_to, should load base addr of "proc"'s new PDT | |||
| void | |||
| proc_run(struct proc_struct *proc) { | |||
| if (proc != current) { | |||
| bool intr_flag; | |||
| struct proc_struct *prev = current, *next = proc; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| current = proc; | |||
| load_esp0(next->kstack + KSTACKSIZE); | |||
| lcr3(next->cr3); | |||
| switch_to(&(prev->context), &(next->context)); | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| } | |||
| } | |||
| // forkret -- the first kernel entry point of a new thread/process | |||
| // NOTE: the addr of forkret is setted in copy_thread function | |||
| // after switch_to, the current proc will execute here. | |||
| static void | |||
| forkret(void) { | |||
| forkrets(current->tf); | |||
| } | |||
| // hash_proc - add proc into proc hash_list | |||
| static void | |||
| hash_proc(struct proc_struct *proc) { | |||
| list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link)); | |||
| } | |||
| // unhash_proc - delete proc from proc hash_list | |||
| static void | |||
| unhash_proc(struct proc_struct *proc) { | |||
| list_del(&(proc->hash_link)); | |||
| } | |||
| // find_proc - find proc frome proc hash_list according to pid | |||
| struct proc_struct * | |||
| find_proc(int pid) { | |||
| if (0 < pid && pid < MAX_PID) { | |||
| list_entry_t *list = hash_list + pid_hashfn(pid), *le = list; | |||
| while ((le = list_next(le)) != list) { | |||
| struct proc_struct *proc = le2proc(le, hash_link); | |||
| if (proc->pid == pid) { | |||
| return proc; | |||
| } | |||
| } | |||
| } | |||
| return NULL; | |||
| } | |||
| // kernel_thread - create a kernel thread using "fn" function | |||
| // NOTE: the contents of temp trapframe tf will be copied to | |||
| // proc->tf in do_fork-->copy_thread function | |||
| int | |||
| kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) { | |||
| struct trapframe tf; | |||
| memset(&tf, 0, sizeof(struct trapframe)); | |||
| tf.tf_cs = KERNEL_CS; | |||
| tf.tf_ds = tf.tf_es = tf.tf_ss = KERNEL_DS; | |||
| tf.tf_regs.reg_ebx = (uint32_t)fn; | |||
| tf.tf_regs.reg_edx = (uint32_t)arg; | |||
| tf.tf_eip = (uint32_t)kernel_thread_entry; | |||
| return do_fork(clone_flags | CLONE_VM, 0, &tf); | |||
| } | |||
| // setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack | |||
| static int | |||
| setup_kstack(struct proc_struct *proc) { | |||
| struct Page *page = alloc_pages(KSTACKPAGE); | |||
| if (page != NULL) { | |||
| proc->kstack = (uintptr_t)page2kva(page); | |||
| return 0; | |||
| } | |||
| return -E_NO_MEM; | |||
| } | |||
| // put_kstack - free the memory space of process kernel stack | |||
| static void | |||
| put_kstack(struct proc_struct *proc) { | |||
| free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE); | |||
| } | |||
| // setup_pgdir - alloc one page as PDT | |||
| static int | |||
| setup_pgdir(struct mm_struct *mm) { | |||
| struct Page *page; | |||
| if ((page = alloc_page()) == NULL) { | |||
| return -E_NO_MEM; | |||
| } | |||
| pde_t *pgdir = page2kva(page); | |||
| memcpy(pgdir, boot_pgdir, PGSIZE); | |||
| pgdir[PDX(VPT)] = PADDR(pgdir) | PTE_P | PTE_W; | |||
| mm->pgdir = pgdir; | |||
| return 0; | |||
| } | |||
| // put_pgdir - free the memory space of PDT | |||
| static void | |||
| put_pgdir(struct mm_struct *mm) { | |||
| free_page(kva2page(mm->pgdir)); | |||
| } | |||
| // copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags | |||
| // - if clone_flags & CLONE_VM, then "share" ; else "duplicate" | |||
| static int | |||
| copy_mm(uint32_t clone_flags, struct proc_struct *proc) { | |||
| struct mm_struct *mm, *oldmm = current->mm; | |||
| /* current is a kernel thread */ | |||
| if (oldmm == NULL) { | |||
| return 0; | |||
| } | |||
| if (clone_flags & CLONE_VM) { | |||
| mm = oldmm; | |||
| goto good_mm; | |||
| } | |||
| int ret = -E_NO_MEM; | |||
| if ((mm = mm_create()) == NULL) { | |||
| goto bad_mm; | |||
| } | |||
| if (setup_pgdir(mm) != 0) { | |||
| goto bad_pgdir_cleanup_mm; | |||
| } | |||
| lock_mm(oldmm); | |||
| { | |||
| ret = dup_mmap(mm, oldmm); | |||
| } | |||
| unlock_mm(oldmm); | |||
| if (ret != 0) { | |||
| goto bad_dup_cleanup_mmap; | |||
| } | |||
| good_mm: | |||
| mm_count_inc(mm); | |||
| proc->mm = mm; | |||
| proc->cr3 = PADDR(mm->pgdir); | |||
| return 0; | |||
| bad_dup_cleanup_mmap: | |||
| exit_mmap(mm); | |||
| put_pgdir(mm); | |||
| bad_pgdir_cleanup_mm: | |||
| mm_destroy(mm); | |||
| bad_mm: | |||
| return ret; | |||
| } | |||
| // copy_thread - setup the trapframe on the process's kernel stack top and | |||
| // - setup the kernel entry point and stack of process | |||
| static void | |||
| copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) { | |||
| proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1; | |||
| *(proc->tf) = *tf; | |||
| proc->tf->tf_regs.reg_eax = 0; | |||
| proc->tf->tf_esp = esp; | |||
| proc->tf->tf_eflags |= FL_IF; | |||
| proc->context.eip = (uintptr_t)forkret; | |||
| proc->context.esp = (uintptr_t)(proc->tf); | |||
| } | |||
| /* do_fork - parent process for a new child process | |||
| * @clone_flags: used to guide how to clone the child process | |||
| * @stack: the parent's user stack pointer. if stack==0, It means to fork a kernel thread. | |||
| * @tf: the trapframe info, which will be copied to child process's proc->tf | |||
| */ | |||
| int | |||
| do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) { | |||
| int ret = -E_NO_FREE_PROC; | |||
| struct proc_struct *proc; | |||
| if (nr_process >= MAX_PROCESS) { | |||
| goto fork_out; | |||
| } | |||
| ret = -E_NO_MEM; | |||
| //LAB4:EXERCISE2 YOUR CODE | |||
| /* | |||
| * Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation. | |||
| * MACROs or Functions: | |||
| * alloc_proc: create a proc struct and init fields (lab4:exercise1) | |||
| * setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack | |||
| * copy_mm: process "proc" duplicate OR share process "current"'s mm according clone_flags | |||
| * if clone_flags & CLONE_VM, then "share" ; else "duplicate" | |||
| * copy_thread: setup the trapframe on the process's kernel stack top and | |||
| * setup the kernel entry point and stack of process | |||
| * hash_proc: add proc into proc hash_list | |||
| * get_pid: alloc a unique pid for process | |||
| * wakup_proc: set proc->state = PROC_RUNNABLE | |||
| * VARIABLES: | |||
| * proc_list: the process set's list | |||
| * nr_process: the number of process set | |||
| */ | |||
| // 1. call alloc_proc to allocate a proc_struct | |||
| // 2. call setup_kstack to allocate a kernel stack for child process | |||
| // 3. call copy_mm to dup OR share mm according clone_flag | |||
| // 4. call copy_thread to setup tf & context in proc_struct | |||
| // 5. insert proc_struct into hash_list && proc_list | |||
| // 6. call wakup_proc to make the new child process RUNNABLE | |||
| // 7. set ret vaule using child proc's pid | |||
| if ((proc = alloc_proc()) == NULL) { | |||
| goto fork_out; | |||
| } | |||
| proc->parent = current; | |||
| assert(current->wait_state == 0); | |||
| if (setup_kstack(proc) != 0) { | |||
| goto bad_fork_cleanup_proc; | |||
| } | |||
| if (copy_mm(clone_flags, proc) != 0) { | |||
| goto bad_fork_cleanup_kstack; | |||
| } | |||
| copy_thread(proc, stack, tf); | |||
| bool intr_flag; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| proc->pid = get_pid(); | |||
| hash_proc(proc); | |||
| set_links(proc); | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| wakeup_proc(proc); | |||
| ret = proc->pid; | |||
| fork_out: | |||
| return ret; | |||
| bad_fork_cleanup_kstack: | |||
| put_kstack(proc); | |||
| bad_fork_cleanup_proc: | |||
| kfree(proc); | |||
| goto fork_out; | |||
| } | |||
| // do_exit - called by sys_exit | |||
| // 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process | |||
| // 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself. | |||
| // 3. call scheduler to switch to other process | |||
| int | |||
| do_exit(int error_code) { | |||
| if (current == idleproc) { | |||
| panic("idleproc exit.\n"); | |||
| } | |||
| if (current == initproc) { | |||
| panic("initproc exit.\n"); | |||
| } | |||
| struct mm_struct *mm = current->mm; | |||
| if (mm != NULL) { | |||
| lcr3(boot_cr3); | |||
| if (mm_count_dec(mm) == 0) { | |||
| exit_mmap(mm); | |||
| put_pgdir(mm); | |||
| mm_destroy(mm); | |||
| } | |||
| current->mm = NULL; | |||
| } | |||
| current->state = PROC_ZOMBIE; | |||
| current->exit_code = error_code; | |||
| bool intr_flag; | |||
| struct proc_struct *proc; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| proc = current->parent; | |||
| if (proc->wait_state == WT_CHILD) { | |||
| wakeup_proc(proc); | |||
| } | |||
| while (current->cptr != NULL) { | |||
| proc = current->cptr; | |||
| current->cptr = proc->optr; | |||
| proc->yptr = NULL; | |||
| if ((proc->optr = initproc->cptr) != NULL) { | |||
| initproc->cptr->yptr = proc; | |||
| } | |||
| proc->parent = initproc; | |||
| initproc->cptr = proc; | |||
| if (proc->state == PROC_ZOMBIE) { | |||
| if (initproc->wait_state == WT_CHILD) { | |||
| wakeup_proc(initproc); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| schedule(); | |||
| panic("do_exit will not return!! %d.\n", current->pid); | |||
| } | |||
| /* load_icode - load the content of binary program(ELF format) as the new content of current process | |||
| * @binary: the memory addr of the content of binary program | |||
| * @size: the size of the content of binary program | |||
| */ | |||
| static int | |||
| load_icode(unsigned char *binary, size_t size) { | |||
| if (current->mm != NULL) { | |||
| panic("load_icode: current->mm must be empty.\n"); | |||
| } | |||
| int ret = -E_NO_MEM; | |||
| struct mm_struct *mm; | |||
| //(1) create a new mm for current process | |||
| if ((mm = mm_create()) == NULL) { | |||
| goto bad_mm; | |||
| } | |||
| //(2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT | |||
| if (setup_pgdir(mm) != 0) { | |||
| goto bad_pgdir_cleanup_mm; | |||
| } | |||
| //(3) copy TEXT/DATA section, build BSS parts in binary to memory space of process | |||
| struct Page *page; | |||
| //(3.1) get the file header of the bianry program (ELF format) | |||
| struct elfhdr *elf = (struct elfhdr *)binary; | |||
| //(3.2) get the entry of the program section headers of the bianry program (ELF format) | |||
| struct proghdr *ph = (struct proghdr *)(binary + elf->e_phoff); | |||
| //(3.3) This program is valid? | |||
| if (elf->e_magic != ELF_MAGIC) { | |||
| ret = -E_INVAL_ELF; | |||
| goto bad_elf_cleanup_pgdir; | |||
| } | |||
| uint32_t vm_flags, perm; | |||
| struct proghdr *ph_end = ph + elf->e_phnum; | |||
| for (; ph < ph_end; ph ++) { | |||
| //(3.4) find every program section headers | |||
| if (ph->p_type != ELF_PT_LOAD) { | |||
| continue ; | |||
| } | |||
| if (ph->p_filesz > ph->p_memsz) { | |||
| ret = -E_INVAL_ELF; | |||
| goto bad_cleanup_mmap; | |||
| } | |||
| if (ph->p_filesz == 0) { | |||
| continue ; | |||
| } | |||
| //(3.5) call mm_map fun to setup the new vma ( ph->p_va, ph->p_memsz) | |||
| vm_flags = 0, perm = PTE_U; | |||
| if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC; | |||
| if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE; | |||
| if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ; | |||
| if (vm_flags & VM_WRITE) perm |= PTE_W; | |||
| if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) { | |||
| goto bad_cleanup_mmap; | |||
| } | |||
| unsigned char *from = binary + ph->p_offset; | |||
| size_t off, size; | |||
| uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE); | |||
| ret = -E_NO_MEM; | |||
| //(3.6) alloc memory, and copy the contents of every program section (from, from+end) to process's memory (la, la+end) | |||
| end = ph->p_va + ph->p_filesz; | |||
| //(3.6.1) copy TEXT/DATA section of bianry program | |||
| while (start < end) { | |||
| if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { | |||
| goto bad_cleanup_mmap; | |||
| } | |||
| off = start - la, size = PGSIZE - off, la += PGSIZE; | |||
| if (end < la) { | |||
| size -= la - end; | |||
| } | |||
| memcpy(page2kva(page) + off, from, size); | |||
| start += size, from += size; | |||
| } | |||
| //(3.6.2) build BSS section of binary program | |||
| end = ph->p_va + ph->p_memsz; | |||
| if (start < la) { | |||
| /* ph->p_memsz == ph->p_filesz */ | |||
| if (start == end) { | |||
| continue ; | |||
| } | |||
| off = start + PGSIZE - la, size = PGSIZE - off; | |||
| if (end < la) { | |||
| size -= la - end; | |||
| } | |||
| memset(page2kva(page) + off, 0, size); | |||
| start += size; | |||
| assert((end < la && start == end) || (end >= la && start == la)); | |||
| } | |||
| while (start < end) { | |||
| if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { | |||
| goto bad_cleanup_mmap; | |||
| } | |||
| off = start - la, size = PGSIZE - off, la += PGSIZE; | |||
| if (end < la) { | |||
| size -= la - end; | |||
| } | |||
| memset(page2kva(page) + off, 0, size); | |||
| start += size; | |||
| } | |||
| } | |||
| //(4) build user stack memory | |||
| vm_flags = VM_READ | VM_WRITE | VM_STACK; | |||
| if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) { | |||
| goto bad_cleanup_mmap; | |||
| } | |||
| assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL); | |||
| assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL); | |||
| assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL); | |||
| assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL); | |||
| //(5) set current process's mm, sr3, and set CR3 reg = physical addr of Page Directory | |||
| mm_count_inc(mm); | |||
| current->mm = mm; | |||
| current->cr3 = PADDR(mm->pgdir); | |||
| lcr3(PADDR(mm->pgdir)); | |||
| //(6) setup trapframe for user environment | |||
| struct trapframe *tf = current->tf; | |||
| memset(tf, 0, sizeof(struct trapframe)); | |||
| /* LAB5:EXERCISE1 YOUR CODE | |||
| * should set tf_cs,tf_ds,tf_es,tf_ss,tf_esp,tf_eip,tf_eflags | |||
| * NOTICE: If we set trapframe correctly, then the user level process can return to USER MODE from kernel. So | |||
| * tf_cs should be USER_CS segment (see memlayout.h) | |||
| * tf_ds=tf_es=tf_ss should be USER_DS segment | |||
| * tf_esp should be the top addr of user stack (USTACKTOP) | |||
| * tf_eip should be the entry point of this binary program (elf->e_entry) | |||
| * tf_eflags should be set to enable computer to produce Interrupt | |||
| */ | |||
| tf->tf_cs = USER_CS; | |||
| tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS; | |||
| tf->tf_esp = USTACKTOP; | |||
| tf->tf_eip = elf->e_entry; | |||
| tf->tf_eflags = FL_IF; | |||
| ret = 0; | |||
| out: | |||
| return ret; | |||
| bad_cleanup_mmap: | |||
| exit_mmap(mm); | |||
| bad_elf_cleanup_pgdir: | |||
| put_pgdir(mm); | |||
| bad_pgdir_cleanup_mm: | |||
| mm_destroy(mm); | |||
| bad_mm: | |||
| goto out; | |||
| } | |||
| // do_execve - call exit_mmap(mm)&pug_pgdir(mm) to reclaim memory space of current process | |||
| // - call load_icode to setup new memory space accroding binary prog. | |||
| int | |||
| do_execve(const char *name, size_t len, unsigned char *binary, size_t size) { | |||
| struct mm_struct *mm = current->mm; | |||
| if (!user_mem_check(mm, (uintptr_t)name, len, 0)) { | |||
| return -E_INVAL; | |||
| } | |||
| if (len > PROC_NAME_LEN) { | |||
| len = PROC_NAME_LEN; | |||
| } | |||
| char local_name[PROC_NAME_LEN + 1]; | |||
| memset(local_name, 0, sizeof(local_name)); | |||
| memcpy(local_name, name, len); | |||
| if (mm != NULL) { | |||
| lcr3(boot_cr3); | |||
| if (mm_count_dec(mm) == 0) { | |||
| exit_mmap(mm); | |||
| put_pgdir(mm); | |||
| mm_destroy(mm); | |||
| } | |||
| current->mm = NULL; | |||
| } | |||
| int ret; | |||
| if ((ret = load_icode(binary, size)) != 0) { | |||
| goto execve_exit; | |||
| } | |||
| set_proc_name(current, local_name); | |||
| return 0; | |||
| execve_exit: | |||
| do_exit(ret); | |||
| panic("already exit: %e.\n", ret); | |||
| } | |||
| // do_yield - ask the scheduler to reschedule | |||
| int | |||
| do_yield(void) { | |||
| current->need_resched = 1; | |||
| return 0; | |||
| } | |||
| // do_wait - wait one OR any children with PROC_ZOMBIE state, and free memory space of kernel stack | |||
| // - proc struct of this child. | |||
| // NOTE: only after do_wait function, all resources of the child proces are free. | |||
| int | |||
| do_wait(int pid, int *code_store) { | |||
| struct mm_struct *mm = current->mm; | |||
| if (code_store != NULL) { | |||
| if (!user_mem_check(mm, (uintptr_t)code_store, sizeof(int), 1)) { | |||
| return -E_INVAL; | |||
| } | |||
| } | |||
| struct proc_struct *proc; | |||
| bool intr_flag, haskid; | |||
| repeat: | |||
| haskid = 0; | |||
| if (pid != 0) { | |||
| proc = find_proc(pid); | |||
| if (proc != NULL && proc->parent == current) { | |||
| haskid = 1; | |||
| if (proc->state == PROC_ZOMBIE) { | |||
| goto found; | |||
| } | |||
| } | |||
| } | |||
| else { | |||
| proc = current->cptr; | |||
| for (; proc != NULL; proc = proc->optr) { | |||
| haskid = 1; | |||
| if (proc->state == PROC_ZOMBIE) { | |||
| goto found; | |||
| } | |||
| } | |||
| } | |||
| if (haskid) { | |||
| current->state = PROC_SLEEPING; | |||
| current->wait_state = WT_CHILD; | |||
| schedule(); | |||
| if (current->flags & PF_EXITING) { | |||
| do_exit(-E_KILLED); | |||
| } | |||
| goto repeat; | |||
| } | |||
| return -E_BAD_PROC; | |||
| found: | |||
| if (proc == idleproc || proc == initproc) { | |||
| panic("wait idleproc or initproc.\n"); | |||
| } | |||
| if (code_store != NULL) { | |||
| *code_store = proc->exit_code; | |||
| } | |||
| local_intr_save(intr_flag); | |||
| { | |||
| unhash_proc(proc); | |||
| remove_links(proc); | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| put_kstack(proc); | |||
| kfree(proc); | |||
| return 0; | |||
| } | |||
| // do_kill - kill process with pid by set this process's flags with PF_EXITING | |||
| int | |||
| do_kill(int pid) { | |||
| struct proc_struct *proc; | |||
| if ((proc = find_proc(pid)) != NULL) { | |||
| if (!(proc->flags & PF_EXITING)) { | |||
| proc->flags |= PF_EXITING; | |||
| if (proc->wait_state & WT_INTERRUPTED) { | |||
| wakeup_proc(proc); | |||
| } | |||
| return 0; | |||
| } | |||
| return -E_KILLED; | |||
| } | |||
| return -E_INVAL; | |||
| } | |||
| // kernel_execve - do SYS_exec syscall to exec a user program called by user_main kernel_thread | |||
| static int | |||
| kernel_execve(const char *name, unsigned char *binary, size_t size) { | |||
| int ret, len = strlen(name); | |||
| asm volatile ( | |||
| "int %1;" | |||
| : "=a" (ret) | |||
| : "i" (T_SYSCALL), "0" (SYS_exec), "d" (name), "c" (len), "b" (binary), "D" (size) | |||
| : "memory"); | |||
| return ret; | |||
| } | |||
| #define __KERNEL_EXECVE(name, binary, size) ({ \ | |||
| cprintf("kernel_execve: pid = %d, name = \"%s\".\n", \ | |||
| current->pid, name); \ | |||
| kernel_execve(name, binary, (size_t)(size)); \ | |||
| }) | |||
| #define KERNEL_EXECVE(x) ({ \ | |||
| extern unsigned char _binary_obj___user_##x##_out_start[], \ | |||
| _binary_obj___user_##x##_out_size[]; \ | |||
| __KERNEL_EXECVE(#x, _binary_obj___user_##x##_out_start, \ | |||
| _binary_obj___user_##x##_out_size); \ | |||
| }) | |||
| #define __KERNEL_EXECVE2(x, xstart, xsize) ({ \ | |||
| extern unsigned char xstart[], xsize[]; \ | |||
| __KERNEL_EXECVE(#x, xstart, (size_t)xsize); \ | |||
| }) | |||
| #define KERNEL_EXECVE2(x, xstart, xsize) __KERNEL_EXECVE2(x, xstart, xsize) | |||
| // user_main - kernel thread used to exec a user program | |||
| static int | |||
| user_main(void *arg) { | |||
| #ifdef TEST | |||
| KERNEL_EXECVE2(TEST, TESTSTART, TESTSIZE); | |||
| #else | |||
| KERNEL_EXECVE(exit); | |||
| #endif | |||
| panic("user_main execve failed.\n"); | |||
| } | |||
| // init_main - the second kernel thread used to create user_main kernel threads | |||
| static int | |||
| init_main(void *arg) { | |||
| size_t nr_free_pages_store = nr_free_pages(); | |||
| size_t kernel_allocated_store = kallocated(); | |||
| int pid = kernel_thread(user_main, NULL, 0); | |||
| if (pid <= 0) { | |||
| panic("create user_main failed.\n"); | |||
| } | |||
| while (do_wait(0, NULL) == 0) { | |||
| schedule(); | |||
| } | |||
| cprintf("all user-mode processes have quit.\n"); | |||
| assert(initproc->cptr == NULL && initproc->yptr == NULL && initproc->optr == NULL); | |||
| assert(nr_process == 2); | |||
| assert(list_next(&proc_list) == &(initproc->list_link)); | |||
| assert(list_prev(&proc_list) == &(initproc->list_link)); | |||
| assert(nr_free_pages_store == nr_free_pages()); | |||
| assert(kernel_allocated_store == kallocated()); | |||
| cprintf("init check memory pass.\n"); | |||
| return 0; | |||
| } | |||
| // proc_init - set up the first kernel thread idleproc "idle" by itself and | |||
| // - create the second kernel thread init_main | |||
| void | |||
| proc_init(void) { | |||
| int i; | |||
| list_init(&proc_list); | |||
| for (i = 0; i < HASH_LIST_SIZE; i ++) { | |||
| list_init(hash_list + i); | |||
| } | |||
| if ((idleproc = alloc_proc()) == NULL) { | |||
| panic("cannot alloc idleproc.\n"); | |||
| } | |||
| idleproc->pid = 0; | |||
| idleproc->state = PROC_RUNNABLE; | |||
| idleproc->kstack = (uintptr_t)bootstack; | |||
| idleproc->need_resched = 1; | |||
| set_proc_name(idleproc, "idle"); | |||
| nr_process ++; | |||
| current = idleproc; | |||
| int pid = kernel_thread(init_main, NULL, 0); | |||
| if (pid <= 0) { | |||
| panic("create init_main failed.\n"); | |||
| } | |||
| initproc = find_proc(pid); | |||
| set_proc_name(initproc, "init"); | |||
| assert(idleproc != NULL && idleproc->pid == 0); | |||
| assert(initproc != NULL && initproc->pid == 1); | |||
| } | |||
| // cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works | |||
| void | |||
| cpu_idle(void) { | |||
| while (1) { | |||
| if (current->need_resched) { | |||
| schedule(); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,89 @@ | |||
| #ifndef __KERN_PROCESS_PROC_H__ | |||
| #define __KERN_PROCESS_PROC_H__ | |||
| #include <defs.h> | |||
| #include <list.h> | |||
| #include <trap.h> | |||
| #include <memlayout.h> | |||
| // process's state in his life cycle | |||
| enum proc_state { | |||
| PROC_UNINIT = 0, // uninitialized | |||
| PROC_SLEEPING, // sleeping | |||
| PROC_RUNNABLE, // runnable(maybe running) | |||
| PROC_ZOMBIE, // almost dead, and wait parent proc to reclaim his resource | |||
| }; | |||
| // Saved registers for kernel context switches. | |||
| // Don't need to save all the %fs etc. segment registers, | |||
| // because they are constant across kernel contexts. | |||
| // Save all the regular registers so we don't need to care | |||
| // which are caller save, but not the return register %eax. | |||
| // (Not saving %eax just simplifies the switching code.) | |||
| // The layout of context must match code in switch.S. | |||
| struct context { | |||
| uint32_t eip; | |||
| uint32_t esp; | |||
| uint32_t ebx; | |||
| uint32_t ecx; | |||
| uint32_t edx; | |||
| uint32_t esi; | |||
| uint32_t edi; | |||
| uint32_t ebp; | |||
| }; | |||
| #define PROC_NAME_LEN 15 | |||
| #define MAX_PROCESS 4096 | |||
| #define MAX_PID (MAX_PROCESS * 2) | |||
| extern list_entry_t proc_list; | |||
| struct proc_struct { | |||
| enum proc_state state; // Process state | |||
| int pid; // Process ID | |||
| int runs; // the running times of Proces | |||
| uintptr_t kstack; // Process kernel stack | |||
| volatile bool need_resched; // bool value: need to be rescheduled to release CPU? | |||
| struct proc_struct *parent; // the parent process | |||
| struct mm_struct *mm; // Process's memory management field | |||
| struct context context; // Switch here to run process | |||
| struct trapframe *tf; // Trap frame for current interrupt | |||
| uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT) | |||
| uint32_t flags; // Process flag | |||
| char name[PROC_NAME_LEN + 1]; // Process name | |||
| list_entry_t list_link; // Process link list | |||
| list_entry_t hash_link; // Process hash list | |||
| int exit_code; // exit code (be sent to parent proc) | |||
| uint32_t wait_state; // waiting state | |||
| struct proc_struct *cptr, *yptr, *optr; // relations between processes | |||
| }; | |||
| #define PF_EXITING 0x00000001 // getting shutdown | |||
| #define WT_CHILD (0x00000001 | WT_INTERRUPTED) | |||
| #define WT_INTERRUPTED 0x80000000 // the wait state could be interrupted | |||
| #define le2proc(le, member) \ | |||
| to_struct((le), struct proc_struct, member) | |||
| extern struct proc_struct *idleproc, *initproc, *current; | |||
| void proc_init(void); | |||
| void proc_run(struct proc_struct *proc); | |||
| int kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags); | |||
| char *set_proc_name(struct proc_struct *proc, const char *name); | |||
| char *get_proc_name(struct proc_struct *proc); | |||
| void cpu_idle(void) __attribute__((noreturn)); | |||
| struct proc_struct *find_proc(int pid); | |||
| int do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf); | |||
| int do_exit(int error_code); | |||
| int do_yield(void); | |||
| int do_execve(const char *name, size_t len, unsigned char *binary, size_t size); | |||
| int do_wait(int pid, int *code_store); | |||
| int do_kill(int pid); | |||
| #endif /* !__KERN_PROCESS_PROC_H__ */ | |||
| @ -0,0 +1,30 @@ | |||
| .text | |||
| .globl switch_to | |||
| switch_to: # switch_to(from, to) | |||
| # save from's registers | |||
| movl 4(%esp), %eax # eax points to from | |||
| popl 0(%eax) # save eip !popl | |||
| movl %esp, 4(%eax) | |||
| movl %ebx, 8(%eax) | |||
| movl %ecx, 12(%eax) | |||
| movl %edx, 16(%eax) | |||
| movl %esi, 20(%eax) | |||
| movl %edi, 24(%eax) | |||
| movl %ebp, 28(%eax) | |||
| # restore to's registers | |||
| movl 4(%esp), %eax # not 8(%esp): popped return address already | |||
| # eax now points to to | |||
| movl 28(%eax), %ebp | |||
| movl 24(%eax), %edi | |||
| movl 20(%eax), %esi | |||
| movl 16(%eax), %edx | |||
| movl 12(%eax), %ecx | |||
| movl 8(%eax), %ebx | |||
| movl 4(%eax), %esp | |||
| pushl 0(%eax) # push eip | |||
| ret | |||
| @ -0,0 +1,52 @@ | |||
| #include <list.h> | |||
| #include <sync.h> | |||
| #include <proc.h> | |||
| #include <sched.h> | |||
| #include <assert.h> | |||
| void | |||
| wakeup_proc(struct proc_struct *proc) { | |||
| assert(proc->state != PROC_ZOMBIE); | |||
| bool intr_flag; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| if (proc->state != PROC_RUNNABLE) { | |||
| proc->state = PROC_RUNNABLE; | |||
| proc->wait_state = 0; | |||
| } | |||
| else { | |||
| warn("wakeup runnable process.\n"); | |||
| } | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| } | |||
| void | |||
| schedule(void) { | |||
| bool intr_flag; | |||
| list_entry_t *le, *last; | |||
| struct proc_struct *next = NULL; | |||
| local_intr_save(intr_flag); | |||
| { | |||
| current->need_resched = 0; | |||
| last = (current == idleproc) ? &proc_list : &(current->list_link); | |||
| le = last; | |||
| do { | |||
| if ((le = list_next(le)) != &proc_list) { | |||
| next = le2proc(le, list_link); | |||
| if (next->state == PROC_RUNNABLE) { | |||
| break; | |||
| } | |||
| } | |||
| } while (le != last); | |||
| if (next == NULL || next->state != PROC_RUNNABLE) { | |||
| next = idleproc; | |||
| } | |||
| next->runs ++; | |||
| if (next != current) { | |||
| proc_run(next); | |||
| } | |||
| } | |||
| local_intr_restore(intr_flag); | |||
| } | |||
| @ -0,0 +1,10 @@ | |||
| #ifndef __KERN_SCHEDULE_SCHED_H__ | |||
| #define __KERN_SCHEDULE_SCHED_H__ | |||
| #include <proc.h> | |||
| void schedule(void); | |||
| void wakeup_proc(struct proc_struct *proc); | |||
| #endif /* !__KERN_SCHEDULE_SCHED_H__ */ | |||
| @ -0,0 +1,57 @@ | |||
| #ifndef __KERN_SYNC_SYNC_H__ | |||
| #define __KERN_SYNC_SYNC_H__ | |||
| #include <x86.h> | |||
| #include <intr.h> | |||
| #include <mmu.h> | |||
| #include <assert.h> | |||
| #include <atomic.h> | |||
| #include <sched.h> | |||
| static inline bool | |||
| __intr_save(void) { | |||
| if (read_eflags() & FL_IF) { | |||
| intr_disable(); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| } | |||
| static inline void | |||
| __intr_restore(bool flag) { | |||
| if (flag) { | |||
| intr_enable(); | |||
| } | |||
| } | |||
| #define local_intr_save(x) do { x = __intr_save(); } while (0) | |||
| #define local_intr_restore(x) __intr_restore(x); | |||
| typedef volatile bool lock_t; | |||
| static inline void | |||
| lock_init(lock_t *lock) { | |||
| *lock = 0; | |||
| } | |||
| static inline bool | |||
| try_lock(lock_t *lock) { | |||
| return !test_and_set_bit(0, lock); | |||
| } | |||
| static inline void | |||
| lock(lock_t *lock) { | |||
| while (!try_lock(lock)) { | |||
| schedule(); | |||
| } | |||
| } | |||
| static inline void | |||
| unlock(lock_t *lock) { | |||
| if (!test_and_clear_bit(0, lock)) { | |||
| panic("Unlock failed.\n"); | |||
| } | |||
| } | |||
| #endif /* !__KERN_SYNC_SYNC_H__ */ | |||
| @ -0,0 +1,101 @@ | |||
| #include <unistd.h> | |||
| #include <proc.h> | |||
| #include <syscall.h> | |||
| #include <trap.h> | |||
| #include <stdio.h> | |||
| #include <pmm.h> | |||
| #include <assert.h> | |||
| static int | |||
| sys_exit(uint32_t arg[]) { | |||
| int error_code = (int)arg[0]; | |||
| return do_exit(error_code); | |||
| } | |||
| static int | |||
| sys_fork(uint32_t arg[]) { | |||
| struct trapframe *tf = current->tf; | |||
| uintptr_t stack = tf->tf_esp; | |||
| return do_fork(0, stack, tf); | |||
| } | |||
| static int | |||
| sys_wait(uint32_t arg[]) { | |||
| int pid = (int)arg[0]; | |||
| int *store = (int *)arg[1]; | |||
| return do_wait(pid, store); | |||
| } | |||
| static int | |||
| sys_exec(uint32_t arg[]) { | |||
| const char *name = (const char *)arg[0]; | |||
| size_t len = (size_t)arg[1]; | |||
| unsigned char *binary = (unsigned char *)arg[2]; | |||
| size_t size = (size_t)arg[3]; | |||
| return do_execve(name, len, binary, size); | |||
| } | |||
| static int | |||
| sys_yield(uint32_t arg[]) { | |||
| return do_yield(); | |||
| } | |||
| static int | |||
| sys_kill(uint32_t arg[]) { | |||
| int pid = (int)arg[0]; | |||
| return do_kill(pid); | |||
| } | |||
| static int | |||
| sys_getpid(uint32_t arg[]) { | |||
| return current->pid; | |||
| } | |||
| static int | |||
| sys_putc(uint32_t arg[]) { | |||
| int c = (int)arg[0]; | |||
| cputchar(c); | |||
| return 0; | |||
| } | |||
| static int | |||
| sys_pgdir(uint32_t arg[]) { | |||
| print_pgdir(); | |||
| return 0; | |||
| } | |||
| static int (*syscalls[])(uint32_t arg[]) = { | |||
| [SYS_exit] sys_exit, | |||
| [SYS_fork] sys_fork, | |||
| [SYS_wait] sys_wait, | |||
| [SYS_exec] sys_exec, | |||
| [SYS_yield] sys_yield, | |||
| [SYS_kill] sys_kill, | |||
| [SYS_getpid] sys_getpid, | |||
| [SYS_putc] sys_putc, | |||
| [SYS_pgdir] sys_pgdir, | |||
| }; | |||
| #define NUM_SYSCALLS ((sizeof(syscalls)) / (sizeof(syscalls[0]))) | |||
| void | |||
| syscall(void) { | |||
| struct trapframe *tf = current->tf; | |||
| uint32_t arg[5]; | |||
| int num = tf->tf_regs.reg_eax; | |||
| if (num >= 0 && num < NUM_SYSCALLS) { | |||
| if (syscalls[num] != NULL) { | |||
| arg[0] = tf->tf_regs.reg_edx; | |||
| arg[1] = tf->tf_regs.reg_ecx; | |||
| arg[2] = tf->tf_regs.reg_ebx; | |||
| arg[3] = tf->tf_regs.reg_edi; | |||
| arg[4] = tf->tf_regs.reg_esi; | |||
| tf->tf_regs.reg_eax = syscalls[num](arg); | |||
| return ; | |||
| } | |||
| } | |||
| print_trapframe(tf); | |||
| panic("undefined syscall %d, pid = %d, name = %s.\n", | |||
| num, current->pid, current->name); | |||
| } | |||
| @ -0,0 +1,7 @@ | |||
| #ifndef __KERN_SYSCALL_SYSCALL_H__ | |||
| #define __KERN_SYSCALL_SYSCALL_H__ | |||
| void syscall(void); | |||
| #endif /* !__KERN_SYSCALL_SYSCALL_H__ */ | |||
| @ -0,0 +1,300 @@ | |||
| #include <defs.h> | |||
| #include <mmu.h> | |||
| #include <memlayout.h> | |||
| #include <clock.h> | |||
| #include <trap.h> | |||
| #include <x86.h> | |||
| #include <stdio.h> | |||
| #include <assert.h> | |||
| #include <console.h> | |||
| #include <vmm.h> | |||
| #include <swap.h> | |||
| #include <kdebug.h> | |||
| #include <unistd.h> | |||
| #include <syscall.h> | |||
| #include <error.h> | |||
| #include <sched.h> | |||
| #include <sync.h> | |||
| #define TICK_NUM 100 | |||
| static void print_ticks() { | |||
| cprintf("%d ticks\n",TICK_NUM); | |||
| #ifdef DEBUG_GRADE | |||
| cprintf("End of Test.\n"); | |||
| panic("EOT: kernel seems ok."); | |||
| #endif | |||
| } | |||
| /* * | |||
| * Interrupt descriptor table: | |||
| * | |||
| * Must be built at run time because shifted function addresses can't | |||
| * be represented in relocation records. | |||
| * */ | |||
| static struct gatedesc idt[256] = {{0}}; | |||
| static struct pseudodesc idt_pd = { | |||
| sizeof(idt) - 1, (uintptr_t)idt | |||
| }; | |||
| /* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */ | |||
| void | |||
| idt_init(void) { | |||
| /* LAB1 YOUR CODE : STEP 2 */ | |||
| /* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)? | |||
| * All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ? | |||
| * __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c | |||
| * (try "make" command in lab1, then you will find vector.S in kern/trap DIR) | |||
| * You can use "extern uintptr_t __vectors[];" to define this extern variable which will be used later. | |||
| * (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT). | |||
| * Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT | |||
| * (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction. | |||
| * You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more. | |||
| * Notice: the argument of lidt is idt_pd. try to find it! | |||
| */ | |||
| /* LAB5 YOUR CODE */ | |||
| //you should update your lab1 code (just add ONE or TWO lines of code), let user app to use syscall to get the service of ucore | |||
| //so you should setup the syscall interrupt gate in here | |||
| extern uintptr_t __vectors[]; | |||
| int i; | |||
| for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) { | |||
| SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL); | |||
| } | |||
| SETGATE(idt[T_SYSCALL], 1, GD_KTEXT, __vectors[T_SYSCALL], DPL_USER); | |||
| lidt(&idt_pd); | |||
| } | |||
| static const char * | |||
| trapname(int trapno) { | |||
| static const char * const excnames[] = { | |||
| "Divide error", | |||
| "Debug", | |||
| "Non-Maskable Interrupt", | |||
| "Breakpoint", | |||
| "Overflow", | |||
| "BOUND Range Exceeded", | |||
| "Invalid Opcode", | |||
| "Device Not Available", | |||
| "Double Fault", | |||
| "Coprocessor Segment Overrun", | |||
| "Invalid TSS", | |||
| "Segment Not Present", | |||
| "Stack Fault", | |||
| "General Protection", | |||
| "Page Fault", | |||
| "(unknown trap)", | |||
| "x87 FPU Floating-Point Error", | |||
| "Alignment Check", | |||
| "Machine-Check", | |||
| "SIMD Floating-Point Exception" | |||
| }; | |||
| if (trapno < sizeof(excnames)/sizeof(const char * const)) { | |||
| return excnames[trapno]; | |||
| } | |||
| if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) { | |||
| return "Hardware Interrupt"; | |||
| } | |||
| return "(unknown trap)"; | |||
| } | |||
| /* trap_in_kernel - test if trap happened in kernel */ | |||
| bool | |||
| trap_in_kernel(struct trapframe *tf) { | |||
| return (tf->tf_cs == (uint16_t)KERNEL_CS); | |||
| } | |||
| static const char *IA32flags[] = { | |||
| "CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF", | |||
| "TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL, | |||
| "RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL, | |||
| }; | |||
| void | |||
| print_trapframe(struct trapframe *tf) { | |||
| cprintf("trapframe at %p\n", tf); | |||
| print_regs(&tf->tf_regs); | |||
| cprintf(" ds 0x----%04x\n", tf->tf_ds); | |||
| cprintf(" es 0x----%04x\n", tf->tf_es); | |||
| cprintf(" fs 0x----%04x\n", tf->tf_fs); | |||
| cprintf(" gs 0x----%04x\n", tf->tf_gs); | |||
| cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); | |||
| cprintf(" err 0x%08x\n", tf->tf_err); | |||
| cprintf(" eip 0x%08x\n", tf->tf_eip); | |||
| cprintf(" cs 0x----%04x\n", tf->tf_cs); | |||
| cprintf(" flag 0x%08x ", tf->tf_eflags); | |||
| int i, j; | |||
| for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) { | |||
| if ((tf->tf_eflags & j) && IA32flags[i] != NULL) { | |||
| cprintf("%s,", IA32flags[i]); | |||
| } | |||
| } | |||
| cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12); | |||
| if (!trap_in_kernel(tf)) { | |||
| cprintf(" esp 0x%08x\n", tf->tf_esp); | |||
| cprintf(" ss 0x----%04x\n", tf->tf_ss); | |||
| } | |||
| } | |||
| void | |||
| print_regs(struct pushregs *regs) { | |||
| cprintf(" edi 0x%08x\n", regs->reg_edi); | |||
| cprintf(" esi 0x%08x\n", regs->reg_esi); | |||
| cprintf(" ebp 0x%08x\n", regs->reg_ebp); | |||
| cprintf(" oesp 0x%08x\n", regs->reg_oesp); | |||
| cprintf(" ebx 0x%08x\n", regs->reg_ebx); | |||
| cprintf(" edx 0x%08x\n", regs->reg_edx); | |||
| cprintf(" ecx 0x%08x\n", regs->reg_ecx); | |||
| cprintf(" eax 0x%08x\n", regs->reg_eax); | |||
| } | |||
| static inline void | |||
| print_pgfault(struct trapframe *tf) { | |||
| /* error_code: | |||
| * bit 0 == 0 means no page found, 1 means protection fault | |||
| * bit 1 == 0 means read, 1 means write | |||
| * bit 2 == 0 means kernel, 1 means user | |||
| * */ | |||
| cprintf("page fault at 0x%08x: %c/%c [%s].\n", rcr2(), | |||
| (tf->tf_err & 4) ? 'U' : 'K', | |||
| (tf->tf_err & 2) ? 'W' : 'R', | |||
| (tf->tf_err & 1) ? "protection fault" : "no page found"); | |||
| } | |||
| static int | |||
| pgfault_handler(struct trapframe *tf) { | |||
| extern struct mm_struct *check_mm_struct; | |||
| if(check_mm_struct !=NULL) { //used for test check_swap | |||
| print_pgfault(tf); | |||
| } | |||
| struct mm_struct *mm; | |||
| if (check_mm_struct != NULL) { | |||
| assert(current == idleproc); | |||
| mm = check_mm_struct; | |||
| } | |||
| else { | |||
| if (current == NULL) { | |||
| print_trapframe(tf); | |||
| print_pgfault(tf); | |||
| panic("unhandled page fault.\n"); | |||
| } | |||
| mm = current->mm; | |||
| } | |||
| return do_pgfault(mm, tf->tf_err, rcr2()); | |||
| } | |||
| static volatile int in_swap_tick_event = 0; | |||
| extern struct mm_struct *check_mm_struct; | |||
| static void | |||
| trap_dispatch(struct trapframe *tf) { | |||
| char c; | |||
| int ret=0; | |||
| switch (tf->tf_trapno) { | |||
| case T_PGFLT: //page fault | |||
| if ((ret = pgfault_handler(tf)) != 0) { | |||
| print_trapframe(tf); | |||
| if (current == NULL) { | |||
| panic("handle pgfault failed. ret=%d\n", ret); | |||
| } | |||
| else { | |||
| if (trap_in_kernel(tf)) { | |||
| panic("handle pgfault failed in kernel mode. ret=%d\n", ret); | |||
| } | |||
| cprintf("killed by kernel.\n"); | |||
| panic("handle user mode pgfault failed. ret=%d\n", ret); | |||
| do_exit(-E_KILLED); | |||
| } | |||
| } | |||
| break; | |||
| case T_SYSCALL: | |||
| syscall(); | |||
| break; | |||
| case IRQ_OFFSET + IRQ_TIMER: | |||
| #if 0 | |||
| LAB3 : If some page replacement algorithm(such as CLOCK PRA) need tick to change the priority of pages, | |||
| then you can add code here. | |||
| #endif | |||
| /* LAB1 YOUR CODE : STEP 3 */ | |||
| /* handle the timer interrupt */ | |||
| /* (1) After a timer interrupt, you should record this event using a global variable (increase it), such as ticks in kern/driver/clock.c | |||
| * (2) Every TICK_NUM cycle, you can print some info using a funciton, such as print_ticks(). | |||
| * (3) Too Simple? Yes, I think so! | |||
| */ | |||
| /* LAB5 YOUR CODE */ | |||
| /* you should upate you lab1 code (just add ONE or TWO lines of code): | |||
| * Every TICK_NUM cycle, you should set current process's current->need_resched = 1 | |||
| */ | |||
| ticks ++; | |||
| if (ticks % TICK_NUM == 0) { | |||
| assert(current != NULL); | |||
| current->need_resched = 1; | |||
| } | |||
| break; | |||
| case IRQ_OFFSET + IRQ_COM1: | |||
| c = cons_getc(); | |||
| cprintf("serial [%03d] %c\n", c, c); | |||
| break; | |||
| case IRQ_OFFSET + IRQ_KBD: | |||
| c = cons_getc(); | |||
| cprintf("kbd [%03d] %c\n", c, c); | |||
| break; | |||
| //LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes. | |||
| case T_SWITCH_TOU: | |||
| case T_SWITCH_TOK: | |||
| panic("T_SWITCH_** ??\n"); | |||
| break; | |||
| case IRQ_OFFSET + IRQ_IDE1: | |||
| case IRQ_OFFSET + IRQ_IDE2: | |||
| /* do nothing */ | |||
| break; | |||
| default: | |||
| print_trapframe(tf); | |||
| if (current != NULL) { | |||
| cprintf("unhandled trap.\n"); | |||
| do_exit(-E_KILLED); | |||
| } | |||
| // in kernel, it must be a mistake | |||
| panic("unexpected trap in kernel.\n"); | |||
| } | |||
| } | |||
| /* * | |||
| * trap - handles or dispatches an exception/interrupt. if and when trap() returns, | |||
| * the code in kern/trap/trapentry.S restores the old CPU state saved in the | |||
| * trapframe and then uses the iret instruction to return from the exception. | |||
| * */ | |||
| void | |||
| trap(struct trapframe *tf) { | |||
| // dispatch based on what type of trap occurred | |||
| // used for previous projects | |||
| if (current == NULL) { | |||
| trap_dispatch(tf); | |||
| } | |||
| else { | |||
| // keep a trapframe chain in stack | |||
| struct trapframe *otf = current->tf; | |||
| current->tf = tf; | |||
| bool in_kernel = trap_in_kernel(tf); | |||
| trap_dispatch(tf); | |||
| current->tf = otf; | |||
| if (!in_kernel) { | |||
| if (current->flags & PF_EXITING) { | |||
| do_exit(-E_KILLED); | |||
| } | |||
| if (current->need_resched) { | |||
| schedule(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,89 @@ | |||
| #ifndef __KERN_TRAP_TRAP_H__ | |||
| #define __KERN_TRAP_TRAP_H__ | |||
| #include <defs.h> | |||
| /* Trap Numbers */ | |||
| /* Processor-defined: */ | |||
| #define T_DIVIDE 0 // divide error | |||
| #define T_DEBUG 1 // debug exception | |||
| #define T_NMI 2 // non-maskable interrupt | |||
| #define T_BRKPT 3 // breakpoint | |||
| #define T_OFLOW 4 // overflow | |||
| #define T_BOUND 5 // bounds check | |||
| #define T_ILLOP 6 // illegal opcode | |||
| #define T_DEVICE 7 // device not available | |||
| #define T_DBLFLT 8 // double fault | |||
| // #define T_COPROC 9 // reserved (not used since 486) | |||
| #define T_TSS 10 // invalid task switch segment | |||
| #define T_SEGNP 11 // segment not present | |||
| #define T_STACK 12 // stack exception | |||
| #define T_GPFLT 13 // general protection fault | |||
| #define T_PGFLT 14 // page fault | |||
| // #define T_RES 15 // reserved | |||
| #define T_FPERR 16 // floating point error | |||
| #define T_ALIGN 17 // aligment check | |||
| #define T_MCHK 18 // machine check | |||
| #define T_SIMDERR 19 // SIMD floating point error | |||
| /* Hardware IRQ numbers. We receive these as (IRQ_OFFSET + IRQ_xx) */ | |||
| #define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET | |||
| #define IRQ_TIMER 0 | |||
| #define IRQ_KBD 1 | |||
| #define IRQ_COM1 4 | |||
| #define IRQ_IDE1 14 | |||
| #define IRQ_IDE2 15 | |||
| #define IRQ_ERROR 19 | |||
| #define IRQ_SPURIOUS 31 | |||
| /* * | |||
| * These are arbitrarily chosen, but with care not to overlap | |||
| * processor defined exceptions or interrupt vectors. | |||
| * */ | |||
| #define T_SWITCH_TOU 120 // user/kernel switch | |||
| #define T_SWITCH_TOK 121 // user/kernel switch | |||
| /* registers as pushed by pushal */ | |||
| struct pushregs { | |||
| uint32_t reg_edi; | |||
| uint32_t reg_esi; | |||
| uint32_t reg_ebp; | |||
| uint32_t reg_oesp; /* Useless */ | |||
| uint32_t reg_ebx; | |||
| uint32_t reg_edx; | |||
| uint32_t reg_ecx; | |||
| uint32_t reg_eax; | |||
| }; | |||
| struct trapframe { | |||
| struct pushregs tf_regs; | |||
| uint16_t tf_gs; | |||
| uint16_t tf_padding0; | |||
| uint16_t tf_fs; | |||
| uint16_t tf_padding1; | |||
| uint16_t tf_es; | |||
| uint16_t tf_padding2; | |||
| uint16_t tf_ds; | |||
| uint16_t tf_padding3; | |||
| uint32_t tf_trapno; | |||
| /* below here defined by x86 hardware */ | |||
| uint32_t tf_err; | |||
| uintptr_t tf_eip; | |||
| uint16_t tf_cs; | |||
| uint16_t tf_padding4; | |||
| uint32_t tf_eflags; | |||
| /* below here only when crossing rings, such as from user to kernel */ | |||
| uintptr_t tf_esp; | |||
| uint16_t tf_ss; | |||
| uint16_t tf_padding5; | |||
| } __attribute__((packed)); | |||
| void idt_init(void); | |||
| void print_trapframe(struct trapframe *tf); | |||
| void print_regs(struct pushregs *regs); | |||
| bool trap_in_kernel(struct trapframe *tf); | |||
| #endif /* !__KERN_TRAP_TRAP_H__ */ | |||
| @ -0,0 +1,49 @@ | |||
| #include <memlayout.h> | |||
| # vectors.S sends all traps here. | |||
| .text | |||
| .globl __alltraps | |||
| __alltraps: | |||
| # push registers to build a trap frame | |||
| # therefore make the stack look like a struct trapframe | |||
| pushl %ds | |||
| pushl %es | |||
| pushl %fs | |||
| pushl %gs | |||
| pushal | |||
| # load GD_KDATA into %ds and %es to set up data segments for kernel | |||
| movl $GD_KDATA, %eax | |||
| movw %ax, %ds | |||
| movw %ax, %es | |||
| # push %esp to pass a pointer to the trapframe as an argument to trap() | |||
| pushl %esp | |||
| # call trap(tf), where tf=%esp | |||
| call trap | |||
| # pop the pushed stack pointer | |||
| popl %esp | |||
| # return falls through to trapret... | |||
| .globl __trapret | |||
| __trapret: | |||
| # restore registers from stack | |||
| popal | |||
| # restore %ds, %es, %fs and %gs | |||
| popl %gs | |||
| popl %fs | |||
| popl %es | |||
| popl %ds | |||
| # get rid of the trap number and error code | |||
| addl $0x8, %esp | |||
| iret | |||
| .globl forkrets | |||
| forkrets: | |||
| # set stack to this new process's trapframe | |||
| movl 4(%esp), %esp | |||
| jmp __trapret | |||
| @ -0,0 +1,81 @@ | |||
| #ifndef __LIBS_ATOMIC_H__ | |||
| #define __LIBS_ATOMIC_H__ | |||
| /* Atomic operations that C can't guarantee us. Useful for resource counting etc.. */ | |||
| static inline void set_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||
| static inline void clear_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||
| static inline void change_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||
| static inline bool test_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||
| /* * | |||
| * set_bit - Atomically set a bit in memory | |||
| * @nr: the bit to set | |||
| * @addr: the address to start counting from | |||
| * | |||
| * Note that @nr may be almost arbitrarily large; this function is not | |||
| * restricted to acting on a single-word quantity. | |||
| * */ | |||
| static inline void | |||
| set_bit(int nr, volatile void *addr) { | |||
| asm volatile ("btsl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr)); | |||
| } | |||
| /* * | |||
| * clear_bit - Atomically clears a bit in memory | |||
| * @nr: the bit to clear | |||
| * @addr: the address to start counting from | |||
| * */ | |||
| static inline void | |||
| clear_bit(int nr, volatile void *addr) { | |||
| asm volatile ("btrl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr)); | |||
| } | |||
| /* * | |||
| * change_bit - Atomically toggle a bit in memory | |||
| * @nr: the bit to change | |||
| * @addr: the address to start counting from | |||
| * */ | |||
| static inline void | |||
| change_bit(int nr, volatile void *addr) { | |||
| asm volatile ("btcl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr)); | |||
| } | |||
| /* * | |||
| * test_bit - Determine whether a bit is set | |||
| * @nr: the bit to test | |||
| * @addr: the address to count from | |||
| * */ | |||
| static inline bool | |||
| test_bit(int nr, volatile void *addr) { | |||
| int oldbit; | |||
| asm volatile ("btl %2, %1; sbbl %0,%0" : "=r" (oldbit) : "m" (*(volatile long *)addr), "Ir" (nr)); | |||
| return oldbit != 0; | |||
| } | |||
| /* * | |||
| * test_and_set_bit - Atomically set a bit and return its old value | |||
| * @nr: the bit to set | |||
| * @addr: the address to count from | |||
| * */ | |||
| static inline bool | |||
| test_and_set_bit(int nr, volatile void *addr) { | |||
| int oldbit; | |||
| asm volatile ("btsl %2, %1; sbbl %0, %0" : "=r" (oldbit), "=m" (*(volatile long *)addr) : "Ir" (nr) : "memory"); | |||
| return oldbit != 0; | |||
| } | |||
| /* * | |||
| * test_and_clear_bit - Atomically clear a bit and return its old value | |||
| * @nr: the bit to clear | |||
| * @addr: the address to count from | |||
| * */ | |||
| static inline bool | |||
| test_and_clear_bit(int nr, volatile void *addr) { | |||
| int oldbit; | |||
| asm volatile ("btrl %2, %1; sbbl %0, %0" : "=r" (oldbit), "=m" (*(volatile long *)addr) : "Ir" (nr) : "memory"); | |||
| return oldbit != 0; | |||
| } | |||
| #endif /* !__LIBS_ATOMIC_H__ */ | |||
| @ -0,0 +1,68 @@ | |||
| #ifndef __LIBS_DEFS_H__ | |||
| #define __LIBS_DEFS_H__ | |||
| #ifndef NULL | |||
| #define NULL ((void *)0) | |||
| #endif | |||
| #define __always_inline inline __attribute__((always_inline)) | |||
| #define __noinline __attribute__((noinline)) | |||
| #define __noreturn __attribute__((noreturn)) | |||
| /* Represents true-or-false values */ | |||
| typedef int bool; | |||
| /* Explicitly-sized versions of integer types */ | |||
| typedef char int8_t; | |||
| typedef unsigned char uint8_t; | |||
| typedef short int16_t; | |||
| typedef unsigned short uint16_t; | |||
| typedef int int32_t; | |||
| typedef unsigned int uint32_t; | |||
| typedef long long int64_t; | |||
| typedef unsigned long long uint64_t; | |||
| /* * | |||
| * Pointers and addresses are 32 bits long. | |||
| * We use pointer types to represent addresses, | |||
| * uintptr_t to represent the numerical values of addresses. | |||
| * */ | |||
| typedef int32_t intptr_t; | |||
| typedef uint32_t uintptr_t; | |||
| /* size_t is used for memory object sizes */ | |||
| typedef uintptr_t size_t; | |||
| /* used for page numbers */ | |||
| typedef size_t ppn_t; | |||
| /* * | |||
| * Rounding operations (efficient when n is a power of 2) | |||
| * Round down to the nearest multiple of n | |||
| * */ | |||
| #define ROUNDDOWN(a, n) ({ \ | |||
| size_t __a = (size_t)(a); \ | |||
| (typeof(a))(__a - __a % (n)); \ | |||
| }) | |||
| /* Round up to the nearest multiple of n */ | |||
| #define ROUNDUP(a, n) ({ \ | |||
| size_t __n = (size_t)(n); \ | |||
| (typeof(a))(ROUNDDOWN((size_t)(a) + __n - 1, __n)); \ | |||
| }) | |||
| /* Return the offset of 'member' relative to the beginning of a struct type */ | |||
| #define offsetof(type, member) \ | |||
| ((size_t)(&((type *)0)->member)) | |||
| /* * | |||
| * to_struct - get the struct from a ptr | |||
| * @ptr: a struct pointer of member | |||
| * @type: the type of the struct this is embedded in | |||
| * @member: the name of the member within the struct | |||
| * */ | |||
| #define to_struct(ptr, type, member) \ | |||
| ((type *)((char *)(ptr) - offsetof(type, member))) | |||
| #endif /* !__LIBS_DEFS_H__ */ | |||
| @ -0,0 +1,48 @@ | |||
| #ifndef __LIBS_ELF_H__ | |||
| #define __LIBS_ELF_H__ | |||
| #include <defs.h> | |||
| #define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian | |||
| /* file header */ | |||
| struct elfhdr { | |||
| uint32_t e_magic; // must equal ELF_MAGIC | |||
| uint8_t e_elf[12]; | |||
| uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image | |||
| uint16_t e_machine; // 3=x86, 4=68K, etc. | |||
| uint32_t e_version; // file version, always 1 | |||
| uint32_t e_entry; // entry point if executable | |||
| uint32_t e_phoff; // file position of program header or 0 | |||
| uint32_t e_shoff; // file position of section header or 0 | |||
| uint32_t e_flags; // architecture-specific flags, usually 0 | |||
| uint16_t e_ehsize; // size of this elf header | |||
| uint16_t e_phentsize; // size of an entry in program header | |||
| uint16_t e_phnum; // number of entries in program header or 0 | |||
| uint16_t e_shentsize; // size of an entry in section header | |||
| uint16_t e_shnum; // number of entries in section header or 0 | |||
| uint16_t e_shstrndx; // section number that contains section name strings | |||
| }; | |||
| /* program section header */ | |||
| struct proghdr { | |||
| uint32_t p_type; // loadable code or data, dynamic linking info,etc. | |||
| uint32_t p_offset; // file offset of segment | |||
| uint32_t p_va; // virtual address to map segment | |||
| uint32_t p_pa; // physical address, not used | |||
| uint32_t p_filesz; // size of segment in file | |||
| uint32_t p_memsz; // size of segment in memory (bigger if contains bss) | |||
| uint32_t p_flags; // read/write/execute bits | |||
| uint32_t p_align; // required alignment, invariably hardware page size | |||
| }; | |||
| /* values for Proghdr::p_type */ | |||
| #define ELF_PT_LOAD 1 | |||
| /* flag bits for Proghdr::p_flags */ | |||
| #define ELF_PF_X 1 | |||
| #define ELF_PF_W 2 | |||
| #define ELF_PF_R 4 | |||
| #endif /* !__LIBS_ELF_H__ */ | |||
| @ -0,0 +1,33 @@ | |||
| #ifndef __LIBS_ERROR_H__ | |||
| #define __LIBS_ERROR_H__ | |||
| /* kernel error codes -- keep in sync with list in lib/printfmt.c */ | |||
| #define E_UNSPECIFIED 1 // Unspecified or unknown problem | |||
| #define E_BAD_PROC 2 // Process doesn't exist or otherwise | |||
| #define E_INVAL 3 // Invalid parameter | |||
| #define E_NO_MEM 4 // Request failed due to memory shortage | |||
| #define E_NO_FREE_PROC 5 // Attempt to create a new process beyond | |||
| #define E_FAULT 6 // Memory fault | |||
| #define E_SWAP_FAULT 7 // SWAP READ/WRITE fault | |||
| #define E_INVAL_ELF 8 // Invalid elf file | |||
| #define E_KILLED 9 // Process is killed | |||
| #define E_PANIC 10 // Panic Failure | |||
| #define E_TIMEOUT 11 // Timeout | |||
| #define E_TOO_BIG 12 // Argument is Too Big | |||
| #define E_NO_DEV 13 // No such Device | |||
| #define E_NA_DEV 14 // Device Not Available | |||
| #define E_BUSY 15 // Device/File is Busy | |||
| #define E_NOENT 16 // No Such File or Directory | |||
| #define E_ISDIR 17 // Is a Directory | |||
| #define E_NOTDIR 18 // Not a Directory | |||
| #define E_XDEV 19 // Cross Device-Link | |||
| #define E_UNIMP 20 // Unimplemented Feature | |||
| #define E_SEEK 21 // Illegal Seek | |||
| #define E_MAX_OPEN 22 // Too Many Files are Open | |||
| #define E_EXISTS 23 // File/Directory Already Exists | |||
| #define E_NOTEMPTY 24 // Directory is Not Empty | |||
| /* the maximum allowed */ | |||
| #define MAXERROR 24 | |||
| #endif /* !__LIBS_ERROR_H__ */ | |||
| @ -0,0 +1,18 @@ | |||
| #include <stdlib.h> | |||
| /* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ | |||
| #define GOLDEN_RATIO_PRIME_32 0x9e370001UL | |||
| /* * | |||
| * hash32 - generate a hash value in the range [0, 2^@bits - 1] | |||
| * @val: the input value | |||
| * @bits: the number of bits in a return value | |||
| * | |||
| * High bits are more random, so we use them. | |||
| * */ | |||
| uint32_t | |||
| hash32(uint32_t val, unsigned int bits) { | |||
| uint32_t hash = val * GOLDEN_RATIO_PRIME_32; | |||
| return (hash >> (32 - bits)); | |||
| } | |||
| @ -0,0 +1,163 @@ | |||
| #ifndef __LIBS_LIST_H__ | |||
| #define __LIBS_LIST_H__ | |||
| #ifndef __ASSEMBLER__ | |||
| #include <defs.h> | |||
| /* * | |||
| * Simple doubly linked list implementation. | |||
| * | |||
| * Some of the internal functions ("__xxx") are useful when manipulating | |||
| * whole lists rather than single entries, as sometimes we already know | |||
| * the next/prev entries and we can generate better code by using them | |||
| * directly rather than using the generic single-entry routines. | |||
| * */ | |||
| struct list_entry { | |||
| struct list_entry *prev, *next; | |||
| }; | |||
| typedef struct list_entry list_entry_t; | |||
| static inline void list_init(list_entry_t *elm) __attribute__((always_inline)); | |||
| static inline void list_add(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); | |||
| static inline void list_add_before(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); | |||
| static inline void list_add_after(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); | |||
| static inline void list_del(list_entry_t *listelm) __attribute__((always_inline)); | |||
| static inline void list_del_init(list_entry_t *listelm) __attribute__((always_inline)); | |||
| static inline bool list_empty(list_entry_t *list) __attribute__((always_inline)); | |||
| static inline list_entry_t *list_next(list_entry_t *listelm) __attribute__((always_inline)); | |||
| static inline list_entry_t *list_prev(list_entry_t *listelm) __attribute__((always_inline)); | |||
| static inline void __list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); | |||
| static inline void __list_del(list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); | |||
| /* * | |||
| * list_init - initialize a new entry | |||
| * @elm: new entry to be initialized | |||
| * */ | |||
| static inline void | |||
| list_init(list_entry_t *elm) { | |||
| elm->prev = elm->next = elm; | |||
| } | |||
| /* * | |||
| * list_add - add a new entry | |||
| * @listelm: list head to add after | |||
| * @elm: new entry to be added | |||
| * | |||
| * Insert the new element @elm *after* the element @listelm which | |||
| * is already in the list. | |||
| * */ | |||
| static inline void | |||
| list_add(list_entry_t *listelm, list_entry_t *elm) { | |||
| list_add_after(listelm, elm); | |||
| } | |||
| /* * | |||
| * list_add_before - add a new entry | |||
| * @listelm: list head to add before | |||
| * @elm: new entry to be added | |||
| * | |||
| * Insert the new element @elm *before* the element @listelm which | |||
| * is already in the list. | |||
| * */ | |||
| static inline void | |||
| list_add_before(list_entry_t *listelm, list_entry_t *elm) { | |||
| __list_add(elm, listelm->prev, listelm); | |||
| } | |||
| /* * | |||
| * list_add_after - add a new entry | |||
| * @listelm: list head to add after | |||
| * @elm: new entry to be added | |||
| * | |||
| * Insert the new element @elm *after* the element @listelm which | |||
| * is already in the list. | |||
| * */ | |||
| static inline void | |||
| list_add_after(list_entry_t *listelm, list_entry_t *elm) { | |||
| __list_add(elm, listelm, listelm->next); | |||
| } | |||
| /* * | |||
| * list_del - deletes entry from list | |||
| * @listelm: the element to delete from the list | |||
| * | |||
| * Note: list_empty() on @listelm does not return true after this, the entry is | |||
| * in an undefined state. | |||
| * */ | |||
| static inline void | |||
| list_del(list_entry_t *listelm) { | |||
| __list_del(listelm->prev, listelm->next); | |||
| } | |||
| /* * | |||
| * list_del_init - deletes entry from list and reinitialize it. | |||
| * @listelm: the element to delete from the list. | |||
| * | |||
| * Note: list_empty() on @listelm returns true after this. | |||
| * */ | |||
| static inline void | |||
| list_del_init(list_entry_t *listelm) { | |||
| list_del(listelm); | |||
| list_init(listelm); | |||
| } | |||
| /* * | |||
| * list_empty - tests whether a list is empty | |||
| * @list: the list to test. | |||
| * */ | |||
| static inline bool | |||
| list_empty(list_entry_t *list) { | |||
| return list->next == list; | |||
| } | |||
| /* * | |||
| * list_next - get the next entry | |||
| * @listelm: the list head | |||
| **/ | |||
| static inline list_entry_t * | |||
| list_next(list_entry_t *listelm) { | |||
| return listelm->next; | |||
| } | |||
| /* * | |||
| * list_prev - get the previous entry | |||
| * @listelm: the list head | |||
| **/ | |||
| static inline list_entry_t * | |||
| list_prev(list_entry_t *listelm) { | |||
| return listelm->prev; | |||
| } | |||
| /* * | |||
| * Insert a new entry between two known consecutive entries. | |||
| * | |||
| * This is only for internal list manipulation where we know | |||
| * the prev/next entries already! | |||
| * */ | |||
| static inline void | |||
| __list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) { | |||
| prev->next = next->prev = elm; | |||
| elm->next = next; | |||
| elm->prev = prev; | |||
| } | |||
| /* * | |||
| * Delete a list entry by making the prev/next entries point to each other. | |||
| * | |||
| * This is only for internal list manipulation where we know | |||
| * the prev/next entries already! | |||
| * */ | |||
| static inline void | |||
| __list_del(list_entry_t *prev, list_entry_t *next) { | |||
| prev->next = next; | |||
| next->prev = prev; | |||
| } | |||
| #endif /* !__ASSEMBLER__ */ | |||
| #endif /* !__LIBS_LIST_H__ */ | |||
| @ -0,0 +1,343 @@ | |||
| #include <defs.h> | |||
| #include <x86.h> | |||
| #include <error.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| /* * | |||
| * Space or zero padding and a field width are supported for the numeric | |||
| * formats only. | |||
| * | |||
| * The special format %e takes an integer error code | |||
| * and prints a string describing the error. | |||
| * The integer may be positive or negative, | |||
| * so that -E_NO_MEM and E_NO_MEM are equivalent. | |||
| * */ | |||
| static const char * const error_string[MAXERROR + 1] = { | |||
| [0] NULL, | |||
| [E_UNSPECIFIED] "unspecified error", | |||
| [E_BAD_PROC] "bad process", | |||
| [E_INVAL] "invalid parameter", | |||
| [E_NO_MEM] "out of memory", | |||
| [E_NO_FREE_PROC] "out of processes", | |||
| [E_FAULT] "segmentation fault", | |||
| [E_INVAL_ELF] "invalid elf file", | |||
| [E_KILLED] "process is killed", | |||
| [E_PANIC] "panic failure", | |||
| }; | |||
| /* * | |||
| * printnum - print a number (base <= 16) in reverse order | |||
| * @putch: specified putch function, print a single character | |||
| * @putdat: used by @putch function | |||
| * @num: the number will be printed | |||
| * @base: base for print, must be in [1, 16] | |||
| * @width: maximum number of digits, if the actual width is less than @width, use @padc instead | |||
| * @padc: character that padded on the left if the actual width is less than @width | |||
| * */ | |||
| static void | |||
| printnum(void (*putch)(int, void*), void *putdat, | |||
| unsigned long long num, unsigned base, int width, int padc) { | |||
| unsigned long long result = num; | |||
| unsigned mod = do_div(result, base); | |||
| // first recursively print all preceding (more significant) digits | |||
| if (num >= base) { | |||
| printnum(putch, putdat, result, base, width - 1, padc); | |||
| } else { | |||
| // print any needed pad characters before first digit | |||
| while (-- width > 0) | |||
| putch(padc, putdat); | |||
| } | |||
| // then print this (the least significant) digit | |||
| putch("0123456789abcdef"[mod], putdat); | |||
| } | |||
| /* * | |||
| * getuint - get an unsigned int of various possible sizes from a varargs list | |||
| * @ap: a varargs list pointer | |||
| * @lflag: determines the size of the vararg that @ap points to | |||
| * */ | |||
| static unsigned long long | |||
| getuint(va_list *ap, int lflag) { | |||
| if (lflag >= 2) { | |||
| return va_arg(*ap, unsigned long long); | |||
| } | |||
| else if (lflag) { | |||
| return va_arg(*ap, unsigned long); | |||
| } | |||
| else { | |||
| return va_arg(*ap, unsigned int); | |||
| } | |||
| } | |||
| /* * | |||
| * getint - same as getuint but signed, we can't use getuint because of sign extension | |||
| * @ap: a varargs list pointer | |||
| * @lflag: determines the size of the vararg that @ap points to | |||
| * */ | |||
| static long long | |||
| getint(va_list *ap, int lflag) { | |||
| if (lflag >= 2) { | |||
| return va_arg(*ap, long long); | |||
| } | |||
| else if (lflag) { | |||
| return va_arg(*ap, long); | |||
| } | |||
| else { | |||
| return va_arg(*ap, int); | |||
| } | |||
| } | |||
| /* * | |||
| * printfmt - format a string and print it by using putch | |||
| * @putch: specified putch function, print a single character | |||
| * @putdat: used by @putch function | |||
| * @fmt: the format string to use | |||
| * */ | |||
| void | |||
| printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...) { | |||
| va_list ap; | |||
| va_start(ap, fmt); | |||
| vprintfmt(putch, putdat, fmt, ap); | |||
| va_end(ap); | |||
| } | |||
| /* * | |||
| * vprintfmt - format a string and print it by using putch, it's called with a va_list | |||
| * instead of a variable number of arguments | |||
| * @putch: specified putch function, print a single character | |||
| * @putdat: used by @putch function | |||
| * @fmt: the format string to use | |||
| * @ap: arguments for the format string | |||
| * | |||
| * Call this function if you are already dealing with a va_list. | |||
| * Or you probably want printfmt() instead. | |||
| * */ | |||
| void | |||
| vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) { | |||
| register const char *p; | |||
| register int ch, err; | |||
| unsigned long long num; | |||
| int base, width, precision, lflag, altflag; | |||
| while (1) { | |||
| while ((ch = *(unsigned char *)fmt ++) != '%') { | |||
| if (ch == '\0') { | |||
| return; | |||
| } | |||
| putch(ch, putdat); | |||
| } | |||
| // Process a %-escape sequence | |||
| char padc = ' '; | |||
| width = precision = -1; | |||
| lflag = altflag = 0; | |||
| reswitch: | |||
| switch (ch = *(unsigned char *)fmt ++) { | |||
| // flag to pad on the right | |||
| case '-': | |||
| padc = '-'; | |||
| goto reswitch; | |||
| // flag to pad with 0's instead of spaces | |||
| case '0': | |||
| padc = '0'; | |||
| goto reswitch; | |||
| // width field | |||
| case '1' ... '9': | |||
| for (precision = 0; ; ++ fmt) { | |||
| precision = precision * 10 + ch - '0'; | |||
| ch = *fmt; | |||
| if (ch < '0' || ch > '9') { | |||
| break; | |||
| } | |||
| } | |||
| goto process_precision; | |||
| case '*': | |||
| precision = va_arg(ap, int); | |||
| goto process_precision; | |||
| case '.': | |||
| if (width < 0) | |||
| width = 0; | |||
| goto reswitch; | |||
| case '#': | |||
| altflag = 1; | |||
| goto reswitch; | |||
| process_precision: | |||
| if (width < 0) | |||
| width = precision, precision = -1; | |||
| goto reswitch; | |||
| // long flag (doubled for long long) | |||
| case 'l': | |||
| lflag ++; | |||
| goto reswitch; | |||
| // character | |||
| case 'c': | |||
| putch(va_arg(ap, int), putdat); | |||
| break; | |||
| // error message | |||
| case 'e': | |||
| err = va_arg(ap, int); | |||
| if (err < 0) { | |||
| err = -err; | |||
| } | |||
| if (err > MAXERROR || (p = error_string[err]) == NULL) { | |||
| printfmt(putch, putdat, "error %d", err); | |||
| } | |||
| else { | |||
| printfmt(putch, putdat, "%s", p); | |||
| } | |||
| break; | |||
| // string | |||
| case 's': | |||
| if ((p = va_arg(ap, char *)) == NULL) { | |||
| p = "(null)"; | |||
| } | |||
| if (width > 0 && padc != '-') { | |||
| for (width -= strnlen(p, precision); width > 0; width --) { | |||
| putch(padc, putdat); | |||
| } | |||
| } | |||
| for (; (ch = *p ++) != '\0' && (precision < 0 || -- precision >= 0); width --) { | |||
| if (altflag && (ch < ' ' || ch > '~')) { | |||
| putch('?', putdat); | |||
| } | |||
| else { | |||
| putch(ch, putdat); | |||
| } | |||
| } | |||
| for (; width > 0; width --) { | |||
| putch(' ', putdat); | |||
| } | |||
| break; | |||
| // (signed) decimal | |||
| case 'd': | |||
| num = getint(&ap, lflag); | |||
| if ((long long)num < 0) { | |||
| putch('-', putdat); | |||
| num = -(long long)num; | |||
| } | |||
| base = 10; | |||
| goto number; | |||
| // unsigned decimal | |||
| case 'u': | |||
| num = getuint(&ap, lflag); | |||
| base = 10; | |||
| goto number; | |||
| // (unsigned) octal | |||
| case 'o': | |||
| num = getuint(&ap, lflag); | |||
| base = 8; | |||
| goto number; | |||
| // pointer | |||
| case 'p': | |||
| putch('0', putdat); | |||
| putch('x', putdat); | |||
| num = (unsigned long long)(uintptr_t)va_arg(ap, void *); | |||
| base = 16; | |||
| goto number; | |||
| // (unsigned) hexadecimal | |||
| case 'x': | |||
| num = getuint(&ap, lflag); | |||
| base = 16; | |||
| number: | |||
| printnum(putch, putdat, num, base, width, padc); | |||
| break; | |||
| // escaped '%' character | |||
| case '%': | |||
| putch(ch, putdat); | |||
| break; | |||
| // unrecognized escape sequence - just print it literally | |||
| default: | |||
| putch('%', putdat); | |||
| for (fmt --; fmt[-1] != '%'; fmt --) | |||
| /* do nothing */; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| /* sprintbuf is used to save enough information of a buffer */ | |||
| struct sprintbuf { | |||
| char *buf; // address pointer points to the first unused memory | |||
| char *ebuf; // points the end of the buffer | |||
| int cnt; // the number of characters that have been placed in this buffer | |||
| }; | |||
| /* * | |||
| * sprintputch - 'print' a single character in a buffer | |||
| * @ch: the character will be printed | |||
| * @b: the buffer to place the character @ch | |||
| * */ | |||
| static void | |||
| sprintputch(int ch, struct sprintbuf *b) { | |||
| b->cnt ++; | |||
| if (b->buf < b->ebuf) { | |||
| *b->buf ++ = ch; | |||
| } | |||
| } | |||
| /* * | |||
| * snprintf - format a string and place it in a buffer | |||
| * @str: the buffer to place the result into | |||
| * @size: the size of buffer, including the trailing null space | |||
| * @fmt: the format string to use | |||
| * */ | |||
| int | |||
| snprintf(char *str, size_t size, const char *fmt, ...) { | |||
| va_list ap; | |||
| int cnt; | |||
| va_start(ap, fmt); | |||
| cnt = vsnprintf(str, size, fmt, ap); | |||
| va_end(ap); | |||
| return cnt; | |||
| } | |||
| /* * | |||
| * vsnprintf - format a string and place it in a buffer, it's called with a va_list | |||
| * instead of a variable number of arguments | |||
| * @str: the buffer to place the result into | |||
| * @size: the size of buffer, including the trailing null space | |||
| * @fmt: the format string to use | |||
| * @ap: arguments for the format string | |||
| * | |||
| * The return value is the number of characters which would be generated for the | |||
| * given input, excluding the trailing '\0'. | |||
| * | |||
| * Call this function if you are already dealing with a va_list. | |||
| * Or you probably want snprintf() instead. | |||
| * */ | |||
| int | |||
| vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { | |||
| struct sprintbuf b = {str, str + size - 1, 0}; | |||
| if (str == NULL || b.buf > b.ebuf) { | |||
| return -E_INVAL; | |||
| } | |||
| // print the string to the buffer | |||
| vprintfmt((void*)sprintputch, &b, fmt, ap); | |||
| // null terminate the buffer | |||
| *b.buf = '\0'; | |||
| return b.cnt; | |||
| } | |||
| @ -0,0 +1,26 @@ | |||
| #include <x86.h> | |||
| #include <stdlib.h> | |||
| static unsigned long long next = 1; | |||
| /* * | |||
| * rand - returns a pseudo-random integer | |||
| * | |||
| * The rand() function return a value in the range [0, RAND_MAX]. | |||
| * */ | |||
| int | |||
| rand(void) { | |||
| next = (next * 0x5DEECE66DLL + 0xBLL) & ((1LL << 48) - 1); | |||
| unsigned long long result = (next >> 12); | |||
| return (int)do_div(result, RAND_MAX + 1); | |||
| } | |||
| /* * | |||
| * srand - seed the random number generator with the given number | |||
| * @seed: the required seed number | |||
| * */ | |||
| void | |||
| srand(unsigned int seed) { | |||
| next = seed; | |||
| } | |||
| @ -0,0 +1,12 @@ | |||
| #ifndef __LIBS_STDARG_H__ | |||
| #define __LIBS_STDARG_H__ | |||
| /* compiler provides size of save area */ | |||
| typedef __builtin_va_list va_list; | |||
| #define va_start(ap, last) (__builtin_va_start(ap, last)) | |||
| #define va_arg(ap, type) (__builtin_va_arg(ap, type)) | |||
| #define va_end(ap) /*nothing*/ | |||
| #endif /* !__LIBS_STDARG_H__ */ | |||
| @ -0,0 +1,24 @@ | |||
| #ifndef __LIBS_STDIO_H__ | |||
| #define __LIBS_STDIO_H__ | |||
| #include <defs.h> | |||
| #include <stdarg.h> | |||
| /* kern/libs/stdio.c */ | |||
| int cprintf(const char *fmt, ...); | |||
| int vcprintf(const char *fmt, va_list ap); | |||
| void cputchar(int c); | |||
| int cputs(const char *str); | |||
| int getchar(void); | |||
| /* kern/libs/readline.c */ | |||
| char *readline(const char *prompt); | |||
| /* libs/printfmt.c */ | |||
| void printfmt(void (*putch)(int, void *), void *putdat, const char *fmt, ...); | |||
| void vprintfmt(void (*putch)(int, void *), void *putdat, const char *fmt, va_list ap); | |||
| int snprintf(char *str, size_t size, const char *fmt, ...); | |||
| int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); | |||
| #endif /* !__LIBS_STDIO_H__ */ | |||
| @ -0,0 +1,17 @@ | |||
| #ifndef __LIBS_STDLIB_H__ | |||
| #define __LIBS_STDLIB_H__ | |||
| #include <defs.h> | |||
| /* the largest number rand will return */ | |||
| #define RAND_MAX 2147483647UL | |||
| /* libs/rand.c */ | |||
| int rand(void); | |||
| void srand(unsigned int seed); | |||
| /* libs/hash.c */ | |||
| uint32_t hash32(uint32_t val, unsigned int bits); | |||
| #endif /* !__LIBS_RAND_H__ */ | |||
| @ -0,0 +1,367 @@ | |||
| #include <string.h> | |||
| #include <x86.h> | |||
| /* * | |||
| * strlen - calculate the length of the string @s, not including | |||
| * the terminating '\0' character. | |||
| * @s: the input string | |||
| * | |||
| * The strlen() function returns the length of string @s. | |||
| * */ | |||
| size_t | |||
| strlen(const char *s) { | |||
| size_t cnt = 0; | |||
| while (*s ++ != '\0') { | |||
| cnt ++; | |||
| } | |||
| return cnt; | |||
| } | |||
| /* * | |||
| * strnlen - calculate the length of the string @s, not including | |||
| * the terminating '\0' char acter, but at most @len. | |||
| * @s: the input string | |||
| * @len: the max-length that function will scan | |||
| * | |||
| * Note that, this function looks only at the first @len characters | |||
| * at @s, and never beyond @s + @len. | |||
| * | |||
| * The return value is strlen(s), if that is less than @len, or | |||
| * @len if there is no '\0' character among the first @len characters | |||
| * pointed by @s. | |||
| * */ | |||
| size_t | |||
| strnlen(const char *s, size_t len) { | |||
| size_t cnt = 0; | |||
| while (cnt < len && *s ++ != '\0') { | |||
| cnt ++; | |||
| } | |||
| return cnt; | |||
| } | |||
| /* * | |||
| * strcpy - copies the string pointed by @src into the array pointed by @dst, | |||
| * including the terminating null character. | |||
| * @dst: pointer to the destination array where the content is to be copied | |||
| * @src: string to be copied | |||
| * | |||
| * The return value is @dst. | |||
| * | |||
| * To avoid overflows, the size of array pointed by @dst should be long enough to | |||
| * contain the same string as @src (including the terminating null character), and | |||
| * should not overlap in memory with @src. | |||
| * */ | |||
| char * | |||
| strcpy(char *dst, const char *src) { | |||
| #ifdef __HAVE_ARCH_STRCPY | |||
| return __strcpy(dst, src); | |||
| #else | |||
| char *p = dst; | |||
| while ((*p ++ = *src ++) != '\0') | |||
| /* nothing */; | |||
| return dst; | |||
| #endif /* __HAVE_ARCH_STRCPY */ | |||
| } | |||
| /* * | |||
| * strncpy - copies the first @len characters of @src to @dst. If the end of string @src | |||
| * if found before @len characters have been copied, @dst is padded with '\0' until a | |||
| * total of @len characters have been written to it. | |||
| * @dst: pointer to the destination array where the content is to be copied | |||
| * @src: string to be copied | |||
| * @len: maximum number of characters to be copied from @src | |||
| * | |||
| * The return value is @dst | |||
| * */ | |||
| char * | |||
| strncpy(char *dst, const char *src, size_t len) { | |||
| char *p = dst; | |||
| while (len > 0) { | |||
| if ((*p = *src) != '\0') { | |||
| src ++; | |||
| } | |||
| p ++, len --; | |||
| } | |||
| return dst; | |||
| } | |||
| /* * | |||
| * strcmp - compares the string @s1 and @s2 | |||
| * @s1: string to be compared | |||
| * @s2: string to be compared | |||
| * | |||
| * This function starts comparing the first character of each string. If | |||
| * they are equal to each other, it continues with the following pairs until | |||
| * the characters differ or until a terminanting null-character is reached. | |||
| * | |||
| * Returns an integral value indicating the relationship between the strings: | |||
| * - A zero value indicates that both strings are equal; | |||
| * - A value greater than zero indicates that the first character that does | |||
| * not match has a greater value in @s1 than in @s2; | |||
| * - And a value less than zero indicates the opposite. | |||
| * */ | |||
| int | |||
| strcmp(const char *s1, const char *s2) { | |||
| #ifdef __HAVE_ARCH_STRCMP | |||
| return __strcmp(s1, s2); | |||
| #else | |||
| while (*s1 != '\0' && *s1 == *s2) { | |||
| s1 ++, s2 ++; | |||
| } | |||
| return (int)((unsigned char)*s1 - (unsigned char)*s2); | |||
| #endif /* __HAVE_ARCH_STRCMP */ | |||
| } | |||
| /* * | |||
| * strncmp - compares up to @n characters of the string @s1 to those of the string @s2 | |||
| * @s1: string to be compared | |||
| * @s2: string to be compared | |||
| * @n: maximum number of characters to compare | |||
| * | |||
| * This function starts comparing the first character of each string. If | |||
| * they are equal to each other, it continues with the following pairs until | |||
| * the characters differ, until a terminating null-character is reached, or | |||
| * until @n characters match in both strings, whichever happens first. | |||
| * */ | |||
| int | |||
| strncmp(const char *s1, const char *s2, size_t n) { | |||
| while (n > 0 && *s1 != '\0' && *s1 == *s2) { | |||
| n --, s1 ++, s2 ++; | |||
| } | |||
| return (n == 0) ? 0 : (int)((unsigned char)*s1 - (unsigned char)*s2); | |||
| } | |||
| /* * | |||
| * strchr - locates first occurrence of character in string | |||
| * @s: the input string | |||
| * @c: character to be located | |||
| * | |||
| * The strchr() function returns a pointer to the first occurrence of | |||
| * character in @s. If the value is not found, the function returns 'NULL'. | |||
| * */ | |||
| char * | |||
| strchr(const char *s, char c) { | |||
| while (*s != '\0') { | |||
| if (*s == c) { | |||
| return (char *)s; | |||
| } | |||
| s ++; | |||
| } | |||
| return NULL; | |||
| } | |||
| /* * | |||
| * strfind - locates first occurrence of character in string | |||
| * @s: the input string | |||
| * @c: character to be located | |||
| * | |||
| * The strfind() function is like strchr() except that if @c is | |||
| * not found in @s, then it returns a pointer to the null byte at the | |||
| * end of @s, rather than 'NULL'. | |||
| * */ | |||
| char * | |||
| strfind(const char *s, char c) { | |||
| while (*s != '\0') { | |||
| if (*s == c) { | |||
| break; | |||
| } | |||
| s ++; | |||
| } | |||
| return (char *)s; | |||
| } | |||
| /* * | |||
| * strtol - converts string to long integer | |||
| * @s: the input string that contains the representation of an integer number | |||
| * @endptr: reference to an object of type char *, whose value is set by the | |||
| * function to the next character in @s after the numerical value. This | |||
| * parameter can also be a null pointer, in which case it is not used. | |||
| * @base: x | |||
| * | |||
| * The function first discards as many whitespace characters as necessary until | |||
| * the first non-whitespace character is found. Then, starting from this character, | |||
| * takes as many characters as possible that are valid following a syntax that | |||
| * depends on the base parameter, and interprets them as a numerical value. Finally, | |||
| * a pointer to the first character following the integer representation in @s | |||
| * is stored in the object pointed by @endptr. | |||
| * | |||
| * If the value of base is zero, the syntax expected is similar to that of | |||
| * integer constants, which is formed by a succession of: | |||
| * - An optional plus or minus sign; | |||
| * - An optional prefix indicating octal or hexadecimal base ("0" or "0x" respectively) | |||
| * - A sequence of decimal digits (if no base prefix was specified) or either octal | |||
| * or hexadecimal digits if a specific prefix is present | |||
| * | |||
| * If the base value is between 2 and 36, the format expected for the integral number | |||
| * is a succession of the valid digits and/or letters needed to represent integers of | |||
| * the specified radix (starting from '0' and up to 'z'/'Z' for radix 36). The | |||
| * sequence may optionally be preceded by a plus or minus sign and, if base is 16, | |||
| * an optional "0x" or "0X" prefix. | |||
| * | |||
| * The strtol() function returns the converted integral number as a long int value. | |||
| * */ | |||
| long | |||
| strtol(const char *s, char **endptr, int base) { | |||
| int neg = 0; | |||
| long val = 0; | |||
| // gobble initial whitespace | |||
| while (*s == ' ' || *s == '\t') { | |||
| s ++; | |||
| } | |||
| // plus/minus sign | |||
| if (*s == '+') { | |||
| s ++; | |||
| } | |||
| else if (*s == '-') { | |||
| s ++, neg = 1; | |||
| } | |||
| // hex or octal base prefix | |||
| if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x')) { | |||
| s += 2, base = 16; | |||
| } | |||
| else if (base == 0 && s[0] == '0') { | |||
| s ++, base = 8; | |||
| } | |||
| else if (base == 0) { | |||
| base = 10; | |||
| } | |||
| // digits | |||
| while (1) { | |||
| int dig; | |||
| if (*s >= '0' && *s <= '9') { | |||
| dig = *s - '0'; | |||
| } | |||
| else if (*s >= 'a' && *s <= 'z') { | |||
| dig = *s - 'a' + 10; | |||
| } | |||
| else if (*s >= 'A' && *s <= 'Z') { | |||
| dig = *s - 'A' + 10; | |||
| } | |||
| else { | |||
| break; | |||
| } | |||
| if (dig >= base) { | |||
| break; | |||
| } | |||
| s ++, val = (val * base) + dig; | |||
| // we don't properly detect overflow! | |||
| } | |||
| if (endptr) { | |||
| *endptr = (char *) s; | |||
| } | |||
| return (neg ? -val : val); | |||
| } | |||
| /* * | |||
| * memset - sets the first @n bytes of the memory area pointed by @s | |||
| * to the specified value @c. | |||
| * @s: pointer the the memory area to fill | |||
| * @c: value to set | |||
| * @n: number of bytes to be set to the value | |||
| * | |||
| * The memset() function returns @s. | |||
| * */ | |||
| void * | |||
| memset(void *s, char c, size_t n) { | |||
| #ifdef __HAVE_ARCH_MEMSET | |||
| return __memset(s, c, n); | |||
| #else | |||
| char *p = s; | |||
| while (n -- > 0) { | |||
| *p ++ = c; | |||
| } | |||
| return s; | |||
| #endif /* __HAVE_ARCH_MEMSET */ | |||
| } | |||
| /* * | |||
| * memmove - copies the values of @n bytes from the location pointed by @src to | |||
| * the memory area pointed by @dst. @src and @dst are allowed to overlap. | |||
| * @dst pointer to the destination array where the content is to be copied | |||
| * @src pointer to the source of data to by copied | |||
| * @n: number of bytes to copy | |||
| * | |||
| * The memmove() function returns @dst. | |||
| * */ | |||
| void * | |||
| memmove(void *dst, const void *src, size_t n) { | |||
| #ifdef __HAVE_ARCH_MEMMOVE | |||
| return __memmove(dst, src, n); | |||
| #else | |||
| const char *s = src; | |||
| char *d = dst; | |||
| if (s < d && s + n > d) { | |||
| s += n, d += n; | |||
| while (n -- > 0) { | |||
| *-- d = *-- s; | |||
| } | |||
| } else { | |||
| while (n -- > 0) { | |||
| *d ++ = *s ++; | |||
| } | |||
| } | |||
| return dst; | |||
| #endif /* __HAVE_ARCH_MEMMOVE */ | |||
| } | |||
| /* * | |||
| * memcpy - copies the value of @n bytes from the location pointed by @src to | |||
| * the memory area pointed by @dst. | |||
| * @dst pointer to the destination array where the content is to be copied | |||
| * @src pointer to the source of data to by copied | |||
| * @n: number of bytes to copy | |||
| * | |||
| * The memcpy() returns @dst. | |||
| * | |||
| * Note that, the function does not check any terminating null character in @src, | |||
| * it always copies exactly @n bytes. To avoid overflows, the size of arrays pointed | |||
| * by both @src and @dst, should be at least @n bytes, and should not overlap | |||
| * (for overlapping memory area, memmove is a safer approach). | |||
| * */ | |||
| void * | |||
| memcpy(void *dst, const void *src, size_t n) { | |||
| #ifdef __HAVE_ARCH_MEMCPY | |||
| return __memcpy(dst, src, n); | |||
| #else | |||
| const char *s = src; | |||
| char *d = dst; | |||
| while (n -- > 0) { | |||
| *d ++ = *s ++; | |||
| } | |||
| return dst; | |||
| #endif /* __HAVE_ARCH_MEMCPY */ | |||
| } | |||
| /* * | |||
| * memcmp - compares two blocks of memory | |||
| * @v1: pointer to block of memory | |||
| * @v2: pointer to block of memory | |||
| * @n: number of bytes to compare | |||
| * | |||
| * The memcmp() functions returns an integral value indicating the | |||
| * relationship between the content of the memory blocks: | |||
| * - A zero value indicates that the contents of both memory blocks are equal; | |||
| * - A value greater than zero indicates that the first byte that does not | |||
| * match in both memory blocks has a greater value in @v1 than in @v2 | |||
| * as if evaluated as unsigned char values; | |||
| * - And a value less than zero indicates the opposite. | |||
| * */ | |||
| int | |||
| memcmp(const void *v1, const void *v2, size_t n) { | |||
| const char *s1 = (const char *)v1; | |||
| const char *s2 = (const char *)v2; | |||
| while (n -- > 0) { | |||
| if (*s1 != *s2) { | |||
| return (int)((unsigned char)*s1 - (unsigned char)*s2); | |||
| } | |||
| s1 ++, s2 ++; | |||
| } | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,25 @@ | |||
| #ifndef __LIBS_STRING_H__ | |||
| #define __LIBS_STRING_H__ | |||
| #include <defs.h> | |||
| size_t strlen(const char *s); | |||
| size_t strnlen(const char *s, size_t len); | |||
| char *strcpy(char *dst, const char *src); | |||
| char *strncpy(char *dst, const char *src, size_t len); | |||
| int strcmp(const char *s1, const char *s2); | |||
| int strncmp(const char *s1, const char *s2, size_t n); | |||
| char *strchr(const char *s, char c); | |||
| char *strfind(const char *s, char c); | |||
| long strtol(const char *s, char **endptr, int base); | |||
| void *memset(void *s, char c, size_t n); | |||
| void *memmove(void *dst, const void *src, size_t n); | |||
| void *memcpy(void *dst, const void *src, size_t n); | |||
| int memcmp(const void *v1, const void *v2, size_t n); | |||
| #endif /* !__LIBS_STRING_H__ */ | |||
| @ -0,0 +1,29 @@ | |||
| #ifndef __LIBS_UNISTD_H__ | |||
| #define __LIBS_UNISTD_H__ | |||
| #define T_SYSCALL 0x80 | |||
| /* syscall number */ | |||
| #define SYS_exit 1 | |||
| #define SYS_fork 2 | |||
| #define SYS_wait 3 | |||
| #define SYS_exec 4 | |||
| #define SYS_clone 5 | |||
| #define SYS_yield 10 | |||
| #define SYS_sleep 11 | |||
| #define SYS_kill 12 | |||
| #define SYS_gettime 17 | |||
| #define SYS_getpid 18 | |||
| #define SYS_brk 19 | |||
| #define SYS_mmap 20 | |||
| #define SYS_munmap 21 | |||
| #define SYS_shmem 22 | |||
| #define SYS_putc 30 | |||
| #define SYS_pgdir 31 | |||
| /* SYS_fork flags */ | |||
| #define CLONE_VM 0x00000100 // set if VM shared between processes | |||
| #define CLONE_THREAD 0x00000200 // thread group | |||
| #endif /* !__LIBS_UNISTD_H__ */ | |||
| @ -0,0 +1,302 @@ | |||
| #ifndef __LIBS_X86_H__ | |||
| #define __LIBS_X86_H__ | |||
| #include <defs.h> | |||
| #define do_div(n, base) ({ \ | |||
| unsigned long __upper, __low, __high, __mod, __base; \ | |||
| __base = (base); \ | |||
| asm ("" : "=a" (__low), "=d" (__high) : "A" (n)); \ | |||
| __upper = __high; \ | |||
| if (__high != 0) { \ | |||
| __upper = __high % __base; \ | |||
| __high = __high / __base; \ | |||
| } \ | |||
| asm ("divl %2" : "=a" (__low), "=d" (__mod) \ | |||
| : "rm" (__base), "0" (__low), "1" (__upper)); \ | |||
| asm ("" : "=A" (n) : "a" (__low), "d" (__high)); \ | |||
| __mod; \ | |||
| }) | |||
| #define barrier() __asm__ __volatile__ ("" ::: "memory") | |||
| static inline uint8_t inb(uint16_t port) __attribute__((always_inline)); | |||
| static inline void insl(uint32_t port, void *addr, int cnt) __attribute__((always_inline)); | |||
| static inline void outb(uint16_t port, uint8_t data) __attribute__((always_inline)); | |||
| static inline void outw(uint16_t port, uint16_t data) __attribute__((always_inline)); | |||
| static inline void outsl(uint32_t port, const void *addr, int cnt) __attribute__((always_inline)); | |||
| static inline uint32_t read_ebp(void) __attribute__((always_inline)); | |||
| static inline void breakpoint(void) __attribute__((always_inline)); | |||
| static inline uint32_t read_dr(unsigned regnum) __attribute__((always_inline)); | |||
| static inline void write_dr(unsigned regnum, uint32_t value) __attribute__((always_inline)); | |||
| /* Pseudo-descriptors used for LGDT, LLDT(not used) and LIDT instructions. */ | |||
| struct pseudodesc { | |||
| uint16_t pd_lim; // Limit | |||
| uintptr_t pd_base; // Base address | |||
| } __attribute__ ((packed)); | |||
| static inline void lidt(struct pseudodesc *pd) __attribute__((always_inline)); | |||
| static inline void sti(void) __attribute__((always_inline)); | |||
| static inline void cli(void) __attribute__((always_inline)); | |||
| static inline void ltr(uint16_t sel) __attribute__((always_inline)); | |||
| static inline uint32_t read_eflags(void) __attribute__((always_inline)); | |||
| static inline void write_eflags(uint32_t eflags) __attribute__((always_inline)); | |||
| static inline void lcr0(uintptr_t cr0) __attribute__((always_inline)); | |||
| static inline void lcr3(uintptr_t cr3) __attribute__((always_inline)); | |||
| static inline uintptr_t rcr0(void) __attribute__((always_inline)); | |||
| static inline uintptr_t rcr1(void) __attribute__((always_inline)); | |||
| static inline uintptr_t rcr2(void) __attribute__((always_inline)); | |||
| static inline uintptr_t rcr3(void) __attribute__((always_inline)); | |||
| static inline void invlpg(void *addr) __attribute__((always_inline)); | |||
| static inline uint8_t | |||
| inb(uint16_t port) { | |||
| uint8_t data; | |||
| asm volatile ("inb %1, %0" : "=a" (data) : "d" (port) : "memory"); | |||
| return data; | |||
| } | |||
| static inline void | |||
| insl(uint32_t port, void *addr, int cnt) { | |||
| asm volatile ( | |||
| "cld;" | |||
| "repne; insl;" | |||
| : "=D" (addr), "=c" (cnt) | |||
| : "d" (port), "0" (addr), "1" (cnt) | |||
| : "memory", "cc"); | |||
| } | |||
| static inline void | |||
| outb(uint16_t port, uint8_t data) { | |||
| asm volatile ("outb %0, %1" :: "a" (data), "d" (port) : "memory"); | |||
| } | |||
| static inline void | |||
| outw(uint16_t port, uint16_t data) { | |||
| asm volatile ("outw %0, %1" :: "a" (data), "d" (port) : "memory"); | |||
| } | |||
| static inline void | |||
| outsl(uint32_t port, const void *addr, int cnt) { | |||
| asm volatile ( | |||
| "cld;" | |||
| "repne; outsl;" | |||
| : "=S" (addr), "=c" (cnt) | |||
| : "d" (port), "0" (addr), "1" (cnt) | |||
| : "memory", "cc"); | |||
| } | |||
| static inline uint32_t | |||
| read_ebp(void) { | |||
| uint32_t ebp; | |||
| asm volatile ("movl %%ebp, %0" : "=r" (ebp)); | |||
| return ebp; | |||
| } | |||
| static inline void | |||
| breakpoint(void) { | |||
| asm volatile ("int $3"); | |||
| } | |||
| static inline uint32_t | |||
| read_dr(unsigned regnum) { | |||
| uint32_t value = 0; | |||
| switch (regnum) { | |||
| case 0: asm volatile ("movl %%db0, %0" : "=r" (value)); break; | |||
| case 1: asm volatile ("movl %%db1, %0" : "=r" (value)); break; | |||
| case 2: asm volatile ("movl %%db2, %0" : "=r" (value)); break; | |||
| case 3: asm volatile ("movl %%db3, %0" : "=r" (value)); break; | |||
| case 6: asm volatile ("movl %%db6, %0" : "=r" (value)); break; | |||
| case 7: asm volatile ("movl %%db7, %0" : "=r" (value)); break; | |||
| } | |||
| return value; | |||
| } | |||
| static void | |||
| write_dr(unsigned regnum, uint32_t value) { | |||
| switch (regnum) { | |||
| case 0: asm volatile ("movl %0, %%db0" :: "r" (value)); break; | |||
| case 1: asm volatile ("movl %0, %%db1" :: "r" (value)); break; | |||
| case 2: asm volatile ("movl %0, %%db2" :: "r" (value)); break; | |||
| case 3: asm volatile ("movl %0, %%db3" :: "r" (value)); break; | |||
| case 6: asm volatile ("movl %0, %%db6" :: "r" (value)); break; | |||
| case 7: asm volatile ("movl %0, %%db7" :: "r" (value)); break; | |||
| } | |||
| } | |||
| static inline void | |||
| lidt(struct pseudodesc *pd) { | |||
| asm volatile ("lidt (%0)" :: "r" (pd) : "memory"); | |||
| } | |||
| static inline void | |||
| sti(void) { | |||
| asm volatile ("sti"); | |||
| } | |||
| static inline void | |||
| cli(void) { | |||
| asm volatile ("cli" ::: "memory"); | |||
| } | |||
| static inline void | |||
| ltr(uint16_t sel) { | |||
| asm volatile ("ltr %0" :: "r" (sel) : "memory"); | |||
| } | |||
| static inline uint32_t | |||
| read_eflags(void) { | |||
| uint32_t eflags; | |||
| asm volatile ("pushfl; popl %0" : "=r" (eflags)); | |||
| return eflags; | |||
| } | |||
| static inline void | |||
| write_eflags(uint32_t eflags) { | |||
| asm volatile ("pushl %0; popfl" :: "r" (eflags)); | |||
| } | |||
| static inline void | |||
| lcr0(uintptr_t cr0) { | |||
| asm volatile ("mov %0, %%cr0" :: "r" (cr0) : "memory"); | |||
| } | |||
| static inline void | |||
| lcr3(uintptr_t cr3) { | |||
| asm volatile ("mov %0, %%cr3" :: "r" (cr3) : "memory"); | |||
| } | |||
| static inline uintptr_t | |||
| rcr0(void) { | |||
| uintptr_t cr0; | |||
| asm volatile ("mov %%cr0, %0" : "=r" (cr0) :: "memory"); | |||
| return cr0; | |||
| } | |||
| static inline uintptr_t | |||
| rcr1(void) { | |||
| uintptr_t cr1; | |||
| asm volatile ("mov %%cr1, %0" : "=r" (cr1) :: "memory"); | |||
| return cr1; | |||
| } | |||
| static inline uintptr_t | |||
| rcr2(void) { | |||
| uintptr_t cr2; | |||
| asm volatile ("mov %%cr2, %0" : "=r" (cr2) :: "memory"); | |||
| return cr2; | |||
| } | |||
| static inline uintptr_t | |||
| rcr3(void) { | |||
| uintptr_t cr3; | |||
| asm volatile ("mov %%cr3, %0" : "=r" (cr3) :: "memory"); | |||
| return cr3; | |||
| } | |||
| static inline void | |||
| invlpg(void *addr) { | |||
| asm volatile ("invlpg (%0)" :: "r" (addr) : "memory"); | |||
| } | |||
| static inline int __strcmp(const char *s1, const char *s2) __attribute__((always_inline)); | |||
| static inline char *__strcpy(char *dst, const char *src) __attribute__((always_inline)); | |||
| static inline void *__memset(void *s, char c, size_t n) __attribute__((always_inline)); | |||
| static inline void *__memmove(void *dst, const void *src, size_t n) __attribute__((always_inline)); | |||
| static inline void *__memcpy(void *dst, const void *src, size_t n) __attribute__((always_inline)); | |||
| #ifndef __HAVE_ARCH_STRCMP | |||
| #define __HAVE_ARCH_STRCMP | |||
| static inline int | |||
| __strcmp(const char *s1, const char *s2) { | |||
| int d0, d1, ret; | |||
| asm volatile ( | |||
| "1: lodsb;" | |||
| "scasb;" | |||
| "jne 2f;" | |||
| "testb %%al, %%al;" | |||
| "jne 1b;" | |||
| "xorl %%eax, %%eax;" | |||
| "jmp 3f;" | |||
| "2: sbbl %%eax, %%eax;" | |||
| "orb $1, %%al;" | |||
| "3:" | |||
| : "=a" (ret), "=&S" (d0), "=&D" (d1) | |||
| : "1" (s1), "2" (s2) | |||
| : "memory"); | |||
| return ret; | |||
| } | |||
| #endif /* __HAVE_ARCH_STRCMP */ | |||
| #ifndef __HAVE_ARCH_STRCPY | |||
| #define __HAVE_ARCH_STRCPY | |||
| static inline char * | |||
| __strcpy(char *dst, const char *src) { | |||
| int d0, d1, d2; | |||
| asm volatile ( | |||
| "1: lodsb;" | |||
| "stosb;" | |||
| "testb %%al, %%al;" | |||
| "jne 1b;" | |||
| : "=&S" (d0), "=&D" (d1), "=&a" (d2) | |||
| : "0" (src), "1" (dst) : "memory"); | |||
| return dst; | |||
| } | |||
| #endif /* __HAVE_ARCH_STRCPY */ | |||
| #ifndef __HAVE_ARCH_MEMSET | |||
| #define __HAVE_ARCH_MEMSET | |||
| static inline void * | |||
| __memset(void *s, char c, size_t n) { | |||
| int d0, d1; | |||
| asm volatile ( | |||
| "rep; stosb;" | |||
| : "=&c" (d0), "=&D" (d1) | |||
| : "0" (n), "a" (c), "1" (s) | |||
| : "memory"); | |||
| return s; | |||
| } | |||
| #endif /* __HAVE_ARCH_MEMSET */ | |||
| #ifndef __HAVE_ARCH_MEMMOVE | |||
| #define __HAVE_ARCH_MEMMOVE | |||
| static inline void * | |||
| __memmove(void *dst, const void *src, size_t n) { | |||
| if (dst < src) { | |||
| return __memcpy(dst, src, n); | |||
| } | |||
| int d0, d1, d2; | |||
| asm volatile ( | |||
| "std;" | |||
| "rep; movsb;" | |||
| "cld;" | |||
| : "=&c" (d0), "=&S" (d1), "=&D" (d2) | |||
| : "0" (n), "1" (n - 1 + src), "2" (n - 1 + dst) | |||
| : "memory"); | |||
| return dst; | |||
| } | |||
| #endif /* __HAVE_ARCH_MEMMOVE */ | |||
| #ifndef __HAVE_ARCH_MEMCPY | |||
| #define __HAVE_ARCH_MEMCPY | |||
| static inline void * | |||
| __memcpy(void *dst, const void *src, size_t n) { | |||
| int d0, d1, d2; | |||
| asm volatile ( | |||
| "rep; movsl;" | |||
| "movl %4, %%ecx;" | |||
| "andl $3, %%ecx;" | |||
| "jz 1f;" | |||
| "rep; movsb;" | |||
| "1:" | |||
| : "=&c" (d0), "=&D" (d1), "=&S" (d2) | |||
| : "0" (n / 4), "g" (n), "1" (dst), "2" (src) | |||
| : "memory"); | |||
| return dst; | |||
| } | |||
| #endif /* __HAVE_ARCH_MEMCPY */ | |||
| #endif /* !__LIBS_X86_H__ */ | |||
| @ -0,0 +1,15 @@ | |||
| OUTPUT_FORMAT("elf32-i386") | |||
| OUTPUT_ARCH(i386) | |||
| SECTIONS { | |||
| . = 0x7C00; | |||
| .startup : { | |||
| *bootasm.o(.text) | |||
| } | |||
| .text : { *(.text) } | |||
| .data : { *(.data .rodata) } | |||
| /DISCARD/ : { *(.eh_*) } | |||
| } | |||
| @ -0,0 +1,95 @@ | |||
| OBJPREFIX := __objs_ | |||
| .SECONDEXPANSION: | |||
| # -------------------- function begin -------------------- | |||
| # list all files in some directories: (#directories, #types) | |||
| listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\ | |||
| $(wildcard $(addsuffix $(SLASH)*,$(1)))) | |||
| # get .o obj files: (#files[, packet]) | |||
| toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\ | |||
| $(addsuffix .o,$(basename $(1)))) | |||
| # get .d dependency files: (#files[, packet]) | |||
| todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2))) | |||
| totarget = $(addprefix $(BINDIR)$(SLASH),$(1)) | |||
| # change $(name) to $(OBJPREFIX)$(name): (#names) | |||
| packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX)) | |||
| # cc compile template, generate rule for dep, obj: (file, cc[, flags, dir]) | |||
| define cc_template | |||
| $$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@) | |||
| @$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@ | |||
| $$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@) | |||
| @echo + cc $$< | |||
| $(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@ | |||
| ALLOBJS += $$(call toobj,$(1),$(4)) | |||
| endef | |||
| # compile file: (#files, cc[, flags, dir]) | |||
| define do_cc_compile | |||
| $$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4)))) | |||
| endef | |||
| # add files to packet: (#files, cc[, flags, packet, dir]) | |||
| define do_add_files_to_packet | |||
| __temp_packet__ := $(call packetname,$(4)) | |||
| ifeq ($$(origin $$(__temp_packet__)),undefined) | |||
| $$(__temp_packet__) := | |||
| endif | |||
| __temp_objs__ := $(call toobj,$(1),$(5)) | |||
| $$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5)))) | |||
| $$(__temp_packet__) += $$(__temp_objs__) | |||
| endef | |||
| # add objs to packet: (#objs, packet) | |||
| define do_add_objs_to_packet | |||
| __temp_packet__ := $(call packetname,$(2)) | |||
| ifeq ($$(origin $$(__temp_packet__)),undefined) | |||
| $$(__temp_packet__) := | |||
| endif | |||
| $$(__temp_packet__) += $(1) | |||
| endef | |||
| # add packets and objs to target (target, #packes, #objs[, cc, flags]) | |||
| define do_create_target | |||
| __temp_target__ = $(call totarget,$(1)) | |||
| __temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3) | |||
| TARGETS += $$(__temp_target__) | |||
| ifneq ($(4),) | |||
| $$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) | |||
| $(V)$(4) $(5) $$^ -o $$@ | |||
| else | |||
| $$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) | |||
| endif | |||
| endef | |||
| # finish all | |||
| define do_finish_all | |||
| ALLDEPS = $$(ALLOBJS:.o=.d) | |||
| $$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)): | |||
| @$(MKDIR) $$@ | |||
| endef | |||
| # -------------------- function end -------------------- | |||
| # compile file: (#files, cc[, flags, dir]) | |||
| cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4))) | |||
| # add files to packet: (#files, cc[, flags, packet, dir]) | |||
| add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5))) | |||
| # add objs to packet: (#objs, packet) | |||
| add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2))) | |||
| # add packets and objs to target (target, #packes, #objs, cc, [, flags]) | |||
| create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5))) | |||
| read_packet = $(foreach p,$(call packetname,$(1)),$($(p))) | |||
| add_dependency = $(eval $(1): $(2)) | |||
| finish_all = $(eval $(call do_finish_all)) | |||
| @ -0,0 +1,3 @@ | |||
| file bin/kernel | |||
| target remote :1234 | |||
| break kern_init | |||
| @ -0,0 +1,556 @@ | |||
| #!/bin/sh | |||
| verbose=false | |||
| if [ "x$1" = "x-v" ]; then | |||
| verbose=true | |||
| out=/dev/stdout | |||
| err=/dev/stderr | |||
| else | |||
| out=/dev/null | |||
| err=/dev/null | |||
| fi | |||
| ## make & makeopts | |||
| if gmake --version > /dev/null 2>&1; then | |||
| make=gmake; | |||
| else | |||
| make=make; | |||
| fi | |||
| makeopts="--quiet --no-print-directory -j" | |||
| make_print() { | |||
| echo `$make $makeopts print-$1` | |||
| } | |||
| ## command tools | |||
| awk='awk' | |||
| bc='bc' | |||
| date='date' | |||
| grep='grep' | |||
| rm='rm -f' | |||
| sed='sed' | |||
| ## symbol table | |||
| sym_table='obj/kernel.sym' | |||
| ## gdb & gdbopts | |||
| gdb="$(make_print GDB)" | |||
| gdbport='1234' | |||
| gdb_in="$(make_print GRADE_GDB_IN)" | |||
| ## qemu & qemuopts | |||
| qemu="$(make_print qemu)" | |||
| qemu_out="$(make_print GRADE_QEMU_OUT)" | |||
| if $qemu -nographic -help | grep -q '^-gdb'; then | |||
| qemugdb="-gdb tcp::$gdbport" | |||
| else | |||
| qemugdb="-s -p $gdbport" | |||
| fi | |||
| ## default variables | |||
| default_timeout=30 | |||
| default_pts=5 | |||
| pts=5 | |||
| part=0 | |||
| part_pos=0 | |||
| total=0 | |||
| total_pos=0 | |||
| ## default functions | |||
| update_score() { | |||
| total=`expr $total + $part` | |||
| total_pos=`expr $total_pos + $part_pos` | |||
| part=0 | |||
| part_pos=0 | |||
| } | |||
| get_time() { | |||
| echo `$date +%s.%N 2> /dev/null` | |||
| } | |||
| show_part() { | |||
| echo "Part $1 Score: $part/$part_pos" | |||
| echo | |||
| update_score | |||
| } | |||
| show_final() { | |||
| update_score | |||
| echo "Total Score: $total/$total_pos" | |||
| if [ $total -lt $total_pos ]; then | |||
| exit 1 | |||
| fi | |||
| } | |||
| show_time() { | |||
| t1=$(get_time) | |||
| time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null` | |||
| echo "(${time}s)" | |||
| } | |||
| show_build_tag() { | |||
| echo "$1:" | $awk '{printf "%-24s ", $0}' | |||
| } | |||
| show_check_tag() { | |||
| echo "$1:" | $awk '{printf " -%-40s ", $0}' | |||
| } | |||
| show_msg() { | |||
| echo $1 | |||
| shift | |||
| if [ $# -gt 0 ]; then | |||
| echo "$@" | awk '{printf " %s\n", $0}' | |||
| echo | |||
| fi | |||
| } | |||
| pass() { | |||
| show_msg OK "$@" | |||
| part=`expr $part + $pts` | |||
| part_pos=`expr $part_pos + $pts` | |||
| } | |||
| fail() { | |||
| show_msg WRONG "$@" | |||
| part_pos=`expr $part_pos + $pts` | |||
| } | |||
| run_qemu() { | |||
| # Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty, | |||
| # wait until $brkfun is reached or $timeout expires, then kill QEMU | |||
| qemuextra= | |||
| if [ "$brkfun" ]; then | |||
| qemuextra="-S $qemugdb" | |||
| fi | |||
| if [ -z "$timeout" ] || [ $timeout -le 0 ]; then | |||
| timeout=$default_timeout; | |||
| fi | |||
| t0=$(get_time) | |||
| ( | |||
| ulimit -t $timeout | |||
| exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra | |||
| ) > $out 2> $err & | |||
| pid=$! | |||
| # wait for QEMU to start | |||
| sleep 1 | |||
| if [ -n "$brkfun" ]; then | |||
| # find the address of the kernel $brkfun function | |||
| brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'` | |||
| ( | |||
| echo "target remote localhost:$gdbport" | |||
| echo "break *0x$brkaddr" | |||
| echo "continue" | |||
| ) > $gdb_in | |||
| $gdb -batch -nx -x $gdb_in > /dev/null 2>&1 | |||
| # make sure that QEMU is dead | |||
| # on OS X, exiting gdb doesn't always exit qemu | |||
| kill $pid > /dev/null 2>&1 | |||
| fi | |||
| } | |||
| build_run() { | |||
| # usage: build_run <tag> <args> | |||
| show_build_tag "$1" | |||
| shift | |||
| if $verbose; then | |||
| echo "$make $@ ..." | |||
| fi | |||
| $make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err | |||
| if [ $? -ne 0 ]; then | |||
| echo $make $@ failed | |||
| exit 1 | |||
| fi | |||
| # now run qemu and save the output | |||
| run_qemu | |||
| show_time | |||
| } | |||
| check_result() { | |||
| # usage: check_result <tag> <check> <check args...> | |||
| show_check_tag "$1" | |||
| shift | |||
| # give qemu some time to run (for asynchronous mode) | |||
| if [ ! -s $qemu_out ]; then | |||
| sleep 4 | |||
| fi | |||
| if [ ! -s $qemu_out ]; then | |||
| fail > /dev/null | |||
| echo 'no $qemu_out' | |||
| else | |||
| check=$1 | |||
| shift | |||
| $check "$@" | |||
| fi | |||
| } | |||
| check_regexps() { | |||
| okay=yes | |||
| not=0 | |||
| reg=0 | |||
| error= | |||
| for i do | |||
| if [ "x$i" = "x!" ]; then | |||
| not=1 | |||
| elif [ "x$i" = "x-" ]; then | |||
| reg=1 | |||
| else | |||
| if [ $reg -ne 0 ]; then | |||
| $grep '-E' "^$i\$" $qemu_out > /dev/null | |||
| else | |||
| $grep '-F' "$i" $qemu_out > /dev/null | |||
| fi | |||
| found=$(($? == 0)) | |||
| if [ $found -eq $not ]; then | |||
| if [ $found -eq 0 ]; then | |||
| msg="!! error: missing '$i'" | |||
| else | |||
| msg="!! error: got unexpected line '$i'" | |||
| fi | |||
| okay=no | |||
| if [ -z "$error" ]; then | |||
| error="$msg" | |||
| else | |||
| error="$error\n$msg" | |||
| fi | |||
| fi | |||
| not=0 | |||
| reg=0 | |||
| fi | |||
| done | |||
| if [ "$okay" = "yes" ]; then | |||
| pass | |||
| else | |||
| fail "$error" | |||
| if $verbose; then | |||
| exit 1 | |||
| fi | |||
| fi | |||
| } | |||
| run_test() { | |||
| # usage: run_test [-tag <tag>] [-prog <prog>] [-Ddef...] [-check <check>] checkargs ... | |||
| tag= | |||
| prog= | |||
| check=check_regexps | |||
| while true; do | |||
| select= | |||
| case $1 in | |||
| -tag|-prog) | |||
| select=`expr substr $1 2 ${#1}` | |||
| eval $select='$2' | |||
| ;; | |||
| esac | |||
| if [ -z "$select" ]; then | |||
| break | |||
| fi | |||
| shift | |||
| shift | |||
| done | |||
| defs= | |||
| while expr "x$1" : "x-D.*" > /dev/null; do | |||
| defs="DEFS+='$1' $defs" | |||
| shift | |||
| done | |||
| if [ "x$1" = "x-check" ]; then | |||
| check=$2 | |||
| shift | |||
| shift | |||
| fi | |||
| if [ -z "$prog" ]; then | |||
| $make $makeopts touch > /dev/null 2>&1 | |||
| args="$defs" | |||
| else | |||
| if [ -z "$tag" ]; then | |||
| tag="$prog" | |||
| fi | |||
| args="build-$prog $defs" | |||
| fi | |||
| build_run "$tag" "$args" | |||
| check_result 'check result' "$check" "$@" | |||
| } | |||
| quick_run() { | |||
| # usage: quick_run <tag> [-Ddef...] | |||
| tag="$1" | |||
| shift | |||
| defs= | |||
| while expr "x$1" : "x-D.*" > /dev/null; do | |||
| defs="DEFS+='$1' $defs" | |||
| shift | |||
| done | |||
| $make $makeopts touch > /dev/null 2>&1 | |||
| build_run "$tag" "$defs" | |||
| } | |||
| quick_check() { | |||
| # usage: quick_check <tag> checkargs ... | |||
| tag="$1" | |||
| shift | |||
| check_result "$tag" check_regexps "$@" | |||
| } | |||
| ## kernel image | |||
| osimg=$(make_print ucoreimg) | |||
| ## swap image | |||
| swapimg=$(make_print swapimg) | |||
| ## set default qemu-options | |||
| qemuopts="-hda $osimg -drive file=$swapimg,media=disk,cache=writeback" | |||
| ## set break-function, default is readline | |||
| brkfun=readline | |||
| default_check() { | |||
| pts=7 | |||
| check_regexps "$@" | |||
| pts=3 | |||
| quick_check 'check output' \ | |||
| 'memory management: default_pmm_manager' \ | |||
| 'check_alloc_page() succeeded!' \ | |||
| 'check_pgdir() succeeded!' \ | |||
| 'check_boot_pgdir() succeeded!' \ | |||
| 'PDE(0e0) c0000000-f8000000 38000000 urw' \ | |||
| ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \ | |||
| 'PDE(001) fac00000-fb000000 00400000 -rw' \ | |||
| ' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \ | |||
| ' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' \ | |||
| 'check_slab() succeeded!' \ | |||
| 'check_vma_struct() succeeded!' \ | |||
| 'page fault at 0x00000100: K/W [no page found].' \ | |||
| 'check_pgfault() succeeded!' \ | |||
| 'check_vmm() succeeded.' \ | |||
| 'page fault at 0x00001000: K/W [no page found].' \ | |||
| 'page fault at 0x00002000: K/W [no page found].' \ | |||
| 'page fault at 0x00003000: K/W [no page found].' \ | |||
| 'page fault at 0x00004000: K/W [no page found].' \ | |||
| 'write Virt Page e in fifo_check_swap' \ | |||
| 'page fault at 0x00005000: K/W [no page found].' \ | |||
| 'page fault at 0x00001000: K/W [no page found]' \ | |||
| 'page fault at 0x00002000: K/W [no page found].' \ | |||
| 'page fault at 0x00003000: K/W [no page found].' \ | |||
| 'page fault at 0x00004000: K/W [no page found].' \ | |||
| 'check_swap() succeeded!' \ | |||
| '++ setup timer interrupts' | |||
| } | |||
| ## check now!! | |||
| run_test -prog 'badsegment' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "badsegment".' \ | |||
| - 'trapframe at 0xc.......' \ | |||
| 'trap 0x0000000d General Protection' \ | |||
| ' err 0x00000028' \ | |||
| - ' eip 0x008.....' \ | |||
| - ' esp 0xaff.....' \ | |||
| ' cs 0x----001b' \ | |||
| ' ss 0x----0023' \ | |||
| ! - 'user panic at .*' | |||
| run_test -prog 'divzero' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "divzero".' \ | |||
| - 'trapframe at 0xc.......' \ | |||
| 'trap 0x00000000 Divide error' \ | |||
| - ' eip 0x008.....' \ | |||
| - ' esp 0xaff.....' \ | |||
| ' cs 0x----001b' \ | |||
| ' ss 0x----0023' \ | |||
| ! - 'user panic at .*' | |||
| run_test -prog 'softint' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "softint".' \ | |||
| - 'trapframe at 0xc.......' \ | |||
| 'trap 0x0000000d General Protection' \ | |||
| ' err 0x00000072' \ | |||
| - ' eip 0x008.....' \ | |||
| - ' esp 0xaff.....' \ | |||
| ' cs 0x----001b' \ | |||
| ' ss 0x----0023' \ | |||
| ! - 'user panic at .*' | |||
| pts=10 | |||
| run_test -prog 'faultread' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "faultread".' \ | |||
| - 'trapframe at 0xc.......' \ | |||
| 'trap 0x0000000e Page Fault' \ | |||
| ' err 0x00000004' \ | |||
| - ' eip 0x008.....' \ | |||
| ! - 'user panic at .*' | |||
| run_test -prog 'faultreadkernel' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "faultreadkernel".' \ | |||
| - 'trapframe at 0xc.......' \ | |||
| 'trap 0x0000000e Page Fault' \ | |||
| ' err 0x00000005' \ | |||
| - ' eip 0x008.....' \ | |||
| ! - 'user panic at .*' | |||
| run_test -prog 'hello' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "hello".' \ | |||
| 'Hello world!!.' \ | |||
| 'I am process 2.' \ | |||
| 'hello pass.' | |||
| run_test -prog 'testbss' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "testbss".' \ | |||
| 'Making sure bss works right...' \ | |||
| 'Yes, good. Now doing a wild write off the end...' \ | |||
| 'testbss may pass.' \ | |||
| - 'trapframe at 0xc.......' \ | |||
| 'trap 0x0000000e Page Fault' \ | |||
| ' err 0x00000006' \ | |||
| - ' eip 0x008.....' \ | |||
| 'killed by kernel.' \ | |||
| ! - 'user panic at .*' | |||
| run_test -prog 'pgdir' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "pgdir".' \ | |||
| 'I am 2, print pgdir.' \ | |||
| 'PDE(001) 00800000-00c00000 00400000 urw' \ | |||
| ' |-- PTE(00002) 00800000-00802000 00002000 ur-' \ | |||
| ' |-- PTE(00001) 00802000-00803000 00001000 urw' \ | |||
| 'PDE(001) afc00000-b0000000 00400000 urw' \ | |||
| ' |-- PTE(00004) afffc000-b0000000 00004000 urw' \ | |||
| 'PDE(0e0) c0000000-f8000000 38000000 urw' \ | |||
| ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \ | |||
| 'pgdir pass.' | |||
| run_test -prog 'yield' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "yield".' \ | |||
| 'Hello, I am process 2.' \ | |||
| 'Back in process 2, iteration 0.' \ | |||
| 'Back in process 2, iteration 1.' \ | |||
| 'Back in process 2, iteration 2.' \ | |||
| 'Back in process 2, iteration 3.' \ | |||
| 'Back in process 2, iteration 4.' \ | |||
| 'All done in process 2.' \ | |||
| 'yield pass.' | |||
| run_test -prog 'badarg' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "badarg".' \ | |||
| 'fork ok.' \ | |||
| 'badarg pass.' \ | |||
| 'all user-mode processes have quit.' \ | |||
| 'init check memory pass.' \ | |||
| ! - 'user panic at .*' | |||
| pts=10 | |||
| run_test -prog 'exit' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "exit".' \ | |||
| 'I am the parent. Forking the child...' \ | |||
| 'I am the parent, waiting now..' \ | |||
| 'I am the child.' \ | |||
| - 'waitpid [0-9]+ ok\.' \ | |||
| 'exit pass.' \ | |||
| 'all user-mode processes have quit.' \ | |||
| 'init check memory pass.' \ | |||
| ! - 'user panic at .*' | |||
| run_test -prog 'spin' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "spin".' \ | |||
| 'I am the parent. Forking the child...' \ | |||
| 'I am the parent. Running the child...' \ | |||
| 'I am the child. spinning ...' \ | |||
| 'I am the parent. Killing the child...' \ | |||
| 'kill returns 0' \ | |||
| 'wait returns 0' \ | |||
| 'spin may pass.' \ | |||
| 'all user-mode processes have quit.' \ | |||
| 'init check memory pass.' \ | |||
| ! - 'user panic at .*' | |||
| run_test -prog 'waitkill' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "waitkill".' \ | |||
| 'wait child 1.' \ | |||
| 'child 2.' \ | |||
| 'child 1.' \ | |||
| 'kill parent ok.' \ | |||
| 'kill child1 ok.' \ | |||
| 'all user-mode processes have quit.' \ | |||
| 'init check memory pass.' \ | |||
| ! - 'user panic at .*' | |||
| pts=15 | |||
| run_test -prog 'forktest' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "forktest".' \ | |||
| 'I am child 31' \ | |||
| 'I am child 19' \ | |||
| 'I am child 13' \ | |||
| 'I am child 0' \ | |||
| 'forktest pass.' \ | |||
| 'all user-mode processes have quit.' \ | |||
| 'init check memory pass.' \ | |||
| ! - 'fork claimed to work [0-9]+ times!' \ | |||
| ! 'wait stopped early' \ | |||
| ! 'wait got too many' \ | |||
| ! - 'user panic at .*' | |||
| pts=10 | |||
| run_test -prog 'forktree' -check default_check \ | |||
| 'kernel_execve: pid = 2, name = "forktree".' \ | |||
| - '....: I am '\'''\' \ | |||
| - '....: I am '\''0'\' \ | |||
| - '....: I am '\'''\' \ | |||
| - '....: I am '\''1'\' \ | |||
| - '....: I am '\''0'\' \ | |||
| - '....: I am '\''01'\' \ | |||
| - '....: I am '\''00'\' \ | |||
| - '....: I am '\''11'\' \ | |||
| - '....: I am '\''10'\' \ | |||
| - '....: I am '\''101'\' \ | |||
| - '....: I am '\''100'\' \ | |||
| - '....: I am '\''111'\' \ | |||
| - '....: I am '\''110'\' \ | |||
| - '....: I am '\''001'\' \ | |||
| - '....: I am '\''000'\' \ | |||
| - '....: I am '\''011'\' \ | |||
| - '....: I am '\''010'\' \ | |||
| - '....: I am '\''0101'\' \ | |||
| - '....: I am '\''0100'\' \ | |||
| - '....: I am '\''0111'\' \ | |||
| - '....: I am '\''0110'\' \ | |||
| - '....: I am '\''0001'\' \ | |||
| - '....: I am '\''0000'\' \ | |||
| - '....: I am '\''0011'\' \ | |||
| - '....: I am '\''0010'\' \ | |||
| - '....: I am '\''1101'\' \ | |||
| - '....: I am '\''1100'\' \ | |||
| - '....: I am '\''1111'\' \ | |||
| - '....: I am '\''1110'\' \ | |||
| - '....: I am '\''1001'\' \ | |||
| - '....: I am '\''1000'\' \ | |||
| - '....: I am '\''1011'\' \ | |||
| - '....: I am '\''1010'\' \ | |||
| 'all user-mode processes have quit.' \ | |||
| 'init check memory pass.' | |||
| ## print final-score | |||
| show_final | |||
| @ -0,0 +1,58 @@ | |||
| /* Simple linker script for the ucore kernel. | |||
| See the GNU ld 'info' manual ("info ld") to learn the syntax. */ | |||
| OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") | |||
| OUTPUT_ARCH(i386) | |||
| ENTRY(kern_entry) | |||
| SECTIONS { | |||
| /* Load the kernel at this address: "." means the current address */ | |||
| . = 0xC0100000; | |||
| .text : { | |||
| *(.text .stub .text.* .gnu.linkonce.t.*) | |||
| } | |||
| PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ | |||
| .rodata : { | |||
| *(.rodata .rodata.* .gnu.linkonce.r.*) | |||
| } | |||
| /* Include debugging information in kernel memory */ | |||
| .stab : { | |||
| PROVIDE(__STAB_BEGIN__ = .); | |||
| *(.stab); | |||
| PROVIDE(__STAB_END__ = .); | |||
| BYTE(0) /* Force the linker to allocate space | |||
| for this section */ | |||
| } | |||
| .stabstr : { | |||
| PROVIDE(__STABSTR_BEGIN__ = .); | |||
| *(.stabstr); | |||
| PROVIDE(__STABSTR_END__ = .); | |||
| BYTE(0) /* Force the linker to allocate space | |||
| for this section */ | |||
| } | |||
| /* Adjust the address for the data segment to the next page */ | |||
| . = ALIGN(0x1000); | |||
| /* The data segment */ | |||
| .data : { | |||
| *(.data) | |||
| } | |||
| PROVIDE(edata = .); | |||
| .bss : { | |||
| *(.bss) | |||
| } | |||
| PROVIDE(end = .); | |||
| /DISCARD/ : { | |||
| *(.eh_frame .note.GNU-stack) | |||
| } | |||
| } | |||
| @ -0,0 +1,43 @@ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <string.h> | |||
| #include <sys/stat.h> | |||
| int | |||
| main(int argc, char *argv[]) { | |||
| struct stat st; | |||
| if (argc != 3) { | |||
| fprintf(stderr, "Usage: <input filename> <output filename>\n"); | |||
| return -1; | |||
| } | |||
| if (stat(argv[1], &st) != 0) { | |||
| fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno)); | |||
| return -1; | |||
| } | |||
| printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size); | |||
| if (st.st_size > 510) { | |||
| fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size); | |||
| return -1; | |||
| } | |||
| char buf[512]; | |||
| memset(buf, 0, sizeof(buf)); | |||
| FILE *ifp = fopen(argv[1], "rb"); | |||
| int size = fread(buf, 1, st.st_size, ifp); | |||
| if (size != st.st_size) { | |||
| fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size); | |||
| return -1; | |||
| } | |||
| fclose(ifp); | |||
| buf[510] = 0x55; | |||
| buf[511] = 0xAA; | |||
| FILE *ofp = fopen(argv[2], "wb+"); | |||
| size = fwrite(buf, 1, 512, ofp); | |||
| if (size != 512) { | |||
| fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size); | |||
| return -1; | |||
| } | |||
| fclose(ofp); | |||
| printf("build 512 bytes boot sector: '%s' success!\n", argv[2]); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,71 @@ | |||
| /* Simple linker script for ucore user-level programs. | |||
| See the GNU ld 'info' manual ("info ld") to learn the syntax. */ | |||
| OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") | |||
| OUTPUT_ARCH(i386) | |||
| ENTRY(_start) | |||
| SECTIONS { | |||
| /* Load programs at this address: "." means the current address */ | |||
| . = 0x800020; | |||
| .text : { | |||
| *(.text .stub .text.* .gnu.linkonce.t.*) | |||
| } | |||
| PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ | |||
| .rodata : { | |||
| *(.rodata .rodata.* .gnu.linkonce.r.*) | |||
| } | |||
| /* Adjust the address for the data segment to the next page */ | |||
| . = ALIGN(0x1000); | |||
| .data : { | |||
| *(.data) | |||
| } | |||
| PROVIDE(edata = .); | |||
| .bss : { | |||
| *(.bss) | |||
| } | |||
| PROVIDE(end = .); | |||
| /* Place debugging symbols so that they can be found by | |||
| * the kernel debugger. | |||
| * Specifically, the four words at 0x200000 mark the beginning of | |||
| * the stabs, the end of the stabs, the beginning of the stabs | |||
| * string table, and the end of the stabs string table, respectively. | |||
| */ | |||
| .stab_info 0x200000 : { | |||
| LONG(__STAB_BEGIN__); | |||
| LONG(__STAB_END__); | |||
| LONG(__STABSTR_BEGIN__); | |||
| LONG(__STABSTR_END__); | |||
| } | |||
| .stab : { | |||
| __STAB_BEGIN__ = DEFINED(__STAB_BEGIN__) ? __STAB_BEGIN__ : .; | |||
| *(.stab); | |||
| __STAB_END__ = DEFINED(__STAB_END__) ? __STAB_END__ : .; | |||
| BYTE(0) /* Force the linker to allocate space | |||
| for this section */ | |||
| } | |||
| .stabstr : { | |||
| __STABSTR_BEGIN__ = DEFINED(__STABSTR_BEGIN__) ? __STABSTR_BEGIN__ : .; | |||
| *(.stabstr); | |||
| __STABSTR_END__ = DEFINED(__STABSTR_END__) ? __STABSTR_END__ : .; | |||
| BYTE(0) /* Force the linker to allocate space | |||
| for this section */ | |||
| } | |||
| /DISCARD/ : { | |||
| *(.eh_frame .note.GNU-stack .comment) | |||
| } | |||
| } | |||
| @ -0,0 +1,29 @@ | |||
| #include <stdio.h> | |||
| int | |||
| main(void) { | |||
| printf("# handler\n"); | |||
| printf(".text\n"); | |||
| printf(".globl __alltraps\n"); | |||
| int i; | |||
| for (i = 0; i < 256; i ++) { | |||
| printf(".globl vector%d\n", i); | |||
| printf("vector%d:\n", i); | |||
| if ((i < 8 || i > 14) && i != 17) { | |||
| printf(" pushl $0\n"); | |||
| } | |||
| printf(" pushl $%d\n", i); | |||
| printf(" jmp __alltraps\n"); | |||
| } | |||
| printf("\n"); | |||
| printf("# vector table\n"); | |||
| printf(".data\n"); | |||
| printf(".globl __vectors\n"); | |||
| printf("__vectors:\n"); | |||
| for (i = 0; i < 256; i ++) { | |||
| printf(" .long vector%d\n", i); | |||
| } | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,22 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int | |||
| main(void) { | |||
| int pid, exit_code; | |||
| if ((pid = fork()) == 0) { | |||
| cprintf("fork ok.\n"); | |||
| int i; | |||
| for (i = 0; i < 10; i ++) { | |||
| yield(); | |||
| } | |||
| exit(0xbeaf); | |||
| } | |||
| assert(pid > 0); | |||
| assert(waitpid(-1, NULL) != 0); | |||
| assert(waitpid(pid, (void *)0xC0000000) != 0); | |||
| assert(waitpid(pid, &exit_code) == 0 && exit_code == 0xbeaf); | |||
| cprintf("badarg pass.\n"); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| /* try to load the kernel's TSS selector into the DS register */ | |||
| int | |||
| main(void) { | |||
| asm volatile("movw $0x28,%ax; movw %ax,%ds"); | |||
| panic("FAIL: T.T\n"); | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int zero; | |||
| int | |||
| main(void) { | |||
| cprintf("value is %d.\n", 1 / zero); | |||
| panic("FAIL: T.T\n"); | |||
| } | |||
| @ -0,0 +1,34 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int magic = -0x10384; | |||
| int | |||
| main(void) { | |||
| int pid, code; | |||
| cprintf("I am the parent. Forking the child...\n"); | |||
| if ((pid = fork()) == 0) { | |||
| cprintf("I am the child.\n"); | |||
| yield(); | |||
| yield(); | |||
| yield(); | |||
| yield(); | |||
| yield(); | |||
| yield(); | |||
| yield(); | |||
| exit(magic); | |||
| } | |||
| else { | |||
| cprintf("I am parent, fork a child pid %d\n",pid); | |||
| } | |||
| assert(pid > 0); | |||
| cprintf("I am the parent, waiting now..\n"); | |||
| assert(waitpid(pid, &code) == 0 && code == magic); | |||
| assert(waitpid(pid, &code) != 0 && wait() != 0); | |||
| cprintf("waitpid %d ok.\n", pid); | |||
| cprintf("exit pass.\n"); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,9 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int | |||
| main(void) { | |||
| cprintf("I read %8x from 0.\n", *(unsigned int *)0); | |||
| panic("FAIL: T.T\n"); | |||
| } | |||
| @ -0,0 +1,9 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int | |||
| main(void) { | |||
| cprintf("I read %08x from 0xfac00000!\n", *(unsigned *)0xfac00000); | |||
| panic("FAIL: T.T\n"); | |||
| } | |||
| @ -0,0 +1,34 @@ | |||
| #include <ulib.h> | |||
| #include <stdio.h> | |||
| const int max_child = 32; | |||
| int | |||
| main(void) { | |||
| int n, pid; | |||
| for (n = 0; n < max_child; n ++) { | |||
| if ((pid = fork()) == 0) { | |||
| cprintf("I am child %d\n", n); | |||
| exit(0); | |||
| } | |||
| assert(pid > 0); | |||
| } | |||
| if (n > max_child) { | |||
| panic("fork claimed to work %d times!\n", n); | |||
| } | |||
| for (; n > 0; n --) { | |||
| if (wait() != 0) { | |||
| panic("wait stopped early\n"); | |||
| } | |||
| } | |||
| if (wait() == 0) { | |||
| panic("wait got too many\n"); | |||
| } | |||
| cprintf("forktest pass.\n"); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,37 @@ | |||
| #include <ulib.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #define DEPTH 4 | |||
| void forktree(const char *cur); | |||
| void | |||
| forkchild(const char *cur, char branch) { | |||
| char nxt[DEPTH + 1]; | |||
| if (strlen(cur) >= DEPTH) | |||
| return; | |||
| snprintf(nxt, DEPTH + 1, "%s%c", cur, branch); | |||
| if (fork() == 0) { | |||
| forktree(nxt); | |||
| yield(); | |||
| exit(0); | |||
| } | |||
| } | |||
| void | |||
| forktree(const char *cur) { | |||
| cprintf("%04x: I am '%s'\n", getpid(), cur); | |||
| forkchild(cur, '0'); | |||
| forkchild(cur, '1'); | |||
| } | |||
| int | |||
| main(void) { | |||
| forktree(""); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int | |||
| main(void) { | |||
| cprintf("Hello world!!.\n"); | |||
| cprintf("I am process %d.\n", getpid()); | |||
| cprintf("hello pass.\n"); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| .text | |||
| .globl _start | |||
| _start: | |||
| # set ebp for backtrace | |||
| movl $0x0, %ebp | |||
| # move down the esp register | |||
| # since it may cause page fault in backtrace | |||
| subl $0x20, %esp | |||
| # call user-program function | |||
| call umain | |||
| 1: jmp 1b | |||
| @ -0,0 +1,28 @@ | |||
| #include <defs.h> | |||
| #include <stdarg.h> | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| #include <error.h> | |||
| void | |||
| __panic(const char *file, int line, const char *fmt, ...) { | |||
| // print the 'message' | |||
| va_list ap; | |||
| va_start(ap, fmt); | |||
| cprintf("user panic at %s:%d:\n ", file, line); | |||
| vcprintf(fmt, ap); | |||
| cprintf("\n"); | |||
| va_end(ap); | |||
| exit(-E_PANIC); | |||
| } | |||
| void | |||
| __warn(const char *file, int line, const char *fmt, ...) { | |||
| va_list ap; | |||
| va_start(ap, fmt); | |||
| cprintf("user warning at %s:%d:\n ", file, line); | |||
| vcprintf(fmt, ap); | |||
| cprintf("\n"); | |||
| va_end(ap); | |||
| } | |||
| @ -0,0 +1,62 @@ | |||
| #include <defs.h> | |||
| #include <stdio.h> | |||
| #include <syscall.h> | |||
| /* * | |||
| * cputch - writes a single character @c to stdout, and it will | |||
| * increace the value of counter pointed by @cnt. | |||
| * */ | |||
| static void | |||
| cputch(int c, int *cnt) { | |||
| sys_putc(c); | |||
| (*cnt) ++; | |||
| } | |||
| /* * | |||
| * vcprintf - format a string and writes it to stdout | |||
| * | |||
| * The return value is the number of characters which would be | |||
| * written to stdout. | |||
| * | |||
| * Call this function if you are already dealing with a va_list. | |||
| * Or you probably want cprintf() instead. | |||
| * */ | |||
| int | |||
| vcprintf(const char *fmt, va_list ap) { | |||
| int cnt = 0; | |||
| vprintfmt((void*)cputch, &cnt, fmt, ap); | |||
| return cnt; | |||
| } | |||
| /* * | |||
| * cprintf - formats a string and writes it to stdout | |||
| * | |||
| * The return value is the number of characters which would be | |||
| * written to stdout. | |||
| * */ | |||
| int | |||
| cprintf(const char *fmt, ...) { | |||
| va_list ap; | |||
| va_start(ap, fmt); | |||
| int cnt = vcprintf(fmt, ap); | |||
| va_end(ap); | |||
| return cnt; | |||
| } | |||
| /* * | |||
| * cputs- writes the string pointed by @str to stdout and | |||
| * appends a newline character. | |||
| * */ | |||
| int | |||
| cputs(const char *str) { | |||
| int cnt = 0; | |||
| char c; | |||
| while ((c = *str ++) != '\0') { | |||
| cputch(c, &cnt); | |||
| } | |||
| cputch('\n', &cnt); | |||
| return cnt; | |||
| } | |||
| @ -0,0 +1,72 @@ | |||
| #include <defs.h> | |||
| #include <unistd.h> | |||
| #include <stdarg.h> | |||
| #include <syscall.h> | |||
| #define MAX_ARGS 5 | |||
| static inline int | |||
| syscall(int num, ...) { | |||
| va_list ap; | |||
| va_start(ap, num); | |||
| uint32_t a[MAX_ARGS]; | |||
| int i, ret; | |||
| for (i = 0; i < MAX_ARGS; i ++) { | |||
| a[i] = va_arg(ap, uint32_t); | |||
| } | |||
| va_end(ap); | |||
| asm volatile ( | |||
| "int %1;" | |||
| : "=a" (ret) | |||
| : "i" (T_SYSCALL), | |||
| "a" (num), | |||
| "d" (a[0]), | |||
| "c" (a[1]), | |||
| "b" (a[2]), | |||
| "D" (a[3]), | |||
| "S" (a[4]) | |||
| : "cc", "memory"); | |||
| return ret; | |||
| } | |||
| int | |||
| sys_exit(int error_code) { | |||
| return syscall(SYS_exit, error_code); | |||
| } | |||
| int | |||
| sys_fork(void) { | |||
| return syscall(SYS_fork); | |||
| } | |||
| int | |||
| sys_wait(int pid, int *store) { | |||
| return syscall(SYS_wait, pid, store); | |||
| } | |||
| int | |||
| sys_yield(void) { | |||
| return syscall(SYS_yield); | |||
| } | |||
| int | |||
| sys_kill(int pid) { | |||
| return syscall(SYS_kill, pid); | |||
| } | |||
| int | |||
| sys_getpid(void) { | |||
| return syscall(SYS_getpid); | |||
| } | |||
| int | |||
| sys_putc(int c) { | |||
| return syscall(SYS_putc, c); | |||
| } | |||
| int | |||
| sys_pgdir(void) { | |||
| return syscall(SYS_pgdir); | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| #ifndef __USER_LIBS_SYSCALL_H__ | |||
| #define __USER_LIBS_SYSCALL_H__ | |||
| int sys_exit(int error_code); | |||
| int sys_fork(void); | |||
| int sys_wait(int pid, int *store); | |||
| int sys_yield(void); | |||
| int sys_kill(int pid); | |||
| int sys_getpid(void); | |||
| int sys_putc(int c); | |||
| int sys_pgdir(void); | |||
| #endif /* !__USER_LIBS_SYSCALL_H__ */ | |||
| @ -0,0 +1,48 @@ | |||
| #include <defs.h> | |||
| #include <syscall.h> | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| void | |||
| exit(int error_code) { | |||
| sys_exit(error_code); | |||
| cprintf("BUG: exit failed.\n"); | |||
| while (1); | |||
| } | |||
| int | |||
| fork(void) { | |||
| return sys_fork(); | |||
| } | |||
| int | |||
| wait(void) { | |||
| return sys_wait(0, NULL); | |||
| } | |||
| int | |||
| waitpid(int pid, int *store) { | |||
| return sys_wait(pid, store); | |||
| } | |||
| void | |||
| yield(void) { | |||
| sys_yield(); | |||
| } | |||
| int | |||
| kill(int pid) { | |||
| return sys_kill(pid); | |||
| } | |||
| int | |||
| getpid(void) { | |||
| return sys_getpid(); | |||
| } | |||
| //print_pgdir - print the PDT&PT | |||
| void | |||
| print_pgdir(void) { | |||
| sys_pgdir(); | |||
| } | |||
| @ -0,0 +1,36 @@ | |||
| #ifndef __USER_LIBS_ULIB_H__ | |||
| #define __USER_LIBS_ULIB_H__ | |||
| #include <defs.h> | |||
| void __warn(const char *file, int line, const char *fmt, ...); | |||
| void __noreturn __panic(const char *file, int line, const char *fmt, ...); | |||
| #define warn(...) \ | |||
| __warn(__FILE__, __LINE__, __VA_ARGS__) | |||
| #define panic(...) \ | |||
| __panic(__FILE__, __LINE__, __VA_ARGS__) | |||
| #define assert(x) \ | |||
| do { \ | |||
| if (!(x)) { \ | |||
| panic("assertion failed: %s", #x); \ | |||
| } \ | |||
| } while (0) | |||
| // static_assert(x) will generate a compile-time error if 'x' is false. | |||
| #define static_assert(x) \ | |||
| switch (x) { case 0: case (x): ; } | |||
| void __noreturn exit(int error_code); | |||
| int fork(void); | |||
| int wait(void); | |||
| int waitpid(int pid, int *store); | |||
| void yield(void); | |||
| int kill(int pid); | |||
| int getpid(void); | |||
| void print_pgdir(void); | |||
| #endif /* !__USER_LIBS_ULIB_H__ */ | |||
| @ -0,0 +1,10 @@ | |||
| #include <ulib.h> | |||
| int main(void); | |||
| void | |||
| umain(void) { | |||
| int ret = main(); | |||
| exit(ret); | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int | |||
| main(void) { | |||
| cprintf("I am %d, print pgdir.\n", getpid()); | |||
| print_pgdir(); | |||
| cprintf("pgdir pass.\n"); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,9 @@ | |||
| #include <stdio.h> | |||
| #include <ulib.h> | |||
| int | |||
| main(void) { | |||
| asm volatile("int $14"); | |||
| panic("FAIL: T.T\n"); | |||
| } | |||