diff --git a/Makefile b/Makefile index d2475df..54046e8 100644 --- a/Makefile +++ b/Makefile @@ -1,91 +1,135 @@ -# -----------Product Config----------- -# product name -PRODUCT_NAME = TOY_OS -BUILD_DIR = build -# -----------Disk Image Config----------- -# image file name -DISK_IMAGE = $(BUILD_DIR)/disk.img -# disk size in MB, can be specified by command line -DISK_SIZE ?= 64 -# sector size, 512 bytes by default -SECTOR_SIZE = 512 -# partition offset in sector, 2048 by default because of 4KB alignment -PART_START = 2048 -# end partition, partition size - 1 is the last partition -PART_END = $(shell echo $$((($(DISK_SIZE) * 1024 * 1024) / $(SECTOR_SIZE) - 1))) -# partition type, `0c` for FAT32 -TYPE = 0c -# mount point -MOUNT_POINT ?= /mnt/toy_os -# -----------C Compiler Config----------- -CFLAGS = -Wall -static -fno-stack-protector -m32 -I. - -.PHONY: clean mount unmount build run - -# -----------make----------- -run: build - qemu-system-x86_64 -drive file=$(DISK_IMAGE),format=raw - -build: $(BUILD_DIR)/flag_installed - -$(BUILD_DIR): - mkdir -p $@ - -# make disk image -$(DISK_IMAGE): - # create an empty disk image file filled with zeros - dd if=/dev/zero of=$@ bs=1M count=$(DISK_SIZE) - # create a MBR partition table with a FAT32 partition - echo "o\nn\np\n1\n$(PART_START)\n$(PART_END)\nt\n$(TYPE)\nw\n" | fdisk $@ - # format the partition as FAT32 - mkfs.vfat -F 32 -n "$(PRODUCT_NAME)" --offset $(PART_START) $@ - -# -----------MBR Boot Code----------- -BUILD_BOOT_DIR = $(BUILD_DIR)/boot - -$(BUILD_BOOT_DIR): $(BUILD_DIR) - mkdir -p $@ - -$(BUILD_BOOT_DIR)/mbr_boot_asm.o: boot/mbr_boot.asm $(BUILD_BOOT_DIR) - nasm $< -f elf32 -o $(BUILD_BOOT_DIR)/mbr_boot_asm.o - -$(BUILD_BOOT_DIR)/mbr_boot_c.o: boot/mbr_boot.c $(BUILD_BOOT_DIR) - gcc $(CFLAGS) -nostdinc -fno-builtin -fno-pie -fno-pic -fno-omit-frame-pointer -fno-strict-aliasing -s -c -o $(BUILD_BOOT_DIR)/mbr_boot_c.o $< - -$(BUILD_BOOT_DIR)/mbr_boot.o: $(BUILD_BOOT_DIR)/mbr_boot_asm.o $(BUILD_BOOT_DIR)/mbr_boot_c.o - # param explaination: - # -N: Do not page align data, do not make text readonly - # -e: Set entry point - # -Ttext: Set address of .text section - # -s: Strip all symbols - ld -m elf_i386 -N -e asm_main -Ttext 0x7c00 -s $(BUILD_BOOT_DIR)/mbr_boot_asm.o $(BUILD_BOOT_DIR)/mbr_boot_c.o -o $(BUILD_BOOT_DIR)/mbr_boot.o - -$(BUILD_BOOT_DIR)/mbr_boot.bin: $(BUILD_BOOT_DIR)/mbr_boot.o - # param explaination: - # -S: Strip all symbols and relocation information - # -O: Output target - # -j .text: Only copy section .text into the output - objcopy -S -O binary -j .text -j .rodata $(BUILD_BOOT_DIR)/mbr_boot.o $(BUILD_BOOT_DIR)/mbr_boot.bin - -$(BUILD_DIR)/flag_installed: $(BUILD_DIR)/boot/mbr_boot.bin $(DISK_IMAGE) - # make sure the size is less than 440 bytes - [ $(shell stat -c %s build/boot/mbr_boot.bin) -lt 440 ] - dd if=$< of=$(DISK_IMAGE) bs=440 count=1 conv=notrunc - touch $(BUILD_DIR)/flag_installed - -# -----------clean----------- -clean: - rm -rf $(BUILD_DIR) - -# -----------mount disk image----------- -mount: - sudo mkdir -p $(MOUNT_POINT) - LOOPDEV=$$(sudo kpartx -av $(DISK_IMAGE) | head -n 1 | cut -d ' ' -f 3); \ - echo "Loop device: $$LOOPDEV"; \ - sudo mount /dev/mapper/$$LOOPDEV $(MOUNT_POINT) - -# -----------unmount disk image----------- -unmount: - sudo umount $(MOUNT_POINT) - sudo kpartx -dv $(DISK_IMAGE) - sudo rm -rf $(MOUNT_POINT) \ No newline at end of file +-include local.mk + +X64 ?= yes + +ifeq ("$(X64)","yes") +BITS = 64 +XOBJS = kobj/vm64.o +XFLAGS = -m64 -DX64 -mcmodel=kernel -mtls-direct-seg-refs -mno-red-zone +LDFLAGS = -m elf_x86_64 +QEMU ?= qemu-system-x86_64 +else +XFLAGS = -m32 +LDFLAGS = -m elf_i386 +QEMU ?= qemu-system-i386 +endif + +OPT ?= -O0 + +OBJS := \ + kobj/console.o\ + kobj/main.o\ + kobj/string.o\ + kobj/vm.o\ + kobj/memblock.o\ + +ifneq ("$(MEMFS)","") +# build filesystem image in to kernel and use memory-ide-device +# instead of mounting the filesystem on ide1 +OBJS := $(filter-out kobj/ide.o,$(OBJS)) kobj/memide.o +endif + +# Cross-compiling (e.g., on Mac OS X) +#TOOLPREFIX = i386-jos-elf- + +# Using native tools (e.g., on X86 Linux) +#TOOLPREFIX = + +CC = $(TOOLPREFIX)gcc +AS = $(TOOLPREFIX)gas +LD = $(TOOLPREFIX)ld +OBJCOPY = $(TOOLPREFIX)objcopy +OBJDUMP = $(TOOLPREFIX)objdump +CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -Wall -MD -ggdb -fno-omit-frame-pointer +CFLAGS += -ffreestanding -fno-common -nostdlib -Iinclude -gdwarf-2 $(XFLAGS) $(OPT) +CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) +ASFLAGS = -fno-pic -gdwarf-2 -Wa,-divide -Iinclude $(XFLAGS) + +xv6.img: out/bootblock out/kernel.elf + dd if=/dev/zero of=xv6.img count=10000 + dd if=out/bootblock of=xv6.img conv=notrunc + dd if=out/kernel.elf of=xv6.img seek=1 conv=notrunc + +xv6memfs.img: out/bootblock out/kernelmemfs.elf + dd if=/dev/zero of=xv6memfs.img count=10000 + dd if=out/bootblock of=xv6memfs.img conv=notrunc + dd if=out/kernelmemfs.elf of=xv6memfs.img seek=1 conv=notrunc + +# kernel object files +kobj/%.o: kernel/%.c + @mkdir -p kobj + $(CC) $(CFLAGS) -c -o $@ $< + +kobj/%.o: kernel/%.S + @mkdir -p kobj + $(CC) $(ASFLAGS) -c -o $@ $< + +out/bootblock: kernel/bootasm.S kernel/bootmain.c + @mkdir -p out + $(CC) -fno-builtin -fno-pic -m32 -nostdinc -Iinclude -O -o out/bootmain.o -c kernel/bootmain.c + $(CC) -fno-builtin -fno-pic -m32 -nostdinc -Iinclude -o out/bootasm.o -c kernel/bootasm.S + $(LD) -m elf_i386 -N -e start -Ttext 0x7C00 -o out/bootblock.o out/bootasm.o out/bootmain.o + $(OBJDUMP) -S out/bootblock.o > out/bootblock.asm + $(OBJCOPY) -S -O binary -j .text out/bootblock.o out/bootblock + tools/sign.pl out/bootblock + +ENTRYCODE = kobj/entry$(BITS).o +LINKSCRIPT = kernel/kernel$(BITS).ld +out/kernel.elf: $(OBJS) $(ENTRYCODE) $(LINKSCRIPT) + $(LD) $(LDFLAGS) -T $(LINKSCRIPT) -o out/kernel.elf $(ENTRYCODE) $(OBJS) -b binary + $(OBJDUMP) -S out/kernel.elf > out/kernel.asm + $(OBJDUMP) -t out/kernel.elf | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > out/kernel.sym + +MKVECTORS = tools/vectors$(BITS).pl +kernel/vectors.S: $(MKVECTORS) + perl $(MKVECTORS) > kernel/vectors.S + +# Prevent deletion of intermediate files, e.g. cat.o, after first build, so +# that disk image changes after first build are persistent until clean. More +# details: +# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html +.PRECIOUS: uobj/%.o + +-include */*.d + +clean: + rm -rf out fs uobj kobj + rm -f kernel/vectors.S xv6.img xv6memfs.img fs.img .gdbinit + +# run in emulators + +bochs : fs.img xv6.img + if [ ! -e .bochsrc ]; then ln -s tools/dot-bochsrc .bochsrc; fi + bochs -q + +# try to generate a unique GDB port +GDBPORT = $(shell expr `id -u` % 5000 + 25000) +# QEMU's gdb stub command line changed in 0.11 +QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ + then echo "-gdb tcp::$(GDBPORT)"; \ + else echo "-s -p $(GDBPORT)"; fi) +ifndef CPUS +CPUS := 2 +endif +QEMUOPTS = -net none -hda xv6.img -smp $(CPUS) -m 512 $(QEMUEXTRA) + +qemu: xv6.img + $(QEMU) -serial mon:stdio $(QEMUOPTS) + +qemu-memfs: xv6memfs.img + $(QEMU) xv6memfs.img -smp $(CPUS) + +qemu-nox: fs.img xv6.img + $(QEMU) -nographic $(QEMUOPTS) + +.gdbinit: tools/gdbinit.tmpl + sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ + +qemu-gdb: fs.img xv6.img .gdbinit + @echo "*** Now run 'gdb'." 1>&2 + $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) + +qemu-nox-gdb: fs.img xv6.img .gdbinit + @echo "*** Now run 'gdb'." 1>&2 + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) + diff --git a/README.md b/README.md index 1b3f98e..779e1a5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # Toy Kernel ## Get started @@ -9,3 +10,4 @@ - run `make` to build and run the os in qemu Virtual Machine. - run `make build` to build the os image only. + diff --git a/boot/gdt.inc b/boot/gdt.inc deleted file mode 100644 index ea4296e..0000000 --- a/boot/gdt.inc +++ /dev/null @@ -1,51 +0,0 @@ -; structure of gdt descriptor - -; @signature: descriptor base, limit, attr -; @param u32 base : Base address of the segment. -; @param u32 limit : Only low 20 bits are in use. The length of the segment - 1, because the max size is 2^20, and the max value of u20 is 2^20-1. -; @param u16 attr : Only the high 4 bits and low 8 bits are in use. -%macro descriptor 3 - dw %2 & 0xffff ; limit low 16 bits - dw %1 & 0xffff ; base low 16 bits - db (%1 >> 16) & 0xff ; base 16-23 bits - dw ( (%2 >> 8) & 0x0f00) | (%3 & 0xf0ff) ; attribute and limit high 8 bits - db (%1 >> 24) & 0xff ; base 24-31 bits -%endmacro - -; attribute - -; 0 bit: accessed -GDT_ACCESSED equ 0000_0001b ; best left clear (0), the CPU will set it when the segment is accessed. - -; 1-4 bits: type -; for data segment -GDT_TYPE_DATA_WRITABLE equ 0000_0010b ; read-only if clear (0) -GDT_TYPE_DATA_GROW_DOWN equ 0000_0100b ; Direction bit. If clear (0) the segment grows up. If set (1) the segment grows down, ie. the Offset has to be greater than the Limit, like stack. -; for code segment -GDT_TYPE_EXEC_READABLE equ 0000_0010b ; execute-only if clear (0) -GDT_TYPE_EXEC_CONFORMING equ 0000_0100b ; if set (1), code in this segment can be executed from an equal or lower privilege level; if clear (0), code in this segment can only be executed from the ring set in DPL. -; for system segment -GDT_TYPE_SYS_LDT equ 0x2 -GDT_TYPE_SYS_TSS equ 0x9 ; 32-bit TSS (Task State Segment) -GDT_TYPE_SYS_TSS_BUSY equ 0xb - -GDT_TYPE_EXECUTABLE equ 0000_1000b ; if clear (0) the descriptor defines a data segment. -GDT_TYPE_NON_SYSTEM equ 0001_0000b ; if clear (0) the descriptor defines a system segment; if set (1) the descriptor defines a data or code segment. - -; 5-6 bits: discriptor privilege level (DPL) -GDT_DPL_0 equ 0x0 << 5 -GDT_DPL_1 equ 0x1 << 5 -GDT_DPL_2 equ 0x2 << 5 -GDT_DPL_3 equ 0x3 << 5 - -; 7 bit: present -GDT_PRESENT equ 1000_0000b ; set (1) for any valid segment. - -; 13 bit: Long-mode -GDT_LONG_MODE equ 0010_0000_0000_0000b ; If set (1), the descriptor defines a 64-bit code segment and `DB` should always be clear (0). - -; 14 bit: -GDT_PROTECTED_MODE equ 0100_0000_0000_0000b ; If set (1), 32-bit segment, for stack segment use ESP rather than SP and the limit is 4GB rather than 64KB. If clear (0), 16-bit segment. - -; 15 bit: -GDT_PAGE_GRANULARITY equ 1000_0000_0000_0000b ; If set (1), the unit for the limit field is 4 KiB (just the size of a page). If clear (0), the unit is byte. \ No newline at end of file diff --git a/boot/mbr_boot.asm b/boot/mbr_boot.asm deleted file mode 100644 index 43e52b8..0000000 --- a/boot/mbr_boot.asm +++ /dev/null @@ -1,63 +0,0 @@ -%include "boot/gdt.inc" ; GDT descriptor structure -%define pos(x,y) (80*(x)+(y))*2 ; 80x25 screen position - -; In legacy boot mode, the bootloader will be load to 0x7c00, so we need `org 0x7c00` -; Now we will compile our code into a relocatable file, the offset `0x7c00` should be specified to the linker `ld` but not here. -; org 0x7c00 - -extern main ; external c main function -global boot_main ; asm entry point for the linker - -; strip most of the debug output to minimize the binary size - -[BITS 16] -boot_main: - ; init stack - mov ax, cs - mov ds, ax - mov es, ax - mov gs, ax - mov ss, ax - mov sp, 0x7c00 - - ; clear screen - mov ah, 0x00 ; new video mode - mov al, 0x03 ; 80x25 color text mode - int 0x10 ; modify video mode will clear the screen - - ; enable_A20 - cli - in al, 0x92 - or al, 0010b - out 0x92, al - - ; protected mode - lgdt [gdt_reg] - mov eax, cr0 - or eax, 0x00000001 - mov cr0, eax - jmp dword (DESC_CODE - GDT) : protected_main - -[BITS 32] -protected_main: - ; prepare segments for c codes - mov ax, DESC_DATA - GDT - mov ds, ax - mov es, ax - mov ss, ax - mov esp, 0x7c00 - mov ax, 0 - mov fs, ax - mov gs, ax - call main ; should never return - - jmp $ - -GDT: -DESC_NULL: descriptor 0, 0, 0 -DESC_CODE: descriptor 0, 0xffffffff, GDT_TYPE_EXECUTABLE | GDT_TYPE_EXEC_READABLE | GDT_TYPE_NON_SYSTEM | GDT_PROTECTED_MODE | GDT_PRESENT -DESC_DATA: descriptor 0, 0xffffffff, GDT_TYPE_DATA_WRITABLE | GDT_TYPE_NON_SYSTEM | GDT_PROTECTED_MODE | GDT_PRESENT - -gdt_len equ $ - GDT -gdt_reg dw gdt_len - 1 ; the length of gdt minus 1 - dd GDT ; the address of gdt \ No newline at end of file diff --git a/boot/mbr_boot.c b/boot/mbr_boot.c deleted file mode 100644 index 69e3921..0000000 --- a/boot/mbr_boot.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "lang/c/types32.h" - -const int SCREEN_WIDTH = 80; -#define screen_pos(x, y) (SCREEN_WIDTH * (x) + (y)) * 2 // 80x25 screen -byte* const screen = (byte*)0xb8000; -static int screen_pos_x = 0; -static int screen_pos_y = 0; - -void putcharb(char c) { - if (c != '\n') screen[screen_pos(screen_pos_x, screen_pos_y)] = c; - if (c == '\n' || ++screen_pos_y >= SCREEN_WIDTH) { - screen_pos_y = 0; - ++screen_pos_x; - } -} - -void printb(char* str) { - while (*str) { - putcharb(*str++); - } -} - -// This function will be called by `mbr_boot.asm` -int main() { - printb("Hello C World!\n"); - return 0; -} \ No newline at end of file diff --git a/include/acpi.h b/include/acpi.h new file mode 100644 index 0000000..ab61db2 --- /dev/null +++ b/include/acpi.h @@ -0,0 +1,33 @@ +// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/index.html# +// 5.2.5.3 +#define SIG_RDSP "RSD PTR " +struct acpi_rdsp { + u8 signature[8]; + u8 checksum; + u8 oem_id[6]; + u8 revision; + u32 rsdt_phys_addr; + u32 length; + u64 xsdt_phys_addr; + u8 extended_checksum; + u8 reserved[3]; +} __attribute__((__packed__)); + +// 5.2.6 +struct acpi_desc_header { + u8 signature[4]; + u32 length; + u8 revision; + u8 checksum; + u8 oem_id[6]; + u8 oem_table_id[8]; + u32 oem_revision; + u8 creator_id[4]; + u32 creator_revision; +} __attribute__((__packed__)); + +// 5.2.8 +struct acpi_xsdt{ + struct acpi_desc_header header; + u64 entry[0]; +} __attribute__((__packed__)); \ No newline at end of file diff --git a/include/asm.h b/include/asm.h new file mode 100644 index 0000000..68210d7 --- /dev/null +++ b/include/asm.h @@ -0,0 +1,21 @@ +// +// assembler macros to create x86 segments +// + +#define SEG_NULLASM \ + .word 0, 0; \ + .byte 0, 0, 0, 0 + +// The 0xC0 means the limit is in 4096-byte units +// and (for executable segments) 32-bit mode. +#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) + +#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 diff --git a/include/console.h b/include/console.h new file mode 100644 index 0000000..e69de29 diff --git a/include/defs.h b/include/defs.h new file mode 100644 index 0000000..ca42538 --- /dev/null +++ b/include/defs.h @@ -0,0 +1,24 @@ +struct list_head; +struct proc; + +// console.c +void cprintf(char* fmt, ...); +void panic(char* s); + +// memblock.c + +void memblock_init(void); +u64 memblock_alloc(u64 size, u64 align); + +// proc.c +//void test(void); + +// string.c +int memcmp(const void*, const void*, u32); +void* memmove(void*, const void*, u32); +void* memset(void*, int, u32); +char* safestrcpy(char*, const char*, int); +int strlen(const char*); +int strncmp(const char*, const char*, u32); +char* strncpy(char*, const char*, int); + diff --git a/include/memblock.h b/include/memblock.h new file mode 100644 index 0000000..7c7792d --- /dev/null +++ b/include/memblock.h @@ -0,0 +1,36 @@ +#define ULLONG_MAX (~0ULL) + +#define MEMBLOCK_ALLOC_ACCESSIBLE 0 + +struct memblock_region{ + u64 base; + u64 size; + //enum memblock_flags flags; +}; + +struct memblock_type{ + u64 cnt; + u64 max; + u64 total_size; + struct memblock_region* regions; + char* name; +}; + +struct memblock{ + struct memblock_type memory; + struct memblock_type reserved; +}memblock; + +#define for_each_free_mem_range_reserve(i, p_start, p_end) \ + for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved, p_start, p_end) + +#define for_each_mem_range_rev(i, type_a, type_b, p_start, p_end) \ + for (i = (u64)ULLONG_MAX, __next_mem_range_rev(&i, type_a, type_b, p_start, p_end); \ + i != (u64)ULLONG_MAX; __next_mem_range_rev(&i, type_a, type_b, p_start, p_end)) + +#define for_each_memblock_type(i, memblock_type, rgn) \ + for (i = 0, rgn = &memblock_type->regions[0]; \ + i < memblock_type->cnt; \ + i++, rgn = &memblock_type->regions[i]) + +#define ULLONG_MAX (~0ULL) diff --git a/include/memlayout.h b/include/memlayout.h new file mode 100644 index 0000000..c203e8c --- /dev/null +++ b/include/memlayout.h @@ -0,0 +1,22 @@ + +#define EXTMEM 0x100000 // Start of extended memory + +#define KERNBASE 0xFFFFFFFF80000000 // First kernel address +#define DEVBASE 0xFFFFFFFF40000000 // First device virtual address +#define ARDSOFFSET 0x8000 // ARDS offset + +#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked + +#ifndef __ASSEMBLER__ + +static inline u64 v2p(void *a) { return ((u64) (a)) - ((u64)KERNBASE); } +static inline void *p2v(u64 a) { return (void *) ((a) + ((u64)KERNBASE)); } + +#endif + +#define V2P(a) (((uintp) (a)) - KERNBASE) +#define P2V(a) (((void *) (a)) + KERNBASE) +#define IO2V(a) (((void *) (a)) + DEVBASE - DEVSPACE) + +#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts +#define P2V_WO(x) ((x) + KERNBASE) // same as V2P, but without casts diff --git a/include/mmu.h b/include/mmu.h new file mode 100644 index 0000000..ed7357a --- /dev/null +++ b/include/mmu.h @@ -0,0 +1,121 @@ +// A virtual address 'va' has a five-part structute as follow +// +--------10------+--------10------+--------10------+-------10-------+---------12----------+ +// | Page-Map | Page Directory | Page Directory | Page Table | Offset within Page | +// | Level-4 Index | Pointer Index | Index | Index | | +// +----------------+----------------+----------------+----------------+---------------------+ +// \--- PML4X(va) --/ \--- PDPTX(va) --/ \--- PDX(va) --/ \--- PTX(va) --/ + +// Page Directory Pointer Index +#define PML4X(va) (((u64)(va) >> PML4XSHIFT ) & PXMASK) +#define PDPTX(va) (((u64)(va) >> PDPTXSHIFT ) & PXMASK) +#define PDX(va) (((u64)(va) >> PDXSHIFT ) & PXMASK) +#define PTX(va) (((u64)(va) >> PTXSHIFT ) & PXMASK) + +// Page directory and page table constants. +#define PGSIZE 4096 // bytes a page + +#define PTXSHIFT 12 // offset of PTX in a linear address +#define PDXSHIFT 21 // offset of PDX in a linear address +#define PDPTXSHIFT 30 // offset of PDPX in a linear address +#define PML4XSHIFT 39 // offset of PML4 in a linear address + +#define PXMASK 0x1FF + +#define ALIGN(x,a) (((x) + (a) -1) & ~(a-1)) +#define ALIGN_DOWN(x,a) ((x) & ~(a-1)) + +// Page entry flags +#define PTE_P 0x001 // Present +#define PTE_W 0x002 // Read/write +#define PTE_U 0x004 // User/supervisor +#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 + +// 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_PSE 0x00000010 // Page size extension + +#define SEG_KCODE 1 // kernel code +#define SEG_KDATA 2 // kernel data+stack +#define SEG_KCPU 3 // kernel per-cpu data +#define SEG_UCODE 4 // user code +#define SEG_UDATA 5 // user data+stack +#define SEG_TSS 6 // this process's task state + +#define BUUDY_TYPES 12 +#define NULL ((void* )0) + +#ifndef __ASSEMBLER__ + +struct MEMORY_E820{ + int nr_map; + struct{ + u64 addr; + u64 len; + u32 type; + }__attribute__((packed)) map[32]; +}; + +// Segment Descriptor +struct segdesc { + u32 lim_15_0 : 16; // Low bits of segment limit + u32 base_15_0 : 16; // Low bits of segment base address + u32 base_23_16 : 8; // Middle bits of segment base address + u32 type : 4; // Segment type (see STS_ constants) + u32 s : 1; // 0 = system, 1 = application + u32 dpl : 2; // Descriptor Privilege Level + u32 p : 1; // Present + u32 lim_19_16 : 4; // High bits of segment limit + u32 avl : 1; // Unused (available for software use) + u32 rsv1 : 1; // Reserved + u32 db : 1; // 0 = 16-bit segment, 1 = 32-bit segment + u32 g : 1; // Granularity: limit scaled by 4K when set + u32 base_31_24 : 8; // High bits of segment base address +}; + +// Normal segment +#define SEG(type, base, lim, dpl) (struct segdesc) \ +{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ + ((uintp)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uintp)(lim) >> 28, 0, 0, 1, 1, (uintp)(base) >> 24 } +#define SEG16(type, base, lim, dpl) (struct segdesc) \ +{ (lim) & 0xffff, (uintp)(base) & 0xffff, \ + ((uintp)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uintp)(lim) >> 16, 0, 0, 1, 0, (uintp)(base) >> 24 } + +struct list_head{ + struct list_head *next, *prev; +}; + +struct free_area{ + //struct list_head free_list; + u64 nr_free; +}; + +//struct zone{ +// struct free_area free_area[BUUDY_TYPES]; + +//}zone1; + +struct page{ + //struct list_head lru; + u64 vaddr; + u64 paddr; +}__attribute__((packed)); + +#endif diff --git a/include/param.h b/include/param.h new file mode 100644 index 0000000..b6f6f46 --- /dev/null +++ b/include/param.h @@ -0,0 +1,12 @@ +#define NPROC 64 // maximum number of processes +#define KSTACKSIZE 4096 // size of per-process kernel stack +#define NCPU 8 // maximum number of CPUs +#define NOFILE 16 // open files per process +#define NFILE 100 // open files per system +#define NBUF 10 // size of disk block cache +#define NINODE 50 // maximum number of active i-nodes +#define NDEV 10 // maximum major device number +#define ROOTDEV 1 // device number of file system root disk +#define MAXARG 32 // max exec arguments +#define LOGSIZE 10 // max data sectors in on-disk log + diff --git a/include/proc.h b/include/proc.h new file mode 100644 index 0000000..e4cea06 --- /dev/null +++ b/include/proc.h @@ -0,0 +1,14 @@ +enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNING, ZOMBIE}; + +struct proc{ + u64 size; + u64* pgdir; + u8* kstack; + enum procstate state; + volatile int pid; + struct proc *parent; + struct trapframe* tf; + struct context* context; + int killed; + char name[16]; +}; diff --git a/include/spinlock.h b/include/spinlock.h new file mode 100644 index 0000000..e69de29 diff --git a/include/syscall.h b/include/syscall.h new file mode 100644 index 0000000..1e75354 --- /dev/null +++ b/include/syscall.h @@ -0,0 +1,2 @@ +// System call numbers +#define SYS_test 1 \ No newline at end of file diff --git a/include/traps.h b/include/traps.h new file mode 100644 index 0000000..e69de29 diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..fd10c5a --- /dev/null +++ b/include/types.h @@ -0,0 +1,30 @@ +typedef char i8; +typedef short i16; +typedef int i32; +typedef long long i64; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef float f32; +typedef double f64; + +typedef u8 byte; + +typedef u64 uintp; + +typedef u64 pde_t; + +#define min(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) diff --git a/lang/c/types32.h b/include/types32.h similarity index 86% rename from lang/c/types32.h rename to include/types32.h index d22e0a0..7d06f4a 100644 --- a/lang/c/types32.h +++ b/include/types32.h @@ -11,4 +11,6 @@ typedef unsigned long long u64; typedef float f32; typedef double f64; -typedef u8 byte; \ No newline at end of file +typedef u8 byte; + +typedef u32 pde_t; diff --git a/include/vm.h b/include/vm.h new file mode 100644 index 0000000..e69de29 diff --git a/include/x86.h b/include/x86.h new file mode 100644 index 0000000..a59a69c --- /dev/null +++ b/include/x86.h @@ -0,0 +1,184 @@ +// Routines to let C code use special x86 instructions. + +static inline u8 +inb(u16 port) +{ + u8 data; + + asm volatile("in %1,%0" : "=a" (data) : "d" (port)); + return data; +} + +static inline void +insl(int port, void *addr, int cnt) +{ + asm volatile("cld; rep insl" : + "=D" (addr), "=c" (cnt) : + "d" (port), "0" (addr), "1" (cnt) : + "memory", "cc"); +} + +static inline void +outb(u16 port, u8 data) +{ + asm volatile("out %0,%1" : : "a" (data), "d" (port)); +} + +static inline void +outw(u16 port, u16 data) +{ + asm volatile("out %0,%1" : : "a" (data), "d" (port)); +} + +static inline void +outsl(int port, const void *addr, int cnt) +{ + asm volatile("cld; rep outsl" : + "=S" (addr), "=c" (cnt) : + "d" (port), "0" (addr), "1" (cnt) : + "cc"); +} + +static inline void +stosb(void *addr, int data, int cnt) +{ + asm volatile("cld; rep stosb" : + "=D" (addr), "=c" (cnt) : + "0" (addr), "1" (cnt), "a" (data) : + "memory", "cc"); +} + +static inline void +stosl(void *addr, int data, int cnt) +{ + asm volatile("cld; rep stosl" : + "=D" (addr), "=c" (cnt) : + "0" (addr), "1" (cnt), "a" (data) : + "memory", "cc"); +} + +struct segdesc; + +static inline void +lgdt(struct segdesc *p, int size) +{ + volatile u16 pd[5]; + + pd[0] = size-1; + pd[1] = (u64)p; + pd[2] = (u64)p >> 16; + pd[3] = (u64)p >> 32; + pd[4] = (u64)p >> 48; + asm volatile("lgdt (%0)" : : "r" (pd)); +} + +struct gatedesc; + +static inline void +lidt(struct gatedesc *p, int size) +{ + volatile u16 pd[5]; + + pd[0] = size-1; + pd[1] = (u64)p; + pd[2] = (u64)p >> 16; + pd[3] = (u64)p >> 32; + pd[4] = (u64)p >> 48; +} + +static inline void +ltr(u16 sel) +{ + asm volatile("ltr %0" : : "r" (sel)); +} + +static inline u64 +readeflags(void) +{ + u64 eflags; + asm volatile("pushf; pop %0" : "=r" (eflags)); + return eflags; +} + +static inline void +loadgs(u16 v) +{ + asm volatile("movw %0, %%gs" : : "r" (v)); +} + +static inline void +cli(void) +{ + asm volatile("cli"); +} + +static inline void +sti(void) +{ + asm volatile("sti"); +} + +static inline void +hlt(void) +{ + asm volatile("hlt"); +} + +static inline u32 +xchg(volatile u32 *addr, u64 newval) +{ + u64 result; + + // The + in "+m" denotes a read-modify-write operand. + asm volatile("lock; xchgq %0, %1" : + "+m" (*addr), "=a" (result) : + "1" (newval) : + "cc"); + return result; +} + +static inline u64 +rcr2(void) +{ + u64 val; + asm volatile("mov %%cr2,%0" : "=r" (val)); + return val; +} + +static inline void +lcr3(u64 val) +{ + asm volatile("mov %0,%%cr3" : : "r" (val)); +} + +//PAGEBREAK: 36 +// Layout of the trap frame built on the stack by the +// hardware and by trapasm.S, and passed to trap(). +// lie about some register names in 64bit mode to avoid +// clunky ifdefs in proc.c and trap.c. +struct trapframe { + u64 eax; // rax + u64 rbx; + u64 rcx; + u64 rdx; + u64 rbp; + u64 rsi; + u64 rdi; + u64 r8; + u64 r9; + u64 r10; + u64 r11; + u64 r12; + u64 r13; + u64 r14; + u64 r15; + + u64 trapno; + u64 err; + + u64 rip; // rip + u64 cs; + u64 eflags; // rflags + u64 rsp; // rsp + u64 ds; // ss +}; diff --git a/kernel/acpi.c b/kernel/acpi.c new file mode 100644 index 0000000..bdf7f47 --- /dev/null +++ b/kernel/acpi.c @@ -0,0 +1,86 @@ +#include "acpi.h" + +static struct acpi_rsdp* search_rdsp(u64 base, u64 len){ + u8* p; + for(p = p2v(base); len >= sizeof(struct acpi_rsdp); len -= 4, p -= 4){ + if(memcmp(p, SIG_RSDP, 8) == 0){ + u32 sum, i; + for(sum = 0, i = 0; i < 20; i++){ + sum += p[i]; + } + if((sum & 0xff) == 0) + return (struct acpi_rsdp *) p; + } + } + return (struct acpi_rsdp*) 0; +} + +static struct acpi_rsdp* find_rdsp(void){ + struct acpi_rsdp* rsdp; + phys_addr pa; + pa = *((u16*) P2V(0x40E)) << 4; + if(pa && (rspd = search_rdsp(pa, 1024))){ + return rsdp; + } + return search_rdsp(0xE0000, 0x20000); +} + +static struct acpi_rsdp* find_rdsp_uefi(void){ + return (struct acpi_rsdp*) 0; +} + +static bool acpi_config_smp(struct acpi_madt* madt){ + u32 lapic_addr = madt->lapic_phys_addr; + u32 nioapic = 0; + u8 *ptr, *end; + + ptr = madt->interrupt_controller_structure; + end = ptr + madt->header.length - sizeof(struct acpi_madt); + + while(ptr < end){ + u32 len; + if((end - ptr) < 2) + break; + len = ptr[1]; + if((end - ptr) < len) + break; + switch(p[0]){ + case TYPE_LAPIC: { + struct madt_lapic *lapic = (void*) ptr; + if(len < sizeof(*lapic)) + break; + if(!(lapic->flags & APIC_LAPIC_ENABLED)) + break; + cpus[ncpu].id = ncpu; + cpus[ncpu].apicid = lapic->apic_id; + ncpu++; + break; + } + case TYPE_IOAPIC: { + struct madt_ioapic *ioapic = (void*) ptr; + if (len < sizeof(*ioapic)) + break; + ioapicid = ioapic->id; + nioapic++; + break; + } + } + p += len; + } +} + +bool acpi_init(void){ + struct acpi_rsdp* rdsp; + struct acpi_xsdt* xsdt; + u32 count,i; + + rdsp = find_rdsp(); + xsdt = p2v(rdsp->xsdt_phys_addr); + count = (xsdt->header.length - sizeof(*xsdt)) / 8; + for(i = 0; i < count ; i++){ + struct acpi_desc_header *hdr = p2v(xsdt->entry[n]); + if(memcmp(hdr->signature, SIG_MADT, 4)){ + madt = (void*) hdr; + } + } +} \ No newline at end of file diff --git a/kernel/bootasm.S b/kernel/bootasm.S new file mode 100644 index 0000000..2c79882 --- /dev/null +++ b/kernel/bootasm.S @@ -0,0 +1,110 @@ +#include "asm.h" +#include "memlayout.h" +#include "mmu.h" + +# Start the first 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 SMAP, 0x534d4150 + +.code16 # Assemble for 16-bit mode +.globl start +start: + + cli # BIOS enabled interrupts; disable + + # Zero data segment registers DS, ES, and SS. + xorw %ax,%ax # Set %ax to zero + movw %ax,%ds # -> Data Segment + movw %ax,%es # -> Extra Segment + movw %ax,%ss # -> Stack Segment + + # Physical address line A20 is tied to zero so that the first PCs + # with 2 MB would run software that assumed 1 MB. Undo that. +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. Use a bootstrap GDT that makes + # virtual addresses map directly to physical addresses so that the + # effective memory map doesn't change during the transition. + lgdt gdtdesc + movl %cr0, %eax + orl $CR0_PE, %eax + movl %eax, %cr0 + +//PAGEBREAK! + # Complete transition to 32-bit protected mode by using long jmp + # to reload %cs and %eip. The segment descriptors are set up with no + # translation, so that the mapping is still the identity mapping. + ljmp $(SEG_KCODE<<3), $start32 + +.code32 # Tell assembler to generate 32-bit code now. +start32: + # Set up the protected-mode data segment registers + movw $(SEG_KDATA<<3), %ax # Our data segment selector + movw %ax, %ds # -> DS: Data Segment + movw %ax, %es # -> ES: Extra Segment + movw %ax, %ss # -> SS: Stack Segment + movw $0, %ax # Zero segments not ready for use + movw %ax, %fs # -> FS + movw %ax, %gs # -> GS + + # Set up the stack pointer and call into C. + movl $start, %esp + call bootmain + + # If bootmain returns (it shouldn't), trigger a Bochs + # breakpoint if running under Bochs, then loop. + movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 + movw %ax, %dx + outw %ax, %dx + movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 + outw %ax, %dx +spin: + jmp spin + +# Bootstrap GDT +.p2align 2 # force 4 byte alignment +gdt: + SEG_NULLASM # null seg + SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg + SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg + +gdtdesc: + .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 + .long gdt # address gdt + diff --git a/kernel/bootmain.c b/kernel/bootmain.c new file mode 100644 index 0000000..64d999a --- /dev/null +++ b/kernel/bootmain.c @@ -0,0 +1,118 @@ +// Boot loader. +// +// Part of the boot sector, along with bootasm.S, which calls bootmain(). +// bootasm.S has put the processor into protected 32-bit mode. +// bootmain() loads a multiboot kernel image from the disk starting at +// sector 1 and then jumps to the kernel entry routine. + +#include "types.h" +#include "x86.h" +#include "memlayout.h" + +#define SECTSIZE 512 + +struct mbheader { + u32 magic; + u32 flags; + u32 checksum; + u32 header_addr; + u32 load_addr; + u32 load_end_addr; + u32 bss_end_addr; + u32 entry_addr; +}; + +void readseg(u8*, u32, u32); + +void +bootmain(void) +{ + struct mbheader *hdr; + void (*entry)(void); + u32 *x; + u32 n; + + x = (u32*) 0x10000; // scratch space + + // multiboot header must be in the first 8192 bytes + readseg((u8*)x, 8192, 0); + + for (n = 0; n < 8192/4; n++) + if (x[n] == 0x1BADB002) + if ((x[n] + x[n+1] + x[n+2]) == 0) + goto found_it; + + // failure + return; + +found_it: + hdr = (struct mbheader *) (x + n); + + if (!(hdr->flags & 0x10000)) + return; // does not have load_* fields, cannot proceed + if (hdr->load_addr > hdr->header_addr) + return; // invalid; + if (hdr->load_end_addr < hdr->load_addr) + return; // no idea how much to load + + readseg((u8*) hdr->load_addr, + (hdr->load_end_addr - hdr->load_addr), + (n * 4) - (hdr->header_addr - hdr->load_addr)); + + if (hdr->bss_end_addr > hdr->load_end_addr) + stosb((void*) hdr->load_end_addr, 0, + hdr->bss_end_addr - hdr->load_end_addr); + + // Call the entry point from the multiboot header. + // Does not return! + entry = (void(*)(void))(hdr->entry_addr); + entry(); +} + +void +waitdisk(void) +{ + // Wait for disk ready. + while((inb(0x1F7) & 0xC0) != 0x40) + ; +} + +// Read a single sector at offset into dst. +void +readsect(void *dst, u32 offset) +{ + // Issue command. + waitdisk(); + outb(0x1F2, 1); // count = 1 + outb(0x1F3, offset); + outb(0x1F4, offset >> 8); + outb(0x1F5, offset >> 16); + outb(0x1F6, (offset >> 24) | 0xE0); + outb(0x1F7, 0x20); // cmd 0x20 - read sectors + + // Read data. + waitdisk(); + insl(0x1F0, dst, SECTSIZE/4); +} + +// Read 'count' bytes at 'offset' from kernel into physical address 'pa'. +// Might copy more than asked. +void +readseg(u8* pa, u32 count, u32 offset) +{ + u8* epa; + + epa = pa + count; + + // Round down to sector boundary. + pa -= offset % SECTSIZE; + + // Translate from bytes to sectors; kernel starts at sector 1. + offset = (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(; pa < epa; pa += SECTSIZE, offset++) + readsect(pa, offset); +} diff --git a/kernel/console.c b/kernel/console.c new file mode 100644 index 0000000..6fafc39 --- /dev/null +++ b/kernel/console.c @@ -0,0 +1,203 @@ +// Console input and output. +// Input is from the keyboard or serial port. +// Output is written to the screen and serial port. + +#include + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "traps.h" +#include "memlayout.h" +#include "mmu.h" +#include "proc.h" +#include "x86.h" + +static void consputc(int); + +static int panicked = 0; + +/*static struct { + struct spinlock lock; + int locking; +} cons;*/ + +static char digits[] = "0123456789abcdef"; + +static void +printptr(uintp x) { + int i; + for (i = 0; i < (sizeof(uintp) * 2); i++, x <<= 4) + consputc(digits[x >> (sizeof(uintp) * 8 - 4)]); +} + +static void +printint(int xx, int base, int sign) +{ + char buf[16]; + int i; + u32 x; + + if(sign && (sign = xx < 0)) + x = -xx; + else + x = xx; + + i = 0; + do{ + buf[i++] = digits[x % base]; + }while((x /= base) != 0); + + if(sign) + buf[i++] = '-'; + + while(--i >= 0) + consputc(buf[i]); +} +//PAGEBREAK: 50 + +static void printlong(u64 xx, int base, int sign){ + static char digits[] = "0123456789abcdef"; + char buf[16]; + int i; + u64 x; + + if(sign && (sign = xx < 0)) + x = -xx; + else + x = xx; + + i = 0; + do{ + buf[i++] = digits[x % base]; + }while((x /= base) != 0); + + if(sign) + buf[i++] = '-'; + + while(--i >= 0) + consputc(buf[i]); +} + +// Print to the console. only understands %d, %x, %p, %s. +void +cprintf(char *fmt, ...) +{ + va_list ap; + int i, c; //locking; + char *s; + + va_start(ap, fmt); + + //locking = cons.locking; + //if(locking) + // acquire(&cons.lock); + + if (fmt == 0) + panic("null fmt"); + + for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ + if(c != '%'){ + consputc(c); + continue; + } + c = fmt[++i] & 0xff; + if(c == 0) + break; + switch(c){ + case 'd': + printint(va_arg(ap, int), 10, 1); + break; + case 'x': + printint(va_arg(ap, int), 16, 0); + break; + case 'l': + printlong(va_arg(ap, u64), 16, 0); + break; + case 'p': + printptr(va_arg(ap, uintp)); + break; + case 's': + if((s = va_arg(ap, char*)) == 0) + s = "(null)"; + for(; *s; s++) + consputc(*s); + break; + case '%': + consputc('%'); + break; + default: + // Print unknown % sequence to draw attention. + consputc('%'); + consputc(c); + break; + } + } + + //if(locking) + // release(&cons.lock); +} + +void +panic(char *s) +{ + //int i; + //u64 pcs[10]; + + cli(); + //cons.locking = 0; + //cprintf("cpu%d: panic: ", cpu->id); + cprintf(s); + cprintf("\n"); + //getcallerpcs(&s, pcs); + //for(i=0; i<10; i++) + // cprintf(" %p", pcs[i]); + panicked = 1; // freeze other CPU + for(;;) + ; +} + +//PAGEBREAK: 50 +#define BACKSPACE 0x100 +#define CRTPORT 0x3d4 +static u16 *crt = (u16*)P2V(0xb8000); // CGA memory + +static void +cgaputc(int c) +{ + int pos; + + // Cursor position: col + 80*row. + outb(CRTPORT, 14); + pos = inb(CRTPORT+1) << 8; + outb(CRTPORT, 15); + pos |= inb(CRTPORT+1); + + if(c == '\n') + pos += 80 - pos%80; + else if(c == BACKSPACE){ + if(pos > 0) --pos; + } else + crt[pos++] = (c&0xff) | 0x0700; // black on white + + if((pos/80) >= 24){ // Scroll up. + memmove(crt, crt+80, sizeof(crt[0])*23*80); + pos -= 80; + memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos)); + } + + outb(CRTPORT, 14); + outb(CRTPORT+1, pos>>8); + outb(CRTPORT, 15); + outb(CRTPORT+1, pos); + crt[pos] = ' ' | 0x0700; +} + +void +consputc(int c) +{ + cgaputc(c); +} + +#define C(x) ((x)-'@') // Control-x + diff --git a/kernel/entry64.S b/kernel/entry64.S new file mode 100644 index 0000000..b1cca90 --- /dev/null +++ b/kernel/entry64.S @@ -0,0 +1,173 @@ +/* entry64.S + * + * Copyright (c) 2013 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define mboot_magic 0x1badb002 +#define mboot_flags 0x00010000 + +.code32 +.global mboot_header +.global mboot_entry + +mboot_header: + .long mboot_magic + .long mboot_flags + .long (-mboot_magic -mboot_flags) # checksum + .long mboot_load_addr # header_addr + .long mboot_load_addr + .long mboot_load_end + .long mboot_bss_end + .long mboot_entry_addr + +mboot_entry: + +# zero 4 pages for our bootstrap page tables + xor %eax, %eax + mov $0x1000, %edi + mov $0x5000, %ecx + rep stosb + +# P4ML[0] -> 0x2000 (PDPT-A) + mov $(0x2000 | 3), %eax + mov %eax, 0x1000 + +# P4ML[511] -> 0x3000 (PDPT-B) + mov $(0x3000 | 3), %eax + mov %eax, 0x1FF8 + +# PDPT-A[0] -> 0x4000 (PD) + mov $(0x4000 | 3), %eax + mov %eax, 0x2000 + +# PDPT-B[510] -> 0x4000 (PD) + mov $(0x4000 | 3), %eax + mov %eax, 0x3FF0 + +# PD[0..511] -> 0..1022MB + mov $0x83, %eax + mov $0x4000, %ebx + mov $512, %ecx +ptbl_loop: + mov %eax, (%ebx) + add $0x200000, %eax + add $0x8, %ebx + dec %ecx + jnz ptbl_loop + +# Clear ebx for initial processor boot. +# When secondary processors boot, they'll call through +# entry32mp (from entryother), but with a nonzero ebx. +# We'll reuse these bootstrap pagetables and GDT. + xor %ebx, %ebx + +.global entry32mp +entry32mp: +# CR3 -> 0x1000 (P4ML) + mov $0x1000, %eax + mov %eax, %cr3 + + lgdt (gdtr64 - mboot_header + mboot_load_addr) + +# Enable PAE - CR4.PAE=1 + mov %cr4, %eax + bts $5, %eax + mov %eax, %cr4 + +# enable long mode - EFER.LME=1 + mov $0xc0000080, %ecx + rdmsr + bts $8, %eax + wrmsr + +# enable paging + mov %cr0, %eax + bts $31, %eax + mov %eax, %cr0 + +# shift to 64bit segment + ljmp $8,$(entry64low - mboot_header + mboot_load_addr) + +.align 16 +gdtr64: + .word gdt64_end - gdt64_begin - 1; + .quad gdt64_begin - mboot_header + mboot_load_addr + +.align 16 +gdt64_begin: + .long 0x00000000 # 0: null desc + .long 0x00000000 + .long 0x00000000 # 1: Code, R/X, Nonconforming + .long 0x00209800 + .long 0x00000000 # 2: Data, R/W, Expand Down + .long 0x00009000 +gdt64_end: + +.align 16 +.code64 +entry64low: + movq $entry64high, %rax + jmp *%rax + +.global _start +_start: +entry64high: + +# ensure data segment registers are sane + xor %rax, %rax + mov %ax, %ss + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + +# check to see if we're booting a secondary core + test %ebx, %ebx + jnz entry64mp + +# setup initial stack + mov $0xFFFFFFFF80010000, %rax + mov %rax, %rsp + +# enter main() + jmp main + +.global __deadloop +__deadloop: +# we should never return here... + jmp . + +entry64mp: +# obtain kstack from data block before entryother + mov $0x7000, %rax + mov -16(%rax), %rsp + +.global wrmsr +wrmsr: + mov %rdi, %rcx # arg0 -> msrnum + mov %rsi, %rax # val.low -> eax + shr $32, %rsi + mov %rsi, %rdx # val.high -> edx + wrmsr + retq + diff --git a/kernel/kernel64.ld b/kernel/kernel64.ld new file mode 100644 index 0000000..143beed --- /dev/null +++ b/kernel/kernel64.ld @@ -0,0 +1,65 @@ +/* Simple linker script for the JOS kernel. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +/* OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +mboot_load_addr = 0x00100000; + +SECTIONS +{ + /* Link the kernel at this address: "." means the current address */ + /* Must be equal to KERNLINK */ + . = 0xFFFFFFFF80100000; + + PROVIDE(begin = .); + + .text : AT(mboot_load_addr) { + *(.text .rela.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); + + /* Conventionally, Unix linkers provide pseudo-symbols + * etext, edata, and end, at the end of the text, data, and bss. + * For the kernel mapping, we need the address at the beginning + * of the data section, but that's not one of the conventional + * symbols, because the convention started before there was a + * read-only rodata section between text and data. */ + PROVIDE(data = .); + + /* The data segment */ + .data : { + *(.data) + } + + . = ALIGN(0x1000); + + PROVIDE(edata = .); + + .bss : { + *(.bss) + *(COMMON) + } + + . = ALIGN(0x1000); + + PROVIDE(end = .); + + /DISCARD/ : { + *(.eh_frame .rela.eh_frame .note.GNU-stack) + } +} + +mboot_load_end = mboot_load_addr + (edata - begin); +mboot_bss_end = mboot_load_addr + (end - begin); +mboot_entry_addr = mboot_load_addr + (mboot_entry - begin); diff --git a/kernel/main.c b/kernel/main.c new file mode 100644 index 0000000..e1ab3ef --- /dev/null +++ b/kernel/main.c @@ -0,0 +1,10 @@ +#include "types.h" +#include "defs.h" +#include "mmu.h" +#include "memlayout.h" +#include "proc.h" + +int main(void){ + memblock_init(); + panic("ok"); +} diff --git a/kernel/memblock.c b/kernel/memblock.c new file mode 100644 index 0000000..945b978 --- /dev/null +++ b/kernel/memblock.c @@ -0,0 +1,285 @@ +#include "types.h" +#include "memblock.h" +#include "memlayout.h" +#include "mmu.h" +#include "defs.h" + +#define clamp(val, lo, hi) min( (typeof(val))(max(val, lo)),hi) + +// Insert a region to the regions list +int memblock_insert_region(struct memblock_type *type, int idx, u64 base, u64 size){ + struct memblock_region* rgn = &type->regions[idx]; + memmove(rgn + 1, rgn, (type->cnt -idx)* sizeof(*rgn)); + rgn->base = base; + rgn->size = size; + + type->cnt++; + type->total_size+=size; +} + +// Merge the adjacent and continuous regions +int memblock_merge_regions(struct memblock_type *type){ + int i = 0; + + while(i < type->cnt -1){ + struct memblock_region* this = &type->regions[i]; + struct memblock_region* next = &type->regions[i+1]; + + if(this->base + this->size != next->base){ + i++; + continue; + } + + this->size += next->size; + memmove(next, next + 1, (type->cnt -(i+2) * sizeof(*next))); + } +} + +void memblock_remove_region(struct memblock_type* type, u64 i){ + type->total_size -= type->regions[i].size; + memmove(&type->regions[i], &type->regions[i+1], + type->cnt - (i + 1) * sizeof( type->regions[i] )); + type->cnt--; + + if(type->cnt == 0){ + type->cnt = 1; + type->regions[0].base = 0; + type->regions[0].size = 0; + } +} + +// The entry is sorted in default +int memblock_add_regions(struct memblock_type *type,u64 base,u64 size){ + int insert = 0; + u64 obase = base; + u64 end = base + size; + int idx, nr_new; + struct memblock_region* rgn; + + if(!size)return 0; + + if(type->regions[0].size == 0){ + type->regions[0].base = base; + type->regions[0].size = size; + type->total_size = size; + return 0; + } + repeat: + base = obase; + nr_new = 0; + for_each_memblock_type(idx, type, rgn){ + u64 rbase = rgn->base; + u64 rend = rbase + rgn->size; + + if(rbase >= end) + break; + if(rend <= base) + continue; + if(rbase > base){ + nr_new++; + if(insert) + memblock_insert_region(type, idx++, base, rbase-base); + } + base = min(rend, end); + } + + if(base < end){ + nr_new++; + if(insert) + memblock_insert_region(type, idx, base, end-base); + } + + if(!nr_new) + return 0; + + if(!insert){ + if(type->cnt + nr_new > type->max) + //panic(); + insert = 1; + goto repeat; + } + else{ + memblock_merge_regions(type); + return 0; + } +} + +int memblock_add(u64 base, u64 size){ + return memblock_add_regions(&memblock.memory, base, size); +} + +int memblock_reserve(u64 base, u64 size){ + return memblock_add_regions(&memblock.reserved, base, size); +} + +void __next_mem_range_rev(u64* idx, struct memblock_type* type_a, struct memblock_type* type_b, u64 *out_start, u64 *out_end){ + int idx_a = *idx & 0xffffffff; + int idx_b = *idx >> 32; + + if (*idx == (u64)ULLONG_MAX){ + idx_a = type_a->cnt-1; + idx_b = type_b->cnt; + } + + for(; idx_a >= 0; idx_a--){ + struct memblock_region* m = &type_a->regions[idx_a]; + + u64 m_start = m->base; + u64 m_end = m->base + m->size; + + for(; idx_b >= 0; idx_b--){ + struct memblock_region* r; + u64 r_start; + u64 r_end; + + r = &type_b->regions[idx_b]; + r_start = idx_b ? r[-1].base + r[-1].size : 0; + r_end = idx_b < type_b->cnt ? r->base : ULLONG_MAX; + + if(r_end <= m_start) + break; + if(m_end > r_start){ + *out_start = max(m_start, r_start); + *out_end = min(m_end, r_end); + + if(m_start >= r_start) + idx_a--; + else + idx_b--; + *idx = (u32)idx_a | (u64)idx_b << 32; + return; + } + } + } + *idx = ULLONG_MAX; +} + +u64 __memblock_find_range_top_down(u64 start, u64 end, u64 size, u64 align){ + u64 this_start, this_end, cand; + u64 i; + + for_each_free_mem_range_reserve(i, &this_start, &this_end){ + this_start = clamp(this_start, start, end); + this_end = clamp(this_end, start, end); + + // The data is unsigned, so need to judge + if(this_end < size) + continue; + + //cand = round_down(this_end - size, align); + cand = 1; + if(cand >= this_start) + return cand; + } + + return 0; +} + +u64 memblock_find_in_range(u64 size, u64 align, u64 start, u64 end){ + start = max(start, PGSIZE); + end = max(start, end); + + return __memblock_find_range_top_down(start, end, size, align); +} + +static u64 memblock_alloc_range(u64 size, u64 align, u64 start, u64 end){ + u64 found; + + found = memblock_find_in_range(size, align, start, end); + if(found && !memblock_reserve(found,size)){ + //kmemleak_alloc_phys(found, size, 0, 0); + return found; + } + return 0; +} + +u64 __memblock_alloc_base(u64 size, u64 align, u64 max_addr){ + return memblock_alloc_range(size, align, 0, max_addr); +} + +u64 memblock_alloc_base(u64 size, u64 align, u64 max_addr){ + u64 alloc; + + alloc = __memblock_alloc_base(size, align, max_addr); + + if (alloc == 0) + //panic(); + return alloc; +} + +u64 memblock_alloc(u64 size, u64 align){ + return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE); +} + +int memblock_isolate_range(struct memblock_type* type, u64 base, u64 size, int *start_rgn, int *end_rgn){ + u64 end = base + size; + int idx; + struct memblock_region* rgn; + + *start_rgn = *end_rgn = 0; + + if(!size) + return 0; + + for_each_memblock_type(idx, type, rgn){ + + u64 rbase = rgn->base; + u64 rend = rbase + rgn->size; + + if(rbase >= end) + break; + if(rend <= base) + continue; + + if (rbase < base){ + rgn->base = base; + rgn->size -= base - rbase; + type->total_size -= base - rbase; + memblock_insert_region(type, idx, rbase, base - rbase); + } + else if(rend > end){ + rgn->base = end; + rgn->size -= end - rbase; + type->total_size -= end - rbase; + memblock_insert_region(type, idx--, rbase, end - rbase); + } else{ + if (!*end_rgn) + *start_rgn = idx; + *end_rgn = idx + 1; + } + } +} + +int memblock_remove_range(struct memblock_type* type, u64 base, u64 size){ + int start_rgn, end_rgn; + int i,ret; + + ret = memblock_isolate_range(type, base, size, &start_rgn, &end_rgn); + if(ret) + return ret; + + for(i = end_rgn - 1; i >= start_rgn; i--) + memblock_remove_region(type, i); +} + +int memblock_free(u64 base, u64 size){ + u64 end = base + size -1; + + //kmemleak_free_part_phys(base, size); + return memblock_remove_range(&memblock.reserved, base, size); +} + +void memblock_init(){ + struct MEMORY_E820* ARDS = (struct MEMORY_E820*)(KERNBASE+ARDSOFFSET); + u32 mem_tot = 0; + for(int i=0; i < 32; i++){ + if(ARDS->map[i].type < 1 || ARDS->map[i].type > 4) break; + mem_tot += ARDS->map[i].len; + cprintf("%l %l %x\n", ARDS->map[i].addr, ARDS->map[i].addr+ARDS->map[i].len, ARDS->map[i].type); + if(ARDS->map[i].type == 1){ + memblock_add(ARDS->map[i].addr, ARDS->map[i].len); + //cprintf("%x %x\n", ARDS->map[i].addr, ARDS->map[i].len); + } + } + cprintf("%dMB\n",mem_tot/1048576 + 1); +} diff --git a/kernel/physical_memory.c b/kernel/physical_memory.c new file mode 100644 index 0000000..1c3c198 --- /dev/null +++ b/kernel/physical_memory.c @@ -0,0 +1,72 @@ +#include "mmu.h" + +void add_list(int order, struct list_head* list); +void delete_list(struct list_head* list); + +static inline u64 find_buddy_pfn(u64 page_pfn, int order){ + return page_pfn ^ (1 << order); +} +void expand_buudy(int order, int order_now, struct list_head* list){ + while(order != order_now){ + // get pages_ + struct page* right_pages = (struct page*)list; + add_list(order_now - 1, list + (1U << (order_now - 1))); + order_now --; + } +} + +void add_list(int order,struct list_head* list){ + list->pre = &zone1.free_area[order].free_list; + list->nex = zone1.free_area[order].free_list->nex; + list->nex->pre = list; + zone1.free_area[order].free_list->nex = list; +} + +void delete_list(struct list_head* list){ + list->pre->nex = list->nex; + list->nex->pre = list->pre; + list->pre = NULL; + list->nex = NULL; +} + +struct page* buddy_allocate(int order){ + if(order > 11) return 0; + + struct page* rt_page; + + int nr_pages = 1U << order; + found: + if(zone1.free_area[order].nr_free != 0){ + zone1.free_area[order].nr_free--; + rt_page = (struct page*) &(zone1.free_area[order].free_list->nex); + delete_list(&zone1.free_area[order].free_list->nex); + return rt_page; // return pages + } + for(int i = order+1; i <= 11; ++i){ + if(zone1.free_area[i].nr_free!=0){ + zone1.free_area[i].nr_free--; + expand_buudy(order, i, &zone1.free_area[order].free_list->nex); + delete_list(&zone1.free_area[order].free_list->nex); + goto found; + } + } + + return 0; +} + +static inline int page_is_buddy(){ + +} + +void free_buddy(struct page* pages,int pfn, int order, struct list_head* list){ + + u64 buddy_pfn = find_buddy_pfn(pfn,order); + while(order < BUDDY_TYPES - 1){ + + } +} + +void init_buddy(){ + +} + diff --git a/kernel/proc.c b/kernel/proc.c new file mode 100644 index 0000000..6f8a9b3 --- /dev/null +++ b/kernel/proc.c @@ -0,0 +1,10 @@ +#include "proc.h" +#include "defs.h" +#include "x86.h" +#include "types.h" +#include "memlayout.h" +#include "mmu.h" + +void test(){ + panic("test!!!"); +} \ No newline at end of file diff --git a/kernel/slab/mem.h b/kernel/slab/mem.h new file mode 100644 index 0000000..978b6d2 --- /dev/null +++ b/kernel/slab/mem.h @@ -0,0 +1,27 @@ +struct Slab_cache{ + u64 size; + u64 total_using; + u64 total_free; + struct Slab* cache_pool; + struct Slab* cache_dma_pool; + void* (* constructor)(void* Vaddr,u64 arg); + void* (* destructor)(void* Vaddr,u64 arg); +}; + +struct Slab{ + struct List_head list; + struct Page* page; + + u64 using_count; + u64 free_count; + + void* vaddr; + u64 map_length; + u64 map_count; + u64* color_map; +}; + +struct mem_manager{ + +}; + diff --git a/kernel/slab/memory.h b/kernel/slab/memory.h new file mode 100644 index 0000000..e902789 --- /dev/null +++ b/kernel/slab/memory.h @@ -0,0 +1,146 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#define PAGE_2M_SHIFT 21 +#define PAGE_2M_SIZE (1UL << PAGE_2M_SHIFT) +#define PAGE_4K_SIZE (1UL << PAGE_4K_SHIFT) +#define PAGE_2M_MASK (~(PAGE_2M_SIZE - 1)) +#define PAGE_4K_MASK (~(PAGE_4K_SIZE - 1)) +#define PAGE_OFFSET ((unsigned long)BASE) + +#define AREABASE 0xffff800000000000 +#define MAPBASE 0xffff800000000200 +#define TABLE3BASE 0x12000 +#define AREASIZE 10 + +int AREANUM; + +struct MEMORY_E820{ + unsigned long addr; + unsigned long len; + unsigned int type; +}__attribute__((packed)); + +struct PAGE{ + unsigned long virtual_addr; + unsigned long physical_addr; + unsigned long attr; +}; + +struct SLAB{ + struct PAGE* page; + unsigned long free_count; + unsigned long used_count; + unsigned long map_len; + unsigned char* bitmap; + struct SLAB* prev, *next; +}; + +struct SLAB_CACHE{ + struct SLAB* cache_pool; + unsigned long free_slab; + unsigned long used_slab; + int size; +}Slab_cache[16]; + +struct AREA{ + unsigned long physical_addr; + unsigned long virtual_addr; + unsigned long free_page; + unsigned long end; +}; + +struct MEMORY_MGR{ + struct AREA *area[AREASIZE]; + struct PAGE* pages; + unsigned long free_page; + unsigned long used_page; + unsigned char* bitmap; + unsigned long kernel_start; + unsigned long kernel_end; + int map_len; + int total_page; + int total_slab; +}memory_manager; + + +void InitMemory(); +unsigned long kmalloc(int size); +void kfree(unsigned long addr); + + +struct PTABLE1{ + struct PTABLE2* next; +}__attribute__((packed)); +struct PTABLE2{ + unsigned long* physical_addr; +}__attribute__((packed)); + + + + + + + + + +#define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) &\ + PAGE_2M_MASK) + +// 虚拟地址与物理地址的相互转换 +#define Virt_To_Phy(addr) ((unsigned long)(addr) - PAGE_OFFSET) +#define Phy_To_Virt(addr) ((unsigned long)((unsigned long)(addr) + \ +PAGE_OFFSET)) + +////页表属性 +// bit 63 Execution Disable: +#define PAGE_XD (unsigned long)0x1000000000000000 +// bit 12 Page Attribute Table +#define PAGE_PAT (unsigned long)0x1000 +// bit 8 Global Page:1,global;0,part +#define PAGE_Global (unsigned long)0x0100 +// bit 7 Page Size:1,big page;0,small page; +#define PAGE_PS (unsigned long)0x0080 +// bit 6 Dirty:1,dirty;0,clean; +#define PAGE_Dirty (unsigned long)0x0040 +// bit 5 Accessed:1,visited;0,unvisited; +#define PAGE_Accessed (unsigned long)0x0020 +// bit 4 Page Level Cache Disable +#define PAGE_PCD (unsigned long)0x0010 +// bit 3 Page Level Write Through +#define PAGE_PWT (unsigned long)0x0008 +// bit 2 User Supervisor:1,user and supervisor;0,supervisor; +#define PAGE_U_S (unsigned long)0x0004 +// bit 1 Read Write:1,read and write;0,read; +#define PAGE_R_W (unsigned long)0x0002 +// bit 0 Present:1,present;0,no present; +#define PAGE_Present (unsigned long)0x0001 + +//1,0 +#define PAGE_KERNEL_GDT (PAGE_R_W | PAGE_Present) +//1,0 +#define PAGE_KERNEL_DIR (PAGE_R_W | PAGE_Present) +//7,1,0 +#define PAGE_KERNEL_PAGE (PAGE_PS | PAGE_R_W | PAGE_Present) +//2,1,0 +#define PAGE_USER_DIR (PAGE_U_S | PAGE_R_W | PAGE_Present) +//7,2,1,0 +#define PAGE_USER_PAGE (PAGE_PS | PAGE_U_S | PAGE_R_W | PAGE_Present) + + + + + + + + + + + + + + + + + +#endif \ No newline at end of file diff --git a/kernel/slab/slab.c b/kernel/slab/slab.c new file mode 100644 index 0000000..f96a025 --- /dev/null +++ b/kernel/slab/slab.c @@ -0,0 +1,393 @@ +#include "memory.h" +#include "io.h" +#include "lib.h" + +extern char _text; +extern char _etext; +extern char _edata; +extern char _end; + +void search_memory(); +void init_pages(); +void free_page(struct Page*); +struct Page* alloc_page(u64 attr); +struct Page* use_page(int n, u64 attr); + +void init_slab(); +u64 alloc_slab(int i, int size); +struct Slab* create_slab(int size); + +void append_slab(struct Slab* slab, int size); +void delete_slab(struct Slab* slab, int size); + +void init_page_table(); + +void InitMemory(){ + search_memory(); + + init_pages(); + init_slab(); + init_page_table(); +} + +void init_pages(){ + unsigned char* bit_start = memory_manager.bitmap; + struct Page* page = (struct PAGE*)(bit_start + memory_manager.map_len); + memory_manager.pages = page; + u64 i; + //cprintf("bit_start at %ux\n", (u64)bit_start); + + memset(memory_manager.bitmap, 0, memory_manager.map_len); + for (i = 0; i * PAGE_2M_SIZE < Virt_To_Phy(memory_manager.kernel_end); i++){ + use_page(i, PAGE_KERNEL_PAGE); + } + //cprintf("free page start at num %d\n", i); + + u64 pre = i; + for (i = 0; i < AREANUM; i++){ + if (memory_manager.area[i]->free_page <= 0) + continue; + u64 start = memory_manager.area[i]->physical_addr; + u64 idx = start / PAGE_2M_SIZE; + + if (idx < pre) + continue; + for( ; pre < idx; pre++){ + //,cprintf("set %ld\n", pre); + use_page(pre, PAGE_KERNEL_PAGE); + } + for( ; idx <= memory_manager.area[i]->free_page; idx++){ + memory_manager.pages[idx].physical_addr = idx * PAGE_2M_SIZE; + memory_manager.pages[idx].virtual_addr = Phy_To_Virt(memory_manager.pages[idx].physical_addr); + memory_manager.pages[idx].attr = 0; + } + pre = memory_manager.area[i]->end & PAGE_2M_MASK; + } + use_page(5, PAGE_KERNEL_PAGE); + use_page(6, PAGE_KERNEL_PAGE); + use_page(7, PAGE_KERNEL_PAGE); + //cprintf("page end at %ux\n", &memory_manager.pages[memory_manager.free_page]); +} + +void search_memory(){ + AREANUM = 0; + struct MEMORY_E820* p = 0; + struct AREA* area = (struct AREA*)AREABASE; + memory_manager.kernel_start = (u64) &_text; + memory_manager.kernel_end = (u64) &_end; + u64 end = 0; + int i; + p = (struct MEMORY_E820*)0xffff800000007c00; // wait for change the address + for(i = 0; i < 32; i++){ + //cprintf("address: %ux\tlength: %ux\ttype: %ux\n", p->addr, p->len, p->type); + if(p->type == 1){ + area->physical_addr = PAGE_2M_ALIGN(p->addr); + area->virtual_addr = Phy_To_Virt(area->physical_addr); + area->free_page = (p->len + PAGE_2M_ALIGN(p->addr) - p->addr) / PAGE_2M_SIZE; + area->end = p->addr + p->len; + + //cprintf"Area start at physic: %ux, virtual: %ux, free: %ux, total_page: %d\n", + //area->physical_addr, area->virtual_addr, area->free_page, area->free_page); + + memory_manager.area[AREANUM++] = area; + if(end < p->addr + p->len) + end = p->addr + p->len; + } + else if(p->type > 4 || p->type < 1) + break; + p++; + area++; + } + + // cprintf("area end at %ux\n", area); + memory_manager.map_len = (memory_manager.total_page + 63) / 8; + memory_manager.bitmap = (u8 *)MAPBASE; + memory_manager.pages = (struct PAGE*)(MAPBASE + memory_manager.map_len); + memory_manager.total_page = end / PAGE_2M_SIZE; + memory_manager.free_page = memory_manager.total_page; + //cprintf("total memory is %uldMB\n", end / 1024 / 1024); +} + +struct PAGE* use_page(int n, u64 attr){ + if(n >= memory_manager.total_page){ + //cprintf("%d is out of page num\n", n); + return 0; + } + if(memory_manager.bitmap[n / 8] & (u8)(1 << (n % 8))){ + //cprintf("page %d is using\n", n); + return 0; + } + + memory_manager.pages[n].attr = attr; + memory_manager.bitmap[n / 8] |= (u8)(1 << (n % 8)); + + //cprintf("use page %d, start at %ux bitmap %ux\n", n, memory_manager.pages[n].virtual_addr, + // memory_manager.bitmap[n / 8]); + return &memory_manager.pages[n]; +} + +struct PAGE* alloc_page(u64 attr){ + if(memory_manager.free_page <= 0){ + //cprintf("No free page\n"); + return 0; + } + + int i; + for(i = 0;i < memory_manager.map_len; i++){ + if(memory_manager.bitmap[i] >= (u8)(~0)) + continue; + int j; + for(j = 0;j < 8; j++){ + // printf("%ud i:%d j:%d\n", (unsigned int)(memory_manager.bitmap[i] & (1 << j)), i, j); + if(!(memory_manager.bitmap[i] & (u8)(1 << j))){ + memory_manager.used_page++; + memory_manager.free_page--; + return use_page(i * 8 + j, attr); + } + } + } + //cprintf("alloc failed\n"); + return 0; +} + +void free_page(struct PAGE* page){ + if(page == 0){ + //cprintf("Page is null\n"); + return; + } + + int i; + for(i = 0;i < memory_manager.total_page; i++){ + if(&memory_manager.pages[i] == page){ + memory_manager.bitmap[i / 8] &= (~(u8)(1 << (i % 8))); + // cprintf("Free page %d at %ux\n", i, page->virtual_addr); + return; + } + } + + //cprintf("free page fail\n"); +} + +void init_slab(){ + int i, size = 32; + for(i = 0;i < 16; i++){ + Slab_cache[i].size = size; + Slab_cache[i].free_slab = 0; + Slab_cache[i].used_slab = 0; + Slab_cache[i].cache_pool = 0; + size *= 2; + } +} + +// Alloc pages from slab cache pool +u64 kmalloc(int size){ + int i, level = 32; + for(i = 0;i < 16; i++){ + if(level >= size){ + // cprintf("addr at %ux\n", tmp); + return alloc_slab(i, level); + } + level *= 2; + } + //cprintf("size too large %ux\n", size); + return 0; +} + +// +u64 alloc_slab(int i, int size){ + struct SLAB* slab = Slab_cache[i].cache_pool; + + while(slab != 0 && slab->free_count <= 0){ + slab = slab->next; + } + if(slab == 0){ + slab = create_slab(size); + } + if(slab == 0) + return 0; + + int total = slab->free_count + slab->used_count; + for(i = 0;i < total;i++){ + if (slab->bitmap[i / 8] == (u8)(~0)) + continue; + + if(!(slab->bitmap[i / 8] & (1 << (i % 8)))){ + slab->free_count--; + slab->used_count++; + slab->bitmap[i / 8] |= (u8)(1 << (i % 8)); + // cprintf("addr at %ux\n", (u64)slab->page->virtual_addr + i * size); + return (u64)slab->page->physical_addr + i * size; + } + } + + //cprintf("alloc slab error\n"); + return 0; +} + +struct SLAB* create_slab(int size){ + struct PAGE* page = alloc_page(PAGE_KERNEL_PAGE); + struct SLAB* slab = 0; + if (page == 0) + return 0; + + switch (size){ + case 32: + case 64: + case 128: + case 256: + case 512: + slab = (struct SLAB *)page->virtual_addr; + slab->page = page; + slab->bitmap = (u8 *)(page->virtual_addr + sizeof(struct SLAB)); + slab->free_count = (u64)PAGE_2M_SIZE / size; + slab->map_len = slab->free_count / 8; + slab->used_count = (slab->map_len + size - 1 + sizeof(struct SLAB)) / size; + slab->free_count -= slab->used_count; + + // cprintf("slab used_count %d, map_len %d\n", slab->used_count, slab->map_len); + memset(slab->bitmap, 0, slab->map_len); + int i; + for(i = 0;i < slab->used_count; i++){ + slab->bitmap[i / 8] |= (u8)(1 << (i % 8)); + } + slab->next = 0; + // cprintf("slab at %ux\n", slab->page->virtual_addr); + + break; + + case 1024: //1KB + case 2048: + case 4096: //4KB + case 8192: + case 16384: + case 32768: + case 65536: + case 131072: //128KB + case 262144: + case 524288: + case 1048576: //1MB + slab = (struct SLAB *)kmalloc(sizeof(struct SLAB)); + if (slab == 0){ + //cprintf("create slab fail\n"); + return 0; + } + slab->map_len = slab->free_count / 8; + slab->bitmap = (u8*)kmalloc(slab->map_len); + slab->page = page; + slab->free_count = (u64)PAGE_2M_SIZE / size; + slab->used_count = 0; + + memset(slab->bitmap, 0, slab->map_len); + + break; + + default: + //cprintf("size error %d\n", size); + break; + } + append_slab(slab, size); + + return slab; +} + +void append_slab(struct SLAB* slab, int size){ + int i; + int level = 32; + for(i = 0;i < 16; i++){ + if(level == size) + break; + level *= 2; + } + if(i == 16){ + //cprintf("end of slab size error %d\n", size); + return; + } + + struct SLAB* tail = Slab_cache[i].cache_pool; + if(tail == 0){ + Slab_cache[i].cache_pool = slab; + return; + } + + while(tail->next != 0){ + tail = tail->next; + } + tail->next = slab; + slab->prev = tail; + Slab_cache[i].free_slab++; +} + +void delete_slab(struct SLAB* slab, int size){ + if(slab->prev == 0) + return; + + slab->prev->next = slab->next; + slab->next->prev = slab->prev; + + switch(size){ + case 32: + case 64: + case 128: + case 256: + case 512: + free_page(slab->page); + break; + + default: + kfree((u64)slab); + break; + } +} + +void kfree(u64 addr){ + u64 pagebase = addr & PAGE_2M_MASK; + int i; + + for(i = 0; i < 16; i++){ + // Cprintf("search slab\n"); + struct SLAB * slab = Slab_cache[i].cache_pool; + + while(slab != NULL){ + if(slab->page->virtual_addr == pagebase){ + // cprintf("find at %d\n", i); + int idx = (addr - pagebase) / Slab_cache[i].size; + slab->bitmap[idx / 8] &= (unsigned char)(~(1 << idx)); + slab->free_count++; + slab->used_count--; + + if(slab->used_count == 0 && slab->free_count * 3 / 2 <= Slab_cache[i].free_slab + && Slab_cache[i].cache_pool != slab){ + delete_slab(slab, Slab_cache[i].size); + } + + return; + } + slab = slab->next; + } + } + + //cprintf("free error\n"); +} + +void init_page_table(){ + struct PTABLE1 * page_table1 = Phy_To_Virt(Get_CR3()); + struct PTABLE2 * page_table2; + page_table1 += 16 * 16; + + if (page_table1->next != 0) + page_table2 = (struct PTABLE2*)Phy_To_Virt(page_table1->next); + + u64* addr = Phy_To_Virt(TABLE3BASE); + struct Page* page = memory_manager.pages; + int i; + //cprintf("start\n"); + + for(i = 0; i < memory_manager.total_page; i++){ + if(addr[i] != 0) + continue; + addr[i] = (page[i].physical_addr & (~0xfffUL)) | PAGE_KERNEL_PAGE; + } + + flush_tlb(); + //cprintf("\nfinish table\n"); +} \ No newline at end of file diff --git a/kernel/string.c b/kernel/string.c new file mode 100644 index 0000000..c1b4d5c --- /dev/null +++ b/kernel/string.c @@ -0,0 +1,85 @@ +#include "types.h" +#include "x86.h" + +//assign c to dst - dst+n +void* memset(void *dst, i32 c, u32 n){ + if ((u64)dst%4 == 0 && n%4 == 0){ + c &= 0xFF; + stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4); + }else + stosb(dst, c, n); + return dst; +} + +//compare v1 with v2 return v1 - v2 +int memcmp(const void* v1, const void* v2, u32 n){ + const u8 *s1, *s2; + + s1 = v1; + s2 = v2; + while(n-- > 0){ + if(*s1 != *s2) + return *s1 - *s2; + s1++, s2++; + } + + return 0; +} + +//copy n chars from src to dst +void* memmove(void* dst, const void* src, u32 n){ + const char* s; + char* d; + + s = src; + d = dst; + if(s d){ + s+=n; + d+=n; + while(n-- > 0) + *--d = * --s; + } + else + while(n-- < 0) + *d++ = *s++; +} +//compare string +int strncmp(const char* p, const char* q, u32 n){ + while(n > 0 && *p && *p == *q) + n--, p++, q++; + if(n == 0) + return 0; + return (u8)*p - (u8)*q; +} + +//copy string most n char +char* strncpy(char* s, const char* t, i32 n){ + char* os; + + os = s; + while(n-- > 0 && (*s++ = *t++) != 0) + ; + while(n-- > 0) + *s++ = 0; + return os; +} + +//get the length of string +int strlen(const char* s){ + int n; + for(n = 0; s[n]; n++) + ; + return n; +} + +char* safestrcpy(char* s, const char* t, i32 n){ + char *os; + + os = s; + if(n <= 0) + return os; + while(--n > 0 && (*s++ = *t++) != 0) + ; + *s = 0; + return os; +} diff --git a/kernel/syscall.c b/kernel/syscall.c new file mode 100644 index 0000000..398a2f1 --- /dev/null +++ b/kernel/syscall.c @@ -0,0 +1,22 @@ +#include "syscall.h" +#include "proc.h" +#include "defs.h" +#include "types.h" + +extern int sys_test(void); + +static int (*syscall[])(void) = { + [SYS_test] sys_test, +}; + +void syscall(void){ + int num; + + num = myproc()->tf->rax; + if(num > 0 && num < NELEM(syscalls) && syscalls[num]){ + proc->tf->rax = syscalls[num](); + } + else{ + + } +} \ No newline at end of file diff --git a/kernel/trap.c b/kernel/trap.c new file mode 100644 index 0000000..0d71580 --- /dev/null +++ b/kernel/trap.c @@ -0,0 +1,16 @@ +#include "types.h" +#include "x86.h" +#include "trap.h" +#include "proc.h" + +void trap(struct trapframe* tf){ + if(tf->trapno == T_SYSCALL){ + if(myproc()->killed) + exit(); + myproc()->tf = tf; + syscall(); + if(myproc()->killed) + exit(); + return; + } +} \ No newline at end of file diff --git a/kernel/trapasm.S b/kernel/trapasm.S new file mode 100644 index 0000000..fab3c1e --- /dev/null +++ b/kernel/trapasm.S @@ -0,0 +1,44 @@ + +# vectors.S sends all traps here. +.global alltraps +alltraps: + # Build trap frame. + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rdi + push rsi + push rbp + push rdx + push rcx + push rbx + push rax + + mov rdi, rsp # frame in arg1 + call trap + +.global trapret +trapret: + pop rax + pop rbx + pop rcx + pop rdx + pop rbp + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + add rsp, 16 + iretq diff --git a/kernel/vm.c b/kernel/vm.c new file mode 100644 index 0000000..f5c6812 --- /dev/null +++ b/kernel/vm.c @@ -0,0 +1,77 @@ +#include "types.h" +#include "defs.h" +#include "mmu.h" +#include "memlayout.h" + +// Return the address of the PTE in pml4 +// that corresponds to virtual address va. If alloc!=0, +// create any required page table pages. +static u64* page_walk(u64* root,const void* va,int alloc){ + + u64 *pml4e, *pdpte, *pde, *pte; + + // Page map level 4 index + pml4e = &root[PML4X(va)]; + + if(!(*pml4e & PTE_P)){ + if(!alloc || (pml4e = (u64*)memblock_alloc(PGSIZE, PGSIZE)) == 0) + return 0; + memset(pml4e, 0, PGSIZE); + root[PML4X(va)] = v2p(pml4e) | PTE_P | PTE_W | PTE_U; + } + + // Page directory pointer index + pdpte = &pdpte[PDPTX(va)]; + + if(!(*pdpte & PTE_P)){ + if(!alloc || (pdpte = (u64*)memblock_alloc(PGSIZE, PGSIZE)) == 0) + return 0; + memset(pdpte, 0, PGSIZE); + pml4e[PDPTX(va)] = v2p(pdpte) | PTE_P | PTE_W | PTE_U; + } + + // Page directory index + pde = &pde[PDX(va)]; + + if(!(*pde & PTE_P)){ + if(!alloc || (pde = (u64*)memblock_alloc(PGSIZE, PGSIZE)) == 0) + return 0; + memset(pde, 0, PGSIZE); + pdpte[PDX(va)] = v2p(pde) | PTE_P | PTE_W | PTE_U; + } + + // Page table index + pte = &pte[PTX(va)]; + + if(!(*pte & PTE_P)){ + if(!alloc || (pte = (u64*)memblock_alloc(PGSIZE, PGSIZE)) == 0) + return 0; + memset(pte, 0, PGSIZE); + pde[PTX(va)] = v2p(pte) | PTE_P | PTE_W | PTE_U; + } + + return pte; +} + +// Create PTEs for virtual addresses starting at va that refer to +// physical addresses starting at pa. va and size might not +// be page-aligned. +static int mamppages(u64* pml4, void* va, u64 size, u64 pa, int perm){ + char *first, *last; + u64* pte; + + first = (char*)ALIGN_DOWN(((u64)va), ((u64)PGSIZE)); + last = (char*)ALIGN_DOWN(((u64)va), ((u64)PGSIZE)); + while(1){ + if(pte = page_walk(pml4, first, 1) == 0) + return -1; + if(*pte & PTE_P) + panic("remap"); + *pte = pa | perm | PTE_P; + if(first == last) + break; + first += PGSIZE; + pa += PGSIZE; + } + return 0; +} diff --git a/lang/c/types.h b/lang/c/types.h deleted file mode 100644 index 0ce1d0d..0000000 --- a/lang/c/types.h +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include - -typedef int8_t i8; -typedef int16_t i16; -typedef int32_t i32; -typedef int64_t i64; - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -typedef float f32; -typedef double f64; - -typedef uint8_t byte; -typedef uint16_t word; -typedef uint32_t dword; -typedef uint64_t qword; \ No newline at end of file diff --git a/tools/dot-bochsrc b/tools/dot-bochsrc new file mode 100644 index 0000000..ba13db7 --- /dev/null +++ b/tools/dot-bochsrc @@ -0,0 +1,738 @@ +# You may now use double quotes around pathnames, in case +# your pathname includes spaces. + +#======================================================================= +# CONFIG_INTERFACE +# +# The configuration interface is a series of menus or dialog boxes that +# allows you to change all the settings that control Bochs's behavior. +# There are two choices of configuration interface: a text mode version +# called "textconfig" and a graphical version called "wx". The text +# mode version uses stdin/stdout and is always compiled in. The graphical +# version is only available when you use "--with-wx" on the configure +# command. If you do not write a config_interface line, Bochs will +# choose a default for you. +# +# NOTE: if you use the "wx" configuration interface, you must also use +# the "wx" display library. +#======================================================================= +#config_interface: textconfig +#config_interface: wx + +#======================================================================= +# DISPLAY_LIBRARY +# +# The display library is the code that displays the Bochs VGA screen. Bochs +# has a selection of about 10 different display library implementations for +# different platforms. If you run configure with multiple --with-* options, +# the display_library command lets you choose which one you want to run with. +# If you do not write a display_library line, Bochs will choose a default for +# you. +# +# The choices are: +# x use X windows interface, cross platform +# win32 use native win32 libraries +# carbon use Carbon library (for MacOS X) +# beos use native BeOS libraries +# macintosh use MacOS pre-10 +# amigaos use native AmigaOS libraries +# sdl use SDL library, cross platform +# svga use SVGALIB library for Linux, allows graphics without X11 +# term text only, uses curses/ncurses library, cross platform +# rfb provides an interface to AT&T's VNC viewer, cross platform +# wx use wxWidgets library, cross platform +# nogui no display at all +# +# NOTE: if you use the "wx" configuration interface, you must also use +# the "wx" display library. +# +# Specific options: +# Some display libraries now support specific option to control their +# behaviour. See the examples below for currently supported options. +#======================================================================= +#display_library: amigaos +#display_library: beos +#display_library: carbon +#display_library: macintosh +#display_library: nogui +#display_library: rfb, options="timeout=60" # time to wait for client +#display_library: sdl, options="fullscreen" # startup in fullscreen mode +#display_library: term +#display_library: win32, options="legacyF12" # use F12 to toggle mouse +#display_library: wx +#display_library: x + +#======================================================================= +# ROMIMAGE: +# The ROM BIOS controls what the PC does when it first powers on. +# Normally, you can use a precompiled BIOS in the source or binary +# distribution called BIOS-bochs-latest. The ROM BIOS is usually loaded +# starting at address 0xf0000, and it is exactly 64k long. +# You can also use the environment variable $BXSHARE to specify the +# location of the BIOS. +# The usage of external large BIOS images (up to 512k) at memory top is +# now supported, but we still recommend to use the BIOS distributed with +# Bochs. Now the start address can be calculated from image size. +#======================================================================= +romimage: file=$BXSHARE/BIOS-bochs-latest +#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top +#romimage: file=mybios.bin # calculate start address from image size + +#======================================================================= +# CPU: +# This defines cpu-related parameters inside Bochs: +# +# COUNT: +# Set the number of processors when Bochs is compiled for SMP emulation. +# Bochs currently supports up to 8 processors. If Bochs is compiled +# without SMP support, it won't accept values different from 1. +# +# IPS: +# Emulated Instructions Per Second. This is the number of IPS that bochs +# is capable of running on your machine. You can recompile Bochs with +# --enable-show-ips option enabled, to find your workstation's capability. +# Measured IPS value will then be logged into your log file or status bar +# (if supported by the gui). +# +# IPS is used to calibrate many time-dependent events within the bochs +# simulation. For example, changing IPS affects the frequency of VGA +# updates, the duration of time before a key starts to autorepeat, and +# the measurement of BogoMips and other benchmarks. +# +# Examples: +# Machine Mips +# ________________________________________________________________ +# 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4 12 to 15 Mips +# 1.6Ghz Intel P4 with Win2000/g++ 3.3 5 to 7 Mips +# 650Mhz Athlon K-7 with Linux 2.4.4/egcs-2.91.66 2 to 2.5 Mips +# 400Mhz Pentium II with Linux 2.0.36/egcs-1.0.3 1 to 1.8 Mips +#======================================================================= +cpu: count=2, ips=10000000 + +#======================================================================= +# MEGS +# Set the number of Megabytes of physical memory you want to emulate. +# The default is 32MB, most OS's won't need more than that. +# The maximum amount of memory supported is 2048Mb. +#======================================================================= +#megs: 256 +#megs: 128 +#megs: 64 +megs: 32 +#megs: 16 +#megs: 8 + +#======================================================================= +# OPTROMIMAGE[1-4]: +# You may now load up to 4 optional ROM images. Be sure to use a +# read-only area, typically between C8000 and EFFFF. These optional +# ROM images should not overwrite the rombios (located at +# F0000-FFFFF) and the videobios (located at C0000-C7FFF). +# Those ROM images will be initialized by the bios if they contain +# the right signature (0x55AA) and a valid checksum. +# It can also be a convenient way to upload some arbitrary code/data +# in the simulation, that can be retrieved by the boot loader +#======================================================================= +#optromimage1: file=optionalrom.bin, address=0xd0000 +#optromimage2: file=optionalrom.bin, address=0xd1000 +#optromimage3: file=optionalrom.bin, address=0xd2000 +#optromimage4: file=optionalrom.bin, address=0xd3000 + +#optramimage1: file=/path/file1.img, address=0x0010000 +#optramimage2: file=/path/file2.img, address=0x0020000 +#optramimage3: file=/path/file3.img, address=0x0030000 +#optramimage4: file=/path/file4.img, address=0x0040000 + +#======================================================================= +# VGAROMIMAGE +# You now need to load a VGA ROM BIOS into C0000. +#======================================================================= +#vgaromimage: file=bios/VGABIOS-elpin-2.40 +vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest +#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus + +#======================================================================= +# VGA: +# Here you can specify the display extension to be used. With the value +# 'none' you can use standard VGA with no extension. Other supported +# values are 'vbe' for Bochs VBE and 'cirrus' for Cirrus SVGA support. +#======================================================================= +#vga: extension=cirrus +#vga: extension=vbe +vga: extension=none + +#======================================================================= +# FLOPPYA: +# Point this to pathname of floppy image file or device +# This should be of a bootable floppy(image/device) if you're +# booting from 'a' (or 'floppy'). +# +# You can set the initial status of the media to 'ejected' or 'inserted'. +# floppya: 2_88=path, status=ejected (2.88M 3.5" floppy) +# floppya: 1_44=path, status=inserted (1.44M 3.5" floppy) +# floppya: 1_2=path, status=ejected (1.2M 5.25" floppy) +# floppya: 720k=path, status=inserted (720K 3.5" floppy) +# floppya: 360k=path, status=inserted (360K 5.25" floppy) +# floppya: 320k=path, status=inserted (320K 5.25" floppy) +# floppya: 180k=path, status=inserted (180K 5.25" floppy) +# floppya: 160k=path, status=inserted (160K 5.25" floppy) +# floppya: image=path, status=inserted (guess type from image size) +# +# The path should be the name of a disk image file. On Unix, you can use a raw +# device name such as /dev/fd0 on Linux. On win32 platforms, use drive letters +# such as a: or b: as the path. The parameter 'image' works with image files +# only. In that case the size must match one of the supported types. +#======================================================================= +floppya: 1_44=/dev/fd0, status=inserted +#floppya: image=../1.44, status=inserted +#floppya: 1_44=/dev/fd0H1440, status=inserted +#floppya: 1_2=../1_2, status=inserted +#floppya: 1_44=a:, status=inserted +#floppya: 1_44=a.img, status=inserted +#floppya: 1_44=/dev/rfd0a, status=inserted + +#======================================================================= +# FLOPPYB: +# See FLOPPYA above for syntax +#======================================================================= +#floppyb: 1_44=b:, status=inserted +floppyb: 1_44=b.img, status=inserted + +#======================================================================= +# ATA0, ATA1, ATA2, ATA3 +# ATA controller for hard disks and cdroms +# +# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number +# +# These options enables up to 4 ata channels. For each channel +# the two base io addresses and the irq must be specified. +# +# ata0 and ata1 are enabled by default with the values shown below +# +# Examples: +# ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +# ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 +# ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 +# ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9 +#======================================================================= +ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 +ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 +ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 + +#======================================================================= +# ATA[0-3]-MASTER, ATA[0-3]-SLAVE +# +# This defines the type and characteristics of all attached ata devices: +# type= type of attached device [disk|cdrom] +# mode= only valid for disks [flat|concat|external|dll|sparse|vmware3] +# mode= only valid for disks [undoable|growing|volatile] +# path= path of the image +# cylinders= only valid for disks +# heads= only valid for disks +# spt= only valid for disks +# status= only valid for cdroms [inserted|ejected] +# biosdetect= type of biosdetection [none|auto], only for disks on ata0 [cmos] +# translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto] +# model= string returned by identify device command +# journal= optional filename of the redolog for undoable and volatile disks +# +# Point this at a hard disk image file, cdrom iso file, or physical cdrom +# device. To create a hard disk image, try running bximage. It will help you +# choose the size and then suggest a line that works with it. +# +# In UNIX it may be possible to use a raw device as a Bochs hard disk, +# but WE DON'T RECOMMEND IT. In Windows there is no easy way. +# +# In windows, the drive letter + colon notation should be used for cdroms. +# Depending on versions of windows and drivers, you may only be able to +# access the "first" cdrom in the system. On MacOSX, use path="drive" +# to access the physical drive. +# +# The path is always mandatory. For flat hard disk images created with +# bximage geometry autodetection can be used (cylinders=0 -> cylinders are +# calculated using heads=16 and spt=63). For other hard disk images and modes +# the cylinders, heads, and spt are mandatory. +# +# Default values are: +# mode=flat, biosdetect=auto, translation=auto, model="Generic 1234" +# +# The biosdetect option has currently no effect on the bios +# +# Examples: +# ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17 +# ata0-slave: type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17 +# ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17 +# ata1-slave: type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17 +# ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17 +# ata2-slave: type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17 +# ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63 +# ata3-slave: type=cdrom, path=iso.sample, status=inserted +#======================================================================= +ata0-master: type=disk, mode=flat, path="xv6.img", cylinders=100, heads=10, spt=10 +ata0-slave: type=disk, mode=flat, path="fs.img", cylinders=1024, heads=1, spt=1 +#ata0-slave: type=cdrom, path=D:, status=inserted +#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted +#ata0-slave: type=cdrom, path="drive", status=inserted +#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted + +#======================================================================= +# BOOT: +# This defines the boot sequence. Now you can specify up to 3 boot drives. +# You can either boot from 'floppy', 'disk' or 'cdrom' +# legacy 'a' and 'c' are also supported +# Examples: +# boot: floppy +# boot: disk +# boot: cdrom +# boot: c +# boot: a +# boot: cdrom, floppy, disk +#======================================================================= +#boot: floppy +boot: disk + +#======================================================================= +# CLOCK: +# This defines the parameters of the clock inside Bochs: +# +# SYNC: +# TO BE COMPLETED (see Greg explanation in feature request #536329) +# +# TIME0: +# Specifies the start (boot) time of the virtual machine. Use a time +# value as returned by the time(2) system call. If no time0 value is +# set or if time0 equal to 1 (special case) or if time0 equal 'local', +# the simulation will be started at the current local host time. +# If time0 equal to 2 (special case) or if time0 equal 'utc', +# the simulation will be started at the current utc time. +# +# Syntax: +# clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc] +# +# Example: +# clock: sync=none, time0=local # Now (localtime) +# clock: sync=slowdown, time0=315529200 # Tue Jan 1 00:00:00 1980 +# clock: sync=none, time0=631148400 # Mon Jan 1 00:00:00 1990 +# clock: sync=realtime, time0=938581955 # Wed Sep 29 07:12:35 1999 +# clock: sync=realtime, time0=946681200 # Sat Jan 1 00:00:00 2000 +# clock: sync=none, time0=1 # Now (localtime) +# clock: sync=none, time0=utc # Now (utc/gmt) +# +# Default value are sync=none, time0=local +#======================================================================= +#clock: sync=none, time0=local + + +#======================================================================= +# FLOPPY_BOOTSIG_CHECK: disabled=[0|1] +# Enables or disables the 0xaa55 signature check on boot floppies +# Defaults to disabled=0 +# Examples: +# floppy_bootsig_check: disabled=0 +# floppy_bootsig_check: disabled=1 +#======================================================================= +#floppy_bootsig_check: disabled=1 +floppy_bootsig_check: disabled=0 + +#======================================================================= +# LOG: +# Give the path of the log file you'd like Bochs debug and misc. verbiage +# to be written to. If you don't use this option or set the filename to +# '-' the output is written to the console. If you really don't want it, +# make it "/dev/null" (Unix) or "nul" (win32). :^( +# +# Examples: +# log: ./bochs.out +# log: /dev/tty +#======================================================================= +#log: /dev/null +log: bochsout.txt + +#======================================================================= +# LOGPREFIX: +# This handles the format of the string prepended to each log line. +# You may use those special tokens : +# %t : 11 decimal digits timer tick +# %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration) +# %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror) +# %d : 5 characters string of the device, between brackets +# +# Default : %t%e%d +# Examples: +# logprefix: %t-%e-@%i-%d +# logprefix: %i%e%d +#======================================================================= +#logprefix: %t%e%d + +#======================================================================= +# LOG CONTROLS +# +# Bochs now has four severity levels for event logging. +# panic: cannot proceed. If you choose to continue after a panic, +# don't be surprised if you get strange behavior or crashes. +# error: something went wrong, but it is probably safe to continue the +# simulation. +# info: interesting or useful messages. +# debug: messages useful only when debugging the code. This may +# spit out thousands per second. +# +# For events of each level, you can choose to crash, report, or ignore. +# TODO: allow choice based on the facility: e.g. crash on panics from +# everything except the cdrom, and only report those. +# +# If you are experiencing many panics, it can be helpful to change +# the panic action to report instead of fatal. However, be aware +# that anything executed after a panic is uncharted territory and can +# cause bochs to become unstable. The panic is a "graceful exit," so +# if you disable it you may get a spectacular disaster instead. +#======================================================================= +panic: action=ask +error: action=report +info: action=report +debug: action=ignore +#pass: action=fatal + +#======================================================================= +# DEBUGGER_LOG: +# Give the path of the log file you'd like Bochs to log debugger output. +# If you really don't want it, make it /dev/null or '-'. :^( +# +# Examples: +# debugger_log: ./debugger.out +#======================================================================= +#debugger_log: /dev/null +#debugger_log: debugger.out +debugger_log: - + +#======================================================================= +# COM1, COM2, COM3, COM4: +# This defines a serial port (UART type 16550A). In the 'term' you can specify +# a device to use as com1. This can be a real serial line, or a pty. To use +# a pty (under X/Unix), create two windows (xterms, usually). One of them will +# run bochs, and the other will act as com1. Find out the tty the com1 +# window using the `tty' command, and use that as the `dev' parameter. +# Then do `sleep 1000000' in the com1 window to keep the shell from +# messing with things, and run bochs in the other window. Serial I/O to +# com1 (port 0x3f8) will all go to the other window. +# Other serial modes are 'null' (no input/output), 'file' (output to a file +# specified as the 'dev' parameter), 'raw' (use the real serial port - under +# construction for win32), 'mouse' (standard serial mouse - requires +# mouse option setting 'type=serial' or 'type=serial_wheel') and 'socket' +# (connect a networking socket). +# +# Examples: +# com1: enabled=1, mode=null +# com1: enabled=1, mode=mouse +# com2: enabled=1, mode=file, dev=serial.out +# com3: enabled=1, mode=raw, dev=com1 +# com3: enabled=1, mode=socket, dev=localhost:8888 +#======================================================================= +#com1: enabled=1, mode=term, dev=/dev/ttyp9 + + +#======================================================================= +# PARPORT1, PARPORT2: +# This defines a parallel (printer) port. When turned on and an output file is +# defined the emulated printer port sends characters printed by the guest OS +# into the output file. On some platforms a device filename can be used to +# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on +# win32 platforms). +# +# Examples: +# parport1: enabled=1, file="parport.out" +# parport2: enabled=1, file="/dev/lp0" +# parport1: enabled=0 +#======================================================================= +parport1: enabled=1, file="/dev/stdout" + +#======================================================================= +# SB16: +# This defines the SB16 sound emulation. It can have several of the +# following properties. +# All properties are in the format sb16: property=value +# midi: The filename is where the midi data is sent. This can be a +# device or just a file if you want to record the midi data. +# midimode: +# 0=no data +# 1=output to device (system dependent. midi denotes the device driver) +# 2=SMF file output, including headers +# 3=output the midi data stream to the file (no midi headers and no +# delta times, just command and data bytes) +# wave: This is the device/file where wave output is stored +# wavemode: +# 0=no data +# 1=output to device (system dependent. wave denotes the device driver) +# 2=VOC file output, incl. headers +# 3=output the raw wave stream to the file +# log: The file to write the sb16 emulator messages to. +# loglevel: +# 0=no log +# 1=resource changes, midi program and bank changes +# 2=severe errors +# 3=all errors +# 4=all errors plus all port accesses +# 5=all errors and port accesses plus a lot of extra info +# dmatimer: +# microseconds per second for a DMA cycle. Make it smaller to fix +# non-continuous sound. 750000 is usually a good value. This needs a +# reasonably correct setting for the IPS parameter of the CPU option. +# +# For an example look at the next line: +#======================================================================= + +#sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=sb16.log, dmatimer=600000 + +#======================================================================= +# VGA_UPDATE_INTERVAL: +# Video memory is scanned for updates and screen updated every so many +# virtual seconds. The default is 40000, about 25Hz. Keep in mind that +# you must tweak the 'cpu: ips=N' directive to be as close to the number +# of emulated instructions-per-second your workstation can do, for this +# to be accurate. +# +# Examples: +# vga_update_interval: 250000 +#======================================================================= +vga_update_interval: 300000 + +# using for Winstone '98 tests +#vga_update_interval: 100000 + +#======================================================================= +# KEYBOARD_SERIAL_DELAY: +# Approximate time in microseconds that it takes one character to +# be transfered from the keyboard to controller over the serial path. +# Examples: +# keyboard_serial_delay: 200 +#======================================================================= +keyboard_serial_delay: 250 + +#======================================================================= +# KEYBOARD_PASTE_DELAY: +# Approximate time in microseconds between attempts to paste +# characters to the keyboard controller. This leaves time for the +# guest os to deal with the flow of characters. The ideal setting +# depends on how your operating system processes characters. The +# default of 100000 usec (.1 seconds) was chosen because it works +# consistently in Windows. +# +# If your OS is losing characters during a paste, increase the paste +# delay until it stops losing characters. +# +# Examples: +# keyboard_paste_delay: 100000 +#======================================================================= +keyboard_paste_delay: 100000 + +#======================================================================= +# MOUSE: +# This option prevents Bochs from creating mouse "events" unless a mouse +# is enabled. The hardware emulation itself is not disabled by this. +# You can turn the mouse on by setting enabled to 1, or turn it off by +# setting enabled to 0. Unless you have a particular reason for enabling +# the mouse by default, it is recommended that you leave it off. +# You can also toggle the mouse usage at runtime (control key + middle +# mouse button on X11, SDL, wxWidgets and Win32). +# With the mouse type option you can select the type of mouse to emulate. +# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse +# on PS/2), 'serial', 'serial_wheel' (one com port requires setting +# 'mode=mouse') and 'usb' (3-button mouse - one of the USB ports must be +# connected with the 'mouse' device - requires PCI and USB support). +# +# Examples: +# mouse: enabled=1 +# mouse: enabled=1, type=imps2 +# mouse: enabled=1, type=serial +# mouse: enabled=0 +#======================================================================= +mouse: enabled=0 + +#======================================================================= +# private_colormap: Request that the GUI create and use it's own +# non-shared colormap. This colormap will be used +# when in the bochs window. If not enabled, a +# shared colormap scheme may be used. Not implemented +# on all GUI's. +# +# Examples: +# private_colormap: enabled=1 +# private_colormap: enabled=0 +#======================================================================= +private_colormap: enabled=0 + +#======================================================================= +# fullscreen: ONLY IMPLEMENTED ON AMIGA +# Request that Bochs occupy the entire screen instead of a +# window. +# +# Examples: +# fullscreen: enabled=0 +# fullscreen: enabled=1 +#======================================================================= +#fullscreen: enabled=0 +#screenmode: name="sample" + +#======================================================================= +# ne2k: NE2000 compatible ethernet adapter +# +# Examples: +# ne2k: ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT +# +# ioaddr, irq: You probably won't need to change ioaddr and irq, unless there +# are IRQ conflicts. +# +# mac: The MAC address MUST NOT match the address of any machine on the net. +# Also, the first byte must be an even number (bit 0 set means a multicast +# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast +# address. For the ethertap module, you must use fe:fd:00:00:00:01. There may +# be other restrictions too. To be safe, just use the b0:c4... address. +# +# ethdev: The ethdev value is the name of the network interface on your host +# platform. On UNIX machines, you can get the name by running ifconfig. On +# Windows machines, you must run niclist to get the name of the ethdev. +# Niclist source code is in misc/niclist.c and it is included in Windows +# binary releases. +# +# script: The script value is optional, and is the name of a script that +# is executed after bochs initialize the network interface. You can use +# this script to configure this network interface, or enable masquerading. +# This is mainly useful for the tun/tap devices that only exist during +# Bochs execution. The network interface name is supplied to the script +# as first parameter +# +# If you don't want to make connections to any physical networks, +# you can use the following 'ethmod's to simulate a virtual network. +# null: All packets are discarded, but logged to a few files. +# arpback: ARP is simulated. Disabled by default. +# vde: Virtual Distributed Ethernet +# vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated. +# The virtual host uses 192.168.10.1. +# DHCP assigns 192.168.10.2 to the guest. +# TFTP uses the ethdev value for the root directory and doesn't +# overwrite files. +# +#======================================================================= +# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx +# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0 +# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0 +# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD +# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0 +# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig +# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0 +# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl" +# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp" + +#======================================================================= +# KEYBOARD_MAPPING: +# This enables a remap of a physical localized keyboard to a +# virtualized us keyboard, as the PC architecture expects. +# If enabled, the keymap file must be specified. +# +# Examples: +# keyboard_mapping: enabled=1, map=gui/keymaps/x11-pc-de.map +#======================================================================= +keyboard_mapping: enabled=0, map= + +#======================================================================= +# KEYBOARD_TYPE: +# Type of keyboard return by a "identify keyboard" command to the +# keyboard controler. It must be one of "xt", "at" or "mf". +# Defaults to "mf". It should be ok for almost everybody. A known +# exception is french macs, that do have a "at"-like keyboard. +# +# Examples: +# keyboard_type: mf +#======================================================================= +#keyboard_type: mf + +#======================================================================= +# USER_SHORTCUT: +# This defines the keyboard shortcut to be sent when you press the "user" +# button in the headerbar. The shortcut string is a combination of maximum +# 3 key names (listed below) separated with a '-' character. The old-style +# syntax (without the '-') still works for the key combinations supported +# in Bochs 2.2.1. +# Valid key names: +# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc", +# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup", +# "plus", "right", "shift", "space", "tab", "up", and "win". +# +# Example: +# user_shortcut: keys=ctrl-alt-del +#======================================================================= +#user_shortcut: keys=ctrl-alt-del + +#======================================================================= +# I440FXSUPPORT: +# This option controls the presence of the i440FX PCI chipset. You can +# also specify the devices connected to PCI slots. Up to 5 slots are +# available now. These devices are currently supported: ne2k, pcivga, +# pcidev and pcipnic. If Bochs is compiled with Cirrus SVGA support +# you'll have the additional choice 'cirrus'. +# +# Example: +# i440fxsupport: enabled=1, slot1=pcivga, slot2=ne2k +#======================================================================= +#i440fxsupport: enabled=1 + +#======================================================================= +# USB1: +# This option controls the presence of the USB root hub which is a part +# of the i440FX PCI chipset. With the portX option you can connect devices +# to the hub (currently supported: 'mouse' and 'keypad'). If you connect +# the mouse to one of the ports and use the mouse option 'type=usb' you'll +# have a 3-button USB mouse. +# +# Example: +# usb1: enabled=1, port1=mouse, port2=keypad +#======================================================================= +#usb1: enabled=1 + +#======================================================================= +# CMOSIMAGE: +# This defines image file that can be loaded into the CMOS RAM at startup. +# The rtc_init parameter controls whether initialize the RTC with values stored +# in the image. By default the time0 argument given to the clock option is used. +# With 'rtc_init=image' the image is the source for the initial time. +# +# Example: +# cmosimage: file=cmos.img, rtc_init=image +#======================================================================= +#cmosimage: file=cmos.img, rtc_init=time0 + +#======================================================================= +# other stuff +#======================================================================= +#magic_break: enabled=1 +#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log +#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img +#text_snapshot_check: enable + +#------------------------- +# PCI host device mapping +#------------------------- +#pcidev: vendor=0x1234, device=0x5678 + +#======================================================================= +# GDBSTUB: +# Enable GDB stub. See user documentation for details. +# Default value is enabled=0. +#======================================================================= +#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0 + +#======================================================================= +# IPS: +# The IPS directive is DEPRECATED. Use the parameter IPS of the CPU +# directive instead. +#======================================================================= +#ips: 10000000 + +#======================================================================= +# for Macintosh, use the style of pathnames in the following +# examples. +# +# vgaromimage: :bios:VGABIOS-elpin-2.40 +# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000 +# floppya: 1_44=[fd:], status=inserted +#======================================================================= diff --git a/tools/gdbinit.tmpl b/tools/gdbinit.tmpl new file mode 100644 index 0000000..f71681a --- /dev/null +++ b/tools/gdbinit.tmpl @@ -0,0 +1,27 @@ +set $lastcs = -1 + +define hook-stop + # There doesn't seem to be a good way to detect if we're in 16- or + # 32-bit mode, but in 32-bit mode we always run with CS == 8 in the + # kernel and CS == 35 in user space + if $cs == 8 || $cs == 35 + if $lastcs != 8 && $lastcs != 35 + set architecture i386 + end + x/i $pc + else + if $lastcs == -1 || $lastcs == 8 || $lastcs == 35 + set architecture i8086 + end + # Translate the segment:offset into a physical address + printf "[%4x:%4x] ", $cs, $eip + x/i $cs*16+$eip + end + set $lastcs = $cs +end + +echo + target remote localhost:1234\n +target remote localhost:1234 + +echo + symbol-file kernel\n +symbol-file kernel diff --git a/tools/gdbutil b/tools/gdbutil new file mode 100644 index 0000000..e0c362f --- /dev/null +++ b/tools/gdbutil @@ -0,0 +1,291 @@ +# -*- gdb-script -*- + +# Utility functions to pretty-print x86 segment/interrupt descriptors. +# To load this file, run "source gdbutil" in gdb. +# printdesc and printdescs are the main entry points. + +# IA32 2007, Volume 3A, Table 3-2 +set $STS_T16A = 0x1 +set $STS_LDT = 0x2 +set $STS_T16B = 0x3 +set $STS_CG16 = 0x4 +set $STS_TG = 0x5 +set $STS_IG16 = 0x6 +set $STS_TG16 = 0x7 +set $STS_T32A = 0x9 +set $STS_T32B = 0xB +set $STS_CG32 = 0xC +set $STS_IG32 = 0xE +set $STS_TG32 = 0xF + +define outputsts + while 1 + if $arg0 == $STS_T16A + echo STS_T16A + loop_break + end + if $arg0 == $STS_LDT + echo STS_LDT\ + loop_break + end + if $arg0 == $STS_T16B + echo STS_T16B + loop_break + end + if $arg0 == $STS_CG16 + echo STS_CG16 + loop_break + end + if $arg0 == $STS_TG + echo STS_TG\ \ + loop_break + end + if $arg0 == $STS_IG16 + echo STS_IG16 + loop_break + end + if $arg0 == $STS_TG16 + echo STS_TG16 + loop_break + end + if $arg0 == $STS_T32A + echo STS_T32A + loop_break + end + if $arg0 == $STS_T32B + echo STS_T32B + loop_break + end + if $arg0 == $STS_CG32 + echo STS_CG32 + loop_break + end + if $arg0 == $STS_IG32 + echo STS_IG32 + loop_break + end + if $arg0 == $STS_TG32 + echo STS_TG32 + loop_break + end + echo Reserved + loop_break + end +end + +# IA32 2007, Volume 3A, Table 3-1 +set $STA_X = 0x8 +set $STA_E = 0x4 +set $STA_C = 0x4 +set $STA_W = 0x2 +set $STA_R = 0x2 +set $STA_A = 0x1 + +define outputsta + if $arg0 & $STA_X + # Code segment + echo code + if $arg0 & $STA_C + echo |STA_C + end + if $arg0 & $STA_R + echo |STA_R + end + else + # Data segment + echo data + if $arg0 & $STA_E + echo |STA_E + end + if $arg0 & $STA_W + echo |STA_W + end + end + if $arg0 & $STA_A + echo |STA_A + else + printf " " + end +end + +# xv6-specific +set $SEG_KCODE = 1 +set $SEG_KDATA = 2 +set $SEG_KCPU = 3 +set $SEG_UCODE = 4 +set $SEG_UDATA = 5 +set $SEG_TSS = 6 + +define outputcs + if ($arg0 & 4) == 0 + if $arg0 >> 3 == $SEG_KCODE + printf "SEG_KCODE<<3" + end + if $arg0 >> 3 == $SEG_KDATA + printf "SEG_KDATA<<3" + end + if $arg0 >> 3 == $SEG_KCPU + printf "SEG_KCPU<<3" + end + if $arg0 >> 3 == $SEG_UCODE + printf "SEG_UCODE<<3" + end + if $arg0 >> 3 == $SEG_UDATA + printf "SEG_UDATA<<3" + end + if $arg0 >> 3 == $SEG_TSS + printf "SEG_TSS<<3" + end + if ($arg0 >> 3 < 1) + ($arg0 >> 3 > 6) + printf "GDT[%d]", $arg0 >> 3 + end + else + printf "LDT[%d]", $arg0 >> 3 + end + if ($arg0 & 3) > 0 + printf "|" + outputdpl ($arg0&3) + end +end + +define outputdpl + if $arg0 == 0 + printf "DPL_KERN" + else + if $arg0 == 3 + printf "DPL_USER" + else + printf "DPL%d", $arg0 + end + end +end + +define printdesc + if $argc != 1 + echo Usage: printdesc expr + else + _printdesc ((uint*)&($arg0))[0] ((uint*)&($arg0))[1] + printf "\n" + end +end + +document printdesc +Print an x86 segment or gate descriptor. +printdesc EXPR +EXPR must evaluate to a descriptor value. It can be of any C type. +end + +define _printdesc + _printdesc1 $arg0 $arg1 ($arg1>>15&1) ($arg1>>13&3) ($arg1>>12&1) ($arg1>>8&15) +end + +define _printdesc1 + # 2:P 3:DPL 4:S 5:Type + if $arg2 == 0 + printf "P = 0 (Not present)" + else + printf "type = " + if $arg4 == 0 + # System segment + outputsts $arg5 + printf " (0x%x) ", $arg5 + _printsysdesc $arg0 $arg1 $arg5 + else + # Code/data segment + outputsta $arg5 + printf " " + _printsegdesc $arg0 $arg1 + end + + printf " DPL = " + outputdpl $arg3 + printf " (%d)", $arg3 + end +end + +define _printsysdesc + # 2:Type + # GDB's || is buggy + if ($arg2 == $STS_TG) + (($arg2&7) == $STS_IG16) + (($arg2&7) == $STS_TG16) + # Gate descriptor + _printgate $arg2 ($arg0>>16) ($arg0&0xFFFF) ($arg1>>16) + else + # System segment descriptor + _printsegdesc $arg0 $arg1 + end +end + +define _printgate + # IA32 2007, Voume 3A, Figure 5-2 + # 0:Type 1:CS 2:Offset 15..0 3:Offset 31..16 + printf "CS = " + outputcs $arg1 + printf " (%d)", $arg1 + + if (($arg0&7) == $STS_IG16) + (($arg0&7) == $STS_TG16) + printf " Offset = " + output/a $arg3 << 16 | $arg2 + end +end + +define _printsegdesc + # IA32 20007, Volume 3A, Figure 3-8 and Figure 4-1 + _printsegdesc1 ($arg0>>16) ($arg1&0xFF) ($arg1>>24) ($arg0&0xFFFF) ($arg1>>16&15) ($arg1>>23&1) + if ($arg1>>12&1) == 1 + printf " AVL = %d", $arg1>>20&1 + if ($arg1>>11&1) == 0 + # Data segment + if ($arg1>>22&1) == 0 + printf " B = small (0) " + else + printf " B = big (1) " + end + else + # Code segment + printf " D = " + if ($arg1>>22&1) == 0 + printf "16-bit (0)" + else + printf "32-bit (1)" + end + end + end +end + +define _printsegdesc1 + # 0:Base 0..15 1:Base 16..23 2:Base 24..32 3:Limit 0..15 4:Limit 16..19 5:G + printf "base = 0x%08x", $arg0 | ($arg1<<16) | ($arg2<<24) + printf " limit = 0x" + if $arg5 == 0 + printf "%08x", $arg3 | ($arg4<<16) + else + printf "%08x", (($arg3 | ($arg4<<16)) << 12) | 0xFFF + end +end + +define printdescs + if $argc < 1 || $argc > 2 + echo Usage: printdescs expr [count] + else + if $argc == 1 + _printdescs ($arg0) (sizeof($arg0)/sizeof(($arg0)[0])) + else + _printdescs ($arg0) ($arg1) + end + end +end + +document printdescs +Print an array of x86 segment or gate descriptors. +printdescs EXPR [COUNT] +EXPR must evaluate to an array of descriptors. +end + +define _printdescs + set $i = 0 + while $i < $arg1 + printf "[%d] ", $i + printdesc $arg0[$i] + set $i = $i + 1 + end +end diff --git a/tools/mkfs.c b/tools/mkfs.c new file mode 100644 index 0000000..bb26c90 --- /dev/null +++ b/tools/mkfs.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include + +#define stat xv6_stat // avoid clash with host struct stat +#include "../include/types.h" +#include "../include/fs.h" +#include "../include/stat.h" +#include "../include/param.h" + +#ifndef static_assert +# define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) +#endif // static_assert + +int nblocks = 985; +int nlog = LOGSIZE; +int ninodes = 200; +int size = 1024; + +int fsfd; +struct superblock sb; +char zeroes[512]; +uint freeblock; +uint usedblocks; +uint bitblocks; +uint freeinode = 1; + +void balloc(int); +void wsect(uint, void*); +void winode(uint, struct dinode*); +void rinode(uint inum, struct dinode *ip); +void rsect(uint sec, void *buf); +uint ialloc(ushort type); +void iappend(uint inum, void *p, int n); + +// convert to intel byte order +ushort +xshort(ushort x) +{ + ushort y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + return y; +} + +uint +xint(uint x) +{ + uint y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + a[2] = x >> 16; + a[3] = x >> 24; + return y; +} + +int +main(int argc, char *argv[]) +{ + int i, cc, fd; + uint rootino, inum, off; + struct dirent de; + char buf[512]; + struct dinode din; + + + static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); + + if(argc < 2){ + fprintf(stderr, "Usage: mkfs fs.img files...\n"); + exit(1); + } + + assert((512 % sizeof(struct dinode)) == 0); + assert((512 % sizeof(struct dirent)) == 0); + + fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); + if(fsfd < 0){ + perror(argv[1]); + exit(1); + } + + sb.size = xint(size); + sb.nblocks = xint(nblocks); // so whole disk is size sectors + sb.ninodes = xint(ninodes); + sb.nlog = xint(nlog); + + bitblocks = size/(512*8) + 1; + usedblocks = ninodes / IPB + 3 + bitblocks; + freeblock = usedblocks; + + printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks, + bitblocks, ninodes/IPB + 1, freeblock, nlog, nblocks+usedblocks+nlog); + + assert(nblocks + usedblocks + nlog == size); + + for(i = 0; i < nblocks + usedblocks + nlog; i++) + wsect(i, zeroes); + + memset(buf, 0, sizeof(buf)); + memmove(buf, &sb, sizeof(sb)); + wsect(1, buf); + + rootino = ialloc(T_DIR); + assert(rootino == ROOTINO); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, "."); + iappend(rootino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, ".."); + iappend(rootino, &de, sizeof(de)); + + for(i = 2; i < argc; i++){ + char *name = argv[i]; + + if (!strncmp(name, "fs/", 3)) + name += 3; + + assert(index(name, '/') == 0); + + if((fd = open(argv[i], 0)) < 0){ + perror(argv[i]); + exit(1); + } + + inum = ialloc(T_FILE); + + bzero(&de, sizeof(de)); + de.inum = xshort(inum); + strncpy(de.name, name, DIRSIZ); + iappend(rootino, &de, sizeof(de)); + + while((cc = read(fd, buf, sizeof(buf))) > 0) + iappend(inum, buf, cc); + + close(fd); + } + + // fix size of root inode dir + rinode(rootino, &din); + off = xint(din.size); + off = ((off/BSIZE) + 1) * BSIZE; + din.size = xint(off); + winode(rootino, &din); + + balloc(usedblocks); + + exit(0); +} + +void +wsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * 512L, 0) != sec * 512L){ + perror("lseek"); + exit(1); + } + if(write(fsfd, buf, 512) != 512){ + perror("write"); + exit(1); + } +} + +uint +i2b(uint inum) +{ + return (inum / IPB) + 2; +} + +void +winode(uint inum, struct dinode *ip) +{ + char buf[512]; + uint bn; + struct dinode *dip; + + bn = i2b(inum); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *dip = *ip; + wsect(bn, buf); +} + +void +rinode(uint inum, struct dinode *ip) +{ + char buf[512]; + uint bn; + struct dinode *dip; + + bn = i2b(inum); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *ip = *dip; +} + +void +rsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * 512L, 0) != sec * 512L){ + perror("lseek"); + exit(1); + } + if(read(fsfd, buf, 512) != 512){ + perror("read"); + exit(1); + } +} + +uint +ialloc(ushort type) +{ + uint inum = freeinode++; + struct dinode din; + + bzero(&din, sizeof(din)); + din.type = xshort(type); + din.nlink = xshort(1); + din.size = xint(0); + winode(inum, &din); + return inum; +} + +void +balloc(int used) +{ + uchar buf[512]; + int i; + + printf("balloc: first %d blocks have been allocated\n", used); + assert(used < 512*8); + bzero(buf, 512); + for(i = 0; i < used; i++){ + buf[i/8] = buf[i/8] | (0x1 << (i%8)); + } + printf("balloc: write bitmap block at sector %zu\n", ninodes/IPB + 3); + wsect(ninodes / IPB + 3, buf); +} + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +void +iappend(uint inum, void *xp, int n) +{ + char *p = (char*)xp; + uint fbn, off, n1; + struct dinode din; + char buf[512]; + uint indirect[NINDIRECT]; + uint x; + + rinode(inum, &din); + + off = xint(din.size); + while(n > 0){ + fbn = off / 512; + assert(fbn < MAXFILE); + if(fbn < NDIRECT){ + if(xint(din.addrs[fbn]) == 0){ + din.addrs[fbn] = xint(freeblock++); + usedblocks++; + } + x = xint(din.addrs[fbn]); + } else { + if(xint(din.addrs[NDIRECT]) == 0){ + // printf("allocate indirect block\n"); + din.addrs[NDIRECT] = xint(freeblock++); + usedblocks++; + } + // printf("read indirect block\n"); + rsect(xint(din.addrs[NDIRECT]), (char*)indirect); + if(indirect[fbn - NDIRECT] == 0){ + indirect[fbn - NDIRECT] = xint(freeblock++); + usedblocks++; + wsect(xint(din.addrs[NDIRECT]), (char*)indirect); + } + x = xint(indirect[fbn-NDIRECT]); + } + n1 = min(n, (fbn + 1) * 512 - off); + rsect(x, buf); + bcopy(p, buf + off - (fbn * 512), n1); + wsect(x, buf); + n -= n1; + off += n1; + p += n1; + } + din.size = xint(off); + winode(inum, &din); +} diff --git a/tools/printpcs b/tools/printpcs new file mode 100644 index 0000000..81d039b --- /dev/null +++ b/tools/printpcs @@ -0,0 +1,14 @@ +#!/bin/sh + +# Decode the symbols from a panic EIP list + +# Find a working addr2line +for p in i386-jos-elf-addr2line addr2line; do + if which $p 2>&1 >/dev/null && \ + $p -h 2>&1 | grep -q '\belf32-i386\b'; then + break + fi +done + +# Enable as much pretty-printing as this addr2line can do +$p $($p -h | grep ' -[aipsf] ' | awk '{print $1}') -e kernel "$@" diff --git a/tools/sign.pl b/tools/sign.pl new file mode 100644 index 0000000..d793035 --- /dev/null +++ b/tools/sign.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl + +open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!"; + +$n = sysread(SIG, $buf, 1000); + +if($n > 510){ + print STDERR "boot block too large: $n bytes (max 510)\n"; + exit 1; +} + +print STDERR "boot block is $n bytes (max 510)\n"; + +$buf .= "\0" x (510-$n); +$buf .= "\x55\xAA"; + +open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!"; +print SIG $buf; +close SIG; diff --git a/tools/sleep1.p b/tools/sleep1.p new file mode 100644 index 0000000..af69772 --- /dev/null +++ b/tools/sleep1.p @@ -0,0 +1,134 @@ +/* +This file defines a Promela model for xv6's +acquire, release, sleep, and wakeup, along with +a model of a simple producer/consumer queue. + +To run: + spinp sleep1.p + +(You may need to install Spin, available at http://spinroot.com/.) + +After a successful run spin prints something like: + + unreached in proctype consumer + (0 of 37 states) + unreached in proctype producer + (0 of 23 states) + +After an unsuccessful run, the spinp script prints +an execution trace that causes a deadlock. + +The safe body of producer reads: + + acquire(lk); + x = value; value = x + 1; x = 0; + wakeup(0); + release(lk); + i = i + 1; + +If this is changed to: + + x = value; value = x + 1; x = 0; + acquire(lk); + wakeup(0); + release(lk); + i = i + 1; + +then a deadlock can happen, because the non-atomic +increment of value conflicts with the non-atomic +decrement in consumer, causing value to have a bad value. +Try this. + +If it is changed to: + + acquire(lk); + x = value; value = x + 1; x = 0; + release(lk); + wakeup(0); + i = i + 1; + +then nothing bad happens: it is okay to wakeup after release +instead of before, although it seems morally wrong. +*/ + +#define ITER 4 +#define N 2 + +bit lk; +byte value; +bit sleeping[N]; + +inline acquire(x) +{ + atomic { x == 0; x = 1 } +} + +inline release(x) +{ + assert x==1; + x = 0 +} + +inline sleep(cond, lk) +{ + assert !sleeping[_pid]; + if + :: cond -> + skip + :: else -> + atomic { release(lk); sleeping[_pid] = 1 }; + sleeping[_pid] == 0; + acquire(lk) + fi +} + +inline wakeup() +{ + w = 0; + do + :: w < N -> + sleeping[w] = 0; + w = w + 1 + :: else -> + break + od +} + +active[N] proctype consumer() +{ + byte i, x; + + i = 0; + do + :: i < ITER -> + acquire(lk); + sleep(value > 0, lk); + x = value; value = x - 1; x = 0; + release(lk); + i = i + 1; + :: else -> + break + od; + i = 0; + skip +} + +active[N] proctype producer() +{ + byte i, x, w; + + i = 0; + do + :: i < ITER -> + acquire(lk); + x = value; value = x + 1; x = 0; + release(lk); + wakeup(); + i = i + 1; + :: else -> + break + od; + i = 0; + skip +} + diff --git a/tools/spinp b/tools/spinp new file mode 100644 index 0000000..db9614b --- /dev/null +++ b/tools/spinp @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $# != 1 ] || [ ! -f "$1" ]; then + echo 'usage: spinp file.p' 1>&2 + exit 1 +fi + +rm -f $1.trail +spin -a $1 || exit 1 +cc -DSAFETY -DREACH -DMEMLIM=500 -o pan pan.c +pan -i +rm pan.* pan +if [ -f $1.trail ]; then + spin -t -p $1 +fi + diff --git a/tools/vectors.pl b/tools/vectors.pl new file mode 100644 index 0000000..57b49dd --- /dev/null +++ b/tools/vectors.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl -w + +# Generate vectors.S, the trap/interrupt entry points. +# There has to be one entry point per interrupt number +# since otherwise there's no way for trap() to discover +# the interrupt number. + +print "# generated by vectors.pl - do not edit\n"; +print "# handlers\n"; +print ".globl alltraps\n"; +for(my $i = 0; $i < 256; $i++){ + print ".globl vector$i\n"; + print "vector$i:\n"; + if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){ + print " pushl \$0\n"; + } + print " pushl \$$i\n"; + print " jmp alltraps\n"; +} + +print "\n# vector table\n"; +print ".data\n"; +print ".globl vectors\n"; +print "vectors:\n"; +for(my $i = 0; $i < 256; $i++){ + print " .long vector$i\n"; +} + +# sample output: +# # handlers +# .globl alltraps +# .globl vector0 +# vector0: +# pushl $0 +# pushl $0 +# jmp alltraps +# ... +# +# # vector table +# .data +# .globl vectors +# vectors: +# .long vector0 +# .long vector1 +# .long vector2 +# ... + diff --git a/tools/vectors64.pl b/tools/vectors64.pl new file mode 100644 index 0000000..d7bc818 --- /dev/null +++ b/tools/vectors64.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w + +# Generate vectors.S, the trap/interrupt entry points. +# There has to be one entry point per interrupt number +# since otherwise there's no way for trap() to discover +# the interrupt number. + +print "# generated by vectors.pl - do not edit\n"; +print "# handlers\n"; +print ".globl alltraps\n"; +for(my $i = 0; $i < 256; $i++){ + print ".globl vector$i\n"; + print "vector$i:\n"; + if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){ + print " push \$0\n"; + } + print " push \$$i\n"; + print " jmp alltraps\n"; +} + +print "\n# vector table\n"; +print ".data\n"; +print ".globl vectors\n"; +print "vectors:\n"; +for(my $i = 0; $i < 256; $i++){ + print " .quad vector$i\n"; +}