├── .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 | 
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