diff --git a/.gitignore b/.gitignore index 48b41ef761a438e1c116fce4ab5b19c3f27bab0b..1821a01cd0bd9df2fae2da47c16dacd4a36844ca 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ tags tags.* typescript vgcore* +.vscode* diff --git a/GNUmakefile b/GNUmakefile index 7157f5ce02078a15b7a7d52483dda222bc9b85ab..203eb5ff24cd7baa2dcfe60a0a50e22fa35c1750 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -87,10 +87,14 @@ CHICKADEE_FIRST_PROCESS ?= allocator ifneq ($(strip $(CHICKADEE_FIRST_PROCESS)),$(DEP_CHICKADEE_FIRST_PROCESS)) FIRST_PROCESS_BUILDSTAMP := $(shell echo "DEP_CHICKADEE_FIRST_PROCESS:=$(CHICKADEE_FIRST_PROCESS)" > $(DEPSDIR)/_first_process.d) $(OBJDIR)/k-firstprocess.h: always +$(OBJDIR)/chickadee.gdb: always endif ifeq ($(wildcard $(OBJDIR)/k-firstprocess.h),) KERNELBUILDSTAMPS += $(OBJDIR)/k-firstprocess.h endif +ifeq ($(wildcard $(OBJDIR)/chickadee.gdb),) +KERNELBUILDSTAMPS += $(OBJDIR)/chickadee.gdb +endif # How to make object files @@ -128,6 +132,9 @@ $(OBJDIR)/u-asm.h: u-lib.hh lib.hh types.h x86-64.h build/mkkernelasm.awk $(BUIL $(OBJDIR)/k-firstprocess.h: $(call run,echo '#ifndef CHICKADEE_FIRST_PROCESS' >$@; echo '#define CHICKADEE_FIRST_PROCESS "$(CHICKADEE_FIRST_PROCESS)"' >>$@; echo '#endif' >>$@,CREATE $@) + $(OBJDIR)/chickadee.gdb: + $(call run,echo 'add-symbol-file obj/p-$(CHICKADEE_FIRST_PROCESS).full 0x100000' >$@,CREATE $@) + $(OBJDIR)/k-initfs.cc: build/mkinitfs.awk \ $(INITFS_CONTENTS) $(INITFS_BUILDSTAMP) $(KERNELBUILDSTAMPS) $(call run,echo $(INITFS_CONTENTS) $(INITFS_PARAMS) | awk -f build/mkinitfs.awk >,CREATE,$@) @@ -190,13 +197,17 @@ $(OBJDIR)/chickadeefsck: $(CHICKADEEFSCK_OBJS) $(BUILDSTAMPS) # How to make disk images # If you change the `-f` argument, also change `boot.cc:KERNEL_START_SECTOR` -chickadeeboot.img: $(OBJDIR)/mkchickadeefs $(OBJDIR)/bootsector $(OBJDIR)/kernel - $(call run,$(OBJDIR)/mkchickadeefs -b 4096 -f 16 -s $(OBJDIR)/bootsector $(OBJDIR)/kernel > $@,CREATE $@) - -chickadeefs.img: $(OBJDIR)/mkchickadeefs \ - $(OBJDIR)/bootsector $(OBJDIR)/kernel $(DISKFS_CONTENTS) \ - $(DISKFS_BUILDSTAMP) - $(call run,$(OBJDIR)/mkchickadeefs -b 32768 -f 16 -j 64 -s $(OBJDIR)/bootsector $(OBJDIR)/kernel $(DISKFS_CONTENTS) > $@,CREATE $@) +# The following two sections have been formatted to highlight their similarities +chickadeeboot.img: $(OBJDIR)/mkchickadeefs \ + $(OBJDIR)/bootsector $(OBJDIR)/kernel + $(call run,$(OBJDIR)/mkchickadeefs -b 4096 -f 16 -s \ + $(OBJDIR)/bootsector $(OBJDIR)/kernel > $@,CREATE $@) + +chickadeefs.img: $(OBJDIR)/mkchickadeefs \ + $(OBJDIR)/bootsector $(OBJDIR)/kernel \ + $(DISKFS_CONTENTS) $(DISKFS_BUILDSTAMP) + $(call run,$(OBJDIR)/mkchickadeefs -b 32768 -f 16 -j 64 -s \ + $(OBJDIR)/bootsector $(OBJDIR)/kernel $(DISKFS_CONTENTS) > $@,CREATE $@) cleanfs: $(call run,rm -f chickadeefs.img,RM chickadeefs.img) @@ -226,11 +237,11 @@ run-monitor: $(QEMUIMAGEFILES) check-qemu $(call run,$(QEMU_PRELOAD) $(QEMU) $(QEMUOPT) -monitor stdio $(QEMUIMG),QEMU $<) run-gdb: run-gdb-$(QEMUDISPLAY) @: +run-gdb-console: $(QEMUIMAGEFILES) check-qemu-console + $(call run,$(QEMU) $(QEMUOPT) -curses -S -gdb tcp::12949 $(QEMUIMG),QEMU $<) run-gdb-graphic: $(QEMUIMAGEFILES) check-qemu - $(call run,$(QEMU_PRELOAD) $(QEMU) $(QEMUOPT) -gdb tcp::12949 $(QEMUIMG) &,QEMU $<) + $(call run,$(QEMU_PRELOAD) $(QEMU) $(QEMUOPT) -S -gdb tcp::12949 $(QEMUIMG) &,QEMU $<) $(call run,sleep 0.5; gdb -x build/chickadee.gdb,GDB) -run-gdb-console: $(QEMUIMAGEFILES) check-qemu-console - $(call run,$(QEMU) $(QEMUOPT) -curses -gdb tcp::12949 $(QEMUIMG),QEMU $<) run-$(RUNSUFFIX): run run-graphic-$(RUNSUFFIX): run-graphic diff --git a/README.md b/README.md index 8767891077a0011be480b9bf1b2c932091cd4c42..942897e9e40076f9aef116d6555334ebf14f9cce 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,11 @@ and CPU resets to the standard error. This setting will also cause QEMU to quit after encountering a [triple fault][] (normally it will reboot). `make run-PROGRAM` runs `p-PROGRAM.cc` as the first non-init process. The -default is `alloc`. +default is `allocator`. If you choose an alternate first process, note a +couple things: + +* An initial process like p-testkalloc.cc calls panic(), but without a console this gives a [page fault](https://github.com/CS161/chickadee/issues/14). +* build/chickadee.gdb is hard-coded to load symbols for p-allocator.cc, so this file must be [modified](https://github.com/CS161/chickadee/issues/13) if you want to debug an alternate process. `make HALT=1 run-PROGRAM` should make QEMU exit once all processes are done. @@ -131,13 +135,14 @@ Source files Build files ----------- -The main output of the build process is a disk image, -`chickadeeos.img`. QEMU “boots” off this disk image, but the image -could conceivably boot on real hardware! The build process also +The main outputs of the build process are two disk images, `chickadeeboot.img` and +`chickadeefs.img`. QEMU “boots” off a disk image, but the image +could conceivably boot on real hardware! The build process relies on and produces other files that can be useful to examine. | File | Description | | -------------------------- | ------------------------------------ | +| `GNUmakefile` | Instructions on how to build things | | `obj/kernel.asm` | Kernel assembly (with addresses) | | `obj/kernel.sym` | Kernel defined symbols | | `obj/p-PROCESS.asm`, `sym` | Same for process binaries | diff --git a/boot.cc b/boot.cc index 5909750d7214c8957afbf9a3caed3f838f0d6259..ade3d96e164132a2483e808b3be0537eb929db08 100644 --- a/boot.cc +++ b/boot.cc @@ -14,7 +14,7 @@ // the boot device (hard drive) into memory at address 0x7C00, and jumps to // that address. // -// The boot loader is contained in bootstart.S and boot.c. Control starts +// The boot loader is contained in bootstart.S and boot.cc. Control starts // in bootstart.S, which initializes the CPU and sets up a stack, then // transfers here. This code reads in the kernel image and calls the // kernel. diff --git a/bootentry.S b/bootentry.S index 12734599f02c988ba3da2995f52a58e4da6afbb6..612a127d5f5a8fa702bd7360c85df49d7497d2bf 100644 --- a/bootentry.S +++ b/bootentry.S @@ -47,7 +47,7 @@ notify_bios64: # Notify the BIOS (the machine's firmware) to optimize itself # for x86-64 code. https://wiki.osdev.org/X86-64 movw $0xEC00, %ax - movw $2, %dx + mov $2, %bl int $0x15 init_boot_pagetable: @@ -63,37 +63,48 @@ init_boot_pagetable: # 0x2000: L3 page table; entries 0 and 510 map 1st 1GB of physmem # This is the minimal page table that maps all of # low-canonical, high-canonical, and kernel-text addresses to - # the first 1GB of physical memory. + # the first 1GB (?) of physical memory. + # (Not sure about the 1 GB number. Emperical tests show only 128 MB!) movl $BOOT_PAGETABLE, %edi - leal 0x1000 + PTE_P + PTE_W(%edi), %ecx + leal 0x1000 + PTE_P + PTE_W(%edi), %ecx # combine address with flags movl %ecx, (%edi) movl %ecx, 0x800(%edi) movl %ecx, 0xFF8(%edi) + # the offsets below are adjusted by -3 to remove the flags! movl $(PTE_P + PTE_W + PTE_PS), -3(%ecx) movl $(PTE_P + PTE_W + PTE_PS), 0xFED(%ecx) -# Switch from real to protected mode: -# Up until now, there's been no protection, so we've gotten along perfectly -# well without explicitly telling the processor how to translate addresses. -# When we switch to protected mode, this is no longer true! +# Switch from real mode (16-bit) to long mode (64-bit): +# Up until now, we have used direct physical memory addressing (real mode) +# without explicitly telling the processor how to translate addresses. +# When we enable paging, this is no longer true! # We need at least to set up some "segments" that tell the processor it's # OK to run code at any address, or write to any address. # The `gdt` and `gdtdesc` tables below define these segments. # This code loads them into the processor. # We need this setup to ensure the transition to protected mode is smooth. +# See https://en.wikipedia.org/wiki/X86-64 for a nice diagram and description of processor modes. -real_to_prot: +real_to_long: movl %cr4, %eax # enable physical address extensions - orl $(CR4_PSE | CR4_PAE), %eax + orl $(CR4_PAE), %eax # https://en.wikipedia.org/wiki/Physical_Address_Extension movl %eax, %cr4 - movl %edi, %cr3 - movl $MSR_IA32_EFER, %ecx # turn on 64-bit mode - rdmsr + movl %edi, %cr3 # set the address of the page table + + movl $MSR_IA32_EFER, %ecx # enable long mode (https://wiki.osdev.org/Setting_Up_Long_Mode) + + rdmsr # read from the model-specific register into %eax + # IA32_EFER_LME is to enable 64-bit mode + # IA32_EFER_SCE is to enable syscall/sysret + # IA32_EFER_NXE is to allow PTE_XD (eXecute Disabled) in page table entries orl $(IA32_EFER_LME | IA32_EFER_SCE | IA32_EFER_NXE), %eax - wrmsr + wrmsr # write %eax to the model-specific register - movl %cr0, %eax # turn on protected mode + movl %cr0, %eax # turn on some other features + # CR0_PE sets Protection Enable + # CR0_WP sets Write Protect + # CR0_PG sets CR0_PG orl $(CR0_PE | CR0_WP | CR0_PG), %eax movl %eax, %cr0 diff --git a/build/chickadee.gdb b/build/chickadee.gdb index 23c2d8cb73276bac42932151ade85594f96a1098..1863e4bcae7521d0fb94a9fbcccdad78728633f7 100644 --- a/build/chickadee.gdb +++ b/build/chickadee.gdb @@ -2,7 +2,7 @@ set $loaded = 1 set arch i386:x86-64 file obj/kernel.full add-symbol-file obj/bootsector.full 0x7c00 -add-symbol-file obj/p-allocator.full 0x100000 +source obj/chickadee.gdb target remote localhost:12949 source build/functions.gdb display/5i $pc diff --git a/k-ahci.cc b/k-ahci.cc index 4f15c87942989ae0dcfc4c418e0707f26093edd3..6afb5dc7fb2e347026d1816436d8d2da3be9fd08 100644 --- a/k-ahci.cc +++ b/k-ahci.cc @@ -175,6 +175,8 @@ void ahcistate::handle_error_interrupt() { pr_->serror = ~0U; pr_->command |= pcmd_start; // XXX must `READ LOG EXT` to clear error + // Note that panic() will prouce a page fault if there is no console + // (https://github.com/CS161/chickadee/issues/14) panic("SATA disk error"); } diff --git a/k-hardware.cc b/k-hardware.cc index 25751213652bbf2f984d948760f6ffbb99338856..33a0a3081ec02fa0a98240aad12fb2ba298b6635 100644 --- a/k-hardware.cc +++ b/k-hardware.cc @@ -175,7 +175,11 @@ struct log_printer : public printer { }; } +// things get ugly when multiple processes output at the same time! +static spinlock printLock; + void log_vprintf(const char* format, va_list val) { + spinlock_guard guard(printLock); log_printer p; p.vprintf(0, format, val); } @@ -434,6 +438,8 @@ void __cxa_guard_release(long long* arg) { // __cxa_pure_virtual() // Used as a placeholder for pure virtual functions. void __cxa_pure_virtual() { + // Note that panic() will prouce a page fault if there is no console + // (https://github.com/CS161/chickadee/issues/14) panic("pure virtual function called in kernel!\n"); } diff --git a/k-proc.cc b/k-proc.cc index d7f4b18c5586eeb4f8802d0f498961beff9c148a..e2079f2ad12457dc31c55481c0c7d61e983c69ba 100644 --- a/k-proc.cc +++ b/k-proc.cc @@ -70,6 +70,8 @@ void proc::init_kernel(pid_t pid, void (*f)()) { // Called when `k-exception.S` tries to run a non-runnable proc. void proc::panic_nonrunnable() { + // Note that panic() will prouce a page fault if there is no console + // (https://github.com/CS161/chickadee/issues/14) panic("Trying to resume proc %d, which is not runnable\n" "(proc state %d, recent user %%rip %p)", id_, pstate_.load(), recent_user_rip_); diff --git a/k-vmiter.cc b/k-vmiter.cc index b96d64d9f8c730dfd850abe8223a685f8f4fee56..94b29079862332e1821f3e20b17c253093b691e9 100644 --- a/k-vmiter.cc +++ b/k-vmiter.cc @@ -34,6 +34,8 @@ void vmiter::down() { pep_ = &pt->entry[pageindex(va_, level_)]; } if ((*pep_ & PTE_PAMASK) >= 0x100000000UL) { + // Note that panic() will prouce a page fault if there is no console + // (https://github.com/CS161/chickadee/issues/14) panic("Page table %p may contain uninitialized memory!\n" "(Page table contents: %p)\n", pt_, *pep_); } diff --git a/kernel.cc b/kernel.cc index ae283ba959b9218be2c482781fbc53c81d8726e9..b83393c70a99363769266e07d41a58068178fe7e 100644 --- a/kernel.cc +++ b/kernel.cc @@ -19,6 +19,7 @@ std::atomic kdisplay; static void tick(); static void boot_process_start(pid_t pid, const char* program_name); +void find_mapped_memory(); // kernel_start(command) @@ -26,6 +27,7 @@ static void boot_process_start(pid_t pid, const char* program_name); // string is an optional string passed from the boot loader. void kernel_start(const char* command) { + // find_mapped_memory(); // uncomment this to test the boot page table init_hardware(); console_clear(); @@ -431,3 +433,37 @@ void tick() { memshow(); } } + +/* The comment at init_boot_pagetable in bootentry.s says that 1 GB of RAM +* is mapped. It appears that only 128 MB is mapped. That is, when we write +* to one address, can we find the value when we read from the other address? +* Disable optimizations so we can step through this in a debugger. +*/ +#pragma GCC push_options +#pragma GCC optimize ("O0") + +void find_mapped_memory() { + long lowCanonical = 0x0; + long highCanonical = 0xffff800000000000; + long kernel = 0xffffffff80000000; + char magic = 0x42; + for (long i = 0x7ffffff; i < 0x80000000; ++i) { + char* p1 = (char*) (lowCanonical + i); + char* p2 = ((char*) highCanonical + i); + char* p3 = ((char*) kernel + i); + *p1 = magic; + char c1 = *p1; + char c2 = *p2; + char c3 = *p3; + if (c1 != magic) { + *p2 = magic; + c2 = *p2; + c1 = *p1; // break here! + } + c1 = c2; // avoid compiler warnings + c2 = c3; + c3 = c1; + } +} + +#pragma GCC pop_options diff --git a/lib.hh b/lib.hh index 855c9772196715e39bbb9582b7873458da5474f5..a306b8cabbc743a605f7e6bea569c6b5211c60f2 100644 --- a/lib.hh +++ b/lib.hh @@ -243,7 +243,6 @@ struct bitset_view { // System call numbers (passed in `%rax` at `syscall` time) -// Used in pset 1: #define SYSCALL_GETPID 1 #define SYSCALL_YIELD 2 #define SYSCALL_PAUSE 3 @@ -251,7 +250,6 @@ struct bitset_view { #define SYSCALL_PANIC 5 #define SYSCALL_PAGE_ALLOC 6 #define SYSCALL_FORK 7 -// Used in later psets: #define SYSCALL_EXIT 8 #define SYSCALL_READ 9 #define SYSCALL_WRITE 10 @@ -491,6 +489,8 @@ assert_memeq_fail(const char* file, int line, const char* msg, // panic(format, ...) // Print the message determined by `format` and fail. +// Note that panic() will prouce a page fault if there is no console +// (https://github.com/CS161/chickadee/issues/14) void __attribute__((noinline, noreturn, cold)) panic(const char* format, ...); diff --git a/p-allocator.cc b/p-allocator.cc index 550d22e633ebc03f795c2af0cec7f99ebde6e024..f16ca36b4a26d31061010de433cc14d3072f206d 100644 --- a/p-allocator.cc +++ b/p-allocator.cc @@ -6,12 +6,19 @@ extern uint8_t end[]; uint8_t* heap_top; uint8_t* stack_bottom; +// turn off optimization to facilitate debugging of forking new copies +#pragma GCC push_options +#pragma GCC optimize ("O0") + void process_main() { sys_kdisplay(KDISPLAY_MEMVIEWER); // Fork three new copies. (But ignore failures.) - (void) sys_fork(); - (void) sys_fork(); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" + pid_t pid1 = sys_fork(); + pid_t pid2 = sys_fork(); +#pragma GCC diagnostic pop pid_t p = sys_getpid(); srand(p); @@ -48,3 +55,5 @@ void process_main() { sys_yield(); } } + + #pragma GCC pop_options diff --git a/p-testkalloc.cc b/p-testkalloc.cc index 5c5f02b70f2b243127039d815d8fc410301aca28..dd350c2600b034bc7c310f58e88b9101a2e14101 100644 --- a/p-testkalloc.cc +++ b/p-testkalloc.cc @@ -5,5 +5,7 @@ void process_main() { // Running `testkalloc` should cause the kernel to run buddy allocator // tests. How you make this work is up to you. + // Note that panic() will prouce a page fault if there is no console + // (https://github.com/CS161/chickadee/issues/14) panic("testkalloc not implemented!\n"); } diff --git a/u-lib.cc b/u-lib.cc index 142fb215f4fd6056b1450eed0e8de0a5982216d5..00361ddf9ecc6a084e91d67344d07a76f29b399f 100644 --- a/u-lib.cc +++ b/u-lib.cc @@ -36,6 +36,8 @@ int printf(const char* format, ...) { // panic, assert_fail // Call the SYSCALL_PANIC system call so the kernel loops until Control-C. +// Note that panic() will prouce a page fault if there is no console +// (https://github.com/CS161/chickadee/issues/14) void panic(const char* format, ...) { va_list val;