├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── crates ├── .gitignore ├── alloc_buddy_simple │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── heap.rs │ │ ├── integration.rs │ │ ├── lib.rs │ │ └── math.rs ├── cpuio │ ├── Cargo.toml │ ├── README.md │ ├── examples │ │ └── read_scancode.rs │ └── src │ │ ├── lib.rs │ │ └── x86.rs └── pic8259_simple │ ├── Cargo.toml │ ├── README.md │ └── src │ └── lib.rs ├── src ├── arch │ ├── mod.rs │ └── x86_64 │ │ ├── boot.asm │ │ ├── common.inc │ │ ├── grub.cfg │ │ ├── interrupt_handlers.asm │ │ ├── interrupts.rs │ │ ├── keyboard.rs │ │ ├── linker.ld │ │ ├── long_mode_init.asm │ │ ├── mod.rs │ │ ├── multiboot_header.asm │ │ ├── pci.rs │ │ ├── serial.rs │ │ └── vga.rs ├── console.rs ├── heap.rs ├── lib.rs ├── macros.rs └── runtime_glue.rs └── x86_64-unknown-none-gnu.json /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | target/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rust"] 2 | path = rust 3 | url = https://github.com/emk/rust 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "toyos" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "alloc_buddy_simple 0.1.1", 6 | "cpuio 0.2.0", 7 | "pic8259_simple 0.1.0", 8 | "rlibc 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 9 | "spin 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 10 | "x86 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 11 | ] 12 | 13 | [[package]] 14 | name = "alloc_buddy_simple" 15 | version = "0.1.1" 16 | dependencies = [ 17 | "spin 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 18 | ] 19 | 20 | [[package]] 21 | name = "byteorder" 22 | version = "0.5.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | 25 | [[package]] 26 | name = "cpuio" 27 | version = "0.2.0" 28 | 29 | [[package]] 30 | name = "csv" 31 | version = "0.14.7" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | dependencies = [ 34 | "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 36 | ] 37 | 38 | [[package]] 39 | name = "libc" 40 | version = "0.2.16" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | 43 | [[package]] 44 | name = "num" 45 | version = "0.1.35" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | dependencies = [ 48 | "num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "num-rational 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 54 | ] 55 | 56 | [[package]] 57 | name = "num-bigint" 58 | version = "0.1.35" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | dependencies = [ 61 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 65 | ] 66 | 67 | [[package]] 68 | name = "num-complex" 69 | version = "0.1.35" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 74 | ] 75 | 76 | [[package]] 77 | name = "num-integer" 78 | version = "0.1.32" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 82 | ] 83 | 84 | [[package]] 85 | name = "num-iter" 86 | version = "0.1.32" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | dependencies = [ 89 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 90 | "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 91 | ] 92 | 93 | [[package]] 94 | name = "num-rational" 95 | version = "0.1.35" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | dependencies = [ 98 | "num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 99 | "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 102 | ] 103 | 104 | [[package]] 105 | name = "num-traits" 106 | version = "0.1.35" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | 109 | [[package]] 110 | name = "phf" 111 | version = "0.7.16" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | dependencies = [ 114 | "phf_shared 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)", 115 | ] 116 | 117 | [[package]] 118 | name = "phf_codegen" 119 | version = "0.7.16" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | dependencies = [ 122 | "phf_generator 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "phf_shared 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)", 124 | ] 125 | 126 | [[package]] 127 | name = "phf_generator" 128 | version = "0.7.16" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | dependencies = [ 131 | "phf_shared 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", 133 | ] 134 | 135 | [[package]] 136 | name = "phf_shared" 137 | version = "0.7.16" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | 140 | [[package]] 141 | name = "pic8259_simple" 142 | version = "0.1.0" 143 | dependencies = [ 144 | "cpuio 0.2.0", 145 | ] 146 | 147 | [[package]] 148 | name = "rand" 149 | version = "0.3.14" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | dependencies = [ 152 | "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", 153 | ] 154 | 155 | [[package]] 156 | name = "raw-cpuid" 157 | version = "2.0.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | 160 | [[package]] 161 | name = "rlibc" 162 | version = "0.1.5" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | 165 | [[package]] 166 | name = "rustc-serialize" 167 | version = "0.3.19" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | 170 | [[package]] 171 | name = "serde" 172 | version = "0.6.15" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | dependencies = [ 175 | "num 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 176 | ] 177 | 178 | [[package]] 179 | name = "serde_json" 180 | version = "0.6.1" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | dependencies = [ 183 | "num 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 184 | "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 185 | ] 186 | 187 | [[package]] 188 | name = "spin" 189 | version = "0.3.5" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | 192 | [[package]] 193 | name = "spin" 194 | version = "0.4.4" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | 197 | [[package]] 198 | name = "x86" 199 | version = "0.6.1" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | dependencies = [ 202 | "csv 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "phf 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)", 204 | "phf_codegen 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)", 205 | "raw-cpuid 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "serde_json 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 207 | ] 208 | 209 | [metadata] 210 | "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" 211 | "checksum csv 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "266c1815d7ca63a5bd86284043faf91e8c95e943e55ce05dc0ae08e952de18bc" 212 | "checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d" 213 | "checksum num 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9699207fab8b02bd0e56f8f06fee3f26d640303130de548898b4c9704f6d01" 214 | "checksum num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "88b14378471f7c2adc5262f05b4701ef53e8da376453a8d8fee48e51db745e49" 215 | "checksum num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c78e054dd19c3fd03419ade63fa661e9c49bb890ce3beb4eee5b7baf93f92f" 216 | "checksum num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "fb24d9bfb3f222010df27995441ded1e954f8f69cd35021f6bef02ca9552fb92" 217 | "checksum num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "287a1c9969a847055e1122ec0ea7a5c5d6f72aad97934e131c83d5c08ab4e45c" 218 | "checksum num-rational 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "54ff603b8334a72fbb27fe66948aac0abaaa40231b3cecd189e76162f6f38aaf" 219 | "checksum num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "8359ea48994f253fa958b5b90b013728b06f54872e5a58bce39540fcdd0f2527" 220 | "checksum phf 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)" = "52c875926de24c01b5b69153eaa258b57920a39b44bbce8ef1f2052a6c5a6a1a" 221 | "checksum phf_codegen 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)" = "0a8912c2cc0ea2e8ae70388ec0af9a445e7ab37b3c9cc61f059c2b34db8ed50b" 222 | "checksum phf_generator 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)" = "04b5ea825e28cb6efd89d9133b129b2003b45a221aeda025509b125b00ecb7c4" 223 | "checksum phf_shared 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2c43b5dbe94d31f1f4ed45c50bb06d70e72fd53f15422b0a915b5c237e130dd6" 224 | "checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5" 225 | "checksum raw-cpuid 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7bc8e963398e6244e1d51c42dd68394f273d39ed8afc9238b74f56041b23dbd3" 226 | "checksum rlibc 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4f725fa0be00ed4a869c61f1adb156b629bb6c8d97f2ee4d0644bba1ce33b04b" 227 | "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" 228 | "checksum serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97b18e9e53de541f11e497357d6c5eaeb39f0cb9c8734e274abe4935f6991fa" 229 | "checksum serde_json 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5aaee47e038bf9552d30380d3973fff2593ee0a76d81ad4c581f267cdcadf36" 230 | "checksum spin 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26a697d0d28d9c7f3c36b21273b4599e63237bbcae98c4eeef4d2cca12f02792" 231 | "checksum spin 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f7035cd2694c317f8ff90efb00feb9e7268febbf6e9b28a8d16da8eb202493" 232 | "checksum x86 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20905ef7afee64d40ba3279c41d1387ad38bcb1e732b1692cfba95f16329336b" 233 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "toyos" 3 | version = "0.1.0" 4 | authors = [ 5 | # Various customizations. 6 | "Eric Kidd ", 7 | # Original blog_os code. 8 | "Philipp Oppermann " 9 | ] 10 | 11 | [lib] 12 | crate-type = ["staticlib"] 13 | 14 | [dependencies] 15 | rlibc = "0.1.4" # Low-level functions like memcpy. 16 | spin = "0.3.4" # Spinlocks. 17 | x86 = "0.6.0" # CPU data structures. 18 | 19 | [dependencies.alloc_buddy_simple] 20 | path = "crates/alloc_buddy_simple" 21 | features = ["use-as-rust-allocator"] 22 | 23 | [dependencies.cpuio] 24 | path = "crates/cpuio" 25 | 26 | [dependencies.pic8259_simple] 27 | path = "crates/pic8259_simple" 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copied from http://blog.phil-opp.com/rust-os/multiboot-kernel.html 2 | 3 | arch ?= x86_64 4 | target ?= $(arch)-unknown-none-gnu 5 | 6 | rust_os := target/$(target)/debug/libtoyos.a 7 | kernel := build/kernel-$(arch).bin 8 | iso := build/os-$(arch).iso 9 | 10 | linker_script := src/arch/$(arch)/linker.ld 11 | grub_cfg := src/arch/$(arch)/grub.cfg 12 | assembly_header_files := $(wildcard src/arch/$(arch)/*.inc) 13 | assembly_source_files := $(wildcard src/arch/$(arch)/*.asm) 14 | assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \ 15 | build/arch/$(arch)/%.o, $(assembly_source_files)) 16 | 17 | .PHONY: all fmt clean run debug iso cargo 18 | 19 | all: $(kernel) 20 | 21 | fmt: 22 | rustfmt --write-mode overwrite src/lib.rs 23 | 24 | clean: 25 | rm -rf build target 26 | 27 | run: $(iso) 28 | @echo QEMU $(iso) 29 | @qemu-system-x86_64 -hda $(iso) -serial stdio 30 | 31 | debug: $(iso) 32 | @echo QEMU -d int $(iso) 33 | @qemu-system-x86_64 -hda $(iso) -d int -no-reboot -serial stdio 34 | 35 | $(iso): $(kernel) $(grub_cfg) 36 | @echo ISO $(iso) 37 | @mkdir -p build/isofiles/boot/grub 38 | @cp $(kernel) build/isofiles/boot/kernel.bin 39 | @cp $(grub_cfg) build/isofiles/boot/grub 40 | @grub-mkrescue /usr/lib/grub/i386-pc -o $(iso) build/isofiles \ 41 | 2> /dev/null 42 | @rm -r build/isofiles 43 | 44 | $(kernel): cargo $(assembly_object_files) $(linker_script) 45 | @echo LD $(kernel) 46 | @ld -n --gc-sections -T $(linker_script) -o $(kernel) \ 47 | $(assembly_object_files) $(rust_os) 48 | 49 | cargo: 50 | @echo CARGO 51 | @cargo build --target $(target) 52 | 53 | build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm $(assembly_header_files) 54 | @echo NASM $< 55 | @mkdir -p $(shell dirname $@) 56 | @nasm -felf64 -Isrc/arch/$(arch)/ $< -o $@ 57 | 58 | 59 | #========================================================================== 60 | # Building the Rust runtime for our bare-metal target 61 | 62 | # Where to put our compiled runtime libraries for this platform. 63 | installed_target_libs := \ 64 | $(shell rustup which rustc | \ 65 | sed s,bin/rustc,lib/rustlib/$(target)/lib,) 66 | 67 | runtime_rlibs := \ 68 | $(installed_target_libs)/libcore.rlib \ 69 | $(installed_target_libs)/liballoc.rlib \ 70 | $(installed_target_libs)/librustc_unicode.rlib \ 71 | $(installed_target_libs)/libcollections.rlib 72 | 73 | RUSTC := \ 74 | rustc --verbose --target $(target) \ 75 | -Z no-landing-pads \ 76 | --out-dir $(installed_target_libs) 77 | 78 | .PHONY: runtime 79 | 80 | runtime: $(runtime_rlibs) 81 | 82 | $(installed_target_libs): 83 | @mkdir -p $(installed_target_libs) 84 | 85 | $(installed_target_libs)/%.rlib: rust/src/%/lib.rs $(installed_target_libs) 86 | @echo RUSTC $< 87 | @$(RUSTC) $< 88 | @echo Check $(installed_target_libs) 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A toy OS in Rust 2 | 3 | This is based on Philipp Oppermann's 4 | [excellent series of blog posts][blog]. It's purely a learning exercise, 5 | to see what Rust feels like on bare metal. 6 | 7 | [blog]: http://blog.phil-opp.com/ 8 | [rust-barebones-kernel]: https://github.com/thepowersgang/rust-barebones-kernel 9 | 10 | ## Building 11 | 12 | First, we need to check out the source and rebuild the Rust runtime using a 13 | bare-metal target and no hardware floating point support: 14 | 15 | ```sh 16 | # Get our source code. 17 | git clone https://github.com/emk/toyos-rs.git 18 | cd toyos-rs 19 | 20 | # Set up a Rust compiler. 21 | curl https://sh.rustup.rs -sSf | sh 22 | rustup update nightly-2016-09-16 23 | rustup override set nightly-2016-09-16 24 | 25 | # Get a copy of the Rust source code so we can rebuild core 26 | # for a bare-metal target. 27 | git submodule update --init 28 | make runtime 29 | ``` 30 | 31 | From here, we should be able to build a kernel and run it using QEMU: 32 | 33 | ```sh 34 | make run 35 | ``` 36 | 37 | You should be able to type. 38 | 39 | ## Licensing 40 | 41 | Licensed under the [Apache License, Version 2.0][LICENSE-APACHE] or the 42 | [MIT license][LICENSE-MIT], at your option. 43 | 44 | [LICENSE-APACHE]: http://www.apache.org/licenses/LICENSE-2.0 45 | [LICENSE-MIT]: http://opensource.org/licenses/MIT 46 | -------------------------------------------------------------------------------- /crates/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | -------------------------------------------------------------------------------- /crates/alloc_buddy_simple/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "alloc_buddy_simple" 3 | version = "0.1.2" 4 | authors = ["Eric Kidd ", "Jethro Beekman "] 5 | 6 | description = "Simple, drop-in replacement allocator for Rust running on bare metal (no_std)" 7 | homepage = "https://github.com/emk/toyos-rs/tree/master/crates/alloc_buddy_simple" 8 | repository = "https://github.com/emk/toyos-rs" 9 | readme = "README.md" 10 | keywords = ["no_std", "kernel", "allocator"] 11 | license = "Apache-2.0/MIT" 12 | 13 | [features] 14 | 15 | # When this feature is enabled, build this crate as the actual Rust system 16 | # allocator. This also requires pulling in a spinlock crate, to control 17 | # access to the shared heap. 18 | use-as-rust-allocator = ["spin"] 19 | 20 | [dependencies] 21 | spin = { version = ">=0.3.4,<0.5", optional = true } 22 | -------------------------------------------------------------------------------- /crates/alloc_buddy_simple/README.md: -------------------------------------------------------------------------------- 1 | # `alloc_buddy_simple`: A simple "buddy allocator" for bare-metal Rust 2 | 3 | Are you using Rust on bare metal with `#[no_std]`? Do you lack even a 4 | working `malloc` and `free`? Would you like to have a Rust-compatible 5 | allocator that works with `libcollections`? 6 | 7 | *WARNING:* OK, you shouldn't use `libcollections` for anything serious in 8 | kernel space, because it will panic if you ever run out of memory. But if 9 | you just want to use a `Vec` or two at startup time, on a well-understood 10 | system, it's very convenient, and maybe you're willing to live with the 11 | consequences. 12 | 13 | This is a simple [buddy allocator][] that you can use a drop-in replacement 14 | for Rust's regular allocators. It's highly experimental and may corrupt 15 | your data, panic your machine, etc. But it appears to be enough to make 16 | `Vec::push` work, at least in _extremely_ limited testing. 17 | 18 | There is a test suite which attempts to allocate and deallocate a bunch of 19 | memory, and which tries to make sure everything winds up at the expected 20 | location in memory each time. 21 | 22 | [buddy allocator]: https://en.wikipedia.org/wiki/Buddy_memory_allocation 23 | 24 | ## Using this allocator 25 | 26 | You can pull this into a Cargo build using: 27 | 28 | ``` 29 | [dependencies.alloc_buddy_simple] 30 | git = "https://github.com/emk/toyos-rs" 31 | features = ["use-as-rust-allocator"] 32 | ``` 33 | 34 | Then you'll need to allocate some memory for your heap somewhere. This 35 | needs to be aligned on a 4096-byte boundary, and it needs to be a power of 36 | 2 in size. You could use the following declarations with `nasm`: 37 | 38 | ```asm 39 | section .bss 40 | align 4096 41 | HEAP_BOTTOM: 42 | resb 4*1024*1024 43 | HEAP_TOP: 44 | ``` 45 | 46 | From there, all you need to do is (1) declare an array of free lists with 47 | enough space: 48 | 49 | ```rust 50 | extern crate alloc_buddy_simple; 51 | 52 | use alloc_buddy_simple::{FreeBlock, initialize_allocator}; 53 | 54 | static mut FREE_LISTS: [*mut FreeBlock; 19] = [0 as *mut _; 19]; 55 | ``` 56 | 57 | The tricky bit here is the `19`. This determines the minimum allocable 58 | block size, which will be `heap_size >> (19 - 1)`. Your minimum block size 59 | must be at least as large as a `FreeBlock`. 60 | 61 | For calling `initialize_allocator`, see [the toyos `heap.rs` file][heap.rs] 62 | for example code. Do this before trying to use your heap, or you will get 63 | a Rust panic! 64 | 65 | [heap.rs]: https://github.com/emk/toyos-rs/blob/master/src/heap.rs 66 | 67 | ## Compiling a custom `libcollections` 68 | 69 | You will need to manually compile a bunch of libraries from the `rust/src` 70 | directory and copy them into 71 | `~/.multirust/toolchains/nightly/lib/rustlib/$(target)/lib` or the 72 | equivalent directory on your system. For example code, see 73 | [the toyos `Makefile`][Makefile]. 74 | 75 | You may also want to apply the [barebones nofp patch][nofp] to `libcore` if 76 | your kernel space does not support floating point. 77 | 78 | [Makefile]: https://github.com/emk/toyos-rs/blob/master/Makefile 79 | [nofp]: https://github.com/thepowersgang/rust-barebones-kernel/blob/master/libcore_nofp.patch 80 | 81 | ## Warning 82 | 83 | This has only been run in the "low half" of memory, and if you store your 84 | heap in the upper half of your memory range, you may run into some issues 85 | with `isize` versus `usize`. 86 | 87 | ## Licensing 88 | 89 | Licensed under the [Apache License, Version 2.0][LICENSE-APACHE] or the 90 | [MIT license][LICENSE-MIT], at your option. This is HIGHLY EXPERIMENTAL 91 | CODE PROVIDED "AS IS", AND IT MAY DO HORRIBLE THINGS TO YOUR COMPUTER OR 92 | DATA. But if you're using random unsafe, unstable Rust libraries in 93 | implementing a panicking version of `malloc` in kernel space, you probably 94 | knew that already. 95 | 96 | [LICENSE-APACHE]: http://www.apache.org/licenses/LICENSE-2.0 97 | [LICENSE-MIT]: http://opensource.org/licenses/MIT 98 | -------------------------------------------------------------------------------- /crates/alloc_buddy_simple/src/heap.rs: -------------------------------------------------------------------------------- 1 | //! A simple heap based on a buddy allocator. For the theory of buddy 2 | //! allocators, see https://en.wikipedia.org/wiki/Buddy_memory_allocation 3 | //! 4 | //! The basic idea is that our heap size is a power of two, and the heap 5 | //! starts out as one giant free block. When a memory allocation request 6 | //! is received, we round the requested size up to a power of two, and find 7 | //! the smallest available block we can use. If the smallest free block is 8 | //! too big (more than twice as big as the memory we want to allocate), we 9 | //! split the smallest free block in half recursively until it's the right 10 | //! size. This simplifies a lot of bookkeeping, because all our block 11 | //! sizes are a power of 2, which makes it easy to have one free list per 12 | //! block size. 13 | 14 | use core::cmp::{max, min}; 15 | use core::mem::size_of; 16 | use core::ptr; 17 | 18 | use math::PowersOf2; 19 | 20 | const MIN_HEAP_ALIGN: usize = 4096; 21 | 22 | /// A free block in our heap. This is actually a header that we store at 23 | /// the start of the block. We don't store any size information in the 24 | /// header, because we a separate free block list for each block size. 25 | pub struct FreeBlock { 26 | /// The next block in the free list, or NULL if this is the final 27 | /// block. 28 | next: *mut FreeBlock, 29 | } 30 | 31 | impl FreeBlock { 32 | /// Construct a `FreeBlock` header pointing at `next`. 33 | fn new(next: *mut FreeBlock) -> FreeBlock { 34 | FreeBlock { next: next } 35 | } 36 | } 37 | 38 | /// The interface to a heap. This data structure is stored _outside_ the 39 | /// heap somewhere, because every single byte of our heap is potentially 40 | /// available for allocation. 41 | pub struct Heap<'a> { 42 | /// The base address of our heap. This must be aligned on a 43 | /// `MIN_HEAP_ALIGN` boundary. 44 | heap_base: *mut u8, 45 | 46 | /// The space available in our heap. This must be a power of 2. 47 | heap_size: usize, 48 | 49 | /// The free lists for our heap. The list at `free_lists[0]` contains 50 | /// the smallest block size we can allocate, and the list at the end 51 | /// can only contain a single free block the size of our entire heap, 52 | /// and only when no memory is allocated. 53 | free_lists: &'a mut [*mut FreeBlock], 54 | 55 | /// Our minimum block size. This is calculated based on `heap_size` 56 | /// and the length of the provided `free_lists` array, and it must be 57 | /// big enough to contain a `FreeBlock` header object. 58 | min_block_size: usize, 59 | 60 | /// The log base 2 of our block size. Cached here so we don't have to 61 | /// recompute it on every allocation (but we haven't benchmarked the 62 | /// performance gain). 63 | min_block_size_log2: u8, 64 | } 65 | 66 | // A Heap struct is the sole owner of the memory it manages 67 | unsafe impl<'a> Send for Heap<'a> {} 68 | 69 | impl<'a> Heap<'a> { 70 | /// Create a new heap. `heap_base` must be aligned on a 71 | /// `MIN_HEAP_ALIGN` boundary, `heap_size` must be a power of 2, and 72 | /// `heap_size / 2.pow(free_lists.len()-1)` must be greater than or 73 | /// equal to `size_of::()`. Passing in invalid parameters 74 | /// may do horrible things. 75 | pub unsafe fn new( 76 | heap_base: *mut u8, 77 | heap_size: usize, 78 | free_lists: &mut [*mut FreeBlock]) 79 | -> Heap 80 | { 81 | // The heap base must not be null. 82 | assert!(heap_base != ptr::null_mut()); 83 | 84 | // We must have at least one free list. 85 | assert!(free_lists.len() > 0); 86 | 87 | // Calculate our minimum block size based on the number of free 88 | // lists we have available. 89 | let min_block_size = heap_size >> (free_lists.len()-1); 90 | 91 | // The heap must be aligned on a 4K bounday. 92 | assert_eq!(heap_base as usize & (MIN_HEAP_ALIGN-1), 0); 93 | 94 | // The heap must be big enough to contain at least one block. 95 | assert!(heap_size >= min_block_size); 96 | 97 | // The smallest possible heap block must be big enough to contain 98 | // the block header. 99 | assert!(min_block_size >= size_of::()); 100 | 101 | // The heap size must be a power of 2. See: 102 | // http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 103 | assert!(heap_size.is_power_of_2()); 104 | 105 | // We must have one free list per possible heap block size. 106 | assert_eq!(min_block_size * 107 | (2u32.pow(free_lists.len() as u32 - 1)) as usize, 108 | heap_size); 109 | 110 | // Zero out our free list pointers. 111 | for ptr in free_lists.iter_mut() { 112 | *ptr = ptr::null_mut(); 113 | } 114 | 115 | // Store all the info about our heap in our struct. 116 | let mut result = Heap { 117 | heap_base: heap_base, 118 | heap_size: heap_size, 119 | free_lists: free_lists, 120 | min_block_size: min_block_size, 121 | min_block_size_log2: min_block_size.log2(), 122 | }; 123 | 124 | // Insert the entire heap onto the appropriate free list as a 125 | // single block. 126 | let order = result.allocation_order(heap_size, 1) 127 | .expect("Failed to calculate order for root heap block"); 128 | result.free_list_insert(order, heap_base); 129 | 130 | // Return our newly-created heap. 131 | result 132 | } 133 | 134 | /// Figure out what size block we'll need to fulfill an allocation 135 | /// request. This is deterministic, and it does not depend on what 136 | /// we've already allocated. In particular, it's important to be able 137 | /// to calculate the same `allocation_size` when freeing memory as we 138 | /// did when allocating it, or everything will break horribly. 139 | pub fn allocation_size(&self, mut size: usize, align: usize) -> Option { 140 | // Sorry, we don't support weird alignments. 141 | if !align.is_power_of_2() { return None; } 142 | 143 | // We can't align any more precisely than our heap base alignment 144 | // without getting much too clever, so don't bother. 145 | if align > MIN_HEAP_ALIGN { return None; } 146 | 147 | // We're automatically aligned to `size` because of how our heap is 148 | // sub-divided, but if we need a larger alignment, we can only do 149 | // it be allocating more memory. 150 | if align > size { size = align; } 151 | 152 | // We can't allocate blocks smaller than `min_block_size`. 153 | size = max(size, self.min_block_size); 154 | 155 | // Round up to the next power of two. 156 | size = size.next_power_of_2(); 157 | 158 | // We can't allocate a block bigger than our heap. 159 | if size > self.heap_size { return None; } 160 | 161 | Some(size) 162 | } 163 | 164 | /// The "order" of an allocation is how many times we need to double 165 | /// `min_block_size` in order to get a large enough block, as well as 166 | /// the index we use into `free_lists`. 167 | pub fn allocation_order(&self, size: usize, align: usize) -> Option { 168 | self.allocation_size(size, align).map(|s| { 169 | (s.log2() - self.min_block_size_log2) as usize 170 | }) 171 | } 172 | 173 | /// The size of the blocks we allocate for a given order. 174 | fn order_size(&self, order: usize) -> usize { 175 | 1 << (self.min_block_size_log2 as usize + order) 176 | } 177 | 178 | /// Pop a block off the appropriate free list. 179 | unsafe fn free_list_pop(&mut self, order: usize) -> Option<*mut u8> { 180 | let candidate = self.free_lists[order]; 181 | if candidate != ptr::null_mut() { 182 | self.free_lists[order] = (*candidate).next; 183 | Some(candidate as *mut u8) 184 | } else { 185 | None 186 | } 187 | } 188 | 189 | /// Insert `block` of order `order` onto the appropriate free list. 190 | unsafe fn free_list_insert(&mut self, order: usize, block: *mut u8) { 191 | let free_block_ptr = block as *mut FreeBlock; 192 | *free_block_ptr = FreeBlock::new(self.free_lists[order]); 193 | self.free_lists[order] = free_block_ptr; 194 | } 195 | 196 | /// Attempt to remove a block from our free list, returning true 197 | /// success, and false if the block wasn't on our free list. This is 198 | /// the slowest part of a primitive buddy allocator, because it runs in 199 | /// O(log N) time where N is the number of blocks of a given size. 200 | /// 201 | /// We could perhaps improve this by keeping our free lists sorted, 202 | /// because then "nursery generation" allocations would probably tend 203 | /// to occur at lower addresses and then be faster to find / rule out 204 | /// finding. 205 | unsafe fn free_list_remove( 206 | &mut self, order: usize, block: *mut u8) 207 | -> bool 208 | { 209 | let block_ptr = block as *mut FreeBlock; 210 | 211 | // Yuck, list traversals are gross without recursion. Here, 212 | // `*checking` is the pointer we want to check, and `checking` is 213 | // the memory location we found it at, which we'll need if we want 214 | // to replace the value `*checking` with a new value. 215 | let mut checking: *mut *mut FreeBlock = &mut self.free_lists[order]; 216 | 217 | // Loop until we run out of free blocks. 218 | while *checking != ptr::null_mut() { 219 | // Is this the pointer we want to remove from the free list? 220 | if *checking == block_ptr { 221 | // Yup, this is the one, so overwrite the value we used to 222 | // get here with the next one in the sequence. 223 | *checking = (*(*checking)).next; 224 | return true; 225 | } 226 | 227 | // Haven't found it yet, so point `checking` at the address 228 | // containing our `next` field. (Once again, this is so we'll 229 | // be able to reach back and overwrite it later if necessary.) 230 | checking = &mut ((*(*checking)).next); 231 | } 232 | false 233 | } 234 | 235 | /// Split a `block` of order `order` down into a block of order 236 | /// `order_needed`, placing any unused chunks on the free list. 237 | unsafe fn split_free_block( 238 | &mut self, block: *mut u8, mut order: usize, order_needed: usize) 239 | { 240 | // Get the size of our starting block. 241 | let mut size_to_split = self.order_size(order); 242 | 243 | // Progressively cut our block down to size. 244 | while order > order_needed { 245 | // Update our loop counters to describe a block half the size. 246 | size_to_split >>= 1; 247 | order -= 1; 248 | 249 | // Insert the "upper half" of the block into the free list. 250 | let split = block.offset(size_to_split as isize); 251 | self.free_list_insert(order, split); 252 | } 253 | } 254 | 255 | /// Allocate a block of memory large enough to contain `size` bytes, 256 | /// and aligned on `align`. This will return NULL if the `align` is 257 | /// greater than `MIN_HEAP_ALIGN`, if `align` is not a power of 2, or 258 | /// if we can't find enough memory. 259 | /// 260 | /// All allocated memory must be passed to `deallocate` with the same 261 | /// `size` and `align` parameter, or else horrible things will happen. 262 | pub unsafe fn allocate(&mut self, size: usize, align: usize) -> *mut u8 263 | { 264 | // Figure out which order block we need. 265 | if let Some(order_needed) = self.allocation_order(size, align) { 266 | 267 | // Start with the smallest acceptable block size, and search 268 | // upwards until we reach blocks the size of the entire heap. 269 | for order in order_needed..self.free_lists.len() { 270 | 271 | // Do we have a block of this size? 272 | if let Some(block) = self.free_list_pop(order) { 273 | 274 | // If the block is too big, break it up. This leaves 275 | // the address unchanged, because we always allocate at 276 | // the head of a block. 277 | if order > order_needed { 278 | self.split_free_block(block, order, order_needed); 279 | } 280 | 281 | // We have an allocation, so quit now. 282 | return block; 283 | } 284 | } 285 | 286 | // We couldn't find a large enough block for this allocation. 287 | ptr::null_mut() 288 | } else { 289 | // We can't allocate a block with the specified size and 290 | // alignment. 291 | ptr::null_mut() 292 | } 293 | } 294 | 295 | /// Given a `block` with the specified `order`, find the "buddy" block, 296 | /// that is, the other half of the block we originally split it from, 297 | /// and also the block we could potentially merge it with. 298 | pub unsafe fn buddy(&self, order: usize, block: *mut u8) -> Option<*mut u8> { 299 | let relative = (block as usize) - (self.heap_base as usize); 300 | let size = self.order_size(order); 301 | if size >= self.heap_size { 302 | // The main heap itself does not have a budy. 303 | None 304 | } else { 305 | // Fun: We can find our buddy by xoring the right bit in our 306 | // offset from the base of the heap. 307 | Some(self.heap_base.offset((relative ^ size) as isize)) 308 | } 309 | } 310 | 311 | /// Deallocate a block allocated using `allocate`. Note that the 312 | /// `old_size` and `align` values must match the values passed to 313 | /// `allocate`, or our heap will be corrupted. 314 | pub unsafe fn deallocate( 315 | &mut self, ptr: *mut u8, old_size: usize, align: usize) 316 | { 317 | let initial_order = self.allocation_order(old_size, align) 318 | .expect("Tried to dispose of invalid block"); 319 | 320 | // The fun part: When deallocating a block, we also want to check 321 | // to see if its "buddy" is on the free list. If the buddy block 322 | // is also free, we merge them and continue walking up. 323 | // 324 | // `block` is the biggest merged block we have so far. 325 | let mut block = ptr; 326 | for order in initial_order..self.free_lists.len() { 327 | // Would this block have a buddy? 328 | if let Some(buddy) = self.buddy(order, block) { 329 | // Is this block's buddy free? 330 | if self.free_list_remove(order, buddy) { 331 | // Merge them! The lower address of the two is the 332 | // newly-merged block. Then we want to try again. 333 | block = min(block, buddy); 334 | continue; 335 | } 336 | } 337 | 338 | // If we reach here, we didn't find a buddy block of this size, 339 | // so take what we've got and mark it as free. 340 | self.free_list_insert(order, block); 341 | return; 342 | } 343 | } 344 | } 345 | 346 | #[cfg(test)] 347 | mod test { 348 | use super::*; 349 | 350 | use core::ptr; 351 | 352 | extern "C" { 353 | /// We need this to allocate aligned memory for our heap. 354 | fn memalign(alignment: usize, size: usize) -> *mut u8; 355 | 356 | // Release our memory. 357 | fn free(ptr: *mut u8); 358 | } 359 | 360 | #[test] 361 | fn test_allocation_size_and_order() { 362 | unsafe { 363 | let heap_size = 256; 364 | let mem = memalign(4096, heap_size); 365 | let mut free_lists: [*mut FreeBlock; 5] = [0 as *mut _; 5]; 366 | let heap = Heap::new(mem, heap_size, &mut free_lists); 367 | 368 | // TEST NEEDED: Can't align beyond MIN_HEAP_ALIGN. 369 | 370 | // Can't align beyond heap_size. 371 | assert_eq!(None, heap.allocation_size(256, 256*2)); 372 | 373 | // Simple allocations just round up to next block size. 374 | assert_eq!(Some(16), heap.allocation_size(0, 1)); 375 | assert_eq!(Some(16), heap.allocation_size(1, 1)); 376 | assert_eq!(Some(16), heap.allocation_size(16, 1)); 377 | assert_eq!(Some(32), heap.allocation_size(17, 1)); 378 | assert_eq!(Some(32), heap.allocation_size(32, 32)); 379 | assert_eq!(Some(256), heap.allocation_size(256, 256)); 380 | 381 | // Aligned allocations use alignment as block size. 382 | assert_eq!(Some(64), heap.allocation_size(16, 64)); 383 | 384 | // Block orders. 385 | assert_eq!(Some(0), heap.allocation_order(0, 1)); 386 | assert_eq!(Some(0), heap.allocation_order(1, 1)); 387 | assert_eq!(Some(0), heap.allocation_order(16, 16)); 388 | assert_eq!(Some(1), heap.allocation_order(32, 32)); 389 | assert_eq!(Some(2), heap.allocation_order(64, 64)); 390 | assert_eq!(Some(3), heap.allocation_order(128, 128)); 391 | assert_eq!(Some(4), heap.allocation_order(256, 256)); 392 | assert_eq!(None, heap.allocation_order(512, 512)); 393 | 394 | free(mem); 395 | } 396 | } 397 | 398 | #[test] 399 | fn test_buddy() { 400 | unsafe { 401 | let heap_size = 256; 402 | let mem = memalign(4096, heap_size); 403 | let mut free_lists: [*mut FreeBlock; 5] = [0 as *mut _; 5]; 404 | let heap = Heap::new(mem, heap_size, &mut free_lists); 405 | 406 | let block_16_0 = mem; 407 | let block_16_1 = mem.offset(16); 408 | assert_eq!(Some(block_16_1), heap.buddy(0, block_16_0)); 409 | assert_eq!(Some(block_16_0), heap.buddy(0, block_16_1)); 410 | 411 | let block_32_0 = mem; 412 | let block_32_1 = mem.offset(32); 413 | assert_eq!(Some(block_32_1), heap.buddy(1, block_32_0)); 414 | assert_eq!(Some(block_32_0), heap.buddy(1, block_32_1)); 415 | 416 | let block_32_2 = mem.offset(64); 417 | let block_32_3 = mem.offset(96); 418 | assert_eq!(Some(block_32_3), heap.buddy(1, block_32_2)); 419 | assert_eq!(Some(block_32_2), heap.buddy(1, block_32_3)); 420 | 421 | let block_256_0 = mem; 422 | assert_eq!(None, heap.buddy(4, block_256_0)); 423 | 424 | free(mem); 425 | } 426 | } 427 | 428 | #[test] 429 | fn test_alloc_and_dealloc() { 430 | unsafe { 431 | let heap_size = 256; 432 | let mem = memalign(4096, heap_size); 433 | let mut free_lists: [*mut FreeBlock; 5] = [0 as *mut _; 5]; 434 | let mut heap = Heap::new(mem, heap_size, &mut free_lists); 435 | 436 | let block_16_0 = heap.allocate(8, 8); 437 | assert_eq!(mem, block_16_0); 438 | 439 | let bigger_than_heap = heap.allocate(4096, heap_size); 440 | assert_eq!(ptr::null_mut(), bigger_than_heap); 441 | 442 | let bigger_than_free = heap.allocate(heap_size, heap_size); 443 | assert_eq!(ptr::null_mut(), bigger_than_free); 444 | 445 | let block_16_1 = heap.allocate(8, 8); 446 | assert_eq!(mem.offset(16), block_16_1); 447 | 448 | let block_16_2 = heap.allocate(8, 8); 449 | assert_eq!(mem.offset(32), block_16_2); 450 | 451 | let block_32_2 = heap.allocate(32, 32); 452 | assert_eq!(mem.offset(64), block_32_2); 453 | 454 | let block_16_3 = heap.allocate(8, 8); 455 | assert_eq!(mem.offset(48), block_16_3); 456 | 457 | let block_128_1 = heap.allocate(128, 128); 458 | assert_eq!(mem.offset(128), block_128_1); 459 | 460 | let too_fragmented = heap.allocate(64, 64); 461 | assert_eq!(ptr::null_mut(), too_fragmented); 462 | 463 | heap.deallocate(block_32_2, 32, 32); 464 | heap.deallocate(block_16_0, 8, 8); 465 | heap.deallocate(block_16_3, 8, 8); 466 | heap.deallocate(block_16_1, 8, 8); 467 | heap.deallocate(block_16_2, 8, 8); 468 | 469 | let block_128_0 = heap.allocate(128, 128); 470 | assert_eq!(mem.offset(0), block_128_0); 471 | 472 | heap.deallocate(block_128_1, 128, 128); 473 | heap.deallocate(block_128_0, 128, 128); 474 | 475 | // And allocate the whole heap, just to make sure everything 476 | // got cleaned up correctly. 477 | let block_256_0 = heap.allocate(256, 256); 478 | assert_eq!(mem.offset(0), block_256_0); 479 | 480 | free(mem); 481 | } 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /crates/alloc_buddy_simple/src/integration.rs: -------------------------------------------------------------------------------- 1 | //! This module integrates a `Heap` into our Rust runtime as the actual 2 | //! system allocator. This will only be built if the 3 | //! `use-as-rust-allocator` feature is enabled at compile time. 4 | 5 | use core::cmp::min; 6 | use core::ptr; 7 | use spin::Mutex; 8 | 9 | use heap::*; 10 | 11 | /// Either our global system heap, or `None` if it hasn't been allocated 12 | /// yet. 13 | static HEAP: Mutex>> = Mutex::new(None); 14 | 15 | pub unsafe fn initialize_allocator( 16 | heap_base: *mut u8, 17 | heap_size: usize, 18 | free_lists: &'static mut [*mut FreeBlock]) 19 | { 20 | let mut heap = HEAP.lock(); 21 | *heap = Some(Heap::new(heap_base, heap_size, free_lists)); 22 | } 23 | 24 | #[no_mangle] 25 | pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 { 26 | unsafe { 27 | HEAP.lock().as_mut() 28 | .expect("Must call initialize_allocator before allocating on heap") 29 | .allocate(size, align) 30 | } 31 | } 32 | 33 | #[no_mangle] 34 | pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { 35 | unsafe { 36 | HEAP.lock().as_mut() 37 | .expect("Trying to deallocate before heap is initialized") 38 | .deallocate(ptr, old_size, align) 39 | } 40 | } 41 | 42 | /// Attempt to resize an existing block of memory, preserving as much data 43 | /// as possible. For now, we always just allocate new memory, copy data, 44 | /// and deallocate the old memory. 45 | #[no_mangle] 46 | pub extern "C" fn __rust_reallocate( 47 | ptr: *mut u8, old_size: usize, size: usize, align: usize) 48 | -> *mut u8 49 | { 50 | let new_ptr = __rust_allocate(size, align); 51 | if new_ptr.is_null() { 52 | return new_ptr; 53 | } else { 54 | unsafe { ptr::copy(ptr, new_ptr, min(size, old_size)); } 55 | __rust_deallocate(ptr, old_size, align); 56 | new_ptr 57 | } 58 | } 59 | 60 | /// We do not support in-place reallocation, so just return `old_size`. 61 | #[no_mangle] 62 | pub extern "C" fn __rust_reallocate_inplace( 63 | _ptr: *mut u8, old_size: usize, _size: usize, _align: usize) 64 | -> usize 65 | { 66 | old_size 67 | } 68 | 69 | /// I have no idea what this actually does, but we're supposed to have one, 70 | /// and the other backends to implement it as something equivalent to the 71 | /// following. 72 | #[no_mangle] 73 | pub extern "C" fn __rust_usable_size(size: usize, _align: usize) -> usize { 74 | size 75 | } 76 | -------------------------------------------------------------------------------- /crates/alloc_buddy_simple/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A simple heap based on a buddy allocator. For the theory of buddy 2 | //! allocators, see https://en.wikipedia.org/wiki/Buddy_memory_allocation 3 | //! 4 | //! This can either be used as a standalone library, or as a replacement 5 | //! for Rust's system allocator. It runs on top of `libcore`, so it can be 6 | //! used on bare metal or in kernel space. 7 | //! 8 | //! Note that our `Heap` API is unstable. 9 | 10 | #![no_std] 11 | 12 | #![cfg_attr(feature = "use-as-rust-allocator", feature(allocator, const_fn))] 13 | #![cfg_attr(feature = "use-as-rust-allocator", allocator)] 14 | 15 | #[cfg(feature = "use-as-rust-allocator")] 16 | extern crate spin; 17 | 18 | #[cfg(feature = "use-as-rust-allocator")] 19 | pub use integration::*; 20 | pub use heap::{Heap, FreeBlock}; 21 | 22 | mod math; 23 | mod heap; 24 | 25 | #[cfg(feature = "use-as-rust-allocator")] 26 | mod integration; 27 | -------------------------------------------------------------------------------- /crates/alloc_buddy_simple/src/math.rs: -------------------------------------------------------------------------------- 1 | use core::mem::size_of; 2 | use core::num::Wrapping; 3 | 4 | /// Basic power-of-2 integer math. 5 | pub trait PowersOf2 { 6 | fn is_power_of_2(self) -> bool; 7 | fn next_power_of_2(self) -> usize; 8 | fn log2(self) -> u8; 9 | } 10 | 11 | impl PowersOf2 for usize { 12 | /// This code is based on 13 | /// http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 14 | fn is_power_of_2(self) -> bool { 15 | self !=0 && (self & (self - 1)) == 0 16 | } 17 | 18 | /// Caluculate the next power of two. 19 | /// 20 | /// Based on 21 | /// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 22 | fn next_power_of_2(self) -> usize { 23 | // Pick off this immediately in hopes that the optimizer can see it 24 | // easily. 25 | if self == 0 { return 1; } 26 | 27 | let mut v = Wrapping(self); 28 | 29 | v = v - Wrapping(1); 30 | v = v | (v >> 1); 31 | v = v | (v >> 2); 32 | v = v | (v >> 4); 33 | v = v | (v >> 8); 34 | v = v | (v >> 16); 35 | if size_of::() > 4 { 36 | v = v | (v >> 32); 37 | } 38 | v = v + Wrapping(1); 39 | 40 | let result = match v { Wrapping(v) => v }; 41 | assert!(result.is_power_of_2()); 42 | assert!(result >= self && self > result >> 1); 43 | result 44 | } 45 | 46 | /// Calculate the base-2 logarithm of this value. 47 | /// 48 | /// This will normally round down, except for the case of `0.log2()`, 49 | /// which will return 0. 50 | /// 51 | /// Based on the obvious code at 52 | /// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious 53 | fn log2(self) -> u8 { 54 | let mut temp = self; 55 | let mut result = 0; 56 | temp >>= 1; 57 | while temp != 0 { 58 | result += 1; 59 | temp >>= 1; 60 | } 61 | result 62 | } 63 | } 64 | 65 | #[test] 66 | fn test_is_power_of_2() { 67 | assert_eq!(false, 0.is_power_of_2()); 68 | assert_eq!(true, 1.is_power_of_2()); 69 | assert_eq!(true, 2.is_power_of_2()); 70 | assert_eq!(false, 3.is_power_of_2()); 71 | assert_eq!(true, 4.is_power_of_2()); 72 | assert_eq!(false, 255.is_power_of_2()); 73 | assert_eq!(true, 256.is_power_of_2()); 74 | assert_eq!(false, 257.is_power_of_2()); 75 | assert_eq!(false, 4294967295.is_power_of_2()); 76 | if size_of::() > 4 { 77 | assert_eq!(false, 18446744073709551615.is_power_of_2()); 78 | } 79 | } 80 | 81 | #[test] 82 | fn test_next_power_of_2() { 83 | assert_eq!(1, 0.next_power_of_2()); 84 | assert_eq!(1, 1.next_power_of_2()); 85 | assert_eq!(2, 2.next_power_of_2()); 86 | assert_eq!(4, 3.next_power_of_2()); 87 | assert_eq!(4, 4.next_power_of_2()); 88 | assert_eq!(8, 5.next_power_of_2()); 89 | assert_eq!(8, 8.next_power_of_2()); 90 | assert_eq!(16, 9.next_power_of_2()); 91 | assert_eq!(16, 16.next_power_of_2()); 92 | assert_eq!(32, 17.next_power_of_2()); 93 | assert_eq!(32, 32.next_power_of_2()); 94 | assert_eq!(8388608, 8376263.next_power_of_2()); 95 | } 96 | 97 | #[test] 98 | fn test_log2() { 99 | assert_eq!(0, 0.log2()); 100 | assert_eq!(0, 1.log2()); 101 | assert_eq!(1, 2.log2()); 102 | assert_eq!(5, 32.log2()); 103 | assert_eq!(10, 1024.log2()); 104 | } 105 | -------------------------------------------------------------------------------- /crates/cpuio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cpuio" 3 | version = "0.3.0" 4 | authors = ["Eric Kidd "] 5 | 6 | description = "Bare metal (no_std) inb, outb, inw, outw, inl, outw instructions with Rust-like API" 7 | homepage = "https://github.com/emk/toyos-rs/tree/master/crates/cpuio" 8 | repository = "https://github.com/emk/toyos-rs" 9 | readme = "README.md" 10 | keywords = ["no_std", "kernel", "io"] 11 | license = "Apache-2.0/MIT" 12 | -------------------------------------------------------------------------------- /crates/cpuio/README.md: -------------------------------------------------------------------------------- 1 | # `cpuio`: Rust wrapper for `inb`, `outb`, etc., instructions 2 | 3 | **WARNING: The interface to the low-level `outb`, `outw` and `outl` 4 | functions has changed to match Linux. Please reverse the order of 5 | arguments to these three functions.** 6 | 7 | This library is intended to be run on bare metal, and it only depends on 8 | the `core` library. 9 | 10 | To use this, add it to your `Cargo.toml` file and call `cpuio::Port::new` 11 | to create a port, being sure to specify `u8`, `u16` or `u32` depending on 12 | the size data supported by the port. 13 | 14 | ```rust 15 | extern crate cpuio; 16 | 17 | use cpuio::Port; 18 | 19 | fn main() { 20 | // Create a port pointing at 0x60, the address of the PS/2 keyboard 21 | // port on x86 hardware. This is an unsafe operation because many 22 | // ports can be used to reconfigure your underlying hardware, and 23 | // it's the responsiblity of the port creator to make sure it's 24 | // used safely. 25 | let mut keyboard: Port = unsafe { Port::new(0x60) }; 26 | 27 | // Read a single scancode from a PS/2 keyboard. If you run this as 28 | // an ordinary user, it will fail with a SIGSEGV. 29 | println!("scancode: {}", keyboard.read()); 30 | } 31 | ``` 32 | 33 | The constructor `Port::new` is available as a `const fn`, which allows you 34 | to configure a port at compile time. 35 | 36 | The is also an `UnsafePort` type which is identical, except that `read` and 37 | `write` are explicitly marked as unsafe. It's better to use `UnsafePort` 38 | whenever any individual port operation might corrupt memory or cause 39 | undefined behavior. 40 | 41 | ## Licensing 42 | 43 | Licensed under the [Apache License, Version 2.0][LICENSE-APACHE] or the 44 | [MIT license][LICENSE-MIT], at your option. 45 | 46 | [LICENSE-APACHE]: http://www.apache.org/licenses/LICENSE-2.0 47 | [LICENSE-MIT]: http://opensource.org/licenses/MIT 48 | -------------------------------------------------------------------------------- /crates/cpuio/examples/read_scancode.rs: -------------------------------------------------------------------------------- 1 | //! An example program showing how to read a single scancode from a PS/2 2 | //! keyboard. 3 | 4 | extern crate cpuio; 5 | 6 | use cpuio::Port; 7 | 8 | fn main() { 9 | let mut keyboard: Port = unsafe { Port::new(0x60) }; 10 | // If you run this as an ordinary user in user space it will fail with 11 | // a SIGSEGV. 12 | println!("scancode: {}", keyboard.read()); 13 | } 14 | -------------------------------------------------------------------------------- /crates/cpuio/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! CPU-level input/output instructions, including `inb`, `outb`, etc., and 2 | //! a high level Rust wrapper. 3 | 4 | #![feature(llvm_asm, const_fn)] 5 | #![no_std] 6 | 7 | use core::marker::PhantomData; 8 | 9 | #[cfg(any(target_arch="x86", target_arch="x86_64"))] 10 | pub use x86::{inb, outb, inw, outw, inl, outl}; 11 | 12 | #[cfg(any(target_arch="x86", target_arch="x86_64"))] 13 | mod x86; 14 | 15 | 16 | /// This trait is defined for any type which can be read or written over a 17 | /// port. The processor supports I/O with `u8`, `u16` and `u32`. The 18 | /// functions in this trait are all unsafe because they can write to 19 | /// arbitrary ports. 20 | pub trait InOut { 21 | /// Read a value from the specified port. 22 | unsafe fn port_in(port: u16) -> Self; 23 | 24 | /// Write a value to the specified port. 25 | unsafe fn port_out(port: u16, value: Self); 26 | } 27 | 28 | impl InOut for u8 { 29 | unsafe fn port_in(port: u16) -> u8 { inb(port) } 30 | unsafe fn port_out(port: u16, value: u8) { outb(value, port); } 31 | } 32 | 33 | impl InOut for u16 { 34 | unsafe fn port_in(port: u16) -> u16 { inw(port) } 35 | unsafe fn port_out(port: u16, value: u16) { outw(value, port); } 36 | } 37 | 38 | impl InOut for u32 { 39 | unsafe fn port_in(port: u16) -> u32 { inl(port) } 40 | unsafe fn port_out(port: u16, value: u32) { outl(value, port); } 41 | } 42 | 43 | /// An I/O port over an arbitrary type supporting the `InOut` interface. 44 | /// 45 | /// This version of `Port` has safe `read` and `write` functions, and it's 46 | /// appropriate for communicating with hardware that can't violate Rust's 47 | /// safety guarantees. 48 | #[derive(Debug)] 49 | pub struct Port { 50 | // Port address. 51 | port: u16, 52 | 53 | // Zero-byte placeholder. This is only here so that we can have a 54 | // type parameter `T` without a compiler error. 55 | phantom: PhantomData, 56 | } 57 | 58 | impl Port { 59 | /// Create a new I/O port. 60 | pub const unsafe fn new(port: u16) -> Port { 61 | Port { port: port, phantom: PhantomData } 62 | } 63 | 64 | /// Read data from the port. This is nominally safe, because you 65 | /// shouldn't be able to get hold of a port object unless somebody 66 | /// thinks it's safe to give you one. 67 | pub fn read(&mut self) -> T { 68 | unsafe { T::port_in(self.port) } 69 | } 70 | 71 | /// Write data to the port. 72 | pub fn write(&mut self, value: T) { 73 | unsafe { T::port_out(self.port, value); } 74 | } 75 | } 76 | 77 | /// An unsafe I/O port over an arbitrary type supporting the `InOut` 78 | /// interface. 79 | /// 80 | /// This version of `Port` has unsafe `read` and `write` functions, and 81 | /// it's appropriate for speaking to hardware that can potentially corrupt 82 | /// memory or cause undefined behavior. 83 | #[derive(Debug)] 84 | pub struct UnsafePort { 85 | port: u16, 86 | phantom: PhantomData, 87 | } 88 | 89 | impl UnsafePort { 90 | /// Create a new I/O port. 91 | pub const unsafe fn new(port: u16) -> UnsafePort { 92 | UnsafePort { port: port, phantom: PhantomData } 93 | } 94 | 95 | /// Read data from the port. 96 | pub unsafe fn read(&mut self) -> T { 97 | T::port_in(self.port) 98 | } 99 | 100 | /// Write data to the port. 101 | pub unsafe fn write(&mut self, value: T) { 102 | T::port_out(self.port, value); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /crates/cpuio/src/x86.rs: -------------------------------------------------------------------------------- 1 | //! Rust wrappers around the x86-family I/O instructions. 2 | 3 | /// Read a `u8`-sized value from `port`. 4 | pub unsafe fn inb(port: u16) -> u8 { 5 | // The registers for the `in` and `out` instructions are always the 6 | // same: `a` for value, and `d` for the port address. 7 | let result: u8; 8 | llvm_asm!("inb %dx, %al" : "={al}"(result) : "{dx}"(port) :: "volatile"); 9 | result 10 | } 11 | 12 | /// Write a `u8`-sized `value` to `port`. 13 | pub unsafe fn outb(value: u8, port: u16) { 14 | llvm_asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(value) :: "volatile"); 15 | } 16 | 17 | /// Read a `u16`-sized value from `port`. 18 | pub unsafe fn inw(port: u16) -> u16 { 19 | let result: u16; 20 | llvm_asm!("inw %dx, %ax" : "={ax}"(result) : "{dx}"(port) :: "volatile"); 21 | result 22 | } 23 | 24 | /// Write a `u8`-sized `value` to `port`. 25 | pub unsafe fn outw(value: u16, port: u16) { 26 | llvm_asm!("outw %ax, %dx" :: "{dx}"(port), "{ax}"(value) :: "volatile"); 27 | } 28 | 29 | /// Read a `u32`-sized value from `port`. 30 | pub unsafe fn inl(port: u16) -> u32 { 31 | let result: u32; 32 | llvm_asm!("inl %dx, %eax" : "={eax}"(result) : "{dx}"(port) :: "volatile"); 33 | result 34 | } 35 | 36 | /// Write a `u32`-sized `value` to `port`. 37 | pub unsafe fn outl(value: u32, port: u16) { 38 | llvm_asm!("outl %eax, %dx" :: "{dx}"(port), "{eax}"(value) :: "volatile"); 39 | } 40 | -------------------------------------------------------------------------------- /crates/pic8259_simple/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pic8259_simple" 3 | version = "0.2.0" 4 | authors = ["Eric Kidd "] 5 | 6 | description = "Kernel-space interface to the 8259 and 8259A interrupt controllers" 7 | homepage = "https://github.com/emk/toyos-rs/tree/master/crates/pic8259_simple" 8 | repository = "https://github.com/emk/toyos-rs" 9 | readme = "README.md" 10 | keywords = ["no_std", "kernel", "interrupts"] 11 | license = "Apache-2.0/MIT" 12 | 13 | [dependencies.cpuio] 14 | version = "0.3.0" 15 | path = "../cpuio" 16 | -------------------------------------------------------------------------------- /crates/pic8259_simple/README.md: -------------------------------------------------------------------------------- 1 | # Kernel-space interface to 8259 and 8259A Programmable Interrupt Controller (PIC) 2 | 3 | **Work in progress:** I am _not_ qualified to have written this crate. 4 | This has been verified to work in simple cases in QEMU. It may break on 5 | real hardware (especially buggy hardware) or in more complicated scenarios. 6 | Your bug reports and PRs are extremely welcome. **Things we may not handle 7 | very well yet include:** 8 | 9 | 1. Masking interrupts. 10 | 2. Dealing with spurious interrupts. 11 | 3. Non-standard configurations. 12 | 13 | This code is based on the [OSDev Wiki PIC notes][PIC], but it's not a 14 | complete implementation of everything they discuss. Also note that if you 15 | want to do more sophisticated interrupt handling, especially on 16 | multiprocessor systems, you'll probably want to read about the newer 17 | [APIC][] and [IOAPIC][] interfaces. 18 | 19 | [PIC]: http://wiki.osdev.org/8259_PIC 20 | [APIC]: http://wiki.osdev.org/APIC 21 | [IOAPIC]: http://wiki.osdev.org/IOAPIC 22 | 23 | ## Using 24 | 25 | This is a very basic interface to the 8259 and 8259A interrupt controllers, 26 | which are used on single processor systems to pass hardware interrupts to 27 | the CPU. 28 | 29 | To use this crate, add it to your `Cargo.toml` file, along with an 30 | appropriate kernel-space mutex implementation such as `spin`: 31 | 32 | ```toml 33 | [dependencies] 34 | pic8259_simple = "*" 35 | spin = "*" 36 | ``` 37 | 38 | You can then declare a global, lockable `ChainedPics` object as follows: 39 | 40 | ```rust 41 | extern crate pic8259_simple; 42 | extern crate spin; 43 | 44 | use pic8259_simple::ChainedPics; 45 | use spin::Mutex; 46 | 47 | // Map PIC interrupts to 0x20 through 0x2f. 48 | static PICS: Mutex = 49 | Mutex::new(unsafe { ChainedPics::new(0x20, 0x28) }); 50 | ``` 51 | 52 | To perform runtime PIC intialization, call `initialize` before enabling 53 | interrupts: 54 | 55 | ```rust 56 | PICS.lock().initialize(); 57 | ``` 58 | 59 | When you've finished handling an interrupt, run: 60 | 61 | ```rust 62 | PICS.lock().notify_end_of_interrupt(interrupt_id); 63 | ``` 64 | 65 | It's safe to call `notify_end_of_interrupt` after every interrupt; the 66 | `notify_end_of_interrupt` function will try to figure out what it needs to 67 | do. 68 | 69 | All public PIC interfaces are `unsafe`, because it's really easy to trigger 70 | undefined behavior by misconfiguring the PIC or using it incorrectly. 71 | 72 | ## Licensing 73 | 74 | Licensed under the [Apache License, Version 2.0][LICENSE-APACHE] or the 75 | [MIT license][LICENSE-MIT], at your option. 76 | 77 | [LICENSE-APACHE]: http://www.apache.org/licenses/LICENSE-2.0 78 | [LICENSE-MIT]: http://opensource.org/licenses/MIT 79 | -------------------------------------------------------------------------------- /crates/pic8259_simple/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Support for the 8259 Programmable Interrupt Controller, which handles 2 | //! basic I/O interrupts. In multicore mode, we would apparently need to 3 | //! replace this with an APIC interface. 4 | //! 5 | //! The basic idea here is that we have two PIC chips, PIC1 and PIC2, and 6 | //! that PIC2 is slaved to interrupt 2 on PIC 1. You can find the whole 7 | //! story at http://wiki.osdev.org/PIC (as usual). Basically, our 8 | //! immensely sophisticated modern chipset is engaging in early-80s 9 | //! cosplay, and our goal is to do the bare minimum required to get 10 | //! reasonable interrupts. 11 | //! 12 | //! The most important thing we need to do here is set the base "offset" 13 | //! for each of our two PICs, because by default, PIC1 has an offset of 14 | //! 0x8, which means that the I/O interrupts from PIC1 will overlap 15 | //! processor interrupts for things like "General Protection Fault". Since 16 | //! interrupts 0x00 through 0x1F are reserved by the processor, we move the 17 | //! PIC1 interrupts to 0x20-0x27 and the PIC2 interrupts to 0x28-0x2F. If 18 | //! we wanted to write a DOS emulator, we'd presumably need to choose 19 | //! different base interrupts, because DOS used interrupt 0x21 for system 20 | //! calls. 21 | 22 | #![feature(const_fn)] 23 | #![no_std] 24 | 25 | extern crate cpuio; 26 | 27 | /// Command sent to begin PIC initialization. 28 | const CMD_INIT: u8 = 0x11; 29 | 30 | /// Command sent to acknowledge an interrupt. 31 | const CMD_END_OF_INTERRUPT: u8 = 0x20; 32 | 33 | // The mode in which we want to run our PICs. 34 | const MODE_8086: u8 = 0x01; 35 | 36 | /// An individual PIC chip. This is not exported, because we always access 37 | /// it through `Pics` below. 38 | struct Pic { 39 | /// The base offset to which our interrupts are mapped. 40 | offset: u8, 41 | 42 | /// The processor I/O port on which we send commands. 43 | command: cpuio::UnsafePort, 44 | 45 | /// The processor I/O port on which we send and receive data. 46 | data: cpuio::UnsafePort, 47 | } 48 | 49 | impl Pic { 50 | /// Are we in change of handling the specified interrupt? 51 | /// (Each PIC handles 8 interrupts.) 52 | fn handles_interrupt(&self, interupt_id: u8) -> bool { 53 | self.offset <= interupt_id && interupt_id < self.offset + 8 54 | } 55 | 56 | /// Notify us that an interrupt has been handled and that we're ready 57 | /// for more. 58 | unsafe fn end_of_interrupt(&mut self) { 59 | self.command.write(CMD_END_OF_INTERRUPT); 60 | } 61 | } 62 | 63 | /// A pair of chained PIC controllers. This is the standard setup on x86. 64 | pub struct ChainedPics { 65 | pics: [Pic; 2], 66 | } 67 | 68 | impl ChainedPics { 69 | /// Create a new interface for the standard PIC1 and PIC2 controllers, 70 | /// specifying the desired interrupt offsets. 71 | pub const unsafe fn new(offset1: u8, offset2: u8) -> ChainedPics { 72 | ChainedPics { 73 | pics: [ 74 | Pic { 75 | offset: offset1, 76 | command: cpuio::UnsafePort::new(0x20), 77 | data: cpuio::UnsafePort::new(0x21), 78 | }, 79 | Pic { 80 | offset: offset2, 81 | command: cpuio::UnsafePort::new(0xA0), 82 | data: cpuio::UnsafePort::new(0xA1), 83 | }, 84 | ] 85 | } 86 | } 87 | 88 | /// Initialize both our PICs. We initialize them together, at the same 89 | /// time, because it's traditional to do so, and because I/O operations 90 | /// might not be instantaneous on older processors. 91 | pub unsafe fn initialize(&mut self) { 92 | // We need to add a delay between writes to our PICs, especially on 93 | // older motherboards. But we don't necessarily have any kind of 94 | // timers yet, because most of them require interrupts. Various 95 | // older versions of Linux and other PC operating systems have 96 | // worked around this by writing garbage data to port 0x80, which 97 | // allegedly takes long enough to make everything work on most 98 | // hardware. Here, `wait` is a closure. 99 | let mut wait_port: cpuio::Port = cpuio::Port::new(0x80); 100 | let mut wait = || { wait_port.write(0) }; 101 | 102 | // Save our original interrupt masks, because I'm too lazy to 103 | // figure out reasonable values. We'll restore these when we're 104 | // done. 105 | let saved_mask1 = self.pics[0].data.read(); 106 | let saved_mask2 = self.pics[1].data.read(); 107 | 108 | // Tell each PIC that we're going to send it a three-byte 109 | // initialization sequence on its data port. 110 | self.pics[0].command.write(CMD_INIT); 111 | wait(); 112 | self.pics[1].command.write(CMD_INIT); 113 | wait(); 114 | 115 | // Byte 1: Set up our base offsets. 116 | self.pics[0].data.write(self.pics[0].offset); 117 | wait(); 118 | self.pics[1].data.write(self.pics[1].offset); 119 | wait(); 120 | 121 | // Byte 2: Configure chaining between PIC1 and PIC2. 122 | self.pics[0].data.write(4); 123 | wait(); 124 | self.pics[1].data.write(2); 125 | wait(); 126 | 127 | // Byte 3: Set our mode. 128 | self.pics[0].data.write(MODE_8086); 129 | wait(); 130 | self.pics[1].data.write(MODE_8086); 131 | wait(); 132 | 133 | // Restore our saved masks. 134 | self.pics[0].data.write(saved_mask1); 135 | self.pics[1].data.write(saved_mask2); 136 | } 137 | 138 | /// Do we handle this interrupt? 139 | pub fn handles_interrupt(&self, interrupt_id: u8) -> bool { 140 | self.pics.iter().any(|p| p.handles_interrupt(interrupt_id)) 141 | } 142 | 143 | /// Figure out which (if any) PICs in our chain need to know about this 144 | /// interrupt. This is tricky, because all interrupts from `pics[1]` 145 | /// get chained through `pics[0]`. 146 | pub unsafe fn notify_end_of_interrupt(&mut self, interrupt_id: u8) { 147 | if self.handles_interrupt(interrupt_id) { 148 | if self.pics[1].handles_interrupt(interrupt_id) { 149 | self.pics[1].end_of_interrupt(); 150 | } 151 | self.pics[0].end_of_interrupt(); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | // Export our platform-specific modules. 2 | #[cfg(target_arch="x86_64")] 3 | pub use self::x86_64::{vga, interrupts, serial, pci}; 4 | 5 | // Implementations for x86_64. 6 | #[cfg(target_arch="x86_64")] 7 | pub mod x86_64; 8 | -------------------------------------------------------------------------------- /src/arch/x86_64/boot.asm: -------------------------------------------------------------------------------- 1 | ;;; Based on http://blog.phil-opp.com/rust-os/multiboot-kernel.html 2 | ;;; and http://blog.phil-opp.com/rust-os/entering-longmode.html 3 | ;;; 4 | ;;; The actual boot code of our kernel. 5 | 6 | global start 7 | global gdt64_code_offset 8 | global HEAP_BOTTOM 9 | global HEAP_TOP 10 | 11 | extern long_mode_start 12 | 13 | ;;; Our main entry point. Invoked by out boot loader. 14 | section .text 15 | bits 32 16 | start: 17 | mov esp, stack_top ; Use our temporary stack. 18 | 19 | ;; Sanity-check our system. 20 | call test_multiboot 21 | call test_cpuid 22 | call test_long_mode 23 | 24 | ;; Turn on paging. 25 | call setup_page_tables 26 | call enable_paging 27 | 28 | ;; Install our GDT. 29 | lgdt [gdt64.pointer] 30 | 31 | ;; Set up our data segment registers. 32 | mov ax, gdt64.data 33 | mov ss, ax 34 | mov ds, ax 35 | mov es, ax 36 | 37 | ;; To set up our code segment, we need to make a jump, and 38 | ;; when the jump finishes, we'll be in 64-bit mode. 39 | jmp gdt64.code:long_mode_start 40 | 41 | ;;; Boot-time error handler. Prints `ERR: ` and a code. 42 | ;;; 43 | ;;; al: Error code. 44 | error: 45 | mov dword [0xb8000], 0x4f524f45 46 | mov dword [0xb8004], 0x4f3a4f52 47 | mov dword [0xb8008], 0x4f204f20 48 | mov byte [0xb800a], al 49 | hlt 50 | 51 | ;;; Make sure we were loaded by multiboot. 52 | test_multiboot: 53 | cmp eax, 0x36d76289 ; Did multiboot put a magic value in eax? 54 | jne .no_multiboot 55 | ret 56 | .no_multiboot: 57 | mov al, "M" 58 | jmp error 59 | 60 | ;;; Test for CPUID. Copied from 61 | ;;; http://blog.phil-opp.com/rust-os/entering-longmode.html 62 | ;;; which copied from 63 | ;;; http://wiki.osdev.org/Setting_Up_Long_Mode#Detection_of_CPUID 64 | test_cpuid: 65 | pushfd ; Store the FLAGS-register. 66 | pop eax ; Restore the A-register. 67 | mov ecx, eax ; Set the C-register to the A-register. 68 | xor eax, 1 << 21 ; Flip the ID-bit, which is bit 21. 69 | push eax ; Store the A-register. 70 | popfd ; Restore the FLAGS-register. 71 | pushfd ; Store the FLAGS-register. 72 | pop eax ; Restore the A-register. 73 | push ecx ; Store the C-register. 74 | popfd ; Restore the FLAGS-register. 75 | xor eax, ecx ; Do a XOR-operation on the A and C. 76 | jz .no_cpuid ; The zero flag is set, no CPUID. 77 | ret ; CPUID is available for use. 78 | .no_cpuid: 79 | mov al, "I" 80 | jmp error 81 | 82 | ;;; Test for presence of 64-bit support. Copied from the same sources as 83 | ;;; test_cpuid. 84 | test_long_mode: 85 | mov eax, 0x80000000 ; Set the A-register to 0x80000000. 86 | cpuid ; CPU identification. 87 | cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. 88 | jb .no_long_mode ; It is less, there is no long mode. 89 | mov eax, 0x80000001 ; Set the A-register to 0x80000001. 90 | cpuid ; CPU identification. 91 | ;; Test if the LM-bit, which is bit 29, is set in the D-register. 92 | test edx, 1 << 29 93 | jz .no_long_mode ; They aren't, there is no long mode. 94 | ret 95 | .no_long_mode: 96 | mov al, "L" 97 | jmp error 98 | 99 | ;;; Configure p4_table and p3_table to map a single, huge 1GB page that 100 | ;;; has the same virtual and physical addresses, located at 0x0. 101 | setup_page_tables: 102 | ;; Point first entry in P4 at P3, setting appropriate flag 103 | ;; bits in the unused portions of the pointer. 104 | mov eax, p3_table 105 | or eax, 0b11 ; Present & writable. 106 | mov [p4_table], eax 107 | 108 | ;; Map first entry in P3 to 0, with flag bits set. 109 | mov dword [p3_table], 0b10000011 ; Present & writable & huge. 110 | ret 111 | 112 | ;;; Turn on paging. 113 | enable_paging: 114 | ;; Load P4 into cr3. 115 | mov eax, p4_table 116 | mov cr3, eax 117 | 118 | ;; Enable Physical Address Extension in cr4. 119 | mov eax, cr4 120 | or eax, 0x20 121 | mov cr4, eax 122 | 123 | ;; Set the long mode bit in the EFER MSR. 124 | mov ecx, 0xC0000080 125 | rdmsr 126 | or eax, 0x100 127 | wrmsr 128 | 129 | ;; Turn on paging in cr0. 130 | mov eax, cr0 131 | or eax, 0x80010000 132 | mov cr0, eax 133 | ret 134 | 135 | section .bss 136 | 137 | ;;; P4 page table for configuring virtual memory. Must be aligned on a 138 | ;;; 4096-byte boundary. 139 | align 4096 140 | p4_table: 141 | resb 4096 142 | 143 | ;;; P3 page table for configuring virtual memory. Must be aligned on a 144 | ;;; 4096-byte boundary. 145 | p3_table: 146 | resb 4096 147 | 148 | ;;; Our kernel stack. We want to make this large enough so that we don't 149 | ;;; need to worry about overflowing it until we figure out how to set up 150 | ;;; a guard page and print errors on page faults. 151 | stack_bottom: 152 | resb 8192 153 | stack_top: 154 | 155 | align 4096 156 | HEAP_BOTTOM: 157 | resb 4*1024*1024 158 | HEAP_TOP: 159 | 160 | ;;; Global Description Table. Used to set segmentation to the restricted 161 | ;;; values needed for 64-bit mode. 162 | section .rodata 163 | gdt64: 164 | dq 0 ; Mandatory 0. 165 | .code: equ $ - gdt64 166 | dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; Code segment. 167 | .data: equ $ - gdt64 168 | dq (1<<44) | (1<<47) | (1<<41) ; Data segment. 169 | .pointer: 170 | dw $ - gdt64 - 1 171 | dq gdt64 172 | 173 | ;;; Export selectors so Rust can access them. 174 | gdt64_code_offset: 175 | dw gdt64.code 176 | -------------------------------------------------------------------------------- /src/arch/x86_64/common.inc: -------------------------------------------------------------------------------- 1 | ;; -*- mode: asm -*- 2 | ;; 3 | ;; NASM constants and macros that we use everywhere. 4 | 5 | SCREEN_BASE equ 0xb8000 6 | 7 | -------------------------------------------------------------------------------- /src/arch/x86_64/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "toyos" { 5 | multiboot2 /boot/kernel.bin 6 | boot 7 | } 8 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt_handlers.asm: -------------------------------------------------------------------------------- 1 | %include 'common.inc' 2 | 3 | global report_interrupt 4 | global interrupt_handlers 5 | 6 | extern rust_interrupt_handler 7 | 8 | section .text 9 | bits 64 10 | 11 | ;;; Call as `TRACE 'x'` to trace our progress through an interrupt handler. 12 | %macro TRACE 1 13 | mov eax, %1 14 | jmp trace 15 | %endmacro 16 | 17 | ;;; Prints "INTx" in green, where "x" is a character literal passed at the 18 | ;;; bottom of eax. 19 | trace: 20 | shl eax, 16 21 | or eax, 0x2f202f54 22 | mov dword [SCREEN_BASE], 0x2f4e2f49 23 | mov dword [SCREEN_BASE + 0x4], eax 24 | hlt 25 | 26 | ;;; Registers to save pieced together from: 27 | ;;; 28 | ;;; http://stackoverflow.com/questions/6837392/how-to-save-the-registers-on-x86-64-for-an-interrupt-service-routine 29 | ;;; https://github.com/torvalds/linux/blob/master/arch/x86/entry/entry_64.S 30 | ;;; http://x86-64.org/documentation/abi.pdf 31 | ;;; http://developer.amd.com/wordpress/media/2012/10/24593_APM_v21.pdf 32 | ;;; https://github.com/redox-os/redox/blob/master/kernel/asm/interrupts-x86_64.asm 33 | ;;; 34 | ;;; We skip any "callee saved" registers, on the theory that the Rust 35 | ;;; compiler will save them if it actually uses them. 36 | ;;; 37 | ;;; We don't save any floating point, MMX, SSE, etc. registers, because 38 | ;;; they're large, complicated, and slow to save, and we want our interrupt 39 | ;;; handlers to be fast. So we just don't use any of those processor 40 | ;;; features in kernel mode. 41 | ;;; 42 | ;;; This needs to be kept in sync with InterruptContext. 43 | %macro push_caller_saved 0 44 | ;; For later: " ekidd: one thing you can also do to preserve 45 | ;; FPU registers in interrupt handlers is to simply set the EM flag in 46 | ;; CR0". Seen on #rust-osdev. 47 | 48 | ;; Save ordinary registers. 49 | push rax 50 | push rcx 51 | push rdx 52 | push r8 53 | push r9 54 | push r10 55 | push r11 56 | ;; These two are caller-saved on x86_64! 57 | push rdi 58 | push rsi 59 | %endmacro 60 | 61 | %macro pop_caller_saved 0 62 | ;; Restore ordinary registers. 63 | pop rsi 64 | pop rdi 65 | pop r11 66 | pop r10 67 | pop r9 68 | pop r8 69 | pop rdx 70 | pop rcx 71 | pop rax 72 | %endmacro 73 | 74 | ;;; "Error" interrupts have two extra dwords pushed onto the stack: a 0 pad to 75 | ;;; keep things aligned, and the the error code. See 76 | ;;; http://developer.amd.com/wordpress/media/2012/10/24593_APM_v21.pdf 77 | ;;; p. 247 "8.9 Long-Mode Interrupt Control Transfers". 78 | ;;; 79 | ;;; This needs to be kept in sync with InterruptContext. 80 | %macro int_entry_error 1 81 | int_entry_%1: 82 | ;; There's already qword error code here, which we're reponsible 83 | ;; for popping before IRET. 84 | push qword %1 ; Record interrupt ID. 85 | jmp int_shared ; Now do the hard work for this interrupt. 86 | %endmacro 87 | 88 | ;;; For non-error interrupts, we push an error code of zero for consistency. 89 | ;;; 90 | ;;; This needs to be kept in sync with InterruptContext. 91 | %macro int_entry_dummy_error 1 92 | int_entry_%1: 93 | push qword 0 ; Push error code of 0. 94 | push qword %1 ; Record interrupt ID. 95 | jmp int_shared ; Now do the hard work for this interrupt. 96 | %endmacro 97 | 98 | ;;; Interrupt names from 99 | ;;; http://developer.amd.com/wordpress/media/2012/10/24593_APM_v21.pdf 100 | ;;; 101 | ;;; You can double-check which ones have errors, and which ones don't, here: 102 | ;;; http://cgit.haiku-os.org/haiku/plain/src/system/kernel/arch/x86/64/interrupts.S 103 | ;;; 104 | ;;; 0 Integer Divide-by-Zero Exception 105 | int_entry_dummy_error 0 106 | ;;; 1 Debug Exception 107 | int_entry_dummy_error 1 108 | ;;; 2 Non-Maskable-Interrupt 109 | int_entry_dummy_error 2 110 | ;;; 3 Breakpoint Exception (INT 3) 111 | int_entry_dummy_error 3 112 | ;;; 4 Overflow Exception (INTO instruction) 113 | int_entry_dummy_error 4 114 | ;;; 5 Bound-Range Exception (BOUND instruction) 115 | int_entry_dummy_error 5 116 | ;;; 6 Invalid-Opcode Exception 117 | int_entry_dummy_error 6 118 | ;;; 7 Device-Not-Available Exception 119 | int_entry_dummy_error 7 120 | ;;; 8 Double-Fault Exception 121 | int_entry_error 8 ; Error code is always 0? 122 | ;;; 9 Coprocessor-Segment-Overrun Exception (reserved in AMD64) 123 | ;;; 10 Invalid-TSS Exception 124 | int_entry_error 10 125 | ;;; 11 Segment-Not-Present Exception 126 | int_entry_error 11 127 | ;;; 12 Stack Exception 128 | int_entry_error 12 129 | ;;; 13 General-Protection Exception 130 | int_entry_error 13 131 | ;;; 14 Page-Fault Exception 132 | int_entry_error 14 ; Maybe? 133 | ;;; 15 (Reserved) 134 | ;;; 16 x87 Floating-Point Exception 135 | int_entry_dummy_error 16 136 | ;;; 17 Alignment-Check Exception 137 | int_entry_error 17 ; Error code is always 0? 138 | ;;; 18 Machine-Check Exception 139 | int_entry_dummy_error 18 140 | ;;; 19 SIMD Floating-Point Exception 141 | int_entry_dummy_error 19 142 | ;;; 30 Security Exception 143 | 144 | ;;; Fill in cutom handlers 32 through 255. 145 | int_entry_dummy_error 32 146 | %assign i 33 147 | %rep 224 148 | int_entry_dummy_error i 149 | %assign i i+1 150 | %endrep 151 | 152 | ;;; All of the interrupt table entries wind up here, and we call into Rust. 153 | int_shared: 154 | push_caller_saved 155 | 156 | mov rdi, rsp ; Pass pointer to interrupt data. 157 | call rust_interrupt_handler 158 | 159 | pop_caller_saved 160 | add rsp, 16 ; Remove err code & interrupt ID. 161 | iretq 162 | 163 | ;;; A dummy interrupt handler. 164 | report_interrupt: 165 | push_caller_saved 166 | 167 | ;; Print "INT!" 168 | mov rax, 0x2f212f542f4e2f49 169 | mov qword [SCREEN_BASE], rax 170 | 171 | pop_caller_saved 172 | iretq 173 | 174 | section .rodata 175 | interrupt_handlers: 176 | dq int_entry_0 177 | dq int_entry_1 178 | dq int_entry_2 179 | dq int_entry_3 180 | dq int_entry_4 181 | dq int_entry_5 182 | dq int_entry_6 183 | dq int_entry_7 184 | dq int_entry_8 185 | dq 0 186 | dq int_entry_10 187 | dq int_entry_11 188 | dq int_entry_12 189 | dq int_entry_13 190 | dq int_entry_14 191 | dq 0 192 | dq int_entry_16 193 | dq int_entry_17 194 | dq int_entry_18 195 | dq int_entry_19 196 | dq 0 197 | dq 0 198 | dq 0 199 | dq 0 200 | dq 0 201 | dq 0 202 | dq 0 203 | dq 0 204 | dq 0 205 | dq 0 206 | dq 0 ; int_entry_30 207 | dq 0 208 | %assign i 32 209 | %rep 224 210 | dq int_entry_%+i 211 | %assign i i+1 212 | %endrep 213 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupts.rs: -------------------------------------------------------------------------------- 1 | /// WIP. Some bits were sanity-checked against 2 | /// https://github.com/ryanra/RustOS/blob/master/src/arch/x86/idt.rs 3 | /// 4 | /// See section 6.10 of 5 | /// http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf 6 | /// 7 | /// See http://jvns.ca/blog/2013/12/04/day-37-how-a-keyboard-works/ for 8 | /// some general advice on setting up interrupts and an entertaining saga 9 | /// of frustration. 10 | 11 | use core::mem::size_of; 12 | use core::ptr; 13 | use pic8259_simple::ChainedPics; 14 | use spin::Mutex; 15 | use x86; 16 | use x86::irq::IdtEntry; 17 | 18 | use arch::x86_64::keyboard; 19 | 20 | 21 | //========================================================================= 22 | // Interface to interrupt_handlers.asm 23 | 24 | /// Maximum possible number of interrupts; we can shrink this later if we 25 | /// want. 26 | const IDT_ENTRY_COUNT: usize = 256; 27 | 28 | #[allow(dead_code)] 29 | extern { 30 | /// The offset of the main code segment in our GDT. Exported by our 31 | /// assembly code. 32 | static gdt64_code_offset: u16; 33 | 34 | /// A primitive interrupt-reporting function. 35 | fn report_interrupt(); 36 | 37 | /// Interrupt handlers which call back to rust_interrupt_handler. 38 | static interrupt_handlers: [*const u8; IDT_ENTRY_COUNT]; 39 | } 40 | 41 | /// Various data available on our stack when handling an interrupt. 42 | /// 43 | /// Only `pub` because `rust_interrupt_handler` is. 44 | #[repr(C, packed)] 45 | pub struct InterruptContext { 46 | rsi: u64, 47 | rdi: u64, 48 | r11: u64, 49 | r10: u64, 50 | r9: u64, 51 | r8: u64, 52 | rdx: u64, 53 | rcx: u64, 54 | rax: u64, 55 | int_id: u32, 56 | _pad_1: u32, 57 | error_code: u32, 58 | _pad_2: u32, 59 | } 60 | 61 | 62 | //========================================================================= 63 | // Handling interrupts 64 | 65 | /// Interface to our PIC (programmable interrupt controller) chips. We 66 | /// want to map hardware interrupts to 0x20 (for PIC1) or 0x28 (for PIC2). 67 | static PICS: Mutex = 68 | Mutex::new(unsafe { ChainedPics::new(0x20, 0x28) }); 69 | 70 | /// Print our information about a CPU exception, and loop. 71 | fn cpu_exception_handler(ctx: &InterruptContext) { 72 | 73 | // Print general information provided by x86::irq. 74 | println!("{}, error 0x{:x}", 75 | x86::irq::EXCEPTIONS[ctx.int_id as usize], 76 | ctx.error_code); 77 | 78 | // Provide detailed information about our error code if we know how to 79 | // parse it. 80 | match ctx.int_id { 81 | 14 => { 82 | let err = x86::irq::PageFaultError::from_bits(ctx.error_code); 83 | println!("{:?}", err); 84 | } 85 | _ => {} 86 | } 87 | 88 | loop {} 89 | } 90 | 91 | /// Called from our assembly-language interrupt handlers to dispatch an 92 | /// interrupt. 93 | #[no_mangle] 94 | pub unsafe extern "C" fn rust_interrupt_handler(ctx: &InterruptContext) { 95 | match ctx.int_id { 96 | 0x00...0x0F => cpu_exception_handler(ctx), 97 | 0x20 => { /* Timer. */ } 98 | 0x21 => { 99 | if let Some(input) = keyboard::read_char() { 100 | if input == '\r' { 101 | println!(""); 102 | } else { 103 | print!("{}", input); 104 | } 105 | } 106 | } 107 | 0x80 => println!("Not actually Linux, sorry."), 108 | _ => { 109 | println!("UNKNOWN INTERRUPT #{}", ctx.int_id); 110 | loop {} 111 | } 112 | } 113 | 114 | PICS.lock().notify_end_of_interrupt(ctx.int_id as u8); 115 | } 116 | 117 | 118 | //========================================================================= 119 | // Interrupt Descriptor Table 120 | 121 | /// An Interrupt Descriptor Table which specifies how to respond to each 122 | /// interrupt. 123 | struct Idt { 124 | table: [IdtEntry; IDT_ENTRY_COUNT], 125 | } 126 | 127 | impl Idt { 128 | /// Initialize interrupt handling. 129 | pub unsafe fn initialize(&mut self) { 130 | self.add_handlers(); 131 | self.load(); 132 | } 133 | 134 | /// Fill in our IDT with our handlers. 135 | fn add_handlers(&mut self) { 136 | for (index, &handler) in interrupt_handlers.iter().enumerate() { 137 | if handler != ptr::null() { 138 | self.table[index] = IdtEntry::new(gdt64_code_offset, handler); 139 | } 140 | } 141 | } 142 | 143 | /// Load this table as our interrupt table. 144 | unsafe fn load(&self) { 145 | let pointer = x86::dtables::DescriptorTablePointer { 146 | base: &self.table[0] as *const IdtEntry as u64, 147 | limit: (size_of::() * IDT_ENTRY_COUNT) as u16, 148 | }; 149 | x86::dtables::lidt(&pointer); 150 | } 151 | } 152 | 153 | /// Our global IDT. 154 | static IDT: Mutex = Mutex::new(Idt { 155 | table: [missing_handler(); IDT_ENTRY_COUNT] 156 | }); 157 | 158 | 159 | //========================================================================= 160 | // Initialization 161 | 162 | /// Use the `int` instruction to manually trigger an interrupt without 163 | /// actually using `sti` to enable interrupts. This is highly recommended by 164 | /// http://jvns.ca/blog/2013/12/04/day-37-how-a-keyboard-works/ 165 | #[allow(dead_code)] 166 | pub unsafe fn test_interrupt() { 167 | println!("Triggering interrupt."); 168 | int!(0x80); 169 | println!("Interrupt returned!"); 170 | } 171 | 172 | /// Platform-independent initialization. 173 | pub unsafe fn initialize() { 174 | PICS.lock().initialize(); 175 | IDT.lock().initialize(); 176 | 177 | // Enable this to trigger a sample interrupt. 178 | test_interrupt(); 179 | 180 | // Turn on real interrupts. 181 | x86::irq::enable(); 182 | } 183 | 184 | 185 | //------------------------------------------------------------------------- 186 | // Being merged upstream 187 | // 188 | // This code will go away when https://github.com/gz/rust-x86/pull/4 189 | // is merged. 190 | 191 | /// Create a IdtEntry marked as "absent". Not tested with real 192 | /// interrupts yet. This contains only simple values, so we can call 193 | /// it at compile time to initialize data structures. 194 | const fn missing_handler() -> IdtEntry { 195 | IdtEntry { 196 | base_lo: 0, 197 | sel: 0, 198 | res0: 0, 199 | flags: 0, 200 | base_hi: 0, 201 | res1: 0, 202 | } 203 | } 204 | 205 | trait IdtEntryExt { 206 | fn new(gdt_code_selector: u16, handler: *const u8) -> IdtEntry; 207 | } 208 | 209 | impl IdtEntryExt for IdtEntry { 210 | 211 | /// Create a new IdtEntry pointing at `handler`. 212 | fn new(gdt_code_selector: u16, handler: *const u8) -> IdtEntry { 213 | IdtEntry { 214 | base_lo: ((handler as u64) & 0xFFFF) as u16, 215 | sel: gdt_code_selector, 216 | res0: 0, 217 | flags: 0b100_01110, 218 | base_hi: (handler as u64) >> 16, 219 | res1: 0, 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/arch/x86_64/keyboard.rs: -------------------------------------------------------------------------------- 1 | //! Basic PS/2 keyboard driver. This needs to be split into a 2 | //! PS/2-specific driver, and a high-level portable driver. 3 | //! 4 | //! Scancode table available at http://wiki.osdev.org/Keyboard#Scan_Code_Set_1 5 | 6 | use spin::Mutex; 7 | use cpuio; 8 | 9 | /// A pair of keys which appear on both the left and right sides of the 10 | /// keyboard, such as "left shift" and "right shift". 11 | #[derive(Debug)] 12 | struct KeyPair { 13 | left: bool, 14 | right: bool, 15 | } 16 | 17 | impl KeyPair { 18 | /// Create a new key pair. Normally, we'd use `#[derive(Default)]` and 19 | /// `Default::default()` for this, but if we use those, we can't make 20 | /// them `const`, which means we can't use them to initialize static 21 | /// variables at compile time. So let's reinvent this wheel. 22 | const fn new() -> Self { 23 | KeyPair { left: false, right: false } 24 | } 25 | 26 | /// Is either of the keys in this pair currently pressed? 27 | fn is_pressed(&self) -> bool { 28 | self.left || self.right 29 | } 30 | } 31 | 32 | /// All of our supported keyboard modifiers. 33 | #[derive(Debug)] 34 | struct Modifiers { 35 | shift: KeyPair, 36 | control: KeyPair, 37 | alt: KeyPair, 38 | caps_lock: bool, 39 | } 40 | 41 | impl Modifiers { 42 | /// Create a new set of modifiers. See notes about `Default` above. 43 | const fn new() -> Self { 44 | Modifiers { 45 | shift: KeyPair::new(), 46 | control: KeyPair::new(), 47 | alt: KeyPair::new(), 48 | caps_lock: false, 49 | } 50 | } 51 | 52 | /// Given the current modifier state, should we convert letters to 53 | /// uppercase? 54 | fn use_uppercase_letters(&self) -> bool { 55 | self.shift.is_pressed() ^ self.caps_lock 56 | } 57 | 58 | /// Apply all of our modifiers to an ASCII character, and return a new 59 | /// ASCII character. 60 | fn apply_to(&self, ascii: u8) -> u8 { 61 | if b'a' <= ascii && ascii <= b'z' { 62 | if self.use_uppercase_letters() { 63 | return ascii - b'a' + b'A'; 64 | } 65 | } 66 | ascii 67 | } 68 | 69 | /// Given a keyboard scancode, update our current modifier state. 70 | fn update(&mut self, scancode: u8) { 71 | match scancode { 72 | 0x1D => self.control.left = true, 73 | 0x2A => self.shift.left = true, 74 | 0x36 => self.shift.right = true, 75 | 0x38 => self.alt.left = true, 76 | /// Caps lock toggles on leading edge, instead of paying 77 | /// attention to key up/down events. 78 | 0x3A => self.caps_lock = !self.caps_lock, 79 | 0x9D => self.control.left = false, 80 | 0xAA => self.shift.left = false, 81 | 0xB6 => self.shift.right = false, 82 | 0xB8 => self.alt.left = false, 83 | 84 | _ => {}, 85 | } 86 | } 87 | } 88 | 89 | /// Our keyboard state, including our I/O port, our currently pressed 90 | /// modifiers, etc. 91 | struct State { 92 | /// The PS/2 serial IO port for the keyboard. There's a huge amount of 93 | /// emulation going on at the hardware level to allow us to pretend to 94 | /// be an early-80s IBM PC. 95 | /// 96 | /// We could read the standard keyboard port directly using 97 | /// `inb(0x60)`, but it's nicer if we wrap it up in an `cpuio::Port` 98 | /// object. 99 | port: cpuio::Port, 100 | 101 | /// We also need to keep track of which modifier keys have been pressed 102 | /// and released. 103 | modifiers: Modifiers, 104 | } 105 | 106 | /// Our global keyboard state, protected by a mutex. 107 | static STATE: Mutex = Mutex::new(State { 108 | port: unsafe { cpuio::Port::new(0x60) }, 109 | modifiers: Modifiers::new(), 110 | }); 111 | 112 | /// Try to convert a scancode to an ASCII character. If we don't recognize 113 | /// it, just return `None`. 114 | fn find_ascii(scancode: u8) -> Option { 115 | let idx = scancode as usize; 116 | match scancode { 117 | 0x01 ... 0x0E => Some(b"\x1B1234567890-=\0x02"[idx-0x01]), 118 | 0x0F ... 0x1C => Some(b"\tqwertyuiop[]\r"[idx-0x0F]), 119 | 0x1E ... 0x28 => Some(b"asdfghjkl;'"[idx-0x1E]), 120 | 0x2C ... 0x35 => Some(b"zxcvbnm,./"[idx-0x2C]), 121 | 0x39 => Some(b' '), 122 | _ => None, 123 | } 124 | } 125 | 126 | /// Try to read a single input character 127 | pub fn read_char() -> Option { 128 | let mut state = STATE.lock(); 129 | 130 | // Read a single scancode off our keyboard port. 131 | let scancode = state.port.read(); 132 | 133 | // Give our modifiers first crack at this. 134 | state.modifiers.update(scancode); 135 | 136 | // Look up the ASCII keycode. 137 | if let Some(ascii) = find_ascii(scancode) { 138 | // The `as char` converts our ASCII data to Unicode, which is 139 | // correct as long as we're only using 7-bit ASCII. 140 | Some(state.modifiers.apply_to(ascii) as char) 141 | } else { 142 | // Either this was a modifier key, or it some key we don't know how 143 | // to handle yet, or it's part of a multibyte scancode. Just look 144 | // innocent and pretend nothing happened. 145 | None 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/arch/x86_64/linker.ld: -------------------------------------------------------------------------------- 1 | /* Based on http://blog.phil-opp.com/rust-os/multiboot-kernel.html 2 | * 3 | * Used to specify a custom linking layout that puts our multiboot header 4 | * before everything else. 5 | */ 6 | 7 | ENTRY(start) 8 | 9 | SECTIONS { 10 | /* Load the kernel reasonably high in memory to avoid special addresses. */ 11 | . = 1M; 12 | 13 | .boot : 14 | { 15 | /* This goes first. */ 16 | KEEP(*(.multiboot_header)) 17 | } 18 | 19 | .text : 20 | { 21 | *(.text) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/arch/x86_64/long_mode_init.asm: -------------------------------------------------------------------------------- 1 | ;;; Based on http://blog.phil-opp.com/rust-os/entering-longmode.html 2 | ;;; and http://blog.phil-opp.com/rust-os/setup-rust.html 3 | ;;; 4 | ;;; Once we've run all our 32-bit setup code, we jump here and enter 64-bit 5 | ;;; mode. 6 | ;;; 7 | ;;; To generate yellow 4-letter debug text values, you can run: 8 | ;;; 9 | ;;; "INT!".chars.map {|c| sprintf("2f%02x", c.ord) }.reverse.join 10 | 11 | %include 'common.inc' 12 | 13 | global long_mode_start 14 | 15 | extern rust_main 16 | 17 | section .text 18 | bits 64 19 | long_mode_start: 20 | call setup_SSE 21 | call rust_main 22 | 23 | ;; Display "OKAY". 24 | mov rax, 0x2f592f412f4b2f4f 25 | mov qword [SCREEN_BASE], rax 26 | hlt 27 | 28 | ;;; Print "ERROR: " and an error code. 29 | ;;; 30 | ;;; a1: Error code. 31 | error: 32 | mov rbx, 0x4f4f4f524f524f45 33 | mov [SCREEN_BASE], rbx 34 | mov rbx, 0x4f204f204f3a4f52 35 | mov [SCREEN_BASE + 8], rbx 36 | mov byte [SCREEN_BASE + 0xe], al 37 | hlt 38 | 39 | ;;; Check for SSE and enable it, or display an error. 40 | ;;; 41 | ;;; Copied from http://blog.phil-opp.com/rust-os/setup-rust.html, which got 42 | ;;; it from http://wiki.osdev.org/SSE#Checking_for_SSE. 43 | setup_SSE: 44 | ;; Check for SSE. 45 | mov rax, 0x1 46 | cpuid 47 | test edx, 1<<25 48 | jz .no_SSE 49 | 50 | ;; Enable SSE. 51 | mov rax, cr0 52 | and ax, 0xFFFB ; Clear coprocessor emulation CR0.EM. 53 | or ax, 0x2 ; Set coprocessor monitoring CR0.MP. 54 | mov cr0, rax 55 | mov rax, cr4 56 | or ax, 3 << 9 ; Set CR4.OSFXSR and CR4.OSXMMEXCPT. 57 | mov cr4, rax 58 | 59 | ret 60 | .no_SSE: 61 | mov al, "S" 62 | jmp error 63 | 64 | -------------------------------------------------------------------------------- /src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod keyboard; 2 | pub mod serial; 3 | pub mod pci; 4 | 5 | pub mod vga; 6 | pub mod interrupts; 7 | -------------------------------------------------------------------------------- /src/arch/x86_64/multiboot_header.asm: -------------------------------------------------------------------------------- 1 | ;;; Based on http://blog.phil-opp.com/rust-os/multiboot-kernel.html 2 | ;;; 3 | ;;; This is our Multiboot 2 header, which Grub uses to find our kernel code 4 | ;;; and load it into memory. 5 | 6 | MULTIBOOT_MAGIC equ 0xe85250d6 ; Magic number for multiboot 2. 7 | ARCHITECTURE equ 0 ; Protected mode i386 architecture. 8 | 9 | section .multiboot_header 10 | header_start: 11 | dd MULTIBOOT_MAGIC ; Magic. 12 | dd ARCHITECTURE ; Architecture. 13 | dd header_end - header_start ; Length. 14 | ;; Checksum. 15 | dd 0x100000000 - (MULTIBOOT_MAGIC + ARCHITECTURE + (header_end - header_start)) 16 | 17 | ;; Multiboot tags. 18 | 19 | ;; End tag. 20 | dw 0 ; Type. 21 | dw 0 ; Flags. 22 | dd 8 ; Size. 23 | header_end: 24 | -------------------------------------------------------------------------------- /src/arch/x86_64/pci.rs: -------------------------------------------------------------------------------- 1 | //! Interface to our PCI devices. 2 | //! 3 | //! As usual, this is heavily inspired by http://wiki.osdev.org/Pci 4 | 5 | use core::fmt; 6 | use core::intrinsics::transmute; 7 | use core::iter::Iterator; 8 | use spin::Mutex; 9 | use cpuio; 10 | 11 | struct Pci { 12 | address: cpuio::Port, 13 | data: cpuio::Port, 14 | } 15 | 16 | impl Pci { 17 | /// Read a 32-bit aligned word from PCI Configuration Address Space. 18 | /// This is marked as `unsafe` because passing in out-of-range 19 | /// parameters probably does excitingly horrible things to the 20 | /// hardware. 21 | unsafe fn read_config(&mut self, bus: u8, slot: u8, function: u8, offset: u8) 22 | -> u32 23 | { 24 | let address: u32 = 25 | 0x80000000 26 | | (bus as u32) << 16 27 | | (slot as u32) << 11 28 | | (function as u32) << 8 29 | | (offset & 0b1111_1100) as u32; 30 | self.address.write(address); 31 | self.data.read() 32 | } 33 | 34 | /// Check for a PCI device, and return information about it if present. 35 | unsafe fn probe( 36 | &mut self, bus: u8, slot: u8, function: u8) 37 | -> Option 38 | { 39 | let config_0 = self.read_config(bus, slot, function, 0); 40 | // We'll receive all 1's if no device is present. 41 | if config_0 == 0xFFFFFFFF { return None } 42 | 43 | let config_4 = self.read_config(bus, slot, function, 0x8); 44 | let config_c = self.read_config(bus, slot, function, 0xC); 45 | 46 | Some(FunctionInfo { 47 | bus: bus, 48 | device: slot, 49 | function: function, 50 | vendor_id: config_0 as u16, 51 | device_id: (config_0 >> 16) as u16, 52 | revision_id: config_4 as u8, 53 | subclass: (config_4 >> 16) as u8, 54 | class_code: DeviceClass::from_u8((config_4 >> 24) as u8), 55 | multifunction: config_c & 0x800000 != 0, 56 | }) 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | #[repr(u8)] 62 | #[allow(dead_code)] 63 | pub enum DeviceClass { 64 | Legacy = 0x00, 65 | MassStorage = 0x01, 66 | Network = 0x02, 67 | Display = 0x03, 68 | Multimedia = 0x04, 69 | Memory = 0x05, 70 | BridgeDevice = 0x06, 71 | SimpleCommunication = 0x07, 72 | BaseSystemPeripheral = 0x08, 73 | InputDevice = 0x09, 74 | DockingStation = 0x0A, 75 | Processor = 0x0B, 76 | SerialBus = 0x0C, 77 | Wireless = 0x0D, 78 | IntelligentIO = 0x0E, 79 | SatelliteCommunication = 0x0F, 80 | EncryptionDecryption = 0x10, 81 | DataAndSignalProcessing = 0x11, 82 | Unknown, 83 | } 84 | 85 | impl DeviceClass { 86 | fn from_u8(c: u8) -> DeviceClass { 87 | if c <= DeviceClass::DataAndSignalProcessing as u8 { 88 | unsafe { transmute(c) } 89 | } else { 90 | DeviceClass::Unknown 91 | } 92 | } 93 | } 94 | 95 | #[derive(Debug)] 96 | pub struct FunctionInfo { 97 | bus: u8, 98 | device: u8, 99 | function: u8, 100 | 101 | vendor_id: u16, 102 | device_id: u16, 103 | revision_id: u8, 104 | subclass: u8, 105 | class_code: DeviceClass, 106 | multifunction: bool, 107 | } 108 | 109 | impl fmt::Display for FunctionInfo { 110 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 111 | write!(f, "{}.{}.{}: {:04x} {:04x} {:?} {:02x}", 112 | self.bus, self.device, self.function, 113 | self.vendor_id, self.device_id, 114 | self.class_code, self.subclass) 115 | } 116 | } 117 | 118 | static PCI: Mutex = Mutex::new(Pci { 119 | address: unsafe { cpuio::Port::new(0xCF8) }, 120 | data: unsafe { cpuio::Port::new(0xCFC) }, 121 | }); 122 | 123 | /// Iterator over all functions on our PCI bus. 124 | pub struct FunctionIterator { 125 | // Invariant: The fields in this struct point at the _next_ device to 126 | // probe our PCI bus for. 127 | done: bool, 128 | bus: u8, 129 | device: u8, 130 | multifunction: bool, 131 | function: u8, 132 | } 133 | 134 | const MAX_BUS: u8 = 255; 135 | const MAX_DEVICE: u8 = 31; 136 | const MAX_FUNCTION: u8 = 7; 137 | 138 | impl Iterator for FunctionIterator { 139 | type Item = FunctionInfo; 140 | 141 | fn next(&mut self) -> Option { 142 | // Give up if we've hit the end of the bus. 143 | if self.done { return None; } 144 | 145 | // Scan until we hit the next entry. 146 | let mut pci = PCI.lock(); 147 | loop { 148 | // Check for something at the current bus/device/function. 149 | let result = unsafe { 150 | pci.probe(self.bus, self.device, self.function) 151 | }; 152 | 153 | // If we found a multifunction flag at function 0, prepare to 154 | // enumerate all the functions of this device. 155 | match result { 156 | Some(FunctionInfo { function: 0, multifunction: true, .. }) => 157 | self.multifunction = true, 158 | _ => {} 159 | } 160 | 161 | // Update our state for the next probe. 162 | if self.multifunction && self.function < MAX_FUNCTION { 163 | self.function += 1; 164 | } else if self.device < MAX_DEVICE { 165 | self.function = 0; 166 | self.multifunction = false; 167 | self.device += 1; 168 | } else if self.bus < MAX_BUS { 169 | self.function = 0; 170 | self.multifunction = false; 171 | self.device = 0; 172 | self.bus += 1; 173 | } else { 174 | self.done = true; 175 | return None; 176 | } 177 | 178 | // If we found anything above, abort out of our loop and 179 | // return it. 180 | if let Some(_) = result { 181 | return result; 182 | } 183 | } 184 | } 185 | } 186 | 187 | /// Brute-force PCI bus probing. 188 | pub fn functions() -> FunctionIterator { 189 | FunctionIterator { 190 | done: false, 191 | bus: 0, 192 | device: 0, 193 | multifunction: false, 194 | function: 0, 195 | } 196 | } 197 | 198 | // Running under QEMU, and checking against http://pcidatabase.com/ , we have: 199 | // 200 | // 0.0: 8086 1237 Intel 82440LX/EX PCI & Memory 201 | // 0.1: 8086 7000 Intel 82371SB PIIX3 PCI-to-ISA Bridge (Triton II) 202 | // 0.2: 1013 00b8 Cirrus Logic CL-GD5446 64-bit VisualMedia Accelerator 203 | // 0.3: 8086 100e Intel 02000 Intel Pro 1000/MT 204 | -------------------------------------------------------------------------------- /src/arch/x86_64/serial.rs: -------------------------------------------------------------------------------- 1 | //! Basic serial port driver. 2 | //! 3 | //! As usual, inspired by http://wiki.osdev.org/Serial_Ports 4 | 5 | use core::fmt; 6 | use spin::Mutex; 7 | use cpuio; 8 | use self::Register::*; 9 | 10 | /// Each COM port has 8 I/O registers associated with it, some of which are 11 | /// dual use. 12 | #[allow(dead_code)] 13 | #[repr(C, u8)] 14 | enum Register { 15 | /// Either a data register or the low byte of our baud divisor, depending 16 | /// on the high bit of LineControl. 17 | DataOrBaudLsb = 0, 18 | /// Either a interrupt flags register or the high byte of our baud 19 | /// divisor. 20 | InterruptEnableOrBaudMsb = 1, 21 | InterruptIdentAndFifo = 2, 22 | /// When the high bit of LineControl is set, the first two registers 23 | /// switch over to their "baud mode". 24 | LineControl = 3, 25 | ModemControl = 4, 26 | LineStatus = 5, 27 | ModemStatus = 6, 28 | Scratch = 7 29 | } 30 | 31 | /// A COM serial port. 32 | pub struct ComPort { 33 | /// COM ports are identified by the base address of their associated 34 | /// I/O registers. 35 | base_addr: u16, 36 | /// Has this port been initialized yet? 37 | initialized: bool, 38 | } 39 | 40 | impl ComPort { 41 | /// Create a new COM port with the specified base address. Note that 42 | /// this does not actually finish initializing the serial port, because 43 | /// this is `const` function that may be computed at compile time. 44 | /// Initialization is finished by `lazy_initialize`, which should be 45 | /// called by all safe, public functions in this API. 46 | const unsafe fn new(base_addr: u16) -> ComPort { 47 | ComPort { base_addr: base_addr, initialized: false } 48 | } 49 | 50 | /// Finish the runtime-only setup needed by this port. 51 | unsafe fn lazy_initialize(&mut self) { 52 | if self.initialized == true { return; } 53 | self.initialized = true; 54 | 55 | // Disable interrupts. 56 | self.port(InterruptEnableOrBaudMsb).write(0x00); 57 | 58 | // Set baud and 8N1 mode. 59 | self.set_baud_divisor(2); // 115,200 / 2 60 | self.port(LineControl).write(0x03); 61 | 62 | // Enable FIFOs with 14-byte threshhold. 63 | self.port(InterruptIdentAndFifo).write(0xC7); 64 | 65 | // Configure modem: RTS/DSR and IRQs on. But if we actually want 66 | // to get IRQs, I think we also need to set up 67 | // InterruptEnableOrBaudMsb. 68 | self.port(ModemControl).write(0x0B); 69 | } 70 | 71 | /// Get an cpuio::Port object for one of our associated ports. This is 72 | /// marked as `unsafe` because the returned port can potentially be 73 | /// used to mess with processor interrupts and otherwise violate 74 | /// fundamental abstractions about how Rust code works. 75 | unsafe fn port(&mut self, register: Register) -> cpuio::Port { 76 | cpuio::Port::new(self.base_addr + (register as u8 as u16)) 77 | } 78 | 79 | /// Set the baud rate as a divisor of 115,200. 80 | fn set_baud_divisor(&mut self, divisor: u16) { 81 | unsafe { 82 | self.lazy_initialize(); 83 | 84 | // Switch ports DataOrBaudLsb and InterruptEnableOrBaudMsb to 85 | // their baud mode by setting the high bit of LineControl. 86 | let saved_line_control = self.port(LineControl).read(); 87 | self.port(LineControl).write(0x80 | saved_line_control); 88 | 89 | // Set baud divisor. 90 | self.port(DataOrBaudLsb).write(divisor as u8); 91 | self.port(InterruptEnableOrBaudMsb).write((divisor >> 8) as u8); 92 | 93 | // Restore previous port modes. 94 | self.port(LineControl).write(saved_line_control); 95 | } 96 | } 97 | 98 | /// Can we safely transmit data on this serial port right now, or will 99 | /// we block? 100 | fn can_transmit(&mut self) -> bool { 101 | unsafe { 102 | self.lazy_initialize(); 103 | // TODO: Check to see what the meaning of this bit is. OSDev 104 | // calls it "is_transmit_empty", so maybe we actually want a 105 | // different bit. 106 | (self.port(LineStatus).read() & 0x20) != 0 107 | } 108 | } 109 | } 110 | 111 | impl fmt::Write for ComPort { 112 | /// Output a string to our COM port. This allows using nice, 113 | /// high-level tools like Rust's `write!` macro. 114 | fn write_str(&mut self, s: &str) -> fmt::Result { 115 | unsafe { 116 | self.lazy_initialize(); 117 | 118 | // Output each byte of our string. 119 | for &b in s.as_bytes() { 120 | // Loop until the port's available. 121 | while !self.can_transmit() {} 122 | 123 | // Write our byte. 124 | self.port(DataOrBaudLsb).write(b); 125 | } 126 | } 127 | Ok(()) 128 | } 129 | } 130 | 131 | /// Our primary serial port. 132 | pub static COM1: Mutex = Mutex::new(unsafe { 133 | ComPort::new(0x03F8) 134 | }); 135 | -------------------------------------------------------------------------------- /src/arch/x86_64/vga.rs: -------------------------------------------------------------------------------- 1 | // The spin::Mutex + Uniq trick here is directly based on 2 | // http://blog.phil-opp.com/rust-os/printing-to-screen.html 3 | 4 | use core::fmt::{Write, Result}; 5 | use core::ptr::Unique; 6 | use spin::Mutex; 7 | 8 | const WIDTH: usize = 80; 9 | const HEIGHT: usize = 25; 10 | 11 | /// Standard VGA colors. 12 | #[derive(Copy, Clone)] 13 | #[repr(u8)] 14 | #[allow(dead_code)] 15 | pub enum Color { 16 | Black = 0, 17 | Blue = 1, 18 | Green = 2, 19 | Cyan = 3, 20 | Red = 4, 21 | Magenta = 5, 22 | Brown = 6, 23 | LightGrey = 7, 24 | DarkGrey = 8, 25 | LightBlue = 9, 26 | LightGreen = 10, 27 | LightCyan = 11, 28 | LightRed = 12, 29 | LightMagenta = 13, 30 | Yellow = 14, 31 | White = 15, 32 | } 33 | 34 | /// VGA foreground and background color set. 35 | #[derive(Copy, Clone)] 36 | #[repr(C)] 37 | pub struct ColorScheme { 38 | value: u8, 39 | } 40 | 41 | impl ColorScheme { 42 | pub const fn new(fore: Color, back: Color) -> Self { 43 | ColorScheme { value: (back as u8) << 4 | (fore as u8) } 44 | } 45 | } 46 | 47 | /// A colored VGA character. 48 | #[derive(Copy, Clone)] 49 | #[repr(C)] 50 | pub struct Char { 51 | pub code: u8, 52 | pub colors: ColorScheme, 53 | } 54 | 55 | type Buffer = [[Char; WIDTH]; HEIGHT]; 56 | 57 | /// A VGA screen, in character mode. 58 | pub struct Screen { 59 | colors: ColorScheme, 60 | x: usize, 61 | y: usize, 62 | buffer: Unique, 63 | } 64 | 65 | impl Screen { 66 | /// Clear the screen to the specified color. 67 | pub fn clear(&mut self, color: Color) -> &mut Self { 68 | let colors = ColorScheme::new(color, color); 69 | let c = Char { 70 | code: b' ', 71 | colors: colors, 72 | }; 73 | for y in 0..HEIGHT { 74 | for x in 0..WIDTH { 75 | self.buffer()[y][x] = c; 76 | } 77 | } 78 | self 79 | } 80 | 81 | /// Set the current text colors. 82 | pub fn set_colors(&mut self, colors: ColorScheme) -> &mut Self { 83 | self.colors = colors; 84 | self 85 | } 86 | 87 | /// Write a string to the screen. 88 | pub fn write(&mut self, text: &[u8]) { 89 | for c in text { 90 | self.write_byte(*c); 91 | } 92 | } 93 | 94 | /// Write a single character to the screen. 95 | pub fn write_byte(&mut self, code: u8) { 96 | if code == b'\n' { 97 | self.x = 0; 98 | self.y += 1; 99 | } else { 100 | let c = Char { 101 | code: code, 102 | colors: self.colors, 103 | }; 104 | self.buffer()[self.y][self.x] = c; 105 | self.x += 1; 106 | if self.x >= WIDTH { 107 | self.x = 0; 108 | self.y += 1; 109 | } 110 | } 111 | if self.y >= HEIGHT { 112 | self.y = HEIGHT - 1; 113 | self.scroll(); 114 | } 115 | } 116 | 117 | fn scroll(&mut self) { 118 | // We'll use character to clear newly exposed areas. 119 | let clear = Char { 120 | code: b' ', 121 | colors: self.colors, 122 | }; 123 | 124 | // Move existing lines up one. 125 | let buffer: &mut _ = self.buffer(); 126 | for y in 1..HEIGHT { 127 | buffer[y-1] = buffer[y]; 128 | } 129 | 130 | // Clear the last line. 131 | for x in 0..WIDTH { 132 | buffer[HEIGHT-1][x] = clear; 133 | } 134 | } 135 | 136 | fn buffer(&mut self) -> &mut Buffer { 137 | unsafe { self.buffer.get_mut() } 138 | } 139 | } 140 | 141 | impl Write for Screen { 142 | fn write_str(&mut self, s: &str) -> Result { 143 | self.write(s.as_bytes()); 144 | Ok(()) 145 | } 146 | } 147 | 148 | /// The system's VGA screen. 149 | pub static SCREEN: Mutex = Mutex::new(Screen { 150 | colors: ColorScheme::new(Color::White, Color::Black), 151 | x: 0, 152 | y: 0, 153 | buffer: unsafe { Unique::new(0xb8000 as *mut _) }, 154 | }); 155 | -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | //! A wrapper around both our VGA console and our serial console. 2 | 3 | use core::fmt; 4 | use spin::Mutex; 5 | use arch::{vga, serial}; 6 | 7 | pub struct Console; 8 | 9 | impl fmt::Write for Console { 10 | /// Output a string to each of our console outputs. 11 | fn write_str(&mut self, s: &str) -> fmt::Result { 12 | try!(vga::SCREEN.lock().write_str(s)); 13 | serial::COM1.lock().write_str(s) 14 | } 15 | } 16 | 17 | pub static CONSOLE: Mutex = Mutex::new(Console); 18 | 19 | -------------------------------------------------------------------------------- /src/heap.rs: -------------------------------------------------------------------------------- 1 | //! Configuration of our system allocator. 2 | //! 3 | //! There's a good chance that `HEAP_BOTTOM` and `HEAP_TOP` stuff involves 4 | //! undefined behavior and thus nasal demons as far as `rustc` is 5 | //! concerned. 6 | 7 | use alloc_buddy_simple::{FreeBlock, initialize_allocator}; 8 | 9 | extern { 10 | /// The bottom of our heap. Declared in `boot.asm` so that we can 11 | /// easily specify alignment constraints. We declare this as a single 12 | /// variable of type `u8`, because that's how we get it to link, but we 13 | /// only want to take the address of it. 14 | static mut HEAP_BOTTOM: u8; 15 | 16 | /// The top of our heap. This is actually "one beyond" the heap space, 17 | /// so storing things here would be Very Bad. Even just declaring this 18 | /// probably invokes undefined behavior, but our fingers are crossed. 19 | static mut HEAP_TOP: u8; 20 | } 21 | 22 | /// An array of free lists which we pass to the system allocator at system 23 | /// startup time. 24 | static mut FREE_LISTS: [*mut FreeBlock; 19] = [0 as *mut _; 19]; 25 | 26 | /// Initialze our system heap. Once this is done, it's theoretically safe 27 | /// to use functions in libcollection that allocate memory. 28 | pub unsafe fn initialize() { 29 | // Convert our fake variables into the pointers we wanted in the first 30 | // place. Again, there may be some risk of undefined behavior here. 31 | let heap_bottom_ptr = &mut HEAP_BOTTOM as *mut _; 32 | let heap_top_ptr = &mut HEAP_TOP as *mut _; 33 | 34 | // Initialize our main allocator library. 35 | let heap_size = heap_top_ptr as usize - heap_bottom_ptr as usize; 36 | initialize_allocator(heap_bottom_ptr, heap_size, &mut FREE_LISTS); 37 | } 38 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(asm, const_fn, lang_items, unique, collections)] 2 | #![no_std] 3 | 4 | extern crate collections; 5 | 6 | extern crate alloc_buddy_simple; 7 | extern crate cpuio; 8 | extern crate pic8259_simple; 9 | extern crate rlibc; 10 | extern crate spin; 11 | 12 | #[macro_use(int)] 13 | extern crate x86; 14 | 15 | use core::fmt::Write; 16 | 17 | // These need to be visible to the linker, so we need to export them. 18 | pub use arch::interrupts::rust_interrupt_handler; 19 | pub use runtime_glue::*; 20 | 21 | #[macro_use] 22 | mod macros; 23 | mod runtime_glue; 24 | mod heap; 25 | mod arch; 26 | mod console; 27 | 28 | 29 | #[no_mangle] 30 | pub extern "C" fn rust_main() { 31 | use arch::vga::{SCREEN, ColorScheme}; 32 | use arch::vga::Color::*; 33 | 34 | SCREEN.lock() 35 | .clear(DarkGrey) 36 | .set_colors(ColorScheme::new(Yellow, DarkGrey)); 37 | println!("Hello, world!"); 38 | 39 | unsafe { 40 | arch::interrupts::initialize(); 41 | heap::initialize(); 42 | } 43 | 44 | let mut vec = collections::vec::Vec::::new(); 45 | vec.push(1); 46 | vec.push(2); 47 | vec.push(3); 48 | println!("Hey, I made a vector in kernel space! {:?}", vec); 49 | 50 | println!("Scanning PCI bus..."); 51 | for function in arch::pci::functions() { 52 | println!("{}", function); 53 | } 54 | 55 | println!("Running."); 56 | 57 | loop {} 58 | } 59 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Macros used by our kernel. 2 | 3 | /// Print formatted text to our console. 4 | /// 5 | /// From http://blog.phil-opp.com/rust-os/printing-to-screen.html, but tweaked 6 | /// to work with our APIs. 7 | macro_rules! print { 8 | ($($arg:tt)*) => ({ 9 | use core::fmt::Write; 10 | $crate::console::CONSOLE.lock().write_fmt(format_args!($($arg)*)).unwrap(); 11 | }); 12 | } 13 | 14 | /// Print formatted text to our console, followed by a newline. 15 | /// 16 | /// From https://doc.rust-lang.org/nightly/std/macro.println!.html 17 | macro_rules! println { 18 | ($fmt:expr) => (print!(concat!($fmt, "\n"))); 19 | ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); 20 | } 21 | -------------------------------------------------------------------------------- /src/runtime_glue.rs: -------------------------------------------------------------------------------- 1 | //! Minor functions that Rust really expects to be defined by our compiler 2 | //! or something, but which we need to provide manually because we're on 3 | //! bare metal. 4 | 5 | #[lang = "eh_personality"] 6 | extern "C" fn eh_personality() { 7 | } 8 | 9 | #[lang = "panic_fmt"] 10 | extern "C" fn panic_fmt( 11 | args: ::core::fmt::Arguments, file: &str, line: usize) 12 | -> ! 13 | { 14 | println!("PANIC: {}:{}: {}", file, line, args); 15 | loop {} 16 | } 17 | 18 | #[no_mangle] 19 | #[allow(non_snake_case)] 20 | pub fn _Unwind_Resume() 21 | { 22 | println!("UNWIND!"); 23 | loop {} 24 | } 25 | -------------------------------------------------------------------------------- /x86_64-unknown-none-gnu.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none-gnu", 3 | "target-endian": "little", 4 | "target-pointer-width": "64", 5 | "os": "none", 6 | "arch": "x86_64", 7 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 8 | "pre-link-args": [ "-m64" ], 9 | "cpu": "x86-64", 10 | "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", 11 | "disable-redzone": true, 12 | "eliminate-frame-pointer": true, 13 | "linker-is-gnu": true, 14 | "no-compiler-rt": true, 15 | "archive-format": "gnu" 16 | } 17 | --------------------------------------------------------------------------------