├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── Xargo.toml ├── axel_tan.png ├── config ├── Makefile.arm11 ├── Makefile.x86_32 ├── Makefile.x86_64 ├── arm-unknown-none-eabihf.json ├── axel.gdb ├── grub.cfg ├── i386-unknown-axel.json └── x86_64-unknown-axel.json ├── rustfmt.toml └── src ├── arch ├── arm11 │ ├── Makefile │ ├── boot.S │ ├── link.ld │ ├── mod.rs │ └── peripheral │ │ ├── addr.rs │ │ ├── gpio.rs │ │ ├── mod.rs │ │ ├── spi.rs │ │ └── timer.rs ├── mod.rs ├── x86_32 │ ├── Makefile │ ├── boot.asm │ ├── link.ld │ └── mod.rs └── x86_64 │ ├── Makefile │ ├── boot.asm │ ├── interrupt.rs │ ├── interrupt │ ├── descriptor.rs │ ├── handler.rs │ ├── pic.rs │ ├── pit.rs │ └── table.rs │ ├── link.ld │ ├── mod.rs │ ├── thread.rs │ └── tss.rs ├── bytes.rs ├── context.rs ├── graphic.rs ├── kernel.rs ├── log.rs ├── memory.rs ├── memory ├── address.rs ├── address │ └── alignment.rs ├── buddy_system │ ├── allocator.rs │ └── mod.rs ├── early_allocator.rs ├── frame.rs ├── frame_allocator.rs ├── global_allocator.rs ├── paging.rs ├── paging │ ├── entry.rs │ ├── page.rs │ ├── page_index.rs │ └── table.rs └── region.rs └── process.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bin 3 | *.iso 4 | *.map 5 | *.sym 6 | *.lst 7 | a.out 8 | .gdb_history 9 | 10 | target 11 | rlibs 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mopp/Axel/2f14ca29a02276a059c2a5a4cb79f28ac10d5fae/.gitmodules -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "array-init" 5 | version = "0.0.4" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "axel" 13 | version = "0.2.0" 14 | dependencies = [ 15 | "bitfield 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "intrusive-collections 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", 19 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 20 | "multiboot2 0.7.1 (git+https://github.com/phil-opp/multiboot2-elf64.git)", 21 | "rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "static_assertions 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 24 | "x86_64 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 25 | ] 26 | 27 | [[package]] 28 | name = "bit_field" 29 | version = "0.9.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | 32 | [[package]] 33 | name = "bitfield" 34 | version = "0.13.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.0.4" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | 42 | [[package]] 43 | name = "failure" 44 | version = "0.1.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | dependencies = [ 47 | "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 48 | ] 49 | 50 | [[package]] 51 | name = "failure_derive" 52 | version = "0.1.5" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | dependencies = [ 55 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 56 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 57 | "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 59 | ] 60 | 61 | [[package]] 62 | name = "intrusive-collections" 63 | version = "0.7.8" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | 66 | [[package]] 67 | name = "lazy_static" 68 | version = "1.2.0" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | dependencies = [ 71 | "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 72 | ] 73 | 74 | [[package]] 75 | name = "multiboot2" 76 | version = "0.7.1" 77 | source = "git+https://github.com/phil-opp/multiboot2-elf64.git#b80bb1852f9c500f025909fc5376a3c072f8c2dc" 78 | dependencies = [ 79 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 80 | ] 81 | 82 | [[package]] 83 | name = "nodrop" 84 | version = "0.1.13" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | 87 | [[package]] 88 | name = "proc-macro2" 89 | version = "0.4.27" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 93 | ] 94 | 95 | [[package]] 96 | name = "quote" 97 | version = "0.6.11" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | dependencies = [ 100 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "rlibc" 105 | version = "1.0.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | 108 | [[package]] 109 | name = "spin" 110 | version = "0.4.10" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | 113 | [[package]] 114 | name = "spin" 115 | version = "0.5.0" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | 118 | [[package]] 119 | name = "static_assertions" 120 | version = "0.3.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | 123 | [[package]] 124 | name = "syn" 125 | version = "0.15.26" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | dependencies = [ 128 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 130 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 131 | ] 132 | 133 | [[package]] 134 | name = "synstructure" 135 | version = "0.10.1" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | dependencies = [ 138 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 139 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 140 | "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", 141 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 142 | ] 143 | 144 | [[package]] 145 | name = "unicode-xid" 146 | version = "0.1.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | 149 | [[package]] 150 | name = "usize_conversions" 151 | version = "0.2.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | 154 | [[package]] 155 | name = "ux" 156 | version = "0.1.3" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | 159 | [[package]] 160 | name = "x86_64" 161 | version = "0.4.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | dependencies = [ 164 | "array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 165 | "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 167 | "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 169 | ] 170 | 171 | [metadata] 172 | "checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" 173 | "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" 174 | "checksum bitfield 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a260ed6b9f3ca16a4389390b1b1cd15a3bc0a9d3e63b1ef39f4978cec58a4e83" 175 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 176 | "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" 177 | "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" 178 | "checksum intrusive-collections 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f0207c3d23d0b13d569d4103a98f31c4cd65f30c92c3a157272966b2affd177e" 179 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" 180 | "checksum multiboot2 0.7.1 (git+https://github.com/phil-opp/multiboot2-elf64.git)" = "" 181 | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" 182 | "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" 183 | "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" 184 | "checksum rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" 185 | "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" 186 | "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" 187 | "checksum static_assertions 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "389ce475f424f267dbed6479cbd8f126c5e1afb053b0acdaa019c74305fc65d1" 188 | "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" 189 | "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" 190 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 191 | "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" 192 | "checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" 193 | "checksum x86_64 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f893c7b302f824d1bb6ab25353e585583fa451c918d950e6f81ff5b4267cc18" 194 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "axel" 3 | version = "0.2.0" 4 | authors = ["mopp "] 5 | edition = "2018" 6 | 7 | 8 | [lib] 9 | crate-type = ["staticlib"] 10 | path = "src/kernel.rs" 11 | 12 | 13 | [dependencies] 14 | bitflags = "1.0" 15 | failure = {version = "0.1", features = ["failure_derive"], default-features = false} 16 | intrusive-collections = {version = "0.7", features = ["nightly", "alloc"]} 17 | lazy_static = {version = "1.2", features = ["spin_no_std"]} 18 | multiboot2 = {git = "https://github.com/phil-opp/multiboot2-elf64.git"} 19 | rlibc = "1.0" 20 | spin = "0.5" 21 | static_assertions = {version = "0.3", features = ["nightly"]} 22 | bitfield = "0.13" 23 | x86_64 = "0.4" 24 | 25 | 26 | [profile.dev] 27 | panic = "abort" 28 | 29 | 30 | [profile.release] 31 | panic = "abort" 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017 mopp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCH := x86_64 2 | 3 | # Load target specific configs. 4 | include ./config/Makefile.$(ARCH) 5 | 6 | # Define commands. 7 | export RM := rm -rf 8 | export CC := gcc 9 | CP := cp 10 | MAKE := make 11 | CARGO := cargo 12 | XARGO := xargo 13 | MKDIR := mkdir -p 14 | MKRESCUE := grub-mkrescue 15 | OBJCOPY := objcopy --only-keep-debug 16 | STRIP := strip 17 | GDB := rust-gdb --nh --nx -q -x ./config/axel.gdb 18 | 19 | # Define file names and config files. 20 | AXEL_BIN := axel.bin 21 | AXEL_ISO := axel.iso 22 | AXEL_LIB := target/$(TARGET_TRIPLE)/debug/libaxel.a 23 | AXEL_MAP := axel.map 24 | ARCH_DIR := src/arch/$(ARCH) 25 | LINK_FILE := $(ARCH_DIR)/link.ld 26 | BOOT_OBJ := $(ARCH_DIR)/boot.o 27 | GRUB_CFG := config/grub.cfg 28 | 29 | # Define Rust configs. 30 | export RUST_TARGET_PATH := $(PWD)/config/ 31 | export RUSTFLAGS := -Z no-landing-pads 32 | 33 | 34 | .PHONY: all 35 | all: $(AXEL_BIN) 36 | 37 | 38 | $(AXEL_ISO): $(AXEL_BIN) $(GRUB_CFG) 39 | $(MKDIR) ./iso/boot/grub/ 40 | $(CP) $(AXEL_BIN) ./iso/boot/ 41 | $(CP) $(GRUB_CFG) ./iso/boot/grub/grub.cfg 42 | $(MKRESCUE) -o $@ ./iso/ 43 | $(RM) ./iso/ 44 | 45 | 46 | $(AXEL_BIN): cargo $(BOOT_OBJ) $(LINK_FILE) 47 | $(LD) $(LD_FLAGS) -Wl,-Map=$(AXEL_MAP) -T $(LINK_FILE) -o $@ $(BOOT_OBJ) -L $(dir $(AXEL_LIB)) $(LIBS) -laxel 48 | $(OBJCOPY) $@ axel.sym 49 | $(STRIP) $@ 50 | 51 | 52 | $(BOOT_OBJ): $(BOOT_DEPS) $(LINK_FILE) 53 | $(MAKE) -C $(ARCH_DIR) 54 | 55 | 56 | .PHONY: cargo 57 | cargo: 58 | $(XARGO) build --target=$(TARGET_TRIPLE) 59 | 60 | 61 | .PHONY: clean 62 | clean: 63 | $(MAKE) -C $(ARCH_DIR) clean 64 | $(XARGO) clean 65 | $(RM) *.d *.o *.bin *.iso *.map *.lst *.log *.sym tags bx_enh_dbg.ini 66 | 67 | 68 | .PHONY: run_kernel 69 | run_kernel: $(AXEL_BIN) 70 | $(QEMU) $(QEMU_FLAGS) --kernel $< 71 | 72 | 73 | .PHONY: run_cdrom 74 | run_cdrom: $(AXEL_ISO) 75 | $(QEMU) $(QEMU_FLAGS) -cdrom $< 76 | 77 | 78 | .PHONY: run_cdrom_debug 79 | run_cdrom_debug: $(AXEL_ISO) 80 | $(QEMU) $(QEMU_FLAGS) -s -S -singlestep -cdrom $< 81 | 82 | 83 | .PHONY: gdb 84 | gdb: 85 | $(GDB) 86 | 87 | 88 | # Xargo does not support building test. 89 | # [Is there a way to do `xargo test`? · Issue #104 · japaric/xargo](https://github.com/japaric/xargo/issues/104) 90 | .PHONY: test 91 | test: 92 | $(CARGO) test 93 | 94 | 95 | .PHONY: test_nocapture 96 | test_nocapture: 97 | $(CARGO) test -- --nocapture 98 | 99 | 100 | .PHONY: doc 101 | doc: 102 | $(CARGO) rustdoc -- --no-defaults --passes strip-hidden --passes collapse-docs --passes unindent-comments --passes strip-priv-imports 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Axel 2 | 3 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE) 4 | 5 | Axel Accelerates All For Me ! 6 | 7 | Axel is general purpose operating system which is written by [Rust](https://www.rust-lang.org/) and some assembly languages([nasm](http://www.nasm.us/), ARM assembly). 8 | The current OS architecture is monolithic kernel. 9 | 10 | 11 | ## Support architecture 12 | - [ ] x86_32 13 | - [ ] x86_64 14 | - [ ] arm6 (Raspberry pi zero) 15 | 16 | 17 | ## Requirements 18 | We need some tools to build Axel. 19 | - gcc 20 | - binutils 21 | - rust (nightly) 22 | - xargo 23 | - nasm 24 | - qemu 25 | - ld 26 | - make 27 | - grub 28 | 29 | In case of Arch Linux, you can install these software easily to execute below commands. 30 | ```console 31 | # For ARM architecture. 32 | yaourt -S arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-newlib 33 | 34 | # For x86_32 architecture. 35 | yaourt -S gcc-multilib nasm qemu 36 | ``` 37 | 38 | 39 | ## Build 40 | ```console 41 | make run_cdrom 42 | ``` 43 | 44 | 45 | ## License 46 | The MIT License (MIT) 47 | See [LICENSE](./LICENSE) 48 | 49 | 50 | ## :) 51 | ![LGTM](./axel_tan.png) 52 | -------------------------------------------------------------------------------- /Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-axel.dependencies] 2 | alloc = {} 3 | -------------------------------------------------------------------------------- /axel_tan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mopp/Axel/2f14ca29a02276a059c2a5a4cb79f28ac10d5fae/axel_tan.png -------------------------------------------------------------------------------- /config/Makefile.arm11: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # @file Makefile 3 | # @brief Config for ARM11 to build Axel. 4 | # @author mopp 5 | # @version 0.2 6 | # Note: 7 | # Please pay attention to compiler. 8 | # `arm-linux-gnueabihf` toolchain is for Linux applications and libraries. 9 | # `arm-none-eabi` toolchain is for bare metal software. 10 | # So, Axel is bare metal software. 11 | # Please see below link. 12 | # https://bugs.launchpad.net/linaro-toolchain-binaries/+bug/1181159 13 | # 14 | # `hf` means hardware float. 15 | # In Axel, we have supposed ARM11 is raspberry pi, and it has hardware float feature. 16 | # However, the name of gcc (arm-none-eabi-gcc) does NOT contain `hf`. 17 | # But, setting is hardware float. 18 | # Please pay attention this things. 19 | ############################################################# 20 | 21 | 22 | # Target triple consists of 4 parts (Last part is option). 23 | # architecture-vendor-os-env_type 24 | # Please see below links for more details. 25 | # https://github.com/rust-lang/rfcs/blob/master/text/0131-target-specification.md 26 | # https://github.com/rust-lang/rust/blob/master/src/librustc_back/target/mod.rs 27 | TARGET_TRIPLE := arm-unknown-none-eabihf 28 | RUSTC_CFG := --cfg RPi_zero 29 | 30 | # We use gcc as linker to generate Axel binary because of libgcc. 31 | # So we build rustc using gcc in development. 32 | # gcc options 33 | # --build-id=none is very important in order to do multiboot. 34 | # The reason is described in this link http://stackoverflow.com/questions/27744126/grub-multiboot-header-not-found 35 | # Briefly, this option suppresses creating a note section. 36 | # If this section exists, grub2 cannot find multiboot header because multiboot header is located after 8KB. 37 | # Also, Please pay attention the order of command line arguments. 38 | LD := arm-none-eabi-gcc 39 | LD_FLAGS := -g -O2 -Wl,--build-id=none -Wl,--gc-sections -ffreestanding -fno-builtin -nostdlib -nostartfiles -static -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s 40 | 41 | # All libs are required during opt-level=0. 42 | LIBS := -lm -lc -lgcc 43 | 44 | QEMU := ~/.local/bin/qemu-system-arm 45 | QEMU_FLAGS := -monitor stdio -vga std -m 256 -M raspi -cpu arm1176 46 | 47 | export AS := arm-none-eabi-as 48 | export AS_FLAGS := -g -mfpu=vfp -mfloat-abi=hard -march=armv6zk 49 | 50 | OBJCOPY := arm-none-eabi-objcopy 51 | -------------------------------------------------------------------------------- /config/Makefile.x86_32: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # @file Makefile 3 | # @brief Config for x86_32 to build Axel. 4 | # @author mopp 5 | # @version 0.2 6 | ############################################################# 7 | 8 | # Target triple consists of 4 parts (Last part is option). 9 | # architecture-vendor-os-env_type 10 | # Please see below links for more details. 11 | # https://github.com/rust-lang/rfcs/blob/master/text/0131-target-specification.md 12 | # https://github.com/rust-lang/rust/blob/master/src/librustc_back/target/mod.rs 13 | TARGET_TRIPLE := i386-unknown-axel 14 | RUSTC_CFG := 15 | 16 | # We use gcc as linker to generate Axel binary because of libgcc. 17 | # So we build rustc using gcc in development. 18 | # gcc options 19 | # --build-id=none is very important in order to do multiboot. 20 | # The reason is described in this link http://stackoverflow.com/questions/27744126/grub-multiboot-header-not-found 21 | # Briefly, this option suppresses creating a note section. 22 | # If this section exists, grub2 cannot find multiboot header because multiboot header is located after 8KB. 23 | # Also, Please pay attention the order of command line arguments. 24 | LD := gcc -m32 25 | LD_FLAGS := -g -O2 -Wl,--build-id=none -Wl,--gc-sections -ffreestanding -fno-builtin -nostdlib -static -march=i386 26 | LIBS := -lgcc 27 | 28 | QEMU := qemu-system-i386 29 | QEMU_FLAGS := -monitor stdio -vga std -m 32 -boot order=dc -no-reboot -d int 30 | -------------------------------------------------------------------------------- /config/Makefile.x86_64: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # @file Makefile 3 | # @brief Config for x86_32 to build Axel. 4 | # @author mopp 5 | # @version 0.2 6 | ############################################################# 7 | 8 | # Target triple consists of 4 parts (Last part is option). 9 | # architecture-vendor-os-env_type 10 | # Please see below links for more details. 11 | # https://github.com/rust-lang/rfcs/blob/master/text/0131-target-specification.md 12 | # https://github.com/rust-lang/rust/blob/master/src/librustc_back/target/mod.rs 13 | TARGET_TRIPLE := x86_64-unknown-axel 14 | RUSTC_CFG := 15 | 16 | # We use gcc as linker to generate Axel binary because of libgcc. 17 | # So we build rustc using gcc in development. 18 | # gcc options 19 | # --build-id=none is very important in order to do multiboot. 20 | # The reason is described in this link http://stackoverflow.com/questions/27744126/grub-multiboot-header-not-found 21 | # Briefly, this option suppresses creating a note section. 22 | # If this section exists, grub2 cannot find multiboot header because multiboot header is located after 8KB. 23 | # Also, Please pay attention the order of command line arguments. 24 | LD := gcc 25 | LD_FLAGS := -g -Og -flto -Wl,--no-relax -Wl,-nmagic -Wl,--build-id=none -Wl,--gc-sections -ffreestanding -fno-builtin -nostdlib -static 26 | LIBS := 27 | 28 | QEMU := qemu-system-x86_64 29 | QEMU_FLAGS := -monitor stdio -vga std -m 32 -boot order=dc -no-reboot -d int 30 | 31 | BOOT_DEPS := src/arch/x86_64/boot.asm 32 | -------------------------------------------------------------------------------- /config/arm-unknown-none-eabihf.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target" : "arm-unknown-none-eabihf", 3 | "target-endian" : "little", 4 | "target-pointer-width" : "32", 5 | "os" : "none", 6 | "target-env" : "eabi", 7 | "target_vendor" : "unknown", 8 | "arch" : "arm", 9 | "linker" : "arm-none-eabi-gcc", 10 | "pre-link-args" : "-Wl,--build-id=none -Wl,--gc-sections -static -ffreestanding -fno-builtin -nostdlib -nostartfiles -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s", 11 | "post-link-args" : "-lgcc", 12 | "cpu" : "arm1176jzf-s", 13 | "dynamic-linking" : false, 14 | "executables" : false, 15 | "code-model" : "kernel", 16 | "no-compiler-rt" : true, 17 | "no-default-libraries" : true 18 | } 19 | -------------------------------------------------------------------------------- /config/axel.gdb: -------------------------------------------------------------------------------- 1 | target remote localhost:1234 2 | symbol-file axel.sym 3 | 4 | set disassembly-flavor intel 5 | set history save on 6 | set history size 10000 7 | set history filename .gdb_history 8 | 9 | tui enable 10 | layout src 11 | layout regs 12 | tabset 4 13 | refresh 14 | -------------------------------------------------------------------------------- /config/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "Axel" { 5 | GRUB_GFXMODE=auto 6 | insmod vbe 7 | multiboot2 /boot/axel.bin 8 | boot 9 | } 10 | -------------------------------------------------------------------------------- /config/i386-unknown-axel.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target" : "i386-unknown-axel", 3 | "cpu" : "i386", 4 | "target-endian" : "little", 5 | "target-pointer-width" : "32", 6 | "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", 7 | "os" : "axel", 8 | "target-env" : "gnu", 9 | "target_vendor" : "unknown", 10 | "arch" : "x86", 11 | "linker" : "gcc -m32", 12 | "pre-link-args" : "-Wl,--build-id=none -Wl,--gc-sections -static -ffreestanding -fno-builtin -nostdlib -nostartfiles -march=i386", 13 | "post-link-args" : "-lgcc", 14 | "dynamic-linking" : false, 15 | "executables" : false, 16 | "code-model" : "kernel", 17 | "no-compiler-rt" : true, 18 | "no-default-libraries" : true 19 | } 20 | -------------------------------------------------------------------------------- /config/x86_64-unknown-axel.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "x86_64", 3 | "cpu": "x86-64", 4 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 5 | "disable-redzone": true, 6 | "eliminate-frame-pointer": false, 7 | "env": "", 8 | "executables": true, 9 | "has-elf-tls": true, 10 | "is-builtin": false, 11 | "linker-flavor": "gcc", 12 | "linker-is-gnu": true, 13 | "llvm-target": "x86_64-unknown-axel", 14 | "max-atomic-width": 64, 15 | "os": "axel", 16 | "panic-strategy": "abort", 17 | "pre-link-args": { 18 | "gcc": [ 19 | "-g", 20 | "-Og", 21 | "-m64", 22 | "-static", 23 | "-nostdlib", 24 | "-flto", 25 | "-ffreestanding", 26 | "-fno-builtin", 27 | "-Wl,-nmagic", 28 | "-Wl,--build-id=none", 29 | "-Wl,--gc-sections", 30 | "-Wl,-z,noexecstack" 31 | ] 32 | }, 33 | "relocation-model": "static", 34 | "stack-probes": true, 35 | "target-endian": "little", 36 | "target-pointer-width": "64", 37 | "target-c-int-width": "32", 38 | "vendor": "unknown", 39 | "code-model" : "large" 40 | } 41 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 256 2 | -------------------------------------------------------------------------------- /src/arch/arm11/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # @file Makefile 3 | # @brief Building the part of Axel for arm11. 4 | # @author mopp 5 | # @version 0.2 6 | ############################################################# 7 | 8 | .PHONY: all clean 9 | 10 | 11 | boot.o: boot.S $(MAKEFILE) 12 | $(AS) $(AS_FLAGS) -c $< -o $@ 13 | 14 | clean: 15 | $(RM) *.o *.lst 16 | -------------------------------------------------------------------------------- /src/arch/arm11/boot.S: -------------------------------------------------------------------------------- 1 | /* 2 | * vim:ft=gas: 3 | * @brief Booting kernel main routine. 4 | * @author mopp 5 | * @version 0.2 6 | * @date 2015-12-10 7 | */ 8 | 9 | /* 10 | * Entry point for the kernel. 11 | * r15 -> should begin execution at 0x8000. 12 | * r0 -> 0x00000000 13 | * r1 -> 0x00000C42 14 | * r2 -> 0x00000100 - start of ATAGS 15 | * preserve these registers as argument for kernel_main 16 | */ 17 | .section .boot_kernel 18 | .globl boot_kernel 19 | boot_kernel: 20 | /* Setup the stack. */ 21 | mov sp, #0x8000 22 | 23 | /* b blink_led */ 24 | 25 | /* Clear out bss. */ 26 | ldr r4, =LD_KERNEL_BSS_BEGIN 27 | ldr r9, =LD_KERNEL_BSS_END 28 | mov r5, #0 29 | mov r6, #0 30 | mov r7, #0 31 | mov r8, #0 32 | b 2f 33 | 34 | 1: 35 | /* Store multiple at r4. */ 36 | stmia r4!, {r5-r8} 37 | 38 | 2: 39 | cmp r4, r9 40 | blo 1b 41 | 42 | ldr r3, =main 43 | blx r3 44 | 45 | halt: 46 | wfe 47 | b halt 48 | 49 | 50 | /* Only for Raspberry Pi B+ and zero. */ 51 | /* 52 | blink_led: 53 | ldr r0,=0x20200000 54 | mov r1,#1 55 | lsl r1,#21 56 | str r1,[r0, #0x10] 57 | 58 | 1: 59 | mov r1,#1 60 | lsl r1,#15 61 | str r1,[r0, #0x2C] 62 | 63 | mov r2,#0x90000 64 | 2: 65 | sub r2,#1 66 | cmp r2,#0 67 | bne 2b 68 | 69 | mov r1,#1 70 | lsl r1,#15 71 | str r1,[r0, #0x20] 72 | 73 | mov r2,#0x90000 74 | 3: 75 | sub r2,#1 76 | cmp r2,#0 77 | bne 3b 78 | 79 | b 1b 80 | */ 81 | -------------------------------------------------------------------------------- /src/arch/arm11/link.ld: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief Linker script to link OS binary. 3 | * @author mopp 4 | * @version 0.2 5 | * @date 2015-12-11 6 | * 7 | * Please refer to documents in below link. 8 | * https://sourceware.org/binutils/docs/ld/index.html 9 | */ 10 | 11 | 12 | SECTIONS 13 | { 14 | /* 15 | * Set kernel load address to location counter. 16 | * The kernel will be load at this address by bootloader. 17 | */ 18 | . = 0x8000; 19 | 20 | /* Set OS entry point. */ 21 | ENTRY(boot_kernel) 22 | 23 | /* 24 | * Each section will be aligned on a 4096. 25 | * This alignment means page size. 26 | */ 27 | .text ALIGN(4096): 28 | { 29 | /* 30 | * If linker option `--gc-sections` is passed, linker eliminates unused section (e.g., never referenced section). 31 | * However, multiboot header should not be removed and located at head. 32 | * So, KEEP is used to keep these sections. 33 | */ 34 | KEEP(*(.boot_kernel)) 35 | *(.text*) 36 | } 37 | 38 | .data ALIGN(4096): 39 | { 40 | *(.data*) 41 | } 42 | 43 | .bss ALIGN(4096): 44 | { 45 | LD_KERNEL_BSS_BEGIN = .; 46 | *(.bss*) 47 | LD_KERNEL_BSS_END = .; 48 | } 49 | 50 | .rodata ALIGN(4096): 51 | { 52 | *(.rodata*) 53 | } 54 | 55 | /* 56 | * This section is unused section. 57 | * But gcc library requires this section. 58 | * http://embdev.net/topic/282936 59 | * http://stackoverflow.com/questions/9752000/exidx-start-and-exidx-end-what-do-they-do 60 | */ 61 | __exidx_start = .; 62 | .ARM.exidx ALIGN(4096): 63 | { 64 | KEEP(*(.ARM.exidx* .gnu.linkonce.armexidx.*)) 65 | } 66 | __exidx_end = .; 67 | .rel.plt : { *(.rel.plt) } 68 | } 69 | -------------------------------------------------------------------------------- /src/arch/arm11/mod.rs: -------------------------------------------------------------------------------- 1 | //! Currently, these module for ARM architecture has been developed for Raspberry Pi Zero. 2 | //! This Pi's CPU is BCM2835. 3 | //! References: 4 | //! (RASPBERRY PI HARDWARE)[https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md] 5 | //! (RPi Hub)[http://elinux.org/RPi_Hub] 6 | //! (RPi BCM2835 GPIOs)[http://elinux.org/RPi_BCM2835_GPIOs] 7 | //! (BCM2835 datasheet errata)[http://elinux.org/BCM2835_datasheet_errata] 8 | 9 | mod peripheral; 10 | 11 | pub fn init() 12 | { 13 | peripheral::init(); 14 | } 15 | -------------------------------------------------------------------------------- /src/arch/arm11/peripheral/addr.rs: -------------------------------------------------------------------------------- 1 | //! This module provides peripheral addresses based on BCM2835. 2 | 3 | extern crate core; 4 | 5 | #[allow(dead_code)] 6 | #[repr(usize)] 7 | pub enum Addr { 8 | Base, 9 | 10 | TimerCs, 11 | TimerClo, 12 | TimerChi, 13 | TimerC0, 14 | TimerC1, 15 | TimerC2, 16 | TimerC3, 17 | 18 | GpioBase, 19 | GpioGpfSel0, // GPIO Function Select 0 to 5, read and write. 20 | GpioGpfSel1, 21 | GpioGpfSel2, 22 | GpioGpfSel3, 23 | GpioGpfSel4, 24 | GpioGpfSel5, 25 | GpioGpSet0, // GPIO Pin Output Set 0 to 1, only write. 26 | GpioGpSet1, 27 | GpioGpClr0, // GPIO Pin Output Clear 0 to 1, only write. 28 | GpioGpClr1, 29 | GpioGpLev0, 30 | GpioGpLev1, 31 | 32 | Spi0Base, 33 | Spi0RegisterCs, 34 | Spi0RegisterFifo, 35 | Spi0RegisterClk, 36 | Spi0RegisterDlen, 37 | Spi0RegisterLtoh, 38 | Spi0RegisterDc, 39 | 40 | AuxIrq, // Auxiliary Interrupt status size: 3 41 | AuxEnables, // Auxiliary enables size: 3 42 | AuxMuIoReg, // Mini Uart I/O Data size: 8 43 | AuxMuIerReg, // Mini Uart Interrupt Enable size: 8 44 | AuxMuIirReg, // Mini Uart Interrupt Identify size: 8 45 | AuxMuLcrReg, // Mini Uart Line Control size: 8 46 | AuxMuMcrReg, // Mini Uart Modem Control size: 8 47 | AuxMuLsrReg, // Mini Uart Line Status size: 8 48 | AuxMuMsrReg, // Mini Uart Modem Status size: 8 49 | AuxMuScratch, // Mini Uart Scratch size: 8 50 | AuxMuCntlReg, // Mini Uart Extra Control size: 8 51 | AuxMuStatReg, // Mini Uart Extra Status size: 32 52 | AuxMuBaudReg, // Mini Uart Baudrate size: 16 53 | AuxSpi1Cntl0Reg, // SPI1 Control register0 size: 32 54 | AuxSpi1Cntl1Reg, // SPI1 Control register1 size: 8 55 | AuxSpi1StatReg, // SPI1 Status size: 32 56 | AuxSpi1IoReg, // SPI1 Data size: 32 57 | AuxSpi1PeekReg, // SPI1 Peek size: 16 58 | AuxSpi2Cntl0Reg, // SPI2 Control register0 size: 32 59 | AuxSpi2Cntl1Reg, // SPI2 Control register1 size: 8 60 | AuxSpi2StatReg, // SPI2 Status size: 32 61 | AuxSpi2IoReg, // SPI2 Data size: 32 62 | AuxSpi2PeekReg, // SPI2 Peek size: 16 63 | } 64 | 65 | 66 | impl Addr { 67 | pub fn to_usize(self) -> usize 68 | { 69 | let base: usize; 70 | if cfg!(any(RPi_a, RPi_aplus, RPi_bplus, RPi_zero)) { 71 | base = 0x20000000; 72 | } else { 73 | base = 0x3F000000; 74 | } 75 | 76 | let timer_base = base + 0x00003000; 77 | let gpio_base = base + 0x00200000; 78 | let spi_base = base + 0x00204000; 79 | let aux_base = base + 0x00215000; 80 | match self { 81 | Addr::Base => base, 82 | Addr::TimerCs => timer_base, 83 | Addr::TimerClo => timer_base + 0x04, 84 | Addr::TimerChi => timer_base + 0x08, 85 | Addr::TimerC0 => timer_base + 0x0C, 86 | Addr::TimerC1 => timer_base + 0x10, 87 | Addr::TimerC2 => timer_base + 0x14, 88 | Addr::TimerC3 => timer_base + 0x18, 89 | Addr::GpioBase => gpio_base, 90 | Addr::GpioGpfSel0 => gpio_base + 0x00, 91 | Addr::GpioGpfSel1 => gpio_base + 0x04, 92 | Addr::GpioGpfSel2 => gpio_base + 0x08, 93 | Addr::GpioGpfSel3 => gpio_base + 0x0C, 94 | Addr::GpioGpfSel4 => gpio_base + 0x10, 95 | Addr::GpioGpfSel5 => gpio_base + 0x14, 96 | Addr::GpioGpSet0 => gpio_base + 0x1C, 97 | Addr::GpioGpSet1 => gpio_base + 0x20, 98 | Addr::GpioGpClr0 => gpio_base + 0x28, 99 | Addr::GpioGpClr1 => gpio_base + 0x2C, 100 | Addr::GpioGpLev0 => gpio_base + 0x34, 101 | Addr::GpioGpLev1 => gpio_base + 0x38, 102 | Addr::Spi0Base => spi_base, 103 | Addr::Spi0RegisterCs => spi_base + 0x00, 104 | Addr::Spi0RegisterFifo => spi_base + 0x04, 105 | Addr::Spi0RegisterClk => spi_base + 0x08, 106 | Addr::Spi0RegisterDlen => spi_base + 0x0C, 107 | Addr::Spi0RegisterLtoh => spi_base + 0x10, 108 | Addr::Spi0RegisterDc => spi_base + 0x14, 109 | Addr::AuxIrq => aux_base + 0x00, 110 | Addr::AuxEnables => aux_base + 0x04, 111 | Addr::AuxMuIoReg => aux_base + 0x40, 112 | Addr::AuxMuIerReg => aux_base + 0x44, 113 | Addr::AuxMuIirReg => aux_base + 0x48, 114 | Addr::AuxMuLcrReg => aux_base + 0x4C, 115 | Addr::AuxMuMcrReg => aux_base + 0x50, 116 | Addr::AuxMuLsrReg => aux_base + 0x54, 117 | Addr::AuxMuMsrReg => aux_base + 0x58, 118 | Addr::AuxMuScratch => aux_base + 0x5C, 119 | Addr::AuxMuCntlReg => aux_base + 0x60, 120 | Addr::AuxMuStatReg => aux_base + 0x64, 121 | Addr::AuxMuBaudReg => aux_base + 0x68, 122 | Addr::AuxSpi1Cntl0Reg => aux_base + 0x80, 123 | Addr::AuxSpi1Cntl1Reg => aux_base + 0x84, 124 | Addr::AuxSpi1StatReg => aux_base + 0x88, 125 | Addr::AuxSpi1IoReg => aux_base + 0x90, 126 | Addr::AuxSpi1PeekReg => aux_base + 0x94, 127 | Addr::AuxSpi2Cntl0Reg => aux_base + 0xC0, 128 | Addr::AuxSpi2Cntl1Reg => aux_base + 0xC4, 129 | Addr::AuxSpi2StatReg => aux_base + 0xC8, 130 | Addr::AuxSpi2IoReg => aux_base + 0xD0, 131 | Addr::AuxSpi2PeekReg => aux_base + 0xD4, 132 | } 133 | } 134 | 135 | 136 | #[allow(dead_code)] 137 | pub fn from_usize(v: usize) -> Addr 138 | { 139 | unsafe { core::mem::transmute(v) } 140 | } 141 | 142 | 143 | pub fn load(self) -> T 144 | { 145 | let ptr = self.to_usize() as *mut T; 146 | unsafe { 147 | core::intrinsics::volatile_load(ptr) 148 | } 149 | } 150 | 151 | 152 | pub fn store(self, value: T) 153 | { 154 | let ptr = self.to_usize() as *mut T; 155 | unsafe { 156 | core::intrinsics::volatile_store(ptr, value); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/arch/arm11/peripheral/gpio.rs: -------------------------------------------------------------------------------- 1 | //! GPIO module. 2 | //! http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/ 3 | //! https://www.raspberrypi.org/documentation//hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf 4 | //! LED ON, Zero -> CLR 5 | //! , Pi 1 B+ -> Set 6 | 7 | use super::addr::Addr; 8 | extern crate core; 9 | 10 | 11 | #[allow(dead_code)] 12 | pub enum Function { 13 | Input = 0b000, 14 | Output = 0b001, 15 | Alternate0 = 0b100, 16 | Alternate1 = 0b101, 17 | Alternate2 = 0b110, 18 | Alternate3 = 0b111, 19 | Alternate4 = 0b011, 20 | Alternate5 = 0b010, 21 | } 22 | 23 | 24 | pub enum Output { 25 | Clear, 26 | Set, 27 | } 28 | 29 | 30 | #[allow(dead_code)] 31 | pub enum Pin { 32 | #[cfg(any(RPi_a, RPi_aplus))] 33 | OkLed = 16, 34 | #[cfg(any(RPi_zero, RPi_bplus, RPi2_b))] 35 | OkLed = 47, 36 | 37 | Spi0Ce1N = 7, 38 | Spi0Ce0N = 8, 39 | Spi0Miso = 9, 40 | Spi0Mosi = 10, 41 | Spi0Sclk = 11, 42 | 43 | // ILI 9340 (pitft 2.2) GPIO PIN 44 | Ili9340Dc = 25, 45 | Ili9340Rst = 18, 46 | 47 | Button1 = 17, 48 | Button2 = 22, 49 | Button3 = 23, 50 | Button4 = 27, 51 | } 52 | 53 | 54 | pub fn set_pin_function(pin: Pin, func: Function) 55 | { 56 | let pin_number = pin as usize; 57 | let gpf_sel_number = pin_number / 10; 58 | let gpf_sel_addr = Addr::GpioBase.to_usize() + (gpf_sel_number * 0x4); 59 | let gpf_sel_ptr = gpf_sel_addr as *mut u32; 60 | let shift = 3 * (pin_number % 10); 61 | let set_value = (func as u32) << shift; 62 | 63 | unsafe { 64 | let current_value = core::intrinsics::volatile_load(gpf_sel_ptr); 65 | let new_value = (current_value & !(7 << shift)) | set_value; 66 | core::intrinsics::volatile_store(gpf_sel_ptr, new_value); 67 | } 68 | } 69 | 70 | 71 | pub fn write_output_pin(pin: Pin, mode: Output) 72 | { 73 | let pin_number = pin as usize; 74 | let reg_addr = 75 | if pin_number <= 31 { 76 | match mode { 77 | Output::Set => Addr::GpioGpSet0.to_usize(), 78 | Output::Clear => Addr::GpioGpClr0.to_usize(), 79 | } 80 | } else { 81 | match mode { 82 | Output::Set => Addr::GpioGpSet1.to_usize(), 83 | Output::Clear => Addr::GpioGpClr1.to_usize(), 84 | } 85 | }; 86 | let reg_ptr = reg_addr as *mut u32; 87 | let shift = pin_number % 32; 88 | 89 | unsafe { 90 | let current_value = core::intrinsics::volatile_load(reg_ptr); 91 | let new_value = (current_value & !(1 << shift)) | (1 << shift); 92 | core::intrinsics::volatile_store(reg_ptr, new_value); 93 | } 94 | } 95 | 96 | 97 | pub fn read_pin_level(pin: Pin) -> bool { 98 | let pin_number = pin as usize; 99 | let shift = pin_number % 32; 100 | 101 | let value; 102 | if pin_number <= 31 { 103 | value = Addr::GpioGpLev0.load::() 104 | } else { 105 | value = Addr::GpioGpLev1.load::() 106 | }; 107 | 108 | let level = (value >> shift) & 0x1; 109 | if level == 0 { 110 | false 111 | } else { 112 | true 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/arch/arm11/peripheral/mod.rs: -------------------------------------------------------------------------------- 1 | mod addr; 2 | mod gpio; 3 | mod spi; 4 | mod timer; 5 | 6 | pub fn init() 7 | { 8 | spi::init_spi0(); 9 | 10 | // Blinking LED. 11 | gpio::set_pin_function(gpio::Pin::OkLed, gpio::Function::Output); 12 | 13 | let mut switch = true; 14 | loop { 15 | gpio::write_output_pin(gpio::Pin::OkLed, gpio::Output::Clear); 16 | timer::wait(500); 17 | gpio::write_output_pin(gpio::Pin::OkLed, gpio::Output::Set); 18 | timer::wait(500); 19 | 20 | if switch == true { 21 | spi::ili9340::draw_girl(); 22 | switch = false; 23 | } else { 24 | spi::ili9340::fill_screen(0x0000); 25 | spi::ili9340::draw_aa(); 26 | switch = true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/arch/arm11/peripheral/timer.rs: -------------------------------------------------------------------------------- 1 | use super::addr::Addr; 2 | 3 | 4 | pub fn wait_usec(usec: u64) 5 | { 6 | let time_begin = get_time_counter_value(); 7 | loop { 8 | let time_current = get_time_counter_value(); 9 | if usec <= (time_current - time_begin) { 10 | break; 11 | } 12 | } 13 | } 14 | 15 | 16 | pub fn wait(msec: u64) 17 | { 18 | wait_usec(msec * 1000); 19 | } 20 | 21 | 22 | 23 | fn get_time_counter_value() -> u64 24 | { 25 | let lo = Addr::TimerClo.load::() as u64; 26 | let hi = Addr::TimerChi.load::() as u64; 27 | (hi << 32) | lo 28 | } 29 | -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains all codes depending on the architecture to abstract these codes. 2 | use crate::graphic; 3 | use crate::memory::address::VirtualAddress; 4 | use crate::memory::Error as MemoryError; 5 | use failure::Fail; 6 | 7 | // TODO: introduce ErrorKind. 8 | #[derive(Fail, Debug)] 9 | pub enum Error { 10 | #[fail(display = "No memory map is in multiboot_info")] 11 | NoMemoryMap, 12 | #[fail(display = "Memory initialization faild: {}", _0)] 13 | MemoryInitializationFailed(MemoryError), 14 | } 15 | 16 | impl From for Error { 17 | fn from(e: MemoryError) -> Error { 18 | Error::MemoryInitializationFailed(e) 19 | } 20 | } 21 | 22 | /// Common interfaces between architectures. 23 | pub trait Initialize { 24 | fn init(argv: &[VirtualAddress]) -> Result<(), Error>; 25 | fn obtain_kernel_console() -> Option>; 26 | } 27 | 28 | macro_rules! with_arch { 29 | ($arch: ident) => { 30 | mod $arch; 31 | pub use self::$arch::Initializer; 32 | pub use self::$arch::Thread; 33 | }; 34 | } 35 | 36 | #[cfg(target_arch = "arm11")] 37 | with_arch!(arm11); 38 | 39 | #[cfg(target_arch = "x86_32")] 40 | with_arch!(x86_32); 41 | 42 | #[cfg(target_arch = "x86_64")] 43 | with_arch!(x86_64); 44 | -------------------------------------------------------------------------------- /src/arch/x86_32/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # @file Makefile 3 | # @brief Building the part of Axel for x86_32. 4 | # @author mopp 5 | # @version 0.2 6 | ############################################################# 7 | 8 | .PHONY: all clean 9 | 10 | 11 | boot.o: boot.asm $(MAKEFILE) 12 | nasm -f elf32 -l $*.lst -o $@ $< 13 | 14 | clean: 15 | $(RM) *.o *.lst 16 | -------------------------------------------------------------------------------- /src/arch/x86_32/boot.asm: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; vim:ft=nasm:foldmethod=marker 3 | ; @file 4 | ; @brief Booting kernel main routine. 5 | ; @author mopp 6 | ; @version 0.2 7 | ; @date 2015-09-25 8 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 9 | bits 32 10 | 11 | 12 | ; Multiboot header section. 13 | ; This header is read by multiboot bootstraps loader (E.g., grub2). 14 | ; And based on "The Multiboot Specification version 0.6.96" 15 | ; {{{ 16 | MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 ; The magic number identifying the header 17 | MULTIBOOT_PAGE_ALIGN_BIT equ 0x00000001 ; Align all boot modules on i386 page (4KB) boundaries. 18 | MULTIBOOT_MEMORY_INFO_BIT equ 0x00000002 ; Must pass memory information to OS. 19 | MULTIBOOT_VIDEO_MODE_BIT equ 0x00000004 ; Must pass video information to OS. 20 | MULTIBOOT_BOOTLOADER_MAGIC equ 0x2BADB002 ; This must be set in %eax by bootloader. 21 | 22 | section .multiboot_header 23 | align 4 24 | dd MULTIBOOT_HEADER_MAGIC 25 | dd (MULTIBOOT_PAGE_ALIGN_BIT | MULTIBOOT_MEMORY_INFO_BIT | MULTIBOOT_VIDEO_MODE_BIT) 26 | dd -(MULTIBOOT_HEADER_MAGIC + (MULTIBOOT_PAGE_ALIGN_BIT | MULTIBOOT_MEMORY_INFO_BIT | MULTIBOOT_VIDEO_MODE_BIT)) 27 | 28 | ; Address field (this fields are required in a.out format) 29 | dd $0 30 | dd $0 31 | dd $0 32 | dd $0 33 | dd $0 34 | 35 | ; Graphic field 36 | dd 0 37 | dd 640 38 | dd 480 39 | dd 32 40 | ; }}} 41 | 42 | 43 | ; Boot kernel section 44 | ; This section invoked by bootloader. 45 | ; Then, This calls kernel main routine. 46 | ; {{{ 47 | section .boot_kernel 48 | align 4 49 | global boot_kernel 50 | extern main 51 | extern LD_KERNEL_BSS_BEGIN 52 | extern LD_KERNEL_BSS_END 53 | boot_kernel: 54 | ; NOTE that We cannot use eax and ebx. 55 | ; Because eax and ebx has multiboot magic number and multiboot info struct addr. 56 | cli 57 | 58 | ; check multiboot magic number. 59 | ; If mismatched, goto sleep loop. 60 | cmp eax, MULTIBOOT_BOOTLOADER_MAGIC 61 | jne sleep 62 | 63 | ; Clean up BSS section. 64 | mov edi, LD_KERNEL_BSS_BEGIN 65 | mov ecx, LD_KERNEL_BSS_END 66 | sub ecx, edi 67 | xor eax, eax 68 | rep stosb 69 | 70 | ; Set global descripter table register. 71 | ; GDTR has 32bit base address of GDT and 16bit table limit. 72 | ; So, lgdt loads 6bytes data from memory. 73 | lgdt [gdtr_info] 74 | jmp 0x08:.change_cs 75 | .change_cs: 76 | mov ax, 0x10 77 | mov es, ax 78 | mov ds, ax 79 | mov fs, ax 80 | mov gs, ax 81 | mov ss, ax 82 | 83 | ; Set kernel stack. 84 | mov esp, kernel_init_stack_top 85 | 86 | ; ebx is pointer to multiboot info struct. 87 | push ebx 88 | ; set ebx as pointer to argument array. 89 | push esp 90 | 91 | inc eax 92 | push eax 93 | 94 | call main 95 | 96 | sleep: 97 | hlt 98 | jmp sleep 99 | ; }}} 100 | 101 | 102 | ; Read only data section 103 | ; {{{ 104 | section .rodata 105 | gdtr_info: 106 | dw 8 * 6 107 | dd axel_gdt 108 | axel_gdt: 109 | dd 0x00000000 ; NULL_SEGMENT_INDEX (0) 110 | dd 0x00000000 111 | dd 0x0000FFFF ; KERNEL_CODE_SEGMENT_INDEX (1) 112 | dd 0x00CF9A00 113 | dd 0x0000FFFF ; KERNEL_DATA_SEGMENT_INDEX (2) 114 | dd 0x00CF9300 115 | dd 0x0000FFFF ; USER_CODE_SEGMENT_INDEX (3) 116 | dd 0x00CFFA00 117 | dd 0x0000FFFF ; USER_DATA_SEGMENT_INDEX (4) 118 | dd 0x00CFF300 119 | dd 0x00000000 ; KERNEL_TSS_SEGMENT_INDEX (5) 120 | dd 0x00000000 121 | ; }}} 122 | 123 | 124 | ; BSS (Block Started by Symbol) section 125 | ; This allocate initial kernel stack witch is 4KB. 126 | ; {{{ 127 | section .bss 128 | align 4 129 | KERNEL_INIT_STACK_SIZE equ 0x1000 130 | kernel_init_stack_bottom: 131 | resb KERNEL_INIT_STACK_SIZE 132 | kernel_init_stack_top: 133 | ; }}} 134 | -------------------------------------------------------------------------------- /src/arch/x86_32/link.ld: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief Linker script to link OS binary. 3 | * @author mopp 4 | * @version 0.2 5 | * @date 2015-12-11 6 | * 7 | * Please refer to documents in below link. 8 | * https://sourceware.org/binutils/docs/ld/index.html 9 | */ 10 | 11 | 12 | /* 13 | * Set output file format. 14 | * You can see available list in your environment by `objdump --info`. 15 | */ 16 | OUTPUT_FORMAT(elf32-i386) 17 | 18 | SECTIONS 19 | { 20 | /* 21 | * Set kernel load address to location counter. 22 | * The kernel will be load at this address by bootloader. 23 | */ 24 | . = 0x00100000; 25 | 26 | LD_KERNEL_BEGIN = .; 27 | 28 | /* Set OS entry point. */ 29 | ENTRY(boot_kernel) 30 | 31 | /* 32 | * Each section will be aligned on a 4096. 33 | * This alignment means page size. 34 | */ 35 | .text ALIGN(4096): 36 | { 37 | /* 38 | * If linker option `--gc-sections` is passed, linker eliminates unused section (e.g., never referenced section). 39 | * However, multiboot header should not be removed and located at head. 40 | * So, KEEP is used to keep these sections. 41 | */ 42 | KEEP(*(.multiboot_header)) 43 | KEEP(*(.boot_kernel)) 44 | *(.text*) 45 | } 46 | 47 | .data ALIGN(4096): 48 | { 49 | *(.data*) 50 | } 51 | 52 | .bss ALIGN(4096): 53 | { 54 | LD_KERNEL_BSS_BEGIN = .; 55 | *(.bss*) 56 | LD_KERNEL_BSS_END = .; 57 | } 58 | 59 | .rodata ALIGN(4096): 60 | { 61 | *(.rodata*) 62 | } 63 | 64 | LD_KERNEL_END = .; 65 | LD_KERNEL_SIZE = LD_KERNEL_END - LD_KERNEL_BEGIN; 66 | } 67 | -------------------------------------------------------------------------------- /src/arch/x86_32/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate multiboot; 2 | 3 | use axel_context; 4 | use core::mem; 5 | use core::slice; 6 | use graphic; 7 | use graphic::*; 8 | 9 | pub fn init(argv: &[usize]) 10 | { 11 | // let x = 640; 12 | // let y = 480; 13 | // for i in 0..(x * y){ 14 | // let addr = 0xFD000000 + (i * 4); 15 | // // unsafe {*(addr as *mut u32) = 0xFF000000}; // rsvd 16 | // // unsafe {*(addr as *mut u32) = 0x00FF0000}; // R 17 | // // unsafe {*(addr as *mut u32) = 0x0000FF00}; // G 18 | // // unsafe {*(addr as *mut u32) = 0x000000FF}; // B 19 | // unsafe {*(addr as *mut u32) = 0x00D7003A}; 20 | // } 21 | { 22 | let BACKGROUND_COLOR = graphic::Color::Code(0xD7003A); 23 | let bit_info = graphic::ColorBitInfo { 24 | size_r: 8, 25 | size_g: 8, 26 | size_b: 8, 27 | size_rsvd: 8, 28 | pos_r: 16, 29 | pos_g: 8, 30 | pos_b: 0, 31 | pos_rsvd: 24, 32 | }; 33 | let mut display = graphic::GraphicalDisplay::new(0xFD000000, graphic::Position(640, 480), bit_info); 34 | display.fill_display(&BACKGROUND_COLOR); 35 | 36 | let base_x = 0; 37 | let base_y = 480 - 27; 38 | display.fill_area_by_size(Position(0, base_y), Position(640, 480), &Color::Code(0xC6C6C6)); 39 | display.fill_area_by_size(Position( 3, 3 + base_y), Position(60, 1), &Color::Code(0xFFFFFF)); // above edge. 40 | display.fill_area_by_size(Position( 3, 23 + base_y), Position(60, 2), &Color::Code(0x848484)); // below edge. 41 | display.fill_area_by_size(Position( 3, 3 + base_y), Position(1, 20), &Color::Code(0xFFFFFF)); // left edge. 42 | display.fill_area_by_size(Position(61, 4 + base_y), Position(1, 20), &Color::Code(0x848484)); // right edge. 43 | display.fill_area_by_size(Position(62, 4 + base_y), Position(1, 20), &Color::Code(0x000001)); // right edge. 44 | } 45 | 46 | // for i in (x * y - x)..(x * y){ 47 | // let addr = 0xFD000000 + (i * 4); 48 | // unsafe {*(addr as *mut u32) = 0x00FF0000}; 49 | // } 50 | 51 | // { 52 | // const TEXT_MODE_VRAM_ADDR: usize = 0xB8000; 53 | // const TEXT_MODE_WIDTH: usize = 80; 54 | // const TEXT_MODE_HEIGHT: usize = 25; 55 | // let mut display = graphic::CharacterDisplay::new(TEXT_MODE_VRAM_ADDR, graphic::Position(TEXT_MODE_WIDTH, TEXT_MODE_HEIGHT)); 56 | // display.clear_screen(); 57 | // unsafe { axel_context::AXEL_CONTEXT.kernel_output_device = Some(display); } 58 | // println!("Start Axel."); 59 | // } 60 | 61 | let mboot = unsafe { 62 | let multiboot_info_addr = argv[0] as multiboot::PAddr; 63 | let wraped = multiboot::Multiboot::new(multiboot_info_addr, paddr_to_slice); 64 | match wraped { 65 | None => panic!("No multiboot info."), 66 | Some(mboot) => mboot, 67 | } 68 | }; 69 | 70 | println!("HLT"); 71 | loop { 72 | unsafe { 73 | asm!("hlt"); 74 | } 75 | } 76 | } 77 | 78 | 79 | // Translate a physical memory address and size into a slice 80 | pub fn paddr_to_slice<'a>(ptr_addr: multiboot::PAddr, sz: usize) -> Option<&'a [u8]> 81 | { 82 | unsafe { 83 | // TODO 84 | // let ptr = mem::transmute(p + KERNEL_BASE); 85 | let ptr = mem::transmute(ptr_addr as usize); 86 | Some(slice::from_raw_parts(ptr, sz)) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/arch/x86_64/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # @file Makefile 3 | # @brief Building the part of Axel for x86_32. 4 | # @author mopp 5 | # @version 0.2 6 | ############################################################# 7 | 8 | 9 | boot.o: boot.asm $(MAKEFILE) 10 | nasm -g -f elf64 -l $*.lst -o $@ $< 11 | 12 | 13 | .PHONY: clean 14 | clean: 15 | $(RM) *.o *.lst 16 | -------------------------------------------------------------------------------- /src/arch/x86_64/boot.asm: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ; vim:ft=nasm:foldmethod=marker 3 | ; @brief 4 | ; Program to boot the kernel. 5 | ; This program have to be called from a bootloader complying multiboot2 spec. 6 | ; Reference: 7 | ; [Memory Map (x86)](http://wiki.osdev.org/Memory_Map_(x86)) 8 | ; [Setting Up Long Mode](http://wiki.osdev.org/Setting_Up_Long_Mode) 9 | ; [Intel(R) 64 and IA-32 Architectures Software Developer's Manual]() 10 | ; [Canonical form addresses](https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details) 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | 13 | bits 32 14 | 15 | ; This section is for the Multiboot header. 16 | ; It must be located in the first 32768 bytes of the OS binary. 17 | section .multiboot2 18 | ; {{{ 19 | 20 | ; The multiboot magic numbers. 21 | MULTIBOOT2_MAGIC equ 0xE85250D6 22 | MULTIBOOT2_EAX_MAGIC equ 0x36D76289 23 | 24 | ; @brief 25 | ; Multiboot header region. 26 | align 8 27 | multiboot2_begin: ; {{{ 28 | dd MULTIBOOT2_MAGIC ; Magic number 29 | dd 0 ; Architecture (i386 32-bit protected mode) 30 | dd multiboot2_end - multiboot2_begin ; Header length 31 | ; Checksum, it subtracts from 0x100000000 to avoid compiler warning. 32 | dd 0x100000000 - (MULTIBOOT2_MAGIC + 0 + (multiboot2_end - multiboot2_begin)) 33 | ; The tag for terminating. 34 | dw 0 ; type 35 | dw 0 ; flags 36 | dd 8 ; size 37 | multiboot2_end: 38 | ; }}} 39 | ; }}} 40 | 41 | 42 | 43 | ; This section is for configure the CPU. 44 | ; Before turning on the paging, we cannot use some instructions such as jmp and call, which want to use 64 bit addresses. 45 | ; Because we are in 32-bit protected mode still here. 46 | ; However, the symbols are linked at the higher kernel space (over 32-bit). 47 | ; Then, in order to use the instructions, this section is mapped to the load memory addresses only using linker script. 48 | section .text_boot_32bit 49 | ; {{{ 50 | 51 | ; This constant refers the virtual kernel address offset. 52 | ; When I tried to read this values using the linker script, an error `relocation truncated to fit: R_X86_64_32 against ~~~` was ommited. 53 | ; Therefore, I had to write the value directly here :( 54 | KERNEL_ADDR_VIRTUAL_OFFSET equ 0xFFFF800000000000 55 | 56 | ; @brief 57 | ; Axel starting function. 58 | global start_axel 59 | start_axel: 60 | ; {{{ 61 | ; Clear interrupt. 62 | cli 63 | 64 | ; Set temporal stack. 65 | ; 0x500 - 0x1000 is free to use. 66 | mov esp, 0x1000 67 | 68 | ; Store the pointer to the multiboot information struct. 69 | push ebx 70 | 71 | ; Check eax has the correct multiboot2 magic number. 72 | test eax, MULTIBOOT2_EAX_MAGIC 73 | jz boot_failure 74 | 75 | call is_cpuid_available 76 | 77 | call is_sse_available 78 | call enable_sse 79 | 80 | call is_long_mode_available 81 | call enter_compatibility_mode 82 | 83 | ; Load the pointer to the multiboot information struct. 84 | pop ebx 85 | 86 | call enter_64bit_mode 87 | 88 | jmp prelude_to_canonical_higher_harf 89 | ; }}} 90 | 91 | 92 | ; @brief 93 | ; If axel could not be execute, this function will be called. 94 | boot_failure: 95 | ; {{{ 96 | hlt 97 | jmp boot_failure 98 | ; }}} 99 | 100 | 101 | ; @brief 102 | ; Check the CPUID instruction available or not. 103 | ; The ID flag (bit 21) in the EFLAGS register indicates support for the CPUID instruction. 104 | ; If this flag can be modified, it refers this CPU has the CPUID. 105 | ; For more information, please refer "19.1 USING THE CPUID INSTRUCTION" in the Intel manual. 106 | is_cpuid_available: 107 | ; {{{ 108 | ; Store the original values in the EFLAGS. 109 | pushfd 110 | 111 | ; Set the invert value of the ID flag (bit 21). 112 | pushfd 113 | xor dword [esp], (1 << 21) 114 | popfd 115 | 116 | pushfd 117 | pop eax 118 | 119 | ; Compare the original EFLAGS and the current EFLAGS. 120 | xor eax, [esp] 121 | 122 | jz boot_failure 123 | 124 | ; Load the original EFLAGS. 125 | popfd 126 | 127 | ret 128 | ; }}} 129 | 130 | 131 | ; @brief 132 | ; Check long mode (IA-32e mode) available or not. 133 | ; Long mode is the name in the AMD64 and IA-32e mode is the name in the Intel 64. 134 | is_long_mode_available: 135 | ; {{{ 136 | ; Try to obtain Extended Function CPUID Information. 137 | mov eax, 0x80000000 138 | cpuid 139 | 140 | ; Now, eax has Maximum Input Value for Extended Function CPUID Information. 141 | ; If the maximum value is 0x80000000, it means this CPU don't have more extended functions. 142 | xor eax, 0x80000000 143 | jz boot_failure 144 | 145 | ; Check CPU extended functions. 146 | ; Bit 29 is 1 if Intel 64 Architecture is available. 147 | mov eax, 0x80000001 148 | cpuid 149 | 150 | test edx, (1 << 29) 151 | jz boot_failure 152 | 153 | ret 154 | ; }}} 155 | 156 | 157 | ; @brief 158 | ; Check SSE extension is supported or not 159 | is_sse_available: 160 | ; {{{ 161 | ; Try to obtain Feature Information. 162 | mov eax, 0x1 163 | cpuid 164 | 165 | ; Check SSE extension. 166 | test edx, (1 << 25) 167 | jz boot_failure 168 | 169 | ret 170 | ; }}} 171 | 172 | 173 | ; @brief 174 | ; Enable SSE extension. 175 | ; For more Information, Please refer 9.6 INITIALIZING SSE/SSE2/SSE3/SSSE3 EXTENSIONS. 176 | enable_sse: 177 | ; {{{ 178 | 179 | ; Clear EM bit and set MP bit in CR0 180 | mov eax, cr0 181 | and ax, 0xFFFB 182 | or ax, 0x2 183 | mov cr0, eax 184 | 185 | ; Set OSFXSR and OSXMMEXCPT bit in CR4. 186 | mov eax, cr4 187 | or ax, (3 << 9) 188 | mov cr4, eax 189 | 190 | ret 191 | ; }}} 192 | 193 | 194 | ; @brief 195 | ; Enter compatibility mode (submode of IA-32e mode). 196 | ; 1. Disable paging. (This is already done by the bootloader which complies with the multiboot2 specification.) 197 | ; 2. Enable physical-address extensions (PAE) by setting PAE bit in CR4. 198 | ; 3. Load CR3 with the physical base address of the Level 4 page map table (PML4). 199 | ; 4. Enable long mode (IA-32e mode) by setting IA32_EFER.LME = 1. 200 | ; 5. Enable paging. 201 | ; For more information, please refer 9.8.5 Initializing IA-32e Mode in the intel manual. 202 | enter_compatibility_mode: 203 | ; {{{ 204 | ; Enable PAE. 205 | mov eax, cr4 206 | or eax, 1 << 5 207 | mov cr4, eax 208 | 209 | ; Set the page struct address to CR3. 210 | ; 64-bit mode paging tables must be located in the first 4 GBytes of physical-address space prior to activating IA-32e mode. 211 | ; The memory region 0x00500 ~ 0x7FFFF can be used. 212 | mov edi, 0x1000 213 | mov cr3, edi 214 | 215 | ; Clean up the memories for the paging. 216 | xor eax, eax 217 | mov ecx, (0x4000 - 0x1000) / 4 218 | rep stosd 219 | 220 | ; Configure temporal page settings. 221 | ; Level4 Table/Entry - Page Map Level 4 Table/Entry 222 | ; Level3 Table/Entry - Page Directory Pointer Table/Entry 223 | ; Level2 Table/Entry - Page Directory Table/Entry 224 | ; Level1 Table/Entry - Page Table/Entry 225 | ; Each mapping region is 1GB for the kernel load address and the kernel virtual address. 226 | ; For more information, Please refer 4.5 IA-32E PAGING in the intel manual. 227 | 228 | ; Entries for the kernel load address. 229 | mov dword [0x1000 + 8 * 0], 0x00002003 ; Set the level4 entry. 230 | mov dword [0x2000 + 8 * 0], 0x00000083 ; Set the level3 entry 231 | 232 | ; Entries for the kernel virtual address. 233 | mov dword [0x1000 + 8 * 256], 0x00003003 ; Set the level4 entry. 234 | mov dword [0x3000 + 8 * 0], 0x00000083 ; Set the level3 entry. 235 | 236 | ; Entry for the recursive page mapping. 237 | mov dword [0x1000 + 8 * 511], 0x00001003 ; Set the level4 entry. 238 | 239 | ; Set the long mode bit in the EFER MSR. 240 | mov ecx, 0xC0000080 241 | rdmsr 242 | or eax, 1 << 8 243 | wrmsr 244 | 245 | ; Enable paging. 246 | mov eax, cr0 247 | or eax, 1 << 31 248 | mov cr0, eax 249 | 250 | ret 251 | ; }}} 252 | 253 | 254 | ; @brief Enter 64-bit mode (submode of IA-32e mode). 255 | ; Set GDT for 64-bit. 256 | ; Code segment-descriptor should be set correctly. 257 | ; For more information, please refer 9.8.5.3 64-bit Mode and Compatibility Mode Operation in the intel manual. 258 | enter_64bit_mode: 259 | ; {{{ 260 | ; Load 64-bit Global Descriptor Table Register (GDTR) 261 | mov eax, gdtr64 - KERNEL_ADDR_VIRTUAL_OFFSET 262 | lgdt [eax] 263 | 264 | ; Change the code segment register. 265 | jmp gdt64.descriptor_kernel_code:.change_segment_register 266 | 267 | .change_segment_register: 268 | ; Set the segment registers. 269 | mov ax, gdt64.descriptor_kernel_data 270 | mov ds, ax 271 | mov es, ax 272 | mov fs, ax 273 | mov gs, ax 274 | mov ss, ax 275 | 276 | ; 64-bit mode is already available here, 277 | ; So, the ret instruction try to load 8bytes (not 4bytes) from the stack to return. 278 | ; However, this function was called from 32-bit world. 279 | ; We have to load correctly 4bytes. 280 | mov eax, dword [esp] 281 | add esp, 4 282 | jmp eax 283 | ; }}} 284 | ; }}} 285 | 286 | 287 | 288 | bits 64 289 | 290 | ; This section is for 64-bit instruction, and it is located at the kernel load address. 291 | ; The purpose of this section is to jump canonical higher harf space because the address is 64-bit. 292 | section .text_boot_64bit 293 | ; {{{ 294 | 295 | ; @brief 296 | ; Jump via register. 297 | ; Because direct jump cause an error 'relocation truncated to fit: R_X86_64_PC32 against `.text_canonical_higher_harf'' 298 | prelude_to_canonical_higher_harf: 299 | ; {{{ 300 | mov rax, canonical_higher_harf 301 | 302 | ; Let's go to the canonical higher harf space :) 303 | jmp rax 304 | ; }}} 305 | ; }}} 306 | 307 | 308 | 309 | ; This section is mapped to the kernel virtual address. 310 | section .text_canonical_higher_harf 311 | ; {{{ 312 | 313 | ; 0x00007E00 - 0x0007FFFF is free region. 314 | ; See x86 memory map. 315 | FRAME_SIZE_BYTES equ 4096 316 | KERNEL_STACK_FRAME_COUNT equ 4 317 | KERNEL_STACK_ADDR_TOP equ 0x00070000 318 | KERNEL_STACK_ADDR_BOTTOM equ KERNEL_STACK_ADDR_TOP + (FRAME_SIZE_BYTES * KERNEL_STACK_FRAME_COUNT) - 1 319 | TASK_STATE_SEGMENT_ADDR equ KERNEL_STACK_ADDR_TOP - FRAME_SIZE_BYTES 320 | 321 | %if 0x0007FFFF < KERNEL_STACK_ADDR_BOTTOM 322 | %error "Kernel stack is out of range" 323 | %endif 324 | 325 | ; @brief 326 | ; Configure some information should be passed to the main function. 327 | ; Then, call the main function. 328 | canonical_higher_harf: 329 | ; {{{ 330 | ; Invalidate the entry for the kernel load address. 331 | ; It is never used in the long mode. 332 | mov dword [0x1000], 0 333 | invlpg [0] 334 | 335 | ; Reload GDTR 336 | ; The prevous GDTR is not 64 bit address. 337 | mov rax, gdtr64 338 | lgdt [rax] 339 | 340 | ; Load task register. 341 | mov ax, gdt64.descriptor_tss 342 | ltr ax 343 | 344 | mov rax, KERNEL_ADDR_VIRTUAL_OFFSET 345 | 346 | ; Set the kernel stask. 347 | ; The harf will be used as a guard page. 348 | mov rcx, KERNEL_STACK_ADDR_BOTTOM 349 | add rcx, rax 350 | mov rsp, rcx 351 | 352 | ; FIXME: 353 | ; Set the number of frames and address. 354 | push KERNEL_STACK_FRAME_COUNT + 1 355 | mov rcx, TASK_STATE_SEGMENT_ADDR 356 | push rcx 357 | 358 | ; rbx is pointer to multiboot info struct. 359 | add rbx, rax 360 | push rbx 361 | 362 | ; Set the arguments of the main function. 363 | mov rdi, 3 364 | mov rsi, rsp 365 | 366 | extern main 367 | call main 368 | 369 | .loop: 370 | hlt 371 | jmp .loop 372 | ; }}} 373 | ; }}} 374 | 375 | 376 | 377 | section .rodata 378 | ; {{{ 379 | 380 | ; Global descriptor table in 64-bit mode. 381 | ; Note that the CPU does not perform segment limit checks at runtime in 64-bit mode. 382 | ; `$ - gdt64` makes each label refers to segment selector value. 383 | align 8 384 | gdt64: 385 | ; {{{ 386 | .descriptor_null: equ $ - gdt64 387 | dd 0x00000000 388 | dd 0x00000000 389 | .descriptor_kernel_code: equ $ - gdt64 390 | ; Set 64-bit flag, present flag. 391 | ; Execute and read. 392 | ; DPL is 0. 393 | dd 0x00000000 394 | dd 0x00A09B00 395 | .descriptor_kernel_data: equ $ - gdt64 396 | ; Set 64-bit flag, present flag. 397 | ; Read and write. 398 | ; DPL is 0. 399 | dd 0x00000000 400 | dd 0x00C09300 401 | .descriptor_user_code: equ $ - gdt64 402 | ; DPL is 3. 403 | dd 0x00000000 404 | dd 0x00A0FB00 405 | .descriptor_user_data: equ $ - gdt64 406 | ; DPL is 3. 407 | dd 0x00000000 408 | dd 0x00C0F300 409 | .descriptor_tss: equ $ - gdt64 410 | dw 0x0064 411 | dw TASK_STATE_SEGMENT_ADDR & 0xFFFF 412 | db (TASK_STATE_SEGMENT_ADDR >> 16) & 0xFF 413 | db 10001001b 414 | dw (TASK_STATE_SEGMENT_ADDR >> 24) & 0xFFFF 415 | dd KERNEL_ADDR_VIRTUAL_OFFSET >> 32 416 | dd 0x00000000 417 | ; }}} 418 | 419 | ; Global descriptor table register in 64-bit mode. 420 | ; For more information, please refer 3.5.1 Segment Descriptor Tables in the intel manual. 421 | gdtr64: 422 | ; {{{ 423 | dw $ - gdt64 - 1 424 | dq gdt64 425 | ; }}} 426 | ; }}} 427 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt.rs: -------------------------------------------------------------------------------- 1 | /// Interrupt descriptor table module. 2 | use crate::memory::address::*; 3 | 4 | mod descriptor; 5 | mod handler; 6 | mod pic; 7 | mod pit; 8 | mod table; 9 | pub use handler::InterruptFrame; 10 | use table::InterruptDescriptorTable; 11 | 12 | // 0x00007E00 - 0x0007FFFF is free region. 13 | // See x86 memory map. 14 | // TODO: Put out this table address. 15 | pub const TABLE_ADDRESS: PhysicalAddress = 0x6E000; 16 | 17 | pub fn init() { 18 | println!("Initialize IDT"); 19 | let table: &mut InterruptDescriptorTable = unsafe { core::mem::transmute(TABLE_ADDRESS.to_virtual_addr()) }; 20 | table.init(); 21 | table.load(); 22 | 23 | // Runtime test. 24 | unsafe { 25 | asm!("int 0xFA" : : : : "intel"); 26 | } 27 | println!("Return from interrupt"); 28 | 29 | pic::init(); 30 | pit::init(); 31 | } 32 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt/descriptor.rs: -------------------------------------------------------------------------------- 1 | use super::handler::{Handler, HandlerWithErrorCode}; 2 | use bitfield::bitfield; 3 | use core::marker::PhantomData; 4 | use static_assertions::assert_eq_size; 5 | 6 | bitfield! { 7 | #[repr(C)] 8 | pub struct Flags(u16); 9 | impl Debug; 10 | interrupt_stack_table, set_interrupt_stack_table: 2, 0; 11 | reserved1, _: 7, 2; 12 | descriptor_type, set_descriptor_type: 11, 8; 13 | reserved2, _: 13, 12; 14 | privilege_level, set_privilege_level: 14, 13; 15 | present, set_present: 15; 16 | } 17 | 18 | type DescriptorType = u16; 19 | const TRAP_GATE: DescriptorType = 0b1111; 20 | 21 | /// An entry for interrupt descriptor table. 22 | #[derive(Debug)] 23 | #[repr(C)] 24 | pub struct Descriptor { 25 | offset_low: u16, 26 | segment_selector: u16, 27 | flags: Flags, 28 | offset_middle: u16, 29 | offset_high: u32, 30 | reserved: u32, 31 | phantom: PhantomData, 32 | } 33 | assert_eq_size!([u8; 16], Descriptor); 34 | 35 | impl Descriptor { 36 | fn new(handler_address: usize) -> Descriptor { 37 | let (offset_low, offset_middle, offset_high) = Descriptor::::parse_addr(handler_address); 38 | let mut flags = Flags(0); 39 | flags.set_present(true); 40 | flags.set_descriptor_type(TRAP_GATE); 41 | 42 | use x86_64::instructions::segmentation; 43 | Descriptor { 44 | offset_low, 45 | segment_selector: segmentation::cs().0, 46 | flags, 47 | offset_middle, 48 | offset_high, 49 | reserved: 0, 50 | phantom: PhantomData, 51 | } 52 | } 53 | 54 | #[inline(always)] 55 | fn parse_addr(addr: usize) -> (u16, u16, u32) { 56 | ((addr & 0xFFFF) as u16, ((addr >> 16) & 0xFFFF) as u16, ((addr >> 32) & 0xFFFFFFFF) as u32) 57 | } 58 | } 59 | 60 | impl Descriptor { 61 | #[inline(always)] 62 | pub fn with_handler(handler: Handler) -> Descriptor { 63 | Descriptor::new((handler as *const Handler) as _) 64 | } 65 | } 66 | 67 | impl Descriptor { 68 | #[inline(always)] 69 | pub fn with_handler_with_error_code(handler: HandlerWithErrorCode) -> Descriptor { 70 | Descriptor::new((handler as *const HandlerWithErrorCode) as _) 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | 78 | #[test] 79 | fn flags_test() { 80 | assert_eq!(0b101, Flags(0b101).interrupt_stack_table()); 81 | assert_eq!(0b1001, Flags(0b1001_000_00_000).descriptor_type()); 82 | assert_eq!(0b11, Flags(0b11_0_0000_000_00_000).privilege_level()); 83 | assert_eq!(true, Flags(0b1_00_0_0000_000_00_000).present()); 84 | } 85 | 86 | #[test] 87 | fn descriptor_test() { 88 | assert_eq!((0x1111, 0x2222, 0x4444_3333), Descriptor::::parse_addr(0x4444_3333_2222_1111)) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt/handler.rs: -------------------------------------------------------------------------------- 1 | use bitfield::bitfield; 2 | use core::fmt; 3 | 4 | bitfield! { 5 | #[repr(C)] 6 | pub struct ErrorCode(u64); 7 | impl Debug; 8 | external_event, _: 0; 9 | descriptor_location, _: 1; 10 | ti, _: 2; 11 | segment_selector_index, _: 13, 3; 12 | reserved, _: 31, 14; 13 | } 14 | 15 | #[repr(C)] 16 | pub struct InterruptFrame { 17 | ip: usize, 18 | pub cs: usize, 19 | flags: usize, 20 | sp: usize, 21 | pub ss: usize, 22 | } 23 | 24 | // impl InterruptFrame { 25 | // pub fn new() -> InterruptFrame { 26 | // InterruptFrame { ip: 0, cs: 0, flags: 0, sp: 0, ss: 0 } 27 | // } 28 | // } 29 | 30 | impl fmt::Debug for InterruptFrame { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | write!(f, "InterruptFrame {{ cs:ip = 0x{:x}:0x{:x}, ss:sp = 0x{:x}:0x{:x}, flags = 0x{:x}}}", self.cs, self.ip, self.ss, self.sp, self.flags) 33 | } 34 | } 35 | 36 | pub type Handler = extern "x86-interrupt" fn(&mut InterruptFrame); 37 | pub type HandlerWithErrorCode = extern "x86-interrupt" fn(&mut InterruptFrame, ErrorCode); 38 | 39 | pub extern "x86-interrupt" fn default_handler(interrupt_frame: &mut InterruptFrame) { 40 | println!("Got interrupt"); 41 | println!(" {:?}", interrupt_frame); 42 | } 43 | 44 | pub extern "x86-interrupt" fn default_handler_with_error_code(interrupt_frame: &mut InterruptFrame, error: ErrorCode) { 45 | println!("Got interrupt"); 46 | println!(" {:?}", interrupt_frame); 47 | println!(" {:x}", error.0); 48 | } 49 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt/pic.rs: -------------------------------------------------------------------------------- 1 | use super::super::thread; 2 | use super::handler::InterruptFrame; 3 | use super::table::InterruptDescriptorTable; 4 | use lazy_static::lazy_static; 5 | use spin::Mutex; 6 | use x86_64::instructions::port::Port; 7 | 8 | lazy_static! { 9 | static ref MASTER_COMMAND_PORT: Mutex> = Mutex::new(Port::new(0x20)); 10 | static ref MASTER_DATA_PORT: Mutex> = Mutex::new(Port::new(0x21)); 11 | static ref SLAVE_COMMAND_PORT: Mutex> = Mutex::new(Port::new(0xA0)); 12 | static ref SLAVE_DATA_PORT: Mutex> = Mutex::new(Port::new(0xA1)); 13 | } 14 | 15 | const IRQ_OFFSET_MASTER: u8 = 0x20; 16 | const IRQ_OFFSET_SLAVE: u8 = IRQ_OFFSET_MASTER + 8; 17 | const IRQ_NUMBER_TIMER: u8 = IRQ_OFFSET_MASTER; 18 | 19 | const END_OF_INTERRUPT: u8 = 0x20; 20 | const ICW1_INIT: u8 = 0x10; 21 | const ICW1_ICW4_REQUIRED: u8 = 0x01; 22 | const ICW4_8086_MODE: u8 = 0x01; 23 | 24 | pub fn init() { 25 | unsafe { 26 | // Initialize PICs. 27 | // 1. Send initialize command 28 | MASTER_COMMAND_PORT.lock().write(ICW1_INIT | ICW1_ICW4_REQUIRED); 29 | io_wait(); 30 | MASTER_COMMAND_PORT.lock().write(ICW1_INIT | ICW1_ICW4_REQUIRED); 31 | io_wait(); 32 | 33 | // 2. Send vector offset 34 | MASTER_DATA_PORT.lock().write(IRQ_OFFSET_MASTER); 35 | io_wait(); 36 | SLAVE_DATA_PORT.lock().write(IRQ_OFFSET_SLAVE); 37 | io_wait(); 38 | 39 | // 3. Send how it is wired to master/slaves 40 | MASTER_DATA_PORT.lock().write(4); 41 | io_wait(); 42 | SLAVE_DATA_PORT.lock().write(2); 43 | io_wait(); 44 | 45 | // 4. Gives additional information about the environment. 46 | MASTER_DATA_PORT.lock().write(ICW4_8086_MODE); 47 | io_wait(); 48 | SLAVE_DATA_PORT.lock().write(ICW4_8086_MODE); 49 | io_wait(); 50 | 51 | // Disable all interrupts. 52 | MASTER_DATA_PORT.lock().write(0b1111_1111); 53 | SLAVE_DATA_PORT.lock().write(0b1111_1111); 54 | } 55 | } 56 | 57 | pub fn set_handlers(idt: &mut InterruptDescriptorTable) { 58 | idt.set_handler(IRQ_NUMBER_TIMER, timer_handler); 59 | } 60 | 61 | /// If the IRQ came from the slave PIC, it is necessary to issue end of interrupt to both PICs. 62 | fn send_end_of_interrupt(irq: u8) { 63 | if IRQ_OFFSET_SLAVE <= irq { 64 | unsafe { SLAVE_COMMAND_PORT.lock().write(END_OF_INTERRUPT) }; 65 | } 66 | 67 | unsafe { MASTER_COMMAND_PORT.lock().write(END_OF_INTERRUPT) }; 68 | } 69 | 70 | pub unsafe fn enable_timer_interrupt() { 71 | // FIXME 72 | MASTER_DATA_PORT.lock().write(0b1111_1110); 73 | } 74 | 75 | pub extern "x86-interrupt" fn timer_handler(frame: &mut InterruptFrame) { 76 | // FIXME: use listener pattern. 77 | thread::switch_context(frame); 78 | 79 | send_end_of_interrupt(IRQ_NUMBER_TIMER); 80 | } 81 | 82 | /// TODO: write document. 83 | fn io_wait() { 84 | unsafe { 85 | asm!("outb %al, $$0x80" 86 | : 87 | : "al"(0) 88 | : 89 | : 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt/pit.rs: -------------------------------------------------------------------------------- 1 | use bitfield::bitfield; 2 | use lazy_static::lazy_static; 3 | use spin::Mutex; 4 | use x86_64::instructions::port::Port; 5 | use super::pic; 6 | 7 | lazy_static! { 8 | static ref CHANNEL0: Mutex> = Mutex::new(Port::new(0x40)); 9 | static ref MODE_COMMAND_REGISTER: Mutex> = Mutex::new(Port::new(0x43)); 10 | } 11 | 12 | bitfield! { 13 | #[repr(C)] 14 | pub struct ModeCommandRegister(u8); 15 | impl Debug; 16 | // 0 refers to four-digit BCD. 17 | // 1 refers to 16-bit binary mode. 18 | bcd_binary_mode, set_bcd_binary_mode: 1, 0; 19 | operating_mode, set_operating_mode: 3, 1; 20 | access_mode, set_access_mode: 5, 4; 21 | select_channel, set_select_channnel: 7, 6; 22 | } 23 | 24 | pub fn init() { 25 | println!("init pit"); 26 | 27 | // Binary mode. 28 | // Channel 0. 29 | // Rate generator mode. 30 | // Ho/hi bytes. 31 | let mut r = ModeCommandRegister(0); 32 | r.set_operating_mode(0b010); 33 | r.set_access_mode(0b11); 34 | 35 | // Target timer tick frequency is 100Hz. 36 | let (h, l) = make_asymptotic_counter_value(100); 37 | 38 | unsafe { 39 | MODE_COMMAND_REGISTER.lock().write(r.0); 40 | 41 | CHANNEL0.lock().write(l); 42 | CHANNEL0.lock().write(h); 43 | 44 | pic::enable_timer_interrupt(); 45 | 46 | // Enable interrupt. 47 | asm!("sti"); 48 | }; 49 | } 50 | 51 | fn make_asymptotic_counter_value(timer_frequency_hz: u32) -> (u8, u8) { 52 | // PIT clock is 1193181666... Hz 53 | // It is equals to 3579545 / 3. 54 | // Counter value is 16 bit value. 55 | // The range is from 1 to 65536 because dividing zero is forbidden and setting value 0 refers to 65536. 56 | // Minimum frequency is 3579545 / 3 / 65536 = 18.2065074. 57 | // Maximum frequency is 3579545 / 3 / 1 = 1193181.66667. 58 | // NOTE: counter value must not be 1 with mode2. 59 | // Maximum frequency in mode 2 is 3579545 / 3 / 2 = 596590.833333 60 | debug_assert!(18 <= timer_frequency_hz); 61 | debug_assert!(timer_frequency_hz < 1193181); 62 | 63 | let c = (3579545u32 / 3u32 / timer_frequency_hz) as u16; 64 | (((c >> 8) as u8), (c & 0xFF) as u8) 65 | } 66 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt/table.rs: -------------------------------------------------------------------------------- 1 | use super::descriptor::Descriptor; 2 | use super::handler::{default_handler, default_handler_with_error_code, Handler, HandlerWithErrorCode}; 3 | use super::pic; 4 | use static_assertions::assert_eq_size; 5 | 6 | #[repr(C, packed)] 7 | struct InterruptDescriptorTableRegister { 8 | limit: u16, 9 | base: usize, 10 | } 11 | assert_eq_size!([u8; 10], InterruptDescriptorTableRegister); 12 | 13 | impl InterruptDescriptorTableRegister { 14 | #[inline(always)] 15 | fn load(&self) { 16 | unsafe { 17 | asm!(".intel_syntax noprefix 18 | lidt [$0]" 19 | : 20 | : "r"(self) 21 | : 22 | : "intel"); 23 | } 24 | } 25 | } 26 | 27 | #[repr(C)] 28 | pub struct InterruptDescriptorTable { 29 | divide_error_exception: Descriptor, 30 | debug: Descriptor, 31 | nonmaskable: Descriptor, 32 | breakpoint: Descriptor, 33 | overflow: Descriptor, 34 | bound_range_exceeded: Descriptor, 35 | invalid_opcode: Descriptor, 36 | device_not_available: Descriptor, 37 | double_fault: Descriptor, 38 | coprocessor_segment_overrun: Descriptor, 39 | invalid_tss: Descriptor, 40 | segment_not_present: Descriptor, 41 | stack_segment_not_present: Descriptor, 42 | general_protection: Descriptor, 43 | page_fault: Descriptor, 44 | reserved: Descriptor, 45 | x87_pfu_floting_point_error: Descriptor, 46 | alignment_check_exception: Descriptor, 47 | machine_check_exception: Descriptor, 48 | simd_floating_point_exception: Descriptor, 49 | virtualization_exception: Descriptor, 50 | reserveds: [Descriptor; 11], 51 | user_defined_interrupts: [Descriptor; 224], 52 | } 53 | assert_eq_size!([u8; 16 * 256], InterruptDescriptorTable); 54 | 55 | // FIXME: How do we protect override the already configured descriptors ?. 56 | impl InterruptDescriptorTable { 57 | #[inline(always)] 58 | pub fn init(&mut self) { 59 | self.divide_error_exception = Descriptor::with_handler(default_handler); 60 | self.debug = Descriptor::with_handler(default_handler); 61 | self.nonmaskable = Descriptor::with_handler(default_handler); 62 | self.breakpoint = Descriptor::with_handler(default_handler); 63 | self.overflow = Descriptor::with_handler(default_handler); 64 | self.bound_range_exceeded = Descriptor::with_handler(default_handler); 65 | self.invalid_opcode = Descriptor::with_handler(default_handler); 66 | self.device_not_available = Descriptor::with_handler(default_handler); 67 | self.double_fault = Descriptor::with_handler_with_error_code(default_handler_with_error_code); 68 | self.coprocessor_segment_overrun = Descriptor::with_handler(default_handler); 69 | self.invalid_tss = Descriptor::with_handler_with_error_code(default_handler_with_error_code); 70 | self.segment_not_present = Descriptor::with_handler_with_error_code(default_handler_with_error_code); 71 | self.stack_segment_not_present = Descriptor::with_handler_with_error_code(default_handler_with_error_code); 72 | self.general_protection = Descriptor::with_handler_with_error_code(default_handler_with_error_code); 73 | self.page_fault = Descriptor::with_handler_with_error_code(default_handler_with_error_code); 74 | self.reserved = Descriptor::with_handler(default_handler); 75 | self.x87_pfu_floting_point_error = Descriptor::with_handler(default_handler); 76 | self.alignment_check_exception = Descriptor::with_handler_with_error_code(default_handler_with_error_code); 77 | self.machine_check_exception = Descriptor::with_handler(default_handler); 78 | self.simd_floating_point_exception = Descriptor::with_handler(default_handler); 79 | self.virtualization_exception = Descriptor::with_handler(default_handler); 80 | for i in self.reserveds.iter_mut() { 81 | *i = Descriptor::with_handler(default_handler) 82 | } 83 | for i in self.user_defined_interrupts.iter_mut() { 84 | *i = Descriptor::with_handler(default_handler) 85 | } 86 | 87 | pic::set_handlers(self); 88 | } 89 | 90 | pub fn set_handler(&mut self, irq_number: u8, f: Handler) { 91 | debug_assert!(0x20 <= irq_number); 92 | self.user_defined_interrupts[(irq_number - 0x20) as usize] = Descriptor::with_handler(f); 93 | } 94 | 95 | #[inline(always)] 96 | pub fn load(&self) { 97 | let idtr = InterruptDescriptorTableRegister { 98 | limit: core::mem::size_of::() as u16 - 1, 99 | base: (self as *const _) as usize, 100 | }; 101 | 102 | idtr.load(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/arch/x86_64/link.ld: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief 3 | * Linker script to link kernel. 4 | * Please refer LD documents for more information. 5 | * https://sourceware.org/binutils/docs/ld/index.html 6 | */ 7 | 8 | 9 | /* 10 | * Set the output file format. 11 | * You can see the available formats by `objdump --info`. 12 | */ 13 | OUTPUT_FORMAT(elf64-x86-64) 14 | 15 | /* Set kernel entry point. */ 16 | ENTRY(start_axel) 17 | 18 | 19 | SECTIONS 20 | { 21 | KERNEL_ADDR_PHYSICAL_BEGIN = 0x00100000; 22 | . = KERNEL_ADDR_PHYSICAL_BEGIN; 23 | 24 | /* 25 | * The kernel LMA (Load Memory Address) and VMA (Virtual Memory Address) is equal here in order to use jump instruction in the protect mode. 26 | */ 27 | .boot : 28 | { 29 | /* 30 | * If linker option `--gc-sections` was passed, linker eliminates unused sections (e.g., multiboot2). 31 | * However, multiboot header must not be removed and located at the head. 32 | * So, KEEP is used to keep these sections here. 33 | */ 34 | KEEP(*(.multiboot2)) 35 | *(.text_boot_32bit) 36 | *(.text_boot_64bit) 37 | } 38 | 39 | /* 40 | * The kernel LMA and VMA is different each other from here. 41 | * LMA = KERNEL_ADDR_PHYSICAL_BEGIN 42 | * VMA = KERNEL_ADDR_PHYSICAL_BEGIN + KERNEL_ADDR_VIRTUAL_OFFSET 43 | * The kernel will be load at this physical address by a bootloader. 44 | * After that, the kernel will be mapped to the higher address. 45 | * You can confirm these information using the command `objdump -w -x`. 46 | * 47 | * KERNEL_ADDR_VIRTUAL_OFFSET is determined based on 3.3.7.1 Canonical Addressing in the intel manual. 48 | * For more information, please refer the intel manual or below link. 49 | * [Canonical form addresses](https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details) 50 | */ 51 | KERNEL_ADDR_VIRTUAL_OFFSET = 0xFFFF800000000000; 52 | KERNEL_ADDR_VIRTUAL_BEGIN = KERNEL_ADDR_PHYSICAL_BEGIN + KERNEL_ADDR_VIRTUAL_OFFSET; 53 | . += KERNEL_ADDR_VIRTUAL_OFFSET; 54 | 55 | /* 56 | * Each section will be aligned on 4096. 57 | * This alignment means the page size. 58 | */ 59 | .text : AT(ADDR(.text) - KERNEL_ADDR_VIRTUAL_OFFSET) 60 | { 61 | *(.text_canonical_higher_harf) 62 | *(.text*) 63 | } 64 | 65 | .data : AT(ADDR(.data) - KERNEL_ADDR_VIRTUAL_OFFSET) 66 | { 67 | *(.data*) 68 | } 69 | 70 | .bss : AT(ADDR(.bss) - KERNEL_ADDR_VIRTUAL_OFFSET) 71 | { 72 | KERNEL_ADDR_BSS_BEGIN = .; 73 | *(.bss*) 74 | KERNEL_ADDR_BSS_END = .; 75 | } 76 | 77 | .rodata : AT(ADDR(.rodata) - KERNEL_ADDR_VIRTUAL_OFFSET) 78 | { 79 | *(.rodata*) 80 | } 81 | 82 | KERNEL_SIZE_BSS = KERNEL_ADDR_BSS_END - KERNEL_ADDR_BSS_BEGIN; 83 | KERNEL_SIZE_TOTAL = . - (KERNEL_ADDR_PHYSICAL_BEGIN + KERNEL_ADDR_VIRTUAL_OFFSET); 84 | KERNEL_ADDR_VIRTUAL_END = .; 85 | KERNEL_ADDR_PHYSICAL_END = . - KERNEL_ADDR_VIRTUAL_OFFSET; 86 | } 87 | -------------------------------------------------------------------------------- /src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | use super::Error; 2 | use super::Initialize; 3 | use crate::graphic; 4 | use crate::graphic::Display; 5 | use crate::memory::address::*; 6 | use crate::memory::{self, Multiboot2Adapter}; 7 | use lazy_static::lazy_static; 8 | 9 | mod interrupt; 10 | mod thread; 11 | mod tss; 12 | 13 | pub use thread::Thread; 14 | 15 | const VRAM_ADDR: PhysicalAddress = 0xB8000; 16 | 17 | pub struct Initializer; 18 | 19 | impl Initialize for Initializer { 20 | fn init(argv: &[VirtualAddress]) -> Result<(), Error> { 21 | let rs = [ 22 | // Kernel stack info (see boot.asm). 23 | memory::IdenticalReMapRequest::new(argv[1], argv[2]), 24 | // FIXME: do not hard-code vram address. 25 | memory::IdenticalReMapRequest::new(VRAM_ADDR, 1), 26 | // IDT 27 | memory::IdenticalReMapRequest::new(interrupt::TABLE_ADDRESS, 1), 28 | ]; 29 | 30 | let multiboot_info = unsafe { multiboot2::load(argv[0]) }; 31 | let memory_map_tag = multiboot_info.memory_map_tag().ok_or(Error::NoMemoryMap)?; 32 | memory::init(&Multiboot2Adapter::new(memory_map_tag) as _, &rs).map_err(Into::::into)?; 33 | interrupt::init(); 34 | 35 | Ok(()) 36 | } 37 | 38 | fn obtain_kernel_console() -> Option> { 39 | lazy_static! { 40 | static ref TEXT_MODE_VRAM_ADDR: usize = VRAM_ADDR.to_virtual_addr(); 41 | static ref TEXT_MODE_WIDTH: usize = 80; 42 | static ref TEXT_MODE_HEIGHT: usize = 25; 43 | } 44 | 45 | let mut display = graphic::CharacterDisplay::new(*TEXT_MODE_VRAM_ADDR, graphic::Position(*TEXT_MODE_WIDTH, *TEXT_MODE_HEIGHT)); 46 | display.clear_screen(); 47 | Some(display) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/arch/x86_64/thread.rs: -------------------------------------------------------------------------------- 1 | use super::interrupt::InterruptFrame; 2 | use crate::process; 3 | 4 | pub struct Thread { 5 | // interrupt_frame: InterruptFrame, 6 | } 7 | 8 | impl Thread { 9 | // pub fn new() -> Thread { 10 | // Thread { interrupt_frame: InterruptFrame::new() } 11 | // } 12 | } 13 | 14 | // Steps 15 | // 1. Change segment selector to user mode. 16 | // 2. Keep the current execution context. 17 | pub fn switch_context(current_interrupt_frame: &mut InterruptFrame) { 18 | process::switch_context(|_current_thread: &mut Thread, _next_thread: &mut Thread| { 19 | current_interrupt_frame.cs = 8 * 3 + 3; 20 | current_interrupt_frame.ss = 8 * 4 + 3; 21 | unsafe { 22 | asm!("mov ds, $0 23 | mov es, $0 24 | mov fs, $0 25 | mov gs, $0 26 | " 27 | : 28 | : "r"(current_interrupt_frame.ss as u16) 29 | : "ax" 30 | : "intel", "volatile" 31 | ); 32 | } 33 | 34 | // let mut current_ip = 0; 35 | // let mut current_sp = 0; 36 | // let mut next_ip = 0; 37 | // let mut next_sp = 0; 38 | // unsafe { 39 | // asm!(" 40 | // pushf 41 | // 42 | // mov [$0], $2 43 | // mov [$1], rsp 44 | // " 45 | // : "=r"(&mut current_ip) "=r"(&mut current_sp) 46 | // : "r"(landing_point as usize) 47 | // : "memory" 48 | // : "intel", "volatile"); 49 | // } 50 | // 51 | // println!("{}", current_ip); 52 | // println!("{}", current_sp); 53 | }) 54 | } 55 | 56 | // #[no_mangle] 57 | // pub extern "C" fn landing_point() { 58 | // unsafe { asm!("popf") } 59 | // } 60 | -------------------------------------------------------------------------------- /src/arch/x86_64/tss.rs: -------------------------------------------------------------------------------- 1 | // use crate::memory::address::*; 2 | // 3 | // #[derive(Debug)] 4 | // #[repr(C)] 5 | // pub struct TaskStateSegment { 6 | // reserved0: u32, 7 | // rsp0: usize, 8 | // rsp1: usize, 9 | // rsp2: usize, 10 | // reserved1: usize, 11 | // ist1: usize, 12 | // ist2: usize, 13 | // ist3: usize, 14 | // ist4: usize, 15 | // ist5: usize, 16 | // ist6: usize, 17 | // ist7: usize, 18 | // reserved2: usize, 19 | // reserved3: u16, 20 | // io_map_base_address: u16, 21 | // } 22 | // 23 | // impl TaskStateSegment { 24 | // pub fn kernel_mode_stack(&self) -> VirtualAddress { 25 | // self.rsp0 26 | // } 27 | // 28 | // pub fn set_kernel_mode_stack(&mut self, addr: usize) { 29 | // self.rsp0 = addr; 30 | // } 31 | // } 32 | -------------------------------------------------------------------------------- /src/bytes.rs: -------------------------------------------------------------------------------- 1 | pub trait Bytes { 2 | fn kb(&self) -> usize; 3 | 4 | fn mb(&self) -> usize { 5 | self.kb() / 1024 6 | } 7 | 8 | fn gb(&self) -> usize { 9 | self.mb() / 1024 10 | } 11 | } 12 | 13 | impl Bytes for usize { 14 | fn kb(&self) -> usize { 15 | self / 1024 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | use self::super::arch; 2 | use self::super::arch::Initialize; 3 | use self::super::graphic::CharacterDisplay; 4 | use lazy_static::lazy_static; 5 | use spin::Mutex; 6 | 7 | pub struct Context { 8 | pub default_output: Mutex>>, 9 | } 10 | 11 | lazy_static! { 12 | pub static ref GLOBAL_CONTEXT: Context = { 13 | Context { 14 | default_output: Mutex::new(arch::Initializer::obtain_kernel_console()), 15 | } 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/graphic.rs: -------------------------------------------------------------------------------- 1 | //! This crate contains stuffs about computer graphic. 2 | //! 3 | //! This includes display (text and visual) object. 4 | 5 | #![allow(dead_code)] 6 | use core; 7 | 8 | /// This struct represents any position of 2d-coordinate. 9 | #[derive(PartialEq, Eq, Debug)] 10 | pub struct Position(pub usize, pub usize); 11 | 12 | trait Area { 13 | fn area_from_origin(&self) -> usize; 14 | } 15 | 16 | impl Area for Position { 17 | fn area_from_origin(&self) -> usize { 18 | (self.0 * self.1) 19 | } 20 | } 21 | 22 | pub enum Color { 23 | Rgb(i8, i8, i8), 24 | Code(u32), 25 | Black, 26 | Blue, 27 | Green, 28 | Cyan, 29 | Red, 30 | Magenta, 31 | Brown, 32 | LightGray, 33 | DarkGray, 34 | LightBlue, 35 | LightGreen, 36 | LightCyan, 37 | LightRed, 38 | LightMagenta, 39 | Yellow, 40 | White, 41 | } 42 | 43 | /// Display trait for any display. 44 | /// 45 | /// This trait is abstract interface for display. 46 | pub trait Display { 47 | fn color_background(&self) -> &Color; 48 | fn set_color_background(&mut self, _: Color) -> &mut Self; 49 | fn color_foreground(&self) -> &Color; 50 | fn set_color_foreground(&mut self, _: Color) -> &mut Self; 51 | fn clear_screen(&mut self); 52 | fn print(&mut self, _: &str); 53 | fn println(&mut self, _: &str); 54 | } 55 | 56 | /// Text display struct to represent text display connected to the computer. 57 | pub struct CharacterDisplay<'a> { 58 | vram_addr: usize, 59 | vram: &'a mut [u16], 60 | current_position: Position, 61 | max_position: Position, 62 | color_background: Color, 63 | color_foreground: Color, 64 | } 65 | 66 | impl<'a> Default for CharacterDisplay<'a> { 67 | fn default() -> CharacterDisplay<'a> { 68 | CharacterDisplay { 69 | vram_addr: 0, 70 | vram: &mut [], 71 | current_position: Position(0, 0), 72 | max_position: Position(0, 0), 73 | color_background: Color::Black, 74 | color_foreground: Color::LightRed, 75 | } 76 | } 77 | } 78 | 79 | impl<'a> CharacterDisplay<'a> { 80 | pub fn new(vram_addr: usize, max_p: Position) -> CharacterDisplay<'a> { 81 | let max_texts_num = max_p.area_from_origin(); 82 | let vram_ptr = vram_addr as *mut u16; 83 | let vram = unsafe { core::slice::from_raw_parts_mut(vram_ptr, max_texts_num) }; 84 | 85 | CharacterDisplay { 86 | vram_addr: vram_addr, 87 | vram: vram, 88 | max_position: max_p, 89 | ..Default::default() 90 | } 91 | } 92 | 93 | pub fn set_current_position(&mut self, pos: Position) -> &mut Self { 94 | self.current_position = pos; 95 | self 96 | } 97 | 98 | /// Return color code based on Enum. 99 | fn color(c: &Color) -> u8 { 100 | match *c { 101 | Color::Black => 0x0, 102 | Color::Blue => 0x1, 103 | Color::Green => 0x2, 104 | Color::Cyan => 0x3, 105 | Color::Red => 0x4, 106 | Color::Magenta => 0x5, 107 | Color::Brown => 0x6, 108 | Color::LightGray => 0x7, 109 | Color::DarkGray => 0x8, 110 | Color::LightBlue => 0x9, 111 | Color::LightGreen => 0xA, 112 | Color::LightCyan => 0xB, 113 | Color::LightRed => 0xC, 114 | Color::LightMagenta => 0xD, 115 | Color::Yellow => 0xE, 116 | Color::White => 0xF, 117 | _ => panic!("Should NOT use Rgb in CharacterDisplay."), // TODO: add error handling Rgb 118 | } 119 | } 120 | 121 | /// Generate one pixel (16bit) 122 | /// [Text UI](http://wiki.osdev.org/Text_UI) 123 | /// Bit 76543210 76543210 124 | /// |||||||| |||||||| 125 | /// |||||||| ^^^^^^^^-Character 126 | /// ||||^^^^-fore colour 127 | /// ^^^^-----back colour 128 | fn gen_pixel(&self, c: char) -> u16 { 129 | let bg = Self::color(&self.color_background) as u16; 130 | let fg = Self::color(&self.color_foreground) as u16; 131 | 132 | (bg << 12) | (fg << 8) | (c as u16) 133 | } 134 | } 135 | 136 | impl<'a> Display for CharacterDisplay<'a> { 137 | fn color_background(&self) -> &Color { 138 | &self.color_background 139 | } 140 | 141 | fn set_color_background(&mut self, bg: Color) -> &mut Self { 142 | self.color_background = bg; 143 | self 144 | } 145 | 146 | fn color_foreground(&self) -> &Color { 147 | &self.color_foreground 148 | } 149 | 150 | fn set_color_foreground(&mut self, fg: Color) -> &mut Self { 151 | self.color_foreground = fg; 152 | self 153 | } 154 | 155 | fn clear_screen(&mut self) { 156 | let space = self.gen_pixel(' '); 157 | for i in 0..(self.max_position.area_from_origin()) { 158 | self.vram[i] = space; 159 | } 160 | } 161 | 162 | fn print(&mut self, string: &str) { 163 | for c in string.chars() { 164 | let width = self.max_position.0; 165 | 166 | if c != '\n' { 167 | let code = self.gen_pixel(c); 168 | let x = &mut self.current_position.0; 169 | let y = self.current_position.1; 170 | self.vram[*x + y * width] = code; 171 | *x += 1; 172 | } else { 173 | let x = &mut self.current_position.0; 174 | let y = &mut self.current_position.1; 175 | *x = 0; 176 | *y += 1; 177 | } 178 | 179 | let space = self.gen_pixel(' '); 180 | let x = &mut self.current_position.0; 181 | let y = &mut self.current_position.1; 182 | 183 | if width <= *x { 184 | *x = 0; 185 | *y += 1; 186 | } 187 | 188 | let height = self.max_position.1; 189 | if height <= *y { 190 | // scroll down. 191 | for i in (width)..self.max_position.area_from_origin() { 192 | self.vram[i - width] = self.vram[i]; 193 | } 194 | 195 | // clear last line. 196 | for i in (width * (height - 1))..self.max_position.area_from_origin() { 197 | self.vram[i] = space; 198 | } 199 | *y -= 1; 200 | } 201 | } 202 | } 203 | 204 | fn println(&mut self, s: &str) { 205 | self.print(s); 206 | self.print("\n"); 207 | } 208 | } 209 | 210 | impl<'a> core::fmt::Write for CharacterDisplay<'a> { 211 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 212 | self.print(s); 213 | Ok(()) 214 | } 215 | } 216 | 217 | // This represents bit size and position infomation from VBE. 218 | pub struct ColorBitInfo { 219 | // this shows each bit size. 220 | pub size_r: u8, 221 | pub size_g: u8, 222 | pub size_b: u8, 223 | pub size_rsvd: u8, 224 | // this shows each bit position. 225 | pub pos_r: u8, 226 | pub pos_g: u8, 227 | pub pos_b: u8, 228 | pub pos_rsvd: u8, 229 | } 230 | 231 | /// Visual display struct to represent text display connected to the computer. 232 | pub struct GraphicalDisplay { 233 | vram_addr: usize, 234 | max_position: Position, 235 | bit_info: ColorBitInfo, 236 | byte_per_pixel: usize, 237 | } 238 | 239 | impl GraphicalDisplay { 240 | pub fn new(vram_addr: usize, max_position: Position, bit_info: ColorBitInfo) -> GraphicalDisplay { 241 | let bpp = (bit_info.size_r + bit_info.size_g + bit_info.size_b + bit_info.size_rsvd) / 8; 242 | GraphicalDisplay { 243 | vram_addr: vram_addr, 244 | max_position: max_position, 245 | bit_info: bit_info, 246 | byte_per_pixel: bpp as usize, 247 | } 248 | } 249 | 250 | pub fn fill_display(&self, c: &Color) { 251 | let Position(x, y) = self.max_position; 252 | let color_bit = self.color(c); 253 | 254 | for i in 0..(x * y) { 255 | let addr = self.vram_addr + (i * 4); 256 | unsafe { *(addr as *mut u32) = color_bit }; 257 | } 258 | } 259 | 260 | pub fn fill_area(&self, begin: Position, end: Position, c: &Color) { 261 | let Position(size_x, _) = self.max_position; 262 | let begin_x = begin.0 * self.byte_per_pixel; 263 | let begin_y = begin.1 * self.byte_per_pixel * size_x; 264 | let end_x = end.0 * self.byte_per_pixel; 265 | let end_y = end.1 * self.byte_per_pixel * size_x; 266 | let dy = size_x * self.byte_per_pixel; 267 | let ccode = self.color(c); 268 | 269 | let vy_e = self.vram_addr + end_y; 270 | let mut vy = self.vram_addr + begin_y; 271 | loop { 272 | if !(vy < vy_e) { 273 | break; 274 | } 275 | 276 | let ve = vy + end_x; 277 | 278 | let mut v = vy + begin_x; 279 | loop { 280 | if !(v < ve) { 281 | break; 282 | } 283 | 284 | unsafe { *(v as *mut u32) = ccode }; 285 | 286 | v += self.byte_per_pixel; 287 | } 288 | 289 | vy += dy; 290 | } 291 | } 292 | 293 | pub fn fill_area_by_size(&self, origin: Position, size: Position, c: &Color) { 294 | let end = Position(origin.0 + size.0, origin.1 + size.1); 295 | self.fill_area(origin, end, c); 296 | } 297 | 298 | /// Return color code based on Enum. 299 | fn color(&self, c: &Color) -> u32 { 300 | match *c { 301 | // TODO: 302 | Color::Black => 0x0, 303 | Color::Blue => 0x1, 304 | Color::Green => 0x2, 305 | Color::Cyan => 0x3, 306 | Color::Red => 0x4, 307 | Color::Magenta => 0x5, 308 | Color::Brown => 0x6, 309 | Color::LightGray => 0x7, 310 | Color::DarkGray => 0x8, 311 | Color::LightBlue => 0x9, 312 | Color::LightGreen => 0xA, 313 | Color::LightCyan => 0xB, 314 | Color::LightRed => 0xC, 315 | Color::LightMagenta => 0xD, 316 | Color::Yellow => 0xE, 317 | Color::White => 0xF, 318 | Color::Code(rgb) => { 319 | let r = (rgb >> 16) as u8; 320 | let g = (rgb >> 8) as u8; 321 | let b = (rgb & 0xFF) as u8; 322 | let bit_info = &self.bit_info; 323 | ((0u32) << bit_info.pos_rsvd) | ((r as u32) << bit_info.pos_r) | ((g as u32) << bit_info.pos_g) | ((b as u32) << bit_info.pos_b) 324 | } 325 | Color::Rgb(r, g, b) => { 326 | let bit_info = &self.bit_info; 327 | ((0u32) << bit_info.pos_rsvd) | ((r as u32) << bit_info.pos_r) | ((g as u32) << bit_info.pos_g) | ((b as u32) << bit_info.pos_b) 328 | } 329 | } 330 | } 331 | } 332 | 333 | #[cfg(test)] 334 | mod test { 335 | use super::{Area, CharacterDisplay, Color, Display, Position}; 336 | 337 | #[test] 338 | fn creating_position() { 339 | let mut p = Position(100, 100); 340 | assert_eq!(p, Position(100, 100)); 341 | assert_eq!(p.area_from_origin(), 100 * 100); 342 | assert_eq!(p.0, 100); 343 | assert_eq!(p.1, 100); 344 | p.1 = 255; 345 | assert_eq!(p.1, 255); 346 | } 347 | 348 | #[test] 349 | fn creating_character_display() { 350 | let c_disp = CharacterDisplay::new(0xB8000, Position(800, 600)); 351 | assert_eq!(c_disp.current_position, Position(0, 0)); 352 | } 353 | 354 | #[test] 355 | fn setting_bg_fg_color() { 356 | let mut c_disp = CharacterDisplay::new(0xB8000, Position(800, 600)); 357 | 358 | c_disp.set_color_background(Color::Red).set_color_foreground(Color::LightBlue); 359 | let color_code = CharacterDisplay::color(c_disp.color_background()); 360 | assert_eq!(color_code, 0x4); 361 | 362 | let color_code = CharacterDisplay::color(c_disp.color_foreground()); 363 | assert_eq!(color_code, 0x9); 364 | } 365 | 366 | // TODO 367 | // #[test] 368 | // #[should_panic] 369 | // fn setting_wrong_color() { 370 | // CharacterDisplay::color(&Color::Rgb(0, 0, 0)); 371 | // } 372 | } 373 | -------------------------------------------------------------------------------- /src/kernel.rs: -------------------------------------------------------------------------------- 1 | #![feature(alloc_error_handler)] 2 | #![feature(abi_x86_interrupt)] 3 | #![feature(asm)] 4 | #![feature(lang_items)] 5 | #![feature(panic_info_message)] 6 | #![feature(ptr_internals)] 7 | #![feature(ptr_wrapping_offset_from)] 8 | #![feature(allocator_api)] 9 | #![feature(start)] 10 | #![no_std] 11 | 12 | #[cfg(test)] 13 | #[macro_use] 14 | extern crate std; 15 | extern crate alloc; 16 | extern crate bitfield; 17 | extern crate bitflags; 18 | extern crate failure; 19 | extern crate intrusive_collections; 20 | extern crate lazy_static; 21 | extern crate multiboot2; 22 | extern crate rlibc; 23 | extern crate spin; 24 | extern crate static_assertions; 25 | extern crate x86_64; 26 | 27 | #[cfg(not(test))] 28 | #[macro_use] 29 | mod log; 30 | mod arch; 31 | mod bytes; 32 | mod context; 33 | mod graphic; 34 | mod memory; 35 | mod process; 36 | 37 | use arch::Initialize; 38 | use core::panic::PanicInfo; 39 | use memory::address::VirtualAddress; 40 | use memory::GlobalAllocator; 41 | 42 | #[global_allocator] 43 | static ALLOCATOR: memory::GlobalAllocator = GlobalAllocator::new(); 44 | 45 | #[cfg(not(test))] 46 | #[start] 47 | #[no_mangle] 48 | pub extern "C" fn main(argc: usize, argv: *const VirtualAddress) { 49 | memory::clean_bss_section(); 50 | 51 | let argv: &[VirtualAddress] = unsafe { core::slice::from_raw_parts(argv, argc) }; 52 | println!("Start Axel"); 53 | if let Err(e) = arch::Initializer::init(argv) { 54 | panic!("arch::init fails: {}", e); 55 | } 56 | println!("Startup complete"); 57 | } 58 | 59 | #[cfg(not(test))] 60 | #[lang = "eh_personality"] 61 | pub extern "C" fn eh_personality() {} 62 | 63 | #[cfg(not(test))] 64 | #[lang = "panic_impl"] 65 | #[no_mangle] 66 | pub extern "C" fn panic_impl(pi: &PanicInfo) -> ! { 67 | println!("Panic !"); 68 | if let Some(msg) = pi.message() { 69 | println!(" msg: {}", msg); 70 | } 71 | if let Some(location) = pi.location() { 72 | println!("panic occurred in file '{}' at line {}", location.file(), location.line()); 73 | } else { 74 | println!("panic occurred but can't get location information..."); 75 | } 76 | 77 | loop {} 78 | } 79 | 80 | #[no_mangle] 81 | pub extern "C" fn abort() { 82 | println!("abort"); 83 | loop {} 84 | } 85 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! print { 3 | ($($args:tt)*) => { 4 | { 5 | use crate::context; 6 | let ref mut default_output = *context::GLOBAL_CONTEXT.default_output.lock(); 7 | if let Some(ref mut default_output) = *default_output { 8 | use core::fmt::Write; 9 | write!(default_output, $($args)*).unwrap(); 10 | } 11 | } 12 | } 13 | } 14 | 15 | #[macro_export] 16 | macro_rules! println { 17 | ($fmt:expr) => (print!(concat!($fmt, "\n"))); 18 | ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); 19 | } 20 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | pub mod address; 2 | mod buddy_system; 3 | mod early_allocator; 4 | mod frame; 5 | mod frame_allocator; 6 | mod global_allocator; 7 | mod paging; 8 | mod region; 9 | 10 | use crate::ALLOCATOR; 11 | use address::*; 12 | use alloc::boxed::Box; 13 | use buddy_system::BuddyAllocator; 14 | use core::mem; 15 | use core::ptr::Unique; 16 | pub use early_allocator::EarlyAllocator; 17 | use failure::Fail; 18 | pub use frame::{Frame, FrameAdapter}; 19 | pub use frame_allocator::FrameAllocator; 20 | pub use global_allocator::GlobalAllocator; 21 | use global_allocator::HeapAllocator; 22 | use paging::table::Error as PageTableError; 23 | pub use paging::IdenticalReMapRequest; 24 | pub use paging::{ActivePageTable, InActivePageTable, ACTIVE_PAGE_TABLE}; 25 | pub use region::{Multiboot2Adapter, Region}; 26 | 27 | #[derive(Fail, Debug)] 28 | pub enum Error { 29 | #[fail(display = "No usable memory region")] 30 | NoUsableMemory, 31 | #[fail(display = "Page table error: {}", inner)] 32 | PageTable { inner: PageTableError }, 33 | } 34 | 35 | impl From for Error { 36 | fn from(inner: PageTableError) -> Error { 37 | Error::PageTable { inner } 38 | } 39 | } 40 | 41 | #[inline(always)] 42 | pub fn clean_bss_section() { 43 | let begin = kernel_bss_section_addr_begin() as *mut u8; 44 | let size = kernel_bss_section_size() as i32; 45 | 46 | unsafe { 47 | rlibc::memset(begin, size, 0x00); 48 | } 49 | } 50 | 51 | #[inline(always)] 52 | pub fn init, T: Iterator>(regions: &dyn region::Adapter, remap_requests: &[IdenticalReMapRequest]) -> Result<(), Error> { 53 | let kernel_addr_begin_physical = kernel_addr_begin_physical(); 54 | 55 | let mut usable_memory_regions = regions.iter().filter(|region| kernel_addr_begin_physical <= region.base_addr()); 56 | 57 | // TODO: Support multiple region. 58 | let free_memory_region = usable_memory_regions.nth(0).ok_or(Error::NoUsableMemory)?; 59 | 60 | // Use free memory region at the kernel tail. 61 | let free_region_addr_begin = kernel_addr_end_virtual(); 62 | let free_region_addr_end = (free_memory_region.base_addr() + free_memory_region.size()).to_virtual_addr(); 63 | let mut allocator = EarlyAllocator::new(free_region_addr_begin, free_region_addr_end); 64 | 65 | let (frames, count_frames) = allocate_frames(&mut allocator); 66 | 67 | // Add the size of `frames` to the kernel size. 68 | let base_addr = allocator.to_addr_begin().to_physical_addr(); 69 | update_kernel_addr_end_physical(base_addr); 70 | 71 | println!("free region: 0x{:x} - 0x{:x}, {}KB", base_addr, base_addr + count_frames * frame::SIZE, count_frames * frame::SIZE / 1024); 72 | 73 | // Initialize frames. 74 | unsafe { 75 | let base = base_addr / frame::SIZE; 76 | for (i, f) in core::slice::from_raw_parts_mut(frames.as_ptr(), count_frames).into_iter().enumerate() { 77 | f.set_number(base + i) 78 | } 79 | }; 80 | 81 | let mut bman = BuddyAllocator::new(base_addr, frames, count_frames, FrameAdapter::new()); 82 | println!("{} buddy objects", bman.count_free_objs()); 83 | 84 | paging::init(remap_requests, &mut bman)?; 85 | 86 | // Initialize Rust heap allocator. 87 | let allocator = HeapAllocator::new(&mut bman).ok_or(Error::NoUsableMemory)?; 88 | ALLOCATOR.init(allocator); 89 | 90 | let mut v = Box::new(100); 91 | *v = 200; 92 | 93 | Ok(()) 94 | } 95 | 96 | #[inline(always)] 97 | fn allocate_frames(allocator: &mut EarlyAllocator) -> (Unique, usize) { 98 | allocator.align_addr_begin(frame::SIZE); 99 | 100 | let struct_size = mem::size_of::(); 101 | let capacity = allocator.capacity(); 102 | let count_frames = capacity / frame::SIZE; 103 | let required_size = count_frames * struct_size; 104 | 105 | let capacity = capacity - required_size; 106 | let count_frames = capacity / frame::SIZE; 107 | 108 | let frames: Unique = allocator.allocate(count_frames); 109 | 110 | allocator.align_addr_begin(frame::SIZE); 111 | debug_assert!(count_frames <= (allocator.capacity() / frame::SIZE)); 112 | 113 | (frames, count_frames) 114 | } 115 | -------------------------------------------------------------------------------- /src/memory/address.rs: -------------------------------------------------------------------------------- 1 | //! Module for functions/traits of address utils. 2 | mod alignment; 3 | 4 | pub use alignment::*; 5 | use lazy_static::lazy_static; 6 | use spin::Mutex; 7 | 8 | pub type PhysicalAddress = usize; 9 | pub type VirtualAddress = usize; 10 | 11 | // These variable are defined in the linker script and their value do NOT have any meanings. 12 | // Their addressees have meanings. 13 | extern "C" { 14 | static KERNEL_ADDR_PHYSICAL_BEGIN: usize; 15 | static KERNEL_ADDR_PHYSICAL_END: usize; 16 | static KERNEL_ADDR_VIRTUAL_BEGIN: usize; 17 | static KERNEL_ADDR_BSS_BEGIN: usize; 18 | static KERNEL_ADDR_VIRTUAL_OFFSET: usize; 19 | static KERNEL_SIZE_BSS: usize; 20 | } 21 | 22 | lazy_static! { 23 | static ref CURRENT_KERNEL_ADDR_PHYSICAL_END: Mutex = Mutex::new(address_of(unsafe { &KERNEL_ADDR_PHYSICAL_END })); 24 | } 25 | 26 | #[inline(always)] 27 | pub fn kernel_addr_begin_virtual() -> VirtualAddress { 28 | address_of(unsafe { &KERNEL_ADDR_VIRTUAL_BEGIN }) 29 | } 30 | 31 | #[inline(always)] 32 | pub fn kernel_addr_begin_physical() -> PhysicalAddress { 33 | address_of(unsafe { &KERNEL_ADDR_PHYSICAL_BEGIN }) 34 | } 35 | 36 | #[inline(always)] 37 | pub fn kernel_addr_end_physical() -> PhysicalAddress { 38 | *(*CURRENT_KERNEL_ADDR_PHYSICAL_END).lock() 39 | } 40 | 41 | #[inline(always)] 42 | pub fn kernel_addr_end_virtual() -> VirtualAddress { 43 | kernel_addr_end_physical().to_virtual_addr() 44 | } 45 | 46 | #[inline(always)] 47 | pub fn update_kernel_addr_end_physical(new_addr: PhysicalAddress) { 48 | *(*CURRENT_KERNEL_ADDR_PHYSICAL_END).lock() = new_addr 49 | } 50 | 51 | #[inline(always)] 52 | pub fn kernel_bss_section_addr_begin() -> VirtualAddress { 53 | address_of(unsafe { &KERNEL_ADDR_BSS_BEGIN }) 54 | } 55 | 56 | #[inline(always)] 57 | pub fn kernel_bss_section_size() -> VirtualAddress { 58 | address_of(unsafe { &KERNEL_SIZE_BSS }) 59 | } 60 | 61 | #[inline(always)] 62 | pub fn address_of(obj: &T) -> VirtualAddress { 63 | (obj as *const _) as usize 64 | } 65 | 66 | pub trait ToPhysicalAddr { 67 | fn to_physical_addr(self) -> PhysicalAddress; 68 | } 69 | 70 | pub trait ToVirtualAddr { 71 | fn to_virtual_addr(self) -> VirtualAddress; 72 | } 73 | 74 | impl ToPhysicalAddr for VirtualAddress { 75 | fn to_physical_addr(self) -> PhysicalAddress { 76 | self - address_of(unsafe { &KERNEL_ADDR_VIRTUAL_OFFSET }) 77 | } 78 | } 79 | 80 | impl ToVirtualAddr for PhysicalAddress { 81 | fn to_virtual_addr(self) -> VirtualAddress { 82 | self + address_of(unsafe { &KERNEL_ADDR_VIRTUAL_OFFSET }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/memory/address/alignment.rs: -------------------------------------------------------------------------------- 1 | /// The trait to make n-byte aligned address (where n is a power of 2). 2 | /// However, this functions accept alignment 1. 3 | /// This means the number will not be changed. 4 | pub trait Alignment { 5 | fn align_up(self, alignment: Self) -> Self; 6 | fn align_down(self, alignment: Self) -> Self; 7 | } 8 | 9 | impl Alignment for usize { 10 | fn align_up(self, alignment: Self) -> Self { 11 | let mask = alignment - 1; 12 | (self + mask) & (!mask) 13 | } 14 | 15 | fn align_down(self, alignment: Self) -> Self { 16 | let mask = alignment - 1; 17 | self & (!mask) 18 | } 19 | } 20 | 21 | #[cfg(test)] 22 | mod test { 23 | use super::Alignment; 24 | 25 | #[test] 26 | fn test_alignment() { 27 | assert_eq!(0x1000.align_up(0x1000), 0x1000); 28 | assert_eq!(0x1000.align_down(0x1000), 0x1000); 29 | assert_eq!(0x1123.align_up(0x1000), 0x2000); 30 | assert_eq!(0x1123.align_down(0x1000), 0x1000); 31 | 32 | assert_eq!(0b1001.align_down(2), 0b1000); 33 | assert_eq!(0b1001.align_up(2), 0b1010); 34 | 35 | assert_eq!(0b1001.align_up(1), 0b1001); 36 | assert_eq!(0b1001.align_down(1), 0b1001); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/memory/buddy_system/allocator.rs: -------------------------------------------------------------------------------- 1 | use super::Object; 2 | use crate::memory::{Frame, FrameAllocator}; 3 | use core::mem; 4 | use core::ptr; 5 | use core::ptr::Unique; 6 | use intrusive_collections::{Adapter, IntrusivePointer, LinkedList, LinkedListLink, UnsafeRef}; 7 | 8 | const MAX_ORDER: usize = 15; 9 | 10 | pub struct BuddyAllocator, Value = T>, T: Object> { 11 | base_addr: usize, 12 | obj_ptr: Unique, 13 | obj_count: usize, 14 | free_lists: [LinkedList; MAX_ORDER], 15 | free_counts: [usize; MAX_ORDER], 16 | } 17 | 18 | impl, Value = T>, T: Object> BuddyAllocator { 19 | pub fn new(base_addr: usize, obj_ptr: Unique, count: usize, adapter: A) -> BuddyAllocator { 20 | let mut free_lists = unsafe { 21 | let mut lists: [LinkedList; MAX_ORDER] = mem::uninitialized(); 22 | 23 | for l in lists.iter_mut() { 24 | ptr::write(l, LinkedList::new(adapter.clone())) 25 | } 26 | 27 | lists 28 | }; 29 | 30 | // Insert each object into list which has suitable order. 31 | let mut index = 0; 32 | let mut free_counts = [0; MAX_ORDER]; 33 | let objs = unsafe { core::slice::from_raw_parts_mut(obj_ptr.as_ptr(), count) }; 34 | 35 | for i in 0..count { 36 | objs[i].reset_link(); 37 | } 38 | 39 | for order in (0..MAX_ORDER).rev() { 40 | let count_in_order = 1 << order; 41 | while count_in_order <= (count - index) { 42 | // Initialize object. 43 | let obj = &mut objs[index]; 44 | obj.set_order(order); 45 | obj.mark_free(); 46 | 47 | free_lists[order].push_back(unsafe { IntrusivePointer::from_raw(obj as _) }); 48 | free_counts[order] += 1; 49 | 50 | index += count_in_order; 51 | } 52 | } 53 | 54 | BuddyAllocator { 55 | base_addr, 56 | obj_ptr: obj_ptr, 57 | obj_count: count, 58 | free_lists: free_lists, 59 | free_counts: free_counts, 60 | } 61 | } 62 | 63 | fn is_managed_obj(&self, ptr: *const T) -> bool { 64 | let head_addr = self.obj_ptr.as_ptr() as usize; 65 | let tail_addr = unsafe { self.obj_ptr.as_ptr().offset((self.obj_count - 1) as isize) as usize }; 66 | 67 | let addr = ptr as usize; 68 | (head_addr <= addr) && (addr <= tail_addr) 69 | } 70 | 71 | fn buddy(&self, obj: UnsafeRef, order: usize) -> Option> { 72 | let raw = obj.into_raw(); 73 | debug_assert!(self.is_managed_obj(raw), "The given object is out of range"); 74 | 75 | if MAX_ORDER <= order { 76 | return None; 77 | } 78 | 79 | let index = raw.wrapping_offset_from(self.obj_ptr.as_ptr()); 80 | if (index < 0) || ((self.obj_count as isize) < index) { 81 | None 82 | } else { 83 | let buddy_index = index ^ (1 << (order as isize)); 84 | let addr = unsafe { self.obj_ptr.as_ptr().offset(buddy_index as isize) }; 85 | 86 | if self.is_managed_obj(addr) { 87 | Some(unsafe { UnsafeRef::::from_raw(addr) }) 88 | } else { 89 | None 90 | } 91 | } 92 | } 93 | 94 | pub fn allocate(&mut self, request_order: usize) -> Option> { 95 | if MAX_ORDER <= request_order { 96 | return None; 97 | } 98 | 99 | // Find last set instruction makes it more accelerate ? 100 | // 0001 1000 101 | // fls(map >> request_order) ? 102 | for order in request_order..MAX_ORDER { 103 | let obj = match self.free_lists[order].front_mut().remove() { 104 | Some(obj) => { 105 | self.free_counts[order] -= 1; 106 | 107 | obj.set_order(request_order); 108 | obj.mark_used(); 109 | obj 110 | } 111 | None => continue, 112 | }; 113 | 114 | // Push the extra frames. 115 | for i in request_order..order { 116 | if let Some(buddy_obj) = self.buddy(obj.clone(), i) { 117 | buddy_obj.set_order(i); 118 | buddy_obj.mark_free(); 119 | 120 | self.free_lists[i].push_back(buddy_obj); 121 | self.free_counts[i] += 1; 122 | } else { 123 | break; 124 | } 125 | } 126 | 127 | return Some(obj); 128 | } 129 | 130 | None 131 | } 132 | 133 | pub fn free(&mut self, mut obj: UnsafeRef) { 134 | debug_assert!(self.is_managed_obj(obj.clone().into_raw()), "The given object is out of range"); 135 | 136 | obj.mark_free(); 137 | 138 | for i in obj.order()..MAX_ORDER { 139 | if let Some(buddy_obj) = self.buddy(obj.clone(), i) { 140 | // println!("obj: {:p} vs buddy: {:p}", obj.clone().into_raw(), buddy_obj.clone().into_raw()); 141 | if buddy_obj.is_used() { 142 | break; 143 | } 144 | // Merge the two object into one large object. 145 | 146 | // Take out the free buddy object. 147 | let buddy_obj = unsafe { self.free_lists[i].cursor_mut_from_ptr(buddy_obj.into_raw()).remove().unwrap() }; 148 | self.free_counts[i] -= 1; 149 | 150 | // Select object which has smaller address. 151 | if buddy_obj.clone().into_raw() < obj.clone().into_raw() { 152 | obj = buddy_obj; 153 | obj.mark_free(); 154 | } 155 | 156 | obj.set_order(i + 1) 157 | } else { 158 | break; 159 | } 160 | } 161 | 162 | let order = obj.order(); 163 | self.free_lists[order].push_back(obj); 164 | self.free_counts[order] += 1; 165 | } 166 | 167 | pub fn count_free_objs(&self) -> usize { 168 | self.free_counts.iter().enumerate().fold(0, |acc, (order, count)| acc + count * (1 << order)) 169 | } 170 | } 171 | 172 | impl, Value = Frame>> FrameAllocator for BuddyAllocator { 173 | fn alloc_one(&mut self) -> Option { 174 | self.allocate(0).map(|frame| (*frame).clone()) 175 | } 176 | 177 | fn free(&mut self, f: Frame) { 178 | unsafe { 179 | use crate::memory::frame; 180 | let addr = self.obj_ptr.as_ptr().offset((f.number() - self.base_addr / frame::SIZE) as isize); 181 | self.free(UnsafeRef::from_raw(addr)); 182 | } 183 | } 184 | } 185 | 186 | #[cfg(test)] 187 | mod tests { 188 | use super::*; 189 | use crate::memory::frame::{Frame, FrameAdapter}; 190 | use std::alloc::{Alloc, Layout, System}; 191 | use std::mem; 192 | 193 | fn allocate_nodes(count: usize) -> *mut T { 194 | let type_size = mem::size_of::(); 195 | let align = mem::align_of::(); 196 | let layout = Layout::from_size_align(count * type_size, align).unwrap(); 197 | let ptr = unsafe { System.alloc(layout) }.unwrap(); 198 | 199 | ptr.as_ptr() as *mut _ 200 | } 201 | 202 | fn make_frame_allocator(count: usize) -> (*mut Frame, BuddyAllocator) { 203 | let nodes = allocate_nodes(count); 204 | let allocator = BuddyAllocator::new(0, unsafe { Unique::new_unchecked(nodes) }, count, FrameAdapter::new()); 205 | (nodes, allocator) 206 | } 207 | 208 | #[test] 209 | fn test_allocate_simple_split() { 210 | const SIZE: usize = 32; 211 | let (_, mut allocator) = make_frame_allocator(SIZE); 212 | 213 | assert_eq!(SIZE, allocator.count_free_objs()); 214 | 215 | let obj = allocator.allocate(4); 216 | assert!(obj.is_some()); 217 | assert_eq!(SIZE - (1 << 4), allocator.count_free_objs()); 218 | for i in 0..MAX_ORDER { 219 | if i == 4 { 220 | assert_eq!(1, allocator.free_counts[i]); 221 | } else { 222 | assert_eq!(0, allocator.free_counts[i]); 223 | } 224 | } 225 | 226 | allocator.free(obj.unwrap()); 227 | assert_eq!(SIZE, allocator.count_free_objs()); 228 | for i in 0..MAX_ORDER { 229 | if i == 5 { 230 | assert_eq!(1, allocator.free_counts[i]); 231 | } else { 232 | assert_eq!(0, allocator.free_counts[i]); 233 | } 234 | } 235 | } 236 | 237 | #[test] 238 | fn test_allocate_and_free() { 239 | const SIZE: usize = 32; 240 | let (_, mut allocator) = make_frame_allocator(SIZE); 241 | 242 | assert_eq!(SIZE, allocator.count_free_objs()); 243 | 244 | if let Some(obj) = allocator.allocate(2) { 245 | assert_eq!(2, obj.order()); 246 | assert_eq!(SIZE - 4, allocator.count_free_objs()); 247 | 248 | allocator.free(obj); 249 | assert_eq!(SIZE, allocator.count_free_objs()); 250 | } else { 251 | assert!(false) 252 | } 253 | } 254 | 255 | #[test] 256 | fn test_try_to_allocate_all() { 257 | const SIZE: usize = 32; 258 | let (_, mut allocator) = make_frame_allocator(SIZE); 259 | 260 | let mut list = LinkedList::new(FrameAdapter::new()); 261 | 262 | // Allocate the all objects. 263 | while let Some(obj) = allocator.allocate(0) { 264 | assert_eq!(0, obj.order()); 265 | list.push_back(obj); 266 | } 267 | 268 | assert_eq!(true, allocator.allocate(0).is_none()); 269 | assert_eq!(0, allocator.count_free_objs()); 270 | assert_eq!(SIZE, list.iter().count()); 271 | 272 | // Deallocate the all objects. 273 | for obj in list.into_iter() { 274 | allocator.free(obj); 275 | } 276 | 277 | assert_eq!(SIZE, allocator.count_free_objs()); 278 | } 279 | 280 | #[test] 281 | fn test_is_managed_obj() { 282 | const SIZE: usize = 32; 283 | let (nodes, mut allocator) = make_frame_allocator(SIZE); 284 | 285 | unsafe { 286 | // The first node. 287 | assert_eq!(true, allocator.is_managed_obj(nodes)); 288 | // The last node. 289 | assert_eq!(true, allocator.is_managed_obj(nodes.offset((SIZE - 1) as isize))); 290 | // Invalid nodes. 291 | assert_eq!(false, allocator.is_managed_obj(nodes.offset(-1))); 292 | assert_eq!(false, allocator.is_managed_obj(nodes.offset(SIZE as isize))); 293 | } 294 | } 295 | 296 | #[test] 297 | fn test_buddy() { 298 | const SIZE: usize = 1 << MAX_ORDER; 299 | let (nodes, mut allocator) = make_frame_allocator(SIZE); 300 | 301 | let compare_buddy = |expected_buddy_ptr: *mut Frame, (node, order): (UnsafeRef, usize)| -> bool { 302 | if let Some(buddy) = allocator.buddy(node, order) { 303 | ptr::eq(expected_buddy_ptr, buddy.into_raw()) 304 | } else { 305 | false 306 | } 307 | }; 308 | 309 | unsafe { 310 | let n0: UnsafeRef = UnsafeRef::from_raw(nodes as *const _); 311 | for i in 0..MAX_ORDER { 312 | assert_eq!(true, compare_buddy(nodes.offset(1 << i), (n0.clone(), i))); 313 | } 314 | assert_eq!(false, compare_buddy(nodes.offset(0), (n0, MAX_ORDER))); 315 | 316 | let n1: UnsafeRef = UnsafeRef::from_raw(nodes.offset(1) as *const _); 317 | assert_eq!(true, compare_buddy(nodes.offset(0), (n1.clone(), 0))); 318 | for i in 1..MAX_ORDER { 319 | assert_eq!(true, compare_buddy(nodes.offset((1 << i) + 1), (n1.clone(), i))); 320 | } 321 | assert_eq!(false, compare_buddy(nodes.offset(0), (n1, MAX_ORDER))); 322 | } 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/memory/buddy_system/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod allocator; 2 | 3 | pub use self::allocator::BuddyAllocator; 4 | 5 | pub trait Object { 6 | fn reset_link(&mut self); 7 | fn mark_used(&self); 8 | fn mark_free(&self); 9 | fn is_used(&self) -> bool; 10 | fn order(&self) -> usize; 11 | fn set_order(&self, order: usize); 12 | 13 | fn size(&self) -> usize { 14 | 1 << self.order() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/memory/early_allocator.rs: -------------------------------------------------------------------------------- 1 | //! The `EarlyAllocator` is the simplest memory allocator. 2 | //! It cutouts a memory region from the given memory region. 3 | 4 | use super::address::Alignment; 5 | use core::mem; 6 | use core::ptr::Unique; 7 | 8 | pub struct EarlyAllocator { 9 | pub addr_begin: usize, 10 | addr_end: usize, 11 | } 12 | 13 | impl EarlyAllocator { 14 | pub fn new(addr_begin: usize, addr_end: usize) -> EarlyAllocator { 15 | debug_assert!(addr_begin < addr_end); 16 | 17 | EarlyAllocator { addr_begin, addr_end } 18 | } 19 | 20 | pub fn capacity(&self) -> usize { 21 | self.addr_end - self.addr_begin 22 | } 23 | 24 | pub fn align_addr_begin(&mut self, alignment: usize) { 25 | self.addr_begin = self.addr_begin.align_up(alignment); 26 | } 27 | 28 | pub fn allocate(&mut self, count: usize) -> Unique { 29 | self.align_addr_begin(mem::align_of::()); 30 | let addr = self.addr_begin; 31 | let size = mem::size_of::() * count; 32 | self.addr_begin += size; 33 | 34 | unsafe { Unique::new_unchecked(addr as *mut _) } 35 | } 36 | 37 | pub fn to_addr_begin(self) -> usize { 38 | self.addr_begin 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod test { 44 | use super::EarlyAllocator; 45 | use std::mem; 46 | 47 | #[test] 48 | fn test_capacity() { 49 | let eallocator = EarlyAllocator::new(0x0000, 0x1000); 50 | 51 | assert_eq!(eallocator.capacity(), 0x1000); 52 | } 53 | 54 | struct Object { 55 | hoge: usize, 56 | } 57 | 58 | #[test] 59 | fn test_allocate() { 60 | let mut eallocator = EarlyAllocator::new(0x0000, 0x1000); 61 | let mut capacity = eallocator.capacity(); 62 | 63 | let _ = eallocator.allocate::(10); 64 | capacity -= mem::size_of::() * 10; 65 | assert_eq!(eallocator.capacity(), capacity); 66 | 67 | let _ = eallocator.allocate::(50); 68 | capacity -= mem::size_of::() * 50; 69 | assert_eq!(eallocator.capacity(), capacity); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/memory/frame.rs: -------------------------------------------------------------------------------- 1 | //! `Frame` define a physical memory region. 2 | //! The size is 4096 and it corresponds page size. 3 | pub const SIZE: usize = 4096; 4 | 5 | use super::buddy_system::Object; 6 | use super::VirtualAddress; 7 | use core::cell::Cell; 8 | use intrusive_collections::{intrusive_adapter, LinkedListLink, UnsafeRef}; 9 | 10 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 11 | enum State { 12 | Used, 13 | Free, 14 | } 15 | 16 | #[derive(Clone, Debug)] 17 | pub struct Frame { 18 | link: LinkedListLink, 19 | number: usize, 20 | order: Cell, 21 | state: Cell, 22 | } 23 | 24 | intrusive_adapter!(pub FrameAdapter = UnsafeRef: Frame { link: LinkedListLink }); 25 | 26 | impl Frame { 27 | pub fn from_address(addr: VirtualAddress) -> Frame { 28 | Frame { 29 | link: LinkedListLink::new(), 30 | order: Cell::new(0), 31 | state: Cell::new(State::Free), 32 | number: addr / SIZE, 33 | } 34 | } 35 | 36 | pub fn set_number(&mut self, i: usize) { 37 | self.number = i; 38 | } 39 | 40 | pub fn number(&self) -> usize { 41 | self.number 42 | } 43 | 44 | pub fn order(&self) -> usize { 45 | self.order.get() 46 | } 47 | 48 | // pub fn level4_index(&self) -> usize { 49 | // (self.number >> 27) & 0o777 50 | // } 51 | 52 | // pub fn level3_index(&self) -> usize { 53 | // (self.number >> 18) & 0o777 54 | // } 55 | 56 | // pub fn level2_index(&self) -> usize { 57 | // (self.number >> 9) & 0o777 58 | // } 59 | 60 | // pub fn level1_index(&self) -> usize { 61 | // (self.number >> 0) & 0o777 62 | // } 63 | 64 | pub fn address(&self) -> usize { 65 | self.number * SIZE 66 | } 67 | } 68 | 69 | impl Object for Frame { 70 | fn reset_link(&mut self) { 71 | self.link = LinkedListLink::new(); 72 | } 73 | 74 | fn mark_used(&self) { 75 | self.state.set(State::Used); 76 | } 77 | 78 | fn mark_free(&self) { 79 | self.state.set(State::Free); 80 | } 81 | 82 | fn is_used(&self) -> bool { 83 | self.state.get() == State::Used 84 | } 85 | 86 | fn order(&self) -> usize { 87 | self.order() 88 | } 89 | 90 | fn set_order(&self, order: usize) { 91 | self.order.set(order); 92 | } 93 | } 94 | 95 | // #[cfg(test)] 96 | // mod tests { 97 | // #[test] 98 | // fn test_index() { 99 | // let f = Frame::from_address(0x00000); 100 | // 101 | // assert_eq!(f.level4_index(), 0); 102 | // assert_eq!(f.level3_index(), 0); 103 | // assert_eq!(f.level2_index(), 0); 104 | // assert_eq!(f.level1_index(), 0); 105 | // 106 | // let f = Frame::from_address(0o456_555_456_123_0000); 107 | // assert_eq!(f.level4_index(), 0o456); 108 | // assert_eq!(f.level3_index(), 0o555); 109 | // assert_eq!(f.level2_index(), 0o456); 110 | // assert_eq!(f.level1_index(), 0o123); 111 | // } 112 | // } 113 | -------------------------------------------------------------------------------- /src/memory/frame_allocator.rs: -------------------------------------------------------------------------------- 1 | use crate::memory::Frame; 2 | 3 | pub trait FrameAllocator { 4 | fn alloc_one(&mut self) -> Option; 5 | fn free(&mut self, f: Frame); 6 | } 7 | -------------------------------------------------------------------------------- /src/memory/global_allocator.rs: -------------------------------------------------------------------------------- 1 | use super::{address::Alignment, BuddyAllocator, Frame, FrameAdapter, ACTIVE_PAGE_TABLE}; 2 | use core::alloc::{GlobalAlloc, Layout}; 3 | use intrusive_collections::LinkedList; 4 | use spin::Mutex; 5 | 6 | /// Wrapper struct for virtual memory allocator (`Allocator`). 7 | /// The purpose is just to provide lock. 8 | pub struct GlobalAllocator(Mutex>); 9 | 10 | unsafe impl GlobalAlloc for GlobalAllocator { 11 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 12 | if let Some(ref mut allocator) = *self.0.lock() { 13 | if let Some(ptr) = allocator.alloc(layout) { 14 | ptr 15 | } else { 16 | panic!("No memory") 17 | } 18 | } else { 19 | panic!("GlobalAllocator is not initialized yet") 20 | } 21 | } 22 | 23 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 24 | if let Some(ref allocator) = *self.0.lock() { 25 | allocator.dealloc(ptr, layout) 26 | } else { 27 | panic!("GlobalAllocator is not initialized yet") 28 | } 29 | } 30 | } 31 | 32 | #[cfg(not(test))] 33 | #[alloc_error_handler] 34 | fn alloc_error(layout: Layout) -> ! { 35 | panic!("Allocation error: {:?}", layout) 36 | } 37 | 38 | impl GlobalAllocator { 39 | pub const fn new() -> GlobalAllocator { 40 | GlobalAllocator(Mutex::new(None)) 41 | } 42 | 43 | pub fn init(&self, allocator: HeapAllocator) { 44 | *self.0.lock() = Some(allocator); 45 | } 46 | } 47 | 48 | pub struct HeapAllocator { 49 | // used_pages: LinkedList, 50 | addr_begin: usize, 51 | addr_end: usize, 52 | } 53 | 54 | impl HeapAllocator { 55 | pub fn new(allocator: &mut BuddyAllocator) -> Option { 56 | let mut pages = LinkedList::new(FrameAdapter::new()); 57 | 58 | // 2^5 * 4096 = 128KB. 59 | let frame = allocator.allocate(5)?; 60 | let (addr_begin, addr_end) = ACTIVE_PAGE_TABLE.lock().auto_continuous_map(&frame, allocator)?; 61 | 62 | pages.push_front(frame); 63 | 64 | // Some(HeapAllocator { used_pages: pages, addr_begin, addr_end }) 65 | Some(HeapAllocator { addr_begin, addr_end }) 66 | } 67 | } 68 | 69 | impl HeapAllocator { 70 | fn alloc(&mut self, layout: Layout) -> Option<*mut u8> { 71 | self.addr_begin.align_up(layout.align()); 72 | 73 | if self.addr_end <= self.addr_begin + layout.size() { 74 | return None; 75 | } 76 | 77 | let addr = self.addr_begin; 78 | self.addr_begin += layout.size(); 79 | 80 | Some(addr as *mut u8) 81 | } 82 | 83 | fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} 84 | } 85 | -------------------------------------------------------------------------------- /src/memory/paging.rs: -------------------------------------------------------------------------------- 1 | //! The name conversions are defined because Intel's names are confusable I think. 2 | //! Name conversions: 3 | //! Frame := Physical memory block 4 | //! Page := Virtual memory block 5 | //! Level4 Table/Entry := PML4E, Page Map Level 4 Table/Entry 6 | //! Level3 Table/Entry := PDPTE, Page Directory Pointer Table/Entry 7 | //! Level2 Table/Entry := PDE, Page Directory Table/Entry 8 | //! Level1 Table/Entry := PTE, Page Table/Entry 9 | //! 10 | //! In x86_64 system, the virtual address is composed of four parts (in 4KB mapping): 11 | //! Note that the virtual address is called linear address in the intel manual. 12 | //! 9bit(Index of level 4) 13 | //! 9bit(Index of level 3) 14 | //! 9bit(Index of level 2) 15 | //! 9bit(Index of level 1) 16 | //! 12bit(Physical address offset) 17 | //! 9 + 9 + 9 + 9 + 12 = 48 18 | //! ------------------------------------------------ 19 | //! | Level4 | Level3 | Level2 | Level1 | Offset | 20 | //! ------------------------------------------------ 21 | //! 47 39 38 30 29 22 20 12 11 0 22 | //! 23 | //! Each index is represented by 9bit. 24 | //! This means the number of the entries in each table is 512 (2^9) respectively. 25 | //! Then, the size of an entry is 8bytes. 26 | //! So, The size of a table is (8 * 512 = 4096 = 4KB) and it corresponds the minimum page size. 27 | //! 28 | //! For more information, please refer 4.5 IA-32E PAGING in the intel manual. 29 | //! 30 | //! Reference: 31 | //! [Page Tables](http://os.phil-opp.com/modifying-page-tables.html) 32 | 33 | mod entry; 34 | mod page; 35 | mod page_index; 36 | pub mod table; 37 | 38 | use super::address; 39 | use super::address::*; 40 | use super::buddy_system::BuddyAllocator; 41 | use super::frame; 42 | use super::Error; 43 | use super::{Frame, FrameAdapter, FrameAllocator}; 44 | use crate::bytes::Bytes; 45 | pub use page::Page; 46 | pub use page_index::PageIndex; 47 | pub use table::{ActivePageTable, InActivePageTable, ACTIVE_PAGE_TABLE}; 48 | 49 | pub struct IdenticalReMapRequest(PhysicalAddress, usize); 50 | 51 | impl IdenticalReMapRequest { 52 | pub fn new(addr: PhysicalAddress, count: usize) -> IdenticalReMapRequest { 53 | IdenticalReMapRequest(addr, count) 54 | } 55 | } 56 | 57 | pub fn init(remap_requests: &[IdenticalReMapRequest], bman: &mut BuddyAllocator) -> Result<(), Error> { 58 | let mut active_page_table = ACTIVE_PAGE_TABLE.lock(); 59 | 60 | runtime_test(&mut active_page_table, bman)?; 61 | 62 | let kernel_begin = address::kernel_addr_begin_virtual(); 63 | let kernel_end = address::kernel_addr_end_physical().to_virtual_addr(); 64 | println!("Kernel virtual address: 0x{:x} - 0x{:x}, {}KB", kernel_begin, kernel_end, (kernel_end - kernel_begin).kb()); 65 | debug_assert!(kernel_begin < kernel_end); 66 | debug_assert_eq!(0, kernel_begin & 0xfff); 67 | debug_assert_eq!(0, kernel_end & 0xfff); 68 | 69 | // Create new kernel page table. 70 | let mut inactive_page_table = InActivePageTable::new(&mut active_page_table, bman).ok_or(Error::NoUsableMemory)?; 71 | active_page_table.with(&mut inactive_page_table, bman, |table, allocator| { 72 | let v_range = (kernel_begin, kernel_end); 73 | let p_range = (address::kernel_addr_begin_physical(), address::kernel_addr_end_physical()); 74 | 75 | for IdenticalReMapRequest(addr, count) in remap_requests { 76 | // debug_assert!(*addr < kernel_begin || kernel_end <= *addr); 77 | // debug_assert!((addr + frame::SIZE * count) < kernel_begin || kernel_end <= (addr + frame::SIZE * count)); 78 | 79 | for i in 0..*count { 80 | let addr = addr + i * frame::SIZE; 81 | let page = Page::from_address(addr.to_virtual_addr()); 82 | let frame = Frame::from_address(addr); 83 | table.map(page, frame, allocator)?; 84 | } 85 | } 86 | 87 | table.map_fitting(v_range, p_range, allocator) 88 | })?; 89 | 90 | // Switch the active page table to new one. 91 | let _old_table = active_page_table.switch(inactive_page_table); 92 | // TODO: Free the old table. 93 | 94 | Ok(()) 95 | } 96 | 97 | #[inline(always)] 98 | pub fn runtime_test(active_page_table: &mut ActivePageTable, allocator: &mut dyn FrameAllocator) -> Result<(), Error> { 99 | // Runtime test. 100 | let frame = allocator.alloc_one().ok_or(Error::NoUsableMemory)?; 101 | 102 | // FIXME: use more proper address. 103 | let mut page = Page::from_address(0x200000); 104 | active_page_table.map(page.clone(), frame, allocator)?; 105 | 106 | // It will not cause page fault. 107 | unsafe { 108 | for i in page.to_slice_mut() { 109 | *i = 1; 110 | } 111 | } 112 | 113 | active_page_table.unmap(page, allocator).map_err(Into::into) 114 | } 115 | -------------------------------------------------------------------------------- /src/memory/paging/entry.rs: -------------------------------------------------------------------------------- 1 | use crate::memory::address::PhysicalAddress; 2 | use bitflags::bitflags; 3 | use core::fmt; 4 | 5 | bitflags! { 6 | pub struct PageEntryFlags: usize { 7 | const PRESENT = 1 << 0; 8 | const WRITABLE = 1 << 1; 9 | const USER_ACCESSIBLE = 1 << 2; 10 | const WRITE_THROUGH = 1 << 3; 11 | const CACHE_DISABLE = 1 << 4; 12 | const ACCESSED = 1 << 5; 13 | const DIRTY = 1 << 6; 14 | const HUGE_PAGE = 1 << 7; 15 | const GLOBAL = 1 << 8; 16 | const NO_EXECUTE = 1 << 63; 17 | } 18 | } 19 | 20 | /// A page entry. 21 | #[derive(Debug, Clone)] 22 | pub struct PageEntry(usize); 23 | 24 | impl PageEntry { 25 | /// Return `EntryFlags`. 26 | pub fn flags(&self) -> PageEntryFlags { 27 | PageEntryFlags::from_bits_truncate(self.0) 28 | } 29 | 30 | pub fn set_flags(&mut self, flags: PageEntryFlags) { 31 | self.0 = self.0 | (self.flags() | flags).bits(); 32 | } 33 | 34 | /// Clear the all flags (set the all flags zero). 35 | pub fn clear_all(&mut self) { 36 | self.0 = 0; 37 | } 38 | 39 | pub fn set_frame_addr(&mut self, addr: PhysicalAddress) { 40 | debug_assert_eq!(addr & 0xFFF, 0); 41 | debug_assert_eq!(addr & 0xFFFF_0000_0000_0000, 0); 42 | let flags = self.flags(); 43 | self.0 = addr; 44 | self.set_flags(flags | PageEntryFlags::PRESENT); 45 | } 46 | 47 | pub fn get_frame_addr(&self) -> Option { 48 | if self.flags().contains(PageEntryFlags::PRESENT) { 49 | Some(self.0 & !0xFFF) 50 | } else { 51 | None 52 | } 53 | } 54 | } 55 | 56 | impl fmt::Display for PageEntry { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 58 | write!(f, "{}", self.0) 59 | } 60 | } 61 | 62 | impl fmt::LowerHex for PageEntry { 63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 64 | write!(f, "{:016x}", self.0) 65 | } 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::*; 71 | 72 | #[test] 73 | fn test_flags() { 74 | let e = PageEntry(0xFFFF_FFFF_FFFF_FF00); 75 | assert_eq!(false, e.flags().contains(PageEntryFlags::PRESENT)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/memory/paging/page.rs: -------------------------------------------------------------------------------- 1 | //! `Page` define a virtual memory region. 2 | use crate::memory::address::VirtualAddress; 3 | 4 | pub const SIZE: usize = 4096; 5 | 6 | #[derive(Debug, Clone)] 7 | pub struct Page { 8 | number: usize, 9 | } 10 | 11 | impl Page { 12 | // pub fn number(&self) -> usize { 13 | // self.number 14 | // } 15 | 16 | pub fn address(&self) -> VirtualAddress { 17 | self.number * SIZE 18 | } 19 | 20 | pub fn from_address(addr: VirtualAddress) -> Page { 21 | Page { number: addr / SIZE } 22 | } 23 | 24 | pub fn from_number(n: usize) -> Page { 25 | Page { number: n } 26 | } 27 | 28 | pub unsafe fn to_slice_mut(&mut self) -> &mut [T] { 29 | core::slice::from_raw_parts_mut(self.address() as *mut T, 4096 / core::mem::size_of::()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/memory/paging/page_index.rs: -------------------------------------------------------------------------------- 1 | use crate::memory::address::VirtualAddress; 2 | 3 | pub trait PageIndex { 4 | fn level4_index(self) -> usize; 5 | fn level3_index(self) -> usize; 6 | fn level2_index(self) -> usize; 7 | fn level1_index(self) -> usize; 8 | fn offset(self) -> usize; 9 | } 10 | 11 | impl PageIndex for VirtualAddress { 12 | fn level4_index(self) -> usize { 13 | (self >> 39) & 0o777 14 | } 15 | 16 | fn level3_index(self) -> usize { 17 | (self >> 30) & 0o777 18 | } 19 | 20 | fn level2_index(self) -> usize { 21 | (self >> 21) & 0o777 22 | } 23 | 24 | fn level1_index(self) -> usize { 25 | (self >> 12) & 0o777 26 | } 27 | 28 | fn offset(self) -> usize { 29 | self & 0xFFF 30 | } 31 | } 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use super::*; 36 | use crate::memory::address::VirtualAddress; 37 | 38 | #[test] 39 | fn test_index_functions() { 40 | let addr: VirtualAddress = 0o123_456_712_345_1234; 41 | 42 | assert_eq!(addr.offset(), 0o1234); 43 | assert_eq!(addr.level1_index(), 0o345); 44 | assert_eq!(addr.level2_index(), 0o712); 45 | assert_eq!(addr.level3_index(), 0o456); 46 | assert_eq!(addr.level4_index(), 0o123); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/memory/paging/table.rs: -------------------------------------------------------------------------------- 1 | use super::entry::{PageEntry, PageEntryFlags}; 2 | use super::page; 3 | use super::{Page, PageIndex}; 4 | use crate::memory::address::{PhysicalAddress, VirtualAddress}; 5 | use crate::memory::frame::{self, Frame}; 6 | use crate::memory::FrameAllocator; 7 | use core::marker::PhantomData; 8 | use core::ops::{Index, IndexMut}; 9 | use core::ptr::Unique; 10 | use failure::Fail; 11 | use spin::Mutex; 12 | use x86_64::instructions::tlb; 13 | use x86_64::registers; 14 | use x86_64::structures::paging::PhysFrame; 15 | 16 | #[derive(Fail, Debug)] 17 | pub enum Error { 18 | #[fail(display = "No page table")] 19 | NoPageTable, 20 | #[fail(display = "Already mapped page")] 21 | AlreadyMapped, 22 | #[fail(display = "There is no page table entry")] 23 | NoEntry, 24 | } 25 | 26 | /// Signature trait for manipulating enries in the `Table` struct. 27 | pub trait Level {} 28 | 29 | /// It provides trait bound for generating next level page table struct. 30 | pub trait HierarchicalLevel: Level { 31 | type NextLevel: Level; 32 | } 33 | 34 | /// Signature struct for Level1 page table. 35 | pub struct Level1; 36 | 37 | /// Signature struct for Level2 page table. 38 | pub struct Level2; 39 | 40 | /// Signature struct for Level3 page table. 41 | pub struct Level3; 42 | 43 | /// Signature struct for Level4 page table. 44 | pub struct Level4; 45 | 46 | /// A page table. 47 | pub struct Table 48 | where 49 | T: Level, 50 | { 51 | entries: [PageEntry; MAX_ENTRY_COUNT], 52 | level: PhantomData, 53 | } 54 | 55 | impl Level for Level4 {} 56 | impl Level for Level3 {} 57 | impl Level for Level2 {} 58 | impl Level for Level1 {} 59 | 60 | impl HierarchicalLevel for Level4 { 61 | type NextLevel = Level3; 62 | } 63 | impl HierarchicalLevel for Level3 { 64 | type NextLevel = Level2; 65 | } 66 | impl HierarchicalLevel for Level2 { 67 | type NextLevel = Level1; 68 | } 69 | 70 | const LEVEL4_PAGE_TABLE: *mut Table = 0xffff_ffff_ffff_f000 as *mut _; 71 | const MAX_ENTRY_COUNT: usize = 512; 72 | 73 | impl Table 74 | where 75 | T: Level, 76 | { 77 | fn clear_all_entries(&mut self) { 78 | for entry in self.entries.iter_mut() { 79 | entry.clear_all(); 80 | } 81 | } 82 | 83 | pub fn find_free_entry_index(&self) -> Option { 84 | self.find_free_entry_index_with(1) 85 | } 86 | 87 | /// Find begin index of N free pages. 88 | pub fn find_free_entry_index_with(&self, count: usize) -> Option { 89 | let mut index = None; 90 | let mut c = count; 91 | 92 | // TODO: implement Iter. 93 | // Start from 1 to avoid using null. 94 | for i in 1..512 { 95 | if self[i].flags().contains(PageEntryFlags::PRESENT) == false { 96 | if index.is_none() { 97 | index = Some(i) 98 | } 99 | 100 | c -= 1; 101 | 102 | if c == 0 { 103 | return index; 104 | } 105 | } else { 106 | c = count 107 | } 108 | } 109 | 110 | None 111 | } 112 | } 113 | 114 | impl Table 115 | where 116 | T: HierarchicalLevel, 117 | { 118 | fn next_level_table_address(&self, index: usize) -> Option { 119 | let flags = self[index].flags(); 120 | if flags.contains(PageEntryFlags::PRESENT) && !flags.contains(PageEntryFlags::HUGE_PAGE) { 121 | Some((((self as *const _) as usize) << 9) | (index << 12)) 122 | } else { 123 | None 124 | } 125 | } 126 | 127 | // pub fn next_level_table<'a>(&'a self, index: usize) -> Option<&'a Table> { 128 | // self.next_level_table_address(index).map(|address| unsafe { &*(address as *const _) }) 129 | // } 130 | 131 | pub fn next_level_table_mut<'a>(&'a self, index: usize) -> Option<&'a mut Table> { 132 | self.next_level_table_address(index).map(|address| unsafe { &mut *(address as *mut _) }) 133 | } 134 | 135 | pub fn next_level_table_create_mut(&mut self, index: usize, allocator: &mut dyn FrameAllocator) -> Option<&mut Table> { 136 | // TODO; refactor 137 | if self.next_level_table_mut(index).is_some() { 138 | return self.next_level_table_mut(index); 139 | } 140 | 141 | if let Some(frame) = allocator.alloc_one() { 142 | self[index].set_frame_addr(frame.address()); 143 | if let Some(table) = self.next_level_table_mut(index) { 144 | table.clear_all_entries(); 145 | 146 | let entry = &mut table[511]; 147 | entry.set_frame_addr(frame.address()); 148 | entry.set_flags(PageEntryFlags::WRITABLE); 149 | 150 | Some(table) 151 | } else { 152 | debug_assert!(false, "error"); 153 | None 154 | } 155 | } else { 156 | // No memory. 157 | None 158 | } 159 | } 160 | } 161 | 162 | impl Index for Table 163 | where 164 | T: Level, 165 | { 166 | type Output = PageEntry; 167 | 168 | fn index(&self, index: usize) -> &PageEntry { 169 | &self.entries[index] 170 | } 171 | } 172 | 173 | impl IndexMut for Table 174 | where 175 | T: Level, 176 | { 177 | fn index_mut(&mut self, index: usize) -> &mut PageEntry { 178 | &mut self.entries[index] 179 | } 180 | } 181 | 182 | pub static ACTIVE_PAGE_TABLE: Mutex = Mutex::new(ActivePageTable { 183 | level4_page_table: unsafe { Unique::new_unchecked(LEVEL4_PAGE_TABLE) }, 184 | }); 185 | 186 | pub struct ActivePageTable { 187 | level4_page_table: Unique>, 188 | } 189 | 190 | impl ActivePageTable { 191 | // pub fn level4_page_table(&self) -> &Table { 192 | // unsafe { self.level4_page_table.as_ref() } 193 | // } 194 | 195 | pub fn level4_page_table_mut(&mut self) -> &mut Table { 196 | unsafe { self.level4_page_table.as_mut() } 197 | } 198 | 199 | // pub fn get_physical_address(&self, addr: VirtualAddress) -> Option { 200 | // debug_assert!(addr <= 0x0000_8000_0000_0000 || 0xffff_8000_0000_0000 <= addr, "0x{:x} is invalid address", addr); 201 | // 202 | // let level3_table = self.level4_page_table().next_level_table(addr.level4_index()); 203 | // 204 | // level3_table 205 | // .and_then(|t| t.next_level_table(addr.level3_index())) 206 | // .and_then(|t| t.next_level_table(addr.level2_index())) 207 | // .and_then(|t| t[addr.level1_index()].get_frame_addr()) 208 | // .or_else(|| { 209 | // level3_table.and_then(|t3| { 210 | // let entry = &t3[addr.level3_index()]; 211 | // // Return address here if the page size is 1GB. 212 | // if entry.flags().contains(PageEntryFlags::HugePage) { 213 | // return entry.get_frame_addr(); 214 | // } 215 | // 216 | // if let Some(t2) = t3.next_level_table(addr.level3_index()) { 217 | // let entry = &t2[addr.level2_index()]; 218 | // if entry.flags().contains(PageEntryFlags::HugePage) { 219 | // return entry.get_frame_addr(); 220 | // } 221 | // } 222 | // 223 | // None 224 | // }) 225 | // }) 226 | // } 227 | 228 | /// FIXME: support N:N mapping. 229 | pub fn map(&mut self, page: Page, frame: Frame, allocator: &mut dyn FrameAllocator) -> Result<(), Error> { 230 | let page_addr = page.address(); 231 | let frame_addr = frame.address(); 232 | 233 | let table = self 234 | .level4_page_table_mut() 235 | .next_level_table_create_mut(page_addr.level4_index(), allocator) 236 | .and_then(|t| t.next_level_table_create_mut(page_addr.level3_index(), allocator)) 237 | .and_then(|t| t.next_level_table_create_mut(page_addr.level2_index(), allocator)); 238 | 239 | if let Some(table) = table { 240 | let entry = &mut table[page_addr.level1_index()]; 241 | if entry.flags().contains(PageEntryFlags::PRESENT) == false { 242 | entry.set_frame_addr(frame_addr); 243 | Ok(()) 244 | } else { 245 | Err(Error::AlreadyMapped) 246 | } 247 | } else { 248 | Err(Error::NoPageTable) 249 | } 250 | } 251 | 252 | // TODO: consider suitable name. 253 | pub fn map_fitting(&mut self, v_range: (VirtualAddress, VirtualAddress), p_range: (PhysicalAddress, PhysicalAddress), allocator: &mut dyn FrameAllocator) -> Result<(), Error> { 254 | let (v_begin, v_end) = v_range; 255 | let (p_begin, p_end) = p_range; 256 | debug_assert_eq!(v_end - v_begin, p_end - p_begin); 257 | 258 | let size = v_end - v_begin; 259 | let count_2mb = size / (2 * 1024 * 1024); 260 | let count_4kb = (size - count_2mb * 2 * 1024 * 1024) / 4096; 261 | 262 | // FIXME: Revert mappings if mapping fails in these process. 263 | let mut offset = 0; 264 | 265 | if 0 < count_2mb { 266 | unimplemented!("TODO: Implement mapping for 2MB pages") 267 | } 268 | 269 | if 0 < count_4kb { 270 | // FIXME: Re-implement them more effectively. 271 | for _ in 0..count_4kb { 272 | let page = Page::from_address(v_begin + offset); 273 | let frame = Frame::from_address(p_begin + offset); 274 | 275 | self.map(page, frame, allocator)?; 276 | 277 | offset += 4096; 278 | } 279 | } 280 | 281 | Ok(()) 282 | } 283 | 284 | pub fn auto_continuous_map(&mut self, frame: &Frame, allocator: &mut dyn FrameAllocator) -> Option<(VirtualAddress, VirtualAddress)> { 285 | let frame_count = 1 << frame.order(); 286 | let size = frame_count * frame::SIZE; 287 | let page = self.find_empty_pages(frame_count, allocator)?; 288 | 289 | for i in 0..frame_count { 290 | let frame = Frame::from_address(frame.address() + i * frame::SIZE); 291 | let page = Page::from_address(page.address() + i * page::SIZE); 292 | 293 | if self.map(page, frame, allocator).is_err() { 294 | panic!("FIXME") 295 | } 296 | } 297 | 298 | let addr = page.address(); 299 | Some((addr, addr + size)) 300 | } 301 | 302 | pub fn unmap(&mut self, page: Page, allocator: &mut dyn FrameAllocator) -> Result<(), Error> { 303 | let addr = page.address(); 304 | 305 | // FIXME: Support huge page. 306 | let table = self 307 | .level4_page_table_mut() 308 | .next_level_table_mut(addr.level4_index()) 309 | .and_then(|t| t.next_level_table_mut(addr.level3_index())) 310 | .and_then(|t| t.next_level_table_mut(addr.level2_index())) 311 | .ok_or(Error::NoEntry)?; 312 | 313 | let entry = &mut table[addr.level1_index()]; 314 | let addr = entry.get_frame_addr().ok_or(Error::NoEntry)?; 315 | allocator.free(Frame::from_address(addr)); 316 | 317 | entry.clear_all(); 318 | Ok(()) 319 | } 320 | 321 | pub fn find_empty_page(&mut self, allocator: &mut dyn FrameAllocator) -> Option { 322 | self.find_empty_pages(1, allocator) 323 | } 324 | 325 | // FIXME: support large page. 326 | pub fn find_empty_pages(&mut self, count: usize, allocator: &mut dyn FrameAllocator) -> Option { 327 | let table = self.level4_page_table_mut(); 328 | 329 | let mut i4: usize = 0; 330 | let mut i3: usize = 0; 331 | let mut i2: usize = 0; 332 | table 333 | .find_free_entry_index() 334 | .and_then(|i| { 335 | i4 = i; 336 | table.next_level_table_create_mut(i, allocator) 337 | }) 338 | .and_then(|table| { 339 | if let Some(i) = table.find_free_entry_index() { 340 | i3 = i; 341 | table.next_level_table_create_mut(i, allocator) 342 | } else { 343 | None 344 | } 345 | }) 346 | .and_then(|table| { 347 | if let Some(i) = table.find_free_entry_index() { 348 | i2 = i; 349 | table.next_level_table_create_mut(i, allocator) 350 | } else { 351 | None 352 | } 353 | }) 354 | .and_then(|table| { 355 | if let Some(i1) = table.find_free_entry_index_with(count) { 356 | let i = i4 * 512 * 512 * 512 + i3 * 512 * 512 + i2 * 512 + i1; 357 | Some(Page::from_number(i)) 358 | } else { 359 | None 360 | } 361 | }) 362 | } 363 | 364 | pub fn with(&mut self, inactive_page_table: &mut InActivePageTable, allocator: &mut dyn FrameAllocator, f: (impl Fn(&mut ActivePageTable, &mut dyn FrameAllocator) -> Result<(), Error>)) -> Result<(), Error> { 365 | // Keep the current active page table entry to restore it. 366 | let addr = registers::control::Cr3::read().0.start_address().as_u64() as usize; 367 | let original_table_frame = Frame::from_address(addr); 368 | let page = self.find_empty_page(allocator).ok_or(Error::NoEntry)?; 369 | self.map(page.clone(), original_table_frame, allocator)?; 370 | let original_table = unsafe { &mut *(page.address() as *mut Table) }; 371 | 372 | // Override the recursive mapping. 373 | let entry = &mut self.level4_page_table_mut()[511]; 374 | entry.set_frame_addr(inactive_page_table.level4_page_table.address()); 375 | entry.set_flags(PageEntryFlags::WRITABLE); 376 | tlb::flush_all(); 377 | 378 | let result = f(self, allocator); 379 | 380 | // Restore the active page table entry. 381 | let entry = &mut original_table[511]; 382 | entry.set_frame_addr(addr); 383 | entry.set_flags(PageEntryFlags::WRITABLE); 384 | 385 | tlb::flush_all(); 386 | 387 | result 388 | } 389 | 390 | pub fn switch(&mut self, new_table: InActivePageTable) -> InActivePageTable { 391 | let old_table = InActivePageTable { 392 | level4_page_table: Frame::from_address(registers::control::Cr3::read().0.start_address().as_u64() as usize), 393 | }; 394 | 395 | let addr = x86_64::PhysAddr::new(new_table.level4_page_table.address() as u64); 396 | let frame = PhysFrame::containing_address(addr); 397 | let flags = registers::control::Cr3Flags::empty(); 398 | 399 | unsafe { 400 | registers::control::Cr3::write(frame, flags); 401 | } 402 | 403 | old_table 404 | } 405 | } 406 | 407 | pub struct InActivePageTable { 408 | level4_page_table: Frame, 409 | } 410 | 411 | impl InActivePageTable { 412 | pub fn new(active_page_table: &mut ActivePageTable, allocator: &mut dyn FrameAllocator) -> Option { 413 | allocator.alloc_one().map(|frame| { 414 | let p = active_page_table.find_empty_page(allocator).unwrap(); 415 | active_page_table.map(p.clone(), frame.clone(), allocator).unwrap(); 416 | let table = unsafe { &mut *(p.address() as *mut Table) }; 417 | table.clear_all_entries(); 418 | 419 | // set recursive page mapping. 420 | let entry = &mut table[511]; 421 | entry.set_frame_addr(frame.address()); 422 | entry.set_flags(PageEntryFlags::WRITABLE); 423 | 424 | InActivePageTable { level4_page_table: frame } 425 | }) 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /src/memory/region.rs: -------------------------------------------------------------------------------- 1 | //! `Region` defines a physical memory region. 2 | use super::PhysicalAddress; 3 | use multiboot2::{MemoryArea, MemoryAreaIter, MemoryMapTag}; 4 | 5 | #[derive(Copy, Clone, PartialEq, Debug)] 6 | pub enum State { 7 | Free, 8 | Used, 9 | Reserved, 10 | } 11 | 12 | #[derive(Clone, Debug)] 13 | pub struct Region { 14 | base_addr: PhysicalAddress, 15 | size: usize, 16 | state: State, 17 | } 18 | 19 | impl Region { 20 | pub fn new(base_addr: PhysicalAddress, size: usize, state: State) -> Region { 21 | Region { base_addr: base_addr, size: size, state: state } 22 | } 23 | 24 | pub fn base_addr(&self) -> PhysicalAddress { 25 | self.base_addr 26 | } 27 | 28 | pub fn size(&self) -> usize { 29 | self.size 30 | } 31 | 32 | pub fn state(&self) -> State { 33 | self.state 34 | } 35 | } 36 | 37 | pub trait Adapter { 38 | type Item: Into; 39 | type Target: Iterator; 40 | 41 | fn iter(&self) -> AdapterIter; 42 | } 43 | 44 | #[derive(Clone, Debug)] 45 | pub struct AdapterIter, U: Iterator> { 46 | iter: U, 47 | } 48 | 49 | impl, U: Iterator> Iterator for AdapterIter { 50 | type Item = Region; 51 | 52 | fn next(&mut self) -> Option { 53 | if let Some(area) = self.iter.next() { 54 | Some(area.into()) 55 | } else { 56 | None 57 | } 58 | } 59 | } 60 | 61 | #[derive(Debug)] 62 | pub struct Multiboot2Adapter { 63 | memory_map_tag: &'static MemoryMapTag, 64 | } 65 | 66 | impl Multiboot2Adapter { 67 | pub fn new(memory_map_tag: &'static MemoryMapTag) -> Multiboot2Adapter { 68 | Multiboot2Adapter { memory_map_tag } 69 | } 70 | } 71 | 72 | impl Adapter for Multiboot2Adapter { 73 | type Target = MemoryAreaIter; 74 | type Item = &'static MemoryArea; 75 | 76 | fn iter(&self) -> AdapterIter<&'static MemoryArea, MemoryAreaIter> { 77 | AdapterIter { iter: self.memory_map_tag.memory_areas() } 78 | } 79 | } 80 | 81 | impl Into for &'static MemoryArea { 82 | fn into(self) -> Region { 83 | Region::new(self.start_address() as usize, self.size() as usize, State::Free) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/process.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::Thread; 2 | // use crate::memory::{ActivePageTable, FrameAllocator, InActivePageTable, ACTIVE_PAGE_TABLE}; 3 | // use alloc::boxed::Box; 4 | use core::mem; 5 | use core::ptr; 6 | use lazy_static::lazy_static; 7 | use spin::Mutex; 8 | 9 | struct Process { 10 | // page_table: InActivePageTable, 11 | thread: Thread, 12 | } 13 | // 14 | // impl Process { 15 | // fn new(active_page_table: &mut ActivePageTable, allocator: &mut FrameAllocator) -> Process { 16 | // // FIXME: remove 1st argument (ActivePageTable). 17 | // Process { 18 | // page_table: InActivePageTable::new(active_page_table, allocator).unwrap(), 19 | // thread: Thread::new(), 20 | // } 21 | // } 22 | // } 23 | 24 | const MAX_PROCESS_COUNT: usize = 16; 25 | 26 | pub struct ProcessManager { 27 | // current_process: Box, 28 | processes: [Option; MAX_PROCESS_COUNT], 29 | // current_index: usize, 30 | } 31 | 32 | impl ProcessManager { 33 | fn new() -> ProcessManager { 34 | unsafe { 35 | // FIXME: `mem::uninitialized()` causes kernel stack overflow if the MAX_PROCESS_COUNT 36 | // is too large. 37 | let mut processes: [Option; MAX_PROCESS_COUNT] = mem::uninitialized(); 38 | for p in processes.iter_mut() { 39 | ptr::write(p, None) 40 | } 41 | 42 | // processes[0] = create_init_user_process(); 43 | 44 | ProcessManager { processes } 45 | } 46 | } 47 | } 48 | 49 | lazy_static! { 50 | static ref PROCESS_MANAGER: Mutex = Mutex::new(ProcessManager::new()); 51 | } 52 | 53 | pub fn switch_context(mut switch_thread: F) 54 | where 55 | F: FnMut(&mut Thread, &mut Thread), 56 | { 57 | // FIXME: 58 | let manager = &mut (*(*PROCESS_MANAGER).lock()); 59 | let (m, n) = manager.processes.split_at_mut(1); 60 | if let Some(ref mut current) = m[0] { 61 | if let Some(ref mut next) = n[0] { 62 | switch_thread(&mut current.thread, &mut next.thread); 63 | } 64 | } 65 | } 66 | 67 | // fn create_init_user_process() -> Process { 68 | // Process::new() 69 | // } 70 | --------------------------------------------------------------------------------