├── .gitignore ├── src ├── arch │ └── x86_64 │ │ ├── grub.cfg │ │ ├── linker.ld │ │ ├── multiboot_header.asm │ │ ├── long_mode_init.asm │ │ └── boot.asm └── lib.rs ├── Cargo.toml ├── Cargo.lock ├── LICENSE ├── Makefile ├── README.md └── Vagrantfile /.gitignore: -------------------------------------------------------------------------------- 1 | multiboot_header 2 | boot 3 | *.o 4 | *.bin 5 | *.iso 6 | .vagrant 7 | target 8 | -------------------------------------------------------------------------------- /src/arch/x86_64/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "my os" { 5 | multiboot2 /boot/kernel.bin 6 | boot 7 | } 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "my_os" 3 | version = "1.0.0" 4 | authors = ["ag_dubs"] 5 | 6 | [lib] 7 | crate-type = ["staticlib"] 8 | 9 | [dependencies] 10 | rlibc = "0.1.4" 11 | -------------------------------------------------------------------------------- /src/arch/x86_64/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | 3 | SECTIONS { 4 | /* sets load address to MiB (convention) */ 5 | . = 1M; 6 | 7 | .boot : { 8 | /* put the header at the beginning */ 9 | KEEP(*(.multiboot_header)) 10 | } 11 | 12 | .text : { 13 | *(.text) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "my_os" 3 | version = "1.0.0" 4 | dependencies = [ 5 | "rlibc 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 6 | ] 7 | 8 | [[package]] 9 | name = "rlibc" 10 | version = "0.1.4" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(no_std)] 2 | #![feature(lang_items)] 3 | #![no_std] 4 | 5 | extern crate rlibc; 6 | 7 | #[no_mangle] 8 | pub extern fn rust_main() { 9 | let hello = b"Hello World!"; 10 | let color_byte = 0x1f; 11 | 12 | let mut hello_colored = [color_byte; 24]; 13 | for (i, char_byte) in hello.into_iter().enumerate() { 14 | hello_colored[i * 2] = *char_byte; 15 | } 16 | 17 | let buffer_ptr = (0xb8000 +1988) as *mut _; 18 | unsafe { *buffer_ptr = hello_colored }; 19 | 20 | loop{} 21 | } 22 | 23 | #[lang = "eh_personality"] extern fn eh_personality() {} 24 | #[lang = "panic_fmt"] extern fn panic_fmt() -> ! { loop{} } 25 | -------------------------------------------------------------------------------- /src/arch/x86_64/multiboot_header.asm: -------------------------------------------------------------------------------- 1 | section .multiboot_header 2 | header_start: 3 | dd 0xe85250d6 ; magic number (multiboot 2) 4 | dd 0 ; architecture 0 (protected mode i386) 5 | dd header_end - header_start ; header length 6 | 7 | ; checksum (magic number + architecture + header length) 8 | ; we subtract from 0x1... to account for signedness 9 | dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) 10 | 11 | ; required end tag (u16, u16, u32) 12 | dw 0 ; type 13 | dw 0 ; flags 14 | dd 8 ; size 15 | header_end: 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ashley williams 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/arch/x86_64/long_mode_init.asm: -------------------------------------------------------------------------------- 1 | global long_mode_start 2 | 3 | section .text 4 | bits 64 5 | long_mode_start: 6 | extern rust_main 7 | call setup_SSE 8 | call rust_main 9 | 10 | .os_returned: 11 | ; rust main returned, print `OS returned!` 12 | mov rax, 0x4f724f204f534f4f 13 | mov [0xb8000], rax 14 | mov rax, 0x4f724f754f744f65 15 | mov [0xb8008], rax 16 | mov rax, 0x4f214f644f654f6e 17 | mov [0xb8010], rax 18 | hlt 19 | 20 | error: 21 | mov rbx, 0x4f4f4f524f524f45 22 | mov [0xb8000], rbx 23 | mov rbx, 0x4f204f204f3a4f52 24 | mov [0xb8008], rbx 25 | mov byte [0xb800e], al 26 | hlt 27 | jmp error 28 | 29 | setup_SSE: 30 | ; check for SSE 31 | mov rax, 0x1 32 | cpuid 33 | test edx, 1<<25 34 | jz .no_SSE 35 | 36 | ; enable SSE 37 | mov rax, cr0 38 | and ax, 0xFFFB ; clear coprocessor emulation CR0.EM 39 | or ax, 0x2 ; set coprocessor monitoring CR0.MP 40 | mov cr0, rax 41 | mov rax, cr4 42 | or ax, 3 << 9 ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time 43 | mov cr4, rax 44 | 45 | ret 46 | .no_SSE: 47 | mov al, "a" 48 | jmp error 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | arch ?= x86_64 2 | kernel := build/kernel-$(arch).bin 3 | iso := build/os-$(arch).iso 4 | target ?= $(arch)-unknown-linux-gnu 5 | rust_os := target/$(target)/debug/libmy_os.a 6 | 7 | linker_script := src/arch/$(arch)/linker.ld 8 | grub_cfg := src/arch/$(arch)/grub.cfg 9 | assembly_source_files := $(wildcard src/arch/$(arch)/*.asm) 10 | assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \ 11 | build/arch/$(arch)/%.o, $(assembly_source_files)) 12 | 13 | .PHONY: all clean run iso 14 | 15 | all: $(kernel) 16 | 17 | clean: 18 | @rm -r build 19 | 20 | run: $(iso) 21 | @qemu-system-x86_64 -hda $(iso) 22 | 23 | iso: $(iso) 24 | 25 | $(iso): $(kernel) $(grub_cfg) 26 | @mkdir -p build/isofiles/boot/grub 27 | @cp $(kernel) build/isofiles/boot/kernel.bin 28 | @cp $(grub_cfg) build/isofiles/boot/grub 29 | @grub-mkrescue -o $(iso) build/isofiles 2> /dev/null 30 | @rm -r build/isofiles 31 | 32 | $(kernel): cargo $(rust_os) $(assembly_object_files) $(linker_script) 33 | @ld -n --gc-sections -T $(linker_script) -o $(kernel) $(assembly_object_files) $(rust_os) 34 | 35 | cargo: 36 | @cargo rustc --target $(target) -- -Z no-landing-pads 37 | 38 | build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm 39 | @mkdir -p $(shell dirname $@) 40 | @nasm -felf64 $< -o $@ 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # x86 kernel 2 | > a simple x86 kernel, extended with Rust 3 | 4 | this is my work following along with a [@phil-opp][2]'s blog post series ["A minimal x86 kernel"][1] 5 | 6 | ## prerequisites 7 | 8 | ### virtualization 9 | > (if you are on OSX, ChromeOS, Windows, etc) 10 | 11 | - [Vagrant]: development environment manager 12 | - [VirtualBox]: virtualizer 13 | - [XQuartz]: X11 Graphics 14 | 15 | ### linux dependencies 16 | - `nasm`: assembler (assembly -> binary) 17 | - `ld`: linker (makes binary out of other files) 18 | - `grub`: creates the bootable iso 19 | - `xorriso`: req'd by grub, filesystem manipulator 20 | - `QEMU`: fake-computer emulator 21 | 22 | ### utilities 23 | you don't need these, but they are nice for viewing 24 | generated code. 25 | 26 | - `hexdump`: allows you to view generated binary 27 | - `objdump`: a nicer viewer for .o files 28 | 29 | ## up and running 30 | 31 | 1. fork and clone this repository 32 | 2. navigate into the repo directory: `cd x86-kernel` 33 | 3. `$ vagrant up` 34 | 4. `$ vagrant ssh -- -Y` 35 | `-- -Y` forwards graphics 36 | 5. `$ multirust default nightly-2015-11-19` 37 | 38 | Sets your default rust to a stable nightly. 39 | The features needed to do OS work in Rust are 40 | not yet in a stable release, so you must use 41 | a nightly build. 42 | 43 | 6. `$ cd /vagrant` 44 | 45 | The `/vagrant` directory is the virtualized directory 46 | that is synced with the `/` directory on your HD. 47 | 48 | 7. `$ make run` 49 | 50 | [Vagrant]: https://www.vagrantup.com/ 51 | [VirtualBox]: https://www.virtualbox.org/ 52 | [XQuartz]: http://www.xquartz.org/ 53 | [1]: http://blog.phil-opp.com/rust-os/multiboot-kernel.html 54 | [2]: https://github.com/phil-opp 55 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure(2) do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "debian/jessie64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # config.vm.network "forwarded_port", guest: 80, host: 8080 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | # config.vm.synced_folder "../data", "/vagrant_data" 41 | 42 | # Provider-specific configuration so you can fine-tune various 43 | # backing providers for Vagrant. These expose provider-specific options. 44 | # Example for VirtualBox: 45 | # 46 | # config.vm.provider "virtualbox" do |vb| 47 | # # Display the VirtualBox GUI when booting the machine 48 | # vb.gui = true 49 | # 50 | # # Customize the amount of memory on the VM: 51 | # vb.memory = "1024" 52 | # end 53 | # 54 | # View the documentation for the provider you are using for more 55 | # information on available options. 56 | 57 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 58 | # such as FTP and Heroku are also available. See the documentation at 59 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 60 | # config.push.define "atlas" do |push| 61 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 62 | # end 63 | 64 | # Enable provisioning with a shell script. Additional provisioners such as 65 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 66 | # documentation for more information about their specific syntax and use. 67 | config.vm.provision "shell", inline: <<-SHELL 68 | sudo apt-get update 69 | sudo apt-get install nasm -y 70 | sudo apt-get install xorriso -y 71 | sudo apt-get install git -y 72 | sudo apt-get install vim -y 73 | sudo apt-get install -y qemu 74 | curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes 75 | multirust default nightly-2015-11-19 76 | SHELL 77 | 78 | config.ssh.forward_x11 = true 79 | end 80 | -------------------------------------------------------------------------------- /src/arch/x86_64/boot.asm: -------------------------------------------------------------------------------- 1 | global start 2 | extern long_mode_start 3 | 4 | section .text 5 | bits 32 6 | start: 7 | mov esp, stack_top 8 | 9 | ; tests 10 | call test_multiboot 11 | call test_cpuid 12 | call test_long_mode 13 | 14 | ; paging 15 | call setup_page_tables 16 | call enable_paging 17 | 18 | lgdt [gdt64.pointer] 19 | 20 | ; update selectors 21 | mov ax, gdt64.data 22 | mov ss, ax ; stack selector 23 | mov ds, ax ; data selector 24 | mov es, ax ; extra selector 25 | 26 | jmp gdt64.code:long_mode_start ; "trampoline" 27 | 28 | hlt 29 | 30 | ; prints `ERR: ` + error code 31 | ; parameter: error code (in ascii) in al 32 | error: 33 | mov dword [0xb8000], 0x4f524f45 34 | mov dword [0xb8004], 0x4f3a4f52 35 | mov dword [0xb8008], 0xff204f20 36 | mov byte [0xb800a], al 37 | hlt 38 | 39 | test_multiboot: 40 | cmp eax, 0x36d76289 41 | jne .no_multiboot 42 | ret 43 | .no_multiboot: 44 | mov al, "0" 45 | jmp error 46 | 47 | test_cpuid: 48 | pushfd ; Store the FLAGS-register. 49 | pop eax ; Restore the A-register. 50 | mov ecx, eax ; Set the C-register to the A-register. 51 | xor eax, 1 << 21 ; Flip the ID-bit, which is bit 21. 52 | push eax ; Store the A-register. 53 | popfd ; Restore the FLAGS-register. 54 | pushfd ; Store the FLAGS-register. 55 | pop eax ; Restore the A-register. 56 | push ecx ; Store the C-register. 57 | popfd ; Restore the FLAGS-register. 58 | xor eax, ecx ; Do a XOR-operation on the A-register and the C-register. 59 | jz .no_cpuid ; The zero flag is set, no CPUID. 60 | ret ; CPUID is available for use. 61 | .no_cpuid: 62 | mov al, "1" 63 | jmp error 64 | 65 | test_long_mode: 66 | mov eax, 0x80000000 ; Set the A-register to 0x80000000. 67 | cpuid ; CPU identification. 68 | cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. 69 | jb .no_long_mode ; It is less, there is no long mode. 70 | mov eax, 0x80000001 ; Set the A-register to 0x80000001. 71 | cpuid ; CPU identification. 72 | test edx, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register. 73 | jz .no_long_mode ; They aren't, there is no long mode. 74 | ret 75 | .no_long_mode: 76 | mov al, "2" 77 | jmp error 78 | 79 | setup_page_tables: 80 | ; map p4 to p3 81 | mov eax, p3_table 82 | or eax, 0b11 83 | mov [p4_table], eax 84 | 85 | ; map p3 to p2 86 | mov eax, p2_table 87 | or eax, 0b11 88 | mov [p3_table], eax 89 | 90 | mov ecx, 0 91 | 92 | .map_p2_table: 93 | mov eax, 0x200000 94 | mul ecx 95 | or eax, 0b10000011 96 | mov [p2_table + ecx * 8], eax 97 | 98 | ; for loop, increment, compare(=512) 99 | inc ecx 100 | cmp ecx, 512 101 | jne .map_p2_table 102 | 103 | ret 104 | 105 | enable_paging: 106 | ; load P4 to cr3 register (cpu uses this to access the P4 table) 107 | mov eax, p4_table 108 | mov cr3, eax 109 | 110 | ; enable PAE-flag in cr4 (Physical Address Extension) 111 | mov eax, cr4 112 | or eax, 1 << 5 113 | mov cr4, eax 114 | 115 | ; set the long mode bit in the EFER MSR (model specific register) 116 | mov ecx, 0xC0000080 117 | rdmsr 118 | or eax, 1 << 8 119 | wrmsr 120 | 121 | ; enable paging in the cr0 register 122 | mov eax, cr0 123 | or eax, 1 << 31 124 | mov cr0, eax 125 | 126 | ret 127 | 128 | section .bss 129 | align 4096 130 | p4_table: 131 | resb 4096 132 | p3_table: 133 | resb 4096 134 | p2_table: 135 | resb 4096 136 | stack_bottom: 137 | resb 64 138 | stack_top: 139 | 140 | section .rodata 141 | gdt64: 142 | dq 0 ; zero entry 143 | .code: equ $ - gdt64 144 | dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; code segment 145 | .data: equ $ - gdt64 146 | dq (1<<44) | (1<<47) | (1<<41) ; data segment 147 | .pointer: 148 | dw $ - gdt64 - 1 149 | dq gdt64 150 | --------------------------------------------------------------------------------