├── .cargo └── config.toml ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── README.md ├── fork.PNG └── workflows │ ├── label-project.yml │ ├── rust-clippy.yml │ ├── rust-pr.yml │ ├── rust.yml │ └── todo.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── boot ├── Cargo.toml └── src │ └── main.rs ├── drivers ├── ata │ ├── Cargo.toml │ └── src │ │ ├── array_combiner.rs │ │ ├── bus.rs │ │ ├── constants.rs │ │ ├── debug.rs │ │ ├── disk.rs │ │ ├── disk_descriptor.rs │ │ ├── lib.rs │ │ ├── partition.rs │ │ └── partition_descriptor.rs └── vga │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── pixel_buffer.rs │ ├── point_2d.rs │ ├── static_stack.rs │ ├── vga_color.rs │ ├── vga_core.rs │ └── vga_device.rs ├── internal_utils ├── Cargo.toml └── src │ ├── constants.rs │ ├── lib.rs │ ├── port_extensions.rs │ ├── serial.rs │ ├── structures.rs │ └── structures │ ├── driver.rs │ └── kernel_information.rs ├── kernel ├── Cargo.toml └── src │ ├── debug.rs │ ├── init.rs │ ├── interrupts.rs │ ├── interrupts │ ├── cpu_handlers.rs │ ├── cpu_handlers │ │ ├── breakpoint.rs │ │ ├── double_fault.rs │ │ ├── general_protection_fault.rs │ │ ├── non_maskable_interrupt.rs │ │ └── page_fault.rs │ ├── gdt.rs │ ├── interrupt_register.rs │ ├── pic.rs │ ├── pic_handlers.rs │ └── pic_handlers │ │ ├── addresses.rs │ │ ├── ata.rs │ │ ├── keyboard.rs │ │ └── timer.rs │ ├── lib.rs │ ├── logger.rs │ ├── memory.rs │ ├── memory │ ├── allocator.rs │ ├── frame_allocator.rs │ ├── heap.rs │ ├── memory_init.rs │ └── page_table.rs │ ├── processes.rs │ ├── processes │ ├── dispatcher.rs │ ├── memory_mapper.rs │ ├── process.rs │ ├── registers_state.rs │ ├── scheduler.rs │ └── thread.rs │ ├── syscalls.rs │ └── syscalls │ └── system_call.rs ├── rost-lib ├── Cargo.toml └── src │ ├── lib.rs │ ├── syscall_name.rs │ └── thread_utils.rs ├── rust-toolchain.toml ├── src ├── assets │ ├── rost-logo.svg │ └── rost-logo.tga └── main.rs ├── test_framework ├── Cargo.toml └── src │ ├── ansi_colors.rs │ ├── ansi_colors │ ├── green.rs │ ├── red.rs │ └── yellow.rs │ ├── lib.rs │ ├── qemu_exit.rs │ ├── test_runner.rs │ └── testable.rs └── x86_64-custom.json /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_os = "none")'] 2 | runner = "cargo run --package boot --release --" 3 | 4 | [alias] 5 | kbuild = "build --target x86_64-custom.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem" #dev 6 | kimage = "run --target x86_64-custom.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem -- --no-run" #dev 7 | krun = "run --target x86_64-custom.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem" #dev 8 | kbuild-r = "build --target x86_64-custom.json -Zbuild-std=core,alloc --release -Zbuild-std-features=compiler-builtins-mem" #dev 9 | kimage-r = "run --target x86_64-custom.json -Zbuild-std=core,alloc --release -Zbuild-std-features=compiler-builtins-mem -- --no-run" #release 10 | krun-r = "run --target x86_64-custom.json -Zbuild-std=core,alloc --release -Zbuild-std-features=compiler-builtins-mem" #release 11 | ktest = "test --target x86_64-custom.json --bins -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem" 12 | kdoc = "doc --target x86_64-custom.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem" 13 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome to the contribution guide 2 | 3 | First of all - we would like to thank you for looking into how to contribute to the project. 4 | 5 | As this project is a hobby project, everyone who wants to contribute is worth their weight in gold. 6 | 7 | ## The flow to contribute to the project 8 | 9 | The project can be contributed to in this way: 10 | 11 | 1. ### Create a fork of this project 12 | 13 | To create a fork, click on the button commonly located in the upper-right corner of the page: 14 | 15 | --- 16 | 17 | ![Fork button in the upper-right corner](./fork.PNG) 18 | 19 | --- 20 | 21 | Or just [click here](https://github.com/0xffset/rOSt/fork) 22 | 23 | 2. ### Find an issue to work on 24 | 25 | Go through the [list of issues](https://github.com/0xffset/rOSt/issues) of the project and find an issue which sounds interesting to you. 26 | 27 | Issues labelled as [**[help wanted]**](https://github.com/0xffset/rOSt/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) are good targets for getting accustomed to the project. 28 | 29 | Some of the issues are labelled as [**[todo 🗒️]**](https://github.com/0xffset/rOSt/issues?q=is%3Aissue+is%3Aopen+label%3A%22todo+%3Aspiral_notepad%3A%22), which usually means they are scoped to a particular part of the project and do not need knowledge of the whole. 30 | 31 | 3. ### Work on the issue on your fork of the project 32 | 33 | It would be best if your work had tests and documentation, so others can easily understand and review the code. 34 | 35 | 4. ### When you are done, submit a Pull Request 36 | 37 | The pull request should generally abide to the Bug Report or a Feature Request templates, but it is not enforced. 38 | 39 | 5. ### The Pull Request is denied 40 | 41 | If your Pull Request is denied, it will most likely not get closed. 42 | You will get a comment on it that highlights the things you would have to fix. After doing that, the Pull Request will surely get accepted! 43 | 44 | 6. ### The Pull Request is accepted 45 | 46 | Congratulations, and thanks for collaborating on the project! 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[REQUEST]" 5 | labels: feature request 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm frustrated by [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | # rOSt, a 64-Bit Rust operating system 2 | 3 | For more information about the project, please visit the [wiki](https://github.com/0xffset/rOSt/wiki), this readme is meant to give a quick overview of the project for developers and anyone interested. 4 | 5 | If you are interested in contributing to the project, please visit the [Contributing file](https://github.com/0xffset/rOSt/blob/main/.github/CONTRIBUTING.md). 6 | 7 | ### Structure 8 | 9 | The project is divided into multiple folders: 10 | 11 | 1. [src](src/) contains the main entry point of the kernel. 12 | 2. [rost-lib](rost-lib/) contains the standard library that will be available to all programs written for the OS. 13 | 3. [boot](boot/) contains the settings for building the image with the bootloader, and QEMU settings. 14 | 4. [utils](utils/) contains utility functions, constants and structures that could be used throughout the kernel. 15 | 5. [drivers](drivers/) contains drivers that add extended functionality that is not in the scope of the kernel core. 16 | 6. [kernel](kernel/) contains the core library and functionality. 17 | 18 | ### Requirements 19 | 20 | - [Rust](https://www.rust-lang.org/) using the nightly channel 21 | - [llvm-tools-preview](https://docs.rs/llvm-tools/latest/llvm_tools/) (installed via `rustup component add llvm-tools-preview`) 22 | - [QEMU](https://www.qemu.org/) 23 | 24 | Rust should automatically switch to the nightly channel and install the llvm tools when it detects the `rust-toolchain.toml`. 25 | 26 | ## How to run 27 | 28 | ```bash 29 | cargo krun 30 | ``` 31 | 32 | will build the kernel and start up a qemu instance booting the kernel in debug mode. 33 | 34 | ## Testing 35 | 36 | Tests are ran after the kernel initializes the necessities like kernel heap, general memory management and interrupts. 37 | 38 | To run the tests do: 39 | 40 | ```bash 41 | cargo ktest 42 | ``` 43 | 44 | ### Troubleshooting 45 | 46 | - If the build fails because of usage of unstable features, make sure that you have enabled the nightly channel using `rustup default nightly` or `rustup upgrade` 47 | 48 | Processor Chip Icon by Kalash 49 | -------------------------------------------------------------------------------- /.github/fork.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComicalCache/rOSt/38b4c0bedaf329845978493209e616e95915d5b4/.github/fork.PNG -------------------------------------------------------------------------------- /.github/workflows/label-project.yml: -------------------------------------------------------------------------------- 1 | on: 2 | issues: 3 | types: [labeled] 4 | jobs: 5 | Move_Labeled_Issue_On_Project_Board: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: konradpabjan/move-labeled-or-milestoned-issue@v2.0 9 | with: 10 | action-token: ${{ secrets.GITHUB_TOKEN }} 11 | project-url: "https://github.com/0xffset/rOSt/projects/2" 12 | column-name: "To-Do" 13 | label-name: "documentation" 14 | columns-to-ignore: "Paused,In Progress,Done" 15 | -------------------------------------------------------------------------------- /.github/workflows/rust-clippy.yml: -------------------------------------------------------------------------------- 1 | # rust-clippy is a tool that runs a bunch of lints to catch common 2 | # mistakes in your Rust code and help improve your Rust code. 3 | # More details at https://github.com/rust-lang/rust-clippy 4 | # and https://rust-lang.github.io/rust-clippy/ 5 | 6 | name: rust-clippy analyze 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [ "main" ] 14 | schedule: 15 | - cron: '30 6 * * 0,2,4' 16 | 17 | jobs: 18 | rust-clippy-analyze: 19 | name: Run rust-clippy analyzing 20 | runs-on: ubuntu-latest 21 | permissions: 22 | contents: read 23 | security-events: write 24 | steps: 25 | - name: Checkout code 26 | uses: actions/checkout@v2 27 | 28 | - name: Install rust 29 | run: cargo check --target x86_64-unknown-none 30 | 31 | - name: Install required cargo 32 | run: cargo install clippy-sarif sarif-fmt 33 | 34 | - name: Run rust-clippy 35 | run: 36 | cargo clippy 37 | --all-features 38 | --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt 39 | continue-on-error: true 40 | 41 | - name: Upload analysis results to GitHub 42 | uses: github/codeql-action/upload-sarif@v2 43 | with: 44 | sarif_file: rust-clippy-results.sarif 45 | wait-for-processing: true 46 | -------------------------------------------------------------------------------- /.github/workflows/rust-pr.yml: -------------------------------------------------------------------------------- 1 | name: Build the project 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | conditional_job_check_files: 13 | runs-on: ubuntu-latest 14 | outputs: 15 | source_changed: ${{ steps.changes.outputs.src }} 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - uses: dorny/paths-filter@v2 20 | id: changes 21 | with: 22 | filters: | 23 | src: 24 | - '**/*.rs' 25 | - '**/*.toml' 26 | - '**/*.json' 27 | 28 | lint: 29 | runs-on: ubuntu-latest 30 | needs: [conditional_job_check_files] 31 | if: needs.conditional_job_check_files.outputs.source_changed == 'true' 32 | 33 | steps: 34 | - uses: actions/checkout@v3 35 | 36 | - name: ⚡ Cache 37 | uses: actions/cache@v2 38 | with: 39 | path: | 40 | ~/.cargo/registry 41 | ~/.cargo/git 42 | target 43 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 44 | 45 | - name: Install rust 46 | run: cargo check --target x86_64-unknown-none 47 | 48 | - name: Install required lint crates 49 | run: cargo install clippy-sarif sarif-fmt 50 | 51 | - name: Run rust-clippy 52 | run: cargo clippy 53 | --all-features 54 | --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt 55 | continue-on-error: true 56 | 57 | - name: Upload clippy results to GitHub 58 | uses: github/codeql-action/upload-sarif@v2 59 | with: 60 | sarif_file: rust-clippy-results.sarif 61 | wait-for-processing: true 62 | 63 | test: 64 | runs-on: ubuntu-latest 65 | needs: [conditional_job_check_files] 66 | if: needs.conditional_job_check_files.outputs.source_changed == 'true' 67 | 68 | steps: 69 | - uses: actions/checkout@v3 70 | 71 | - name: ⚡ Cache 72 | uses: actions/cache@v2 73 | with: 74 | path: | 75 | ~/.cargo/registry 76 | ~/.cargo/git 77 | target 78 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 79 | 80 | - name: Install rust 81 | run: cargo check --target x86_64-unknown-none 82 | 83 | - name: Set up QEMU 84 | run: sudo apt-get install qemu-system-x86 85 | 86 | - name: Test 87 | run: cargo ktest --verbose 88 | 89 | build: 90 | runs-on: ubuntu-latest 91 | needs: [conditional_job_check_files] 92 | if: needs.conditional_job_check_files.outputs.source_changed == 'true' 93 | 94 | steps: 95 | - uses: actions/checkout@v3 96 | 97 | - name: ⚡ Cache 98 | uses: actions/cache@v2 99 | with: 100 | path: | 101 | ~/.cargo/registry 102 | ~/.cargo/git 103 | target 104 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 105 | 106 | - name: Install rust 107 | run: cargo check --target x86_64-unknown-none 108 | 109 | - name: Build debug 110 | run: cargo kbuild --verbose 111 | 112 | - name: Build release 113 | run: cargo kbuild-r --verbose 114 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Build the images 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Install rust 18 | run: cargo check --target x86_64-unknown-none 19 | 20 | - name: Build Images 21 | run: cargo kimage-r --verbose 22 | 23 | - name: Upload a Build Artifact 24 | uses: actions/upload-artifact@v3.0.0 25 | with: 26 | name: Build-Images 27 | path: /home/runner/work/rOSt/rOSt/target/x86_64-custom/release/*.img 28 | if-no-files-found: error 29 | -------------------------------------------------------------------------------- /.github/workflows/todo.yml: -------------------------------------------------------------------------------- 1 | name: Create issues from todos 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | importAll: 7 | default: 'false' 8 | required: false 9 | type: boolean 10 | description: Enable, if you want to import all TODOs. Runs on checked out branch! Only use if you're sure what you are doing. 11 | push: 12 | branches: 13 | - main 14 | 15 | permissions: 16 | issues: write 17 | repository-projects: read 18 | contents: read 19 | 20 | jobs: 21 | todos: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v3 25 | 26 | - name: todo-issue 27 | uses: DerJuulsn/todo-issue@v1.0.5 28 | with: 29 | excludePattern: '^(\.cargo/)' 30 | autoAssign: '' 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/.DS_Store 3 | .idea 4 | test_disk.img 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "serayuzgur.crates", 4 | "bungcip.better-toml", 5 | "rust-lang.rust-analyzer" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "target": true 4 | }, 5 | "editor.formatOnSave": true, 6 | "files.autoSave": "afterDelay", 7 | "rust-analyzer.checkOnSave.command": "clippy" 8 | } 9 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ata" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "bitflags", 10 | "internal_utils", 11 | "kernel", 12 | "lazy_static", 13 | "spin 0.9.3", 14 | "x86_64", 15 | ] 16 | 17 | [[package]] 18 | name = "autocfg" 19 | version = "1.1.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 22 | 23 | [[package]] 24 | name = "az" 25 | version = "1.2.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4" 28 | 29 | [[package]] 30 | name = "bit_field" 31 | version = "0.10.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 34 | 35 | [[package]] 36 | name = "bitflags" 37 | version = "1.3.2" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 40 | 41 | [[package]] 42 | name = "boot" 43 | version = "0.1.0" 44 | dependencies = [ 45 | "bootloader-locator", 46 | "locate-cargo-manifest", 47 | "runner-utils", 48 | ] 49 | 50 | [[package]] 51 | name = "bootloader" 52 | version = "0.10.12" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "f2d9b14b92a825ecc3b24e4c163a578af473fbba5f190bfaf48092b29b604504" 55 | 56 | [[package]] 57 | name = "bootloader-locator" 58 | version = "0.0.4" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "aaaa9db3339d32c2622f2e5d0731eb82a468d3439797c9d4fe426744fe2bd551" 61 | dependencies = [ 62 | "json", 63 | ] 64 | 65 | [[package]] 66 | name = "byteorder" 67 | version = "1.4.3" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 70 | 71 | [[package]] 72 | name = "embedded-graphics" 73 | version = "0.7.1" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "750082c65094fbcc4baf9ba31583ce9a8bb7f52cadfb96f6164b1bc7f922f32b" 76 | dependencies = [ 77 | "az", 78 | "byteorder", 79 | "embedded-graphics-core", 80 | "float-cmp", 81 | "micromath", 82 | ] 83 | 84 | [[package]] 85 | name = "embedded-graphics-core" 86 | version = "0.3.3" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "b8b1239db5f3eeb7e33e35bd10bd014e7b2537b17e071f726a09351431337cfa" 89 | dependencies = [ 90 | "az", 91 | "byteorder", 92 | ] 93 | 94 | [[package]] 95 | name = "float-cmp" 96 | version = "0.8.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" 99 | dependencies = [ 100 | "num-traits", 101 | ] 102 | 103 | [[package]] 104 | name = "internal_utils" 105 | version = "0.1.0" 106 | dependencies = [ 107 | "bootloader", 108 | "lazy_static", 109 | "spin 0.9.3", 110 | "uart_16550", 111 | "x86_64", 112 | ] 113 | 114 | [[package]] 115 | name = "json" 116 | version = "0.12.4" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" 119 | 120 | [[package]] 121 | name = "kernel" 122 | version = "0.1.0" 123 | dependencies = [ 124 | "bootloader", 125 | "internal_utils", 126 | "lazy_static", 127 | "linked_list_allocator", 128 | "pc-keyboard", 129 | "pic8259", 130 | "spin 0.9.3", 131 | "test_framework", 132 | "uart_16550", 133 | "x86_64", 134 | ] 135 | 136 | [[package]] 137 | name = "lazy_static" 138 | version = "1.4.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 141 | dependencies = [ 142 | "spin 0.5.2", 143 | ] 144 | 145 | [[package]] 146 | name = "libc" 147 | version = "0.2.125" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" 150 | 151 | [[package]] 152 | name = "linked_list_allocator" 153 | version = "0.9.1" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" 156 | dependencies = [ 157 | "spinning_top", 158 | ] 159 | 160 | [[package]] 161 | name = "locate-cargo-manifest" 162 | version = "0.2.2" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "db985b63431fe09e8d71f50aeceffcc31e720cb86be8dad2f38d084c5a328466" 165 | dependencies = [ 166 | "json", 167 | ] 168 | 169 | [[package]] 170 | name = "lock_api" 171 | version = "0.4.7" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 174 | dependencies = [ 175 | "autocfg", 176 | "scopeguard", 177 | ] 178 | 179 | [[package]] 180 | name = "memchr" 181 | version = "2.3.4" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 184 | 185 | [[package]] 186 | name = "micromath" 187 | version = "1.1.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "bc4010833aea396656c2f91ee704d51a6f1329ec2ab56ffd00bfd56f7481ea94" 190 | 191 | [[package]] 192 | name = "nom" 193 | version = "6.2.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" 196 | dependencies = [ 197 | "memchr", 198 | "version_check", 199 | ] 200 | 201 | [[package]] 202 | name = "noto-sans-mono-bitmap" 203 | version = "0.1.5" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "643bee6617a18c64f5a72ef358a9426d3682dc768db7c320825d3d936e2232d4" 206 | 207 | [[package]] 208 | name = "num-traits" 209 | version = "0.2.14" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 212 | dependencies = [ 213 | "autocfg", 214 | ] 215 | 216 | [[package]] 217 | name = "os" 218 | version = "0.1.0" 219 | dependencies = [ 220 | "ata", 221 | "bootloader", 222 | "internal_utils", 223 | "kernel", 224 | "rost-lib", 225 | "test_framework", 226 | "tinytga", 227 | "vga", 228 | "x86_64", 229 | ] 230 | 231 | [[package]] 232 | name = "pc-keyboard" 233 | version = "0.5.1" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac" 236 | 237 | [[package]] 238 | name = "pic8259" 239 | version = "0.10.2" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "24ec21f514e2e16e94649f1d041ca4a7069b512c037ac156360652a775e6229d" 242 | dependencies = [ 243 | "x86_64", 244 | ] 245 | 246 | [[package]] 247 | name = "proc-macro2" 248 | version = "1.0.37" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" 251 | dependencies = [ 252 | "unicode-xid", 253 | ] 254 | 255 | [[package]] 256 | name = "quote" 257 | version = "1.0.18" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" 260 | dependencies = [ 261 | "proc-macro2", 262 | ] 263 | 264 | [[package]] 265 | name = "rost-lib" 266 | version = "0.1.0" 267 | dependencies = [ 268 | "ata", 269 | "bitflags", 270 | "internal_utils", 271 | "kernel", 272 | "lazy_static", 273 | ] 274 | 275 | [[package]] 276 | name = "runner-utils" 277 | version = "0.0.2" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "c9dc6848b056990cd51e72aa5556bdbea4a96013e8b18635d183c84159c2988f" 280 | dependencies = [ 281 | "thiserror", 282 | "wait-timeout", 283 | ] 284 | 285 | [[package]] 286 | name = "rustversion" 287 | version = "1.0.6" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" 290 | 291 | [[package]] 292 | name = "scopeguard" 293 | version = "1.1.0" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 296 | 297 | [[package]] 298 | name = "spin" 299 | version = "0.5.2" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 302 | 303 | [[package]] 304 | name = "spin" 305 | version = "0.9.3" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" 308 | dependencies = [ 309 | "lock_api", 310 | ] 311 | 312 | [[package]] 313 | name = "spinning_top" 314 | version = "0.2.4" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" 317 | dependencies = [ 318 | "lock_api", 319 | ] 320 | 321 | [[package]] 322 | name = "syn" 323 | version = "1.0.92" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" 326 | dependencies = [ 327 | "proc-macro2", 328 | "quote", 329 | "unicode-xid", 330 | ] 331 | 332 | [[package]] 333 | name = "test_framework" 334 | version = "0.1.0" 335 | dependencies = [ 336 | "internal_utils", 337 | "x86_64", 338 | ] 339 | 340 | [[package]] 341 | name = "thiserror" 342 | version = "1.0.30" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 345 | dependencies = [ 346 | "thiserror-impl", 347 | ] 348 | 349 | [[package]] 350 | name = "thiserror-impl" 351 | version = "1.0.30" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 354 | dependencies = [ 355 | "proc-macro2", 356 | "quote", 357 | "syn", 358 | ] 359 | 360 | [[package]] 361 | name = "tinytga" 362 | version = "0.4.1" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "756dcc0078b35e5f8ab052f19a48225821fd046011342d31deef6b8d5c825d2b" 365 | dependencies = [ 366 | "embedded-graphics", 367 | "nom", 368 | ] 369 | 370 | [[package]] 371 | name = "uart_16550" 372 | version = "0.2.18" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "b074eb9300ad949edd74c529c0e8d451625af71bb948e6b65fe69f72dc1363d9" 375 | dependencies = [ 376 | "bitflags", 377 | "rustversion", 378 | "x86_64", 379 | ] 380 | 381 | [[package]] 382 | name = "unicode-xid" 383 | version = "0.2.2" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 386 | 387 | [[package]] 388 | name = "version_check" 389 | version = "0.9.4" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 392 | 393 | [[package]] 394 | name = "vga" 395 | version = "0.1.0" 396 | dependencies = [ 397 | "internal_utils", 398 | "kernel", 399 | "lazy_static", 400 | "noto-sans-mono-bitmap", 401 | "spin 0.9.3", 402 | "tinytga", 403 | ] 404 | 405 | [[package]] 406 | name = "volatile" 407 | version = "0.4.5" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c" 410 | 411 | [[package]] 412 | name = "wait-timeout" 413 | version = "0.2.0" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 416 | dependencies = [ 417 | "libc", 418 | ] 419 | 420 | [[package]] 421 | name = "x86_64" 422 | version = "0.14.9" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "958cd5cb28e720db2f59ee9dc4235b5f82a183d079fb0e6caf43ad074cfdc66a" 425 | dependencies = [ 426 | "bit_field", 427 | "bitflags", 428 | "rustversion", 429 | "volatile", 430 | ] 431 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["workspace-inheritance"] 2 | 3 | [package] 4 | name = "os" 5 | version = "0.1.0" 6 | edition = { workspace=true } 7 | 8 | [package.metadata.bootloader] 9 | map-physical-memory = true 10 | physical-memory-offset = "0x0000_4000_0000_0000" 11 | kernel-stack-address = "0x_007F_8000_0000" 12 | kernel-stack-size = "0x1_4000" 13 | boot-info-address = "0x_7555_AAAE_0000" 14 | framebuffer-address = "0x_7F55_AAAA_0000" 15 | #dynamic-range-start = "0xFFFF_8000_0000_0000" # can't do that cause we don't have the newest shiny version of bootloader 16 | #kernel is placed at 0x007F_C000_0000, set in target json 17 | 18 | [dependencies] 19 | internal_utils = { workspace=true } 20 | kernel = { workspace=true } 21 | vga = { workspace=true } 22 | ata = { workspace=true } 23 | x86_64 = { workspace=true } 24 | rost-lib = { workspace=true } 25 | test_framework = { workspace=true } 26 | bootloader = { workspace=true } 27 | tinytga = { workspace=true } 28 | 29 | [profile.release] 30 | lto = true 31 | codegen-units = 1 32 | incremental = false 33 | 34 | [workspace] 35 | members = [ 36 | "internal_utils", 37 | "boot", 38 | "kernel", 39 | "drivers/ata", 40 | "drivers/vga", 41 | "rost-lib", 42 | "test_framework" 43 | ] 44 | 45 | [workspace.package] 46 | edition = "2021" 47 | 48 | [workspace.dependencies] 49 | internal_utils = { path = "internal_utils"} 50 | kernel = { path = "kernel" } 51 | vga = { path = "drivers/vga" } 52 | ata = { path = "drivers/ata" } 53 | rost-lib = { path = "rost-lib" } 54 | test_framework = { path = "test_framework" } 55 | bitflags = "1.3" 56 | bootloader = "0.10.12" 57 | pic8259 = "0.10.2" # interrupt controller 58 | noto-sans-mono-bitmap = { version = "0.1.5", features = ["size_14"] } # nice bitmap font 59 | pc-keyboard = "0.5.1" # keyboard scan code converter 60 | linked_list_allocator = "0.9.1" # allocator implementation 61 | volatile = "0.4.4" # volatile wrapper to prevent aggressive compiler optimizations 62 | spin = "0.9.3" # spinlock implementation 63 | x86_64 = "0.14.9" # configures the serial port, cpu exception table 64 | tinytga = "0.4.1" # for loading the OS logo 65 | uart_16550 = "0.2.18" # serial port interface used for sending debug output to the host 66 | lazy_static = { version = "1.4.0", features = [ "spin_no_std" ]} 67 | 68 | ################################### 69 | # Register all failing tests here # 70 | ################################### 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 René Gottschalk 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 | -------------------------------------------------------------------------------- /boot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "boot" 3 | version = "0.1.0" 4 | authors = ["Philipp Oppermann "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | bootloader-locator = "0.0.4" # for locating the `bootloader` dependency on disk 9 | locate-cargo-manifest = "0.2.0" # for locating the kernel's `Cargo.toml` 10 | runner-utils = "0.0.2" -------------------------------------------------------------------------------- /boot/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::{Path, PathBuf}, 3 | process::{Command, ExitStatus}, 4 | time::Duration, 5 | }; 6 | 7 | const RUN_ARGS: &[&str] = &["--no-reboot", "-s", "-serial", "stdio"]; 8 | const TEST_ARGS: &[&str] = &[ 9 | "-device", 10 | "isa-debug-exit,iobase=0xf4,iosize=0x04", 11 | "-serial", 12 | "stdio", 13 | "-display", 14 | "none", 15 | "--no-reboot", 16 | ]; 17 | const TEST_TIMEOUT_SECS: u64 = 10; 18 | 19 | fn main() { 20 | let mut args = std::env::args().skip(1); // skip executable name 21 | 22 | let kernel_binary_path = { 23 | let path = PathBuf::from(args.next().unwrap()); 24 | path.canonicalize().unwrap() 25 | }; 26 | let no_boot = if let Some(arg) = args.next() { 27 | match arg.as_str() { 28 | "--no-run" => true, 29 | other => panic!("unexpected argument `{}`", other), 30 | } 31 | } else { 32 | false 33 | }; 34 | 35 | let bios = create_disk_images(&kernel_binary_path); 36 | 37 | if no_boot { 38 | println!("Created disk image at `{}`", bios.display()); 39 | return; 40 | } 41 | let mut run_cmd = Command::new("qemu-system-x86_64"); 42 | run_cmd 43 | .args(["-m", "256"]) 44 | .args(["-hda", &bios.display().to_string()]) 45 | /*.args(["-drive", "if=none,id=disk,file=test_disk.img"]) 46 | .args([ 47 | "-device", 48 | "ide-hd,drive=disk,bus=ide.1,serial=11cebfc108002be10318,model=INTEL SSDSC2BW120A4", 49 | ])*/; 50 | 51 | let binary_kind = runner_utils::binary_kind(&kernel_binary_path); 52 | if binary_kind.is_test() { 53 | run_cmd.args(TEST_ARGS); 54 | 55 | let exit_status = run_test_command(run_cmd); 56 | match exit_status.code() { 57 | Some(33) => {} // success 58 | other => panic!("Test failed (exit code: {:?})", other), 59 | } 60 | } else { 61 | run_cmd.args(RUN_ARGS); 62 | 63 | let exit_status = run_cmd.status().unwrap(); 64 | if !exit_status.success() { 65 | std::process::exit(exit_status.code().unwrap_or(1)); 66 | } 67 | } 68 | } 69 | 70 | fn run_test_command(mut cmd: Command) -> ExitStatus { 71 | runner_utils::run_with_timeout(&mut cmd, Duration::from_secs(TEST_TIMEOUT_SECS)).unwrap() 72 | } 73 | 74 | pub fn create_disk_images(kernel_binary_path: &Path) -> PathBuf { 75 | let bootloader_manifest_path = bootloader_locator::locate_bootloader("bootloader").unwrap(); 76 | let kernel_manifest_path = locate_cargo_manifest::locate_manifest().unwrap(); 77 | 78 | let mut build_cmd = Command::new(env!("CARGO")); 79 | build_cmd.current_dir(bootloader_manifest_path.parent().unwrap()); 80 | build_cmd.arg("builder"); 81 | build_cmd 82 | .arg("--kernel-manifest") 83 | .arg(&kernel_manifest_path); 84 | build_cmd.arg("--kernel-binary").arg(&kernel_binary_path); 85 | build_cmd 86 | .arg("--target-dir") 87 | .arg(kernel_manifest_path.parent().unwrap().join("target")); 88 | build_cmd 89 | .arg("--out-dir") 90 | .arg(kernel_binary_path.parent().unwrap()); 91 | build_cmd.arg("--quiet"); 92 | 93 | if !build_cmd.status().unwrap().success() { 94 | panic!("build failed"); 95 | } 96 | 97 | let kernel_binary_name = kernel_binary_path.file_name().unwrap().to_str().unwrap(); 98 | let disk_image = kernel_binary_path 99 | .parent() 100 | .unwrap() 101 | .join(format!("boot-bios-{}.img", kernel_binary_name)); 102 | if !disk_image.exists() { 103 | panic!( 104 | "Disk image does not exist at {} after bootloader build", 105 | disk_image.display() 106 | ); 107 | } 108 | disk_image 109 | } 110 | -------------------------------------------------------------------------------- /drivers/ata/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["workspace-inheritance"] 2 | 3 | [package] 4 | name = "ata" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | internal_utils = { workspace=true } 10 | kernel = { workspace=true } 11 | x86_64 = { workspace=true } 12 | spin = { workspace=true } 13 | bitflags = { workspace=true } 14 | lazy_static = { workspace=true } 15 | -------------------------------------------------------------------------------- /drivers/ata/src/array_combiner.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | pub struct Combiner<'a, T: Sized + Default + Copy> { 4 | size: usize, 5 | data: Vec<&'a [T]>, 6 | } 7 | 8 | impl<'a, T: Sized + Default + Copy> Combiner<'a, T> { 9 | pub fn new() -> Self { 10 | Combiner { 11 | size: 0, 12 | data: Vec::new(), 13 | } 14 | } 15 | 16 | pub fn with(mut self, data: &'a [T]) -> Self { 17 | self.size += data.len(); 18 | self.data.push(data); 19 | self 20 | } 21 | 22 | pub fn build(self) -> Option<[T; S]> { 23 | if self.size != S { 24 | return None; 25 | } 26 | let mut result = [T::default(); S]; 27 | let mut start: usize = 0; 28 | for data in self.data { 29 | result[start..start + data.len()].copy_from_slice(data); 30 | start += data.len(); 31 | } 32 | Some(result) 33 | } 34 | } 35 | 36 | impl<'a, T: Sized + Default + Copy> Default for Combiner<'a, T> { 37 | fn default() -> Self { 38 | Self::new() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /drivers/ata/src/bus.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use internal_utils::port_extensions::{PortExtRead, PortExtWrite}; 3 | use spin::Mutex; 4 | use x86_64::instructions::{ 5 | interrupts::without_interrupts, 6 | port::{Port, PortReadOnly, PortWriteOnly}, 7 | }; 8 | 9 | use crate::{ 10 | constants::{ATACommands, ErrorRegisterFlags, StatusRegisterFlags}, 11 | ATADisk, 12 | }; 13 | 14 | use super::{constants::ATAIdentifyError, disk_descriptor::DiskDescriptor}; 15 | 16 | #[allow(dead_code)] 17 | pub struct ATABus { 18 | data_register_rw: Port, 19 | error_register_r: PortReadOnly, 20 | features_register_w: PortWriteOnly, 21 | sector_count_register_rw: Port, 22 | lba_low_register_rw: Port, 23 | lba_mid_register_rw: Port, 24 | lba_high_register_rw: Port, 25 | drive_head_register_rw: Port, 26 | status_register_r: PortReadOnly, 27 | command_register_w: PortWriteOnly, 28 | alternate_status_register_r: PortReadOnly, 29 | device_control_register_w: PortWriteOnly, 30 | drive_address_register_r: PortReadOnly, 31 | 32 | disk_1_descriptor: Option, 33 | disk_2_descriptor: Option, 34 | } 35 | 36 | impl ATABus { 37 | pub(crate) const fn new(base_port: u16) -> Self { 38 | ATABus { 39 | data_register_rw: Port::new(base_port), 40 | error_register_r: PortReadOnly::new(base_port + 0x01), 41 | features_register_w: PortWriteOnly::new(base_port + 0x01), 42 | sector_count_register_rw: Port::new(base_port + 0x02), 43 | lba_low_register_rw: Port::new(base_port + 0x03), 44 | lba_mid_register_rw: Port::new(base_port + 0x04), 45 | lba_high_register_rw: Port::new(base_port + 0x05), 46 | drive_head_register_rw: Port::new(base_port + 0x06), 47 | status_register_r: PortReadOnly::new(base_port + 0x07), 48 | command_register_w: PortWriteOnly::new(base_port + 0x07), 49 | alternate_status_register_r: PortReadOnly::new(base_port + 0x206), 50 | device_control_register_w: PortWriteOnly::new(base_port + 0x206), 51 | drive_address_register_r: PortReadOnly::new(base_port + 0x207), 52 | disk_1_descriptor: None, 53 | disk_2_descriptor: None, 54 | } 55 | } 56 | 57 | pub fn connected(&mut self) -> bool { 58 | unsafe { self.status_register_r.read() != 0xFF } 59 | } 60 | 61 | pub fn wait_for( 62 | &mut self, 63 | flag: StatusRegisterFlags, 64 | should_be_on: bool, 65 | ) -> Result<(), ErrorRegisterFlags> { 66 | let condition = if should_be_on { 67 | flag 68 | } else { 69 | StatusRegisterFlags::empty() 70 | }; 71 | loop { 72 | unsafe { 73 | let status = 74 | StatusRegisterFlags::from_bits_unchecked(self.status_register_r.read()); 75 | if status.intersection(flag) == condition { 76 | break; 77 | } 78 | if status.contains(StatusRegisterFlags::ERR) { 79 | let error = self.error_register_r.read(); 80 | if error != 0 { 81 | return Err(ErrorRegisterFlags::from_bits_unchecked(error)); 82 | } 83 | } 84 | } 85 | } 86 | Ok(()) 87 | } 88 | 89 | pub fn wait_400ns(&mut self) -> Result<(), ErrorRegisterFlags> { 90 | for _ in 0..15 { 91 | unsafe { 92 | let status = 93 | StatusRegisterFlags::from_bits_unchecked(self.status_register_r.read()); 94 | if status.contains(StatusRegisterFlags::ERR) { 95 | let error = self.error_register_r.read(); 96 | if error != 0 { 97 | return Err(ErrorRegisterFlags::from_bits_unchecked(error)); 98 | } 99 | } 100 | } 101 | } 102 | Ok(()) 103 | } 104 | } 105 | 106 | impl ATABus { 107 | pub(crate) fn identify(&mut self, master: bool) -> Result { 108 | if master && self.disk_1_descriptor.is_some() { 109 | return Ok(self.disk_1_descriptor.as_ref().unwrap().clone()); 110 | } 111 | if !master && self.disk_2_descriptor.is_some() { 112 | return Ok(self.disk_2_descriptor.as_ref().unwrap().clone()); 113 | } 114 | unsafe fn handle_identify_error( 115 | bus: &mut ATABus, 116 | error: ErrorRegisterFlags, 117 | ) -> ATAIdentifyError { 118 | if error != ErrorRegisterFlags::ABRT { 119 | return ATAIdentifyError::DeviceIsATAPI; 120 | } 121 | let mid = bus.lba_mid_register_rw.read(); 122 | let high = bus.lba_high_register_rw.read(); 123 | 124 | match (mid, high) { 125 | (0x14, 0xEB) => ATAIdentifyError::DeviceIsATAPI, 126 | (0x3C, 0xC3) => ATAIdentifyError::DeviceIsSATA, 127 | (0, 0) => ATAIdentifyError::Unknown, 128 | (_, _) => ATAIdentifyError::DeviceIsNotATA, 129 | } 130 | } 131 | 132 | unsafe { 133 | if !self.connected() { 134 | return Err(ATAIdentifyError::BusNotConnected); 135 | } 136 | if self.wait_for(StatusRegisterFlags::BSY, false).is_err() { 137 | return Err(ATAIdentifyError::Unknown); 138 | } 139 | without_interrupts(|| { 140 | self.drive_head_register_rw 141 | .write(if master { 0xA0 } else { 0xB0 }); 142 | self.device_control_register_w.write(0x00); 143 | if self.wait_400ns().is_err() { 144 | return Err(ATAIdentifyError::Unknown); 145 | } 146 | self.sector_count_register_rw.write(0x00); 147 | self.lba_low_register_rw.write(0x00); 148 | self.lba_mid_register_rw.write(0x00); 149 | self.lba_high_register_rw.write(0x00); 150 | self.command_register_w.write(ATACommands::Identify as u8); 151 | let status = self.status_register_r.read(); 152 | if status == 0 { 153 | return Err(ATAIdentifyError::NoDevice); 154 | } 155 | if let Err(error) = self.wait_for(StatusRegisterFlags::BSY, false) { 156 | return Err(handle_identify_error(self, error)); 157 | } 158 | if let Err(error) = self.wait_for(StatusRegisterFlags::DRQ, true) { 159 | return Err(handle_identify_error(self, error)); 160 | } 161 | let mut identify_buffer: [u16; 256] = [0; 256]; 162 | self.data_register_rw.read_to_buffer(&mut identify_buffer); 163 | let descriptor = DiskDescriptor::from_bytes(identify_buffer); 164 | if master { 165 | self.disk_1_descriptor = Some(descriptor); 166 | Ok(self.disk_1_descriptor.as_ref().unwrap().clone()) 167 | } else { 168 | self.disk_2_descriptor = Some(descriptor); 169 | Ok(self.disk_2_descriptor.as_ref().unwrap().clone()) 170 | } 171 | }) 172 | } 173 | } 174 | 175 | pub(crate) fn read_sector( 176 | &mut self, 177 | master: bool, 178 | lba: u64, 179 | ) -> Result<[u8; 512], ErrorRegisterFlags> { 180 | // TODO Add LBA48 support to the ATA driver 181 | // We need to check the IO calls for LBA48 support 182 | if lba > u32::MAX.into() { 183 | todo!("LBA48 not supported"); 184 | } 185 | unsafe { 186 | let slave = if master { 0xE0 } else { 0xF0 }; 187 | self.drive_head_register_rw 188 | .write(slave | ((lba >> 24) & 0x0F) as u8); 189 | self.features_register_w.write(0x00); 190 | self.sector_count_register_rw.write(0x01); 191 | self.lba_low_register_rw.write(lba as u8); 192 | self.lba_mid_register_rw.write((lba >> 8) as u8); 193 | self.lba_high_register_rw.write((lba >> 16) as u8); 194 | self.command_register_w 195 | .write(ATACommands::ReadSectors as u8); 196 | self.wait_for(StatusRegisterFlags::BSY, false)?; 197 | self.wait_for(StatusRegisterFlags::DRQ, true)?; 198 | let mut buffer = [0u8; 512]; 199 | self.data_register_rw.read_to_buffer(&mut buffer); 200 | self.wait_400ns()?; 201 | Ok(buffer) 202 | } 203 | } 204 | 205 | pub(crate) fn write_sector( 206 | &mut self, 207 | master: bool, 208 | lba: u64, 209 | buffer: &[u8; 512], 210 | ) -> Result<(), ErrorRegisterFlags> { 211 | // TODO Add LBA48 support to the ATA driver 212 | // We need to check the IO calls for LBA48 support 213 | if lba > u32::MAX.into() { 214 | todo!("LBA48 not supported"); 215 | } 216 | unsafe { 217 | let slave = if master { 0xE0 } else { 0xF0 }; 218 | self.drive_head_register_rw 219 | .write(slave | ((lba >> 24) & 0x0F) as u8); 220 | self.features_register_w.write(0x00); 221 | self.sector_count_register_rw.write(0x01); 222 | self.lba_low_register_rw.write(lba as u8); 223 | self.lba_mid_register_rw.write((lba >> 8) as u8); 224 | self.lba_high_register_rw.write((lba >> 16) as u8); 225 | self.command_register_w 226 | .write(ATACommands::WriteSectors as u8); 227 | self.wait_for(StatusRegisterFlags::BSY, false)?; 228 | self.wait_for(StatusRegisterFlags::DRQ, true)?; 229 | self.data_register_rw.write_from_buffer(buffer); 230 | self.wait_400ns()?; 231 | self.wait_for(StatusRegisterFlags::DRQ, false)?; 232 | self.wait_for(StatusRegisterFlags::BSY, false)?; 233 | self.command_register_w.write(ATACommands::CacheFlush as u8); 234 | self.wait_for(StatusRegisterFlags::BSY, false)?; 235 | Ok(()) 236 | } 237 | } 238 | 239 | pub fn get_disk(this: &Arc>, master: bool) -> Result { 240 | let descriptor = this.lock().identify(master)?; 241 | Ok(ATADisk { 242 | bus: this.clone(), 243 | descriptor, 244 | master, 245 | }) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /drivers/ata/src/constants.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use spin::Mutex; 3 | 4 | use super::bus::ATABus; 5 | use bitflags::bitflags; 6 | use lazy_static::lazy_static; 7 | 8 | bitflags! { 9 | #[repr(C)] 10 | pub struct StatusRegisterFlags: u8 { 11 | /// Busy 12 | const BSY = 0b10000000; 13 | /// Device Ready 14 | const DRDY = 0b01000000; 15 | /// Device Fault 16 | const DF = 0b00100000; 17 | /// Seek Complete 18 | const DSC = 0b00010000; 19 | /// Data Transfer Required 20 | const DRQ = 0b00001000; 21 | /// Data Corrected 22 | const CORR = 0b00000100; 23 | /// Index Mark 24 | const IDX = 0b00000010; 25 | /// Error 26 | const ERR = 0b00000001; 27 | } 28 | 29 | #[repr(C)] 30 | pub struct ErrorRegisterFlags: u8 { 31 | /// Bad Block 32 | const BBK = 0b10000000; 33 | /// Uncorrectable Data Error 34 | const UNC = 0b10000000; 35 | /// Media Changed 36 | const MC = 0b10000000; 37 | /// ID Mark Not Found 38 | const IDNF = 0b10000000; 39 | /// Media Change Requested 40 | const MCR = 0b10000000; 41 | /// Command Aborted 42 | const ABRT = 0b10000000; 43 | /// Track 0 Not Found 44 | const TK0NF = 0b10000000; 45 | /// Address Mark Not Found 46 | const AMNF = 0b10000000; 47 | } 48 | } 49 | 50 | #[derive(Debug, Clone, Copy)] 51 | #[repr(u8)] 52 | pub enum ATAIdentifyError { 53 | BusNotConnected = 0, 54 | NoDevice, 55 | DeviceIsNotATA, 56 | DeviceIsATAPI, 57 | DeviceIsSATA, 58 | Unknown = 255, 59 | } 60 | 61 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] 62 | #[repr(u8)] 63 | #[non_exhaustive] 64 | pub enum ATACommands { 65 | Identify = 0xEC, 66 | WriteSectors = 0x30, 67 | ReadSectors = 0x20, 68 | CacheFlush = 0xE7, 69 | } 70 | 71 | lazy_static! { 72 | pub static ref PRIMARY_ATA_BUS: Arc> = Arc::new(Mutex::new(ATABus::new(0x1F0))); 73 | pub static ref SECONDARY_ATA_BUS: Arc> = Arc::new(Mutex::new(ATABus::new(0x170))); 74 | } 75 | -------------------------------------------------------------------------------- /drivers/ata/src/debug.rs: -------------------------------------------------------------------------------- 1 | use internal_utils::{format_size, serial_println}; 2 | 3 | use crate::{ATABus, ATADisk, ATAIdentifyError}; 4 | 5 | pub fn debug_disks() { 6 | let disk_a = ATABus::get_disk(&*crate::PRIMARY_ATA_BUS, true); 7 | let disk_b = ATABus::get_disk(&*crate::PRIMARY_ATA_BUS, false); 8 | let disk_c = ATABus::get_disk(&*crate::SECONDARY_ATA_BUS, true); 9 | let disk_d = ATABus::get_disk(&*crate::SECONDARY_ATA_BUS, false); 10 | serial_println!("[ ---{:^15}--- ]", "DISKS"); 11 | debug_disk(disk_a, "Primary 1"); 12 | debug_disk(disk_b, "Primary 2"); 13 | debug_disk(disk_c, "Secondary 1"); 14 | debug_disk(disk_d, "Secondary 2"); 15 | } 16 | 17 | fn debug_disk(disk: Result, disk_type: &str) { 18 | if let Ok(disk) = disk { 19 | serial_println!( 20 | "[{:^11}] {:<20}: {} ({} partitions){}", 21 | disk_type, 22 | disk.descriptor.model_number().trim(), 23 | format_size(disk.descriptor.lba_48_addressable_sectors * 512), 24 | disk.clone().get_partitions().map(|p| p.len()).unwrap_or(0), 25 | disk.clone() 26 | .has_bootloader() 27 | .map(|b| if b { " (has bootloader)" } else { "" }) 28 | .unwrap_or(", Error while reading start sector") 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /drivers/ata/src/disk.rs: -------------------------------------------------------------------------------- 1 | use crate::array_combiner::Combiner; 2 | use alloc::{sync::Arc, vec::Vec}; 3 | use spin::Mutex; 4 | 5 | use crate::{ 6 | constants::ErrorRegisterFlags, ATABus, ATAPartition, DiskDescriptor, PartitionDescriptor, 7 | PartitionIOError, 8 | }; 9 | 10 | #[derive(Clone)] 11 | pub struct ATADisk { 12 | pub(crate) bus: Arc>, 13 | pub descriptor: DiskDescriptor, 14 | pub(crate) master: bool, 15 | } 16 | 17 | impl ATADisk { 18 | pub fn has_bootloader(&mut self) -> Result { 19 | let buffer = self.read_sector(0)?; 20 | Ok(buffer[510] == 0x55 && buffer[511] == 0xAA) 21 | } 22 | 23 | pub(crate) fn read_sector(&mut self, lba: u64) -> Result<[u8; 512], ErrorRegisterFlags> { 24 | self.bus.lock().read_sector(self.master, lba) 25 | } 26 | 27 | pub(crate) fn write_sector( 28 | &mut self, 29 | lba: u64, 30 | buffer: &[u8; 512], 31 | ) -> Result<(), ErrorRegisterFlags> { 32 | self.bus.lock().write_sector(self.master, lba, buffer) 33 | } 34 | 35 | pub fn get_partitions(&mut self) -> Result, ErrorRegisterFlags> { 36 | let mbr = self.read_sector(0)?; 37 | let descriptors = mbr[446..510] 38 | .chunks(16) 39 | .filter_map(PartitionDescriptor::from_bytes); 40 | let mut partitions = Vec::new(); 41 | for descriptor in descriptors { 42 | partitions.push(ATAPartition { 43 | disk: self.clone(), 44 | descriptor, 45 | }); 46 | } 47 | Ok(partitions) 48 | } 49 | 50 | pub fn create_partition( 51 | &mut self, 52 | sectors: u32, 53 | partition_type: u8, 54 | ) -> Result { 55 | let mut mbr = self.read_sector(0).map_err(PartitionIOError::ATAError)?; 56 | let descriptors: Vec = mbr[446..510] 57 | .chunks(16) 58 | .filter_map(PartitionDescriptor::from_bytes) 59 | .collect(); 60 | if descriptors.len() >= 4 { 61 | return Err(PartitionIOError::TooManyPartitions); 62 | } 63 | 64 | let start_sector_bytes = { 65 | let start_sector = (descriptors 66 | .iter() 67 | .map(|d| d.start_lba + d.sectors) 68 | .max() 69 | .unwrap_or(0) 70 | + 1) as u32; 71 | u32::to_le_bytes(start_sector) 72 | }; 73 | 74 | let sectors_bytes = u32::to_le_bytes(sectors); 75 | 76 | let partition_bytes = Combiner::new() 77 | .with(&[0x00, 0xFF, 0xFF, 0xFF]) 78 | .with(&[partition_type, 0xFF, 0xFF, 0xFF]) 79 | .with(&start_sector_bytes) 80 | .with(§ors_bytes) 81 | .build::<16>() 82 | .expect("Wrong number of bytes for a partition descriptor"); 83 | 84 | let descriptor = PartitionDescriptor::from_bytes(&partition_bytes); 85 | if descriptor.is_none() { 86 | return Err(PartitionIOError::Unknown); 87 | } 88 | let descriptor = descriptor.unwrap(); 89 | let partition_free_index = mbr[446..510] 90 | .chunks(16) 91 | .enumerate() 92 | .map(|(index, val)| (PartitionDescriptor::from_bytes(val), index)) 93 | .find(|(val, _)| val.is_none()) 94 | .map(|(_, i)| i); 95 | if partition_free_index.is_none() { 96 | return Err(PartitionIOError::Unknown); 97 | } 98 | let partition_free_index = partition_free_index.unwrap(); 99 | mbr[446 + partition_free_index * 16..446 + partition_free_index * 16 + 16] 100 | .copy_from_slice(&partition_bytes); 101 | self.write_sector(0, &mbr) 102 | .map_err(PartitionIOError::ATAError)?; 103 | Ok(ATAPartition { 104 | disk: self.clone(), 105 | descriptor, 106 | }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /drivers/ata/src/disk_descriptor.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq, Clone)] 2 | pub struct DiskDescriptor { 3 | pub fixed_device: bool, 4 | pub removable_media: bool, 5 | pub is_ata_device: bool, 6 | 7 | pub cylinders: u16, 8 | pub heads: u16, 9 | pub sectors_per_track: u16, 10 | pub vendor_unique: [u16; 3], 11 | serial_number_bytes: [u8; 20], 12 | pub firmware_revision: [u8; 8], 13 | model_number_bytes: [u8; 40], 14 | pub udma_available_modes: [bool; 8], 15 | pub udma_current_mode: u8, 16 | pub supports_lba_48: bool, 17 | pub lba_28_addressable_sectors: u32, 18 | pub lba_48_addressable_sectors: u64, 19 | } 20 | 21 | impl DiskDescriptor { 22 | pub fn serial_number(&self) -> &str { 23 | core::str::from_utf8(&self.serial_number_bytes).unwrap() 24 | } 25 | 26 | pub fn model_number(&self) -> &str { 27 | core::str::from_utf8(&self.model_number_bytes).unwrap() 28 | } 29 | 30 | pub(crate) fn from_bytes(buffer: [u16; 256]) -> Self { 31 | let fixed_device = buffer[0] & 0x0040 != 0; 32 | let removable_media = buffer[0] & 0x0080 != 0; 33 | let is_ata_device = buffer[0] & 0x8000 != 0; 34 | 35 | let cylinders = buffer[1]; 36 | let heads = buffer[3]; 37 | let sectors_per_track = buffer[6]; 38 | let vendor_unique = [buffer[7], buffer[8], buffer[9]]; 39 | let mut serial_number = [0; 20]; 40 | for (index, word) in buffer[10..20].iter().enumerate() { 41 | serial_number[index * 2] = (*word >> 8) as u8; 42 | serial_number[index * 2 + 1] = *word as u8; 43 | } 44 | 45 | let mut firmware_revision = [0; 8]; 46 | for (index, word) in buffer[23..26].iter().enumerate() { 47 | firmware_revision[index * 2] = (*word >> 8) as u8; 48 | firmware_revision[index * 2 + 1] = *word as u8; 49 | } 50 | 51 | let mut model_number = [0u8; 40]; 52 | for (index, word) in buffer[27..47].iter().enumerate() { 53 | model_number[index * 2] = (*word >> 8) as u8; 54 | model_number[index * 2 + 1] = *word as u8; 55 | } 56 | 57 | let udma = buffer[88]; 58 | let udma_current_mode = (udma >> 8) as u8; 59 | let udma_available_modes = { 60 | let udma_available_modes = udma as u8; 61 | let mut udma_buffer = [false; 8]; 62 | for (i, item) in udma_buffer.iter_mut().enumerate() { 63 | if udma_available_modes & (1 << i) != 0 { 64 | *item = true; 65 | } 66 | } 67 | udma_buffer 68 | }; 69 | 70 | let supports_lba_48 = buffer[83] & 0x0400 != 0; 71 | let lba_28_addressable_sectors = (buffer[61] as u32) << 16 | (buffer[60] as u32); 72 | let lba_48_addressable_sectors = (buffer[103] as u64) << 48 73 | | (buffer[102] as u64) << 32 74 | | (buffer[101] as u64) << 16 75 | | (buffer[100] as u64); 76 | 77 | Self { 78 | fixed_device, 79 | removable_media, 80 | is_ata_device, 81 | cylinders, 82 | heads, 83 | sectors_per_track, 84 | vendor_unique, 85 | serial_number_bytes: serial_number, 86 | firmware_revision, 87 | model_number_bytes: model_number, 88 | udma_available_modes, 89 | udma_current_mode, 90 | supports_lba_48, 91 | lba_28_addressable_sectors, 92 | lba_48_addressable_sectors, 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /drivers/ata/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // no standard library 2 | #![no_main] 3 | #![allow(incomplete_features)] 4 | #![feature(generic_const_exprs)] 5 | use alloc::vec::Vec; 6 | use internal_utils::structures::{driver::Driver, kernel_information::KernelInformation}; 7 | extern crate alloc; 8 | 9 | mod constants; 10 | pub use constants::{ATAIdentifyError, PRIMARY_ATA_BUS, SECONDARY_ATA_BUS}; 11 | 12 | mod bus; 13 | pub use bus::ATABus; 14 | 15 | mod disk_descriptor; 16 | pub use disk_descriptor::DiskDescriptor; 17 | 18 | mod partition_descriptor; 19 | pub use partition_descriptor::PartitionDescriptor; 20 | 21 | mod disk; 22 | pub use disk::ATADisk; 23 | 24 | mod partition; 25 | pub use partition::{ATAPartition, PartitionIOError}; 26 | 27 | mod array_combiner; 28 | 29 | #[cfg(debug_assertions)] 30 | mod debug; 31 | 32 | pub extern "C" fn driver_init(_kernel_info: KernelInformation) -> Driver { 33 | #[cfg(debug_assertions)] 34 | debug::debug_disks(); 35 | Driver { 36 | signature: [ 37 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf0, 0xf1, 0xf2, 0xf3, 0xf0, 0xf1, 0xf2, 0xf3, 0xf0, 0xf1, 38 | 0xf2, 0xf3, 39 | ], 40 | } 41 | } 42 | 43 | pub fn get_all_disks() -> Vec { 44 | [ 45 | (&*PRIMARY_ATA_BUS, true), 46 | (&*PRIMARY_ATA_BUS, false), 47 | (&*SECONDARY_ATA_BUS, true), 48 | (&*SECONDARY_ATA_BUS, false), 49 | ] 50 | .iter() 51 | .filter_map(|(bus, master)| ATABus::get_disk(bus, *master).ok()) 52 | .collect() 53 | } 54 | -------------------------------------------------------------------------------- /drivers/ata/src/partition.rs: -------------------------------------------------------------------------------- 1 | use crate::{constants::ErrorRegisterFlags, ATADisk, DiskDescriptor, PartitionDescriptor}; 2 | 3 | #[derive(Clone)] 4 | pub struct ATAPartition { 5 | pub(crate) disk: ATADisk, 6 | pub descriptor: PartitionDescriptor, 7 | } 8 | 9 | impl ATAPartition { 10 | pub fn disk_descriptor(&self) -> DiskDescriptor { 11 | self.disk.descriptor.clone() 12 | } 13 | 14 | pub fn read_sector(&mut self, lba: u64) -> Result<[u8; 512], PartitionIOError> { 15 | if lba >= self.descriptor.sectors { 16 | Err(PartitionIOError::AddressNotInRange) 17 | } else { 18 | self.disk 19 | .read_sector(lba + self.descriptor.start_lba) 20 | .map_err(PartitionIOError::ATAError) 21 | } 22 | } 23 | 24 | pub fn write_sector(&mut self, lba: u64, buffer: &[u8; 512]) -> Result<(), PartitionIOError> { 25 | if lba >= self.descriptor.sectors { 26 | Err(PartitionIOError::AddressNotInRange) 27 | } else { 28 | self.disk 29 | .write_sector(lba + self.descriptor.start_lba, buffer) 30 | .map_err(PartitionIOError::ATAError) 31 | } 32 | } 33 | } 34 | 35 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 36 | pub enum PartitionIOError { 37 | ATAError(ErrorRegisterFlags), 38 | AddressNotInRange, 39 | TooManyPartitions, 40 | Unknown, 41 | } 42 | -------------------------------------------------------------------------------- /drivers/ata/src/partition_descriptor.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq, Clone)] 2 | pub struct PartitionDescriptor { 3 | pub bootable: bool, 4 | pub file_system: u8, 5 | pub start_lba: u64, 6 | pub sectors: u64, 7 | } 8 | 9 | impl PartitionDescriptor { 10 | pub(crate) fn from_bytes(bytes: &[u8]) -> Option { 11 | if bytes.iter().all(|b| *b == 0x00) { 12 | return None; 13 | } 14 | Some(PartitionDescriptor { 15 | bootable: bytes[0] == 0x80, 16 | file_system: bytes[4], 17 | start_lba: u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as u64, 18 | sectors: u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]) as u64, 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /drivers/vga/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["workspace-inheritance"] 2 | 3 | [package] 4 | name = "vga" 5 | version = "0.1.0" 6 | edition = { workspace=true } 7 | 8 | [dependencies] 9 | internal_utils = { workspace=true } 10 | kernel = { workspace=true } 11 | noto-sans-mono-bitmap = { workspace=true } 12 | spin = { workspace=true } 13 | tinytga = { workspace=true } 14 | lazy_static = { workspace=true } -------------------------------------------------------------------------------- /drivers/vga/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // no standard library 2 | #![no_main] 3 | #![allow(incomplete_features)] 4 | #![feature(ptr_const_cast, generic_const_exprs, adt_const_params)] 5 | use alloc::boxed::Box; 6 | use core::fmt; 7 | use internal_utils::structures::{driver::Driver, kernel_information::KernelInformation}; 8 | use kernel::logger::Logger; 9 | use vga_core::{Clearable, TextDrawable, CHAR_HEIGHT}; 10 | use vga_device::{VGADevice, VGADeviceFactory}; 11 | extern crate alloc; 12 | 13 | mod pixel_buffer; 14 | pub mod point_2d; 15 | mod static_stack; 16 | pub mod vga_color; 17 | pub mod vga_core; 18 | pub mod vga_device; 19 | 20 | struct VGALogger { 21 | x: u16, 22 | y: u16, 23 | start_x: u16, 24 | device: VGADevice, 25 | took_over: bool, 26 | } 27 | 28 | impl VGALogger { 29 | fn __log(&mut self, text: &str) { 30 | let (x, y) = 31 | self.device 32 | .draw_string(self.x, self.y, vga_color::CHARLOTTE, text, self.start_x); 33 | self.x = x; 34 | self.y = y; 35 | } 36 | } 37 | 38 | impl Logger for VGALogger { 39 | fn log(&mut self, text: &str) { 40 | if !self.took_over { 41 | self.device.clear(vga_color::BSOD_BLUE); 42 | self.took_over = true; 43 | } 44 | self.__log(text); 45 | } 46 | 47 | fn logln(&mut self, text: &str) { 48 | self.log(text); 49 | if self.x > 0 { 50 | self.x = 0; 51 | self.y += CHAR_HEIGHT as u16; 52 | } 53 | } 54 | } 55 | 56 | impl fmt::Write for VGALogger { 57 | /// This will never fail and can always be unwrapped. 58 | fn write_str(&mut self, s: &str) -> fmt::Result { 59 | self.log(s); 60 | Ok(()) 61 | } 62 | } 63 | 64 | pub extern "C" fn driver_init(kernel_info: KernelInformation) -> Driver { 65 | kernel::LOGGER.lock().replace(Box::new(VGALogger { 66 | x: 0, 67 | start_x: 0, 68 | y: 0, 69 | device: VGADeviceFactory::from_kernel_info(kernel_info), 70 | took_over: false, 71 | })); 72 | Driver { 73 | signature: [ 74 | 0xf2, 0xf3, 0xf4, 0xf5, 0xf2, 0xf3, 0xf4, 0xf5, 0xf2, 0xf3, 0xf4, 0xf5, 0xf2, 0xf3, 75 | 0xf4, 0xf5, 76 | ], 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /drivers/vga/src/pixel_buffer.rs: -------------------------------------------------------------------------------- 1 | use internal_utils::div_255_fast; 2 | use internal_utils::structures::kernel_information::PixelFormat; 3 | 4 | use crate::vga_color::VGAColor; 5 | 6 | pub trait PixelBuffer: Send { 7 | fn put_pixel(&mut self, index: usize, color: VGAColor); 8 | } 9 | 10 | pub(crate) struct BasePixelBuffer { 11 | pub frame_pointer: &'static mut [u8], 12 | pub bytes_per_pixel_shift: u8, 13 | } 14 | 15 | impl PixelBuffer for BasePixelBuffer<{ PixelFormat::RGB }> { 16 | #[inline(always)] 17 | fn put_pixel(&mut self, index: usize, color: VGAColor) { 18 | let index = index << self.bytes_per_pixel_shift; 19 | let frame_color = VGAColor { 20 | red: self.frame_pointer[index], 21 | green: self.frame_pointer[index + 1], 22 | blue: self.frame_pointer[index + 2], 23 | alpha: self.frame_pointer[index + 3], 24 | }; 25 | let result_color = VGAColor::interpolate(frame_color, color, color.alpha); 26 | self.frame_pointer[index] = result_color.red; 27 | self.frame_pointer[index + 1] = result_color.green; 28 | self.frame_pointer[index + 2] = result_color.blue; 29 | self.frame_pointer[index + 3] = result_color.alpha; 30 | } 31 | } 32 | 33 | impl PixelBuffer for BasePixelBuffer<{ PixelFormat::BGR }> { 34 | #[inline(always)] 35 | fn put_pixel(&mut self, index: usize, color: VGAColor) { 36 | let index = index << self.bytes_per_pixel_shift; 37 | let frame_color = VGAColor { 38 | red: self.frame_pointer[index + 2], 39 | green: self.frame_pointer[index + 1], 40 | blue: self.frame_pointer[index], 41 | alpha: self.frame_pointer[index + 3], 42 | }; 43 | let result_color = VGAColor::interpolate(frame_color, color, color.alpha); 44 | self.frame_pointer[index + 2] = result_color.red; 45 | self.frame_pointer[index + 1] = result_color.green; 46 | self.frame_pointer[index] = result_color.blue; 47 | self.frame_pointer[index + 3] = result_color.alpha; 48 | } 49 | } 50 | 51 | impl PixelBuffer for BasePixelBuffer<{ PixelFormat::U8 }> { 52 | #[inline(always)] 53 | fn put_pixel(&mut self, index: usize, color: VGAColor) { 54 | let index = index << self.bytes_per_pixel_shift; 55 | let gray = self.frame_pointer[index] as u16; 56 | let color_gray = color.to_grayscale() as u16; 57 | let alpha = color.alpha as u16; 58 | let alpha1 = 255 - alpha; 59 | self.frame_pointer[index] = div_255_fast(gray * alpha1 + color_gray * alpha); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /drivers/vga/src/point_2d.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, Div, Mul, Sub}; 2 | 3 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 4 | pub struct Point2D { 5 | pub x: T, 6 | pub y: T, 7 | } 8 | pub static ZERO: Point2D = Point2D { x: 0, y: 0 }; 9 | 10 | impl Add for Point2D 11 | where 12 | T: core::ops::Add, 13 | { 14 | type Output = Point2D; 15 | 16 | fn add(self, rhs: Self) -> Self::Output { 17 | Point2D { 18 | x: self.x + rhs.x, 19 | y: self.y + rhs.y, 20 | } 21 | } 22 | } 23 | 24 | impl Sub for Point2D 25 | where 26 | T: core::ops::Sub, 27 | { 28 | type Output = Point2D; 29 | 30 | fn sub(self, rhs: Self) -> Self::Output { 31 | Point2D { 32 | x: self.x - rhs.x, 33 | y: self.y - rhs.y, 34 | } 35 | } 36 | } 37 | 38 | impl Mul for Point2D 39 | where 40 | T: core::ops::Mul, 41 | { 42 | type Output = Point2D; 43 | 44 | fn mul(self, rhs: Self) -> Self::Output { 45 | Point2D { 46 | x: self.x * rhs.x, 47 | y: self.y * rhs.y, 48 | } 49 | } 50 | } 51 | 52 | impl Mul for Point2D 53 | where 54 | T: core::ops::Mul + Copy, 55 | { 56 | type Output = Point2D; 57 | 58 | fn mul(self, rhs: T) -> Self::Output { 59 | Point2D { 60 | x: self.x * rhs, 61 | y: self.y * rhs, 62 | } 63 | } 64 | } 65 | 66 | impl Div for Point2D 67 | where 68 | T: core::ops::Div, 69 | { 70 | type Output = Point2D; 71 | 72 | fn div(self, rhs: Self) -> Self::Output { 73 | Point2D { 74 | x: self.x / rhs.x, 75 | y: self.y / rhs.y, 76 | } 77 | } 78 | } 79 | 80 | impl Div for Point2D 81 | where 82 | T: core::ops::Div + Copy, 83 | { 84 | type Output = Point2D; 85 | 86 | fn div(self, rhs: T) -> Self::Output { 87 | Point2D { 88 | x: self.x / rhs, 89 | y: self.y / rhs, 90 | } 91 | } 92 | } 93 | 94 | impl From> for Point2D { 95 | fn from(p: Point2D) -> Self { 96 | Point2D { 97 | x: p.x as f32, 98 | y: p.y as f32, 99 | } 100 | } 101 | } 102 | 103 | impl From> for Point2D { 104 | fn from(p: Point2D) -> Self { 105 | Point2D { 106 | x: p.x as f32, 107 | y: p.y as f32, 108 | } 109 | } 110 | } 111 | 112 | impl From> for Point2D { 113 | fn from(p: Point2D) -> Self { 114 | Point2D { 115 | x: p.x as u32, 116 | y: p.y as u32, 117 | } 118 | } 119 | } 120 | 121 | impl From> for Point2D { 122 | fn from(p: Point2D) -> Self { 123 | Point2D { 124 | x: p.x as u16, 125 | y: p.y as u16, 126 | } 127 | } 128 | } 129 | 130 | impl Point2D 131 | where 132 | T: Ord + core::ops::Sub + Copy, 133 | { 134 | /// Returns the squared distance between two points. 135 | pub fn sqr_distance(self, other: Self) -> V 136 | where 137 | V: core::ops::Mul + core::ops::Add + Copy, 138 | T: Into, 139 | { 140 | let a = (self.x.max(other.x) - self.x.min(other.x)).into(); 141 | let b = (self.y.max(other.y) - self.y.min(other.y)).into(); 142 | a * a + b * b 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /drivers/vga/src/static_stack.rs: -------------------------------------------------------------------------------- 1 | /// A stack with a fixed size. 2 | pub struct StaticStack { 3 | buffer: [T; C], 4 | top: usize, 5 | } 6 | 7 | /// The error type for the `StaticStack` struct. 8 | #[derive(Debug)] 9 | #[repr(u8)] 10 | pub enum StaticStackError { 11 | /// The stack is full. 12 | Eos, 13 | } 14 | 15 | impl StaticStack { 16 | /// Creates a new `StaticStack` of capacity `C` and type `T`. 17 | pub fn new() -> Self { 18 | Self { 19 | buffer: [T::default(); C], 20 | top: 0, 21 | } 22 | } 23 | 24 | /// Pushes a value onto the stack. 25 | /// 26 | /// ## Returns 27 | /// Returns `Ok(())` if the operation was successful, and `Err(StaticStackError::Eos)` if the stack is full. 28 | pub fn push(&mut self, item: &T) -> Result<(), StaticStackError> { 29 | if self.top == C { 30 | return Err(StaticStackError::Eos); 31 | } 32 | self.buffer[self.top] = *item; 33 | self.top += 1; 34 | Ok(()) 35 | } 36 | 37 | /// Pops a value off the stack. 38 | /// 39 | /// ## Returns 40 | /// Returns `Some(T)` if the operation was successful, and `None` if the stack is empty. 41 | pub fn pop(&mut self) -> Option { 42 | if self.top == 0 { 43 | return None; 44 | } 45 | self.top -= 1; 46 | Some(self.buffer[self.top]) 47 | } 48 | 49 | /// Returns the number of elements in the stack. 50 | pub fn length(&self) -> usize { 51 | self.top 52 | } 53 | } 54 | 55 | impl Default for StaticStack { 56 | fn default() -> Self { 57 | Self::new() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /drivers/vga/src/vga_color.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, Div, Mul, Sub}; 2 | 3 | use internal_utils::div_255_fast; 4 | 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | #[repr(C)] 7 | pub struct VGAColor { 8 | pub red: T, 9 | pub green: T, 10 | pub blue: T, 11 | pub alpha: T, 12 | } 13 | pub static TRANSPARENT: VGAColor = VGAColor { 14 | red: 0, 15 | green: 0, 16 | blue: 0, 17 | alpha: 0, 18 | }; 19 | pub static WHITE: VGAColor = VGAColor { 20 | red: 255, 21 | green: 255, 22 | blue: 255, 23 | alpha: 255, 24 | }; 25 | pub static BLACK: VGAColor = VGAColor { 26 | red: 0, 27 | green: 0, 28 | blue: 0, 29 | alpha: 255, 30 | }; 31 | pub static RED: VGAColor = VGAColor { 32 | red: 255, 33 | green: 0, 34 | blue: 0, 35 | alpha: 255, 36 | }; 37 | pub static GREEN: VGAColor = VGAColor { 38 | red: 0, 39 | green: 255, 40 | blue: 0, 41 | alpha: 255, 42 | }; 43 | pub static BLUE: VGAColor = VGAColor { 44 | red: 0, 45 | green: 0, 46 | blue: 255, 47 | alpha: 255, 48 | }; 49 | pub static CLAY: VGAColor = VGAColor { 50 | red: 128, 51 | green: 64, 52 | blue: 11, 53 | alpha: 255, 54 | }; 55 | pub static BSOD_BLUE: VGAColor = VGAColor { 56 | red: 9, 57 | green: 78, 58 | blue: 130, 59 | alpha: 255, 60 | }; 61 | pub static CHARLOTTE: VGAColor = VGAColor { 62 | red: 161, 63 | green: 232, 64 | blue: 223, 65 | alpha: 255, 66 | }; 67 | 68 | impl VGAColor { 69 | #[inline(always)] 70 | pub const fn from_rgba(data: &[u8]) -> VGAColor { 71 | VGAColor { 72 | red: data[0], 73 | green: data[1], 74 | blue: data[2], 75 | alpha: data[3], 76 | } 77 | } 78 | 79 | #[inline(always)] 80 | pub const fn from_bgra(data: &[u8]) -> VGAColor { 81 | VGAColor { 82 | blue: data[0], 83 | green: data[1], 84 | red: data[2], 85 | alpha: data[3], 86 | } 87 | } 88 | 89 | /// Interpolates between two colors, where t=0 -> First color, t=255 -> Second color 90 | pub fn interpolate(a: VGAColor, b: VGAColor, t: u8) -> VGAColor { 91 | let _t = t as u16; 92 | let t1 = 255 - _t; 93 | VGAColor { 94 | red: div_255_fast(a.red as u16 * t1 + b.red as u16 * _t), 95 | green: div_255_fast(a.green as u16 * t1 + b.green as u16 * _t), 96 | blue: div_255_fast(a.blue as u16 * t1 + b.blue as u16 * _t), 97 | alpha: div_255_fast(a.alpha as u16 * t1 + b.alpha as u16 * _t), 98 | } 99 | } 100 | 101 | #[inline(always)] 102 | pub const fn rgba(&self) -> [u8; 4] { 103 | [self.red, self.green, self.blue, self.alpha] 104 | } 105 | 106 | #[inline(always)] 107 | pub const fn bgra(&self) -> [u8; 4] { 108 | [self.blue, self.green, self.red, self.alpha] 109 | } 110 | } 111 | 112 | impl Add for VGAColor 113 | where 114 | T: core::ops::Add, 115 | { 116 | type Output = VGAColor; 117 | 118 | fn add(self, rhs: Self) -> Self::Output { 119 | VGAColor { 120 | red: self.red + rhs.red, 121 | green: self.green + rhs.green, 122 | blue: self.blue + rhs.blue, 123 | alpha: self.alpha + rhs.alpha, 124 | } 125 | } 126 | } 127 | 128 | impl Sub for VGAColor 129 | where 130 | T: core::ops::Sub, 131 | { 132 | type Output = VGAColor; 133 | 134 | fn sub(self, rhs: Self) -> Self::Output { 135 | VGAColor { 136 | red: self.red - rhs.red, 137 | green: self.green - rhs.green, 138 | blue: self.blue - rhs.blue, 139 | alpha: self.alpha - rhs.alpha, 140 | } 141 | } 142 | } 143 | 144 | impl Mul for VGAColor 145 | where 146 | T: core::ops::Mul, 147 | { 148 | type Output = VGAColor; 149 | 150 | fn mul(self, rhs: Self) -> Self::Output { 151 | VGAColor { 152 | red: self.red * rhs.red, 153 | green: self.green * rhs.green, 154 | blue: self.blue * rhs.blue, 155 | alpha: self.alpha * rhs.alpha, 156 | } 157 | } 158 | } 159 | 160 | impl Mul for VGAColor 161 | where 162 | T: core::ops::Mul + Copy, 163 | { 164 | type Output = VGAColor; 165 | 166 | fn mul(self, rhs: T) -> Self::Output { 167 | VGAColor { 168 | red: self.red * rhs, 169 | green: self.green * rhs, 170 | blue: self.blue * rhs, 171 | alpha: self.alpha * rhs, 172 | } 173 | } 174 | } 175 | 176 | impl Div for VGAColor 177 | where 178 | T: core::ops::Div, 179 | { 180 | type Output = VGAColor; 181 | 182 | fn div(self, rhs: Self) -> Self::Output { 183 | VGAColor { 184 | red: self.red / rhs.red, 185 | green: self.green / rhs.green, 186 | blue: self.blue / rhs.blue, 187 | alpha: self.alpha / rhs.alpha, 188 | } 189 | } 190 | } 191 | 192 | impl Div for VGAColor 193 | where 194 | T: core::ops::Div + Copy, 195 | { 196 | type Output = VGAColor; 197 | 198 | fn div(self, rhs: T) -> Self::Output { 199 | VGAColor { 200 | red: self.red / rhs, 201 | green: self.green / rhs, 202 | blue: self.blue / rhs, 203 | alpha: self.alpha / rhs, 204 | } 205 | } 206 | } 207 | 208 | impl VGAColor 209 | where 210 | T: Into + Copy, 211 | { 212 | /// Returns the value for the grayscale version of the color, using the human light perception formula 213 | pub fn to_grayscale(self) -> u32 { 214 | (self.red.into() * 299 + self.green.into() * 587 + self.blue.into() * 114) / 1000 215 | } 216 | } 217 | 218 | impl VGAColor { 219 | /// Multiplies only the alpha value by the opacity value, returning a new color with alpha scaled back to 0-255. 220 | pub fn mul_alpha(self, opacity: u8) -> VGAColor { 221 | VGAColor { 222 | red: self.red, 223 | green: self.green, 224 | blue: self.blue, 225 | alpha: div_255_fast(self.alpha as u16 * opacity as u16), 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /drivers/vga/src/vga_core.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use noto_sans_mono_bitmap::{get_bitmap, get_bitmap_width, BitmapChar, BitmapHeight, FontWeight}; 3 | use tinytga::RawTga; 4 | 5 | use super::{point_2d::Point2D, vga_color::VGAColor}; 6 | 7 | pub const CHAR_HEIGHT: BitmapHeight = BitmapHeight::Size14; 8 | pub const CHAR_WEIGHT: FontWeight = FontWeight::Regular; 9 | pub const CHAR_WIDTH: u16 = get_bitmap_width(CHAR_WEIGHT, CHAR_HEIGHT) as u16; 10 | lazy_static! { 11 | pub static ref INVALID_CHAR: BitmapChar = get_bitmap(' ', CHAR_WEIGHT, CHAR_HEIGHT).unwrap(); 12 | } 13 | 14 | pub trait Clearable { 15 | fn clear(&mut self, color: VGAColor); 16 | } 17 | 18 | pub trait PlainDrawable { 19 | fn draw_point(&mut self, x: u16, y: u16, color: VGAColor); 20 | fn draw_point_p(&mut self, p: Point2D, color: VGAColor); 21 | fn draw_line(&mut self, x1: u16, y1: u16, x2: u16, y2: u16, color: VGAColor); 22 | fn draw_line_p(&mut self, a: Point2D, b: Point2D, color: VGAColor); 23 | fn draw_bezier( 24 | &mut self, 25 | p1: Point2D, 26 | p2: Point2D, 27 | p3: Point2D, 28 | p4: Point2D, 29 | color: VGAColor, 30 | ); 31 | } 32 | 33 | pub trait ShapeDrawable { 34 | fn draw_rectangle(&mut self, x: u16, y: u16, width: u16, height: u16, color: VGAColor); 35 | fn draw_rectangle_p(&mut self, min: Point2D, max: Point2D, color: VGAColor); 36 | fn fill_rectangle(&mut self, x: u16, y: u16, width: u16, height: u16, color: VGAColor); 37 | fn fill_rectangle_p(&mut self, min: Point2D, max: Point2D, color: VGAColor); 38 | } 39 | 40 | pub trait TextDrawable { 41 | fn draw_string( 42 | &mut self, 43 | x: u16, 44 | y: u16, 45 | color: VGAColor, 46 | text: &str, 47 | reset_x: u16, 48 | ) -> (u16, u16); 49 | fn measure_string(&self, x: u16, y: u16, text: &str, reset_x: u16) -> (u16, u16); 50 | } 51 | 52 | pub trait ImageDrawable { 53 | fn draw_image(&mut self, x: u16, y: u16, image: &RawTga); 54 | fn draw_image_p(&mut self, p: Point2D, image: &RawTga); 55 | } 56 | -------------------------------------------------------------------------------- /drivers/vga/src/vga_device.rs: -------------------------------------------------------------------------------- 1 | use crate::static_stack::StaticStack; 2 | use alloc::{boxed::Box, slice}; 3 | use internal_utils::structures::kernel_information::{KernelInformation, PixelFormat}; 4 | use noto_sans_mono_bitmap::{get_bitmap, BitmapChar}; 5 | use tinytga::RawTga; 6 | 7 | use crate::{ 8 | pixel_buffer::{BasePixelBuffer, PixelBuffer}, 9 | vga_core::{ImageDrawable, CHAR_HEIGHT, INVALID_CHAR}, 10 | }; 11 | 12 | use super::{ 13 | point_2d::Point2D, 14 | vga_color::VGAColor, 15 | vga_core::{Clearable, PlainDrawable, ShapeDrawable, TextDrawable, CHAR_WEIGHT, CHAR_WIDTH}, 16 | }; 17 | 18 | pub struct VGADevice { 19 | pub width: usize, 20 | pub height: usize, 21 | pub stride: usize, 22 | pixel_buffer: Box, 23 | } 24 | 25 | impl Clearable for VGADevice { 26 | fn clear(&mut self, color: VGAColor) { 27 | for x in 0..self.width { 28 | for y in 0..self.height { 29 | self.draw_point(x as u16, y as u16, color); 30 | } 31 | } 32 | } 33 | } 34 | 35 | impl PlainDrawable for VGADevice { 36 | #[inline(always)] 37 | fn draw_point(&mut self, x: u16, y: u16, color: VGAColor) { 38 | let x = x as usize; 39 | let y = y as usize; 40 | let index = (y * self.stride) + x; 41 | self.pixel_buffer.put_pixel(index, color); 42 | } 43 | fn draw_point_p(&mut self, p: Point2D, color: VGAColor) { 44 | self.draw_point(p.x, p.y, color); 45 | } 46 | 47 | fn draw_line(&mut self, x1: u16, y1: u16, x2: u16, y2: u16, color: VGAColor) { 48 | let x2 = x2 as i16; 49 | let y2 = y2 as i16; 50 | // Bresenham's algorithm 51 | 52 | let mut x1 = x1 as i16; 53 | let mut y1 = y1 as i16; 54 | 55 | let xi: i16; 56 | let dx: i16; 57 | if x1 < x2 { 58 | xi = 1; 59 | dx = x2 - x1; 60 | } else { 61 | xi = -1; 62 | dx = x1 - x2; 63 | } 64 | 65 | let yi: i16; 66 | let dy: i16; 67 | if y1 < y2 { 68 | yi = 1; 69 | dy = y2 - y1; 70 | } else { 71 | yi = -1; 72 | dy = y1 - y2; 73 | } 74 | self.draw_point(x1 as u16, y1 as u16, color); 75 | 76 | let ai; 77 | let bi; 78 | let mut d: i16; 79 | // OX axis 80 | if dx > dy { 81 | ai = (dy - dx) * 2; 82 | bi = dy * 2; 83 | d = bi - dx; 84 | // Loop over next Xs 85 | while x1 != x2 { 86 | if d >= 0 { 87 | x1 += xi; 88 | y1 += yi; 89 | d += ai; 90 | } else { 91 | d += bi; 92 | x1 += xi; 93 | } 94 | self.draw_point(x1 as u16, y1 as u16, color); 95 | } 96 | } 97 | // OY axis 98 | else { 99 | ai = (dx - dy) * 2; 100 | bi = dx * 2; 101 | d = bi - dy; 102 | // Loop over next Ys 103 | while y1 != y2 { 104 | if d >= 0 { 105 | x1 += xi; 106 | y1 += yi; 107 | d += ai; 108 | } else { 109 | d += bi; 110 | y1 += yi; 111 | } 112 | self.draw_point(x1 as u16, y1 as u16, color); 113 | } 114 | } 115 | } 116 | fn draw_line_p(&mut self, a: Point2D, b: Point2D, color: VGAColor) { 117 | self.draw_line(a.x, a.y, b.x, b.y, color); 118 | } 119 | 120 | fn draw_bezier( 121 | &mut self, 122 | p1: Point2D, 123 | p2: Point2D, 124 | p3: Point2D, 125 | p4: Point2D, 126 | color: VGAColor, 127 | ) { 128 | let mut t_stack: StaticStack<(f32, f32), 32> = StaticStack::new(); 129 | t_stack.push(&(0f32, 1f32)).unwrap(); 130 | while t_stack.length() > 0 { 131 | let frame = t_stack.pop().unwrap(); 132 | let a = bezier_point(p1, p2, p3, p4, frame.0); 133 | let b = bezier_point(p1, p2, p3, p4, frame.1); 134 | if a.sqr_distance::(b) > 16 { 135 | let mid = (frame.1 + frame.0) * 0.5; 136 | t_stack.push(&(frame.0, mid)).unwrap(); 137 | t_stack.push(&(mid, frame.1)).unwrap(); 138 | } else { 139 | self.draw_line_p(a, b, color); 140 | } 141 | } 142 | } 143 | } 144 | 145 | fn bezier_point( 146 | p1: Point2D, 147 | p2: Point2D, 148 | p3: Point2D, 149 | p4: Point2D, 150 | t: f32, 151 | ) -> Point2D { 152 | let t_1 = 1f32 - t; 153 | let t2 = t * t; 154 | let t3 = t2 * t; 155 | let _p1: Point2D = p1.into(); 156 | let _p2: Point2D = (p2 * 3).into(); 157 | let _p3: Point2D = (p3 * 3).into(); 158 | let _p4: Point2D = p4.into(); 159 | (((_p1 * t_1 + _p2 * t) * t_1 + _p3 * t2) * t_1 + _p4 * t3).into() 160 | } 161 | 162 | impl ShapeDrawable for VGADevice { 163 | fn draw_rectangle(&mut self, x: u16, y: u16, width: u16, height: u16, color: VGAColor) { 164 | self.draw_line(x, y, x + width, y, color); 165 | self.draw_line(x, y + height, x + width, y + height, color); 166 | self.draw_line(x, y, x, y + height, color); 167 | self.draw_line(x + width, y, x + width, y + height, color); 168 | } 169 | fn draw_rectangle_p(&mut self, min: Point2D, max: Point2D, color: VGAColor) { 170 | self.draw_rectangle(min.x, min.y, max.x - min.x, max.y - min.y, color); 171 | } 172 | 173 | fn fill_rectangle(&mut self, x: u16, y: u16, width: u16, height: u16, color: VGAColor) { 174 | for i in x..x + width { 175 | for j in y..y + height { 176 | self.draw_point(i, j, color); 177 | } 178 | } 179 | } 180 | fn fill_rectangle_p(&mut self, min: Point2D, max: Point2D, color: VGAColor) { 181 | self.fill_rectangle(min.x, min.y, max.x - min.x, max.y - min.y, color); 182 | } 183 | } 184 | 185 | impl TextDrawable for VGADevice { 186 | fn draw_string( 187 | &mut self, 188 | x: u16, 189 | y: u16, 190 | color: VGAColor, 191 | text: &str, 192 | reset_x: u16, 193 | ) -> (u16, u16) { 194 | let mut pos_x = x; 195 | let mut pos_y = y; 196 | for (_i, c) in text.chars().enumerate() { 197 | match c { 198 | '\n' => { 199 | pos_x = reset_x; 200 | pos_y += CHAR_HEIGHT as u16; 201 | } 202 | _ => { 203 | if pos_x + CHAR_WIDTH > self.width as u16 { 204 | pos_x = reset_x; 205 | pos_y += CHAR_HEIGHT as u16; 206 | } 207 | let invalid_char = &*INVALID_CHAR; 208 | let bitmap_char = get_bitmap(c, CHAR_WEIGHT, CHAR_HEIGHT); 209 | self.draw_char( 210 | pos_x, 211 | pos_y, 212 | bitmap_char.as_ref().unwrap_or(invalid_char), 213 | color, 214 | ); 215 | pos_x += CHAR_WIDTH; 216 | } 217 | } 218 | } 219 | (pos_x, pos_y) 220 | } 221 | 222 | fn measure_string(&self, x: u16, y: u16, text: &str, reset_x: u16) -> (u16, u16) { 223 | let mut pos_x = x; 224 | let mut pos_y = y; 225 | for (_i, c) in text.chars().enumerate() { 226 | match c { 227 | '\n' => { 228 | pos_x = reset_x; 229 | pos_y += CHAR_HEIGHT as u16; 230 | } 231 | _ => { 232 | pos_x += CHAR_WIDTH; 233 | if pos_x > self.width as u16 { 234 | pos_x = reset_x; 235 | pos_y += CHAR_HEIGHT as u16; 236 | } 237 | } 238 | } 239 | } 240 | (pos_x, pos_y) 241 | } 242 | } 243 | 244 | impl ImageDrawable for VGADevice { 245 | fn draw_image(&mut self, x0: u16, y0: u16, image: &RawTga) { 246 | for pixel in image.pixels() { 247 | let pos = pixel.position; 248 | let color = VGAColor:: { 249 | red: (pixel.color >> 16) as u8, 250 | green: (pixel.color >> 8) as u8, 251 | blue: pixel.color as u8, 252 | alpha: 255, 253 | }; 254 | self.draw_point(pos.x as u16 + x0, pos.y as u16 + y0, color); 255 | } 256 | } 257 | fn draw_image_p(&mut self, pos: Point2D, image: &RawTga) { 258 | self.draw_image(pos.x, pos.y, image); 259 | } 260 | } 261 | 262 | impl VGADevice { 263 | fn draw_char(&mut self, x: u16, y: u16, char: &BitmapChar, color: VGAColor) { 264 | for (iy, row) in char.bitmap().iter().enumerate() { 265 | for (ix, byte) in row.iter().enumerate() { 266 | self.draw_point(ix as u16 + x, iy as u16 + y, color.mul_alpha(*byte)); 267 | } 268 | } 269 | } 270 | } 271 | 272 | pub struct VGADeviceFactory; 273 | 274 | impl VGADeviceFactory { 275 | pub fn from_kernel_info(kernel_info: KernelInformation) -> VGADevice { 276 | let buffer = kernel_info.framebuffer.as_ref().unwrap(); 277 | VGADevice { 278 | width: buffer.width, 279 | height: buffer.height, 280 | stride: buffer.stride, 281 | pixel_buffer: match buffer.format { 282 | PixelFormat::RGB => Box::new(BasePixelBuffer::<{ PixelFormat::RGB }> { 283 | bytes_per_pixel_shift: log2(buffer.bytes_per_pixel), 284 | frame_pointer: unsafe { 285 | slice::from_raw_parts_mut::( 286 | buffer.buffer, 287 | buffer.bytes_per_pixel * buffer.stride * buffer.height, 288 | ) 289 | }, 290 | }), 291 | PixelFormat::BGR => Box::new(BasePixelBuffer::<{ PixelFormat::BGR }> { 292 | bytes_per_pixel_shift: log2(buffer.bytes_per_pixel), 293 | frame_pointer: unsafe { 294 | slice::from_raw_parts_mut::( 295 | buffer.buffer, 296 | buffer.bytes_per_pixel * buffer.stride * buffer.height, 297 | ) 298 | }, 299 | }), 300 | PixelFormat::U8 => Box::new(BasePixelBuffer::<{ PixelFormat::U8 }> { 301 | bytes_per_pixel_shift: log2(buffer.bytes_per_pixel), 302 | frame_pointer: unsafe { 303 | slice::from_raw_parts_mut::( 304 | buffer.buffer, 305 | buffer.bytes_per_pixel * buffer.stride * buffer.height, 306 | ) 307 | }, 308 | }), 309 | }, 310 | } 311 | } 312 | } 313 | 314 | fn log2(value: usize) -> u8 { 315 | match value { 316 | 1 => 0, 317 | 2 => 1, 318 | 3 => 2, 319 | 4 => 2, 320 | // Low chance of getting that, screens generally don't support over 4 bytes per pixel 321 | _ => unimplemented!("log2 needs more values"), 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /internal_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["workspace-inheritance"] 2 | 3 | [package] 4 | name = "internal_utils" 5 | version = "0.1.0" 6 | edition = { workspace=true } 7 | 8 | [dependencies] 9 | x86_64 = { workspace=true } 10 | bootloader = { workspace=true } 11 | spin = { workspace=true } 12 | uart_16550 = { workspace=true } 13 | lazy_static = { workspace=true } -------------------------------------------------------------------------------- /internal_utils/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const KIB: u64 = 1 << 10; 2 | pub const MIB: u64 = 1 << 20; 3 | pub const GIB: u64 = 1 << 30; 4 | -------------------------------------------------------------------------------- /internal_utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // no standard library 2 | #![no_main] 3 | #![allow(incomplete_features)] 4 | #![feature(generic_const_exprs, core_intrinsics)] 5 | 6 | use alloc::{format, string::String}; 7 | use core::arch::asm; 8 | use x86_64::structures::paging::{FrameAllocator, FrameDeallocator, Size2MiB, Size4KiB}; 9 | 10 | extern crate alloc; 11 | pub mod constants; 12 | use crate::constants::{GIB, KIB, MIB}; 13 | pub mod port_extensions; 14 | pub mod serial; 15 | pub mod structures; 16 | 17 | /// Formats the size in bytes to a human readable string. 18 | pub fn format_size(bytes: u64) -> String { 19 | match bytes { 20 | b if b < KIB => format!("{}B", b), 21 | b if b < MIB => format!("{}KiB", b / KIB), 22 | b if b < GIB => format!("{}MiB", b / MIB), 23 | b => format!("{}GiB", b / GIB), 24 | } 25 | } 26 | 27 | #[macro_export] 28 | /// Macro for pushing all registers onto the stack. 29 | macro_rules! push_all { 30 | () => { 31 | "push rax;push rbx;push rcx;push rdx;push rbp;push rsi;push rdi;push r8;push r9;push r10;push r11;push r12;push r13;push r14;push r15" 32 | }; 33 | } 34 | 35 | #[macro_export] 36 | /// Macro for popping all registers from the stack. 37 | macro_rules! pop_all { 38 | () => { 39 | "pop r15;pop r14;pop r13;pop r12;pop r11;pop r10;pop r9;pop r8;pop rdi;pop rsi;pop rbp;pop rdx;pop rcx;pop rbx;pop rax" 40 | }; 41 | } 42 | 43 | #[macro_export] 44 | /// Macro for mov'ing all registers from a RegistersState struct stored in r9. 45 | macro_rules! mov_all { 46 | () => { 47 | "mov r15, [r9]; mov r14, [r9 + 8]; mov r13, [r9 + 16]; mov r12, [r9 + 24]; mov r11, [r9 + 32]; mov r10, [r9 + 40]; mov r8, [r9 + 56]; mov rdi, [r9 + 64]; mov rsi, [r9 + 72]; mov rbp, [r9 + 80]; mov rdx, [r9 + 88]; mov rcx, [r9 + 96]; mov rbx, [r9 + 104]; mov rax, [r9 + 112]; mov r9, [r9 + 48]" 48 | }; 49 | } 50 | 51 | #[inline(always)] 52 | /// Returns the current CPU tick. May be off a bit. 53 | pub fn get_current_tick() -> u64 { 54 | let start_tick_low: u32; 55 | let start_tick_high: u32; 56 | unsafe { 57 | asm!( 58 | "rdtsc", 59 | out("eax")(start_tick_low), 60 | out("edx")(start_tick_high) 61 | ); 62 | } 63 | u64::from(start_tick_low) | (u64::from(start_tick_high) << 32) 64 | } 65 | 66 | #[inline(always)] 67 | /// Fast division by 255 using additions and shifts. 68 | pub fn div_255_fast(x: u16) -> u8 { 69 | (((x) + (((x) + 257) >> 8)) >> 8) as u8 70 | } 71 | 72 | pub trait FullFrameAllocator: 73 | FrameAllocator 74 | + FrameAllocator 75 | + FrameDeallocator 76 | + FrameDeallocator 77 | { 78 | /// Returns total memory available in the system. 79 | fn get_total_memory_size(&self) -> u64; 80 | /// Returns the amount of memory free to use. 81 | fn get_free_memory_size(&self) -> u64; 82 | } 83 | -------------------------------------------------------------------------------- /internal_utils/src/port_extensions.rs: -------------------------------------------------------------------------------- 1 | use x86_64::{ 2 | instructions::port::{PortGeneric, PortReadAccess, PortWriteAccess}, 3 | structures::port::{PortRead, PortWrite}, 4 | }; 5 | 6 | pub trait PortExtRead { 7 | /// Reads a given number of values from the port and into a buffer. 8 | /// 9 | /// ## Safety 10 | /// 11 | /// This function is unsafe because the I/O port could have side effects that violate memory 12 | /// safety. 13 | unsafe fn read_to_buffer(&mut self, buffer: &mut [T]); 14 | } 15 | 16 | pub trait PortExtWrite { 17 | /// Reads a given number of values from the port and into a buffer. 18 | /// 19 | /// ## Safety 20 | /// 21 | /// This function is unsafe because the I/O port could have side effects that violate memory 22 | /// safety. 23 | unsafe fn write_from_buffer(&mut self, buffer: &[T]); 24 | } 25 | 26 | impl PortExtRead for PortGeneric { 27 | #[inline] 28 | unsafe fn read_to_buffer(self: &mut PortGeneric, buffer: &mut [T]) { 29 | for data in buffer { 30 | *data = self.read(); 31 | } 32 | } 33 | } 34 | 35 | impl PortExtRead for PortGeneric { 36 | #[inline] 37 | unsafe fn read_to_buffer(self: &mut PortGeneric, buffer: &mut [u8]) { 38 | let mut index = 0; 39 | while index < buffer.len() { 40 | let value = self.read(); 41 | buffer[index] = value as u8; 42 | index += 1; 43 | buffer[index] = (value >> 8) as u8; 44 | index += 1; 45 | } 46 | } 47 | } 48 | 49 | impl PortExtWrite for PortGeneric { 50 | /// Writes a buffer to the port. 51 | /// 52 | /// ## Safety 53 | /// 54 | /// This function is unsafe because the I/O port could have side effects that violate memory 55 | /// safety. 56 | #[inline] 57 | unsafe fn write_from_buffer(self: &mut PortGeneric, buffer: &[u8]) { 58 | let mut index = 0; 59 | while index < buffer.len() { 60 | let mut value = buffer[index] as u16; 61 | index += 1; 62 | value |= (buffer[index] as u16) << 8; 63 | self.write(value); 64 | index += 1; 65 | } 66 | } 67 | } 68 | 69 | impl PortExtWrite for PortGeneric { 70 | /// Writes a buffer to the port. 71 | /// 72 | /// ## Safety 73 | /// 74 | /// This function is unsafe because the I/O port could have side effects that violate memory 75 | /// safety. 76 | #[inline] 77 | unsafe fn write_from_buffer(self: &mut PortGeneric, buffer: &[T]) { 78 | for data in buffer { 79 | self.write(*data); 80 | x86_64::instructions::nop(); // We need a tiny delay when batch-writing to IO ports 81 | x86_64::instructions::nop(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /internal_utils/src/serial.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use spin::Mutex; 3 | use uart_16550::SerialPort; 4 | 5 | /// Prints to the host through the serial interface. 6 | #[macro_export] 7 | macro_rules! serial_print { 8 | ($($arg:tt)*) => { 9 | $crate::serial::__print(format_args!($($arg)*)); 10 | }; 11 | } 12 | 13 | /// Prints to the host through the serial interface, appending a newline. 14 | #[macro_export] 15 | macro_rules! serial_println { 16 | () => ($crate::serial_print!("\r\n")); 17 | ($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\r\n"))); 18 | ($fmt:expr, $($arg:tt)*) => ($crate::serial_print!( 19 | concat!($fmt, "\r\n"), $($arg)*)); 20 | } 21 | 22 | #[doc(hidden)] 23 | pub fn __print(args: ::core::fmt::Arguments) { 24 | use core::fmt::Write; 25 | use x86_64::instructions::interrupts; 26 | 27 | interrupts::without_interrupts(|| { 28 | SERIAL1 29 | .lock() 30 | .write_fmt(args) 31 | .expect("Printing to serial failed"); 32 | }); 33 | } 34 | 35 | lazy_static! { 36 | static ref SERIAL1: Mutex = { 37 | let mut serial_port = unsafe { SerialPort::new(0x3F8) }; 38 | serial_port.init(); 39 | Mutex::new(serial_port) 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /internal_utils/src/structures.rs: -------------------------------------------------------------------------------- 1 | pub mod driver; 2 | pub mod kernel_information; 3 | -------------------------------------------------------------------------------- /internal_utils/src/structures/driver.rs: -------------------------------------------------------------------------------- 1 | use super::kernel_information::KernelInformation; 2 | 3 | /// extern C for future compatibility with loading drivers from the file system 4 | pub type Registrator = extern "C" fn(KernelInformation) -> Driver; 5 | 6 | #[derive(Clone, Copy)] 7 | #[repr(C)] 8 | pub struct Driver { 9 | /// The signature of the driver. Should be unique through all the drivers. 10 | pub signature: [u8; 16], 11 | } 12 | -------------------------------------------------------------------------------- /internal_utils/src/structures/kernel_information.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use bootloader::{ 3 | boot_info::{FrameBuffer, MemoryRegions, Optional}, 4 | BootInfo, 5 | }; 6 | use spin::Mutex; 7 | use x86_64::PhysAddr; 8 | 9 | use crate::FullFrameAllocator; 10 | 11 | #[derive(Clone)] 12 | #[repr(C)] 13 | pub struct KernelInformation { 14 | pub bootloader_version: [u16; 3], 15 | pub physical_memory_offset: u64, 16 | pub framebuffer: Optional, 17 | pub memory_regions: &'static MemoryRegions, 18 | pub allocator: Arc>, 19 | /// The start address of the kernel space in all page maps 20 | pub kernel_start: PhysAddr, 21 | } 22 | 23 | #[derive(Clone, Copy)] 24 | #[repr(C)] 25 | pub struct KernelFrameBuffer { 26 | pub width: usize, 27 | pub height: usize, 28 | pub format: PixelFormat, 29 | pub bytes_per_pixel: usize, 30 | pub stride: usize, 31 | pub buffer: *mut u8, 32 | } 33 | 34 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 35 | #[repr(C)] 36 | pub enum PixelFormat { 37 | /// One byte red, then one byte green, then one byte blue. 38 | /// 39 | /// Length might be larger than 3, check [`bytes_per_pixel`][FrameBufferInfo::bytes_per_pixel] 40 | /// for this. 41 | RGB, 42 | /// One byte blue, then one byte green, then one byte red. 43 | /// 44 | /// Length might be larger than 3, check [`bytes_per_pixel`][FrameBufferInfo::bytes_per_pixel] 45 | /// for this. 46 | BGR, 47 | /// A single byte, representing the grayscale value. 48 | /// 49 | /// Length might be larger than 1, check [`bytes_per_pixel`][FrameBufferInfo::bytes_per_pixel] 50 | /// for this. 51 | U8, 52 | } 53 | 54 | impl KernelFrameBuffer { 55 | pub(crate) fn new(buffer: &FrameBuffer) -> KernelFrameBuffer { 56 | let info = buffer.info(); 57 | KernelFrameBuffer { 58 | width: info.horizontal_resolution, 59 | height: info.vertical_resolution, 60 | format: match info.pixel_format { 61 | bootloader::boot_info::PixelFormat::RGB => PixelFormat::RGB, 62 | bootloader::boot_info::PixelFormat::BGR => PixelFormat::BGR, 63 | bootloader::boot_info::PixelFormat::U8 => PixelFormat::U8, 64 | _ => panic!("Unsupported pixel format: {:?}", info.pixel_format), 65 | }, 66 | bytes_per_pixel: info.bytes_per_pixel, 67 | stride: info.stride, 68 | buffer: buffer.buffer().as_ptr() as *mut u8, 69 | } 70 | } 71 | } 72 | 73 | impl KernelInformation { 74 | pub fn new( 75 | boot_info: &'static BootInfo, 76 | allocator: Arc>, 77 | ) -> KernelInformation { 78 | let bootloader_version = [ 79 | boot_info.version_major, 80 | boot_info.version_minor, 81 | boot_info.version_patch, 82 | ]; 83 | let framebuffer = match boot_info.framebuffer.as_ref() { 84 | Some(framebuffer) => Optional::Some(KernelFrameBuffer::new(framebuffer)), 85 | None => Optional::None, 86 | }; 87 | KernelInformation { 88 | bootloader_version, 89 | physical_memory_offset: *boot_info 90 | .physical_memory_offset 91 | .as_ref() 92 | .expect("No physical memory mapping"), 93 | framebuffer, 94 | memory_regions: &boot_info.memory_regions, 95 | allocator, 96 | kernel_start: PhysAddr::new(0x007F_C000_0000u64), 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["workspace-inheritance"] 2 | 3 | [package] 4 | name = "kernel" 5 | version = "0.1.0" 6 | edition = { workspace=true } 7 | 8 | [dependencies] 9 | internal_utils = { workspace=true } 10 | bootloader = { workspace=true } 11 | spin = { workspace=true } 12 | x86_64 = { workspace=true } 13 | uart_16550 = { workspace=true } 14 | pic8259 = { workspace=true } 15 | pc-keyboard = { workspace=true } 16 | linked_list_allocator = { workspace=true } 17 | lazy_static = { workspace=true } 18 | test_framework = { workspace=true } 19 | -------------------------------------------------------------------------------- /kernel/src/debug.rs: -------------------------------------------------------------------------------- 1 | use bootloader::boot_info::{MemoryRegionKind, MemoryRegions}; 2 | use internal_utils::{serial_println, FullFrameAllocator}; 3 | 4 | use crate::memory::frame_allocator::BitmapFrameAllocator; 5 | 6 | #[inline(always)] 7 | pub fn log(msg: &str) { 8 | #[cfg(debug_assertions)] 9 | serial_println!("[debug] {}", msg); 10 | } 11 | 12 | #[inline(always)] 13 | pub fn print_frame_memory(allocator: &BitmapFrameAllocator) { 14 | #[cfg(debug_assertions)] 15 | { 16 | serial_println!("[ ---{:^15}--- ]", "FRAME ALLOCATOR"); 17 | { 18 | let mut size = allocator.get_total_memory_size(); 19 | let mut size_format = "B"; 20 | if size >= 2 * 1024 { 21 | if size < 2 * 1024 * 1024 { 22 | size /= 1024; 23 | size_format = "KiB"; 24 | } else if size < 2 * 1024 * 1024 * 1024 { 25 | size /= 1024 * 1024; 26 | size_format = "MiB"; 27 | } else { 28 | size /= 1024 * 1024 * 1024; 29 | size_format = "GiB"; 30 | } 31 | } 32 | serial_println!("[debug] Total memory: {:>4}{:>3}", size, size_format); 33 | } 34 | { 35 | let mut size = allocator.get_free_memory_size(); 36 | let mut size_format = "B"; 37 | if size >= 2 * 1024 { 38 | if size < 2 * 1024 * 1024 { 39 | size /= 1024; 40 | size_format = "KiB"; 41 | } else if size < 2 * 1024 * 1024 * 1024 { 42 | size /= 1024 * 1024; 43 | size_format = "MiB"; 44 | } else { 45 | size /= 1024 * 1024 * 1024; 46 | size_format = "GiB"; 47 | } 48 | } 49 | serial_println!("[debug] Free memory: {:>4}{:>3}", size, size_format); 50 | } 51 | } 52 | } 53 | 54 | #[inline(always)] 55 | pub fn print_memory_map(memory_map: &MemoryRegions) { 56 | #[cfg(debug_assertions)] 57 | { 58 | serial_println!("[ ---{:^15}--- ]", "MEMORY MAP"); 59 | memory_map.iter().for_each(|region| { 60 | let mut size = region.end - region.start; 61 | let mut size_format = "B"; 62 | if size >= 2 * 1024 { 63 | if size < 2 * 1024 * 1024 { 64 | size /= 1024; 65 | size_format = "KiB"; 66 | } else if size < 2 * 1024 * 1024 * 1024 { 67 | size /= 1024 * 1024; 68 | size_format = "MiB"; 69 | } else { 70 | size /= 1024 * 1024 * 1024; 71 | size_format = "GiB"; 72 | } 73 | } 74 | serial_println!( 75 | "{:14}- {:>4}{:>3} ({:X}) ({:X})", 76 | decode_memory_kind(region.kind), 77 | size, 78 | size_format, 79 | region.start, 80 | region.end 81 | ); 82 | }); 83 | } 84 | } 85 | 86 | #[cfg(debug_assertions)] 87 | fn decode_memory_kind(kind: MemoryRegionKind) -> &'static str { 88 | match kind { 89 | MemoryRegionKind::Usable => "usable", 90 | MemoryRegionKind::Bootloader => "bootloader", 91 | MemoryRegionKind::UnknownBios(kind) => match kind { 92 | 1 => "usable BIOS", 93 | 2 => "Reserved BIOS", 94 | 3 => "ACPI reclaimable", 95 | 4 => "ACPI NVS", 96 | 5 => "Bad memory", 97 | _ => "unknown BIOS", 98 | }, 99 | MemoryRegionKind::UnknownUefi(kind) => match kind { 100 | 0 => "EfiReservedMemoryType", 101 | 1 => "EfiLoaderCode", 102 | 2 => "EfiLoaderData", 103 | 3 => "EfiBootServicesCode", 104 | 4 => "EfiBootServicesData", 105 | 5 => "EfiRuntimeServiceCode", 106 | 6 => "EfiRuntimeServicesData", 107 | 7 => "EfiConventionalMemory", 108 | 8 => "EfiUnusableMemory", 109 | 9 => "EfiACPIReclaimMemory", 110 | 10 => "EfiACPIMemoryNVS", 111 | 11 => "EfiMemoryMappedIO", 112 | 12 => "EfiMemoryMappedIOPort Space", 113 | 13 => "EfiPalCode", 114 | 14 => "EfiPersistentMemory", 115 | _ => "unknown UEFI", 116 | }, 117 | _ => "unknown", 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /kernel/src/init.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | 3 | use alloc::{rc::Rc, sync::Arc, vec::Vec}; 4 | use bootloader::BootInfo; 5 | use internal_utils::serial_println; 6 | use lazy_static::lazy_static; 7 | use spin::Mutex; 8 | 9 | use crate::{ 10 | interrupts, 11 | memory::{self, frame_allocator::BitmapFrameAllocator}, 12 | processes::thread::Thread, 13 | syscalls::system_call::{register_syscall, setup_syscalls}, 14 | }; 15 | 16 | use internal_utils::structures::{ 17 | driver::{Driver, Registrator}, 18 | kernel_information::KernelInformation, 19 | }; 20 | 21 | use crate::debug; 22 | 23 | lazy_static! { 24 | static ref REGISTERED_DRIVERS: Mutex> = Mutex::new(Vec::new()); 25 | static ref INITIALIZED_DRIVERS: Mutex> = Mutex::new(Vec::new()); 26 | } 27 | 28 | pub(crate) static mut KERNEL_INFORMATION: Option = None; 29 | 30 | extern "C" fn test_syscall(a: u64, b: u64, caller: Rc>) -> u64 { 31 | let thread = caller.borrow(); 32 | serial_println!( 33 | "Syscall 0 from process {} and thread {}", 34 | thread.process.as_ref().borrow().id, 35 | thread.id 36 | ); 37 | 0 38 | } 39 | 40 | extern "C" fn test_syscall2(a: u64, b: u64, caller: Rc>) -> u64 { 41 | let thread = caller.borrow(); 42 | serial_println!( 43 | "Syscall 1 from process {} and thread {}", 44 | thread.process.as_ref().borrow().id, 45 | thread.id 46 | ); 47 | 1 48 | } 49 | 50 | /// Initialises the components of the OS, **must** be called before any other functions. 51 | pub fn init(boot_info: &'static BootInfo) -> KernelInformation { 52 | debug::print_memory_map(&boot_info.memory_regions); 53 | memory::save_kernel_memory(); 54 | let mut allocator = BitmapFrameAllocator::init(boot_info); 55 | memory::init(boot_info, &mut allocator); 56 | let kernel_info = KernelInformation::new(boot_info, Arc::new(Mutex::new(allocator))); 57 | interrupts::reload_gdt(); 58 | interrupts::init_idt(); 59 | setup_syscalls(); 60 | interrupts::enable(); 61 | 62 | register_syscall(0, test_syscall); 63 | register_syscall(1, test_syscall2); 64 | 65 | unsafe { 66 | KERNEL_INFORMATION = Some(kernel_info.clone()); 67 | } 68 | 69 | kernel_info 70 | } 71 | 72 | pub fn get_kernel_information() -> KernelInformation { 73 | unsafe { KERNEL_INFORMATION.clone().unwrap() } 74 | } 75 | 76 | /// Reinitializes all the registered drivers 77 | pub fn reload_drivers() { 78 | let kernel_info = get_kernel_information(); 79 | let mut initialized_drivers = INITIALIZED_DRIVERS.lock(); 80 | initialized_drivers.clear(); 81 | initialized_drivers.extend( 82 | REGISTERED_DRIVERS 83 | .lock() 84 | .iter() 85 | .map(|registrator| registrator(kernel_info.clone())), 86 | ); 87 | } 88 | 89 | /// Registers a driver. After registering drivers call reload_drivers to initialize them. 90 | pub fn register_driver(registrator: Registrator) { 91 | REGISTERED_DRIVERS.lock().push(registrator); 92 | } 93 | 94 | /// Endless loop calling halt continuously. 95 | pub fn hlt_loop() -> ! { 96 | loop { 97 | x86_64::instructions::hlt(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /kernel/src/interrupts.rs: -------------------------------------------------------------------------------- 1 | // This might be reimplemented from scratch in the future. 2 | 3 | // TODO: Implement all remaining interrupt handlers for CPU interrupts 4 | // We need to implement all interrupt handlers and add basic handling to them so we don't double fault. 5 | // Better handling for each of them will be added later. 6 | 7 | mod cpu_handlers; 8 | mod interrupt_register; 9 | pub use interrupt_register::init_idt; 10 | pub(crate) mod gdt; 11 | mod pic_handlers; 12 | pub use gdt::{reload_gdt, GDT}; 13 | mod pic; 14 | 15 | use crate::debug; 16 | 17 | /// Initializes the PICs and enables interrupts 18 | pub fn enable() { 19 | unsafe { 20 | // can cause undefined behaviour if the offsets were not set correctly 21 | pic::PICS.lock().initialize(); 22 | } 23 | x86_64::instructions::interrupts::enable(); 24 | debug::log("Interrupts enabled"); 25 | } 26 | -------------------------------------------------------------------------------- /kernel/src/interrupts/cpu_handlers.rs: -------------------------------------------------------------------------------- 1 | mod breakpoint; 2 | pub use breakpoint::breakpoint_handler; 3 | mod double_fault; 4 | pub use double_fault::double_fault_handler; 5 | mod page_fault; 6 | pub use page_fault::page_fault_handler; 7 | mod general_protection_fault; 8 | pub use general_protection_fault::general_protection_fault_handler; 9 | mod non_maskable_interrupt; 10 | pub use non_maskable_interrupt::nmi_handler; 11 | -------------------------------------------------------------------------------- /kernel/src/interrupts/cpu_handlers/breakpoint.rs: -------------------------------------------------------------------------------- 1 | use crate::log_println; 2 | use x86_64::structures::idt::InterruptStackFrame; 3 | 4 | /// Handles a breakpoint interrupt (like `int3`). 5 | pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { 6 | log_println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); 7 | } 8 | -------------------------------------------------------------------------------- /kernel/src/interrupts/cpu_handlers/double_fault.rs: -------------------------------------------------------------------------------- 1 | use x86_64::structures::idt::InterruptStackFrame; 2 | 3 | /// Handles a double fault. 4 | /// 5 | /// Does not return. 6 | pub extern "x86-interrupt" fn double_fault_handler( 7 | stack_frame: InterruptStackFrame, 8 | _error_code: u64, 9 | ) -> ! { 10 | // ! this should never do stack heavy operations because this handles has a separate stack 11 | // ! that has no stack guard page and thus could corrupt the stack 12 | panic!( 13 | "EXCEPTION: DOUBLE FAULT\n{:#?}\n{:#?}", 14 | stack_frame, _error_code 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /kernel/src/interrupts/cpu_handlers/general_protection_fault.rs: -------------------------------------------------------------------------------- 1 | use internal_utils::serial_print; 2 | use x86_64::structures::idt::InterruptStackFrame; 3 | 4 | /// Handles a general protection fault. 5 | pub extern "x86-interrupt" fn general_protection_fault_handler( 6 | _stack_frame: InterruptStackFrame, 7 | _error_code: u64, 8 | ) { 9 | serial_print!("GP Fault {},", _error_code); 10 | } 11 | -------------------------------------------------------------------------------- /kernel/src/interrupts/cpu_handlers/non_maskable_interrupt.rs: -------------------------------------------------------------------------------- 1 | use x86_64::structures::idt::InterruptStackFrame; 2 | 3 | /// Handles a non-maskable interrupt. 4 | /// 5 | pub extern "x86-interrupt" fn nmi_handler(stack_frame: InterruptStackFrame) { 6 | // ! this should never do stack heavy operations because this handles has a separate stack 7 | // ! that has no stack guard page and thus could corrupt the stack 8 | panic!("EXCEPTION: NMI\n{:#?}", stack_frame); 9 | } 10 | -------------------------------------------------------------------------------- /kernel/src/interrupts/cpu_handlers/page_fault.rs: -------------------------------------------------------------------------------- 1 | use internal_utils::serial_println; 2 | use x86_64::structures::idt::InterruptStackFrame; 3 | use x86_64::structures::idt::PageFaultErrorCode; 4 | 5 | use crate::hlt_loop; 6 | 7 | /// Handles a page fault. 8 | pub extern "x86-interrupt" fn page_fault_handler( 9 | stack_frame: InterruptStackFrame, 10 | error_code: PageFaultErrorCode, 11 | ) { 12 | use x86_64::registers::control::Cr2; 13 | x86_64::instructions::interrupts::disable(); 14 | 15 | serial_println!("EXCEPTION: PAGE FAULT"); 16 | serial_println!("{:?}", error_code); 17 | serial_println!("Page: {:X?}", Cr2::read_raw()); 18 | serial_println!("{:#?}", stack_frame); 19 | hlt_loop(); 20 | } 21 | -------------------------------------------------------------------------------- /kernel/src/interrupts/gdt.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use x86_64::registers::segmentation::{SegmentSelector, DS, ES, SS}; 3 | use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable}; 4 | use x86_64::structures::tss::TaskStateSegment; 5 | use x86_64::{PrivilegeLevel, VirtAddr}; 6 | 7 | use crate::debug; 8 | 9 | /// the interrupt stack table index of the stack used for double faults 10 | pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; 11 | pub const NMI_IST_INDEX: u16 = 1; 12 | pub const TIMER_IST_INDEX: u16 = 2; 13 | 14 | lazy_static! { 15 | /// The TSS of the OS. 16 | static ref TSS: TaskStateSegment = { 17 | let mut tss = TaskStateSegment::new(); 18 | 19 | const STACK_SIZE: usize = 4096; 20 | #[repr(align(16))] 21 | struct Stack([u8; STACK_SIZE]); 22 | 23 | // Stack used when an exception happens in user mode 24 | tss.privilege_stack_table[0] = { 25 | 26 | static mut STACK: Stack = Stack([0; STACK_SIZE]); 27 | 28 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 29 | 30 | // returns the highest address of the stack because the stack grows downwards 31 | stack_start + STACK_SIZE 32 | }; 33 | 34 | // set the interrupt stack table to the appropriate address 35 | tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { 36 | 37 | static mut STACK: Stack = Stack([0; STACK_SIZE]); 38 | 39 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 40 | 41 | // returns the highest address of the stack because the stack grows downwards 42 | stack_start + STACK_SIZE 43 | }; 44 | 45 | tss.interrupt_stack_table[NMI_IST_INDEX as usize] = { 46 | 47 | static mut STACK: Stack = Stack([0; STACK_SIZE]); 48 | 49 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 50 | 51 | // returns the highest address of the stack because the stack grows downwards 52 | stack_start + STACK_SIZE 53 | }; 54 | 55 | tss.interrupt_stack_table[TIMER_IST_INDEX as usize] = { 56 | 57 | static mut STACK: Stack = Stack([0; STACK_SIZE]); 58 | 59 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 60 | 61 | // returns the highest address of the stack because the stack grows downwards 62 | stack_start + STACK_SIZE 63 | }; 64 | 65 | tss 66 | }; 67 | 68 | /// The GDT used by the OS. 69 | pub static ref GDT: (GlobalDescriptorTable, Selectors) = { 70 | let mut gdt = GlobalDescriptorTable::new(); 71 | 72 | let kernel_code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); 73 | let kernel_data_selector = gdt.add_entry(Descriptor::kernel_data_segment()); 74 | 75 | let user_data_selector = gdt.add_entry(Descriptor::user_data_segment()); 76 | let user_code_selector = gdt.add_entry(Descriptor::user_code_segment()); 77 | 78 | let mut tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); 79 | tss_selector.set_rpl(PrivilegeLevel::Ring0); 80 | 81 | ( 82 | gdt, 83 | Selectors { 84 | kernel_code_selector, 85 | kernel_data_selector, 86 | user_code_selector, 87 | user_data_selector, 88 | tss_selector 89 | }, 90 | ) 91 | }; 92 | } 93 | 94 | pub struct Selectors { 95 | pub kernel_code_selector: SegmentSelector, 96 | pub kernel_data_selector: SegmentSelector, 97 | pub user_code_selector: SegmentSelector, 98 | pub user_data_selector: SegmentSelector, 99 | tss_selector: SegmentSelector, 100 | } 101 | 102 | /// Initialises the GDT and TSS. 103 | pub fn reload_gdt() { 104 | use x86_64::instructions::segmentation::{Segment, CS}; 105 | use x86_64::instructions::tables::load_tss; 106 | debug::log("Loading GDT and segment registers"); 107 | GDT.0.load(); 108 | debug::log("GDT loaded"); 109 | let selector = &GDT.1; 110 | unsafe { 111 | CS::set_reg(selector.kernel_code_selector); 112 | load_tss(selector.tss_selector); 113 | SS::set_reg(selector.kernel_data_selector); 114 | DS::set_reg(selector.kernel_data_selector); 115 | ES::set_reg(selector.kernel_data_selector); 116 | } 117 | debug::log("Segment registers loaded"); 118 | } 119 | -------------------------------------------------------------------------------- /kernel/src/interrupts/interrupt_register.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use x86_64::structures::idt::InterruptDescriptorTable; 3 | 4 | use crate::{ 5 | debug, 6 | interrupts::{ 7 | cpu_handlers::{ 8 | breakpoint_handler, double_fault_handler, general_protection_fault_handler, 9 | nmi_handler, page_fault_handler, 10 | }, 11 | pic::InterruptIndex, 12 | pic_handlers::{ 13 | _timer, ata_primary_interrupt_handler, ata_secondary_interrupt_handler, 14 | keyboard_interrupt_handler, 15 | }, 16 | }, 17 | }; 18 | use x86_64::VirtAddr; 19 | 20 | lazy_static! { 21 | /// The IDT used by the OS. 22 | static ref IDT: InterruptDescriptorTable = { 23 | let mut idt = InterruptDescriptorTable::new(); 24 | 25 | // ################## 26 | // # CPU interrupts # 27 | // ################## 28 | idt.breakpoint.set_handler_fn(breakpoint_handler); 29 | unsafe { 30 | idt.non_maskable_interrupt 31 | .set_handler_fn(nmi_handler) 32 | .set_stack_index(crate::interrupts::gdt::NMI_IST_INDEX); 33 | 34 | idt.double_fault 35 | .set_handler_fn(double_fault_handler) 36 | // changes stack for double fault to avoid triple faults 37 | .set_stack_index(crate::interrupts::gdt::DOUBLE_FAULT_IST_INDEX); 38 | } 39 | 40 | idt.page_fault.set_handler_fn(page_fault_handler); 41 | 42 | idt.general_protection_fault.set_handler_fn(general_protection_fault_handler); 43 | 44 | // ################## 45 | // # PIC interrupts # 46 | // ################## 47 | unsafe { 48 | idt[InterruptIndex::Timer.as_usize()] 49 | .set_handler_addr(VirtAddr::from_ptr(_timer as *const ())) 50 | .set_stack_index(crate::interrupts::gdt::TIMER_IST_INDEX); 51 | } 52 | 53 | idt[InterruptIndex::Keyboard.as_usize()] 54 | .set_handler_fn(keyboard_interrupt_handler); 55 | 56 | idt[InterruptIndex::AtaPrimary.as_usize()] 57 | .set_handler_fn(ata_primary_interrupt_handler); 58 | 59 | idt[InterruptIndex::AtaSecondary.as_usize()] 60 | .set_handler_fn(ata_secondary_interrupt_handler); 61 | 62 | idt 63 | }; 64 | } 65 | 66 | /// Loads the IDT. 67 | pub fn init_idt() { 68 | IDT.load(); 69 | debug::log("IDT loaded"); 70 | } 71 | -------------------------------------------------------------------------------- /kernel/src/interrupts/pic.rs: -------------------------------------------------------------------------------- 1 | use pic8259::ChainedPics; 2 | use spin::Mutex; 3 | 4 | pub const PIC_1_OFFSET: u8 = 32; 5 | pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; 6 | 7 | /// Stores the interrupt address for a given interrupt type 8 | #[derive(Debug, Clone, Copy)] 9 | #[repr(u8)] 10 | /// Stores the interrupt address for a given interrupt type 11 | pub enum InterruptIndex { 12 | Timer = PIC_1_OFFSET, 13 | Keyboard, 14 | 15 | AtaPrimary = PIC_2_OFFSET + 6, 16 | AtaSecondary, 17 | } 18 | 19 | impl InterruptIndex { 20 | /// Returns the corresponding interrupt number for this interrupt type as a u8 21 | pub fn as_u8(self) -> u8 { 22 | self as u8 23 | } 24 | 25 | /// Returns the corresponding interrupt number for this interrupt type as a usize 26 | pub fn as_usize(self) -> usize { 27 | usize::from(self.as_u8()) 28 | } 29 | } 30 | 31 | /// The PICs of the system. 32 | pub static PICS: Mutex = Mutex::new(unsafe { 33 | // this is unsafe, because wrong offsets will cause undefined behavior 34 | ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) 35 | }); 36 | -------------------------------------------------------------------------------- /kernel/src/interrupts/pic_handlers.rs: -------------------------------------------------------------------------------- 1 | mod timer; 2 | pub use timer::_timer; 3 | mod keyboard; 4 | pub use keyboard::keyboard_interrupt_handler; 5 | mod ata; 6 | pub use ata::{ata_primary_interrupt_handler, ata_secondary_interrupt_handler}; 7 | mod addresses; 8 | -------------------------------------------------------------------------------- /kernel/src/interrupts/pic_handlers/addresses.rs: -------------------------------------------------------------------------------- 1 | // ! ignore dead code 2 | pub static PS2_INTERRUPT_CONTROLLER_SCAN_CODE_PORT: u16 = 0x60; 3 | -------------------------------------------------------------------------------- /kernel/src/interrupts/pic_handlers/ata.rs: -------------------------------------------------------------------------------- 1 | use x86_64::structures::idt::InterruptStackFrame; 2 | 3 | use crate::interrupts::pic::{InterruptIndex, PICS}; 4 | 5 | pub extern "x86-interrupt" fn ata_primary_interrupt_handler(_stack_frame: InterruptStackFrame) { 6 | unsafe { 7 | PICS.lock() 8 | .notify_end_of_interrupt(InterruptIndex::AtaPrimary.as_u8()); 9 | } 10 | } 11 | 12 | pub extern "x86-interrupt" fn ata_secondary_interrupt_handler(_stack_frame: InterruptStackFrame) { 13 | unsafe { 14 | PICS.lock() 15 | .notify_end_of_interrupt(InterruptIndex::AtaSecondary.as_u8()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /kernel/src/interrupts/pic_handlers/keyboard.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; 3 | use spin::Mutex; 4 | use x86_64::structures::idt::InterruptStackFrame; 5 | 6 | use crate::interrupts::pic::PICS; 7 | use crate::interrupts::{ 8 | pic::InterruptIndex, pic_handlers::addresses::PS2_INTERRUPT_CONTROLLER_SCAN_CODE_PORT, 9 | }; 10 | use crate::log_print; 11 | use crate::memory::with_kernel_memory; 12 | 13 | lazy_static! { 14 | static ref KEYBOARD: Mutex> = Mutex::new( 15 | Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore) 16 | ); 17 | } 18 | 19 | /// Handles a keyboard interrupt. 20 | pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { 21 | use x86_64::instructions::port::Port; 22 | with_kernel_memory(|| { 23 | let mut keyboard = KEYBOARD.lock(); 24 | let mut port = Port::new(PS2_INTERRUPT_CONTROLLER_SCAN_CODE_PORT); 25 | let scancode: u8 = unsafe { port.read() }; 26 | 27 | if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { 28 | if let Some(key) = keyboard.process_keyevent(key_event) { 29 | match key { 30 | // ! this introduces deadlock potential because print will lock the VgaTextBufferInterface 31 | DecodedKey::Unicode(character) => log_print!("{}", character), 32 | DecodedKey::RawKey(key) => log_print!("{:?}", key), 33 | } 34 | } 35 | } 36 | }); 37 | 38 | unsafe { 39 | PICS.lock() 40 | .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /kernel/src/interrupts/pic_handlers/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::interrupts::pic::{InterruptIndex, PICS}; 2 | use crate::memory::with_kernel_memory; 3 | use crate::processes::{get_scheduler, run_next_thread, RegistersState}; 4 | use core::arch::asm; 5 | use internal_utils::get_current_tick; 6 | use internal_utils::{pop_all, push_all}; 7 | 8 | #[no_mangle] 9 | #[naked] 10 | pub unsafe extern "C" fn _timer() -> ! { 11 | asm!( 12 | // We have RFLAGS and RIP on the stack already. 13 | push_all!(), 14 | "mov rdi, rsp", 15 | "call timer_interrupt_handler", 16 | pop_all!(), 17 | "iretq", 18 | options(noreturn) 19 | ); 20 | } 21 | 22 | #[no_mangle] 23 | extern "C" fn timer_interrupt_handler(registers_state: *const RegistersState) { 24 | let registers_state = unsafe { *registers_state }; 25 | let tick = get_current_tick(); 26 | 27 | with_kernel_memory(|| { 28 | get_scheduler().timer_tick(registers_state, tick); 29 | unsafe { 30 | PICS.lock() 31 | .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); 32 | } 33 | run_next_thread(); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /kernel/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // no standard library 2 | #![no_main] 3 | #![allow(incomplete_features)] 4 | #![feature( 5 | option_get_or_insert_default, 6 | abi_x86_interrupt, 7 | generic_const_exprs, 8 | core_intrinsics, 9 | asm_const, 10 | naked_functions 11 | )] 12 | 13 | extern crate alloc; 14 | 15 | use alloc::{boxed::Box, sync::Arc}; 16 | use lazy_static::lazy_static; 17 | use spin::Mutex; 18 | 19 | mod init; 20 | pub use init::{hlt_loop, init, register_driver, reload_drivers}; 21 | 22 | use crate::logger::Logger; 23 | 24 | mod debug; 25 | mod interrupts; 26 | pub mod logger; 27 | mod memory; 28 | pub mod processes; 29 | pub mod syscalls; 30 | 31 | lazy_static! { 32 | pub static ref LOGGER: Arc>>> = Arc::from(Mutex::new(None)); 33 | } 34 | -------------------------------------------------------------------------------- /kernel/src/logger.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | 3 | use crate::LOGGER; 4 | 5 | pub trait Logger: Write + Send { 6 | fn log(&mut self, message: &str); 7 | fn logln(&mut self, message: &str); 8 | } 9 | 10 | #[doc(hidden)] 11 | pub fn __print(args: fmt::Arguments) { 12 | if let Some(logger) = LOGGER.lock().as_mut() { 13 | (*logger).write_fmt(args).unwrap(); 14 | } 15 | } 16 | 17 | #[macro_export] 18 | /// Prints a string to the VGA buffer 19 | macro_rules! log_print { 20 | ($($arg:tt)*) => ($crate::logger::__print(format_args!($($arg)*))); 21 | } 22 | 23 | #[macro_export] 24 | /// Prints a string to the VGA buffer and appends a newline 25 | macro_rules! log_println { 26 | () => ($crate::log_print!("\n")); 27 | ($($arg:tt)*) => ($crate::log_print!("{}\n", format_args!($($arg)*))); 28 | } 29 | -------------------------------------------------------------------------------- /kernel/src/memory.rs: -------------------------------------------------------------------------------- 1 | mod allocator; 2 | pub mod frame_allocator; 3 | mod heap; 4 | mod memory_init; 5 | mod page_table; 6 | pub use memory_init::init; 7 | pub use page_table::MEMORY_MAPPER; 8 | 9 | use lazy_static::lazy_static; 10 | use spin::Mutex; 11 | use x86_64::{ 12 | registers::control::{Cr3, Cr3Flags}, 13 | structures::paging::PhysFrame, 14 | PhysAddr, 15 | }; 16 | 17 | lazy_static! { 18 | static ref KERNEL_CR3: Mutex = Mutex::new(PhysAddr::new(0)); 19 | } 20 | 21 | /// Saves the current paging table used as the kernel's paging table. 22 | pub(crate) fn save_kernel_memory() { 23 | *KERNEL_CR3.lock() = x86_64::registers::control::Cr3::read().0.start_address(); 24 | } 25 | 26 | /// Switches the paging table used to the kernel's paging table. 27 | pub(crate) fn switch_to_kernel_memory() { 28 | let kernel_cr3 = *KERNEL_CR3.lock(); 29 | if !kernel_cr3.is_null() { 30 | unsafe { 31 | Cr3::write( 32 | PhysFrame::from_start_address_unchecked(kernel_cr3), 33 | Cr3Flags::empty(), 34 | ); 35 | } 36 | } 37 | } 38 | 39 | /// Performs an action while having kernel paging table. Then switches back. 40 | pub(crate) fn with_kernel_memory(action: impl FnOnce() -> V) -> V { 41 | let cr3 = Cr3::read().0.start_address(); 42 | switch_to_kernel_memory(); 43 | let result = action(); 44 | unsafe { 45 | Cr3::write( 46 | PhysFrame::from_start_address_unchecked(cr3), 47 | Cr3Flags::empty(), 48 | ) 49 | }; 50 | result 51 | } 52 | -------------------------------------------------------------------------------- /kernel/src/memory/allocator.rs: -------------------------------------------------------------------------------- 1 | use linked_list_allocator::LockedHeap; 2 | 3 | /// The global memory allocator 4 | #[global_allocator] 5 | pub static ALLOCATOR: LockedHeap = LockedHeap::empty(); 6 | -------------------------------------------------------------------------------- /kernel/src/memory/frame_allocator.rs: -------------------------------------------------------------------------------- 1 | use bootloader::{ 2 | boot_info::{MemoryRegionKind, MemoryRegions}, 3 | BootInfo, 4 | }; 5 | use internal_utils::FullFrameAllocator; 6 | use spin::Mutex; 7 | use x86_64::{ 8 | structures::paging::{ 9 | FrameAllocator, FrameDeallocator, PageSize, PhysFrame, Size2MiB, Size4KiB, 10 | }, 11 | PhysAddr, VirtAddr, 12 | }; 13 | 14 | use lazy_static::lazy_static; 15 | 16 | use crate::debug; 17 | 18 | lazy_static! { 19 | /// The maximum size of usable memory is 64GiB with this bitflag. 20 | /// Have to pre-allocate one 4K frame and one 2M frame. 21 | static ref TWO_MEGABYTES_FRAMES_BITFLAG: Mutex> = 22 | Mutex::new(None); 23 | static ref FOUR_KILOBYTES_FRAMES_BITFLAG: Mutex> = 24 | Mutex::new(None); 25 | } 26 | 27 | /// A Frame Allocator that allocates according to the usage bitmap of the memory. 28 | #[repr(C)] 29 | #[derive(Clone, Copy)] 30 | pub struct BitmapFrameAllocator { 31 | memory_map: &'static MemoryRegions, 32 | total_region_area: u64, 33 | } 34 | 35 | impl BitmapFrameAllocator { 36 | /// Creates a FrameAllocator from the passed memory map. 37 | pub fn init(boot_info: &'static BootInfo) -> Self { 38 | let memory_map = &boot_info.memory_regions; 39 | let total_region_area = memory_map 40 | .iter() 41 | .map(|region| region.end - region.start) 42 | .sum::(); 43 | 44 | if TWO_MEGABYTES_FRAMES_BITFLAG.lock().is_some() { 45 | // Frames already allocated 46 | BitmapFrameAllocator { 47 | memory_map, 48 | total_region_area, 49 | } 50 | } else { 51 | let pmo = boot_info.physical_memory_offset.as_ref().unwrap(); 52 | 53 | // We first need to take a 2M frame and 2x4K frames from the memory map for the bitflags. 54 | let usable_memory_region = memory_map 55 | .iter() 56 | .find(|region| region.kind == MemoryRegionKind::Usable) 57 | .unwrap(); 58 | 59 | let (four_kilobytes_frames_bitflag, two_megabyte_frames_bitflag) = 60 | get_bitflag_frames(PhysAddr::new(usable_memory_region.start)); 61 | 62 | *FOUR_KILOBYTES_FRAMES_BITFLAG.lock() = Some(unsafe { 63 | VirtAddr::new(four_kilobytes_frames_bitflag.start_address().as_u64() + pmo) 64 | .as_mut_ptr::<[u64; 262144]>() 65 | .as_mut() 66 | .expect("Cannot allocate the 2M frame") 67 | }); 68 | *TWO_MEGABYTES_FRAMES_BITFLAG.lock() = Some(unsafe { 69 | VirtAddr::new(two_megabyte_frames_bitflag.start_address().as_u64() + pmo) 70 | .as_mut_ptr::<[u64; 512]>() 71 | .as_mut() 72 | .expect("Cannot allocate the 4K frame") 73 | }); 74 | 75 | let mut allocator = BitmapFrameAllocator { 76 | memory_map, 77 | total_region_area, 78 | }; 79 | 80 | // We set everything as used because BIOS may return holes in the memory map. 81 | for frame in 0..32768 { 82 | allocator.set_used(frame << 21, Size2MiB::SIZE); 83 | } 84 | 85 | // Now we need to set the usable memory regions as unused so they're not allocated. 86 | for region in memory_map 87 | .iter() 88 | .filter(|region| region.kind == MemoryRegionKind::Usable) 89 | { 90 | let start = PhysFrame::containing_address( 91 | PhysAddr::new(region.start).align_up(Size4KiB::SIZE), 92 | ); 93 | let end = PhysFrame::containing_address( 94 | PhysAddr::new(region.end - 1).align_down(Size4KiB::SIZE), 95 | ); 96 | let frame_range = PhysFrame::::range_inclusive(start, end); 97 | 98 | frame_range.for_each(|f| { 99 | allocator 100 | .set_unused(f.start_address().as_u64(), Size4KiB::SIZE) 101 | .expect("Failed setting memory regions as unused"); 102 | }); 103 | } 104 | 105 | allocator.set_used( 106 | four_kilobytes_frames_bitflag.start_address().as_u64(), 107 | Size2MiB::SIZE, 108 | ); 109 | allocator.set_used( 110 | two_megabyte_frames_bitflag.start_address().as_u64(), 111 | Size4KiB::SIZE, 112 | ); 113 | 114 | debug::print_frame_memory(&allocator); 115 | 116 | allocator 117 | } 118 | } 119 | 120 | /// Unconditionally sets the frame at the start_address as used in the bitflags 121 | fn set_used(&mut self, start_address: u64, size: u64) -> Option<()> { 122 | match size { 123 | Size2MiB::SIZE => { 124 | // Align to 2M frame. 125 | let start_address = start_address >> 21; 126 | 127 | // The index in the bitflags 128 | let index = (start_address >> 6) as usize; 129 | 130 | // We set the flag for the frame to 1 131 | let mut mbl = TWO_MEGABYTES_FRAMES_BITFLAG.lock(); 132 | let value = mbl.as_mut()?[index]; 133 | if value == value | (1 << (start_address & 63)) { 134 | panic!( 135 | "2M Frame at {} bit {} already set as used", 136 | index, 137 | start_address & 63 138 | ); 139 | } 140 | mbl.as_mut()?[index] |= 1 << (start_address & 63); 141 | 142 | // Now we need to set all the 4K frames in this 2M frame as used. 143 | let mut fbl = FOUR_KILOBYTES_FRAMES_BITFLAG.lock(); 144 | let four_kilobytes_frames_bitflag_lock = fbl.as_mut()?; 145 | for i in (start_address << 3)..((start_address << 3) + 8) { 146 | four_kilobytes_frames_bitflag_lock[i as usize] = u64::MAX; 147 | } 148 | } 149 | Size4KiB::SIZE => { 150 | // Align to 4K frame. 151 | let start_address = start_address >> 12; 152 | 153 | // The index in the bitflags 154 | let index = (start_address >> 6) as usize; 155 | 156 | // We set the flag for the frame to 1 157 | let mut fbl = FOUR_KILOBYTES_FRAMES_BITFLAG.lock(); 158 | let four_kilobytes_frames_bitflag_lock = fbl.as_mut()?; 159 | let value = four_kilobytes_frames_bitflag_lock[index]; 160 | if value == value | (1 << (start_address & 63)) { 161 | panic!( 162 | "4K Frame at {} bit {} already set as used", 163 | index, 164 | start_address & 63 165 | ); 166 | } 167 | four_kilobytes_frames_bitflag_lock[index] |= 1 << (start_address & 63); 168 | 169 | // Now we need to set the 2M frame as used 170 | let start_address = start_address >> 9; 171 | let index = (start_address >> 6) as usize; 172 | TWO_MEGABYTES_FRAMES_BITFLAG.lock().as_mut()?[index] |= 1 << (start_address & 63); 173 | } 174 | _ => todo!("Implement 1G frame bitflags"), 175 | } 176 | Some(()) 177 | } 178 | 179 | /// Unconditionally sets the frame at the start_address as unused in the bitflags 180 | fn set_unused(&mut self, start_address: u64, size: u64) -> Option<()> { 181 | match size { 182 | Size2MiB::SIZE => { 183 | // Align to 2M frame. 184 | let start_address = start_address >> 21; 185 | 186 | // The index in the bitflags 187 | let index = (start_address >> 6) as usize; 188 | 189 | // We set the flag for the frame to 1 190 | let mut mbl = TWO_MEGABYTES_FRAMES_BITFLAG.lock(); 191 | let value = mbl.as_mut()?[index]; 192 | if value == value & !(1 << (start_address & 63)) { 193 | panic!( 194 | "2M Frame at {} bit {} already set as unused", 195 | index, 196 | start_address & 63 197 | ); 198 | } 199 | mbl.as_mut()?[index] &= !(1 << (start_address & 63)); 200 | 201 | // Now we need to set all the 4K frames in this 2M frame as unused. 202 | let mut fbl = FOUR_KILOBYTES_FRAMES_BITFLAG.lock(); 203 | let four_kilobytes_frames_bitflag_lock = fbl.as_mut()?; 204 | for i in (start_address << 3)..((start_address << 3) + 8) { 205 | four_kilobytes_frames_bitflag_lock[i as usize] = 0u64; 206 | } 207 | } 208 | Size4KiB::SIZE => { 209 | // Align to 4K frame. 210 | let start_address = start_address >> 12; 211 | 212 | // The index in the bitflags 213 | let index = (start_address >> 6) as usize; 214 | 215 | // We set the flag for the frame to 1 216 | let mut fbl = FOUR_KILOBYTES_FRAMES_BITFLAG.lock(); 217 | let four_kilobytes_frames_bitflag_lock = fbl.as_mut()?; 218 | let value = four_kilobytes_frames_bitflag_lock[index]; 219 | if value == value & !(1 << (start_address & 63)) { 220 | panic!( 221 | "4K Frame at {} bit {} already set as unused", 222 | index, 223 | start_address & 63 224 | ); 225 | } 226 | four_kilobytes_frames_bitflag_lock[index] &= !(1 << (start_address & 63)); 227 | 228 | // If all the 4K frames in the 2M frame are unused, we need to set the 2M frame itself as unused 229 | let start_address = start_address >> 9; 230 | let index = (start_address >> 6) as usize; 231 | if four_kilobytes_frames_bitflag_lock 232 | [(start_address << 3) as usize..((start_address << 3) + 8) as usize] 233 | .iter() 234 | .all(|flags| *flags == 0u64) 235 | { 236 | TWO_MEGABYTES_FRAMES_BITFLAG.lock().as_mut()?[index] &= 237 | !(1 << (start_address & 63)); 238 | } 239 | } 240 | _ => todo!("Implement 1G frame bitflags"), 241 | } 242 | Some(()) 243 | } 244 | } 245 | 246 | impl FrameDeallocator for BitmapFrameAllocator 247 | where 248 | S: PageSize, 249 | { 250 | unsafe fn deallocate_frame(&mut self, frame: PhysFrame) { 251 | self.set_unused(frame.start_address().as_u64(), frame.size()); 252 | } 253 | } 254 | 255 | unsafe impl FrameAllocator for BitmapFrameAllocator { 256 | /// Returns the next usable frame 257 | fn allocate_frame(&mut self) -> Option> { 258 | let frame_address: PhysAddr; 259 | { 260 | let mut fbl = FOUR_KILOBYTES_FRAMES_BITFLAG.lock(); 261 | let four_kilobytes_frames_bitflag_lock = fbl.as_mut()?; 262 | 263 | // We go through all the 4K frames and find the first free one 264 | let free_4k_frame_flag = four_kilobytes_frames_bitflag_lock 265 | .iter() 266 | .enumerate() 267 | .find(|(_, flag)| **flag != u64::MAX)?; 268 | 269 | // We get the position of the free 4K frame 270 | let free_4k_frame = free_4k_frame_flag.1.trailing_ones() as usize 271 | + (free_4k_frame_flag.0 << 6) as usize; 272 | 273 | // We set the 4K frame as used 274 | frame_address = PhysAddr::new((free_4k_frame as u64) << 12); 275 | } 276 | self.set_used(frame_address.as_u64(), Size4KiB::SIZE)?; 277 | PhysFrame::from_start_address(frame_address).ok() 278 | } 279 | } 280 | 281 | unsafe impl FrameAllocator for BitmapFrameAllocator { 282 | /// Returns the next usable frame 283 | fn allocate_frame(&mut self) -> Option> { 284 | let frame_address: PhysAddr; 285 | { 286 | let mut mbl = TWO_MEGABYTES_FRAMES_BITFLAG.lock(); 287 | let two_megabytes_frames_bitflag_lock = mbl.as_mut()?; 288 | 289 | // First we iterate through the negated 2M flags to find a 2M frame with free slots inside 290 | let free_2m_frame = two_megabytes_frames_bitflag_lock 291 | .iter() 292 | .enumerate() 293 | .find(|(_, flag)| **flag != u64::MAX)?; 294 | 295 | // We get the position of the free 2M frame 296 | let free_2m_frame = 297 | free_2m_frame.1.trailing_ones() as usize + (free_2m_frame.0 << 6) as usize; 298 | 299 | // We set the 2M frame as used 300 | frame_address = PhysAddr::new((free_2m_frame as u64) << 21); 301 | } 302 | self.set_used(frame_address.as_u64(), Size2MiB::SIZE)?; 303 | PhysFrame::from_start_address(frame_address).ok() 304 | } 305 | } 306 | 307 | /// Allocates the frames required for the frame allocator. 308 | /// 309 | /// Chicken and egg? 310 | fn get_bitflag_frames(start_address: PhysAddr) -> (PhysFrame, PhysFrame) { 311 | let four_kilobytes_frames_bitflag: PhysFrame; 312 | let two_megabyte_frames_bitflag: PhysFrame; 313 | // We need to allocate one 2M frame and 2x4K frames, but the region addresses do not have to be 2M aligned! 314 | // So first we need to check the alignment, and we have 3 options here: 315 | // 1. The start address is 2M aligned - we allocate the 2M frame then 2x4K frames, easy. 316 | // 2. The start address + 4K is 2M aligned - we allocate one 4K frame first, then 2M, then the other 4K after it. 317 | // 3. The start address is not 2M aligned at all - we allocate both 4K frames, then we allocate the 2M frame aligned wherever it is. 318 | if start_address.is_aligned(Size2MiB::SIZE) { 319 | four_kilobytes_frames_bitflag = PhysFrame::::from_start_address(start_address) 320 | .expect("2M frame address not aligned"); 321 | two_megabyte_frames_bitflag = 322 | PhysFrame::::from_start_address(start_address + Size2MiB::SIZE) 323 | .expect("4K frame address not aligned"); 324 | } else if (start_address + Size4KiB::SIZE).is_aligned(Size2MiB::SIZE) { 325 | four_kilobytes_frames_bitflag = 326 | PhysFrame::::from_start_address(start_address + Size4KiB::SIZE) 327 | .expect("2M frame address not aligned"); 328 | two_megabyte_frames_bitflag = PhysFrame::::from_start_address(start_address) 329 | .expect("4K frame address not aligned"); 330 | } else { 331 | four_kilobytes_frames_bitflag = 332 | PhysFrame::::from_start_address(start_address.align_up(Size2MiB::SIZE)) 333 | .expect("2M frame address not aligned"); 334 | two_megabyte_frames_bitflag = PhysFrame::::from_start_address(start_address) 335 | .expect("4K frame address not aligned"); 336 | } 337 | (four_kilobytes_frames_bitflag, two_megabyte_frames_bitflag) 338 | } 339 | 340 | impl FullFrameAllocator for BitmapFrameAllocator { 341 | fn get_total_memory_size(&self) -> u64 { 342 | self.total_region_area 343 | } 344 | 345 | fn get_free_memory_size(&self) -> u64 { 346 | FOUR_KILOBYTES_FRAMES_BITFLAG 347 | .lock() 348 | .as_ref() 349 | .unwrap() 350 | .iter() 351 | .map(|flag| flag.count_zeros() as u64) 352 | .sum::() 353 | * 4096 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /kernel/src/memory/heap.rs: -------------------------------------------------------------------------------- 1 | /// Where the kernel heap starts 2 | const HEAP_START: usize = 0x_5555_AAAA_0000; 3 | /// Size of the kernel heap 4 | const HEAP_SIZE: usize = 16 * 1024 * 1024; // 16 MiB 5 | 6 | use x86_64::{ 7 | structures::paging::{ 8 | mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size2MiB, 9 | }, 10 | VirtAddr, 11 | }; 12 | 13 | use super::{allocator::ALLOCATOR, frame_allocator::BitmapFrameAllocator}; 14 | 15 | /// maps the kernels heap memory area to physical addresses 16 | pub fn init_heap( 17 | mapper: &mut impl Mapper, 18 | frame_allocator: &mut BitmapFrameAllocator, 19 | ) -> Result<(), MapToError> { 20 | let page_range = { 21 | let heap_start = VirtAddr::new(HEAP_START as u64); 22 | let heap_end = heap_start + HEAP_SIZE - 1u64; 23 | let heap_start_page = Page::containing_address(heap_start); 24 | let heap_end_page = Page::containing_address(heap_end); 25 | Page::range_inclusive(heap_start_page, heap_end_page) 26 | }; 27 | 28 | // actually map all frames and exit on error 29 | for page in page_range { 30 | let frame = frame_allocator 31 | .allocate_frame() 32 | .ok_or(MapToError::FrameAllocationFailed)?; 33 | let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; 34 | unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; 35 | } 36 | 37 | unsafe { 38 | ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); 39 | } 40 | 41 | Ok(()) 42 | } 43 | -------------------------------------------------------------------------------- /kernel/src/memory/memory_init.rs: -------------------------------------------------------------------------------- 1 | use crate::debug; 2 | use bootloader::BootInfo; 3 | use x86_64::VirtAddr; 4 | 5 | use super::{ 6 | frame_allocator::BitmapFrameAllocator, 7 | heap::init_heap, 8 | page_table::{self, MEMORY_MAPPER}, 9 | }; 10 | 11 | /// Initializes the page tables and kernel heap memory 12 | pub fn init(boot_info: &'static BootInfo, allocator: &mut BitmapFrameAllocator) { 13 | let pmo = VirtAddr::new( 14 | boot_info 15 | .physical_memory_offset 16 | .into_option() 17 | .expect("physical memory mapping not set"), 18 | ); 19 | unsafe { page_table::init(pmo) }; 20 | let mut mapper = MEMORY_MAPPER.lock(); 21 | init_heap(mapper.as_mut().unwrap(), allocator).expect("heap initialization failed"); 22 | debug::log("Heap initialized"); 23 | } 24 | -------------------------------------------------------------------------------- /kernel/src/memory/page_table.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use spin::Mutex; 3 | use x86_64::registers::control::Cr3; 4 | use x86_64::{ 5 | structures::paging::{OffsetPageTable, PageTable}, 6 | VirtAddr, 7 | }; 8 | 9 | lazy_static! { 10 | pub static ref MEMORY_MAPPER: Mutex>> = Mutex::new(None); 11 | } 12 | 13 | /// Initialize a new OffsetPageTable. 14 | /// 15 | /// This function is unsafe because the caller must guarantee that the 16 | /// complete physical memory is mapped to virtual memory at the passed 17 | /// `physical_memory_offset`. Also, this function must be only called once 18 | /// to avoid aliasing `&mut` references (which is undefined behavior). 19 | pub unsafe fn init(physical_memory_offset: VirtAddr) { 20 | let level_4_table = active_level_4_table(physical_memory_offset); 21 | let _ = MEMORY_MAPPER 22 | .lock() 23 | .insert(OffsetPageTable::new(level_4_table, physical_memory_offset)); 24 | } 25 | 26 | /// Returns a mutable reference to the active level 4 table. 27 | /// 28 | /// This function is unsafe because the caller must guarantee that the 29 | /// complete physical memory is mapped to virtual memory at the passed 30 | /// `physical_memory_offset`. Also, this function must be only called once 31 | /// to avoid aliasing `&mut` references (which is undefined behavior). 32 | unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { 33 | let (level_4_table_frame, _) = Cr3::read(); 34 | 35 | let phys = level_4_table_frame.start_address(); 36 | let virt = physical_memory_offset + phys.as_u64(); 37 | let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); 38 | 39 | &mut *page_table_ptr // unsafe 40 | } 41 | -------------------------------------------------------------------------------- /kernel/src/processes.rs: -------------------------------------------------------------------------------- 1 | pub mod dispatcher; 2 | 3 | mod memory_mapper; 4 | 5 | pub mod process; 6 | 7 | pub mod thread; 8 | 9 | mod registers_state; 10 | pub use registers_state::RegistersState; 11 | 12 | mod scheduler; 13 | pub use scheduler::{add_process, get_scheduler, run_next_thread, run_processes}; 14 | -------------------------------------------------------------------------------- /kernel/src/processes/dispatcher.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use core::cell::Ref; 3 | use core::cell::RefCell; 4 | use core::cell::RefMut; 5 | 6 | use alloc::rc::Rc; 7 | use x86_64::structures::paging::page::AddressNotAligned; 8 | use x86_64::PhysAddr; 9 | 10 | use crate::debug; 11 | use crate::interrupts::GDT; 12 | use internal_utils::get_current_tick; 13 | use internal_utils::mov_all; 14 | 15 | use super::get_scheduler; 16 | use super::memory_mapper::clear_user_mode_mapping; 17 | use super::process::Process; 18 | use super::thread::Thread; 19 | use super::thread::ThreadState; 20 | use super::RegistersState; 21 | 22 | /// Runs the thread immediately. 23 | pub fn switch_to_thread(thread: Rc>) -> ! { 24 | let code_selector_id: u64; 25 | let data_selector_id: u64; 26 | let cr3: PhysAddr; 27 | let state: RegistersState; 28 | x86_64::instructions::interrupts::disable(); 29 | { 30 | let tick = get_current_tick(); 31 | let mut thread_mut = thread.borrow_mut(); 32 | thread_mut.last_tick = tick; 33 | let mut process = thread_mut.process.borrow_mut(); 34 | process.last_tick = tick; 35 | code_selector_id = if process.kernel_process { 36 | (GDT.1.kernel_code_selector.index() * 8) as u64 37 | } else { 38 | ((GDT.1.user_code_selector.index() * 8) | 3) as u64 39 | }; 40 | data_selector_id = if process.kernel_process { 41 | (GDT.1.kernel_data_selector.index() * 8) as u64 42 | } else { 43 | ((GDT.1.user_data_selector.index() * 8) | 3) as u64 44 | }; 45 | cr3 = process.cr3; 46 | state = thread_mut.registers_state; 47 | } 48 | 49 | get_scheduler().running_thread.replace(thread.clone()); 50 | unsafe { 51 | // We decrement the counter forcefully because that function doesn't return by Rust. 52 | Rc::decrement_strong_count(Rc::into_raw(thread)); 53 | asm!( 54 | "mov cr3, r10", 55 | "push r14", // data selector 56 | "push r12", // process stack pointer 57 | "or r11, 0x200", 58 | "and r11, 0xffffffffffffbfff", 59 | "push r11", // rflags 60 | "push r13", // code selector 61 | "push r15", // instruction address to return to 62 | // Loading register state before jumping into thread 63 | mov_all!(), 64 | "iretq", 65 | in("r9") (&state as *const RegistersState as *const u8), 66 | in("r10") (cr3.as_u64()), 67 | in("r11") (state.rflags), 68 | in("r12") (state.rsp.as_u64()), 69 | in("r13") (code_selector_id), 70 | in("r14") (data_selector_id), 71 | in("r15") (state.rip.as_u64()), 72 | options(noreturn) 73 | ); 74 | } 75 | } 76 | 77 | /// Removes the thread from it's process. If this thread is the last one, the process is cleaned up. 78 | pub fn exit_thread(thread: Rc>) -> Result<(), AddressNotAligned> { 79 | debug::log("Exiting thread"); 80 | let borrowed_thread = thread.borrow(); 81 | let mut borrowed_process = borrowed_thread.process.borrow_mut(); 82 | 83 | remove_thread_from_process_queues(&borrowed_thread, thread.clone(), &mut borrowed_process); 84 | 85 | debug::log("Removed thread from process"); 86 | 87 | check_should_remove_process(borrowed_process, &borrowed_thread)?; 88 | Ok(()) 89 | } 90 | 91 | /// Removes the thread from the respective process queue, depending on the thread state. 92 | pub(crate) fn remove_thread_from_process_queues( 93 | borrowed_thread: &Ref, 94 | thread: Rc>, 95 | borrowed_process: &mut RefMut, 96 | ) { 97 | match borrowed_thread.state { 98 | ThreadState::Running => { 99 | let scheduler = get_scheduler(); 100 | if let Some(current_thread) = scheduler.running_thread.clone() { 101 | if Rc::ptr_eq(&thread, ¤t_thread) { 102 | scheduler.running_thread = None; 103 | } 104 | } 105 | } 106 | ThreadState::NotStarted => { 107 | let nst_pos = borrowed_process 108 | .not_started_threads 109 | .iter() 110 | .position(|t| Rc::ptr_eq(t, &thread)) 111 | .unwrap(); 112 | borrowed_process.not_started_threads.swap_remove(nst_pos); 113 | } 114 | ThreadState::Ready => { 115 | let nst_pos = borrowed_process 116 | .ready_threads 117 | .iter() 118 | .position(|t| Rc::ptr_eq(t, &thread)) 119 | .unwrap(); 120 | borrowed_process.ready_threads.swap_remove(nst_pos); 121 | } 122 | ThreadState::Sleeping(_) => { 123 | let nst_pos = borrowed_process 124 | .sleeping_threads 125 | .iter() 126 | .position(|t| Rc::ptr_eq(t, &thread)) 127 | .unwrap(); 128 | borrowed_process.sleeping_threads.swap_remove(nst_pos); 129 | } 130 | _ => {} 131 | } 132 | } 133 | 134 | /// Checks if the process has no threads and can be safely removed. 135 | fn check_should_remove_process( 136 | borrowed_process: RefMut, 137 | borrowed_thread: &Ref, 138 | ) -> Result<(), AddressNotAligned> { 139 | let thread_vectors = [ 140 | &borrowed_process.not_started_threads, 141 | &borrowed_process.ready_threads, 142 | &borrowed_process.sleeping_threads, 143 | ]; 144 | if thread_vectors.into_iter().all(|v| v.is_empty()) { 145 | //Clean up the process 146 | get_scheduler().remove_process(borrowed_thread.process.clone()); 147 | debug::log("Removed process from scheduler"); 148 | unsafe { 149 | clear_user_mode_mapping(borrowed_process.cr3)?; 150 | } 151 | } 152 | Ok(()) 153 | } 154 | -------------------------------------------------------------------------------- /kernel/src/processes/memory_mapper.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::fence; 2 | 3 | use x86_64::{ 4 | structures::paging::{ 5 | page::AddressNotAligned, PageTable, PageTableFlags, PhysFrame, Size2MiB, Size4KiB, 6 | }, 7 | PhysAddr, 8 | }; 9 | 10 | use crate::{debug, init::get_kernel_information}; 11 | 12 | /// Initializes and returns the level-4 page table that maps memory for a user-mode process. 13 | pub unsafe fn get_user_mode_mapping() -> Option<(PhysFrame, PhysAddr)> { 14 | let kernel_info = get_kernel_information(); 15 | let pmo = kernel_info.physical_memory_offset; 16 | let allocator = kernel_info.allocator; 17 | 18 | debug::log("Creating user mode mapping"); 19 | 20 | let mut allocator = allocator.lock(); 21 | let level_4_frame: PhysFrame = allocator.allocate_frame()?; 22 | let level_3_frame: PhysFrame = allocator.allocate_frame()?; 23 | let level_2_frame: PhysFrame = allocator.allocate_frame()?; 24 | 25 | let page_table_flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; 26 | let user_page_table_flags = page_table_flags | PageTableFlags::USER_ACCESSIBLE; 27 | 28 | let level_4_table_address = level_4_frame.start_address(); 29 | let level_3_table_address = level_3_frame.start_address(); 30 | let level_2_table_address = level_2_frame.start_address(); 31 | // Just take the mapping from the bootloader's page tables 32 | let (level_2_kernel_data_table_address, level_2_kernel_stack_table_address) = 33 | get_kernel_data_and_stack_level_2_table_addresses(pmo); 34 | 35 | let level_4_table = (level_4_table_address.as_u64() + pmo) as *mut PageTable; 36 | let level_4_table = level_4_table.as_mut().unwrap(); 37 | // Mapping 0x0000_0000_0000 to level 3 table 38 | level_4_table[0].set_addr(level_3_table_address, user_page_table_flags); 39 | 40 | let level_3_table = (level_3_table_address.as_u64() + pmo) as *mut PageTable; 41 | let level_3_table = level_3_table.as_mut().unwrap(); 42 | // Mapping 0x0000_0000_0000 to level 2 table 43 | level_3_table[0].set_addr(level_2_table_address, user_page_table_flags); 44 | // Mapping 0x007F_8000_0000 to kernel stack 45 | level_3_table[510].set_addr(level_2_kernel_stack_table_address, page_table_flags); 46 | // Mapping 0x007F_C000_0000 to kernel data 47 | level_3_table[511].set_addr(level_2_kernel_data_table_address, page_table_flags); 48 | 49 | let level_2_table = (level_2_table_address.as_u64() + pmo) as *mut PageTable; 50 | // Mapping level 2 entries to 2mb frames 51 | let level_2_table = level_2_table.as_mut().unwrap(); 52 | level_2_table 53 | .iter_mut() 54 | .take(8) // We're mapping 16mb for now, e.g. 0x0100_0000 55 | .for_each(|entry| { 56 | let frame: PhysFrame = allocator 57 | .allocate_frame() 58 | .expect("Failed to allocate user process frame"); 59 | entry.set_addr( 60 | frame.start_address(), 61 | PageTableFlags::HUGE_PAGE | user_page_table_flags, 62 | ); 63 | }); 64 | 65 | Some((level_4_frame, level_2_table[0].addr())) 66 | } 67 | 68 | unsafe fn get_kernel_data_and_stack_level_2_table_addresses(pmo: u64) -> (PhysAddr, PhysAddr) { 69 | use x86_64::registers::control::Cr3; 70 | let level4 = (Cr3::read().0.start_address().as_u64() + pmo) as *const PageTable; 71 | let level4 = level4.as_ref().unwrap(); 72 | 73 | let level3 = ((level4[0].addr().as_u64() + pmo) as *const PageTable) 74 | .as_ref() 75 | .unwrap(); 76 | (level3[511].addr(), level3[510].addr()) 77 | } 78 | 79 | /// Clears the memory and page-table mapping for a given level 4 page table (assuming user process). 80 | pub unsafe fn clear_user_mode_mapping(level_4_addr: PhysAddr) -> Result<(), AddressNotAligned> { 81 | let kernel_info = get_kernel_information(); 82 | let pmo = kernel_info.physical_memory_offset; 83 | let allocator = kernel_info.allocator; 84 | let mut allocator = allocator.lock(); 85 | let level_4_frame: PhysFrame = PhysFrame::from_start_address(level_4_addr)?; 86 | let level_4_table = { 87 | let level_4_table = (level_4_addr.as_u64() + pmo) as *mut PageTable; 88 | level_4_table.as_mut().unwrap() 89 | }; 90 | 91 | let level_3_addr = level_4_table[0].addr(); 92 | let level_3_frame: PhysFrame = PhysFrame::from_start_address(level_3_addr)?; 93 | let level_3_table = { 94 | let level_3_table = (level_3_addr.as_u64() + pmo) as *mut PageTable; 95 | level_3_table.as_mut().unwrap() 96 | }; 97 | 98 | let level_2_addr = level_3_table[0].addr(); 99 | let level_2_frame: PhysFrame = PhysFrame::from_start_address(level_2_addr)?; 100 | let level_2_table = { 101 | let level_2_table = (level_2_addr.as_u64() + pmo) as *mut PageTable; 102 | level_2_table.as_mut().unwrap() 103 | }; 104 | fence(core::sync::atomic::Ordering::SeqCst); 105 | // First we go through the memory allocations and free them 106 | level_2_table 107 | .iter_mut() 108 | .filter(|entry| !entry.is_unused()) 109 | .for_each(|entry| { 110 | if entry.flags().contains(PageTableFlags::HUGE_PAGE) { 111 | allocator.deallocate_frame(PhysFrame::::containing_address(entry.addr())); 112 | } else { 113 | allocator.deallocate_frame(entry.frame().unwrap()); 114 | } 115 | }); 116 | 117 | // Then we free the page tables themselves 118 | allocator.deallocate_frame(level_2_frame); 119 | allocator.deallocate_frame(level_3_frame); 120 | allocator.deallocate_frame(level_4_frame); 121 | 122 | debug::log("Cleared user mode mapping"); 123 | 124 | Ok(()) 125 | } 126 | -------------------------------------------------------------------------------- /kernel/src/processes/process.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | 3 | use crate::processes::memory_mapper::get_user_mode_mapping; 4 | use crate::{debug, init::get_kernel_information}; 5 | use alloc::rc::Rc; 6 | use internal_utils::get_current_tick; 7 | use x86_64::{PhysAddr, VirtAddr}; 8 | 9 | use alloc::vec::Vec; 10 | 11 | use super::thread::{Thread, ThreadState}; 12 | 13 | #[derive(Debug)] 14 | pub struct Process { 15 | /// The process's ID. 16 | pub id: u64, 17 | /// The page table the process is using. 18 | pub cr3: PhysAddr, 19 | /// Total ticks the process has been running for. 20 | pub total_ticks: u64, 21 | /// The tick the process has been created on. 22 | pub start_tick: u64, 23 | /// The tick the process has been last ran on. 24 | pub last_tick: u64, 25 | /// Is the process a kernel process (should it run in ring 0 or 3?). 26 | pub kernel_process: bool, 27 | /// The threads of the process that have not started yet. 28 | pub not_started_threads: Vec>>, 29 | /// The threads of the process that are eligible to run. 30 | pub ready_threads: Vec>>, 31 | /// The threads of the process that are sleeping. 32 | pub sleeping_threads: Vec>>, 33 | } 34 | 35 | impl Process { 36 | /// Returns the percentage of ticks the process spent running, calculated from the creation time of the process 37 | pub fn tick_density(&self, current_tick: u64) -> u64 { 38 | let ticks_maximum = current_tick - self.start_tick; 39 | self.total_ticks * 100 / ticks_maximum 40 | } 41 | 42 | /// Creates a new process from a function pointer. 43 | /// 44 | /// # Safety 45 | /// This function is unsafe as it copies the first 1024 bytes from the function pointer. 46 | // TODO: Loading the process from e.g. an ELF file 47 | // We have to look up the structure of an ELF file and prepare the user memory mapping according to it. 48 | // Then we can load the program and it's data to proper places and create a process out of it. 49 | pub unsafe fn from_extern(function: extern "C" fn(), id: u64) -> Self { 50 | let function_pointer = function as *const () as *const u8; 51 | let kernel_info = get_kernel_information(); 52 | unsafe { 53 | let (user_page_map, user_physical_address) = 54 | get_user_mode_mapping().expect("Error while creating user mode mapping"); 55 | 56 | let user_mode_code_address = 0x1000u64; 57 | 58 | let virtual_address = VirtAddr::new( 59 | user_physical_address.as_u64() 60 | + user_mode_code_address 61 | + kernel_info.physical_memory_offset, 62 | ) 63 | .as_mut_ptr::(); 64 | debug::log("Loading program"); 65 | 66 | virtual_address.copy_from_nonoverlapping(function_pointer, 1024); 67 | 68 | Process { 69 | id, 70 | cr3: user_page_map.start_address(), 71 | total_ticks: 0, 72 | start_tick: get_current_tick(), 73 | last_tick: 0, 74 | kernel_process: false, 75 | not_started_threads: Vec::new(), 76 | ready_threads: Vec::new(), 77 | sleeping_threads: Vec::new(), 78 | } 79 | } 80 | } 81 | 82 | /// Updates the sleeping threads, waking them up if they are sleeping for too long. 83 | pub fn update_sleeping_threads(this: Rc>) { 84 | let mut process = this.borrow_mut(); 85 | if process.sleeping_threads.is_empty() { 86 | return; 87 | } 88 | let mut drained = Vec::new(); 89 | process.sleeping_threads.retain(|thread| { 90 | let mut borrowed_thread = thread.borrow_mut(); 91 | match borrowed_thread.state { 92 | ThreadState::Sleeping(ref mut sleep_ticks) => { 93 | if *sleep_ticks > 0 { 94 | *sleep_ticks -= 1; 95 | true 96 | } else { 97 | borrowed_thread.state = ThreadState::Ready; 98 | drained.push(thread.clone()); 99 | false 100 | } 101 | } 102 | _ => unreachable!(), 103 | } 104 | }); 105 | process.ready_threads.extend(drained); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /kernel/src/processes/registers_state.rs: -------------------------------------------------------------------------------- 1 | use x86_64::VirtAddr; 2 | 3 | #[repr(C)] 4 | #[derive(Copy, Clone, Debug)] 5 | pub struct RegistersState { 6 | pub r15: u64, 7 | pub r14: u64, 8 | pub r13: u64, 9 | pub r12: u64, 10 | pub r11: u64, 11 | pub r10: u64, 12 | pub r9: u64, 13 | pub r8: u64, 14 | pub rdi: u64, 15 | pub rsi: u64, 16 | pub rbp: u64, 17 | pub rdx: u64, 18 | pub rcx: u64, 19 | pub rbx: u64, 20 | pub rax: u64, 21 | pub rip: VirtAddr, 22 | pub cs: u64, 23 | pub rflags: u64, 24 | pub rsp: VirtAddr, 25 | pub ss: u64, 26 | } 27 | 28 | impl RegistersState { 29 | pub fn new(rip: VirtAddr, rflags: u64, rsp: VirtAddr) -> Self { 30 | RegistersState { 31 | r15: 0, 32 | r14: 0, 33 | r13: 0, 34 | r12: 0, 35 | r11: 0, 36 | r10: 0, 37 | r9: 0, 38 | r8: 0, 39 | rdi: 0, 40 | rsi: 0, 41 | rbp: 0, 42 | rdx: 0, 43 | rcx: 0, 44 | rbx: 0, 45 | rax: 0, 46 | rip, 47 | cs: 0, 48 | rflags, 49 | rsp, 50 | ss: 0, 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /kernel/src/processes/scheduler.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | 3 | use alloc::{collections::VecDeque, rc::Rc}; 4 | 5 | use super::{process::Process, thread::Thread, RegistersState}; 6 | use crate::processes::dispatcher::switch_to_thread; 7 | 8 | static mut SCHEDULER: Option = None; 9 | 10 | pub fn get_scheduler() -> &'static mut Scheduler { 11 | unsafe { SCHEDULER.get_or_insert_default() } 12 | } 13 | 14 | /// Runs the scheduler, giving it control of the CPU. 15 | /// 16 | /// Will return only if there are no threads at all to run. 17 | pub fn run_processes() -> Option<()> { 18 | switch_to_thread(get_scheduler().schedule()?); 19 | } 20 | 21 | pub fn add_process(process: Process) -> Rc> { 22 | get_scheduler().add_process(process) 23 | } 24 | 25 | pub fn run_next_thread() -> Option<()> { 26 | let next_thread = get_scheduler().schedule(); 27 | if let Some(thread) = next_thread { 28 | crate::processes::dispatcher::switch_to_thread(thread); 29 | } else { 30 | Some(()) 31 | } 32 | } 33 | 34 | #[derive(Default)] 35 | pub struct Scheduler { 36 | /// The currently running process. 37 | pub running_thread: Option>>, 38 | /// The list of processes that are registered. 39 | processes: VecDeque>>, 40 | } 41 | 42 | impl Scheduler { 43 | /// Adds a process to the scheduling queue so it will be ran. 44 | pub fn add_process(&mut self, process: Process) -> Rc> { 45 | let rc = Rc::new(RefCell::new(process)); 46 | self.processes.push_back(rc.clone()); 47 | rc 48 | } 49 | 50 | /// Removes the process from the queue. 51 | pub fn remove_process(&mut self, process: Rc>) { 52 | self.processes.retain(|p| !Rc::ptr_eq(p, &process)); 53 | } 54 | 55 | /// Manages scheduler operations on a timer tick 56 | pub fn timer_tick(&self, registers_state: RegistersState, tick: u64) { 57 | if let Some(thread) = self.running_thread.clone() { 58 | let mut thread_mut = thread.borrow_mut(); 59 | 60 | thread_mut.registers_state = registers_state; 61 | thread_mut.total_ticks += tick - thread_mut.last_tick; 62 | thread_mut.last_tick = tick; 63 | let mut process = thread_mut.process.borrow_mut(); 64 | process.total_ticks += tick - process.last_tick; 65 | process.last_tick = tick; 66 | } 67 | } 68 | 69 | /// Returns the thread that should be ran next. 70 | pub fn schedule(&mut self) -> Option>> { 71 | if self.processes.is_empty() { 72 | return None; 73 | } 74 | // We're taking the first process in the queue that returns a runnable thread 75 | let processes = &self.processes; 76 | let process_index = processes.iter().position(|process| { 77 | Process::update_sleeping_threads(process.clone()); 78 | !process.borrow().ready_threads.is_empty() 79 | })?; 80 | let process = self.processes.remove(process_index)?; 81 | let thread = Scheduler::get_thread_to_run(process.clone())?; 82 | // Putting the process at the back of the queue 83 | self.processes.push_back(process); 84 | 85 | Some(thread) 86 | } 87 | 88 | /// Returns the thread from the process that should be ran next. 89 | fn get_thread_to_run(process: Rc>) -> Option>> { 90 | let mut process_borrowed = process.borrow_mut(); 91 | // Taking the first thread in the chosen process 92 | if process_borrowed.ready_threads.is_empty() { 93 | return None; 94 | } 95 | let thread = process_borrowed.ready_threads.remove(0); 96 | // Putting the thread at the back of the thread-queue 97 | process_borrowed.ready_threads.push(thread.clone()); 98 | Some(thread) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /kernel/src/processes/thread.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | 3 | use alloc::rc::Rc; 4 | use internal_utils::get_current_tick; 5 | use x86_64::VirtAddr; 6 | 7 | use super::process::Process; 8 | 9 | use super::dispatcher::remove_thread_from_process_queues; 10 | use super::RegistersState; 11 | 12 | #[derive(Debug, Clone)] 13 | pub enum ThreadState { 14 | NotStarted, 15 | Ready, 16 | Running, 17 | Sleeping(u64), 18 | Terminated, 19 | } 20 | 21 | #[derive(Debug)] 22 | pub struct Thread { 23 | /// The thread's ID (in-process). 24 | pub id: u64, 25 | /// The thread's current state. 26 | pub state: ThreadState, 27 | /// The state of the registers. 28 | pub registers_state: RegistersState, 29 | /// Total ticks the thread has been running for. 30 | pub total_ticks: u64, 31 | /// The tick the thread has been created on. 32 | pub start_tick: u64, 33 | /// The tick the thread has been last ran on. 34 | pub last_tick: u64, 35 | /// The process the thread is running for. 36 | pub process: Rc>, 37 | } 38 | 39 | impl Thread { 40 | /// Returns the percentage of ticks the thread spent running, calculated from the creation time of the thread 41 | pub fn tick_density(&self, current_tick: u64) -> u64 { 42 | let ticks_maximum = current_tick - self.start_tick; 43 | self.total_ticks * 100 / ticks_maximum 44 | } 45 | 46 | pub fn change_state(thread: Rc>, state: ThreadState) { 47 | { 48 | let borrowed_thread = thread.borrow(); 49 | let mut borrowed_process = borrowed_thread.process.borrow_mut(); 50 | remove_thread_from_process_queues( 51 | &borrowed_thread, 52 | thread.clone(), 53 | &mut borrowed_process, 54 | ); 55 | } 56 | let mut borrowed_thread = thread.borrow_mut(); 57 | borrowed_thread.state = state; 58 | { 59 | let mut borrowed_process = borrowed_thread.process.borrow_mut(); 60 | match borrowed_thread.state { 61 | ThreadState::NotStarted => borrowed_process.not_started_threads.push(thread.clone()), 62 | ThreadState::Ready => borrowed_process.ready_threads.push(thread.clone()), 63 | ThreadState::Running => panic!("Trying to change a thread to running state - use dispatcher::switch_to_thread() instead"), 64 | ThreadState::Sleeping(_) => borrowed_process.sleeping_threads.push(thread.clone()), 65 | ThreadState::Terminated => {} 66 | } 67 | } 68 | } 69 | 70 | /// Creates a new thread with the given starting address and stack pointer. 71 | /// 72 | /// # Safety 73 | /// This function is unsafe as it does not enforce pointing the instruction and stack pointers to valid addresses. 74 | pub unsafe fn new_native( 75 | address: u64, 76 | stack_pointer: u64, 77 | process: Rc>, 78 | ) -> Rc> { 79 | let thread = Thread { 80 | id: { 81 | let process = process.borrow(); 82 | process.not_started_threads.len() 83 | + process.ready_threads.len() 84 | + process.sleeping_threads.len() 85 | } as u64, 86 | state: ThreadState::NotStarted, 87 | total_ticks: 0, 88 | start_tick: get_current_tick(), 89 | last_tick: 0, 90 | process: process.clone(), 91 | registers_state: RegistersState::new( 92 | VirtAddr::new(address), 93 | 0x200, 94 | VirtAddr::new(stack_pointer), 95 | ), 96 | }; 97 | let rc = Rc::new(RefCell::new(thread)); 98 | process.borrow_mut().not_started_threads.push(rc.clone()); 99 | rc 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /kernel/src/syscalls.rs: -------------------------------------------------------------------------------- 1 | pub mod system_call; 2 | -------------------------------------------------------------------------------- /kernel/src/syscalls/system_call.rs: -------------------------------------------------------------------------------- 1 | use alloc::rc::Rc; 2 | use lazy_static::lazy_static; 3 | use spin::Mutex; 4 | use x86_64::VirtAddr; 5 | 6 | use crate::processes::thread::Thread; 7 | use crate::{debug, memory::with_kernel_memory, processes::get_scheduler}; 8 | 9 | use crate::interrupts::gdt::GDT; 10 | use core::arch::asm; 11 | use core::cell::RefCell; 12 | use internal_utils::{mov_all, push_all}; 13 | 14 | pub type SysCallHandlerFunc = extern "C" fn(u64, u64, Rc>) -> u64; 15 | 16 | /// A system call handler that panics. 17 | extern "C" fn fail_syscall(_arg1: u64, _arg2: u64, calling_thread: Rc>) -> u64 { 18 | panic!("NO SYSCALL DEFINED"); 19 | } 20 | 21 | lazy_static! { 22 | static ref SYSCALLS: Mutex<[SysCallHandlerFunc; 1024]> = Mutex::new([fail_syscall; 1024]); 23 | } 24 | 25 | /// Sets up the LSTAR, FSTAR and STAR model-specific registers so it's possible to use `syscall`. 26 | pub(crate) fn setup_syscalls() { 27 | use x86_64::registers::model_specific; 28 | use x86_64::registers::model_specific::{Efer, EferFlags}; 29 | use x86_64::registers::rflags::RFlags; 30 | debug::log("Loading LSTAR, FSTAR and STAR"); 31 | // LSTAR stores the address of the `syscall` handler. 32 | model_specific::LStar::write(VirtAddr::from_ptr(_syscall as *const ())); 33 | // FSTAR stores which bits of the flag register are cleared by `syscall`. 34 | model_specific::SFMask::write(RFlags::all()); 35 | // STAR stores the indices of the GDT entries for the kernel and user descriptors. 36 | model_specific::Star::write( 37 | GDT.1.user_code_selector, 38 | GDT.1.user_data_selector, 39 | GDT.1.kernel_code_selector, 40 | GDT.1.kernel_data_selector, 41 | ) 42 | .unwrap(); 43 | let new_efer_flags = { 44 | let mut flags = Efer::read(); 45 | flags.set(EferFlags::SYSTEM_CALL_EXTENSIONS, true); 46 | flags 47 | }; 48 | unsafe { 49 | Efer::write(new_efer_flags); 50 | } 51 | debug::log("Syscalls active"); 52 | } 53 | 54 | #[allow(dead_code)] 55 | /// Registers a system call with a handler. 56 | pub fn register_syscall(syscall_number: u16, handler: SysCallHandlerFunc) { 57 | SYSCALLS.lock()[syscall_number as usize] = handler; 58 | } 59 | 60 | /// Handles a system call. 61 | /// On entry to this function: 62 | /// - the instruction pointer is stored in RCX 63 | /// - the flags are stored in R11 64 | /// - the stack pointer is still targeting the user mode stack 65 | /// 66 | /// To properly handle this, we need to: 67 | /// 1. save the user mode stack pointer 68 | /// 2. set the syscall stack pointer 69 | /// 3. save all the registers we need to preserve on the stack 70 | /// 4. do our thing with the values we got from the user 71 | /// 5. restore the registers from the stack 72 | /// 6. restore the user mode stack pointer 73 | /// 7. iretq 74 | #[no_mangle] 75 | #[naked] 76 | unsafe extern "C" fn _syscall() -> ! { 77 | asm!( 78 | "cli", 79 | "mov r10, rsp", 80 | "mov rsp, 0x007F80014000", // User stack saved in R10, start of kernel stack loaded 81 | push_all!(), 82 | "mov r9, rsp", 83 | "push r9", 84 | " call handler", // Return value is in RAX 85 | " push rax", 86 | " call get_code_selector", 87 | " push rax", 88 | " call get_data_selector", 89 | " pop rbx", 90 | " mov rcx, rax", // struct is in RBX+RCX now 91 | " pop rax", // We get the syscall value back 92 | "pop r9", // We get the register state value back 93 | // Preparing iretq 94 | "push rcx", // data selector 95 | "push [r9 + 40]", // process stack pointer 96 | "mov r11, [r9 + 32]", // rflags 97 | "or r11, 0x200", 98 | "and r11, 0xffffffffffffbfff", 99 | "push r11", // rflags 100 | "push rbx", // code selector 101 | "push [r9 + 96]", // instruction address to return to 102 | "push rax", // We want to keep the RAX 103 | mov_all!(), 104 | "pop rax", 105 | "iretq", 106 | options(noreturn) 107 | ); 108 | } 109 | 110 | #[no_mangle] 111 | extern "C" fn handler(name: u64, arg1: u64, arg2: u64) -> u64 { 112 | // This block executes after saving the user state and before returning back 113 | with_kernel_memory(|| { 114 | let thread = get_scheduler().running_thread.clone().unwrap(); 115 | SYSCALLS.lock()[name as u16 as usize](arg1, arg2, thread) 116 | }) 117 | } 118 | 119 | // TODO Try to combine both of these functions to make it faster. 120 | // We should be able to return a C-style struct with two u64s and manage it through ASM. 121 | #[no_mangle] 122 | extern "C" fn get_code_selector() -> u64 { 123 | with_kernel_memory(|| { 124 | let thread = get_scheduler().running_thread.clone().unwrap(); 125 | let thread = thread.as_ref().borrow(); 126 | let process = thread.process.as_ref().borrow(); 127 | if process.kernel_process { 128 | (GDT.1.kernel_code_selector.index() * 8) as u64 129 | } else { 130 | ((GDT.1.user_code_selector.index() * 8) | 3) as u64 131 | } 132 | }) 133 | } 134 | 135 | #[no_mangle] 136 | extern "C" fn get_data_selector() -> u64 { 137 | with_kernel_memory(|| { 138 | let thread = get_scheduler().running_thread.clone().unwrap(); 139 | let thread = thread.as_ref().borrow(); 140 | let process = thread.process.as_ref().borrow(); 141 | if process.kernel_process { 142 | (GDT.1.kernel_data_selector.index() * 8) as u64 143 | } else { 144 | ((GDT.1.user_data_selector.index() * 8) | 3) as u64 145 | } 146 | }) 147 | } 148 | -------------------------------------------------------------------------------- /rost-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["workspace-inheritance"] 2 | 3 | [package] 4 | name = "rost-lib" 5 | version = "0.1.0" 6 | edition = { workspace=true } 7 | 8 | [dependencies] 9 | internal_utils = { workspace=true } 10 | kernel = { workspace=true } 11 | ata = { workspace=true } 12 | bitflags = { workspace=true } 13 | lazy_static = { workspace=true } 14 | -------------------------------------------------------------------------------- /rost-lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // no standard library 2 | #![no_main] 3 | #![allow(incomplete_features)] 4 | #![feature(generic_const_exprs, core_intrinsics, alloc_error_handler)] 5 | 6 | use core::arch::asm; 7 | 8 | use crate::syscall_name::SysCallName; 9 | extern crate alloc; 10 | 11 | pub mod syscall_name; 12 | pub mod thread_utils; 13 | 14 | pub fn __initialize_syscalls() { 15 | use kernel::syscalls::system_call::register_syscall; 16 | register_syscall( 17 | SysCallName::ThreadExit as u16, 18 | thread_utils::handler_thread_exit, 19 | ); 20 | register_syscall( 21 | SysCallName::ThreadYield as u16, 22 | thread_utils::handler_thread_yield, 23 | ); 24 | register_syscall( 25 | SysCallName::ThreadSleep as u16, 26 | thread_utils::handler_thread_sleep, 27 | ); 28 | } 29 | 30 | #[inline(always)] 31 | pub(crate) fn syscall(name: SysCallName, arg1: u64, arg2: u64) -> u64 { 32 | unsafe { 33 | let result: u64; 34 | asm!( 35 | "push r10; push r11; push rcx", 36 | "syscall", 37 | "pop rcx; pop r11; pop r10", 38 | in("rdi")(name as u64), 39 | in("rsi")(arg1), 40 | in("rdx")(arg2), 41 | out("rax")(result) 42 | ); 43 | result 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rost-lib/src/syscall_name.rs: -------------------------------------------------------------------------------- 1 | #[repr(u64)] 2 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 3 | pub enum SysCallName { 4 | ThreadExit = 300, 5 | ThreadYield = 301, 6 | ThreadSleep = 302, 7 | } 8 | -------------------------------------------------------------------------------- /rost-lib/src/thread_utils.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | 3 | use alloc::rc::Rc; 4 | use kernel::processes::dispatcher::exit_thread; 5 | use kernel::processes::run_next_thread; 6 | use kernel::processes::thread::{Thread, ThreadState}; 7 | 8 | use crate::syscall_name::SysCallName; 9 | 10 | // TODO Add handling for process/thread exit code 11 | // We should probably have thread exit code handlers for non-zero exit codes, and pass them as the process exit code. 12 | pub(crate) extern "C" fn handler_thread_exit( 13 | _code: u64, 14 | _: u64, 15 | caller: Rc>, 16 | ) -> u64 { 17 | exit_thread(caller).unwrap(); 18 | run_next_thread(); 19 | panic!("No threads to run"); 20 | } 21 | 22 | pub(crate) extern "C" fn handler_thread_yield(_: u64, _: u64, _caller: Rc>) -> u64 { 23 | run_next_thread(); 24 | panic!("No threads to run"); 25 | } 26 | 27 | pub(crate) extern "C" fn handler_thread_sleep( 28 | time: u64, 29 | _: u64, 30 | caller: Rc>, 31 | ) -> u64 { 32 | Thread::change_state(caller, ThreadState::Sleeping(time)); 33 | run_next_thread(); 34 | panic!("No threads to run"); 35 | } 36 | 37 | pub extern "C" fn thread_exit(status: u64) -> ! { 38 | crate::syscall(SysCallName::ThreadExit, status, 0); 39 | panic!("Thread exited"); 40 | } 41 | 42 | pub extern "C" fn thread_yield() { 43 | crate::syscall(SysCallName::ThreadYield, 0, 0); 44 | } 45 | 46 | pub extern "C" fn thread_sleep(time: u64) { 47 | crate::syscall(SysCallName::ThreadSleep, time, 0); 48 | } 49 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2022-04-27" 3 | components = [ "llvm-tools-preview", "rust-src", "rustfmt", "clippy" ] 4 | targets = [ "x86_64-unknown-none" ] 5 | profile = "default" -------------------------------------------------------------------------------- /src/assets/rost-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 23 | 24 | 26 | image/svg+xml 27 | 29 | 30 | 31 | 32 | 33 | 35 | 55 | 60 | 65 | 66 | -------------------------------------------------------------------------------- /src/assets/rost-logo.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ComicalCache/rOSt/38b4c0bedaf329845978493209e616e95915d5b4/src/assets/rost-logo.tga -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // no standard library 2 | #![no_main] 3 | #![allow(incomplete_features)] 4 | #![feature( 5 | custom_test_frameworks, 6 | abi_x86_interrupt, 7 | generic_const_exprs, 8 | core_intrinsics, 9 | alloc_error_handler 10 | )] 11 | #![test_runner(test_framework::test_runner::test_runner)] 12 | #![reexport_test_harness_main = "test_main"] 13 | extern crate alloc; 14 | 15 | use alloc::rc::Rc; 16 | use bootloader::{entry_point, BootInfo}; 17 | use core::arch::asm; 18 | use core::cell::RefCell; 19 | use core::panic::PanicInfo; 20 | use internal_utils::structures::kernel_information::KernelInformation; 21 | use internal_utils::{constants::MIB, serial_println}; 22 | use rost_lib::syscall_name::SysCallName; 23 | use tinytga::RawTga; 24 | use vga::vga_core::{Clearable, ImageDrawable}; 25 | 26 | use core::alloc::Layout; 27 | 28 | entry_point!(kernel); 29 | pub fn kernel(boot_info: &'static mut BootInfo) -> ! { 30 | let kernel_info = kernel::init(boot_info); 31 | bootup_sequence(kernel_info.clone()); 32 | 33 | #[cfg(test)] 34 | kernel_test(kernel_info); 35 | #[cfg(not(test))] 36 | kernel_main(kernel_info); 37 | 38 | kernel::hlt_loop(); 39 | } 40 | 41 | fn bootup_sequence(kernel_info: KernelInformation) { 42 | rost_lib::__initialize_syscalls(); 43 | kernel::register_driver(vga::driver_init); 44 | kernel::register_driver(ata::driver_init); 45 | kernel::reload_drivers(); 46 | let data = include_bytes!("./assets/rost-logo.tga"); 47 | let logo = RawTga::from_slice(data).unwrap(); 48 | let logo_header = logo.header(); 49 | let mut vga_device = vga::vga_device::VGADeviceFactory::from_kernel_info(kernel_info); 50 | vga_device.clear(vga::vga_color::BLACK); 51 | vga_device.draw_image( 52 | (vga_device.width as u16 - logo_header.width) / 2, 53 | (vga_device.height as u16 - logo_header.height) / 2, 54 | &logo, 55 | ); 56 | } 57 | 58 | #[no_mangle] 59 | extern "C" fn user_mode_check_1() { 60 | exit(0); 61 | } 62 | 63 | #[no_mangle] 64 | extern "C" fn user_mode_check_2() { 65 | let mut i = 1; 66 | loop { 67 | i += 1; 68 | if i > 100_000_000 { 69 | break; 70 | } 71 | } 72 | exit(0); 73 | } 74 | 75 | #[inline(always)] 76 | pub(crate) fn syscall(name: SysCallName, arg1: u64, arg2: u64) -> u64 { 77 | unsafe { 78 | let result: u64; 79 | asm!( 80 | "push r10; push r11; push rcx", 81 | "syscall", 82 | "pop rcx; pop r11; pop r10", 83 | in("rdi")(name as u64), 84 | in("rsi")(arg1), 85 | in("rdx")(arg2), 86 | out("rax")(result) 87 | ); 88 | result 89 | } 90 | } 91 | 92 | fn exit(status: u64) -> ! { 93 | crate::syscall(SysCallName::ThreadExit, status, 0); 94 | panic!("Thread exited"); 95 | } 96 | 97 | fn sleep(time: u64) { 98 | crate::syscall(SysCallName::ThreadSleep, time, 0); 99 | } 100 | 101 | pub fn kernel_main(kernel_info: KernelInformation) { 102 | use kernel::processes::{ 103 | add_process, 104 | process::Process, 105 | run_processes, 106 | thread::{Thread, ThreadState}, 107 | }; 108 | let process1: Rc>; 109 | let thread1: Rc>; 110 | unsafe { 111 | process1 = add_process(Process::from_extern(user_mode_check_1, 1)); 112 | thread1 = Thread::new_native(0x1000, 2 * MIB, process1); 113 | } 114 | Thread::change_state(thread1, ThreadState::Ready); 115 | 116 | //let process2 = add_process(Process::new(user_mode_check_2, 2)); 117 | //let _thread2 = Thread::new(0x1000, 2 * MIB, process2); 118 | 119 | run_processes(); 120 | serial_println!("Something went wrong"); 121 | /* 122 | let test = Box::new(4); 123 | log_println!("New boxed value: {:#?}", test); 124 | log_println!("im not dying :)"); 125 | */ 126 | /* 127 | log_println!("Getting all disks..."); 128 | let disks = ata::get_all_disks(); 129 | log_println!("Got {} disks, taking the non-bootable one...", disks.len()); 130 | let mut disk = disks 131 | .into_iter() 132 | .map(|mut disk| (disk.has_bootloader(), disk)) 133 | .find(|(boot, _)| !boot.unwrap_or(true)) 134 | .expect("No non-bootable disk found") 135 | .1; 136 | log_println!("Got a disk, looking for partitions..."); 137 | let mut partitions = disk.get_partitions().expect("Error getting partitions"); 138 | if partitions.len() == 0 { 139 | log_println!("No partitions found, creating a new one..."); 140 | let partition_size = disk.descriptor.lba_48_addressable_sectors as u32 / 2; 141 | disk.create_partition(partition_size, 0xED) 142 | .expect("Error creating partition"); 143 | log_println!("Partition created, double-checking..."); 144 | partitions = disk.get_partitions().expect("Error getting partitions"); 145 | if partitions.len() == 0 { 146 | log_println!("No partitions found, giving up."); 147 | return; 148 | } 149 | } 150 | log_println!("Found {} partitions:", partitions.len()); 151 | for partition in partitions { 152 | log_println!( 153 | "{:8} - starting at {:8X}", 154 | format_size(partition.descriptor.sectors * 512), 155 | partition.descriptor.start_lba 156 | ) 157 | } 158 | */ 159 | } 160 | 161 | /// This is the main function for tests. 162 | #[cfg(test)] 163 | pub fn kernel_test(_kernel_info: KernelInformation) { 164 | use test_framework::test_runner::KERNEL_INFO; 165 | 166 | unsafe { KERNEL_INFO = Some(_kernel_info) }; 167 | test_main(); 168 | } 169 | 170 | #[alloc_error_handler] 171 | fn alloc_error_handler(layout: Layout) -> ! { 172 | panic!("allocation error: {:?}", layout) 173 | } 174 | 175 | #[cfg(not(test))] 176 | #[panic_handler] 177 | pub fn panic_handler(info: &PanicInfo) -> ! { 178 | use test_framework::ansi_colors; 179 | 180 | serial_println!("{}", ansi_colors::Red("[PANIC]")); 181 | serial_println!("Error: {}\n", info); 182 | kernel::hlt_loop(); 183 | } 184 | 185 | #[cfg(test)] 186 | #[panic_handler] 187 | pub fn test_panic_handler(info: &PanicInfo) -> ! { 188 | use test_framework::{ 189 | ansi_colors, 190 | qemu_exit::{exit_qemu, QemuExitCode}, 191 | }; 192 | 193 | serial_println!("{}", ansi_colors::Red("[PANIC]")); 194 | serial_println!("Error: {}\n", info); 195 | exit_qemu(QemuExitCode::Failed); 196 | 197 | kernel::hlt_loop(); 198 | } 199 | 200 | #[cfg(test)] 201 | mod tests { 202 | use alloc::boxed::Box; 203 | use internal_utils::structures::kernel_information::KernelInformation; 204 | use x86_64::structures::paging::{Size2MiB, Size4KiB}; 205 | 206 | #[test_case] 207 | fn should_allocate_frame(kernel_information: KernelInformation) { 208 | use x86_64::structures::paging::PhysFrame; 209 | let mut allocator = kernel_information.allocator.lock(); 210 | let size = allocator.get_free_memory_size(); 211 | let frame: Option> = allocator.allocate_frame(); 212 | assert!(frame.is_some()); 213 | assert_eq!(4096, size - allocator.get_free_memory_size()); 214 | } 215 | 216 | #[test_case] 217 | fn should_allocate_big_frame(kernel_information: KernelInformation) { 218 | use x86_64::structures::paging::PhysFrame; 219 | let mut allocator = kernel_information.allocator.lock(); 220 | let size = allocator.get_free_memory_size(); 221 | let frame: Option> = allocator.allocate_frame(); 222 | assert!(frame.is_some()); 223 | assert_eq!(2 * 1024 * 1024, size - allocator.get_free_memory_size()); 224 | } 225 | 226 | #[test_case] 227 | fn should_allocate_small_box(_: KernelInformation) { 228 | let boxed = Box::new(4); 229 | assert_eq!(4, *boxed); 230 | } 231 | 232 | #[test_case] 233 | fn should_allocate_large_box(_: KernelInformation) { 234 | let boxed = Box::new([13u8; 256]); 235 | assert_eq!(boxed.len(), 256); 236 | for i in 0..256 { 237 | assert_eq!(boxed[i], 13); 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /test_framework/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["workspace-inheritance"] 2 | 3 | [package] 4 | name = "test_framework" 5 | version = "0.1.0" 6 | edition = { workspace=true } 7 | 8 | [dependencies] 9 | x86_64 = { workspace=true } 10 | internal_utils = { workspace=true } -------------------------------------------------------------------------------- /test_framework/src/ansi_colors.rs: -------------------------------------------------------------------------------- 1 | mod green; 2 | pub use green::Green; 3 | mod red; 4 | pub use red::Red; 5 | mod yellow; 6 | pub use yellow::Yellow; 7 | -------------------------------------------------------------------------------- /test_framework/src/ansi_colors/green.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// Colors text Green using the ANSI escape sequence. 4 | pub struct Green(pub &'static str); 5 | 6 | impl fmt::Display for Green { 7 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 8 | write!(f, "\x1B[32m{}\x1B[0m", self.0)?; 9 | Ok(()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test_framework/src/ansi_colors/red.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// Colors text Red using the ANSI escape sequence. 4 | pub struct Red(pub &'static str); 5 | 6 | impl fmt::Display for Red { 7 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 8 | write!(f, "\x1B[31m{}\x1B[0m", self.0)?; 9 | Ok(()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test_framework/src/ansi_colors/yellow.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// Colors text Yellow using the ANSI escape sequence. 4 | pub struct Yellow(pub &'static str); 5 | 6 | impl fmt::Display for Yellow { 7 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 8 | write!(f, "\x1B[93m{}\x1B[0m", self.0)?; 9 | Ok(()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test_framework/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // no standard library 2 | #![no_main] 3 | 4 | pub mod ansi_colors; 5 | pub mod qemu_exit; 6 | pub mod test_runner; 7 | pub mod testable; 8 | -------------------------------------------------------------------------------- /test_framework/src/qemu_exit.rs: -------------------------------------------------------------------------------- 1 | /// Stores the exit codes for the QEMU system. 2 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 3 | #[repr(u32)] 4 | #[allow(dead_code)] 5 | pub enum QemuExitCode { 6 | Success = 0x10, 7 | Failed = 0x11, 8 | } 9 | 10 | /// Exits the QEMU system with the given exit code. 11 | #[allow(dead_code)] 12 | pub fn exit_qemu(exit_code: QemuExitCode) { 13 | use x86_64::instructions::port::Port; 14 | 15 | unsafe { 16 | let mut port = Port::new(0xf4); 17 | port.write(exit_code as u32); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test_framework/src/test_runner.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ansi_colors::Yellow, 3 | qemu_exit::{exit_qemu, QemuExitCode}, 4 | testable::Testable, 5 | }; 6 | use internal_utils::serial_println; 7 | use internal_utils::structures::kernel_information::KernelInformation; 8 | 9 | pub static mut KERNEL_INFO: Option = None; 10 | 11 | /// Rusts test runner function that is called to run all annotated tests. 12 | #[allow(dead_code)] 13 | pub fn test_runner(tests: &[&dyn Testable]) { 14 | let test_count = tests.len(); 15 | if test_count > 0 { 16 | serial_println!( 17 | "{} {} {}", 18 | Yellow("Running"), 19 | test_count, 20 | Yellow("test(s):") 21 | ); 22 | for test in tests { 23 | test.run(unsafe { KERNEL_INFO.clone() }.unwrap()); 24 | } 25 | } 26 | 27 | exit_qemu(QemuExitCode::Success); 28 | } 29 | -------------------------------------------------------------------------------- /test_framework/src/testable.rs: -------------------------------------------------------------------------------- 1 | use internal_utils::structures::kernel_information::KernelInformation; 2 | use internal_utils::{serial_print, serial_println}; 3 | 4 | use crate::ansi_colors::Green; 5 | 6 | /// Self documenting test runner trait 7 | pub trait Testable { 8 | fn run(&self, kernel_information: KernelInformation); 9 | } 10 | 11 | impl Testable for T 12 | where 13 | T: Fn(KernelInformation), 14 | { 15 | /// Runs the test and prints the result 16 | fn run(&self, kernel_information: KernelInformation) { 17 | serial_print!("{}...\t", core::any::type_name::()); 18 | self(kernel_information); 19 | serial_println!("{}", Green("[ok]")); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /x86_64-custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 4 | "arch": "x86_64", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "os": "none", 9 | "executables": true, 10 | "linker-flavor": "ld.lld", 11 | "linker": "rust-lld", 12 | "panic-strategy": "abort", 13 | "disable-redzone": true, 14 | "features": "-mmx,-sse,+soft-float", 15 | "pre-link-args": { 16 | "ld.lld": ["--image-base", "0x007FC0000000", "--gc-sections"] 17 | } 18 | } --------------------------------------------------------------------------------