├── .cargo └── config.toml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── canicula-common ├── .cargo │ └── config.toml ├── Cargo.toml └── src │ ├── bootloader.rs │ ├── bootloader │ └── x86.rs │ ├── entry.rs │ ├── fs.rs │ └── libs.rs ├── canicula-ext4 ├── .cargo │ └── config.toml ├── Cargo.toml └── src │ ├── ext4.rs │ ├── tests.rs │ ├── types.rs │ └── types │ ├── data_block.rs │ ├── data_block_bitmap.rs │ ├── group_descriptors.rs │ ├── inode_bitmap.rs │ ├── inode_table.rs │ └── super_block.rs ├── canicula-kernel ├── Cargo.toml ├── aarch64-unknown-none.json ├── riscv64gc-unknown-none-elf.json ├── src │ ├── arch │ │ ├── aarch64 │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── riscv64 │ │ │ ├── console.rs │ │ │ ├── entry.asm │ │ │ ├── logging.rs │ │ │ ├── mod.rs │ │ │ ├── panic.rs │ │ │ ├── qemu.rs │ │ │ └── sbi.rs │ │ └── x86 │ │ │ ├── acpi │ │ │ ├── handler.rs │ │ │ └── mod.rs │ │ │ ├── apic.rs │ │ │ ├── bga.rs │ │ │ ├── console.rs │ │ │ ├── gdt.rs │ │ │ ├── interrupts.rs │ │ │ ├── logging.rs │ │ │ ├── memory │ │ │ ├── heap_allocator.rs │ │ │ ├── mod.rs │ │ │ └── page_allocator.rs │ │ │ ├── mod.rs │ │ │ ├── pcie.rs │ │ │ ├── process.rs │ │ │ ├── qemu.rs │ │ │ ├── serial.rs │ │ │ └── virtualization │ │ │ ├── mod.rs │ │ │ ├── svm.rs │ │ │ ├── vmcb.rs │ │ │ ├── vmcs.rs │ │ │ └── vmx.rs │ ├── linker │ │ ├── aarch64-linker.ld │ │ ├── riscv64-linker.ld │ │ └── x86-linker.ld │ ├── main.rs │ ├── resources.rs │ └── types │ │ ├── elf.rs │ │ └── mod.rs └── x86_64-unknown-none.json ├── canicula-libs ├── Cargo.toml └── src │ ├── config.rs │ ├── config │ ├── aarch64.rs │ ├── riscv64.rs │ └── x86_64.rs │ └── libs.rs ├── docs ├── architecture.md ├── bootloader.md ├── bootloader.old.md ├── dev-environment.md └── ext4.md ├── resources └── images │ └── logo.png └── rust-toolchain.toml /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std = ["core", "compiler_builtins", "alloc"] 3 | 4 | [target.x86_64-unknown-none] 5 | rustflags = [ 6 | "-Clink-arg=-Tcanicula-kernel/src/linker/x86-linker.ld", 7 | ] 8 | 9 | [target.riscv64gc-unknown-none-elf] 10 | rustflags = [ 11 | "-Clink-arg=-Tcanicula-kernel/src/linker/riscv64-linker.ld", 12 | "-Cforce-frame-pointers=yes", 13 | ] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Does not come with compiled products. 2 | /target 3 | 4 | # EFI files 5 | /esp 6 | 7 | # This file is the rustsbi startup image of qemu and needs to be downloaded by the user. 8 | rustsbi-qemu.bin 9 | 10 | # GDB Files 11 | .gdb_history 12 | .gdbinit 13 | 14 | .tasks -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bootloader"] 2 | path = bootloader 3 | url = git@github.com:rust-osdev/bootloader.git 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "x86_64-unknown-none", 3 | "rust-analyzer.checkOnSave.allTargets": false, 4 | "rust-analyzer.checkOnSave.target": "x86_64-unknown-none", 5 | "rust-analyzer.linkedProjects": [ 6 | "./Cargo.toml", 7 | "./canicula-common/Cargo.toml", 8 | "./canicula-ext4/Cargo.toml", 9 | "./canicula-kernel/Cargo.toml", 10 | "./canicula-libs/Cargo.toml", 11 | ], 12 | "cSpell.words": [ 13 | "rustc", 14 | "rustup", 15 | "builtins", 16 | "rustflags", 17 | "Tkernel", 18 | "Cforce", 19 | "riscv", 20 | "aarch", 21 | "riscv64gc-unknown-none-elf", 22 | "cafs", 23 | "libc", 24 | "bootloader", 25 | "uefi", 26 | "repr", 27 | "proto", 28 | "e_phoff", 29 | "e_shoff", 30 | "e_ehsize", 31 | "e_phentsize", 32 | "e_phnum", 33 | "e_shentsize", 34 | "e_shnum", 35 | "e_shstrndx", 36 | "p_vaddr", 37 | "p_paddr", 38 | "p_filesz", 39 | "p_memsz", 40 | "sh_addralign", 41 | "sh_entsize", 42 | "inodes", 43 | "wtime", 44 | "lastcheck", 45 | "checkinterval", 46 | "resuid", 47 | "resgid", 48 | "incompat", 49 | "prealloc", 50 | "inum", 51 | "kbytes", 52 | "uninit", 53 | ], 54 | "rust-analyzer.runnables.extraArgs": [ 55 | "--target=x86_64-unknown-none", 56 | "-Zbuild-std=std,panic_abort", 57 | ], 58 | } -------------------------------------------------------------------------------- /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 = "acpi" 7 | version = "5.2.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "94476c7ef97af4c4d998b3f422c1b01d5211aad57c80ed200baf148d1f1efab6" 10 | dependencies = [ 11 | "bit_field", 12 | "bitflags 2.5.0", 13 | "log", 14 | ] 15 | 16 | [[package]] 17 | name = "adler" 18 | version = "1.0.2" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 21 | 22 | [[package]] 23 | name = "aml" 24 | version = "0.16.4" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "c4f8cba7d4260ea05671dda81029f6f718b54402a4ec926a0d9a41bdbb96b415" 27 | dependencies = [ 28 | "bit_field", 29 | "bitvec", 30 | "byteorder", 31 | "log", 32 | "spinning_top", 33 | ] 34 | 35 | [[package]] 36 | name = "arrayvec" 37 | version = "0.7.6" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 40 | 41 | [[package]] 42 | name = "atomic-polyfill" 43 | version = "1.0.3" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 46 | dependencies = [ 47 | "critical-section", 48 | ] 49 | 50 | [[package]] 51 | name = "autocfg" 52 | version = "1.4.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 55 | 56 | [[package]] 57 | name = "bit" 58 | version = "0.1.1" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "2b645c5c09a7d4035949cfce1a915785aaad6f17800c35fda8a8c311c491f284" 61 | 62 | [[package]] 63 | name = "bit_field" 64 | version = "0.10.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 67 | 68 | [[package]] 69 | name = "bitflags" 70 | version = "1.3.2" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 73 | 74 | [[package]] 75 | name = "bitflags" 76 | version = "2.5.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 79 | 80 | [[package]] 81 | name = "bitvec" 82 | version = "1.0.1" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" 85 | dependencies = [ 86 | "funty", 87 | "radium", 88 | "tap", 89 | "wyz", 90 | ] 91 | 92 | [[package]] 93 | name = "bootloader-boot-config" 94 | version = "0.11.8" 95 | dependencies = [ 96 | "serde", 97 | ] 98 | 99 | [[package]] 100 | name = "bootloader-x86_64-common" 101 | version = "0.11.8" 102 | dependencies = [ 103 | "bootloader-boot-config", 104 | "bootloader_api 0.11.8", 105 | "conquer-once 0.3.2", 106 | "log", 107 | "noto-sans-mono-bitmap 0.2.0", 108 | "rand", 109 | "rand_hc", 110 | "raw-cpuid", 111 | "spinning_top", 112 | "uart_16550 0.2.19", 113 | "usize_conversions", 114 | "x86_64 0.14.13", 115 | "xmas-elf", 116 | ] 117 | 118 | [[package]] 119 | name = "bootloader-x86_64-uefi" 120 | version = "0.11.8" 121 | dependencies = [ 122 | "bootloader-boot-config", 123 | "bootloader-x86_64-common", 124 | "bootloader_api 0.11.8", 125 | "log", 126 | "serde-json-core", 127 | "uefi 0.20.0", 128 | "x86_64 0.14.13", 129 | ] 130 | 131 | [[package]] 132 | name = "bootloader_api" 133 | version = "0.11.8" 134 | dependencies = [ 135 | "rand", 136 | ] 137 | 138 | [[package]] 139 | name = "bootloader_api" 140 | version = "0.11.9" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "57a08a674787a3711ea0ba3bd730e6ffeb74832dcf6c51caf553528b33b54f73" 143 | 144 | [[package]] 145 | name = "byteorder" 146 | version = "1.5.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 149 | 150 | [[package]] 151 | name = "canicula-common" 152 | version = "0.1.0" 153 | dependencies = [ 154 | "arrayvec", 155 | "noto-sans-mono-bitmap 0.3.1", 156 | "uefi 0.28.0", 157 | ] 158 | 159 | [[package]] 160 | name = "canicula-ext4" 161 | version = "0.1.0" 162 | dependencies = [ 163 | "canicula-common", 164 | ] 165 | 166 | [[package]] 167 | name = "canicula-kernel" 168 | version = "0.1.0" 169 | dependencies = [ 170 | "acpi", 171 | "aml", 172 | "bootloader_api 0.11.9", 173 | "canicula-common", 174 | "conquer-once 0.4.0", 175 | "lazy_static", 176 | "linked_list_allocator", 177 | "log", 178 | "nostd", 179 | "noto-sans-mono-bitmap 0.3.1", 180 | "png-decoder", 181 | "riscv", 182 | "sbi-rt", 183 | "spin 0.10.0", 184 | "uart_16550 0.3.2", 185 | "x2apic", 186 | "x86", 187 | "x86_64 0.15.2", 188 | ] 189 | 190 | [[package]] 191 | name = "canicula-libs" 192 | version = "0.1.0" 193 | 194 | [[package]] 195 | name = "cfg-if" 196 | version = "1.0.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 199 | 200 | [[package]] 201 | name = "conquer-once" 202 | version = "0.3.2" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "7c6d3a9775a69f6d1fe2cc888999b67ed30257d3da4d2af91984e722f2ec918a" 205 | dependencies = [ 206 | "conquer-util", 207 | ] 208 | 209 | [[package]] 210 | name = "conquer-once" 211 | version = "0.4.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "5d008a441c0f269f36ca13712528069a86a3e60dffee1d98b976eb3b0b2160b4" 214 | dependencies = [ 215 | "conquer-util", 216 | ] 217 | 218 | [[package]] 219 | name = "conquer-util" 220 | version = "0.3.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582" 223 | 224 | [[package]] 225 | name = "crc32fast" 226 | version = "1.4.2" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 229 | dependencies = [ 230 | "cfg-if", 231 | ] 232 | 233 | [[package]] 234 | name = "critical-section" 235 | version = "1.2.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 238 | 239 | [[package]] 240 | name = "embedded-hal" 241 | version = "1.0.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 244 | 245 | [[package]] 246 | name = "funty" 247 | version = "2.0.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 250 | 251 | [[package]] 252 | name = "getrandom" 253 | version = "0.2.15" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 256 | dependencies = [ 257 | "cfg-if", 258 | "libc", 259 | "wasi", 260 | ] 261 | 262 | [[package]] 263 | name = "hash32" 264 | version = "0.2.1" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 267 | dependencies = [ 268 | "byteorder", 269 | ] 270 | 271 | [[package]] 272 | name = "heapless" 273 | version = "0.7.17" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" 276 | dependencies = [ 277 | "atomic-polyfill", 278 | "hash32", 279 | "rustc_version", 280 | "spin 0.9.8", 281 | "stable_deref_trait", 282 | ] 283 | 284 | [[package]] 285 | name = "lazy_static" 286 | version = "1.5.0" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 289 | dependencies = [ 290 | "spin 0.9.8", 291 | ] 292 | 293 | [[package]] 294 | name = "libc" 295 | version = "0.2.169" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 298 | 299 | [[package]] 300 | name = "linked_list_allocator" 301 | version = "0.10.5" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" 304 | dependencies = [ 305 | "spinning_top", 306 | ] 307 | 308 | [[package]] 309 | name = "lock_api" 310 | version = "0.4.12" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 313 | dependencies = [ 314 | "autocfg", 315 | "scopeguard", 316 | ] 317 | 318 | [[package]] 319 | name = "log" 320 | version = "0.4.27" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 323 | 324 | [[package]] 325 | name = "memchr" 326 | version = "2.7.4" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 329 | 330 | [[package]] 331 | name = "miniz_oxide" 332 | version = "0.4.4" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 335 | dependencies = [ 336 | "adler", 337 | "autocfg", 338 | ] 339 | 340 | [[package]] 341 | name = "nostd" 342 | version = "0.1.4" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "fa3dd4c68516f7c5a1838eda0de376d4bc3c2aba3893da0ca2cf400bbc18435f" 345 | dependencies = [ 346 | "memchr", 347 | ] 348 | 349 | [[package]] 350 | name = "noto-sans-mono-bitmap" 351 | version = "0.2.0" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "a27daf9557165efe1d09b52f97393bf6283cadb0a76fbe64a1061e15553a994a" 354 | 355 | [[package]] 356 | name = "noto-sans-mono-bitmap" 357 | version = "0.3.1" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "1064d564ae026ae123bf5d607318b42a5b31d70a3c48e6ea7ee44cce4cdb095e" 360 | 361 | [[package]] 362 | name = "num_enum" 363 | version = "0.5.11" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" 366 | dependencies = [ 367 | "num_enum_derive", 368 | ] 369 | 370 | [[package]] 371 | name = "num_enum_derive" 372 | version = "0.5.11" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" 375 | dependencies = [ 376 | "proc-macro2", 377 | "quote", 378 | "syn 1.0.109", 379 | ] 380 | 381 | [[package]] 382 | name = "paste" 383 | version = "1.0.15" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 386 | 387 | [[package]] 388 | name = "png-decoder" 389 | version = "0.1.1" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "3e3e93d4884a2609f2dccbafabd3e25c49e89bb872ab84bfea60feaf17615944" 392 | dependencies = [ 393 | "crc32fast", 394 | "miniz_oxide", 395 | "num_enum", 396 | ] 397 | 398 | [[package]] 399 | name = "ppv-lite86" 400 | version = "0.2.20" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 403 | dependencies = [ 404 | "zerocopy", 405 | ] 406 | 407 | [[package]] 408 | name = "proc-macro2" 409 | version = "1.0.82" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" 412 | dependencies = [ 413 | "unicode-ident", 414 | ] 415 | 416 | [[package]] 417 | name = "ptr_meta" 418 | version = "0.2.0" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" 421 | dependencies = [ 422 | "ptr_meta_derive", 423 | ] 424 | 425 | [[package]] 426 | name = "ptr_meta_derive" 427 | version = "0.2.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" 430 | dependencies = [ 431 | "proc-macro2", 432 | "quote", 433 | "syn 1.0.109", 434 | ] 435 | 436 | [[package]] 437 | name = "quote" 438 | version = "1.0.36" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 441 | dependencies = [ 442 | "proc-macro2", 443 | ] 444 | 445 | [[package]] 446 | name = "radium" 447 | version = "0.7.0" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 450 | 451 | [[package]] 452 | name = "rand" 453 | version = "0.8.5" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 456 | dependencies = [ 457 | "libc", 458 | "rand_chacha", 459 | "rand_core", 460 | ] 461 | 462 | [[package]] 463 | name = "rand_chacha" 464 | version = "0.3.1" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 467 | dependencies = [ 468 | "ppv-lite86", 469 | "rand_core", 470 | ] 471 | 472 | [[package]] 473 | name = "rand_core" 474 | version = "0.6.4" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 477 | dependencies = [ 478 | "getrandom", 479 | ] 480 | 481 | [[package]] 482 | name = "rand_hc" 483 | version = "0.3.2" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "7b363d4f6370f88d62bf586c80405657bde0f0e1b8945d47d2ad59b906cb4f54" 486 | dependencies = [ 487 | "rand_core", 488 | ] 489 | 490 | [[package]] 491 | name = "raw-cpuid" 492 | version = "10.7.0" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" 495 | dependencies = [ 496 | "bitflags 1.3.2", 497 | ] 498 | 499 | [[package]] 500 | name = "riscv" 501 | version = "0.13.0" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "afa3cdbeccae4359f6839a00e8b77e5736caa200ba216caf38d24e4c16e2b586" 504 | dependencies = [ 505 | "critical-section", 506 | "embedded-hal", 507 | "paste", 508 | "riscv-macros", 509 | "riscv-pac", 510 | ] 511 | 512 | [[package]] 513 | name = "riscv-macros" 514 | version = "0.2.0" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc" 517 | dependencies = [ 518 | "proc-macro2", 519 | "quote", 520 | "syn 2.0.64", 521 | ] 522 | 523 | [[package]] 524 | name = "riscv-pac" 525 | version = "0.2.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" 528 | 529 | [[package]] 530 | name = "rustc_version" 531 | version = "0.4.1" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 534 | dependencies = [ 535 | "semver", 536 | ] 537 | 538 | [[package]] 539 | name = "rustversion" 540 | version = "1.0.19" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 543 | 544 | [[package]] 545 | name = "ryu" 546 | version = "1.0.18" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 549 | 550 | [[package]] 551 | name = "sbi-rt" 552 | version = "0.0.3" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "7fbaa69be1eedc61c426e6d489b2260482e928b465360576900d52d496a58bd0" 555 | dependencies = [ 556 | "sbi-spec", 557 | ] 558 | 559 | [[package]] 560 | name = "sbi-spec" 561 | version = "0.0.7" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890" 564 | 565 | [[package]] 566 | name = "scopeguard" 567 | version = "1.2.0" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 570 | 571 | [[package]] 572 | name = "semver" 573 | version = "1.0.25" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" 576 | 577 | [[package]] 578 | name = "serde" 579 | version = "1.0.210" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 582 | dependencies = [ 583 | "serde_derive", 584 | ] 585 | 586 | [[package]] 587 | name = "serde-json-core" 588 | version = "0.5.1" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "3c9e1ab533c0bc414c34920ec7e5f097101d126ed5eac1a1aac711222e0bbb33" 591 | dependencies = [ 592 | "heapless", 593 | "ryu", 594 | "serde", 595 | ] 596 | 597 | [[package]] 598 | name = "serde_derive" 599 | version = "1.0.210" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 602 | dependencies = [ 603 | "proc-macro2", 604 | "quote", 605 | "syn 2.0.64", 606 | ] 607 | 608 | [[package]] 609 | name = "spin" 610 | version = "0.9.8" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 613 | dependencies = [ 614 | "lock_api", 615 | ] 616 | 617 | [[package]] 618 | name = "spin" 619 | version = "0.10.0" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" 622 | dependencies = [ 623 | "lock_api", 624 | ] 625 | 626 | [[package]] 627 | name = "spinning_top" 628 | version = "0.2.5" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" 631 | dependencies = [ 632 | "lock_api", 633 | ] 634 | 635 | [[package]] 636 | name = "stable_deref_trait" 637 | version = "1.2.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 640 | 641 | [[package]] 642 | name = "syn" 643 | version = "1.0.109" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 646 | dependencies = [ 647 | "proc-macro2", 648 | "quote", 649 | "unicode-ident", 650 | ] 651 | 652 | [[package]] 653 | name = "syn" 654 | version = "2.0.64" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" 657 | dependencies = [ 658 | "proc-macro2", 659 | "quote", 660 | "unicode-ident", 661 | ] 662 | 663 | [[package]] 664 | name = "tap" 665 | version = "1.0.1" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 668 | 669 | [[package]] 670 | name = "uart_16550" 671 | version = "0.2.19" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "614ff2a87880d4bd4374722268598a970bbad05ced8bf630439417347254ab2e" 674 | dependencies = [ 675 | "bitflags 1.3.2", 676 | "rustversion", 677 | "x86_64 0.14.13", 678 | ] 679 | 680 | [[package]] 681 | name = "uart_16550" 682 | version = "0.3.2" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "e492212ac378a5e00da953718dafb1340d9fbaf4f27d6f3c5cab03d931d1c049" 685 | dependencies = [ 686 | "bitflags 2.5.0", 687 | "rustversion", 688 | "x86", 689 | ] 690 | 691 | [[package]] 692 | name = "ucs2" 693 | version = "0.3.3" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "df79298e11f316400c57ec268f3c2c29ac3c4d4777687955cd3d4f3a35ce7eba" 696 | dependencies = [ 697 | "bit_field", 698 | ] 699 | 700 | [[package]] 701 | name = "uefi" 702 | version = "0.20.0" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "ab39d5e7740f21ed4c46d6659f31038bbe3fe7a8be1f702d8a984348837c43b1" 705 | dependencies = [ 706 | "bitflags 1.3.2", 707 | "log", 708 | "ptr_meta", 709 | "ucs2", 710 | "uefi-macros 0.11.0", 711 | ] 712 | 713 | [[package]] 714 | name = "uefi" 715 | version = "0.28.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "a9c0a56dc9fed2589aad6ddca11c2584968fc21f227b5d7083bb8961d26a69fa" 718 | dependencies = [ 719 | "bitflags 2.5.0", 720 | "cfg-if", 721 | "log", 722 | "ptr_meta", 723 | "ucs2", 724 | "uefi-macros 0.13.0", 725 | "uefi-raw", 726 | "uguid", 727 | ] 728 | 729 | [[package]] 730 | name = "uefi-macros" 731 | version = "0.11.0" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "e0caeb0e7b31b9f1f347e541106be10aa8c66c76fa722a3298a4cd21433fabd4" 734 | dependencies = [ 735 | "proc-macro2", 736 | "quote", 737 | "syn 1.0.109", 738 | ] 739 | 740 | [[package]] 741 | name = "uefi-macros" 742 | version = "0.13.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "26a7b1c2c808c3db854a54d5215e3f7e7aaf5dcfbce095598cba6af29895695d" 745 | dependencies = [ 746 | "proc-macro2", 747 | "quote", 748 | "syn 2.0.64", 749 | ] 750 | 751 | [[package]] 752 | name = "uefi-raw" 753 | version = "0.5.2" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "efa8716f52e8cab8bcedfd5052388a0f263b69fe5cc2561548dc6a530678333c" 756 | dependencies = [ 757 | "bitflags 2.5.0", 758 | "ptr_meta", 759 | "uguid", 760 | ] 761 | 762 | [[package]] 763 | name = "uguid" 764 | version = "2.2.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533" 767 | 768 | [[package]] 769 | name = "unicode-ident" 770 | version = "1.0.12" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 773 | 774 | [[package]] 775 | name = "usize_conversions" 776 | version = "0.2.0" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" 779 | 780 | [[package]] 781 | name = "volatile" 782 | version = "0.4.6" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" 785 | 786 | [[package]] 787 | name = "wasi" 788 | version = "0.11.0+wasi-snapshot-preview1" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 791 | 792 | [[package]] 793 | name = "wyz" 794 | version = "0.5.1" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" 797 | dependencies = [ 798 | "tap", 799 | ] 800 | 801 | [[package]] 802 | name = "x2apic" 803 | version = "0.5.0" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "db5cbcb7faedfa15f90376004ffc0cb42e427623ab56629f0073d275ee8e7043" 806 | dependencies = [ 807 | "bit", 808 | "bitflags 1.3.2", 809 | "paste", 810 | "raw-cpuid", 811 | "x86_64 0.15.2", 812 | ] 813 | 814 | [[package]] 815 | name = "x86" 816 | version = "0.52.0" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" 819 | dependencies = [ 820 | "bit_field", 821 | "bitflags 1.3.2", 822 | "raw-cpuid", 823 | ] 824 | 825 | [[package]] 826 | name = "x86_64" 827 | version = "0.14.13" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491" 830 | dependencies = [ 831 | "bit_field", 832 | "bitflags 2.5.0", 833 | "rustversion", 834 | "volatile", 835 | ] 836 | 837 | [[package]] 838 | name = "x86_64" 839 | version = "0.15.2" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" 842 | dependencies = [ 843 | "bit_field", 844 | "bitflags 2.5.0", 845 | "rustversion", 846 | "volatile", 847 | ] 848 | 849 | [[package]] 850 | name = "xmas-elf" 851 | version = "0.8.0" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "8d29b4d8e7beaceb4e77447ba941a7600d23d0319ab52da0461abea214832d5a" 854 | dependencies = [ 855 | "zero", 856 | ] 857 | 858 | [[package]] 859 | name = "zero" 860 | version = "0.1.3" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784" 863 | 864 | [[package]] 865 | name = "zerocopy" 866 | version = "0.7.35" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 869 | dependencies = [ 870 | "byteorder", 871 | "zerocopy-derive", 872 | ] 873 | 874 | [[package]] 875 | name = "zerocopy-derive" 876 | version = "0.7.35" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 879 | dependencies = [ 880 | "proc-macro2", 881 | "quote", 882 | "syn 2.0.64", 883 | ] 884 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "bootloader/uefi", 5 | "bootloader/api", 6 | "bootloader/common", 7 | "bootloader/common/config", 8 | 9 | "canicula-common", 10 | "canicula-ext4", 11 | "canicula-kernel", 12 | "canicula-libs", 13 | ] 14 | 15 | [workspace.dependencies] 16 | bootloader_api = { version = "0.11.8", path = "bootloader/api" } 17 | bootloader-x86_64-common = { version = "0.11.8", path = "bootloader/common" } 18 | bootloader-boot-config = { version = "0.11.8", path = "bootloader/common/config" } 19 | bootloader-x86_64-bios-common = { version = "0.11.8", path = "bootloader/bios/common" } 20 | 21 | [workspace.package] 22 | version = "0.11.8" 23 | authors = ["hanbings hanbings@hanbings.io"] 24 | repository = "https://github.com/hanbings/canicula" 25 | license = "MIT" 26 | 27 | [profile.release] 28 | debug = true 29 | panic = "unwind" 30 | 31 | [profile.dev] 32 | debug = true 33 | panic = "unwind" 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OS := $(shell uname) 2 | DISTRO := $(shell cat /etc/*release | grep '^ID=' | cut -d '=' -f2) 3 | LOG_LEVEL ?= DEBUG 4 | 5 | OVMF_CODE_PATH := /usr/share/OVMF/OVMF_CODE.fd 6 | OVMF_VARS_PATH := /usr/share/OVMF/OVMF_VARS.fd 7 | 8 | ifeq ($(OS),Linux) 9 | ifeq ($(DISTRO),arch) 10 | OVMF_CODE_PATH := /usr/share/OVMF/x64/OVMF_CODE.4m.fd 11 | OVMF_VARS_PATH := /usr/share/OVMF/x64/OVMF_VARS.4m.fd 12 | endif 13 | 14 | ifeq ($(DISTRO),debian) 15 | OVMF_CODE_PATH := /usr/share/OVMF/OVMF_CODE_4M.fd 16 | OVMF_VARS_PATH := /usr/share/OVMF/OVMF_VARS_4M.fd 17 | endif 18 | endif 19 | 20 | $(info OS=$(OS)) 21 | $(info DISTRO=$(DISTRO)) 22 | $(info OVMF_CODE_PATH=$(OVMF_CODE_PATH)) 23 | $(info OVMF_VARS_PATH=$(OVMF_VARS_PATH)) 24 | 25 | all: efi kernel 26 | 27 | efi: 28 | cd bootloader/uefi && cargo build --target x86_64-unknown-uefi --release -Zbuild-std=core -Zbuild-std-features=compiler-builtins-mem 29 | mkdir -p esp/efi/boot/ 30 | cp bootloader/target/x86_64-unknown-uefi/release/bootloader-x86_64-uefi.efi esp/efi/boot/bootx64.efi 31 | 32 | kernel: 33 | LOG_LEVEL=$(LOG_LEVEL) cargo build --bin canicula-kernel --target x86_64-unknown-none 34 | mkdir -p esp 35 | cp target/x86_64-unknown-none/debug/canicula-kernel esp/kernel-x86_64 36 | 37 | clean: 38 | rm -rf target 39 | rm -rf esp 40 | 41 | clean-esp: 42 | rm -rf esp 43 | 44 | qemu: 45 | qemu-system-x86_64 \ 46 | -m 256M \ 47 | -serial stdio \ 48 | -enable-kvm \ 49 | -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ 50 | -drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE_PATH) \ 51 | -drive if=pflash,format=raw,readonly=on,file=$(OVMF_VARS_PATH) \ 52 | -drive format=raw,file=fat:rw:esp 53 | 54 | kill-qemu: 55 | pgrep qemu | xargs kill -9 56 | 57 | .PHONY: efi kernel clean qemu kill-qemu clean-esp all -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Canicula OS](https://picture.hanbings.com/2024/09/22/f1b8f29c20aba151c2c5e987b2c50ddd.png) 2 | 3 |

⭐ Canicula OS

4 | 5 | ## ⭐ Canicula OS 6 | 7 | 感谢 [xv6-rev7](https://pdos.csail.mit.edu/6.828/2012/xv6/book-rev7.pdf)、[xv6(中文文档)](https://th0ar.gitbooks.io/xv6-chinese/content/)、[rCore](https://rcore-os.cn/rCore-Tutorial-Book-v3/index.html) 和 [os.phil-opp.com](https://os.phil-opp.com/zh-CN/) 这样优秀的教材! 8 | 9 | 那么旅途从这里开始! 10 | 11 | ## 🔨 快速构建 12 | 13 | ```shell 14 | git submodule init 15 | git submodule update 16 | 17 | # 构建 bootloader 和内核 18 | make 19 | # 使用 qemu 运行 20 | make qemu 21 | ``` 22 | 23 | ## 📦 博客 / 文档 24 | 25 | > [!WARNING] 26 | > 本人还并不是很熟悉 Rust 语言并且这份文档只是作为学习操作系统的知识的记录,还会存在很多错误的地方,仅供参考。 27 | > 还请多多指教! 28 | 29 | > [!NOTE] 30 | > Blog 主要为补充性内容,用于补充文档中的前置知识。 31 | > 数字序号部分是主要的文档,用于描述一个内核中应该有的功能。 32 | > Ext 部分补充 “教学” 内核之外的扩展性内容。 33 | 34 | [Blog - Rust:使用 uefi-rs 编写一个 UEFI 应用并加载内核](https://blog.hanbings.io/posts/rust-uefi-bootloader/) 35 | 36 | [0 - 基本开发环境](docs/dev-environment.md) 37 | 38 | [1 - 引导](docs/bootloader.md) 39 | 40 | [2 - 中断与异常处理(WIP)](docs/exceptions-and-interrupts.md) 41 | 42 | [3 - 段、分段、分页与页表(WIP)](docs/paging.md) 43 | 44 | [4 - 内存管理(WIP)](docs/mm.md) 45 | 46 | [5 - 进程调度(WIP)](docs/process.md) 47 | 48 | [6 - 文件系统(WIP)](bdocs/fs.md) 49 | 50 | [7 - 线程、线程通信(WIP)](docs/thread.md) 51 | 52 | [8 - 多核(WIP)](docs/muilt-core.md) 53 | 54 | [9 - 外部接口:USB、网卡与显卡(WIP)](docs/extend-interface.md) 55 | 56 | [10 - 显存映射与图形化(WIP)](docs/graphics.md) 57 | 58 | [Ext - 处理器架构(WIP)](docs/architecture.md) 59 | 60 | [Ext - 模块化设计(WIP)](docs/design.md) 61 | 62 | [Ext - Ext4 文件系统(WIP)](docs/ext4.md) 63 | 64 | **引用名称说明** 65 | 66 | | 手册 | 原始链接 | 文中引用名称 | 67 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------ | 68 | | Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4 (Order Number: 253665-086US December 2024) | https://cdrdv2-public.intel.com/843820/325462-sdm-vol-1-2abcd-3abcd-4-1.pdf | Intel 手册 | 69 | | AMD64 Architecture Programmer’s Manual Volumes 1–5 (Publication No. 40332 Revision 4.08 Date April 2024) | https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/40332.pdf | AMD 手册 | 70 | | Unified Extensible Firmware Interface (UEFI) Specification Release 2.11 (Nov 21, 2024) | https://uefi.org/sites/default/files/resources/UEFI_Spec_Final_2.11.pdf | UEFI Spec | -------------------------------------------------------------------------------- /canicula-common/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std = ["core", "compiler_builtins", "alloc"] 3 | 4 | [build] 5 | target = [ 6 | "x86_64-unknown-none", 7 | "aarch64-unknown-none", 8 | "riscv64gc-unknown-none-elf", 9 | "x86_64-unknown-linux-gnu", 10 | ] 11 | -------------------------------------------------------------------------------- /canicula-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "canicula-common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "canicula_common" 8 | path = "src/libs.rs" 9 | 10 | [dependencies] 11 | noto-sans-mono-bitmap = "0.3.1" 12 | 13 | [target.x86_64-unknown-uefi.dependencies] 14 | uefi = { version = "0.28.0", features = ["logger", "panic_handler"] } 15 | arrayvec = { version = "0.7.2", default-features = false } 16 | 17 | [target.aarch64-unknown-uefi.dependencies] 18 | 19 | [target.x86_64-unknown-none.dependencies] 20 | uefi = { version = "0.28.0", features = ["logger", "panic_handler"] } 21 | arrayvec = { version = "0.7.2", default-features = false } 22 | 23 | [target.aarch64-unknown-none.dependencies] 24 | 25 | [target.riscv64gc-unknown-none-elf.dependencies] -------------------------------------------------------------------------------- /canicula-common/src/bootloader.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86_64")] 2 | pub mod x86; 3 | -------------------------------------------------------------------------------- /canicula-common/src/bootloader/x86.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-common/src/entry.rs: -------------------------------------------------------------------------------- 1 | pub trait KernelEntry { 2 | fn entry() -> !; 3 | } 4 | -------------------------------------------------------------------------------- /canicula-common/src/fs.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum OperateError { 3 | InvalidFileDescriptor, 4 | Fault, 5 | SystemInterrupted, 6 | IO, 7 | DeviceNoFreeSpace, 8 | NotFoundDev, 9 | TimeOut, 10 | } 11 | -------------------------------------------------------------------------------- /canicula-common/src/libs.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | pub mod bootloader; 5 | pub mod entry; 6 | pub mod fs; 7 | -------------------------------------------------------------------------------- /canicula-ext4/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std = ["core", "compiler_builtins", "alloc"] 3 | 4 | [build] 5 | target = [ 6 | "x86_64-unknown-none", 7 | "aarch64-unknown-none", 8 | "riscv64gc-unknown-none-elf", 9 | "x86_64-unknown-linux-gnu", 10 | ] 11 | -------------------------------------------------------------------------------- /canicula-ext4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "canicula-ext4" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "canicula_ext4" 8 | path = "src/ext4.rs" 9 | 10 | [dependencies] 11 | canicula-common = { path = "../canicula-common" } 12 | -------------------------------------------------------------------------------- /canicula-ext4/src/ext4.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | 3 | extern crate alloc; 4 | 5 | use alloc::vec::Vec; 6 | use canicula_common::fs::OperateError; 7 | use core::mem::MaybeUninit; 8 | use types::super_block::SuperBlock; 9 | 10 | mod tests; 11 | mod types; 12 | 13 | const GROUP_ZERO_PADDING: usize = 1024; 14 | 15 | #[allow(unused)] 16 | pub struct Ext4FS { 17 | read_byte: fn(usize) -> Result, 18 | write_byte: fn(u8, usize) -> Result, 19 | super_block: Option, 20 | } 21 | 22 | #[allow(unused)] 23 | impl Ext4FS { 24 | pub fn new( 25 | read_byte: fn(usize) -> Result, 26 | write_byte: fn(u8, usize) -> Result, 27 | ) -> Self { 28 | let mut super_block = MaybeUninit::::uninit(); 29 | let void_super_block_fields = unsafe { 30 | core::slice::from_raw_parts( 31 | &super_block as *const _ as *const u8, 32 | core::mem::size_of::(), 33 | ) 34 | }; 35 | 36 | let mut data_index = 0; 37 | for (i, field) in void_super_block_fields 38 | .chunks(core::mem::size_of::()) 39 | .enumerate() 40 | { 41 | // get the current field length. 42 | let size = field.len(); 43 | let length = size / 8; 44 | let mut contents: Vec = Vec::new(); 45 | 46 | // read data from physical device. 47 | let mut count = 0; 48 | while count < length { 49 | count = count + 1; 50 | let byte = (read_byte)(length + GROUP_ZERO_PADDING); 51 | 52 | match byte { 53 | Ok(byte) => contents.push(byte), 54 | Err(_) => contents.push(0), 55 | } 56 | } 57 | 58 | let ptr = super_block.as_mut_ptr(); 59 | // initialize the value of the structure 60 | // because it is little-endian data, it is read backwards 61 | for content in contents.iter().rev() { 62 | unsafe { 63 | core::ptr::write((ptr as *const u8).offset(data_index) as *mut u8, *content) 64 | }; 65 | data_index = data_index + 1; 66 | } 67 | } 68 | 69 | Ext4FS { 70 | read_byte, 71 | write_byte, 72 | super_block: Some(unsafe { super_block.assume_init() }), 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /canicula-ext4/src/tests.rs: -------------------------------------------------------------------------------- 1 | mod test { 2 | #[test] 3 | fn test() { 4 | use crate::Ext4FS; 5 | use canicula_common::fs::OperateError; 6 | 7 | let read_byte = |_offset: usize| -> Result { 8 | // Implement your read_byte function here 9 | Ok(0) 10 | }; 11 | 12 | let write_byte = |_byte: u8, _offset: usize| -> Result { 13 | // Implement your write_byte function here 14 | Ok(1) 15 | }; 16 | 17 | let _fs: Ext4FS<1024> = Ext4FS::new(read_byte, write_byte); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /canicula-ext4/src/types.rs: -------------------------------------------------------------------------------- 1 | pub mod data_block; 2 | pub mod data_block_bitmap; 3 | pub mod group_descriptors; 4 | pub mod inode_bitmap; 5 | pub mod inode_table; 6 | pub mod super_block; 7 | -------------------------------------------------------------------------------- /canicula-ext4/src/types/data_block.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-ext4/src/types/data_block_bitmap.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-ext4/src/types/group_descriptors.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-ext4/src/types/inode_bitmap.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-ext4/src/types/inode_table.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-ext4/src/types/super_block.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #[derive(Debug, Clone, PartialEq, Eq)] 3 | pub enum SuperBlock { 4 | InodesCount, 5 | BlocksCountLo, 6 | RBlocksCountLo, 7 | FreeBlocksCountLo, 8 | FreeInodesCount, 9 | FirstDataBlock, 10 | LogBlockSize, 11 | LogClusterSize, 12 | BlocksPerGroup, 13 | ClustersPerGroup, 14 | InodesPerGroup, 15 | Mtime, 16 | Wtime, 17 | MntCount, 18 | MaxMntCount, 19 | Magic, 20 | State, 21 | Errors, 22 | MinorRevLevel, 23 | LastCheck, 24 | CheckInterval, 25 | CreatorOs, 26 | RevLevel, 27 | DefResuid, 28 | DefResgid, 29 | FirstIno, 30 | InodeSize, 31 | BlockGroupNr, 32 | FeatureCompat, 33 | FeatureIncompat, 34 | FeatureRoCompat, 35 | Uuid, 36 | VolumeName, 37 | LastMounted, 38 | AlgorithmUsageBitmap, 39 | PreallocBlocks, 40 | PreallocDirBlocks, 41 | ReservedGdtBlocks, 42 | JournalUuid, 43 | JournalInum, 44 | JournalDev, 45 | LastOrphan, 46 | HashSeed, 47 | DefHashVersion, 48 | JnlBackupType, 49 | DescSize, 50 | DefaultMountOpts, 51 | FirstMetaBg, 52 | MkfsTime, 53 | JnlBlocks, 54 | BlocksCountHi, 55 | RBlocksCountHi, 56 | FreeBlocksCountHi, 57 | MinExtraIsize, 58 | WantExtraIsize, 59 | Flags, 60 | RaidStride, 61 | MMPInterval, 62 | MMPBlock, 63 | RaidStripeWidth, 64 | LogGroupsPerFlex, 65 | ChecksumType, 66 | ReservedPad, 67 | KbytesWritten, 68 | SnapshotInum, 69 | SnapshotId, 70 | SnapshotRBlocksCount, 71 | SnapshotList, 72 | ErrorCount, 73 | FirstErrorTime, 74 | FirstErrorIno, 75 | FirstErrorBlock, 76 | FirstErrorFunc, 77 | FirstErrorLine, 78 | LastErrorTime, 79 | LastErrorIno, 80 | LastErrorLine, 81 | LastErrorBlock, 82 | LastErrorFunc, 83 | MountOpts, 84 | UsrQuotaInum, 85 | GrpQuotaInum, 86 | OverheadBlocks, 87 | BackupBgs, 88 | EncryptAlgos, 89 | EncryptPwSalt, 90 | LpfIno, 91 | PrjQuotaInum, 92 | ChecksumSeed, 93 | Reserved, 94 | Checksum, 95 | } 96 | 97 | impl SuperBlock { 98 | pub fn slice(&self) -> SuperBlockSlice { 99 | match self { 100 | SuperBlock::InodesCount => SuperBlockSlice { offset: 0, size: 4 }, 101 | SuperBlock::BlocksCountLo => SuperBlockSlice { offset: 4, size: 4 }, 102 | SuperBlock::RBlocksCountLo => SuperBlockSlice { offset: 8, size: 4 }, 103 | SuperBlock::FreeBlocksCountLo => SuperBlockSlice { 104 | offset: 12, 105 | size: 4, 106 | }, 107 | SuperBlock::FreeInodesCount => SuperBlockSlice { 108 | offset: 16, 109 | size: 4, 110 | }, 111 | SuperBlock::FirstDataBlock => SuperBlockSlice { 112 | offset: 20, 113 | size: 4, 114 | }, 115 | SuperBlock::LogBlockSize => SuperBlockSlice { 116 | offset: 24, 117 | size: 4, 118 | }, 119 | SuperBlock::LogClusterSize => SuperBlockSlice { 120 | offset: 28, 121 | size: 4, 122 | }, 123 | SuperBlock::BlocksPerGroup => SuperBlockSlice { 124 | offset: 32, 125 | size: 4, 126 | }, 127 | SuperBlock::ClustersPerGroup => SuperBlockSlice { 128 | offset: 36, 129 | size: 4, 130 | }, 131 | SuperBlock::InodesPerGroup => SuperBlockSlice { 132 | offset: 40, 133 | size: 4, 134 | }, 135 | SuperBlock::Mtime => SuperBlockSlice { 136 | offset: 44, 137 | size: 4, 138 | }, 139 | SuperBlock::Wtime => SuperBlockSlice { 140 | offset: 48, 141 | size: 4, 142 | }, 143 | SuperBlock::MntCount => SuperBlockSlice { 144 | offset: 52, 145 | size: 2, 146 | }, 147 | SuperBlock::MaxMntCount => SuperBlockSlice { 148 | offset: 54, 149 | size: 2, 150 | }, 151 | SuperBlock::Magic => SuperBlockSlice { 152 | offset: 56, 153 | size: 2, 154 | }, 155 | SuperBlock::State => SuperBlockSlice { 156 | offset: 58, 157 | size: 2, 158 | }, 159 | SuperBlock::Errors => SuperBlockSlice { 160 | offset: 60, 161 | size: 2, 162 | }, 163 | SuperBlock::MinorRevLevel => SuperBlockSlice { 164 | offset: 62, 165 | size: 2, 166 | }, 167 | SuperBlock::LastCheck => SuperBlockSlice { 168 | offset: 64, 169 | size: 4, 170 | }, 171 | SuperBlock::CheckInterval => SuperBlockSlice { 172 | offset: 68, 173 | size: 4, 174 | }, 175 | SuperBlock::CreatorOs => SuperBlockSlice { 176 | offset: 72, 177 | size: 4, 178 | }, 179 | SuperBlock::RevLevel => SuperBlockSlice { 180 | offset: 76, 181 | size: 2, 182 | }, 183 | SuperBlock::DefResuid => SuperBlockSlice { 184 | offset: 78, 185 | size: 2, 186 | }, 187 | SuperBlock::DefResgid => SuperBlockSlice { 188 | offset: 80, 189 | size: 2, 190 | }, 191 | SuperBlock::FirstIno => SuperBlockSlice { 192 | offset: 82, 193 | size: 4, 194 | }, 195 | SuperBlock::InodeSize => SuperBlockSlice { 196 | offset: 86, 197 | size: 2, 198 | }, 199 | SuperBlock::BlockGroupNr => SuperBlockSlice { 200 | offset: 88, 201 | size: 2, 202 | }, 203 | SuperBlock::FeatureCompat => SuperBlockSlice { 204 | offset: 90, 205 | size: 4, 206 | }, 207 | SuperBlock::FeatureIncompat => SuperBlockSlice { 208 | offset: 94, 209 | size: 4, 210 | }, 211 | SuperBlock::FeatureRoCompat => SuperBlockSlice { 212 | offset: 98, 213 | size: 4, 214 | }, 215 | SuperBlock::Uuid => SuperBlockSlice { 216 | offset: 102, 217 | size: 16, 218 | }, 219 | SuperBlock::VolumeName => SuperBlockSlice { 220 | offset: 118, 221 | size: 16, 222 | }, 223 | SuperBlock::LastMounted => SuperBlockSlice { 224 | offset: 134, 225 | size: 4, 226 | }, 227 | SuperBlock::AlgorithmUsageBitmap => SuperBlockSlice { 228 | offset: 138, 229 | size: 4, 230 | }, 231 | SuperBlock::PreallocBlocks => SuperBlockSlice { 232 | offset: 142, 233 | size: 4, 234 | }, 235 | SuperBlock::PreallocDirBlocks => SuperBlockSlice { 236 | offset: 146, 237 | size: 4, 238 | }, 239 | SuperBlock::ReservedGdtBlocks => SuperBlockSlice { 240 | offset: 150, 241 | size: 16, 242 | }, 243 | SuperBlock::JournalUuid => SuperBlockSlice { 244 | offset: 166, 245 | size: 16, 246 | }, 247 | SuperBlock::JournalInum => SuperBlockSlice { 248 | offset: 182, 249 | size: 4, 250 | }, 251 | SuperBlock::JournalDev => SuperBlockSlice { 252 | offset: 186, 253 | size: 4, 254 | }, 255 | SuperBlock::LastOrphan => SuperBlockSlice { 256 | offset: 190, 257 | size: 4, 258 | }, 259 | SuperBlock::HashSeed => SuperBlockSlice { 260 | offset: 194, 261 | size: 8, 262 | }, 263 | SuperBlock::DefHashVersion => SuperBlockSlice { 264 | offset: 202, 265 | size: 4, 266 | }, 267 | SuperBlock::JnlBackupType => SuperBlockSlice { 268 | offset: 206, 269 | size: 4, 270 | }, 271 | SuperBlock::DescSize => SuperBlockSlice { 272 | offset: 210, 273 | size: 4, 274 | }, 275 | SuperBlock::DefaultMountOpts => SuperBlockSlice { 276 | offset: 214, 277 | size: 4, 278 | }, 279 | SuperBlock::FirstMetaBg => SuperBlockSlice { 280 | offset: 218, 281 | size: 4, 282 | }, 283 | SuperBlock::MkfsTime => SuperBlockSlice { 284 | offset: 222, 285 | size: 4, 286 | }, 287 | SuperBlock::JnlBlocks => SuperBlockSlice { 288 | offset: 226, 289 | size: 12, 290 | }, 291 | SuperBlock::RBlocksCountHi => SuperBlockSlice { 292 | offset: 238, 293 | size: 4, 294 | }, 295 | SuperBlock::FreeBlocksCountHi => SuperBlockSlice { 296 | offset: 242, 297 | size: 4, 298 | }, 299 | SuperBlock::MinExtraIsize => SuperBlockSlice { 300 | offset: 246, 301 | size: 4, 302 | }, 303 | SuperBlock::WantExtraIsize => SuperBlockSlice { 304 | offset: 250, 305 | size: 4, 306 | }, 307 | SuperBlock::Flags => SuperBlockSlice { 308 | offset: 254, 309 | size: 4, 310 | }, 311 | SuperBlock::RaidStride => SuperBlockSlice { 312 | offset: 258, 313 | size: 4, 314 | }, 315 | SuperBlock::MMPInterval => SuperBlockSlice { 316 | offset: 262, 317 | size: 4, 318 | }, 319 | SuperBlock::MMPBlock => SuperBlockSlice { 320 | offset: 266, 321 | size: 4, 322 | }, 323 | SuperBlock::RaidStripeWidth => SuperBlockSlice { 324 | offset: 270, 325 | size: 4, 326 | }, 327 | SuperBlock::LogGroupsPerFlex => SuperBlockSlice { 328 | offset: 274, 329 | size: 4, 330 | }, 331 | SuperBlock::ChecksumType => SuperBlockSlice { 332 | offset: 278, 333 | size: 4, 334 | }, 335 | SuperBlock::ReservedPad => SuperBlockSlice { 336 | offset: 282, 337 | size: 6, 338 | }, 339 | SuperBlock::KbytesWritten => SuperBlockSlice { 340 | offset: 288, 341 | size: 8, 342 | }, 343 | SuperBlock::SnapshotInum => SuperBlockSlice { 344 | offset: 296, 345 | size: 4, 346 | }, 347 | SuperBlock::SnapshotId => SuperBlockSlice { 348 | offset: 300, 349 | size: 4, 350 | }, 351 | SuperBlock::SnapshotRBlocksCount => SuperBlockSlice { 352 | offset: 304, 353 | size: 8, 354 | }, 355 | SuperBlock::SnapshotList => SuperBlockSlice { 356 | offset: 312, 357 | size: 8, 358 | }, 359 | SuperBlock::ErrorCount => SuperBlockSlice { 360 | offset: 320, 361 | size: 4, 362 | }, 363 | SuperBlock::FirstErrorTime => SuperBlockSlice { 364 | offset: 324, 365 | size: 4, 366 | }, 367 | SuperBlock::FirstErrorIno => SuperBlockSlice { 368 | offset: 328, 369 | size: 4, 370 | }, 371 | SuperBlock::FirstErrorBlock => SuperBlockSlice { 372 | offset: 332, 373 | size: 4, 374 | }, 375 | SuperBlock::FirstErrorFunc => SuperBlockSlice { 376 | offset: 336, 377 | size: 4, 378 | }, 379 | SuperBlock::FirstErrorLine => SuperBlockSlice { 380 | offset: 340, 381 | size: 4, 382 | }, 383 | SuperBlock::LastErrorTime => SuperBlockSlice { 384 | offset: 344, 385 | size: 8, 386 | }, 387 | SuperBlock::LastErrorIno => SuperBlockSlice { 388 | offset: 352, 389 | size: 4, 390 | }, 391 | SuperBlock::LastErrorLine => SuperBlockSlice { 392 | offset: 364, 393 | size: 4, 394 | }, 395 | SuperBlock::LastErrorBlock => SuperBlockSlice { 396 | offset: 356, 397 | size: 4, 398 | }, 399 | SuperBlock::LastErrorFunc => SuperBlockSlice { 400 | offset: 360, 401 | size: 4, 402 | }, 403 | SuperBlock::MountOpts => SuperBlockSlice { 404 | offset: 368, 405 | size: 64, 406 | }, 407 | SuperBlock::UsrQuotaInum => SuperBlockSlice { 408 | offset: 432, 409 | size: 4, 410 | }, 411 | SuperBlock::GrpQuotaInum => SuperBlockSlice { 412 | offset: 436, 413 | size: 4, 414 | }, 415 | SuperBlock::OverheadBlocks => SuperBlockSlice { 416 | offset: 440, 417 | size: 4, 418 | }, 419 | SuperBlock::BackupBgs => SuperBlockSlice { 420 | offset: 444, 421 | size: 6, 422 | }, 423 | SuperBlock::EncryptAlgos => SuperBlockSlice { 424 | offset: 450, 425 | size: 4, 426 | }, 427 | SuperBlock::EncryptPwSalt => SuperBlockSlice { 428 | offset: 454, 429 | size: 16, 430 | }, 431 | SuperBlock::LpfIno => SuperBlockSlice { 432 | offset: 470, 433 | size: 4, 434 | }, 435 | SuperBlock::PrjQuotaInum => SuperBlockSlice { 436 | offset: 474, 437 | size: 4, 438 | }, 439 | SuperBlock::ChecksumSeed => SuperBlockSlice { 440 | offset: 478, 441 | size: 8, 442 | }, 443 | SuperBlock::Reserved => SuperBlockSlice { 444 | offset: 486, 445 | size: 2, 446 | }, 447 | SuperBlock::Checksum => SuperBlockSlice { 448 | offset: 488, 449 | size: 4, 450 | }, 451 | _ => SuperBlockSlice { offset: 0, size: 0 }, 452 | } 453 | } 454 | 455 | #[allow(dead_code)] 456 | pub fn offset(&self) -> usize { 457 | self.slice().offset 458 | } 459 | 460 | #[allow(dead_code)] 461 | pub fn size(&self) -> usize { 462 | self.slice().size 463 | } 464 | } 465 | 466 | #[derive(Debug, Clone, PartialEq, Eq)] 467 | pub struct SuperBlockSlice { 468 | pub offset: usize, 469 | pub size: usize, 470 | } 471 | 472 | #[derive(Debug, Clone, PartialEq, Eq)] 473 | pub struct SuperBlockSnapshot { 474 | pub s_inodes_count: u32, 475 | pub s_blocks_count_lo: u32, 476 | pub s_r_blocks_count_lo: u32, 477 | pub s_free_blocks_count_lo: u32, 478 | pub s_free_inodes_count: u32, 479 | pub s_first_data_block: u32, 480 | pub s_log_block_size: u32, 481 | pub s_log_cluster_size: u32, 482 | pub s_blocks_per_group: u32, 483 | pub s_clusters_per_group: u32, 484 | pub s_inodes_per_group: u32, 485 | pub s_mtime: u32, 486 | pub s_wtime: u32, 487 | pub s_mnt_count: u16, 488 | pub s_max_mnt_count: u16, 489 | pub s_magic: u16, 490 | pub s_state: u16, 491 | pub s_errors: u16, 492 | pub s_minor_rev_level: u16, 493 | pub s_lastcheck: u32, 494 | pub s_checkinterval: u32, 495 | pub s_creator_os: u32, 496 | pub s_rev_level: u32, 497 | pub s_def_resuid: u16, 498 | pub s_def_resgid: u16, 499 | pub s_first_ino: u32, 500 | pub s_inode_size: u16, 501 | pub s_block_group_nr: u16, 502 | pub s_feature_compat: u32, 503 | pub s_feature_incompat: u32, 504 | pub s_feature_ro_compat: u32, 505 | pub s_uuid: [u8; 16], 506 | pub s_volume_name: [char; 16], 507 | pub s_last_mounted: [char; 64], 508 | pub s_algorithm_usage_bitmap: u32, 509 | pub s_prealloc_blocks: u8, 510 | pub s_prealloc_dir_blocks: u8, 511 | pub s_reserved_gdt_blocks: u16, 512 | pub s_journal_uuid: [u8; 16], 513 | pub s_journal_inum: u32, 514 | pub s_journal_dev: u32, 515 | pub s_last_orphan: u32, 516 | pub s_hash_seed: [u32; 4], 517 | pub s_def_hash_version: u8, 518 | pub s_jnl_backup_type: u8, 519 | pub s_desc_size: u16, 520 | pub s_default_mount_opts: u32, 521 | pub s_first_meta_bg: u32, 522 | pub s_mkfs_time: u32, 523 | pub s_jnl_blocks: [u32; 17], 524 | pub s_blocks_count_hi: u32, 525 | pub s_r_blocks_count_hi: u32, 526 | pub s_free_blocks_count_hi: u32, 527 | pub s_min_extra_isize: u16, 528 | pub s_want_extra_isize: u16, 529 | pub s_flags: u32, 530 | pub s_raid_stride: u16, 531 | pub s_mmp_interval: u16, 532 | pub s_mmp_block: u64, 533 | pub s_raid_stripe_width: u32, 534 | pub s_log_groups_per_flex: u8, 535 | pub s_checksum_type: u8, 536 | pub s_reserved_pad: u16, 537 | pub s_kbytes_written: u64, 538 | pub s_snapshot_inum: u32, 539 | pub s_snapshot_id: u32, 540 | pub s_snapshot_r_blocks_count: u64, 541 | pub s_snapshot_list: u32, 542 | pub s_error_count: u32, 543 | pub s_first_error_time: u32, 544 | pub s_first_error_ino: u32, 545 | pub s_first_error_block: u64, 546 | pub s_first_error_func: [u8; 32], 547 | pub s_first_error_line: u32, 548 | pub s_last_error_time: u32, 549 | pub s_last_error_ino: u32, 550 | pub s_last_error_line: u32, 551 | pub s_last_error_block: u64, 552 | pub s_last_error_func: [u8; 32], 553 | pub s_mount_opts: [u8; 64], 554 | pub s_usr_quota_inum: u32, 555 | pub s_grp_quota_inum: u32, 556 | pub s_overhead_blocks: u32, 557 | pub s_backup_bgs: [u32; 2], 558 | pub s_encrypt_algos: [u8; 4], 559 | pub s_encrypt_pw_salt: [u8; 16], 560 | pub s_lpf_ino: u32, 561 | pub s_prj_quota_inum: u32, 562 | pub s_checksum_seed: u32, 563 | pub s_reserved: [u32; 98], 564 | pub s_checksum: u32, 565 | } 566 | -------------------------------------------------------------------------------- /canicula-kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "canicula-kernel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "canicula-kernel" 8 | path = "src/main.rs" 9 | 10 | [dependencies] 11 | log = "0.4.27" 12 | spin = "0.10.0" 13 | bootloader_api = "0.11.8" 14 | noto-sans-mono-bitmap = { version = "0.3.1", features = [ "font_weights_all", "raster_heights_all"] } 15 | 16 | canicula-common = { path = "../canicula-common" } 17 | 18 | [target.x86_64-unknown-none.dependencies] 19 | aml = "0.16.4" 20 | acpi = "5.2.0" 21 | x86 = "0.52.0" 22 | nostd = "0.1.4" 23 | x2apic = "0.5.0" 24 | x86_64 = "0.15.2" 25 | uart_16550 = "0.3.2" 26 | png-decoder = "0.1.1" 27 | conquer-once = { version = "0.4.0", default-features = false } 28 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 29 | linked_list_allocator = "0.10.5" 30 | 31 | [target.aarch64-unknown-none.dependencies] 32 | 33 | [target.riscv64gc-unknown-none-elf.dependencies] 34 | sbi-rt = { version = "0.0.3", features = ["legacy"] } 35 | riscv = "0.13.0" -------------------------------------------------------------------------------- /canicula-kernel/aarch64-unknown-none.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "aarch64-unknown-none", 3 | "crt-objects-fallback": "false", 4 | "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", 5 | "disable-redzone": true, 6 | "features": "+v8a,+strict-align,+neon,+fp-armv8", 7 | "linker": "rust-lld", 8 | "linker-flavor": "gnu-lld", 9 | "llvm-target": "aarch64-unknown-none", 10 | "max-atomic-width": 128, 11 | "metadata": { 12 | "description": "Bare ARM64, hardfloat", 13 | "host_tools": false, 14 | "std": false, 15 | "tier": 2 16 | }, 17 | "panic-strategy": "abort", 18 | "pre-link-args": { 19 | "gnu": ["--fix-cortex-a53-843419"], 20 | "gnu-lld": ["--fix-cortex-a53-843419"], 21 | "ld.lld": ["-Tcanicula-kernel/src/linker/aarch64-linker.ld", "--strip-all"] 22 | }, 23 | "relocation-model": "static", 24 | "stack-probes": { 25 | "kind": "inline" 26 | }, 27 | "supported-sanitizers": [ 28 | "kcfi", 29 | "kernel-address" 30 | ], 31 | "target-pointer-width": "64" 32 | } 33 | -------------------------------------------------------------------------------- /canicula-kernel/riscv64gc-unknown-none-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "riscv64", 3 | "code-model": "medium", 4 | "cpu": "generic-rv64", 5 | "crt-objects-fallback": "false", 6 | "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", 7 | "eh-frame-header": false, 8 | "emit-debug-gdb-scripts": false, 9 | "features": "+m,+a,+f,+d,+c", 10 | "linker": "rust-lld", 11 | "linker-flavor": "gnu-lld", 12 | "llvm-abiname": "lp64d", 13 | "llvm-target": "riscv64", 14 | "max-atomic-width": 64, 15 | "metadata": { 16 | "description": "Bare RISC-V (RV64IMAFDC ISA)", 17 | "host_tools": false, 18 | "std": false, 19 | "tier": 2 20 | }, 21 | "panic-strategy": "abort", 22 | "relocation-model": "static", 23 | "supported-sanitizers": [ 24 | "shadow-call-stack", 25 | "kernel-address" 26 | ], 27 | "target-pointer-width": "64", 28 | "pre-link-args": { 29 | "ld.lld": ["-Tcanicula-kernel/src/linker/riscv64-linker.ld"] 30 | } 31 | } -------------------------------------------------------------------------------- /canicula-kernel/src/arch/aarch64/mod.rs: -------------------------------------------------------------------------------- 1 | use core::panic::PanicInfo; 2 | 3 | pub fn entry() -> ! { 4 | loop {} 5 | } 6 | 7 | #[panic_handler] 8 | fn panic(_info: &PanicInfo) -> ! { 9 | loop {} 10 | } 11 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "aarch64")] 2 | #[path = "aarch64/mod.rs"] 3 | pub mod aarch; 4 | #[cfg(target_arch = "riscv64")] 5 | #[path = "riscv64/mod.rs"] 6 | pub mod riscv; 7 | #[cfg(target_arch = "x86_64")] 8 | #[path = "x86/mod.rs"] 9 | pub mod x86; 10 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/riscv64/console.rs: -------------------------------------------------------------------------------- 1 | use super::sbi::console_write_byte; 2 | use core::fmt::{self, Write}; 3 | 4 | struct Stdout; 5 | 6 | impl Write for Stdout { 7 | fn write_str(&mut self, s: &str) -> fmt::Result { 8 | for c in s.chars() { 9 | console_write_byte(c as usize); 10 | } 11 | Ok(()) 12 | } 13 | } 14 | 15 | pub fn print(args: fmt::Arguments) { 16 | Stdout.write_fmt(args).unwrap(); 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! print { 21 | ($fmt: literal $(, $($arg: tt)+)?) => { 22 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)) 23 | } 24 | } 25 | 26 | #[macro_export] 27 | macro_rules! println { 28 | ($fmt: literal $(, $($arg: tt)+)?) => { 29 | $crate::arch::riscv::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/riscv64/entry.asm: -------------------------------------------------------------------------------- 1 | .section .text.entry 2 | .globl _start 3 | _start: 4 | la sp, boot_stack_top 5 | call kernel 6 | 7 | .section .bss.stack 8 | .globl boot_stack_lower_bound 9 | boot_stack_lower_bound: 10 | .space 4096 * 16 11 | .globl boot_stack_top 12 | boot_stack_top: -------------------------------------------------------------------------------- /canicula-kernel/src/arch/riscv64/logging.rs: -------------------------------------------------------------------------------- 1 | use crate::println; 2 | use log::{Level, LevelFilter, Log, Metadata, Record}; 3 | 4 | struct SimpleLogger; 5 | 6 | impl Log for SimpleLogger { 7 | fn enabled(&self, _metadata: &Metadata) -> bool { 8 | true 9 | } 10 | fn log(&self, record: &Record) { 11 | if !self.enabled(record.metadata()) { 12 | return; 13 | } 14 | let color = match record.level() { 15 | // Red 16 | Level::Error => 31, 17 | // BrightYellow 18 | Level::Warn => 93, 19 | // Blue 20 | Level::Info => 34, 21 | // Green 22 | Level::Debug => 32, 23 | // BrightBlack 24 | Level::Trace => 90, 25 | }; 26 | println!( 27 | "\u{1B}[{}m[{:>5}] {}\u{1B}[0m", 28 | color, 29 | record.level(), 30 | record.args(), 31 | ); 32 | } 33 | fn flush(&self) {} 34 | } 35 | 36 | pub fn init() { 37 | static LOGGER: SimpleLogger = SimpleLogger; 38 | log::set_logger(&LOGGER).unwrap(); 39 | log::set_max_level(match option_env!("log_level") { 40 | Some("ERROR") => LevelFilter::Error, 41 | Some("WARN") => LevelFilter::Warn, 42 | Some("INFO") => LevelFilter::Info, 43 | Some("DEBUG") => LevelFilter::Debug, 44 | Some("TRACE") => LevelFilter::Trace, 45 | _ => LevelFilter::Off, 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/riscv64/mod.rs: -------------------------------------------------------------------------------- 1 | use core::arch::global_asm; 2 | use log::*; 3 | use qemu::QEMUExit; 4 | use qemu::QEMU_EXIT_HANDLE; 5 | 6 | use crate::println; 7 | 8 | #[macro_use] 9 | mod panic; 10 | mod console; 11 | mod logging; 12 | mod qemu; 13 | mod sbi; 14 | 15 | pub fn clear_bss() { 16 | extern "C" { 17 | fn sbss(); 18 | fn ebss(); 19 | } 20 | (sbss as usize..ebss as usize).for_each(|a| unsafe { (a as *mut u8).write_volatile(0) }); 21 | } 22 | 23 | global_asm!(include_str!("entry.asm")); 24 | 25 | pub fn entry() -> ! { 26 | extern "C" { 27 | // begin addr of text segment 28 | fn stext(); 29 | fn etext(); 30 | // start addr of Read-Only data segment 31 | fn srodata(); 32 | fn erodata(); 33 | // start addr of data segment 34 | fn sdata(); 35 | fn edata(); 36 | // start addr of BSS segment 37 | fn sbss(); 38 | fn ebss(); 39 | // stack lower bound 40 | fn boot_stack_lower_bound(); 41 | fn boot_stack_top(); 42 | } 43 | clear_bss(); 44 | logging::init(); 45 | println!("[kernel] Hello, world!"); 46 | debug!( 47 | "[kernel] .text [{:#x}, {:#x})", 48 | stext as usize, etext as usize 49 | ); 50 | debug!( 51 | "[kernel] .rodata [{:#x}, {:#x})", 52 | srodata as usize, erodata as usize 53 | ); 54 | debug!( 55 | "[kernel] .data [{:#x}, {:#x})", 56 | sdata as usize, edata as usize 57 | ); 58 | debug!( 59 | "[kernel] boot_stack top=bottom={:#x}, lower_bound={:#x}", 60 | boot_stack_top as usize, boot_stack_lower_bound as usize 61 | ); 62 | debug!("[kernel] .bss [{:#x}, {:#x})", sbss as usize, ebss as usize); 63 | 64 | QEMU_EXIT_HANDLE.exit_success(); 65 | } 66 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/riscv64/panic.rs: -------------------------------------------------------------------------------- 1 | use core::panic::PanicInfo; 2 | 3 | use crate::println; 4 | 5 | use super::sbi::shutdown; 6 | 7 | #[panic_handler] 8 | fn panic(info: &PanicInfo) -> ! { 9 | if let Some(location) = info.location() { 10 | println!( 11 | "Panicked at {}:{} {}", 12 | location.file(), 13 | location.line(), 14 | info.message() 15 | ); 16 | } else { 17 | println!("Panicked: {}", info.message()); 18 | } 19 | 20 | shutdown(true); 21 | } 22 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/riscv64/qemu.rs: -------------------------------------------------------------------------------- 1 | //ref:: https://github.com/andre-richter/qemu-exit 2 | use core::arch::asm; 3 | 4 | const VIRT_TEST: u64 = 0x100000; 5 | const EXIT_SUCCESS: u32 = 0x5555; // Equals `exit(0)`. qemu successful exit 6 | const EXIT_FAILURE_FLAG: u32 = 0x3333; 7 | const EXIT_FAILURE: u32 = exit_code_encode(1); // Equals `exit(1)`. qemu failed exit 8 | const EXIT_RESET: u32 = 0x7777; // qemu reset 9 | 10 | pub trait QEMUExit { 11 | /// Exit with specified return code. 12 | /// 13 | /// Note: For `X86`, code is binary-OR'ed with `0x1` inside QEMU. 14 | fn exit(&self, code: u32) -> !; 15 | 16 | /// Exit QEMU using `EXIT_SUCCESS`, aka `0`, if possible. 17 | /// 18 | /// Note: Not possible for `X86`. 19 | fn exit_success(&self) -> !; 20 | 21 | /// Exit QEMU using `EXIT_FAILURE`, aka `1`. 22 | fn exit_failure(&self) -> !; 23 | } 24 | 25 | /// RISCV64 configuration 26 | pub struct RISCV64 { 27 | /// Address of the sifive_test mapped device. 28 | addr: u64, 29 | } 30 | 31 | /// Encode the exit code using EXIT_FAILURE_FLAG. 32 | const fn exit_code_encode(code: u32) -> u32 { 33 | (code << 16) | EXIT_FAILURE_FLAG 34 | } 35 | 36 | impl RISCV64 { 37 | /// Create an instance. 38 | pub const fn new(addr: u64) -> Self { 39 | RISCV64 { addr } 40 | } 41 | } 42 | 43 | impl QEMUExit for RISCV64 { 44 | /// Exit qemu with specified exit code. 45 | fn exit(&self, code: u32) -> ! { 46 | // If code is not a special value, we need to encode it with EXIT_FAILURE_FLAG. 47 | let code_new = match code { 48 | EXIT_SUCCESS | EXIT_FAILURE | EXIT_RESET => code, 49 | _ => exit_code_encode(code), 50 | }; 51 | 52 | unsafe { 53 | asm!( 54 | "sw {0}, 0({1})", 55 | in(reg)code_new, in(reg)self.addr 56 | ); 57 | 58 | // For the case that the QEMU exit attempt did not work, transition into an infinite 59 | // loop. Calling `panic!()` here is unfeasible, since there is a good chance 60 | // this function here is the last expression in the `panic!()` handler 61 | // itself. This prevents a possible infinite loop. 62 | loop { 63 | asm!("wfi", options(nomem, nostack)); 64 | } 65 | } 66 | } 67 | 68 | fn exit_success(&self) -> ! { 69 | self.exit(EXIT_SUCCESS); 70 | } 71 | 72 | fn exit_failure(&self) -> ! { 73 | self.exit(EXIT_FAILURE); 74 | } 75 | } 76 | 77 | pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(VIRT_TEST); 78 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/riscv64/sbi.rs: -------------------------------------------------------------------------------- 1 | use sbi_rt::{system_reset, NoReason, Shutdown, SystemFailure}; 2 | 3 | pub fn console_write_byte(c: usize) { 4 | #[allow(deprecated)] 5 | sbi_rt::legacy::console_putchar(c); 6 | } 7 | 8 | pub fn shutdown(failure: bool) -> ! { 9 | if !failure { 10 | system_reset(Shutdown, NoReason); 11 | } else { 12 | system_reset(Shutdown, SystemFailure); 13 | } 14 | unreachable!() 15 | } 16 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/acpi/handler.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::NonNull; 2 | 3 | use acpi::PhysicalMapping; 4 | use x86_64::{instructions::port::Port, PhysAddr}; 5 | 6 | #[derive(Debug, Clone, Copy)] 7 | pub struct AcpiHandler; 8 | 9 | impl acpi::AcpiHandler for AcpiHandler { 10 | unsafe fn map_physical_region( 11 | &self, 12 | physical_address: usize, 13 | size: usize, 14 | ) -> acpi::PhysicalMapping { 15 | let phys_addr = PhysAddr::new(physical_address as u64); 16 | let virt_addr = crate::arch::x86::memory::physical_to_virtual(phys_addr); 17 | let ptr = NonNull::new(virt_addr.as_mut_ptr()).unwrap(); 18 | PhysicalMapping::new(physical_address, ptr, size, size, Self) 19 | } 20 | 21 | fn unmap_physical_region(_region: &acpi::PhysicalMapping) {} 22 | } 23 | 24 | #[derive(Debug, Clone, Copy)] 25 | pub(crate) struct AmlHandler; 26 | 27 | impl aml::Handler for AmlHandler { 28 | fn read_u8(&self, address: usize) -> u8 { 29 | read_addr::(address) 30 | } 31 | 32 | fn read_u16(&self, address: usize) -> u16 { 33 | read_addr::(address) 34 | } 35 | 36 | fn read_u32(&self, address: usize) -> u32 { 37 | read_addr::(address) 38 | } 39 | 40 | fn read_u64(&self, address: usize) -> u64 { 41 | read_addr::(address) 42 | } 43 | 44 | fn write_u8(&mut self, address: usize, value: u8) { 45 | write_addr::(address, value) 46 | } 47 | 48 | fn write_u16(&mut self, address: usize, value: u16) { 49 | write_addr::(address, value) 50 | } 51 | 52 | fn write_u32(&mut self, address: usize, value: u32) { 53 | write_addr::(address, value) 54 | } 55 | 56 | fn write_u64(&mut self, address: usize, value: u64) { 57 | write_addr::(address, value) 58 | } 59 | 60 | // ==== IO Port Read ==== 61 | fn read_io_u8(&self, port: u16) -> u8 { 62 | unsafe { Port::new(port).read() } 63 | } 64 | 65 | fn read_io_u16(&self, port: u16) -> u16 { 66 | unsafe { Port::new(port).read() } 67 | } 68 | 69 | fn read_io_u32(&self, port: u16) -> u32 { 70 | unsafe { Port::new(port).read() } 71 | } 72 | 73 | fn write_io_u8(&self, port: u16, value: u8) { 74 | unsafe { Port::new(port).write(value) } 75 | } 76 | 77 | fn write_io_u16(&self, port: u16, value: u16) { 78 | unsafe { Port::new(port).write(value) } 79 | } 80 | 81 | fn write_io_u32(&self, port: u16, value: u32) { 82 | unsafe { Port::new(port).write(value) } 83 | } 84 | 85 | fn read_pci_u8(&self, seg: u16, bus: u8, dev: u8, func: u8, offset: u16) -> u8 { 86 | pci_config_read_u32(seg, bus, dev, func, offset) as u8 87 | } 88 | 89 | fn read_pci_u16(&self, seg: u16, bus: u8, dev: u8, func: u8, offset: u16) -> u16 { 90 | pci_config_read_u32(seg, bus, dev, func, offset) as u16 91 | } 92 | 93 | fn read_pci_u32(&self, seg: u16, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { 94 | pci_config_read_u32(seg, bus, dev, func, offset) 95 | } 96 | 97 | fn write_pci_u8(&self, seg: u16, bus: u8, dev: u8, func: u8, offset: u16, value: u8) { 98 | let old = pci_config_read_u32(seg, bus, dev, func, offset); 99 | let shift = ((offset & 3) * 8) as u32; 100 | let mask = !(0xFF << shift); 101 | let new = (old & mask) | ((value as u32) << shift); 102 | pci_config_write_u32(seg, bus, dev, func, offset, new); 103 | } 104 | 105 | fn write_pci_u16(&self, seg: u16, bus: u8, dev: u8, func: u8, offset: u16, value: u16) { 106 | let old = pci_config_read_u32(seg, bus, dev, func, offset); 107 | let shift = ((offset & 2) * 8) as u32; 108 | let mask = !(0xFFFF << shift); 109 | let new = (old & mask) | ((value as u32) << shift); 110 | pci_config_write_u32(seg, bus, dev, func, offset, new); 111 | } 112 | 113 | fn write_pci_u32(&self, seg: u16, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { 114 | pci_config_write_u32(seg, bus, dev, func, offset, value); 115 | } 116 | } 117 | 118 | fn read_addr(addr: usize) -> T { 119 | let virt = unsafe { crate::arch::x86::memory::physical_to_virtual(PhysAddr::new(addr as u64)) }; 120 | unsafe { *virt.as_ptr::() } 121 | } 122 | 123 | fn write_addr(addr: usize, value: T) { 124 | let virt = unsafe { crate::arch::x86::memory::physical_to_virtual(PhysAddr::new(addr as u64)) }; 125 | unsafe { *virt.as_mut_ptr::() = value }; 126 | } 127 | 128 | fn pci_config_address(bus: u8, dev: u8, func: u8, offset: u16) -> u32 { 129 | (1 << 31) 130 | | ((bus as u32) << 16) 131 | | ((dev as u32) << 11) 132 | | ((func as u32) << 8) 133 | | ((offset as u32) & 0xFC) 134 | } 135 | 136 | fn pci_config_read_u32(_seg: u16, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { 137 | unsafe { 138 | let addr = pci_config_address(bus, dev, func, offset); 139 | Port::new(0xCF8).write(addr); 140 | Port::new(0xCFC).read() 141 | } 142 | } 143 | 144 | fn pci_config_write_u32(_seg: u16, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { 145 | unsafe { 146 | let addr = pci_config_address(bus, dev, func, offset); 147 | Port::new(0xCF8).write(addr); 148 | Port::new(0xCFC).write(value); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/acpi/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod handler; 2 | 3 | use acpi::{fadt::Fadt, AcpiTables}; 4 | use aml::{AmlContext, AmlName, AmlValue, DebugVerbosity}; 5 | use lazy_static::lazy_static; 6 | use log::debug; 7 | use spin::Mutex; 8 | use x86_64::{instructions::port::Port, PhysAddr}; 9 | 10 | extern crate alloc; 11 | use alloc::boxed::Box; 12 | 13 | #[derive(Debug, Clone, Copy)] 14 | pub struct AcpiShutdown { 15 | pub pm1a_control_block: u64, 16 | pub slp_typ_a: u16, 17 | pub slp_len: u16, 18 | } 19 | 20 | lazy_static! { 21 | pub static ref ACPI_SHUTDOWN: Mutex = Mutex::new(AcpiShutdown { 22 | pm1a_control_block: 0, 23 | slp_typ_a: 0, 24 | slp_len: 0, 25 | }); 26 | } 27 | 28 | pub fn init(rsdp_addr: &u64) { 29 | let tables = unsafe { 30 | AcpiTables::from_rsdp( 31 | crate::arch::x86::acpi::handler::AcpiHandler, 32 | *rsdp_addr as usize, 33 | ) 34 | .unwrap() 35 | }; 36 | 37 | let dsdt = tables 38 | .dsdt() 39 | .unwrap_or_else(|_| panic!("Failed to get DSDT table")); 40 | let fadt = tables 41 | .find_table::() 42 | .unwrap_or_else(|_| panic!("Failed to get FADT table")); 43 | 44 | let pm1a_control_block = fadt.pm1a_control_block().unwrap(); 45 | let slp_typ_a = { 46 | let table = unsafe { 47 | let ptr = 48 | crate::arch::x86::memory::physical_to_virtual(PhysAddr::new(dsdt.address as u64)); 49 | core::slice::from_raw_parts(ptr.as_ptr(), dsdt.length as usize) 50 | }; 51 | 52 | let handler = Box::new(crate::arch::x86::acpi::handler::AmlHandler); 53 | let mut aml = AmlContext::new(handler, DebugVerbosity::None); 54 | 55 | let name = AmlName::from_str("\\_S5").unwrap(); 56 | aml.parse_table(table).unwrap(); 57 | 58 | let s5 = match aml.namespace.get_by_path(&name).unwrap() { 59 | AmlValue::Package(p) => p, 60 | _ => panic!("\\_S5 is not a Package"), 61 | }; 62 | 63 | let value = match s5[0] { 64 | AmlValue::Integer(v) => v as u16, 65 | _ => panic!("\\_S5[0] is not an Integer"), 66 | }; 67 | 68 | value 69 | }; 70 | let slp_len = 1 << 13; 71 | 72 | *ACPI_SHUTDOWN.lock() = AcpiShutdown { 73 | pm1a_control_block: pm1a_control_block.address, 74 | slp_typ_a, 75 | slp_len, 76 | }; 77 | 78 | debug!("PM1A Control Block: {:#x}", pm1a_control_block.address); 79 | debug!("S5 Sleep Type: {:#x}", slp_typ_a); 80 | debug!("S5 Sleep Length: {:#x}", slp_len); 81 | } 82 | 83 | pub fn shutdown() { 84 | let pm1a_control_block = ACPI_SHUTDOWN.lock().pm1a_control_block; 85 | let slp_typ_a = ACPI_SHUTDOWN.lock().slp_typ_a; 86 | let slp_len = ACPI_SHUTDOWN.lock().slp_len; 87 | 88 | unsafe { 89 | let mut port: Port = Port::new(pm1a_control_block as u16); 90 | port.write(slp_typ_a | slp_len); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/apic.rs: -------------------------------------------------------------------------------- 1 | use acpi::{AcpiTables, InterruptModel}; 2 | use conquer_once::spin::OnceCell; 3 | use log::debug; 4 | use spin::{Mutex, Once}; 5 | 6 | extern crate alloc; 7 | use alloc::vec::Vec; 8 | 9 | use x2apic::{ 10 | ioapic::{IoApic, IrqMode, RedirectionTableEntry}, 11 | lapic::{LocalApic, LocalApicBuilder}, 12 | }; 13 | use x86_64::{instructions::port::Port, PhysAddr}; 14 | 15 | pub static IOAPIC: Once>> = Once::new(); 16 | pub static mut LAPIC: OnceCell> = OnceCell::uninit(); 17 | 18 | pub struct IOApic { 19 | addr: u64, 20 | ioapic: Option, 21 | } 22 | 23 | pub struct LApic { 24 | addr: u64, 25 | lapic: Option, 26 | } 27 | 28 | impl IOApic { 29 | pub fn new(addr: u64) -> Self { 30 | Self { 31 | addr: unsafe { 32 | crate::arch::x86::memory::physical_to_virtual(PhysAddr::new(addr)).as_u64() 33 | }, 34 | ioapic: None, 35 | } 36 | } 37 | 38 | pub fn init(&mut self) { 39 | debug!("Initializing IOAPIC"); 40 | self.ioapic = unsafe { Some(IoApic::new(self.addr)) }; 41 | debug!("IOAPIC initialized"); 42 | } 43 | 44 | #[allow(clippy::missing_safety_doc)] 45 | pub unsafe fn enable(&mut self) { 46 | if let Some(ioapic) = self.ioapic.as_mut() { 47 | ioapic.init(32); 48 | let mut entry = RedirectionTableEntry::default(); 49 | entry.set_mode(IrqMode::Fixed); 50 | entry.set_vector(33); 51 | entry.set_dest(0); 52 | 53 | ioapic.set_table_entry(1, entry); 54 | ioapic.enable_irq(1); 55 | } 56 | } 57 | 58 | pub fn get_ioapic(&self) -> Option<&IoApic> { 59 | self.ioapic.as_ref() 60 | } 61 | } 62 | 63 | impl LApic { 64 | pub fn new(addr: u64) -> Self { 65 | Self { 66 | addr: unsafe { 67 | crate::arch::x86::memory::physical_to_virtual(PhysAddr::new(addr)).as_u64() 68 | }, 69 | lapic: None, 70 | } 71 | } 72 | 73 | pub fn init(&mut self) { 74 | unsafe { 75 | let mut cmd_8259a = Port::::new(0x20); 76 | let mut data_8259a = Port::::new(0x21); 77 | let mut cmd_8259b = Port::::new(0xa0); 78 | let mut data_8259b = Port::::new(0xa1); 79 | 80 | let mut spin_port = Port::::new(0x80); 81 | let mut spin = || spin_port.write(0); 82 | 83 | cmd_8259a.write(0x11); 84 | cmd_8259b.write(0x11); 85 | spin(); 86 | 87 | data_8259a.write(0xf8); 88 | data_8259b.write(0xff); 89 | spin(); 90 | 91 | data_8259a.write(0b100); 92 | spin(); 93 | 94 | data_8259b.write(0b10); 95 | spin(); 96 | 97 | data_8259a.write(0x1); 98 | data_8259b.write(0x1); 99 | spin(); 100 | 101 | data_8259a.write(u8::MAX); 102 | data_8259b.write(u8::MAX); 103 | } 104 | 105 | self.lapic = LocalApicBuilder::default() 106 | .timer_vector(32) 107 | .error_vector(51) 108 | .spurious_vector(0xff) 109 | .set_xapic_base(self.addr) 110 | .build() 111 | .ok(); 112 | } 113 | 114 | pub fn enable(&mut self) { 115 | unsafe { 116 | self.lapic.as_mut().unwrap().enable(); 117 | } 118 | } 119 | 120 | pub fn end_interrupts(&mut self) { 121 | unsafe { 122 | self.lapic.as_mut().unwrap().end_of_interrupt(); 123 | } 124 | } 125 | } 126 | 127 | #[allow(static_mut_refs)] 128 | pub fn init_lapic(lapic_addr: u64) { 129 | unsafe { 130 | LAPIC.init_once(|| Mutex::new(LApic::new(lapic_addr))); 131 | LAPIC.get().unwrap().lock().init(); 132 | } 133 | } 134 | 135 | pub fn init_ioapic(ioapic_addr: u64) { 136 | IOAPIC.call_once(|| Mutex::new(alloc::vec![IOApic::new(ioapic_addr)])); 137 | 138 | let mut ioapic_lock = IOAPIC.get().unwrap().lock(); 139 | ioapic_lock.push(IOApic::new(ioapic_addr)); 140 | } 141 | 142 | pub fn init(rsdp_addr: &u64) { 143 | let tables = unsafe { 144 | AcpiTables::from_rsdp( 145 | crate::arch::x86::acpi::handler::AcpiHandler, 146 | *rsdp_addr as usize, 147 | ) 148 | .unwrap() 149 | }; 150 | let platform_info = tables.platform_info().unwrap(); 151 | let interrupt_model = platform_info.interrupt_model; 152 | 153 | debug!("Interrupt Model: {:?}", interrupt_model); 154 | 155 | if let InterruptModel::Apic(apic) = interrupt_model { 156 | let lapic_physical_address: u64 = apic.local_apic_address; 157 | init_lapic(lapic_physical_address); 158 | for i in apic.io_apics.iter() { 159 | init_ioapic(i.address as u64); 160 | debug!("IO Pushed: {:?}", i); 161 | } 162 | 163 | unsafe { 164 | for ioapic in IOAPIC.get().unwrap().lock().iter_mut() { 165 | ioapic.init(); 166 | ioapic.enable(); 167 | debug!("IO Enabled: {:?}", ioapic.get_ioapic()); 168 | } 169 | } 170 | 171 | #[allow(static_mut_refs)] 172 | unsafe { 173 | LAPIC.get().unwrap().lock().enable(); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/bga.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use x86_64::instructions::port::Port; 4 | 5 | const VBE_DISPI_IOPORT_INDEX: u16 = 0x01CE; 6 | const VBE_DISPI_IOPORT_DATA: u16 = 0x01CF; 7 | 8 | const VBE_DISPI_INDEX_ID: u16 = 0x00; 9 | const VBE_DISPI_INDEX_XRES: u16 = 0x01; 10 | const VBE_DISPI_INDEX_YRES: u16 = 0x02; 11 | const VBE_DISPI_INDEX_BPP: u16 = 0x03; 12 | const VBE_DISPI_INDEX_ENABLE: u16 = 0x04; 13 | const VBE_DISPI_INDEX_BANK: u16 = 0x05; 14 | const VBE_DISPI_INDEX_VIRT_WIDTH: u16 = 0x06; 15 | const VBE_DISPI_INDEX_VIRT_HEIGHT: u16 = 0x07; 16 | const VBE_DISPI_INDEX_X_OFFSET: u16 = 0x08; 17 | const VBE_DISPI_INDEX_Y_OFFSET: u16 = 0x09; 18 | 19 | const VBE_DISPI_ID5: u16 = 0xB0C5; 20 | const VBE_DISPI_DISABLED: u16 = 0x00; 21 | const VBE_DISPI_ENABLED: u16 = 0x01; 22 | const VBE_DISPI_LFB_ENABLED: u16 = 0x02; 23 | const VBE_DISPI_NOCLEARMEM: u16 = 0x04; 24 | 25 | pub const VBE_DISPI_BPP_4: u16 = 0x04; 26 | pub const VBE_DISPI_BPP_8: u16 = 0x08; 27 | pub const VBE_DISPI_BPP_15: u16 = 0x0F; 28 | pub const VBE_DISPI_BPP_16: u16 = 0x10; 29 | pub const VBE_DISPI_BPP_24: u16 = 0x18; 30 | pub const VBE_DISPI_BPP_32: u16 = 0x20; 31 | 32 | pub fn bga_write_register(index_value: u16, data_value: u16) { 33 | let mut index_port = Port::::new(VBE_DISPI_IOPORT_INDEX); 34 | let mut data_port = Port::::new(VBE_DISPI_IOPORT_DATA); 35 | unsafe { 36 | index_port.write(index_value); 37 | data_port.write(data_value); 38 | } 39 | } 40 | 41 | pub fn bga_read_register(index_value: u16) -> u16 { 42 | let mut index_port = Port::::new(VBE_DISPI_IOPORT_INDEX); 43 | let mut data_port = Port::::new(VBE_DISPI_IOPORT_DATA); 44 | unsafe { 45 | index_port.write(index_value); 46 | data_port.read() 47 | } 48 | } 49 | 50 | pub fn bga_is_available() -> bool { 51 | bga_read_register(VBE_DISPI_INDEX_ID) == VBE_DISPI_ID5 52 | } 53 | 54 | pub fn bga_set_video_mode( 55 | width: u32, 56 | height: u32, 57 | bit_depth: u32, 58 | use_linear_frame_buffer: bool, 59 | clear_video_memory: bool, 60 | ) { 61 | bga_write_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); 62 | 63 | bga_write_register(VBE_DISPI_INDEX_XRES, width as u16); 64 | bga_write_register(VBE_DISPI_INDEX_YRES, height as u16); 65 | bga_write_register(VBE_DISPI_INDEX_BPP, bit_depth as u16); 66 | 67 | let mut enable_value = VBE_DISPI_ENABLED; 68 | if use_linear_frame_buffer { 69 | enable_value |= VBE_DISPI_LFB_ENABLED; 70 | } 71 | if !clear_video_memory { 72 | enable_value |= VBE_DISPI_NOCLEARMEM; 73 | } 74 | bga_write_register(VBE_DISPI_INDEX_ENABLE, enable_value); 75 | } 76 | 77 | pub fn bga_set_bank(bank_number: u16) { 78 | bga_write_register(VBE_DISPI_INDEX_BANK, bank_number); 79 | } 80 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/console.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | 3 | use bootloader_api::info::FrameBuffer; 4 | 5 | use lazy_static::lazy_static; 6 | use noto_sans_mono_bitmap::get_raster; 7 | use noto_sans_mono_bitmap::FontWeight; 8 | use noto_sans_mono_bitmap::RasterHeight; 9 | use spin::Mutex; 10 | use x86_64::instructions::interrupts; 11 | 12 | lazy_static! { 13 | pub static ref CONSOLE: Mutex> = Mutex::new(None); 14 | } 15 | 16 | pub struct NotoFontDisplay { 17 | width: usize, 18 | height: usize, 19 | draw_buffer: &'static mut [u32], 20 | 21 | font_weight: FontWeight, 22 | raster_height: RasterHeight, 23 | 24 | cursor_x: usize, 25 | cursor_y: usize, 26 | } 27 | 28 | pub fn init(frame_buffer: &mut FrameBuffer) { 29 | let buffer = frame_buffer.buffer_mut().as_ptr() as *mut u32; 30 | let width = frame_buffer.info().width; 31 | let height = frame_buffer.info().height; 32 | 33 | for index in 0..(width * height) { 34 | unsafe { 35 | buffer.add(index as usize).write(0xff408deb); 36 | } 37 | } 38 | 39 | let console = NotoFontDisplay::new( 40 | width as usize, 41 | height as usize, 42 | unsafe { core::slice::from_raw_parts_mut(buffer, (width * height) as usize) }, 43 | FontWeight::Light, 44 | RasterHeight::Size24, 45 | ); 46 | 47 | interrupts::without_interrupts(|| { 48 | CONSOLE.lock().replace(console); 49 | 50 | CONSOLE 51 | .lock() 52 | .as_mut() 53 | .unwrap() 54 | .draw_string("Kernel Message"); 55 | }); 56 | } 57 | 58 | #[doc(hidden)] 59 | pub fn _print(args: ::core::fmt::Arguments) { 60 | use core::fmt::Write; 61 | interrupts::without_interrupts(|| { 62 | CONSOLE 63 | .lock() 64 | .as_mut() 65 | .unwrap() 66 | .write_fmt(args) 67 | .expect("Printing to serial failed"); 68 | }); 69 | } 70 | 71 | #[macro_export] 72 | macro_rules! print { 73 | ($($arg:tt)*) => { 74 | $crate::arch::x86::console::_print(format_args!($($arg)*)); 75 | }; 76 | } 77 | 78 | #[macro_export] 79 | macro_rules! println { 80 | () => ($crate::print!("\n")); 81 | ($fmt:expr) => ($crate::print!(concat!($fmt, "\n"))); 82 | ($fmt:expr, $($arg:tt)*) => ($crate::print!( 83 | concat!($fmt, "\n"), $($arg)*)); 84 | } 85 | 86 | impl NotoFontDisplay { 87 | pub fn new( 88 | width: usize, 89 | height: usize, 90 | draw_buffer: &'static mut [u32], 91 | font_weight: FontWeight, 92 | raster_height: RasterHeight, 93 | ) -> Self { 94 | Self { 95 | width, 96 | height, 97 | draw_buffer, 98 | font_weight, 99 | raster_height, 100 | cursor_x: 0, 101 | cursor_y: 0, 102 | } 103 | } 104 | 105 | pub fn clear(&mut self) { 106 | for pixel in self.draw_buffer.iter_mut() { 107 | *pixel = 0; 108 | } 109 | } 110 | 111 | pub fn draw_string(&mut self, msg: &str) { 112 | for (char_i, char) in msg.chars().enumerate() { 113 | let char_raster = match get_raster(char, self.font_weight, self.raster_height) { 114 | Some(raster) => raster, 115 | None => get_raster(' ', self.font_weight, self.raster_height).unwrap(), 116 | }; 117 | for (row_i, row) in char_raster.raster().iter().enumerate() { 118 | for (col_i, intensity) in row.iter().enumerate() { 119 | let index = char_i * char_raster.width() 120 | + col_i 121 | + row_i * self.width 122 | + (self.cursor_x as usize) 123 | + (self.cursor_y as usize * self.width); 124 | 125 | let curr_pixel_rgb = self.draw_buffer[index]; 126 | let mut r = ((curr_pixel_rgb & 0xff0000) >> 16) as u8; 127 | let mut g = ((curr_pixel_rgb & 0xff00) >> 8) as u8; 128 | let mut b = (curr_pixel_rgb & 0xff) as u8; 129 | 130 | r = r.saturating_add(*intensity); 131 | g = g.saturating_add(*intensity); 132 | b = b.saturating_add(*intensity); 133 | 134 | let new_pixel_rgb = ((r as u32) << 16) + ((g as u32) << 8) + (b as u32); 135 | self.draw_buffer[index] = new_pixel_rgb; 136 | } 137 | } 138 | } 139 | 140 | self.cursor_y += msg.len(); 141 | 142 | if self.cursor_x >= self.width { 143 | self.cursor_y = 0; 144 | self.cursor_x += self.raster_height as usize; 145 | } 146 | } 147 | } 148 | 149 | impl Write for NotoFontDisplay { 150 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 151 | self.draw_string(s); 152 | Ok(()) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/gdt.rs: -------------------------------------------------------------------------------- 1 | use spin::Lazy; 2 | 3 | use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; 4 | use x86_64::structures::tss::TaskStateSegment; 5 | use x86_64::VirtAddr; 6 | 7 | use core::ptr::addr_of; 8 | 9 | pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; 10 | pub const DEBUG_IST_INDEX: u16 = 1; 11 | pub const NON_MASKABLE_INTERRUPT_IST_INDEX: u16 = 2; 12 | 13 | pub static TSS: Lazy = Lazy::new(|| { 14 | let mut tss = TaskStateSegment::new(); 15 | let frames = [ 16 | 0, 17 | DOUBLE_FAULT_IST_INDEX, 18 | DEBUG_IST_INDEX, 19 | NON_MASKABLE_INTERRUPT_IST_INDEX, 20 | ]; 21 | for (i, &_frame) in frames.iter().enumerate() { 22 | tss.interrupt_stack_table[i] = { 23 | const STACK_SIZE: usize = 4096 * 5; 24 | static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; 25 | 26 | #[allow(unused_unsafe)] 27 | let stack_start = VirtAddr::from_ptr(unsafe { addr_of!(STACK) }); 28 | stack_start + STACK_SIZE as u64 29 | }; 30 | } 31 | tss 32 | }); 33 | 34 | pub static GDT: Lazy = Lazy::new(|| { 35 | let mut gdt = GlobalDescriptorTable::new(); 36 | let kernel_code_selector = gdt.append(Descriptor::kernel_code_segment()); 37 | let kernel_data_selector = gdt.append(Descriptor::kernel_data_segment()); 38 | let user_code_selector = gdt.append(Descriptor::user_code_segment()); 39 | let user_data_selector = gdt.append(Descriptor::user_data_segment()); 40 | let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); 41 | 42 | Gdt { 43 | gdt, 44 | tss_selector, 45 | kernel: Selectors { 46 | code_selector: kernel_code_selector, 47 | data_selector: kernel_data_selector, 48 | }, 49 | user: Selectors { 50 | code_selector: user_code_selector, 51 | data_selector: user_data_selector, 52 | }, 53 | } 54 | }); 55 | 56 | pub struct Gdt { 57 | pub gdt: GlobalDescriptorTable, 58 | pub tss_selector: SegmentSelector, 59 | pub kernel: Selectors, 60 | pub user: Selectors, 61 | } 62 | 63 | pub struct Selectors { 64 | pub code_selector: SegmentSelector, 65 | pub data_selector: SegmentSelector, 66 | } 67 | 68 | pub fn init() { 69 | use x86_64::instructions::segmentation::{Segment, CS, DS, SS}; 70 | use x86_64::instructions::tables::load_tss; 71 | 72 | GDT.gdt.load(); 73 | unsafe { 74 | CS::set_reg(GDT.kernel.code_selector); 75 | DS::set_reg(GDT.kernel.data_selector); 76 | SS::set_reg(GDT.kernel.data_selector); 77 | load_tss(GDT.tss_selector); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/interrupts.rs: -------------------------------------------------------------------------------- 1 | use log::{debug, warn}; 2 | use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; 3 | 4 | use lazy_static::lazy_static; 5 | 6 | use crate::{println, serial_println}; 7 | 8 | #[derive(Debug, Clone, Copy)] 9 | #[repr(u8)] 10 | pub enum InterruptIndex { 11 | Timer = 32, 12 | Keyboard = 33, 13 | } 14 | 15 | impl InterruptIndex { 16 | pub fn as_u8(self) -> u8 { 17 | self as u8 18 | } 19 | 20 | pub fn as_usize(self) -> usize { 21 | usize::from(self.as_u8()) 22 | } 23 | } 24 | 25 | macro_rules! simple_handlers { 26 | ($($name:ident => $info:expr),* $(,)?) => { 27 | $( 28 | pub extern "x86-interrupt" fn $name( 29 | stack_frame: InterruptStackFrame 30 | ) { 31 | panic!("EXCEPTION: {}\n{:#?}", $info, stack_frame); 32 | } 33 | )* 34 | }; 35 | } 36 | 37 | macro_rules! error_code_handlers { 38 | ($($name:ident => $info:expr),* $(,)?) => { 39 | $( 40 | pub extern "x86-interrupt" fn $name( 41 | stack_frame: InterruptStackFrame, 42 | error_code: u64 43 | ) { 44 | panic!( 45 | "EXCEPTION: {} - ERROR CODE: {}\n{:#?}", 46 | $info, error_code, stack_frame 47 | ); 48 | } 49 | )* 50 | }; 51 | } 52 | 53 | #[rustfmt::skip] 54 | const SIMPLE_HANDLERS: &[( 55 | fn(&mut InterruptDescriptorTable) -> &mut x86_64::structures::idt::Entry, 56 | extern "x86-interrupt" fn(InterruptStackFrame), 57 | )] = &[ 58 | (|idt| &mut idt.divide_error, divide_by_zero_handler), 59 | (|idt| &mut idt.debug, debug_handler), 60 | (|idt| &mut idt.non_maskable_interrupt, non_maskable_interrupt_handler), 61 | (|idt| &mut idt.overflow, overflow_handler), 62 | (|idt| &mut idt.bound_range_exceeded, bound_range_exceeded_handler), 63 | (|idt| &mut idt.invalid_opcode, invalid_opcode_handler), 64 | (|idt| &mut idt.device_not_available, device_not_available_handler), 65 | (|idt| &mut idt.x87_floating_point, x87_floating_point_handler), 66 | (|idt| &mut idt.simd_floating_point, simd_floating_point_handler), 67 | (|idt| &mut idt.virtualization, virtualization_handler), 68 | (|idt| &mut idt.breakpoint, breakpoint_handler), 69 | ]; 70 | 71 | #[rustfmt::skip] 72 | const ERROR_CODE_HANDLERS: &[( 73 | fn(&mut InterruptDescriptorTable) -> &mut x86_64::structures::idt::Entry, 74 | extern "x86-interrupt" fn(InterruptStackFrame, u64), 75 | )] = &[ 76 | (|idt| &mut idt.invalid_tss, invalid_tss_handler), 77 | (|idt| &mut idt.segment_not_present, segment_not_present_handler), 78 | (|idt| &mut idt.stack_segment_fault, stack_segment_fault_handler), 79 | (|idt| &mut idt.general_protection_fault, general_protection_fault_handler), 80 | (|idt| &mut idt.alignment_check, alignment_check_handler), 81 | (|idt| &mut idt.security_exception, security_exception_handler), 82 | ]; 83 | 84 | simple_handlers!( 85 | divide_by_zero_handler => "DIVIDE BY ZERO", 86 | debug_handler => "DEBUG", 87 | non_maskable_interrupt_handler => "NON MASKABLE INTERRUPT", 88 | overflow_handler => "OVERFLOW", 89 | bound_range_exceeded_handler => "BOUND RANGE EXCEEDED", 90 | invalid_opcode_handler => "INVALID OPCODE", 91 | device_not_available_handler => "DEVICE NOT AVAILABLE", 92 | x87_floating_point_handler => "X87 FLOATING POINT", 93 | simd_floating_point_handler => "SIMD FLOATING POINT", 94 | virtualization_handler => "VIRTUALIZATION", 95 | ); 96 | 97 | error_code_handlers!( 98 | invalid_tss_handler => "INVALID TSS", 99 | segment_not_present_handler => "SEGMENT NOT PRESENT", 100 | stack_segment_fault_handler => "STACK SEGMENT FAULT", 101 | general_protection_fault_handler => "GENERAL PROTECTION FAULT", 102 | alignment_check_handler => "ALIGNMENT CHECK", 103 | security_exception_handler => "SECURITY EXCEPTION", 104 | ); 105 | 106 | lazy_static! { 107 | static ref IDT: InterruptDescriptorTable = { 108 | let mut idt = InterruptDescriptorTable::new(); 109 | 110 | for (getter, handler) in SIMPLE_HANDLERS { 111 | getter(&mut idt).set_handler_fn(*handler); 112 | } 113 | 114 | for (getter, handler) in ERROR_CODE_HANDLERS { 115 | getter(&mut idt).set_handler_fn(*handler); 116 | } 117 | 118 | unsafe { 119 | idt.double_fault 120 | .set_handler_fn(double_fault_handler) 121 | .set_stack_index(crate::arch::x86::gdt::DOUBLE_FAULT_IST_INDEX); 122 | } 123 | 124 | #[rustfmt::skip] 125 | idt.machine_check.set_handler_fn(machine_check_handler); 126 | #[rustfmt::skip] 127 | idt.page_fault.set_handler_fn(page_fault_handler); 128 | 129 | #[rustfmt::skip] 130 | idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); 131 | #[rustfmt::skip] 132 | idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); 133 | 134 | idt 135 | }; 136 | } 137 | 138 | pub extern "x86-interrupt" fn double_fault_handler( 139 | stack_frame: InterruptStackFrame, 140 | error_code: u64, 141 | ) -> ! { 142 | panic!( 143 | "EXCEPTION: DOUBLE FAULT - ERROR CODE: {}\n{:#?}", 144 | error_code, stack_frame 145 | ); 146 | } 147 | 148 | pub extern "x86-interrupt" fn machine_check_handler(stack_frame: InterruptStackFrame) -> ! { 149 | panic!("EXCEPTION: MACHINE CHECK\n{:#?}", stack_frame); 150 | } 151 | 152 | pub extern "x86-interrupt" fn page_fault_handler( 153 | stack_frame: InterruptStackFrame, 154 | error_code: PageFaultErrorCode, 155 | ) { 156 | use x86_64::registers::control::Cr2; 157 | 158 | panic!( 159 | "EXCEPTION: PAGE FAULT - ERROR CODE: {:?}\nAccessed Address: {:?}\n{:#?}", 160 | error_code, 161 | Cr2::read(), 162 | stack_frame 163 | ); 164 | } 165 | 166 | pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { 167 | println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); 168 | 169 | serial_println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); 170 | } 171 | 172 | pub extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { 173 | use crate::arch::x86::apic::LAPIC; 174 | 175 | unsafe { 176 | #[allow(static_mut_refs)] 177 | LAPIC.get().unwrap().lock().end_interrupts(); 178 | } 179 | } 180 | 181 | pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { 182 | use x86_64::instructions::port::Port; 183 | 184 | use crate::arch::x86::apic::LAPIC; 185 | 186 | let mut port = Port::new(0x60); 187 | let scancode: u8 = unsafe { port.read() }; 188 | 189 | warn!("Keyboard scancode: {}", scancode); 190 | if scancode == 28 { 191 | crate::arch::x86::acpi::shutdown(); 192 | } 193 | 194 | unsafe { 195 | #[allow(static_mut_refs)] 196 | LAPIC.get().unwrap().lock().end_interrupts(); 197 | } 198 | } 199 | 200 | pub fn init() { 201 | IDT.load(); 202 | } 203 | 204 | pub fn enable_interrupts() { 205 | debug!("Enabling interrupts"); 206 | x86_64::instructions::interrupts::enable(); 207 | debug!("Interrupts enabled"); 208 | } 209 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/logging.rs: -------------------------------------------------------------------------------- 1 | use crate::serial_println; 2 | use log::{Level, LevelFilter, Log, Metadata, Record}; 3 | 4 | struct SimpleLogger; 5 | 6 | impl Log for SimpleLogger { 7 | fn enabled(&self, _metadata: &Metadata) -> bool { 8 | true 9 | } 10 | fn log(&self, record: &Record) { 11 | if !self.enabled(record.metadata()) { 12 | return; 13 | } 14 | let color = match record.level() { 15 | // Red 16 | Level::Error => 31, 17 | // BrightYellow 18 | Level::Warn => 93, 19 | // Blue 20 | Level::Info => 34, 21 | // Green 22 | Level::Debug => 32, 23 | // BrightBlack 24 | Level::Trace => 90, 25 | }; 26 | serial_println!( 27 | "\u{1B}[{}m[{:>5}] {}\u{1B}[0m", 28 | color, 29 | record.level(), 30 | record.args(), 31 | ); 32 | } 33 | fn flush(&self) {} 34 | } 35 | 36 | pub fn init() { 37 | static LOGGER: SimpleLogger = SimpleLogger; 38 | log::set_logger(&LOGGER).unwrap(); 39 | log::set_max_level(match option_env!("LOG_LEVEL") { 40 | Some("ERROR") => LevelFilter::Error, 41 | Some("WARN") => LevelFilter::Warn, 42 | Some("INFO") => LevelFilter::Info, 43 | Some("DEBUG") => LevelFilter::Debug, 44 | Some("TRACE") => LevelFilter::Trace, 45 | _ => LevelFilter::Off, 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/memory/heap_allocator.rs: -------------------------------------------------------------------------------- 1 | use linked_list_allocator::LockedHeap; 2 | use log::{debug, error}; 3 | use x86_64::{ 4 | structures::paging::{ 5 | mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, 6 | }, 7 | VirtAddr, 8 | }; 9 | 10 | extern crate alloc; 11 | use core::alloc::Layout; 12 | 13 | pub const HEAP_START: usize = 0x_ffff_a000_0000_0000; 14 | pub const HEAP_SIZE: usize = 32 * 1024 * 1024; 15 | 16 | #[alloc_error_handler] 17 | fn alloc_error_handler(layout: Layout) -> ! { 18 | error!("allocation error: {:?}", layout); 19 | 20 | loop { 21 | x86_64::instructions::hlt(); 22 | } 23 | } 24 | 25 | #[global_allocator] 26 | static ALLOCATOR: LockedHeap = LockedHeap::empty(); 27 | 28 | pub fn init( 29 | mapper: &mut impl Mapper, 30 | frame_allocator: &mut impl FrameAllocator, 31 | ) -> Result<(), MapToError> { 32 | let page_range = { 33 | let heap_start = VirtAddr::new(HEAP_START as u64); 34 | let heap_end = heap_start + HEAP_SIZE.try_into().unwrap() - 1u64; 35 | let heap_start_page = Page::containing_address(heap_start); 36 | let heap_end_page = Page::containing_address(heap_end); 37 | Page::range_inclusive(heap_start_page, heap_end_page) 38 | }; 39 | 40 | for page in page_range { 41 | let frame = frame_allocator 42 | .allocate_frame() 43 | .ok_or(MapToError::FrameAllocationFailed)?; 44 | let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; 45 | unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; 46 | } 47 | 48 | unsafe { 49 | let heap_start = HEAP_START as *mut u8; 50 | let heap_size = HEAP_SIZE; 51 | 52 | debug!( 53 | "Heap start: {:#x}, size: {}", 54 | heap_start as usize, heap_size 55 | ); 56 | ALLOCATOR.lock().init(heap_start, heap_size); 57 | } 58 | 59 | Ok(()) 60 | } 61 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/memory/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod heap_allocator; 2 | pub mod page_allocator; 3 | 4 | extern crate alloc; 5 | use alloc::vec::Vec; 6 | 7 | use lazy_static::lazy_static; 8 | use log::info; 9 | use spin::Once; 10 | use x86_64::{ 11 | registers::control::Cr3, 12 | structures::paging::{page_table::FrameError, PageTable}, 13 | PhysAddr, VirtAddr, 14 | }; 15 | 16 | lazy_static! { 17 | static ref PHYSICAL_MEMORY_OFFSET: Once = Once::new(); 18 | } 19 | 20 | pub unsafe fn physical_to_virtual(addr: PhysAddr) -> VirtAddr { 21 | let phys = PHYSICAL_MEMORY_OFFSET.get().unwrap(); 22 | VirtAddr::new(phys.as_u64() + addr.as_u64()) 23 | } 24 | 25 | pub unsafe fn virtual_to_physical( 26 | addr: VirtAddr, 27 | physical_memory_offset: VirtAddr, 28 | ) -> Option { 29 | let (level_4_table_frame, _) = Cr3::read(); 30 | 31 | let table_indexes = [ 32 | addr.p4_index(), 33 | addr.p3_index(), 34 | addr.p2_index(), 35 | addr.p1_index(), 36 | ]; 37 | let mut frame = level_4_table_frame; 38 | 39 | for &index in &table_indexes { 40 | let virt = physical_memory_offset + frame.start_address().as_u64(); 41 | let table_ptr: *const PageTable = virt.as_ptr(); 42 | let table = unsafe { &*table_ptr }; 43 | 44 | let entry = &table[index]; 45 | frame = match entry.frame() { 46 | Ok(frame) => frame, 47 | Err(FrameError::FrameNotPresent) => return None, 48 | Err(FrameError::HugeFrame) => panic!("huge pages not supported"), 49 | }; 50 | } 51 | 52 | Some(frame.start_address() + u64::from(addr.page_offset())) 53 | } 54 | 55 | pub fn init(boot_info: &'static mut bootloader_api::BootInfo) -> &'static bootloader_api::BootInfo { 56 | let physical_memory_offset = 57 | VirtAddr::new(boot_info.physical_memory_offset.into_option().unwrap()); 58 | 59 | PHYSICAL_MEMORY_OFFSET.call_once(|| physical_memory_offset); 60 | 61 | let (mut mapper, mut frame_allocator, boot_info) = page_allocator::init(boot_info); 62 | let _ = heap_allocator::init(&mut mapper, &mut frame_allocator); 63 | 64 | boot_info 65 | } 66 | 67 | pub fn alloc_test() { 68 | info!("Running bumb tests..."); 69 | 70 | let mut pool = Vec::new(); 71 | 72 | for i in 0..8 { 73 | info!("Indicator: {}", i); 74 | let mut items = alloc_pass(i); 75 | free_pass(&mut items, i as u8); 76 | 77 | pool.append(&mut items); 78 | assert_eq!(items.len(), 0); 79 | } 80 | 81 | info!("Bumb tests run OK!"); 82 | } 83 | 84 | fn alloc_pass(delta: usize) -> Vec> { 85 | let mut items = Vec::new(); 86 | let mut base = 32; 87 | loop { 88 | let c = (delta % 256) as u8; 89 | let a = alloc::vec![c; base+delta]; 90 | items.push(a); 91 | if base >= 512 * 1024 { 92 | break; 93 | } 94 | base *= 2; 95 | } 96 | items 97 | } 98 | 99 | fn free_pass(items: &mut Vec>, delta: u8) { 100 | let total = items.len(); 101 | for j in (0..total).rev() { 102 | if j % 2 == 0 { 103 | let ret = items.remove(j); 104 | assert_eq!(delta, ret[0]); 105 | assert_eq!(delta, ret[ret.len() - 1]); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/memory/page_allocator.rs: -------------------------------------------------------------------------------- 1 | use bootloader_api::info::{MemoryRegionKind, MemoryRegions}; 2 | use log::debug; 3 | use x86_64::registers::control::Cr3; 4 | use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB}; 5 | use x86_64::{PhysAddr, VirtAddr}; 6 | 7 | pub struct AbyssFrameAllocator { 8 | memory_map: &'static MemoryRegions, 9 | next: usize, 10 | } 11 | 12 | impl AbyssFrameAllocator { 13 | pub unsafe fn init(memory_map: &'static MemoryRegions) -> Self { 14 | AbyssFrameAllocator { 15 | memory_map, 16 | next: 0, 17 | } 18 | } 19 | 20 | fn usable_frames(&self) -> impl Iterator { 21 | let regions = self.memory_map.iter(); 22 | let usable_regions = regions.filter(|r| r.kind == MemoryRegionKind::Usable); 23 | let addr_ranges = usable_regions.map(|r| r.start..r.end); 24 | let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); 25 | frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr))) 26 | } 27 | } 28 | 29 | unsafe impl FrameAllocator for AbyssFrameAllocator { 30 | fn allocate_frame(&mut self) -> Option { 31 | let frame = self.usable_frames().nth(self.next); 32 | self.next += 1; 33 | frame 34 | } 35 | } 36 | 37 | pub unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { 38 | let (level_4_table_frame, _) = Cr3::read(); 39 | 40 | let phys = level_4_table_frame.start_address(); 41 | let virt = physical_memory_offset + phys.as_u64(); 42 | let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); 43 | 44 | unsafe { &mut *page_table_ptr } 45 | } 46 | 47 | pub fn init( 48 | boot_info: &'static mut bootloader_api::BootInfo, 49 | ) -> ( 50 | OffsetPageTable<'static>, 51 | AbyssFrameAllocator, 52 | &'static bootloader_api::BootInfo, 53 | ) { 54 | debug!("boot info {:#?}", boot_info); 55 | 56 | let physical_memory_offset = 57 | VirtAddr::new(boot_info.physical_memory_offset.into_option().unwrap()); 58 | 59 | let l4_table = unsafe { active_level_4_table(physical_memory_offset) }; 60 | 61 | for (index, entry) in l4_table.iter().enumerate() { 62 | if !entry.is_unused() { 63 | debug!("Level 4 Table Entry {}: {:?}", index, entry); 64 | } 65 | } 66 | 67 | for region in boot_info.memory_regions.iter() { 68 | let start = VirtAddr::new(region.start); 69 | let end = VirtAddr::new(region.end); 70 | let size = end - start; 71 | let region_type = region.kind; 72 | 73 | debug!( 74 | "Memory region: 0x{:x} - 0x{:x} ({:x} bytes) - {:?}", 75 | start.as_u64(), 76 | end.as_u64(), 77 | size, 78 | region_type 79 | ); 80 | } 81 | 82 | unsafe { 83 | let level_4_table = active_level_4_table(physical_memory_offset); 84 | let table = OffsetPageTable::new(level_4_table, physical_memory_offset); 85 | let frame_allocator = AbyssFrameAllocator::init(&boot_info.memory_regions); 86 | 87 | (table, frame_allocator, boot_info) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/mod.rs: -------------------------------------------------------------------------------- 1 | use core::panic::PanicInfo; 2 | 3 | use bga::{bga_set_bank, bga_set_video_mode, VBE_DISPI_BPP_32}; 4 | use log::*; 5 | 6 | use crate::{println, serial_println}; 7 | 8 | mod acpi; 9 | mod memory; 10 | mod virtualization; 11 | 12 | mod apic; 13 | mod bga; 14 | mod console; 15 | mod gdt; 16 | mod interrupts; 17 | mod logging; 18 | mod pcie; 19 | mod process; 20 | mod qemu; 21 | mod serial; 22 | 23 | extern crate alloc; 24 | 25 | #[panic_handler] 26 | pub fn panic(info: &PanicInfo) -> ! { 27 | error!("PANIC: {:#?}", info); 28 | 29 | loop { 30 | x86_64::instructions::hlt(); 31 | } 32 | } 33 | 34 | pub fn entry(boot_info: &'static mut bootloader_api::BootInfo) -> ! { 35 | crate::arch::x86::logging::init(); 36 | crate::arch::x86::console::init(boot_info.framebuffer.as_mut().unwrap()); 37 | 38 | crate::arch::x86::interrupts::init(); 39 | crate::arch::x86::gdt::init(); 40 | info!("Interrupts initialized"); 41 | 42 | let boot_info = crate::arch::x86::memory::init(boot_info); 43 | info!("Memory initialized"); 44 | 45 | crate::arch::x86::acpi::init(boot_info.rsdp_addr.as_ref().unwrap()); 46 | info!("ACPI Initialized"); 47 | 48 | crate::arch::x86::apic::init(boot_info.rsdp_addr.as_ref().unwrap()); 49 | crate::arch::x86::interrupts::enable_interrupts(); 50 | info!("APIC Initialized"); 51 | 52 | crate::arch::x86::pcie::init(); 53 | info!("PCIe Initialized"); 54 | 55 | println!("Hello from the x86_64 kernel!"); 56 | println!("More debug info will be display in the serial console."); 57 | println!("Press Enter to poweroff."); 58 | serial_println!("If you can't see more content here, you need to specify LOG_LEVEL env at compile time to enable higher level log filtering."); 59 | 60 | info!("Hello from the x86_64 kernel!"); 61 | info!("This is the last message from the kernel."); 62 | 63 | let logo = png_decoder::decode(crate::resources::LOGO).unwrap(); 64 | let width = logo.0.width; 65 | let height = logo.0.height; 66 | let pixels = logo.1; 67 | 68 | bga_set_video_mode(width, height, VBE_DISPI_BPP_32 as u32, true, true); 69 | bga_set_bank(0); 70 | 71 | let framebuffer = boot_info.framebuffer.as_ref().unwrap().buffer().as_ptr() as *mut u32; 72 | unsafe { 73 | for y in 0..height { 74 | for x in 0..width { 75 | let index = y * width + x; 76 | let r = pixels[(index * 4) as usize]; 77 | let g = pixels[(index * 4 + 1) as usize]; 78 | let b = pixels[(index * 4 + 2) as usize]; 79 | let a = pixels[(index * 4 + 3) as usize]; 80 | let color = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32); 81 | if a > 0 { 82 | *framebuffer.offset(index as isize) = color; 83 | } else { 84 | *framebuffer.offset(index as isize) = 0x00000000; 85 | } 86 | } 87 | } 88 | } 89 | 90 | let vec = alloc::vec![1, 1, 4, 5, 1, 4]; 91 | let hello = alloc::string::String::from("Hello"); 92 | 93 | debug!("{:?}", vec); 94 | debug!("{:?} from the x86_64 kernel alloctor!", hello); 95 | 96 | crate::arch::x86::memory::alloc_test(); 97 | 98 | loop { 99 | x86_64::instructions::hlt(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/pcie.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | use x86_64::instructions::port::Port; 3 | 4 | fn pci_class_code_description(class: u8, subclass: u8) -> &'static str { 5 | match (class, subclass) { 6 | (0x00, 0x00) => "Unclassified: Non-VGA compatible device", 7 | (0x00, 0x01) => "Unclassified: VGA compatible device", 8 | (0x01, 0x00) => "Mass Storage: SCSI", 9 | (0x01, 0x01) => "Mass Storage: IDE", 10 | (0x01, 0x06) => "Mass Storage: SATA", 11 | (0x01, 0x80) => "Mass Storage: Other", 12 | (0x02, 0x00) => "Network: Ethernet controller", 13 | (0x03, 0x00) => "Display: VGA compatible controller", 14 | (0x03, 0x01) => "Display: XGA controller", 15 | (0x03, 0x80) => "Display: Other", 16 | (0x06, 0x00) => "Bridge: Host bridge", 17 | (0x06, 0x01) => "Bridge: ISA bridge", 18 | (0x06, 0x04) => "Bridge: PCI-to-PCI bridge", 19 | (0x06, 0x80) => "Bridge: Other bridge device", 20 | _ => "Unknown device", 21 | } 22 | } 23 | 24 | fn pci_config_read(bus: u8, device: u8, function: u8, offset: u8) -> u32 { 25 | let address: u32 = (1 << 31) 26 | | ((bus as u32) << 16) 27 | | ((device as u32) << 11) 28 | | ((function as u32) << 8) 29 | | ((offset as u32) & 0xFC); 30 | 31 | let mut address_port = Port::::new(0xCF8); 32 | let mut data_port = Port::::new(0xCFC); 33 | 34 | unsafe { 35 | address_port.write(address); 36 | data_port.read() 37 | } 38 | } 39 | 40 | pub fn enumerate_pci() { 41 | for bus in 0u8..=255 { 42 | for device in 0u8..32 { 43 | for function in 0u8..8 { 44 | let vendor_device = pci_config_read(bus, device, function, 0x00); 45 | if vendor_device == 0xFFFF_FFFF { 46 | continue; 47 | } 48 | 49 | let vendor_id = (vendor_device & 0xFFFF) as u16; 50 | let device_id = ((vendor_device >> 16) & 0xFFFF) as u16; 51 | 52 | let class_reg = pci_config_read(bus, device, function, 0x08); 53 | let class_code = ((class_reg >> 24) & 0xFF) as u8; 54 | let subclass = ((class_reg >> 16) & 0xFF) as u8; 55 | let prog_if = ((class_reg >> 8) & 0xFF) as u8; 56 | 57 | let header_type_reg = pci_config_read(bus, device, function, 0x0C); 58 | let header_type = ((header_type_reg >> 16) & 0xFF) as u8; 59 | 60 | debug!( 61 | "PCI: Bus {:02X}, Dev {:02X}, Func {:X} => Vendor: {:04X}, Device: {:04X}, Class: {:02X}:{:02X}, ProgIF: {:02X} ({})", 62 | bus, 63 | device, 64 | function, 65 | vendor_id, 66 | device_id, 67 | class_code, 68 | subclass, 69 | prog_if, 70 | pci_class_code_description(class_code, subclass) 71 | ); 72 | 73 | if function == 0 && (header_type & 0x80) == 0 { 74 | break; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | pub fn init() { 82 | enumerate_pci(); 83 | } 84 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/process.rs: -------------------------------------------------------------------------------- 1 | use core::iter::Map; 2 | 3 | use lazy_static::lazy_static; 4 | 5 | extern crate alloc; 6 | use alloc::vec::Vec; 7 | 8 | lazy_static! { 9 | pub static ref PROCESSES: Processes = Processes { 10 | processes: Vec::new(), 11 | }; 12 | } 13 | 14 | #[allow(unused_variables)] 15 | pub fn entry_point(args: &[&str]) {} 16 | 17 | #[derive(Debug, Clone)] 18 | pub struct Processes { 19 | // Processes list fot pre physical processor 20 | processes: Vec>, 21 | } 22 | 23 | #[derive(Debug, Clone, Copy)] 24 | pub enum ProcessState { 25 | Running, 26 | Waiting, 27 | Stopped, 28 | Zombie, 29 | Terminated, 30 | } 31 | 32 | #[derive(Debug, Clone, Copy)] 33 | pub struct ProcessRegister { 34 | eax: usize, 35 | ebx: usize, 36 | ecx: usize, 37 | edx: usize, 38 | esi: usize, 39 | edi: usize, 40 | ebp: usize, 41 | esp: usize, 42 | } 43 | 44 | #[derive(Debug, Clone, Copy)] 45 | pub struct ProcessControlBlock { 46 | process_id: usize, 47 | process_state: ProcessState, 48 | process_priority: usize, 49 | created_time: usize, 50 | group_id: usize, 51 | parent_id: usize, 52 | user_id: usize, 53 | exit_code: usize, 54 | 55 | entry_point: usize, 56 | 57 | page_table: usize, 58 | stack_pointer: usize, 59 | instruction_pointer: usize, 60 | register: ProcessRegister, 61 | } 62 | 63 | impl ProcessControlBlock { 64 | pub fn new(entry_point: usize) -> Self { 65 | ProcessControlBlock { 66 | process_id: 0, 67 | process_state: ProcessState::Running, 68 | process_priority: 0, 69 | created_time: 0, 70 | group_id: 0, 71 | parent_id: 0, 72 | user_id: 0, 73 | exit_code: 0, 74 | 75 | entry_point, 76 | 77 | page_table: 0, 78 | stack_pointer: 0, 79 | instruction_pointer: 0, 80 | register: ProcessRegister { 81 | eax: 0, 82 | ebx: 0, 83 | ecx: 0, 84 | edx: 0, 85 | esi: 0, 86 | edi: 0, 87 | ebp: 0, 88 | esp: 0, 89 | }, 90 | } 91 | } 92 | } 93 | 94 | pub fn create_process(entry_point: usize) {} 95 | pub fn distory_process(pid: usize) {} 96 | pub fn switch_process(pid: usize) {} 97 | pub fn wait_process(pid: usize) {} 98 | pub fn exit_process(pid: usize, exit_code: usize) {} 99 | pub fn poll_process() {} 100 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/qemu.rs: -------------------------------------------------------------------------------- 1 | pub fn shutdown(exit_code: u32) { 2 | use x86_64::instructions::port::Port; 3 | 4 | unsafe { 5 | let mut port = Port::new(0xf4); 6 | port.write(exit_code); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/serial.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use spin::Mutex; 3 | use uart_16550::SerialPort; 4 | use x86_64::instructions::interrupts; 5 | 6 | lazy_static! { 7 | pub static ref SERIAL: Mutex = { 8 | let mut serial_port = unsafe { SerialPort::new(0x3F8) }; 9 | serial_port.init(); 10 | Mutex::new(serial_port) 11 | }; 12 | } 13 | 14 | #[doc(hidden)] 15 | pub fn _print(args: ::core::fmt::Arguments) { 16 | use core::fmt::Write; 17 | interrupts::without_interrupts(|| { 18 | SERIAL 19 | .lock() 20 | .write_fmt(args) 21 | .expect("Printing to serial failed"); 22 | }); 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! serial_print { 27 | ($($arg:tt)*) => { 28 | $crate::arch::x86::serial::_print(format_args!($($arg)*)); 29 | }; 30 | } 31 | 32 | #[macro_export] 33 | macro_rules! serial_println { 34 | () => ($crate::serial_print!("\n")); 35 | ($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n"))); 36 | ($fmt:expr, $($arg:tt)*) => ($crate::serial_print!( 37 | concat!($fmt, "\n"), $($arg)*)); 38 | } 39 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/virtualization/mod.rs: -------------------------------------------------------------------------------- 1 | // intel virtualization 2 | pub mod vmcs; 3 | pub mod vmx; 4 | 5 | // amd virtualization 6 | pub mod svm; 7 | pub mod vmcb; 8 | 9 | pub fn list_virtual_machines() {} 10 | pub fn create_virtual_machine() {} 11 | pub fn destroy_virtual_machine() {} 12 | pub fn start_virtual_machine() {} 13 | pub fn stop_virtual_machine() {} 14 | pub fn suspend_virtual_machine() {} 15 | pub fn resume_virtual_machine() {} 16 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/virtualization/svm.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/virtualization/vmcb.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/virtualization/vmcs.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-kernel/src/arch/x86/virtualization/vmx.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-kernel/src/linker/aarch64-linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | . = 0x80000; 6 | .text : { *(.text*) } 7 | .rodata : { *(.rodata*) } 8 | .data : { *(.data*) } 9 | .bss : { *(.bss*) } 10 | } -------------------------------------------------------------------------------- /canicula-kernel/src/linker/riscv64-linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | BASE_ADDRESS = 0x80200000; 4 | 5 | SECTIONS 6 | { 7 | . = BASE_ADDRESS; 8 | skernel = .; 9 | 10 | stext = .; 11 | .text : { 12 | *(.text.entry) 13 | *(.text .text.*) 14 | } 15 | 16 | . = ALIGN(4K); 17 | etext = .; 18 | srodata = .; 19 | .rodata : { 20 | *(.rodata .rodata.*) 21 | *(.srodata .srodata.*) 22 | } 23 | 24 | . = ALIGN(4K); 25 | erodata = .; 26 | sdata = .; 27 | .data : { 28 | *(.data .data.*) 29 | *(.sdata .sdata.*) 30 | } 31 | 32 | . = ALIGN(4K); 33 | edata = .; 34 | .bss : { 35 | *(.bss.stack) 36 | sbss = .; 37 | *(.bss .bss.*) 38 | *(.sbss .sbss.*) 39 | } 40 | 41 | . = ALIGN(4K); 42 | ebss = .; 43 | ekernel = .; 44 | 45 | /DISCARD/ : { 46 | *(.eh_frame) 47 | } 48 | } -------------------------------------------------------------------------------- /canicula-kernel/src/linker/x86-linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | KERNEL_BEGIN = 0xfffff80000000000; 4 | 5 | SECTIONS { 6 | 7 | . = KERNEL_BEGIN; 8 | 9 | .rodata ALIGN(4K): 10 | { 11 | *(.rodata .rodata.*) 12 | } 13 | 14 | .text ALIGN(4K): 15 | { 16 | *(.text .text.*) 17 | } 18 | 19 | .data ALIGN(4K): 20 | { 21 | *(.data .data.*) 22 | } 23 | 24 | .got ALIGN(4K): 25 | { 26 | *(.got .got.*) 27 | } 28 | 29 | .bss ALIGN(4K): 30 | { 31 | *(.bss .bss.*) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /canicula-kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![allow(dead_code)] 4 | #![cfg_attr( 5 | any(target_arch = "x86", target_arch = "x86_64"), 6 | feature(abi_x86_interrupt, naked_functions) 7 | )] 8 | #![feature(alloc_error_handler)] 9 | 10 | mod arch; 11 | mod resources; 12 | mod types; 13 | 14 | #[no_mangle] 15 | #[cfg(target_arch = "riscv64")] 16 | pub fn kernel() -> ! { 17 | arch::riscv::entry(); 18 | } 19 | 20 | #[no_mangle] 21 | #[cfg(target_arch = "aarch64")] 22 | pub fn kernel() -> ! { 23 | arch::aarch::entry(); 24 | } 25 | 26 | #[cfg(target_arch = "x86_64")] 27 | const CONFIG: bootloader_api::BootloaderConfig = { 28 | let mut config = bootloader_api::BootloaderConfig::new_default(); 29 | config.kernel_stack_size = 1000 * 1024; 30 | config.mappings.physical_memory = Some(bootloader_api::config::Mapping::Dynamic); 31 | config 32 | }; 33 | #[cfg(target_arch = "x86_64")] 34 | bootloader_api::entry_point!(kernel_main, config = &CONFIG); 35 | 36 | #[no_mangle] 37 | #[cfg(target_arch = "x86_64")] 38 | fn kernel_main(boot_info: &'static mut bootloader_api::BootInfo) -> ! { 39 | arch::x86::entry(boot_info) 40 | } 41 | -------------------------------------------------------------------------------- /canicula-kernel/src/resources.rs: -------------------------------------------------------------------------------- 1 | pub static LOGO: &'static [u8] = include_bytes!("../../resources/images/logo.png"); 2 | -------------------------------------------------------------------------------- /canicula-kernel/src/types/elf.rs: -------------------------------------------------------------------------------- 1 | pub type Elf32Addr = u32; 2 | pub type Elf32Half = u16; 3 | pub type Elf32Off = u32; 4 | pub type Elf32Word = u32; 5 | pub type Elf32Sword = u32; 6 | pub type UnsignedChar = u8; 7 | 8 | pub struct ELFHeader { 9 | e_ident: [UnsignedChar; 16], 10 | e_type: Elf32Half, 11 | e_machine: Elf32Half, 12 | e_version: Elf32Sword, 13 | e_entry: Elf32Addr, 14 | e_phoff: Elf32Off, 15 | e_shoff: Elf32Off, 16 | e_flags: Elf32Word, 17 | e_ehsize: Elf32Half, 18 | e_phentsize: Elf32Half, 19 | e_phnum: Elf32Half, 20 | e_shentsize: Elf32Half, 21 | e_shnum: Elf32Half, 22 | e_shstrndx: Elf32Half, 23 | } 24 | 25 | pub struct ELFProgramHeaderTable { 26 | p_type: Elf32Sword, 27 | p_offset: Elf32Off, 28 | p_vaddr: Elf32Addr, 29 | p_paddr: Elf32Addr, 30 | p_filesz: Elf32Sword, 31 | p_memsz: Elf32Sword, 32 | p_flags: Elf32Sword, 33 | p_align: Elf32Sword, 34 | } 35 | 36 | pub struct ELFSectionHeaderTable { 37 | sh_name: Elf32Sword, 38 | sh_type: Elf32Sword, 39 | sh_flags: Elf32Sword, 40 | sh_addr: Elf32Addr, 41 | sh_offset: Elf32Off, 42 | sh_size: Elf32Sword, 43 | sh_link: Elf32Sword, 44 | sh_info: Elf32Sword, 45 | sh_addralign: Elf32Sword, 46 | sh_entsize: Elf32Sword, 47 | } 48 | -------------------------------------------------------------------------------- /canicula-kernel/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod elf; 2 | -------------------------------------------------------------------------------- /canicula-kernel/x86_64-unknown-none.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "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", 4 | "linker-flavor": "ld.lld", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "arch": "x86_64", 9 | "os": "none", 10 | "executables": true, 11 | "linker": "rust-lld", 12 | "disable-redzone": true, 13 | "features": "-mmx,-sse,+soft-float", 14 | "panic-strategy": "abort", 15 | "pre-link-args": { 16 | "ld.lld": ["-Tcanicula-kernel/linker/x86-linker.ld", "--strip-all"] 17 | } 18 | } -------------------------------------------------------------------------------- /canicula-libs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "canicula-libs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "canicula_libs" 8 | path = "src/libs.rs" 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /canicula-libs/src/config.rs: -------------------------------------------------------------------------------- 1 | pub mod aarch64; 2 | pub mod riscv64; 3 | pub mod x86_64; 4 | -------------------------------------------------------------------------------- /canicula-libs/src/config/aarch64.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanbings/canicula/f29c7b84773bee5ab7891780f7484eccc2b5f049/canicula-libs/src/config/aarch64.rs -------------------------------------------------------------------------------- /canicula-libs/src/config/riscv64.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /canicula-libs/src/config/x86_64.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanbings/canicula/f29c7b84773bee5ab7891780f7484eccc2b5f049/canicula-libs/src/config/x86_64.rs -------------------------------------------------------------------------------- /canicula-libs/src/libs.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | - [目录](#目录) 3 | - [处理器架构](#处理器架构) 4 | - [指令结构](#指令结构) 5 | - [常用寄存器](#常用寄存器) 6 | - [分页机制](#分页机制) 7 | - [拓展指令集](#拓展指令集) 8 | 9 | 10 | # 处理器架构 11 | 12 | 这一部分文档讲述的是各个架构的不同之处,包含指令结构、常用寄存器、分页机制、拓展指令集等。 13 | 14 | ## 指令结构 15 | 16 | ## 常用寄存器 17 | 18 | ## 分页机制 19 | 20 | ## 拓展指令集 -------------------------------------------------------------------------------- /docs/bootloader.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > 在 uefi-rs 0.32 更新后,API 进行了一些修改,因此原先 “引导” 部分也更新了一篇新的博客到 [Blog - Rust:使用 uefi-rs 编写一个 UEFI 应用并加载内核](https://blog.hanbings.io/posts/rust-uefi-bootloader/),包含简单解释 uefi 概念、如何使用 uefi-rs 和跳转到 bin 格式内核的例子,对应代码位于本仓库分支 [blog-uefi-bootloader](https://github.com/hanbings/canicula/tree/blog-uefi-bootloader)。 3 | > 参阅: 4 | > 5 | > - [uefi-rs 变更日志](https://github.com/rust-osdev/uefi-rs/blob/main/uefi/CHANGELOG.md#uefi---0320-2024-09-09) 6 | > - [上一个版本的文档](bootloader.old.md) 7 | > - [Blog - Rust:使用 uefi-rs 编写一个 UEFI 应用并加载内核](https://blog.hanbings.io/posts/rust-uefi-bootloader/) 8 | 9 | ## x86-64 10 | 11 | … 12 | 13 | ## AArch64 14 | 15 | … 16 | 17 | ## RISC-V 18 | 19 | … 20 | -------------------------------------------------------------------------------- /docs/bootloader.old.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | - [目录](#目录) 3 | - [引导](#引导) 4 | - [UEFI](#uefi) 5 | - [uefi-rs](#uefi-rs) 6 | - [数据类型](#数据类型) 7 | - [修饰符](#修饰符) 8 | - [入口函数](#入口函数) 9 | - [调用函数](#调用函数) 10 | - [x86-64](#x86-64) 11 | - [AArch64](#aarch64) 12 | - [RustSBI](#rustsbi) 13 | - [RISC-V64](#risc-v64) 14 | 15 | 16 | # 引导 17 | 18 | > 引导在英文中为 “boot”,是 bootstrap 的缩写,源自于短语 “Pull oneself up by one's bootstraps”,即“靠自己振作起来”。 -- [维基百科 - 引导程序](https://zh.wikipedia.org/wiki/%E5%95%9F%E5%8B%95%E7%A8%8B%E5%BC%8F) 19 | 20 | Linux 有 [GRUB2](https://www.gnu.org/software/grub/) 和 [systemd-boot](https://systemd.io/BOOT/),Windows 有 [Windows Boot Manager](https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/boot-and-uefi#understanding-the-windows-boot-manager),Android 有 [U-Boot](https://docs.u-boot.org/en/latest/android/boot-image.html)。 21 | 22 | 我们也得写一个引导器才行! 23 | 24 | ## UEFI 25 | 26 | UEFI(Unified Extensible Firmware Interface),统一可扩展固件接口,是一个负责连接硬件和软件之间的接口。 27 | 28 | 本文是为了编写了一个可以加载内核的引导器,因此将对使用 `uefi-rs`、 `Boot Service` 和 `Runtime Service` 以及一些必要的 `Handle` 和 `Protocol` 进行说明,但不会对于 UEFI 本身进行详细的解析,如果对这一方面可以参考 [UEFI 手册](https://uefi.org/specs/UEFI/2.10/index.html)、罗冰老师的《UEFI 编程实践》和戴正华老师的《UEFI 原理与编程》。 29 | 30 | ### uefi-rs 31 | 32 | > Our mission is to provide **safe** and **performant** wrappers for UEFI interfaces, and allow developers to write idiomatic Rust code. -- [uefi-rs](https://github.com/rust-osdev/uefi-rs) 33 | 34 | [EDK2](https://github.com/tianocore/edk2) (EFI Development Kit)是 UEFI 的开发工具包,使用 C 语言进行 UEFI 工程编程。[uefi-rs](https://github.com/rust-osdev/uefi-rs) 是 rust 语言下的 EDK2 封装,巧妙运用了很多 rust 语言的语言特性,使得开发效率大大提升。 35 | 36 | 现有大多数的 UEFI 编程资料是基于 C 语言的,使用了很多指针特性来实现功能。在 Rust 中我们有更好的写法抽象和隐藏或安全传递这些指针,因此本节主要目的是说明 C 语言的写法与 Rust 写法的异同,以便应对阅读参考资料代码时的语言障碍。如果您有 C / C++ 基础且掌握 Rust 语言那就更好了! 37 | 38 | #### 数据类型 39 | 40 | 从数据类型说起: 41 | 42 | 在 EDK2 中,为了适配多种不同架构不同位数的 CPU 而对 C 语言的数据类型系统进行了封装,这些数据类型基本能够对应到 Rust 的类型系统中,下表是从 UEFI 手册中抽取的一部分,完整表格在[这里](https://uefi.org/specs/UEFI/2.10/02_Overview.html#data-types)查看。 43 | 44 | | EDK2 Type | Rust / uefi-rs Type | Description | 45 | | --------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 46 | | _BOOLEAN_ | bool | Logical Boolean. 1-byte value containing a 0 for **FALSE** or a 1 for **TRUE**. Other values are undefined. | 47 | | _INTN_ | iszie | Signed value of native width. (4 bytes on supported 32-bit processor instructions, 8 bytes on supported 64-bit processor instructions, 16 bytes on supported 128-bit processor instructions) | 48 | | _UINTN_ | usize | Unsigned value of native width. (4 bytes on supported 32-bit processor instructions, 8 bytes on supported 64-bit processor instructions, 16 bytes on supported 128-bit processor instructions) | 49 | | _INT8_ | i8 | 1-byte signed value. | 50 | | _UINT8_ | u8 | 1-byte unsigned value. | 51 | | _INT16_ | i16 | 2-byte signed value. | 52 | | _UINT16_ | u16 | 2-byte unsigned value. | 53 | | _INT32_ | i32 | 4-byte signed value. | 54 | | _UINT32_ | u32 | 4-byte unsigned value. | 55 | | _INT64_ | i64 | 8-byte signed value. | 56 | | _UINT64_ | u64 | 8-byte unsigned value. | 57 | | _INT128_ | i128 | 16-byte signed value. | 58 | | _UINT128_ | u128 | 16-byte unsigned value. | 59 | | _CHAR8_ | CStr8 | 1-byte character. Unless otherwise specified, all 1-byte or ASCII characters and strings are stored in 8-bit ASCII encoding format, using the ISO-Latin-1 character set. | 60 | | _CHAR16_ | [CStr16](https://docs.rs/uefi/latest/uefi/data_types/struct.CString16.html) | 2-byte Character. Unless otherwise specified all characters and strings are stored in the UCS-2 encoding format as defined by Unicode 2.1 and ISO/IEC 10646 standards. | 61 | 62 | 其中,CStr8 和 CStr16 可以分别使用宏 [cstr8](https://docs.rs/uefi-macros/latest/uefi_macros/macro.cstr8.html) 和 [cstr16](https://docs.rs/uefi-macros/latest/uefi_macros/macro.cstr16.html) 进行构建。 63 | 64 | 此外常用的还有: 65 | 66 | **EFI_STATUS**,用于表达函数返回状态(是否出错,是否有值)。 67 | 68 | **EFI_HANDLE**,即是后续我们会提到的 Handle。 69 | 70 | #### 修饰符 71 | 72 | 在 UEFI 手册中的接口描述中,使用了一些助记词作为参数的修饰符,如下: 73 | 74 | | **Mnemonic** | **Description** | 75 | | ------------ | ------------------------------------------------------------------------------------------------------- | 76 | | _IN_ | Datum is passed to the function. | 77 | | _OUT_ | Datum is returned from the function. | 78 | | _OPTIONAL_ | Passing the datum to the function is optional, and a _NULL_ may be passed if the value is not supplied. | 79 | | _CONST_ | Datum is read-only. | 80 | | _EFIAPI_ | Defines the calling convention for UEFI interfaces. | 81 | 82 | #### 入口函数 83 | 84 | **EDK2:** 85 | 86 | ```c 87 | EFI_STATUS EFIAPI main ( 88 | IN EFI_HANDLE ImageHandle, 89 | IN EFI_SYSTEM_TABLE *SystemTable 90 | ) { } 91 | ``` 92 | 93 | **uefi-rs:** 94 | 95 | ```rust 96 | fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { } 97 | ``` 98 | 99 | 可以看到 IN 类型数据写法实际上是没有什么区别的,但在 Rust 中能够隐藏指针类型和添加准确的泛型。 100 | 101 | 在入口中 Image Handle 指向当前 Image(其实也就是当前 EFI 程序),System Table 是一个 UEFI 环境下的全局资源表,存有一些公共数据和函数。 102 | 103 | #### 调用函数 104 | 105 | 一般来说,在 EDK2 中函数的返回值为 EFI*STATUS 类型,(返回的)数据地址会赋值给参数类型为指针的 \_OUT* 参数中,这意味着调用一个函数的步骤是: 106 | 107 | 1. 在手册中找到函数所在的 `Table`、`Service`、`Handle` 和 `Protocol` 等对应的数据结构,以函数指针 `->` 的方式访问函数。 108 | 2. 查看哪些是 _IN_ 类型参数,哪些是 _OUT_ 类型参数 109 | 3. 准备好用于 _OUT_ 类型参数的空指针 110 | 4. 调用后判断 EFI*STATUS 而得到 \_OUT* 类型参数的指针是否已指向数据 111 | 5. 从 _OUT_ 类型参数取出数据 112 | 113 | 以获取 Graphics Output Protocol 为例子: 114 | 115 | **EDK2:** 116 | 117 | 使用 [LocateProtocol](https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html?highlight=locateprotocol#efi-boot-services-locateprotocol) 函数获取 Graphics Output Protocol。 118 | 119 | 其函数原型为: 120 | 121 | ```c 122 | typedef 123 | EFI_STATUS 124 | (EFIAPI *EFI_LOCATE_PROTOCOL) ( 125 | IN EFI_GUID *Protocol, 126 | IN VOID *Registration OPTIONAL, 127 | OUT VOID **Interface 128 | ); 129 | ``` 130 | 131 | 我们需要关注的是第三个参数 Interface,可以看到是一个指针类型的 OUT 类型参数。 132 | 133 | > On return, a pointer to the first interface that matches _Protocol_ and _Registration_. -- EFI_LOCATE_PROTOCOL - Interface 134 | 135 | 因此有代码: 136 | 137 | ```c 138 | // 声明一个状态,用于接受函数表明执行状态的返回值 139 | EFI_STATUS Status; 140 | // 提前声明一个指针用于指向函数的返回值数据 141 | EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; 142 | 143 | // gBS 是 BootService,通过 SystemTable->BootService 获取 144 | Status = gBS->LocateProtocol( 145 | // gEfiGraphicsOutputProtocolGuid 定义在头文件中,是 Graphics Output Protocol 的 UUID 146 | &gEfiGraphicsOutputProtocolGuid, 147 | NULL, 148 | (VOID **)&GraphicsOutput 149 | ); 150 | if (EFI_ERROR(Status)) { 151 | return Status; 152 | } 153 | ``` 154 | 155 | **uefi-rs:** 156 | 157 | 基于 Rust 的特性,可以使用 Result 替换掉 EFI_STATUS 这种需要额外声明一个变量来存放状态的方式。 158 | 159 | ```rust 160 | let graphics_output_protocol_handle = boot_service 161 | .get_handle_for_protocol::() 162 | // 返回类型为 Result 163 | // 这里便于理解直接使用了 unwarp,但在正常编码中,应该使用 map_or 或 expect 等方式显式处理错误。 164 | // 尤其是在 UEFI 这类难于调试的环境下,应该尽可能地留下有用的错误信息 165 | .unwrap(); 166 | 167 | let mut graphics_output_protocol = boot_service 168 | .open_protocol_exclusive::(graphics_output_protocol_handle) 169 | // 返回类型为 Result> 170 | .unwrap(); 171 | ``` 172 | 173 | ### x86-64 174 | 175 | 要加载内核,一共有三步! 176 | 177 | **第一步**:~~把冰箱门打开~~ 初始化 Boot Service 和加载 Protocol 178 | 179 | ```rust 180 | // 为了 println 宏能够正常使用,还需要先初始化工具类 181 | uefi::helpers::init(&mut system_table).unwrap(); 182 | 183 | // 加载系统服务 184 | let boot_services = system_table.boot_services(); 185 | 186 | // 加载 Simple File System Handle 187 | let simple_file_system_handle = boot_services 188 | .get_handle_for_protocol::() 189 | .expect("Cannot get protocol handle"); 190 | 191 | // 从 Handle 中获取 Protocol 192 | let mut simple_file_system_protocol = boot_services 193 | .open_protocol_exclusive::(simple_file_system_handle) 194 | .expect("Cannot get simple file system protocol"); 195 | ``` 196 | 197 | **第二步**:开辟内存空间,先将内核路径名字加载到内存,再将内核文件信息加载到内存,最后再把内核文件本体加载到内存 198 | 199 | 注意这里的单位换算: 200 | 201 | `0x400` 实际上是十进制的 `1024`,也就是 _1k(kbit)_ 202 | 203 | `0x1000` 实际上是十进制的 `4096`,也就是 _4k(kbit)_ 204 | 205 | ```rust 206 | pub const FILE_BUFFER_SIZE: usize = 0x400; 207 | pub const PAGE_SIZE: usize = 0x1000; 208 | pub const KERNEL_PATH: &str = "\\canicula-kernel"; 209 | ``` 210 | 211 | 我们的内核名称为 canicula-kernel,目录在 esp/canicula-kernel,即是 UEFI 在 QEMU 能读取到的标卷的根目录,所以我们只需要获取一个根目录就够了。 212 | 213 | ```rust 214 | let mut root = simple_file_system_protocol 215 | .open_volume() 216 | .expect("Cannot open volume"); 217 | ``` 218 | 219 | 然后要把内核路径的名字加载到内存,获取到 `File` 的 `Handle`。 220 | 221 | ```rust 222 | // 先创建一个路径名称的缓冲区(实际上并不需要这么大的空间 我们的路径没有这么长) 223 | let mut kernel_path_buffer = [0u16; FILE_BUFFER_SIZE]; 224 | // 将路径转为 CStr16 类型 225 | let kernel_path = CStr16::from_str_with_buf(KERNEL_PATH, &mut kernel_path_buffer) 226 | .expect("Invalid kernel path!"); 227 | // 然后在根目录下以文件名形式获取 File Handle 228 | let kernel_file_handle = root 229 | .open(kernel_path, FileMode::Read, FileAttribute::empty()) 230 | .expect("Cannot open kernel file"); 231 | // 但注意只是获取到了文件的 Handle,文件还没有真正加载到内存 232 | let mut kernel_file = match kernel_file_handle.into_type().unwrap() { 233 | FileType::Regular(f) => f, 234 | _ => panic!("This file does not exist!"), 235 | }; 236 | ``` 237 | 238 | 接着获取到文件信息,我们是想要拿到文件的长度。 239 | 240 | ```rust 241 | // 为了将文件真正加载到内存还需要文件的长度(也就是大小) 242 | // 这个长度在文件信息里 243 | // 所以为文件信息开辟一片缓冲区,然后将它读取到这里 244 | let mut kernel_file_info_buffer = [0u8; FILE_BUFFER_SIZE]; 245 | let kernel_file_info: &mut FileInfo = kernel_file 246 | .get_info(&mut kernel_file_info_buffer) 247 | .expect("Cannot get file info"); 248 | // 然后拿到文件长度 249 | let kernel_file_size = 250 | usize::try_from(kernel_file_info.file_size()).expect("Invalid file size!"); 251 | ``` 252 | 253 | 最后,为内核开辟一整片内存空间,然后从文件读到内存中。 254 | 255 | ```rust 256 | // 接着要用 allocate_pages 开辟一篇内存空间,确保内核可以独自占用一片内存空间 257 | let kernel_file_address = boot_services 258 | .allocate_pages( 259 | AllocateType::AnyPages, 260 | MemoryType::LOADER_DATA, 261 | // 先用内核长度除以页大小,然后再额外多加一页 262 | // 这样就能保证开辟的内存能装得下内核了 263 | // 页是 UEFI 内存管理机制的一部分,可以搜索关键词 “内存管理页表” 了解这部分的内容,本文不再详细展开了 264 | kernel_file_size / PAGE_SIZE + 1, 265 | ) 266 | .expect("Cannot allocate memory in the RAM!") as *mut u8; 267 | 268 | // 防止这块地址以前有其他程序写入过内容 269 | // 我们用 0 再填充一次 270 | let kernel_file_in_memory = unsafe { 271 | core::ptr::write_bytes(kernel_file_address, 0, kernel_file_size); 272 | core::slice::from_raw_parts_mut(kernel_file_address, kernel_file_size) 273 | }; 274 | // 最后用 Handle 的 read 函数将内核文件内容转写到这块内存中 275 | // 这个 kernel_file_size 指的是读进内存的长度 276 | let kernel_file_size = kernel_file 277 | .read(kernel_file_in_memory) 278 | .expect("Cannot read file into the memory!"); 279 | ``` 280 | 281 | **第三步**:获取到内核内存的起始地址,然后跳转! 282 | 283 | ```rust 284 | let kernel_content = &mut kernel_file_in_memory[..kernel_file_size]; 285 | let kernel_address = kernel_content.as_ptr() as *const u8 as usize; 286 | 287 | // 偷懒力,用 xmas-elf 解析 elf 文件 288 | let kernel_elf = ElfFile::new(kernel_content).expect("Not a valid ELF file."); 289 | let kernel_entry_offset = kernel_elf.header.pt2.entry_point() as usize; 290 | 291 | // 将内核文件地址加上内核入口编译得到最终地址 292 | let kernel_entry_address = kernel_address + kernel_entry_offest; 293 | 294 | // 跳转! 295 | unsafe { 296 | core::arch::asm!("jmp {}", in(reg) kernel_entry_address); 297 | } 298 | ``` 299 | 300 | **完整代码** 301 | 302 |
303 | 贴一个完整的代码 304 | 305 | ```rust 306 | // Cargo.toml 307 | [package] 308 | name = "canicula-efi" 309 | version = "0.1.0" 310 | edition = "2021" 311 | 312 | [[bin]] 313 | name = "canicula-efi" 314 | path = "src/efi.rs" 315 | 316 | [dependencies] 317 | log = "0.4" 318 | xmas-elf = "0.9.1" 319 | uefi = { version = "0.28.0", features = ["logger", "panic_handler"] } 320 | 321 | [build-dependencies] 322 | toml = "0.8.13" 323 | ``` 324 | 325 | ```rust 326 | // src/efi.rs 327 | #![no_std] 328 | #![no_main] 329 | 330 | mod config; 331 | 332 | use log::info; 333 | use uefi::{ 334 | prelude::*, 335 | proto::media::{ 336 | file::{File, FileAttribute, FileInfo, FileMode, FileType}, 337 | fs::SimpleFileSystem, 338 | }, 339 | table::boot::{AllocateType, MemoryType}, 340 | CStr16, 341 | }; 342 | use xmas_elf::ElfFile; 343 | 344 | use crate::config::x86_64::{FILE_BUFFER_SIZE, KERNEL_PATH, PAGE_SIZE}; 345 | 346 | #[entry] 347 | fn main(_image_handle: Handle, mut system_table: SystemTable) -> Status { 348 | uefi::helpers::init(&mut system_table).unwrap(); 349 | info!("Canicula: Starting the UEFI bootloader..."); 350 | info!( 351 | "config: file_buffer_size = {}, page_size = {}, kernel_path = {}", 352 | FILE_BUFFER_SIZE, PAGE_SIZE, KERNEL_PATH 353 | ); 354 | 355 | // load boot table 356 | let boot_services = system_table.boot_services(); 357 | 358 | // load simple file system protocol 359 | let simple_file_system_handle = boot_services 360 | .get_handle_for_protocol::() 361 | .expect("Cannot get protocol handle"); 362 | 363 | let mut simple_file_system_protocol = boot_services 364 | .open_protocol_exclusive::(simple_file_system_handle) 365 | .expect("Cannot get simple file system protocol"); 366 | 367 | // open volume 368 | let mut root = simple_file_system_protocol 369 | .open_volume() 370 | .expect("Cannot open volume"); 371 | 372 | // open kernel file in the root using simple file system 373 | let mut kernel_path_buffer = [0u16; FILE_BUFFER_SIZE]; 374 | let kernel_path = CStr16::from_str_with_buf(KERNEL_PATH, &mut kernel_path_buffer) 375 | .expect("Invalid kernel path!"); 376 | let kernel_file_handle = root 377 | .open(kernel_path, FileMode::Read, FileAttribute::empty()) 378 | .expect("Cannot open kernel file"); 379 | let mut kernel_file = match kernel_file_handle.into_type().unwrap() { 380 | FileType::Regular(f) => f, 381 | _ => panic!("This file does not exist!"), 382 | }; 383 | info!("Kernel file opened successfully!"); 384 | 385 | // load kernel file info and size 386 | let mut kernel_file_info_buffer = [0u8; FILE_BUFFER_SIZE]; 387 | let kernel_file_info: &mut FileInfo = kernel_file 388 | .get_info(&mut kernel_file_info_buffer) 389 | .expect("Cannot get file info"); 390 | info!("Kernel file info: {:?}", kernel_file_info); 391 | let kernel_file_size = 392 | usize::try_from(kernel_file_info.file_size()).expect("Invalid file size!"); 393 | info!("Kernel file size: {:?}", kernel_file_size); 394 | 395 | // load kernel file into memory 396 | let kernel_file_address = boot_services 397 | .allocate_pages( 398 | AllocateType::AnyPages, 399 | MemoryType::LOADER_DATA, 400 | kernel_file_size / PAGE_SIZE + 1, 401 | ) 402 | .expect("Cannot allocate memory in the RAM!") as *mut u8; 403 | 404 | let kernel_file_in_memory = unsafe { 405 | core::ptr::write_bytes(kernel_file_address, 0, kernel_file_size); 406 | core::slice::from_raw_parts_mut(kernel_file_address, kernel_file_size) 407 | }; 408 | let kernel_file_size = kernel_file 409 | .read(kernel_file_in_memory) 410 | .expect("Cannot read file into the memory!"); 411 | info!("Kernel file loaded into memory successfully!"); 412 | 413 | let kernel_content = &mut kernel_file_in_memory[..kernel_file_size]; 414 | let kernel_address = kernel_content.as_ptr() as *const u8 as usize; 415 | info!("Kernel file address: 0x{:x}", kernel_address); 416 | 417 | // parsing kernel elf 418 | let kernel_elf = ElfFile::new(kernel_content).expect("Not a valid ELF file."); 419 | let kernel_entry_offset = kernel_elf.header.pt2.entry_point() as usize; 420 | 421 | let kernel_entry_address = kernel_address + kernel_entry_offest; 422 | 423 | // jmp to kernel 424 | unsafe { 425 | core::arch::asm!("jmp {}", in(reg) kernel_entry_address); 426 | } 427 | 428 | boot_services.stall(10_000_000); 429 | Status::SUCCESS 430 | } 431 | ``` 432 | 433 |
434 | 435 | ### AArch64 436 | 437 | 这里本来应该还有一份 AArch64 的适配代码,~~但因为有点懒了~~ 稍后再补充。 438 | 439 | ## RustSBI 440 | 441 | SBI(Supervisor Binary Interface) 442 | 443 | ### RISC-V64 444 | -------------------------------------------------------------------------------- /docs/dev-environment.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | - [目录](#目录) 3 | - [基本开发环境](#基本开发环境) 4 | - [构建 QEMU](#构建-qemu) 5 | - [配置项目环境](#配置项目环境) 6 | - [x86-64](#x86-64) 7 | - [AArch64](#aarch64) 8 | - [RISC-V](#risc-v) 9 | 10 | 11 | # 基本开发环境 12 | 13 | _本文基于 [Debian 发行版](https://www.debian.org/),如果使用其他发行版可能需要自行补齐依赖。_ 14 | 15 | 为了支持 x86-64、AArch64 和 RISC-V,我们需要这些架构对应的 QEMU 模拟器和在 Rust 中添加差异处理的代码。 16 | 17 | ## 构建 QEMU 18 | 19 | 首先需要构建 [QEMU](https://www.qemu.org/)。 20 | 21 | 1. 安装编译 QEMU 的构建依赖 22 | 23 | ```shell 24 | $ sudo apt install autoconf automake autotools-dev curl wget libmpc-dev libmpfr-dev libgmp-dev \ 25 | gawk build-essential bison flex texinfo gperf libtool patchutils bc \ 26 | zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev libsdl2-dev libslirp-dev \ 27 | git tmux python3 python3-pip ninja-build 28 | ``` 29 | 30 | 2. 下载最新版本的 QEMU 源码 31 | 32 | ```shell 33 | $ wget https://download.qemu.org/qemu-9.0.0.tar.xz 34 | $ tar xvJf qemu-9.0.0.tar.xz 35 | $ cd qemu-9.0.0 36 | ``` 37 | 38 | 3. 编译 39 | 40 | ```shell 41 | # 建议把 --enable-sdl 图形接口支持和 --enable-slirp 网卡支持打开 42 | $ ./configure --target-list=x86_64-softmmu,x86_64-linux-user,riscv64-softmmu,riscv64-linux-user,aarch64-softmmu,aarch64-linux-user \ 43 | --enable-sdl --enable-slirp 44 | $ make -j$(nproc) 45 | ``` 46 | 47 | 4. 配置环境变量 48 | 49 | ```shell 50 | # 记得替换 {path} 为你的路径 51 | $ export PATH=$PATH:/{path}/qemu-9.0.0/build 52 | ``` 53 | 54 | ## 配置项目环境 55 | 56 | 1. 安装 Rust 57 | 58 | ```shell 59 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 60 | ``` 61 | 62 | 2. 安装 Rust 工具链 63 | 64 | ```shell 65 | $ rustup default nightly 66 | # UEFI 的构建目标 67 | $ rustup target add x86_64-unknown-uefi 68 | $ rustup target add aarch64-unknown-uefi 69 | # 内核目标架构的构建目标 70 | $ rustup target add x86_64-unknown-none 71 | $ rustup target add aarch64-unknown-none 72 | $ rustup target add riscv64gc-unknown-none-elf 73 | $ cargo install cargo-binutils 74 | $ rustup component add llvm-tools-preview 75 | ``` 76 | 77 | 3. 使用如下指令构建 x86 版本的内核模块。 78 | 79 | ```shell 80 | $ cargo build --bin canicula-kernel --target x86_64-unknown-none 81 | ``` 82 | 83 | ### x86-64 84 | 85 | 1. 首先编译 x86-64 的 EFI 文件。 86 | 87 | ```shell 88 | $ cargo build --bin canicula-efi --target x86_64-unknown-uefi 89 | $ cp target/x86_64-unknown-uefi/debug/canicula-efi.efi esp/efi/boot/bootx64.efi 90 | ``` 91 | 92 | 2. 启动 QEMU 虚拟机: 93 | 94 | ```shell 95 | $ sudo apt install ovmf 96 | $ qemu-system-x86_64 -enable-kvm \ 97 | -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd \ 98 | -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_VARS.fd \ 99 | -drive format=raw,file=fat:rw:esp 100 | ``` 101 | 102 | 如果出现 `qemu-system-x86_64: failed to initialize kvm: Permission denied` 问题可以尝试 `sudo chmod 666 /dev/kvm`。 103 | 104 | ### AArch64 105 | 106 | ### RISC-V 107 | 108 | 在 RISC-V 架构中,我们使用 [RustSBI](https://github.com/rustsbi/rustsbi) 直接加载内核文件。 109 | 110 | 1. 将 ELF 格式转换为二进制格式。 111 | 112 | ```shell 113 | $ rust-objcopy \ 114 | --binary-architecture=riscv64 target/riscv64gc-unknown-none-elf/release/kernel \ 115 | --strip-all -O binary target/riscv64gc-unknown-none-elf/release/kernel.bin 116 | ``` 117 | 118 | 2. [下载](https://github.com/rustsbi/rustsbi-qemu/releases) 适合 QEMU 使用的 rustsbi 二进制文件。 119 | 120 | 解压获得 `rustsbi-qemu.bin` 文件,它将作为 QEMU 的 BIOS 文件,使用 QEMU 启动内核。 121 | 122 | ```shell 123 | $ qemu-system-riscv64 \ 124 | -machine virt \ 125 | -nographic \ 126 | -bios rustsbi-qemu.bin \ 127 | -device loader,file=target/riscv64gc-unknown-none-elf/release/kernel.bin,addr=0x80200000 128 | ``` 129 | -------------------------------------------------------------------------------- /docs/ext4.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | - [目录](#目录) 3 | - [Ext4](#ext4) 4 | - [构建](#构建) 5 | 6 | 7 | # Ext4 8 | 9 | 参考:[Ext4](https://ext4.wiki.kernel.org/index.php/Main_Page) 10 | 11 | ## 构建 12 | 13 | ```shell 14 | # 对不同架构进行编译 15 | $ cd canicula-ext4 16 | $ cargo build --target x86_64-unknown-none 17 | $ cargo build --target aarch64-unknown-none 18 | $ cargo build --target riscv64gc-unknown-none-elf 19 | # 运行测试 20 | $ cargo test --target x86_64-unknown-linux-gnu -Z build-std=std -- --show-output 21 | ``` 22 | -------------------------------------------------------------------------------- /resources/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanbings/canicula/f29c7b84773bee5ab7891780f7484eccc2b5f049/resources/images/logo.png -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "default" 3 | channel = "nightly-2024-12-17" 4 | components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"] 5 | targets = [ 6 | "x86_64-unknown-uefi", 7 | "aarch64-unknown-uefi", 8 | "x86_64-unknown-none", 9 | "aarch64-unknown-none", 10 | "riscv64gc-unknown-none-elf", 11 | "x86_64-unknown-linux-gnu", 12 | ] --------------------------------------------------------------------------------