From af7c1f336b99faa000aa27d16371d619e7e79780 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Mon, 26 Jan 2015 16:28:11 +0800 Subject: [PATCH] amd64 "port" boots and prints "Hello World" to debug --- Kernel/Makefile | 32 +++++-- Kernel/arch/amd64/debug.rs | 43 ++++++++++ Kernel/arch/amd64/link.ld | 24 ++++-- Kernel/arch/amd64/mod.rs | 18 ++++ Kernel/arch/amd64/start.S | 162 ++++++++++++++++++++++++++++++++++-- Kernel/arch/amd64/x86_io.rs | 54 ++++++++++++ Kernel/main.rs | 6 ++ 7 files changed, 316 insertions(+), 23 deletions(-) create mode 100644 Kernel/arch/amd64/debug.rs create mode 100644 Kernel/arch/amd64/x86_io.rs diff --git a/Kernel/Makefile b/Kernel/Makefile index 0c952a1..88d7137 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -6,7 +6,7 @@ ARCH ?= amd64 RUSTC ?= rustc ifeq ($(ARCH),amd64) LD := x86_64-elf-ld - LD := x86_64-elf-ld + AS := x86_64-elf-as OBJDUMP := x86_64-elf-objdump else ifeq ($(ARCH),x86) LD := i586-elf-ld @@ -22,31 +22,45 @@ OBJDIR := .obj/$(ARCH)/ # Compiler Options LINKFLAGS := -T arch/$(ARCH)/link.ld LINKFLAGS += -Map $(OBJDIR)map.txt +LINKFLAGS += -z max-page-size=0x1000 RUSTFLAGS := -O --cfg arch__$(ARCH) --target=arch/$(ARCH)/target.json # Objects LIBCORE := $(OBJDIR)libcore.rlib OBJS := start.o kernel.o libcore.rlib - OBJS := $(OBJS:%=$(OBJDIR)%) +BIN := ../kernel.$(ARCH).bin + +.PHONY: all clean all: ../kernel.$(ARCH).bin clean: - $(RM) -rf ../kernel.$(ARCH).bin $(OBJDIR) + $(RM) -rf $(BIN) $(BIN).dsm $(OBJDIR) -../kernel.$(ARCH).bin: $(OBJS) arch/$(ARCH)/link.ld +# Final link command +$(BIN): $(OBJS) arch/$(ARCH)/link.ld $(LD) -o $@ $(LINKFLAGS) $(OBJS) $(OBJDUMP) -S $@ > $@.dsm +ifeq ($(ARCH),amd64) + @mv $@ $@.elf64 + @x86_64-elf-objcopy $@.elf64 -F elf32-i386 $@ +endif -$(OBJDIR)libcore.rlib: ../libcore/lib.rs +# Compile libcore from ../libcore/ +$(OBJDIR)libcore.rlib: ../libcore/lib.rs Makefile @mkdir -p $(dir $@) - $(RUSTC) $(RUSTFLAGS) -o $@ --crate-type=lib $< + $(RUSTC) $(RUSTFLAGS) -o $@ --crate-type=lib --emit=link,dep-info $< -$(OBJDIR)kernel.o: main.rs $(LIBCORE) +# Compile rust kernel object +$(OBJDIR)kernel.o: main.rs $(LIBCORE) Makefile @mkdir -p $(dir $@) - $(RUSTC) $(RUSTFLAGS) -o $@ --emit=obj $< --extern core=$(LIBCORE) + $(RUSTC) $(RUSTFLAGS) -o $@ --emit=obj,dep-info $< --extern core=$(LIBCORE) -$(OBJDIR)start.o: arch/$(ARCH)/start.S +# Compile architecture's assembly stub +$(OBJDIR)start.o: arch/$(ARCH)/start.S Makefile @mkdir -p $(dir $@) $(AS) $(ASFLAGS) -o $@ $< + +# Include dependency files +-include $(OBJ:%=%.d) diff --git a/Kernel/arch/amd64/debug.rs b/Kernel/arch/amd64/debug.rs new file mode 100644 index 0000000..ae3a117 --- /dev/null +++ b/Kernel/arch/amd64/debug.rs @@ -0,0 +1,43 @@ +/* + * Rust BareBones OS + * - By John Hodge (Mutabah/thePowersGang) + * + * arch/amd64/debug.rs + * - Debug output channel + * + * Writes debug to the standard PC serial port (0x3F8 .. 0x3FF) + * + * == LICENCE == + * This code has been put into the public domain, there are no restrictions on + * its use, and the author takes no liability. + */ +use prelude::*; + +/// Write a string to the output channel +/// +/// This method is unsafe because it does port accesses without synchronisation +pub unsafe fn puts(s: &str) +{ + for b in s.bytes() + { + putb(b); + } +} + +/// Write a single byte to the output channel +/// +/// This method is unsafe because it does port accesses without synchronisation +pub unsafe fn putb(b: u8) +{ + // Wait for the serial port's fifo to not be empty + while (::arch::x86_io::inb(0x3F8+5) & 0x20) == 0 + { + // Do nothing + } + // Send the byte out the serial port + ::arch::x86_io::outb(0x3F8, b); + + // Also send to the bochs 0xe9 hack + ::arch::x86_io::outb(0xe9, b); +} + diff --git a/Kernel/arch/amd64/link.ld b/Kernel/arch/amd64/link.ld index 4a08d0c..48024c0 100644 --- a/Kernel/arch/amd64/link.ld +++ b/Kernel/arch/amd64/link.ld @@ -1,28 +1,31 @@ ENTRY(start) -OUTPUT_FORMAT(elf32-i386) +OUTPUT_FORMAT(elf64-x86-64) KERNEL_BASE = 0xFFFFFFFF80000000; SECTIONS { - .multiboot : AT(ADDR(.multiboot)) { + . = 0x100000; + + . += SIZEOF_HEADERS; + + .init : AT(ADDR(.init)) { *(.multiboot) + *(.inittext) } - + . += KERNEL_BASE; - .text : AT(ADDR(.text) - KERNEL_BASE) { + .text ALIGN(0x1000) : AT(ADDR(.text) - KERNEL_BASE) { *(.text .text.*) } /* read-only data, page aligned to allow use of the no-execute feature */ - . = ALIGN(0x1000); - .rodata : AT(ADDR(.rodata) - KERNEL_BASE) { + .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - KERNEL_BASE) { *(.rodata .rodata.*) } /* Read-write data, page aligned for the .padata section */ - . = ALIGN(0x1000); - .data : AT(ADDR(.data) - KERNEL_BASE) { + .data ALIGN(0x1000) : AT(ADDR(.data) - KERNEL_BASE) { *(.padata) *(.data .data.*) } @@ -32,7 +35,10 @@ SECTIONS { *(.bss .bss.*) } - . = ALIGN(0x1000); kernel_end = .; + + /DISCARD/ : { + *(.note .note.*) + } } diff --git a/Kernel/arch/amd64/mod.rs b/Kernel/arch/amd64/mod.rs index e69de29..a73eca8 100644 --- a/Kernel/arch/amd64/mod.rs +++ b/Kernel/arch/amd64/mod.rs @@ -0,0 +1,18 @@ +/* + * Rust BareBones OS + * - By John Hodge (Mutabah/thePowersGang) + * + * arch/amd64/mod.rs + * - Top-level file for amd64 architecture + * + * == LICENCE == + * This code has been put into the public domain, there are no restrictions on + * its use, and the author takes no liability. + */ + +// x86 port IO +mod x86_io; + +// Debug output channel (uses serial) +pub mod debug; + diff --git a/Kernel/arch/amd64/start.S b/Kernel/arch/amd64/start.S index ad0a791..87438a3 100644 --- a/Kernel/arch/amd64/start.S +++ b/Kernel/arch/amd64/start.S @@ -20,7 +20,7 @@ MULTIBOOT_REQVIDMODE = (1<<2) MULTIBOOT_HEADER_MAGIC = 0x1BADB002 MULTIBOOT_HEADER_FLAGS = (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_REQVIDMODE) MULTIBOOT_CHECKSUM = -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) -.section .multiboot +.section .multiboot, "a" .globl mboot mboot: .long MULTIBOOT_HEADER_MAGIC @@ -35,15 +35,167 @@ mboot: .long 0 /* Height (no preference) */ .long 32 /* Depth (32-bit preferred) */ +#define DEBUG(c) mov $0x3f8, %dx ; mov $c, %al ; outb %al, %dx + /* === Code === */ -.section .text +.section .inittext, "ax" .globl start +.code32 start: - // The kernel starts in protected mode (32-bit mode, we want to switch to long mode) -.l: - jmp .l + /* The kernel starts in protected mode (32-bit mode, we want to switch to long mode) */ + + /* 1. Save multiboot state */ + mov %eax, mboot_sig - KERNEL_BASE + mov %ebx, mboot_ptr - KERNEL_BASE + + /* 2. Ensure that the CPU support long mode */ + mov $0x80000000, %eax + cpuid + /* - Check if CPUID supports the field we want to query */ + cmp $0x80000001, %eax + jbe not64bitCapable + /* - Test the IA-32e bit */ + mov $0x80000001, %eax + cpuid + test $0x20000000, %edx /* bit 29 = */ + jz not64bitCapable + + /* 3. Set up state for long mode */ + /* Enable: + PGE (Page Global Enable) + + PAE (Physical Address Extension) + + PSE (Page Size Extensions) + */ + mov %cr4, %eax + or $(0x80|0x20|0x10), %eax + mov %eax, %cr4 + + /* Load PDP4 */ + mov $(init_pml4 - KERNEL_BASE), %eax + mov %eax, %cr3 + + /* Enable IA-32e mode (Also enables SYSCALL and NX) */ + mov $0xC0000080, %ecx + rdmsr + or $(1 << 11)|(1 << 8)|(1 << 0), %eax /* NXE, LME, SCE */ + wrmsr + + /* Enable paging and enter long mode */ + mov %cr0, %eax + or $0x80010000, %eax /* PG & WP */ + mov %eax, %cr0 + lgdt GDTPtr_low - KERNEL_BASE + ljmp $0x08, $start64 + + +not64bitCapable: + /* If the CPU isn't 64-bit capable, print a message to serial/b8000 then busy wait */ + mov $0x3f8, %dx + mov $'N', %al ; outb %al, %dx + movw $0x100|'N', 0xb8000 + mov $'o', %al ; outb %al, %dx + movw $0x100|'o', 0xb8002 + mov $'t', %al ; outb %al, %dx + movw $0x100|'t', 0xb8004 + mov $'6', %al ; outb %al, %dx + movw $0x100|'6', 0xb8006 + mov $'4', %al ; outb %al, %dx + movw $0x100|'4', 0xb8008 + +not64bitCapable.loop: + hlt + jmp not64bitCapable.loop +.code64 +.globl start64 +start64: + /* Running in 64-bit mode, jump to high memory */ + lgdt GDTPtr + mov $start64_high, %rax + jmp *%rax + +.section .text +.extern kmain +.globl start64_high +start64_high: + /* and clear low-memory mapping */ + mov $0, %rax + mov %rax, init_pml4 - KERNEL_BASE + 0 + + /* Set up segment registers */ + mov $0x10, %ax + mov %ax, %ss + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + /* Set up stack pointer */ + mov $init_stack, %rsp + + /* call the rust code */ + call kmain + + /* and if that returns (it shouldn't) loop forever */ +start64.loop: + hlt + jmp start64.loop + + /* === Page-aligned data === */ .section .padata +/* Initial paging structures, four levels */ +/* The +3 for sub-pages indicates "present (1) + writable (2)" */ +init_pml4: + .quad low_pdpt - KERNEL_BASE + 3 /* low map for startup, will be cleared before rust code runs */ + .rept 512 - 3 + .quad 0 + .endr + .quad 0 /* If you so wish, this is a good place for the "Fractal" mapping */ + .quad init_pdpt - KERNEL_BASE + 3 /* Final mapping */ +low_pdpt: + .quad init_pd - KERNEL_BASE + 3 /* early init identity map */ + .rept 512 - 1 + .quad 0 + .endr +init_pdpt: /* covers the top 512GB, 1GB each entry */ + .rept 512 - 2 + .quad 0 + .endr + .quad init_pd - KERNEL_BASE + 3 /* at -2GB, identity map the kernel image */ + .quad 0 +init_pd: + /* 0x80 = Page size extension */ + .quad 0x000000 + 0x80 + 3 /* Map 2MB, enough for a 1MB kernel */ + .quad 0x200000 + 0x80 + 3 /* - give it another 2MB, just in case */ + .rept 512 - 2 + .quad 0 + .endr +init_stack_base: + .rept 0x1000 * 2 + .byte 0 + .endr +init_stack: +/* === General Data === */ +.section .data +mboot_sig: .long 0 +mboot_ptr: .long 0 +/* Global Descriptor Table */ +GDTPtr_low: + .word GDT - GDTEnd + .long GDT - KERNEL_BASE +GDTPtr: + .word GDT - GDTEnd + .quad GDT +.globl GDT +GDT: + .long 0, 0 + .long 0x00000000, 0x00209A00 /* 0x08: 64-bit Code */ + .long 0x00000000, 0x00009200 /* 0x10: 64-bit Data */ + .long 0x00000000, 0x0040FA00 /* 0x18: 32-bit User Code */ + .long 0x00000000, 0x0040F200 /* 0x20: User Data */ + .long 0x00000000, 0x0020FA00 /* 0x28: 64-bit User Code */ + .long 0x00000000, 0x0000F200 /* 0x30: User Data (64 version) */ +GDTEnd: diff --git a/Kernel/arch/amd64/x86_io.rs b/Kernel/arch/amd64/x86_io.rs new file mode 100644 index 0000000..888b808 --- /dev/null +++ b/Kernel/arch/amd64/x86_io.rs @@ -0,0 +1,54 @@ +/* + * Rust BareBones OS + * - By John Hodge (Mutabah/thePowersGang) + * + * arch/amd64/x86_io.rs + * - Support for the x86 IO bus + * + * == LICENCE == + * This code has been put into the public domain, there are no restrictions on + * its use, and the author takes no liability. + */ + +/// Write a byte to the specified port +pub unsafe fn outb(port: u16, val: u8) +{ + asm!("outb %al, %dx" : : "{dx}"(port), "{al}"(val)); +} + +/// Read a single byte from the specified port +pub unsafe fn inb(port: u16) -> u8 +{ + let ret : u8; + asm!("inb %dx, %al" : "={ax}"(ret) : "{dx}"(port)); + return ret; +} + +/// Write a word (16-bits) to the specified port +pub unsafe fn outw(port: u16, val: u16) +{ + asm!("outb %ax, %dx" : : "{dx}"(port), "{al}"(val)); +} + +/// Read a word (16-bits) from the specified port +pub unsafe fn inw(port: u16) -> u16 +{ + let ret : u16; + asm!("inb %dx, %ax" : "={ax}"(ret) : "{dx}"(port)); + return ret; +} + +/// Write a long/double-word (32-bits) to the specified port +pub unsafe fn outl(port: u16, val: u32) +{ + asm!("outb %eax, %dx" : : "{dx}"(port), "{al}"(val)); +} + +/// Read a long/double-word (32-bits) from the specified port +pub unsafe fn inl(port: u16) -> u32 +{ + let ret : u32; + asm!("inb %dx, %eax" : "={ax}"(ret) : "{dx}"(port)); + return ret; +} + diff --git a/Kernel/main.rs b/Kernel/main.rs index f608c66..f50a664 100644 --- a/Kernel/main.rs +++ b/Kernel/main.rs @@ -10,6 +10,7 @@ */ #![no_std] //< Kernels can't use std #![feature(lang_items)] //< unwind needs to define lang items +#![feature(asm)] //< As a kernel, we need inline assembly use prelude::*; @@ -29,9 +30,14 @@ mod prelude; mod unwind; // Kernel entrypoint +#[lang="start"] #[no_mangle] fn kmain() { + unsafe { + // The arch::debug functions are unsafe, as they do no synchronisation + ::arch::debug::puts("Hello World!\n") + } loop {} }