├── .gitignore ├── .travis.yml ├── Kernel ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── arch │ ├── amd64 │ │ ├── link.ld │ │ ├── mod.rs │ │ ├── start.S │ │ └── target.json │ ├── x86 │ │ ├── link.ld │ │ ├── mod.rs │ │ ├── start.S │ │ └── target.json │ └── x86_common │ │ ├── debug.rs │ │ └── io.rs ├── logging.rs ├── macros.rs ├── main.rs └── unwind.rs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | 3 | /Kernel/target 4 | 5 | /*.dsm 6 | /*.bin 7 | /*.bin.elf64 8 | /*.tar.gz 9 | 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: nightly 4 | env: 5 | matrix: 6 | - ARCH=amd64 7 | # - ARCH=x86 8 | script: 9 | - make -C Kernel/ UPDATE TRIPLE= 10 | - make -C Kernel/ ../libcore/lib.rs TRIPLE= 11 | - make -C Kernel/ all TRIPLE= 12 | -------------------------------------------------------------------------------- /Kernel/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "kernel" 5 | version = "0.0.0" 6 | -------------------------------------------------------------------------------- /Kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel" 3 | version = "0.0.0" 4 | 5 | [lib] 6 | path = "main.rs" 7 | crate-type = ["staticlib"] 8 | -------------------------------------------------------------------------------- /Kernel/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # CONFIG: Architecture to build for 3 | ARCH ?= amd64 4 | 5 | ifeq ($(ARCH),amd64) 6 | TRIPLE ?= x86_64-none-elf- 7 | else ifeq ($(ARCH),x86) 8 | TRIPLE ?= i686-elf- 9 | else 10 | $(error Unknown architecture $(ARCH)) 11 | endif 12 | 13 | 14 | # Toolchain commands (can be overridden) 15 | CARGO ?= cargo 16 | RUSTC ?= rustc 17 | LD := $(TRIPLE)ld 18 | AS := $(TRIPLE)as 19 | OBJDUMP := $(TRIPLE)objdump 20 | OBJCOPY := $(TRIPLE)objcopy 21 | 22 | # Object directory 23 | OBJDIR := .obj/$(ARCH)/ 24 | 25 | LINKSCRIPT := arch/$(ARCH)/link.ld 26 | TARGETSPEC := arch/$(ARCH)/target.json 27 | # Compiler Options 28 | LINKFLAGS := -T $(LINKSCRIPT) 29 | LINKFLAGS += -Map $(OBJDIR)map.txt 30 | LINKFLAGS += --gc-sections 31 | LINKFLAGS += -z max-page-size=0x1000 32 | 33 | RUSTFLAGS := --cfg arch__$(ARCH) -C soft-float 34 | RUSTFLAGS += -C panic=abort 35 | 36 | # Objects 37 | OBJS := start.o kernel.a 38 | OBJS := $(OBJS:%=$(OBJDIR)%) 39 | BIN := ../kernel.$(ARCH).bin 40 | 41 | .PHONY: all clean PHONY 42 | 43 | all: $(BIN) 44 | 45 | clean: 46 | $(RM) -rf $(BIN) $(BIN).dsm $(OBJDIR) 47 | 48 | # Final link command 49 | $(BIN): $(OBJS) arch/$(ARCH)/link.ld 50 | $(LD) -o $@ $(LINKFLAGS) $(OBJS) 51 | $(OBJDUMP) -S $@ > $@.dsm 52 | ifeq ($(ARCH),amd64) 53 | @mv $@ $@.elf64 54 | @$(OBJCOPY) $@.elf64 -F elf32-i386 $@ 55 | endif 56 | 57 | 58 | # Compile rust kernel object 59 | $(OBJDIR)kernel.a: PHONY Makefile $(TARGETSPEC) 60 | @mkdir -p $(dir $@) 61 | RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build -Z build-std=core --target=$(TARGETSPEC) --release 62 | @cp --preserve target/target/release/libkernel.a $@ 63 | 64 | # Compile architecture's assembly stub 65 | $(OBJDIR)start.o: arch/$(ARCH)/start.S Makefile 66 | @mkdir -p $(dir $@) 67 | $(AS) $(ASFLAGS) -o $@ $< 68 | 69 | 70 | # Include dependency files 71 | -include $(OBJDIR)start.d 72 | -------------------------------------------------------------------------------- /Kernel/arch/amd64/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | OUTPUT_FORMAT(elf64-x86-64) 3 | 4 | KERNEL_BASE = 0xFFFFFFFF80000000; 5 | 6 | SECTIONS { 7 | 8 | . = 0x100000; 9 | 10 | . += SIZEOF_HEADERS; 11 | 12 | .init : AT(ADDR(.init)) { 13 | KEEP( *(.multiboot) ) 14 | *(.inittext) 15 | } 16 | 17 | . += KERNEL_BASE; 18 | 19 | .text ALIGN(0x1000) : AT(ADDR(.text) - KERNEL_BASE) { 20 | *(.text .text.*) 21 | } 22 | 23 | /* read-only data, page aligned to allow use of the no-execute feature */ 24 | .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - KERNEL_BASE) { 25 | *(.rodata .rodata.*) 26 | } 27 | 28 | /* Read-write data, page aligned for the .padata section */ 29 | .data ALIGN(0x1000) : AT(ADDR(.data) - KERNEL_BASE) { 30 | *(.padata) 31 | *(.data .data.*) 32 | } 33 | 34 | /* Zero-initialised data */ 35 | .bss : AT(ADDR(.bss) - KERNEL_BASE) { 36 | *(.bss .bss.*) 37 | } 38 | 39 | kernel_end = .; 40 | 41 | /DISCARD/ : { 42 | *(.note .note.*) 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Kernel/arch/amd64/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * arch/amd64/mod.rs 6 | * - Top-level file for amd64 architecture 7 | * 8 | * == LICENCE == 9 | * This code has been put into the public domain, there are no restrictions on 10 | * its use, and the author takes no liability. 11 | */ 12 | 13 | // x86 port IO 14 | #[path = "../x86_common/io.rs"] 15 | mod x86_io; 16 | 17 | // Debug output channel (uses serial) 18 | #[path = "../x86_common/debug.rs"] 19 | pub mod debug; 20 | 21 | -------------------------------------------------------------------------------- /Kernel/arch/amd64/start.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * arcm/amd64/start.S 6 | * - AMD64 Entrypoint 7 | * 8 | * == LICENCE == 9 | * This code has been put into the public domain, there are no restrictions on 10 | * its use, and the author takes no liability. 11 | */ 12 | 13 | /* The kernel is linked to run at -2GB. This allows efficient addressing */ 14 | KERNEL_BASE = 0xFFFFFFFF80000000 15 | 16 | /* === Multiboot Header === */ 17 | MULTIBOOT_PAGE_ALIGN = (1<<0) 18 | MULTIBOOT_MEMORY_INFO = (1<<1) 19 | MULTIBOOT_REQVIDMODE = (1<<2) 20 | MULTIBOOT_HEADER_MAGIC = 0x1BADB002 21 | MULTIBOOT_HEADER_FLAGS = (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_REQVIDMODE) 22 | MULTIBOOT_CHECKSUM = -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) 23 | .section .multiboot, "a" 24 | .globl mboot 25 | mboot: 26 | .long MULTIBOOT_HEADER_MAGIC 27 | .long MULTIBOOT_HEADER_FLAGS 28 | .long MULTIBOOT_CHECKSUM 29 | .long mboot 30 | /* a.out kludge (not used, the kernel is elf) */ 31 | .long 0, 0, 0, 0 /* load_addr, load_end_addr, bss_end_addr, entry_addr */ 32 | /* Video mode */ 33 | .long 0 /* Mode type (0: LFB) */ 34 | .long 0 /* Width (no preference) */ 35 | .long 0 /* Height (no preference) */ 36 | .long 32 /* Depth (32-bit preferred) */ 37 | 38 | #define DEBUG(c) mov $0x3f8, %dx ; mov $c, %al ; outb %al, %dx 39 | 40 | /* === Code === */ 41 | .section .inittext, "ax" 42 | .globl start 43 | .code32 44 | start: 45 | /* The kernel starts in protected mode (32-bit mode, we want to switch to long mode) */ 46 | 47 | /* 1. Save multiboot state */ 48 | mov %eax, mboot_sig - KERNEL_BASE 49 | mov %ebx, mboot_ptr - KERNEL_BASE 50 | 51 | /* 2. Ensure that the CPU support long mode */ 52 | mov $0x80000000, %eax 53 | cpuid 54 | /* - Check if CPUID supports the field we want to query */ 55 | cmp $0x80000001, %eax 56 | jbe not64bitCapable 57 | /* - Test the IA-32e bit */ 58 | mov $0x80000001, %eax 59 | cpuid 60 | test $0x20000000, %edx /* bit 29 = */ 61 | jz not64bitCapable 62 | 63 | /* 3. Set up state for long mode */ 64 | /* Enable: 65 | PGE (Page Global Enable) 66 | + PAE (Physical Address Extension) 67 | + PSE (Page Size Extensions) 68 | */ 69 | mov %cr4, %eax 70 | or $(0x80|0x20|0x10), %eax 71 | mov %eax, %cr4 72 | 73 | /* Load PDP4 */ 74 | mov $(init_pml4 - KERNEL_BASE), %eax 75 | mov %eax, %cr3 76 | 77 | /* Enable IA-32e mode (Also enables SYSCALL and NX) */ 78 | mov $0xC0000080, %ecx 79 | rdmsr 80 | or $(1 << 11)|(1 << 8)|(1 << 0), %eax /* NXE, LME, SCE */ 81 | wrmsr 82 | 83 | /* Enable paging and enter long mode */ 84 | mov %cr0, %eax 85 | or $0x80010000, %eax /* PG & WP */ 86 | mov %eax, %cr0 87 | lgdt GDTPtr_low - KERNEL_BASE 88 | ljmp $0x08, $start64 89 | 90 | 91 | not64bitCapable: 92 | /* If the CPU isn't 64-bit capable, print a message to serial/b8000 then busy wait */ 93 | mov $0x3f8, %dx 94 | mov $'N', %al ; outb %al, %dx 95 | movw $0x100|'N', 0xb8000 96 | mov $'o', %al ; outb %al, %dx 97 | movw $0x100|'o', 0xb8002 98 | mov $'t', %al ; outb %al, %dx 99 | movw $0x100|'t', 0xb8004 100 | mov $'6', %al ; outb %al, %dx 101 | movw $0x100|'6', 0xb8006 102 | mov $'4', %al ; outb %al, %dx 103 | movw $0x100|'4', 0xb8008 104 | 105 | not64bitCapable.loop: 106 | hlt 107 | jmp not64bitCapable.loop 108 | .code64 109 | .globl start64 110 | start64: 111 | /* Running in 64-bit mode, jump to high memory */ 112 | lgdt GDTPtr 113 | mov $start64_high, %rax 114 | jmp *%rax 115 | 116 | .section .text 117 | .extern kmain 118 | .globl start64_high 119 | start64_high: 120 | /* and clear low-memory mapping */ 121 | mov $0, %rax 122 | mov %rax, init_pml4 - KERNEL_BASE + 0 123 | 124 | /* Set up segment registers */ 125 | mov $0x10, %ax 126 | mov %ax, %ss 127 | mov %ax, %ds 128 | mov %ax, %es 129 | mov %ax, %fs 130 | mov %ax, %gs 131 | 132 | /* Set up stack pointer */ 133 | mov $init_stack, %rsp 134 | 135 | /* call the rust code */ 136 | call kmain 137 | 138 | /* and if that returns (it shouldn't) loop forever */ 139 | start64.loop: 140 | hlt 141 | jmp start64.loop 142 | 143 | /* 144 | RDI = Destination 145 | RSI = Value 146 | RDX = Count 147 | */ 148 | .section .text.memset 149 | .globl memset 150 | memset: 151 | mov %rsi, %rax 152 | mov %rdx, %rcx 153 | rep stosb 154 | ret 155 | /* 156 | RDI = Destination 157 | RSI = Source 158 | RDX = Count 159 | */ 160 | .section .text.memcpy 161 | .globl memcpy 162 | memcpy: 163 | mov %rdx, %rcx 164 | rep movsb 165 | ret 166 | 167 | 168 | /* === Page-aligned data === */ 169 | .section .padata 170 | /* Initial paging structures, four levels */ 171 | /* The +3 for sub-pages indicates "present (1) + writable (2)" */ 172 | init_pml4: 173 | .quad low_pdpt - KERNEL_BASE + 3 /* low map for startup, will be cleared before rust code runs */ 174 | .rept 512 - 3 175 | .quad 0 176 | .endr 177 | .quad 0 /* If you so wish, this is a good place for the "Fractal" mapping */ 178 | .quad init_pdpt - KERNEL_BASE + 3 /* Final mapping */ 179 | low_pdpt: 180 | .quad init_pd - KERNEL_BASE + 3 /* early init identity map */ 181 | .rept 512 - 1 182 | .quad 0 183 | .endr 184 | init_pdpt: /* covers the top 512GB, 1GB each entry */ 185 | .rept 512 - 2 186 | .quad 0 187 | .endr 188 | .quad init_pd - KERNEL_BASE + 3 /* at -2GB, identity map the kernel image */ 189 | .quad 0 190 | init_pd: 191 | /* 0x80 = Page size extension */ 192 | .quad 0x000000 + 0x80 + 3 /* Map 2MB, enough for a 1MB kernel */ 193 | .quad 0x200000 + 0x80 + 3 /* - give it another 2MB, just in case */ 194 | .rept 512 - 2 195 | .quad 0 196 | .endr 197 | init_stack_base: 198 | .rept 0x1000 * 2 199 | .byte 0 200 | .endr 201 | init_stack: 202 | 203 | /* === General Data === */ 204 | .section .data 205 | .globl mboot_sig 206 | .globl mboot_ptr 207 | mboot_sig: .long 0 208 | mboot_ptr: .long 0 209 | 210 | /* Global Descriptor Table */ 211 | GDTPtr_low: 212 | .word GDTEnd - GDT - 1 213 | .long GDT - KERNEL_BASE 214 | GDTPtr: 215 | .word GDTEnd - GDT - 1 216 | .quad GDT 217 | .globl GDT 218 | GDT: 219 | .long 0, 0 220 | .long 0x00000000, 0x00209A00 /* 0x08: 64-bit Code */ 221 | .long 0x00000000, 0x00009200 /* 0x10: 64-bit Data */ 222 | .long 0x00000000, 0x0040FA00 /* 0x18: 32-bit User Code */ 223 | .long 0x00000000, 0x0040F200 /* 0x20: User Data */ 224 | .long 0x00000000, 0x0020FA00 /* 0x28: 64-bit User Code */ 225 | .long 0x00000000, 0x0000F200 /* 0x30: User Data (64 version) */ 226 | GDTEnd: 227 | -------------------------------------------------------------------------------- /Kernel/arch/amd64/target.json: -------------------------------------------------------------------------------- 1 | { 2 | "cpu": "x86-64", 3 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 4 | "llvm-target": "x86_64-unknown-none", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "features": "-mmx,-sse,+soft-float", 9 | "os": "tifflin", 10 | "arch": "x86_64", 11 | "linker-flavor": "ld", 12 | "pre-link-args": { "ld": ["-m64"] }, 13 | "no-compiler-rt": true, 14 | "disable-redzone": true, 15 | "eliminate-frame-pointer": false, 16 | "morestack": false 17 | } 18 | -------------------------------------------------------------------------------- /Kernel/arch/x86/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | OUTPUT_FORMAT(elf32-i386) 3 | 4 | KERNEL_BASE = 0xC0000000; 5 | 6 | SECTIONS { 7 | . = 0x100000; 8 | . += SIZEOF_HEADERS; 9 | 10 | .init : AT(ADDR(.init)) { 11 | KEEP( *(.multiboot) ) 12 | *(.inittext) 13 | } 14 | 15 | . += KERNEL_BASE; 16 | 17 | .text ALIGN(0x1000) : AT(ADDR(.text) - KERNEL_BASE) { 18 | *(.text .text.*) 19 | } 20 | 21 | /* read-only data, page aligned to allow use of the no-execute feature */ 22 | . = ALIGN(0x1000); 23 | .rodata : AT(ADDR(.rodata) - KERNEL_BASE) { 24 | *(.rodata .rodata.*) 25 | } 26 | 27 | /* Read-write data, page aligned for the .padata section */ 28 | . = ALIGN(0x1000); 29 | .data : AT(ADDR(.data) - KERNEL_BASE) { 30 | *(.padata) 31 | *(.data .data.*) 32 | } 33 | 34 | /* Zero-initialised data */ 35 | .bss : AT(ADDR(.bss) - KERNEL_BASE) { 36 | *(.bss .bss.*) 37 | } 38 | 39 | . = ALIGN(0x1000); 40 | kernel_end = .; 41 | } 42 | -------------------------------------------------------------------------------- /Kernel/arch/x86/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * arch/x86/mod.rs 6 | * - Top-level file for x86 architecture 7 | * 8 | * == LICENCE == 9 | * This code has been put into the public domain, there are no restrictions on 10 | * its use, and the author takes no liability. 11 | */ 12 | 13 | // x86 port IO 14 | #[path = "../x86_common/io.rs"] 15 | mod x86_io; 16 | 17 | // Debug output channel (uses serial) 18 | #[path = "../x86_common/debug.rs"] 19 | pub mod debug; 20 | 21 | #[no_mangle] 22 | pub fn x86_prep_page_table(buf: &mut [u32; 1024]) 23 | { 24 | for i in 0u32 .. 1024 25 | { 26 | buf[i as usize] = i * 0x1000 + 3; 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /Kernel/arch/x86/start.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * arcm/x86/start.S 6 | * - x86 Entrypoint 7 | * 8 | * == LICENCE == 9 | * This code has been put into the public domain, there are no restrictions on 10 | * its use, and the author takes no liability. 11 | */ 12 | 13 | /* The kernel is linked to run at 3GB */ 14 | LINKED_BASE = 0xC0000000 15 | 16 | /* === Multiboot Header === */ 17 | MULTIBOOT_PAGE_ALIGN = (1<<0) 18 | MULTIBOOT_MEMORY_INFO = (1<<1) 19 | MULTIBOOT_REQVIDMODE = (1<<2) 20 | MULTIBOOT_HEADER_MAGIC = 0x1BADB002 21 | MULTIBOOT_HEADER_FLAGS = (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_REQVIDMODE) 22 | MULTIBOOT_CHECKSUM = -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) 23 | .section .multiboot, "a" 24 | .globl mboot 25 | mboot: 26 | .long MULTIBOOT_HEADER_MAGIC 27 | .long MULTIBOOT_HEADER_FLAGS 28 | .long MULTIBOOT_CHECKSUM 29 | .long mboot 30 | /* a.out kludge (not used, the kernel is elf) */ 31 | .long 0, 0, 0, 0 /* load_addr, load_end_addr, bss_end_addr, entry_addr */ 32 | /* Video mode */ 33 | .long 0 /* Mode type (0: LFB) */ 34 | .long 0 /* Width (no preference) */ 35 | .long 0 /* Height (no preference) */ 36 | .long 32 /* Depth (32-bit preferred) */ 37 | 38 | .extern x86_prep_page_table 39 | /* === Code === */ 40 | .section .inittext, "ax" 41 | .globl start 42 | start: 43 | /* Save multiboot state */ 44 | mov %eax, mboot_sig - LINKED_BASE 45 | mov %ebx, mboot_ptr - LINKED_BASE 46 | 47 | /* XXX: Get rust code to prepare the page table */ 48 | mov $init_stack - LINKED_BASE, %esp 49 | push $init_pt - LINKED_BASE 50 | call x86_prep_page_table - LINKED_BASE 51 | add 4, %esp 52 | 53 | /* Enable paging */ 54 | mov $init_pd - LINKED_BASE, %eax 55 | mov %eax, %cr3 56 | mov %cr0, %eax 57 | or $0x80010000, %eax /* PG & WP */ 58 | mov %eax, %cr0 59 | 60 | lgdt GDTPtr 61 | 62 | /* Jump High and set CS */ 63 | ljmp $0x08,$start_high 64 | .section .text 65 | .globl start_high 66 | .extern kmain 67 | start_high: 68 | /* Clear identity mapping */ 69 | movl $0, init_pd+0 70 | 71 | /* Prep segment registers */ 72 | mov $0x10, %ax 73 | mov %ax, %ss 74 | mov %ax, %ds 75 | mov %ax, %es 76 | mov %ax, %fs 77 | mov %ax, %gs 78 | 79 | mov $init_stack, %esp 80 | call kmain 81 | 82 | /* If kmain returns, loop forefer */ 83 | .l: 84 | hlt 85 | jmp .l 86 | 87 | /* 88 | + 8 = Destination 89 | +12 = Value 90 | +16 = Count 91 | */ 92 | .section .text.memset 93 | .globl memset 94 | memset: 95 | push %ebp 96 | mov %esp, %ebp 97 | push %edi 98 | 99 | mov 8(%ebp), %edi 100 | mov 12(%ebp), %eax 101 | mov 16(%ebp), %ecx 102 | rep stosb 103 | 104 | pop %edi 105 | pop %ebp 106 | ret 107 | /* 108 | + 8 = Destination 109 | +12 = Source 110 | +16 = Count 111 | */ 112 | .section .text.memcpy 113 | .globl memcpy 114 | memcpy: 115 | push %ebp 116 | mov %esp, %ebp 117 | push %esi 118 | push %edi 119 | 120 | mov 8(%ebp), %edi 121 | mov 12(%ebp), %esi 122 | mov 16(%ebp), %ecx 123 | 124 | rep movsb 125 | 126 | pop %edi 127 | pop %esi 128 | pop %ebp 129 | ret 130 | 131 | /* === Page-aligned data === */ 132 | .section .padata 133 | init_pd: 134 | .long init_pt - LINKED_BASE + 3 135 | .rept 768-1 136 | .long 0 137 | .endr 138 | .long init_pt - LINKED_BASE + 3 139 | .rept 256-1 140 | .long 0 141 | .endr 142 | init_pt: 143 | /* The contents of this table is filled by the x86_prep_page_table function */ 144 | .rept 1024 145 | .long 0 146 | .endr 147 | 148 | /* === Read-write data === */ 149 | .section .data 150 | .globl mboot_sig 151 | .globl mboot_ptr 152 | mboot_sig: 153 | .long 0 154 | mboot_ptr: 155 | .long 0 156 | GDTPtr: 157 | .word GDTEnd - GDT - 1 158 | .long GDT 159 | GDT: 160 | .long 0x00000000, 0x00000000 /* 00 NULL Entry */ 161 | .long 0x0000FFFF, 0x00CF9A00 /* 08 PL0 Code */ 162 | .long 0x0000FFFF, 0x00CF9200 /* 10 PL0 Data */ 163 | .long 0x0000FFFF, 0x00CFFA00 /* 18 PL3 Code */ 164 | .long 0x0000FFFF, 0x00CFF200 /* 20 PL3 Data */ 165 | GDTEnd: 166 | 167 | 168 | .section .bss 169 | .space 0x1000*2 170 | init_stack: 171 | 172 | -------------------------------------------------------------------------------- /Kernel/arch/x86/target.json: -------------------------------------------------------------------------------- 1 | { 2 | "cpu": "pentium4", 3 | "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", 4 | "llvm-target": "i686-unknown-none", 5 | "target-endian": "little", 6 | "target-pointer-width": "32", 7 | "target-c-int-width": "32", 8 | "features": "-mmx,-sse,+soft-float", 9 | "os": "none", 10 | "arch": "x86", 11 | "linker-flavor": "ld", 12 | "pre-link-args": { "ld": ["-m32"] }, 13 | "no-compiler-rt": true, 14 | "eliminate-frame-pointer": false, 15 | "morestack": false 16 | } 17 | -------------------------------------------------------------------------------- /Kernel/arch/x86_common/debug.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * arch/x86/debug.rs 6 | * - Debug output channel 7 | * 8 | * Writes debug to the standard PC serial port (0x3F8 .. 0x3FF) 9 | * 10 | * == LICENCE == 11 | * This code has been put into the public domain, there are no restrictions on 12 | * its use, and the author takes no liability. 13 | */ 14 | 15 | /// Write a string to the output channel 16 | /// 17 | /// This method is unsafe because it does port accesses without synchronisation 18 | pub unsafe fn puts(s: &str) 19 | { 20 | for b in s.bytes() 21 | { 22 | putb(b); 23 | } 24 | } 25 | 26 | /// Write a single byte to the output channel 27 | /// 28 | /// This method is unsafe because it does port accesses without synchronisation 29 | pub unsafe fn putb(b: u8) 30 | { 31 | // Wait for the serial port's fifo to not be empty 32 | while (::arch::x86_io::inb(0x3F8+5) & 0x20) == 0 33 | { 34 | // Do nothing 35 | } 36 | // Send the byte out the serial port 37 | ::arch::x86_io::outb(0x3F8, b); 38 | 39 | // Also send to the bochs 0xe9 hack 40 | ::arch::x86_io::outb(0xe9, b); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Kernel/arch/x86_common/io.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * arch/x86/x86_io.rs 6 | * - Support for the x86 IO bus 7 | * 8 | * == LICENCE == 9 | * This code has been put into the public domain, there are no restrictions on 10 | * its use, and the author takes no liability. 11 | */ 12 | #![allow(dead_code)] // < This sample doesn't use them, but you might :) 13 | 14 | /// Write a byte to the specified port 15 | pub unsafe fn outb(port: u16, val: u8) 16 | { 17 | ::core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(preserves_flags, nomem, nostack)); 18 | } 19 | 20 | /// Read a single byte from the specified port 21 | pub unsafe fn inb(port: u16) -> u8 22 | { 23 | let ret : u8; 24 | ::core::arch::asm!("in al, dx", out("al") ret, in("dx") port, options(preserves_flags, nomem, nostack)); 25 | return ret; 26 | } 27 | 28 | /// Write a word (16-bits) to the specified port 29 | pub unsafe fn outw(port: u16, val: u16) 30 | { 31 | ::core::arch::asm!("out dx, ax", in("dx") port, in("ax") val, options(preserves_flags, nomem, nostack)); 32 | } 33 | 34 | /// Read a word (16-bits) from the specified port 35 | pub unsafe fn inw(port: u16) -> u16 36 | { 37 | let ret : u16; 38 | ::core::arch::asm!("in eax, dx", out("eax") ret, in("dx") port, options(preserves_flags, nomem, nostack)); 39 | return ret; 40 | } 41 | 42 | /// Write a long/double-word (32-bits) to the specified port 43 | pub unsafe fn outl(port: u16, val: u32) 44 | { 45 | ::core::arch::asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack)); 46 | } 47 | 48 | /// Read a long/double-word (32-bits) from the specified port 49 | pub unsafe fn inl(port: u16) -> u32 50 | { 51 | let ret : u32; 52 | ::core::arch::asm!("in eax, dx", out("eax") ret, in("dx") port, options(preserves_flags, nomem, nostack)); 53 | return ret; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /Kernel/logging.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * logging.rs 6 | * - Debug output using rust's core::fmt system 7 | * 8 | * This code has been put into the public domain, there are no restrictions on 9 | * its use, and the author takes no liability. 10 | */ 11 | use core::sync::atomic; 12 | use core::fmt; 13 | 14 | /// A formatter object 15 | pub struct Writer(bool); 16 | 17 | /// A primitive lock for the logging output 18 | /// 19 | /// This is not really a lock. Since there is no threading at the moment, all 20 | /// it does is prevent writing when a collision would occur. 21 | static LOGGING_LOCK: atomic::AtomicBool = atomic::AtomicBool::new(false); 22 | 23 | impl Writer 24 | { 25 | /// Obtain a logger for the specified module 26 | pub fn get(module: &str) -> Writer { 27 | // This "acquires" the lock (actually just disables output if paralel writes are attempted 28 | let mut ret = Writer( ! LOGGING_LOCK.swap(true, atomic::Ordering::Acquire) ); 29 | 30 | // Print the module name before returning (prefixes all messages) 31 | { 32 | use core::fmt::Write; 33 | let _ = write!(&mut ret, "[{}] ", module); 34 | } 35 | 36 | ret 37 | } 38 | } 39 | 40 | impl ::core::ops::Drop for Writer 41 | { 42 | fn drop(&mut self) 43 | { 44 | // Write a terminating newline before releasing the lock 45 | { 46 | use core::fmt::Write; 47 | let _ = write!(self, "\n"); 48 | } 49 | // On drop, "release" the lock 50 | if self.0 { 51 | LOGGING_LOCK.store(false, atomic::Ordering::Release); 52 | } 53 | } 54 | } 55 | 56 | impl fmt::Write for Writer 57 | { 58 | fn write_str(&mut self, s: &str) -> fmt::Result 59 | { 60 | // If the lock is owned by this instance, then we can safely write to the output 61 | if self.0 62 | { 63 | unsafe { 64 | ::arch::debug::puts( s ); 65 | } 66 | } 67 | Ok( () ) 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /Kernel/macros.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * macros.rs 6 | * - Macros used by the kernel 7 | * 8 | * This code has been put into the public domain, there are no restrictions on 9 | * its use, and the author takes no liability. 10 | */ 11 | 12 | /// A very primitive logging macro 13 | /// 14 | /// Obtaines a logger instance (locking the log channel) with the current module name passed 15 | /// then passes the standard format! arguments to it 16 | macro_rules! log{ 17 | ( $($arg:tt)* ) => ({ 18 | // Import the Writer trait (required by write!) 19 | use core::fmt::Write; 20 | let _ = write!(&mut ::logging::Writer::get(module_path!()), $($arg)*); 21 | }) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Kernel/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * main.rs 6 | * - Top-level file for kernel 7 | * 8 | * This code has been put into the public domain, there are no restrictions on 9 | * its use, and the author takes no liability. 10 | */ 11 | #![feature(panic_info_message)] //< Panic handling 12 | #![no_std] //< Kernels can't use std 13 | #![crate_name="kernel"] 14 | 15 | /// Macros, need to be loaded before everything else due to how rust parses 16 | #[macro_use] 17 | mod macros; 18 | 19 | // Achitecture-specific modules 20 | #[cfg(target_arch="x86_64")] #[path="arch/amd64/mod.rs"] 21 | pub mod arch; 22 | #[cfg(target_arch="x86")] #[path="arch/x86/mod.rs"] 23 | pub mod arch; 24 | 25 | /// Exception handling (panic) 26 | pub mod unwind; 27 | 28 | /// Logging code 29 | mod logging; 30 | 31 | // Kernel entrypoint (called by arch//start.S) 32 | #[no_mangle] 33 | pub fn kmain() 34 | { 35 | log!("Hello world! 1={}", 1); 36 | loop {} 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Kernel/unwind.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Rust BareBones OS 3 | * - By John Hodge (Mutabah/thePowersGang) 4 | * 5 | * unwind.rs 6 | * - Stack unwind (panic) handling 7 | * 8 | * == LICENCE == 9 | * This code has been put into the public domain, there are no restrictions on 10 | * its use, and the author takes no liability. 11 | */ 12 | 13 | #[panic_handler] 14 | pub fn panic_implementation(info: &::core::panic::PanicInfo) -> ! 15 | { 16 | let (file,line) = match info.location() 17 | { 18 | Some(loc) => (loc.file(), loc.line(),), 19 | None => ("", 0), 20 | }; 21 | if let Some(m) = info.message() { 22 | log!("PANIC file='{}', line={} :: {}", file, line, m); 23 | } 24 | else if let Some(m) = info.payload().downcast_ref::<&str>() { 25 | log!("PANIC file='{}', line={} :: {}", file, line, m); 26 | } 27 | else { 28 | log!("PANIC file='{}', line={} :: ?", file, line); 29 | } 30 | loop {} 31 | } 32 | 33 | #[allow(non_camel_case_types)] 34 | #[repr(C)] 35 | #[derive(Clone,Copy)] 36 | pub enum _Unwind_Reason_Code 37 | { 38 | _URC_NO_REASON = 0, 39 | _URC_FOREIGN_EXCEPTION_CAUGHT = 1, 40 | _URC_FATAL_PHASE2_ERROR = 2, 41 | _URC_FATAL_PHASE1_ERROR = 3, 42 | _URC_NORMAL_STOP = 4, 43 | _URC_END_OF_STACK = 5, 44 | _URC_HANDLER_FOUND = 6, 45 | _URC_INSTALL_CONTEXT = 7, 46 | _URC_CONTINUE_UNWIND = 8, 47 | } 48 | 49 | #[allow(non_camel_case_types)] 50 | #[derive(Clone,Copy)] 51 | pub struct _Unwind_Context; 52 | 53 | #[allow(non_camel_case_types)] 54 | pub type _Unwind_Action = u32; 55 | static _UA_SEARCH_PHASE: _Unwind_Action = 1; 56 | 57 | #[allow(non_camel_case_types)] 58 | #[repr(C)] 59 | #[derive(Clone,Copy)] 60 | pub struct _Unwind_Exception 61 | { 62 | exception_class: u64, 63 | exception_cleanup: fn(_Unwind_Reason_Code,*const _Unwind_Exception), 64 | private: [u64; 2], 65 | } 66 | 67 | #[no_mangle] 68 | #[allow(non_snake_case)] 69 | pub fn _Unwind_Resume() 70 | { 71 | loop{} 72 | } 73 | 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rust Bare-Bones Kernel 2 | ===== 3 | 4 | This is designed to be a rust equivalent of the OSDev.org Bare\_Bones article, presenting the bare minimum you need to get started. 5 | 6 | 7 | Requirements 8 | --- 9 | * A recent (nightly) build of rustc (at least the date of the most recent commit to this repo) 10 | * A suitable cross-compiling copy of binutils (i586-elf or x86\_64-elf) 11 | * by running `TRIPLE= make` instead of `make`, you can use the system linker, but it may not work. 12 | 13 | 14 | Features 15 | --- 16 | * x86 and x86\_64 (amd64) "ports" 17 | * Initial paging for both (with higher-half) 18 | * Serial output using the classic PC serial port, formatted using `::core::fmt` 19 | * Links with libcore 20 | 21 | Building 22 | --- 23 | 24 | Roughly, this: 25 | 26 | ```bash 27 | $ git clone https://github.com/thepowersgang/rust-barebones-kernel 28 | $ cd rust-barebones-kernel 29 | $ cd Kernel 30 | $ make 31 | $ cd .. 32 | $ qemu-system-x86_64 -kernel kernel.amd64.bin -serial stdio 33 | ``` 34 | 35 | You should see a 36 | 37 | ```text 38 | [main] Hello world! 39 | ``` 40 | 41 | print to the console. 42 | 43 | --------------------------------------------------------------------------------