├── .gitattributes ├── .gitignore ├── Cargo.lock ├── .editorconfig ├── Cargo.toml ├── src ├── no_std.rs ├── main.rs └── arch │ └── x86 │ ├── link.ld │ └── start.S ├── LICENSE.md ├── x86_64-bios.json ├── Makefile └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /target/ 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "biors" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{rs,json}] 11 | indent_style = space 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "biors" 3 | version = "0.1.0" 4 | authors = ["Gabriel Majeri "] 5 | build = "build.rs" 6 | publish = false 7 | 8 | [dependencies] 9 | x86 = "0.8" 10 | 11 | [build-dependencies] 12 | cc = "1" 13 | -------------------------------------------------------------------------------- /src/no_std.rs: -------------------------------------------------------------------------------- 1 | // extern crate rlibc; 2 | // extern crate compiler_intrinsics; 3 | 4 | #[lang = "eh_personality"] 5 | extern fn eh_personality() {} 6 | 7 | #[lang = "panic_fmt"] 8 | #[allow(private_no_mangle_fns)] 9 | #[no_mangle] 10 | extern fn panic_fmt() -> ! { 11 | loop {} 12 | } 13 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items)] 2 | #![no_std] 3 | #![no_main] 4 | 5 | extern crate x86; 6 | 7 | mod no_std; 8 | 9 | #[no_mangle] 10 | pub extern fn rust_start(_bist: u32) { 11 | unsafe { 12 | x86::shared::io::outb(0x9E, b'!'); 13 | x86::shared::io::outsb(0x9E, b"Hello world!"); 14 | } 15 | 16 | loop { 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2017 Gabriel Majeri 2 | 3 | This program is **free software**: you can redistribute it and/or modify 4 | it under the terms of the [GNU General Public License][gpl] as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 | 12 | See the [GNU General Public License][gpl] for more details. 13 | 14 | [gpl]: https://www.gnu.org/licenses/ 15 | -------------------------------------------------------------------------------- /x86_64-bios.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 4 | "linker-flavor": "gcc", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "arch": "x86_64", 9 | "os": "none", 10 | "disable-redzone": true, 11 | "panic": "abort", 12 | "executables": true, 13 | "exe-suffix": ".elf", 14 | "pre-link-args": { 15 | "gcc": [ 16 | "-static", 17 | "-ffreestanding", 18 | "-nostdlib", 19 | "-Wl,--script=src/arch/x86/link.ld" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Directory to store built files. 2 | BUILD_DIR := build 3 | 4 | # The BIOS as an ELF executable. 5 | bios_elf := target/x86_64-bios/debug/biors.elf 6 | 7 | # The BIOS ROM image. 8 | bios_bin := $(BUILD_DIR)/bios.bin 9 | 10 | # QEMU's debug console log file. 11 | log_file := $(BUILD_DIR)/debug.log 12 | 13 | dis := $(BUILD_DIR)/bios.dis.asm 14 | 15 | .PHONY: build run 16 | 17 | all: build run 18 | 19 | build: $(bios_bin) 20 | 21 | run: $(bios_bin) 22 | @echo Running QEMU... 23 | @qemu-system-x86_64 \ 24 | -nodefaults -nographic \ 25 | -M q35 \ 26 | -bios '$<' \ 27 | -debugcon file:$(log_file) \ 28 | -no-reboot \ 29 | -d int,guest_errors -D build/qemu.log 30 | 31 | $(bios_bin): $(bios_elf) 32 | @objcopy --output-format binary $< $@ 33 | 34 | $(bios_elf): FORCE 35 | @xargo build --target x86_64-bios 36 | 37 | FORCE: 38 | 39 | dis: $(dis) 40 | 41 | $(dis): $(bios_elf) 42 | @objdump -d $< -M intel,amd64 > $@ 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # biors 2 | ## The Basic Input / Output [Rust][rust] System 3 | This repository contains an x86 platform firmware implementation - more commonly known as a [BIOS][BIOS]. 4 | 5 | It is written in Rust, and is designed for modern x86_64 processors. 6 | 7 | Similarly to [coreboot][cboot], it is designed to deliver a "payload" - 8 | this could be a PC-AT compatible BIOS, or a [UEFI][uefi] implementation. 9 | 10 | [rust]: https://www.rust-lang.org 11 | [bios]: https://en.wikipedia.org/wiki/BIOS 12 | [cboot]: https://www.coreboot.org/ 13 | [uefi]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface 14 | 15 | ## Build requirements 16 | - [Make](https://www.gnu.org/software/make/) 17 | - [Xargo](https://github.com/japaric/xargo/) 18 | - C compiler (such as Clang or GCC) to assemble the `start.S` file. 19 | 20 | ## Pronounciation 21 | BIOS is pronounced "**BY**-oss", this project is pronounced "**BY**-orss". 22 | 23 | ## License 24 | Licensed under the [GNU General Public License][gpl] version 3 25 | or any other newer version, at your option. 26 | 27 | [gpl]: https://www.gnu.org/licenses/gpl-3.0.html 28 | -------------------------------------------------------------------------------- /src/arch/x86/link.ld: -------------------------------------------------------------------------------- 1 | /* Top of 32-bit address space. */ 2 | HIDDEN(top = 4096M); 3 | 4 | /* Flash size. */ 5 | HIDDEN(size = 64K); 6 | ASSERT(size >= 64K, "ROM size too small for QEMU"); 7 | 8 | /* Load point of the image. */ 9 | HIDDEN(base = top - size); 10 | 11 | SECTIONS { 12 | . = base; 13 | 14 | .rodata : { 15 | *(.rodata*) 16 | } 17 | 18 | gdt_ptr_offset = gdt_ptr & 0xFFFF; 19 | null_ivt_ptr_offset = null_ivt_ptr & 0xFFFF; 20 | 21 | /* Build a paging structure. */ 22 | .rodata.pm : { 23 | /* Flags used for paging structures. */ 24 | HIDDEN(PAGE_PRESENT = 1 << 0); 25 | HIDDEN(PAGE_WRITABLE = 1 << 1); 26 | HIDDEN(PAGE_SIZE = 1 << 7); 27 | 28 | /* Top-level page mapping structure. Each entry manages 512 GiB. */ 29 | . = ALIGN(4K); 30 | pml4 = .; 31 | /* PDP for first 512 GiB. */ 32 | QUAD(pdp | PAGE_PRESENT | PAGE_WRITABLE); 33 | 34 | /* The PDP's entries each manage a 1 GiB area. */ 35 | . = ALIGN(4K); 36 | HIDDEN(pdp = .); 37 | HIDDEN(page_flags = PAGE_PRESENT | PAGE_WRITABLE | PAGE_SIZE); 38 | QUAD(0 | page_flags); 39 | QUAD(1024M | page_flags); 40 | QUAD(2048M | page_flags); 41 | QUAD(3072M | page_flags); 42 | } = 0 43 | 44 | .text : { 45 | *(.text*) 46 | } 47 | 48 | . = top - 16; 49 | 50 | reset_vector : { 51 | *(reset_vector) 52 | } 53 | 54 | ASSERT(. == top, "Flash size is not right.") 55 | 56 | /DISCARD/ : { 57 | *(.eh_frame) 58 | *(.comment*) 59 | *(.note*) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/arch/x86/start.S: -------------------------------------------------------------------------------- 1 | .code16 2 | 3 | .rodata 4 | /* Global descriptor table */ 5 | gdt: 6 | /* Use null entry to store pointer to GDT. */ 7 | .global gdt_ptr 8 | gdt_ptr: 9 | .word gdt_end - gdt - 1; 10 | .int gdt 11 | .align 8 12 | gdt_code: 13 | .quad 0x00209A0000000000 14 | 15 | gdt_data: 16 | .quad 0x0000920000000000 17 | 18 | gdt_end: 19 | 20 | .set CODE_SELECTOR, gdt_code - gdt 21 | .set DATA_SELECTOR, gdt_data - gdt 22 | 23 | .global null_ivt_ptr 24 | null_ivt_ptr: 25 | .word 0 26 | .int 0 27 | 28 | .text 29 | real_start: 30 | /* Disable maskable interrupts. */ 31 | cli 32 | 33 | /* Save the Built-In Self Test's result. */ 34 | mov %eax, %ebp 35 | 36 | /* 37 | After a RESET, the CPU's TLB might still have some pages cached. 38 | Invalidate them to prevent any issues. 39 | */ 40 | xor %eax, %eax 41 | mov %eax, %cr3 42 | 43 | /* 44 | Code segment's base on startup is 0xFFFF_0000. 45 | We can use that to access our data structures. 46 | */ 47 | mov %cs, %ax 48 | shl $4, %ax 49 | 50 | /* Load a null IVT so the CPU will triple fault on any interrupt. */ 51 | mov $null_ivt_ptr_offset, %bx 52 | sub %ax, %bx 53 | 54 | lidtl %cs:(%bx) 55 | 56 | /* Load a 64-bit GDT. */ 57 | mov $gdt_ptr_offset, %bx 58 | sub %ax, %bx 59 | 60 | lgdtl %cs:(%bx) 61 | 62 | /* Need to set some CR4 flags for long-mode. */ 63 | mov %cr4, %eax 64 | 65 | /* 66 | Long mode uses the same structures as Physical-Address Extensions, 67 | therefore it must be enabled. 68 | */ 69 | or $(1 << 5), %eax 70 | 71 | mov %eax, %cr4 72 | 73 | /* Enable some features in the EFER MSR. */ 74 | mov $0xC0000080, %ecx 75 | rdmsr 76 | 77 | /* Enable long mode. */ 78 | or $(1 << 8), %eax 79 | 80 | wrmsr 81 | 82 | /* CR3 must point to the top-level page mapping table. */ 83 | mov $pml4, %eax 84 | mov %eax, %cr3 85 | 86 | /* Set up some CR0 flags. */ 87 | mov %cr0, %eax 88 | 89 | /* Enable protected mode, cache disable and paging. */ 90 | or $((1 << 0) | (1 << 30) | (1 << 31)), %eax 91 | 92 | mov %eax, %cr0 93 | 94 | /* Force the CPU to reload CS, to enter long mode. */ 95 | ljmpl $CODE_SELECTOR, $long_mode_start 96 | 97 | .section reset_vector, "ax" 98 | .global _start 99 | .type _start, function 100 | _start: 101 | /* Manually encode jump instruction. */ 102 | .byte 0xE9 103 | .int real_start - (. + 2) 104 | 105 | .align 16 106 | .previous 107 | 108 | .code64 109 | long_mode_start: 110 | /* Load the various segments with the 64-bit data segment. */ 111 | mov $DATA_SELECTOR, %ax 112 | 113 | mov %ax, %ds 114 | mov %ax, %ss 115 | mov %ax, %es 116 | 117 | /* Restore BIST value. */ 118 | mov %ebp, %edx 119 | 120 | /* Set up a stack. */ 121 | mov $0x10000, %esp 122 | 123 | /* Create a new stack frame. */ 124 | mov $., %ebp 125 | 126 | call rust_start 127 | 128 | hang: 129 | hlt 130 | jmp hang 131 | --------------------------------------------------------------------------------