Commit b25de1a4 authored by Eddie Kohler's avatar Eddie Kohler

Handout code for 2020.

parents
#*#
*.aux
*.backup
*.core
*.dSYM
*.dvi
*.gcda
*.img
*.log
*.noopt
*.o
*.optimized
*.unsafe
*~
.DS_Store
.cproject
.deps
.gitcheckout
.goutputstream-*
.project
a.out
config.mk
core*
debuglog.txt
log.txt
obj
pset.tgz
pset[1-9].tgz
pset[1-9]grade.tgz
strace.out
tags
tags.*
typescript
vgcore*
QEMUIMAGEFILES = chickadeeboot.img chickadeefs.img
all: $(QEMUIMAGEFILES)
# Place local configuration options, such as `CC=clang`, in
# `config.mk` so you don't have to list them every time.
-include config.mk
# `$(V)` controls whether the makefiles print verbose commands (the shell
# commands run by Make) or brief commands (like `COMPILE`).
# For brief commands, run `make all`.
# For verbose commands, run `make V=1 all`.
V = 0
ifeq ($(V),1)
cxxcompile = $(CXX) $(CPPFLAGS) $(DEPCFLAGS) $(1)
assemble = $(CXX) $(CPPFLAGS) $(DEPCFLAGS) $(ASFLAGS) $(1)
link = $(LD) $(LDFLAGS) $(1)
run = $(1) $(3)
else
cxxcompile = @/bin/echo " " $(2) && $(CXX) $(CPPFLAGS) $(DEPCFLAGS) $(1)
assemble = @/bin/echo " " $(2) && $(CXX) $(CPPFLAGS) $(DEPCFLAGS) $(ASFLAGS) $(1)
link = @/bin/echo " " $(2) $(patsubst %.full,%,$@) && $(LD) $(LDFLAGS) $(1)
run = @$(if $(2),/bin/echo " " $(2) $(3) &&,) $(1) $(3)
endif
# `$(D)` controls how QEMU responds to faults. Run `make D=1 run` to
# ask QEMU to print debugging information about interrupts and CPU resets,
# and to quit after the first triple fault instead of rebooting.
#
# `$(NCPU)` controls the number of CPUs QEMU should use. It defaults to 2.
NCPU = 2
LOG ?= file:log.txt
QEMUOPT = -net none -parallel $(LOG) -smp $(NCPU)
ifeq ($(D),1)
QEMUOPT += -d int,cpu_reset,guest_errors -no-reboot
endif
# Sets of object files
BOOT_OBJS = $(OBJDIR)/bootentry.o $(OBJDIR)/boot.o
KERNEL_OBJS = $(OBJDIR)/k-exception.ko \
$(OBJDIR)/kernel.ko $(OBJDIR)/k-cpu.ko $(OBJDIR)/k-proc.ko \
$(OBJDIR)/k-alloc.ko $(OBJDIR)/k-vmiter.ko $(OBJDIR)/k-devices.ko \
$(OBJDIR)/k-init.ko $(OBJDIR)/k-hardware.ko $(OBJDIR)/k-mpspec.ko \
$(OBJDIR)/crc32c.ko \
$(OBJDIR)/k-ahci.ko $(OBJDIR)/k-chkfs.ko $(OBJDIR)/k-chkfsiter.ko \
$(OBJDIR)/k-memviewer.ko $(OBJDIR)/lib.ko $(OBJDIR)/k-initfs.ko
PROCESSES = $(patsubst %.cc,%,$(wildcard p-*.cc))
PROCESS_LIB_OBJS = $(OBJDIR)/lib.uo $(OBJDIR)/u-lib.uo $(OBJDIR)/crc32c.uo
INITFS_CONTENTS = \
$(shell find initfs -type f -not -name '\#*\#' -not -name '*~' 2>/dev/null) \
$(patsubst %,obj/%,$(PROCESSES))
INITFS_PARAMS ?=
ifeq ($(HALT),1)
INITFS_PARAMS += .halt=1
endif
DISKFS_CONTENTS = \
$(shell find initfs -type f -not -name '\#*\#' -not -name '*~' 2>/dev/null) \
$(shell find diskfs -type f -not -name '\#*\#' -not -name '*~' 2>/dev/null) \
$(patsubst %,obj/%,$(PROCESSES))
-include build/rules.mk
# Define `CHICKADEE_FIRST_PROCESS` if appropriate
RUNCMD_LASTWORD := $(filter run-%,$(MAKECMDGOALS))
ifeq ($(words $(RUNCMD_LASTWORD)),1)
RUNCMD_LASTWORD := $(lastword $(subst -, ,$(RUNCMD_LASTWORD)))
ifneq ($(filter p-$(RUNCMD_LASTWORD),$(PROCESSES)),)
RUNSUFFIX := $(RUNCMD_LASTWORD)
CHICKADEE_FIRST_PROCESS := $(RUNCMD_LASTWORD)
endif
endif
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
endif
ifeq ($(wildcard $(OBJDIR)/k-firstprocess.h),)
KERNELBUILDSTAMPS += $(OBJDIR)/k-firstprocess.h
endif
# How to make object files
$(OBJDIR)/%.ko: %.cc $(KERNELBUILDSTAMPS)
$(call cxxcompile,$(KERNELCXXFLAGS) -O2 -DCHICKADEE_KERNEL -mcmodel=kernel -c $< -o $@,COMPILE $<)
$(OBJDIR)/%.ko: %.S $(OBJDIR)/k-asm.h $(KERNELBUILDSTAMPS)
$(call assemble,-O2 -mcmodel=kernel -c $< -o $@,ASSEMBLE $<)
$(OBJDIR)/boot.o: $(OBJDIR)/%.o: boot.cc $(KERNELBUILDSTAMPS)
$(call cxxcompile,$(CXXFLAGS) -Os -fomit-frame-pointer -c $< -o $@,COMPILE $<)
$(OBJDIR)/bootentry.o: $(OBJDIR)/%.o: \
bootentry.S $(OBJDIR)/k-asm.h $(KERNELBUILDSTAMPS)
$(call assemble,-Os -fomit-frame-pointer -c $< -o $@,ASSEMBLE $<)
$(OBJDIR)/%.uo: %.cc $(BUILDSTAMPS)
$(call cxxcompile,$(CXXFLAGS) -O1 -DCHICKADEE_PROCESS -c $< -o $@,COMPILE $<)
$(OBJDIR)/%.uo: %.S $(OBJDIR)/u-asm.h $(KERNELBUILDSTAMPS)
$(call assemble,-O2 -c $< -o $@,ASSEMBLE $<)
# How to make supporting source files
$(OBJDIR)/k-asm.h: kernel.hh lib.hh types.h x86-64.h build/mkkernelasm.awk $(KERNELBUILDSTAMPS)
$(call cxxcompile,-dM -E kernel.hh | awk -f build/mkkernelasm.awk | sort > $@,CREATE $@)
@if test ! -s $@; then echo '* Error creating $@!' 1>&2; exit 1; fi
$(OBJDIR)/u-asm.h: u-lib.hh lib.hh types.h x86-64.h build/mkkernelasm.awk $(BUILDSTAMPS)
$(call cxxcompile,-dM -E u-lib.hh | awk -f build/mkkernelasm.awk | sort > $@,CREATE $@)
@if test ! -s $@; then echo '* Error creating $@!' 1>&2; exit 1; fi
$(OBJDIR)/k-firstprocess.h:
$(call run,echo '#ifndef CHICKADEE_FIRST_PROCESS' >$@; echo '#define CHICKADEE_FIRST_PROCESS "$(CHICKADEE_FIRST_PROCESS)"' >>$@; echo '#endif' >>$@,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,$@)
$(OBJDIR)/k-initfs.ko: $(OBJDIR)/k-initfs.cc
$(call cxxcompile,$(KERNELCXXFLAGS) -O2 -DCHICKADEE_KERNEL -mcmodel=kernel -c $< -o $@,COMPILE $<)
# How to make binaries and the boot sector
$(OBJDIR)/kernel.full: $(KERNEL_OBJS) $(INITFS_CONTENTS) kernel.ld
$(call link,-T kernel.ld -o $@ $(KERNEL_OBJS) -b binary $(INITFS_CONTENTS),LINK)
$(OBJDIR)/p-%.full: $(OBJDIR)/p-%.uo $(PROCESS_LIB_OBJS) process.ld
$(call link,-T process.ld -o $@ $< $(PROCESS_LIB_OBJS),LINK)
$(OBJDIR)/kernel: $(OBJDIR)/kernel.full $(OBJDIR)/mkchickadeesymtab
$(call run,$(OBJDUMP) -C -S -j .lowtext -j .text -j .ctors $< >$@.asm)
$(call run,$(NM) -n $< >$@.sym)
$(call run,$(OBJCOPY) -j .lowtext -j .lowdata -j .text -j .rodata -j .data -j .bss -j .ctors -j .init_array $<,STRIP,$@)
@if $(OBJDUMP) -p $@ | grep off | grep -iv 'off[ 0-9a-fx]*000 ' >/dev/null 2>&1; then echo "* Warning: Some sections of kernel object file are not page-aligned." 1>&2; fi
$(call run,$(OBJDIR)/mkchickadeesymtab $@)
$(OBJDIR)/%: $(OBJDIR)/%.full
$(call run,$(OBJDUMP) -C -S -j .text -j .ctors $< >$@.asm)
$(call run,$(NM) -n $< >$@.sym)
$(call run,$(OBJCOPY) -j .text -j .rodata -j .data -j .bss -j .ctors -j .init_array $<,STRIP,$@)
$(OBJDIR)/bootsector: $(BOOT_OBJS) boot.ld
$(call link,-T boot.ld -o $@.full $(BOOT_OBJS),LINK)
$(call run,$(OBJDUMP) -C -S $@.full >$@.asm)
$(call run,$(NM) -n $@.full >$@.sym)
$(call run,$(OBJCOPY) -S -O binary -j .text $@.full $@)
# How to make host program for ensuring a loaded symbol table
$(OBJDIR)/mkchickadeesymtab: build/mkchickadeesymtab.cc $(BUILDSTAMPS)
$(call run,$(HOSTCXX) $(CPPFLAGS) $(HOSTCXXFLAGS) $(DEPCFLAGS) -g -o $@,HOSTCOMPILE,$<)
# How to make host programs for constructing & checking file systems
$(OBJDIR)/%.o: %.cc $(BUILDSTAMPS)
$(call run,$(HOSTCXX) $(CPPFLAGS) $(HOSTCXXFLAGS) $(DEPCFLAGS) -c -o $@,HOSTCOMPILE,$<)
$(OBJDIR)/%.o: build/%.cc $(BUILDSTAMPS)
$(call run,$(HOSTCXX) $(CPPFLAGS) $(HOSTCXXFLAGS) $(DEPCFLAGS) -c -o $@,HOSTCOMPILE,$<)
$(OBJDIR)/mkchickadeefs: build/mkchickadeefs.cc $(BUILDSTAMPS)
$(call run,$(HOSTCXX) $(CPPFLAGS) $(HOSTCXXFLAGS) $(DEPCFLAGS) -o $@,HOSTCOMPILE,$<)
CHICKADEEFSCK_OBJS = $(OBJDIR)/chickadeefsck.o \
$(OBJDIR)/journalreplayer.o \
$(OBJDIR)/crc32c.o
$(OBJDIR)/chickadeefsck: $(CHICKADEEFSCK_OBJS) $(BUILDSTAMPS)
$(call run,$(HOSTCXX) $(CPPFLAGS) $(HOSTCXXFLAGS) $(DEPCFLAGS) $(CHICKADEEFSCK_OBJS) -o,HOSTLINK,$@)
# 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 $@)
cleanfs:
$(call run,rm -f chickadeefs.img,RM chickadeefs.img)
fsck: chickadeefs.img $(OBJDIR)/chickadeefsck
$(call run,$(OBJDIR)/chickadeefsck $< && echo "* File system OK",FSCK $<)
# How to run QEMU
QEMUIMG = -M q35 \
-device piix4-ide,bus=pcie.0,id=piix4-ide \
-drive file=chickadeeboot.img,if=none,format=raw,id=bootdisk \
-device ide-hd,drive=bootdisk,bus=piix4-ide.0 \
-drive file=chickadeefs.img,if=none,format=raw,id=maindisk \
-device ide-hd,drive=maindisk,bus=ide.0
run: run-$(QEMUDISPLAY)
@:
run-graphic: $(QEMUIMAGEFILES) check-qemu
@echo '* Run `gdb -x build/chickadee.gdb` to connect gdb to qemu.' 1>&2
$(call run,$(QEMU_PRELOAD) $(QEMU) $(QEMUOPT) -gdb tcp::12949 $(QEMUIMG),QEMU $<)
run-console: $(QEMUIMAGEFILES) check-qemu-console
@echo '* Run `gdb -x build/chickadee.gdb` to connect gdb to qemu.' 1>&2
$(call run,$(QEMU) $(QEMUOPT) -curses -gdb tcp::12949 $(QEMUIMG),QEMU $<)
run-monitor: $(QEMUIMAGEFILES) check-qemu
$(call run,$(QEMU_PRELOAD) $(QEMU) $(QEMUOPT) -monitor stdio $(QEMUIMG),QEMU $<)
run-gdb: run-gdb-$(QEMUDISPLAY)
@:
run-gdb-graphic: $(QEMUIMAGEFILES) check-qemu
$(call run,$(QEMU_PRELOAD) $(QEMU) $(QEMUOPT) -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
run-console-$(RUNSUFFIX): run-console
run-monitor-$(RUNSUFFIX): run-monitor
run-gdb-$(RUNSUFFIX): run-gdb
run-gdb-graphic-$(RUNSUFFIX): run-gdb-graphic
run-gdb-console-$(RUNSUFFIX): run-gdb-console
# Kill all my qemus
kill:
-killall -u $$(whoami) $(QEMU)
@sleep 0.2; if ps -U $$(whoami) | grep $(QEMU) >/dev/null; then killall -9 -u $$(whoami) $(QEMU); fi
Chickadee OS
============
This is Chickadee, a teaching operating system built for Harvard’s
[CS 161].
Quickstart: `make run` or `make run-PROGRAM`
Make targets
------------
Run `make NCPU=N run` to run with `N` virtual CPUs (default is 2).
Run `make SAN=1 run` to run with sanitizers.
Normally Chickadee’s debug log is written to `log.txt`. Run `make LOG=stdio
run` to redirect the debug log to the standard output, or `make
LOG=file:FILENAME run` to redirect it to `FILENAME`.
Run `make D=1 run` to ask QEMU to print verbose information about interrupts
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`.
`make HALT=1 run-PROGRAM` should make QEMU exit once all processes are done.
Troubleshooting
---------------
If you experience runtime errors involving `obj/libqemu-nograb.so.1`, put
`QEMU_PRELOAD_LIBRARY=` in `config.mk`. This disables a shim we use that
prevents QEMU from grabbing the mouse.
Source files
------------
### Common files
| File | Description |
| --------------- | ---------------------------- |
| `types.h` | Type definitions |
| `lib.hh/cc` | Chickadee C library |
| `x86-64.h` | x86-64 hardware definitions |
| `elf.h` | ELF64 structures |
### Boot loader
| File | Description |
| --------------- | ---------------------------- |
| `bootentry.S` | Boot loader entry point |
| `boot.cc` | Boot loader main code |
| `boot.ld` | Boot loader linker script |
### Kernel core
| File | Description |
| ------------------- | ------------------------------------ |
| `kernel.hh` | Kernel declarations |
| `k-exception.S` | Kernel entry points |
| `k-init.cc` | Kernel initialization |
| `k-lock.hh` | Kernel spinlock |
| `k-vmiter.hh/cc` | Page table iterators |
| `k-cpu.cc` | Kernel `cpustate` type |
| `k-proc.cc` | Kernel `proc` type |
| `kernel.cc` | Kernel exception handlers |
| `k-memviewer.cc` | Kernel memory viewer |
| `kernel.ld` | Kernel linker script |
### Kernel libraries
| File | Description |
| ------------------- | ------------------------------------ |
| `k-memrange.hh` | Memory range type tracker |
| `k-hardware.cc` | General hardware access |
| `k-devices.hh/cc` | Keyboard, console, memory files |
| `k-apic.hh/cc` | Interrupt controller hardware |
| `k-pci.hh` | PCI bus hardware |
| `k-mpspec.cc` | Boot-time configuration |
| `k-sanitizers.cc` | Sanitizer support |
### Processes
| File | Description |
| ----------------- | ------------------------------------------------ |
| `u-lib.cc/hh` | Process library and system call implementations |
| `p-allocator.cc` | Allocator process |
| `process.ld` | Process binary linker script |
### File system
| File | Description |
| --------------------- | ------------------------------------------------ |
| `chickadeefs.hh` | Defines chkfs (ChickadeeFS) layout |
| `journalreplayer.cc` | Logic for replaying chkfs journals |
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
produces other files that can be useful to examine.
| File | Description |
| ---------------------------- | ------------------------------------ |
| `obj/kernel.asm` | Kernel assembly (with addresses) |
| `obj/kernel.sym` | Kernel defined symbols |
| `obj/p-allocator.asm`, `sym` | Same for process binaries |
[CS 161]: https://read.seas.harvard.edu/cs161/2020/
[triple fault]: https://en.wikipedia.org/wiki/Triple_fault
#include "x86-64.h"
#include "elf.h"
// boot.cc
//
// Chickadee boot loader. Loads the kernel from the first IDE hard disk.
//
// A BOOT LOADER is a tiny program that loads an operating system into
// memory. It has to be tiny because it can contain no more than 510 bytes
// of instructions: it is stored in the disk's first 512-byte sector.
//
// When the CPU boots it loads the BIOS into memory and executes it. The
// BIOS intializes devices and CPU state, reads the first 512-byte sector of
// 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
// 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.
//
// The main kernel is stored as a contiguous ELF executable image
// starting in the disk's sector KERNEL_START_SECTOR.
#define SECTORSIZE 512
#define ELFHDR ((elf_header*) 0x3000) // scratch space
#define KERNEL_START_SECTOR 128
extern "C" {
[[noreturn]] void boot();
static void boot_readsect(uintptr_t dst, uint32_t src_sect);
static void boot_readseg(uintptr_t dst, uint32_t src_sect,
size_t filesz, size_t memsz);
}
// boot
// Load the kernel and jump to it.
[[noreturn]] void boot() {
// read 1st page off disk (should include programs as well as header)
// and check validity
boot_readseg((uintptr_t) ELFHDR, KERNEL_START_SECTOR,
PAGESIZE, PAGESIZE);
while (ELFHDR->e_magic != ELF_MAGIC) {
/* do nothing */
}
// load each program segment
elf_program* ph = (elf_program*) ((uint8_t*) ELFHDR + ELFHDR->e_phoff);
elf_program* eph = ph + ELFHDR->e_phnum;
for (; ph < eph; ++ph) {
boot_readseg(ph->p_va,
KERNEL_START_SECTOR + ph->p_offset / SECTORSIZE,
ph->p_filesz, ph->p_memsz);
}
// jump to the kernel
using kernel_entry_t = void (*)();
[[noreturn]] kernel_entry_t kernel_entry = (kernel_entry_t) ELFHDR->e_entry;
kernel_entry();
}
// boot_readseg(dst, src_sect, filesz, memsz)
// Load an ELF segment at virtual address `dst` from the IDE disk's sector
// `src_sect`. Copies `filesz` bytes into memory at `dst` from sectors
// `src_sect` and up, then clears memory in the range
// `[dst+filesz, dst+memsz)`.
static void boot_readseg(uintptr_t ptr, uint32_t src_sect,
size_t filesz, size_t memsz) {
uintptr_t end_ptr = ptr + filesz;
memsz += ptr;
// round down to sector boundary
ptr &= ~(SECTORSIZE - 1);
// read sectors
for (; ptr < end_ptr; ptr += SECTORSIZE, ++src_sect) {
boot_readsect(ptr, src_sect);
}
// clear bss segment
for (; end_ptr < memsz; ++end_ptr) {
*(uint8_t*) end_ptr = 0;
}
}
// boot_waitdisk
// Wait for the disk to be ready.
static void boot_waitdisk() {
// Wait until the ATA status register says ready (0x40 is on)
// & not busy (0x80 is off)
while ((inb(0x1F7) & 0xC0) != 0x40) {
/* do nothing */
}
}
// boot_readsect(dst, src_sect)
// Read disk sector number `src_sect` into address `dst`.
static void boot_readsect(uintptr_t dst, uint32_t src_sect) {
// programmed I/O for "read sector"
boot_waitdisk();
outb(0x1F2, 1); // send `count = 1` as an ATA argument
outb(0x1F3, src_sect); // send `src_sect`, the sector number
outb(0x1F4, src_sect >> 8);
outb(0x1F5, src_sect >> 16);
outb(0x1F6, (src_sect >> 24) | 0xE0);
outb(0x1F7, 0x20); // send the command: 0x20 = read sectors
// then move the data into memory
boot_waitdisk();
insl(0x1F0, (void*) dst, SECTORSIZE/4); // read 128 words from the disk
}
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
ENTRY(boot_start)
SECTIONS {
. = 0x7c00;
/* Text segment: instructions only.
The boot loader must fit in 512 bytes. */
.text : {
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
}
/DISCARD/ : { *(.eh_frame .note.GNU-stack) }
}
###############################################################################
# BOOT ENTRY POINT
#
# After the BIOS initializes the hardware on startup or system reset,
# it loads the first 512-byte sector of the hard disk
# into physical addresses 0x7C00-0x7DFF.
# Then it jumps to address 0x7C00 and the OS starts running!
#
# This file contains the code loaded at that address.
# The `boot_start` routine switches the CPU out of compatibility mode,
# then calls `boot` from `boot.cc` to finish the booting process.
#
# There is no need to understand this code in detail!
# (You may stop reading now.)
#
###############################################################################
###############################################################################
# For Your Information: COMPATIBILITY MODES
#
# The Intel x86 architecture has many compatibility modes, going back to
# the 8086, which supported only 16-bit addresses. When the BIOS calls
# into the OS, it is running in the "most compatible" mode, 16-bit real
# mode. The machine acts like addresses are only 16 bits long,
# there's no paging support, and there isn't even any support for
# user-mode applications. The following weird magic transitions the
# processor to 64-bit mode and sets up a page table suitable for initial
# kernel execution.
#
###############################################################################
/* import constants from kernel.hh and x86-64.h */
#include "obj/k-asm.h"
.globl boot_start # Entry point
boot_start:
.code16 # This runs in real mode
cli # Disable interrupts
cld # String operations increment
# All segments are initially 0.
# Set up the stack pointer, growing downward from 0x7c00.
movw $boot_start, %sp
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
int $0x15
init_boot_pagetable:
# clear memory for boot page table
.set BOOT_PAGETABLE,0x1000
movl $BOOT_PAGETABLE, %edi
xorl %eax, %eax
movl $(0x2000 / 4), %ecx
rep stosl
# set up boot page table
# 0x1000: L1 page table; entries 0, 256, and 511 point to:
# 0x2000: L2 page table; entries 0 and 510 map 1st 1GB of physmem
# This weird setup is the minimal page table that maps all of
# low-canonical, high-canonical, and kernel-text addresses to
# the first 1GB of physical memory.
movl $BOOT_PAGETABLE, %edi
leal 0x1000 + PTE_P + PTE_W(%edi), %ecx
movl %ecx, (%edi)