├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .qemu └── OVMF-pure-efi.fd ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile.toml ├── README.md ├── compose.rs ├── rust-toolchain ├── xv7-boot ├── .gitignore ├── Cargo.toml └── src │ ├── config.rs │ └── lib.rs ├── xv7-bootloader-uefi ├── .gitignore ├── Cargo.toml ├── Makefile.toml └── src │ ├── config.rs │ ├── io.rs │ ├── loader.rs │ ├── macros.rs │ ├── main.rs │ └── paging.rs ├── xv7-kernel ├── .gitignore ├── Cargo.toml ├── Makefile.toml ├── resources │ ├── logo.bmp │ ├── logo.psd │ └── logo@2x.bmp └── src │ ├── allocator.rs │ ├── ansi.rs │ ├── arch.rs │ ├── arch │ ├── aarch64.json │ ├── aarch64 │ │ ├── mod.rs │ │ └── start.rs │ ├── x86_64.json │ └── x86_64 │ │ ├── allocator.rs │ │ ├── config.rs │ │ ├── console.rs │ │ ├── context.rs │ │ ├── cpuid.rs │ │ ├── device.rs │ │ ├── device │ │ ├── monitor_console.rs │ │ ├── serial_console.rs │ │ └── uart.rs │ │ ├── gdt.rs │ │ ├── interrupt.rs │ │ ├── interrupt │ │ ├── breakpoint.rs │ │ ├── com.rs │ │ ├── controller.rs │ │ ├── controller │ │ │ ├── ioapic.rs │ │ │ ├── lapic.rs │ │ │ └── pic.rs │ │ ├── double_fault.rs │ │ ├── general_protection_fault.rs │ │ ├── keyboard.rs │ │ ├── page_fault.rs │ │ └── timer.rs │ │ ├── macros.rs │ │ ├── mod.rs │ │ ├── paging.rs │ │ ├── start.rs │ │ └── syscall.rs │ ├── config.rs │ ├── cpu.rs │ ├── device.rs │ ├── device │ └── console.rs │ ├── fs.rs │ ├── fs │ ├── dev.rs │ ├── file.rs │ └── vfs.rs │ ├── macros.rs │ ├── main.rs │ ├── memory │ ├── bitmap.rs │ ├── buddy.rs │ └── mod.rs │ ├── pretty.rs │ ├── process.rs │ ├── rt.rs │ ├── scheduler.rs │ ├── syscall.rs │ ├── syscall │ ├── fs.rs │ └── process.rs │ └── video.rs ├── xv7-user ├── Cargo.toml ├── Makefile.toml └── src │ ├── bin │ └── init.rs │ ├── io.rs │ ├── lib.rs │ ├── macros.rs │ ├── process.rs │ └── rt.rs └── xv7-usyscall ├── Cargo.toml ├── Makefile.toml └── src ├── arch.rs ├── arch └── x86_64.rs ├── error.rs ├── lib.rs ├── number.rs └── syscall.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: Install latest nightly rust toolchain 12 | uses: actions-rs/toolchain@v1 13 | with: 14 | toolchain: nightly 15 | override: true 16 | - name: Install rust-src 17 | run: rustup component add rust-src 18 | - name: Install cargo-make 19 | run: cargo install cargo-make 20 | - name: Build 21 | run: cargo make xbuild 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | 4 | # Created by https://www.gitignore.io/api/macos 5 | # Edit at https://www.gitignore.io/?templates=macos 6 | 7 | ### macOS ### 8 | # General 9 | .DS_Store 10 | .AppleDouble 11 | .LSOverride 12 | 13 | # Icon must end with two \r 14 | Icon 15 | 16 | # Thumbnails 17 | ._* 18 | 19 | # Files that might appear in the root of a volume 20 | .DocumentRevisions-V100 21 | .fseventsd 22 | .Spotlight-V100 23 | .TemporaryItems 24 | .Trashes 25 | .VolumeIcon.icns 26 | .com.apple.timemachine.donotpresent 27 | 28 | # Directories potentially created on remote AFP share 29 | .AppleDB 30 | .AppleDesktop 31 | Network Trash Folder 32 | Temporary Items 33 | .apdisk 34 | 35 | # End of https://www.gitignore.io/api/macos 36 | -------------------------------------------------------------------------------- /.qemu/OVMF-pure-efi.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imtsuki/xv7/edab461a6a7ceab2236e24ca726598107d346467/.qemu/OVMF-pure-efi.fd -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayvec" 7 | version = "0.5.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 10 | 11 | [[package]] 12 | name = "autocfg" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 16 | 17 | [[package]] 18 | name = "bit_field" 19 | version = "0.9.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" 22 | 23 | [[package]] 24 | name = "bit_field" 25 | version = "0.10.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" 28 | 29 | [[package]] 30 | name = "bitflags" 31 | version = "1.2.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 34 | 35 | [[package]] 36 | name = "bitvec" 37 | version = "0.17.4" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" 40 | dependencies = [ 41 | "either", 42 | "radium", 43 | ] 44 | 45 | [[package]] 46 | name = "byteorder" 47 | version = "1.3.4" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 50 | 51 | [[package]] 52 | name = "cfg-if" 53 | version = "0.1.10" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 56 | 57 | [[package]] 58 | name = "cfg-if" 59 | version = "1.0.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 62 | 63 | [[package]] 64 | name = "chrono" 65 | version = "0.4.13" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" 68 | dependencies = [ 69 | "num-integer", 70 | "num-traits", 71 | ] 72 | 73 | [[package]] 74 | name = "cortex-a" 75 | version = "2.9.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "fd807a12241940332c7c9f6aeaf3f7539d4213853d32445d17c97f3e7eebe118" 78 | dependencies = [ 79 | "register", 80 | ] 81 | 82 | [[package]] 83 | name = "crossbeam-queue" 84 | version = "0.2.3" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" 87 | dependencies = [ 88 | "cfg-if 0.1.10", 89 | "crossbeam-utils", 90 | "maybe-uninit", 91 | ] 92 | 93 | [[package]] 94 | name = "crossbeam-utils" 95 | version = "0.7.2" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 98 | dependencies = [ 99 | "autocfg", 100 | "cfg-if 0.1.10", 101 | ] 102 | 103 | [[package]] 104 | name = "either" 105 | version = "1.5.3" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 108 | 109 | [[package]] 110 | name = "embedded-graphics" 111 | version = "0.6.2" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "40a69991ceb896bd4810a0cf2bcc46fc94b7860573c71f965d8e5b3d66942fed" 114 | dependencies = [ 115 | "byteorder", 116 | ] 117 | 118 | [[package]] 119 | name = "goblin" 120 | version = "0.4.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "532a09cd3df2c6bbfc795fb0434bff8f22255d1d07328180e918a2e6ce122d4d" 123 | dependencies = [ 124 | "log", 125 | "plain", 126 | "scroll", 127 | ] 128 | 129 | [[package]] 130 | name = "lazy_static" 131 | version = "1.4.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 134 | dependencies = [ 135 | "spin 0.5.2", 136 | ] 137 | 138 | [[package]] 139 | name = "linked_list_allocator" 140 | version = "0.8.11" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24" 143 | dependencies = [ 144 | "spinning_top", 145 | ] 146 | 147 | [[package]] 148 | name = "lock_api" 149 | version = "0.3.4" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 152 | dependencies = [ 153 | "scopeguard", 154 | ] 155 | 156 | [[package]] 157 | name = "lock_api" 158 | version = "0.4.3" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" 161 | dependencies = [ 162 | "scopeguard", 163 | ] 164 | 165 | [[package]] 166 | name = "log" 167 | version = "0.4.11" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 170 | dependencies = [ 171 | "cfg-if 0.1.10", 172 | ] 173 | 174 | [[package]] 175 | name = "maybe-uninit" 176 | version = "2.0.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 179 | 180 | [[package]] 181 | name = "memchr" 182 | version = "2.3.3" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 185 | 186 | [[package]] 187 | name = "nom" 188 | version = "5.1.2" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" 191 | dependencies = [ 192 | "memchr", 193 | "version_check", 194 | ] 195 | 196 | [[package]] 197 | name = "num-integer" 198 | version = "0.1.43" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 201 | dependencies = [ 202 | "autocfg", 203 | "num-traits", 204 | ] 205 | 206 | [[package]] 207 | name = "num-traits" 208 | version = "0.2.12" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 211 | dependencies = [ 212 | "autocfg", 213 | ] 214 | 215 | [[package]] 216 | name = "pc-keyboard" 217 | version = "0.5.1" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac" 220 | 221 | [[package]] 222 | name = "plain" 223 | version = "0.2.3" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 226 | 227 | [[package]] 228 | name = "proc-macro2" 229 | version = "1.0.19" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 232 | dependencies = [ 233 | "unicode-xid", 234 | ] 235 | 236 | [[package]] 237 | name = "quote" 238 | version = "1.0.7" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 241 | dependencies = [ 242 | "proc-macro2", 243 | ] 244 | 245 | [[package]] 246 | name = "radium" 247 | version = "0.3.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" 250 | 251 | [[package]] 252 | name = "raw-cpuid" 253 | version = "9.0.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "c27cb5785b85bd05d4eb171556c9a1a514552e26123aeae6bb7d811353148026" 256 | dependencies = [ 257 | "bitflags", 258 | ] 259 | 260 | [[package]] 261 | name = "register" 262 | version = "0.5.1" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "deaba5b0e477d21f61a57504bb5cef4a1e86de30300b457d38971c1cfc98b815" 265 | dependencies = [ 266 | "tock-registers", 267 | ] 268 | 269 | [[package]] 270 | name = "scopeguard" 271 | version = "1.1.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 274 | 275 | [[package]] 276 | name = "scroll" 277 | version = "0.10.1" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" 280 | dependencies = [ 281 | "scroll_derive", 282 | ] 283 | 284 | [[package]] 285 | name = "scroll_derive" 286 | version = "0.10.2" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9" 289 | dependencies = [ 290 | "proc-macro2", 291 | "quote", 292 | "syn", 293 | ] 294 | 295 | [[package]] 296 | name = "spin" 297 | version = "0.5.2" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 300 | 301 | [[package]] 302 | name = "spin" 303 | version = "0.9.0" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "b87bbf98cb81332a56c1ee8929845836f85e8ddd693157c30d76660196014478" 306 | dependencies = [ 307 | "lock_api 0.4.3", 308 | ] 309 | 310 | [[package]] 311 | name = "spinning_top" 312 | version = "0.1.1" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "047031d6df5f5ae0092c97aa4f6bb04cfc9c081b4cd4cb9cdb38657994279a00" 315 | dependencies = [ 316 | "lock_api 0.3.4", 317 | ] 318 | 319 | [[package]] 320 | name = "syn" 321 | version = "1.0.38" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" 324 | dependencies = [ 325 | "proc-macro2", 326 | "quote", 327 | "unicode-xid", 328 | ] 329 | 330 | [[package]] 331 | name = "tinybmp" 332 | version = "0.2.3" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "781e52493917138f60c3b98b95e39967eafd28d3c470d0d35ae020b47f011b5d" 335 | dependencies = [ 336 | "embedded-graphics", 337 | "nom", 338 | ] 339 | 340 | [[package]] 341 | name = "tock-registers" 342 | version = "0.5.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "70323afdb8082186c0986da0e10f6e4ed103d681c921c00597e98d9806dac20f" 345 | 346 | [[package]] 347 | name = "uart_16550" 348 | version = "0.2.12" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "8d45a3c9181dc9ba7d35d02b3c36d1604155289d935b7946510fb3d2f4b976d9" 351 | dependencies = [ 352 | "bitflags", 353 | "x86_64 0.13.6", 354 | ] 355 | 356 | [[package]] 357 | name = "ucs2" 358 | version = "0.3.1" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" 361 | dependencies = [ 362 | "bit_field 0.10.0", 363 | ] 364 | 365 | [[package]] 366 | name = "uefi" 367 | version = "0.8.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "41f86f972322768901e872186fc8b7df009ca4ce3e945fc337614cdca0c526d4" 370 | dependencies = [ 371 | "bitflags", 372 | "log", 373 | "ucs2", 374 | "uefi-macros", 375 | ] 376 | 377 | [[package]] 378 | name = "uefi-macros" 379 | version = "0.3.3" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "3dcca10ca861f34a320d178f3fdb29ffbf05087fc2c70d2a99860e3329bee1a8" 382 | dependencies = [ 383 | "proc-macro2", 384 | "quote", 385 | "syn", 386 | ] 387 | 388 | [[package]] 389 | name = "uefi-services" 390 | version = "0.5.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "03df29f1a75c0b92df0b701f815fa3e66a25cb510f99a8d59f720b289ed94a3d" 393 | dependencies = [ 394 | "cfg-if 1.0.0", 395 | "log", 396 | "uefi", 397 | "x86_64 0.13.6", 398 | ] 399 | 400 | [[package]] 401 | name = "unicode-xid" 402 | version = "0.2.1" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 405 | 406 | [[package]] 407 | name = "utf8parse" 408 | version = "0.2.0" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" 411 | 412 | [[package]] 413 | name = "version_check" 414 | version = "0.9.2" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 417 | 418 | [[package]] 419 | name = "volatile" 420 | version = "0.4.4" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0" 423 | 424 | [[package]] 425 | name = "vte" 426 | version = "0.7.1" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "8299f6cd1505d50a633aa31b4088aeffe511099d8c7bc38e5d3669379a08a2b8" 429 | dependencies = [ 430 | "arrayvec", 431 | "utf8parse", 432 | "vte_generate_state_changes", 433 | ] 434 | 435 | [[package]] 436 | name = "vte_generate_state_changes" 437 | version = "0.1.1" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" 440 | dependencies = [ 441 | "proc-macro2", 442 | "quote", 443 | ] 444 | 445 | [[package]] 446 | name = "x86_64" 447 | version = "0.13.6" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "021b49a4cb0a0d9490265cc169ca816014cbf61d3f3b75424815912977b81871" 450 | dependencies = [ 451 | "bit_field 0.9.0", 452 | "bitflags", 453 | ] 454 | 455 | [[package]] 456 | name = "x86_64" 457 | version = "0.14.0" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "c48c878623ca5318ce0d3fde56541e05c973e1683cfc19d5384db516999569dc" 460 | dependencies = [ 461 | "bit_field 0.9.0", 462 | "bitflags", 463 | "volatile", 464 | ] 465 | 466 | [[package]] 467 | name = "xv7-boot" 468 | version = "0.1.0" 469 | dependencies = [ 470 | "uefi", 471 | "x86_64 0.14.0", 472 | ] 473 | 474 | [[package]] 475 | name = "xv7-bootloader-uefi" 476 | version = "0.1.0" 477 | dependencies = [ 478 | "chrono", 479 | "goblin", 480 | "log", 481 | "uefi", 482 | "uefi-services", 483 | "x86_64 0.14.0", 484 | "xv7-boot", 485 | "zeroize", 486 | ] 487 | 488 | [[package]] 489 | name = "xv7-kernel" 490 | version = "0.1.0" 491 | dependencies = [ 492 | "bitvec", 493 | "cortex-a", 494 | "crossbeam-queue", 495 | "embedded-graphics", 496 | "goblin", 497 | "lazy_static", 498 | "linked_list_allocator", 499 | "pc-keyboard", 500 | "raw-cpuid", 501 | "spin 0.9.0", 502 | "tinybmp", 503 | "uart_16550", 504 | "uefi", 505 | "vte", 506 | "x86_64 0.14.0", 507 | "xv7-boot", 508 | "xv7-usyscall", 509 | "zeroize", 510 | ] 511 | 512 | [[package]] 513 | name = "xv7-user" 514 | version = "0.1.0" 515 | dependencies = [ 516 | "xv7-usyscall", 517 | ] 518 | 519 | [[package]] 520 | name = "xv7-usyscall" 521 | version = "0.1.0" 522 | 523 | [[package]] 524 | name = "zeroize" 525 | version = "1.1.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" 528 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "xv7-usyscall", 4 | "xv7-user", 5 | "xv7-kernel", 6 | "xv7-boot", 7 | "xv7-bootloader-uefi", 8 | ] 9 | 10 | [profile.dev] 11 | panic = "abort" 12 | 13 | [profile.release] 14 | panic = "abort" 15 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | https://www.apache.org/licenses/LICENSE-2.0 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) 2020 imtsuki 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | https://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 imtsuki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | CARGO_MAKE_WORKSPACE_SKIP_MEMBERS = "xv7-boot;xv7-usyscall" 3 | ESP = "target/esp" 4 | QEMU_X86_64 = "qemu-system-x86_64" 5 | QEMU_X86_64_OPT = ''' 6 | -drive if=pflash,format=raw,file=.qemu/OVMF-pure-efi.fd,readonly=on 7 | -device isa-debug-exit,iobase=0xf4,iosize=0x04 8 | -serial stdio 9 | -m 1G 10 | -net none 11 | ''' 12 | 13 | [tasks.xbuild] 14 | command = "cargo" 15 | args = ["xbuild"] 16 | 17 | [tasks.member-xbuild] 18 | command = "cargo" 19 | args = ["make", "xbuild"] 20 | 21 | [tasks.compose-esp] 22 | workspace = false 23 | dependencies = ["member-xbuild"] 24 | script_runner = "@shell" 25 | script = [ 26 | ''' 27 | mkdir -p ${ESP}/EFI/Boot 28 | cp target/x86_64-unknown-uefi/debug/xv7-bootloader-uefi.efi ${ESP}/EFI/Boot/BootX64.efi 29 | mkdir -p ${ESP}/EFI/xv7 30 | cp target/x86_64/debug/xv7-kernel ${ESP}/EFI/xv7/kernel 31 | rust-objdump \ 32 | --disassemble \ 33 | --source \ 34 | --dynamic-reloc \ 35 | ${ESP}/EFI/xv7/kernel | \ 36 | rustfilt > \ 37 | ${ESP}/EFI/xv7/kernel.asm || true 38 | ''' 39 | ] 40 | 41 | 42 | [tasks.qemu] 43 | workspace = false 44 | dependencies = ["compose-esp"] 45 | script_runner = "@shell" 46 | script = [ 47 | ''' 48 | ${QEMU_X86_64} ${QEMU_X86_64_OPT} \ 49 | -drive format=raw,file=fat:rw:${ESP} 50 | ''' 51 | ] 52 | 53 | [tasks.qemu-kvm] 54 | workspace = false 55 | dependencies = ["compose-esp"] 56 | script_runner = "@shell" 57 | script = [ 58 | ''' 59 | ${QEMU_X86_64} ${QEMU_X86_64_OPT} \ 60 | -drive format=raw,file=fat:rw:${ESP} \ 61 | -enable-kvm 62 | ''' 63 | ] 64 | 65 | [tasks.qemu-gdb] 66 | workspace = false 67 | dependencies = ["compose-esp"] 68 | script_runner = "@shell" 69 | script = [ 70 | ''' 71 | ${QEMU_X86_64} ${QEMU_X86_64_OPT} \ 72 | -drive format=raw,file=fat:rw:${ESP} \ 73 | -S -gdb tcp::1234 74 | ''' 75 | ] 76 | 77 | [tasks.doc] 78 | workspace = false 79 | command = "cargo" 80 | args = ["doc", "--no-deps", "--open"] 81 | 82 | [tasks.compose-image] 83 | workspace = false 84 | dependencies = ["member-xbuild"] 85 | script_runner = "@rust" 86 | script = { file = "compose.rs" } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xv7 2 | 3 | You can view the development notes on [the Telegram Channel](https://t.me/xv7notes). 4 | 5 | ## Prerequisites 6 | 7 | Install cargo-make: 8 | 9 | ```bash 10 | cargo install cargo-make 11 | ``` 12 | 13 | Also, you should have the latest version of [QEMU](https://www.qemu.org) installed. 14 | 15 | ## Build and Run 16 | 17 | Simply run the following command: 18 | 19 | ```bash 20 | cargo make qemu 21 | ``` 22 | 23 | You can use KVM to speed up emulation: 24 | 25 | ```bash 26 | cargo make qemu-kvm 27 | ``` 28 | 29 | To compose a bootable USB drive: 30 | 31 | ```bash 32 | cargo make compose-esp --env ESP=/path/to/your/drive 33 | ``` 34 | 35 | ## Tips 36 | 37 | For better development experience, install these additional tools: 38 | 39 | ```bash 40 | rustup component add llvm-tools-preview 41 | cargo install cargo-binutils 42 | cargo install rustfilt 43 | ``` 44 | 45 | ## License 46 | 47 | This project is licensed under either of 48 | 49 | - Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 50 | [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) 51 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or 52 | [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) 53 | 54 | at your option. 55 | -------------------------------------------------------------------------------- /compose.rs: -------------------------------------------------------------------------------- 1 | //! ```cargo 2 | //! [dependencies] 3 | //! fatfs = "0.3.3" 4 | //! tempfile = "3.1.0" 5 | //! ``` 6 | 7 | fn main() { 8 | println!("This is a WIP compose script"); 9 | } 10 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /xv7-boot/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /xv7-boot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xv7-boot" 3 | version = "0.1.0" 4 | authors = ["imtsuki "] 5 | license = "MIT OR Apache-2.0" 6 | edition = "2018" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | x86_64 = "0.14.0" 12 | uefi = "0.8.0" 13 | -------------------------------------------------------------------------------- /xv7-boot/src/config.rs: -------------------------------------------------------------------------------- 1 | //! # Configuration Constants 2 | //! 3 | //! ## Virtual memory map of xv7 4 | //! 5 | //! | Range | Size | Description | Constant | 6 | //! |-------------------------------------------------|----------|-----------------------------------------|-----------------------| 7 | //! | `0x0000_0000_0000_0000 ~ 0x0000_7FFF_FFFF_FFFF` | 128 TiB | User space | | 8 | //! | `0xFFFF_8000_0000_0000 ~ 0xFFFF_BFFF_FFFF_FFFF` | 64 TiB | Direct physical memory mapping | [`PAGE_OFFSET_BASE`] | 9 | //! | `0xFFFF_C000_0000_0000 ~ 0xFFFF_CFFF_FFFF_FFFF` | 16 TiB | Kernel `.text` `.data` `.rodata` `.bss` | [`KERNEL_BASE`] | 10 | //! | `0xFFFF_D000_0000_0000 ~ 0xFFFF_DFFF_FFFF_FFFF` | 16 TiB | Kernel heap and stack | [`KERNEL_HEAP_BASE`] | 11 | //! | `0xFFFF_E000_0000_0000 ~ 0xFFFF_E7FF_FFFF_FFFF` | 8 TiB | UEFI mapping | [`UEFI_MAPPING_BASE`] | 12 | //! | `0xFFFF_E800_0000_0000 ~ 0xFFFF_EFFF_FFFF_FFFF` | 8 TiB | MMIO, Device space | [`DEVICE_BASE`] | 13 | //! | `0xFFFF_F000_0000_0000 ~ 0xFFFF_FF7F_FFFF_FFFF` | 15.5 TiB | Guard hole (Unused) | | 14 | //! | `0xFFFF_FF80_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF` | 0.5 TiB | Recursive page table | | 15 | 16 | /// Direct physical memory mapping. 17 | pub const PAGE_OFFSET_BASE: u64 = 0xFFFF_8000_0000_0000; 18 | /// Base address where kernel is loaded. `.text` `.data` `.rodata` `.bss` 19 | pub const KERNEL_BASE: u64 = 0xFFFF_C000_0000_0000; 20 | /// Kernel heap. 21 | pub const KERNEL_HEAP_BASE: u64 = 0xFFFF_D000_0000_0000; 22 | /// Size of kernel heap. 23 | pub const KERNEL_HEAP_SIZE: usize = 128 * 1024; 24 | /// Top address of kernel stack. 25 | pub const KERNEL_STACK_TOP: u64 = 0xFFFF_E000_0000_0000; 26 | /// UEFI mapping 27 | #[allow(unused)] 28 | pub const UEFI_MAPPING_BASE: u64 = 0xFFFF_E000_0000_0000; 29 | /// MMIO, Device space 30 | #[allow(unused)] 31 | pub const DEVICE_BASE: u64 = 0xFFFF_E800_0000_0000; 32 | 33 | /// Initial kernel stack size. 34 | pub const KERNEL_STACK_SIZE: usize = 0x1_0000; 35 | -------------------------------------------------------------------------------- /xv7-boot/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! FIXME: Add missing docs 2 | 3 | #![no_std] 4 | #![deny(missing_docs)] 5 | 6 | pub mod config; 7 | 8 | use core::fmt; 9 | use uefi::table::boot::MemoryDescriptor; 10 | pub use x86_64::{PhysAddr, VirtAddr}; 11 | 12 | /// Function signature for kernel entry point. 13 | #[cfg(target_arch = "x86_64")] 14 | pub type KernelEntryFn = extern "sysv64" fn(args: &BootArgs) -> !; 15 | 16 | /// Function signature for kernel entry point. 17 | /// 18 | /// FIXME: Do we really need a bootloader for aarch64? 19 | #[cfg(target_arch = "aarch64")] 20 | pub type KernelEntryFn = extern "C" fn(args: &BootArgs) -> !; 21 | 22 | // For other platforms, no bootloader is needed AFAIK. 23 | #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] 24 | pub type KernelEntryFn = extern "C" fn() -> !; 25 | 26 | /// Kernel entry's virtual address. 27 | #[derive(Clone, Copy, Debug)] 28 | #[repr(transparent)] 29 | pub struct KernelEntry(pub VirtAddr); 30 | 31 | impl Into for KernelEntry { 32 | fn into(self) -> VirtAddr { 33 | self.0 34 | } 35 | } 36 | 37 | impl From for KernelEntry { 38 | fn from(addr: VirtAddr) -> Self { 39 | Self(addr) 40 | } 41 | } 42 | 43 | /// Bootloader passes `BootArgs` to the kernel entry, 44 | /// containing boot information. 45 | #[derive(Clone, Debug)] 46 | #[repr(C)] 47 | pub struct BootArgs { 48 | /// Magic number for checking whether `BootArgs` is passed correctly 49 | pub magic: u64, 50 | /// Video frame buffer 51 | pub frame_buffer: FrameBufferDescriptor, 52 | /// Memory map 53 | pub memory_map: MemoryMap, 54 | /* pub memory_map: &'static [MemoryDescriptor], */ 55 | } 56 | 57 | /// Workaround 58 | #[derive(Debug, Clone)] 59 | pub struct MemoryMapIter<'buf> { 60 | buffer: &'buf [u8], 61 | entry_size: usize, 62 | index: usize, 63 | len: usize, 64 | } 65 | 66 | impl<'buf> Iterator for MemoryMapIter<'buf> { 67 | type Item = &'buf MemoryDescriptor; 68 | 69 | fn size_hint(&self) -> (usize, Option) { 70 | let sz = self.len - self.index; 71 | 72 | (sz, Some(sz)) 73 | } 74 | 75 | fn next(&mut self) -> Option { 76 | if self.index < self.len { 77 | let ptr = self.buffer.as_ptr() as usize + self.entry_size * self.index; 78 | 79 | self.index += 1; 80 | 81 | let descriptor = unsafe { &*(ptr as *const MemoryDescriptor) }; 82 | 83 | Some(descriptor) 84 | } else { 85 | None 86 | } 87 | } 88 | } 89 | 90 | impl ExactSizeIterator for MemoryMapIter<'_> {} 91 | 92 | /// Memory map 93 | #[repr(C)] 94 | pub struct MemoryMap { 95 | /// Memory map iterator 96 | pub iter: MemoryMapIter<'static>, 97 | } 98 | 99 | impl Clone for MemoryMap { 100 | fn clone(&self) -> Self { 101 | unsafe { core::ptr::read(self) } 102 | } 103 | } 104 | 105 | impl fmt::Debug for MemoryMap { 106 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 107 | f.debug_struct("MemoryMap") 108 | .field("len", &self.iter.len()) 109 | .finish() 110 | } 111 | } 112 | 113 | /// `BootArgs` magic value. 114 | pub const BOOT_ARGS_MAGIC: u64 = 0xcafe_beef; 115 | 116 | /// Represents a range of pyhsical memory. 117 | #[derive(Clone, Copy, Debug)] 118 | #[repr(C)] 119 | pub struct PhysMemoryDescriptor { 120 | /// Pyhsical Memory type 121 | pub memory_type: PhysMemoryType, 122 | /// Base address, should be aligned to `PAGE_SIZE` 123 | pub base: PhysAddr, 124 | /// number of pages in this range 125 | pub page_count: usize, 126 | } 127 | 128 | /// Types of `PhysMemoryDescriptor`. 129 | #[derive(Clone, Copy, Debug)] 130 | #[repr(C)] 131 | pub enum PhysMemoryType { 132 | /// Conventional memory, can be used freely 133 | Usable, 134 | /// Occupied by kernel 135 | Kernel, 136 | /// ACPI related memory region. 137 | Acpi, 138 | /// Used for UEFI Runtime services. 139 | UefiRuntime, 140 | /// Reserved 141 | Reserved, 142 | } 143 | 144 | /// Describe video frame buffer. 145 | #[derive(Clone, Copy, Debug)] 146 | #[repr(C)] 147 | pub struct FrameBufferDescriptor { 148 | /// Base address 149 | pub base: PhysAddr, 150 | /// buffer length 151 | pub len: usize, 152 | /// resolution 153 | pub resolution: (usize, usize), 154 | } 155 | 156 | #[allow(missing_docs)] 157 | #[derive(Clone, Copy, Debug)] 158 | #[repr(C, packed)] 159 | pub struct RsdpDescriptor { 160 | signature: [u8; 8], 161 | checksum: u8, 162 | oem_id: [u8; 6], 163 | revision: u8, 164 | rsdt_address: u32, 165 | } 166 | 167 | #[allow(missing_docs)] 168 | #[derive(Clone, Copy, Debug)] 169 | #[repr(C, packed)] 170 | pub struct RsdpDescriptor20 { 171 | first_part: RsdpDescriptor, 172 | length: u32, 173 | xsdt_address: u64, 174 | extended_checksum: u8, 175 | reserved: [u8; 3], 176 | } 177 | 178 | #[allow(missing_docs)] 179 | #[derive(Clone, Copy, Debug)] 180 | #[repr(C, packed)] 181 | pub struct SmbiosEntryPoint { 182 | /// This is `_SM_` 183 | entry_point_string: [u8; 4], 184 | /// This value summed with all the values of the table, should be 0 (overflow) 185 | checksum: u8, 186 | /// Length of the Entry Point Table. Since version 2.1 of SMBIOS, this is 0x1F 187 | length: u8, 188 | /// Major Version of SMBIOS 189 | major_version: u8, 190 | /// Minor Version of SMBIOS 191 | minor_version: u8, 192 | /// Maximum size of a SMBIOS Structure (we will se later) 193 | max_structure_size: u16, 194 | /// ... 195 | entry_point_revision: u8, 196 | /// ... 197 | formatted_area: [u8; 5], 198 | /// This is `_DMI_` 199 | entry_point_string2: [u8; 5], 200 | /// Checksum for values from EntryPointString2 to the end of table 201 | checksum2: u8, 202 | /// Length of the Table containing all the structures 203 | table_length: u16, 204 | /// Address of the Table 205 | table_address: u32, 206 | /// Number of structures in the table 207 | number_of_structures: u16, 208 | /// Unused 209 | bcd_revision: u8, 210 | } 211 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xv7-bootloader-uefi" 3 | version = "0.1.0" 4 | authors = ["imtsuki "] 5 | license = "MIT OR Apache-2.0" 6 | description = "XV7 UEFI Bootloader" 7 | edition = "2018" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | uefi = "0.8.0" 13 | uefi-services = "=0.5.0" 14 | x86_64 = "0.14.0" 15 | zeroize = "1.1.0" 16 | goblin = { version = "0.4.0", default-features = false, features = ["elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive", "endian_fd"] } 17 | log = { version = "0.4.8", default-features = false } 18 | chrono = { version = "0.4.11", default-features = false } 19 | boot = { path = "../xv7-boot", package = "xv7-boot" } 20 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.xbuild] 2 | command = "cargo" 3 | args = ["xbuild", "--target", "x86_64-unknown-uefi"] 4 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/src/config.rs: -------------------------------------------------------------------------------- 1 | /// Kernel image path in ESP. 2 | pub const KERNEL_IMAGE_PATH: &'static str = r"\EFI\xv7\kernel"; 3 | 4 | pub use boot::config::*; 5 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/src/io.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use uefi::prelude::*; 3 | use uefi::proto::media::file::*; 4 | use uefi::proto::media::fs::SimpleFileSystem; 5 | use uefi::Result; 6 | 7 | pub fn read_file(services: &BootServices, path: &str) -> Result<(usize, Vec)> { 8 | let fatfs = services 9 | .locate_protocol::() 10 | .log_warning()?; 11 | let fatfs = unsafe { &mut *fatfs.get() }; 12 | 13 | let mut volume = fatfs.open_volume().log_warning()?; 14 | 15 | let file_handle = volume 16 | .open(path, FileMode::Read, FileAttribute::empty()) 17 | .log_warning()?; 18 | 19 | let mut file = match file_handle.into_type().log_warning()? { 20 | FileType::Regular(file) => file, 21 | FileType::Dir(_) => unreachable!(), 22 | }; 23 | 24 | // Use an empty buffer to retrieve the actual FileInfo size 25 | let mut empty_buf = Vec::new(); 26 | let len = match *file 27 | .get_info::(&mut empty_buf) 28 | .expect_error("passing an empty buffer will return the size of FileInfo") 29 | .data() 30 | { 31 | Some(len) => len, 32 | None => unreachable!(), 33 | }; 34 | 35 | let mut file_info = vec![0u8; len]; 36 | let file_info = file 37 | .get_info::(&mut file_info) 38 | .discard_errdata() 39 | .log_warning()?; 40 | 41 | let mut buf = vec![0u8; file_info.file_size() as usize]; 42 | let len = file.read(&mut buf).discard_errdata().log_warning()?; 43 | 44 | Ok((len, buf).into()) 45 | } 46 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/src/loader.rs: -------------------------------------------------------------------------------- 1 | use crate::config::*; 2 | use crate::io::read_file; 3 | use boot::KernelEntry; 4 | use goblin::elf; 5 | use goblin::elf::reloc::*; 6 | use uefi::prelude::*; 7 | use uefi::table::boot::{AllocateType, MemoryType}; 8 | use x86_64::structures::paging::FrameAllocator; 9 | use x86_64::structures::paging::{Mapper, Page, PageSize, PageTableFlags, PhysFrame, Size4KiB}; 10 | use x86_64::{align_down, align_up, PhysAddr, VirtAddr}; 11 | use zeroize::Zeroize; 12 | 13 | /// Loads kernel image to `KERNEL_BASE`. 14 | /// Returns entry's virtual address. 15 | pub fn load_elf( 16 | services: &BootServices, 17 | page_table: &mut impl Mapper, 18 | allocator: &mut impl FrameAllocator, 19 | path: &str, 20 | ) -> KernelEntry { 21 | let (len, kernel_image) = 22 | read_file(services, path).expect_success("Could not load kernel image"); 23 | 24 | dbg!(len); 25 | 26 | let kernel_elf = elf::Elf::parse(&kernel_image).expect("Failed to parse ELF file"); 27 | 28 | dbg!(KERNEL_BASE); 29 | 30 | for ph in kernel_elf.program_headers { 31 | if ph.p_type == elf::program_header::PT_LOAD { 32 | info!( 33 | "PT_LOAD range = {:#x?}, to address {:#x} + {:#x?}", 34 | ph.file_range(), 35 | KERNEL_BASE, 36 | ph.vm_range(), 37 | ); 38 | 39 | // Allocate pages for this segment. 40 | let page_count = (align_up( 41 | ph.p_vaddr - align_down(ph.p_vaddr, Size4KiB::SIZE) + ph.p_memsz, 42 | Size4KiB::SIZE, 43 | ) / Size4KiB::SIZE) as usize; 44 | 45 | info!("page_count: {}", page_count); 46 | let phys_addr = services 47 | .allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, page_count) 48 | .expect_success("Failed to allocate pages while loading kernel segment"); 49 | 50 | let dst = unsafe { 51 | core::slice::from_raw_parts_mut( 52 | (ph.p_vaddr - align_down(ph.p_vaddr, Size4KiB::SIZE) + phys_addr) as *mut u8, 53 | ph.vm_range().len(), 54 | ) 55 | }; 56 | 57 | dst.zeroize(); 58 | 59 | dst[0..ph.file_range().len()].copy_from_slice(&kernel_image[ph.file_range()]); 60 | 61 | // Map to `KERNEL_BASE`. 62 | let flags = PageTableFlags::PRESENT 63 | | if ph.is_write() { 64 | PageTableFlags::WRITABLE 65 | } else { 66 | PageTableFlags::empty() 67 | } 68 | | if !ph.is_executable() { 69 | PageTableFlags::NO_EXECUTE 70 | } else { 71 | PageTableFlags::empty() 72 | }; 73 | 74 | let start_frame = PhysFrame::containing_address(PhysAddr::new(phys_addr)); 75 | let end_frame = PhysFrame::containing_address( 76 | PhysAddr::new(phys_addr) + ph.p_vaddr - align_down(ph.p_vaddr, Size4KiB::SIZE) 77 | + dst.len() 78 | - 1u64, 79 | ); 80 | 81 | for (i, frame) in PhysFrame::range_inclusive(start_frame, end_frame).enumerate() { 82 | let page = Page::containing_address( 83 | VirtAddr::new(ph.p_vaddr + i as u64 * Size4KiB::SIZE) + KERNEL_BASE, 84 | ); 85 | unsafe { 86 | page_table 87 | .map_to(page, frame, flags, allocator) 88 | .expect("Failed to map kernel segment") 89 | .flush(); 90 | } 91 | } 92 | } 93 | } 94 | 95 | // Relocate our kernel because it is linked as a PIE executable. 96 | for reloc in kernel_elf.dynrelas.iter() { 97 | match reloc.r_type { 98 | R_X86_64_RELATIVE => { 99 | let addr = (KERNEL_BASE + reloc.r_offset) as *mut u64; 100 | unsafe { 101 | *addr = KERNEL_BASE + reloc.r_addend.unwrap() as u64; 102 | } 103 | } 104 | _ => unimplemented!("Unhandled reloc type!"), 105 | } 106 | } 107 | 108 | assert_eq!(kernel_elf.dynrels.len(), 0); 109 | assert_eq!(kernel_elf.pltrelocs.len(), 0); 110 | 111 | KernelEntry(VirtAddr::new(KERNEL_BASE + kernel_elf.entry)) 112 | } 113 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Prints and returns the value of a given expression for quick and dirty 2 | /// debugging. 3 | /// 4 | /// Copied from standard library with slight modifications. 5 | #[macro_export] 6 | macro_rules! dbg { 7 | () => { 8 | info!("[{}:{}]", file!(), line!()); 9 | }; 10 | ($val:expr) => { 11 | match $val { 12 | tmp => { 13 | info!("[{}:{}] {} = {:#x?}", 14 | file!(), line!(), stringify!($val), &tmp); 15 | tmp 16 | } 17 | } 18 | }; 19 | ($val:expr,) => { $crate::dbg!($val) }; 20 | ($($val:expr),+ $(,)?) => { 21 | ($($crate::dbg!($val)),+,) 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![cfg_attr(doc, allow(unused_attributes))] 4 | #![feature(abi_efiapi)] 5 | #![feature(box_patterns)] 6 | #![feature(box_syntax)] 7 | #![feature(llvm_asm)] 8 | #![feature(maybe_uninit_extra)] 9 | 10 | #[macro_use] 11 | extern crate alloc; 12 | #[macro_use] 13 | extern crate log; 14 | #[macro_use] 15 | mod macros; 16 | 17 | mod config; 18 | mod io; 19 | mod loader; 20 | mod paging; 21 | 22 | use alloc::boxed::Box; 23 | use core::mem::MaybeUninit; 24 | 25 | use boot::MemoryMapIter; 26 | use boot::BOOT_ARGS_MAGIC; 27 | use boot::{BootArgs, FrameBufferDescriptor, KernelEntry, KernelEntryFn, MemoryMap}; 28 | 29 | use chrono::prelude::*; 30 | use uefi::prelude::*; 31 | use x86_64::{ 32 | structures::paging::{PageSize, Size4KiB}, 33 | PhysAddr, VirtAddr, 34 | }; 35 | 36 | use config::*; 37 | 38 | static mut KERNEL_ENTRY: KernelEntry = KernelEntry(VirtAddr::new_truncate(0x0)); 39 | static mut FRAME_BUFFER_BASE: u64 = 0x0; 40 | static mut FRAME_BUFFER_LEN: usize = 0x0; 41 | static mut RESOLUTION: (usize, usize) = (0, 0); 42 | static mut MMAP_ITER: MaybeUninit = MaybeUninit::uninit(); 43 | 44 | #[entry] 45 | fn efi_main(image_handle: Handle, system_table: SystemTable) -> Status { 46 | uefi_services::init(&system_table).expect_success("Failed to initialize UEFI environment"); 47 | let _ = system_table.stdout().clear().unwrap(); 48 | 49 | let boot_services = system_table.boot_services(); 50 | 51 | boot_services 52 | .set_watchdog_timer(0, 0x10000, None) 53 | .expect_success("Could not set watchdog timer"); 54 | 55 | print_system_information(&system_table).expect_success("Failed to print system information"); 56 | 57 | // Initialize our "kernel" frame allocator which marks frames as `MEMORY_TYPE_KERNEL`. 58 | let mut frame_allocator = paging::KernelFrameAllocator::new(boot_services); 59 | 60 | let mut page_table = paging::init_recursive(&mut frame_allocator); 61 | // load kernel ELF image. 62 | let kernel_entry = loader::load_elf( 63 | boot_services, 64 | &mut page_table, 65 | &mut frame_allocator, 66 | dbg!(KERNEL_IMAGE_PATH), 67 | ); 68 | 69 | dbg!(kernel_entry); 70 | 71 | let mmap_size = boot_services.memory_map_size(); 72 | let mut mmap_buf = vec![0u8; mmap_size * 2]; 73 | let (_, mmap_iter) = boot_services 74 | .memory_map(&mut mmap_buf) 75 | .expect_success("Failed to get memory map"); 76 | 77 | let max_addr = PhysAddr::new( 78 | mmap_iter 79 | .map(|m| m.phys_start + m.page_count * Size4KiB::SIZE - 1) 80 | .max() 81 | .unwrap() 82 | .max(0xFFFF_FFFF), 83 | ); 84 | 85 | // Map complete pyhsical memory to `PAGE_OFFSET_BASE`. 86 | paging::map_physical_memory( 87 | VirtAddr::new(dbg!(PAGE_OFFSET_BASE)), 88 | max_addr, 89 | &mut page_table, 90 | &mut frame_allocator, 91 | ); 92 | 93 | paging::map_stack( 94 | VirtAddr::new(KERNEL_STACK_TOP), 95 | KERNEL_STACK_SIZE, 96 | &mut page_table, 97 | &mut frame_allocator, 98 | ); 99 | 100 | // Exit boot services and jump to the kernel. 101 | info!("Exiting UEFI boot services and jumping to the kernel"); 102 | let mmap_size = boot_services.memory_map_size(); 103 | let mmap_buf = Box::leak(vec![0u8; mmap_size * 2].into_boxed_slice()); 104 | let (_, mmap_iter) = system_table 105 | .exit_boot_services(image_handle, mmap_buf) 106 | .expect_success("UEFI exit boot services failed"); 107 | 108 | unsafe { 109 | // FIXME: A dirty HACK to get `mmap_iter` to point to mapped memory. 110 | let mut tuple: (u64, usize, usize, usize, usize) = core::mem::transmute(mmap_iter); 111 | tuple.0 += PAGE_OFFSET_BASE; 112 | let mmap_iter: MemoryMapIter = core::mem::transmute(tuple); 113 | MMAP_ITER.write(mmap_iter); 114 | 115 | KERNEL_ENTRY = kernel_entry; 116 | llvm_asm!("mov $0, %rsp" : : "r"(KERNEL_STACK_TOP) : "memory" : "volatile"); 117 | // NOTICE: after we changed rsp, all local variables are no longer avaliable 118 | // and we must call another function immediately 119 | call_kernel_entry(); 120 | } 121 | } 122 | 123 | /// This function runs on new kernel stack. 124 | unsafe fn call_kernel_entry() -> ! { 125 | use core::mem; 126 | let kernel_entry: KernelEntryFn = mem::transmute(KERNEL_ENTRY); 127 | let args = BootArgs { 128 | magic: BOOT_ARGS_MAGIC, 129 | frame_buffer: FrameBufferDescriptor { 130 | base: PhysAddr::new(FRAME_BUFFER_BASE), 131 | len: FRAME_BUFFER_LEN, 132 | resolution: RESOLUTION, 133 | }, 134 | memory_map: MemoryMap { 135 | iter: MMAP_ITER.assume_init_read(), 136 | }, 137 | }; 138 | kernel_entry(&args); 139 | } 140 | 141 | fn print_system_information(system_table: &SystemTable) -> uefi::Result { 142 | info!( 143 | "{} v{}", 144 | env!("CARGO_PKG_DESCRIPTION"), 145 | env!("CARGO_PKG_VERSION") 146 | ); 147 | info!("By {}", env!("CARGO_PKG_AUTHORS")); 148 | 149 | info!( 150 | "UEFI Firmware {} {:#?}", 151 | system_table.firmware_vendor(), 152 | system_table.firmware_revision() 153 | ); 154 | 155 | let now = system_table.runtime_services().get_time().log_warning()?; 156 | let now = Utc 157 | .ymd(now.year() as i32, now.month() as u32, now.day() as u32) 158 | .and_hms(now.hour() as u32, now.minute() as u32, now.second() as u32); 159 | info!("TimeZone Bupt/Jwxt: {}", now); 160 | 161 | let now = now.with_timezone(&FixedOffset::east(8 * 3600)); 162 | info!("TimeZone Asia/Shanghai: {}", now); 163 | 164 | for e in system_table.config_table() { 165 | if e.guid == uefi::table::cfg::SMBIOS_GUID { 166 | let addr = e.address; 167 | let smbios = unsafe { *(addr as *const boot::SmbiosEntryPoint) }; 168 | debug!("{:?}", smbios); 169 | } 170 | } 171 | 172 | let boot_services = system_table.boot_services(); 173 | 174 | let gop = boot_services 175 | .locate_protocol::() 176 | .expect_success(""); 177 | 178 | let gop = unsafe { &mut *gop.get() }; 179 | 180 | let mut buf = gop.frame_buffer(); 181 | 182 | info!("Graphic buffer: {:p}, {:#x}", buf.as_mut_ptr(), buf.size()); 183 | 184 | unsafe { 185 | FRAME_BUFFER_BASE = buf.as_mut_ptr() as u64; 186 | FRAME_BUFFER_LEN = buf.size(); 187 | RESOLUTION = gop.current_mode_info().resolution(); 188 | } 189 | 190 | Ok(().into()) 191 | } 192 | -------------------------------------------------------------------------------- /xv7-bootloader-uefi/src/paging.rs: -------------------------------------------------------------------------------- 1 | use uefi::prelude::*; 2 | use uefi::table::boot::{AllocateType, MemoryType}; 3 | use x86_64::registers::control::{Cr3, Cr3Flags, Cr4, Cr4Flags}; 4 | use x86_64::registers::model_specific::{Efer, EferFlags}; 5 | use x86_64::structures::paging::{ 6 | FrameAllocator, Mapper, Page, PageSize, PageTable, PageTableFlags, PhysFrame, 7 | RecursivePageTable, Size2MiB, Size4KiB, 8 | }; 9 | use x86_64::{align_up, PhysAddr, VirtAddr}; 10 | 11 | /// UEFI allows us to introduce new memory types 12 | /// in the 0x70000000..0xFFFFFFFF range. 13 | #[allow(unused)] 14 | pub const MEMORY_TYPE_KERNEL: u32 = 0x80000000; 15 | 16 | /// This frame allocator marks frames as `MEMORY_TYPE_KERNEL`. 17 | pub struct KernelFrameAllocator<'a>(&'a BootServices); 18 | 19 | impl<'a> KernelFrameAllocator<'a> { 20 | pub fn new(services: &'a BootServices) -> Self { 21 | Self(services) 22 | } 23 | } 24 | 25 | unsafe impl<'a> FrameAllocator for KernelFrameAllocator<'a> { 26 | fn allocate_frame(&mut self) -> Option> { 27 | let phys_addr = self 28 | .0 29 | .allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, 1) 30 | .expect_success("Failed to allocate physical frame"); 31 | let phys_addr = PhysAddr::new(phys_addr); 32 | let phys_frame = PhysFrame::containing_address(phys_addr); 33 | Some(phys_frame) 34 | } 35 | } 36 | 37 | /// Set up a basic recursive page table. 38 | pub fn init_recursive( 39 | allocator: &mut impl FrameAllocator, 40 | ) -> RecursivePageTable<'static> { 41 | // First we do a copy for the level 4 table here, because the old table 42 | // has memory type `BOOT_SERVICES_DATA`. Level 3 ~ level 1 tables will 43 | // be discarded eventually so we can ignore them. 44 | let old_l4_table_addr = Cr3::read().0.start_address().as_u64(); 45 | let l4_table_frame = allocator.allocate_frame().unwrap(); 46 | let l4_table_addr = l4_table_frame.start_address().as_u64(); 47 | 48 | // Safety: newly allocated frame is guaranteed to be valid and unused 49 | unsafe { 50 | core::ptr::copy( 51 | old_l4_table_addr as *const u8, 52 | l4_table_addr as *mut u8, 53 | l4_table_frame.size() as usize, 54 | ) 55 | }; 56 | 57 | // Safety: same as above 58 | let l4_table = unsafe { &mut *(l4_table_addr as *mut PageTable) }; 59 | 60 | // Recursive mapping 61 | l4_table[0b111_111_111].set_frame( 62 | l4_table_frame, 63 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE, 64 | ); 65 | 66 | // Enable all CPU extensions we need. 67 | unsafe { 68 | Cr4::update(|cr4| { 69 | cr4.insert( 70 | Cr4Flags::PAGE_SIZE_EXTENSION 71 | | Cr4Flags::PHYSICAL_ADDRESS_EXTENSION 72 | | Cr4Flags::PAGE_GLOBAL 73 | | Cr4Flags::OSFXSR, 74 | ) 75 | }); 76 | Efer::update(|efer| efer.insert(EferFlags::NO_EXECUTE_ENABLE)); 77 | }; 78 | 79 | // Switch to the new page table... 80 | unsafe { Cr3::write(l4_table_frame, Cr3Flags::empty()) }; 81 | 82 | // And we have it! 83 | let l4_table = unsafe { &mut *(0xFFFF_FFFF_FFFF_F000 as *mut PageTable) }; 84 | 85 | RecursivePageTable::new(l4_table).unwrap() 86 | } 87 | 88 | /// Map complete pyhsical memory to `offset`, which is `PAGE_OFFSET_BASE`. 89 | pub fn map_physical_memory( 90 | offset: VirtAddr, 91 | max_addr: PhysAddr, 92 | page_table: &mut impl Mapper, 93 | allocator: &mut impl FrameAllocator, 94 | ) { 95 | let start_frame = PhysFrame::containing_address(PhysAddr::new(0)); 96 | let end_frame = PhysFrame::containing_address(max_addr); 97 | for frame in PhysFrame::range_inclusive(start_frame, end_frame) { 98 | let page = Page::containing_address(offset + frame.start_address().as_u64()); 99 | unsafe { 100 | page_table 101 | .map_to( 102 | page, 103 | frame, 104 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE, 105 | allocator, 106 | ) 107 | .expect("Error occured while mapping complete pyhsical memory") 108 | .flush(); 109 | } 110 | } 111 | } 112 | 113 | /// Map kernel stack under `KERNEL_STACK_TOP`. 114 | pub fn map_stack( 115 | stack_top: VirtAddr, 116 | size: usize, 117 | page_table: &mut impl Mapper, 118 | allocator: &mut impl FrameAllocator, 119 | ) { 120 | let page_count = align_up(size as u64, Size4KiB::SIZE) / Size4KiB::SIZE; 121 | let stack_top = Page::containing_address(stack_top); 122 | let stack_bottom = stack_top - page_count; 123 | for page in Page::range(stack_bottom, stack_top) { 124 | let frame = allocator.allocate_frame().unwrap(); 125 | unsafe { 126 | page_table 127 | .map_to( 128 | page, 129 | frame, 130 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE, 131 | allocator, 132 | ) 133 | .expect("Error occured while mapping kernel stack") 134 | .flush(); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /xv7-kernel/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /xv7-kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xv7-kernel" 3 | version = "0.1.0" 4 | authors = ["imtsuki "] 5 | license = "MIT OR Apache-2.0" 6 | edition = "2018" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 12 | embedded-graphics = { version = "0.6.2" } 13 | tinybmp = { version = "0.2.2", features = [ "graphics" ]} 14 | bitvec = { version = "0.17.4", default-features = false, features = ["atomic"] } 15 | vte = "0.7.1" 16 | spin = "0.9.0" 17 | linked_list_allocator = "0.8.11" 18 | goblin = { version = "0.4.0", default-features = false, features = ["elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive", "endian_fd"] } 19 | zeroize = "1.1.0" 20 | boot = { path = "../xv7-boot", package = "xv7-boot" } 21 | usyscall = { path = "../xv7-usyscall", package = "xv7-usyscall" } 22 | crossbeam-queue = { version = "0.2.1", default-features = false, features = ["alloc"] } 23 | 24 | [features] 25 | default = ["frame-allocator-bitmap"] 26 | frame-allocator-bitmap = [] 27 | frame-allocator-buddy = [] 28 | 29 | [target.'cfg(target_arch = "x86_64")'.dependencies] 30 | x86_64 = "0.14.0" 31 | uart_16550 = "0.2.12" 32 | uefi = "0.8.0" 33 | raw-cpuid = "9.0.0" 34 | pc-keyboard = "0.5.0" 35 | 36 | [target.'cfg(target_arch = "aarch64")'.dependencies] 37 | cortex-a = "2.9.0" 38 | -------------------------------------------------------------------------------- /xv7-kernel/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.xbuild] 2 | command = "cargo" 3 | args = ["xbuild", "--target", "src/arch/x86_64.json"] 4 | 5 | [tasks.xbuild-aarch64] 6 | command = "cargo" 7 | args = ["xbuild", "--target", "src/arch/aarch64.json"] 8 | -------------------------------------------------------------------------------- /xv7-kernel/resources/logo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imtsuki/xv7/edab461a6a7ceab2236e24ca726598107d346467/xv7-kernel/resources/logo.bmp -------------------------------------------------------------------------------- /xv7-kernel/resources/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imtsuki/xv7/edab461a6a7ceab2236e24ca726598107d346467/xv7-kernel/resources/logo.psd -------------------------------------------------------------------------------- /xv7-kernel/resources/logo@2x.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imtsuki/xv7/edab461a6a7ceab2236e24ca726598107d346467/xv7-kernel/resources/logo@2x.bmp -------------------------------------------------------------------------------- /xv7-kernel/src/allocator.rs: -------------------------------------------------------------------------------- 1 | use linked_list_allocator::LockedHeap; 2 | 3 | pub fn init_heap() { 4 | crate::arch::allocator::init_heap(); 5 | } 6 | 7 | #[global_allocator] 8 | pub static ALLOCATOR: LockedHeap = LockedHeap::empty(); 9 | -------------------------------------------------------------------------------- /xv7-kernel/src/ansi.rs: -------------------------------------------------------------------------------- 1 | //! ANSI Escape Codes. 2 | //! 3 | //! See 4 | //! - [Console Virtual Terminal Sequences - Windows Console | Microsoft Docs](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) 5 | //! - [console_codes(4) - Linux manual page](http://man7.org/linux/man-pages/man4/console_codes.4.html) 6 | //! - [ANSI escape code - Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code) 7 | //! - https://www.inwap.com/pdp10/ansicode.txt 8 | 9 | use core::fmt; 10 | 11 | const ESC: char = '\x1B'; 12 | 13 | /// Control Sequence Introducer 14 | /// 15 | /// Begins with [ESC](ESC) [CSI](CSI). 16 | #[allow(unused)] 17 | #[derive(Clone, Copy, Debug)] 18 | pub enum CtrlSeq { 19 | /* Cursor Positioning */ 20 | /// Cursor up by n 21 | /// 22 | /// Code: [CUU](CUU), Char: `A` 23 | CursorUp(Option), 24 | /// Cursor down by n 25 | /// 26 | /// Code: [CUD](CUD), Char: `B` 27 | CursorDown(Option), 28 | /// Cursor forward (Right) by n 29 | /// 30 | /// Code: [CUF](CUF), Char: `C` 31 | CursorForward(Option), 32 | /// Cursor backward (Left) by n 33 | /// 34 | /// Code: [CUB](CUB), Char: `D` 35 | CursorBackward(Option), 36 | /// Cursor down to beginning of nth line in the viewport 37 | /// 38 | /// Code: [CNL](CNL), Char: `E` 39 | CursorNextLine(Option), 40 | /// Cursor up to beginning of nth line in the viewport 41 | /// 42 | /// Code: [CPL](CPL), Char: `F` 43 | CursorPreviousLine(Option), 44 | /// Cursor moves to nth position horizontally in the current line 45 | /// 46 | /// Code: [CHA](CHA), Char: `G` 47 | CursorHorizontalAbsolute(Option), 48 | /// Cursor moves to x; y coordinate within the viewport, 49 | /// where x is the column of the y line 50 | /// 51 | /// Code: [CUP](CUP), Char: `H` 52 | CursorPosition(Option, Option), 53 | /// Cursor moves to x; y coordinate within the viewport, 54 | /// where x is the column of the y line 55 | /// 56 | /// Code: [HVP](HVP), Char:` f 57 | HorizontalVerticalPosition(Option, Option), 58 | /// Cursor moves to the nth position vertically in the current column 59 | /// 60 | /// Code: [VPA](VPA), Char: `d` 61 | VerticalPositionAbsolute(Option), 62 | /// Code: [VPR](VPR), Char: `e` 63 | VerticalPositionRelative(Option), 64 | /// Code: [HPA](HPA), Char: ` 65 | HorizontalPositionAbsolute(Option), 66 | /// Code: [HPR](HPR), Char: `a` 67 | HorizontalPositionRelative(Option), 68 | /// Code: [SCP](SCP), Char: `s` 69 | SaveCursorPosition, 70 | /// Code: [RCP](RCP), Char: `u` 71 | RestoreCursorPosition, 72 | /* End of Cursor Positioning */ 73 | /* Viewport Positioning */ 74 | /// Scroll text up by n 75 | /// 76 | /// Code: [SU](SU), Char: `S` 77 | ScrollUp(Option), 78 | /// Scroll text down by n 79 | /// 80 | /// Code: [SD](SD) Char: `T` 81 | ScrollDown(Option), 82 | /* End of Viewport Positioning */ 83 | /* Text Modification */ 84 | /// Insert n spaces at the current cursor position, shifting 85 | /// all existing text to the right. Text exiting the screen to 86 | /// the right is removed. 87 | /// 88 | /// Code: [ICH](ICH), Char: `@` 89 | InsertCharacter(Option), 90 | /// Delete n characters at the current cursor position, shifting 91 | /// in space characters from the right edge of the screen. 92 | /// 93 | /// Code: [DCH](DCH), Char: `P` 94 | DeleteCharacter(Option), 95 | /// Erase n characters from the current cursor position by 96 | /// overwriting them with a space character. 97 | /// 98 | /// Code: [ECH](ECH), Char: `X` 99 | EraseCharacter(Option), 100 | /// Inserts n lines into the buffer at the cursor position. The line 101 | /// the cursor is on, and lines below it, will be shifted downwards. 102 | /// 103 | /// Code: [IL](IL), Char: `L` 104 | InsertLine(Option), 105 | /// Deletes n lines from the buffer, starting with the row the cursor is on. 106 | /// 107 | /// Code: [DL](DL), Char: `M` 108 | DeleteLine(Option), 109 | /// Replace all text in the current viewport/screen specified by `EraseParam` 110 | /// with space characters 111 | /// 112 | /// Code: [ED](ED), Char: `J` 113 | EraseDisplay(Option), 114 | /// Replace all text on the line with the cursor specified by `EraseParam` 115 | /// with space characters 116 | /// 117 | /// Code: [EL](EL), Char: `K` 118 | EraseLine(Option), 119 | /* End of Text Modification */ 120 | /* Text Formatting */ 121 | /// Set the format of the screen and text as specified by n 122 | /// 123 | /// Code: [SGR](SGR), Char: `m` 124 | SelectGraphicRendition(Option), 125 | /* End of Text Formatting */ 126 | /* Tabs */ 127 | /// Code: [CHT](CHT), Char: `I` 128 | CursorHorizontalTab(Option), 129 | /// Code: [CBT](CBT), Char: `Z` 130 | CursorBackwardsTab(Option), 131 | /// Code: [TBC](TBC), Char: `g` 132 | TabClear(Option), 133 | } 134 | 135 | pub const CSI: char = '['; 136 | /* Cursor Positioning */ 137 | pub const CUU: char = 'A'; 138 | pub const CUD: char = 'B'; 139 | pub const CUF: char = 'C'; 140 | pub const CUB: char = 'D'; 141 | pub const CNL: char = 'E'; 142 | pub const CPL: char = 'F'; 143 | pub const CHA: char = 'G'; 144 | pub const CUP: char = 'H'; 145 | pub const HVP: char = 'f'; 146 | pub const VPA: char = 'd'; 147 | pub const VPR: char = 'e'; 148 | pub const HPA: char = '`'; 149 | pub const HPR: char = 'a'; 150 | pub const SCP: char = 's'; 151 | pub const RCP: char = 'u'; 152 | /* Viewport Positioning */ 153 | pub const SU: char = 'S'; 154 | pub const SD: char = 'T'; 155 | /* Text Modification */ 156 | pub const ICH: char = '@'; 157 | pub const DCH: char = 'P'; 158 | pub const ECH: char = 'X'; 159 | pub const IL: char = 'L'; 160 | pub const DL: char = 'M'; 161 | pub const ED: char = 'J'; 162 | pub const EL: char = 'K'; 163 | /* Text Formatting */ 164 | pub const SGR: char = 'm'; 165 | /* Tabs */ 166 | pub const CHT: char = 'I'; 167 | pub const CBT: char = 'Z'; 168 | pub const TBC: char = 'g'; 169 | 170 | impl fmt::Display for CtrlSeq { 171 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 172 | use CtrlSeq::*; 173 | write!(f, "{}{}", ESC, CSI)?; 174 | match self { 175 | CursorUp(Some(n)) => write!(f, "{}{}", n, CUU), 176 | CursorUp(None) => write!(f, "{}", CUU), 177 | CursorDown(Some(n)) => write!(f, "{}{}", n, CUD), 178 | CursorDown(None) => write!(f, "{}", CUD), 179 | CursorForward(Some(n)) => write!(f, "{}{}", n, CUF), 180 | CursorForward(None) => write!(f, "{}", CUF), 181 | CursorBackward(Some(n)) => write!(f, "{}{}", n, CUB), 182 | CursorBackward(None) => write!(f, "{}", CUB), 183 | CursorNextLine(Some(n)) => write!(f, "{}{}", n, CNL), 184 | CursorNextLine(None) => write!(f, "{}", CNL), 185 | CursorPreviousLine(Some(n)) => write!(f, "{}{}", n, CPL), 186 | CursorPreviousLine(None) => write!(f, "{}", CPL), 187 | CursorHorizontalAbsolute(Some(n)) => write!(f, "{}{}", n, CHA), 188 | CursorHorizontalAbsolute(None) => write!(f, "{}", CHA), 189 | CursorPosition(Some(x), Some(y)) => write!(f, "{};{}{}", y, x, CUP), 190 | CursorPosition(Some(x), None) => write!(f, ";{}{}", x, CUP), 191 | CursorPosition(None, Some(y)) => write!(f, "{};{}", y, CUP), 192 | CursorPosition(None, None) => write!(f, "{}", CUP), 193 | HorizontalVerticalPosition(Some(x), Some(y)) => write!(f, "{};{}{}", y, x, HVP), 194 | HorizontalVerticalPosition(Some(x), None) => write!(f, ";{}{}", x, HVP), 195 | HorizontalVerticalPosition(None, Some(y)) => write!(f, "{};{}", y, HVP), 196 | HorizontalVerticalPosition(None, None) => write!(f, "{}", HVP), 197 | VerticalPositionAbsolute(Some(n)) => write!(f, "{}{}", n, VPA), 198 | VerticalPositionAbsolute(None) => write!(f, "{}", VPA), 199 | VerticalPositionRelative(Some(n)) => write!(f, "{}{}", n, VPR), 200 | VerticalPositionRelative(None) => write!(f, "{}", VPR), 201 | HorizontalPositionAbsolute(Some(n)) => write!(f, "{}{}", n, HPA), 202 | HorizontalPositionAbsolute(None) => write!(f, "{}", HPA), 203 | HorizontalPositionRelative(Some(n)) => write!(f, "{}{}", n, HPR), 204 | HorizontalPositionRelative(None) => write!(f, "{}", HPR), 205 | SaveCursorPosition => write!(f, "{}", SCP), 206 | RestoreCursorPosition => write!(f, "{}", RCP), 207 | ScrollUp(Some(n)) => write!(f, "{}{}", n, SU), 208 | ScrollUp(None) => write!(f, "{}", SU), 209 | ScrollDown(Some(n)) => write!(f, "{}{}", n, SD), 210 | ScrollDown(None) => write!(f, "{}", SD), 211 | InsertCharacter(Some(n)) => write!(f, "{}{}", n, ICH), 212 | InsertCharacter(None) => write!(f, "{}", ICH), 213 | DeleteCharacter(Some(n)) => write!(f, "{}{}", n, DCH), 214 | DeleteCharacter(None) => write!(f, "{}", DCH), 215 | EraseCharacter(Some(n)) => write!(f, "{}{}", n, ECH), 216 | EraseCharacter(None) => write!(f, "{}", ECH), 217 | InsertLine(Some(n)) => write!(f, "{}{}", n, IL), 218 | InsertLine(None) => write!(f, "{}", IL), 219 | DeleteLine(Some(n)) => write!(f, "{}{}", n, DL), 220 | DeleteLine(None) => write!(f, "{}", DL), 221 | EraseDisplay(Some(n)) => write!(f, "{}{}", n, ED), 222 | EraseDisplay(None) => write!(f, "{}", ED), 223 | EraseLine(Some(n)) => write!(f, "{}{}", n, EL), 224 | EraseLine(None) => write!(f, "{}", EL), 225 | SelectGraphicRendition(Some(n)) => write!(f, "{}{}", n, SGR), 226 | SelectGraphicRendition(None) => write!(f, "{}", SGR), 227 | CursorHorizontalTab(Some(n)) => write!(f, "{}{}", n, CHT), 228 | CursorHorizontalTab(None) => write!(f, "{}", CHT), 229 | CursorBackwardsTab(Some(n)) => write!(f, "{}{}", n, CBT), 230 | CursorBackwardsTab(None) => write!(f, "{}", CBT), 231 | TabClear(Some(n)) => write!(f, "{}{}", n, TBC), 232 | TabClear(None) => write!(f, "{}", TBC), 233 | } 234 | } 235 | } 236 | 237 | #[allow(unused)] 238 | #[derive(Clone, Copy, Debug)] 239 | pub enum EraseParam { 240 | FromCurrentToEnd, 241 | FromBeginningToCurrent, 242 | Entire, 243 | } 244 | 245 | impl fmt::Display for EraseParam { 246 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 247 | use EraseParam::*; 248 | match self { 249 | FromCurrentToEnd => write!(f, "0"), 250 | FromBeginningToCurrent => write!(f, "1"), 251 | Entire => write!(f, "2"), 252 | } 253 | } 254 | } 255 | 256 | /// C0 set of 7-bit control characters (from ANSI X3.4-1977). 257 | #[allow(non_snake_case)] 258 | pub mod C0 { 259 | /// Null filler, terminal should ignore this character 260 | pub const NUL: u8 = 0x00; 261 | /// Start of Header 262 | pub const SOH: u8 = 0x01; 263 | /// Start of Text, implied end of header 264 | pub const STX: u8 = 0x02; 265 | /// End of Text, causes some terminal to respond with ACK or NAK 266 | pub const ETX: u8 = 0x03; 267 | /// End of Transmission 268 | pub const EOT: u8 = 0x04; 269 | /// Enquiry, causes terminal to send ANSWER-BACK ID 270 | pub const ENQ: u8 = 0x05; 271 | /// Acknowledge, usually sent by terminal in response to ETX 272 | pub const ACK: u8 = 0x06; 273 | /// Bell, triggers the bell, buzzer, or beeper on the terminal 274 | pub const BEL: u8 = 0x07; 275 | /// Backspace, can be used to define overstruck characters 276 | pub const BS: u8 = 0x08; 277 | /// Horizontal Tabulation, move to next predetermined position 278 | pub const HT: u8 = 0x09; 279 | /// Linefeed, move to same position on next line (see also NL) 280 | pub const LF: u8 = 0x0A; 281 | /// Vertical Tabulation, move to next predetermined line 282 | pub const VT: u8 = 0x0B; 283 | /// Form Feed, move to next form or page 284 | pub const FF: u8 = 0x0C; 285 | /// Carriage Return, move to first character of current line 286 | pub const CR: u8 = 0x0D; 287 | /// Shift Out, switch to G1 (other half of character set) 288 | pub const SO: u8 = 0x0E; 289 | /// Shift In, switch to G0 (normal half of character set) 290 | pub const SI: u8 = 0x0F; 291 | /// Data Link Escape, interpret next control character specially 292 | pub const DLE: u8 = 0x10; 293 | /// (DC1) Terminal is allowed to resume transmitting 294 | pub const XON: u8 = 0x11; 295 | /// Device Control 2, causes ASR-33 to activate paper-tape reader 296 | pub const DC2: u8 = 0x12; 297 | /// (DC2) Terminal must pause and refrain from transmitting 298 | pub const XOFF: u8 = 0x13; 299 | /// Device Control 4, causes ASR-33 to deactivate paper-tape reader 300 | pub const DC4: u8 = 0x14; 301 | /// Negative Acknowledge, used sometimes with ETX and ACK 302 | pub const NAK: u8 = 0x15; 303 | /// Synchronous Idle, used to maintain timing in Sync communication 304 | pub const SYN: u8 = 0x16; 305 | /// End of Transmission block 306 | pub const ETB: u8 = 0x17; 307 | /// Cancel (makes VT100 abort current escape sequence if any) 308 | pub const CAN: u8 = 0x18; 309 | /// End of Medium 310 | pub const EM: u8 = 0x19; 311 | /// Substitute (VT100 uses this to display parity errors) 312 | pub const SUB: u8 = 0x1A; 313 | /// Prefix to an escape sequence 314 | pub const ESC: u8 = 0x1B; 315 | /// File Separator 316 | pub const FS: u8 = 0x1C; 317 | /// Group Separator 318 | pub const GS: u8 = 0x1D; 319 | /// Record Separator (sent by VT132 in block-transfer mode) 320 | pub const RS: u8 = 0x1E; 321 | /// Unit Separator 322 | pub const US: u8 = 0x1F; 323 | /// Delete, should be ignored by terminal 324 | pub const DEL: u8 = 0x7f; 325 | } 326 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86_64")] 2 | pub mod x86_64; 3 | 4 | #[cfg(target_arch = "x86_64")] 5 | pub use self::x86_64::*; 6 | 7 | #[cfg(target_arch = "aarch64")] 8 | mod aarch64; 9 | 10 | #[cfg(target_arch = "aarch64")] 11 | pub use self::aarch64::*; 12 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/aarch64.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "aarch64-unknown-none", 3 | "target-endian": "little", 4 | "target-pointer-width": "64", 5 | "target-c-int-width": "32", 6 | "os": "none", 7 | "arch": "aarch64", 8 | "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", 9 | "linker-flavor": "ld.lld", 10 | "linker": "rust-lld", 11 | "features": "+a53,+strict-align,-neon,-fp-armv8", 12 | "executables": true, 13 | "position-independent-executables": true, 14 | "disable-redzone": true, 15 | "panic-strategy": "abort" 16 | } 17 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/aarch64/mod.rs: -------------------------------------------------------------------------------- 1 | mod start; 2 | 3 | #[inline(always)] 4 | pub fn idle() -> ! { 5 | loop { 6 | unsafe { 7 | asm!("wfe"); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/aarch64/start.rs: -------------------------------------------------------------------------------- 1 | use boot::{BootArgs, KernelEntryFn, BOOT_ARGS_MAGIC}; 2 | 3 | #[used] 4 | static BSS_ZERO_CHECK: u64 = 0; 5 | #[used] 6 | static DATA_NONZERO_CHECK: u64 = 0xFFFF_FFFF_FFFF_FFFF; 7 | 8 | #[doc(hidden)] 9 | #[allow(unused)] 10 | const KERNEL_ENTRY_SIGNATURE_TYPE_CHECK: KernelEntryFn = _start; 11 | 12 | #[no_mangle] 13 | extern "C" fn _start(args: &BootArgs) -> ! { 14 | assert_eq!(BSS_ZERO_CHECK, 0); 15 | assert_eq!(DATA_NONZERO_CHECK, 0xFFFF_FFFF_FFFF_FFFF); 16 | assert_eq!( 17 | args.magic, BOOT_ARGS_MAGIC, 18 | "BootArgs magic number check failed" 19 | ); 20 | 21 | crate::kmain(); 22 | } 23 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "target-endian": "little", 4 | "target-pointer-width": "64", 5 | "target-c-int-width": "32", 6 | "os": "none", 7 | "arch": "x86_64", 8 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 9 | "linker-flavor": "ld.lld", 10 | "linker": "rust-lld", 11 | "executables": true, 12 | "position-independent-executables": true, 13 | "disable-redzone": true, 14 | "panic-strategy": "abort" 15 | } 16 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/allocator.rs: -------------------------------------------------------------------------------- 1 | use super::paging; 2 | use crate::{ 3 | config::*, 4 | memory::{FrameAllocator, FRAME_ALLOCATOR}, 5 | }; 6 | use x86_64::structures::paging::{Mapper, Page}; 7 | use x86_64::structures::paging::{OffsetPageTable, PageTableFlags}; 8 | use x86_64::VirtAddr; 9 | 10 | pub fn init_heap() { 11 | let mut frame_allocator = FRAME_ALLOCATOR.lock(); 12 | let page_table = unsafe { paging::active_page_table() }; 13 | let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(PAGE_OFFSET_BASE)) }; 14 | 15 | let page_range = { 16 | let heap_start = VirtAddr::new(KERNEL_HEAP_BASE as u64); 17 | let heap_end = heap_start + KERNEL_HEAP_SIZE - 1u64; 18 | let heap_start_page = Page::containing_address(heap_start); 19 | let heap_end_page = Page::containing_address(heap_end); 20 | Page::range_inclusive(heap_start_page, heap_end_page) 21 | }; 22 | 23 | for page in page_range { 24 | let frame = frame_allocator.allocate_frame().unwrap(); 25 | let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE; 26 | unsafe { 27 | mapper 28 | .map_to(page, frame, flags, &mut *frame_allocator) 29 | .unwrap() 30 | .flush(); 31 | } 32 | } 33 | 34 | unsafe { 35 | crate::allocator::ALLOCATOR 36 | .lock() 37 | .init(KERNEL_HEAP_BASE as usize, KERNEL_HEAP_SIZE); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/config.rs: -------------------------------------------------------------------------------- 1 | pub use boot::config::*; 2 | 3 | /// User stack. 4 | pub const USER_STACK: u64 = 0x0000_7FFF_FFFF_F000; 5 | 6 | /// User base 7 | pub const USER_BASE: u64 = 0x0000_0001_0000_0000; 8 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/console.rs: -------------------------------------------------------------------------------- 1 | use super::device::{MonitorConsole, SerialConsole}; 2 | 3 | use crate::ansi::{CtrlSeq, EraseParam}; 4 | 5 | pub fn init() { 6 | let mut console_drivers = crate::device::console::CONSOLE_DRIVERS.lock(); 7 | 8 | console_drivers.register(box MonitorConsole::new()); 9 | console_drivers.register(box SerialConsole::new()); 10 | 11 | drop(console_drivers); 12 | 13 | print!( 14 | "{}{}{}", 15 | CtrlSeq::EraseDisplay(Some(EraseParam::Entire)), 16 | CtrlSeq::CursorPosition(None, None), 17 | CtrlSeq::SelectGraphicRendition(None), 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/context.rs: -------------------------------------------------------------------------------- 1 | use crate::paging::VirtAddr; 2 | 3 | #[derive(Debug)] 4 | #[repr(C)] 5 | pub struct Context { 6 | pub cr3: usize, 7 | pub rsp: usize, 8 | pub rflags: usize, 9 | pub r15: usize, 10 | pub r14: usize, 11 | pub r13: usize, 12 | pub r12: usize, 13 | pub rbp: usize, 14 | pub rbx: usize, 15 | } 16 | 17 | impl Context { 18 | pub const fn new() -> Context { 19 | Context { 20 | cr3: 0, 21 | rflags: 0, 22 | rbx: 0, 23 | r12: 0, 24 | r13: 0, 25 | r14: 0, 26 | r15: 0, 27 | rbp: 0, 28 | rsp: 0, 29 | } 30 | } 31 | 32 | pub const fn user(stack_pointer: VirtAddr) -> Context { 33 | Context { 34 | cr3: 0, 35 | rflags: 0x282, 36 | rbx: 0, 37 | r12: 0, 38 | r13: 0, 39 | r14: 0, 40 | r15: 0, 41 | rbp: 0, 42 | rsp: stack_pointer.as_u64() as usize, 43 | } 44 | } 45 | 46 | #[inline(never)] 47 | #[naked] 48 | pub unsafe extern "C" fn switch_to(&mut self, _next: &Context) { 49 | asm!( 50 | " 51 | mov rcx, cr3 52 | mov [rdi], rcx 53 | mov rax, [rsi] 54 | cmp rax, rcx 55 | 56 | je switch_to.same_cr3 57 | mov cr3, rax 58 | 59 | switch_to.same_cr3: 60 | pushfq 61 | pop QWORD PTR [rdi + 0x10] 62 | 63 | push QWORD PTR [rsi + 0x10] 64 | popfq 65 | 66 | mov [rdi + 0x18], r15 67 | mov r15, [rsi + 0x18] 68 | 69 | mov [rdi + 0x20], r14 70 | mov r14, [rsi + 0x20] 71 | 72 | mov [rdi + 0x28], r13 73 | mov r13, [rsi + 0x28] 74 | 75 | mov [rdi + 0x30], r12 76 | mov r12, [rsi + 0x30] 77 | 78 | mov [rdi + 0x38], rbp 79 | mov rbp, [rsi + 0x38] 80 | 81 | mov [rdi + 0x40], rbx 82 | mov rbx, [rsi + 0x40] 83 | 84 | mov [rdi + 0x08], rsp 85 | mov rsp, [rsi + 0x08] 86 | 87 | ret 88 | ", 89 | options(noreturn) 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/cpuid.rs: -------------------------------------------------------------------------------- 1 | use raw_cpuid::CpuId; 2 | 3 | pub fn init() { 4 | println!("{:x}", unsafe { 5 | x86_64::registers::model_specific::Msr::new(0x1b).read() 6 | }); 7 | 8 | let cpuid = CpuId::new(); 9 | 10 | println!( 11 | "CPU Vendor: {}", 12 | cpuid 13 | .get_vendor_info() 14 | .as_ref() 15 | .map_or_else(|| "unknown", |vf| vf.as_string(),) 16 | ); 17 | 18 | println!( 19 | "CPU Model: {}", 20 | cpuid.get_extended_function_info().as_ref().map_or_else( 21 | || "n/a", 22 | |extfuninfo| extfuninfo.processor_brand_string().unwrap_or("unreadable"), 23 | ) 24 | ); 25 | 26 | cpuid.get_feature_info().as_ref().map_or_else( 27 | || println!("Family: n/a, Extended Family: n/a, Model: n/a, Extended Model: n/a, Stepping: n/a, Brand Index: n/a"), 28 | |finfo| { 29 | println!( 30 | "Family: {}, Extended Family: {}, Model: {}, Extended Model: {}, Stepping: {}, Brand Index: {}", 31 | finfo.family_id(), 32 | finfo.extended_family_id(), 33 | finfo.model_id(), 34 | finfo.extended_model_id(), 35 | finfo.stepping_id(), 36 | finfo.brand_index(), 37 | ); 38 | }, 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/device.rs: -------------------------------------------------------------------------------- 1 | pub mod monitor_console; 2 | pub mod serial_console; 3 | 4 | pub mod uart; 5 | 6 | pub use monitor_console::MonitorConsole; 7 | pub use serial_console::SerialConsole; 8 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/device/monitor_console.rs: -------------------------------------------------------------------------------- 1 | use crate::ansi; 2 | use crate::ansi::C0; 3 | use crate::device::console::Console; 4 | use crate::video::GOP_DISPLAY; 5 | use embedded_graphics::style::TextStyleBuilder; 6 | use embedded_graphics::{ 7 | fonts::{Font8x16, Text}, 8 | prelude::*, 9 | }; 10 | use vte::{Parser, Perform}; 11 | 12 | struct Performer { 13 | pos: (usize, usize), 14 | size: (usize, usize), 15 | } 16 | 17 | pub struct MonitorConsole { 18 | parser: Parser, 19 | performer: Performer, 20 | } 21 | 22 | impl MonitorConsole { 23 | pub fn new() -> MonitorConsole { 24 | if let Some(display) = &mut *GOP_DISPLAY.lock() { 25 | let display_size = display.size(); 26 | let (width, height) = (display_size.width as usize, display_size.height as usize); 27 | let rows = height / 16; 28 | let columns = width / 8; 29 | MonitorConsole { 30 | parser: Parser::new(), 31 | performer: Performer { 32 | pos: (0, 0), 33 | size: (rows, columns), 34 | }, 35 | } 36 | } else { 37 | panic!("Display error"); 38 | } 39 | } 40 | } 41 | 42 | impl Console for MonitorConsole { 43 | fn write(&mut self, buf: &[u8]) { 44 | for byte in buf { 45 | self.parser.advance(&mut self.performer, *byte); 46 | } 47 | } 48 | } 49 | 50 | impl Performer { 51 | fn linefeed(&mut self) { 52 | let (mut line, _) = self.pos; 53 | line += 1; 54 | 55 | if line == self.size.0 { 56 | if let Some(display) = &mut *GOP_DISPLAY.lock() { 57 | display.scroll_up(16); 58 | } 59 | line = self.size.0 - 1; 60 | } 61 | self.pos = (line, 0) 62 | } 63 | 64 | fn backspace(&mut self) { 65 | let (line, col) = self.pos; 66 | self.pos = (line, if col > 0 { col - 1 } else { col }); 67 | self.print(' '); 68 | let (line, col) = self.pos; 69 | self.pos = (line, if col > 0 { col - 1 } else { col }); 70 | } 71 | 72 | fn next_char(&mut self) { 73 | let (mut line, mut col) = self.pos; 74 | col += 1; 75 | 76 | if col == self.size.1 { 77 | col = 0; 78 | line += 1; 79 | } 80 | 81 | if line == self.size.0 { 82 | if let Some(display) = &mut *GOP_DISPLAY.lock() { 83 | display.scroll_up(16); 84 | } 85 | line = self.size.0 - 1; 86 | } 87 | self.pos = (line, col) 88 | } 89 | } 90 | 91 | impl vte::Perform for Performer { 92 | fn print(&mut self, c: char) { 93 | if let Some(display) = &mut *GOP_DISPLAY.lock() { 94 | let (line, col) = self.pos; 95 | 96 | let mut buf = [0; 4]; 97 | let result = c.encode_utf8(&mut buf); 98 | let style = TextStyleBuilder::new(Font8x16) 99 | .text_color(RgbColor::WHITE) 100 | .background_color(RgbColor::BLACK) 101 | .build(); 102 | Text::new(result, Point::new(col as i32 * 8, line as i32 * 16)) 103 | .into_styled(style) 104 | .draw(display) 105 | .ok(); 106 | } 107 | self.next_char(); 108 | } 109 | 110 | fn execute(&mut self, byte: u8) { 111 | match byte { 112 | C0::LF => self.linefeed(), 113 | C0::BS => self.backspace(), 114 | _ => (/* TODO */), 115 | } 116 | } 117 | 118 | fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _action: char) { 119 | /* UNHANDLED */ 120 | } 121 | 122 | fn put(&mut self, _byte: u8) { 123 | /* UNHANDLED */ 124 | } 125 | 126 | fn unhook(&mut self) { 127 | /* UNHANDLED */ 128 | } 129 | 130 | fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) { 131 | /* TODO */ 132 | } 133 | 134 | fn csi_dispatch(&mut self, _params: &[i64], intermediates: &[u8], _ignore: bool, action: char) { 135 | if let Some(display) = &mut *GOP_DISPLAY.lock() { 136 | match (action, intermediates.get(0)) { 137 | (ansi::ED, None) => { 138 | display.clear(RgbColor::BLACK).unwrap(); 139 | } 140 | _ => (/* UNHANDLED */), 141 | } 142 | } 143 | } 144 | 145 | fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) { 146 | /* TODO */ 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/device/serial_console.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::device::uart; 2 | use crate::device::console::Console; 3 | 4 | pub struct SerialConsole; 5 | 6 | impl SerialConsole { 7 | pub fn new() -> Self { 8 | Self 9 | } 10 | } 11 | 12 | impl Console for SerialConsole { 13 | fn write(&mut self, buf: &[u8]) { 14 | let mut port = uart::COM1.lock(); 15 | for &c in buf { 16 | port.send(c); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/device/uart.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use spin::Mutex; 3 | use uart_16550::SerialPort; 4 | 5 | lazy_static! { 6 | pub static ref COM1: Mutex = Mutex::new({ 7 | let mut serial_port = unsafe { SerialPort::new(COM1_PORT) }; 8 | serial_port.init(); 9 | serial_port 10 | }); 11 | } 12 | 13 | pub const COM1_PORT: u16 = 0x3f8; 14 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/gdt.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use x86_64::instructions::segmentation::{load_ds, load_es, load_gs, load_ss, set_cs}; 3 | use x86_64::instructions::tables::load_tss; 4 | use x86_64::structures::gdt::*; 5 | use x86_64::structures::tss::TaskStateSegment; 6 | use x86_64::PrivilegeLevel; 7 | use x86_64::VirtAddr; 8 | 9 | pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; 10 | pub const PAGE_FAULT_IST_INDEX: u16 = 1; 11 | 12 | lazy_static! { 13 | pub static ref GDT: (GlobalDescriptorTable, Selectors) = { 14 | let mut gdt = GlobalDescriptorTable::new(); 15 | 16 | let kernel_code = { 17 | let flags = DescriptorFlags::USER_SEGMENT 18 | | DescriptorFlags::PRESENT 19 | | DescriptorFlags::EXECUTABLE 20 | | DescriptorFlags::LONG_MODE; 21 | Descriptor::UserSegment(flags.bits()) 22 | }; 23 | 24 | let kernel_data = { 25 | let flags = DescriptorFlags::USER_SEGMENT 26 | | DescriptorFlags::PRESENT 27 | | DescriptorFlags::WRITABLE; 28 | Descriptor::UserSegment(flags.bits()) 29 | }; 30 | 31 | let user_code = { 32 | let flags = DescriptorFlags::USER_SEGMENT 33 | | DescriptorFlags::PRESENT 34 | | DescriptorFlags::EXECUTABLE 35 | | DescriptorFlags::LONG_MODE 36 | | DescriptorFlags::DPL_RING_3; 37 | Descriptor::UserSegment(flags.bits()) 38 | }; 39 | 40 | let user_data = { 41 | let flags = DescriptorFlags::USER_SEGMENT 42 | | DescriptorFlags::PRESENT 43 | | DescriptorFlags::WRITABLE 44 | | DescriptorFlags::DPL_RING_3; 45 | Descriptor::UserSegment(flags.bits()) 46 | }; 47 | 48 | // The order is required. 49 | let kernel_code_selector = gdt.add_entry(kernel_code); 50 | 51 | let kernel_data_selector = gdt.add_entry(kernel_data); 52 | 53 | let mut user_data_selector = gdt.add_entry(user_data); 54 | user_data_selector.set_rpl(PrivilegeLevel::Ring3); 55 | 56 | let mut user_code_selector = gdt.add_entry(user_code); 57 | user_code_selector.set_rpl(PrivilegeLevel::Ring3); 58 | 59 | let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); 60 | 61 | ( 62 | gdt, 63 | Selectors { 64 | kernel_code_selector, 65 | kernel_data_selector, 66 | user_code_selector, 67 | user_data_selector, 68 | tss_selector, 69 | }, 70 | ) 71 | }; 72 | pub static ref TSS: TaskStateSegment = { 73 | let mut tss = TaskStateSegment::new(); 74 | tss.privilege_stack_table[0] = { 75 | const STACK_SIZE: usize = 4096; 76 | static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; 77 | 78 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 79 | stack_start + STACK_SIZE 80 | }; 81 | tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { 82 | const STACK_SIZE: usize = 4096; 83 | static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; 84 | 85 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 86 | stack_start + STACK_SIZE 87 | }; 88 | tss.interrupt_stack_table[PAGE_FAULT_IST_INDEX as usize] = { 89 | const STACK_SIZE: usize = 4096; 90 | static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; 91 | 92 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 93 | stack_start + STACK_SIZE 94 | }; 95 | tss 96 | }; 97 | } 98 | 99 | pub struct Selectors { 100 | pub kernel_code_selector: SegmentSelector, 101 | pub kernel_data_selector: SegmentSelector, 102 | pub user_code_selector: SegmentSelector, 103 | pub user_data_selector: SegmentSelector, 104 | pub tss_selector: SegmentSelector, 105 | } 106 | 107 | pub fn init() { 108 | GDT.0.load(); 109 | unsafe { 110 | load_ss(GDT.1.kernel_data_selector); 111 | load_ds(GDT.1.kernel_data_selector); 112 | load_es(GDT.1.kernel_data_selector); 113 | load_gs(GDT.1.kernel_data_selector); 114 | 115 | set_cs(GDT.1.kernel_code_selector); 116 | load_tss(GDT.1.tss_selector); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use x86_64::structures::idt::InterruptDescriptorTable; 3 | 4 | pub use x86_64::instructions::interrupts::*; 5 | 6 | pub mod controller; 7 | 8 | mod breakpoint; 9 | mod double_fault; 10 | mod general_protection_fault; 11 | mod page_fault; 12 | 13 | mod com; 14 | mod keyboard; 15 | mod timer; 16 | 17 | use controller::*; 18 | 19 | lazy_static! { 20 | static ref IDT: InterruptDescriptorTable = { 21 | let mut idt = InterruptDescriptorTable::new(); 22 | idt.breakpoint.set_handler_fn(breakpoint::handler); 23 | idt.general_protection_fault 24 | .set_handler_fn(general_protection_fault::handler); 25 | unsafe { 26 | idt.page_fault 27 | .set_handler_fn(page_fault::handler) 28 | .set_stack_index(super::gdt::PAGE_FAULT_IST_INDEX); 29 | idt.double_fault 30 | .set_handler_fn(double_fault::handler) 31 | .set_stack_index(super::gdt::DOUBLE_FAULT_IST_INDEX); 32 | } 33 | 34 | idt[(T_IRQ0 + IRQ_TIMER) as usize].set_handler_fn(timer::handler); 35 | idt[(T_IRQ0 + IRQ_KEYBOARD) as usize].set_handler_fn(keyboard::handler); 36 | idt[(T_IRQ0 + IRQ_COM1) as usize].set_handler_fn(com::handler); 37 | idt 38 | }; 39 | } 40 | 41 | pub fn init() { 42 | IDT.load(); 43 | } 44 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/breakpoint.rs: -------------------------------------------------------------------------------- 1 | use x86_64::structures::idt::InterruptStackFrame; 2 | 3 | pub extern "x86-interrupt" fn handler(stack_frame: InterruptStackFrame) { 4 | println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); 5 | } 6 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/com.rs: -------------------------------------------------------------------------------- 1 | use super::controller::LOCAL_APIC; 2 | use crate::arch::device::uart; 3 | use x86_64::structures::idt::InterruptStackFrame; 4 | 5 | pub extern "x86-interrupt" fn handler(_stack_frame: InterruptStackFrame) { 6 | let byte = { 7 | let mut port = uart::COM1.lock(); 8 | port.receive() 9 | }; 10 | 11 | // http://web.mit.edu/broder/Public/fixing-jos-serial.txt 12 | let byte = match byte { 13 | b'\r' => b'\n', 14 | b'\x7f' => b'\x08', 15 | _ => byte, 16 | }; 17 | 18 | print!("{}", byte as char); 19 | 20 | match crate::device::console::KEYBOARD_BUFFER.push(byte) { 21 | Ok(()) => (), 22 | Err(_) => println!("key queue full; dropping key"), 23 | } 24 | 25 | LOCAL_APIC.lock().end_of_interrupt(); 26 | } 27 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/controller.rs: -------------------------------------------------------------------------------- 1 | pub mod ioapic; 2 | pub mod lapic; 3 | pub mod pic; 4 | 5 | pub use lapic::LOCAL_APIC; 6 | 7 | pub fn init() { 8 | unsafe { 9 | pic::disable_8259_pic(); 10 | } 11 | 12 | lapic::LOCAL_APIC.lock().init(); 13 | 14 | let mut ioapic = ioapic::IoApic::default(); 15 | 16 | ioapic.write_irq(IRQ_KEYBOARD, 0, 0); 17 | ioapic.write_irq(IRQ_COM1, 0, 0); 18 | } 19 | 20 | pub const T_IRQ0: u8 = 0x20; 21 | 22 | pub const IRQ_TIMER: u8 = 0; 23 | pub const IRQ_KEYBOARD: u8 = 1; 24 | pub const IRQ_COM1: u8 = 4; 25 | pub const IRQ_SPURIOUS: u8 = 31; 26 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/controller/ioapic.rs: -------------------------------------------------------------------------------- 1 | use x86_64::VirtAddr; 2 | 3 | use super::*; 4 | use crate::config::*; 5 | 6 | pub const IOAPIC_BASE: u64 = 0xFEC0_0000; 7 | 8 | pub struct IoApic { 9 | sel: *mut u32, 10 | data: *mut u32, 11 | } 12 | 13 | impl IoApic { 14 | #[allow(unused)] 15 | pub unsafe fn new(addr: VirtAddr) -> Self { 16 | Self { 17 | sel: addr.as_mut_ptr(), 18 | data: (addr as VirtAddr + 0x10u64).as_mut_ptr(), 19 | } 20 | } 21 | 22 | #[allow(unused)] 23 | pub unsafe fn read(&mut self, reg: u8) -> u32 { 24 | self.sel.write_volatile(reg as u32); 25 | self.data.read_volatile() 26 | } 27 | 28 | pub unsafe fn write(&mut self, reg: u8, data: u32) { 29 | self.sel.write_volatile(reg as u32); 30 | self.data.write_volatile(data); 31 | } 32 | 33 | pub fn write_irq(&mut self, irq: u8, flags: u32, apic_id: u8) { 34 | unsafe { 35 | self.write(0x10 + 2 * irq, (T_IRQ0 + irq) as u32 | flags); 36 | self.write(0x10 + 2 * irq + 1, (apic_id as u32) << 24); 37 | } 38 | } 39 | 40 | #[allow(unused)] 41 | pub fn enable(&mut self, irq: u8, apic_id: u8) { 42 | self.write_irq(irq, 0, apic_id); 43 | } 44 | } 45 | 46 | impl Default for IoApic { 47 | fn default() -> Self { 48 | Self { 49 | sel: (PAGE_OFFSET_BASE + IOAPIC_BASE) as *mut u32, 50 | data: (PAGE_OFFSET_BASE + IOAPIC_BASE + 0x10) as *mut u32, 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/controller/lapic.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use spin::Mutex; 3 | use x86_64::VirtAddr; 4 | 5 | use super::*; 6 | use crate::config::*; 7 | 8 | pub const LOCAL_APIC_BASE: u64 = 0xFEE0_0000; 9 | 10 | pub struct LocalApic { 11 | base: VirtAddr, 12 | } 13 | 14 | impl LocalApic { 15 | pub unsafe fn new(addr: VirtAddr) -> Self { 16 | Self { base: addr } 17 | } 18 | 19 | pub unsafe fn read(&self, reg: u32) -> u32 { 20 | (self.base + reg as u64).as_ptr::().read_volatile() 21 | } 22 | 23 | pub unsafe fn write(&mut self, reg: u32, value: u32) { 24 | (self.base + reg as u64) 25 | .as_mut_ptr::() 26 | .write_volatile(value); 27 | // Wait for completion 28 | self.read(0x20); 29 | } 30 | 31 | pub fn init(&mut self) { 32 | unsafe { 33 | self.write(LAPIC_SVR, 0x100 + (T_IRQ0 + IRQ_SPURIOUS) as u32); 34 | 35 | self.write(LAPIC_TDCR, X1); // Timer divided by 1 36 | self.write(LAPIC_TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER) as u32); 37 | self.write(LAPIC_TICR, 10000000); 38 | 39 | self.write(LAPIC_EOI, 0); 40 | 41 | self.write(LAPIC_TPR, 0); 42 | } 43 | } 44 | 45 | pub fn end_of_interrupt(&mut self) { 46 | unsafe { 47 | self.write(LAPIC_EOI, 0); 48 | } 49 | } 50 | } 51 | 52 | lazy_static! { 53 | pub static ref LOCAL_APIC: Mutex = 54 | Mutex::new(unsafe { LocalApic::new(VirtAddr::new(PAGE_OFFSET_BASE + LOCAL_APIC_BASE)) }); 55 | } 56 | 57 | /// Local APIC ID 58 | pub const LAPIC_ID: u32 = 0x0020; 59 | /// Local APIC Version 60 | pub const LAPIC_VER: u32 = 0x0030; 61 | /// Task Priority 62 | pub const LAPIC_TPR: u32 = 0x0080; 63 | /// Arbitration Priority 64 | pub const LAPIC_APR: u32 = 0x0090; 65 | /// Processor Priority 66 | pub const LAPIC_PPR: u32 = 0x00a0; 67 | /// EOI 68 | pub const LAPIC_EOI: u32 = 0x00b0; 69 | /// Remote Read 70 | pub const LAPIC_RRD: u32 = 0x00c0; 71 | /// Logical Destination 72 | pub const LAPIC_LDR: u32 = 0x00d0; 73 | /// Destination Format 74 | pub const LAPIC_DFR: u32 = 0x00e0; 75 | /// Spurious Interrupt Vector 76 | pub const LAPIC_SVR: u32 = 0x00f0; 77 | /// In-Service (8 registers) 78 | pub const LAPIC_ISR: u32 = 0x0100; 79 | /// Trigger Mode (8 registers) 80 | pub const LAPIC_TMR: u32 = 0x0180; 81 | /// Interrupt Request (8 registers) 82 | pub const LAPIC_IRR: u32 = 0x0200; 83 | /// Error Status 84 | pub const LAPIC_ESR: u32 = 0x0280; 85 | /// Interrupt Command 86 | pub const LAPIC_ICRLO: u32 = 0x0300; 87 | /// Interrupt Command [63:32] 88 | pub const LAPIC_ICRHI: u32 = 0x0310; 89 | /// LVT Timer 90 | pub const LAPIC_TIMER: u32 = 0x0320; 91 | /// LVT Thermal Sensor 92 | pub const LAPIC_THERMAL: u32 = 0x0330; 93 | /// LVT Performance Counter 94 | pub const LAPIC_PERF: u32 = 0x0340; 95 | /// LVT LINT0 96 | pub const LAPIC_LINT0: u32 = 0x0350; 97 | /// LVT LINT1 98 | pub const LAPIC_LINT1: u32 = 0x0360; 99 | /// LVT Error 100 | pub const LAPIC_ERROR: u32 = 0x0370; 101 | /// Initial Count (for Timer) 102 | pub const LAPIC_TICR: u32 = 0x0380; 103 | /// Current Count (for Timer) 104 | pub const LAPIC_TCCR: u32 = 0x0390; 105 | /// Divide Configuration (for Timer) 106 | pub const LAPIC_TDCR: u32 = 0x03e0; 107 | 108 | pub const X1: u32 = 0x0000000B; 109 | pub const PERIODIC: u32 = 0x00020000; 110 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/controller/pic.rs: -------------------------------------------------------------------------------- 1 | use x86_64::instructions::port::Port; 2 | 3 | static mut MASTER: Pic = Pic::new(0x20); 4 | static mut SLAVE: Pic = Pic::new(0xA0); 5 | 6 | pub unsafe fn disable_8259_pic() { 7 | MASTER.cmd.write(0x11); 8 | SLAVE.cmd.write(0x11); 9 | 10 | MASTER.data.write(0x20); 11 | SLAVE.data.write(0x28); 12 | 13 | MASTER.data.write(4); 14 | SLAVE.data.write(2); 15 | 16 | MASTER.data.write(1); 17 | SLAVE.data.write(1); 18 | 19 | MASTER.data.write(0xff); 20 | SLAVE.data.write(0xff); 21 | 22 | MASTER.ack(); 23 | SLAVE.ack(); 24 | } 25 | 26 | pub struct Pic { 27 | cmd: Port, 28 | data: Port, 29 | } 30 | 31 | impl Pic { 32 | pub const fn new(port: u16) -> Self { 33 | Self { 34 | cmd: Port::new(port), 35 | data: Port::new(port + 1), 36 | } 37 | } 38 | 39 | pub fn ack(&mut self) { 40 | unsafe { 41 | self.cmd.write(0x20); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/double_fault.rs: -------------------------------------------------------------------------------- 1 | use x86_64::structures::idt::InterruptStackFrame; 2 | 3 | pub extern "x86-interrupt" fn handler(stack_frame: InterruptStackFrame, error_code: u64) -> ! { 4 | panic!( 5 | "EXCEPTION: DOUBLE FAULT\nerror_code: {:#x}\n{:#?}", 6 | error_code, stack_frame 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/general_protection_fault.rs: -------------------------------------------------------------------------------- 1 | use x86_64::structures::idt::InterruptStackFrame; 2 | 3 | pub extern "x86-interrupt" fn handler(stack_frame: InterruptStackFrame, error_code: u64) { 4 | panic!( 5 | "EXCEPTION: GENERAL PROTECTION FAULT\nerror_code: {:#x}\n{:#?}", 6 | error_code, stack_frame 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/keyboard.rs: -------------------------------------------------------------------------------- 1 | use super::controller::LOCAL_APIC; 2 | use lazy_static::lazy_static; 3 | use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; 4 | use spin::Mutex; 5 | use x86_64::instructions::port::Port; 6 | use x86_64::structures::idt::InterruptStackFrame; 7 | 8 | lazy_static! { 9 | static ref KEYBOARD: Mutex> = Mutex::new( 10 | Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore) 11 | ); 12 | } 13 | 14 | pub extern "x86-interrupt" fn handler(_stack_frame: InterruptStackFrame) { 15 | let mut keyboard = KEYBOARD.lock(); 16 | let mut port = Port::::new(0x60); 17 | let scancode = unsafe { port.read() }; 18 | 19 | if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { 20 | if let Some(key) = keyboard.process_keyevent(key_event) { 21 | match key { 22 | DecodedKey::Unicode(c) => { 23 | print!("{}", c); 24 | let mut buf = [0; 1]; 25 | c.encode_utf8(&mut buf); 26 | 27 | match crate::device::console::KEYBOARD_BUFFER.push(buf[0]) { 28 | Ok(()) => (), 29 | Err(_) => println!("key queue full; dropping key"), 30 | } 31 | } 32 | DecodedKey::RawKey(key) => print!("{:?}", key), 33 | } 34 | } 35 | } 36 | LOCAL_APIC.lock().end_of_interrupt(); 37 | } 38 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/page_fault.rs: -------------------------------------------------------------------------------- 1 | use x86_64::registers::control::Cr2; 2 | use x86_64::structures::idt::InterruptStackFrame; 3 | use x86_64::structures::idt::PageFaultErrorCode; 4 | 5 | pub extern "x86-interrupt" fn handler( 6 | stack_frame: InterruptStackFrame, 7 | error_code: PageFaultErrorCode, 8 | ) { 9 | panic!( 10 | "EXCEPTION: PAGE FAULT\ncr2: {:#?}\nerror_code: {:#?}\n{:#?}", 11 | Cr2::read(), 12 | error_code, 13 | stack_frame 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/interrupt/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::cpu::my_cpu; 2 | 3 | use super::controller::LOCAL_APIC; 4 | use x86_64::structures::idt::InterruptStackFrame; 5 | 6 | pub extern "x86-interrupt" fn handler(_stack_frame: InterruptStackFrame) { 7 | unsafe { asm!("cli") }; 8 | LOCAL_APIC.lock().end_of_interrupt(); 9 | 10 | let cpu = my_cpu(); 11 | if cpu.current_process.is_some() { 12 | //unsafe { 13 | // cpu.switch_to_kernel(); 14 | //} 15 | } 16 | unsafe { asm!("sti") }; 17 | } 18 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/macros.rs: -------------------------------------------------------------------------------- 1 | #[repr(packed)] 2 | pub struct ScratchRegisters { 3 | pub rcx: usize, 4 | pub rdx: usize, 5 | pub rsi: usize, 6 | pub rdi: usize, 7 | pub r8: usize, 8 | pub r9: usize, 9 | pub r10: usize, 10 | pub r11: usize, 11 | pub rax: usize, 12 | } 13 | 14 | #[repr(packed)] 15 | pub struct PreservedRegisters { 16 | pub r15: usize, 17 | pub r14: usize, 18 | pub r13: usize, 19 | pub r12: usize, 20 | pub rbp: usize, 21 | pub rbx: usize, 22 | } 23 | 24 | #[repr(packed)] 25 | pub struct IretRegisters { 26 | pub rip: usize, 27 | pub cs: usize, 28 | pub rflags: usize, 29 | pub rsp: usize, 30 | pub ss: usize, 31 | } 32 | 33 | #[repr(packed)] 34 | pub struct SyscallStackFrame { 35 | pub scratch: ScratchRegisters, 36 | pub preserved: PreservedRegisters, 37 | pub iret: IretRegisters, 38 | } 39 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | 4 | pub mod allocator; 5 | pub mod config; 6 | mod console; 7 | pub mod context; 8 | mod cpuid; 9 | pub mod device; 10 | pub mod gdt; 11 | pub mod interrupt; 12 | pub mod paging; 13 | mod start; 14 | pub mod syscall; 15 | 16 | #[inline(always)] 17 | pub fn idle() -> ! { 18 | loop { 19 | unsafe { 20 | llvm_asm!("hlt"); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/paging.rs: -------------------------------------------------------------------------------- 1 | use crate::{config::*, memory::FrameAllocator}; 2 | use boot::BootArgs; 3 | use uefi::table::boot::MemoryType; 4 | use x86_64::registers::control::Cr3; 5 | use x86_64::structures::paging::{PageTable, PageTableFlags, PhysFrame}; 6 | pub use x86_64::{PhysAddr, VirtAddr}; 7 | 8 | pub fn disable_identity_mapping() { 9 | let page_table = unsafe { active_page_table() }; 10 | 11 | for i in 0..256 { 12 | page_table[i].set_unused(); 13 | } 14 | 15 | x86_64::instructions::tlb::flush_all(); 16 | } 17 | 18 | pub fn init_frame_allocator(args: &BootArgs) { 19 | let mut allocator = crate::memory::FRAME_ALLOCATOR.lock(); 20 | for descriptor in args.memory_map.clone().iter { 21 | if descriptor.ty == MemoryType::CONVENTIONAL { 22 | allocator.install_memory_region( 23 | PhysAddr::new(descriptor.phys_start), 24 | descriptor.page_count as usize, 25 | ); 26 | } 27 | } 28 | } 29 | 30 | pub unsafe fn active_page_table() -> &'static mut PageTable { 31 | let (level_4_table_frame, _) = Cr3::read(); 32 | 33 | let phys = level_4_table_frame.start_address(); 34 | let virt = PAGE_OFFSET_BASE + phys.as_u64(); 35 | let page_table_ptr: *mut PageTable = virt as *mut _; 36 | 37 | &mut *page_table_ptr 38 | } 39 | 40 | pub struct AddressSpace { 41 | pub cr3: PhysFrame, 42 | } 43 | 44 | impl AddressSpace { 45 | pub fn new() -> AddressSpace { 46 | let table_frame = crate::memory::FRAME_ALLOCATOR 47 | .lock() 48 | .allocate_frame() 49 | .unwrap(); 50 | let table = unsafe { 51 | &mut *{ (PAGE_OFFSET_BASE + table_frame.start_address().as_u64()) as *mut PageTable } 52 | }; 53 | let current_table = unsafe { active_page_table() }; 54 | for i in 0..256 { 55 | table[i].set_unused(); 56 | } 57 | for i in 256..512 { 58 | table[i] = current_table[i].clone(); 59 | } 60 | table[511].set_frame( 61 | table_frame, 62 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE, 63 | ); 64 | AddressSpace { cr3: table_frame } 65 | } 66 | 67 | pub unsafe fn page_table(&mut self) -> &'static mut PageTable { 68 | let phys = self.cr3.start_address(); 69 | let virt = PAGE_OFFSET_BASE + phys.as_u64(); 70 | let page_table_ptr: *mut PageTable = virt as *mut _; 71 | 72 | &mut *page_table_ptr 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/start.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::memory; 3 | use boot::{BootArgs, KernelEntryFn, BOOT_ARGS_MAGIC}; 4 | 5 | #[used] 6 | static BSS_ZERO_CHECK: u64 = 0; 7 | #[used] 8 | static DATA_NONZERO_CHECK: u64 = 0xFFFF_FFFF_FFFF_FFFF; 9 | 10 | #[doc(hidden)] 11 | #[allow(unused)] 12 | const KERNEL_ENTRY_SIGNATURE_TYPE_CHECK: KernelEntryFn = _start; 13 | 14 | #[no_mangle] 15 | extern "sysv64" fn _start(args: &BootArgs) -> ! { 16 | assert_eq!(BSS_ZERO_CHECK, 0); 17 | assert_eq!(DATA_NONZERO_CHECK, 0xFFFF_FFFF_FFFF_FFFF); 18 | assert_eq!(args.magic, BOOT_ARGS_MAGIC); 19 | 20 | crate::video::init(args); 21 | 22 | paging::disable_identity_mapping(); 23 | 24 | paging::init_frame_allocator(args); 25 | // After this point, we can allocate memory 26 | crate::allocator::init_heap(); 27 | 28 | // We wanna see outputs 29 | console::init(); 30 | 31 | dbg!(args); 32 | 33 | memory::print_memory_map(&args.memory_map); 34 | 35 | cpuid::init(); 36 | 37 | gdt::init(); 38 | 39 | interrupt::init(); 40 | 41 | interrupt::controller::init(); 42 | 43 | syscall::init(); 44 | 45 | interrupt::enable(); 46 | 47 | // crate::video::fun_things(); 48 | 49 | crate::kmain(); 50 | } 51 | -------------------------------------------------------------------------------- /xv7-kernel/src/arch/x86_64/syscall.rs: -------------------------------------------------------------------------------- 1 | use super::gdt; 2 | use super::macros::SyscallStackFrame; 3 | use x86_64::registers::model_specific::{Efer, EferFlags, KernelGsBase, LStar, SFMask, Star}; 4 | use x86_64::registers::rflags::RFlags; 5 | use x86_64::VirtAddr; 6 | 7 | #[naked] 8 | #[inline(never)] 9 | pub unsafe extern "C" fn syscall_entry() { 10 | asm!( 11 | "swapgs", // Load kernel TSS pointer 12 | "mov gs:[0x1c], rsp", // Save userspace %rsp 13 | "mov rsp, gs:[0x4]", // Load TSS %rsp 14 | "push 0x1b", // Push userspace data segment 15 | "push gs:[0x1c]", // Push userspace %rsp 16 | "mov QWORD PTR gs:[0x1c], 0", // Clear userspace %rsp 17 | "push r11", // Push rflags 18 | "push 0x23", // Push userspace code segment 19 | "push rcx", // Push userspace return pointer 20 | "swapgs", // Restore %gs 21 | // push preserved 22 | "push rbx", 23 | "push rbp", 24 | "push r12", 25 | "push r13", 26 | "push r14", 27 | "push r15", 28 | // push scratch 29 | "push rax", 30 | "push r11", 31 | "push r10", 32 | "push r9", 33 | "push r8", 34 | "push rdi", 35 | "push rsi", 36 | "push rdx", 37 | "push rcx", 38 | // call syscall_inner 39 | "mov rdi, rsp", 40 | "call {}", 41 | // pop scratch 42 | "pop rcx", 43 | "pop rdx", 44 | "pop rsi", 45 | "pop rdi", 46 | "pop r8", 47 | "pop r9", 48 | "pop r10", 49 | "pop r11", 50 | "pop rax", 51 | // pop preserved 52 | "pop r15", 53 | "pop r14", 54 | "pop r13", 55 | "pop r12", 56 | "pop rbp", 57 | "pop rbx", 58 | // interrupt return 59 | "iretq", 60 | sym syscall_inner, 61 | options(noreturn), 62 | ); 63 | } 64 | 65 | extern "C" fn syscall_inner(stack: *mut SyscallStackFrame) { 66 | let stack = unsafe { &mut *stack }; 67 | let scratch = &stack.scratch; 68 | stack.scratch.rax = crate::syscall::syscall( 69 | scratch.rax, 70 | scratch.rdi, 71 | scratch.rsi, 72 | scratch.rdx, 73 | scratch.r10, 74 | scratch.r8, 75 | ); 76 | } 77 | 78 | pub fn init() { 79 | // Setup syscall/sysret cs/ss 80 | Star::write( 81 | gdt::GDT.1.user_code_selector, 82 | gdt::GDT.1.user_data_selector, 83 | gdt::GDT.1.kernel_code_selector, 84 | gdt::GDT.1.kernel_data_selector, 85 | ) 86 | .unwrap(); 87 | 88 | // Setup syscall target rip 89 | LStar::write(VirtAddr::from_ptr(syscall_entry as *const u8)); 90 | 91 | // Setup flags to clear 92 | let mask = RFlags::TRAP_FLAG 93 | | RFlags::DIRECTION_FLAG 94 | | RFlags::INTERRUPT_FLAG 95 | | RFlags::IOPL_HIGH 96 | | RFlags::IOPL_LOW 97 | | RFlags::ALIGNMENT_CHECK 98 | | RFlags::NESTED_TASK; 99 | 100 | SFMask::write(mask); 101 | 102 | KernelGsBase::write(VirtAddr::from_ptr(&*gdt::TSS as *const _)); 103 | 104 | // Enable syscall extensions 105 | unsafe { 106 | Efer::update(|efer| efer.insert(EferFlags::SYSTEM_CALL_EXTENSIONS)); 107 | } 108 | 109 | dbg!(&*gdt::TSS); 110 | 111 | dbg!(Efer::read()); 112 | dbg!(Star::read()); 113 | dbg!(LStar::read()); 114 | dbg!(SFMask::read()); 115 | dbg!(KernelGsBase::read()); 116 | } 117 | -------------------------------------------------------------------------------- /xv7-kernel/src/config.rs: -------------------------------------------------------------------------------- 1 | pub use crate::arch::config::*; 2 | 3 | /// Maximum pyhsical memory that we decided to support. 4 | #[allow(unused)] 5 | pub const MAX_PHYSICAL_ADDRESS_SUPPORTED: usize = 35; 6 | /// Maximum number of pyhsical frames calculated from `MAX_PHYSICAL_ADDRESS_SUPPORTED`. 7 | #[allow(unused)] 8 | pub const MAX_FRAMES_SUPPORTED: usize = 1 << MAX_PHYSICAL_ADDRESS_SUPPORTED >> 12; 9 | -------------------------------------------------------------------------------- /xv7-kernel/src/cpu.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::gdt; 2 | use crate::context::Context; 3 | use crate::process::Process; 4 | use x86_64::structures::tss::TaskStateSegment; 5 | 6 | #[repr(C)] 7 | pub struct Cpu { 8 | pub kernel_context: Context, 9 | pub current_process: Option, 10 | } 11 | 12 | impl Cpu { 13 | pub const fn new() -> Cpu { 14 | Cpu { 15 | kernel_context: Context::new(), 16 | current_process: None, 17 | } 18 | } 19 | 20 | pub unsafe fn switch_to_process(&mut self) { 21 | let proc = &self.current_process.as_ref().unwrap(); 22 | // HACK: Verrrrry dirty hack here 23 | let tss = &mut *((&*gdt::TSS) as *const _ as *mut TaskStateSegment); 24 | tss.privilege_stack_table[1] = tss.privilege_stack_table[0]; 25 | tss.privilege_stack_table[0] = proc.kstack + 4096u64; 26 | self.kernel_context.switch_to(&proc.context); 27 | } 28 | 29 | pub unsafe fn switch_to_kernel(&mut self) { 30 | match self.current_process.as_mut() { 31 | Some(p) => { 32 | let tss = &mut *((&*gdt::TSS) as *const _ as *mut TaskStateSegment); 33 | tss.privilege_stack_table[0] = tss.privilege_stack_table[1]; 34 | p.context.switch_to(&self.kernel_context) 35 | } 36 | None => panic!("No process running"), 37 | } 38 | } 39 | } 40 | 41 | /// TODO: Support more cpus 42 | static mut CPUS: [Cpu; 1] = [Cpu::new(); 1]; 43 | 44 | pub fn my_cpu() -> &'static mut Cpu { 45 | unsafe { &mut CPUS[0] } 46 | } 47 | -------------------------------------------------------------------------------- /xv7-kernel/src/device.rs: -------------------------------------------------------------------------------- 1 | pub mod console; 2 | -------------------------------------------------------------------------------- /xv7-kernel/src/device/console.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use alloc::vec::Vec; 3 | use core::fmt; 4 | use crossbeam_queue::ArrayQueue; 5 | use lazy_static::lazy_static; 6 | use spin::Mutex; 7 | 8 | /// A console device. 9 | pub trait Console { 10 | fn write(&mut self, buf: &[u8]); 11 | } 12 | 13 | pub struct ConsoleDrivers { 14 | consoles: Vec>, 15 | } 16 | 17 | impl ConsoleDrivers { 18 | pub fn new() -> Self { 19 | Self { 20 | consoles: Vec::new(), 21 | } 22 | } 23 | 24 | pub fn register(&mut self, console: Box) { 25 | self.consoles.push(console) 26 | } 27 | 28 | pub fn write(&mut self, buf: &[u8]) { 29 | for console in &mut self.consoles { 30 | console.write(buf); 31 | } 32 | } 33 | } 34 | 35 | impl fmt::Write for ConsoleDrivers { 36 | fn write_str(&mut self, s: &str) -> fmt::Result { 37 | for console in &mut self.consoles { 38 | console.write(s.as_bytes()); 39 | } 40 | Ok(()) 41 | } 42 | } 43 | 44 | lazy_static! { 45 | pub static ref CONSOLE_DRIVERS: Mutex = Mutex::new(ConsoleDrivers::new()); 46 | } 47 | 48 | lazy_static! { 49 | pub static ref KEYBOARD_BUFFER: ArrayQueue = ArrayQueue::new(1024); 50 | } 51 | -------------------------------------------------------------------------------- /xv7-kernel/src/fs.rs: -------------------------------------------------------------------------------- 1 | pub mod dev; 2 | pub mod file; 3 | pub mod vfs; 4 | 5 | pub type Result = core::result::Result; 6 | 7 | #[derive(Debug, Eq, PartialEq)] 8 | pub enum FsError { 9 | NotSupported, 10 | FileError, 11 | } 12 | 13 | use alloc::collections::BTreeMap; 14 | use alloc::string::String; 15 | use alloc::sync::Arc; 16 | use lazy_static::lazy_static; 17 | use spin::Mutex; 18 | use vfs::Inode; 19 | 20 | lazy_static! { 21 | pub static ref FILE_SYSTEM: Mutex>> = 22 | Mutex::new(BTreeMap::new()); 23 | } 24 | -------------------------------------------------------------------------------- /xv7-kernel/src/fs/dev.rs: -------------------------------------------------------------------------------- 1 | use super::vfs::Inode; 2 | use crate::arch::interrupt::without_interrupts; 3 | pub struct Console; 4 | 5 | impl Inode for Console { 6 | fn read_at(&self, _offset: usize, buf: &mut [u8]) -> super::Result { 7 | let mut cnt = 0; 8 | while let Ok(b) = crate::device::console::KEYBOARD_BUFFER.pop() { 9 | if cnt >= buf.len() { 10 | break; 11 | } 12 | buf[cnt] = b; 13 | cnt += 1; 14 | } 15 | Ok(cnt) 16 | } 17 | 18 | fn write_at(&self, _offset: usize, buf: &[u8]) -> super::Result { 19 | without_interrupts(|| crate::device::console::CONSOLE_DRIVERS.lock().write(buf)); 20 | Ok(buf.len()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /xv7-kernel/src/fs/file.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::vfs::Inode; 2 | use crate::fs::Result; 3 | use alloc::sync::Arc; 4 | 5 | use super::vfs::Metadata; 6 | 7 | pub struct File { 8 | inode: Arc, 9 | offset: usize, 10 | readable: bool, 11 | writable: bool, 12 | } 13 | 14 | impl File { 15 | pub fn new(inode: Arc, readable: bool, writable: bool) -> Self { 16 | File { 17 | inode, 18 | offset: 0, 19 | readable, 20 | writable, 21 | } 22 | } 23 | 24 | pub fn read(&mut self, buf: &mut [u8]) -> Result { 25 | assert!(self.readable); 26 | let len = self.inode.read_at(self.offset, buf)?; 27 | self.offset += len; 28 | Ok(len) 29 | } 30 | 31 | pub fn write(&mut self, buf: &[u8]) -> Result { 32 | assert!(self.writable); 33 | let len = self.inode.write_at(self.offset, buf)?; 34 | self.offset += len; 35 | Ok(len) 36 | } 37 | 38 | pub fn info(&self) -> Result { 39 | self.inode.metadata() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /xv7-kernel/src/fs/vfs.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::{FsError, Result}; 2 | use core::any::Any; 3 | 4 | pub trait Inode: Any + Sync + Send { 5 | fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result; 6 | fn write_at(&self, offset: usize, buf: &[u8]) -> Result; 7 | fn metadata(&self) -> Result { 8 | Err(FsError::NotSupported) 9 | } 10 | fn set_metadata(&self, _metadata: &Metadata) -> Result<()> { 11 | Err(FsError::NotSupported) 12 | } 13 | } 14 | 15 | #[derive(Debug, Eq, PartialEq, Clone)] 16 | pub struct Metadata { 17 | /// Device ID 18 | pub dev: usize, // (major << 8) | minor 19 | /// Inode number 20 | pub inode: usize, 21 | /// Size in bytes 22 | /// 23 | /// SFS Note: for normal file size is the actuate file size 24 | /// for directory this is count of dirent. 25 | pub size: usize, 26 | /// Type of file 27 | pub type_: FileType, 28 | /// Permission 29 | pub mode: u16, 30 | /// Number of hard links 31 | pub nlinks: usize, 32 | /// Raw device id 33 | /// e.g. /dev/null: makedev(0x1, 0x3) 34 | pub rdev: usize, // (major << 8) | minor 35 | } 36 | 37 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 38 | pub enum FileType { 39 | File, 40 | Dir, 41 | SymLink, 42 | CharDevice, 43 | BlockDevice, 44 | NamedPipe, 45 | Socket, 46 | } 47 | -------------------------------------------------------------------------------- /xv7-kernel/src/macros.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::interrupt::without_interrupts; 2 | use core::fmt; 3 | use core::fmt::Write; 4 | 5 | #[macro_export] 6 | macro_rules! print { 7 | ($($arg:tt)*) => ($crate::macros::_print(format_args!($($arg)*))); 8 | } 9 | 10 | #[macro_export] 11 | macro_rules! println { 12 | () => ($crate::print!("\n")); 13 | ($($arg:tt)*) => ({ 14 | $crate::print!("{}\n", format_args!($($arg)*)); 15 | }) 16 | } 17 | 18 | #[doc(hidden)] 19 | pub fn _print(args: fmt::Arguments) { 20 | without_interrupts(|| { 21 | crate::device::console::CONSOLE_DRIVERS 22 | .lock() 23 | .write_fmt(args) 24 | .unwrap(); 25 | }) 26 | } 27 | 28 | /// Prints and returns the value of a given expression for quick and dirty 29 | /// debugging. 30 | /// 31 | /// Copied from standard library with slight modifications. 32 | #[macro_export] 33 | macro_rules! dbg { 34 | () => { 35 | $crate::println!("[{}:{}]", file!(), line!()); 36 | }; 37 | ($val:expr) => { 38 | match $val { 39 | tmp => { 40 | $crate::println!("[{}:{}] {} = {:?}", 41 | file!(), line!(), stringify!($val), &tmp); 42 | tmp 43 | } 44 | } 45 | }; 46 | ($val:expr,) => { $crate::dbg!($val) }; 47 | ($($val:expr),+ $(,)?) => { 48 | ($($crate::dbg!($val)),+,) 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /xv7-kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![cfg_attr(doc, allow(unused_attributes))] 4 | #![cfg_attr(target_arch = "x86_64", feature(abi_x86_interrupt))] 5 | #![feature(alloc_error_handler)] 6 | #![feature(asm)] 7 | #![feature(box_syntax)] 8 | #![feature(box_patterns)] 9 | #![feature(llvm_asm)] 10 | #![feature(naked_functions)] 11 | 12 | extern crate alloc; 13 | 14 | #[macro_use] 15 | mod macros; 16 | 17 | mod allocator; 18 | pub mod ansi; 19 | pub mod arch; 20 | mod config; 21 | pub mod cpu; 22 | pub mod device; 23 | pub mod fs; 24 | #[cfg(target_arch = "x86_64")] 25 | mod memory; 26 | mod pretty; 27 | pub mod process; 28 | mod rt; 29 | pub mod scheduler; 30 | pub mod syscall; 31 | mod video; 32 | 33 | pub use crate::arch::context; 34 | pub use crate::arch::paging; 35 | 36 | pub fn kmain() -> ! { 37 | println!("We are alive!"); 38 | scheduler::scheduler(); 39 | } 40 | -------------------------------------------------------------------------------- /xv7-kernel/src/memory/bitmap.rs: -------------------------------------------------------------------------------- 1 | use crate::config::*; 2 | use crate::pretty::Pretty; 3 | use bitvec::prelude::*; 4 | use boot::PhysAddr; 5 | use lazy_static::lazy_static; 6 | use spin::Mutex; 7 | pub use x86_64::structures::paging::{FrameAllocator, FrameDeallocator}; 8 | use x86_64::structures::paging::{PageSize, PhysFrame, Size4KiB}; 9 | 10 | pub struct BitmapFrameAllocator<'map> { 11 | #[allow(unused)] 12 | inner: &'map mut BitSlice, 13 | } 14 | 15 | impl<'map> BitmapFrameAllocator<'map> { 16 | pub fn new(map: &'map mut [u8]) -> Self { 17 | Self { 18 | inner: BitSlice::from_slice_mut(map), 19 | } 20 | } 21 | 22 | pub fn install_memory_region(&mut self, phys_start: PhysAddr, page_count: usize) { 23 | assert!(phys_start.is_aligned(Size4KiB::SIZE)); 24 | let frame_start = (phys_start.as_u64() / Size4KiB::SIZE) as usize; 25 | for i in frame_start..frame_start + page_count { 26 | self.inner.set(i, true); 27 | } 28 | } 29 | 30 | #[allow(unused)] 31 | pub fn print_statistics(&mut self) { 32 | println!( 33 | "BitmapFrameAllocator: bitmap occupies {}, maximum supported pyhsical memory: {}", 34 | self.inner.len().pretty(), 35 | (self.inner.len() * Size4KiB::SIZE as usize).pretty(), 36 | ); 37 | 38 | let frames = self.inner.count_ones(); 39 | 40 | println!( 41 | "BitmapFrameAllocator: {} frames available, which is {} of memory", 42 | frames, 43 | (frames * Size4KiB::SIZE as usize).pretty(), 44 | ); 45 | } 46 | } 47 | 48 | unsafe impl<'map> FrameAllocator for BitmapFrameAllocator<'map> { 49 | fn allocate_frame(&mut self) -> Option> { 50 | let frame = self 51 | .inner 52 | .iter() 53 | .enumerate() 54 | .filter_map(|(index, unused)| { 55 | if *unused { 56 | Some(( 57 | index, 58 | PhysFrame::containing_address(PhysAddr::new(index as u64 * Size4KiB::SIZE)), 59 | )) 60 | } else { 61 | None 62 | } 63 | }) 64 | .next(); 65 | 66 | if let Some((index, frame)) = frame { 67 | self.inner.set(index, false); 68 | Some(frame) 69 | } else { 70 | None 71 | } 72 | } 73 | } 74 | 75 | impl<'map> FrameDeallocator for BitmapFrameAllocator<'map> { 76 | unsafe fn deallocate_frame(&mut self, frame: PhysFrame) { 77 | let index = frame.start_address().as_u64() / Size4KiB::SIZE; 78 | self.inner.set(index as usize, true); 79 | } 80 | } 81 | 82 | lazy_static! { 83 | pub static ref FRAME_ALLOCATOR: Mutex> = { 84 | static mut MAP: [u8; MAX_FRAMES_SUPPORTED / 8] = [0; MAX_FRAMES_SUPPORTED / 8]; 85 | Mutex::new(BitmapFrameAllocator::new(unsafe { &mut MAP })) 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /xv7-kernel/src/memory/buddy.rs: -------------------------------------------------------------------------------- 1 | use crate::config::*; 2 | use crate::pretty::Pretty; 3 | use bitvec::prelude::*; 4 | use boot::PhysAddr; 5 | use core::mem; 6 | use core::ptr; 7 | use lazy_static::lazy_static; 8 | use spin::Mutex; 9 | pub use x86_64::structures::paging::{FrameAllocator, FrameDeallocator}; 10 | use x86_64::structures::paging::{PageSize, PhysFrame, Size4KiB}; 11 | 12 | /// Buddy system allocator 13 | /// 14 | /// another frame allocator implemented with buddy system 15 | pub struct BuddyFrameAllocator<'zone> { 16 | zone: &'zone mut BuddyZone, 17 | frames: &'zone mut [BuddyFrame; MAX_FRAMES_SUPPORTED], 18 | } 19 | 20 | /// The max value of order. Consecutive (1< { 26 | (($val + $align - 1) / $align * $align) 27 | }; 28 | } 29 | 30 | /// Macro to Round down $val by $align 31 | macro_rules! align_to_lower { 32 | ($val: expr, $align:expr) => { 33 | ($val / $align * $align) 34 | }; 35 | } 36 | 37 | /// Macro to get the buddy of a block 38 | macro_rules! get_buddy { 39 | ($frame_index: expr, $order:expr) => { 40 | if ($frame_index) & (1 << ($order + 1) - 1) == 0 { 41 | $frame_index + (1 << $order) 42 | } else { 43 | $frame_index - (1 << $order) 44 | } 45 | }; 46 | } 47 | 48 | /// The status of each block 49 | #[repr(u8)] 50 | #[derive(Copy, Clone, PartialEq)] 51 | enum BuddyFrameStatus { 52 | UNCHECKED = 0, // default value, which means the block is not used and cannot be used 53 | USED = 1, // the block was used 54 | NOTUSED = 2, // the block was not used, so it should be stored in a BuddyFreeArea 55 | } 56 | 57 | /// A block of frames in buddy system, represents the physical memory of (1 << order) size 58 | #[derive(Copy, Clone)] 59 | struct BuddyFrame { 60 | next: *mut BuddyFrame, // Simply embed a pointers in it 61 | use_status: BuddyFrameStatus, 62 | order: u8, 63 | } 64 | 65 | /// Represens a group of blocks with same order. 66 | #[derive(Copy, Clone)] 67 | struct BuddyFreeArea { 68 | head: *mut BuddyFrame, 69 | length: usize, 70 | } 71 | /// Memory zone, BuddyFrameAllocator may allocate memory in different areas according to demand(). 72 | /// Only one zone is implemented now. 73 | #[derive(Copy, Clone)] 74 | struct BuddyZone { 75 | head: *mut BuddyFrame, 76 | free_area: [BuddyFreeArea; MAX_ORDER as usize], 77 | } 78 | 79 | impl BuddyFreeArea { 80 | unsafe fn drop_frame(&mut self, frame: *mut BuddyFrame) -> *mut BuddyFrame { 81 | let mut next = self.head; 82 | let mut pre = ptr::null_mut(); 83 | while !next.is_null() && next != frame { 84 | pre = next; 85 | next = (*next).next; 86 | } 87 | if next.is_null() { 88 | return ptr::null_mut(); 89 | } 90 | self.length -= 1; 91 | let next = (*frame).next; 92 | 93 | if !pre.is_null() { 94 | (*pre).next = next; 95 | } else { 96 | // head 97 | self.head = next; 98 | } 99 | (*frame).next = ptr::null_mut(); 100 | (*frame).use_status = BuddyFrameStatus::NOTUSED; 101 | return frame; 102 | } 103 | 104 | unsafe fn push_frame(&mut self, frame: *mut BuddyFrame) { 105 | self.length += 1; 106 | (*frame).next = self.head; 107 | self.head = frame; 108 | (*frame).use_status = BuddyFrameStatus::NOTUSED; 109 | } 110 | 111 | unsafe fn pop_frame(&mut self) -> *mut BuddyFrame { 112 | if self.length == 0 { 113 | return ptr::null_mut(); 114 | } 115 | self.length -= 1; 116 | let head = self.head; 117 | self.head = (*head).next; 118 | (*head).next = ptr::null_mut(); 119 | (*head).use_status = BuddyFrameStatus::USED; 120 | return head; 121 | } 122 | } 123 | 124 | impl BuddyZone { 125 | fn count_free_mem(&self) -> usize { 126 | let mut mem_count = 0usize; 127 | for i in 0..MAX_ORDER { 128 | mem_count += self.free_area[i as usize].length * (1 << i); 129 | } 130 | return mem_count; 131 | } 132 | } 133 | 134 | unsafe impl<'zone> Send for BuddyFrameAllocator<'zone> {} 135 | 136 | impl<'zone> BuddyFrameAllocator<'zone> { 137 | fn new( 138 | zone: &'zone mut BuddyZone, 139 | frames: &'zone mut [BuddyFrame; MAX_FRAMES_SUPPORTED], 140 | ) -> Self { 141 | Self { 142 | zone: zone, 143 | frames: frames, 144 | } 145 | } 146 | 147 | fn index_of_frame(&mut self, frame: *mut BuddyFrame) -> usize { 148 | (frame as usize - (&mut self.frames[0] as *mut BuddyFrame) as usize) 149 | / mem::size_of::() 150 | } 151 | pub fn install_memory_region(&mut self, phys_start: PhysAddr, page_count: usize) { 152 | assert!(phys_start.is_aligned(Size4KiB::SIZE)); 153 | let frame_start = (phys_start.as_u64() / Size4KiB::SIZE) as usize; 154 | unsafe { 155 | self.free_frame_range(frame_start, frame_start + page_count); 156 | } 157 | } 158 | 159 | unsafe fn free_frame_range(&mut self, index_l: usize, index_r: usize) { 160 | self.free_frame_range_top_down(index_l, index_r, MAX_ORDER - 1) 161 | } 162 | 163 | unsafe fn free_frame_range_top_down(&mut self, index_l: usize, index_r: usize, order: u8) { 164 | if index_l >= index_r || order >= MAX_ORDER { 165 | return; 166 | } 167 | 168 | let block_size: usize = 1 << order; 169 | 170 | let align_index_l: usize = align_to_upper!(index_l, block_size); 171 | let align_index_r: usize = align_to_lower!(index_r, block_size); 172 | if align_index_l <= align_index_r { 173 | self.free_frame_range_top_down(index_l, align_index_l, order.wrapping_sub(1)); 174 | for frame_index in (align_index_l..align_index_r).step_by(1 << order) { 175 | self.free_frame_specific_order(frame_index, order); 176 | } 177 | self.free_frame_range_top_down(align_index_r, index_r, order.wrapping_sub(1)); 178 | } else { 179 | self.free_frame_range_top_down(index_l, index_r, order.wrapping_sub(1)); 180 | } 181 | } 182 | 183 | unsafe fn free_frame_specific_order(&mut self, mut frame_index: usize, mut order: u8) { 184 | if order >= MAX_ORDER { 185 | return; 186 | } 187 | 188 | if self.frames[frame_index].use_status == BuddyFrameStatus::NOTUSED { 189 | println!( 190 | "BuddyFrameAllocator: free twice on frame({}) detected", 191 | frame_index 192 | ); 193 | return; 194 | } 195 | // there are one zome only in this implementation 196 | while order < MAX_ORDER { 197 | if order == MAX_ORDER - 1 { 198 | break; 199 | } 200 | let area = &mut self.zone.free_area[order as usize]; 201 | let buddy_index = get_buddy!(frame_index, order); 202 | let buddy_frame = area.drop_frame(&mut self.frames[buddy_index]); 203 | if !buddy_frame.is_null() { 204 | frame_index = if frame_index < buddy_index { 205 | frame_index 206 | } else { 207 | buddy_index 208 | }; 209 | order += 1; 210 | } else { 211 | break; 212 | } 213 | } 214 | assert_eq!( 215 | frame_index, 216 | align_to_lower!(frame_index, (1 << order) as usize), 217 | "frame_index {} cannot match order {}", 218 | frame_index, 219 | order 220 | ); 221 | 222 | self.frames[frame_index].order = order; 223 | self.zone.free_area[order as usize].push_frame(&mut self.frames[frame_index]); 224 | } 225 | 226 | unsafe fn alloc_frame_specific_order(&mut self, order: u8) -> *mut BuddyFrame { 227 | let mut upper_order = order; 228 | while upper_order < MAX_ORDER && self.zone.free_area[upper_order as usize].length <= 0 { 229 | upper_order += 1; // search down to top 230 | } 231 | if upper_order >= MAX_ORDER { 232 | return ptr::null_mut(); // alloc failed, not enough space 233 | } 234 | let large_frame = self.zone.free_area[upper_order as usize].pop_frame(); 235 | while upper_order > order { 236 | let offset = (1 << (upper_order - 1)) + self.index_of_frame(large_frame); 237 | self.frames[offset].order = upper_order - 1; 238 | self.zone.free_area[(upper_order - 1) as usize] 239 | .push_frame(&mut self.frames[offset] as *mut BuddyFrame); 240 | upper_order -= 1; 241 | } 242 | (*large_frame).use_status = BuddyFrameStatus::USED; 243 | (*large_frame).order = order; 244 | return large_frame; 245 | } 246 | 247 | pub unsafe fn check_bugs(&mut self) { 248 | for i in 0..MAX_ORDER { 249 | let area = self.zone.free_area[i as usize]; 250 | 251 | let mut j = 0; 252 | let mut cur = area.head; 253 | while !cur.is_null() { 254 | let next = (*cur).next; 255 | let offset = self.index_of_frame(cur); 256 | assert_eq!( 257 | offset, 258 | align_to_lower!(offset, (1 << i) as usize), 259 | "area({})'s frame at index({}) has offset({}), cannot match order", 260 | i, 261 | j, 262 | offset 263 | ); 264 | j += 1; 265 | cur = next; 266 | } 267 | assert_eq!( 268 | j, area.length, 269 | "area({})'s length was not equals to it's link length", 270 | i 271 | ); 272 | } 273 | } 274 | 275 | #[allow(unused)] 276 | pub unsafe fn print_statistics(&mut self) { 277 | self.check_bugs(); 278 | let free_mem_count = self.zone.count_free_mem(); 279 | println!( 280 | "BuddyFrameAllocator: {} frames available, which is {} of memory", 281 | free_mem_count, 282 | (free_mem_count * Size4KiB::SIZE as usize).pretty(), 283 | ); 284 | 285 | print!("default zone:\t"); 286 | for i in 0..MAX_ORDER { 287 | print!("{:>8}", self.zone.free_area[i as usize].length); 288 | } 289 | println!(); 290 | } 291 | } 292 | 293 | unsafe impl<'zone> FrameAllocator for BuddyFrameAllocator<'zone> { 294 | fn allocate_frame(&mut self) -> Option> { 295 | let frame = unsafe { self.alloc_frame_specific_order(0) }; 296 | if !frame.is_null() { 297 | Some(PhysFrame::containing_address(PhysAddr::new( 298 | self.index_of_frame(frame) as u64 * Size4KiB::SIZE, 299 | ))) 300 | } else { 301 | None 302 | } 303 | } 304 | } 305 | 306 | impl<'zone> FrameDeallocator for BuddyFrameAllocator<'zone> { 307 | unsafe fn deallocate_frame(&mut self, frame: PhysFrame) { 308 | let index = frame.start_address().as_u64() / Size4KiB::SIZE; 309 | self.free_frame_specific_order(index as usize, 0); 310 | } 311 | } 312 | 313 | lazy_static! { 314 | pub static ref FRAME_ALLOCATOR: Mutex> = { 315 | unsafe { 316 | static mut FRAMES: [BuddyFrame; MAX_FRAMES_SUPPORTED] = [BuddyFrame { 317 | next: ptr::null_mut(), 318 | use_status: BuddyFrameStatus::UNCHECKED, 319 | order: 0, 320 | }; 321 | MAX_FRAMES_SUPPORTED]; 322 | static mut DEFAULT_ZONE: BuddyZone = BuddyZone { 323 | head: ptr::null_mut(), 324 | free_area: [BuddyFreeArea { 325 | head: ptr::null_mut(), 326 | length: 0, 327 | }; MAX_ORDER as usize], 328 | }; 329 | DEFAULT_ZONE.head = &mut FRAMES[0]; 330 | Mutex::new(BuddyFrameAllocator::new(&mut DEFAULT_ZONE, &mut FRAMES)) 331 | } 332 | }; 333 | } 334 | -------------------------------------------------------------------------------- /xv7-kernel/src/memory/mod.rs: -------------------------------------------------------------------------------- 1 | use boot::MemoryMap; 2 | pub use x86_64::structures::paging::{FrameAllocator, FrameDeallocator}; 3 | use x86_64::structures::paging::{PageSize, Size4KiB}; 4 | 5 | #[cfg(feature = "frame-allocator-bitmap")] 6 | mod bitmap; 7 | #[cfg(feature = "frame-allocator-bitmap")] 8 | pub use bitmap::FRAME_ALLOCATOR; 9 | 10 | #[cfg(feature = "frame-allocator-buddy")] 11 | mod buddy; 12 | #[cfg(feature = "frame-allocator-buddy")] 13 | pub use buddy::FRAME_ALLOCATOR; 14 | 15 | pub fn print_memory_map(mmap: &MemoryMap) { 16 | println!("Mem phys map:"); 17 | for descriptor in mmap.clone().iter { 18 | println!( 19 | "[mem {:#016x}-{:#016x} {:>8}] {:?}", 20 | descriptor.phys_start, 21 | descriptor.phys_start + descriptor.page_count * Size4KiB::SIZE - 1, 22 | descriptor.page_count, 23 | descriptor.ty 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /xv7-kernel/src/pretty.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | pub struct PrettyRef<'a, T>(&'a T); 4 | 5 | pub trait Pretty { 6 | fn pretty(&self) -> PrettyRef; 7 | } 8 | 9 | impl Pretty for T { 10 | fn pretty(&self) -> PrettyRef { 11 | PrettyRef(self) 12 | } 13 | } 14 | 15 | impl<'a> fmt::Display for PrettyRef<'a, usize> { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | if *self.0 >= 1 << 40 { 18 | write!(f, "{}TiB", self.0 >> 40) 19 | } else if *self.0 >= 1 << 30 { 20 | write!(f, "{}GiB", self.0 >> 30) 21 | } else if *self.0 >= 1 << 20 { 22 | write!(f, "{}MiB", self.0 >> 20) 23 | } else if *self.0 >= 1 << 10 { 24 | write!(f, "{}KiB", self.0 >> 10) 25 | } else { 26 | write!(f, "{}B", self.0) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /xv7-kernel/src/process.rs: -------------------------------------------------------------------------------- 1 | use crate::context::Context; 2 | use crate::cpu::my_cpu; 3 | use crate::fs::file::File; 4 | use crate::paging::{AddressSpace, VirtAddr}; 5 | use crate::{ 6 | config::*, 7 | memory::{FrameAllocator, FRAME_ALLOCATOR}, 8 | }; 9 | use alloc::vec::Vec; 10 | use core::sync::atomic::{AtomicUsize, Ordering}; 11 | use x86_64::structures::idt::InterruptStackFrameValue; 12 | 13 | pub enum ProcessState { 14 | Spawn, 15 | Runnable, 16 | Running, 17 | Zombie, 18 | } 19 | 20 | static NEXT_PID: AtomicUsize = AtomicUsize::new(0); 21 | 22 | pub struct Process { 23 | pub pid: usize, 24 | pub state: ProcessState, 25 | pub vm: AddressSpace, 26 | pub context: Context, 27 | pub kstack: VirtAddr, 28 | pub fds: Vec, 29 | } 30 | 31 | impl Process { 32 | pub fn new() -> Process { 33 | let kstack = { 34 | let frame = FRAME_ALLOCATOR.lock().allocate_frame().unwrap(); 35 | VirtAddr::new(frame.start_address().as_u64() + PAGE_OFFSET_BASE) 36 | }; 37 | 38 | let stack_pointer = 39 | kstack + 4096usize - core::mem::size_of::() - 8usize; 40 | 41 | let mut context = Context::user(stack_pointer); 42 | 43 | let vm = AddressSpace::new(); 44 | 45 | context.cr3 = vm.cr3.start_address().as_u64() as usize; 46 | 47 | unsafe { 48 | stack_pointer 49 | .as_mut_ptr::() 50 | .write(interrupt_return as *const u8 as u64); 51 | }; 52 | 53 | Process { 54 | pid: NEXT_PID.fetch_add(1, Ordering::Relaxed), 55 | context, 56 | vm, 57 | state: ProcessState::Spawn, 58 | kstack, 59 | fds: Vec::new(), 60 | } 61 | } 62 | 63 | pub fn initcode() -> Process { 64 | let mut p = Process::new(); 65 | p.set_context_switch_return_address(VirtAddr::new(initcode as *const u8 as u64)); 66 | p 67 | } 68 | 69 | pub fn intr_stack_frame(&mut self) -> &mut InterruptStackFrameValue { 70 | unsafe { 71 | &mut *((self.kstack.as_u64() + 4096 72 | - core::mem::size_of::() as u64) 73 | as *mut InterruptStackFrameValue) 74 | } 75 | } 76 | 77 | fn set_context_switch_return_address(&mut self, addr: VirtAddr) { 78 | let stack_pointer = 79 | self.kstack + 4096usize - core::mem::size_of::() - 8usize; 80 | unsafe { 81 | stack_pointer.as_mut_ptr::().write(addr.as_u64()); 82 | }; 83 | } 84 | 85 | pub fn set_userspace_return_address( 86 | &mut self, 87 | instruction_pointer: VirtAddr, 88 | stack_pointer: VirtAddr, 89 | ) { 90 | self.intr_stack_frame().instruction_pointer = instruction_pointer; 91 | self.intr_stack_frame().stack_pointer = stack_pointer; 92 | // FIXME: magic number 93 | self.intr_stack_frame().code_segment = 0x23; 94 | self.intr_stack_frame().stack_segment = 0x1b; 95 | self.intr_stack_frame().cpu_flags = 0x282; 96 | } 97 | } 98 | 99 | impl Clone for Process { 100 | fn clone(&self) -> Self { 101 | let mut p = Process::new(); 102 | 103 | p 104 | } 105 | } 106 | 107 | #[naked] 108 | unsafe extern "C" fn interrupt_return() { 109 | asm!("iretq", options(noreturn)) 110 | } 111 | 112 | #[naked] 113 | unsafe extern "C" fn initcode() { 114 | extern "C" fn initcode_exec() { 115 | crate::syscall::process::exec("/init"); 116 | } 117 | asm!( 118 | "call {}", 119 | "iretq", 120 | sym initcode_exec, 121 | options(noreturn) 122 | ) 123 | } 124 | 125 | #[naked] 126 | unsafe extern "C" fn fork_return() { 127 | asm!("retq", options(noreturn)); 128 | } 129 | 130 | pub fn my_proc() -> &'static mut Process { 131 | let cpu = my_cpu(); 132 | cpu.current_process.as_mut().unwrap() 133 | } 134 | -------------------------------------------------------------------------------- /xv7-kernel/src/rt.rs: -------------------------------------------------------------------------------- 1 | use alloc::alloc::Layout; 2 | use core::panic::PanicInfo; 3 | 4 | #[panic_handler] 5 | fn panic(info: &PanicInfo) -> ! { 6 | crate::arch::interrupt::disable(); 7 | crate::println!("kernel {}", info); 8 | crate::arch::idle(); 9 | } 10 | 11 | #[alloc_error_handler] 12 | fn alloc_error_handler(layout: Layout) -> ! { 13 | panic!("Failed to allocate memory: {:?}", layout) 14 | } 15 | -------------------------------------------------------------------------------- /xv7-kernel/src/scheduler.rs: -------------------------------------------------------------------------------- 1 | use crate::cpu::my_cpu; 2 | use crate::process::Process; 3 | 4 | pub fn scheduler() -> ! { 5 | let cpu = my_cpu(); 6 | let p = Process::initcode(); 7 | 8 | cpu.current_process = Some(p); 9 | 10 | loop { 11 | println!("[scheduler] pick next process to run"); 12 | unsafe { 13 | cpu.switch_to_process(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /xv7-kernel/src/syscall.rs: -------------------------------------------------------------------------------- 1 | pub mod fs; 2 | pub mod process; 3 | 4 | use core::slice; 5 | use core::str; 6 | 7 | use usyscall::error::*; 8 | use usyscall::number::*; 9 | 10 | pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> usize { 11 | fn inner(a: usize, b: usize, c: usize, d: usize, _e: usize, _f: usize) -> Result { 12 | match a { 13 | SYS_EXIT => process::exit(b as isize), 14 | SYS_WRITE => fs::write(b, validate_slice(c as *const u8, d)?), 15 | SYS_READ => fs::read(b, validate_slice_mut(c as *mut u8, d)?), 16 | SYS_FORK => process::fork(), 17 | SYS_GETPID => process::getpid(), 18 | SYS_YIELD => process::r#yield(), 19 | SYS_MKNOD => fs::mknod(validate_str(b as *const u8, c)?, d), 20 | SYS_OPEN => fs::open(validate_str(b as *const u8, c)?), 21 | _ => Err(Error::new(ENOSYS)), 22 | } 23 | } 24 | 25 | let result = inner(a, b, c, d, e, f); 26 | 27 | Error::mux(result) 28 | } 29 | 30 | pub fn validate_slice(ptr: *const T, len: usize) -> Result<&'static [T]> { 31 | Ok(unsafe { slice::from_raw_parts(ptr, len) }) 32 | } 33 | 34 | pub fn validate_slice_mut(ptr: *mut T, len: usize) -> Result<&'static mut [T]> { 35 | Ok(unsafe { slice::from_raw_parts_mut(ptr, len) }) 36 | } 37 | 38 | pub fn validate_str(ptr: *const u8, len: usize) -> Result<&'static str> { 39 | let slice = validate_slice(ptr, len)?; 40 | str::from_utf8(slice).map_err(|_| Error::new(EINVAL)) 41 | } 42 | -------------------------------------------------------------------------------- /xv7-kernel/src/syscall/fs.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::file::File; 2 | use crate::fs::FILE_SYSTEM; 3 | use crate::{fs::dev::Console, process::my_proc}; 4 | use alloc::string::String; 5 | use alloc::sync::Arc; 6 | use usyscall::error::*; 7 | 8 | pub fn mknod(path: &str, dev: usize) -> Result { 9 | if dev == 1 { 10 | FILE_SYSTEM 11 | .lock() 12 | .insert(String::from(path), Arc::new(Console)); 13 | Ok(0) 14 | } else { 15 | Err(Error::new(ENODEV)) 16 | } 17 | } 18 | 19 | pub fn open(path: &str) -> Result { 20 | let proc = my_proc(); 21 | match FILE_SYSTEM.lock().get(path) { 22 | None => dbg!(Err(Error::new(ENOENT))), 23 | Some(inode) => { 24 | let inode = inode.clone(); 25 | let file = File::new(inode, true, true); 26 | proc.fds.push(file); 27 | dbg!(Ok(proc.fds.len() - 1)) 28 | } 29 | } 30 | } 31 | 32 | pub fn write(fd: usize, buf: &[u8]) -> Result { 33 | let proc = my_proc(); 34 | 35 | match proc.fds.get_mut(fd) { 36 | Some(f) => f.write(buf).map_err(|_| Error::new(EFAULT)), 37 | None => Err(Error::new(ENOENT)), 38 | } 39 | } 40 | 41 | pub fn read(fd: usize, buf: &mut [u8]) -> Result { 42 | let proc = my_proc(); 43 | 44 | match proc.fds.get_mut(fd) { 45 | Some(f) => f.read(buf).map_err(|_| Error::new(EFAULT)), 46 | None => Err(Error::new(ENOENT)), 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /xv7-kernel/src/syscall/process.rs: -------------------------------------------------------------------------------- 1 | use crate::process; 2 | use crate::{ 3 | config::*, 4 | memory::{FrameAllocator, FRAME_ALLOCATOR}, 5 | }; 6 | use crate::{cpu::my_cpu, paging::VirtAddr}; 7 | use goblin::elf; 8 | use goblin::elf::reloc::*; 9 | use x86_64::structures::paging::{Mapper, Page}; 10 | use x86_64::structures::paging::{OffsetPageTable, PageTableFlags}; 11 | use zeroize::Zeroize; 12 | 13 | use usyscall::error::*; 14 | 15 | pub fn exit(code: isize) -> Result { 16 | let proc = process::my_proc(); 17 | if proc.pid == 0 { 18 | panic!("pid 0 exited with code {}", code); 19 | } 20 | Ok(0) 21 | } 22 | 23 | pub fn exec(path: &str) { 24 | let proc = process::my_proc(); 25 | 26 | let image = match path { 27 | "/init" => images::INIT, 28 | _ => panic!("We have no filesystem yet; executables are hardcoded"), 29 | }; 30 | 31 | let image_elf = elf::Elf::parse(image).expect("Failed to parse ELF file"); 32 | 33 | let mut frame_allocator = FRAME_ALLOCATOR.lock(); 34 | // FIXME: we should free the previous vm and set up a new vm 35 | let page_table = unsafe { proc.vm.page_table() }; 36 | let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(PAGE_OFFSET_BASE)) }; 37 | 38 | for ph in image_elf.program_headers { 39 | if ph.p_type == elf::program_header::PT_LOAD { 40 | let page_range = { 41 | let start_addr = VirtAddr::new(ph.p_vaddr); 42 | let end_addr = start_addr + ph.p_memsz - 1u64; 43 | let start_page = Page::containing_address(start_addr); 44 | let end_page = Page::containing_address(end_addr); 45 | Page::range_inclusive(start_page, end_page) 46 | }; 47 | 48 | let mut flags = PageTableFlags::PRESENT 49 | | PageTableFlags::USER_ACCESSIBLE 50 | | PageTableFlags::WRITABLE; 51 | //if ph.is_write() { 52 | //} 53 | if !ph.is_executable() { 54 | flags |= PageTableFlags::NO_EXECUTE; 55 | } 56 | 57 | for page in page_range { 58 | let frame = frame_allocator.allocate_frame().unwrap(); 59 | unsafe { 60 | mapper 61 | .map_to(page, frame, flags, &mut *frame_allocator) 62 | .unwrap() 63 | .flush(); 64 | } 65 | } 66 | 67 | let dst = unsafe { 68 | core::slice::from_raw_parts_mut((ph.p_vaddr) as *mut u8, ph.vm_range().len()) 69 | }; 70 | 71 | dst.zeroize(); 72 | 73 | dst[0..ph.file_range().len()].copy_from_slice(&image[ph.file_range()]); 74 | } 75 | } 76 | 77 | // allocate and map user stack. 78 | { 79 | let page = Page::containing_address(VirtAddr::new(USER_STACK)); 80 | let frame = frame_allocator.allocate_frame().unwrap(); 81 | let flags = PageTableFlags::PRESENT 82 | | PageTableFlags::USER_ACCESSIBLE 83 | | PageTableFlags::WRITABLE 84 | | PageTableFlags::NO_EXECUTE; 85 | 86 | unsafe { 87 | mapper 88 | .map_to(page, frame, flags, &mut *frame_allocator) 89 | .unwrap() 90 | .flush(); 91 | } 92 | } 93 | 94 | // Relocate executable 95 | for reloc in image_elf.dynrelas.iter() { 96 | match reloc.r_type { 97 | R_X86_64_RELATIVE => { 98 | let addr = (reloc.r_offset) as *mut u64; 99 | unsafe { 100 | *addr = reloc.r_addend.unwrap() as u64; 101 | } 102 | } 103 | _ => unimplemented!("Unhandled reloc type!"), 104 | } 105 | } 106 | 107 | // FIXME: magic number 108 | proc.set_userspace_return_address( 109 | VirtAddr::new(image_elf.entry), 110 | VirtAddr::new(USER_STACK + 4096 - 16), 111 | ); 112 | } 113 | 114 | pub fn fork() -> Result { 115 | let _proc = process::my_proc(); 116 | 117 | Ok(0) 118 | } 119 | 120 | pub fn getpid() -> Result { 121 | Ok(process::my_proc().pid) 122 | } 123 | 124 | mod images { 125 | pub const INIT: &'static [u8] = include_bytes!("../../../target/x86_64/debug/init"); 126 | } 127 | 128 | pub(crate) fn r#yield() -> Result { 129 | let cpu = my_cpu(); 130 | if cpu.current_process.is_some() { 131 | unsafe { 132 | cpu.switch_to_kernel(); 133 | } 134 | } 135 | Ok(0) 136 | } 137 | -------------------------------------------------------------------------------- /xv7-kernel/src/video.rs: -------------------------------------------------------------------------------- 1 | use crate::config::PAGE_OFFSET_BASE; 2 | use boot::BootArgs; 3 | use embedded_graphics::{egtext, text_style}; 4 | use embedded_graphics::{fonts::Font8x16, image::Image, pixelcolor::Rgb888, prelude::*}; 5 | use lazy_static::lazy_static; 6 | use spin::Mutex; 7 | use tinybmp::Bmp; 8 | 9 | lazy_static! { 10 | pub static ref GOP_DISPLAY: Mutex> = Mutex::new(None); 11 | } 12 | 13 | pub struct GopDisplay(u64, (usize, usize)); 14 | 15 | impl DrawTarget for GopDisplay { 16 | type Error = (); 17 | 18 | #[inline(always)] 19 | fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), ()> { 20 | let Pixel(coord, color) = pixel; 21 | let index = self.size().width as i32 * coord.y + coord.x; 22 | 23 | unsafe { 24 | core::ptr::write_volatile( 25 | ((PAGE_OFFSET_BASE + self.0) as *mut u32).add(index as usize), 26 | ((color.r() as u32) << 16) | ((color.g() as u32) << 8) | (color.b() as u32), 27 | ); 28 | } 29 | 30 | Ok(()) 31 | } 32 | 33 | #[inline(always)] 34 | fn size(&self) -> Size { 35 | Size::new((self.1).0 as u32, (self.1).1 as u32) 36 | } 37 | 38 | fn clear(&mut self, _color: Rgb888) -> Result<(), ()> { 39 | unsafe { 40 | core::ptr::write_bytes( 41 | (PAGE_OFFSET_BASE + self.0) as *mut u32, 42 | 0, /* FIXME: color? */ 43 | (self.1).0 * (self.1).1, 44 | ) 45 | } 46 | Ok(()) 47 | } 48 | } 49 | 50 | impl GopDisplay { 51 | pub fn scroll_up(&mut self, distance: usize) { 52 | unsafe { 53 | let base = (PAGE_OFFSET_BASE + self.0) as *mut u32; 54 | let (width, height) = self.1; 55 | for y in 0..height - distance { 56 | let dst = base.add(y * width); 57 | let src = base.add((y + distance) * width); 58 | core::ptr::copy_nonoverlapping(src, dst, width); 59 | } 60 | for y in height - distance..height { 61 | let dst = base.add(y * width); 62 | core::ptr::write_bytes(dst, 0, width); 63 | } 64 | } 65 | } 66 | } 67 | 68 | pub fn init(args: &BootArgs) { 69 | GOP_DISPLAY.lock().replace(GopDisplay( 70 | args.frame_buffer.base.as_u64(), 71 | args.frame_buffer.resolution, 72 | )); 73 | } 74 | 75 | #[allow(unused)] 76 | pub fn splash_screen() { 77 | if let Some(display) = &mut *GOP_DISPLAY.lock() { 78 | display.clear(RgbColor::WHITE).unwrap(); 79 | 80 | let img = Bmp::from_slice(include_bytes!("../resources/logo.bmp")).unwrap(); 81 | let logo = Image::new(&img, Point::zero()); 82 | 83 | logo.translate( 84 | ( 85 | (display.size().width - img.width()) as i32 / 2, 86 | (display.size().height - img.height()) as i32 / 2, 87 | ) 88 | .into(), 89 | ) 90 | .draw(display) 91 | .unwrap(); 92 | 93 | egtext!( 94 | text = "XV7: Yet Another Operating System by imtsuki", 95 | top_left = (100, 100), 96 | style = text_style!(font = Font8x16, text_color = RgbColor::BLACK) 97 | ) 98 | .draw(display) 99 | .unwrap(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /xv7-user/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xv7-user" 3 | version = "0.1.0" 4 | authors = ["imtsuki "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | usyscall = { path = "../xv7-usyscall", package = "xv7-usyscall" } 11 | -------------------------------------------------------------------------------- /xv7-user/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.xbuild] 2 | command = "cargo" 3 | args = ["xbuild", "--target", "../xv7-kernel/src/arch/x86_64.json"] 4 | 5 | [tasks.xbuild-aarch64] 6 | command = "cargo" 7 | args = ["xbuild", "--target", "../xv7-kernel/src/arch/aarch64.json"] 8 | -------------------------------------------------------------------------------- /xv7-user/src/bin/init.rs: -------------------------------------------------------------------------------- 1 | //! user space init process (pid 0). 2 | #![no_std] 3 | 4 | #[macro_use] 5 | extern crate xv7_user; 6 | 7 | use xv7_user::io; 8 | use xv7_user::syscall; 9 | fn main() { 10 | syscall::mknod("console", 1).unwrap(); 11 | syscall::open("console").unwrap(); 12 | syscall::open("console").unwrap(); 13 | 14 | syscall::r#yield().unwrap(); 15 | println!( 16 | "[init] Hello from userspace! getpid() = {}", 17 | syscall::getpid().unwrap() 18 | ); 19 | 20 | println!( 21 | r#" 22 | ___ ___ ___ 23 | |\__\ /\__\ /\ \ 24 | |:| | /:/ / \:\ \ 25 | |:| | /:/ / \:\ \ 26 | |:|__|__ /:/__/ ___ \:\ \ 27 | ____/::::\__\ |:| | /\__\ \:\__\ 28 | \::::/~~/~ |:| |/:/ / /:/ / 29 | ~~|:|~~| |:|__/:/ / /:/ / 30 | |:| | \::::/__/ /:/ / 31 | |:| | ~~~~ /:/ / 32 | \|__| \/__/ 33 | "# 34 | ); 35 | 36 | println!("Available commands (actually they are not): ls, cat, cp, mv, touch"); 37 | let mut buf = [0; 128]; 38 | loop { 39 | print!("$ "); 40 | let cmd = io::stdin().read_line(&mut buf); 41 | println!("sh: no such command `{}`", cmd); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /xv7-user/src/io.rs: -------------------------------------------------------------------------------- 1 | use crate::syscall; 2 | use core::fmt; 3 | use core::fmt::Write; 4 | 5 | struct Stdout; 6 | 7 | impl fmt::Write for Stdout { 8 | fn write_str(&mut self, s: &str) -> fmt::Result { 9 | syscall::write(1, s.as_bytes()).map_err(|_| fmt::Error)?; 10 | Ok(()) 11 | } 12 | } 13 | 14 | #[doc(hidden)] 15 | pub fn _print(args: fmt::Arguments) { 16 | Stdout.write_fmt(args).unwrap(); 17 | } 18 | pub struct Stdin; 19 | 20 | impl Stdin { 21 | pub fn read_line<'a>(&self, buf: &'a mut [u8]) -> &'a str { 22 | let mut idx = 0; 23 | loop { 24 | if idx >= buf.len() { 25 | break; 26 | } 27 | if let Ok(n) = syscall::read(0, &mut buf[idx..idx + 1]) { 28 | if n > 0 { 29 | if buf[idx] == b'\n' || buf[idx] == b'\r' { 30 | break; 31 | } 32 | idx += n; 33 | } 34 | } else { 35 | panic!("read_line"); 36 | } 37 | } 38 | core::str::from_utf8(&buf[0..idx]).unwrap() 39 | } 40 | } 41 | 42 | pub fn stdin() -> Stdin { 43 | Stdin 44 | } 45 | -------------------------------------------------------------------------------- /xv7-user/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(llvm_asm)] 3 | #![feature(lang_items)] 4 | 5 | #[macro_use] 6 | pub mod macros; 7 | 8 | pub mod io; 9 | pub mod process; 10 | mod rt; 11 | 12 | pub use usyscall as syscall; 13 | -------------------------------------------------------------------------------- /xv7-user/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! print { 3 | ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); 4 | } 5 | 6 | #[macro_export] 7 | macro_rules! println { 8 | () => ($crate::print!("\n")); 9 | ($($arg:tt)*) => ({ 10 | $crate::print!("{}\n", format_args!($($arg)*)); 11 | }) 12 | } 13 | 14 | /// Prints and returns the value of a given expression for quick and dirty 15 | /// debugging. 16 | /// 17 | /// Copied from standard library with slight modifications. 18 | #[macro_export] 19 | macro_rules! dbg { 20 | () => { 21 | $crate::println!("[{}:{}]", file!(), line!()); 22 | }; 23 | ($val:expr) => { 24 | match $val { 25 | tmp => { 26 | $crate::println!("[{}:{}] {} = {:?}", 27 | file!(), line!(), stringify!($val), &tmp); 28 | tmp 29 | } 30 | } 31 | }; 32 | ($val:expr,) => { $crate::dbg!($val) }; 33 | ($($val:expr),+ $(,)?) => { 34 | ($($crate::dbg!($val)),+,) 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /xv7-user/src/process.rs: -------------------------------------------------------------------------------- 1 | /// A trait for implementing arbitrary return types in the `main` function. 2 | /// 3 | /// The C-main function only supports to return integers as return type. 4 | /// So, every type implementing the `Termination` trait has to be converted 5 | /// to an integer. 6 | /// 7 | /// The default implementations are returning `libc::EXIT_SUCCESS` to indicate 8 | /// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned. 9 | pub trait Termination { 10 | /// Is called to get the representation of the value as status code. 11 | /// This status code is returned to the operating system. 12 | fn report(self) -> i32; 13 | } 14 | 15 | impl Termination for () { 16 | #[inline] 17 | fn report(self) -> i32 { 18 | 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /xv7-user/src/rt.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] 2 | #[panic_handler] 3 | fn panic(info: &core::panic::PanicInfo) -> ! { 4 | println!("{}", info); 5 | crate::syscall::exit(-1); 6 | } 7 | 8 | #[no_mangle] 9 | pub unsafe extern "C" fn _start() -> ! { 10 | extern "C" { 11 | fn main(argc: isize, argv: *const *const u8) -> isize; 12 | } 13 | // TODO: setup argc and argv 14 | let exit_code = main(0, 0 as *const *const u8); 15 | crate::syscall::exit(exit_code); 16 | } 17 | 18 | #[cfg(not(test))] 19 | #[lang = "start"] 20 | extern "C" fn lang_start( 21 | main: fn() -> T, 22 | _argc: isize, 23 | _argv: *const *const u8, 24 | ) -> isize { 25 | let exit_code = main().report(); 26 | exit_code as isize 27 | } 28 | -------------------------------------------------------------------------------- /xv7-usyscall/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xv7-usyscall" 3 | version = "0.1.0" 4 | authors = ["imtsuki "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /xv7-usyscall/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.xbuild] 2 | command = "cargo" 3 | args = ["xbuild", "--target", "../xv7-kernel/src/arch/x86_64.json"] 4 | 5 | [tasks.xbuild-aarch64] 6 | command = "cargo" 7 | args = ["xbuild", "--target", "../xv7-kernel/src/arch/aarch64.json"] 8 | -------------------------------------------------------------------------------- /xv7-usyscall/src/arch.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86_64")] 2 | pub mod x86_64; 3 | 4 | #[cfg(target_arch = "x86_64")] 5 | pub use self::x86_64::*; 6 | -------------------------------------------------------------------------------- /xv7-usyscall/src/arch/x86_64.rs: -------------------------------------------------------------------------------- 1 | use crate::{Error, Result}; 2 | 3 | pub unsafe fn syscall0(mut a: usize) -> Result { 4 | llvm_asm!( 5 | "syscall" 6 | : "={rax}"(a) 7 | : "{rax}"(a) 8 | : "rcx", "r11", "memory" 9 | : "volatile" 10 | ); 11 | Error::demux(a) 12 | } 13 | 14 | pub unsafe fn syscall1(mut a: usize, b: usize) -> Result { 15 | llvm_asm!( 16 | "syscall" 17 | : "={rax}"(a) 18 | : "{rax}"(a), "{rdi}"(b) 19 | : "rcx", "r11", "memory" 20 | : "volatile" 21 | ); 22 | Error::demux(a) 23 | } 24 | 25 | pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result { 26 | llvm_asm!( 27 | "syscall" 28 | : "={rax}"(a) 29 | : "{rax}"(a), "{rdi}"(b), "{rsi}"(c) 30 | : "rcx", "r11", "memory" 31 | : "volatile" 32 | ); 33 | Error::demux(a) 34 | } 35 | 36 | pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result { 37 | llvm_asm!( 38 | "syscall" 39 | : "={rax}"(a) 40 | : "{rax}"(a), "{rdi}"(b), "{rsi}"(c), "{rdx}"(d) 41 | : "rcx", "r11", "memory" 42 | : "volatile" 43 | ); 44 | Error::demux(a) 45 | } 46 | 47 | pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result { 48 | llvm_asm!( 49 | "syscall" 50 | : "={rax}"(a) 51 | : "{rax}"(a), "{rdi}"(b), "{rsi}"(c), "{rdx}"(d), "{r10}"(e) 52 | : "rcx", "r11", "memory" 53 | : "volatile" 54 | ); 55 | Error::demux(a) 56 | } 57 | 58 | pub unsafe fn syscall5( 59 | mut a: usize, 60 | b: usize, 61 | c: usize, 62 | d: usize, 63 | e: usize, 64 | f: usize, 65 | ) -> Result { 66 | llvm_asm!( 67 | "syscall" 68 | : "={rax}"(a) 69 | : "{rax}"(a), "{rdi}"(b), "{rsi}"(c), "{rdx}"(d), "{r10}"(e), "{r8}"(f) 70 | : "rcx", "r11", "memory" 71 | : "volatile" 72 | ); 73 | Error::demux(a) 74 | } 75 | -------------------------------------------------------------------------------- /xv7-usyscall/src/error.rs: -------------------------------------------------------------------------------- 1 | // Code borrowed from redox-os 2 | use core::fmt; 3 | 4 | #[derive(Eq, PartialEq)] 5 | pub struct Error { 6 | pub errno: i32, 7 | } 8 | 9 | pub type Result = core::result::Result; 10 | 11 | impl Error { 12 | pub fn new(errno: i32) -> Error { 13 | Error { errno } 14 | } 15 | 16 | pub fn mux(result: Result) -> usize { 17 | match result { 18 | Ok(value) => value, 19 | Err(error) => -error.errno as usize, 20 | } 21 | } 22 | 23 | pub fn demux(value: usize) -> Result { 24 | let errno = -(value as i32); 25 | if errno >= 1 && errno < STR_ERROR.len() as i32 { 26 | Err(Error::new(errno)) 27 | } else { 28 | Ok(value) 29 | } 30 | } 31 | 32 | pub fn text(&self) -> &'static str { 33 | STR_ERROR 34 | .get(self.errno as usize) 35 | .map(|&x| x) 36 | .unwrap_or("Unknown Error") 37 | } 38 | } 39 | 40 | impl fmt::Debug for Error { 41 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42 | f.write_str(self.text()) 43 | } 44 | } 45 | 46 | impl fmt::Display for Error { 47 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 48 | f.write_str(self.text()) 49 | } 50 | } 51 | 52 | pub const EPERM: i32 = 1; /* Operation not permitted */ 53 | pub const ENOENT: i32 = 2; /* No such file or directory */ 54 | pub const ESRCH: i32 = 3; /* No such process */ 55 | pub const EINTR: i32 = 4; /* Interrupted system call */ 56 | pub const EIO: i32 = 5; /* I/O error */ 57 | pub const ENXIO: i32 = 6; /* No such device or address */ 58 | pub const E2BIG: i32 = 7; /* Argument list too long */ 59 | pub const ENOEXEC: i32 = 8; /* Exec format error */ 60 | pub const EBADF: i32 = 9; /* Bad file number */ 61 | pub const ECHILD: i32 = 10; /* No child processes */ 62 | pub const EAGAIN: i32 = 11; /* Try again */ 63 | pub const ENOMEM: i32 = 12; /* Out of memory */ 64 | pub const EACCES: i32 = 13; /* Permission denied */ 65 | pub const EFAULT: i32 = 14; /* Bad address */ 66 | pub const ENOTBLK: i32 = 15; /* Block device required */ 67 | pub const EBUSY: i32 = 16; /* Device or resource busy */ 68 | pub const EEXIST: i32 = 17; /* File exists */ 69 | pub const EXDEV: i32 = 18; /* Cross-device link */ 70 | pub const ENODEV: i32 = 19; /* No such device */ 71 | pub const ENOTDIR: i32 = 20; /* Not a directory */ 72 | pub const EISDIR: i32 = 21; /* Is a directory */ 73 | pub const EINVAL: i32 = 22; /* Invalid argument */ 74 | pub const ENFILE: i32 = 23; /* File table overflow */ 75 | pub const EMFILE: i32 = 24; /* Too many open files */ 76 | pub const ENOTTY: i32 = 25; /* Not a typewriter */ 77 | pub const ETXTBSY: i32 = 26; /* Text file busy */ 78 | pub const EFBIG: i32 = 27; /* File too large */ 79 | pub const ENOSPC: i32 = 28; /* No space left on device */ 80 | pub const ESPIPE: i32 = 29; /* Illegal seek */ 81 | pub const EROFS: i32 = 30; /* Read-only file system */ 82 | pub const EMLINK: i32 = 31; /* Too many links */ 83 | pub const EPIPE: i32 = 32; /* Broken pipe */ 84 | pub const EDOM: i32 = 33; /* Math argument out of domain of func */ 85 | pub const ERANGE: i32 = 34; /* Math result not representable */ 86 | pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */ 87 | pub const ENAMETOOLONG: i32 = 36; /* File name too long */ 88 | pub const ENOLCK: i32 = 37; /* No record locks available */ 89 | pub const ENOSYS: i32 = 38; /* Function not implemented */ 90 | pub const ENOTEMPTY: i32 = 39; /* Directory not empty */ 91 | pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */ 92 | pub const EWOULDBLOCK: i32 = 41; /* Operation would block */ 93 | pub const ENOMSG: i32 = 42; /* No message of desired type */ 94 | pub const EIDRM: i32 = 43; /* Identifier removed */ 95 | pub const ECHRNG: i32 = 44; /* Channel number out of range */ 96 | pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */ 97 | pub const EL3HLT: i32 = 46; /* Level 3 halted */ 98 | pub const EL3RST: i32 = 47; /* Level 3 reset */ 99 | pub const ELNRNG: i32 = 48; /* Link number out of range */ 100 | pub const EUNATCH: i32 = 49; /* Protocol driver not attached */ 101 | pub const ENOCSI: i32 = 50; /* No CSI structure available */ 102 | pub const EL2HLT: i32 = 51; /* Level 2 halted */ 103 | pub const EBADE: i32 = 52; /* Invalid exchange */ 104 | pub const EBADR: i32 = 53; /* Invalid request descriptor */ 105 | pub const EXFULL: i32 = 54; /* Exchange full */ 106 | pub const ENOANO: i32 = 55; /* No anode */ 107 | pub const EBADRQC: i32 = 56; /* Invalid request code */ 108 | pub const EBADSLT: i32 = 57; /* Invalid slot */ 109 | pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */ 110 | pub const EBFONT: i32 = 59; /* Bad font file format */ 111 | pub const ENOSTR: i32 = 60; /* Device not a stream */ 112 | pub const ENODATA: i32 = 61; /* No data available */ 113 | pub const ETIME: i32 = 62; /* Timer expired */ 114 | pub const ENOSR: i32 = 63; /* Out of streams resources */ 115 | pub const ENONET: i32 = 64; /* Machine is not on the network */ 116 | pub const ENOPKG: i32 = 65; /* Package not installed */ 117 | pub const EREMOTE: i32 = 66; /* Object is remote */ 118 | pub const ENOLINK: i32 = 67; /* Link has been severed */ 119 | pub const EADV: i32 = 68; /* Advertise error */ 120 | pub const ESRMNT: i32 = 69; /* Srmount error */ 121 | pub const ECOMM: i32 = 70; /* Communication error on send */ 122 | pub const EPROTO: i32 = 71; /* Protocol error */ 123 | pub const EMULTIHOP: i32 = 72; /* Multihop attempted */ 124 | pub const EDOTDOT: i32 = 73; /* RFS specific error */ 125 | pub const EBADMSG: i32 = 74; /* Not a data message */ 126 | pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */ 127 | pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */ 128 | pub const EBADFD: i32 = 77; /* File descriptor in bad state */ 129 | pub const EREMCHG: i32 = 78; /* Remote address changed */ 130 | pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */ 131 | pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */ 132 | pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */ 133 | pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */ 134 | pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */ 135 | pub const EILSEQ: i32 = 84; /* Illegal byte sequence */ 136 | pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */ 137 | pub const ESTRPIPE: i32 = 86; /* Streams pipe error */ 138 | pub const EUSERS: i32 = 87; /* Too many users */ 139 | pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */ 140 | pub const EDESTADDRREQ: i32 = 89; /* Destination address required */ 141 | pub const EMSGSIZE: i32 = 90; /* Message too long */ 142 | pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */ 143 | pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */ 144 | pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */ 145 | pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */ 146 | pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */ 147 | pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */ 148 | pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */ 149 | pub const EADDRINUSE: i32 = 98; /* Address already in use */ 150 | pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */ 151 | pub const ENETDOWN: i32 = 100; /* Network is down */ 152 | pub const ENETUNREACH: i32 = 101; /* Network is unreachable */ 153 | pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */ 154 | pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */ 155 | pub const ECONNRESET: i32 = 104; /* Connection reset by peer */ 156 | pub const ENOBUFS: i32 = 105; /* No buffer space available */ 157 | pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */ 158 | pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */ 159 | pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */ 160 | pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */ 161 | pub const ETIMEDOUT: i32 = 110; /* Connection timed out */ 162 | pub const ECONNREFUSED: i32 = 111; /* Connection refused */ 163 | pub const EHOSTDOWN: i32 = 112; /* Host is down */ 164 | pub const EHOSTUNREACH: i32 = 113; /* No route to host */ 165 | pub const EALREADY: i32 = 114; /* Operation already in progress */ 166 | pub const EINPROGRESS: i32 = 115; /* Operation now in progress */ 167 | pub const ESTALE: i32 = 116; /* Stale NFS file handle */ 168 | pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */ 169 | pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */ 170 | pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */ 171 | pub const EISNAM: i32 = 120; /* Is a named type file */ 172 | pub const EREMOTEIO: i32 = 121; /* Remote I/O error */ 173 | pub const EDQUOT: i32 = 122; /* Quota exceeded */ 174 | pub const ENOMEDIUM: i32 = 123; /* No medium found */ 175 | pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */ 176 | pub const ECANCELED: i32 = 125; /* Operation Canceled */ 177 | pub const ENOKEY: i32 = 126; /* Required key not available */ 178 | pub const EKEYEXPIRED: i32 = 127; /* Key has expired */ 179 | pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */ 180 | pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */ 181 | pub const EOWNERDEAD: i32 = 130; /* Owner died */ 182 | pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */ 183 | 184 | pub static STR_ERROR: [&'static str; 132] = [ 185 | "Success", 186 | "Operation not permitted", 187 | "No such file or directory", 188 | "No such process", 189 | "Interrupted system call", 190 | "I/O error", 191 | "No such device or address", 192 | "Argument list too long", 193 | "Exec format error", 194 | "Bad file number", 195 | "No child processes", 196 | "Try again", 197 | "Out of memory", 198 | "Permission denied", 199 | "Bad address", 200 | "Block device required", 201 | "Device or resource busy", 202 | "File exists", 203 | "Cross-device link", 204 | "No such device", 205 | "Not a directory", 206 | "Is a directory", 207 | "Invalid argument", 208 | "File table overflow", 209 | "Too many open files", 210 | "Not a typewriter", 211 | "Text file busy", 212 | "File too large", 213 | "No space left on device", 214 | "Illegal seek", 215 | "Read-only file system", 216 | "Too many links", 217 | "Broken pipe", 218 | "Math argument out of domain of func", 219 | "Math result not representable", 220 | "Resource deadlock would occur", 221 | "File name too long", 222 | "No record locks available", 223 | "Function not implemented", 224 | "Directory not empty", 225 | "Too many symbolic links encountered", 226 | "Operation would block", 227 | "No message of desired type", 228 | "Identifier removed", 229 | "Channel number out of range", 230 | "Level 2 not synchronized", 231 | "Level 3 halted", 232 | "Level 3 reset", 233 | "Link number out of range", 234 | "Protocol driver not attached", 235 | "No CSI structure available", 236 | "Level 2 halted", 237 | "Invalid exchange", 238 | "Invalid request descriptor", 239 | "Exchange full", 240 | "No anode", 241 | "Invalid request code", 242 | "Invalid slot", 243 | "Resource deadlock would occur", 244 | "Bad font file format", 245 | "Device not a stream", 246 | "No data available", 247 | "Timer expired", 248 | "Out of streams resources", 249 | "Machine is not on the network", 250 | "Package not installed", 251 | "Object is remote", 252 | "Link has been severed", 253 | "Advertise error", 254 | "Srmount error", 255 | "Communication error on send", 256 | "Protocol error", 257 | "Multihop attempted", 258 | "RFS specific error", 259 | "Not a data message", 260 | "Value too large for defined data type", 261 | "Name not unique on network", 262 | "File descriptor in bad state", 263 | "Remote address changed", 264 | "Can not access a needed shared library", 265 | "Accessing a corrupted shared library", 266 | ".lib section in a.out corrupted", 267 | "Attempting to link in too many shared libraries", 268 | "Cannot exec a shared library directly", 269 | "Illegal byte sequence", 270 | "Interrupted system call should be restarted", 271 | "Streams pipe error", 272 | "Too many users", 273 | "Socket operation on non-socket", 274 | "Destination address required", 275 | "Message too long", 276 | "Protocol wrong type for socket", 277 | "Protocol not available", 278 | "Protocol not supported", 279 | "Socket type not supported", 280 | "Operation not supported on transport endpoint", 281 | "Protocol family not supported", 282 | "Address family not supported by protocol", 283 | "Address already in use", 284 | "Cannot assign requested address", 285 | "Network is down", 286 | "Network is unreachable", 287 | "Network dropped connection because of reset", 288 | "Software caused connection abort", 289 | "Connection reset by peer", 290 | "No buffer space available", 291 | "Transport endpoint is already connected", 292 | "Transport endpoint is not connected", 293 | "Cannot send after transport endpoint shutdown", 294 | "Too many references: cannot splice", 295 | "Connection timed out", 296 | "Connection refused", 297 | "Host is down", 298 | "No route to host", 299 | "Operation already in progress", 300 | "Operation now in progress", 301 | "Stale NFS file handle", 302 | "Structure needs cleaning", 303 | "Not a XENIX named type file", 304 | "No XENIX semaphores available", 305 | "Is a named type file", 306 | "Remote I/O error", 307 | "Quota exceeded", 308 | "No medium found", 309 | "Wrong medium type", 310 | "Operation Canceled", 311 | "Required key not available", 312 | "Key has expired", 313 | "Key has been revoked", 314 | "Key was rejected by service", 315 | "Owner died", 316 | "State not recoverable", 317 | ]; 318 | -------------------------------------------------------------------------------- /xv7-usyscall/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(llvm_asm)] 3 | 4 | pub mod arch; 5 | pub mod error; 6 | pub mod number; 7 | pub mod syscall; 8 | 9 | pub use error::Error; 10 | pub use error::Result; 11 | 12 | pub use syscall::*; 13 | -------------------------------------------------------------------------------- /xv7-usyscall/src/number.rs: -------------------------------------------------------------------------------- 1 | pub const SYS_EXIT: usize = 1; 2 | pub const SYS_WRITE: usize = 2; 3 | pub const SYS_READ: usize = 3; 4 | pub const SYS_EXEC: usize = 4; 5 | pub const SYS_OPEN: usize = 5; 6 | pub const SYS_CLOSE: usize = 6; 7 | pub const SYS_FORK: usize = 7; 8 | pub const SYS_GETPID: usize = 8; 9 | pub const SYS_YIELD: usize = 9; 10 | pub const SYS_MKNOD: usize = 10; 11 | -------------------------------------------------------------------------------- /xv7-usyscall/src/syscall.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::*; 2 | use crate::number::*; 3 | use crate::Result; 4 | 5 | pub fn exit(code: isize) -> ! { 6 | unsafe { 7 | syscall1(SYS_EXIT, code as usize).unwrap(); 8 | } 9 | unreachable!() 10 | } 11 | 12 | pub fn write(fd: usize, buf: &[u8]) -> Result { 13 | unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) } 14 | } 15 | 16 | pub fn read(fd: usize, buf: &mut [u8]) -> Result { 17 | unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) } 18 | } 19 | 20 | pub fn open(path: &str) -> Result { 21 | unsafe { syscall2(SYS_OPEN, path.as_ptr() as usize, path.len()) } 22 | } 23 | 24 | pub fn close(fd: usize) -> Result { 25 | unsafe { syscall1(SYS_CLOSE, fd) } 26 | } 27 | 28 | pub fn exec(fd: usize, args: &[&str], envs: &[&str]) -> Result { 29 | unsafe { 30 | syscall5( 31 | SYS_EXEC, 32 | fd, 33 | args.as_ptr() as usize, 34 | args.len(), 35 | envs.as_ptr() as usize, 36 | envs.len(), 37 | ) 38 | } 39 | } 40 | 41 | pub fn fork() -> Result { 42 | unsafe { syscall0(SYS_FORK) } 43 | } 44 | 45 | pub fn getpid() -> Result { 46 | unsafe { syscall0(SYS_GETPID) } 47 | } 48 | 49 | pub fn r#yield() -> Result { 50 | unsafe { syscall0(SYS_YIELD) } 51 | } 52 | 53 | pub fn mknod(path: &str, dev: usize) -> Result { 54 | unsafe { syscall3(SYS_MKNOD, path.as_ptr() as usize, path.len(), dev) } 55 | } 56 | --------------------------------------------------------------------------------