├── .cargo └── config.toml ├── .gitignore ├── BUILDING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── build.rs ├── doc ├── arch │ └── x86 │ │ └── memory32.md ├── img │ └── screenshot.png └── roadmap.md ├── media ├── fr.json ├── fr.keymap ├── iosevka.pxfont ├── us.json ├── us.keymap ├── wallpaper.data └── wallpaper.png ├── src ├── arch │ ├── mod.rs │ ├── test │ │ ├── export │ │ │ ├── cpu.rs │ │ │ ├── logging.rs │ │ │ ├── mem.rs │ │ │ ├── mod.rs │ │ │ ├── sync.rs │ │ │ └── task.rs │ │ ├── frame.rs │ │ └── mod.rs │ └── x86 │ │ ├── cpuid.rs │ │ ├── driver │ │ ├── apic.rs │ │ ├── mod.rs │ │ ├── pic8259.rs │ │ ├── ps2.rs │ │ ├── serial.rs │ │ ├── vesa.rs │ │ └── vga.rs │ │ ├── export │ │ ├── cpu.rs │ │ ├── logging.rs │ │ ├── mem │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── sync.rs │ │ └── task.rs │ │ ├── gdt.rs │ │ ├── init.rs │ │ ├── irq.rs │ │ ├── isr_entry64.S │ │ ├── mem │ │ ├── mod.rs │ │ └── paging.rs │ │ ├── mod.rs │ │ ├── multiboot2.S │ │ └── start64.S ├── backtrace.rs ├── driver │ ├── gpio.rs │ ├── keyboard.rs │ ├── mod.rs │ ├── screen.rs │ └── vga.rs ├── logging.rs ├── main.rs ├── mem │ ├── frame.rs │ ├── kalloc │ │ ├── bump_kalloc.rs │ │ ├── freelist_kalloc.rs │ │ ├── mimalloc │ │ │ ├── heap.rs │ │ │ └── mod.rs │ │ └── mod.rs │ ├── load.rs │ └── mod.rs ├── misc.rs ├── panic.rs ├── screen.rs ├── sync.rs ├── task │ ├── cpu.rs │ ├── cpu_local.rs │ ├── mod.rs │ └── vm.rs └── ui │ ├── keymap.rs │ ├── kterm.rs │ ├── mod.rs │ ├── pxfont.rs │ └── term.rs └── targets ├── x86_64-nucloid.json └── x86_64.ld /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | #[target.'cfg(target_os = "linux")'] 2 | #rustflags = ["-C", "link-arg=-nostartfiles"] 3 | 4 | [target.x86_64-nucloid] 5 | rustflags = [ 6 | "-C", "link-arg=-Ttargets/x86_64.ld", 7 | "-C", "link-arg=-nostartfiles", 8 | "-C", "link-arg=-n", 9 | "-C", "force-unwind-tables=yes" 10 | ] 11 | 12 | # Building the actual kernel image requires the two settings below. We can't, 13 | # however, enable them here because they would prevent the tests to build since 14 | # they require the host's stdlib. To the best of my knowledge, it is not 15 | # possible to have these parameters to be conditional. So we pass them as `-Z` 16 | # flags to cargo (the Makefile inserts them) when making the kernel ELF, but not 17 | # when making the test build. 18 | #[unstable] 19 | #build-std = ["core", "compiler_builtins", "alloc"] 20 | #build-std-features = ["compiler-builtins-mem"] 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building instructions # 2 | 3 | Currently, only Linux is supported as a development platform. That doesn't mean 4 | Nucloid can't be built on other operating systems, just that is hasn't been 5 | tested, and some adjustments are almost certainly required. On Windows, it may 6 | be a good idea to go with WSL. 7 | 8 | ## rustup ## 9 | 10 | You need rustup to be installed. If it isn't, run: 11 | 12 | ```sh 13 | $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 14 | ``` 15 | 16 | Or checkout your distribution for a package: `rustup` on Arch Linux; Debian does 17 | not package rustup. 18 | 19 | You then need to install the `nightly-x86_64-unknown-linux-gnu` toolchain: 20 | 21 | ```sh 22 | $ rustup toolchain install nightly-x86_64-unknown-linux-gnu 23 | ``` 24 | 25 | As well as the standard library's sources: 26 | 27 | ```sh 28 | $ rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu 29 | ``` 30 | 31 | ## GCC x86-64 ELF cross-compiler ## 32 | 33 | You need a GCC cross-compiler `x86_64-elf-gcc`. 34 | 35 | On Arch Linux, just install the AUR package `x86_64-elf-gcc`. 36 | 37 | ### Debian, and other distributions ### 38 | 39 | Debian does not ship a package for the `x86_64-elf-gcc` toolchain, unlike Arch 40 | Linux. You will have to build it yourself from the sources. Note that this 41 | method is not strictly limited to Debian and can work for any distribution. 42 | 43 | You first need to install dependendies needed to build GCC and the binutils; 44 | for Debian, these are the packages to install: 45 | 46 | ```sh 47 | # apt install libgmp3-dev libmpc-dev texinfo 48 | ``` 49 | 50 | You can download the sources of binutils and GCC from GNU's website. For this 51 | tutorial, I will take binutils v2.40 and GCC v12.2; feel free to use any later 52 | version. 53 | 54 | Note: you must build the binutils first. 55 | 56 | ```sh 57 | wget https://ftp.gnu.org/gnu/binutils/binutils-2.40.tar.xz 58 | tar xfJ binutils-2.40.tar.xz 59 | mkdir build_binutils && cd build_binutils 60 | ../binutils-2.40/configure --target=x86_64-elf --prefix=/usr/local --with-sysroot --disable-werror 61 | make -j16 62 | sudo make install 63 | ``` 64 | 65 | Do not run `make -j`: this does not limit the number of concurrent compilation 66 | processes and will consume an enormous amount of RAM with no performance 67 | benefit (on my setup, it immediately filled up the 32 GiB of RAM and the 8 GiB 68 | of swap, triggering the OOM killer). 69 | 70 | Then for GCC: 71 | 72 | ```sh 73 | wget https://ftp.gnu.org/gnu/gcc/gcc-12.2.0/gcc-12.2.0.tar.xz 74 | tar xfJ gcc-12.2.0.tar.xz 75 | mkdir build_gcc && cd build_gcc 76 | ../gcc-12.2.0/configure --target=x86_64-elf --prefix=/usr/local --without-headers 77 | make -j16 all-gcc 78 | make -j16 all-target-libgcc 79 | sudo make install-gcc 80 | sudo make install-target-libgcc 81 | ``` 82 | 83 | You can then delete the two build directories. 84 | 85 | ## Rust build ## 86 | 87 | Once rustup and your cross-compiler toolchain are installed, you can trigger the 88 | Rust compilation via make: 89 | 90 | ```sh 91 | make x86_64-debug 92 | ``` 93 | 94 | For the debug version, or use the `x86_64-release` target for the release build. 95 | 96 | This will generate the output kernel ELF in the `target` directory: 97 | `target/x86_64-nucloid/debug/nucloid` for the debug build, and 98 | `target/x86_64-nucloid/release/nucloid` for the release. 99 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 10 | 11 | [[package]] 12 | name = "array-init" 13 | version = "2.1.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" 16 | 17 | [[package]] 18 | name = "arrayvec" 19 | version = "0.7.6" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 22 | 23 | [[package]] 24 | name = "autocfg" 25 | version = "1.3.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 28 | 29 | [[package]] 30 | name = "binrw" 31 | version = "0.14.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "7d4bca59c20d6f40c2cc0802afbe1e788b89096f61bdf7aeea6bf00f10c2909b" 34 | dependencies = [ 35 | "array-init", 36 | "binrw_derive", 37 | "bytemuck", 38 | ] 39 | 40 | [[package]] 41 | name = "binrw_derive" 42 | version = "0.14.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "d8ba42866ce5bced2645bfa15e97eef2c62d2bdb530510538de8dd3d04efff3c" 45 | dependencies = [ 46 | "either", 47 | "proc-macro2", 48 | "quote", 49 | "syn 1.0.109", 50 | ] 51 | 52 | [[package]] 53 | name = "bit_field" 54 | version = "0.10.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 57 | 58 | [[package]] 59 | name = "bitflags" 60 | version = "1.3.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 63 | 64 | [[package]] 65 | name = "bitflags" 66 | version = "2.6.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 69 | 70 | [[package]] 71 | name = "bytemuck" 72 | version = "1.16.1" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 75 | 76 | [[package]] 77 | name = "cc" 78 | version = "1.2.15" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" 81 | dependencies = [ 82 | "shlex", 83 | ] 84 | 85 | [[package]] 86 | name = "ctor" 87 | version = "0.4.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "a7747ac3a66a06f4ee6718686c8ea976d2d05fb30ada93ebd76b3f9aef97257c" 90 | dependencies = [ 91 | "ctor-proc-macro", 92 | "dtor", 93 | ] 94 | 95 | [[package]] 96 | name = "ctor-proc-macro" 97 | version = "0.0.5" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" 100 | 101 | [[package]] 102 | name = "derive_more" 103 | version = "1.0.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 106 | dependencies = [ 107 | "derive_more-impl", 108 | ] 109 | 110 | [[package]] 111 | name = "derive_more-impl" 112 | version = "1.0.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 115 | dependencies = [ 116 | "proc-macro2", 117 | "quote", 118 | "syn 2.0.69", 119 | "unicode-xid", 120 | ] 121 | 122 | [[package]] 123 | name = "dtor" 124 | version = "0.0.4" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "8bf39a0bfd1f94d62ffdb2802a7e6244c0f34f6ebacf5d4c26547d08cd1d67a5" 127 | dependencies = [ 128 | "dtor-proc-macro", 129 | ] 130 | 131 | [[package]] 132 | name = "dtor-proc-macro" 133 | version = "0.0.5" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" 136 | 137 | [[package]] 138 | name = "either" 139 | version = "1.13.0" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 142 | 143 | [[package]] 144 | name = "equivalent" 145 | version = "1.0.2" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 148 | 149 | [[package]] 150 | name = "foldhash" 151 | version = "0.1.4" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" 154 | 155 | [[package]] 156 | name = "gimli" 157 | version = "0.31.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 160 | 161 | [[package]] 162 | name = "hashbrown" 163 | version = "0.15.2" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 166 | dependencies = [ 167 | "allocator-api2", 168 | "equivalent", 169 | "foldhash", 170 | ] 171 | 172 | [[package]] 173 | name = "log" 174 | version = "0.4.22" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 177 | 178 | [[package]] 179 | name = "multiboot2" 180 | version = "0.23.1" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "43fcee184de68e344a888bc4f2b9d6b2f2f527cae8cedbb4d62a4df727d1ceae" 183 | dependencies = [ 184 | "bitflags 2.6.0", 185 | "derive_more", 186 | "log", 187 | "multiboot2-common", 188 | "ptr_meta", 189 | "uefi-raw", 190 | ] 191 | 192 | [[package]] 193 | name = "multiboot2-common" 194 | version = "0.2.1" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "fbabf8d9980d55576ba487924fa0b9a467fc0012b996b93d2319904f168ed8ab" 197 | dependencies = [ 198 | "derive_more", 199 | "ptr_meta", 200 | ] 201 | 202 | [[package]] 203 | name = "nucloid" 204 | version = "0.1.0" 205 | dependencies = [ 206 | "arrayvec", 207 | "binrw", 208 | "cc", 209 | "ctor", 210 | "gimli", 211 | "hashbrown", 212 | "multiboot2", 213 | "num-integer", 214 | "thiserror-no-std", 215 | "x86", 216 | ] 217 | 218 | [[package]] 219 | name = "num-integer" 220 | version = "0.1.46" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 223 | dependencies = [ 224 | "num-traits", 225 | ] 226 | 227 | [[package]] 228 | name = "num-traits" 229 | version = "0.2.19" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 232 | dependencies = [ 233 | "autocfg", 234 | ] 235 | 236 | [[package]] 237 | name = "proc-macro2" 238 | version = "1.0.86" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 241 | dependencies = [ 242 | "unicode-ident", 243 | ] 244 | 245 | [[package]] 246 | name = "ptr_meta" 247 | version = "0.2.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" 250 | dependencies = [ 251 | "ptr_meta_derive", 252 | ] 253 | 254 | [[package]] 255 | name = "ptr_meta_derive" 256 | version = "0.2.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" 259 | dependencies = [ 260 | "proc-macro2", 261 | "quote", 262 | "syn 1.0.109", 263 | ] 264 | 265 | [[package]] 266 | name = "quote" 267 | version = "1.0.36" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 270 | dependencies = [ 271 | "proc-macro2", 272 | ] 273 | 274 | [[package]] 275 | name = "raw-cpuid" 276 | version = "10.7.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" 279 | dependencies = [ 280 | "bitflags 1.3.2", 281 | ] 282 | 283 | [[package]] 284 | name = "shlex" 285 | version = "1.3.0" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 288 | 289 | [[package]] 290 | name = "syn" 291 | version = "1.0.109" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 294 | dependencies = [ 295 | "proc-macro2", 296 | "quote", 297 | "unicode-ident", 298 | ] 299 | 300 | [[package]] 301 | name = "syn" 302 | version = "2.0.69" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" 305 | dependencies = [ 306 | "proc-macro2", 307 | "quote", 308 | "unicode-ident", 309 | ] 310 | 311 | [[package]] 312 | name = "thiserror-impl-no-std" 313 | version = "2.0.2" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" 316 | dependencies = [ 317 | "proc-macro2", 318 | "quote", 319 | "syn 1.0.109", 320 | ] 321 | 322 | [[package]] 323 | name = "thiserror-no-std" 324 | version = "2.0.2" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" 327 | dependencies = [ 328 | "thiserror-impl-no-std", 329 | ] 330 | 331 | [[package]] 332 | name = "uefi-raw" 333 | version = "0.8.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "b463030b802e1265a3800fab24df95d3229c202c2e408832a206f05b4d1496ca" 336 | dependencies = [ 337 | "bitflags 2.6.0", 338 | "ptr_meta", 339 | "uguid", 340 | ] 341 | 342 | [[package]] 343 | name = "uguid" 344 | version = "2.2.0" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533" 347 | 348 | [[package]] 349 | name = "unicode-ident" 350 | version = "1.0.12" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 353 | 354 | [[package]] 355 | name = "unicode-xid" 356 | version = "0.2.6" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 359 | 360 | [[package]] 361 | name = "x86" 362 | version = "0.52.0" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" 365 | dependencies = [ 366 | "bit_field", 367 | "bitflags 1.3.2", 368 | "raw-cpuid", 369 | ] 370 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Copyright © 2021-2023 Kévin Lesénéchal # 3 | # This file is part of the Nucloid operating system. # 4 | # # 5 | # Nucloid is free software; you can redistribute it and/or modify it under # 6 | # the terms of the GNU General Public License as published by the Free # 7 | # Software Foundation; either version 2 of the License, or (at your option) # 8 | # any later version. See LICENSE file for more information. # 9 | ############################################################################## 10 | 11 | [package] 12 | name = "nucloid" 13 | version = "0.1.0" 14 | authors = ["Kévin Lesénéchal "] 15 | edition = "2024" 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [profile.release] 20 | # Uncontrolled integer overflows are a big deal in a kernel: we are not going to 21 | # let them happen in production. Overflowing is a bug in Nucloid, it must never 22 | # happen unless explicity allowed in a controlled way. Only checking safety 23 | # invariants in debug builds and not in release is a serious design mistake. 24 | overflow-checks = true 25 | 26 | [build-dependencies] 27 | cc = "1.2.15" 28 | 29 | [dev-dependencies] 30 | ctor = "0.4.0" 31 | 32 | [dependencies] 33 | thiserror-no-std = "2.0" 34 | arrayvec = { version = "0.7.6", default-features = false, features = [] } 35 | num-integer = { version = "0.1.46", default-features = false } 36 | hashbrown = "0.15.2" 37 | binrw = { version = "0.14.1", default-features = false } 38 | gimli = { version = "0.31.1", default-features = false, features = ["read"] } 39 | 40 | [target.'cfg(target_arch = "x86_64")'.dependencies] 41 | x86 = "0.52" 42 | multiboot2 = "0.23.1" 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CARGO_FLAGS := -Zbuild-std=core,compiler_builtins,alloc \ 2 | -Zbuild-std-features=compiler-builtins-mem 3 | CARGO_BUILD := cargo +nightly build $(CARGO_FLAGS) 4 | 5 | x86_64-debug: 6 | $(CARGO_BUILD) --target targets/x86_64-nucloid.json 7 | 8 | x86_64-release: 9 | $(CARGO_BUILD) --release --target targets/x86_64-nucloid.json 10 | 11 | tests: 12 | cargo +nightly test 13 | 14 | .PHONY: x86_64-debug x86_64-release tests 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nucloid kernel # 2 | 3 | Nucloid is a simple monolithic kernel written in Rust currently targeting 4 | x86-64; with intent to support AArch64. There will be no support for 32 bits 5 | architectures. 6 | 7 | ![](doc/img/screenshot.png) 8 | 9 | Nucloid is highly experimental and still in its early stage of development, 10 | use this code with caution. 11 | 12 | ## Building Nucloid ## 13 | 14 | For build instructions, see [`BUILDING.md`](BUILDING.md). 15 | 16 | ## Credits ## 17 | 18 | The `media/iosevka.pxfont` is a bitmap font generated from Iosevka, by 19 | *Belleve Invis*; see https://typeof.net/Iosevka/. 20 | 21 | The wallpaper `media/wallpaper.png` "Milky way" by *ruvkr* published under 22 | CC-BY-SA-4.0; see https://github.com/ruvkr/milkyway. 23 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let target = &*std::env::var("TARGET") 3 | .expect("Expected TARGET environment variable"); 4 | 5 | match target { 6 | "x86_64-nucloid" => build_x86(target), 7 | _ => (), 8 | } 9 | 10 | println!("cargo:rerun-if-changed=src/arch/x86/multiboot2.S"); 11 | println!("cargo:rerun-if-changed=src/arch/x86/start64.S"); 12 | println!("cargo:rerun-if-changed=src/arch/x86/isr_entry64.S"); 13 | println!("cargo:rerun-if-changed=targets/x86_64.ld"); 14 | } 15 | 16 | fn build_x86(target: &str) { 17 | let mut build = make_c_builder(); 18 | 19 | build.file("src/arch/x86/multiboot2.S"); 20 | 21 | if target == "x86_64-nucloid" { 22 | build 23 | .file("src/arch/x86/start64.S") 24 | .file("src/arch/x86/isr_entry64.S"); 25 | } 26 | 27 | build 28 | .link_lib_modifier("+whole-archive") 29 | .compile("nucloid_c"); 30 | } 31 | 32 | fn make_c_builder() -> cc::Build { 33 | let mut build = cc::Build::new(); 34 | 35 | set_compiler(&mut build); 36 | add_c_flags(&mut build); 37 | 38 | build 39 | } 40 | 41 | fn add_c_flags(build: &mut cc::Build) { 42 | build.pic(false); 43 | build.flag("-ffreestanding"); 44 | build.flag("-nostdlib"); 45 | build.flag("-mno-red-zone"); 46 | build.flag("-mno-sse"); 47 | build.flag("-mno-avx"); 48 | build.flag("-no-pie"); 49 | } 50 | 51 | fn set_compiler(build: &mut cc::Build) { 52 | let target = 53 | std::env::var("TARGET").expect("Expected TARGET environment variable"); 54 | 55 | let compiler = match target.as_ref() { 56 | "x86_64-nucloid" => "x86_64-elf-gcc", 57 | other => panic!("unsupported target '{}'", other), 58 | }; 59 | 60 | build.compiler(compiler); 61 | } 62 | -------------------------------------------------------------------------------- /doc/arch/x86/memory32.md: -------------------------------------------------------------------------------- 1 | # Memory layout for x86-32 # 2 | 3 | ## Virtual memory space ## 4 | 5 | 6 | -------------------------------------------------------------------------------- /doc/img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/nucloid/4fd2de586596568a6982b25e7f5fb92f579ca8b0/doc/img/screenshot.png -------------------------------------------------------------------------------- /doc/roadmap.md: -------------------------------------------------------------------------------- 1 | # Memory management # 2 | 3 | - High-memory allocator 4 | - (DONE) Allocate virtual addresses 5 | - (DONE) Map high-memory virtual addresses to highmem PA 6 | - (DONE) Use a guard to ensure high-memory unmapping and deallocation 7 | - (WORKEDAROUND) General purpose allocator 8 | 9 | # Process management # 10 | 11 | # Devices # 12 | 13 | # User interface # 14 | 15 | - Kernel-space keyboard support: 16 | - Keymaps: 17 | 1. Define a keymap format (or use standard one?) 18 | 2. Generate keymap files for US-QWERTY and FR-AZERTY first 19 | 3. Map physical scan-codes into logical keys 20 | - Graphical terminal: 21 | - (DONE) Pixmap font generator 22 | - (DONE) Pixmap font loader 23 | - (DONE) Text renderer 24 | 25 | # User-space # 26 | -------------------------------------------------------------------------------- /media/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "`": {"regular": "²³", "altgr": "¹\u0000"}, 3 | "1": {"regular": "&1", "altgr": "\u030c\u031c"}, 4 | "2": {"regular": "é2É2", "altgr": "~É"}, 5 | "3": {"regular": "\"3", "altgr": "#\u0306"}, 6 | "4": {"regular": "'4", "altgr": "{—"}, 7 | "5": {"regular": "(5", "altgr": "[–"}, 8 | "6": {"regular": "-6", "altgr": "|‑"}, 9 | "7": {"regular": "è7È7", "altgr": "`È"}, 10 | "8": {"regular": "_8", "altgr": "\\™"}, 11 | "9": {"regular": "ç9Ç9", "altgr": "^Ç"}, 12 | "0": {"regular": "à0À0", "altgr": "@À"}, 13 | "-": {"regular": ")°", "altgr": "]≠"}, 14 | "=": {"regular": "=+", "altgr": "}±"}, 15 | 16 | "Q": {"regular": "aAAa", "altgr": "æÆÆæ"}, 17 | "W": {"regular": "zZZz", "altgr": "âÂÂâ"}, 18 | "E": {"regular": "eEEe", "altgr": "€¢€¢"}, 19 | "R": {"regular": "rRRr", "altgr": "êÊÊê"}, 20 | "T": {"regular": "tTTt", "altgr": "þÞÞþ"}, 21 | "Y": {"regular": "yYYy", "altgr": "ÿŸŸÿ"}, 22 | "U": {"regular": "uUUu", "altgr": "ûÛÛû"}, 23 | "I": {"regular": "iIIi", "altgr": "îÎÎî"}, 24 | "O": {"regular": "oOOo", "altgr": "œŒŒœ"}, 25 | "P": {"regular": "pPPp", "altgr": "ôÔÔô"}, 26 | "[": {"regular": "\u0302\u0308", "altgr": "\u0303\u030a"}, 27 | "]": {"regular": "$£", "altgr": "øØØø"}, 28 | 29 | "A": {"regular": "qQQq", "altgr": "äÄÄä"}, 30 | "S": {"regular": "sSSs", "altgr": "ß„"}, 31 | "D": {"regular": "dDDd", "altgr": "ëËËë"}, 32 | "F": {"regular": "fFFf", "altgr": "‘‚"}, 33 | "G": {"regular": "gGGg", "altgr": "’¥"}, 34 | "H": {"regular": "hHHh", "altgr": "ðÐÐð"}, 35 | "J": {"regular": "jJJj", "altgr": "üÜÜü"}, 36 | "K": {"regular": "kKKk", "altgr": "ïÏÏï"}, 37 | "L": {"regular": "lLLl", "altgr": "ŀĿĿŀ"}, 38 | ";": {"regular": "mMMm", "altgr": "öÖÖö"}, 39 | "'": {"regular": "ù%Ù%", "altgr": "\u0301Ù\u0301ù"}, 40 | "\\": {"regular": "*µ", "altgr": "\u0300\u0304"}, 41 | 42 | "iso": {"regular": "<>", "altgr": "≤≥"}, 43 | "Z": {"regular": "wWWw", "altgr": "«“"}, 44 | "X": {"regular": "xXXx", "altgr": "»”"}, 45 | "C": {"regular": "cCCc", "altgr": "©®"}, 46 | "V": {"regular": "vVVv", "altgr": " ←"}, 47 | "B": {"regular": "bBBb", "altgr": "↓↑"}, 48 | "N": {"regular": "nNNn", "altgr": "¬→"}, 49 | "M": {"regular": ",?", "altgr": "¿…"}, 50 | ",": {"regular": ";.", "altgr": "×⋅"}, 51 | ".": {"regular": ":/", "altgr": "÷∕"}, 52 | "/": {"regular": "!§", "altgr": "¡−"}, 53 | 54 | "KP0": {"regular": "00", "altgr": "↕↕"}, 55 | "KP1": {"regular": "11", "altgr": "↙↙"}, 56 | "KP2": {"regular": "22", "altgr": "↓↓"}, 57 | "KP3": {"regular": "33", "altgr": "↘↘"}, 58 | "KP4": {"regular": "44", "altgr": "←←"}, 59 | "KP5": {"regular": "55", "altgr": "↔↔"}, 60 | "KP6": {"regular": "66", "altgr": "→→"}, 61 | "KP7": {"regular": "77", "altgr": "↖↖"}, 62 | "KP8": {"regular": "88", "altgr": "↑↑"}, 63 | "KP9": {"regular": "99", "altgr": "↗↗"}, 64 | "KP.": {"regular": ".."}, 65 | "KP+": {"regular": "++"}, 66 | "KP-": {"regular": "--"}, 67 | "KP*": {"regular": "**"}, 68 | "KP/": {"regular": "//", "altgr": "÷÷"} 69 | } 70 | -------------------------------------------------------------------------------- /media/fr.keymap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/nucloid/4fd2de586596568a6982b25e7f5fb92f579ca8b0/media/fr.keymap -------------------------------------------------------------------------------- /media/iosevka.pxfont: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/nucloid/4fd2de586596568a6982b25e7f5fb92f579ca8b0/media/iosevka.pxfont -------------------------------------------------------------------------------- /media/us.json: -------------------------------------------------------------------------------- 1 | { 2 | "`": {"regular": "`~"}, 3 | "1": {"regular": "1!"}, 4 | "2": {"regular": "2@"}, 5 | "3": {"regular": "3#"}, 6 | "4": {"regular": "4$"}, 7 | "5": {"regular": "5%"}, 8 | "6": {"regular": "6^"}, 9 | "7": {"regular": "7&"}, 10 | "8": {"regular": "8*"}, 11 | "9": {"regular": "9("}, 12 | "0": {"regular": "0)"}, 13 | "-": {"regular": "-_"}, 14 | "=": {"regular": "=+"}, 15 | 16 | "Q": {"regular": "qQQq"}, 17 | "W": {"regular": "wWWw"}, 18 | "E": {"regular": "eEEe"}, 19 | "R": {"regular": "rRRr"}, 20 | "T": {"regular": "tTTt"}, 21 | "Y": {"regular": "yYYy"}, 22 | "U": {"regular": "uUUu"}, 23 | "I": {"regular": "iIIi"}, 24 | "O": {"regular": "oOOo"}, 25 | "P": {"regular": "pPPp"}, 26 | "[": {"regular": "[{"}, 27 | "]": {"regular": "]}"}, 28 | 29 | "A": {"regular": "aAAa"}, 30 | "S": {"regular": "sSSs"}, 31 | "D": {"regular": "dDDd"}, 32 | "F": {"regular": "fFFf"}, 33 | "G": {"regular": "gGGg"}, 34 | "H": {"regular": "hHHh"}, 35 | "J": {"regular": "jJJj"}, 36 | "K": {"regular": "kKKk"}, 37 | "L": {"regular": "lLLl"}, 38 | ";": {"regular": ";:"}, 39 | "'": {"regular": "'\""}, 40 | "\\": {"regular": "\\|"}, 41 | 42 | "iso": {"regular": "\\|"}, 43 | "Z": {"regular": "zZZz"}, 44 | "X": {"regular": "xXXx"}, 45 | "C": {"regular": "cCCc"}, 46 | "V": {"regular": "vVVv"}, 47 | "B": {"regular": "bBBb"}, 48 | "N": {"regular": "nNNn"}, 49 | "M": {"regular": "mMMm"}, 50 | ",": {"regular": ",<"}, 51 | ".": {"regular": ".>"}, 52 | "/": {"regular": "/?"}, 53 | 54 | "KP0": {"regular": "00", "altgr": "↕↕"}, 55 | "KP1": {"regular": "11", "altgr": "↙↙"}, 56 | "KP2": {"regular": "22", "altgr": "↓↓"}, 57 | "KP3": {"regular": "33", "altgr": "↘↘"}, 58 | "KP4": {"regular": "44", "altgr": "←←"}, 59 | "KP5": {"regular": "55", "altgr": "↔↔"}, 60 | "KP6": {"regular": "66", "altgr": "→→"}, 61 | "KP7": {"regular": "77", "altgr": "↖↖"}, 62 | "KP8": {"regular": "88", "altgr": "↑↑"}, 63 | "KP9": {"regular": "99", "altgr": "↗↗"}, 64 | "KP.": {"regular": ".."}, 65 | "KP+": {"regular": "++"}, 66 | "KP-": {"regular": "--"}, 67 | "KP*": {"regular": "**"}, 68 | "KP/": {"regular": "//", "altgr": "÷÷"} 69 | } 70 | -------------------------------------------------------------------------------- /media/us.keymap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/nucloid/4fd2de586596568a6982b25e7f5fb92f579ca8b0/media/us.keymap -------------------------------------------------------------------------------- /media/wallpaper.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/nucloid/4fd2de586596568a6982b25e7f5fb92f579ca8b0/media/wallpaper.data -------------------------------------------------------------------------------- /media/wallpaper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/nucloid/4fd2de586596568a6982b25e7f5fb92f579ca8b0/media/wallpaper.png -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | /*#[cfg(all( 12 | //target_arch = "x86_64", 13 | not(test) 14 | ))]*/ 15 | mod x86; 16 | 17 | /*#[cfg(all( 18 | //target_arch = "x86_64", 19 | not(test) 20 | ))]*/ 21 | pub use crate::arch::x86::export::*; 22 | 23 | /*#[cfg(test)] 24 | mod test; 25 | 26 | #[cfg(test)] 27 | pub use crate::arch::test::export::*;*/ 28 | -------------------------------------------------------------------------------- /src/arch/test/export/cpu.rs: -------------------------------------------------------------------------------- 1 | use crate::driver::vga::VgaScreen; 2 | use core::fmt; 3 | use core::fmt::{Display, Formatter}; 4 | 5 | pub struct MachineState {} 6 | 7 | impl MachineState { 8 | pub fn print(&self, _vga: &mut impl VgaScreen) -> fmt::Result { 9 | unimplemented!(); 10 | } 11 | } 12 | 13 | impl Display for MachineState { 14 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 15 | writeln!(f, "[test MachineState]") 16 | } 17 | } 18 | 19 | pub fn halt() { 20 | unimplemented!(); 21 | } 22 | 23 | pub fn perm_halt() -> ! { 24 | unimplemented!(); 25 | } 26 | -------------------------------------------------------------------------------- /src/arch/test/export/logging.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::fmt::Write; 3 | use std::print; 4 | use crate::logging::{DEFAULT_LOGGER, Logger, Severity}; 5 | 6 | pub struct SerialDevice; 7 | 8 | pub static mut LOGGER_SERIAL: Option = Some(SerialDevice); 9 | 10 | impl fmt::Write for SerialDevice { 11 | fn write_str(&mut self, s: &str) -> fmt::Result { 12 | print!("{}", s); 13 | 14 | Ok(()) 15 | } 16 | } 17 | 18 | impl Logger for SerialDevice { 19 | fn log(&mut self, severity: Severity, args: fmt::Arguments) { 20 | let (color, severity_str) = match severity { 21 | Severity::Debug => ("\x1b[90m", "debug"), 22 | Severity::Info => ("\x1b[37m", "info"), 23 | Severity::Notice => ("\x1b[97m", "notice"), 24 | Severity::Warning => ("\x1b[93m", "warning"), 25 | Severity::Error => ("\x1b[31m", "error"), 26 | Severity::Critical => ("\x1b[1;31m", "critic."), 27 | Severity::Alert => ("\x1b[1;97;41m", "ALERT"), 28 | Severity::Emergency => ("\x1b[1;93;41m", "EMERG."), 29 | }; 30 | 31 | write!(self, "{}{:>7}: ", color, severity_str).unwrap(); 32 | self.write_fmt(args).unwrap(); 33 | write!(self, "\x1b[0m\n").unwrap(); 34 | } 35 | } 36 | 37 | #[ctor::ctor] 38 | fn init() { 39 | *DEFAULT_LOGGER.lock() = unsafe { LOGGER_SERIAL.as_mut().unwrap() }; 40 | } 41 | -------------------------------------------------------------------------------- /src/arch/test/export/mem.rs: -------------------------------------------------------------------------------- 1 | use crate::mem::{PagePermissions, VAddr}; 2 | use crate::mem::highmem::HighmemGuard; 3 | use crate::sync::Spinlock; 4 | 5 | pub const FRAME_SIZE: usize = 4096; 6 | pub const FRAME_SIZE_BITS: usize = 12; 7 | pub const NR_PHYS_FRAMES: usize = 32; 8 | 9 | #[repr(align(4096))] 10 | pub struct VmMemory(pub [u8; NR_PHYS_FRAMES << 12]); 11 | 12 | pub static mut MEMORY: VmMemory = VmMemory([0xf9; NR_PHYS_FRAMES << 12]); 13 | pub static MEMORY_MUTEX: Spinlock<()> = Spinlock::new(()); 14 | 15 | #[derive(Copy, Clone)] 16 | #[repr(C)] 17 | pub struct PAddr(pub u64); 18 | 19 | impl PAddr { 20 | pub fn into_vaddr(self, _nr_pages: usize) -> Option { 21 | unimplemented!() 22 | } 23 | 24 | pub fn into_lowmem_vaddr(self) -> Option { 25 | Some(VAddr( 26 | unsafe { MEMORY.0.get(self.0 as usize)? } as *const u8 as usize 27 | )) 28 | } 29 | 30 | pub fn from_lowmem_vaddr(_vaddr: usize) -> Option { 31 | unimplemented!() 32 | } 33 | 34 | pub fn is_highmem(&self) -> bool { 35 | false 36 | } 37 | } 38 | 39 | pub fn reset_memory() { 40 | unsafe { MEMORY.0.fill(0xf9); } 41 | } 42 | 43 | pub fn page_permissions(_vaddr: VAddr) -> PagePermissions { 44 | unimplemented!() 45 | } 46 | 47 | pub unsafe fn unmap_highmem_vaddr(_vaddr: VAddr) { 48 | unimplemented!() 49 | } 50 | -------------------------------------------------------------------------------- /src/arch/test/export/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sync; 2 | pub mod mem; 3 | pub mod cpu; 4 | pub mod logging; 5 | pub mod task; 6 | -------------------------------------------------------------------------------- /src/arch/test/export/sync.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicU32, Ordering}; 2 | 3 | static CRITICAL_REGION_DEPTH: AtomicU32 = AtomicU32::new(0); 4 | 5 | pub fn push_critical_region() { 6 | CRITICAL_REGION_DEPTH.fetch_add(1, Ordering::SeqCst); 7 | } 8 | 9 | pub fn pop_critical_region() { 10 | CRITICAL_REGION_DEPTH.fetch_sub(1, Ordering::SeqCst); 11 | } 12 | -------------------------------------------------------------------------------- /src/arch/test/export/task.rs: -------------------------------------------------------------------------------- 1 | pub struct TaskMachineContext { 2 | pub a: u64, 3 | pub b: u64, 4 | pub c: u64, 5 | pub d: u64, 6 | } 7 | -------------------------------------------------------------------------------- /src/arch/test/frame.rs: -------------------------------------------------------------------------------- 1 | use std::vec::Vec; 2 | use crate::arch::test::export::mem::NR_PHYS_FRAMES; 3 | use crate::mem::{VAddr, PAddr}; 4 | use crate::mem::frame::{AllocatorBuilder, Frame, FRAME_ALLOCATOR}; 5 | use crate::mem::LOWMEM_VA_END; 6 | 7 | pub fn reset_frame_allocator() { 8 | unsafe { LOWMEM_VA_END = VAddr(usize::MAX); } 9 | 10 | let frames = Vec::::with_capacity(NR_PHYS_FRAMES) 11 | .leak().as_mut_ptr(); 12 | let mem_size = (NR_PHYS_FRAMES as u64) << 12; 13 | 14 | let allocator = unsafe { 15 | let mut a = AllocatorBuilder::new(frames.into(), mem_size); 16 | a.declare_unused_ram(PAddr(0), mem_size); 17 | a.build() 18 | }; 19 | 20 | *FRAME_ALLOCATOR.lock() = Some(allocator); 21 | } 22 | -------------------------------------------------------------------------------- /src/arch/test/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod export; 2 | pub mod frame; 3 | -------------------------------------------------------------------------------- /src/arch/x86/cpuid.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use x86::cpuid::CpuId; 12 | 13 | static mut CPUID: Option = None; 14 | 15 | pub unsafe fn init() { 16 | unsafe { 17 | CPUID = Some(CpuId::new()); 18 | } 19 | } 20 | 21 | pub fn get() -> &'static CpuId { 22 | unsafe { CPUID.as_ref().expect("CPUID not initialized") } 23 | } 24 | -------------------------------------------------------------------------------- /src/arch/x86/driver/apic.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::arch::x86::cpuid; 12 | 13 | pub fn is_supported() -> bool { 14 | if let Some(features) = cpuid::get().get_feature_info() { 15 | features.has_apic() 16 | } else { 17 | false 18 | } 19 | } 20 | 21 | mod register { 22 | pub const LOCAL_APIC_ID: usize = 0x20; 23 | pub const LOCAL_APIC_VERSION: usize = 0x30; 24 | pub const EOI: usize = 0xb0; 25 | } 26 | 27 | pub struct Apic { 28 | regs: *mut u32, 29 | } 30 | 31 | impl Apic { 32 | pub unsafe fn new(registers: *mut u32) -> Apic { 33 | Apic { regs: registers } 34 | } 35 | 36 | pub fn eoi(&self) { 37 | self.write(register::EOI, 0); 38 | } 39 | 40 | fn write(&self, reg: usize, value: u32) { 41 | let index = reg >> 2; 42 | assert!(index < 252); 43 | 44 | unsafe { 45 | core::ptr::write_volatile(self.regs.add(index), value); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/arch/x86/driver/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub mod apic; 12 | pub mod pic8259; 13 | pub mod ps2; 14 | pub mod serial; 15 | pub mod vesa; 16 | pub mod vga; 17 | -------------------------------------------------------------------------------- /src/arch/x86/driver/pic8259.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::arch::x86::Ioport; 12 | use x86::io::outb; 13 | 14 | pub struct Pic8259 { 15 | master_port: Ioport, 16 | slave_port: Ioport, 17 | } 18 | 19 | impl Pic8259 { 20 | pub unsafe fn new(master_port: Ioport, slave_port: Ioport) -> Pic8259 { 21 | Pic8259 { 22 | master_port, 23 | slave_port, 24 | } 25 | } 26 | 27 | pub unsafe fn init(&mut self, master_vec_base: u8, slave_vec_base: u8) { 28 | unsafe { 29 | outb(self.master_port, 0b0001_0001); 30 | outb(self.slave_port, 0b0001_0001); 31 | outb(self.master_port + 1, master_vec_base); 32 | outb(self.slave_port + 1, slave_vec_base); 33 | outb(self.master_port + 1, 1 << 2); // Slave on IRQ 2 34 | outb(self.slave_port + 1, 1 << 1); // Slave ID 1 35 | outb(self.master_port + 1, 0b0000_0001); 36 | outb(self.slave_port + 1, 0b0000_0001); 37 | outb(self.master_port + 1, 0b0000_0000); 38 | outb(self.slave_port + 1, 0b0000_0000); 39 | } 40 | } 41 | 42 | pub fn ack_irq(&mut self, irq: u32) { 43 | if irq >= 8 { 44 | unsafe { 45 | outb(self.slave_port, 0x20); 46 | } 47 | } 48 | 49 | unsafe { 50 | outb(self.master_port, 0x20); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/arch/x86/driver/ps2.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use x86::io::{inb, outb}; 12 | 13 | use crate::arch::sync::{pop_critical_region, push_critical_region}; 14 | use crate::driver::keyboard::{Key, KeyEvent, on_key_event}; 15 | use crate::sync::Spinlock; 16 | 17 | const DATA_PORT: u16 = 0x60; 18 | const STATUS_REGISTER: u16 = 0x64; 19 | const COMMAND_REGISTER: u16 = 0x64; 20 | 21 | const CMD_READ_CONF: u8 = 0x20; 22 | const CMD_WRITE_CONF: u8 = 0x20; 23 | const CMD_DISABLE_DEV1: u8 = 0xae; 24 | const CMD_DISABLE_DEV2: u8 = 0xa7; 25 | const CMD_ENABLE_DEV1: u8 = 0xae; 26 | 27 | const STATUS_OUTPUT_BUSY: u8 = 1 << 0; 28 | const STATUS_INPUT_BUSY: u8 = 1 << 1; 29 | 30 | const CTRL_CONF_DEV1_INTERRUPT: u8 = 1 << 0; 31 | const CTRL_CONF_DEV2_INTERRUPT: u8 = 1 << 1; 32 | const CTRL_CONF_DEV1_TRANSLATION: u8 = 1 << 6; 33 | 34 | static PS2_KEYBOARD: Spinlock> = Spinlock::new(None); 35 | 36 | pub struct PS2Keyboard { 37 | is_e0_state: bool, 38 | } 39 | 40 | impl PS2Keyboard { 41 | pub fn new() -> Self { 42 | Self { is_e0_state: false } 43 | } 44 | 45 | pub fn on_irq(&mut self) { 46 | if is_output_full() { 47 | let byte = unsafe { inb(DATA_PORT) }; 48 | if byte == 0xe0 { 49 | self.is_e0_state = true; 50 | } else { 51 | let ev = self.read_key(byte); 52 | self.is_e0_state = false; 53 | on_key_event(ev); 54 | } 55 | } 56 | } 57 | 58 | fn read_key(&self, key: u8) -> KeyEvent { 59 | let pressed = key & 1 << 7 == 0; 60 | let key = key & !(1 << 7); 61 | 62 | let key = if self.is_e0_state { 63 | match key { 64 | 0x1d => Key::RightCtrl, 65 | 0x38 => Key::AltGr, 66 | 0x5b => Key::LeftMeta, 67 | 0x5c => Key::RightMeta, 68 | 0x5d => Key::Menu, 69 | 70 | 0x52 => Key::Insert, 71 | 0x53 => Key::Del, 72 | 0x47 => Key::Home, 73 | 0x4f => Key::End, 74 | 0x49 => Key::PgUp, 75 | 0x51 => Key::PgDown, 76 | 0x48 => Key::Up, 77 | 0x50 => Key::Down, 78 | 0x4b => Key::Left, 79 | 0x4d => Key::Right, 80 | 81 | 0x1c => Key::KeypadEnter, 82 | 0x35 => Key::KeypadDiv, 83 | 84 | _ => return KeyEvent::Unknown, 85 | } 86 | } else { 87 | match key { 88 | 0x01 => Key::Escape, 89 | 0x3b..=0x44 => Key::F(key - 0x3b + 1), 90 | 0x57 => Key::F(11), 91 | 0x58 => Key::F(12), 92 | 0x46 => Key::ScrollLock, 93 | 94 | 0x29 => Key::Backquote, 95 | 0x02..=0x0a => Key::Digit(key - 0x02 + 1), 96 | 0x0b => Key::Digit(0), 97 | 0x0c => Key::Dash, 98 | 0x0d => Key::Equal, 99 | 0x0e => Key::Backspace, 100 | 101 | 0x0f => Key::Tab, 102 | 0x10 => Key::Letter('Q'), 103 | 0x11 => Key::Letter('W'), 104 | 0x12 => Key::Letter('E'), 105 | 0x13 => Key::Letter('R'), 106 | 0x14 => Key::Letter('T'), 107 | 0x15 => Key::Letter('Y'), 108 | 0x16 => Key::Letter('U'), 109 | 0x17 => Key::Letter('I'), 110 | 0x18 => Key::Letter('O'), 111 | 0x19 => Key::Letter('P'), 112 | 0x1a => Key::LeftBracket, 113 | 0x1b => Key::RightBracket, 114 | 0x2b => Key::Backslash, 115 | 0x1c => Key::Enter, 116 | 117 | 0x3a => Key::CapsLock, 118 | 0x1e => Key::Letter('A'), 119 | 0x1f => Key::Letter('S'), 120 | 0x20 => Key::Letter('D'), 121 | 0x21 => Key::Letter('F'), 122 | 0x22 => Key::Letter('G'), 123 | 0x23 => Key::Letter('H'), 124 | 0x24 => Key::Letter('J'), 125 | 0x25 => Key::Letter('K'), 126 | 0x26 => Key::Letter('L'), 127 | 0x27 => Key::Semicolon, 128 | 0x28 => Key::SingleQuote, 129 | 130 | 0x2a => Key::LeftShift, 131 | 0x56 => Key::Iso, 132 | 0x2c => Key::Letter('Z'), 133 | 0x2d => Key::Letter('X'), 134 | 0x2e => Key::Letter('C'), 135 | 0x2f => Key::Letter('V'), 136 | 0x30 => Key::Letter('B'), 137 | 0x31 => Key::Letter('N'), 138 | 0x32 => Key::Letter('M'), 139 | 0x33 => Key::Comma, 140 | 0x34 => Key::Period, 141 | 0x35 => Key::Slash, 142 | 0x36 => Key::RightShift, 143 | 144 | 0x1d => Key::LeftCtrl, 145 | 0x38 => Key::Alt, 146 | 0x39 => Key::Space, 147 | 148 | 0x52 => Key::KeypadDigit(0), 149 | 0x4f => Key::KeypadDigit(1), 150 | 0x50 => Key::KeypadDigit(2), 151 | 0x51 => Key::KeypadDigit(3), 152 | 0x4b => Key::KeypadDigit(4), 153 | 0x4c => Key::KeypadDigit(5), 154 | 0x4d => Key::KeypadDigit(6), 155 | 0x47 => Key::KeypadDigit(7), 156 | 0x48 => Key::KeypadDigit(8), 157 | 0x49 => Key::KeypadDigit(9), 158 | 0x45 => Key::KeypadNumLock, 159 | 0x53 => Key::KeypadPeriod, 160 | 0x4e => Key::KeypadPlus, 161 | 0x4a => Key::KeypadMinus, 162 | 0x37 => Key::KeypadMul, 163 | 164 | _ => return KeyEvent::Unknown, 165 | } 166 | }; 167 | 168 | if pressed { 169 | KeyEvent::Pressed(key) 170 | } else { 171 | KeyEvent::Released(key) 172 | } 173 | } 174 | } 175 | 176 | pub fn init() { 177 | push_critical_region(); 178 | 179 | drain_output(); 180 | 181 | send_cmd(CMD_DISABLE_DEV1); 182 | send_cmd(CMD_DISABLE_DEV2); 183 | 184 | let mut ctrl = read_conf_byte(0); 185 | ctrl &= !CTRL_CONF_DEV1_INTERRUPT; 186 | ctrl &= !CTRL_CONF_DEV2_INTERRUPT; 187 | ctrl &= !CTRL_CONF_DEV1_TRANSLATION; 188 | write_conf_byte(0, ctrl); 189 | 190 | send_cmd(CMD_ENABLE_DEV1); 191 | 192 | drain_output(); 193 | 194 | *PS2_KEYBOARD.lock() = Some(PS2Keyboard::new()); 195 | 196 | pop_critical_region(); 197 | } 198 | 199 | pub fn on_irq() { 200 | if let Some(kb) = PS2_KEYBOARD.lock().as_mut() { 201 | kb.on_irq(); 202 | } 203 | } 204 | 205 | pub fn hard_reset() -> ! { 206 | unsafe { 207 | outb(COMMAND_REGISTER, 0xfe); 208 | } 209 | 210 | unreachable!() 211 | } 212 | 213 | fn read_conf_byte(offset: u8) -> u8 { 214 | if offset > 17 { 215 | panic!("Invalid offset"); 216 | } 217 | 218 | send_cmd(CMD_READ_CONF + offset); 219 | wait_for_output(); 220 | unsafe { inb(DATA_PORT) } 221 | } 222 | 223 | fn write_conf_byte(offset: u8, byte: u8) { 224 | if offset > 17 { 225 | panic!("Invalid offset"); 226 | } 227 | wait_input_ready(); 228 | unsafe { 229 | outb(DATA_PORT, byte); 230 | } 231 | send_cmd(CMD_WRITE_CONF + offset); 232 | } 233 | 234 | fn send_cmd(cmd: u8) { 235 | wait_input_ready(); 236 | unsafe { 237 | outb(COMMAND_REGISTER, cmd); 238 | } 239 | } 240 | 241 | fn wait_input_ready() { 242 | while unsafe { inb(STATUS_REGISTER) } & STATUS_INPUT_BUSY > 0 {} 243 | } 244 | 245 | fn wait_for_output() { 246 | while !is_output_full() {} 247 | } 248 | 249 | fn is_output_full() -> bool { 250 | (unsafe { inb(STATUS_REGISTER) } & STATUS_OUTPUT_BUSY) > 0 251 | } 252 | 253 | fn drain_output() { 254 | while is_output_full() { 255 | unsafe { 256 | inb(DATA_PORT); 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/arch/x86/driver/serial.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::fmt; 12 | use core::fmt::Write; 13 | use x86::io::{inb, outb}; 14 | 15 | use crate::logging::{Logger, Severity}; 16 | 17 | pub const COM1_IOPORT: u16 = 0x03f8; 18 | pub const COM2_IOPORT: u16 = 0x02f8; 19 | pub const COM3_IOPORT: u16 = 0x03e8; 20 | pub const COM4_IOPORT: u16 = 0x02e8; 21 | 22 | const REG_DATA: u16 = 0; // DLAB = 0 23 | const REG_DIVISOR_LSB: u16 = 0; // DLAB = 1 24 | const REG_IRQ_ENABLE: u16 = 1; // DLAB = 0 25 | const REG_DIVISOR_MSB: u16 = 1; // DLAB = 1 26 | const REG_IRQ_ID: u16 = 2; 27 | const REG_LINE_CTRL: u16 = 3; 28 | const REG_MODEM_CTRL: u16 = 4; 29 | const REG_LINE_STATUS: u16 = 5; 30 | const REG_MODEM_STATUS: u16 = 6; 31 | const REG_SCRATCH: u16 = 7; 32 | 33 | pub struct SerialDevice { 34 | ioport_base: u16, 35 | baud_rate: u32, 36 | parity: ParityMode, 37 | bits: u8, 38 | stop_bits: StopBits, 39 | } 40 | 41 | pub enum ParityMode { 42 | None, 43 | Odd, 44 | Even, 45 | Mark, 46 | Space, 47 | } 48 | 49 | pub enum StopBits { 50 | One, 51 | Two, 52 | } 53 | 54 | impl SerialDevice { 55 | pub unsafe fn new( 56 | ioport_base: u16, 57 | baud_rate: u32, 58 | parity: ParityMode, 59 | bits: u8, 60 | stop_bits: StopBits, 61 | ) -> Result { 62 | let mut dev = Self { 63 | ioport_base, 64 | baud_rate, 65 | parity, 66 | bits, 67 | stop_bits, 68 | }; 69 | 70 | dev.init()?; 71 | 72 | Ok(dev) 73 | } 74 | 75 | fn init(&mut self) -> Result<(), &'static str> { 76 | let divisor: u16 = match self.baud_rate { 77 | 115_200 => 1, 78 | 57_600 => 2, 79 | 38_400 => 3, 80 | 19_200 => 6, 81 | 9600 => 12, 82 | 4800 => 24, 83 | 2400 => 48, 84 | 1200 => 96, 85 | 600 => 192, 86 | 300 => 384, 87 | 220 => 524, 88 | 110 => 1047, 89 | 50 => 2304, 90 | _ => return Err("Unsupported baud rate, no divisor available"), 91 | }; 92 | 93 | let parity_bits = match &self.parity { 94 | ParityMode::None => 0b000, 95 | ParityMode::Odd => 0b001, 96 | ParityMode::Even => 0b011, 97 | ParityMode::Mark => 0b101, 98 | ParityMode::Space => 0b111, 99 | }; 100 | let stop_bits = match &self.stop_bits { 101 | StopBits::One => 0, 102 | StopBits::Two => 1, 103 | }; 104 | let bits = match self.bits { 105 | 5 => 0b00, 106 | 6 => 0b01, 107 | 7 => 0b10, 108 | 8 => 0b11, 109 | _ => return Err("Unsupported number of data bits"), 110 | }; 111 | let line_ctrl: u8 = (parity_bits << 3) | (stop_bits << 2) | (bits << 0); 112 | 113 | unsafe { 114 | outb(self.ioport_base + REG_LINE_CTRL, 1 << 7); // DLAB = 1 115 | outb(self.ioport_base + REG_DIVISOR_MSB, (divisor >> 8) as u8); 116 | outb(self.ioport_base + REG_DIVISOR_LSB, (divisor & 0xff) as u8); 117 | outb(self.ioport_base + REG_LINE_CTRL, line_ctrl); // DLAB = 0 118 | outb(self.ioport_base + REG_IRQ_ENABLE, 0x00); 119 | } 120 | 121 | Ok(()) 122 | } 123 | 124 | pub fn may_read(&self) -> bool { 125 | (unsafe { inb(self.ioport_base + REG_LINE_STATUS) } & (1 << 0)) > 0 126 | } 127 | 128 | pub fn read_blocking(&self) -> u8 { 129 | while !self.may_read() {} 130 | 131 | unsafe { inb(self.ioport_base + REG_DATA) } 132 | } 133 | 134 | pub fn may_write(&self) -> bool { 135 | (unsafe { inb(self.ioport_base + REG_LINE_STATUS) } & (1 << 5)) > 0 136 | } 137 | 138 | pub fn write_byte(&mut self, byte: u8) { 139 | while !self.may_write() {} 140 | 141 | unsafe { 142 | outb(self.ioport_base + REG_DATA, byte); 143 | } 144 | } 145 | } 146 | 147 | impl fmt::Write for SerialDevice { 148 | fn write_str(&mut self, s: &str) -> fmt::Result { 149 | for &byte in s.as_bytes().iter() { 150 | self.write_byte(byte); 151 | } 152 | 153 | Ok(()) 154 | } 155 | } 156 | 157 | impl Logger for SerialDevice { 158 | fn log(&mut self, severity: Severity, args: fmt::Arguments) { 159 | let (color, severity_str) = match severity { 160 | Severity::Debug => ("\x1b[90m", "debug"), 161 | Severity::Info => ("\x1b[37m", "info"), 162 | Severity::Notice => ("\x1b[97m", "notice"), 163 | Severity::Warning => ("\x1b[93m", "warning"), 164 | Severity::Error => ("\x1b[31m", "error"), 165 | Severity::Critical => ("\x1b[1;31m", "critic."), 166 | Severity::Alert => ("\x1b[1;97;41m", "ALERT"), 167 | Severity::Emergency => ("\x1b[1;93;41m", "EMERG."), 168 | }; 169 | 170 | write!(self, "{}{:>7}: ", color, severity_str).unwrap(); 171 | self.write_fmt(args).unwrap(); 172 | write!(self, "\x1b[0m\n").unwrap(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/arch/x86/driver/vesa.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::driver::screen::{Color, FramebufferScreen}; 12 | 13 | pub struct VesaFramebuffer { 14 | mem: &'static mut [u32], 15 | width: usize, 16 | height: usize, 17 | pitch: usize, 18 | bpp: u8, 19 | } 20 | 21 | impl VesaFramebuffer { 22 | pub unsafe fn new( 23 | buffer: *mut u32, 24 | width: usize, 25 | height: usize, 26 | pitch: usize, 27 | bpp: u8, 28 | ) -> Self { 29 | let buff_size = pitch * height; 30 | 31 | assert_eq!(bpp, 32); 32 | 33 | VesaFramebuffer { 34 | mem: unsafe { 35 | core::slice::from_raw_parts_mut(buffer, buff_size >> 2) 36 | }, 37 | width, 38 | height, 39 | pitch, 40 | bpp, 41 | } 42 | } 43 | } 44 | 45 | impl FramebufferScreen for VesaFramebuffer { 46 | fn dimensions(&self) -> (usize, usize) { 47 | (self.width, self.height) 48 | } 49 | 50 | fn put(&mut self, x: usize, y: usize, color: Color) { 51 | let px = 52 | (color.r as u32) << 16 | (color.g as u32) << 8 | (color.b as u32); 53 | let index = (self.pitch >> 2) * y + x; 54 | self.mem[index] = px; 55 | } 56 | 57 | fn copy(&mut self, x: usize, y: usize, data: &[u32]) { 58 | let index = (self.pitch >> 2) * y + x; 59 | let target = &mut self.mem[index..(index + data.len())]; 60 | target.copy_from_slice(data); 61 | } 62 | 63 | fn clear(&mut self) { 64 | todo!() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/arch/x86/driver/vga.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::driver::vga::VgaScreen; 12 | 13 | use core::fmt; 14 | use core::slice; 15 | 16 | pub struct Vga<'a> { 17 | mem: &'a mut [u8], 18 | width: u8, 19 | height: u8, 20 | curs_x: u8, 21 | curs_y: u8, 22 | attr: u8, 23 | } 24 | 25 | impl<'a> Vga<'a> { 26 | pub unsafe fn new( 27 | addr: *mut u8, 28 | size: usize, 29 | width: u8, 30 | height: u8, 31 | ) -> Self { 32 | assert_eq!(size, width as usize * height as usize * 2); 33 | Self { 34 | mem: unsafe { slice::from_raw_parts_mut(addr, size) }, 35 | width, 36 | height, 37 | curs_x: 0, 38 | curs_y: 0, 39 | attr: 0x07, 40 | } 41 | } 42 | 43 | fn cursor_index(&self) -> usize { 44 | (self.curs_y as usize * self.width as usize * 2) 45 | + (self.curs_x as usize * 2) 46 | } 47 | } 48 | 49 | impl<'a> VgaScreen for Vga<'a> { 50 | fn put_char(&mut self, c: u8) { 51 | match c { 52 | b'\n' => { 53 | self.curs_x = 0; 54 | self.curs_y += 1; 55 | } 56 | b'\t' => { 57 | self.curs_x += 8 - (self.curs_x % 8); 58 | } 59 | b'\r' => { 60 | self.curs_x = 0; 61 | } 62 | _ => { 63 | let index = self.cursor_index(); 64 | self.mem[index] = c; 65 | self.mem[index + 1] = self.attr; 66 | self.curs_x += 1; 67 | } 68 | } 69 | 70 | if self.curs_x >= self.width { 71 | self.curs_x = 0; 72 | self.curs_y += 1; 73 | } 74 | 75 | if self.curs_y >= self.height { 76 | self.scroll_up(self.curs_y - self.height + 1); 77 | } 78 | } 79 | 80 | fn put_str(&mut self, str: &str) { 81 | for c in str.chars() { 82 | if c.is_ascii() { 83 | self.put_char(c as u8); 84 | } else { 85 | self.put_char(b'?'); 86 | } 87 | } 88 | } 89 | 90 | fn set_attributes(&mut self, attr: u8) { 91 | self.attr = attr; 92 | } 93 | 94 | fn move_cursor(&mut self, x: u8, y: u8) { 95 | assert!(x < self.width && y < self.height); 96 | self.curs_x = x; 97 | self.curs_y = y; 98 | } 99 | 100 | fn cursor(&self) -> (u8, u8) { 101 | (self.curs_x, self.curs_y) 102 | } 103 | 104 | fn scroll_up(&mut self, lines: u8) { 105 | let start = lines as usize * self.width as usize * 2; 106 | let len = (self.height - lines) as usize * self.width as usize * 2; 107 | 108 | self.mem.copy_within(start..(start + len), 0); 109 | self.mem[len..].fill(0); 110 | self.curs_y -= lines; 111 | } 112 | 113 | fn clear(&mut self) { 114 | self.mem.fill(0); 115 | self.curs_x = 0; 116 | self.curs_y = 0; 117 | } 118 | } 119 | 120 | impl<'a> fmt::Write for Vga<'a> { 121 | fn write_str(&mut self, s: &str) -> fmt::Result { 122 | self.put_str(s); 123 | Ok(()) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/arch/x86/export/cpu.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::arch::asm; 12 | use core::fmt; 13 | use core::fmt::{Display, Formatter}; 14 | 15 | use crate::arch::x86::driver::ps2; 16 | use crate::driver::vga::VgaScreen; 17 | use crate::println; 18 | 19 | pub struct MachineState { 20 | pub rax: u64, 21 | pub rbx: u64, 22 | pub rcx: u64, 23 | pub rdx: u64, 24 | 25 | pub r8: u64, 26 | pub r9: u64, 27 | pub r10: u64, 28 | pub r11: u64, 29 | pub r12: u64, 30 | pub r13: u64, 31 | pub r14: u64, 32 | pub r15: u64, 33 | 34 | pub rdi: u64, 35 | pub rsi: u64, 36 | 37 | pub rsp: u64, 38 | pub rbp: u64, 39 | 40 | pub rip: u64, 41 | pub rflags: u64, 42 | 43 | pub cs: u16, 44 | pub ss: u16, 45 | pub ds: u16, 46 | pub es: u16, 47 | pub fs: u16, 48 | pub gs: u16, 49 | } 50 | 51 | impl MachineState { 52 | #[inline(always)] 53 | pub fn here() -> Self { 54 | let rip; 55 | let rsp; 56 | let rbp; 57 | let cs; 58 | let ss; 59 | let ds; 60 | let es; 61 | let fs; 62 | let gs; 63 | 64 | unsafe { 65 | asm!( 66 | "lea {}, [rip - 7]", 67 | "mov {}, rsp", 68 | "mov {}, rbp", 69 | "mov {:x}, cs", 70 | "mov {:x}, ss", 71 | "mov {:x}, ds", 72 | "mov {:x}, es", 73 | "mov {:x}, fs", 74 | "mov {:x}, gs", 75 | out(reg) rip, 76 | out(reg) rsp, 77 | out(reg) rbp, 78 | out(reg) cs, 79 | out(reg) ss, 80 | out(reg) ds, 81 | out(reg) es, 82 | out(reg) fs, 83 | out(reg) gs, 84 | ); 85 | } 86 | 87 | Self { 88 | rax: 0, 89 | rbx: 0, 90 | rcx: 0, 91 | rdx: 0, 92 | r8: 0, 93 | r9: 0, 94 | r10: 0, 95 | r11: 0, 96 | r12: 0, 97 | r13: 0, 98 | r14: 0, 99 | r15: 0, 100 | rdi: 0, 101 | rsi: 0, 102 | rip, 103 | rsp, 104 | rbp, 105 | rflags: 0, 106 | cs, 107 | ss, 108 | ds, 109 | es, 110 | fs, 111 | gs, 112 | } 113 | } 114 | 115 | pub fn print(&self, vga: &mut impl VgaScreen) -> fmt::Result { 116 | writeln!( 117 | vga, 118 | "rax{:016x} rbx{:016x} rcx{:016x} rdx{:016x}", 119 | self.rax, self.rbx, self.rcx, self.rdx 120 | )?; 121 | writeln!( 122 | vga, 123 | "rdi{:016x} rsi{:016x} rbp{:016x} rsp{:016x}", 124 | self.rdi, self.rsi, self.rbp, self.rsp 125 | )?; 126 | writeln!( 127 | vga, 128 | "r8 {:016x} r9 {:016x} r10{:016x} r11{:016x}", 129 | self.r8, self.r9, self.r10, self.r11 130 | )?; 131 | writeln!( 132 | vga, 133 | "r12{:016x} r13{:016x} r14{:016x} r15{:016x}", 134 | self.r12, self.r13, self.r14, self.r15 135 | )?; 136 | writeln!( 137 | vga, 138 | "rip={:016x} cs={:04x} ss={:04x} ds={:04x} es={:04x} fs={:04x} gs={:04x}", 139 | self.rip, self.cs, self.ss, self.ds, self.es, self.fs, self.gs 140 | ) 141 | } 142 | 143 | pub fn print_term(&self) { 144 | use crate::screen::R; 145 | 146 | println!( 147 | "\x1brax=\x1b{:x} \x1brbx=\x1b{:x} \x1brcx=\x1b{:x} \x1brdx=\x1b{:x}", 148 | R(self.rax), 149 | R(self.rbx), 150 | R(self.rcx), 151 | R(self.rdx) 152 | ); 153 | println!( 154 | "\x1brdi=\x1b{:x} \x1brsi=\x1b{:x} \x1brbp=\x1b{:x} \x1brsp=\x1b{:x}", 155 | R(self.rdi), 156 | R(self.rsi), 157 | R(self.rbp), 158 | R(self.rsp) 159 | ); 160 | println!( 161 | " \x1br8=\x1b{:x} \x1br9=\x1b{:x} \x1br10=\x1b{:x} \x1br11=\x1b{:x}", 162 | R(self.r8), 163 | R(self.r9), 164 | R(self.r10), 165 | R(self.r11) 166 | ); 167 | println!( 168 | "\x1br12=\x1b{:x} \x1br13=\x1b{:x} \x1br14=\x1b{:x} \x1br15=\x1b{:x}", 169 | R(self.r12), 170 | R(self.r13), 171 | R(self.r14), 172 | R(self.r15) 173 | ); 174 | println!( 175 | "\x1brip=\x1b{:016x} \x1bcs=\x1b{:04x} \x1bss=\x1b{:04x} \x1bds=\x1b{:04x} \x1bes=\x1b{:04x} \x1bfs=\x1b{:04x} \x1bgs=\x1b{:04x}", 176 | R(self.rip), 177 | self.cs, 178 | self.ss, 179 | self.ds, 180 | self.es, 181 | self.fs, 182 | self.gs 183 | ); 184 | } 185 | } 186 | 187 | impl Display for MachineState { 188 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 189 | use crate::screen::R; 190 | 191 | writeln!( 192 | f, 193 | "rax={:x} rbx={:x} rcx={:x} rdx={:x}", 194 | R(self.rax), 195 | R(self.rbx), 196 | R(self.rcx), 197 | R(self.rdx) 198 | )?; 199 | writeln!( 200 | f, 201 | "rdi={:x} rsi={:x} rbp={:x} rsp={:x}", 202 | R(self.rdi), 203 | R(self.rsi), 204 | R(self.rbp), 205 | R(self.rsp) 206 | )?; 207 | writeln!( 208 | f, 209 | " r8={:x} r9={:x} r10={:x} r11={:x}", 210 | R(self.r8), 211 | R(self.r9), 212 | R(self.r10), 213 | R(self.r11) 214 | )?; 215 | writeln!( 216 | f, 217 | "r12={:x} r13={:x} r14={:x} r15={:x}", 218 | R(self.r12), 219 | R(self.r13), 220 | R(self.r14), 221 | R(self.r15) 222 | )?; 223 | writeln!( 224 | f, 225 | "rip={:016x} cs={:04x} ss={:04x} ds={:04x} es={:04x} fs={:04x} gs={:04x}", 226 | self.rip, self.cs, self.ss, self.ds, self.es, self.fs, self.gs 227 | ) 228 | } 229 | } 230 | 231 | pub fn halt() { 232 | unsafe { 233 | x86::halt(); 234 | } 235 | } 236 | 237 | pub fn perm_halt() -> ! { 238 | unsafe { x86::irq::disable() }; 239 | loop { 240 | halt(); 241 | } 242 | } 243 | 244 | pub fn reset() -> ! { 245 | ps2::hard_reset(); 246 | } 247 | -------------------------------------------------------------------------------- /src/arch/x86/export/logging.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::arch::x86::driver::serial::SerialDevice; 12 | 13 | pub static mut LOGGER_SERIAL: Option = None; 14 | -------------------------------------------------------------------------------- /src/arch/x86/export/mem/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::fmt::{self, Debug, Formatter}; 12 | 13 | use crate::arch::x86::mem::paging::{AnyEntry, locate_page_entry}; 14 | use crate::mem::{PagePermissions, VAddr, get_lowmem_va_end}; 15 | 16 | #[derive(Copy, Clone)] 17 | #[repr(C)] 18 | pub struct PAddr(pub u64); 19 | 20 | impl Debug for PAddr { 21 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 22 | write!(f, "PA {:#016x}", self.0) 23 | } 24 | } 25 | 26 | impl PAddr { 27 | /// Convert the physical address into a virtual address. 28 | pub fn into_vaddr(self) -> VAddr { 29 | VAddr(self.0 as usize) + LOWMEM_VA_START 30 | } 31 | 32 | pub fn from_lowmem_vaddr(vaddr: VAddr) -> Option { 33 | if vaddr < LOWMEM_VA_START || vaddr >= get_lowmem_va_end() { 34 | None 35 | } else { 36 | Some(Self(vaddr.0 as u64 - LOWMEM_VA_START.0 as u64)) 37 | } 38 | } 39 | } 40 | 41 | impl VAddr { 42 | /// Retrieve the physical address at which this virtual address is mapped to 43 | /// if such mapping exists. This operation is rather costful since it 44 | /// requires traversing page tables. 45 | pub fn to_paddr(self) -> Option { 46 | Some(locate_page_entry(self)?.paddr()) 47 | } 48 | 49 | pub fn pml4e(&self) -> usize { 50 | (self.0 & (0x1ff << 39)) >> 39 51 | } 52 | 53 | pub fn pdpte(&self) -> usize { 54 | (self.0 & (0x1ff << 30)) >> 30 55 | } 56 | 57 | pub fn pde(&self) -> usize { 58 | (self.0 & (0x1ff << 21)) >> 21 59 | } 60 | 61 | pub fn pte(&self) -> usize { 62 | (self.0 & (0x1ff << 12)) >> 12 63 | } 64 | 65 | pub fn pt_offset(&self) -> usize { 66 | self.0 & 0xfff 67 | } 68 | } 69 | 70 | /// The virtual address of the first byte of the low-memory area, i.e. the 71 | /// virtual addresses that identity-map the physical address space. 72 | pub const LOWMEM_VA_START: VAddr = VAddr(0xffff8000_00000000); 73 | 74 | pub const LOWMEM_SIZE: usize = (128 << 40) - 1; // 128 Tio - 1 75 | 76 | pub const PAGE_SIZE: usize = 4096; 77 | pub const PAGE_SIZE_BITS: usize = 12; 78 | pub const FRAME_SIZE: usize = 4096; 79 | pub const FRAME_SIZE_BITS: usize = 12; 80 | 81 | pub fn page_permissions(vaddr: VAddr) -> PagePermissions { 82 | let entry = locate_page_entry(vaddr); 83 | 84 | if entry.is_none() { 85 | return PagePermissions { 86 | accessible: false, 87 | readable: false, 88 | writable: false, 89 | executable: false, 90 | }; 91 | } 92 | let entry = entry.unwrap(); 93 | 94 | match entry { 95 | AnyEntry::PML4Entry(_) => unreachable!(), 96 | AnyEntry::PDPTEntry(_) => unimplemented!(), 97 | AnyEntry::PDEntry(pde) => PagePermissions { 98 | accessible: pde.is_present(), 99 | readable: pde.is_present(), 100 | writable: pde.is_present() && pde.is_writable(), 101 | executable: pde.is_present() && pde.is_executable(), 102 | }, 103 | AnyEntry::PTEntry(pte) => PagePermissions { 104 | accessible: pte.is_present(), 105 | readable: pte.is_present(), 106 | writable: pte.is_present() && pte.is_writable(), 107 | executable: pte.is_present() && pte.is_executable(), 108 | }, 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/arch/x86/export/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub mod cpu; 12 | pub mod logging; 13 | pub mod mem; 14 | pub mod sync; 15 | pub mod task; 16 | 17 | pub use super::driver::vesa::VesaFramebuffer; 18 | -------------------------------------------------------------------------------- /src/arch/x86/export/sync.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::sync::atomic::{AtomicU32, Ordering}; 12 | 13 | // FIXME: implement per SMP processor 14 | static CRITICAL_REGION_DEPTH: AtomicU32 = AtomicU32::new(0); 15 | 16 | pub fn push_critical_region() { 17 | let prev = CRITICAL_REGION_DEPTH.fetch_add(1, Ordering::SeqCst); 18 | 19 | if prev == 0 { 20 | unsafe { x86::irq::disable() }; 21 | } 22 | } 23 | 24 | pub fn pop_critical_region() { 25 | let prev = CRITICAL_REGION_DEPTH.fetch_sub(1, Ordering::SeqCst); 26 | 27 | if prev == 1 { 28 | unsafe { x86::irq::enable() }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/arch/x86/export/task.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub struct TaskMachineContext { 12 | pub rax: u64, 13 | pub rbx: u64, 14 | pub rcx: u64, 15 | pub rdx: u64, 16 | 17 | pub r8: u64, 18 | pub r9: u64, 19 | pub r10: u64, 20 | pub r11: u64, 21 | pub r12: u64, 22 | pub r13: u64, 23 | pub r14: u64, 24 | pub r15: u64, 25 | 26 | pub rdi: u64, 27 | pub rsi: u64, 28 | 29 | pub rsp: u64, 30 | pub rbp: u64, 31 | 32 | pub rip: u64, 33 | pub rflags: u64, 34 | 35 | pub cs: u16, 36 | pub ss: u16, 37 | pub ds: u16, 38 | pub es: u16, 39 | pub fs: u16, 40 | pub gs: u16, 41 | 42 | pub cr3: u64, 43 | } 44 | -------------------------------------------------------------------------------- /src/arch/x86/gdt.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use x86::Ring::Ring0; 12 | use x86::current::task::TaskStateSegment; 13 | use x86::dtables::{DescriptorTablePointer, lgdt}; 14 | use x86::segmentation::{ 15 | BuildDescriptor, Descriptor as Descriptor32, DescriptorBuilder, 16 | GateDescriptorBuilder, SegmentDescriptorBuilder, SegmentSelector, load_cs, 17 | load_ds, load_es, load_fs, load_gs, load_ss, 18 | }; 19 | use x86::task::load_tr; 20 | 21 | #[cfg(target_arch = "x86_64")] 22 | use x86::bits64::segmentation::Descriptor64; 23 | 24 | use crate::mem::{PAddr, VAddr}; 25 | 26 | type DescriptorN = Descriptor64; 27 | 28 | type UsizeT = u64; 29 | 30 | #[derive(Default)] 31 | #[repr(C, packed)] 32 | struct Gdt { 33 | pub null: Descriptor32, 34 | pub kernel_cs: Descriptor32, 35 | pub kernel_ds: Descriptor32, 36 | pub user_cs32: Descriptor32, 37 | pub user_cs64: Descriptor32, 38 | pub user_ds: Descriptor32, 39 | pub tss: DescriptorN, 40 | } 41 | 42 | static mut BSP_GDT: Gdt = Gdt { 43 | null: Descriptor32::NULL, 44 | kernel_cs: Descriptor32::NULL, 45 | kernel_ds: Descriptor32::NULL, 46 | user_cs32: Descriptor32::NULL, 47 | user_cs64: Descriptor32::NULL, 48 | user_ds: Descriptor32::NULL, 49 | tss: DescriptorN::NULL, 50 | }; 51 | 52 | pub const KERNEL_CODE_SELECTOR: SegmentSelector = 53 | SegmentSelector::new(1, Ring0); 54 | 55 | static mut BSP_TSS: TaskStateSegment = TaskStateSegment::new(); 56 | 57 | pub unsafe fn setup_table() { 58 | use x86::Ring::*; 59 | use x86::segmentation::CodeSegmentType::*; 60 | use x86::segmentation::DataSegmentType::*; 61 | 62 | let mut cs = DescriptorBuilder::code_descriptor(0, 0xfffff, ExecuteRead) 63 | .present() 64 | .dpl(Ring0) 65 | .limit_granularity_4kb(); 66 | cs = cs.l(); 67 | 68 | unsafe { 69 | BSP_GDT.kernel_cs = cs.finish(); 70 | 71 | BSP_GDT.kernel_ds = 72 | DescriptorBuilder::data_descriptor(0, 0xfffff, ReadWrite) 73 | .present() 74 | .dpl(Ring0) 75 | .limit_granularity_4kb() 76 | .db() 77 | .finish(); 78 | BSP_GDT.user_cs32 = 79 | DescriptorBuilder::code_descriptor(0, 0xfffff, ExecuteRead) 80 | .present() 81 | .dpl(Ring3) 82 | .limit_granularity_4kb() 83 | .db() 84 | .finish(); 85 | BSP_GDT.user_cs64 = 86 | DescriptorBuilder::code_descriptor(0, 0xfffff, ExecuteRead) 87 | .present() 88 | .dpl(Ring3) 89 | .limit_granularity_4kb() 90 | .l() 91 | .finish(); 92 | BSP_GDT.user_ds = 93 | DescriptorBuilder::data_descriptor(0, 0xfffff, ReadWrite) 94 | .present() 95 | .dpl(Ring3) 96 | .limit_granularity_4kb() 97 | .db() 98 | .finish(); 99 | 100 | BSP_GDT.tss = 101 | >::tss_descriptor( 102 | PAddr::from_lowmem_vaddr(VAddr(&BSP_TSS as *const _ as _)) 103 | .unwrap() 104 | .0 as _, 105 | core::mem::size_of_val(&BSP_TSS) as _, 106 | true, 107 | ) 108 | .present() 109 | .finish(); 110 | 111 | let ptr = DescriptorTablePointer::new(&BSP_GDT); 112 | lgdt(&ptr); 113 | } 114 | } 115 | 116 | pub unsafe fn load_kernel_selectors() { 117 | use x86::Ring::*; 118 | 119 | unsafe { 120 | load_cs(SegmentSelector::new(1, Ring0)); 121 | load_ss(SegmentSelector::new(2, Ring0)); 122 | load_ds(SegmentSelector::new(2, Ring0)); 123 | load_es(SegmentSelector::new(2, Ring0)); 124 | load_fs(SegmentSelector::new(2, Ring0)); 125 | load_gs(SegmentSelector::new(2, Ring0)); 126 | load_tr(SegmentSelector::new(6, Ring0)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/arch/x86/init.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::arch; 12 | use crate::arch::mem::LOWMEM_VA_START; 13 | use crate::arch::sync::{pop_critical_region, push_critical_region}; 14 | use crate::arch::x86::driver::ps2; 15 | use crate::arch::x86::driver::serial::{ 16 | COM1_IOPORT, ParityMode, SerialDevice, StopBits, 17 | }; 18 | use crate::arch::x86::{gdt, irq}; 19 | use crate::mem::{LOWMEM_VA_END, PAddr, PHYS_MEM_SIZE}; 20 | use crate::{debug, info, main, notice}; 21 | use alloc::boxed::Box; 22 | use core::mem; 23 | use multiboot2::BootInformation; 24 | 25 | use crate::arch::x86::driver::vesa::VesaFramebuffer; 26 | use crate::arch::x86::export::logging::LOGGER_SERIAL; 27 | use crate::arch::x86::mem::{lowmem_va_size, physical_memory_size}; 28 | use crate::driver::keyboard; 29 | use crate::logging::{DEFAULT_LOGGER, reset_logger}; 30 | use crate::mem::load::{ 31 | kernel_image, kernel_rodata_segment, kernel_text_segment, 32 | }; 33 | use crate::screen::R; 34 | use crate::ui::kterm::{KERNEL_TERMINAL, TerminalLogger}; 35 | use crate::ui::term::Terminal; 36 | 37 | /// Welcome in Rust land! This is the very first Rust code to run on the CPU 38 | /// once the previous `_start` routine in assembly ran. We did the bare 39 | /// minimum in this routine to run Rust (mostly setting up paging) since 40 | /// assembly is not my preffered programming language. 41 | /// 42 | /// It is time to perform the most vital initializations; the order in which to 43 | /// perform them is critical: we want to be able to easely debug the kernel. 44 | /// 45 | /// First, we need to setup a USART so that crashes and debug information are 46 | /// able to be exfiltrated since a lot can go wrong before we can print anything 47 | /// onto the screen. This is convenient on both QEMU and real hardware. 48 | /// 49 | /// Then, some basic global variables are set from the Multiboot information 50 | /// structure: `PHYS_MEM_SIZE` and `LOWMEM_VA_END`. 51 | /// 52 | /// We then set up the kernel's GDT, since the GDT created by `_start` is not 53 | /// enough to run ring 3 code or 32 bits code. 54 | /// 55 | /// After that, the IDT is initialized: from there, we can handle CPU exceptions 56 | /// and print useful crash report. 57 | /// 58 | /// Then most important part: we set up the memory management, composed of: 59 | /// * mapping all low-memory in the virtual address space; 60 | /// * setting up proper page protections for read/write/execute; 61 | /// * creating and configuring the physical frames allocator; 62 | /// * (i386) constructing the high-memory allocator. 63 | /// 64 | /// Interrupts can now be enabled. 65 | /// 66 | /// Finally, we call the kernel's `main` function to start the architecture- 67 | /// agnostic code. 68 | #[unsafe(no_mangle)] 69 | pub unsafe extern "C" fn arch_init(multiboot_info_pa: PAddr) -> ! { 70 | // We are not yet ready to handle interruptions: we don't even have an IDT! 71 | push_critical_region(); 72 | 73 | unsafe { 74 | LOGGER_SERIAL = Some(unsafe { 75 | SerialDevice::new( 76 | COM1_IOPORT, 77 | 115200, 78 | ParityMode::None, 79 | 8, 80 | StopBits::One, 81 | ) 82 | .expect("Couldn't initialize serial device") 83 | }); 84 | *DEFAULT_LOGGER.lock() = LOGGER_SERIAL.as_mut().unwrap(); 85 | } 86 | 87 | let mbi = unsafe { 88 | BootInformation::load(multiboot_info_pa.into_vaddr().0 as *const _) 89 | } 90 | .unwrap(); 91 | 92 | notice!("Nucloid v{}", env!("CARGO_PKG_VERSION")); 93 | 94 | let mem_map = mbi 95 | .memory_map_tag() 96 | .expect("No memory map provided by the bootloader"); 97 | 98 | unsafe { 99 | PHYS_MEM_SIZE = physical_memory_size(&mem_map); 100 | LOWMEM_VA_END = LOWMEM_VA_START + lowmem_va_size(&mem_map); 101 | } 102 | 103 | unsafe { 104 | debug!( 105 | "phys_mem_size = 0x{:x}, va_size = 0x{:x}", 106 | R(PHYS_MEM_SIZE), 107 | R(LOWMEM_VA_END) 108 | ); 109 | } 110 | debug!("Kernel image: {:#?}", kernel_image()); 111 | debug!("Text segment: {:#?}", kernel_text_segment()); 112 | debug!("Rodata segment: {:#?}", kernel_rodata_segment()); 113 | 114 | info!("Setting up GDT..."); 115 | unsafe { 116 | gdt::setup_table(); 117 | gdt::load_kernel_selectors(); 118 | } 119 | 120 | info!("Setting up interrupts..."); 121 | unsafe { 122 | irq::setup(); 123 | } 124 | 125 | let fb_info = mbi.framebuffer_tag().expect("No framebuffer").unwrap(); 126 | let fb_addr = PAddr(fb_info.address()); 127 | let fb_width = fb_info.width(); 128 | let fb_height = fb_info.height(); 129 | let fb_pitch = fb_info.pitch(); 130 | let fb_bpp = fb_info.bpp(); 131 | 132 | info!("Setting up memory management..."); 133 | unsafe { 134 | arch::x86::mem::boot_setup(&mem_map); 135 | } 136 | mem::forget(mbi); // FIXME: Multiboot info is invalidated 137 | 138 | // We can now activate and handle interruptions safely. 139 | pop_critical_region(); 140 | 141 | let fb_bsize = fb_pitch as usize * fb_height as usize; 142 | let fb_vaddr = fb_addr.into_vaddr(); 143 | 144 | let fb = unsafe { 145 | VesaFramebuffer::new( 146 | fb_vaddr.0 as _, 147 | fb_width as usize, 148 | fb_height as usize, 149 | fb_pitch as usize, 150 | fb_bpp, 151 | ) 152 | }; 153 | 154 | debug!( 155 | "fb ({fb_width}×{fb_height}) paddr = {:?}, vaddr = {:?}, size = {}", 156 | fb_addr, fb_vaddr, fb_bsize 157 | ); 158 | *KERNEL_TERMINAL.lock() = Some(Terminal::create(fb)); 159 | let term_logger = 160 | Box::leak(Box::new(TerminalLogger::new(unsafe { reset_logger() }))); 161 | *DEFAULT_LOGGER.lock() = term_logger; 162 | 163 | keyboard::init(); 164 | ps2::init(); 165 | 166 | main(); 167 | } 168 | -------------------------------------------------------------------------------- /src/arch/x86/irq.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use x86::Ring::Ring0; 12 | use x86::dtables::{DescriptorTablePointer, lidt}; 13 | use x86::irq::InterruptDescription; 14 | use x86::segmentation::{ 15 | BuildDescriptor, DescriptorBuilder, GateDescriptorBuilder, 16 | }; 17 | 18 | use crate::arch::cpu::MachineState; 19 | use crate::arch::sync::{pop_critical_region, push_critical_region}; 20 | use crate::arch::x86::driver::pic8259::Pic8259; 21 | use crate::arch::x86::driver::ps2; 22 | use crate::arch::x86::gdt::KERNEL_CODE_SELECTOR; 23 | use crate::mem::{AccessAttempt, VAddr, handle_pagefault}; 24 | use crate::panic::panic_at_state; 25 | use crate::println; 26 | 27 | #[repr(C, packed)] 28 | struct IsrRegisters { 29 | rip: u64, 30 | cs: u64, 31 | rflags: u64, 32 | rsp: u64, 33 | ss: u64, 34 | } 35 | 36 | #[repr(C, packed)] 37 | struct GPRegisters { 38 | rdi: u64, 39 | rsi: u64, 40 | rbp: u64, 41 | rbx: u64, 42 | rdx: u64, 43 | rcx: u64, 44 | rax: u64, 45 | r8: u64, 46 | r9: u64, 47 | r10: u64, 48 | r11: u64, 49 | r12: u64, 50 | r13: u64, 51 | r14: u64, 52 | r15: u64, 53 | } 54 | 55 | unsafe extern "C" { 56 | unsafe fn isr_entry_exception_0(); 57 | unsafe fn isr_entry_exception_1(); 58 | unsafe fn isr_entry_exception_2(); 59 | unsafe fn isr_entry_exception_3(); 60 | unsafe fn isr_entry_exception_4(); 61 | unsafe fn isr_entry_exception_5(); 62 | unsafe fn isr_entry_exception_6(); 63 | unsafe fn isr_entry_exception_7(); 64 | unsafe fn isr_entry_exception_8(); 65 | unsafe fn isr_entry_exception_9(); 66 | unsafe fn isr_entry_exception_10(); 67 | unsafe fn isr_entry_exception_11(); 68 | unsafe fn isr_entry_exception_12(); 69 | unsafe fn isr_entry_exception_13(); 70 | unsafe fn isr_entry_exception_14(); 71 | unsafe fn isr_entry_exception_15(); 72 | unsafe fn isr_entry_exception_16(); 73 | unsafe fn isr_entry_exception_17(); 74 | unsafe fn isr_entry_exception_18(); 75 | unsafe fn isr_entry_exception_19(); 76 | unsafe fn isr_entry_exception_20(); 77 | unsafe fn isr_entry_exception_21(); 78 | unsafe fn isr_entry_exception_22(); 79 | unsafe fn isr_entry_exception_23(); 80 | unsafe fn isr_entry_exception_24(); 81 | unsafe fn isr_entry_exception_25(); 82 | unsafe fn isr_entry_exception_26(); 83 | unsafe fn isr_entry_exception_27(); 84 | unsafe fn isr_entry_exception_28(); 85 | unsafe fn isr_entry_exception_29(); 86 | unsafe fn isr_entry_exception_30(); 87 | unsafe fn isr_entry_exception_31(); 88 | unsafe fn isr_entry_irq_0(); 89 | unsafe fn isr_entry_irq_1(); 90 | unsafe fn isr_entry_irq_2(); 91 | unsafe fn isr_entry_irq_3(); 92 | unsafe fn isr_entry_irq_4(); 93 | unsafe fn isr_entry_irq_5(); 94 | unsafe fn isr_entry_irq_6(); 95 | unsafe fn isr_entry_irq_7(); 96 | unsafe fn isr_entry_irq_8(); 97 | unsafe fn isr_entry_irq_9(); 98 | unsafe fn isr_entry_irq_10(); 99 | unsafe fn isr_entry_irq_11(); 100 | unsafe fn isr_entry_irq_12(); 101 | unsafe fn isr_entry_irq_13(); 102 | unsafe fn isr_entry_irq_14(); 103 | unsafe fn isr_entry_irq_15(); 104 | } 105 | 106 | static VECTORS: [unsafe extern "C" fn(); 48] = [ 107 | isr_entry_exception_0, 108 | isr_entry_exception_1, 109 | isr_entry_exception_2, 110 | isr_entry_exception_3, 111 | isr_entry_exception_4, 112 | isr_entry_exception_5, 113 | isr_entry_exception_6, 114 | isr_entry_exception_7, 115 | isr_entry_exception_8, 116 | isr_entry_exception_9, 117 | isr_entry_exception_10, 118 | isr_entry_exception_11, 119 | isr_entry_exception_12, 120 | isr_entry_exception_13, 121 | isr_entry_exception_14, 122 | isr_entry_exception_15, 123 | isr_entry_exception_16, 124 | isr_entry_exception_17, 125 | isr_entry_exception_18, 126 | isr_entry_exception_19, 127 | isr_entry_exception_20, 128 | isr_entry_exception_21, 129 | isr_entry_exception_22, 130 | isr_entry_exception_23, 131 | isr_entry_exception_24, 132 | isr_entry_exception_25, 133 | isr_entry_exception_26, 134 | isr_entry_exception_27, 135 | isr_entry_exception_28, 136 | isr_entry_exception_29, 137 | isr_entry_exception_30, 138 | isr_entry_exception_31, 139 | isr_entry_irq_0, 140 | isr_entry_irq_1, 141 | isr_entry_irq_2, 142 | isr_entry_irq_3, 143 | isr_entry_irq_4, 144 | isr_entry_irq_5, 145 | isr_entry_irq_6, 146 | isr_entry_irq_7, 147 | isr_entry_irq_8, 148 | isr_entry_irq_9, 149 | isr_entry_irq_10, 150 | isr_entry_irq_11, 151 | isr_entry_irq_12, 152 | isr_entry_irq_13, 153 | isr_entry_irq_14, 154 | isr_entry_irq_15, 155 | ]; 156 | 157 | static mut PIC8259: Option = None; 158 | 159 | type DescriptorType = x86::bits64::segmentation::Descriptor64; 160 | 161 | static mut IDT: [DescriptorType; 64] = [DescriptorType::NULL; 64]; 162 | 163 | pub unsafe fn get_pic() -> &'static mut Pic8259 { 164 | unsafe { PIC8259.as_mut().unwrap() } 165 | } 166 | 167 | pub unsafe fn setup() { 168 | type IdtType = u64; 169 | 170 | unsafe { 171 | let mut pic = Pic8259::new(0x20, 0xa0); 172 | pic.init(32, 40); 173 | PIC8259 = Some(pic); 174 | 175 | let mut vec = 0; 176 | 177 | for isr in VECTORS.iter() { 178 | let offset = core::mem::transmute::<_, usize>(*isr); 179 | 180 | IDT[vec] = > 181 | ::interrupt_descriptor( 182 | KERNEL_CODE_SELECTOR, 183 | offset as IdtType 184 | ).present() 185 | .dpl(Ring0) 186 | .finish(); 187 | vec += 1; 188 | } 189 | 190 | let ptr = DescriptorTablePointer::new(&IDT); 191 | lidt(&ptr); 192 | } 193 | } 194 | 195 | #[unsafe(no_mangle)] 196 | unsafe extern "C" fn isr_exception( 197 | vec_i: usize, 198 | errc: usize, 199 | isr_regs: &IsrRegisters, 200 | regs: &GPRegisters, 201 | ) { 202 | let machine_state = MachineState { 203 | rax: regs.rax, 204 | rbx: regs.rbx, 205 | rcx: regs.rcx, 206 | rdx: regs.rdx, 207 | r8: regs.r8, 208 | r9: regs.r9, 209 | r10: regs.r10, 210 | r11: regs.r11, 211 | r12: regs.r12, 212 | r13: regs.r13, 213 | r14: regs.r14, 214 | r15: regs.r15, 215 | rdi: regs.rdi, 216 | rsi: regs.rsi, 217 | rsp: isr_regs.rsp, 218 | rbp: regs.rbp, 219 | rip: isr_regs.rip, 220 | rflags: isr_regs.rflags, 221 | cs: isr_regs.cs as u16, 222 | ss: isr_regs.ss as u16, 223 | ds: 0, 224 | es: 0, 225 | fs: 0, 226 | gs: 0, // TODO: seg regs 227 | }; 228 | 229 | unsafe { 230 | handle_exception(vec_i, Some(errc), &machine_state); 231 | } 232 | } 233 | 234 | unsafe fn handle_exception( 235 | vec_i: usize, 236 | errc: Option, 237 | machine_state: &MachineState, 238 | ) { 239 | push_critical_region(); 240 | 241 | let ex = x86::irq::EXCEPTIONS.get(vec_i as usize).unwrap_or( 242 | &InterruptDescription { 243 | vector: 0, 244 | mnemonic: "#??", 245 | description: "(unknown)", 246 | irqtype: "???", 247 | source: "???", 248 | }, 249 | ); 250 | 251 | if vec_i == x86::irq::PAGE_FAULT_VECTOR as usize { 252 | let errc = errc.expect("Page fault must provide an error code"); 253 | let is_write = errc & (1 << 1) > 0; 254 | let is_exec = errc & (1 << 4) > 0; 255 | 256 | let addr = VAddr(unsafe { x86::controlregs::cr2() }); 257 | 258 | let access = if is_exec { 259 | AccessAttempt::Execute 260 | } else if is_write { 261 | AccessAttempt::Write 262 | } else { 263 | AccessAttempt::Read 264 | }; 265 | 266 | handle_pagefault(addr, access, machine_state); 267 | return; 268 | } 269 | 270 | if let Some(errc) = errc { 271 | panic_at_state( 272 | format_args!( 273 | "Exception ({}; errc={}) {} {}", 274 | vec_i, errc, ex.mnemonic, ex.description 275 | ), 276 | Some(machine_state), 277 | 0, 278 | ); 279 | } else { 280 | panic_at_state( 281 | format_args!( 282 | "Exception ({}) {} {}", 283 | vec_i, ex.mnemonic, ex.description 284 | ), 285 | Some(machine_state), 286 | 0, 287 | ); 288 | } 289 | } 290 | 291 | #[unsafe(no_mangle)] 292 | unsafe extern "C" fn isr_irq(irq: usize) { 293 | push_critical_region(); 294 | 295 | if irq == 0 { 296 | } else if irq == 1 { 297 | ps2::on_irq(); 298 | } else { 299 | println!("IRQ={}", irq); 300 | } 301 | 302 | unsafe { 303 | get_pic().ack_irq(irq as u32); 304 | } 305 | 306 | pop_critical_region(); 307 | } 308 | -------------------------------------------------------------------------------- /src/arch/x86/isr_entry64.S: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | **************************************************************************** */ 10 | 11 | # Size of all registers = 120 o 12 | .macro PUSH_REGS 13 | push %r15 14 | push %r14 15 | push %r13 16 | push %r12 17 | push %r11 18 | push %r10 19 | push %r9 20 | push %r8 21 | push %rax 22 | push %rcx 23 | push %rdx 24 | push %rbx 25 | push %rbp 26 | push %rsi 27 | push %rdi 28 | .endm 29 | 30 | .macro POP_REGS 31 | pop %rdi 32 | pop %rsi 33 | pop %rbp 34 | pop %rbx 35 | pop %rdx 36 | pop %rcx 37 | pop %rax 38 | pop %r8 39 | pop %r9 40 | pop %r10 41 | pop %r11 42 | pop %r12 43 | pop %r13 44 | pop %r14 45 | pop %r15 46 | .endm 47 | 48 | .macro ISR_EXCEPTION vec_n 49 | .global isr_entry_exception_\vec_n 50 | isr_entry_exception_\vec_n: 51 | PUSH_REGS 52 | mov $\vec_n, %rdi 53 | mov $0, %rsi 54 | lea 120(%rsp), %rdx 55 | mov %rsp, %rcx 56 | call isr_exception 57 | POP_REGS 58 | iretq 59 | .endm 60 | 61 | .macro ISR_EXCEPTION_ERRC vec_n 62 | .global isr_entry_exception_\vec_n 63 | isr_entry_exception_\vec_n: 64 | PUSH_REGS 65 | mov $\vec_n, %rdi 66 | mov 120(%rsp), %rsi 67 | lea 128(%rsp), %rdx 68 | mov %rsp, %rcx 69 | call isr_exception 70 | POP_REGS 71 | iretq 72 | .endm 73 | 74 | .macro ISR_IRQ irq_n 75 | .global isr_entry_irq_\irq_n 76 | isr_entry_irq_\irq_n: 77 | PUSH_REGS 78 | mov $\irq_n, %rdi 79 | call isr_irq 80 | POP_REGS 81 | iretq 82 | .endm 83 | 84 | .text 85 | 86 | .global isr_default_vec 87 | isr_default_vec: 88 | iretq 89 | 90 | ISR_EXCEPTION 0 # DE Divide-by-zero Error 91 | ISR_EXCEPTION 1 # BD Debug 92 | ISR_EXCEPTION 2 # Non-maskable Interrupt 93 | ISR_EXCEPTION 3 # BP Breakpoint 94 | ISR_EXCEPTION 4 # OF Overflow 95 | ISR_EXCEPTION 5 # BR Bound Range Exceeded 96 | ISR_EXCEPTION 6 # UD Invalid Opcode 97 | ISR_EXCEPTION 7 # NM Device Not Available 98 | ISR_EXCEPTION_ERRC 8 # DF Double Fault 99 | ISR_EXCEPTION 9 # Coprocessor Segment Overrun 100 | ISR_EXCEPTION_ERRC 10 # TS Invalid TSS 101 | ISR_EXCEPTION_ERRC 11 # NP Segment Not Present 102 | ISR_EXCEPTION_ERRC 12 # SS Stack-Segment Fault 103 | ISR_EXCEPTION_ERRC 13 # GP General Protection Fault 104 | ISR_EXCEPTION_ERRC 14 # PF Page Fault 105 | ISR_EXCEPTION 15 # (reserved) 106 | ISR_EXCEPTION 16 # MF x87 Floating-Point Exception 107 | ISR_EXCEPTION 17 # AC Alignment Check 108 | ISR_EXCEPTION 18 # MC Machine Check 109 | ISR_EXCEPTION 19 # XM/XF SIMD Floating-Point Exception 110 | ISR_EXCEPTION 20 # VE Virtualization Exception 111 | ISR_EXCEPTION 21 # (reserved) 112 | ISR_EXCEPTION 22 # (reserved) 113 | ISR_EXCEPTION 23 # (reserved) 114 | ISR_EXCEPTION 24 # (reserved) 115 | ISR_EXCEPTION 25 # (reserved) 116 | ISR_EXCEPTION 26 # (reserved) 117 | ISR_EXCEPTION 27 # (reserved) 118 | ISR_EXCEPTION 28 # (reserved) 119 | ISR_EXCEPTION 29 # (reserved) 120 | ISR_EXCEPTION 30 # SX Security Exception 121 | ISR_EXCEPTION 31 # (reserved) 122 | 123 | ISR_IRQ 0 124 | ISR_IRQ 1 125 | ISR_IRQ 2 126 | ISR_IRQ 3 127 | ISR_IRQ 4 128 | ISR_IRQ 5 129 | ISR_IRQ 6 130 | ISR_IRQ 7 131 | ISR_IRQ 8 132 | ISR_IRQ 9 133 | ISR_IRQ 10 134 | ISR_IRQ 11 135 | ISR_IRQ 12 136 | ISR_IRQ 13 137 | ISR_IRQ 14 138 | ISR_IRQ 15 139 | -------------------------------------------------------------------------------- /src/arch/x86/mem/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use arrayvec::ArrayVec; 12 | use core::mem::MaybeUninit; 13 | use core::ptr::copy_nonoverlapping; 14 | use multiboot2::MemoryMapTag; 15 | 16 | use crate::arch::mem::{LOWMEM_SIZE, LOWMEM_VA_START}; 17 | use crate::arch::x86::mem::paging::setup_kernel_paging; 18 | use crate::debug; 19 | use crate::mem::frame::{AllocatorBuilder, FRAME_ALLOCATOR}; 20 | use crate::mem::{PAddr, PHYS_MEM_SIZE}; 21 | use crate::misc::BinSize; 22 | 23 | pub mod paging; 24 | 25 | pub fn lowmem_va_size(mem_maps: &MemoryMapTag) -> usize { 26 | let mut lowmem_size = 0; 27 | 28 | for area in mem_maps.memory_areas() { 29 | if area.start_address() >= LOWMEM_SIZE as u64 { 30 | break; 31 | } else if area.end_address() > LOWMEM_SIZE as u64 { 32 | return LOWMEM_SIZE; 33 | } else { 34 | lowmem_size = area.end_address() as usize; 35 | } 36 | } 37 | 38 | assert!(lowmem_size <= LOWMEM_SIZE); 39 | 40 | lowmem_size 41 | } 42 | 43 | pub fn physical_memory_size(mem_maps: &MemoryMapTag) -> u64 { 44 | mem_maps 45 | .memory_areas() 46 | .into_iter() 47 | .map(|area| area.end_address()) 48 | .max() 49 | .unwrap() 50 | } 51 | 52 | pub unsafe fn boot_setup(mem_maps: &MemoryMapTag) { 53 | // We must first copy the array of memory area in the Multiboot struct that 54 | // will be destroyed by the call to `setup_kernel_paging()`. 55 | let mem_maps = copy_mbi_mem_areas(mem_maps); 56 | 57 | for area in mem_maps.iter() { 58 | debug!( 59 | "[{}] {:?} -> {:?} {:#10x} ({})", 60 | area.typ, 61 | PAddr(area.base_addr), 62 | PAddr(area.base_addr + area.length), 63 | area.length, 64 | BinSize(area.length) 65 | ); 66 | } 67 | 68 | let curr_heap = unsafe { setup_kernel_paging() }; 69 | 70 | assert_eq!(curr_heap.0 & 0xfff, 0); 71 | let boot_used_bytes = (curr_heap - LOWMEM_VA_START).0 as u64; 72 | 73 | let mut allocator_b = 74 | unsafe { AllocatorBuilder::new(curr_heap, PHYS_MEM_SIZE) }; 75 | 76 | for area in mem_maps { 77 | let paddr = PAddr(area.base_addr); 78 | let mut bsize = area.length; 79 | 80 | if paddr.0 == 0x9fc00 { 81 | continue; 82 | } else if paddr.0 == 0 { 83 | bsize &= !0xfff; 84 | } 85 | 86 | unsafe { 87 | match area.typ { 88 | 1 => { 89 | allocator_b.declare_unused_ram(paddr, bsize); 90 | } 91 | 2 | 3 => { 92 | allocator_b.declare_reserved(paddr, bsize); 93 | } 94 | _ => { 95 | allocator_b.declare_unusable(paddr, bsize); 96 | } 97 | } 98 | } 99 | } 100 | 101 | unsafe { 102 | allocator_b.declare_allocated_ram(PAddr(0), boot_used_bytes); 103 | } 104 | 105 | { 106 | let mut allocator = FRAME_ALLOCATOR.lock(); 107 | assert!(allocator.is_none()); 108 | *allocator = Some(unsafe { allocator_b.build() }); 109 | } 110 | } 111 | 112 | fn copy_mbi_mem_areas(mem_maps: &MemoryMapTag) -> ArrayVec { 113 | let mut mem_areas: ArrayVec = ArrayVec::new(); 114 | for area in mem_maps.memory_areas() { 115 | let mut area_copy = MaybeUninit::::uninit(); 116 | unsafe { 117 | copy_nonoverlapping( 118 | area as *const _ as *const u8, 119 | area_copy.as_mut_ptr() as *mut u8, 120 | core::mem::size_of::(), 121 | ); 122 | } 123 | mem_areas.push(unsafe { area_copy.assume_init() }); 124 | } 125 | 126 | mem_areas 127 | } 128 | 129 | /// Exact copy of the multiboot2 crate's structure `MemoryArea`, since is does 130 | /// not implement `Clone` nor can we access its fields, and we need to perm a 131 | /// deep copy before trashing the MBI buffer. 132 | #[repr(C)] 133 | #[derive(Copy, Clone, Debug)] 134 | struct MbiMemArea { 135 | base_addr: u64, 136 | length: u64, 137 | typ: u32, 138 | _reserved: u32, 139 | } 140 | -------------------------------------------------------------------------------- /src/arch/x86/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub mod cpuid; 12 | pub mod driver; 13 | pub(super) mod export; 14 | pub mod gdt; 15 | pub mod init; 16 | pub mod irq; 17 | pub mod mem; 18 | 19 | pub type Ioport = u16; 20 | -------------------------------------------------------------------------------- /src/arch/x86/multiboot2.S: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | **************************************************************************** */ 10 | 11 | .set MAGIC, 0xe85250d6 12 | .set ARCH, 0 # IA-32 13 | .set HEADER_SIZE, 44 # Multiboot2 header size (octets) 14 | 15 | #ifdef __x86_64__ 16 | # define VA_BASE 0xffff800000000000 17 | #else 18 | # define VA_BASE 0xc0000000 19 | #endif 20 | 21 | .section .multiboot2 22 | .long MAGIC 23 | .long ARCH 24 | .long HEADER_SIZE 25 | .long (-(MAGIC + ARCH + HEADER_SIZE)) & 0xffffffff 26 | 27 | # Entry address 28 | .short 3 # Type 29 | .short 0 # Flags 30 | .long 12 # Size 31 | .long _start - VA_BASE 32 | .skip 4 # Align to 8 octets 33 | 34 | # Framebuffer 35 | .short 5 # Type 36 | .short 0 # Flags 37 | .long 20 # Size 38 | .long 1920 # Width 39 | .long 1080 # Height 40 | .long 32 # Depth 41 | .skip 4 # Align to 8 octets 42 | 43 | # End tag 44 | .short 0 # Type 45 | .short 0 # Flags 46 | .long 8 # Size 47 | -------------------------------------------------------------------------------- /src/arch/x86/start64.S: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | **************************************************************************** */ 10 | 11 | .set VA_BASE, 0xffff800000000000 12 | .set VGA_ADDR, 0xb8000 13 | .set VGA_SIZE, 0xfa0 14 | .set NR_PT, 16 15 | 16 | /* 17 | * PML4: 18 | * |- PDPT0 Identity-maps the first 16 Mio of physical addresses. 19 | * | \- PD0 20 | * | |- PDE0 (2 Mio pages, no page-tables.) 21 | * | |- PDE1 22 | * | |- PDE2 23 | * | |- PDE3 24 | * | |- PDE4 25 | * | |- PDE5 26 | * | |- PDE6 27 | * | \- PDE7 28 | * \- PDPT256 VA starting at 0xffff8000_00000000 29 | * \- PD0 30 | * |- PDE0 31 | * | \- 512 PTEs 32 | * |- PDE1 33 | * | \- 512 PTEs 34 | * |- PDE2 35 | * | \- 512 PTEs 36 | * |- PDE3 37 | * | \- 512 PTEs 38 | * |- PDE4 39 | * | \- 512 PTEs 40 | * |- PDE5 41 | * | \- 512 PTEs 42 | * |- PDE6 43 | * | \- 512 PTEs 44 | * \- PDE7 45 | * \- 512 PTEs 46 | */ 47 | 48 | .code32 49 | .section .text 50 | 51 | .global _start 52 | .type _start, @function 53 | _start: 54 | .cfi_startproc 55 | .cfi_undefined 16 # There is no return address. 56 | // Multiboot specification says EFLAGS.DF is undefined, let's clear it. 57 | cld 58 | 59 | // Setup a temporary 32 Kio stack for the boot process. 60 | mov $(boot_stack_top - VA_BASE), %esp 61 | xor %ebp, %ebp 62 | movl $0xdeadbeef, (boot_stack_bottom_guard - VA_BASE) 63 | 64 | // Immediately save the Multiboot info pointer passed by the bootloader as 65 | // the first argument of arch_init(), before we trash it with CPUID calls. 66 | push %ebx 67 | 68 | call clear_screen 69 | call check_has_cpuid 70 | call check_has_longmode 71 | 72 | // Clear PML4 entries 73 | mov $(boot_pml4 - VA_BASE), %edi 74 | xor %eax, %eax 75 | mov $1024, %ecx 76 | rep stosl 77 | 78 | // Setup PML4 79 | mov $(boot_pml4 - VA_BASE), %edi 80 | movl $(boot_pdpt0 - VA_BASE), 0(%edi) 81 | movl $(boot_pdpt256 - VA_BASE), 256*8(%edi) 82 | orl $((1 << 0) | (1 << 1)), 0(%edi) 83 | orl $((1 << 0) | (1 << 1)), 256*8(%edi) 84 | 85 | // Clear PML4[0] PDPT 86 | mov $(boot_pdpt0 - VA_BASE), %edi 87 | xor %eax, %eax 88 | mov $1024, %ecx 89 | rep stosl 90 | 91 | // Setup PML4[0] PDPT 92 | mov $(boot_pdpt0 - VA_BASE), %edi 93 | movl $(boot_pdpt0_pd0 - VA_BASE), (%edi) 94 | orl $((1 << 0) | (1 << 1)), (%edi) // Present and writable 95 | 96 | // Clear PML4[256] PDPT 97 | mov $(boot_pdpt256 - VA_BASE), %edi 98 | xor %eax, %eax 99 | mov $1024, %ecx 100 | rep stosl 101 | 102 | // Setup PML4[256] PDPT 103 | mov $(boot_pdpt256 - VA_BASE), %edi 104 | movl $(boot_pdpt256_pd0 - VA_BASE), (%edi) 105 | orl $((1 << 0) | (1 << 1)), (%edi) // Present and writable 106 | 107 | // Clear PML4[0].PDPT[0] PD 108 | mov $(boot_pdpt0_pd0 - VA_BASE), %edi 109 | xor %eax, %eax 110 | mov $1024, %ecx 111 | rep stosl 112 | 113 | // Clear PML4[256].PDPT[0] PD 114 | mov $(boot_pdpt256_pd0 - VA_BASE), %edi 115 | xor %eax, %eax 116 | mov $1024, %ecx 117 | rep stosl 118 | 119 | // Setup PML4[0].PDPT[0].PD[0] PDEs (NR_PT) 120 | mov $(boot_pdpt0_pd0 - VA_BASE), %edi 121 | xor %eax, %eax 122 | mov $NR_PT, %ecx 123 | 1: movl %eax, (%edi) 124 | orl $((1 << 0) | (1 << 1) | (1 << 7)), (%edi) // Present, writable, PS 125 | add $8, %edi 126 | add $(2 << 20), %eax 127 | loop 1b 128 | 129 | // Setup PML4[256].PDPT[0].PD[0] PDEs (NR_PT) 130 | mov $(boot_pdpt256_pd0 - VA_BASE), %edi 131 | mov $(boot_pdpt256_pd0_pts - VA_BASE), %eax 132 | mov $NR_PT, %ecx 133 | 1: movl %eax, (%edi) 134 | orl $((1 << 0) | (1 << 1)), (%edi) // Present, writable 135 | add $8, %edi 136 | add $4096, %eax 137 | loop 1b 138 | 139 | // Setup PML4[256].PDPT[0].PD[0..NR_PT].PT[0..512] 140 | mov $(boot_pdpt256_pd0_pts - VA_BASE), %edi 141 | xor %eax, %eax 142 | mov $(NR_PT * 512), %ecx 143 | 1: movl %eax, (%edi) 144 | orl $((1 << 0) | (1 << 1)), (%edi) // Present, writable 145 | add $8, %edi 146 | add $4096, %eax 147 | loop 1b 148 | 149 | // Switching to long-mode (compatibility) 150 | mov $0xc0000080, %ecx // IA32_EFER 151 | rdmsr 152 | or $(1 << 8), %eax // LME 153 | or $(1 << 11), %eax // NXE 154 | wrmsr 155 | 156 | // Enable PAE and PSE 157 | mov %cr4, %eax 158 | or $((1 << 4) | (1 << 5)), %eax 159 | mov %eax, %cr4 160 | 161 | // Enable paging 162 | mov $(boot_pml4 - VA_BASE), %eax 163 | mov %eax, %cr3 164 | mov %cr0, %eax 165 | or $(1 << 31), %eax # CR0.PG 166 | or $(1 << 16), %eax # CR0.WP 167 | mov %eax, %cr0 168 | 169 | // Load a new GDT with a 64 bits code segment descriptor 170 | lgdt (boot_gdt64_ptr - VA_BASE) 171 | mov $0x10, %ax 172 | mov %ax, %ss 173 | mov %ax, %ds 174 | mov %ax, %es 175 | mov %ax, %fs 176 | mov %ax, %gs 177 | 178 | // Switch to 64 bit mode 179 | jmp $0x08, $(1f - VA_BASE) 180 | .code64 181 | 1: 182 | // Let's use the virtual address for RIP 183 | movabs $1f, %rax 184 | jmp *%rax 185 | 1: 186 | // Update stack pointer to a virtual address 187 | mov $VA_BASE, %rax 188 | add %rax, %rsp 189 | 190 | pop %rdi // Multiboot pointer argument 191 | and $(~0b111), %rsp // Align stack to 8 bytes 192 | 193 | jmp arch_init 194 | .cfi_endproc 195 | .size _start, .-_start 196 | 197 | .code32 198 | 199 | clear_screen: 200 | mov $' ', %al 201 | mov $0b00000111, %ah 202 | mov $VGA_ADDR, %edi 203 | mov $(VGA_SIZE >> 1), %ecx 204 | rep stosw 205 | ret 206 | 207 | prints: 208 | lodsb 209 | cmp $0, %al 210 | je 1f 211 | stosb 212 | mov $0b00000111, %al 213 | stosb 214 | jmp prints 215 | 1: ret 216 | 217 | check_has_cpuid: 218 | pushfl 219 | pop %eax 220 | or $(1 << 21), %eax 221 | push %eax 222 | popfl 223 | 224 | pushfl 225 | popfl // pop %eax ? 226 | test $(1 << 21), %eax 227 | jz 1f 228 | ret 229 | 1: mov $(VGA_ADDR + 0 * 160), %edi 230 | mov $(msg_no_cpuid - VA_BASE), %esi 231 | call prints 232 | 2: hlt 233 | jmp 2b 234 | 235 | check_has_longmode: 236 | mov $0x80000000, %eax 237 | cpuid 238 | cmp $0x80000001, %eax 239 | jb 1f 240 | mov $0x80000001, %eax 241 | cpuid 242 | test $(1 << 29), %edx 243 | jz 1f 244 | ret 245 | 246 | 1: mov $(VGA_ADDR + 0 * 160), %edi 247 | mov $(msg_no_longmode - VA_BASE), %esi 248 | call prints 249 | 2: hlt 250 | jmp 2b 251 | 252 | .section .boot_page_tables 253 | 254 | .global boot_pml4 255 | 256 | .align 4096 257 | boot_pml4: .skip 4096 258 | 259 | boot_pdpt0: .skip 4096 260 | boot_pdpt256: .skip 4096 261 | 262 | boot_pdpt0_pd0: .skip 4096 263 | boot_pdpt256_pd0: .skip 4096 264 | 265 | boot_pdpt256_pd0_pts: .skip 4096 * NR_PT 266 | 267 | .section .boot_stack 268 | 269 | .global boot_stack_bottom_guard 270 | boot_stack_bottom_guard: 271 | .skip 4096 // Guard page 272 | 273 | .skip 32 * 4096 274 | 275 | boot_stack_top: 276 | .skip 4096 // Guard page 277 | 278 | .section .rodata 279 | 280 | .global boot_gdt64 281 | boot_gdt64: 282 | // Null GDT entry 283 | .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 284 | 285 | /* 286 | * GDT entry for 64 bits code segment. 287 | * base = 0x0000'0000 288 | * limit = 0xf'ffff (4 Gio) 289 | * type = 1011 (code, non-conformant, readable, accessed) 290 | * S = 1 (segment descriptor) 291 | * DPL = 00 (ring level) 292 | * P = 1 (physical memory) 293 | * flags = 1010 (global, limit in pages, 64 bits instructions, (non-used)) 294 | */ 295 | .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0b10011011, 0b10101111, 0x00 296 | 297 | /* 298 | * GDT entry for data segment. 299 | * base = 0x0000'0000 300 | * limit = 0xf'ffff (4 Gio) 301 | * type = 0011 (data, not stack, writable, accessed) 302 | * S = 1 (segment descriptor) 303 | * DPL = 00 (ring level) 304 | * P = 1 (physical memory) 305 | * flags = 1100 (global, limit in pages, (non-used)) 306 | */ 307 | .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0b10010011, 0b11001111, 0x00 308 | 309 | boot_gdt64_ptr: 310 | .short 24 // octets, for 3 entries 311 | .long boot_gdt64 - VA_BASE 312 | 313 | msg_no_cpuid: .asciz "ERROR: The CPU does not support CPUID" 314 | msg_no_longmode:.asciz "The CPU does not support 64 bit mode" 315 | msg_ok: .asciz "OK!" 316 | -------------------------------------------------------------------------------- /src/backtrace.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use alloc::boxed::Box; 12 | use core::fmt::{Debug, Formatter}; 13 | use core::mem::size_of; 14 | use core::ptr::addr_of; 15 | use core::slice; 16 | use gimli::{ 17 | BaseAddresses, CfaRule, EhFrame, EhFrameHdr, EhHdrTable, EndianSlice, 18 | LittleEndian, ParsedEhFrameHdr, Register, RegisterRule, UnwindContext, 19 | UnwindSection, 20 | }; 21 | 22 | use crate::arch::cpu::MachineState; 23 | use crate::mem::VAddr; 24 | 25 | pub struct Backtrace { 26 | unwinder: Unwinder, 27 | } 28 | 29 | pub struct CallFrame { 30 | pub pc: VAddr, 31 | pub symbol: Option<&'static str>, 32 | pub sym_off: Option, 33 | pub file_line: Option<(&'static str, u32)>, 34 | } 35 | 36 | impl Backtrace { 37 | pub fn from_machine_state(machine: &MachineState) -> Self { 38 | Self { 39 | unwinder: Unwinder::new( 40 | EhInfo::new(), 41 | RegisterSet::from_machine_state(machine), 42 | ), 43 | } 44 | } 45 | } 46 | 47 | impl Iterator for Backtrace { 48 | type Item = CallFrame; 49 | 50 | fn next(&mut self) -> Option { 51 | let pc = self.unwinder.next().ok()??; 52 | 53 | if pc == 0 { 54 | return None; 55 | } 56 | 57 | Some(CallFrame { 58 | pc: VAddr(pc as usize), 59 | symbol: None, 60 | sym_off: None, 61 | file_line: None, 62 | }) 63 | } 64 | } 65 | 66 | unsafe extern "C" { 67 | static __kernel_eh_frame_hdr: u8; 68 | static __kernel_eh_frame_hdr_end: u8; 69 | static __kernel_eh_frame: u8; 70 | static __kernel_eh_frame_end: u8; 71 | } 72 | 73 | #[derive(Debug)] 74 | enum UnwinderError { 75 | UnexpectedRegister(Register), 76 | UnsupportedCfaRule, 77 | CfaRuleUnknownRegister(Register), 78 | UnimplementedRegisterRule, 79 | NoUnwindInfo, 80 | NoPcRegister, 81 | NoReturnAddr, 82 | } 83 | 84 | struct EhInfo { 85 | base_addrs: BaseAddresses, 86 | hdr: &'static ParsedEhFrameHdr>, 87 | hdr_table: EhHdrTable<'static, EndianSlice<'static, LittleEndian>>, 88 | eh_frame: EhFrame>, 89 | } 90 | 91 | impl EhInfo { 92 | fn new() -> Self { 93 | let hdr = unsafe { addr_of!(__kernel_eh_frame_hdr) }; 94 | let hdr_len = (unsafe { addr_of!(__kernel_eh_frame_hdr_end) } as usize) 95 | - (hdr as usize); 96 | let eh_frame = unsafe { addr_of!(__kernel_eh_frame) }; 97 | let eh_frame_len = (unsafe { addr_of!(__kernel_eh_frame_end) } 98 | as usize) 99 | - (eh_frame as usize); 100 | 101 | let mut base_addrs = BaseAddresses::default(); 102 | base_addrs = base_addrs.set_eh_frame_hdr(hdr as u64); 103 | 104 | let hdr = Box::leak(Box::new( 105 | EhFrameHdr::new( 106 | // TODO: remove Box 107 | unsafe { slice::from_raw_parts(hdr, hdr_len) }, 108 | LittleEndian, 109 | ) 110 | .parse(&base_addrs, size_of::() as u8) 111 | .unwrap(), 112 | )); 113 | 114 | base_addrs = base_addrs.set_eh_frame(eh_frame as u64); 115 | 116 | let eh_frame = EhFrame::new( 117 | unsafe { slice::from_raw_parts(eh_frame, eh_frame_len) }, 118 | LittleEndian, 119 | ); 120 | 121 | Self { 122 | base_addrs, 123 | hdr, 124 | hdr_table: hdr.table().unwrap(), 125 | eh_frame, 126 | } 127 | } 128 | } 129 | 130 | struct Unwinder { 131 | eh_info: EhInfo, 132 | //unwind_ctx: UnwindContext>, 133 | unwind_ctx: UnwindContext, 134 | regs: RegisterSet, 135 | cfa: u64, 136 | is_first: bool, 137 | } 138 | 139 | impl Debug for Unwinder { 140 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 141 | f.debug_struct("Unwinder") 142 | .field("regs", &self.regs) 143 | .field("cfa", &self.cfa) 144 | .finish() 145 | } 146 | } 147 | 148 | impl Unwinder { 149 | fn new(eh_info: EhInfo, register_set: RegisterSet) -> Self { 150 | Self { 151 | eh_info, 152 | unwind_ctx: UnwindContext::new(), // TODO: no alloc 153 | regs: register_set, 154 | cfa: 0, 155 | is_first: true, 156 | } 157 | } 158 | 159 | fn next(&mut self) -> Result, UnwinderError> { 160 | let pc = self.regs.get_pc().ok_or(UnwinderError::NoPcRegister)?; 161 | 162 | if self.is_first { 163 | self.is_first = false; 164 | return Ok(Some(pc)); 165 | } 166 | 167 | let row = self 168 | .eh_info 169 | .hdr_table 170 | .unwind_info_for_address( 171 | &self.eh_info.eh_frame, 172 | &self.eh_info.base_addrs, 173 | &mut self.unwind_ctx, 174 | pc, 175 | |section, bases, offset| section.cie_from_offset(bases, offset), 176 | ) 177 | .map_err(|_| UnwinderError::NoUnwindInfo)?; 178 | 179 | match row.cfa() { 180 | CfaRule::RegisterAndOffset { register, offset } => { 181 | let reg_val = self 182 | .regs 183 | .get(*register) 184 | .ok_or(UnwinderError::CfaRuleUnknownRegister(*register))?; 185 | self.cfa = (reg_val as i64 + offset) as u64; 186 | } 187 | _ => return Err(UnwinderError::UnsupportedCfaRule), 188 | } 189 | 190 | for reg in RegisterSet::iter() { 191 | match row.register(reg) { 192 | RegisterRule::Undefined => self.regs.undef(reg), 193 | RegisterRule::SameValue => (), 194 | RegisterRule::Offset(offset) => { 195 | let ptr = (self.cfa as i64 + offset) as u64 as *const usize; 196 | self.regs.set(reg, unsafe { ptr.read() } as u64)?; 197 | } 198 | _ => return Err(UnwinderError::UnimplementedRegisterRule), 199 | } 200 | } 201 | 202 | let ret = self.regs.get_ret().ok_or(UnwinderError::NoReturnAddr)?; 203 | self.regs.set_pc(ret); 204 | self.regs.set_stack_ptr(self.cfa); 205 | 206 | Ok(Some(ret)) 207 | } 208 | } 209 | 210 | #[cfg(target_arch = "x86_64")] 211 | mod arch { 212 | use crate::arch::cpu::MachineState; 213 | use crate::backtrace::UnwinderError; 214 | use gimli::{Register, X86_64}; 215 | 216 | #[derive(Debug, Default)] 217 | pub(super) struct RegisterSet { 218 | rip: Option, 219 | rsp: Option, 220 | rbp: Option, 221 | ret: Option, 222 | } 223 | 224 | impl RegisterSet { 225 | pub(super) fn from_machine_state(machine: &MachineState) -> Self { 226 | Self { 227 | rip: Some(machine.rip), 228 | rsp: Some(machine.rsp), 229 | rbp: Some(machine.rbp), 230 | ret: None, 231 | } 232 | } 233 | 234 | pub(super) fn get(&self, reg: Register) -> Option { 235 | match reg { 236 | X86_64::RSP => self.rsp, 237 | X86_64::RBP => self.rbp, 238 | X86_64::RA => self.ret, 239 | _ => None, 240 | } 241 | } 242 | 243 | pub(super) fn set( 244 | &mut self, 245 | reg: Register, 246 | val: u64, 247 | ) -> Result<(), UnwinderError> { 248 | *match reg { 249 | X86_64::RSP => &mut self.rsp, 250 | X86_64::RBP => &mut self.rbp, 251 | X86_64::RA => &mut self.ret, 252 | _ => return Err(UnwinderError::UnexpectedRegister(reg)), 253 | } = Some(val); 254 | 255 | Ok(()) 256 | } 257 | 258 | pub(super) fn undef(&mut self, reg: Register) { 259 | *match reg { 260 | X86_64::RSP => &mut self.rsp, 261 | X86_64::RBP => &mut self.rbp, 262 | X86_64::RA => &mut self.ret, 263 | _ => return, 264 | } = None; 265 | } 266 | 267 | pub(super) fn get_pc(&self) -> Option { 268 | self.rip 269 | } 270 | 271 | pub(super) fn set_pc(&mut self, val: u64) { 272 | self.rip = Some(val); 273 | } 274 | 275 | pub(super) fn get_ret(&self) -> Option { 276 | self.ret 277 | } 278 | 279 | pub(super) fn set_stack_ptr(&mut self, val: u64) { 280 | self.rsp = Some(val); 281 | } 282 | 283 | pub(super) fn iter() -> impl Iterator { 284 | [X86_64::RSP, X86_64::RBP, X86_64::RA].into_iter() 285 | } 286 | } 287 | } 288 | 289 | use arch::RegisterSet; 290 | -------------------------------------------------------------------------------- /src/driver/gpio.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | -------------------------------------------------------------------------------- /src/driver/keyboard.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::str::FromStr; 12 | 13 | use crate::sync::Spinlock; 14 | use crate::ui::keymap::{Keymap, KeymapState}; 15 | use crate::ui::kterm::KERNEL_TERMINAL; 16 | use crate::{arch, print, println, warning}; 17 | 18 | #[derive(Debug)] 19 | pub enum KeyEvent { 20 | Unknown, 21 | Pressed(Key), 22 | Released(Key), 23 | } 24 | 25 | #[derive(Debug, Hash, PartialEq, Eq)] 26 | pub enum Key { 27 | LeftShift, 28 | RightShift, 29 | LeftCtrl, 30 | RightCtrl, 31 | LeftMeta, 32 | RightMeta, 33 | Alt, 34 | AltGr, 35 | Menu, 36 | 37 | Backquote, 38 | Digit(u8), 39 | Dash, 40 | Equal, 41 | Backslash, 42 | LeftBracket, 43 | RightBracket, 44 | Semicolon, 45 | SingleQuote, 46 | Comma, 47 | Period, 48 | Slash, 49 | Iso, 50 | 51 | Letter(char), 52 | 53 | Tab, 54 | CapsLock, 55 | Space, 56 | Enter, 57 | Escape, 58 | Backspace, 59 | F(u8), 60 | 61 | Insert, 62 | Del, 63 | Home, 64 | End, 65 | PgUp, 66 | PgDown, 67 | Up, 68 | Down, 69 | Left, 70 | Right, 71 | 72 | ScrollLock, 73 | 74 | KeypadDigit(u8), 75 | KeypadPeriod, 76 | KeypadEnter, 77 | KeypadPlus, 78 | KeypadMinus, 79 | KeypadMul, 80 | KeypadDiv, 81 | KeypadNumLock, 82 | } 83 | 84 | #[derive(Debug, PartialEq, Eq)] 85 | pub enum Deadkey { 86 | GraveAccent, 87 | AcuteAccent, 88 | Circumflex, 89 | Tilde, 90 | Macron, 91 | Breve, 92 | Diaeresis, 93 | Ring, 94 | Caron, 95 | } 96 | 97 | impl FromStr for Key { 98 | type Err = (); 99 | 100 | fn from_str(s: &str) -> Result { 101 | Ok(if s.len() == 1 { 102 | let c = s.chars().nth(0).unwrap(); 103 | match c { 104 | '`' => Key::Backquote, 105 | '0'..='9' => Key::Digit(c as u8 - 0x30), 106 | '-' => Key::Dash, 107 | '=' => Key::Equal, 108 | '[' => Key::LeftBracket, 109 | ']' => Key::RightBracket, 110 | ';' => Key::Semicolon, 111 | '\'' => Key::SingleQuote, 112 | ',' => Key::Comma, 113 | '.' => Key::Period, 114 | '/' => Key::Slash, 115 | '\\' => Key::Backslash, 116 | 'A'..='Z' => Key::Letter(c), 117 | _ => return Err(()), 118 | } 119 | } else { 120 | match s { 121 | "iso" => Key::Iso, 122 | "KP0" => Key::KeypadDigit(0), 123 | "KP1" => Key::KeypadDigit(1), 124 | "KP2" => Key::KeypadDigit(2), 125 | "KP3" => Key::KeypadDigit(3), 126 | "KP4" => Key::KeypadDigit(4), 127 | "KP5" => Key::KeypadDigit(5), 128 | "KP6" => Key::KeypadDigit(6), 129 | "KP7" => Key::KeypadDigit(7), 130 | "KP8" => Key::KeypadDigit(8), 131 | "KP9" => Key::KeypadDigit(9), 132 | "KP." => Key::KeypadPeriod, 133 | "KP+" => Key::KeypadPlus, 134 | "KP-" => Key::KeypadMinus, 135 | "KP*" => Key::KeypadMul, 136 | "KP/" => Key::KeypadDiv, 137 | _ => return Err(()), 138 | } 139 | }) 140 | } 141 | } 142 | 143 | impl Deadkey { 144 | pub fn apply(&self, c: char) -> Option { 145 | Some(match self { 146 | Deadkey::Circumflex => match c { 147 | 'a' => 'â', 148 | 'z' => 'ẑ', 149 | 'e' => 'ê', 150 | 'y' => 'ŷ', 151 | 'u' => 'û', 152 | 'i' => 'î', 153 | 'o' => 'ô', 154 | 's' => 'ŝ', 155 | 'g' => 'ĝ', 156 | 'h' => 'ĥ', 157 | 'j' => 'ĵ', 158 | 'w' => 'ŵ', 159 | 'c' => 'ĉ', 160 | 'A' => 'Â', 161 | 'Z' => 'Ẑ', 162 | 'E' => 'Ê', 163 | 'Y' => 'Ŷ', 164 | 'U' => 'Û', 165 | 'I' => 'Î', 166 | 'O' => 'Ô', 167 | 'S' => 'Ŝ', 168 | 'G' => 'Ĝ', 169 | 'H' => 'Ĥ', 170 | 'J' => 'Ĵ', 171 | 'W' => 'Ŵ', 172 | 'C' => 'Ĉ', 173 | _ => return None, 174 | }, 175 | Deadkey::Diaeresis => match c { 176 | 'a' => 'ä', 177 | 'e' => 'ë', 178 | 't' => 'ẗ', 179 | 'y' => 'ÿ', 180 | 'u' => 'ü', 181 | 'i' => 'ï', 182 | 'o' => 'ö', 183 | 'h' => 'ḧ', 184 | 'w' => 'ẅ', 185 | 'x' => 'ẍ', 186 | 'A' => 'Ä', 187 | 'E' => 'Ë', 188 | 'Y' => 'Ÿ', 189 | 'U' => 'Ü', 190 | 'I' => 'Ï', 191 | 'O' => 'Ö', 192 | 'H' => 'Ḧ', 193 | 'W' => 'Ẅ', 194 | 'X' => 'Ẍ', 195 | _ => return None, 196 | }, 197 | // TODO: Implement the rest 198 | _ => return None, 199 | }) 200 | } 201 | 202 | pub fn as_standalone(&self) -> Option { 203 | Some(match self { 204 | Deadkey::GraveAccent => '`', 205 | Deadkey::AcuteAccent => '´', 206 | Deadkey::Circumflex => '^', 207 | Deadkey::Tilde => '~', 208 | Deadkey::Macron => '¯', 209 | Deadkey::Breve => '˘', 210 | Deadkey::Diaeresis => '¨', 211 | Deadkey::Ring => '°', 212 | Deadkey::Caron => 'ˇ', 213 | }) 214 | } 215 | } 216 | 217 | impl TryFrom for Deadkey { 218 | type Error = (); 219 | 220 | fn try_from(value: char) -> Result { 221 | Ok(match value { 222 | '\u{0300}' => Deadkey::GraveAccent, 223 | '\u{0301}' => Deadkey::AcuteAccent, 224 | '\u{0302}' => Deadkey::Circumflex, 225 | '\u{0303}' => Deadkey::Tilde, 226 | '\u{0304}' => Deadkey::Macron, 227 | '\u{0306}' => Deadkey::Breve, 228 | '\u{0308}' => Deadkey::Diaeresis, 229 | '\u{030a}' => Deadkey::Ring, 230 | '\u{030c}' => Deadkey::Caron, 231 | _ => return Err(()), 232 | }) 233 | } 234 | } 235 | 236 | static KEYBOARD: Spinlock> = Spinlock::new(None); 237 | 238 | struct Keyboard { 239 | keymap: KeymapState, 240 | 241 | lctrl: bool, 242 | rctrl: bool, 243 | lshift: bool, 244 | rshift: bool, 245 | alt: bool, 246 | altgr: bool, 247 | lmeta: bool, 248 | rmeta: bool, 249 | capslock: bool, 250 | } 251 | 252 | impl Keyboard { 253 | pub fn new() -> Self { 254 | Self { 255 | lctrl: false, 256 | rctrl: false, 257 | lshift: false, 258 | rshift: false, 259 | alt: false, 260 | altgr: false, 261 | lmeta: false, 262 | rmeta: false, 263 | capslock: false, 264 | keymap: KeymapState::new( 265 | Keymap::from_file(include_bytes!(concat!( 266 | env!("CARGO_MANIFEST_DIR"), 267 | "/media/fr.keymap" 268 | ))) 269 | .unwrap(), 270 | ), 271 | } 272 | } 273 | 274 | pub fn has_ctrl(&self) -> bool { 275 | self.lctrl || self.rctrl 276 | } 277 | 278 | pub fn has_shift(&self) -> bool { 279 | self.lshift || self.rshift 280 | } 281 | 282 | pub fn has_meta(&self) -> bool { 283 | self.lmeta || self.rmeta 284 | } 285 | 286 | pub fn on_key_event(&mut self, event: KeyEvent) { 287 | match event { 288 | KeyEvent::Pressed(key) => match key { 289 | Key::Space => print!(" "), 290 | Key::Enter | Key::KeypadEnter => println!(), 291 | Key::ScrollLock => arch::cpu::reset(), 292 | 293 | Key::LeftShift => self.lshift = true, 294 | Key::RightShift => self.rshift = true, 295 | Key::LeftCtrl => self.lctrl = true, 296 | Key::RightCtrl => self.rctrl = true, 297 | Key::Alt => self.alt = true, 298 | Key::AltGr => self.altgr = true, 299 | Key::LeftMeta => self.lmeta = true, 300 | Key::RightMeta => self.rmeta = true, 301 | Key::CapsLock => self.capslock = !self.capslock, // TODO: LED 302 | 303 | _ => { 304 | if self.has_ctrl() { 305 | match key { 306 | Key::Letter('L') => { 307 | KERNEL_TERMINAL.lock().as_mut().unwrap().clear() 308 | } 309 | _ => (), 310 | } 311 | return; 312 | } 313 | 314 | let c = self.keymap.glyph( 315 | key, 316 | self.altgr, 317 | self.capslock, 318 | self.has_shift(), 319 | ); 320 | if let Some(c) = c { 321 | print!("{c}"); 322 | } 323 | } 324 | }, 325 | KeyEvent::Released(key) => match key { 326 | Key::LeftShift => self.lshift = false, 327 | Key::RightShift => self.rshift = false, 328 | Key::LeftCtrl => self.lctrl = false, 329 | Key::RightCtrl => self.rctrl = false, 330 | Key::Alt => self.alt = false, 331 | Key::AltGr => self.altgr = false, 332 | Key::LeftMeta => self.lmeta = false, 333 | Key::RightMeta => self.rmeta = false, 334 | _ => (), 335 | }, 336 | _ => (), 337 | } 338 | } 339 | } 340 | 341 | pub fn init() { 342 | *KEYBOARD.lock() = Some(Keyboard::new()); 343 | } 344 | 345 | pub fn on_key_event(event: KeyEvent) { 346 | if let Some(kb) = KEYBOARD.lock().as_mut() { 347 | kb.on_key_event(event); 348 | } else { 349 | warning!("key event with no kernel keyboard"); 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/driver/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub mod gpio; 12 | pub mod keyboard; 13 | pub mod screen; 14 | pub mod vga; 15 | -------------------------------------------------------------------------------- /src/driver/screen.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::mem::transmute; 12 | use core::str::FromStr; 13 | 14 | #[derive(Debug, Copy, Clone, Default)] 15 | pub struct Color { 16 | pub r: u8, 17 | pub g: u8, 18 | pub b: u8, 19 | } 20 | 21 | #[derive(Debug, Copy, Clone, Default)] 22 | #[repr(C, align(4))] 23 | pub struct ColorA { 24 | pub b: u8, 25 | pub g: u8, 26 | pub r: u8, 27 | pub a: u8, 28 | } 29 | 30 | impl Color { 31 | pub fn from_bgra_u32(bgra: u32) -> Self { 32 | Self { 33 | r: ((bgra & 0x00ff0000) >> 16) as u8, 34 | g: ((bgra & 0x0000ff00) >> 8) as u8, 35 | b: (bgra & 0x000000ff) as u8, 36 | } 37 | } 38 | 39 | pub fn blend(fg: Color, alpha: u8, bg: Color) -> Color { 40 | Color { 41 | r: Self::blend_channel(fg.r, alpha, bg.r), 42 | g: Self::blend_channel(fg.g, alpha, bg.g), 43 | b: Self::blend_channel(fg.b, alpha, bg.b), 44 | } 45 | } 46 | 47 | pub fn with_alpha(self, a: u8) -> ColorA { 48 | ColorA { 49 | r: self.r, 50 | g: self.g, 51 | b: self.b, 52 | a, 53 | } 54 | } 55 | 56 | #[inline] 57 | pub fn as_bgra(self) -> [u8; 4] { 58 | [self.b, self.g, self.r, 255] 59 | } 60 | 61 | #[inline] 62 | fn blend_channel(fg: u8, alpha: u8, bg: u8) -> u8 { 63 | let fg = (alpha as u16 * fg as u16 / 255) as u8; 64 | let bg = ((255 - alpha) as u16 * bg as u16 / 255) as u8; 65 | 66 | fg + bg 67 | } 68 | } 69 | 70 | impl ColorA { 71 | #[inline] 72 | pub fn from_bgra_u32(bgra: u32) -> Self { 73 | unsafe { transmute(bgra) } 74 | } 75 | 76 | #[inline] 77 | pub fn blend(self, other: ColorA) -> ColorA { 78 | if self.a == 0 { 79 | other 80 | } else if self.a == 255 { 81 | self 82 | } else { 83 | ColorA { 84 | r: Color::blend_channel(self.r, self.a, other.r), 85 | g: Color::blend_channel(self.g, self.a, other.g), 86 | b: Color::blend_channel(self.b, self.a, other.b), 87 | a: 255, 88 | } 89 | } 90 | } 91 | 92 | #[inline] 93 | pub fn as_bgra(self) -> [u8; 4] { 94 | unsafe { transmute(self) } 95 | } 96 | 97 | #[inline] 98 | pub fn as_bgra_u32(self) -> u32 { 99 | unsafe { transmute(self) } 100 | } 101 | 102 | #[inline] 103 | pub fn as_rgb(self) -> Color { 104 | Color { 105 | r: self.r, 106 | g: self.g, 107 | b: self.b, 108 | } 109 | } 110 | } 111 | 112 | impl FromStr for Color { 113 | type Err = (); 114 | 115 | fn from_str(s: &str) -> Result { 116 | if s.len() == 3 { 117 | let rgb = u16::from_str_radix(s, 16).map_err(|_| ())?; 118 | Ok(Color { 119 | r: (((rgb & 0x0f00) >> 8) << 4) as u8, 120 | g: (((rgb & 0x00f0) >> 4) << 4) as u8, 121 | b: (((rgb & 0x000f) >> 0) << 4) as u8, 122 | }) 123 | } else if s.len() == 6 { 124 | let rgb = u32::from_str_radix(s, 16).map_err(|_| ())?; 125 | Ok(Color { 126 | r: ((rgb & 0x00ff_0000) >> 16) as u8, 127 | g: ((rgb & 0x0000_ff00) >> 8) as u8, 128 | b: ((rgb & 0x0000_00ff) >> 0) as u8, 129 | }) 130 | } else { 131 | Err(()) 132 | } 133 | } 134 | } 135 | 136 | pub trait FramebufferScreen { 137 | fn dimensions(&self) -> (usize, usize); 138 | 139 | fn put(&mut self, x: usize, y: usize, color: Color); 140 | 141 | fn copy(&mut self, x: usize, y: usize, data: &[u32]); 142 | 143 | fn clear(&mut self); 144 | } 145 | 146 | pub struct CharAttrs { 147 | pub color: Color, 148 | } 149 | 150 | pub trait TextScreen { 151 | fn put(&mut self, x: usize, y: usize, c: char, attrs: CharAttrs); 152 | 153 | fn scroll_up(&mut self, lines: u8); 154 | 155 | fn clear(&mut self); 156 | } 157 | 158 | pub struct FramebufferTextScreen { 159 | fb: F, 160 | } 161 | 162 | impl TextScreen for FramebufferTextScreen { 163 | fn put(&mut self, _x: usize, _y: usize, _c: char, _attrs: CharAttrs) { 164 | todo!() 165 | } 166 | 167 | fn scroll_up(&mut self, _lines: u8) { 168 | todo!() 169 | } 170 | 171 | fn clear(&mut self) { 172 | todo!() 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/driver/vga.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub trait VgaScreen: core::fmt::Write { 12 | fn put_char(&mut self, c: u8); 13 | 14 | fn put_str(&mut self, str: &str); 15 | 16 | fn println(&mut self, str: &str) { 17 | self.put_str(str); 18 | self.put_char(b'\n'); 19 | } 20 | 21 | fn set_attributes(&mut self, attr: u8); 22 | 23 | fn move_cursor(&mut self, x: u8, y: u8); 24 | 25 | fn cursor(&self) -> (u8, u8); 26 | 27 | fn scroll_up(&mut self, lines: u8); 28 | 29 | fn clear(&mut self); 30 | } 31 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::sync::Spinlock; 12 | 13 | use core::ops::DerefMut; 14 | use core::{fmt, mem}; 15 | 16 | pub static DEFAULT_LOGGER: Spinlock<&'static mut (dyn Logger + Send)> = 17 | Spinlock::new(unsafe { &mut NULL_LOGGER }); 18 | 19 | pub trait Logger { 20 | fn log(&mut self, severity: Severity, args: fmt::Arguments); 21 | } 22 | 23 | #[derive(Copy, Clone)] 24 | pub enum Severity { 25 | Debug, 26 | Info, 27 | Notice, 28 | Warning, 29 | Error, 30 | Critical, 31 | Alert, 32 | Emergency, 33 | } 34 | 35 | impl Severity { 36 | pub fn label(&self) -> &str { 37 | use Severity::*; 38 | 39 | match self { 40 | Debug => "debug", 41 | Info => "info", 42 | Notice => "notice", 43 | Warning => "warn", 44 | Error => "error", 45 | Critical => "critical", 46 | Alert => "alert", 47 | Emergency => "emerg", 48 | } 49 | } 50 | } 51 | 52 | #[macro_export] 53 | macro_rules! debug { 54 | ($($arg:tt)*) => ({ 55 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 56 | logger.log($crate::logging::Severity::Debug, format_args!($($arg)*)); 57 | }); 58 | } 59 | 60 | #[macro_export] 61 | macro_rules! info { 62 | ($($arg:tt)*) => ({ 63 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 64 | logger.log($crate::logging::Severity::Info, format_args!($($arg)*)); 65 | }); 66 | } 67 | 68 | #[macro_export] 69 | macro_rules! notice { 70 | ($($arg:tt)*) => ({ 71 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 72 | logger.log($crate::logging::Severity::Notice, format_args!($($arg)*)); 73 | }); 74 | } 75 | 76 | #[macro_export] 77 | macro_rules! warning { 78 | ($($arg:tt)*) => ({ 79 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 80 | logger.log($crate::logging::Severity::Warning, format_args!($($arg)*)); 81 | }); 82 | } 83 | 84 | #[macro_export] 85 | macro_rules! error { 86 | ($($arg:tt)*) => ({ 87 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 88 | logger.log($crate::logging::Severity::Error, format_args!($($arg)*)); 89 | }); 90 | } 91 | 92 | #[macro_export] 93 | macro_rules! critical { 94 | ($($arg:tt)*) => ({ 95 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 96 | logger.log($crate::logging::Severity::Critical, format_args!($($arg)*)); 97 | }); 98 | } 99 | 100 | #[macro_export] 101 | macro_rules! alert { 102 | ($($arg:tt)*) => ({ 103 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 104 | logger.log($crate::logging::Severity::Alert, format_args!($($arg)*)); 105 | }); 106 | } 107 | 108 | #[macro_export] 109 | macro_rules! emergency { 110 | ($($arg:tt)*) => ({ 111 | let mut logger = $crate::logging::DEFAULT_LOGGER.lock(); 112 | logger.log($crate::logging::Severity::Emergency, format_args!($($arg)*)); 113 | }); 114 | } 115 | 116 | struct NullLogger; 117 | 118 | static mut NULL_LOGGER: NullLogger = NullLogger; 119 | 120 | impl Logger for NullLogger { 121 | fn log(&mut self, _severity: Severity, _args: fmt::Arguments) {} 122 | } 123 | 124 | pub unsafe fn reset_logger() -> &'static mut (dyn Logger + Send) { 125 | let mut logger = DEFAULT_LOGGER.lock(); 126 | mem::replace(logger.deref_mut(), unsafe { &mut NULL_LOGGER }) 127 | } 128 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | #![cfg_attr(not(test), no_std)] 12 | #![cfg_attr(not(test), no_main)] 13 | #![feature(alloc_error_handler)] 14 | #![feature(const_trait_impl)] 15 | #![feature(const_for)] 16 | #![feature(iter_advance_by)] 17 | #![allow(unused_unsafe)] 18 | #![allow(dead_code)] 19 | // TODO: fix all &mut of statics 20 | #![allow(static_mut_refs)] 21 | 22 | #[cfg(not(test))] 23 | extern crate alloc; 24 | 25 | pub mod arch; 26 | pub mod driver; 27 | pub mod logging; 28 | pub mod mem; 29 | pub mod panic; 30 | pub mod screen; 31 | pub mod sync; 32 | 33 | #[macro_use] 34 | pub mod misc; 35 | 36 | mod backtrace; 37 | pub mod task; 38 | pub mod ui; 39 | 40 | fn main() -> ! { 41 | println!("Nucloid\r v{}", env!("CARGO_PKG_VERSION")); 42 | println!(); 43 | println!("\x1bimpl\x1b PxFont {{"); 44 | println!( 45 | " \x1bpub fn\x1b \x1bfrom_data\x1b(data: &[\x1bu8\x1b]) -> Result<\x1bSelf,\x1b PxFontError> {{" 46 | ); 47 | println!( 48 | " \x1blet mut\x1b reader = Cursor::\x1bnew\x1b(data)\x1b;\x1b" 49 | ); 50 | println!( 51 | " \x1blet\x1b header = FileHeader::\x1bread\x1b(&\x1bmut\x1b reader)" 52 | ); 53 | println!( 54 | " .\x1bmap_err\x1b(|e| PxFontError::\x1bInvalidHeader\x1b(e))\x1b?;\x1b" 55 | ); 56 | println!( 57 | " \x1blet\x1b glyph_size = header.width \x1bas\x1b usize * header.height \x1bas\x1b usize\x1b;\x1b" 58 | ); 59 | println!( 60 | " \x1blet mut\x1b chars = HashMap::\x1bnew\x1b()\x1b;\x1b" 61 | ); 62 | println!(); 63 | println!( 64 | "Voix ambiguë d’un \x1bcœur\x1b qui, au \x1bzéphyr\x1b, préfère les jattes de \x1bkiwis\x1b." 65 | ); 66 | println!("В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!"); 67 | println!("Ξεσκεπάζω την ψυχοφθόρα σας βδελυγμία."); 68 | println!("Ça fera 1 035,00 €, ou £20."); 69 | println!("a\tbb\tccc\tdddd\teeeeee\teeeeeee\teeeeeeee\tf"); 70 | println!("Hello \x1bWORLD\x1b!"); 71 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 72 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 73 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 74 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 75 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 76 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 77 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 78 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 79 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 80 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 81 | println!("Nucloid is powered by 🦀 \x1bRust\x1b."); 82 | 83 | debug!("Test debug"); 84 | info!("Un petite info"); 85 | notice!("Avez-vous vu ça ?"); 86 | warning!("Ceci est un warning !"); 87 | error!("Oops, un erreur s'est produite..."); 88 | critical!("Aïe ! C'est sérieux !"); 89 | 90 | loop { 91 | arch::cpu::halt(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/mem/kalloc/bump_kalloc.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::marker::PhantomData; 12 | use core::ptr::NonNull; 13 | 14 | use crate::mem::kalloc::freelist_kalloc::AllocatorBackend; 15 | use crate::misc::align_up; 16 | 17 | pub struct BumpAllocator { 18 | heap_top: NonNull<()>, 19 | end_of_page: bool, 20 | _phantom: PhantomData, 21 | } 22 | 23 | unsafe impl Send for BumpAllocator {} // TODO: justify 24 | 25 | impl BumpAllocator { 26 | pub const fn new() -> Self { 27 | Self { 28 | heap_top: unsafe { NonNull::new_unchecked(4096 as _) }, 29 | end_of_page: false, 30 | _phantom: PhantomData, 31 | } 32 | } 33 | 34 | pub fn alloc(&mut self, bsize: usize) -> Option> { 35 | let mut block = unsafe { 36 | NonNull::new_unchecked( 37 | align_up(self.heap_top.as_ptr() as usize, 16) as *mut (), 38 | ) 39 | }; 40 | let bytes_left = align_up(self.heap_top.as_ptr() as usize, 4096) 41 | .saturating_sub(block.as_ptr() as usize); 42 | 43 | if bytes_left < bsize { 44 | let nr_pages = align_up(bsize, 4096) >> 12; 45 | block = B::new_pages(nr_pages)?; 46 | } 47 | 48 | self.heap_top = unsafe { 49 | NonNull::new_unchecked( 50 | (block.as_ptr() as *mut u8).add(bsize) as *mut () 51 | ) 52 | }; 53 | 54 | Some(block) 55 | } 56 | 57 | pub unsafe fn dealloc(&mut self, _ptr: *mut ()) {} 58 | 59 | pub unsafe fn realloc( 60 | &mut self, 61 | ptr: *mut (), 62 | bsize: usize, 63 | ) -> Option> { 64 | if ptr.is_null() { 65 | return self.alloc(bsize); 66 | } 67 | 68 | unimplemented!() 69 | /*let new = self.alloc(bsize)?; 70 | let copy_size = min(block.bsize, bsize); 71 | 72 | unsafe { 73 | copy_nonoverlapping(ptr, new.as_ptr(), copy_size); 74 | } 75 | 76 | Some(new)*/ 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/mem/kalloc/mimalloc/heap.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::arch::sync::{pop_critical_region, push_critical_region}; 12 | use crate::mem::kalloc::mimalloc::{ 13 | BlockHeader, NR_DIRECT_PAGES, PageHeader, SMALL_SIZE_BUCKET_INC, 14 | SMALL_SIZE_BUCKET_INC_SHIFT, 15 | }; 16 | use crate::misc::{align_up, first_bit_pos}; 17 | use crate::task::cpu::{CpuIndex, MAX_CPUS}; 18 | use crate::task::cpu_local::CpuLocal; 19 | use core::cell::RefCell; 20 | use core::mem::{MaybeUninit, size_of}; 21 | use core::ptr::NonNull; 22 | 23 | static HEAPS: CpuLocal> = 24 | CpuLocal::new(Heap::new_dangling_array()); 25 | 26 | #[derive(Copy, Clone)] 27 | pub struct Heap { 28 | pub cpu_index: u8, 29 | pub direct_pages: [NonNull>; NR_DIRECT_PAGES], 30 | pub pages_list: [NonNull>; NR_DIRECT_PAGES], 31 | } 32 | 33 | impl Heap { 34 | const fn new_dangling() -> Self { 35 | Self { 36 | cpu_index: 0, 37 | direct_pages: [NonNull::dangling(); NR_DIRECT_PAGES], 38 | pages_list: [NonNull::dangling(); NR_DIRECT_PAGES], 39 | } 40 | } 41 | 42 | const fn new_dangling_array() -> [RefCell; MAX_CPUS] { 43 | let mut arr = MaybeUninit::<[RefCell; MAX_CPUS]>::uninit(); 44 | 45 | let mut i = 0; 46 | while i < MAX_CPUS { 47 | let arr_ref = unsafe { &mut *arr.as_mut_ptr() }; 48 | arr_ref[i] = RefCell::new(Heap::new_dangling()); 49 | i += 1; 50 | } 51 | 52 | unsafe { arr.assume_init() } 53 | } 54 | 55 | unsafe fn init(&mut self, cpu_index: u8) { 56 | self.cpu_index = cpu_index; 57 | } 58 | 59 | /// Proceed to initialize all per-CPU heap. This function must be called 60 | /// once during the early boot before using the allocator. 61 | /// 62 | /// # Safety # 63 | /// 64 | /// The function must be called once during the early boot process with 65 | /// only one active CPU. 66 | pub unsafe fn init_all() { 67 | push_critical_region(); 68 | 69 | // SAFETY: we are during the early boot process, there is only one CPU 70 | // active, and within a critical region: no interrupts or preemption. 71 | for (index, heap) in unsafe { HEAPS.iter_unchecked() }.enumerate() { 72 | unsafe { 73 | heap.borrow_mut().init(index as u8); 74 | } 75 | } 76 | 77 | pop_critical_region(); 78 | } 79 | 80 | pub fn for_cpu(cpu_index: &CpuIndex) -> &RefCell { 81 | HEAPS.get(cpu_index) 82 | } 83 | 84 | pub fn pages_list(&mut self, size: usize) -> NonNull> { 85 | self.pages_list[Self::bucket_for_size(size) as usize] 86 | } 87 | 88 | pub fn small_alloc(&mut self, size: usize) -> NonNull { 89 | let bucket = 90 | (size + (SMALL_SIZE_BUCKET_INC - 1)) >> SMALL_SIZE_BUCKET_INC_SHIFT; 91 | let mut page = 92 | unsafe { self.direct_pages[bucket].as_ref() }.borrow_mut(); 93 | 94 | if let Some(block) = page.free_list { 95 | let block = unsafe { block.as_ref() }; 96 | page.free_list = block.next; 97 | page.nr_block_used += 1; 98 | 99 | block.into() 100 | } else { 101 | self.generic_alloc(size); 102 | todo!() 103 | } 104 | } 105 | 106 | pub fn generic_alloc(&mut self, _size: usize) { 107 | // deferred free 108 | // heap delayed free 109 | // find or make a page from heap 110 | todo!() 111 | } 112 | 113 | pub fn acquire_free_page(&mut self) -> Option> { 114 | todo!() 115 | } 116 | 117 | pub fn bucket_for_size(size: usize) -> u8 { 118 | debug_assert!(size > 0); 119 | 120 | let wsize = align_up(size, size_of::()) / size_of::(); 121 | 122 | if wsize == 1 { 123 | 1 124 | } else if wsize <= 8 { 125 | align_up(wsize as u8, 2) 126 | } else { 127 | let wsize = wsize - 1; 128 | let bit_pos = first_bit_pos(wsize); 129 | 130 | ((((bit_pos as usize) << 2) | ((wsize >> (bit_pos - 2)) & 3)) - 3) 131 | as u8 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/mem/kalloc/mimalloc/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | mod heap; 12 | 13 | use crate::mem::kalloc::mimalloc::heap::Heap; 14 | use crate::sync::Spinlock; 15 | use crate::task::cpu::current_cpu_index; 16 | use core::ptr::NonNull; 17 | 18 | const SMALL_SIZE_MAX: usize = 1024; 19 | const SMALL_SIZE_BUCKET_INC: usize = 8; 20 | const SMALL_SIZE_BUCKET_INC_SHIFT: usize = 3; 21 | const NR_DIRECT_PAGES: usize = SMALL_SIZE_MAX / SMALL_SIZE_BUCKET_INC; 22 | 23 | #[repr(C)] 24 | struct Segment { 25 | cpu_index: u8, 26 | magic: [u8; 3], 27 | page_shift: u32, 28 | pages: [PageHeader; 42], 29 | } 30 | 31 | pub struct PageHeader { 32 | prev: Option>, 33 | next: Option>, 34 | 35 | free_list: Option>, 36 | deferred_free_list: Option>, 37 | foreign_free_list: Spinlock>>, 38 | 39 | nr_block_used: usize, 40 | } 41 | 42 | enum PageAreaContainer { 43 | Small([u8; 42]), 44 | } 45 | 46 | pub struct BlockHeader { 47 | next: Option>, 48 | } 49 | 50 | fn small_alloc(size: usize) -> NonNull { 51 | let cpu_index = current_cpu_index(); 52 | let mut heap = Heap::for_cpu(&cpu_index).borrow_mut(); 53 | 54 | heap.small_alloc(size) 55 | } 56 | -------------------------------------------------------------------------------- /src/mem/kalloc/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | mod bump_kalloc; 12 | mod freelist_kalloc; 13 | mod mimalloc; 14 | 15 | use crate::error; 16 | use crate::mem::frame::allocate_frames; 17 | use crate::mem::kalloc::bump_kalloc::BumpAllocator; 18 | use crate::mem::kalloc::freelist_kalloc::AllocatorBackend; 19 | use crate::sync::Spinlock; 20 | use core::alloc::{GlobalAlloc, Layout}; 21 | use core::ptr; 22 | use core::ptr::NonNull; 23 | 24 | pub struct KernelAllocatorWrapper( 25 | Spinlock>, 26 | ); 27 | 28 | struct FrameAllocatorBackend; 29 | 30 | impl AllocatorBackend for FrameAllocatorBackend { 31 | fn new_pages(nr_pages: usize) -> Option> { 32 | allocate_frames() 33 | .nr_frames(nr_pages) 34 | .map_lowmem() 35 | .map(|vaddr| NonNull::new(vaddr.as_mut_ptr()).unwrap()) 36 | } 37 | } 38 | 39 | #[cfg_attr(not(test), global_allocator)] 40 | pub static KERNEL_ALLOCATOR: KernelAllocatorWrapper = 41 | KernelAllocatorWrapper(Spinlock::new(BumpAllocator::new())); 42 | 43 | unsafe impl GlobalAlloc for KernelAllocatorWrapper { 44 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 45 | if layout.align() > 16 { 46 | error!( 47 | "kernel allocator doesn't handle alignment requirements above 16 bytes" 48 | ); 49 | return ptr::null_mut(); 50 | } 51 | 52 | self.0 53 | .lock() 54 | .alloc(layout.size()) 55 | .map(|p| p.as_ptr() as *mut u8) 56 | .unwrap_or(ptr::null_mut()) 57 | } 58 | 59 | #[inline] 60 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { 61 | unsafe { self.0.lock().dealloc(ptr as *mut ()) } 62 | } 63 | 64 | #[inline] 65 | unsafe fn realloc( 66 | &self, 67 | ptr: *mut u8, 68 | _layout: Layout, 69 | new_size: usize, 70 | ) -> *mut u8 { 71 | unsafe { 72 | self.0 73 | .lock() 74 | .realloc(ptr as *mut (), new_size) 75 | .map(|p| p.as_ptr() as *mut u8) 76 | .unwrap_or(ptr::null_mut()) 77 | } 78 | } 79 | } 80 | 81 | #[cfg(not(test))] 82 | #[alloc_error_handler] 83 | fn allocator_error(layout: Layout) -> ! { 84 | panic!("Kernel allocator failed: {:?}", layout) 85 | } 86 | -------------------------------------------------------------------------------- /src/mem/load.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::ops::Range; 12 | 13 | use crate::mem::VAddr; 14 | 15 | unsafe extern "C" { 16 | /// The virtual address at which the kernel image, as loaded by the 17 | /// bootloader, resides; the address is guaranteed to be page-aligned. 18 | /// The value is passed as a symbol, i.e. a memory address, what this 19 | /// address points to is irrelevant; ONLY take the ADDRESS of this variable 20 | /// and *IN NO CASE ACCESS THE VALUE EVEN FOR READING*. 21 | static __kernel_image_start: u8; 22 | 23 | /// The address of the first byte past the kernel image in virtual memory. 24 | /// The address is guaranteed to be page-aligned. 25 | /// The value is passed as a symbol, i.e. a memory address, what this 26 | /// address points to is irrelevant; ONLY take the ADDRESS of this variable 27 | /// and *IN NO CASE ACCESS THE VALUE EVEN FOR READING*. 28 | static __kernel_image_end: u8; 29 | 30 | /// The numbers of bytes of the kernel image, including padding. The size 31 | /// is guaranteed to be page-aligned. 32 | /// The value is passed as a symbol, i.e. a memory address, what this 33 | /// address points to is irrelevant; ONLY take the ADDRESS of this variable 34 | /// and *IN NO CASE ACCESS THE VALUE EVEN FOR READING*. 35 | static __kernel_image_size: u8; 36 | 37 | static __kernel_text_start: u8; 38 | 39 | static __kernel_text_end: u8; 40 | 41 | static __kernel_rodata_start: u8; 42 | 43 | static __kernel_rodata_end: u8; 44 | } 45 | 46 | #[inline] 47 | pub fn kernel_image() -> Range { 48 | unsafe { 49 | VAddr(&__kernel_image_start as *const u8 as usize) 50 | ..VAddr(&__kernel_image_end as *const u8 as usize) 51 | } 52 | } 53 | 54 | #[inline] 55 | pub fn kernel_text_segment() -> Range { 56 | unsafe { 57 | VAddr(&__kernel_text_start as *const u8 as usize) 58 | ..VAddr(&__kernel_text_end as *const u8 as usize) 59 | } 60 | } 61 | 62 | #[inline] 63 | pub fn kernel_rodata_segment() -> Range { 64 | unsafe { 65 | VAddr(&__kernel_rodata_start as *const u8 as usize) 66 | ..VAddr(&__kernel_rodata_end as *const u8 as usize) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/mem/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::arch; 12 | use crate::arch::cpu::MachineState; 13 | use crate::panic::panic_at_state; 14 | use core::cmp::Ordering; 15 | use core::fmt::{Debug, Formatter, LowerHex}; 16 | use core::ops::{Add, AddAssign, Sub}; 17 | 18 | pub mod frame; 19 | pub mod kalloc; 20 | pub mod load; 21 | 22 | pub use arch::mem::PAddr; 23 | 24 | use crate::arch::mem::page_permissions; 25 | use crate::screen::R; 26 | 27 | pub static mut PHYS_MEM_SIZE: u64 = 0; 28 | pub static mut LOWMEM_VA_END: VAddr = VAddr(0); 29 | 30 | impl Add for PAddr { 31 | type Output = Self; 32 | 33 | fn add(self, rhs: u64) -> Self::Output { 34 | Self(self.0 + rhs) 35 | } 36 | } 37 | 38 | #[derive(Copy, Clone, PartialEq)] 39 | pub struct VAddr(pub usize); 40 | 41 | impl VAddr { 42 | #[inline] 43 | pub const fn as_ptr(self) -> *const T { 44 | self.0 as _ 45 | } 46 | 47 | #[inline] 48 | pub const fn as_mut_ptr(self) -> *mut T { 49 | self.0 as _ 50 | } 51 | } 52 | 53 | impl From<*const T> for VAddr { 54 | fn from(ptr: *const T) -> Self { 55 | Self(ptr as usize) 56 | } 57 | } 58 | 59 | impl From<*mut T> for VAddr { 60 | fn from(ptr: *mut T) -> Self { 61 | Self(ptr as usize) 62 | } 63 | } 64 | 65 | impl Add for VAddr { 66 | type Output = Self; 67 | 68 | fn add(self, rhs: VAddr) -> Self::Output { 69 | Self(self.0 + rhs.0) 70 | } 71 | } 72 | 73 | impl Add for VAddr { 74 | type Output = Self; 75 | 76 | fn add(self, rhs: usize) -> Self::Output { 77 | Self(self.0 + rhs) 78 | } 79 | } 80 | 81 | impl AddAssign for VAddr { 82 | fn add_assign(&mut self, rhs: usize) { 83 | self.0 += rhs; 84 | } 85 | } 86 | 87 | impl Sub for VAddr { 88 | type Output = Self; 89 | 90 | fn sub(self, rhs: Self) -> Self::Output { 91 | Self(self.0 - rhs.0) 92 | } 93 | } 94 | 95 | impl PartialOrd for VAddr { 96 | fn partial_cmp(&self, other: &Self) -> Option { 97 | self.0.partial_cmp(&other.0) 98 | } 99 | } 100 | 101 | impl Debug for VAddr { 102 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 103 | R(*self).fmt(f) 104 | } 105 | } 106 | 107 | pub fn get_lowmem_va_end() -> VAddr { 108 | unsafe { LOWMEM_VA_END } 109 | } 110 | 111 | pub struct PagePermissions { 112 | pub accessible: bool, 113 | pub readable: bool, 114 | pub writable: bool, 115 | pub executable: bool, 116 | } 117 | 118 | pub enum AccessAttempt { 119 | Read, 120 | Write, 121 | Execute, 122 | } 123 | 124 | pub fn handle_pagefault( 125 | fault_addr: VAddr, 126 | access: AccessAttempt, 127 | machine_state: &MachineState, 128 | ) { 129 | let op_str = match access { 130 | AccessAttempt::Read => "Invalid read", 131 | AccessAttempt::Write => "Invalid write", 132 | AccessAttempt::Execute => "Invalid execution", 133 | }; 134 | 135 | let perms = page_permissions(fault_addr); 136 | let reason = if !perms.accessible { 137 | "page is not mapped" 138 | } else if matches!(access, AccessAttempt::Write) && !perms.writable { 139 | "page is read-only" 140 | } else if matches!(access, AccessAttempt::Execute) && !perms.executable { 141 | "page is non-executable" 142 | } else { 143 | "unknown error" 144 | }; 145 | 146 | panic_at_state( 147 | format_args!("{} at {:?}: {}", op_str, fault_addr, reason), 148 | Some(machine_state), 149 | 0, 150 | ); 151 | } 152 | -------------------------------------------------------------------------------- /src/misc.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::fmt; 12 | use core::fmt::Formatter; 13 | use core::ops::{BitAnd, Not}; 14 | use num_integer::Integer; 15 | 16 | pub struct BinSize(pub u64); 17 | 18 | impl fmt::Display for BinSize { 19 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 20 | let size; 21 | let unit; 22 | 23 | if self.0 < 1024 { 24 | size = self.0 as f64; // TODO: ensure no FPU register is used 25 | unit = "o"; 26 | } else if self.0 < 1024 * 1024 { 27 | size = self.0 as f64 / 1024.0; 28 | unit = "Kio"; 29 | } else if self.0 < 1024 * 1024 * 1024 { 30 | size = self.0 as f64 / 1024.0 / 1024.0; 31 | unit = "Mio"; 32 | } else { 33 | size = self.0 as f64 / 1024.0 / 1024.0 / 1024.0; 34 | unit = "Gio"; 35 | } 36 | 37 | if unit == "o" { 38 | write!(f, "{} {}", size, unit) 39 | } else { 40 | write!(f, "{:.2} {}", size, unit) 41 | } 42 | } 43 | } 44 | 45 | /// Returns the next integer multiple of `multiple` or `n` if already a 46 | /// multiple of `multiple`. 47 | // TODO: make const (num_integer does not support it) 48 | pub fn align_up(n: T, multiple: T) -> T 49 | where 50 | T: Integer + Not + BitAnd + Copy, 51 | { 52 | (n + (multiple - T::one())) & !(multiple - T::one()) 53 | } 54 | 55 | /// Return the bit position (counting from 0 from the LSB) of the first bit at 56 | /// one from MSB to LSB. Return 0 if all zeroes. 57 | pub fn first_bit_pos(n: usize) -> u8 { 58 | for i in (0..64).rev() { 59 | if (n & (1 << i)) > 0 { 60 | return i; 61 | } 62 | } 63 | 64 | 0 65 | } 66 | 67 | #[cfg(test)] 68 | mod test { 69 | use crate::misc::first_bit_pos; 70 | 71 | #[test] 72 | fn test_first_bit_pos() { 73 | assert_eq!(first_bit_pos(0b0001_0101), 4); 74 | assert_eq!(first_bit_pos(0b1001_0101), 7); 75 | assert_eq!(first_bit_pos(0b11100000_10010101), 15); 76 | assert_eq!(first_bit_pos(0), 0); 77 | } 78 | } 79 | 80 | #[macro_use] 81 | pub mod macros { 82 | // Author: https://users.rust-lang.org/u/ExpHP 83 | #[repr(C)] // guarantee 'bytes' comes after '_align' 84 | pub struct AlignedAs { 85 | pub _align: [Align; 0], 86 | pub bytes: Bytes, 87 | } 88 | 89 | macro_rules! include_bytes_align_as { 90 | ($align_ty:ty, $path:expr) => {{ 91 | // const block expression to encapsulate the static 92 | use $crate::misc::macros::AlignedAs; 93 | 94 | // this assignment is made possible by CoerceUnsized 95 | static ALIGNED: &AlignedAs<$align_ty, [u8]> = &AlignedAs { 96 | _align: [], 97 | bytes: *include_bytes!($path), 98 | }; 99 | 100 | &ALIGNED.bytes 101 | }}; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/panic.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::fmt; 12 | use core::sync::atomic::{AtomicBool, Ordering}; 13 | 14 | #[cfg(not(test))] 15 | use core::panic::PanicInfo; 16 | 17 | use crate::arch::cpu::MachineState; 18 | use crate::arch::logging::LOGGER_SERIAL; 19 | use crate::backtrace::Backtrace; 20 | use crate::driver::vga::VgaScreen; 21 | use crate::{arch, print, println}; 22 | 23 | static PANIC_ENTERED: AtomicBool = AtomicBool::new(false); 24 | 25 | pub fn panic_at_state( 26 | message: fmt::Arguments, 27 | machine: Option<&MachineState>, 28 | skip_frames: usize, 29 | ) -> ! { 30 | // Make sure there is only one thread panicking; if another thread panics, 31 | // we terminate it. This implies that the panic handler is non-reentrant 32 | // and, therefore, we must try our best not to trigger one in it. 33 | if PANIC_ENTERED 34 | .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) 35 | .is_err() 36 | { 37 | arch::cpu::perm_halt(); 38 | } 39 | 40 | let logger = unsafe { LOGGER_SERIAL.as_mut() }; 41 | 42 | // We do nothing if the screen was not initialized, meaning we panicked very 43 | // early in the boot process and aren't able to print anything. 44 | if let Some(logger) = logger { 45 | print_panic(logger, message, machine); 46 | } 47 | 48 | print_terminal(message, machine, skip_frames); 49 | 50 | arch::cpu::perm_halt(); 51 | } 52 | 53 | #[allow(unused_must_use)] 54 | fn print_panic_screen( 55 | vga: &mut impl VgaScreen, 56 | message: fmt::Arguments, 57 | machine: Option<&MachineState>, 58 | ) { 59 | let (orig_x, mut anchor_y) = vga.cursor(); 60 | 61 | if orig_x > 0 { 62 | write!(vga, "\n"); 63 | anchor_y += 1; 64 | } 65 | 66 | vga.move_cursor(orig_x, anchor_y); 67 | vga.set_attributes(0x4f); 68 | write!(vga, "{:80}", ""); 69 | vga.move_cursor(0, anchor_y); 70 | writeln!(vga, "PANIC! {}", message); 71 | 72 | if let Some(machine) = machine { 73 | vga.set_attributes(0x0f); 74 | machine.print(vga); 75 | } 76 | 77 | vga.set_attributes(0x1f); 78 | write!(vga, "{:80}", "STACK TRACE"); 79 | 80 | vga.set_attributes(0x0f); 81 | writeln!(vga, "> arch_init()"); 82 | vga.set_attributes(0x08); 83 | writeln!(vga, " src/arch/x86/init.rs:124 <0xc0100600 + 0x2849>"); 84 | vga.set_attributes(0x0f); 85 | writeln!(vga, "> _start()"); 86 | vga.set_attributes(0x08); 87 | writeln!(vga, " src/arch/x86/start32.S:194 <0xc0108bdf + 0x121>"); 88 | 89 | vga.set_attributes(0x1f); 90 | write!( 91 | vga, 92 | "{:79}", 93 | "This was Nucloid v0.1.0, goodbye cruel world." 94 | ); 95 | } 96 | 97 | #[allow(unused_must_use)] 98 | fn print_panic( 99 | w: &mut impl fmt::Write, 100 | message: fmt::Arguments, 101 | machine: Option<&MachineState>, 102 | ) { 103 | writeln!(w, "\x1b[31mPANIC! {}", message); 104 | if let Some(machine) = machine { 105 | writeln!(w, "{}", machine); 106 | } 107 | } 108 | 109 | fn print_terminal( 110 | message: fmt::Arguments, 111 | machine: Option<&MachineState>, 112 | skip_frames: usize, 113 | ) { 114 | println!("\x1bKERNEL PANIC!\x1b {message}"); 115 | 116 | if let Some(machine) = machine { 117 | machine.print_term(); 118 | 119 | for frame in Backtrace::from_machine_state(machine).skip(skip_frames) { 120 | if let Some(sym) = frame.symbol { 121 | println!(" > \x1b{sym}\x1b"); 122 | } else { 123 | println!(" > ???"); 124 | } 125 | print!(" "); 126 | if let Some((file, line)) = frame.file_line { 127 | print!("{file}:{line} "); 128 | } 129 | if let Some(sym_off) = frame.sym_off { 130 | println!("<0x{:?} + {sym_off:#x}>", frame.pc); 131 | } else { 132 | println!("<0x{:?}>", frame.pc); 133 | } 134 | } 135 | } 136 | } 137 | 138 | #[cfg(not(test))] 139 | #[panic_handler] 140 | fn panic_handler(panic_info: &PanicInfo) -> ! { 141 | let machine_state = MachineState::here(); 142 | 143 | panic_at_state( 144 | format_args!( 145 | "Rust: {} ({})", 146 | panic_info.message(), 147 | panic_info.location().unwrap() 148 | ), 149 | Some(&machine_state), 150 | 2, 151 | ); 152 | } 153 | -------------------------------------------------------------------------------- /src/screen.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::fmt; 12 | 13 | use crate::arch::logging::LOGGER_SERIAL; 14 | use crate::mem::VAddr; 15 | use core::fmt::{Formatter, Write}; 16 | 17 | pub fn _print(args: fmt::Arguments) { 18 | let screen = unsafe { LOGGER_SERIAL.as_mut().unwrap() }; 19 | screen.write_fmt(args).unwrap(); 20 | } 21 | 22 | pub struct R(pub T); 23 | 24 | impl fmt::LowerHex for R { 25 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 26 | write!(f, "{:04x}", &(self.0 >> 16))?; 27 | write!(f, "'")?; 28 | write!(f, "{:04x}", &(self.0 & 0xffff)) 29 | } 30 | } 31 | 32 | impl fmt::LowerHex for R { 33 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 34 | write!(f, "{:08x}", &(self.0 >> 32))?; 35 | write!(f, "'")?; 36 | write!(f, "{:08x}", &(self.0 & 0xffffffff)) 37 | } 38 | } 39 | 40 | impl fmt::LowerHex for R { 41 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 42 | #[cfg(target_pointer_width = "32")] 43 | { 44 | R(self.0 as u32).fmt(f) 45 | } 46 | #[cfg(target_pointer_width = "64")] 47 | { 48 | R(self.0 as u64).fmt(f) 49 | } 50 | } 51 | } 52 | 53 | impl fmt::LowerHex for R { 54 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 55 | R(self.0.0).fmt(f) 56 | } 57 | } 58 | 59 | struct NullScreen; 60 | 61 | static mut NULL_SCREEN: NullScreen = NullScreen; 62 | 63 | impl fmt::Write for NullScreen { 64 | fn write_str(&mut self, _s: &str) -> fmt::Result { 65 | Ok(()) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/sync.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::cell::UnsafeCell; 12 | use core::ops::{Deref, DerefMut}; 13 | use core::sync::atomic::{AtomicBool, Ordering}; 14 | 15 | use crate::arch::sync::{pop_critical_region, push_critical_region}; 16 | 17 | pub struct Spinlock { 18 | lock: AtomicBool, 19 | data: UnsafeCell, 20 | } 21 | 22 | unsafe impl Sync for Spinlock {} 23 | unsafe impl Send for Spinlock {} 24 | 25 | impl Spinlock { 26 | pub const fn new(value: T) -> Self { 27 | Self { 28 | lock: AtomicBool::new(false), 29 | data: UnsafeCell::new(value), 30 | } 31 | } 32 | 33 | pub fn lock(&self) -> SpinlockGuard { 34 | push_critical_region(); 35 | while self 36 | .lock 37 | .compare_exchange_weak( 38 | false, 39 | true, 40 | Ordering::Acquire, 41 | Ordering::Relaxed, 42 | ) 43 | .is_err() 44 | { 45 | pop_critical_region(); 46 | while self.is_locked() { 47 | core::hint::spin_loop(); 48 | } 49 | push_critical_region(); 50 | } 51 | 52 | // Safety: The spinlock guarantees exclusive access to the resource 53 | // wrapped inside it, we just acquired the lock, we are the only owner 54 | // of the resource so we can create a mutable reference to it. 55 | let data = unsafe { &mut *self.data.get() }; 56 | 57 | SpinlockGuard { 58 | lock: &self.lock, 59 | data, 60 | } 61 | } 62 | 63 | /// Checks whether the lock is held right now, without any lock or 64 | /// synchronization. 65 | pub fn is_locked(&self) -> bool { 66 | self.lock.load(Ordering::Relaxed) 67 | } 68 | 69 | pub unsafe fn bypass_lock(&self) -> *mut T { 70 | self.data.get() 71 | } 72 | } 73 | 74 | pub struct SpinlockGuard<'a, T> { 75 | lock: &'a AtomicBool, 76 | data: &'a mut T, 77 | } 78 | 79 | impl Deref for SpinlockGuard<'_, T> { 80 | type Target = T; 81 | 82 | fn deref(&self) -> &Self::Target { 83 | self.data 84 | } 85 | } 86 | 87 | impl DerefMut for SpinlockGuard<'_, T> { 88 | fn deref_mut(&mut self) -> &mut Self::Target { 89 | self.data 90 | } 91 | } 92 | 93 | impl Drop for SpinlockGuard<'_, T> { 94 | fn drop(&mut self) { 95 | self.lock.store(false, Ordering::Release); 96 | pop_critical_region(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/task/cpu.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::arch::sync::{pop_critical_region, push_critical_region}; 12 | use core::sync::atomic::AtomicUsize; 13 | 14 | pub const MAX_CPUS: usize = 32; 15 | pub static NR_CPUS: AtomicUsize = AtomicUsize::new(0); 16 | 17 | pub struct CpuIndex(usize); 18 | 19 | impl CpuIndex { 20 | /// Warning! Avoid copying the return value, but rather use it directly. 21 | /// In fact, once the `CpuIndex` is dropped, there is no more guarantee that 22 | /// the returned CPU index will be the current executing CPU's index: the 23 | /// current task could be preempted or interrupted and rescheduled to 24 | /// another CPU. Always ensure that the `&self` reference outlives the 25 | /// numerical value. 26 | pub fn get(&self) -> usize { 27 | self.0 28 | } 29 | } 30 | 31 | impl Drop for CpuIndex { 32 | fn drop(&mut self) { 33 | pop_critical_region(); 34 | } 35 | } 36 | 37 | pub fn current_cpu_index() -> CpuIndex { 38 | push_critical_region(); 39 | 40 | let curr_cpu = 0; // TODO 41 | 42 | CpuIndex(curr_cpu) 43 | } 44 | -------------------------------------------------------------------------------- /src/task/cpu_local.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use crate::task::cpu::{CpuIndex, MAX_CPUS, NR_CPUS}; 12 | use core::sync::atomic::Ordering; 13 | 14 | pub struct CpuLocal([T; MAX_CPUS]); 15 | 16 | // SAFETY: it is guaranteed that as long as we hold an instance of 17 | // `CpuIndex`, we run on the associated CPU within a critical section, 18 | // i.e. there is no possibility of interruption or preemption, so 19 | // there is no risk of race-condition on accessing the array. 20 | unsafe impl Sync for CpuLocal {} 21 | 22 | impl CpuLocal { 23 | pub const fn new(items: [T; MAX_CPUS]) -> Self { 24 | Self(items) 25 | } 26 | 27 | /// Access the current CPU's value. 28 | pub fn get(&self, cpu_index: &CpuIndex) -> &T { 29 | // SAFETY: this function relies on the fact that as long as the 30 | // reference to `CpuIndex` is valid, the given CPU index is the current 31 | // executing CPU that won't change (through preemption or interruption) 32 | // during the reference's lifetime. The returned reference's lifetime is 33 | // therefor tied to the `CpuIndex`'s lifetime. 34 | &self.0[cpu_index.get()] 35 | } 36 | 37 | /// Iterate over all CPU-local variables (dangerous). Albeit dangerous, this 38 | /// function is useful to perform runtime initialization of CPU-local 39 | /// variables during the early boot process. *This is a niche function, 40 | /// you're probably not going to need it.* 41 | /// 42 | /// # Safety # 43 | /// 44 | /// This function may only be called during the early boot environment when 45 | /// only one CPU is running and with both preemption and interruptions 46 | /// disabled. Failing to do all that *will* lead to data-race conditions and 47 | /// invoke undefined behavior. 48 | pub unsafe fn iter_unchecked(&self) -> impl Iterator { 49 | self.0.iter().take(NR_CPUS.load(Ordering::Relaxed)) 50 | } 51 | } 52 | 53 | impl CpuLocal { 54 | pub const fn new_copy(item: T) -> Self { 55 | Self([item; MAX_CPUS]) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/task/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub mod cpu; 12 | pub mod cpu_local; 13 | pub mod vm; 14 | 15 | use crate::arch::task::TaskMachineContext; 16 | 17 | //use alloc::string::String; 18 | 19 | #[allow(unused)] 20 | pub struct Task { 21 | /// A unique task identifier, there should be no other existing task with 22 | /// the same TID at the same time. TIDs can be reused after a task 23 | /// completed. Zero is not a valid TID. 24 | tid: u32, 25 | 26 | /// Tasks can be grouped into processes, the `pid` contains the task ID 27 | /// (TID) of the "master" task of the process; each thread of a given 28 | /// process thus share the same `pid` value. 29 | /// 30 | /// `pid` is equal to zero (0) for kernel threads. 31 | pid: u32, 32 | 33 | /// The parent PID of the process this task belongs to. Zero means there is 34 | /// no parent; only kernel threads and `userd` (pid=1) are allowed to not 35 | /// have a parent. 36 | parent_pid: u32, 37 | 38 | /// A descriptive name for the task, this is usually the program's name. 39 | //name: String, 40 | 41 | /// The current state of the state, whether it is running, waiting to be 42 | /// scheduled, waiting for an external event, suspended, etc. 43 | state: TaskState, 44 | 45 | /// The saved execution machine context used for context switching; it 46 | /// contains an exhaustive description of all the states to be saved and 47 | /// restored when switching between tasks, typically CPU registers. The 48 | /// exact content of this struct is arch-specific, and only arch-specific 49 | /// code is allowed to handle its internals. 50 | machine_ctx: TaskMachineContext, 51 | vm: vm::VirtualMemory, 52 | priority: i32, 53 | } 54 | 55 | #[allow(unused)] 56 | pub enum TaskState { 57 | /// This task is currently running on a CPU. 58 | Running, 59 | 60 | /// This task is not currently running on a CPU but is ready and willing to, 61 | /// but instead is waiting to be scheduled for execution; this may be 62 | /// because there are more tasks asking for execution than there are 63 | /// processors available. In this state, the task is within the scheduler's 64 | /// runnable list. 65 | Runnable, 66 | 67 | /// The task is not currently executing on a CPU, nor is it ready to run 68 | /// since it is waiting for an external event to happen, or a condition to 69 | /// be fulfilled; this can be a point in time (e.g. a `sleep()`), the 70 | /// availability of a lock (sleeping for a mutex), an I/O operation, etc. 71 | /// It is the responsibility of the kernel to make sure the task's state is 72 | /// updated to `Runnable` whenever the event occurs by subscribing to it. 73 | Waiting, 74 | 75 | /// The task has been suspended, it is not running and will not run until 76 | /// it is resumed. 77 | Suspended, 78 | 79 | /// The task completed execution and died, but still exist as long as its 80 | /// parent has not read the completion status. 81 | Zombie, 82 | } 83 | -------------------------------------------------------------------------------- /src/task/vm.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | #[allow(unused)] 12 | pub struct VirtualMemory { 13 | //areas: Vec, 14 | } 15 | 16 | #[allow(unused)] 17 | pub struct VMArea { 18 | addr: usize, 19 | size: usize, 20 | 21 | enabled: bool, 22 | writable: bool, 23 | executable: bool, 24 | } 25 | -------------------------------------------------------------------------------- /src/ui/keymap.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use alloc::string::ToString; 12 | use binrw::io::Cursor; 13 | use binrw::{BinRead, NullString}; 14 | use hashbrown::HashMap; 15 | 16 | use crate::driver::keyboard::{Deadkey, Key}; 17 | 18 | pub struct KeymapState { 19 | keymap: Keymap, 20 | deadkey: Option, 21 | } 22 | 23 | pub struct Keymap { 24 | map: HashMap, 25 | } 26 | 27 | #[derive(Debug)] 28 | pub enum KeymapError { 29 | InvalidKeymapFile, 30 | } 31 | 32 | impl KeymapState { 33 | pub fn new(keymap: Keymap) -> Self { 34 | Self { 35 | keymap, 36 | deadkey: None, 37 | } 38 | } 39 | 40 | pub fn glyph( 41 | &mut self, 42 | key: Key, 43 | altgr: bool, 44 | capslock: bool, 45 | shift: bool, 46 | ) -> Option { 47 | let c = self.keymap.glyph(key, altgr, capslock, shift)?; 48 | 49 | if let Some(deadkey) = >::try_into(c).ok() { 50 | if let Some(ref curr_deadkey) = self.deadkey { 51 | let c = if *curr_deadkey == deadkey { 52 | deadkey.as_standalone() 53 | } else { 54 | None 55 | }; 56 | 57 | self.deadkey = None; 58 | c 59 | } else { 60 | self.deadkey = Some(deadkey); 61 | None 62 | } 63 | } else { 64 | if let Some(ref deadkey) = self.deadkey { 65 | let c = deadkey.apply(c); 66 | self.deadkey = None; 67 | c 68 | } else { 69 | Some(c) 70 | } 71 | } 72 | } 73 | } 74 | 75 | impl Keymap { 76 | pub fn from_file(data: &[u8]) -> Result { 77 | let mut reader = Cursor::new(data); 78 | let header = FileHeader::read(&mut reader) 79 | .map_err(|_| KeymapError::InvalidKeymapFile)?; 80 | 81 | let mut map = HashMap::new(); 82 | 83 | for _ in 0..header.nr_mapping { 84 | let mapping = KeyMapping::read(&mut reader) 85 | .map_err(|_| KeymapError::InvalidKeymapFile)?; 86 | let key: Key = mapping 87 | .key_name 88 | .to_string() 89 | .parse() 90 | .map_err(|_| KeymapError::InvalidKeymapFile)?; 91 | map.insert( 92 | key, 93 | CharMatrix([ 94 | mapping.matrix[0].try_into().ok(), 95 | mapping.matrix[1].try_into().ok(), 96 | mapping.matrix[2].try_into().ok(), 97 | mapping.matrix[3].try_into().ok(), 98 | mapping.matrix[4].try_into().ok(), 99 | mapping.matrix[5].try_into().ok(), 100 | mapping.matrix[6].try_into().ok(), 101 | mapping.matrix[7].try_into().ok(), 102 | ]), 103 | ); 104 | } 105 | 106 | Ok(Self { map }) 107 | } 108 | 109 | pub fn glyph( 110 | &self, 111 | key: Key, 112 | altgr: bool, 113 | capslock: bool, 114 | shift: bool, 115 | ) -> Option { 116 | self.map 117 | .get(&key) 118 | .and_then(|matrix| matrix.get(altgr, capslock, shift)) 119 | } 120 | } 121 | 122 | struct CharMatrix([Option; 8]); 123 | 124 | #[derive(BinRead, Debug)] 125 | #[br(little, magic = b"KEYMAP")] 126 | struct FileHeader { 127 | nr_mapping: u32, 128 | } 129 | 130 | #[derive(BinRead, Debug)] 131 | #[br(little)] 132 | struct KeyMapping { 133 | key_name: NullString, 134 | matrix: [u32; 8], 135 | } 136 | 137 | impl CharMatrix { 138 | #[inline] 139 | pub fn get( 140 | &self, 141 | altgr: bool, 142 | capslock: bool, 143 | shift: bool, 144 | ) -> Option { 145 | let index = if shift { 1 } else { 0 } << 0 146 | | if capslock { 1 } else { 0 } << 1 147 | | if altgr { 1 } else { 0 } << 2; 148 | self.0[index] 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/ui/kterm.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use core::fmt::{Arguments, Write}; 12 | 13 | use crate::arch::VesaFramebuffer; 14 | use crate::logging::{Logger, Severity}; 15 | use crate::sync::Spinlock; 16 | use crate::ui::term::Terminal; 17 | 18 | pub static KERNEL_TERMINAL: Spinlock>> = 19 | Spinlock::new(None); 20 | 21 | pub struct TerminalLogger { 22 | serial: &'static mut (dyn Logger + Send), 23 | } 24 | 25 | impl TerminalLogger { 26 | pub fn new(serial: &'static mut (dyn Logger + Send)) -> Self { 27 | Self { serial } 28 | } 29 | } 30 | 31 | impl Logger for TerminalLogger { 32 | fn log(&mut self, severity: Severity, args: Arguments) { 33 | self.serial.log(severity, args.clone()); 34 | 35 | let (color, severity_str) = match severity { 36 | Severity::Debug => ("\x1b", "debug"), 37 | Severity::Info => ("\x1b", "info"), 38 | Severity::Notice => ("\x1b", "notice"), 39 | Severity::Warning => ("\x1b", "warning"), 40 | Severity::Error => ("\x1b", "error"), 41 | Severity::Critical => ("\x1b", "critic."), 42 | Severity::Alert => ("\x1b", "ALERT"), 43 | Severity::Emergency => ("\x1b", "EMERG."), 44 | }; 45 | let mut kterm = KERNEL_TERMINAL.lock(); 46 | if let Some(ref mut kterm) = *kterm { 47 | write!(kterm, "{}{:>7}: ", color, severity_str).unwrap(); 48 | kterm.write_fmt(args).unwrap(); 49 | write!(kterm, "\x1b\n").unwrap(); 50 | } 51 | } 52 | } 53 | 54 | pub fn _print(args: Arguments) { 55 | let mut kterm = KERNEL_TERMINAL.lock(); 56 | if let Some(ref mut kterm) = *kterm { 57 | let _ = kterm.write_fmt(args); 58 | } 59 | } 60 | 61 | pub fn _fprint(args: Arguments) { 62 | let mut kterm = KERNEL_TERMINAL.lock(); 63 | if let Some(ref mut kterm) = *kterm { 64 | let _ = kterm.write_fmt(args); 65 | } 66 | } 67 | 68 | #[macro_export] 69 | macro_rules! print { 70 | ($($arg:tt)*) => { 71 | $crate::ui::kterm::_print(format_args!($($arg)*)) 72 | }; 73 | } 74 | 75 | #[macro_export] 76 | macro_rules! println { 77 | () => { $crate::print!("\n") }; 78 | ($($arg:tt)*) => { 79 | $crate::print!("{}\n", format_args!($($arg)*)) 80 | }; 81 | } 82 | 83 | #[macro_export] 84 | macro_rules! fprint { 85 | ($($arg:tt)*) => { 86 | $crate::ui::kterm::_fprint(format_args!($($arg)*)) 87 | }; 88 | } 89 | 90 | #[macro_export] 91 | macro_rules! fprintln { 92 | () => { $crate::print!("\n") }; 93 | ($($arg:tt)*) => { 94 | $crate::fprint!("{}\n", format_args!($($arg)*)) 95 | }; 96 | } 97 | 98 | #[macro_export] 99 | macro_rules! dbg { 100 | // NOTE: We cannot use `concat!` to make a static string as a format argument 101 | // of `eprintln!` because `file!` could contain a `{` or 102 | // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!` 103 | // will be malformed. 104 | () => { 105 | $crate::debug!("[{}:{}]", core::file!(), core::line!()) 106 | }; 107 | ($val:expr $(,)?) => { 108 | // Use of `match` here is intentional because it affects the lifetimes 109 | // of temporaries - https://stackoverflow.com/a/48732525/1063961 110 | match $val { 111 | tmp => { 112 | $crate::debug!("[{}:{}] {} = {:#?}", 113 | core::file!(), core::line!(), core::stringify!($val), &tmp); 114 | tmp 115 | } 116 | } 117 | }; 118 | ($($val:expr),+ $(,)?) => { 119 | ($($crate::dbg!($val)),+,) 120 | }; 121 | } 122 | -------------------------------------------------------------------------------- /src/ui/mod.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | pub mod keymap; 12 | pub mod kterm; 13 | pub mod pxfont; 14 | pub mod term; 15 | -------------------------------------------------------------------------------- /src/ui/pxfont.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use alloc::vec::Vec; 12 | use binrw::BinRead; 13 | use binrw::io::{Cursor, Seek, SeekFrom}; 14 | use core::char::REPLACEMENT_CHARACTER; 15 | use core::convert::TryInto; 16 | use hashbrown::HashMap; 17 | use thiserror_no_std::Error; 18 | 19 | pub struct PxFont { 20 | chars: HashMap, 21 | glyph_width: u8, 22 | glyph_height: u8, 23 | } 24 | 25 | pub struct Glyph { 26 | px: Vec, 27 | nr_cols: u8, 28 | is_rgba: bool, 29 | } 30 | 31 | #[derive(Error, Debug)] 32 | pub enum PxFontError { 33 | #[error("invalid PXFONT file header: {0}")] 34 | InvalidHeader(#[source] binrw::error::Error), 35 | 36 | #[error("invalid glyph block header: {0}")] 37 | InvalidGlyphBlockHeader(#[source] binrw::error::Error), 38 | 39 | #[error("invalid point code range {0}..={1} in glyph block")] 40 | InvalidGlyphBlockRange(u32, u32), 41 | 42 | #[error("invalid glyph {0:?}")] 43 | InvalidGlyph(char), 44 | 45 | #[error("the replacement glyph '�' is missing")] 46 | MissingReplacementGlyph, 47 | } 48 | 49 | #[derive(BinRead, Debug)] 50 | #[br(little, magic = b"PXFONT")] 51 | struct FileHeader { 52 | width: u8, 53 | height: u8, 54 | } 55 | 56 | #[derive(BinRead, Debug)] 57 | #[br(little)] 58 | struct GlyphBlock { 59 | start: u32, 60 | end: u32, 61 | rgba: u8, 62 | } 63 | 64 | impl PxFont { 65 | pub fn from_data(data: &[u8]) -> Result { 66 | let mut reader = Cursor::new(data); 67 | let header = FileHeader::read(&mut reader) 68 | .map_err(|e| PxFontError::InvalidHeader(e))?; 69 | let mut chars = HashMap::new(); 70 | 71 | loop { 72 | let block = GlyphBlock::read(&mut reader) 73 | .map_err(|e| PxFontError::InvalidGlyphBlockHeader(e))?; 74 | let is_rgba = block.rgba > 0; 75 | let (start, end) = 76 | match (block.start.try_into(), block.end.try_into()) { 77 | (Ok(start), Ok(end)) => (start, end), 78 | _ => { 79 | return Err(PxFontError::InvalidGlyphBlockRange( 80 | block.start, 81 | block.end, 82 | )); 83 | } 84 | }; 85 | 86 | for c in start..=end { 87 | let mut data = remaining(&reader); 88 | let nr_cols = data[0]; 89 | data = &data[1..]; 90 | 91 | let glyph_size = match is_rgba { 92 | false => { 93 | nr_cols as usize 94 | * header.width as usize 95 | * header.height as usize 96 | } 97 | true => { 98 | nr_cols as usize 99 | * header.width as usize 100 | * header.height as usize 101 | * 4 102 | } 103 | }; 104 | 105 | if data.len() < glyph_size { 106 | return Err(PxFontError::InvalidGlyph(c)); 107 | } 108 | let glyph = Glyph { 109 | px: data[..glyph_size].to_vec(), 110 | nr_cols, 111 | is_rgba, 112 | }; 113 | reader 114 | .seek(SeekFrom::Current(glyph_size as i64 + 1)) 115 | .unwrap(); 116 | chars.insert(c, glyph); 117 | } 118 | 119 | if remaining(&reader).is_empty() { 120 | break; 121 | } 122 | } 123 | 124 | if !chars.contains_key(&REPLACEMENT_CHARACTER) { 125 | return Err(PxFontError::MissingReplacementGlyph); 126 | } 127 | 128 | Ok(Self { 129 | chars, 130 | glyph_width: header.width, 131 | glyph_height: header.height, 132 | }) 133 | } 134 | 135 | #[inline] 136 | pub fn get_glyph(&self, glyph: char) -> Option<&Glyph> { 137 | self.chars.get(&glyph) 138 | } 139 | 140 | #[inline] 141 | pub fn glyph_width(&self) -> u8 { 142 | self.glyph_width 143 | } 144 | 145 | #[inline] 146 | pub fn glyph_height(&self) -> u8 { 147 | self.glyph_height 148 | } 149 | 150 | #[inline] 151 | pub fn replacement_glyph(&self) -> &Glyph { 152 | &self.chars[&REPLACEMENT_CHARACTER] 153 | } 154 | } 155 | 156 | impl Glyph { 157 | #[inline] 158 | pub fn data(&self) -> &[u8] { 159 | &self.px 160 | } 161 | 162 | #[inline] 163 | pub fn is_rgba(&self) -> bool { 164 | self.is_rgba 165 | } 166 | 167 | #[inline] 168 | pub fn nr_columns(&self) -> usize { 169 | self.nr_cols as usize 170 | } 171 | } 172 | 173 | fn remaining<'a>(cursor: &Cursor<&'a [u8]>) -> &'a [u8] { 174 | &cursor.get_ref()[(cursor.position() as usize)..] 175 | } 176 | -------------------------------------------------------------------------------- /targets/x86_64-nucloid.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "rustc-abi": "x86-softfloat", 4 | "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", 5 | "arch": "x86_64", 6 | "target-endian": "little", 7 | "target-pointer-width": "64", 8 | "target-c-int-width": "32", 9 | "os": "none", 10 | "executables": true, 11 | "linker": "x86_64-elf-gcc", 12 | "linker-flavor": "gcc", 13 | "panic-strategy": "abort", 14 | "disable-redzone": true, 15 | "features": "-mmx,-sse,+soft-float" 16 | } 17 | -------------------------------------------------------------------------------- /targets/x86_64.ld: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2021-2023 Kévin Lesénéchal * 3 | * This file is part of the Nucloid operating system. * 4 | * * 5 | * Nucloid is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 2 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | ENTRY(_start) 12 | 13 | VA_BASE = 0xffff800000000000; 14 | PA_BASE = 0x0000000000100000; 15 | 16 | SECTIONS { 17 | . = VA_BASE + PA_BASE; 18 | __kernel_image_start = .; 19 | 20 | __kernel_text_start = .; 21 | .text ALIGN(4K) : AT(ADDR(.text) - VA_BASE) { 22 | KEEP(*(.multiboot2)) 23 | *(.text .text.*) 24 | } 25 | . = ALIGN(4K); 26 | __kernel_text_end = .; 27 | 28 | __kernel_rodata_start = .; 29 | .rodata ALIGN(4K) : AT(ADDR(.rodata) - VA_BASE) { 30 | *(.rodata .rodata.*) 31 | } 32 | . = ALIGN(4K); 33 | 34 | __kernel_eh_frame = .; 35 | .eh_frame ALIGN(4K) : AT(ADDR(.eh_frame) - VA_BASE) { 36 | KEEP(*(.eh_frame .eh_frame.*)) 37 | } 38 | . = ALIGN(4K); 39 | __kernel_eh_frame_end = .; 40 | 41 | __kernel_eh_frame_hdr = .; 42 | .eh_frame_hdr ALIGN(4K) : AT(ADDR(.eh_frame_hdr) - VA_BASE) { 43 | *(.eh_frame_hdr .eh_frame_hdr.*) 44 | } 45 | . = ALIGN(4K); 46 | __kernel_eh_frame_hdr_end = .; 47 | 48 | __kernel_rodata_end = .; 49 | 50 | __kernel_data_start = .; 51 | .data ALIGN(4K) : AT(ADDR(.data) - VA_BASE) { 52 | *(.data .data.*) 53 | } 54 | 55 | .bss ALIGN(4K) (NOLOAD) : AT(ADDR(.bss) - VA_BASE) { 56 | . = ALIGN(4K); 57 | *(.boot_page_tables) 58 | . = ALIGN(4K); 59 | *(.boot_stack) 60 | *(.bss .bss.*) 61 | } 62 | . = ALIGN(4K); 63 | __kernel_data_end = .; 64 | 65 | __kernel_image_end = .; 66 | __kernel_image_size = __kernel_image_end - __kernel_image_start; 67 | } 68 | --------------------------------------------------------------------------------