From df86be29731b1bfdd5b1f06c4c977fdeee966875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Sun, 24 Jul 2016 00:45:20 +0200 Subject: [PATCH] [BOOT] Multiboot compliant kernel which runs 64 bit code --- .gdbinit | 22 ++++++ .gitignore | 2 + Makefile | 16 ++++- kernel/Link.ld | 33 +++++++++ kernel/Makefile | 71 +++++++++++++++++++ kernel/boot/boot.S | 122 +++++++++++++++++++++++++++++++++ kernel/boot/boot_structures.S | 54 +++++++++++++++ kernel/boot/kmain.c | 34 +++++++++ kernel/boot/multiboot_header.S | 23 +++++++ kernel/include/gdt.h | 12 ++++ kernel/include/mem.h | 27 ++++++++ kernel/include/multiboot.h | 14 ++++ util/grub.cfg | 7 +- 13 files changed, 433 insertions(+), 4 deletions(-) create mode 100644 kernel/Link.ld create mode 100644 kernel/Makefile create mode 100644 kernel/boot/boot.S create mode 100644 kernel/boot/boot_structures.S create mode 100644 kernel/boot/kmain.c create mode 100644 kernel/boot/multiboot_header.S create mode 100644 kernel/include/gdt.h create mode 100644 kernel/include/mem.h create mode 100644 kernel/include/multiboot.h diff --git a/.gdbinit b/.gdbinit index 24efe1e..5c8410b 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1 +1,23 @@ +file sysroot/boot/kernel target remote localhost:1234 + +define q +monitor quit +end + +define reg +monitor info registers +end + +define mm +monitor info mem +end + +define cpu +monitor info cpus +end + +define reset +monitor system_reset +end + diff --git a/.gitignore b/.gitignore index d929617..4bfe27b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ sysroot/ *.iso *.img *.log +kernel/obj/ +tags diff --git a/Makefile b/Makefile index 1088b87..8d9a638 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,24 @@ ifeq ($(MITTOS64),) $(error Build environment is not activated. Please source activate) endif -.PHONY: all clean +.PHONY: all clean kernel SHELL := bash -all: +CC=$(TARGET)-gcc +FLAGS_TO_PASS:= \ + CC=$(CC) + +all: kernel + +# A trick to only build phony target if necessary +kernel: +ifeq ($(shell make -sqC kernel || echo 1), 1) + @(. util/helpers.sh; print_info "Building kernel") + $(MAKE) -C kernel install $(FLAGS_TO_PASS) +endif clean: @(. util/helpers.sh; print_info "Cleaning up") + $(MAKE) -C kernel clean rm -f mittos64.iso rm -f qemu-error.log diff --git a/kernel/Link.ld b/kernel/Link.ld new file mode 100644 index 0000000..8522638 --- /dev/null +++ b/kernel/Link.ld @@ -0,0 +1,33 @@ +/* Guidance from http://wiki.osdev.org/Bare_Bones */ +ENTRY(_start) + +KERNEL_OFFSET = 0xFFFFFF8000000000; +KERNEL_START = 0x100000; + +SECTIONS +{ + . = KERNEL_START + KERNEL_OFFSET; + + .text : AT(ADDR(.text) - KERNEL_OFFSET) + { + *(.multiboot) /* Multiboot header must be in first 4k of elf */ + *(.text) + . = ALIGN(4096); + } + + .rodata BLOCK(4K) :AT(ADDR(.rodata) - KERNEL_OFFSET) + { + *(.rodata*) + } + + .data BLOCK(4K) :AT(ADDR(.data) - KERNEL_OFFSET) + { + *(.data) + } + + .bss BLOCK(4K) :AT(ADDR(.bss) - KERNEL_OFFSET) + { + *(.COMMON) + *(.bss) + } +} diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 0000000..8c6b86f --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,71 @@ +# +# Makefile for mittos64 kernel +# (c) Thomas Lovén 2016-2017 +# mittos64@thomasloven.com +# + +ifeq ($(MITTOS64),) + $(error Build environment is not activated. Please source activate) +endif + + +# Find all source files and corresponding object files +KERNEL_SRC := $(wildcard **/*.[cS]) +KERNEL_OBJS := $(addprefix obj/,$(patsubst %,%.o,$(basename $(KERNEL_SRC)))) + +# Kernel object file +KERNEL := obj/boot/kernel + + +# Default compilation flags +CFLAGS ?= -Wall -Wextra +CFLAGS += -ffreestanding -mcmodel=large +CPPFLAGS += -Iinclude +# Optimize only if not debugging +ifdef NDEBUG +CFLAGS += -O2 +CPPFLAGS += -DNDEBUG +else +CFLAGS += -ggdb -O0 +ASFLAGS += -ggdb +endif + +KERNEL_DEP := $(KERNEL_OBJS:.o=.d) +DEPFLAGS = -MT $@ -MMD -MP -MF obj/$*.d + +all: $(KERNEL) + +# Make sure the object directories exist +OBJ_DIRS := $(sort $(dir $(KERNEL_OBJS))) +$(KERNEL_OBJS): | $(OBJ_DIRS) +$(OBJ_DIRS): + mkdir -p $@ + +# The kernel needs some special flags +$(KERNEL): LDFLAGS += -n -nostdlib -T Link.ld +$(KERNEL): LDLIBS := -lgcc +$(KERNEL): $(KERNEL_OBJS) + $(LINK.c) $^ -o $@ + +# Use the default make compilation rules +obj/%.o: %.c obj/%.d + $(COMPILE.c) $(DEPFLAGS) $< -o $@ +obj/%.o: %.S obj/%.d + $(COMPILE.S) $(DEPFLAGS) $< -o $@ +obj/%.d: ; + +# For installing stuff +${SYSROOT}/%: obj/% + mkdir -p $(dir $@) + cp $< $@ + +install-kernel: ${SYSROOT}/boot/kernel + +install: install-kernel + +clean: + rm -rf obj/ + +-include $(KERNEL_DEP) + +.PHONY: all clean install install-kernel diff --git a/kernel/boot/boot.S b/kernel/boot/boot.S new file mode 100644 index 0000000..e78ad57 --- /dev/null +++ b/kernel/boot/boot.S @@ -0,0 +1,122 @@ +.intel_syntax noprefix +#include + +// Some info from here: http://os.phil-opp.com/multiboot-kernel.html +.section .text +.global _start +.code32 // GRUB leaves us in 32 bit mode +_start: + cli + + + // Check if long mode is available + // Otherwise, there's no point in continuing + call check_longmode + + + // Enable long mode through process described in + // AMD64 manual vol. 2 ch. 14 + + // Enable Physical Address Extension + mov eax, cr4 + or eax, 1<<5 + mov cr4, eax + + // Load page directory into cr3 + mov eax, offset V2P(BootP4) + mov cr3, eax + + // Enable long mode + mov ecx, 0xC0000080 + rdmsr + or eax, 1<<8 + wrmsr + + // Enable paging + mov eax, cr0 + or eax, 1<<31 + mov cr0, eax + + + // We are now in 32 bit long mode + // Go to 64 bit mode by loading a GDT + // and perforing a far jump + lgdt [V2P(BootGDTp)] + + mov eax, 0x10 + mov ss, eax + mov ds, eax + mov es, eax + + .extern long_mode_start + jmp 0x8:V2P(long_mode_start) + + +.code64 +long_mode_start: + // We are now in 64 bit long mode + // but we are still running code in the identity mapped low memory kernel mirror + // Next we jump to code in high memory (> 32 bits) + + movabs rax, offset .upper_memory + jmp rax + + +.upper_memory: + // We are now in high memory + + // Set up a stack and set up a fake return address to stop backtracing + movabs rsp, offset BootStack + push 0 + mov rbp, rsp + + // Remove identity mapping + mov rax, 0x0 + mov [V2P(BootP4)], rax + + // Call c kernel code + .extern kmain + call kmain + hlt + + + +.code32 + +// Check if CPUID is available +// Method described in AMD64 manual vol. 3 +check_cpuid: + pushfd + pushfd + xor dword ptr[esp], 0x00200000 + popfd + pushfd + pop eax + xor eax, [esp] + popfd + and eax, 1<<21 + jz error + ret + +// Check if long mode is available using cpuid +// AMD manual vol. 3 Appendix E +check_longmode: + call check_cpuid + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000001 + jb error + mov eax, 0x80000001 + cpuid + test edx, 1<<29 + jz error + ret + + + +// Jump here if something went wrong +error: + // Print ERR! to screen + mov [0xb8000], dword ptr 0x4f524f45 + mov [0xb8004], dword ptr 0x4f214f52 + jmp . diff --git a/kernel/boot/boot_structures.S b/kernel/boot/boot_structures.S new file mode 100644 index 0000000..2b83d47 --- /dev/null +++ b/kernel/boot/boot_structures.S @@ -0,0 +1,54 @@ +.intel_syntax noprefix +#include +#include + + +// Hardcode the initial page mapping +// Identity map the first GiB of memory +// Also map the same memory at KERNEL_OFFSET +.section .data +.align 0x1000 +.global BootP4 +BootP4: + .quad V2P(BootP3) + (PAGE_PRESENT | PAGE_WRITE) + .rept P4_OFFSET(KERNEL_OFFSET)-1 + .quad 0x0 + .endr + .quad V2P(BootP3) + (PAGE_PRESENT | PAGE_WRITE) + .rept 510 - P4_OFFSET(KERNEL_OFFSET) + 1 + .quad 0x0 + .endr +BootP3: + .quad V2P(BootP2) + (PAGE_PRESENT | PAGE_WRITE) + .rept 511 + .quad 0x0 + .endr +BootP2: + .set i, 0 + .rept 512 + .quad (i << 21) + (PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE) + .set i, (i+1) + .endr + + +// Hardcode initial GDT segments +// Only kernel code (0x8) and data (0x10) for now +.section .rodata +.align 0x1000 +.global BootGDT +.global BootGDTp +BootGDT: + .quad 0 + .quad (GDT_PRESENT | GDT_CODEDATA | GDT_WRITE | GDT_EXECUTE | GDT_64BIT) + .quad (GDT_PRESENT | GDT_CODEDATA | GDT_WRITE) +BootGDTp: // We can't put the gdt pointer in the gdt, it's too big + .short 3*8-1 + .quad V2P(BootGDT) + + +// Reserved space for the stack +.section .bss +.global BootStack +.align 0x1000 + .skip 0x1000 +BootStack: diff --git a/kernel/boot/kmain.c b/kernel/boot/kmain.c new file mode 100644 index 0000000..ed29e8a --- /dev/null +++ b/kernel/boot/kmain.c @@ -0,0 +1,34 @@ +#include + +#define VGA_MEMORY 0xb8000 +#define VGA_ROWS 24 +#define VGA_COLS 80 + +void clear_screen() +{ + // Clear the video memory + unsigned char *m = (void *)P2V(VGA_MEMORY); + for(int i = 0; i < VGA_ROWS*VGA_COLS*2; i++) + { + *m++ = 0; + } +} + +void prints(const char *s, unsigned int row, unsigned int col) +{ + // Very simple pure ascii string printing + unsigned char *m = (void *)P2V(VGA_MEMORY); + m += 2*VGA_COLS*row+2*col; + while(*s) + { + *m++ = *s++; + *m++ = 0x7; + } +} + +int kmain(void) +{ + clear_screen(); + prints("Hello, world!", 0, 0); + for(;;)asm("hlt"); +} diff --git a/kernel/boot/multiboot_header.S b/kernel/boot/multiboot_header.S new file mode 100644 index 0000000..e061259 --- /dev/null +++ b/kernel/boot/multiboot_header.S @@ -0,0 +1,23 @@ +.section .multiboot +#include + + +.align 0x1000 +MultiBootHeader1: + .long MBOOT1_MAGIC + .long MBOOT1_HEADER_FLAGS + .long MBOOT1_HEADER_CHECKSUM + + +.align 0x1000 +MultiBootHeader: + .long MBOOT2_MAGIC + .long MBOOT2_ARCH + .long MBOOT2_LENGTH + .long MBOOT2_CHECKSUM +// Multiboot tags + // End tag + .short 0 + .short 0 + .long 8 +MultiBootHeaderEnd: diff --git a/kernel/include/gdt.h b/kernel/include/gdt.h new file mode 100644 index 0000000..8da116a --- /dev/null +++ b/kernel/include/gdt.h @@ -0,0 +1,12 @@ +#pragma once + +#define GDT_WRITE (1<<41) +#define GDT_EXECUTE (1<<43) +#define GDT_CODEDATA (1<<44) +#define GDT_PRESENT (1<<47) +#define GDT_64BIT (1<<53) + +#ifndef __ASSEMBLER__ +#include +extern uint64_t BootGDT; +#endif diff --git a/kernel/include/mem.h b/kernel/include/mem.h new file mode 100644 index 0000000..25bdcb6 --- /dev/null +++ b/kernel/include/mem.h @@ -0,0 +1,27 @@ +#pragma once + +#define KERNEL_OFFSET 0xFFFFFF8000000000 + +#ifdef __ASSEMBLER__ + #define V2P(a) ((a) - KERNEL_OFFSET) + #define P2V(a) ((a) + KERNEL_OFFSET) + #define P1_OFFSET(p) (((p) >> 12) & 0x1FF) + #define P2_OFFSET(p) (((p) >> 21) & 0x1FF) + #define P3_OFFSET(p) (((p) >> 30) & 0x1FF) + #define P4_OFFSET(p) (((p) >> 39) & 0x1FF) +#else + #include + #define V2P(a) ((uintptr_t)(a) - KERNEL_OFFSET) + #define P2V(a) ((void *)((uintptr_t)(a) + KERNEL_OFFSET)) +#endif + +// Paging +#define PAGE_PRESENT 0x001 +#define PAGE_WRITE 0x002 +#define PAGE_USER 0x004 +#define PAGE_WRITETHROUGH 0x008 +#define PAGE_NOCACHE 0x010 +#define PAGE_ACCESSED 0x020 +#define PAGE_DIRTY 0x040 +#define PAGE_HUGE 0x080 +#define PAGE_GLOBAL 0x100 diff --git a/kernel/include/multiboot.h b/kernel/include/multiboot.h new file mode 100644 index 0000000..a80dc04 --- /dev/null +++ b/kernel/include/multiboot.h @@ -0,0 +1,14 @@ +#pragma once +#define MBOOT1_MAGIC 0x1BADB002 +#define MBOOT1_MAGIC2 0x2BADB002 +#define MBOOT1_FLAG_PAGE_ALIGN 0x01 +#define MBOOT1_FLAG_MEM_INFO 0x02 +#define MBOOT1_HEADER_FLAGS (MBOOT1_FLAG_PAGE_ALIGN | MBOOT1_FLAG_MEM_INFO) +#define MBOOT1_HEADER_CHECKSUM -(MBOOT1_HEADER_FLAGS + MBOOT1_MAGIC) + +#define MBOOT2_MAGIC 0xE85250D6 +#define MBOOT2_MAGIC2 0x36D76289 +#define MBOOT2_ARCH 0 +#define MBOOT2_LENGTH (MultiBootHeaderEnd - MultiBootHeader) +#define MBOOT2_CHECKSUM -(MBOOT2_MAGIC + MBOOT2_ARCH + MBOOT2_LENGTH) + diff --git a/util/grub.cfg b/util/grub.cfg index 07bf364..39678ab 100644 --- a/util/grub.cfg +++ b/util/grub.cfg @@ -1,6 +1,9 @@ -set timeout=0 +set timeout=1 set default=0 menuentry "mittos64" { - + multiboot2 /boot/kernel +} +menuentry "mittos64 MultiBoot1" { + multiboot /boot/kernel }