├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── crates ├── cmos │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── e9 │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── hpet │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── i8042 │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── keyboard.rs │ │ ├── lib.rs │ │ └── mouse.rs └── snalloc │ ├── Cargo.toml │ └── src │ └── lib.rs ├── kernel ├── Cargo.toml ├── assets │ ├── logo.png │ └── logo_text.png ├── build.rs ├── config.toml ├── limine.cfg ├── linker.ld └── src │ ├── allocator.rs │ ├── arch │ ├── common.rs │ ├── mod.rs │ └── x86_64 │ │ ├── acpi.rs │ │ ├── gdt.rs │ │ ├── hpet.rs │ │ ├── interrupts.rs │ │ ├── local.rs │ │ ├── memory.rs │ │ ├── mod.rs │ │ ├── pci.rs │ │ ├── pit.rs │ │ └── time.rs │ ├── debug.rs │ ├── drivers │ ├── dma.rs │ ├── e1000.rs │ ├── i8042.rs │ ├── keyboard.rs │ ├── mod.rs │ ├── nvme.rs │ └── virtio │ │ ├── mod.rs │ │ ├── net.rs │ │ └── sock.rs │ ├── framebuffer.rs │ ├── local.rs │ ├── main.rs │ ├── net │ └── mod.rs │ ├── panic.rs │ ├── shell.rs │ ├── stack_allocator.rs │ └── task │ ├── executor.rs │ ├── mod.rs │ └── timer.rs ├── nvm.img └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | /out 3 | **/*.rgba 4 | **/*.rs.bk 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "kernel", 5 | "crates/cmos", 6 | "crates/i8042", 7 | "crates/snalloc", 8 | "crates/e9", 9 | "crates/hpet", 10 | ] 11 | exclude = ["programs"] 12 | 13 | [workspace.dependencies] 14 | mycelium-bitfield = { git = "https://github.com/hawkw/mycelium.git", rev = "82284c465ee6aa0b1468854def75ced2b09e4fc7" } 15 | tracing = { git = "https://github.com/tokio-rs/tracing", default-features = false } 16 | tracing-subscriber = { git = "https://github.com/tokio-rs/tracing", default-features = false } 17 | tracing-core = { git = "https://github.com/tokio-rs/tracing", default-features = false } 18 | log = { version = "0.4", default-features = false } 19 | 20 | [profile.release] 21 | debug = 1 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | 375 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD ?= debug 2 | TARGET ?= x86_64-unknown-none 3 | CARGO_PROFILE ?= $(BUILD) 4 | 5 | ifeq ($(BUILD), debug) 6 | CARGO_PROFILE = dev 7 | endif 8 | 9 | TARGET_ARCH = $(shell cut -d- -f1 <<< $(TARGET)) 10 | KERNEL = target/$(TARGET)/$(BUILD)/snek_kernel 11 | ISO = out/$(TARGET)/$(BUILD)/snek_os.iso 12 | OVMF = out/ovmf/OVMF.fd 13 | LIMINE_BIN = out/limine/limine 14 | LIMINE ?= out/limine 15 | LIMINE_CFG = kernel/limine.cfg 16 | 17 | .PHONY: all 18 | all: $(ISO) 19 | 20 | $(OVMF): 21 | mkdir -p out/ovmf 22 | curl -Lo out/ovmf/OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd 23 | curl -Lo out/ovmf/OVMF_CODE.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_CODE.fd 24 | curl -Lo out/ovmf/OVMF_VARS.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_VARS.fd 25 | 26 | $(LIMINE_BIN): 27 | mkdir -p out 28 | cd out && git clone https://github.com/limine-bootloader/limine.git --branch=v5.x-branch-binary --depth=1 29 | cd out/limine && $(MAKE) 30 | 31 | $(KERNEL): FORCE 32 | cargo build --profile $(CARGO_PROFILE) --package snek_kernel --target $(TARGET) --config kernel/config.toml 33 | 34 | $(ISO): $(KERNEL) $(LIMINE_BIN) $(LIMINE_CFG) 35 | rm -rf out/iso_root 36 | mkdir -p out/iso_root 37 | mkdir -p out/$(TARGET)/$(BUILD) 38 | cp $(KERNEL) out/iso_root/kernel.elf 39 | cp $(LIMINE_CFG) out/iso_root/ 40 | cp -v $(LIMINE)/limine-bios.sys $(LIMINE)/limine-bios-cd.bin $(LIMINE)/limine-uefi-cd.bin out/iso_root/ 41 | mkdir -p out/iso_root/EFI/BOOT 42 | cp -v $(LIMINE)/BOOTX64.EFI out/iso_root/EFI/BOOT/ 43 | xorriso -as mkisofs -b limine-bios-cd.bin \ 44 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 45 | --efi-boot limine-uefi-cd.bin \ 46 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 47 | out/iso_root -o $(ISO) 48 | $(LIMINE_BIN) bios-install $(ISO) 49 | 50 | FORCE: ; 51 | 52 | .PHONY: clean format clippy run kernel iso 53 | 54 | kernel: $(KERNEL) 55 | 56 | iso: $(ISO) 57 | 58 | clean: 59 | cargo clean 60 | rm -rf out 61 | 62 | fmt: 63 | cargo fmt 64 | 65 | lint: 66 | cargo clippy --package snek_kernel --target $(TARGET) --config kernel/config.toml 67 | 68 | run: $(ISO) $(OVMF) 69 | "qemu-system-$(TARGET_ARCH)" \ 70 | -machine q35 \ 71 | -accel kvm \ 72 | -cpu host \ 73 | -debugcon /dev/stdout \ 74 | -smp 4 \ 75 | -m 8G \ 76 | -rtc base=utc,clock=host \ 77 | -drive file=nvm.img,if=none,id=nvm,format=raw \ 78 | -device nvme,serial=deadbeef,drive=nvm \ 79 | -netdev user,id=u1,ipv6=on,ipv4=on -device virtio-net,netdev=u1 \ 80 | -device vhost-vsock-pci,guest-cid=3 \ 81 | -drive format=raw,if=pflash,readonly=on,file=out/ovmf/OVMF_CODE.fd \ 82 | -drive format=raw,if=pflash,file=out/ovmf/OVMF_VARS.fd \ 83 | -drive file=$(ISO),format=raw 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # snek_os 2 | 3 | Experimental OS. Aiming to have: 4 | 5 | - [x] Lightweight cooperative scheduler 6 | - [ ] WebAssembly userspace 7 | - [ ] Reasonable collection of networking drivers to run on real systems 8 | 9 | ## Building 10 | 11 | ```shell 12 | $ make 13 | ``` 14 | 15 | ## Running in QEMU 16 | 17 | ```shell 18 | $ make run 19 | ``` 20 | 21 | ## Running elsewhere 22 | 23 | Grab `out/x86_64-unknown-none/debug/snek_os.iso` and use as needed. 24 | 25 | ![Screenshot of the OS after booting, displaying a logo and debug logs](./screenshot.png) 26 | -------------------------------------------------------------------------------- /crates/cmos/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cmos" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | x86_64 = "0.14" 8 | chrono = { version = "0.4", default-features = false } 9 | -------------------------------------------------------------------------------- /crates/cmos/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(clippy::new_without_default)] 3 | 4 | use chrono::{NaiveDate, NaiveDateTime}; 5 | use x86_64::instructions::port::Port; 6 | 7 | #[derive(Debug)] 8 | pub struct CMOS { 9 | address_port: Port, 10 | data_port: Port, 11 | } 12 | 13 | impl CMOS { 14 | pub fn new() -> Self { 15 | Self { 16 | address_port: Port::::new(0x70), 17 | data_port: Port::::new(0x71), 18 | } 19 | } 20 | 21 | fn read(&mut self, reg: u8) -> u8 { 22 | unsafe { 23 | self.address_port.write(reg); 24 | self.data_port.read() 25 | } 26 | } 27 | 28 | pub fn read_rtc(&mut self, century_reg: u8) -> NaiveDateTime { 29 | let mut rtc_time = RTCDateTime { 30 | second: 0, 31 | minute: 0, 32 | hour: 0, 33 | day: 0, 34 | month: 0, 35 | year: 0, 36 | }; 37 | let mut last_rtc_time = rtc_time; 38 | 39 | loop { 40 | loop { 41 | rtc_time.second = self.read(0x00); 42 | rtc_time.minute = self.read(0x02); 43 | rtc_time.hour = self.read(0x04); 44 | rtc_time.day = self.read(0x07); 45 | rtc_time.month = self.read(0x08); 46 | rtc_time.year = self.read(0x09) as usize; 47 | if (self.read(0x0A) & 0x80) == 0 { 48 | break; 49 | } 50 | } 51 | 52 | if rtc_time == last_rtc_time { 53 | break; 54 | } 55 | 56 | last_rtc_time = rtc_time; 57 | } 58 | 59 | let register_b = self.read(0x0B); 60 | 61 | let mut century = self.read(century_reg); 62 | 63 | if (register_b & 0x04) == 0 { 64 | rtc_time.second = (rtc_time.second & 0x0F) + ((rtc_time.second / 16) * 10); 65 | rtc_time.minute = (rtc_time.minute & 0x0F) + ((rtc_time.minute / 16) * 10); 66 | rtc_time.hour = ((rtc_time.hour & 0x0F) + (((rtc_time.hour & 0x70) / 16) * 10)) 67 | | (rtc_time.hour & 0x80); 68 | rtc_time.day = (rtc_time.day & 0x0F) + ((rtc_time.day / 16) * 10); 69 | rtc_time.month = (rtc_time.month & 0x0F) + ((rtc_time.month / 16) * 10); 70 | rtc_time.year = (rtc_time.year & 0x0F) + ((rtc_time.year / 16) * 10); 71 | century = (century & 0x0F) + ((century / 16) * 10); 72 | } 73 | 74 | if ((register_b & 0x02) == 0) && ((rtc_time.hour & 0x80) != 0) { 75 | rtc_time.hour = ((rtc_time.hour & 0x7F) + 12) % 24; 76 | } 77 | 78 | rtc_time.year += century as usize * 100; 79 | 80 | NaiveDate::from_ymd_opt(rtc_time.year as _, rtc_time.month as _, rtc_time.day as _) 81 | .unwrap() 82 | .and_hms_opt( 83 | rtc_time.hour as _, 84 | rtc_time.minute as _, 85 | rtc_time.second as _, 86 | ) 87 | .unwrap() 88 | } 89 | } 90 | 91 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] 92 | struct RTCDateTime { 93 | pub year: usize, 94 | pub month: u8, 95 | pub day: u8, 96 | pub hour: u8, 97 | pub minute: u8, 98 | pub second: u8, 99 | } 100 | -------------------------------------------------------------------------------- /crates/e9/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "e9" 3 | version = "0.1.0" 4 | edition = "2021" 5 | -------------------------------------------------------------------------------- /crates/e9/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub fn detect() -> bool { 4 | let mut b: u8; 5 | unsafe { 6 | core::arch::asm!(r#" 7 | in al, 0xe9 8 | "#, out("al") b); 9 | } 10 | b == 0xe9 11 | } 12 | 13 | pub fn write_byte(b: u8) { 14 | if b == b'\n' { 15 | write_byte(b'\r'); 16 | } 17 | unsafe { 18 | core::arch::asm!(r#" 19 | out 0xe9, al 20 | "#, in("al") b); 21 | } 22 | } 23 | 24 | pub fn print(args: core::fmt::Arguments) { 25 | if let Some(s) = args.as_str() { 26 | for b in s.bytes() { 27 | write_byte(b); 28 | } 29 | } else { 30 | struct Writer; 31 | impl core::fmt::Write for Writer { 32 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 33 | for b in s.bytes() { 34 | write_byte(b); 35 | } 36 | Ok(()) 37 | } 38 | } 39 | core::fmt::write(&mut Writer, args).unwrap(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crates/hpet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hpet" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | volatile = "0.5" 8 | mycelium-bitfield = { workspace = true } 9 | -------------------------------------------------------------------------------- /crates/hpet/README.md: -------------------------------------------------------------------------------- 1 | # HPET 2 | 3 | The HPET (High-Precision Event Timer) is a hardware module intended to replace 4 | the legacy 8254 Programmable Interval Timer and Real Time Clock Periodic 5 | interrupt generation functions that are often used on PCs. 6 | 7 | A HEPT contains a monotonically increasing *counter* and between 3 and 32 8 | *timers* which can be used to generate interrupts. 9 | 10 | Timers 0 and 1 may be connected to ISRs 0 and 8 (IRQs 2 and 8) respectively, 11 | which allows legacy software to supplement use of the 8254 and RTC hardware. 12 | 13 | A specification is available online, though note that it contains a number of 14 | miscellaneous errata: 15 | 16 | https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf 17 | 18 | To use a HPET, you will need to load the `"HPET"` section from the ACPI 19 | tables, which contains useful information like the base address of the HPET 20 | in memory and how long each tick takes (in femtoseconds). 21 | -------------------------------------------------------------------------------- /crates/hpet/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![doc=include_str!("../README.md")] 3 | 4 | use core::ptr::NonNull; 5 | use volatile::{access::ReadOnly, VolatilePtr}; 6 | 7 | mycelium_bitfield::bitfield! { 8 | pub struct CapabilitiesAndID { 9 | /// Function revision 10 | const REV_ID: u8; 11 | /// Number of timers 12 | const NUM_TIM_CAP = 4; 13 | /// false = 32 bits, true = 64 bits 14 | const COUNT_SIZE_CAP: bool; 15 | const _RESERVED = 1; 16 | /// Indicates that irq 0 is supported 17 | const LEG_ROUTE_CAP: bool; 18 | /// Same as if this was a PCI function 19 | const VENDOR_ID: u16; 20 | /// Period of counter increments in femtoseconds(!) 21 | const COUNTER_CLK_PERIOD: u32; 22 | } 23 | } 24 | 25 | mycelium_bitfield::bitfield! { 26 | pub struct Configuration { 27 | /// false - halt counter and disable interrupts 28 | /// true - allow main counter to run and allow interrupts 29 | const ENABLE_CNF: bool; 30 | /// Enable legacy interrupt (irq 0) 31 | const LEG_ROUTE_CNF: bool; 32 | const _RESERVED = 62; 33 | } 34 | } 35 | 36 | mycelium_bitfield::bitfield! { 37 | pub struct InterruptStatus { 38 | /// Tn_INT_STS 0-31 39 | const T_INT_STS: u32; 40 | const _RESERVED = 32; 41 | } 42 | } 43 | 44 | mycelium_bitfield::bitfield! { 45 | pub struct ConfigurationAndCapabilities { 46 | const _RESERVED0 = 1; 47 | const TN_INT_TYPE_CNF: bool; 48 | const TN_INT_ENB_CNF: bool; 49 | const TN_TYPE_CNF: bool; 50 | const TN_PER_INT_CAP: bool; 51 | const TN_SIZE_CAP: bool; 52 | const TN_VAL_SET_CNF: bool; 53 | const _RESERVED1 = 1; 54 | const TN_32MODE_CNF: bool; 55 | const TN_INT_ROUTE_CNF = 4; 56 | const TN_FSB_EN_CNF: bool; 57 | const TN_FSB_INT_DEL_CAP: bool; 58 | const _RESERVED3 = 16; 59 | const TN_INT_ROUTE_CAP: u32; 60 | } 61 | } 62 | 63 | /// Represents the HPET in memory. 64 | #[derive(Debug)] 65 | #[repr(C)] 66 | pub struct Hpet { 67 | pub capabilities_and_id: CapabilitiesAndID, 68 | pub padding0: u64, 69 | pub configuration: Configuration, 70 | pub padding1: u64, 71 | pub general_interrupt_status: InterruptStatus, 72 | pub padding2: [u64; 25], 73 | pub counter_value: u64, 74 | pub padding3: u64, 75 | pub timers: [HpetTimer; 32], 76 | } 77 | 78 | /// Represents an HPET timer in memory. 79 | #[derive(Debug)] 80 | #[repr(C)] 81 | pub struct HpetTimer { 82 | pub configuration_and_capabilities: ConfigurationAndCapabilities, 83 | pub comparator_value: u64, 84 | pub fsb_interrupt: u64, 85 | } 86 | 87 | impl Hpet { 88 | fn capabilities_and_id(&self) -> VolatilePtr { 89 | unsafe { VolatilePtr::new_read_only(NonNull::from(&self.capabilities_and_id)) } 90 | } 91 | 92 | fn configuration(&self) -> VolatilePtr { 93 | unsafe { VolatilePtr::new(NonNull::from(&self.configuration)) } 94 | } 95 | 96 | fn general_interrupt_status(&self) -> VolatilePtr { 97 | unsafe { VolatilePtr::new(NonNull::from(&self.general_interrupt_status)) } 98 | } 99 | 100 | /// Get the counter period in femtoseconds. 101 | pub fn counter_period(&self) -> u32 { 102 | self.capabilities_and_id() 103 | .read() 104 | .get(CapabilitiesAndID::COUNTER_CLK_PERIOD) 105 | } 106 | 107 | /// Get the current counter value. 108 | pub fn counter_value(&self) -> u64 { 109 | unsafe { (&self.counter_value as *const u64).read_volatile() } 110 | } 111 | 112 | /// If using level-triggered interrupts, get whether a timer is asserting. 113 | pub fn interrupt_status(&self, timer: u8) -> bool { 114 | let timer = timer as u32; 115 | self.general_interrupt_status() 116 | .read() 117 | .get(InterruptStatus::T_INT_STS) 118 | & timer 119 | == timer 120 | } 121 | 122 | /// If using level-triggered interrupts, clear an asserting timer. 123 | pub fn clear_interrupt_status(&mut self, timer: u8) { 124 | self.general_interrupt_status().update(|mut s| { 125 | let mut sts = s.get(InterruptStatus::T_INT_STS); 126 | sts |= 1 << timer; 127 | s.set(InterruptStatus::T_INT_STS, sts); 128 | s 129 | }) 130 | } 131 | 132 | /// Enable or disable the counter. 133 | pub fn set_counter_enable(&mut self, enable: bool) { 134 | self.configuration().update(|mut c| { 135 | c.set(Configuration::ENABLE_CNF, enable); 136 | c 137 | }) 138 | } 139 | 140 | /// Get the available timers. 141 | pub fn timers(&self) -> &[HpetTimer] { 142 | let last_timer_index = self 143 | .capabilities_and_id() 144 | .read() 145 | .get(CapabilitiesAndID::NUM_TIM_CAP) as usize; 146 | &self.timers[..=last_timer_index] 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /crates/i8042/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "i8042" 3 | version = "0.1.0" 4 | authors = ["snek"] 5 | edition = "2021" 6 | description = "Driver for the 8042 PS/2 Controller" 7 | license-file = "../../LICENSE" 8 | repository = "https://github.com/devsnek/snek_os" 9 | 10 | [dependencies] 11 | pc-keyboard = "0.8" 12 | -------------------------------------------------------------------------------- /crates/i8042/README.md: -------------------------------------------------------------------------------- 1 | # i8042 2 | 3 | A driver for the Intel 8042 PS/2 Controller. 4 | -------------------------------------------------------------------------------- /crates/i8042/src/keyboard.rs: -------------------------------------------------------------------------------- 1 | use pc_keyboard::{ 2 | layouts::Us104Key, DecodedKey, HandleControl, Keyboard as PcKeyboard, ScancodeSet1, 3 | ScancodeSet2, 4 | }; 5 | 6 | #[derive(Debug, PartialEq, Eq)] 7 | pub(crate) enum KeyboardKind { 8 | MF2, 9 | Short, 10 | N97, 11 | K122, 12 | MF2Emul, 13 | JapG, 14 | JapP, 15 | JapA, 16 | Sun, 17 | } 18 | 19 | enum SomeKeyboard { 20 | Set1(PcKeyboard), 21 | Set2(PcKeyboard), 22 | } 23 | 24 | impl SomeKeyboard { 25 | fn handle_data(&mut self, data: u8) -> Option { 26 | macro_rules! get_kbd { 27 | ($k:ident, $e:expr) => {{ 28 | match self { 29 | SomeKeyboard::Set1(k) => { 30 | let $k = k; 31 | $e 32 | } 33 | SomeKeyboard::Set2(k) => { 34 | let $k = k; 35 | $e 36 | } 37 | } 38 | }}; 39 | } 40 | 41 | if let Ok(Some(event)) = get_kbd!(k, k.add_byte(data)) { 42 | get_kbd!(k, k.process_keyevent(event)) 43 | } else { 44 | None 45 | } 46 | } 47 | } 48 | 49 | impl core::fmt::Debug for SomeKeyboard { 50 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 51 | write!(f, "SomeKeyboard")?; 52 | Ok(()) 53 | } 54 | } 55 | 56 | #[derive(Debug)] 57 | enum State { 58 | Disabled, 59 | Init(Init), 60 | Normal(SomeKeyboard), 61 | } 62 | 63 | #[derive(Debug)] 64 | enum Init { 65 | ReqScancodeSetAck, 66 | ReqScancodeSetRsp, 67 | EnableAck(u8), 68 | } 69 | 70 | #[derive(Debug)] 71 | pub(crate) struct Keyboard { 72 | state: State, 73 | } 74 | 75 | impl Keyboard { 76 | pub(crate) fn new(kind: KeyboardKind) -> (Option, Self) { 77 | if kind == KeyboardKind::MF2 { 78 | ( 79 | Some(0xF0), 80 | Self { 81 | state: State::Init(Init::ReqScancodeSetAck), 82 | }, 83 | ) 84 | } else { 85 | ( 86 | None, 87 | Self { 88 | state: State::Disabled, 89 | }, 90 | ) 91 | } 92 | } 93 | 94 | pub(crate) fn handle_data(&mut self, data: u8) -> (Option, Option) { 95 | match self.state { 96 | State::Disabled => (None, None), 97 | State::Init(ref mut init) => match init { 98 | Init::ReqScancodeSetAck => { 99 | self.state = State::Init(Init::ReqScancodeSetRsp); 100 | (Some(0x00), None) 101 | } 102 | Init::ReqScancodeSetRsp => match data { 103 | #[allow(clippy::manual_range_patterns)] 104 | 1 | 2 | 3 => { 105 | self.state = State::Init(Init::EnableAck(data)); 106 | (Some(0xF4), None) 107 | } 108 | 0xFA => (None, None), 109 | _ => { 110 | self.state = State::Disabled; 111 | (None, None) 112 | } 113 | }, 114 | Init::EnableAck(set) => match data { 115 | 0xFA => { 116 | let keyboard = match set { 117 | 1 => SomeKeyboard::Set1(PcKeyboard::new( 118 | ScancodeSet1::new(), 119 | Us104Key, 120 | HandleControl::MapLettersToUnicode, 121 | )), 122 | 2 => SomeKeyboard::Set2(PcKeyboard::new( 123 | ScancodeSet2::new(), 124 | Us104Key, 125 | HandleControl::MapLettersToUnicode, 126 | )), 127 | 3 => { 128 | self.state = State::Disabled; 129 | return (None, None); 130 | } 131 | _ => { 132 | self.state = State::Disabled; 133 | return (None, None); 134 | } 135 | }; 136 | 137 | self.state = State::Normal(keyboard); 138 | (None, None) 139 | } 140 | _ => { 141 | self.state = State::Disabled; 142 | (None, None) 143 | } 144 | }, 145 | }, 146 | State::Normal(ref mut keyboard) => match data { 147 | 0x00 | 0xFF | 0xAA | 0xFC | 0xFD | 0xEE | 0xFA | 0xFE => (None, None), 148 | _ => { 149 | let key = keyboard.handle_data(data); 150 | (None, key) 151 | } 152 | }, 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /crates/i8042/src/lib.rs: -------------------------------------------------------------------------------- 1 | // This code is published under the terms of the 2-clause BSD licence (see below) 2 | // 3 | // Copyright (c) 2014, John Hodge (thePowersGang) 4 | // All rights reserved. 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // 1. Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #![no_std] 29 | 30 | mod keyboard; 31 | mod mouse; 32 | 33 | use keyboard::{Keyboard, KeyboardKind}; 34 | pub use mouse::MouseState; 35 | use mouse::{Mouse, MouseKind}; 36 | pub use pc_keyboard::{DecodedKey, KeyCode}; 37 | 38 | #[derive(Debug)] 39 | #[non_exhaustive] 40 | pub enum Error { 41 | /// The driver timed out while waiting to send data to or receive data from the controller. 42 | TimedOut, 43 | /// The controller failed the self test. 44 | SelfTestFailed, 45 | /// No available ports were found. 46 | NoPorts, 47 | } 48 | 49 | /// Identifies which IRQ is being processed. 50 | #[derive(Debug, PartialEq, Eq)] 51 | pub enum Irq { 52 | Irq1, 53 | Irq12, 54 | } 55 | 56 | /// This trait provides the actual functionality of communicating with the 8042 controller to the 57 | /// driver instance. 58 | /// 59 | /// One possible implementation would use x86 `inb` and `outb` instructions to 60 | /// communicate on ports `0x60` and `0x64`: 61 | /// ```rust 62 | /// struct MyImpl; 63 | /// 64 | /// impl Impl for MyImpl { 65 | /// fn write_cmd(&mut self, cmd: u8) { 66 | /// unsafe { asm!("outb 064h, al", in(al) cmd) }; 67 | /// } 68 | /// fn read_status(&mut self) -> u8 { 69 | /// let mut status; 70 | /// unsafe { asm!("inb al, 064h", out(al) status) }; 71 | /// status 72 | /// } 73 | /// fn write_data(&mut self, cmd: u8) { 74 | /// unsafe { asm!("outb 060h, al", in(al) cmd) }; 75 | /// } 76 | /// fn read_data(&mut self) -> u8 { 77 | /// let mut status; 78 | /// unsafe { asm!("inb al, 060h", out(al) status) }; 79 | /// status 80 | /// } 81 | /// } 82 | /// ``` 83 | pub trait Impl: core::fmt::Debug { 84 | fn write_cmd(&mut self, cmd: u8); 85 | fn read_status(&mut self) -> u8; 86 | fn write_data(&mut self, data: u8); 87 | fn read_data(&mut self) -> u8; 88 | } 89 | 90 | /// A driver for the Intel 8042 PS/2 Controller. 91 | #[derive(Debug)] 92 | pub struct Driver8042 { 93 | r#impl: T, 94 | port1: Option, 95 | port2: Option, 96 | } 97 | 98 | impl Driver8042 { 99 | /// Create a new instance of the driver. An implementation of `Impl` must be passed in to 100 | /// provide the driver with hooks for communicating with the 8042 controller. 101 | pub const fn new(r#impl: T) -> Self { 102 | Self { 103 | r#impl, 104 | port1: None, 105 | port2: None, 106 | } 107 | } 108 | 109 | /// Initialize the driver. 110 | /// 111 | /// # Safety 112 | /// You must ensure that your system has an 8042 controller. One possible way of doing 113 | /// this is to check the presence via ACPI tables. 114 | pub unsafe fn init(&mut self) -> Result<(), Error> { 115 | // Disable controller 116 | self.write_cmd(0xAD)?; 117 | self.write_cmd(0xA7)?; 118 | self.flush()?; 119 | 120 | // Set config 121 | self.write_cmd(0x20)?; 122 | let mut config = self.read_data()?; 123 | config &= !((1 << 0) | (1 << 1) | (1 << 6)); 124 | let can_have_second_port = config & (1 << 5) != 0; 125 | self.write_cmd(0x60)?; 126 | self.write_data(config)?; 127 | 128 | // Self test 129 | self.write_cmd(0xAA)?; 130 | match self.read_data() { 131 | Ok(0x55) => {} 132 | _ => { 133 | return Err(Error::SelfTestFailed); 134 | } 135 | } 136 | 137 | let has_second_port = if can_have_second_port { 138 | // Enable and disable 2nd port, see if the config changes in response 139 | self.write_cmd(0xA8)?; 140 | self.write_cmd(0x20)?; 141 | let config = self.read_data()?; 142 | self.write_cmd(0xA7)?; 143 | config & (1 << 5) == 0 144 | } else { 145 | false 146 | }; 147 | 148 | self.flush()?; 149 | 150 | let port1_works = { 151 | self.write_cmd(0xAB)?; 152 | self.read_data()? == 0x00 153 | }; 154 | let port2_works = if has_second_port { 155 | self.write_cmd(0xA9)?; 156 | self.read_data()? == 0x00 157 | } else { 158 | false 159 | }; 160 | 161 | if !port1_works && !port2_works { 162 | return Err(Error::NoPorts); 163 | } 164 | 165 | self.write_cmd(0x20)?; 166 | let mut config = self.read_data()?; 167 | if port1_works { 168 | config |= 1 << 0; 169 | } 170 | if port2_works { 171 | config |= 1 << 1; 172 | } 173 | self.write_cmd(0x60)?; 174 | self.write_data(config)?; 175 | 176 | if port1_works { 177 | self.port1 = Some(Port::new()); 178 | self.write_cmd(0xAE)?; 179 | self.write_data(0xFF)?; 180 | } 181 | if port2_works { 182 | self.port2 = Some(Port::new()); 183 | self.write_cmd(0xA8)?; 184 | self.write_cmd(0xD4)?; 185 | self.write_data(0xFF)?; 186 | } 187 | 188 | Ok(()) 189 | } 190 | 191 | /// The 8042 controller is connected to IRQ1 and IRQ12. You should call this function when 192 | /// these are asserted in order to update the driver with new data and receive the resulting 193 | /// processed keyboard and mouse events. 194 | pub fn interrupt(&mut self, irq: Irq) -> Option { 195 | let mask = match irq { 196 | Irq::Irq1 => 0x01, 197 | Irq::Irq12 => 0x20, 198 | }; 199 | if self.r#impl.read_status() & mask == 0 { 200 | return None; 201 | } 202 | let data = self.r#impl.read_data(); 203 | let port = match irq { 204 | Irq::Irq1 => &mut self.port1, 205 | Irq::Irq12 => &mut self.port2, 206 | }; 207 | if let Some(port) = port { 208 | let (data, key) = port.handle_data(data); 209 | if let Some(data) = data { 210 | if irq == Irq::Irq12 { 211 | self.r#impl.write_cmd(0xD4); 212 | } 213 | self.r#impl.write_data(data); 214 | } 215 | return key; 216 | } 217 | None 218 | } 219 | 220 | fn poll_out(&mut self) -> bool { 221 | self.r#impl.read_status() & 2 == 0 222 | } 223 | 224 | fn poll_in(&mut self) -> bool { 225 | self.r#impl.read_status() & 1 != 0 226 | } 227 | 228 | fn wait_out(&mut self) -> Result<(), Error> { 229 | const MAX_SPINS: usize = 1000; 230 | let mut spin_count = 0; 231 | while !self.poll_out() { 232 | spin_count += 1; 233 | if spin_count == MAX_SPINS { 234 | return Err(Error::TimedOut); 235 | } 236 | } 237 | Ok(()) 238 | } 239 | 240 | fn wait_in(&mut self) -> Result<(), Error> { 241 | const MAX_SPINS: usize = 100 * 1000; 242 | let mut spin_count = 0; 243 | while !self.poll_in() { 244 | spin_count += 1; 245 | if spin_count == MAX_SPINS { 246 | return Err(Error::TimedOut); 247 | } 248 | } 249 | Ok(()) 250 | } 251 | 252 | fn write_cmd(&mut self, cmd: u8) -> Result<(), Error> { 253 | self.wait_out()?; 254 | self.r#impl.write_cmd(cmd); 255 | Ok(()) 256 | } 257 | 258 | fn write_data(&mut self, data: u8) -> Result<(), Error> { 259 | self.wait_out()?; 260 | self.r#impl.write_data(data); 261 | Ok(()) 262 | } 263 | 264 | fn read_data(&mut self) -> Result { 265 | self.wait_in()?; 266 | Ok(self.r#impl.read_data()) 267 | } 268 | 269 | fn flush(&mut self) -> Result<(), Error> { 270 | while self.poll_in() { 271 | self.r#impl.read_data(); 272 | } 273 | Ok(()) 274 | } 275 | } 276 | 277 | /// A change in the state of the keyboard or the mouse. 278 | #[derive(Debug)] 279 | pub enum Change { 280 | /// A change in the state of the keyboard. 281 | Keyboard(DecodedKey), 282 | /// A change in the state of the mouse. 283 | Mouse(MouseState), 284 | } 285 | 286 | #[derive(Debug, Clone, Copy)] 287 | enum EnumWaitState { 288 | DSAck, 289 | IdentAck, 290 | IdentB1, 291 | IdentB2(u8), 292 | } 293 | 294 | #[derive(Debug)] 295 | enum PortState { 296 | None, 297 | Unknown, 298 | Enumerating(EnumWaitState), 299 | Keyboard(Keyboard), 300 | Mouse(Mouse), 301 | } 302 | 303 | #[derive(Debug)] 304 | struct Port { 305 | state: PortState, 306 | } 307 | 308 | impl Port { 309 | fn new() -> Self { 310 | Self { 311 | state: PortState::None, 312 | } 313 | } 314 | 315 | fn handle_data(&mut self, data: u8) -> (Option, Option) { 316 | let (rv, state) = match self.state { 317 | PortState::None => { 318 | if data == 0xFA { 319 | (None, None) 320 | } else if data == 0xAA { 321 | ( 322 | Some(0xF5), 323 | Some(PortState::Enumerating(EnumWaitState::DSAck)), 324 | ) 325 | } else { 326 | (None, None) 327 | } 328 | } 329 | PortState::Unknown => (None, None), 330 | PortState::Enumerating(state) => match state { 331 | EnumWaitState::DSAck => { 332 | if data == 0xFA { 333 | ( 334 | Some(0xF2), 335 | Some(PortState::Enumerating(EnumWaitState::IdentAck)), 336 | ) 337 | } else if data == 0x00 { 338 | (None, None) 339 | } else { 340 | (None, Some(PortState::Unknown)) 341 | } 342 | } 343 | EnumWaitState::IdentAck => { 344 | if data == 0xFA { 345 | (None, Some(PortState::Enumerating(EnumWaitState::IdentB1))) 346 | } else { 347 | (None, Some(PortState::Unknown)) 348 | } 349 | } 350 | EnumWaitState::IdentB1 => match data { 351 | 0x00 => Self::new_mouse(MouseKind::Standard), 352 | 0x03 => Self::new_mouse(MouseKind::Scroll), 353 | 0x04 => Self::new_mouse(MouseKind::FiveButton), 354 | 0xAB => ( 355 | None, 356 | Some(PortState::Enumerating(EnumWaitState::IdentB2(data))), 357 | ), 358 | _ => (None, Some(PortState::Unknown)), 359 | }, 360 | EnumWaitState::IdentB2(b1) => match (b1, data) { 361 | (0xAB, 0x41) => Self::new_keyboard(KeyboardKind::MF2Emul), 362 | (0xAB, 0x83) => Self::new_keyboard(KeyboardKind::MF2), 363 | (0xAB, 0x84) => Self::new_keyboard(KeyboardKind::Short), 364 | (0xAB, 0x85) => Self::new_keyboard(KeyboardKind::N97), 365 | (0xAB, 0x86) => Self::new_keyboard(KeyboardKind::K122), 366 | (0xAB, 0x90) => Self::new_keyboard(KeyboardKind::JapG), 367 | (0xAB, 0x91) => Self::new_keyboard(KeyboardKind::JapP), 368 | (0xAB, 0x92) => Self::new_keyboard(KeyboardKind::JapA), 369 | (0xAB, 0xA1) => Self::new_keyboard(KeyboardKind::Sun), 370 | (0xAB, 0xC1) => Self::new_keyboard(KeyboardKind::MF2Emul), 371 | _ => (None, Some(PortState::Unknown)), 372 | }, 373 | }, 374 | PortState::Keyboard(ref mut keyboard) => { 375 | let (data, key) = keyboard.handle_data(data); 376 | return (data, key.map(Change::Keyboard)); 377 | } 378 | PortState::Mouse(ref mut mouse) => { 379 | let state = mouse.handle_data(data); 380 | return (None, state.map(Change::Mouse)); 381 | } 382 | }; 383 | 384 | if let Some(state) = state { 385 | self.state = state; 386 | } 387 | 388 | (rv, None) 389 | } 390 | 391 | fn new_keyboard(kind: KeyboardKind) -> (Option, Option) { 392 | let (data, keyboard) = Keyboard::new(kind); 393 | (data, Some(PortState::Keyboard(keyboard))) 394 | } 395 | 396 | fn new_mouse(kind: MouseKind) -> (Option, Option) { 397 | let (data, mouse) = Mouse::new(kind); 398 | (data, Some(PortState::Mouse(mouse))) 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /crates/i8042/src/mouse.rs: -------------------------------------------------------------------------------- 1 | pub(crate) enum MouseKind { 2 | Standard, 3 | Scroll, 4 | FiveButton, 5 | } 6 | 7 | #[derive(Debug)] 8 | enum State { 9 | Ack, 10 | Idle, 11 | WaitByte2(u8), 12 | WaitByte3(u8, u8), 13 | } 14 | 15 | /// The state of the mouse. 16 | #[derive(Debug, Clone, PartialEq, Eq)] 17 | pub struct MouseState { 18 | /// The current x coordinate of the mouse, originating from the left. 19 | pub x: u16, 20 | /// The current y coordinate of the mouse, originating from the top. 21 | pub y: u16, 22 | /// Whether the left button is pressed. 23 | pub left: bool, 24 | /// Whether the right button is pressed. 25 | pub right: bool, 26 | /// Whether the middle button is pressed. 27 | pub middle: bool, 28 | } 29 | 30 | #[derive(Debug)] 31 | pub(crate) struct Mouse { 32 | state: State, 33 | mouse_state: MouseState, 34 | } 35 | 36 | impl Mouse { 37 | pub(crate) fn new(_kind: MouseKind) -> (Option, Self) { 38 | ( 39 | Some(0xF4), 40 | Self { 41 | state: State::Ack, 42 | mouse_state: MouseState { 43 | x: 0, 44 | y: 0, 45 | middle: false, 46 | right: false, 47 | left: false, 48 | }, 49 | }, 50 | ) 51 | } 52 | 53 | pub(crate) fn handle_data(&mut self, data: u8) -> Option { 54 | match self.state { 55 | State::Ack => { 56 | self.state = State::Idle; 57 | None 58 | } 59 | State::Idle => { 60 | if data & (1 << 3) != 0 { 61 | self.state = State::WaitByte2(data) 62 | } 63 | None 64 | } 65 | State::WaitByte2(b1) => { 66 | self.state = State::WaitByte3(b1, data); 67 | None 68 | } 69 | State::WaitByte3(b1, b2) => { 70 | let dx = get_signed_9(((b1 >> 6) & 1) != 0, ((b1 >> 4) & 1) != 0, b2); 71 | let dy = get_signed_9(((b1 >> 7) & 1) != 0, ((b1 >> 5) & 1) != 0, data); 72 | 73 | let new_state = MouseState { 74 | x: self.mouse_state.x.saturating_add_signed(dx), 75 | y: self.mouse_state.y.saturating_add_signed(-dy), 76 | left: b1 & (1 << 0) != 0, 77 | right: b1 & (1 << 1) != 0, 78 | middle: b1 & (1 << 2) != 0, 79 | }; 80 | 81 | self.state = State::Idle; 82 | 83 | if new_state != self.mouse_state { 84 | self.mouse_state = new_state.clone(); 85 | Some(new_state) 86 | } else { 87 | None 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | fn get_signed_9(overflow: bool, sign: bool, val: u8) -> i16 { 95 | if sign { 96 | if overflow { 97 | -256 98 | } else { 99 | val as i16 - 0x100 100 | } 101 | } else if overflow { 102 | 256 103 | } else { 104 | val as i16 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /crates/snalloc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "snalloc" 3 | version = "0.1.0" 4 | authors = ["snek"] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | spin = "0.9" 9 | linked_list_allocator = "0.10" 10 | -------------------------------------------------------------------------------- /crates/snalloc/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | extern crate alloc; 4 | 5 | use alloc::alloc::{GlobalAlloc, Layout}; 6 | use linked_list_allocator::Heap as LinkedListAllocator; 7 | use spin::Mutex; 8 | 9 | /// A memory allocator which uses a combination of 10 | /// slab allocation and a fallback allocator. 11 | pub struct Allocator { 12 | inner: Mutex, 13 | } 14 | 15 | struct Inner { 16 | start: usize, 17 | size: usize, 18 | slabs: [SlabAllocator; 7], 19 | fallback: LinkedListAllocator, 20 | } 21 | 22 | impl Inner { 23 | fn slab(&mut self, layout: Layout) -> Option<&mut SlabAllocator> { 24 | let needed = layout.size().max(layout.align()); 25 | self.slabs.iter_mut().find(|slab| slab.size >= needed) 26 | } 27 | 28 | fn stats(&self) -> [[usize; 2]; 8] { 29 | [ 30 | [self.slabs[0].used(), self.slabs[0].size()], 31 | [self.slabs[1].used(), self.slabs[1].size()], 32 | [self.slabs[2].used(), self.slabs[2].size()], 33 | [self.slabs[3].used(), self.slabs[3].size()], 34 | [self.slabs[4].used(), self.slabs[4].size()], 35 | [self.slabs[5].used(), self.slabs[5].size()], 36 | [self.slabs[6].used(), self.slabs[6].size()], 37 | [self.fallback.used(), self.fallback.size()], 38 | ] 39 | } 40 | } 41 | 42 | impl Allocator { 43 | pub const fn new() -> Self { 44 | Self { 45 | inner: Mutex::new(Inner { 46 | start: 0, 47 | size: 0, 48 | slabs: [ 49 | SlabAllocator::new(16), 50 | SlabAllocator::new(32), 51 | SlabAllocator::new(64), 52 | SlabAllocator::new(128), 53 | SlabAllocator::new(256), 54 | SlabAllocator::new(512), 55 | SlabAllocator::new(2048), 56 | ], 57 | fallback: LinkedListAllocator::empty(), 58 | }), 59 | } 60 | } 61 | 62 | pub fn init(&self, start: usize, size: usize) { 63 | let mut inner = self.inner.lock(); 64 | inner.start = start; 65 | inner.size = size; 66 | let region_size = size / (inner.slabs.len() + 1); 67 | for (i, slab) in inner.slabs.iter_mut().enumerate() { 68 | let start = start + (region_size * i); 69 | slab.init(start, region_size); 70 | } 71 | let start = start + (region_size * inner.slabs.len()); 72 | unsafe { 73 | inner.fallback.init(start as _, region_size); 74 | } 75 | } 76 | 77 | pub fn stats(&self) -> [[usize; 2]; 8] { 78 | self.inner.lock().stats() 79 | } 80 | } 81 | 82 | unsafe impl GlobalAlloc for Allocator { 83 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 84 | let mut inner = self.inner.lock(); 85 | if let Some(slab) = inner.slab(layout) { 86 | slab.alloc(layout) 87 | } else { 88 | match inner.fallback.allocate_first_fit(layout).ok() { 89 | Some(allocation) => allocation.as_ptr(), 90 | None => core::ptr::null_mut(), 91 | } 92 | } 93 | } 94 | 95 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 96 | let mut inner = self.inner.lock(); 97 | if let Some(slab) = inner.slab(layout) { 98 | slab.dealloc(ptr, layout) 99 | } else { 100 | if ptr.is_null() { 101 | return; 102 | } 103 | inner 104 | .fallback 105 | .deallocate(core::ptr::NonNull::new_unchecked(ptr), layout) 106 | } 107 | } 108 | } 109 | 110 | #[repr(C)] 111 | struct SlabCell { 112 | next: usize, 113 | } 114 | 115 | #[derive(Debug)] 116 | struct SlabAllocator { 117 | start: usize, 118 | end: usize, 119 | next: usize, 120 | size: usize, 121 | used: usize, 122 | } 123 | 124 | impl SlabAllocator { 125 | const fn new(size: usize) -> Self { 126 | Self { 127 | start: 0, 128 | end: 0, 129 | next: 0, 130 | size, 131 | used: 0, 132 | } 133 | } 134 | 135 | fn init(&mut self, start: usize, size: usize) { 136 | assert!((self.start % self.size) == 0); 137 | self.start = start; 138 | self.end = start + size; 139 | self.next = start; 140 | } 141 | 142 | fn used(&self) -> usize { 143 | self.used 144 | } 145 | 146 | fn size(&self) -> usize { 147 | self.end - self.start 148 | } 149 | 150 | unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 { 151 | debug_assert!(layout.size() <= self.size); 152 | debug_assert!(layout.align() <= self.size); 153 | 154 | if self.next >= self.end { 155 | return core::ptr::null_mut(); 156 | } 157 | 158 | let cell = self.next as *mut SlabCell; 159 | let cell_next = (*cell).next; 160 | (*cell).next = 0; 161 | self.next = if cell_next == 0 { 162 | self.next + self.size 163 | } else { 164 | cell_next 165 | }; 166 | 167 | self.used += layout.size(); 168 | 169 | cell as *mut u8 170 | } 171 | 172 | unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { 173 | debug_assert!(layout.size() <= self.size); 174 | debug_assert!(layout.align() <= self.size); 175 | 176 | let cell = ptr as *mut SlabCell; 177 | (*cell).next = self.next; 178 | self.next = cell as usize; 179 | 180 | self.used -= layout.size(); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "snek_kernel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | spin = "0.9" 8 | lazy_static = { version = "1.4", features = ["spin_no_std"] } 9 | crossbeam-queue = { version = "0.3", default-features = false, features = ["alloc"] } 10 | futures = { version = "0.3", default-features = false, features = ["alloc", "async-await"] } 11 | conquer-once = { version = "0.3", default-features = false } 12 | noto-sans-mono-bitmap = { version = "0.2", default-features = false, features = ["size_16", "regular", "unicode-basic-latin", "unicode-specials"] } 13 | mycelium-bitfield = { workspace = true } 14 | maitake = { git = "https://github.com/hawkw/mycelium.git", rev = "87448c5" } 15 | rand_xoshiro = "0.6" 16 | rand = { version = "0.8", default-features = false, features = ["getrandom"] } 17 | getrandom = { version = "0.2", default-features = false, features = ["rdrand"] } 18 | pci-ids = "0.2" 19 | pci_types = "0.10" 20 | rustc-demangle = "0.1" 21 | snalloc = { path = "../crates/snalloc" } 22 | embedded-graphics = "0.8" 23 | pin-project = "1.1" 24 | bitflags = "2.4" 25 | chrono = { version = "0.4", default-features = false } 26 | libm = "0.2" 27 | unique = "0.9" 28 | xmas-elf = "0.9" 29 | unwinding = { version = "0.2", default-features = false, features = ["unwinder", "fde-static", "personality", "panic", "dwarf-expr", "hide-trace"] } 30 | limine = "0.1" 31 | tracing.workspace = true 32 | tracing-subscriber.workspace = true 33 | tracing-core.workspace = true 34 | hashbrown = { version = "0.15" } 35 | thiserror = { version = "2.0", default-features = false } 36 | log.workspace = true 37 | smoltcp = { path = "../../smoltcp", default-features = false, features = ["alloc", "proto-ipv4", "proto-ipv6", "socket", "medium-ip", "medium-ethernet", "socket-tcp", "socket-icmp", "async", "proto-dhcpv4", "proto-dns", "socket-dhcpv4", "socket-dns", "socket-mdns", "log"] } 38 | async-channel = { version = "2.3", default-features = false } 39 | url = { version = "2.5", default-features = false } 40 | addr2line = { version = "0.24", default-features = false, features = ["rustc-demangle"] } 41 | anyhow = { version = "1", default-features = false } 42 | wasmi = { version = "0.45", default-features = false } 43 | httparse = { version = "1.10", default-features = false } 44 | http = { path = "../../http", default-features = false } #{ version = "1.3", default-features = false } 45 | virtio-drivers = { path = "../../virtio-drivers" }#"0.11" 46 | bit_field = "0.10" 47 | 48 | [target.'cfg(target_arch = "x86_64")'.dependencies] 49 | x86_64 = "0.14" 50 | pic8259 = "0.10" 51 | acpi = { git = "https://github.com/devsnek/acpi.git", rev = "5ac0db01e47b549bdbb5dc16dcc75e5ea4c5d870" } 52 | os_units = "0.4" 53 | x2apic = "0.4" 54 | raw-cpuid = "10.6" 55 | cmos = { path = "../crates/cmos" } 56 | i8042 = { path = "../crates/i8042" } 57 | e9 = { path = "../crates/e9" } 58 | hpet = { path = "../crates/hpet" } 59 | lai = { path = "../../lai-rs" }#{ git = "https://github.com/devsnek/lai-rs.git", rev = "406e271e5ae24390cce50165ea1cdd4841e308e4" } 60 | #acpica-bindings = "0.1" 61 | 62 | [build-dependencies] 63 | image = { version = "0.24", default-features = false, features = ["png"] } 64 | 65 | [features] 66 | work-stealing = [] 67 | -------------------------------------------------------------------------------- /kernel/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsnek/snek_os/8fe76b0f1f5894855719a256f4250d9b14e7b2eb/kernel/assets/logo.png -------------------------------------------------------------------------------- /kernel/assets/logo_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsnek/snek_os/8fe76b0f1f5894855719a256f4250d9b14e7b2eb/kernel/assets/logo_text.png -------------------------------------------------------------------------------- /kernel/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-link-arg=-T./kernel/linker.ld"); 3 | println!("cargo:rustc-link-arg=--no-dynamic-linker"); 4 | println!("cargo:rerun-if-changed=linker.ld"); 5 | 6 | let out_dir = std::env::var("OUT_DIR").unwrap(); 7 | 8 | for file in ["logo", "logo_text"] { 9 | println!("cargo:rerun-if-changed=./assets/{file}.png"); 10 | let img = image::open(format!("./assets/{file}.png")).unwrap(); 11 | let image::DynamicImage::ImageRgba8(img) = img else { 12 | panic!() 13 | }; 14 | 15 | let mut buf: Vec = Vec::new(); 16 | 17 | buf.extend(img.width().to_le_bytes()); 18 | buf.extend(img.height().to_le_bytes()); 19 | 20 | let mut last = [0; 4]; 21 | let mut count: u16 = 0; 22 | for pixel in img.pixels() { 23 | if pixel.0 == last { 24 | count += 1; 25 | } else { 26 | if count > 0 { 27 | buf.extend(count.to_le_bytes()); 28 | buf.extend(last); 29 | } 30 | last = pixel.0; 31 | count = 1; 32 | } 33 | } 34 | 35 | std::fs::write(format!("{out_dir}/{file}.rgba"), buf).unwrap(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kernel/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = [ 3 | "-Cforce-frame-pointers=yes", 4 | "-Csymbol-mangling-version=v0", 5 | "-Cforce-unwind-tables", 6 | "-Cpanic=unwind", 7 | ] 8 | 9 | [unstable] 10 | build-std = ["core", "alloc"] 11 | -------------------------------------------------------------------------------- /kernel/limine.cfg: -------------------------------------------------------------------------------- 1 | :snek_os (KASLR on) 2 | PROTOCOL=limine 3 | KERNEL_PATH=boot:///kernel.elf 4 | TIMEOUT=0 5 | -------------------------------------------------------------------------------- /kernel/linker.ld: -------------------------------------------------------------------------------- 1 | /* We want the symbol _start to be our entry point */ 2 | ENTRY(_start) 3 | 4 | /* Define the program headers we want so the bootloader gives us the right */ 5 | /* MMU permissions */ 6 | PHDRS 7 | { 8 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 9 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 10 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 11 | dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic PHDR for relocations */ 12 | } 13 | 14 | SECTIONS 15 | { 16 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 17 | /* and because that is what the Limine spec mandates. */ 18 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 19 | /* that is the beginning of the region. */ 20 | . = 0xffffffff80000000; 21 | 22 | .text : { 23 | *(.text .text.*) 24 | } :text 25 | PROVIDE (__etext = .); 26 | 27 | /* Move to the next memory page for .rodata */ 28 | . += CONSTANT(MAXPAGESIZE); 29 | 30 | .rodata : { 31 | *(.rodata .rodata.*) 32 | } :rodata 33 | 34 | .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } 35 | . = ALIGN(8); 36 | PROVIDE(__eh_frame = .); 37 | .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } 38 | .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } 39 | 40 | /* Move to the next memory page for .data */ 41 | . += CONSTANT(MAXPAGESIZE); 42 | 43 | .data : { 44 | *(.data .data.*) 45 | } :data 46 | 47 | /* Dynamic section for relocations, both in its own PHDR and inside data PHDR */ 48 | .dynamic : { 49 | *(.dynamic) 50 | } :data :dynamic 51 | 52 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 53 | /* unnecessary zeros will be written to the binary. */ 54 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 55 | /* above this. */ 56 | .bss : { 57 | *(.bss .bss.*) 58 | *(COMMON) 59 | } :data 60 | 61 | /DISCARD/ : { 62 | *(.note .note.*) 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /kernel/src/allocator.rs: -------------------------------------------------------------------------------- 1 | use snalloc::Allocator; 2 | 3 | pub const MANAGED_START: usize = 0x0000_1000_0000_0000; 4 | pub const MANAGED_END: usize = 0x0000_7fff_ffff_f000; 5 | 6 | #[global_allocator] 7 | pub static ALLOCATOR: Allocator = Allocator::new(); 8 | 9 | pub fn init() { 10 | ALLOCATOR.init(MANAGED_START, MANAGED_END - MANAGED_START); 11 | 12 | debug!("[ALLOCATOR] initialized"); 13 | } 14 | -------------------------------------------------------------------------------- /kernel/src/arch/common.rs: -------------------------------------------------------------------------------- 1 | use pci_ids::{Device as DeviceInfo, Subclass as SubclassInfo}; 2 | use pci_types::{capability::PciCapability, Bar, PciAddress}; 3 | 4 | bitflags::bitflags! { 5 | #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 6 | pub struct PciCommand: u16 { 7 | const IO_SPACE = 1 << 0; 8 | const MEMORY_SPACE = 1 << 1; 9 | const BUS_MASTER = 1 << 2; 10 | const SPECIAL_CYCLES = 1 << 3; 11 | const MEMORY_WRITE_AND_INVALIDATE_ENABLE = 1 << 4; 12 | const VGA_PALETTE_SNOOP = 1 << 5; 13 | const PARITY_ERROR_RESPONSE = 1 << 6; 14 | const SERR_ENABLE = 1 << 8; 15 | const FAST_BACK_TO_BACK_ENABLE = 1 << 9; 16 | const INTERRUPT_DISABLE = 1 << 10; 17 | } 18 | } 19 | 20 | bitflags::bitflags! { 21 | #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 22 | pub struct PciStatus: u16 { 23 | const INTERRUPT_STATUS = 1 << 3; 24 | const CAPABILITIES_LIST = 1 << 4; 25 | const MHZ_66_CAPABLE = 1 << 5; 26 | const FAST_BACK_TO_BACK_CAPABLE = 1 << 7; 27 | const MASTER_DATA_PARITY_ERROR = 1 << 8; 28 | const SIGNALED_TARGET_ABORT = 1 << 11; 29 | const RECEIVED_TARGET_ABORT = 1 << 12; 30 | const RECEIVED_MASTER_ABORT = 1 << 13; 31 | const SIGNALED_SYSTEM_ERROR = 1 << 14; 32 | const DETECTED_PARITY_ERROR = 1 << 15; 33 | } 34 | } 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct PciDevice { 38 | pub physical_offset: usize, 39 | pub configuration_address: usize, 40 | pub address: PciAddress, 41 | pub class: u8, 42 | pub sub_class: u8, 43 | pub interface: u8, 44 | pub vendor_id: u16, 45 | pub device_id: u16, 46 | pub sub_vendor_id: u16, 47 | pub sub_device_id: u16, 48 | pub revision: u8, 49 | pub bars: [Option; 6], 50 | pub interrupt_pin: u8, 51 | pub interrupt_line: u8, 52 | pub capabilities: Vec, 53 | } 54 | 55 | impl PciDevice { 56 | pub fn name(&self) -> String { 57 | if let Some(device) = DeviceInfo::from_vid_pid(self.vendor_id, self.device_id) { 58 | format!("{} {}", device.vendor().name(), device.name()) 59 | } else { 60 | SubclassInfo::from_cid_sid(self.class, self.sub_class) 61 | .map(|subclass| { 62 | subclass 63 | .prog_ifs() 64 | .find(|i| i.id() == self.interface) 65 | .map(|i| i.name()) 66 | .unwrap_or(subclass.name()) 67 | }) 68 | .unwrap_or("Unknown Device") 69 | .to_owned() 70 | } 71 | } 72 | 73 | pub unsafe fn read(&self, offset: u16) -> T { 74 | ((self.configuration_address + offset as usize) as *const T).read_volatile() 75 | } 76 | 77 | pub unsafe fn write(&self, offset: u16, value: T) { 78 | ((self.configuration_address + offset as usize) as *mut T).write_volatile(value) 79 | } 80 | 81 | pub fn command(&self) -> PciCommand { 82 | PciCommand::from_bits_truncate(unsafe { self.read::(0x04) }) 83 | } 84 | 85 | pub fn set_command(&self, cmd: PciCommand) { 86 | unsafe { self.write(0x04, cmd) } 87 | } 88 | 89 | pub fn status(&self) -> PciStatus { 90 | PciStatus::from_bits_truncate(unsafe { self.read::(0x06) }) 91 | } 92 | 93 | pub fn capabilities_offset(&self) -> Option { 94 | if self.status().contains(PciStatus::CAPABILITIES_LIST) { 95 | Some((unsafe { self.read::(0x34) } & 0xFC) as u8) 96 | } else { 97 | None 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /kernel/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86_64")] 2 | mod x86_64; 3 | #[cfg(target_arch = "x86_64")] 4 | pub use x86_64::*; 5 | 6 | mod common; 7 | 8 | pub use common::*; 9 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/acpi.rs: -------------------------------------------------------------------------------- 1 | use super::interrupts::{set_interrupt_static, InterruptType}; 2 | use crate::{arch::PciDevice, stack_allocator::StackAllocator}; 3 | use acpi::{ 4 | fadt::Fadt, 5 | hpet::HpetInfo, 6 | mcfg::PciConfigRegions, 7 | platform::PlatformInfo, 8 | sdt::{SdtHeader, Signature}, 9 | AcpiHandler, AcpiTables, PhysicalMapping, 10 | }; 11 | use conquer_once::spin::OnceCell; 12 | use core::convert::TryInto; 13 | use x86_64::{instructions::port::Port, VirtAddr}; 14 | 15 | #[derive(thiserror::Error, Debug)] 16 | pub enum Error { 17 | #[error("{0:?}")] 18 | Lai(lai::Error), 19 | } 20 | 21 | pub type AcpiAllocator = StackAllocator<2048>; 22 | 23 | struct AcpiHolder(AcpiTables); 24 | 25 | unsafe impl Sync for AcpiHolder {} 26 | 27 | static ACPI_TABLES: OnceCell = OnceCell::uninit(); 28 | 29 | fn get_tables() -> &'static AcpiTables { 30 | &ACPI_TABLES.get().unwrap().0 31 | } 32 | 33 | pub fn early_init( 34 | allocator: &AcpiAllocator, 35 | rsdp_address: VirtAddr, 36 | ) -> PlatformInfo<'_, &AcpiAllocator> { 37 | ACPI_TABLES 38 | .try_init_once(|| unsafe { 39 | AcpiHolder(AcpiTables::from_rsdp(AcpiHandlerImpl, rsdp_address.as_u64() as _).unwrap()) 40 | }) 41 | .unwrap(); 42 | 43 | debug!("[ACPI] early initialized"); 44 | 45 | get_tables().platform_info_in(allocator).unwrap() 46 | } 47 | 48 | static HOST: Host = Host; 49 | 50 | pub fn late_init() { 51 | lai::init(&HOST); 52 | 53 | lai::set_acpi_revision(get_tables().revision() as _); 54 | lai::create_namespace(); 55 | 56 | let fadt = get_tables().find_table::().unwrap(); 57 | core::mem::forget(set_interrupt_static( 58 | fadt.sci_interrupt as _, 59 | InterruptType::LevelLow, 60 | handle_interrupt, 61 | )); 62 | 63 | lai::enable_acpi(lai::PICMethod::APIC); 64 | 65 | debug!("[ACPI] late initialized"); 66 | } 67 | 68 | fn handle_interrupt() { 69 | let event = lai::get_sci_event(); 70 | if event.contains(lai::SciEvent::POWER_BUTTON) { 71 | shutdown(); 72 | } 73 | } 74 | 75 | pub fn pci_route_pin(device: &PciDevice) -> Result { 76 | let resource = lai::pci_route_pin( 77 | device.address.segment(), 78 | device.address.bus(), 79 | device.address.device(), 80 | device.address.function(), 81 | device.interrupt_pin, 82 | ) 83 | .map_err(Error::Lai)?; 84 | Ok(resource.base as _) 85 | } 86 | 87 | pub fn shutdown() { 88 | lai::enter_sleep(lai::SleepState::Shutdown).unwrap(); 89 | } 90 | 91 | pub fn reboot() { 92 | lai::reset().unwrap(); 93 | } 94 | 95 | #[derive(Clone)] 96 | struct AcpiHandlerImpl; 97 | 98 | impl AcpiHandler for AcpiHandlerImpl { 99 | unsafe fn map_physical_region( 100 | &self, 101 | physical_address: usize, 102 | size: usize, 103 | ) -> PhysicalMapping { 104 | // limine has already mapped everything for us :) 105 | PhysicalMapping::new( 106 | physical_address, 107 | core::ptr::NonNull::new(physical_address as _).unwrap(), 108 | size, 109 | size, 110 | self.clone(), 111 | ) 112 | } 113 | 114 | fn unmap_physical_region(_region: &PhysicalMapping) { 115 | // we didn't map anything, so don't unmap anything 116 | } 117 | } 118 | 119 | struct Host; 120 | 121 | impl Host { 122 | fn pci_address(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> usize { 123 | let base = get_pci_config_regions() 124 | .physical_address(segment, bus, device, function) 125 | .unwrap() as usize; 126 | base + (offset as usize) 127 | } 128 | } 129 | 130 | impl lai::Host for Host { 131 | fn log(&self, level: lai::LogLevel, message: &str) { 132 | match level { 133 | lai::LogLevel::Debug => debug!("[LAI] {}", message), 134 | lai::LogLevel::Warn => warn!("[LAI] {}", message), 135 | } 136 | } 137 | 138 | fn scan(&self, signature: &str, index: usize) -> *mut u8 { 139 | let aml_table_ptr = match signature { 140 | "DSDT" => get_tables().dsdt().map(|aml| aml.address).unwrap_or(0), 141 | _ => { 142 | let signature = Signature::from_raw(signature.as_bytes().try_into().unwrap()); 143 | get_tables() 144 | .find_sdt(signature, index) 145 | .map(|aml| aml.address) 146 | .unwrap_or(0) 147 | } 148 | }; 149 | 150 | if aml_table_ptr == 0 { 151 | core::ptr::null_mut() 152 | } else { 153 | let sdt_ptr = aml_table_ptr - core::mem::size_of::(); 154 | sdt_ptr as _ 155 | } 156 | } 157 | 158 | fn outb(&self, port: u16, value: u8) { 159 | unsafe { Port::new(port).write(value) } 160 | } 161 | 162 | fn outw(&self, port: u16, value: u16) { 163 | unsafe { Port::new(port).write(value) } 164 | } 165 | 166 | fn outd(&self, port: u16, value: u32) { 167 | unsafe { Port::new(port).write(value) } 168 | } 169 | 170 | fn inb(&self, port: u16) -> u8 { 171 | unsafe { Port::new(port).read() } 172 | } 173 | 174 | fn inw(&self, port: u16) -> u16 { 175 | unsafe { Port::new(port).read() } 176 | } 177 | 178 | fn ind(&self, port: u16) -> u32 { 179 | unsafe { Port::new(port).read() } 180 | } 181 | 182 | fn pci_readb(&self, seg: u16, bus: u8, slot: u8, fun: u8, offset: u16) -> u8 { 183 | unsafe { (self.pci_address(seg, bus, slot, fun, offset) as *const u8).read_volatile() } 184 | } 185 | 186 | fn pci_readw(&self, seg: u16, bus: u8, slot: u8, fun: u8, offset: u16) -> u16 { 187 | unsafe { (self.pci_address(seg, bus, slot, fun, offset) as *const u16).read_volatile() } 188 | } 189 | 190 | fn pci_readd(&self, seg: u16, bus: u8, slot: u8, fun: u8, offset: u16) -> u32 { 191 | unsafe { (self.pci_address(seg, bus, slot, fun, offset) as *const u32).read_volatile() } 192 | } 193 | 194 | fn pci_writeb(&self, seg: u16, bus: u8, slot: u8, fun: u8, offset: u16, value: u8) { 195 | unsafe { (self.pci_address(seg, bus, slot, fun, offset) as *mut u8).write_volatile(value) } 196 | } 197 | 198 | fn pci_writew(&self, seg: u16, bus: u8, slot: u8, fun: u8, offset: u16, value: u16) { 199 | unsafe { (self.pci_address(seg, bus, slot, fun, offset) as *mut u16).write_volatile(value) } 200 | } 201 | 202 | fn pci_writed(&self, seg: u16, bus: u8, slot: u8, fun: u8, offset: u16, value: u32) { 203 | unsafe { (self.pci_address(seg, bus, slot, fun, offset) as *mut u32).write_volatile(value) } 204 | } 205 | 206 | fn map(&self, address: usize, _count: usize) -> *mut u8 { 207 | address as _ 208 | } 209 | 210 | fn unmap(&self, _address: usize, _count: usize) {} 211 | 212 | fn sleep(&self, _ms: u64) {} 213 | 214 | fn timer(&self) -> u64 { 215 | unimplemented!() 216 | } 217 | } 218 | 219 | pub fn get_pci_config_regions() -> PciConfigRegions<'static, alloc::alloc::Global> { 220 | PciConfigRegions::new(get_tables()).unwrap() 221 | } 222 | 223 | pub fn get_century_register() -> u8 { 224 | let fadt = get_tables().find_table::().unwrap(); 225 | fadt.century 226 | } 227 | 228 | pub fn get_platform_info(g: &G) -> PlatformInfo<'static, &G> { 229 | get_tables().platform_info_in(g).unwrap() 230 | } 231 | 232 | pub fn get_hpet() -> Option { 233 | HpetInfo::new(get_tables()).ok() 234 | } 235 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/gdt.rs: -------------------------------------------------------------------------------- 1 | use x86_64::{ 2 | instructions::{ 3 | segmentation::{CS, DS, ES, FS, GS, SS}, 4 | tables::load_tss, 5 | }, 6 | structures::{ 7 | gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}, 8 | tss::TaskStateSegment, 9 | }, 10 | VirtAddr, 11 | }; 12 | 13 | const STACK_SIZE: usize = 65535; 14 | 15 | pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; 16 | pub const PAGE_FAULT_IST_INDEX: u16 = 1; 17 | 18 | macro_rules! tss { 19 | ($stack:expr) => {{ 20 | let mut tss = TaskStateSegment::new(); 21 | tss.privilege_stack_table[0] = { 22 | let stack_start = VirtAddr::from_ptr($stack); 23 | stack_start + STACK_SIZE 24 | }; 25 | tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { 26 | let stack_start = VirtAddr::from_ptr($stack); 27 | stack_start + STACK_SIZE 28 | }; 29 | tss.interrupt_stack_table[PAGE_FAULT_IST_INDEX as usize] = { 30 | let stack_start = VirtAddr::from_ptr($stack); 31 | stack_start + STACK_SIZE 32 | }; 33 | tss 34 | }}; 35 | } 36 | 37 | pub struct Selectors { 38 | pub kernel_code: SegmentSelector, 39 | pub kernel_data: SegmentSelector, 40 | pub user_code: SegmentSelector, 41 | pub user_data: SegmentSelector, 42 | pub tss: SegmentSelector, 43 | } 44 | 45 | fn build(tss: &'static TaskStateSegment) -> (GlobalDescriptorTable, Selectors) { 46 | let mut gdt = GlobalDescriptorTable::new(); 47 | 48 | let kernel_code = Descriptor::kernel_code_segment(); 49 | let kernel_data = Descriptor::kernel_data_segment(); 50 | let user_code = Descriptor::user_code_segment(); 51 | let user_data = Descriptor::user_data_segment(); 52 | 53 | // The order is required. 54 | let kernel_code_selector = gdt.add_entry(kernel_code); 55 | let kernel_data_selector = gdt.add_entry(kernel_data); 56 | 57 | let user_data_selector = gdt.add_entry(user_data); 58 | let user_code_selector = gdt.add_entry(user_code); 59 | 60 | let tss_selector = gdt.add_entry(Descriptor::tss_segment(tss)); 61 | 62 | let selectors = Selectors { 63 | kernel_code: kernel_code_selector, 64 | kernel_data: kernel_data_selector, 65 | user_code: user_code_selector, 66 | user_data: user_data_selector, 67 | tss: tss_selector, 68 | }; 69 | 70 | (gdt, selectors) 71 | } 72 | 73 | fn load(gdt: &'static GlobalDescriptorTable, selectors: &Selectors) { 74 | gdt.load(); 75 | 76 | unsafe { 77 | use x86_64::instructions::segmentation::Segment; 78 | 79 | CS::set_reg(selectors.kernel_code); 80 | DS::set_reg(selectors.kernel_data); 81 | ES::set_reg(selectors.kernel_data); 82 | FS::set_reg(selectors.kernel_data); 83 | GS::set_reg(selectors.kernel_data); 84 | SS::set_reg(selectors.kernel_data); 85 | 86 | load_tss(selectors.tss); 87 | } 88 | } 89 | 90 | lazy_static::lazy_static! { 91 | pub static ref BP_TSS: TaskStateSegment = tss!({ 92 | static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; 93 | core::ptr::addr_of!(STACK) 94 | }); 95 | pub static ref BP_GDT: (GlobalDescriptorTable, Selectors) = build(&BP_TSS); 96 | } 97 | 98 | pub fn init() { 99 | load(&BP_GDT.0, &BP_GDT.1); 100 | 101 | debug!("[GDT] initialized"); 102 | } 103 | 104 | pub struct ApInfo { 105 | gdt: &'static GlobalDescriptorTable, 106 | selectors: Selectors, 107 | } 108 | 109 | pub fn allocate_for_ap() -> ApInfo { 110 | let tss = Box::leak(Box::new(tss!(&vec![0u8; STACK_SIZE].leak()))); 111 | let (gdt, selectors) = build(tss); 112 | let gdt = Box::leak(Box::new(gdt)); 113 | ApInfo { gdt, selectors } 114 | } 115 | 116 | pub fn init_smp(info: &ApInfo) { 117 | load(info.gdt, &info.selectors); 118 | } 119 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/hpet.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicPtr, Ordering}; 2 | use hpet::Hpet; 3 | use x86_64::PhysAddr; 4 | 5 | static HPET: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); 6 | 7 | fn get_hpet() -> Option<&'static mut Hpet> { 8 | let hpet = HPET.load(Ordering::SeqCst); 9 | if hpet.is_null() { 10 | None 11 | } else { 12 | Some(unsafe { &mut *hpet }) 13 | } 14 | } 15 | 16 | pub fn get_counter() -> Option { 17 | get_hpet().map(|h| h.counter_value()) 18 | } 19 | 20 | pub fn get_counter_period() -> Option { 21 | get_hpet().map(|h| h.counter_period()) 22 | } 23 | 24 | pub fn init() { 25 | let Some(hpet_info) = super::acpi::get_hpet() else { 26 | return; 27 | }; 28 | 29 | let hpet = super::memory::map_address( 30 | PhysAddr::new(hpet_info.base_address as u64), 31 | core::mem::size_of::(), 32 | ); 33 | 34 | HPET.store(hpet.as_u64() as _, Ordering::Relaxed); 35 | 36 | get_hpet().unwrap().set_counter_enable(true); 37 | } 38 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/local.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::BTreeMap; 2 | use core::{marker::PhantomPinned, pin::Pin}; 3 | use spin::Mutex; 4 | use x86_64::{ 5 | registers::model_specific::{GsBase, KernelGsBase}, 6 | VirtAddr, 7 | }; 8 | 9 | #[repr(C)] 10 | #[derive(Debug)] 11 | pub struct GsLocalData { 12 | _self: *const Self, 13 | magic: usize, 14 | pub data: Mutex>, 15 | _must_pin: PhantomPinned, 16 | } 17 | 18 | impl GsLocalData { 19 | const MAGIC: usize = 0xDEADBEEF; 20 | fn new() -> Self { 21 | Self { 22 | _self: core::ptr::null(), 23 | magic: Self::MAGIC, 24 | data: Mutex::new(BTreeMap::new()), 25 | _must_pin: PhantomPinned, 26 | } 27 | } 28 | 29 | pub fn get() -> Option> { 30 | let this = unsafe { 31 | let ptr: *const Self; 32 | asm!("mov {}, gs:0x0", out(reg) ptr); 33 | if ptr.is_null() { 34 | return None; 35 | } 36 | Pin::new_unchecked(&*ptr) 37 | }; 38 | if this.magic != Self::MAGIC { 39 | return None; 40 | } 41 | Some(this) 42 | } 43 | } 44 | 45 | pub fn init() { 46 | let ptr = Box::into_raw(Box::new(GsLocalData::new())); 47 | unsafe { 48 | (*ptr)._self = ptr as *const _; 49 | GsBase::write(VirtAddr::new(ptr as u64)); 50 | KernelGsBase::write(VirtAddr::new(ptr as u64)); 51 | } 52 | 53 | debug!("[LOCAL] initialized"); 54 | } 55 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/memory.rs: -------------------------------------------------------------------------------- 1 | use crate::stack_allocator::StackAllocator; 2 | use limine::{MemmapEntry, MemoryMapEntryType, NonNullPtr}; 3 | use os_units::{Bytes, NumOfPages}; 4 | use spin::Mutex; 5 | use x86_64::{ 6 | structures::paging::{ 7 | page::{PageRange, Size4KiB}, 8 | FrameAllocator, Mapper, OffsetPageTable, Page, PageSize, PageTable, PageTableFlags, 9 | PhysFrame, Translate, 10 | }, 11 | PhysAddr, VirtAddr, 12 | }; 13 | 14 | lazy_static::lazy_static! { 15 | pub static ref MAPPER: Mutex>> = Mutex::new(None); 16 | pub static ref FRAME_ALLOCATOR_ALLOCATOR: StackAllocator::<2048> = StackAllocator::new(); 17 | pub static ref FRAME_ALLOCATOR: Mutex> = Mutex::new(None); 18 | } 19 | 20 | pub fn init(physical_memory_offset: u64, memory_regions: &'static [NonNullPtr]) { 21 | let level_4_table = unsafe { active_level_4_table(physical_memory_offset) }; 22 | let table = 23 | unsafe { OffsetPageTable::new(level_4_table, VirtAddr::new(physical_memory_offset)) }; 24 | 25 | let _ = MAPPER.lock().insert(table); 26 | 27 | let frame_allocator = unsafe { BootInfoFrameAllocator::init(memory_regions) }; 28 | 29 | let _ = FRAME_ALLOCATOR.lock().insert(frame_allocator); 30 | 31 | debug!("[MEMORY] initialized"); 32 | } 33 | 34 | unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut PageTable { 35 | use x86_64::registers::control::Cr3; 36 | 37 | let (level_4_table_frame, _) = Cr3::read(); 38 | 39 | let phys = level_4_table_frame.start_address(); 40 | let virt = VirtAddr::new(physical_memory_offset + phys.as_u64()); 41 | let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); 42 | 43 | &mut *page_table_ptr // unsafe 44 | } 45 | 46 | pub struct BootInfoFrameAllocator { 47 | iter: Box, &'static StackAllocator<2048>>, 48 | free_frames: *mut FreePhysFrame, 49 | } 50 | unsafe impl Send for BootInfoFrameAllocator {} 51 | 52 | impl BootInfoFrameAllocator { 53 | pub unsafe fn init(memory_regions: &'static [NonNullPtr]) -> Self { 54 | debug!( 55 | "Available memory {}b", 56 | memory_regions 57 | .iter() 58 | .map(|p| &*p.as_ptr()) 59 | .filter(|r| r.typ == MemoryMapEntryType::Usable) 60 | .map(|r| r.len) 61 | .sum::() 62 | ); 63 | 64 | let iter = Box::new_in( 65 | memory_regions 66 | .iter() 67 | .map(|p| &*p.as_ptr()) 68 | .filter(|r| r.typ == MemoryMapEntryType::Usable) 69 | .map(|r| r.base..(r.base + r.len)) 70 | .flat_map(|r| r.step_by(4096)) 71 | .map(|addr| PhysFrame::containing_address(PhysAddr::new(addr))), 72 | &*FRAME_ALLOCATOR_ALLOCATOR, 73 | ); 74 | 75 | BootInfoFrameAllocator { 76 | iter, 77 | free_frames: core::ptr::null_mut(), 78 | } 79 | } 80 | 81 | #[allow(unused)] 82 | pub fn deallocate_frame(&mut self, frame: PhysFrame) { 83 | let next = self.free_frames; 84 | let free_frame = FRAME_ALLOCATOR_ALLOCATOR.own(FreePhysFrame { frame, next }); 85 | self.free_frames = free_frame; 86 | } 87 | } 88 | 89 | unsafe impl FrameAllocator for BootInfoFrameAllocator { 90 | fn allocate_frame(&mut self) -> Option { 91 | if !self.free_frames.is_null() { 92 | unsafe { 93 | let frame = (*self.free_frames).frame; 94 | self.free_frames = (*self.free_frames).next; 95 | return Some(frame); 96 | } 97 | } 98 | self.iter.next() 99 | } 100 | } 101 | 102 | struct FreePhysFrame { 103 | frame: PhysFrame, 104 | next: *mut FreePhysFrame, 105 | } 106 | 107 | fn search_free_addr_from(num_pages: NumOfPages, region: PageRange) -> Option { 108 | let mut cnt = 0; 109 | let mut start = None; 110 | for page in region { 111 | let addr = page.start_address(); 112 | if available(addr) { 113 | if start.is_none() { 114 | start = Some(addr); 115 | } 116 | 117 | cnt += 1; 118 | 119 | if cnt >= num_pages.as_usize() { 120 | return start; 121 | } 122 | } else { 123 | cnt = 0; 124 | start = None; 125 | } 126 | } 127 | 128 | None 129 | } 130 | 131 | fn available(addr: VirtAddr) -> bool { 132 | let mut binding = super::memory::MAPPER.lock(); 133 | let mapper = binding.as_mut().unwrap(); 134 | 135 | mapper.translate_addr(addr).is_none() && !addr.is_null() 136 | } 137 | 138 | pub fn map_pages_from(start: PhysAddr, object_size: usize, region: PageRange) -> VirtAddr { 139 | let start_frame_addr = start.align_down(Size4KiB::SIZE); 140 | let end_frame_addr = (start + object_size).align_down(Size4KiB::SIZE); 141 | 142 | let num_pages = 143 | Bytes::new((end_frame_addr - start_frame_addr) as usize + 1).as_num_of_pages::(); 144 | 145 | let virt = search_free_addr_from(num_pages, region).unwrap(); 146 | 147 | let mut mapper = super::memory::MAPPER.lock(); 148 | let mapper = mapper.as_mut().unwrap(); 149 | 150 | let mut frame_allocator = super::memory::FRAME_ALLOCATOR.lock(); 151 | let frame_allocator = frame_allocator.as_mut().unwrap(); 152 | 153 | for i in 0..num_pages.as_usize() { 154 | let page = Page::::containing_address(virt + Size4KiB::SIZE * i as u64); 155 | let frame = PhysFrame::containing_address(start_frame_addr + Size4KiB::SIZE * i as u64); 156 | let flag = 157 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE; 158 | 159 | unsafe { 160 | mapper 161 | .map_to(page, frame, flag, frame_allocator) 162 | .unwrap() 163 | .flush(); 164 | } 165 | } 166 | 167 | super::interrupts::send_flush_tlb(); 168 | 169 | let page_offset = start.as_u64() % Size4KiB::SIZE; 170 | 171 | virt + page_offset 172 | } 173 | 174 | pub fn map_address(phys: PhysAddr, size: usize) -> VirtAddr { 175 | map_pages_from( 176 | phys, 177 | size, 178 | PageRange { 179 | start: Page::from_start_address(VirtAddr::new(0xFFFF_8000_0000_0000)).unwrap(), 180 | end: Page::containing_address(VirtAddr::new(0xFFFF_FFFF_FFFF_FFFF)), 181 | }, 182 | ) 183 | } 184 | 185 | pub fn translate_virt_addr(addr: VirtAddr) -> Option { 186 | let mut binding = super::memory::MAPPER.lock(); 187 | let mapper = binding.as_mut().unwrap(); 188 | 189 | mapper.translate_addr(addr) 190 | } 191 | 192 | pub fn translate_phys_addr(addr: PhysAddr) -> VirtAddr { 193 | let mut binding = super::memory::MAPPER.lock(); 194 | let mapper = binding.as_mut().unwrap(); 195 | 196 | mapper.phys_offset() + addr.as_u64() 197 | } 198 | 199 | pub fn lazy_map(address: VirtAddr) -> bool { 200 | if address.as_u64() < crate::allocator::MANAGED_START as u64 201 | || address.as_u64() >= crate::allocator::MANAGED_END as u64 202 | { 203 | return false; 204 | } 205 | 206 | let mut binding = super::memory::MAPPER.lock(); 207 | let mapper = binding.as_mut().unwrap(); 208 | 209 | if mapper.translate_addr(address).is_some() { 210 | return false; 211 | } 212 | 213 | let mut binding = super::memory::FRAME_ALLOCATOR.lock(); 214 | let frame_allocator = binding.as_mut().unwrap(); 215 | 216 | let page = Page::containing_address(address); 217 | 218 | let frame = frame_allocator.allocate_frame().unwrap(); 219 | let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; 220 | unsafe { 221 | mapper 222 | .map_to(page, frame, flags, frame_allocator) 223 | .unwrap() 224 | .flush() 225 | }; 226 | 227 | super::interrupts::send_flush_tlb(); 228 | 229 | unsafe { 230 | core::slice::from_raw_parts_mut(page.start_address().as_u64() as *mut u8, page.size() as _) 231 | .fill(0); 232 | } 233 | 234 | true 235 | } 236 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | mod acpi; 2 | mod gdt; 3 | mod hpet; 4 | mod interrupts; 5 | mod local; 6 | mod memory; 7 | mod pci; 8 | mod pit; 9 | mod time; 10 | 11 | use conquer_once::spin::OnceCell; 12 | use limine::{HhdmRequest, MemmapRequest, RsdpRequest, SmpInfo, SmpRequest}; 13 | use x86_64::{registers::model_specific::Msr, VirtAddr}; 14 | 15 | static HHDM: HhdmRequest = HhdmRequest::new(0); 16 | static MEMMAP: MemmapRequest = MemmapRequest::new(0); 17 | static RSDP: RsdpRequest = RsdpRequest::new(0); 18 | static SMP: SmpRequest = SmpRequest::new(0); 19 | 20 | pub fn init() { 21 | init_sse(); 22 | 23 | set_pid(0); 24 | 25 | gdt::init(); 26 | 27 | let memmap = MEMMAP.get_response().get_mut().unwrap().memmap_mut(); 28 | let physical_memory_offset = HHDM.get_response().get().unwrap().offset; 29 | 30 | memory::init(physical_memory_offset, memmap); 31 | 32 | crate::panic::init(); 33 | 34 | { 35 | let acpi_allocator = acpi::AcpiAllocator::new(); 36 | let rsdp_addr = RSDP.get_response().get().unwrap().address.as_ptr().unwrap() as u64; 37 | 38 | let acpi_platform_info = acpi::early_init(&acpi_allocator, VirtAddr::new(rsdp_addr)); 39 | 40 | interrupts::init(&acpi_platform_info); 41 | } 42 | 43 | crate::allocator::init(); 44 | 45 | crate::framebuffer::late_init(); 46 | 47 | acpi::late_init(); 48 | 49 | hpet::init(); 50 | 51 | time::init(); 52 | 53 | local::init(); 54 | 55 | crate::panic::late_init(); 56 | 57 | pci::init(physical_memory_offset); 58 | 59 | init_smp(); 60 | } 61 | 62 | struct ApInfo { 63 | gdt: gdt::ApInfo, 64 | wake: u8, 65 | } 66 | 67 | static AP_INFO: OnceCell> = OnceCell::uninit(); 68 | 69 | fn init_smp() { 70 | let smp = SMP.get_response().get_mut().unwrap(); 71 | 72 | // allocate on main thread because ap can't set up 73 | // lazy allocation interrupt until it sets up gdt. 74 | let infos = smp 75 | .cpus() 76 | .iter() 77 | .map(|_| ApInfo { 78 | gdt: gdt::allocate_for_ap(), 79 | wake: 0, 80 | }) 81 | .collect(); 82 | AP_INFO.try_init_once(|| infos).unwrap(); 83 | 84 | let bsp_lapic_id = smp.bsp_lapic_id; 85 | for cpu in smp.cpus().iter_mut() { 86 | if cpu.lapic_id == bsp_lapic_id { 87 | continue; 88 | } 89 | cpu.goto_address = ap_entry; 90 | } 91 | } 92 | 93 | extern "C" fn ap_entry(boot_info: *const SmpInfo) -> ! { 94 | crate::panic::catch_unwind(|| -> ! { 95 | let boot_info = unsafe { &*boot_info }; 96 | start_smp(boot_info); 97 | }); 98 | } 99 | 100 | fn start_smp(boot_info: &SmpInfo) -> ! { 101 | init_sse(); 102 | 103 | set_pid(boot_info.processor_id as _); 104 | 105 | let ap_info = AP_INFO.get().unwrap(); 106 | gdt::init_smp(&ap_info[boot_info.processor_id as usize].gdt); 107 | 108 | interrupts::init_smp(); 109 | 110 | local::init(); 111 | 112 | crate::ap_main(boot_info.processor_id as _); 113 | } 114 | 115 | fn init_sse() { 116 | // unwinding uses stmxcsr which will UD if this isn't enabled 117 | unsafe { 118 | asm!( 119 | " 120 | mov rax, cr0 121 | and ax, 0xFFFB // clear coprocessor emulation CR0.EM 122 | or ax, 0x2 // set coprocessor monitoring CR0.MP 123 | mov cr0, rax 124 | mov rax, cr4 125 | or ax, 3 << 9 // set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time 126 | mov cr4, rax 127 | ", 128 | out("rax") _, 129 | ); 130 | } 131 | } 132 | 133 | fn set_pid(pid: u64) { 134 | let mut msr = Msr::new(0xc0000103); 135 | unsafe { 136 | msr.write(pid); 137 | } 138 | } 139 | 140 | pub fn get_pid() -> u64 { 141 | let mut pid; 142 | unsafe { 143 | asm!("rdpid {}", out(reg) pid); 144 | } 145 | pid 146 | } 147 | 148 | pub use acpi::get_pci_config_regions; 149 | pub use acpi::pci_route_pin; 150 | pub use acpi::reboot; 151 | pub use acpi::shutdown; 152 | pub use interrupts::{ 153 | set_interrupt_dyn, set_interrupt_msi, set_interrupt_static, InterruptGuard, InterruptType, 154 | TIMER_INTERVAL, 155 | }; 156 | pub use local::GsLocalData as LocalData; 157 | pub use memory::{map_address, translate_phys_addr, translate_virt_addr}; 158 | pub use pci::get_devices as get_pci_devices; 159 | pub use time::{now, timestamp}; 160 | 161 | #[inline(always)] 162 | pub fn halt_loop() -> ! { 163 | loop { 164 | x86_64::instructions::hlt(); 165 | } 166 | } 167 | 168 | #[inline(always)] 169 | pub fn enable_interrupts_and_halt() { 170 | if let Ok(aps) = AP_INFO.try_get() { 171 | let pid = get_pid(); 172 | let addr = (&aps[pid as usize].wake) as *const u8; 173 | 174 | unsafe { 175 | asm!(" 176 | mov rcx, 0 177 | mov rdx, 0 178 | # rax set by `inout` below 179 | monitor 180 | 181 | mov rax, 0 182 | mov rcx, 0 183 | sti 184 | mwait 185 | ", in("rax") addr, out("rcx") _, out("rdx") _); 186 | } 187 | } else { 188 | unsafe { 189 | asm!( 190 | " 191 | sti 192 | hlt 193 | " 194 | ); 195 | } 196 | } 197 | } 198 | 199 | pub fn print(args: core::fmt::Arguments) { 200 | if e9::detect() { 201 | e9::print(args); 202 | } 203 | } 204 | pub use x86_64::instructions::interrupts::without_interrupts; 205 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/pci.rs: -------------------------------------------------------------------------------- 1 | use super::{get_pci_config_regions, memory::translate_phys_addr}; 2 | use crate::arch::PciDevice; 3 | use acpi::PciConfigRegions; 4 | use alloc::collections::BTreeMap; 5 | use pci_types::{Bar, EndpointHeader, HeaderType, PciAddress, PciHeader, PciPciBridgeHeader}; 6 | use spin::{rwlock::RwLockReadGuard, RwLock}; 7 | use x86_64::PhysAddr; 8 | 9 | pub struct ConfigRegionAccess { 10 | regions: PciConfigRegions<'static, alloc::alloc::Global>, 11 | } 12 | 13 | impl Default for ConfigRegionAccess { 14 | fn default() -> Self { 15 | Self { 16 | regions: get_pci_config_regions(), 17 | } 18 | } 19 | } 20 | 21 | impl pci_types::ConfigRegionAccess for ConfigRegionAccess { 22 | unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 { 23 | let phys = self 24 | .regions 25 | .physical_address( 26 | address.segment(), 27 | address.bus(), 28 | address.device(), 29 | address.function(), 30 | ) 31 | .unwrap(); 32 | let ptr = translate_phys_addr(PhysAddr::new(phys)) 33 | .as_ptr::() 34 | .byte_add(offset as _); 35 | core::ptr::read_volatile(ptr) 36 | } 37 | 38 | unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) { 39 | let phys = self 40 | .regions 41 | .physical_address( 42 | address.segment(), 43 | address.bus(), 44 | address.device(), 45 | address.function(), 46 | ) 47 | .unwrap(); 48 | let ptr = translate_phys_addr(PhysAddr::new(phys)) 49 | .as_mut_ptr::() 50 | .byte_add(offset as _); 51 | core::ptr::write_volatile(ptr, value); 52 | } 53 | } 54 | 55 | struct Resolver { 56 | access: ConfigRegionAccess, 57 | offset: u64, 58 | devices: BTreeMap, 59 | } 60 | 61 | impl Resolver { 62 | fn resolve(mut self) -> BTreeMap { 63 | let segments = self 64 | .access 65 | .regions 66 | .iter() 67 | .map(|region| region.segment_group) 68 | .collect::>(); 69 | 70 | segments.into_iter().for_each(|segment| { 71 | self.scan_segment(segment); 72 | }); 73 | 74 | self.devices 75 | } 76 | 77 | fn function_exists(&self, address: PciAddress) -> bool { 78 | self.access 79 | .regions 80 | .physical_address( 81 | address.segment(), 82 | address.bus(), 83 | address.device(), 84 | address.function(), 85 | ) 86 | .is_some() 87 | } 88 | 89 | fn scan_segment(&mut self, segment: u16) { 90 | let address = PciAddress::new(segment, 0, 0, 0); 91 | if self.function_exists(address) 92 | && PciHeader::new(address).has_multiple_functions(&self.access) 93 | { 94 | for i in 0..8 { 95 | self.scan_bus(segment, i); 96 | } 97 | } else { 98 | self.scan_bus(segment, 0); 99 | } 100 | } 101 | 102 | fn scan_bus(&mut self, segment: u16, bus: u8) { 103 | for device in 0..32 { 104 | let address = PciAddress::new(segment, bus, device, 0); 105 | if self.function_exists(address) { 106 | self.scan_function(segment, bus, device, 0); 107 | let header = PciHeader::new(address); 108 | if header.has_multiple_functions(&self.access) { 109 | for function in 1..8 { 110 | self.scan_function(segment, bus, device, function); 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | fn scan_function(&mut self, segment: u16, bus: u8, device: u8, function: u8) { 118 | let address = PciAddress::new(segment, bus, device, function); 119 | if self.function_exists(address) { 120 | let header = PciHeader::new(address); 121 | let (vendor_id, device_id) = header.id(&self.access); 122 | 123 | if vendor_id == 0xffff || device_id == 0xffff { 124 | return; 125 | } 126 | 127 | let (revision, class, sub_class, interface) = header.revision_and_class(&self.access); 128 | 129 | match header.header_type(&self.access) { 130 | HeaderType::Endpoint => { 131 | let endpoint_header = 132 | EndpointHeader::from_header(header, &self.access).unwrap(); 133 | let (sub_vendor_id, sub_device_id) = endpoint_header.subsystem(&self.access); 134 | let bars = { 135 | let mut bars = [None; 6]; 136 | 137 | let mut skip_next = false; 138 | for i in 0..6 { 139 | if skip_next { 140 | skip_next = false; 141 | continue; 142 | } 143 | 144 | let bar = endpoint_header.bar(i, &self.access); 145 | skip_next = matches!(bar, Some(Bar::Memory64 { .. })); 146 | bars[i as usize] = bar; 147 | } 148 | 149 | bars 150 | }; 151 | 152 | let (interrupt_pin, interrupt_line) = endpoint_header.interrupt(&self.access); 153 | let configuration_address = self 154 | .access 155 | .regions 156 | .physical_address( 157 | address.segment(), 158 | address.bus(), 159 | address.device(), 160 | address.function(), 161 | ) 162 | .unwrap(); 163 | 164 | let capabilities = endpoint_header.capabilities(&self.access).collect(); 165 | 166 | self.devices.insert( 167 | address, 168 | PciDevice { 169 | physical_offset: self.offset as _, 170 | configuration_address: configuration_address as _, 171 | address, 172 | class, 173 | sub_class, 174 | interface, 175 | vendor_id, 176 | device_id, 177 | sub_vendor_id, 178 | sub_device_id, 179 | revision, 180 | bars, 181 | interrupt_pin, 182 | interrupt_line, 183 | capabilities, 184 | }, 185 | ); 186 | } 187 | HeaderType::PciPciBridge => { 188 | let header = PciPciBridgeHeader::from_header(header, &self.access).unwrap(); 189 | let start = header.secondary_bus_number(&self.access); 190 | let end = header.subordinate_bus_number(&self.access); 191 | for bus_id in start..=end { 192 | self.scan_bus(segment, bus_id); 193 | } 194 | } 195 | _ => {} 196 | } 197 | } 198 | } 199 | } 200 | 201 | static DEVICES: RwLock> = RwLock::new(BTreeMap::new()); 202 | 203 | pub fn get_devices() -> RwLockReadGuard<'static, BTreeMap> { 204 | DEVICES.read() 205 | } 206 | 207 | pub fn init(physical_offset: u64) { 208 | let resolver = Resolver { 209 | access: Default::default(), 210 | offset: physical_offset, 211 | devices: BTreeMap::new(), 212 | }; 213 | 214 | let mut devices = resolver.resolve(); 215 | 216 | DEVICES.write().append(&mut devices); 217 | 218 | debug!("[PCI] initialized"); 219 | } 220 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/pit.rs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2022 Eliza Weisman 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 | 23 | use super::interrupts::{set_interrupt_static, InterruptType}; 24 | use core::sync::atomic::{AtomicBool, Ordering}; 25 | use core::time::Duration; 26 | use spin::Mutex; 27 | use x86_64::instructions::port::Port; 28 | 29 | const BASE_FREQUENCY_HZ: usize = 1193182; 30 | 31 | lazy_static::lazy_static! { 32 | pub static ref PIT: Mutex = { 33 | core::mem::forget(set_interrupt_static(0, InterruptType::EdgeHigh, on_tick)); 34 | Mutex::new(Pit::new()) 35 | }; 36 | } 37 | 38 | pub static SLEEPING: AtomicBool = AtomicBool::new(false); 39 | 40 | pub fn on_tick() { 41 | let _was_sleeping = super::pit::SLEEPING 42 | .compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire) 43 | .is_ok(); 44 | } 45 | 46 | pub struct Pit { 47 | channel0: Port, 48 | // channel1: Port, 49 | // channel2: Port, 50 | command: Port, 51 | } 52 | 53 | impl Pit { 54 | fn new() -> Self { 55 | let base = 0x40; 56 | Self { 57 | channel0: Port::new(base), 58 | // channel1: Port::new(base + 1), 59 | // channel2: Port::new(base + 2), 60 | command: Port::new(base + 3), 61 | } 62 | } 63 | 64 | pub fn sleep(&mut self, duration: Duration) { 65 | SLEEPING 66 | .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) 67 | .unwrap(); 68 | 69 | let duration_ms = duration.as_millis() as u64 as f64; 70 | let ticks_per_ms = BASE_FREQUENCY_HZ as f64 / 1000.0; 71 | let target_time = ticks_per_ms * duration_ms; 72 | let divisor = libm::round(target_time) as u16; 73 | 74 | let interrupts_enabled = x86_64::instructions::interrupts::are_enabled(); 75 | x86_64::instructions::interrupts::disable(); 76 | 77 | let command = Command::new() 78 | .with(Command::BCD_BINARY, false) 79 | .with(Command::MODE, OperatingMode::Interrupt) 80 | .with(Command::ACCESS, AccessMode::Both) 81 | .with(Command::CHANNEL, ChannelSelect::Channel0); 82 | self.send_command(command); 83 | self.set_divisor(divisor); 84 | 85 | while SLEEPING.load(Ordering::Acquire) { 86 | x86_64::instructions::interrupts::enable_and_hlt(); 87 | } 88 | if !interrupts_enabled { 89 | x86_64::instructions::interrupts::disable(); 90 | } 91 | } 92 | 93 | fn set_divisor(&mut self, divisor: u16) { 94 | let low = divisor as u8; 95 | let high = (divisor >> 8) as u8; 96 | unsafe { 97 | self.channel0.write(low); 98 | self.channel0.write(high); 99 | } 100 | } 101 | 102 | fn send_command(&mut self, command: Command) { 103 | unsafe { 104 | self.command.write(command.bits()); 105 | } 106 | } 107 | } 108 | 109 | mycelium_bitfield::bitfield! { 110 | struct Command { 111 | const BCD_BINARY: bool; 112 | const MODE: OperatingMode; 113 | const ACCESS: AccessMode; 114 | const CHANNEL: ChannelSelect; 115 | } 116 | } 117 | 118 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 119 | #[repr(u8)] 120 | enum OperatingMode { 121 | Interrupt = 0b000, 122 | HwOneshot = 0b001, 123 | RateGenerator = 0b010, 124 | SquareWave = 0b011, 125 | SwStrobe = 0b100, 126 | HwStrobe = 0b101, 127 | RateGenerator2 = 0b110, 128 | SquareWave2 = 0b111, 129 | } 130 | 131 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 132 | #[repr(u8)] 133 | enum AccessMode { 134 | LatchCount = 0b00, 135 | LowByte = 0b01, 136 | HighByte = 0b10, 137 | Both = 0b11, 138 | } 139 | 140 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 141 | #[repr(u8)] 142 | enum ChannelSelect { 143 | Channel0 = 0b00, 144 | Channel1 = 0b01, 145 | Channel2 = 0b10, 146 | Readback = 0b11, 147 | } 148 | 149 | impl mycelium_bitfield::FromBits for OperatingMode { 150 | const BITS: u32 = 3; 151 | type Error = core::convert::Infallible; 152 | 153 | fn try_from_bits(bits: u8) -> Result { 154 | Ok(match bits { 155 | 0b000 => Self::Interrupt, 156 | 0b001 => Self::HwOneshot, 157 | 0b010 => Self::RateGenerator, 158 | 0b011 => Self::SquareWave, 159 | 0b100 => Self::SwStrobe, 160 | 0b101 => Self::HwStrobe, 161 | 0b110 => Self::RateGenerator2, 162 | 0b111 => Self::SquareWave2, 163 | bits => unreachable!( 164 | "unexpected bitpattern for `AccessMode`: {:#b} (this \ 165 | should never happen as all 2-bit patterns are covered!)", 166 | bits 167 | ), 168 | }) 169 | } 170 | 171 | fn into_bits(self) -> u8 { 172 | self as u8 173 | } 174 | } 175 | 176 | impl mycelium_bitfield::FromBits for AccessMode { 177 | const BITS: u32 = 2; 178 | type Error = core::convert::Infallible; 179 | 180 | fn try_from_bits(bits: u8) -> Result { 181 | Ok(match bits { 182 | 0b00 => Self::LatchCount, 183 | 0b01 => Self::LowByte, 184 | 0b10 => Self::HighByte, 185 | 0b11 => Self::Both, 186 | bits => unreachable!( 187 | "unexpected bitpattern for `AccessMode`: {:#b} (this \ 188 | should never happen as all 2-bit patterns are covered!)", 189 | bits 190 | ), 191 | }) 192 | } 193 | 194 | fn into_bits(self) -> u8 { 195 | self as u8 196 | } 197 | } 198 | 199 | impl mycelium_bitfield::FromBits for ChannelSelect { 200 | const BITS: u32 = 2; 201 | type Error = core::convert::Infallible; 202 | 203 | fn try_from_bits(bits: u8) -> Result { 204 | Ok(match bits { 205 | 0b00 => Self::Channel0, 206 | 0b01 => Self::Channel1, 207 | 0b10 => Self::Channel2, 208 | 0b11 => Self::Readback, 209 | bits => unreachable!( 210 | "unexpected bitpattern for `ChannelSelect`: {:#b} (this \ 211 | should never happen as all 2-bit patterns are covered!)", 212 | bits 213 | ), 214 | }) 215 | } 216 | 217 | fn into_bits(self) -> u8 { 218 | self as u8 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /kernel/src/arch/x86_64/time.rs: -------------------------------------------------------------------------------- 1 | use super::interrupts::TIMER_INTERVAL; 2 | use chrono::{TimeZone, Utc}; 3 | use cmos::CMOS; 4 | use core::{ 5 | sync::atomic::{AtomicU64, Ordering}, 6 | time::Duration, 7 | }; 8 | 9 | static UPTIME_MS: AtomicU64 = AtomicU64::new(0); 10 | static BOOT_SEC: AtomicU64 = AtomicU64::new(0); 11 | static LAST_HPET: AtomicU64 = AtomicU64::new(0); 12 | 13 | pub fn init() { 14 | let mut cmos = CMOS::new(); 15 | let rtc = cmos.read_rtc(super::acpi::get_century_register()); 16 | BOOT_SEC.store( 17 | Utc.from_utc_datetime(&rtc).timestamp() as _, 18 | Ordering::Relaxed, 19 | ); 20 | } 21 | 22 | pub fn on_tick() { 23 | UPTIME_MS.fetch_add(TIMER_INTERVAL.as_millis() as _, Ordering::Relaxed); 24 | if let Some(counter) = super::hpet::get_counter() { 25 | LAST_HPET.store(counter, Ordering::Relaxed); 26 | } 27 | } 28 | 29 | pub fn now() -> Duration { 30 | let counter = super::hpet::get_counter(); 31 | let now = Duration::from_millis(UPTIME_MS.load(Ordering::SeqCst)); 32 | if let Some(counter) = counter { 33 | let period = super::hpet::get_counter_period().unwrap(); 34 | let last = LAST_HPET.load(Ordering::SeqCst); 35 | let fs = counter.saturating_sub(last) * period as u64; 36 | now + Duration::from_nanos(fs / 1000000) 37 | } else { 38 | now 39 | } 40 | } 41 | 42 | pub fn timestamp() -> Duration { 43 | Duration::from_secs(BOOT_SEC.load(Ordering::SeqCst)) + now() 44 | } 45 | -------------------------------------------------------------------------------- /kernel/src/debug.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicPtr, Ordering}; 2 | use spin::Mutex; 3 | use tracing::{ 4 | dispatch::set_global_default, 5 | field::{Field, Visit}, 6 | span::{Attributes, Record}, 7 | Collect, Dispatch, Event, Id, Metadata, 8 | }; 9 | 10 | static PRINT: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); 11 | 12 | type PrintFn = fn(core::fmt::Arguments); 13 | 14 | fn print(args: core::fmt::Arguments) { 15 | crate::arch::print(format_args!("{args}")); 16 | 17 | let print = PRINT.load(Ordering::Relaxed); 18 | if !print.is_null() { 19 | // SAFETY: value is a non-null pointer 20 | let print = unsafe { core::mem::transmute::<*mut (), PrintFn>(print) }; 21 | print(args); 22 | } 23 | } 24 | 25 | struct Collector {} 26 | 27 | impl Collect for Collector { 28 | fn enabled(&self, _metadata: &Metadata<'_>) -> bool { 29 | true 30 | } 31 | 32 | fn new_span(&self, _span: &Attributes<'_>) -> Id { 33 | Id::from_u64(0) 34 | } 35 | 36 | fn record(&self, _span: &Id, _values: &Record<'_>) {} 37 | 38 | fn record_follows_from(&self, _span: &Id, _follows: &Id) {} 39 | 40 | fn event(&self, event: &Event<'_>) { 41 | static LOCK: Mutex<()> = Mutex::new(()); 42 | let _guard = LOCK.lock(); 43 | 44 | let meta = event.metadata(); 45 | // let target = meta.target(); 46 | let level = meta.level(); 47 | 48 | print(format_args!("{level}")); 49 | 50 | struct Visitor(bool); 51 | impl Visit for Visitor { 52 | fn record_debug(&mut self, field: &Field, value: &dyn core::fmt::Debug) { 53 | if self.0 { 54 | self.0 = false; 55 | print(format_args!(" ")); 56 | } else { 57 | print(format_args!("; ")); 58 | } 59 | 60 | if field.name() == "message" { 61 | print(format_args!("{:?}", value)); 62 | } else { 63 | print(format_args!("{} = {:?}", field.name(), value)); 64 | self.0 = false; 65 | } 66 | } 67 | } 68 | 69 | event.record(&mut Visitor(true)); 70 | 71 | print(format_args!("\n")); 72 | } 73 | 74 | fn enter(&self, _span: &Id) {} 75 | 76 | fn exit(&self, _span: &Id) {} 77 | 78 | fn current_span(&self) -> tracing_core::span::Current { 79 | tracing_core::span::Current::none() 80 | } 81 | } 82 | 83 | impl log::Log for Collector { 84 | fn enabled(&self, _metadata: &log::Metadata) -> bool { 85 | true 86 | } 87 | 88 | fn log(&self, record: &log::Record) { 89 | if log::Log::enabled(self, record.metadata()) { 90 | print(format_args!("{} - {}\n", record.level(), record.args())); 91 | } 92 | } 93 | 94 | fn flush(&self) {} 95 | } 96 | 97 | static COLLECTOR: Collector = Collector {}; 98 | 99 | pub fn init() { 100 | let dispatch = Dispatch::from_static(&COLLECTOR); 101 | let _ = set_global_default(dispatch); 102 | let _ = log::set_logger(&COLLECTOR); 103 | log::set_max_level(log::LevelFilter::Trace); 104 | } 105 | 106 | pub fn set_print(f: PrintFn) { 107 | PRINT.store(f as _, Ordering::Relaxed); 108 | } 109 | -------------------------------------------------------------------------------- /kernel/src/drivers/dma.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::translate_virt_addr; 2 | use alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error, Layout}; 3 | use unique::Unique; 4 | use x86_64::VirtAddr; 5 | 6 | pub struct Dma { 7 | layout: Layout, 8 | dma: Unique, 9 | } 10 | 11 | impl Dma { 12 | pub fn new_zeroed(align: usize) -> Dma { 13 | assert!(align >= core::mem::align_of::()); 14 | let layout = Layout::new::().align_to(align).unwrap(); 15 | let ptr = unsafe { alloc_zeroed(layout) }; 16 | if ptr.is_null() { 17 | handle_alloc_error(layout); 18 | } 19 | let dma = unsafe { Unique::new_unchecked(ptr as _) }; 20 | Self { layout, dma } 21 | } 22 | 23 | pub fn as_ptr(&mut self) -> *mut T { 24 | self.dma.as_ptr() 25 | } 26 | } 27 | 28 | impl Dma<[T]> { 29 | pub fn new_zeroed_slice(len: usize, align: usize) -> Dma<[T]> { 30 | assert!(align >= core::mem::align_of::()); 31 | let layout = Layout::array::(len).unwrap().align_to(align).unwrap(); 32 | let ptr = unsafe { alloc_zeroed(layout) }; 33 | if ptr.is_null() { 34 | handle_alloc_error(layout); 35 | } 36 | let slice = unsafe { core::slice::from_raw_parts_mut(ptr as *mut T, len) }; 37 | let dma = unsafe { Unique::new_unchecked(slice) }; 38 | Self { layout, dma } 39 | } 40 | } 41 | 42 | impl Dma { 43 | pub fn phys_addr(&self) -> usize { 44 | translate_virt_addr(VirtAddr::new(self.dma.as_ptr() as *mut () as _)) 45 | .unwrap() 46 | .as_u64() as _ 47 | } 48 | 49 | pub fn leak(self) -> *mut T { 50 | let ptr = self.dma.as_ptr(); 51 | core::mem::forget(self); 52 | ptr 53 | } 54 | } 55 | 56 | impl Drop for Dma { 57 | fn drop(&mut self) { 58 | unsafe { 59 | let ptr = self.dma.as_ptr(); 60 | if !ptr.is_null() { 61 | core::ptr::drop_in_place(ptr); 62 | dealloc(ptr as _, self.layout); 63 | } 64 | } 65 | } 66 | } 67 | 68 | impl core::ops::Deref for Dma { 69 | type Target = T; 70 | 71 | fn deref(&self) -> &T { 72 | unsafe { self.dma.as_ref() } 73 | } 74 | } 75 | 76 | impl core::ops::DerefMut for Dma { 77 | fn deref_mut(&mut self) -> &mut T { 78 | unsafe { self.dma.as_mut() } 79 | } 80 | } 81 | 82 | impl core::fmt::Debug for Dma 83 | where 84 | T: core::fmt::Debug, 85 | { 86 | fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { 87 | unsafe { self.dma.as_ref().fmt(fmt) } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /kernel/src/drivers/e1000.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::{translate_phys_addr, translate_virt_addr, PciCommand, PciDevice}; 2 | use alloc::{ 3 | alloc::{alloc, dealloc, Layout}, 4 | sync::Arc, 5 | }; 6 | use core::{marker::PhantomData, pin::Pin}; 7 | use futures::task::AtomicWaker; 8 | use pci_types::Bar; 9 | use spin::Mutex; 10 | use x86_64::{PhysAddr, VirtAddr}; 11 | 12 | #[derive(Copy, Clone)] 13 | #[repr(usize)] 14 | enum Register { 15 | Control = 0x0, 16 | Status = 0x8, 17 | 18 | Eeprom = 0x14, 19 | 20 | ICause = 0xc0, 21 | IMask = 0xd0, 22 | 23 | RCtrl = 0x100, 24 | RxDescLo = 0x2800, 25 | RxDescHi = 0x2804, 26 | RxDescLen = 0x2808, 27 | RxDescHead = 0x2810, 28 | RxDescTail = 0x2818, 29 | 30 | TCtrl = 0x400, 31 | TxDesLo = 0x3800, 32 | TxDesHi = 0x3804, 33 | TxDescLen = 0x3808, 34 | TxDescHead = 0x3810, 35 | TxDescTail = 0x3818, 36 | } 37 | 38 | bitflags::bitflags! { 39 | struct ControlFlags: u32 { 40 | const LRST = 1 << 3; 41 | const ASDE = 1 << 5; 42 | const SLU = 1 << 6; 43 | const ILOS = 1 << 7; 44 | const RST = 1 << 26; 45 | const VME = 1 << 30; 46 | const PHY_RST = 1 << 31; 47 | } 48 | } 49 | 50 | bitflags::bitflags! { 51 | #[derive(Default, Debug, Clone, Copy)] 52 | struct TStatus: u8 { 53 | const DD = 1 << 0; // Descriptor Done 54 | const EC = 1 << 1; // Excess Collisions 55 | const LC = 1 << 2; // Late Collision 56 | const TU = 1 << 3; // Transmit Underrun 57 | } 58 | } 59 | 60 | bitflags::bitflags! { 61 | #[derive(Default, Clone, Copy, Debug)] 62 | struct RStatus: u8 { 63 | const DD = 1 << 0; // Descriptor Done 64 | const EOP = 1 << 1; // End of Packet 65 | const IXSM = 1 << 2; // Ignore Checksum 66 | const VP = 1 << 3; // 802.1Q 67 | const RSV = 1 << 4; // Reserved 68 | const TCPCS = 1 << 5; // TCP Checksum Calculated on Packet 69 | const IPCS = 1 << 6; // IP Checksum Calculated on Packet 70 | const PIF = 1 << 7; // Passed in-exact Filter 71 | } 72 | } 73 | 74 | bitflags::bitflags! { 75 | struct ECtl: u32 { 76 | const LRST = 1 << 3; 77 | const ASDE = 1 << 5; 78 | const SLU = 1 << 6; // Set Link Up 79 | const ILOS = 1 << 7; 80 | const RST = 1 << 26; 81 | const VME = 1 << 30; 82 | const PHY_RST = 1 << 31; 83 | } 84 | } 85 | 86 | bitflags::bitflags! { 87 | struct TCtl: u32 { 88 | const EN = 1 << 1; // Transmit Enable 89 | const PSP = 1 << 3; // Pad Short Packets 90 | const SWXOFF = 1 << 22; // Software XOFF Transmission 91 | const RTLC = 1 << 24; // Re-transmit on Late Collision 92 | } 93 | } 94 | 95 | impl TCtl { 96 | fn set_collision_threshold(&mut self, value: u8) { 97 | *self = Self::from_bits_retain(self.bits() | ((value as u32) << 4)); 98 | } 99 | 100 | fn set_collision_distance(&mut self, value: u8) { 101 | *self = Self::from_bits_retain(self.bits() | ((value as u32) << 12)); 102 | } 103 | } 104 | 105 | bitflags::bitflags! { 106 | struct RCtl: u32 { 107 | const EN = 1 << 1; // Receiver Enable 108 | const SBP = 1 << 2; // Store Bad Packets 109 | const UPE = 1 << 3; // Unicast Promiscuous Enabled 110 | const MPE = 1 << 4; // Multicast Promiscuous Enabled 111 | const LPE = 1 << 5; // Long Packet Reception Enable 112 | const LBM_NONE = 0 << 6; // No Loopback 113 | const LBM_PHY = 3 << 6; // PHY or external SerDesc loopback 114 | const RDMTS_HALF = 0 << 8; // Free Buffer Threshold is 1/2 of RDLEN 115 | const RDMTS_QUARTER = 1 << 8; // Free Buffer Threshold is 1/4 of RDLEN 116 | const RDMTS_EIGHTH = 2 << 8; // Free Buffer Threshold is 1/8 of RDLEN 117 | const MO_36 = 0 << 12; // Multicast Offset - bits 47:36 118 | const MO_35 = 1 << 12; // Multicast Offset - bits 46:35 119 | const MO_34 = 2 << 12; // Multicast Offset - bits 45:34 120 | const MO_32 = 3 << 12; // Multicast Offset - bits 43:32 121 | const BAM = 1 << 15; // Broadcast Accept Mode 122 | const VFE = 1 << 18; // VLAN Filter Enable 123 | const CFIEN = 1 << 19; // Canonical Form Indicator Enable 124 | const CFI = 1 << 20; // Canonical Form Indicator Bit Value 125 | const DPF = 1 << 22; // Discard Pause Frames 126 | const PMCF = 1 << 23; // Pass MAC Control Frames 127 | const SECRC = 1 << 26; // Strip Ethernet CRC 128 | 129 | // Receive Buffer Size - bits 17:16 130 | const BSIZE_256 = 3 << 16; 131 | const BSIZE_512 = 2 << 16; 132 | const BSIZE_1024 = 1 << 16; 133 | const BSIZE_2048 = 0 << 16; 134 | const BSIZE_4096 = (3 << 16) | (1 << 25); 135 | const BSIZE_8192 = (2 << 16) | (1 << 25); 136 | const BSIZE_16384 = (1 << 16) | (1 << 25); 137 | } 138 | } 139 | 140 | bitflags::bitflags! { 141 | #[derive(Debug, Clone, Copy)] 142 | pub struct InterruptFlags: u32 { 143 | const TXDW = 1 << 0; // Transmit Descriptor Written Back 144 | const TXQE = 1 << 1; // Transmit Queue Empty 145 | const LSC = 1 << 2; // Link Status Change 146 | const RXDMT0 = 1 << 4; // Receive Descriptor Minimum Threshold 147 | const DSW = 1 << 5; // Disable SW Write Access 148 | const RXO = 1 << 6; // Receiver Overrun 149 | const RXT0 = 1 << 7; // Receiver Timer Interrupt 150 | const MDAC = 1 << 9; // MDIO Access Complete 151 | const PHYINT = 1 << 12; // PHY Interrupt 152 | const LSECPN = 1 << 14; // MACsec Packet Number 153 | const TXD_LOW = 1 << 15; // Transmit Descriptor Low Threshold hit 154 | const SRPD = 1 << 16; // Small Receive Packet Detected 155 | const ACK = 1 << 17; // Receive ACK Frame Detected 156 | const ECCER = 1 << 22; // ECC Error 157 | } 158 | } 159 | 160 | #[derive(thiserror::Error, Debug)] 161 | enum Error { 162 | #[error("unknown bar")] 163 | UnknownBar, 164 | #[error("no eeprom")] 165 | NoEeprom, 166 | #[error("route gsi failed {0:?}")] 167 | RouteGsi(anyhow::Error), 168 | } 169 | 170 | #[derive(Debug)] 171 | #[repr(C, packed)] 172 | struct TxDescriptor { 173 | pub addr: PhysAddr, 174 | pub length: u16, 175 | pub cso: u8, 176 | pub cmd: TxCommand, 177 | pub status: TStatus, 178 | pub css: u8, 179 | pub special: u16, 180 | } 181 | 182 | bitflags::bitflags! { 183 | /// Section 3.3.3.1 184 | #[derive(Debug, Clone, Copy)] 185 | struct TxCommand: u8 { 186 | const EOP = 1 << 0; // End of Packet 187 | const IFCS = 1 << 1; // FCS/CRC 188 | const IC = 1 << 2; // Insert Checksum 189 | const RS = 1 << 3; // Report Status 190 | const RSV = 1 << 4; // RESERVED 191 | const DEXT = 1 << 5; // Extension(?) 192 | const VLE = 1 << 6; // VLAN Packet Enable 193 | const IDE = 1 << 7; // Interrupt Delay Enable 194 | } 195 | } 196 | 197 | mycelium_bitfield::bitfield! { 198 | struct Status { 199 | const FD: bool; // Full Duplex 200 | const LU: bool; // Link Up 201 | const FUNCTION_ID = 2; 202 | const TXOFF: bool; // Transmission Paused 203 | const TBIMODE: bool; // TBI Mode/internal SerDes Indication 204 | const SPEED = 2; // Link speed setting 205 | const ASDV = 2; // Auto Speed Detection Value 206 | const _RESERVED1 = 1; 207 | const PCI66: bool; 208 | const BUS64: bool; 209 | const PCIX_MODE: bool; 210 | const PCIXSPD = 2; 211 | const _RESERVED2 = 20; 212 | } 213 | } 214 | 215 | #[derive(Debug)] 216 | #[repr(C, packed)] 217 | struct RxDescriptor { 218 | pub addr: PhysAddr, 219 | pub length: u16, 220 | pub checksum: u16, 221 | pub status: RStatus, 222 | pub errors: u8, 223 | pub special: u16, 224 | } 225 | 226 | const TX_DESC_NUM: u32 = 32; 227 | const TX_DESC_SIZE: u32 = TX_DESC_NUM * core::mem::size_of::() as u32; 228 | const RX_DESC_NUM: u32 = 32; 229 | const RX_DESC_SIZE: u32 = RX_DESC_NUM * core::mem::size_of::() as u32; 230 | 231 | #[derive(Debug, Clone, Copy)] 232 | struct Registers { 233 | base: VirtAddr, 234 | } 235 | 236 | impl Registers { 237 | fn remove_flags(&self, register: Register, flag: u32) { 238 | self.write(register, self.read(register) & !flag); 239 | } 240 | 241 | fn insert_flags(&self, register: Register, flag: u32) { 242 | self.write(register, self.read(register) | flag); 243 | } 244 | 245 | fn read(&self, register: Register) -> u32 { 246 | unsafe { 247 | let register = self.base.as_ptr::().add(register as usize); 248 | core::ptr::read_volatile(register as *const u32) 249 | } 250 | } 251 | 252 | fn write(&self, register: Register, value: u32) { 253 | self.write_raw(register as _, value); 254 | } 255 | 256 | fn write_raw(&self, register: u32, value: u32) { 257 | unsafe { 258 | let register = self.base.as_mut_ptr::().add(register as usize); 259 | core::ptr::write_volatile(register as *mut u32, value); 260 | } 261 | } 262 | 263 | fn detect_eeprom(&self) -> bool { 264 | self.write(Register::Eeprom, 1); 265 | 266 | for _ in 0..1000 { 267 | let value = self.read(Register::Eeprom); 268 | 269 | if value & (1 << 4) > 0 { 270 | return true; 271 | } 272 | } 273 | 274 | false 275 | } 276 | 277 | fn read_eeprom(&self, addr: u8) -> u32 { 278 | self.write(Register::Eeprom, 1 | ((addr as u32) << 8)); 279 | 280 | loop { 281 | let res = self.read(Register::Eeprom); 282 | 283 | if res & (1 << 4) > 0 { 284 | return (res >> 16) & 0xffff; 285 | } 286 | } 287 | } 288 | 289 | fn reset(&self) { 290 | self.insert_flags(Register::Control, ControlFlags::RST.bits()); 291 | 292 | while ControlFlags::from_bits_truncate(self.read(Register::Control)) 293 | .contains(ControlFlags::RST) 294 | { 295 | core::hint::spin_loop(); 296 | } 297 | 298 | self.remove_flags( 299 | Register::Control, 300 | (ControlFlags::LRST | ControlFlags::PHY_RST | ControlFlags::VME).bits(), 301 | ); 302 | } 303 | 304 | fn status(&self) -> Status { 305 | Status::from_bits(self.read(Register::Status)) 306 | } 307 | } 308 | 309 | struct E1000 { 310 | registers: Registers, 311 | address: [u8; 6], 312 | 313 | tx_cur: usize, 314 | tx_ring: *mut [TxDescriptor; TX_DESC_NUM as usize], 315 | 316 | rx_cur: usize, 317 | rx_ring: *mut [RxDescriptor; RX_DESC_NUM as usize], 318 | 319 | interrupt_guard: Option, 320 | 321 | waker: AtomicWaker, 322 | } 323 | 324 | impl E1000 { 325 | fn new(header: &PciDevice) -> Result>, Error> { 326 | header.set_command(header.command() | PciCommand::BUS_MASTER | PciCommand::MEMORY_SPACE); 327 | 328 | let registers_addr = match header.bars[0] { 329 | Some(Bar::Memory64 { address, .. }) => PhysAddr::new(address), 330 | Some(Bar::Memory32 { address, .. }) => PhysAddr::new(address as u64), 331 | _ => return Err(Error::UnknownBar), 332 | }; 333 | 334 | let registers = Registers { 335 | base: VirtAddr::new(registers_addr.as_u64() + header.physical_offset as u64), 336 | }; 337 | 338 | registers.reset(); 339 | 340 | if !registers.detect_eeprom() { 341 | return Err(Error::NoEeprom); 342 | } 343 | 344 | let mut address = [0; 6]; 345 | for i in 0..3 { 346 | let x = registers.read_eeprom(i) as u16; 347 | address[i as usize * 2] = (x & 0xff) as u8; 348 | address[i as usize * 2 + 1] = (x >> 8) as u8; 349 | } 350 | 351 | let tx_ring = { 352 | let addr = unsafe { alloc(Layout::from_size_align(4096, 4096).unwrap()) }; 353 | 354 | let descriptors = unsafe { &mut *(addr as *mut [TxDescriptor; TX_DESC_NUM as usize]) }; 355 | 356 | for desc in descriptors { 357 | *desc = TxDescriptor { 358 | addr: PhysAddr::zero(), 359 | length: 0, 360 | cso: 0, 361 | cmd: TxCommand::empty(), 362 | status: TStatus::empty(), 363 | css: 0, 364 | special: 0, 365 | }; 366 | } 367 | 368 | let phys = translate_virt_addr(VirtAddr::new(addr as _)).unwrap(); 369 | 370 | registers.write(Register::TxDesLo, phys.as_u64() as _); 371 | registers.write(Register::TxDesHi, (phys.as_u64() >> 32) as _); 372 | registers.write(Register::TxDescLen, TX_DESC_SIZE); 373 | registers.write(Register::TxDescHead, 0); 374 | registers.write(Register::TxDescTail, 0); 375 | 376 | let mut flags = TCtl::from_bits_retain(1 << 28) | TCtl::EN | TCtl::PSP | TCtl::RTLC; 377 | flags.set_collision_distance(64); 378 | flags.set_collision_threshold(15); 379 | 380 | registers.write(Register::TCtrl, flags.bits()); 381 | 382 | addr 383 | }; 384 | 385 | let rx_ring = { 386 | let addr = unsafe { alloc(Layout::from_size_align(4096, 4096).unwrap()) }; 387 | 388 | let descriptors = unsafe { &mut *(addr as *mut [RxDescriptor; RX_DESC_NUM as usize]) }; 389 | 390 | for desc in descriptors { 391 | let recv_buffer = unsafe { alloc(Layout::from_size_align(4096, 4096).unwrap()) }; 392 | 393 | *desc = RxDescriptor { 394 | addr: translate_virt_addr(VirtAddr::new(recv_buffer as _)).unwrap(), 395 | length: 0, 396 | checksum: 0, 397 | status: RStatus::empty(), 398 | errors: 0, 399 | special: 0, 400 | }; 401 | } 402 | 403 | let phys = translate_virt_addr(VirtAddr::new(addr as _)).unwrap(); 404 | 405 | registers.write(Register::RxDescLo, phys.as_u64() as _); 406 | registers.write(Register::RxDescHi, (phys.as_u64() >> 32) as _); 407 | registers.write(Register::RxDescLen, RX_DESC_SIZE); 408 | registers.write(Register::RxDescHead, 0); 409 | registers.write(Register::RxDescTail, RX_DESC_NUM - 1); 410 | 411 | let flags = RCtl::EN 412 | | RCtl::SBP 413 | | RCtl::UPE 414 | | RCtl::LPE 415 | | RCtl::MPE 416 | | RCtl::LBM_NONE 417 | | RCtl::RDMTS_EIGHTH 418 | | RCtl::BAM 419 | | RCtl::SECRC 420 | | RCtl::BSIZE_4096; 421 | 422 | registers.write(Register::RCtrl, flags.bits()); 423 | 424 | addr 425 | }; 426 | 427 | for i in 0..128 { 428 | registers.write_raw(0x5200 + i * 4, 0); 429 | } 430 | 431 | registers.write( 432 | Register::IMask, 433 | (InterruptFlags::TXDW 434 | | InterruptFlags::TXQE 435 | | InterruptFlags::LSC 436 | | InterruptFlags::RXDMT0 437 | | InterruptFlags::DSW 438 | | InterruptFlags::RXO 439 | | InterruptFlags::RXT0 440 | | InterruptFlags::MDAC 441 | | InterruptFlags::PHYINT 442 | | InterruptFlags::LSECPN 443 | | InterruptFlags::TXD_LOW 444 | | InterruptFlags::SRPD 445 | | InterruptFlags::ACK 446 | | InterruptFlags::ECCER) 447 | .bits(), 448 | ); 449 | registers.read(Register::ICause); 450 | 451 | let mut this = Box::new(Self { 452 | registers, 453 | address, 454 | 455 | tx_cur: 0, 456 | tx_ring: tx_ring as _, 457 | 458 | rx_cur: 0, 459 | rx_ring: rx_ring as _, 460 | 461 | interrupt_guard: None, 462 | waker: AtomicWaker::new(), 463 | }); 464 | 465 | let this_raw = &mut *this as *mut E1000; 466 | let mut this = Box::into_pin(this); 467 | 468 | let gsi = crate::arch::pci_route_pin(header).map_err(|e| Error::RouteGsi(e.into()))?; 469 | this.interrupt_guard = Some(crate::arch::set_interrupt_dyn( 470 | gsi, 471 | crate::arch::InterruptType::LevelLow, 472 | Box::new(move || { 473 | let this = unsafe { &mut *this_raw }; 474 | this.handle_irq(); 475 | }), 476 | )); 477 | 478 | this.link_up(); 479 | 480 | Ok(this) 481 | } 482 | 483 | fn link_up(&self) { 484 | self.registers 485 | .insert_flags(Register::Control, ECtl::SLU.bits()); 486 | 487 | while !self.registers.status().get(Status::LU) { 488 | core::hint::spin_loop(); 489 | } 490 | } 491 | 492 | fn handle_irq(&mut self) { 493 | InterruptFlags::from_bits_retain(self.registers.read(Register::ICause)); 494 | 495 | self.waker.wake(); 496 | } 497 | 498 | fn send(&mut self, packet: &[u8]) { 499 | let cur = self.tx_cur; 500 | let ring = self.tx_ring(); 501 | 502 | ring[cur].addr = translate_virt_addr(VirtAddr::new(packet.as_ptr().addr() as _)).unwrap(); 503 | ring[cur].length = packet.len() as _; 504 | ring[cur].cmd = TxCommand::RS | TxCommand::IFCS | TxCommand::EOP; 505 | ring[cur].status = TStatus::empty(); 506 | 507 | self.tx_cur = (self.tx_cur + 1) % TX_DESC_NUM as usize; 508 | 509 | self.registers 510 | .write(Register::TxDescTail, self.tx_cur as u32); 511 | } 512 | 513 | fn recv(&mut self) -> Option<(usize, *mut [u8])> { 514 | let id = self.rx_cur; 515 | let desc = &mut self.rx_ring()[id]; 516 | 517 | if !desc.status.contains(RStatus::DD) { 518 | return None; 519 | } 520 | 521 | Some(( 522 | id, 523 | core::ptr::from_raw_parts_mut( 524 | translate_phys_addr(desc.addr).as_u64() as *mut u8, 525 | desc.length as usize, 526 | ), 527 | )) 528 | } 529 | 530 | fn has_recv(&mut self) -> bool { 531 | let id = self.rx_cur; 532 | self.rx_ring()[id].status.contains(RStatus::DD) 533 | } 534 | 535 | fn recv_end(&mut self, id: usize) { 536 | let desc = &mut self.rx_ring()[id]; 537 | 538 | assert!(desc.status.contains(RStatus::DD)); 539 | 540 | desc.status = RStatus::empty(); 541 | 542 | let old = self.rx_cur; 543 | self.rx_cur = (self.rx_cur + 1) % RX_DESC_NUM as usize; 544 | self.registers.write(Register::RxDescTail, old as u32); 545 | } 546 | 547 | fn rx_ring(&mut self) -> &mut [RxDescriptor] { 548 | unsafe { &mut *self.rx_ring } 549 | } 550 | 551 | fn tx_ring(&mut self) -> &mut [TxDescriptor] { 552 | unsafe { &mut *self.tx_ring } 553 | } 554 | } 555 | 556 | unsafe impl Send for E1000 {} 557 | unsafe impl Sync for E1000 {} 558 | 559 | impl Drop for E1000 { 560 | fn drop(&mut self) { 561 | unsafe { 562 | dealloc( 563 | self.tx_ring as _, 564 | Layout::from_size_align(4096, 4096).unwrap(), 565 | ); 566 | for rx_desc in self.rx_ring() { 567 | let addr = translate_phys_addr(rx_desc.addr).as_u64() as _; 568 | dealloc(addr, Layout::from_size_align(4096, 4096).unwrap()); 569 | } 570 | dealloc( 571 | self.rx_ring as _, 572 | Layout::from_size_align(4096, 4096).unwrap(), 573 | ); 574 | }; 575 | } 576 | } 577 | 578 | pub struct Driver { 579 | inner: Arc>>>, 580 | } 581 | 582 | pub struct TxToken { 583 | inner: Arc>>>, 584 | } 585 | 586 | impl smoltcp::phy::TxToken for TxToken { 587 | fn consume(self, len: usize, f: F) -> R 588 | where 589 | F: FnOnce(&mut [u8]) -> R, 590 | { 591 | let mut buffer = vec![0; len]; 592 | let result = f(&mut buffer); 593 | crate::arch::without_interrupts(|| { 594 | self.inner.lock().send(&buffer); 595 | }); 596 | result 597 | } 598 | } 599 | 600 | pub struct RxToken<'a> { 601 | inner: Arc>>>, 602 | id: usize, 603 | buffer: *mut [u8], 604 | phantom: PhantomData<&'a [u8]>, 605 | } 606 | 607 | impl<'a> smoltcp::phy::RxToken for RxToken<'a> { 608 | fn consume(self, f: F) -> R 609 | where 610 | F: FnOnce(&[u8]) -> R, 611 | { 612 | let result = f(unsafe { &*self.buffer }); 613 | crate::arch::without_interrupts(|| { 614 | self.inner.lock().recv_end(self.id); 615 | }); 616 | result 617 | } 618 | } 619 | 620 | impl smoltcp::phy::Device for Driver { 621 | type RxToken<'a> 622 | = RxToken<'a> 623 | where 624 | Self: 'a; 625 | type TxToken<'a> 626 | = TxToken 627 | where 628 | Self: 'a; 629 | 630 | fn receive( 631 | &mut self, 632 | _timestamp: smoltcp::time::Instant, 633 | ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 634 | let (id, buffer) = crate::arch::without_interrupts(|| { 635 | let mut e1000 = self.inner.lock(); 636 | e1000.recv() 637 | })?; 638 | Some(( 639 | RxToken { 640 | id, 641 | buffer, 642 | inner: self.inner.clone(), 643 | phantom: PhantomData, 644 | }, 645 | TxToken { 646 | inner: self.inner.clone(), 647 | }, 648 | )) 649 | } 650 | 651 | fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { 652 | Some(TxToken { 653 | inner: self.inner.clone(), 654 | }) 655 | } 656 | 657 | fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { 658 | let mut caps = smoltcp::phy::DeviceCapabilities::default(); 659 | caps.medium = smoltcp::phy::Medium::Ethernet; 660 | caps.max_transmission_unit = 1500; 661 | caps.max_burst_size = Some(RX_DESC_NUM as _); 662 | caps 663 | } 664 | } 665 | 666 | impl crate::net::Driver for Driver { 667 | fn address(&self) -> smoltcp::wire::HardwareAddress { 668 | smoltcp::wire::EthernetAddress(self.inner.lock().address).into() 669 | } 670 | 671 | fn poll(&self, cx: &mut core::task::Context) -> core::task::Poll<()> { 672 | let mut inner = self.inner.lock(); 673 | inner.waker.register(cx.waker()); 674 | if inner.has_recv() { 675 | core::task::Poll::Ready(()) 676 | } else { 677 | core::task::Poll::Pending 678 | } 679 | } 680 | } 681 | 682 | pub fn init(header: &PciDevice) -> Result { 683 | if header.vendor_id != 0x8086 || header.device_id != 0x100e { 684 | return Ok(false); 685 | } 686 | 687 | let e1000 = E1000::new(header)?; 688 | let driver = Driver { 689 | inner: Arc::new(Mutex::new(e1000)), 690 | }; 691 | 692 | crate::net::register(driver); 693 | 694 | Ok(true) 695 | } 696 | -------------------------------------------------------------------------------- /kernel/src/drivers/i8042.rs: -------------------------------------------------------------------------------- 1 | use conquer_once::spin::OnceCell; 2 | use core::{future::poll_fn, task::Poll}; 3 | use crossbeam_queue::ArrayQueue; 4 | use futures::task::AtomicWaker; 5 | use i8042::{Change, DecodedKey, Driver8042, Irq, MouseState}; 6 | use spin::Mutex; 7 | use x86_64::instructions::port::Port; 8 | 9 | #[derive(Debug)] 10 | struct DriverImpl; 11 | 12 | impl i8042::Impl for DriverImpl { 13 | fn read_data(&mut self) -> u8 { 14 | unsafe { Port::new(0x60).read() } 15 | } 16 | 17 | fn write_data(&mut self, data: u8) { 18 | unsafe { Port::new(0x60).write(data) } 19 | } 20 | 21 | fn write_cmd(&mut self, cmd: u8) { 22 | unsafe { Port::new(0x64).write(cmd) } 23 | } 24 | 25 | fn read_status(&mut self) -> u8 { 26 | unsafe { Port::new(0x64).read() } 27 | } 28 | } 29 | 30 | static DRIVER: Mutex> = Mutex::new(Driver8042::new(DriverImpl)); 31 | 32 | static KEY_QUEUE: OnceCell> = OnceCell::uninit(); 33 | static KEY_WAKER: AtomicWaker = AtomicWaker::new(); 34 | 35 | static MOUSE_QUEUE: OnceCell> = OnceCell::uninit(); 36 | static MOUSE_WAKER: AtomicWaker = AtomicWaker::new(); 37 | 38 | pub fn interrupt(irq: Irq) { 39 | if let Some(change) = DRIVER.lock().interrupt(irq) { 40 | match change { 41 | Change::Keyboard(key) => { 42 | if let Ok(queue) = KEY_QUEUE.try_get() { 43 | if queue.push(key).is_ok() { 44 | KEY_WAKER.wake(); 45 | } 46 | } 47 | } 48 | Change::Mouse(state) => { 49 | if let Ok(queue) = MOUSE_QUEUE.try_get() { 50 | if queue.push(state).is_ok() { 51 | MOUSE_WAKER.wake(); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | pub fn init() { 60 | unsafe { 61 | DRIVER.lock().init().unwrap(); 62 | } 63 | 64 | KEY_QUEUE.try_init_once(|| ArrayQueue::new(32)).unwrap(); 65 | MOUSE_QUEUE.try_init_once(|| ArrayQueue::new(32)).unwrap(); 66 | 67 | core::mem::forget(crate::arch::set_interrupt_static( 68 | 1, 69 | crate::arch::InterruptType::LevelLow, 70 | || { 71 | interrupt(i8042::Irq::Irq1); 72 | }, 73 | )); 74 | core::mem::forget(crate::arch::set_interrupt_static( 75 | 12, 76 | crate::arch::InterruptType::LevelLow, 77 | || { 78 | interrupt(i8042::Irq::Irq12); 79 | }, 80 | )); 81 | } 82 | 83 | pub async fn next_key() -> Option { 84 | poll_fn(|cx| { 85 | let Ok(queue) = KEY_QUEUE.try_get() else { 86 | return Poll::Ready(None); 87 | }; 88 | 89 | // fast path 90 | if let Some(key) = queue.pop() { 91 | return Poll::Ready(Some(key)); 92 | } 93 | 94 | KEY_WAKER.register(cx.waker()); 95 | match queue.pop() { 96 | Some(key) => { 97 | KEY_WAKER.take(); 98 | Poll::Ready(Some(key)) 99 | } 100 | None => Poll::Pending, 101 | } 102 | }) 103 | .await 104 | } 105 | 106 | #[allow(unused)] 107 | pub async fn next_mouse_state() -> Option { 108 | poll_fn(|cx| { 109 | let Ok(queue) = MOUSE_QUEUE.try_get() else { 110 | return Poll::Ready(None); 111 | }; 112 | 113 | // fast path 114 | if let Some(state) = queue.pop() { 115 | return Poll::Ready(Some(state)); 116 | } 117 | 118 | MOUSE_WAKER.register(cx.waker()); 119 | match queue.pop() { 120 | Some(state) => { 121 | MOUSE_WAKER.take(); 122 | Poll::Ready(Some(state)) 123 | } 124 | None => Poll::Pending, 125 | } 126 | }) 127 | .await 128 | } 129 | -------------------------------------------------------------------------------- /kernel/src/drivers/keyboard.rs: -------------------------------------------------------------------------------- 1 | pub use super::i8042::next_key; 2 | pub use i8042::{DecodedKey, KeyCode}; 3 | -------------------------------------------------------------------------------- /kernel/src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | mod dma; 2 | mod e1000; 3 | #[cfg(target_arch = "x86_64")] 4 | pub mod i8042; 5 | pub mod keyboard; 6 | mod virtio; 7 | // mod nvme; 8 | 9 | use crate::arch::PciDevice; 10 | 11 | type DriverInit = fn(&PciDevice) -> Result; 12 | 13 | static DRIVER_INITS: &[DriverInit] = &[ 14 | e1000::init, 15 | virtio::net::init, 16 | virtio::sock::init, 17 | // nvme::init, 18 | ]; 19 | 20 | pub fn init() { 21 | #[cfg(target_arch = "x86_64")] 22 | i8042::init(); 23 | 24 | for (address, device) in &*crate::arch::get_pci_devices() { 25 | debug!( 26 | "PCI {address} {:04x}:{:04x} {}", 27 | device.vendor_id, 28 | device.device_id, 29 | device.name() 30 | ); 31 | 32 | for init in DRIVER_INITS { 33 | match init(device) { 34 | Ok(mapped) => { 35 | if mapped { 36 | break; 37 | } 38 | } 39 | Err(e) => { 40 | debug!("Driver initialization failed: {e:?}"); 41 | break; 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /kernel/src/drivers/virtio/mod.rs: -------------------------------------------------------------------------------- 1 | use super::dma::Dma; 2 | use crate::arch::{map_address, translate_phys_addr, translate_virt_addr, PciDevice}; 3 | use virtio_drivers::transport::pci::{ 4 | bus::{Command, DeviceFunction}, 5 | PciTransport, 6 | }; 7 | use x86_64::{PhysAddr, VirtAddr}; 8 | 9 | pub mod net; 10 | pub mod sock; 11 | 12 | struct Hal; 13 | 14 | unsafe impl virtio_drivers::Hal for Hal { 15 | fn dma_alloc( 16 | pages: usize, 17 | _direction: virtio_drivers::BufferDirection, 18 | ) -> (virtio_drivers::PhysAddr, core::ptr::NonNull) { 19 | let dma = Dma::<[u8]>::new_zeroed_slice(pages * 4096, 4096); 20 | let phys = dma.phys_addr(); 21 | let virt = core::ptr::NonNull::new(dma.leak() as _).unwrap(); 22 | (phys, virt) 23 | } 24 | 25 | unsafe fn dma_dealloc( 26 | _paddr: virtio_drivers::PhysAddr, 27 | _vaddr: core::ptr::NonNull, 28 | _pages: usize, 29 | ) -> i32 { 30 | 0 31 | } 32 | 33 | unsafe fn mmio_phys_to_virt( 34 | paddr: virtio_drivers::PhysAddr, 35 | size: usize, 36 | ) -> core::ptr::NonNull { 37 | core::ptr::NonNull::new(map_address(PhysAddr::new(paddr as _), size).as_u64() as _).unwrap() 38 | } 39 | 40 | unsafe fn share( 41 | buffer: core::ptr::NonNull<[u8]>, 42 | _direction: virtio_drivers::BufferDirection, 43 | ) -> virtio_drivers::PhysAddr { 44 | translate_virt_addr(VirtAddr::new(buffer.as_ptr() as *mut u8 as _)) 45 | .unwrap() 46 | .as_u64() as _ 47 | } 48 | 49 | unsafe fn unshare( 50 | _paddr: virtio_drivers::PhysAddr, 51 | _buffer: core::ptr::NonNull<[u8]>, 52 | _direction: virtio_drivers::BufferDirection, 53 | ) { 54 | } 55 | } 56 | 57 | struct ConfigAccess { 58 | segment: u16, 59 | regions: acpi::PciConfigRegions<'static, alloc::alloc::Global>, 60 | } 61 | 62 | impl ConfigAccess { 63 | fn physical_address(&self, bus: u8, device: u8, function: u8, offset: u16) -> u64 { 64 | let addr = self 65 | .regions 66 | .physical_address(self.segment, bus, device, function) 67 | .unwrap(); 68 | addr + (offset as u64) 69 | } 70 | } 71 | 72 | impl virtio_drivers::transport::pci::bus::ConfigurationAccess for ConfigAccess { 73 | fn read_word( 74 | &self, 75 | device_function: virtio_drivers::transport::pci::bus::DeviceFunction, 76 | register_offset: u8, 77 | ) -> u32 { 78 | let addr = self.physical_address( 79 | device_function.bus, 80 | device_function.device, 81 | device_function.function, 82 | register_offset as _, 83 | ); 84 | let addr = translate_phys_addr(PhysAddr::new(addr)).as_u64(); 85 | unsafe { (addr as *mut u32).read_volatile() } 86 | } 87 | 88 | fn write_word( 89 | &mut self, 90 | device_function: virtio_drivers::transport::pci::bus::DeviceFunction, 91 | register_offset: u8, 92 | data: u32, 93 | ) { 94 | let addr = self.physical_address( 95 | device_function.bus, 96 | device_function.device, 97 | device_function.function, 98 | register_offset as _, 99 | ); 100 | let addr = translate_phys_addr(PhysAddr::new(addr)).as_u64(); 101 | unsafe { 102 | (addr as *mut u32).write_volatile(data); 103 | } 104 | } 105 | 106 | unsafe fn unsafe_clone(&self) -> Self { 107 | Self { 108 | segment: self.segment, 109 | regions: crate::arch::get_pci_config_regions(), 110 | } 111 | } 112 | } 113 | 114 | pub fn create_transport(header: &PciDevice) -> Result { 115 | let config_access = ConfigAccess { 116 | segment: header.address.segment(), 117 | regions: crate::arch::get_pci_config_regions(), 118 | }; 119 | 120 | let mut root = virtio_drivers::transport::pci::bus::PciRoot::new(config_access); 121 | 122 | let device = DeviceFunction { 123 | bus: header.address.bus(), 124 | device: header.address.device(), 125 | function: header.address.function(), 126 | }; 127 | 128 | root.set_command( 129 | device, 130 | Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER, 131 | ); 132 | 133 | Ok(PciTransport::new::(&mut root, device)?) 134 | } 135 | -------------------------------------------------------------------------------- /kernel/src/drivers/virtio/net.rs: -------------------------------------------------------------------------------- 1 | use super::{create_transport, Hal}; 2 | use crate::arch::InterruptGuard; 3 | use crate::arch::PciDevice; 4 | use alloc::sync::Arc; 5 | use futures::task::AtomicWaker; 6 | use spin::Mutex; 7 | use virtio_drivers::{ 8 | device::net::{RxBuffer, VirtIONet}, 9 | transport::pci::PciTransport, 10 | Error, 11 | }; 12 | 13 | type Device = VirtIONet; 14 | 15 | struct WrapperInner { 16 | device: Device, 17 | waker: AtomicWaker, 18 | } 19 | 20 | impl WrapperInner { 21 | fn handle_irq(&self) { 22 | self.waker.wake(); 23 | } 24 | } 25 | 26 | struct Wrapper { 27 | inner: Arc>, 28 | _guard: InterruptGuard, 29 | } 30 | 31 | impl Wrapper { 32 | fn new(header: &PciDevice, device: Device) -> Result { 33 | let inner = Arc::new(Mutex::new(WrapperInner { 34 | device, 35 | waker: AtomicWaker::new(), 36 | })); 37 | 38 | let weak = Arc::downgrade(&inner); 39 | 40 | let interrupt_guard = crate::arch::set_interrupt_msi( 41 | header.clone(), 42 | Box::new(move || { 43 | if let Some(w) = weak.upgrade() { 44 | w.lock().handle_irq(); 45 | } 46 | }), 47 | ) 48 | .unwrap(); 49 | 50 | crate::arch::without_interrupts(|| { 51 | let mut w = inner.lock(); 52 | w.device.transport_mut().set_queue_msix_vector(0x00); 53 | w.device.enable_interrupts(); 54 | assert_eq!(w.device.transport().get_queue_msix_vector(), 0x00); 55 | }); 56 | 57 | Ok(Self { 58 | inner, 59 | _guard: interrupt_guard, 60 | }) 61 | } 62 | } 63 | 64 | impl smoltcp::phy::Device for Wrapper { 65 | type RxToken<'a> 66 | = RxToken 67 | where 68 | Self: 'a; 69 | type TxToken<'a> 70 | = TxToken 71 | where 72 | Self: 'a; 73 | 74 | fn receive( 75 | &mut self, 76 | _timestamp: smoltcp::time::Instant, 77 | ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 78 | match crate::arch::without_interrupts(|| self.inner.lock().device.receive()) { 79 | Ok(buf) => Some(( 80 | RxToken(self.inner.clone(), buf), 81 | TxToken(self.inner.clone()), 82 | )), 83 | Err(Error::NotReady) => None, 84 | Err(e) => { 85 | error!("receive failed {e}"); 86 | None 87 | } 88 | } 89 | } 90 | 91 | fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { 92 | Some(TxToken(self.inner.clone())) 93 | } 94 | 95 | fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { 96 | let mut caps = smoltcp::phy::DeviceCapabilities::default(); 97 | caps.max_transmission_unit = 1536; 98 | caps.max_burst_size = Some(1); 99 | caps.medium = smoltcp::phy::Medium::Ethernet; 100 | caps 101 | } 102 | } 103 | 104 | struct RxToken(Arc>, RxBuffer); 105 | 106 | impl smoltcp::phy::RxToken for RxToken { 107 | fn consume(self, f: F) -> R 108 | where 109 | F: FnOnce(&[u8]) -> R, 110 | { 111 | let result = f(self.1.packet()); 112 | crate::arch::without_interrupts(|| { 113 | self.0.lock().device.recycle_rx_buffer(self.1).unwrap(); 114 | }); 115 | result 116 | } 117 | } 118 | 119 | struct TxToken(Arc>); 120 | 121 | impl smoltcp::phy::TxToken for TxToken { 122 | fn consume(self, len: usize, f: F) -> R 123 | where 124 | F: FnOnce(&mut [u8]) -> R, 125 | { 126 | crate::arch::without_interrupts(|| { 127 | let mut w = self.0.lock(); 128 | let mut tx_buf = w.device.new_tx_buffer(len); 129 | let result = f(tx_buf.packet_mut()); 130 | w.device.send(tx_buf).unwrap(); 131 | result 132 | }) 133 | } 134 | } 135 | 136 | impl crate::net::Driver for Wrapper { 137 | fn address(&self) -> smoltcp::wire::HardwareAddress { 138 | smoltcp::wire::HardwareAddress::Ethernet(smoltcp::wire::EthernetAddress( 139 | crate::arch::without_interrupts(|| self.inner.lock().device.mac_address()), 140 | )) 141 | } 142 | 143 | fn poll(&self, cx: &mut core::task::Context) -> core::task::Poll<()> { 144 | crate::arch::without_interrupts(|| { 145 | let w = self.inner.lock(); 146 | w.waker.register(cx.waker()); 147 | if w.device.can_recv() { 148 | core::task::Poll::Ready(()) 149 | } else { 150 | core::task::Poll::Pending 151 | } 152 | }) 153 | } 154 | } 155 | 156 | pub fn init(header: &PciDevice) -> Result { 157 | if header.vendor_id != 0x1af4 || (header.device_id != 0x1000 && header.device_id != 0x1041) { 158 | return Ok(false); 159 | } 160 | 161 | let transport = create_transport(header)?; 162 | 163 | let device = Device::new(transport, 1600)?; 164 | 165 | let wrapper = Wrapper::new(header, device)?; 166 | 167 | crate::net::register(wrapper); 168 | 169 | Ok(true) 170 | } 171 | -------------------------------------------------------------------------------- /kernel/src/drivers/virtio/sock.rs: -------------------------------------------------------------------------------- 1 | use super::{create_transport, Hal}; 2 | use crate::arch::PciDevice; 3 | use virtio_drivers::{device::socket::VirtIOSocket, transport::pci::PciTransport}; 4 | 5 | type Device = VirtIOSocket; 6 | 7 | pub fn init(header: &PciDevice) -> Result { 8 | if header.vendor_id != 0x1af4 || header.device_id != 0x1053 { 9 | return Ok(false); 10 | } 11 | 12 | let transport = create_transport(header)?; 13 | 14 | let _device = Device::new(transport)?; 15 | 16 | Ok(true) 17 | } 18 | -------------------------------------------------------------------------------- /kernel/src/framebuffer.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | use embedded_graphics::{pixelcolor::Rgb888, prelude::*, primitives::Rectangle}; 3 | use limine::FramebufferRequest; 4 | use noto_sans_mono_bitmap::{get_raster, FontWeight, RasterHeight, RasterizedChar}; 5 | use spin::Mutex; 6 | 7 | static FRAMEBUFFER: FramebufferRequest = FramebufferRequest::new(0); 8 | 9 | #[derive(Debug, Clone, Copy)] 10 | pub enum PixelFormat { 11 | Rgb, 12 | Bgr, 13 | U8, 14 | Unknown, 15 | } 16 | 17 | lazy_static::lazy_static! { 18 | pub static ref DISPLAY: Mutex = Mutex::new(Display { 19 | buffer: &mut [], 20 | cache: None, 21 | width: 0, 22 | height: 0, 23 | bytes_per_pixel: 0, 24 | pixel_format: PixelFormat::Bgr, 25 | stride: 0, 26 | x_pos: BORDER_PADDING, 27 | y_pos: BORDER_PADDING, 28 | last_char_width: 0, 29 | }); 30 | } 31 | 32 | pub fn early_init() { 33 | let Some(framebuffer_response) = FRAMEBUFFER.get_response().get() else { 34 | return; 35 | }; 36 | if framebuffer_response.framebuffer_count == 0 { 37 | return; 38 | } 39 | let info = &framebuffer_response.framebuffers()[0]; 40 | 41 | let mut display = DISPLAY.lock(); 42 | display.buffer = 43 | unsafe { core::slice::from_raw_parts_mut(info.address.as_ptr().unwrap(), info.size()) }; 44 | display.width = info.width as _; 45 | display.height = info.height as _; 46 | display.bytes_per_pixel = (info.bpp / 8) as _; 47 | display.stride = (info.pitch / 4) as _; 48 | display.pixel_format = match ( 49 | info.red_mask_shift, 50 | info.green_mask_shift, 51 | info.blue_mask_shift, 52 | ) { 53 | (0x00, 0x08, 0x10) => PixelFormat::Rgb, 54 | (0x10, 0x08, 0x00) => PixelFormat::Bgr, 55 | (0x00, 0x00, 0x00) => PixelFormat::U8, 56 | _ => PixelFormat::Unknown, 57 | }; 58 | 59 | display.clear(); 60 | display.write_rgba(include_bytes!(concat!(env!("OUT_DIR"), "/logo_text.rgba"))); 61 | } 62 | 63 | pub fn late_init() { 64 | let mut display = DISPLAY.lock(); 65 | display.cache = Some(display.buffer.to_owned()); 66 | } 67 | 68 | pub fn logo() { 69 | let mut display = DISPLAY.lock(); 70 | display.clear(); 71 | display.write_rgba(include_bytes!(concat!(env!("OUT_DIR"), "/logo_text.rgba"))); 72 | } 73 | 74 | pub fn print(args: core::fmt::Arguments) { 75 | crate::arch::without_interrupts(|| { 76 | DISPLAY.lock().write_fmt(args).unwrap(); 77 | }); 78 | } 79 | 80 | const LINE_SPACING: usize = 2; 81 | const BORDER_PADDING: usize = 1; 82 | const CHAR_RASTER_HEIGHT: RasterHeight = RasterHeight::Size16; 83 | const FONT_WEIGHT: FontWeight = FontWeight::Regular; 84 | const LINE_HEIGHT: usize = CHAR_RASTER_HEIGHT.val() + LINE_SPACING; 85 | const BACKUP_CHAR: char = '\u{FFFD}'; 86 | 87 | fn get_char_raster(c: char) -> RasterizedChar { 88 | fn get(c: char) -> Option { 89 | get_raster(c, FONT_WEIGHT, CHAR_RASTER_HEIGHT) 90 | } 91 | get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("Should get raster of backup char.")) 92 | } 93 | 94 | pub struct Display { 95 | buffer: &'static mut [u8], 96 | cache: Option>, 97 | width: usize, 98 | height: usize, 99 | stride: usize, 100 | bytes_per_pixel: usize, 101 | pixel_format: PixelFormat, 102 | x_pos: usize, 103 | y_pos: usize, 104 | last_char_width: usize, 105 | } 106 | 107 | unsafe impl Send for Display {} 108 | 109 | impl Display { 110 | fn newline(&mut self) { 111 | self.y_pos += LINE_HEIGHT; 112 | self.carriage_return(); 113 | 114 | if self.y_pos + LINE_HEIGHT >= self.height { 115 | self.scroll(); 116 | self.y_pos -= LINE_HEIGHT; 117 | } 118 | } 119 | 120 | fn carriage_return(&mut self) { 121 | self.x_pos = BORDER_PADDING; 122 | } 123 | 124 | fn clear(&mut self) { 125 | self.x_pos = BORDER_PADDING; 126 | self.y_pos = BORDER_PADDING; 127 | self.buffer.fill(0); 128 | if let Some(cache) = &mut self.cache { 129 | cache.fill(0); 130 | } 131 | } 132 | 133 | fn scroll(&mut self) { 134 | let bytes_per_pixel = self.bytes_per_pixel; 135 | 136 | let next_line_offset = (self.stride * LINE_HEIGHT) * bytes_per_pixel; 137 | 138 | let pixel_height = self.height - LINE_HEIGHT; 139 | let pixels = self.stride * pixel_height; 140 | let bytes = pixels * bytes_per_pixel; 141 | 142 | if let Some(cache) = &mut self.cache { 143 | cache.copy_within(next_line_offset.., 0); 144 | cache[(bytes + 1)..].fill(0); 145 | self.buffer.copy_from_slice(cache); 146 | } else { 147 | self.buffer.copy_within(next_line_offset.., 0); 148 | self.buffer[(bytes + 1)..].fill(0); 149 | } 150 | } 151 | 152 | fn write_pixel(&mut self, x: usize, y: usize, mut r: u8, mut g: u8, mut b: u8, a: u8) { 153 | if a == 0 { 154 | return; 155 | } 156 | 157 | let pixel_offset = y * self.stride + x; 158 | let bytes_per_pixel = self.bytes_per_pixel; 159 | let byte_offset = pixel_offset * bytes_per_pixel; 160 | 161 | if a != 255 { 162 | let bytes = if let Some(cache) = &mut self.cache { 163 | &cache[byte_offset..(byte_offset + bytes_per_pixel)] 164 | } else { 165 | &self.buffer[byte_offset..(byte_offset + bytes_per_pixel)] 166 | }; 167 | let (r0, g0, b0) = match self.pixel_format { 168 | PixelFormat::Rgb => (bytes[0], bytes[1], bytes[2]), 169 | PixelFormat::Bgr => (bytes[2], bytes[1], bytes[0]), 170 | PixelFormat::U8 => (bytes[0], bytes[0], bytes[0]), 171 | other => { 172 | self.pixel_format = PixelFormat::Rgb; 173 | panic!("pixel format {:?} not supported in logger", other) 174 | } 175 | }; 176 | 177 | let blend = |old: u8, new: u8| { 178 | let alpha = a as f64 / 255.0; 179 | let x = alpha * (new as f64); 180 | let z = (1.0 - alpha) * (old as f64); 181 | (x + z) as u8 182 | }; 183 | 184 | r = blend(r0, r); 185 | g = blend(g0, g); 186 | b = blend(b0, b); 187 | } 188 | 189 | let color = match self.pixel_format { 190 | PixelFormat::Rgb => [r, g, b, 0], 191 | PixelFormat::Bgr => [b, g, r, 0], 192 | PixelFormat::U8 => { 193 | let lum = 0.2126 * (r as f64) + 0.7125 * (g as f64) + 0.722 * (b as f64); 194 | [if lum > 200.0 { 0xf } else { 0 }, 0, 0, 0] 195 | } 196 | other => { 197 | self.pixel_format = PixelFormat::Rgb; 198 | panic!("pixel format {:?} not supported in logger", other) 199 | } 200 | }; 201 | 202 | self.buffer[byte_offset..(byte_offset + bytes_per_pixel)] 203 | .copy_from_slice(&color[..bytes_per_pixel]); 204 | if let Some(cache) = &mut self.cache { 205 | cache[byte_offset..(byte_offset + bytes_per_pixel)] 206 | .copy_from_slice(&color[..bytes_per_pixel]); 207 | } 208 | } 209 | 210 | pub fn write_char(&mut self, c: char) { 211 | match c { 212 | '\n' => self.newline(), 213 | '\r' => self.carriage_return(), 214 | '\u{0008}' => { 215 | if self.x_pos > BORDER_PADDING { 216 | self.x_pos -= self.last_char_width; 217 | for y in 0..LINE_HEIGHT { 218 | for x in 0..self.last_char_width { 219 | self.write_pixel(self.x_pos + x, self.y_pos + y, 0, 0, 0, 255); 220 | } 221 | } 222 | } 223 | } 224 | c => { 225 | let rendered_char = get_char_raster(c); 226 | let width = rendered_char.width(); 227 | 228 | let new_xpos = self.x_pos + width; 229 | if new_xpos >= self.width { 230 | self.newline(); 231 | } 232 | 233 | for (y, row) in rendered_char.raster().iter().enumerate() { 234 | for (x, byte) in row.iter().enumerate() { 235 | let r = *byte; 236 | let g = *byte; 237 | let b = *byte; 238 | let a = 255; 239 | self.write_pixel(self.x_pos + x, self.y_pos + y, r, g, b, a); 240 | } 241 | } 242 | 243 | self.last_char_width = width; 244 | self.x_pos += width; 245 | } 246 | } 247 | } 248 | 249 | pub fn write_str(&mut self, s: &str) { 250 | for c in s.chars() { 251 | self.write_char(c); 252 | } 253 | } 254 | 255 | fn write_rgba(&mut self, bytes: &[u8]) { 256 | let mut image = bytes.iter(); 257 | 258 | let width = u32::from_le_bytes([(); 4].map(|_| *image.next().unwrap())); 259 | let height = u32::from_le_bytes([(); 4].map(|_| *image.next().unwrap())); 260 | 261 | 'outer: loop { 262 | let count = u16::from_le_bytes([(); 2].map(|_| *image.next().unwrap())); 263 | let r = *image.next().unwrap(); 264 | let g = *image.next().unwrap(); 265 | let b = *image.next().unwrap(); 266 | let a = *image.next().unwrap(); 267 | 268 | for _ in 0..count { 269 | self.write_pixel(self.x_pos, self.y_pos, r, g, b, a); 270 | 271 | self.x_pos += 1; 272 | 273 | if (self.x_pos - BORDER_PADDING) >= width as usize { 274 | self.x_pos = BORDER_PADDING; 275 | self.y_pos += 1; 276 | } 277 | 278 | if self.y_pos >= height as usize { 279 | break 'outer; 280 | } 281 | } 282 | } 283 | 284 | self.x_pos = BORDER_PADDING; 285 | self.y_pos += LINE_SPACING; 286 | } 287 | } 288 | 289 | impl Write for Display { 290 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 291 | Display::write_str(self, s); 292 | Ok(()) 293 | } 294 | } 295 | 296 | impl Dimensions for Display { 297 | fn bounding_box(&self) -> Rectangle { 298 | Rectangle { 299 | top_left: Point { x: 0, y: 0 }, 300 | size: Size { 301 | width: self.width as _, 302 | height: self.height as _, 303 | }, 304 | } 305 | } 306 | } 307 | 308 | impl DrawTarget for Display { 309 | type Color = Rgb888; 310 | type Error = (); 311 | 312 | fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> 313 | where 314 | I: IntoIterator>, 315 | { 316 | for pixel in pixels { 317 | self.write_pixel( 318 | pixel.0.x as _, 319 | pixel.0.y as _, 320 | pixel.1.r(), 321 | pixel.1.g(), 322 | pixel.1.b(), 323 | 255, 324 | ); 325 | } 326 | Ok(()) 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /kernel/src/local.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::btree_map::Entry; 2 | use core::sync::atomic::{AtomicUsize, Ordering}; 3 | use spin::Lazy; 4 | 5 | pub struct Local { 6 | id: Lazy, 7 | initializer: fn() -> T, 8 | } 9 | 10 | impl Local { 11 | pub const fn new(initializer: fn() -> T) -> Self { 12 | Self { 13 | id: Lazy::new(Self::next_id), 14 | initializer, 15 | } 16 | } 17 | 18 | fn next_id() -> usize { 19 | static ID: AtomicUsize = AtomicUsize::new(0); 20 | ID.fetch_add(1, Ordering::Relaxed) 21 | } 22 | 23 | pub fn with(&self, f: F) -> U 24 | where 25 | F: FnOnce(&T) -> U, 26 | { 27 | let local = crate::arch::LocalData::get().expect("failed to load LocalData"); 28 | let ptr = match local.data.lock().entry(*self.id) { 29 | Entry::Occupied(e) => *e.get(), 30 | Entry::Vacant(v) => { 31 | let ptr = Box::into_raw(Box::new((self.initializer)())) as *mut (); 32 | v.insert(ptr); 33 | ptr 34 | } 35 | }; 36 | let data = unsafe { &*(ptr as *const T) }; 37 | f(data) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![allow(internal_features)] 4 | #![feature(abi_x86_interrupt)] 5 | #![feature(alloc_error_handler)] 6 | #![feature(prelude_import)] 7 | #![feature(naked_functions)] 8 | #![feature(never_type)] 9 | #![feature(allocator_api)] 10 | #![feature(ptr_metadata)] 11 | #![feature(slice_ptr_get)] 12 | #![feature(panic_can_unwind)] 13 | #![feature(duration_millis_float)] 14 | 15 | extern crate alloc; 16 | 17 | #[macro_use] 18 | extern crate tracing; 19 | 20 | mod prelude { 21 | #![allow(unused)] 22 | pub use alloc::{ 23 | borrow::ToOwned, 24 | boxed::Box, 25 | format, 26 | string::{String, ToString}, 27 | vec, 28 | vec::Vec, 29 | }; 30 | pub use core::arch::{asm, global_asm}; 31 | pub use core::prelude::v1::*; 32 | } 33 | 34 | #[prelude_import] 35 | #[allow(unused_imports)] 36 | use prelude::*; 37 | 38 | mod allocator; 39 | mod arch; 40 | mod debug; 41 | mod drivers; 42 | mod framebuffer; 43 | mod local; 44 | mod net; 45 | mod panic; 46 | mod shell; 47 | mod stack_allocator; 48 | mod task; 49 | 50 | #[no_mangle] 51 | unsafe extern "C" fn _start() -> ! { 52 | crate::panic::catch_unwind(start); 53 | } 54 | 55 | fn start() -> ! { 56 | debug::init(); 57 | 58 | framebuffer::early_init(); 59 | 60 | debug::set_print(framebuffer::print); 61 | 62 | arch::init(); 63 | 64 | shell::start(); 65 | 66 | drivers::init(); 67 | 68 | ap_main(0); 69 | } 70 | 71 | pub fn ap_main(ap_id: u8) -> ! { 72 | task::start(ap_id); 73 | arch::halt_loop(); 74 | } 75 | -------------------------------------------------------------------------------- /kernel/src/panic.rs: -------------------------------------------------------------------------------- 1 | use crate::local::Local; 2 | use core::{ 3 | any::Any, 4 | panic::PanicInfo, 5 | sync::atomic::{AtomicBool, AtomicUsize, Ordering}, 6 | }; 7 | use limine::{KernelAddressRequest, KernelFileRequest}; 8 | use rustc_demangle::demangle; 9 | use unwinding::abi::{UnwindContext, UnwindReasonCode, _Unwind_Backtrace, _Unwind_GetIP}; 10 | use xmas_elf::{ 11 | program::Type as ProgramType, 12 | sections::{SectionData, ShType}, 13 | symbol_table::Entry, 14 | ElfFile, 15 | }; 16 | 17 | static KERNEL_FILE: KernelFileRequest = KernelFileRequest::new(0); 18 | static KERNEL_ADDRESS: KernelAddressRequest = KernelAddressRequest::new(0); 19 | static PANIC_COUNT: Local = Local::new(|| AtomicUsize::new(0)); 20 | static mut KERNEL_SLICE: &[u8] = &[]; 21 | static KERNEL_BASE: AtomicUsize = AtomicUsize::new(0); 22 | static FULL: AtomicBool = AtomicBool::new(false); 23 | 24 | pub fn init() { 25 | let kernel_file = KERNEL_FILE 26 | .get_response() 27 | .get() 28 | .unwrap() 29 | .kernel_file 30 | .get() 31 | .unwrap(); 32 | 33 | let kernel_file_base = kernel_file.base.as_ptr().unwrap(); 34 | 35 | unsafe { 36 | KERNEL_SLICE = core::slice::from_raw_parts(kernel_file_base as _, kernel_file.length as _); 37 | } 38 | 39 | let kernel_address = KERNEL_ADDRESS.get_response().get().unwrap(); 40 | KERNEL_BASE.store(kernel_address.virtual_base as _, Ordering::Relaxed); 41 | 42 | debug!("[PANIC] init (kbase={:x})", kernel_address.virtual_base); 43 | } 44 | 45 | pub fn late_init() { 46 | FULL.store(true, Ordering::Relaxed); 47 | } 48 | 49 | struct PanicData { 50 | info: String, 51 | stack_frames: Vec, 52 | } 53 | 54 | impl Drop for PanicData { 55 | fn drop(&mut self) { 56 | PANIC_COUNT.with(|c| { 57 | c.fetch_sub(1, Ordering::SeqCst); 58 | }); 59 | } 60 | } 61 | 62 | #[panic_handler] 63 | fn panic(info: &PanicInfo) -> ! { 64 | if !FULL.load(Ordering::Relaxed) { 65 | error!("PANIC DURING INIT {}", info); 66 | stack_trace(16, |frame| { 67 | print_stack_frames(&[frame]); 68 | }); 69 | crate::arch::halt_loop(); 70 | } 71 | 72 | if PANIC_COUNT.with(|c| c.fetch_add(1, Ordering::SeqCst) > 0) { 73 | error!("PANIC IN PANIC: {}", info); 74 | crate::arch::halt_loop(); 75 | } 76 | 77 | let mut stack_frames = Vec::new(); 78 | stack_trace(16, |frame| { 79 | stack_frames.push(frame); 80 | }); 81 | 82 | if !info.can_unwind() { 83 | error!("UNHANDLED (no-unwind) {}", info); 84 | print_stack_frames(&stack_frames); 85 | crate::arch::halt_loop(); 86 | } 87 | 88 | let code = unwinding::panic::begin_panic(Box::new(PanicData { 89 | info: format!("{}", info), 90 | stack_frames: stack_frames.clone(), 91 | })); 92 | error!( 93 | "UNHANDLED ({}) {}", 94 | match code { 95 | UnwindReasonCode::NO_REASON => "no reason", 96 | UnwindReasonCode::FOREIGN_EXCEPTION_CAUGHT => "foreign exception caught", 97 | UnwindReasonCode::FATAL_PHASE1_ERROR => "fatal phase 1 error", 98 | UnwindReasonCode::FATAL_PHASE2_ERROR => "fatal phase 2 error", 99 | UnwindReasonCode::NORMAL_STOP => "normal stop", 100 | UnwindReasonCode::END_OF_STACK => "end of stack", 101 | UnwindReasonCode::HANDLER_FOUND => "handler found", 102 | UnwindReasonCode::INSTALL_CONTEXT => "install context", 103 | UnwindReasonCode::CONTINUE_UNWIND => "continue unwind", 104 | _ => "??", 105 | }, 106 | info 107 | ); 108 | print_stack_frames(&stack_frames); 109 | crate::arch::halt_loop(); 110 | } 111 | 112 | fn print_stack_frames(stack_frames: &[StackFrame]) { 113 | for frame in stack_frames { 114 | if let Some((f_addr, f_name)) = frame.symbol { 115 | error!( 116 | " 0x{:016x} {:#}+0x{:x}", 117 | frame.address, 118 | demangle(f_name), 119 | frame.address - f_addr 120 | ); 121 | } else { 122 | error!(" 0x{:016x}", frame.address); 123 | } 124 | error!( 125 | " at {}:{}:{}", 126 | frame.file.as_deref().unwrap_or_default(), 127 | frame.line.unwrap_or_default(), 128 | frame.column.unwrap_or_default() 129 | ); 130 | } 131 | } 132 | 133 | pub fn inspect(e: &Box) { 134 | if let Some(data) = e.downcast_ref::() { 135 | error!("{}", data.info); 136 | print_stack_frames(&data.stack_frames); 137 | } else if let Some(data) = e.downcast_ref::<&'static str>() { 138 | error!("{data}"); 139 | } else if let Some(data) = e.downcast_ref::() { 140 | error!("{data}"); 141 | } else { 142 | error!("external panic, halting"); 143 | } 144 | } 145 | 146 | pub fn catch_unwind(f: F) -> T 147 | where 148 | F: FnOnce() -> T, 149 | { 150 | match unwinding::panic::catch_unwind(f) { 151 | Ok(v) => v, 152 | Err(e) => { 153 | inspect(&e); 154 | crate::arch::halt_loop(); 155 | } 156 | } 157 | } 158 | 159 | #[derive(Debug, Clone)] 160 | pub struct StackFrame { 161 | pub address: usize, 162 | pub symbol: Option<(usize, &'static str)>, 163 | pub file: Option, 164 | pub line: Option, 165 | pub column: Option, 166 | } 167 | 168 | pub fn stack_trace(limit: usize, mut f: F) 169 | where 170 | F: FnMut(StackFrame), 171 | { 172 | let Ok(file) = ElfFile::new(unsafe { KERNEL_SLICE }) else { 173 | return; 174 | }; 175 | 176 | let sections = addr2line::gimli::DwarfSections::load(|id| { 177 | Ok::<_, !>(match file.find_section_by_name(id.name()) { 178 | Some(data) => data.raw_data(&file), 179 | None => &[], 180 | }) 181 | }) 182 | .unwrap(); 183 | let dwarf = sections.borrow(|section| { 184 | addr2line::gimli::EndianSlice::new(section, addr2line::gimli::RunTimeEndian::Little) 185 | }); 186 | 187 | let context = addr2line::Context::from_dwarf(dwarf).ok(); 188 | 189 | let symbols = file.section_iter().find_map(|s| { 190 | if s.get_type() != Ok(ShType::SymTab) { 191 | return None; 192 | } 193 | let Ok(data) = s.get_data(&file) else { 194 | return None; 195 | }; 196 | if let SectionData::SymbolTable64(table) = data { 197 | return Some(table); 198 | } 199 | None 200 | }); 201 | 202 | let link_base = file 203 | .program_iter() 204 | .find_map(|h| { 205 | if h.get_type() != Ok(ProgramType::Load) { 206 | return None; 207 | } 208 | if !h.flags().is_execute() { 209 | return None; 210 | } 211 | Some(h.virtual_addr() as usize) 212 | }) 213 | .unwrap_or(0xffffffff80000000); 214 | let load_base = KERNEL_BASE.load(Ordering::SeqCst); 215 | let offset = load_base - link_base; 216 | 217 | #[derive(Default)] 218 | struct DebugInfo<'a> { 219 | symbol: Option<(usize, &'static str)>, 220 | file: Option<&'a str>, 221 | line: Option, 222 | column: Option, 223 | } 224 | 225 | let get_symbol = |addr: usize| -> DebugInfo { 226 | let mut info = DebugInfo::default(); 227 | let mapped = addr.saturating_sub(offset); 228 | if mapped == 0 { 229 | return info; 230 | } 231 | if let Some(location) = context 232 | .as_ref() 233 | .and_then(|c| c.find_location(mapped as _).ok()) 234 | .flatten() 235 | { 236 | info.file = location.file; 237 | info.line = location.line; 238 | info.column = location.column; 239 | } 240 | if let Some(table) = &symbols { 241 | for symbol in table.iter() { 242 | let start = symbol.value() as usize + offset; 243 | let end = start + symbol.size() as usize; 244 | if addr >= start && addr < end { 245 | if let Ok(r) = symbol.get_name(&file) { 246 | info.symbol = Some((start, r)); 247 | } 248 | break; 249 | } 250 | } 251 | } 252 | info 253 | }; 254 | 255 | struct Data<'a> { 256 | limit: usize, 257 | f: &'a mut dyn FnMut(StackFrame), 258 | get_symbol: &'a dyn Fn(usize) -> DebugInfo<'a>, 259 | } 260 | 261 | extern "C" fn callback( 262 | unwind_ctx: &UnwindContext<'_>, 263 | arg: *mut core::ffi::c_void, 264 | ) -> UnwindReasonCode { 265 | let data = unsafe { &mut *(arg as *mut Data) }; 266 | let address = _Unwind_GetIP(unwind_ctx); 267 | let info = (data.get_symbol)(address); 268 | 269 | if data.limit > 0 { 270 | (data.f)(StackFrame { 271 | address, 272 | symbol: info.symbol, 273 | file: info.file.map(|s| s.to_owned()), 274 | line: info.line, 275 | column: info.column, 276 | }); 277 | data.limit -= 1; 278 | } 279 | 280 | UnwindReasonCode::NO_REASON 281 | } 282 | 283 | let mut data = Data { 284 | limit, 285 | f: &mut f, 286 | get_symbol: &get_symbol, 287 | }; 288 | 289 | _Unwind_Backtrace(callback, core::ptr::addr_of_mut!(data) as _); 290 | } 291 | -------------------------------------------------------------------------------- /kernel/src/shell.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | drivers::keyboard::{next_key, DecodedKey, KeyCode}, 3 | framebuffer::DISPLAY, 4 | }; 5 | use core::{fmt::Write, pin::Pin, str::FromStr, time::Duration}; 6 | use futures::FutureExt; 7 | use hashbrown::HashMap; 8 | use spin::Mutex; 9 | 10 | #[derive(Debug)] 11 | struct Args { 12 | args: Vec, 13 | } 14 | 15 | impl Args { 16 | fn write_str(&self, s: &str) { 17 | DISPLAY.lock().write_str(s); 18 | } 19 | 20 | fn write_fmt(&self, args: core::fmt::Arguments) { 21 | let _ = DISPLAY.lock().write_fmt(args); 22 | } 23 | } 24 | 25 | type CmdRet = Result<(), Box>; 26 | type Command = 27 | Box Pin + Send>>) + Send>; 28 | 29 | lazy_static::lazy_static! { 30 | static ref DEBUG: Mutex> = Mutex::new(Vec::new()); 31 | } 32 | 33 | fn print(args: core::fmt::Arguments) { 34 | struct W<'a>(&'a mut Vec); 35 | impl<'a> core::fmt::Write for W<'a> { 36 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 37 | self.0.extend_from_slice(s.as_bytes()); 38 | Ok(()) 39 | } 40 | } 41 | 42 | let mut debug = DEBUG.lock(); 43 | let _ = W(&mut debug).write_fmt(args); 44 | } 45 | 46 | async fn shell() { 47 | crate::framebuffer::logo(); 48 | 49 | let mut commands = HashMap::<&'static str, Command>::new(); 50 | macro_rules! reg { 51 | ($name:ident) => { 52 | commands.insert( 53 | stringify!($name), 54 | Box::new(|a| Box::pin(commands::$name(a))), 55 | ); 56 | }; 57 | } 58 | 59 | reg!(panic); 60 | reg!(timing); 61 | reg!(ifconfig); 62 | reg!(dig); 63 | reg!(http); 64 | reg!(ping); 65 | reg!(shutdown); 66 | reg!(reboot); 67 | reg!(logs); 68 | reg!(logo); 69 | reg!(lspci); 70 | // reg!(wasm); 71 | 72 | let command_names = commands.keys().map(|s| s.to_owned()).collect::>(); 73 | commands.insert( 74 | "help", 75 | Box::new(move |args| { 76 | let command_names = command_names.clone(); 77 | Box::pin(async move { 78 | args.write_fmt(format_args!( 79 | "Available commands:\n{}\n", 80 | command_names.join("\n") 81 | )); 82 | Ok(()) 83 | }) 84 | }), 85 | ); 86 | 87 | let mut line = String::new(); 88 | DISPLAY.lock().write_str("> "); 89 | while let Some(key) = next_key().await { 90 | match key { 91 | DecodedKey::Unicode('\r') | DecodedKey::Unicode('\n') => { 92 | DISPLAY.lock().write_char('\n'); 93 | 94 | let mut args = line.split(" "); 95 | if let Some(cmd) = args.next() { 96 | match commands.get(cmd) { 97 | Some(f) => { 98 | let args = args.map(|s| s.to_owned()).collect(); 99 | let args = Args { args }; 100 | 101 | let mut cmd_fut = Fuse { 102 | inner: Some(crate::task::spawn(f(args))), 103 | }; 104 | let mut key_fut = Box::pin( 105 | async { 106 | while let Some(key) = next_key().await { 107 | if key == DecodedKey::Unicode('\u{0003}') { 108 | break; 109 | } 110 | } 111 | } 112 | .fuse(), 113 | ); 114 | 115 | futures::select_biased! { 116 | r = cmd_fut => { 117 | if let Err(e) = r { 118 | let _ = DISPLAY.lock().write_fmt(format_args!("{e:?}\n")); 119 | } 120 | } 121 | _ = key_fut => { 122 | if let Some(handle) = cmd_fut.inner { 123 | handle.cancel(); 124 | } 125 | DISPLAY.lock().write_str("Cancelled task\n"); 126 | } 127 | }; 128 | } 129 | _ => { 130 | DISPLAY.lock().write_str("unknown command\n"); 131 | } 132 | } 133 | }; 134 | 135 | line.clear(); 136 | DISPLAY.lock().write_str("> "); 137 | } 138 | DecodedKey::RawKey(KeyCode::Backspace) 139 | | DecodedKey::RawKey(KeyCode::Delete) 140 | | DecodedKey::Unicode('\u{0008}') => { 141 | line.pop(); 142 | DISPLAY.lock().write_char('\u{0008}'); 143 | } 144 | DecodedKey::Unicode(c) => { 145 | line.push(c); 146 | DISPLAY.lock().write_char(c); 147 | } 148 | DecodedKey::RawKey(_key) => {} 149 | } 150 | } 151 | } 152 | 153 | pub fn start() { 154 | crate::debug::set_print(print); 155 | crate::task::spawn(shell()); 156 | } 157 | 158 | mod commands { 159 | use super::*; 160 | 161 | pub async fn panic(_: Args) -> CmdRet { 162 | panic!("a panic"); 163 | } 164 | 165 | pub async fn timing(args: Args) -> CmdRet { 166 | let n = args.args[0].parse::().unwrap(); 167 | for _ in 0..n { 168 | let uptime = crate::arch::now(); 169 | let unix = crate::arch::timestamp(); 170 | args.write_fmt(format_args!("{uptime:?} {unix:?}\n")); 171 | maitake::time::sleep(Duration::from_secs(1)).await; 172 | } 173 | Ok(()) 174 | } 175 | 176 | pub async fn ifconfig(args: Args) -> CmdRet { 177 | let Some(conf) = crate::net::config() else { 178 | args.write_str("No interface\n"); 179 | return Ok(()); 180 | }; 181 | if let Some(mac) = conf.mac { 182 | args.write_fmt(format_args!( 183 | "ether {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}\n", 184 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] 185 | )); 186 | } 187 | for addr in &conf.ip_addrs { 188 | args.write_fmt(format_args!("inet {addr}\n")); 189 | } 190 | for addr in &conf.dns_servers { 191 | args.write_fmt(format_args!("dns {addr}\n")); 192 | } 193 | for route in &conf.routes { 194 | args.write_fmt(format_args!( 195 | "route {} via {}\n", 196 | route.cidr, route.via_router 197 | )); 198 | } 199 | Ok(()) 200 | } 201 | 202 | pub async fn dig(args: Args) -> CmdRet { 203 | use crate::net::{DnsQueryType, DnsSocket}; 204 | 205 | let host = args.args[1].clone(); 206 | let typ = match args.args[0].to_lowercase().as_str() { 207 | "a" => DnsQueryType::A, 208 | "aaaa" => DnsQueryType::Aaaa, 209 | "cname" => DnsQueryType::Cname, 210 | "ns" => DnsQueryType::Ns, 211 | "soa" => DnsQueryType::Soa, 212 | _ => return Ok(()), 213 | }; 214 | let dns = DnsSocket::new()?; 215 | let results = 216 | match maitake::time::timeout(Duration::from_millis(500), dns.query(&host, typ)).await { 217 | Ok(v) => v?, 218 | Err(_) => { 219 | args.write_str("DNS query timed out\n"); 220 | return Ok(()); 221 | } 222 | }; 223 | for item in results { 224 | args.write_fmt(format_args!("{item}\n")); 225 | } 226 | 227 | Ok(()) 228 | } 229 | 230 | async fn get(url: &str) -> Result>, anyhow::Error> { 231 | use crate::net::{DnsQueryType, DnsSocket, TcpSocket}; 232 | 233 | let url = url::Url::parse(url)?; 234 | 235 | let ip = match url.host() { 236 | Some(url::Host::Domain(name)) => { 237 | let dns = DnsSocket::new()?; 238 | let results = match maitake::time::timeout( 239 | Duration::from_millis(3000), 240 | dns.query(name, DnsQueryType::A), 241 | ) 242 | .await 243 | { 244 | Ok(v) => v?, 245 | Err(_) => { 246 | return Err(anyhow::anyhow!("DNS lookup timed out")); 247 | } 248 | }; 249 | results[0] 250 | } 251 | Some(url::Host::Ipv4(ip)) => core::net::IpAddr::V4(ip), 252 | Some(url::Host::Ipv6(ip)) => core::net::IpAddr::V6(ip), 253 | _ => panic!(), 254 | }; 255 | 256 | let sock = TcpSocket::new(); 257 | sock.set_timeout(Some(core::time::Duration::from_millis(3000))); 258 | sock.connect((ip, 80)).await?; 259 | 260 | let payload = format!( 261 | "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", 262 | url.path(), 263 | url.host_str().unwrap() 264 | ); 265 | sock.write(payload.as_bytes()).await?; 266 | 267 | let mut body = vec![]; 268 | let mut data = [0; 512]; 269 | loop { 270 | let n = sock.read(&mut data).await?; 271 | if n == 0 { 272 | break; 273 | } 274 | body.extend(&data[..n]); 275 | } 276 | 277 | let mut headers = [httparse::EMPTY_HEADER; 64]; 278 | let mut res = httparse::Response::new(&mut headers); 279 | let n = res.parse(&body).unwrap().unwrap(); 280 | let body = body[n..].to_vec(); 281 | 282 | let mut res = http::Response::builder() 283 | .status(res.code.unwrap()) 284 | .body(body) 285 | .unwrap(); 286 | for h in headers { 287 | let Ok(name) = http::HeaderName::from_str(h.name) else { 288 | continue; 289 | }; 290 | let Ok(value) = http::HeaderValue::from_bytes(h.value) else { 291 | continue; 292 | }; 293 | res.headers_mut().insert(name, value); 294 | } 295 | 296 | Ok(res) 297 | } 298 | 299 | pub async fn http(args: Args) -> CmdRet { 300 | match args.args[0].as_str() { 301 | "serve" => { 302 | //crate::net::serve_task(); 303 | } 304 | "get" => match get(&args.args[1]).await { 305 | Ok(res) => { 306 | let (_, body) = res.into_parts(); 307 | let s = String::from_utf8_lossy(&body); 308 | args.write_fmt(format_args!("{s}")); 309 | } 310 | Err(e) => { 311 | args.write_fmt(format_args!("Error: {e}")); 312 | } 313 | }, 314 | _ => {} 315 | } 316 | 317 | Ok(()) 318 | } 319 | 320 | /* 321 | pub async fn wasm(args: Args) -> CmdRet { 322 | let res = get(&args.args[1]).await?; 323 | let (_, body) = res.into_parts(); 324 | crate::wasm::run(body); 325 | Ok(()) 326 | } 327 | */ 328 | 329 | pub async fn ping(args: Args) -> CmdRet { 330 | use crate::net::{DnsQueryType, DnsSocket}; 331 | let ip = if let Ok(ip) = args.args[0].parse() { 332 | ip 333 | } else { 334 | let dns = DnsSocket::new()?; 335 | let results = match maitake::time::timeout( 336 | Duration::from_millis(3000), 337 | dns.query(&args.args[0], DnsQueryType::A), 338 | ) 339 | .await 340 | { 341 | Ok(v) => v?, 342 | Err(_) => { 343 | args.write_str("DNS lookup timed out\n"); 344 | return Ok(()); 345 | } 346 | }; 347 | results[0] 348 | }; 349 | let rx = crate::net::ping(ip).await?; 350 | while let Ok((src, bytes, seq, time)) = rx.recv().await { 351 | let time = time.as_millis_f64(); 352 | args.write_fmt(format_args!( 353 | "{bytes} bytes from {src} icmp_seq={seq} time={time}ms\n" 354 | )); 355 | } 356 | Ok(()) 357 | } 358 | 359 | pub async fn shutdown(_: Args) -> CmdRet { 360 | crate::arch::shutdown(); 361 | 362 | Ok(()) 363 | } 364 | 365 | pub async fn reboot(_: Args) -> CmdRet { 366 | crate::arch::reboot(); 367 | 368 | Ok(()) 369 | } 370 | 371 | pub async fn logs(args: Args) -> CmdRet { 372 | let debug = DEBUG.lock(); 373 | let debug = if let Some(mut lines) = args.args.first().and_then(|s| s.parse::().ok()) { 374 | lines += 1; 375 | let index = debug.iter().rposition(|v| { 376 | if *v == b'\n' { 377 | lines -= 1; 378 | lines == 0 379 | } else { 380 | false 381 | } 382 | }); 383 | if let Some(index) = index { 384 | &debug[index..debug.len()] 385 | } else { 386 | &debug[..] 387 | } 388 | } else { 389 | &debug[..] 390 | }; 391 | let debug = unsafe { core::str::from_utf8_unchecked(debug) }; 392 | args.write_fmt(format_args!("{debug}\n")); 393 | 394 | Ok(()) 395 | } 396 | 397 | pub async fn logo(_: Args) -> CmdRet { 398 | crate::framebuffer::logo(); 399 | Ok(()) 400 | } 401 | 402 | pub async fn lspci(args: Args) -> CmdRet { 403 | for (address, device) in &*crate::arch::get_pci_devices() { 404 | args.write_fmt(format_args!( 405 | "PCI {address} {:04x}:{:04x} {}\n", 406 | device.vendor_id, 407 | device.device_id, 408 | device.name() 409 | )); 410 | } 411 | Ok(()) 412 | } 413 | } 414 | 415 | #[pin_project::pin_project] 416 | #[derive(Debug)] 417 | #[must_use = "futures do nothing unless you `.await` or poll them"] 418 | pub struct Fuse { 419 | #[pin] 420 | inner: Option, 421 | } 422 | 423 | impl futures::future::FusedFuture for Fuse { 424 | fn is_terminated(&self) -> bool { 425 | self.inner.is_none() 426 | } 427 | } 428 | 429 | impl core::future::Future for Fuse { 430 | type Output = Fut::Output; 431 | 432 | fn poll( 433 | mut self: Pin<&mut Self>, 434 | cx: &mut core::task::Context<'_>, 435 | ) -> core::task::Poll { 436 | match self.as_mut().project().inner.as_pin_mut() { 437 | Some(fut) => fut.poll(cx).map(|output| { 438 | self.project().inner.set(None); 439 | output 440 | }), 441 | None => core::task::Poll::Pending, 442 | } 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /kernel/src/stack_allocator.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | alloc::{AllocError, Allocator, Layout}, 3 | ptr::NonNull, 4 | }; 5 | use spin::Mutex; 6 | 7 | pub struct StackAllocator { 8 | inner: Mutex>, 9 | } 10 | 11 | impl core::fmt::Debug for StackAllocator { 12 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 13 | write!(f, "StackAllocator {{ size: {SIZE} }}") 14 | } 15 | } 16 | 17 | struct StackAllocatorInner { 18 | memory: [u8; SIZE], 19 | current: usize, 20 | } 21 | 22 | impl StackAllocator { 23 | pub fn new() -> Self { 24 | Self { 25 | inner: Mutex::new(StackAllocatorInner { 26 | memory: [0; SIZE], 27 | current: 0, 28 | }), 29 | } 30 | } 31 | 32 | #[allow(clippy::mut_from_ref)] 33 | pub fn own(&self, value: T) -> &mut T { 34 | let ptr = self.allocate(Layout::new::()).unwrap().as_mut_ptr() as *mut T; 35 | unsafe { 36 | core::ptr::write(ptr, value); 37 | &mut *ptr 38 | } 39 | } 40 | 41 | #[allow(unused)] 42 | pub fn used(&self) -> usize { 43 | self.inner.lock().current 44 | } 45 | } 46 | 47 | unsafe impl Allocator for StackAllocator { 48 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 49 | let mut inner = self.inner.lock(); 50 | if inner.current + layout.size() >= inner.memory.len() { 51 | return Err(AllocError); 52 | } 53 | let ptr = &inner.memory[inner.current] as *const u8 as *mut (); 54 | let ptr = core::ptr::from_raw_parts_mut(ptr, layout.size()); 55 | inner.current += layout.size(); 56 | Ok(unsafe { NonNull::new_unchecked(ptr) }) 57 | } 58 | 59 | unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) {} 60 | } 61 | -------------------------------------------------------------------------------- /kernel/src/task/executor.rs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2022 Eliza Weisman 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 | 23 | use crate::local::Local; 24 | use conquer_once::spin::OnceCell; 25 | use core::{ 26 | any::Any, 27 | cell::Cell, 28 | future::Future, 29 | pin::Pin, 30 | sync::atomic::{AtomicBool, AtomicUsize, Ordering}, 31 | task::{Context, Poll}, 32 | }; 33 | use maitake::{ 34 | scheduler::{Injector, StaticScheduler, Stealer, TaskStub}, 35 | task::JoinHandle, 36 | }; 37 | use pin_project::pin_project; 38 | use rand::{rngs::OsRng, Rng, RngCore, SeedableRng}; 39 | 40 | static SCHEDULER: Local>> = Local::new(|| Cell::new(None)); 41 | 42 | static RUNTIME: Runtime = Runtime { 43 | cores: [const { OnceCell::uninit() }; MAX_CORES], 44 | initialized: AtomicUsize::new(0), 45 | injector: { 46 | static STUB_TASK: TaskStub = TaskStub::new(); 47 | unsafe { Injector::new_with_static_stub(&STUB_TASK) } 48 | }, 49 | }; 50 | 51 | const MAX_CORES: usize = 32; 52 | 53 | struct Runtime { 54 | cores: [OnceCell; MAX_CORES], 55 | 56 | injector: Injector<&'static StaticScheduler>, 57 | initialized: AtomicUsize, 58 | } 59 | 60 | impl Runtime { 61 | fn active_cores(&self) -> usize { 62 | self.initialized.load(Ordering::Acquire) 63 | } 64 | 65 | fn new_scheduler(&self) -> (usize, &StaticScheduler) { 66 | let next = self.initialized.fetch_add(1, Ordering::AcqRel); 67 | assert!(next < MAX_CORES); 68 | let scheduler = self.cores[next] 69 | .try_get_or_init(StaticScheduler::new) 70 | .unwrap(); 71 | (next, scheduler) 72 | } 73 | 74 | fn try_steal_from( 75 | &'static self, 76 | idx: usize, 77 | ) -> Option> { 78 | self.cores[idx].try_get().ok()?.try_steal().ok() 79 | } 80 | } 81 | 82 | pub struct Executor { 83 | id: usize, 84 | scheduler: &'static StaticScheduler, 85 | running: AtomicBool, 86 | rng: rand_xoshiro::Xoroshiro128PlusPlus, 87 | } 88 | 89 | impl Executor { 90 | pub fn new() -> Executor { 91 | let (id, scheduler) = RUNTIME.new_scheduler(); 92 | Executor { 93 | id, 94 | scheduler, 95 | running: AtomicBool::new(false), 96 | rng: rand_xoshiro::Xoroshiro128PlusPlus::seed_from_u64(OsRng.next_u64()), 97 | } 98 | } 99 | 100 | #[inline] 101 | pub fn is_running(&self) -> bool { 102 | self.running.load(Ordering::Acquire) 103 | } 104 | 105 | fn tick(&mut self) -> bool { 106 | let tick = self.scheduler.tick(); 107 | 108 | super::timer::TIMER.turn(); 109 | 110 | if tick.has_remaining { 111 | return true; 112 | } 113 | 114 | let stolen = self.try_steal(); 115 | if stolen > 0 { 116 | return true; 117 | } 118 | 119 | false 120 | } 121 | 122 | pub fn run(&mut self) { 123 | struct CoreGuard; 124 | impl Drop for CoreGuard { 125 | fn drop(&mut self) { 126 | SCHEDULER.with(|scheduler| scheduler.set(None)); 127 | } 128 | } 129 | 130 | if self 131 | .running 132 | .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) 133 | .is_err() 134 | { 135 | return; 136 | } 137 | 138 | SCHEDULER.with(|scheduler| scheduler.set(Some(self.scheduler))); 139 | let _unset = CoreGuard; 140 | 141 | loop { 142 | if self.tick() { 143 | continue; 144 | } 145 | 146 | if !self.is_running() { 147 | return; 148 | } 149 | 150 | crate::arch::enable_interrupts_and_halt(); 151 | } 152 | } 153 | 154 | fn try_steal(&mut self) -> usize { 155 | const MAX_STEAL_ATTEMPTS: usize = 16; 156 | const MAX_STOLEN_PER_TICK: usize = 256; 157 | 158 | if let Ok(injector) = RUNTIME.injector.try_steal() { 159 | return injector.spawn_n(&self.scheduler, MAX_STOLEN_PER_TICK); 160 | } 161 | 162 | for _ in 0..MAX_STEAL_ATTEMPTS { 163 | let active_cores = RUNTIME.active_cores(); 164 | 165 | if active_cores <= 1 { 166 | break; 167 | } 168 | 169 | let victim_idx = self.rng.gen_range(0..active_cores); 170 | 171 | if victim_idx == self.id { 172 | continue; 173 | } 174 | 175 | if let Some(victim) = RUNTIME.try_steal_from(victim_idx) { 176 | let num_steal = 177 | core::cmp::min(victim.initial_task_count() / 2, MAX_STOLEN_PER_TICK); 178 | return victim.spawn_n(&self.scheduler, num_steal); 179 | } 180 | } 181 | 182 | if let Ok(injector) = RUNTIME.injector.try_steal() { 183 | return injector.spawn_n(&self.scheduler, MAX_STOLEN_PER_TICK); 184 | } 185 | 186 | 0 187 | } 188 | } 189 | 190 | #[pin_project] 191 | pub struct CatchUnwind { 192 | #[pin] 193 | future: F, 194 | } 195 | 196 | impl Future for CatchUnwind 197 | where 198 | F: Future, 199 | { 200 | type Output = Result>; 201 | 202 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 203 | let f = self.project().future; 204 | match unwinding::panic::catch_unwind(|| f.poll(cx)) { 205 | Ok(v) => v.map(Ok), 206 | Err(e) => { 207 | crate::panic::inspect(&e); 208 | Poll::Ready(Err(e)) 209 | } 210 | } 211 | } 212 | } 213 | 214 | pub fn spawn(future: F) -> JoinHandle< as Future>::Output> 215 | where 216 | F: Future + Send + 'static, 217 | F::Output: Send + 'static, 218 | { 219 | let future = CatchUnwind { future }; 220 | SCHEDULER.with(|scheduler| { 221 | if let Some(scheduler) = scheduler.get() { 222 | scheduler.spawn(future) 223 | } else { 224 | RUNTIME.injector.spawn(future) 225 | } 226 | }) 227 | } 228 | -------------------------------------------------------------------------------- /kernel/src/task/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod executor; 2 | pub mod timer; 3 | 4 | pub use executor::spawn; 5 | 6 | pub fn start(ap_id: u8) { 7 | debug!("[TASK] {ap_id} initialized"); 8 | let mut executor = executor::Executor::new(); 9 | executor.run(); 10 | } 11 | -------------------------------------------------------------------------------- /kernel/src/task/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::TIMER_INTERVAL; 2 | use core::sync::atomic::{AtomicU64, Ordering}; 3 | use maitake::time::{set_global_timer, Clock, Timer}; 4 | 5 | static CLOCK_TICKS: AtomicU64 = AtomicU64::new(0); 6 | 7 | pub static TIMER: Timer = Timer::new(Clock::new(TIMER_INTERVAL, || { 8 | CLOCK_TICKS.load(Ordering::Relaxed) 9 | })); 10 | 11 | pub fn init() { 12 | set_global_timer(&TIMER).unwrap(); 13 | } 14 | 15 | pub fn on_tick() { 16 | CLOCK_TICKS.fetch_add(1, Ordering::Relaxed); 17 | } 18 | -------------------------------------------------------------------------------- /nvm.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsnek/snek_os/8fe76b0f1f5894855719a256f4250d9b14e7b2eb/nvm.img -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsnek/snek_os/8fe76b0f1f5894855719a256f4250d9b14e7b2eb/screenshot.png --------------------------------------------------------------------------------