├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── app_background ├── .cargo │ └── config.toml ├── .cargo_x86 │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── rust-toolchain ├── src │ ├── main.rs │ ├── qr.png │ └── st.rs └── x86_64.json ├── app_c ├── README.md ├── build.sh └── main.c ├── app_console ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── rust-toolchain ├── src │ ├── main.rs │ └── st.rs └── x86_64.json ├── app_cursor ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── rust-toolchain ├── src │ ├── main.rs │ └── st.rs └── x86_64.json ├── app_test ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── rust-toolchain ├── src │ └── main.rs └── x86_64.json ├── assets └── demo.mp4 ├── bootloader ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── kernel │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── allocator.rs │ │ ├── app.rs │ │ ├── drivers │ │ ├── mod.rs │ │ ├── virtio_gpu.rs │ │ └── virtio_input.rs │ │ ├── framebuffer.rs │ │ ├── gdt.rs │ │ ├── globals.rs │ │ ├── interrupts.rs │ │ ├── ioapic.rs │ │ ├── local_apic.rs │ │ ├── logger.rs │ │ ├── main.rs │ │ ├── memory.rs │ │ ├── pci.rs │ │ ├── serial.rs │ │ ├── task │ │ ├── executor.rs │ │ └── mod.rs │ │ └── virtio.rs ├── ovmf ├── rust-toolchain └── src │ └── main.rs ├── build.sh └── docs └── BUILD.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | */target 4 | replay.bin 5 | uefi.img 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // "rust-analyzer.check.allTargets": false, 3 | // "rust-analyzer.check.extraArgs": [ 4 | // "--target", 5 | // "x86_64" 6 | // ], 7 | "rust-analyzer.linkedProjects": [ 8 | "./bootloader/Cargo.toml", 9 | "./app_background/Cargo.toml", 10 | "./app_console/Cargo.toml", 11 | "./app_cursor/Cargo.toml", 12 | "./app_test/Cargo.toml" 13 | ], 14 | "git.ignoreLimitWarning": true 15 | } 16 | 17 | // "app_back", 18 | // "bootloader", 19 | // "func", 20 | // "app_cursor", 21 | // "vconsol", 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Thomas SIMON 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > You can support this night time project by hiring me for a day time job ! 2 | 3 | # Fomos 4 | 5 | Experimental OS, built with Rust 6 | 7 | https://github.com/Ruddle/Fomos/assets/14235713/3ee75d5e-5ebe-4cc1-b267-8b73337ee157 8 | 9 | **Fun fact**: there are 3 apps running in the video. A background app, a cursor app, and a console app. 10 | 11 | # Why 12 | 13 | I wanted to experiment with Non-Unix OS ideas. 14 | 15 | Exo-kernels are interesting, but it is mostly a theory. This project helps me understand the challenges involved in that pattern. 16 | 17 | OS development is extremely hard, Rust makes it more bearable. 18 | 19 | # Features 20 | 21 | - Has a graphical output 22 | - Dynamic allocation 23 | - Load and run concurrent apps 24 | - All apps run in an async loop 25 | - Support Virtio mouse and keyboard (drivers are async tasks) 26 | - Cooperative scheduling (apps yield control as much as possible) 27 | - No context switches once booted 28 | - _Nearly support Virgl_ ™ 29 | 30 | There is 5 examples of apps in this repo named `app_*`, some in Rust, one in C. 31 | The kernel is in `bootloader`. 32 | 33 | # What is unique 34 | 35 | The signature of an app in Fomos: 36 | 37 | ```rust 38 | pub extern "C" fn _start(ctx: &mut Context) -> i32 39 | ``` 40 | 41 | Apps do not need a standard library, any OS functionality is given to the app through the _Context_. 42 | 43 | the _Context_ is mostly a pointer to a bag of kernel functionnalities 44 | 45 | ```rust 46 | pub extern "C" fn 47 | ``` 48 | 49 | In Fomos, an app is really just a **function**. There is nothing else ! This is a **huge** claim. An executable for a Unix or Windows OS is extremely complex compared to a freestanding function. 50 | 51 | `` 52 | 53 | It is out a frustration for all my Glibc problems during day job dev on linux that I chose to try this approach. 54 | 55 | I want a flat contract between an app and the OS. So what if an app was a function ? The contract is then **only** the explicit argument type. 56 | 57 | In Unix, an app has to know the target OS, but also what standard library it uses, that is 2 levels of indirections. Sometimes the os level has a conflict, sometimes the standard library level has a conflict, and sometimes I just don't have the level to understand why something doesn't work. I merely know it is related. 58 | 59 | `` 60 | 61 | I am trying to know if it is possible to have an OS-App ecosystem that does not suppose **ANY** **implicit** configuration. An app would **JUST** have to handle its explicit `start` _context_ argument. 62 | 63 | _Context_ gives any OS function necessary, think alloc, free, access to a framebuffer, or any hardware, any system calls etc. 64 | 65 | That way, apps could be freestanding, and compatible on multiple OS. 66 | 67 | ### More about Context 68 | 69 | Here is the _Context_ for the last version of this OS 70 | 71 | ```rust 72 | #[repr(C)] 73 | pub struct Context<'a, T> { 74 | pub version: u8, 75 | pub start_time: u64, 76 | pub log: extern "C" fn(s: *const u8, l: u32), 77 | pub pid: u64, 78 | pub fb: FB<'a>, 79 | pub calloc: extern "C" fn(usize, usize) -> *mut u8, 80 | pub cdalloc: extern "C" fn(*mut u8, usize, usize), 81 | pub store: &'a mut Option>, 82 | pub input: &'a Input, 83 | } 84 | ``` 85 | 86 | Note that `app_test` for instance, uses an old version of the _Context_, and still works on the newer version of the OS 87 | 88 | Old Context used by `app_test`: 89 | 90 | ```rust 91 | #[repr(C)] 92 | pub struct Context<'a> { 93 | pub version: u8, 94 | start_time: u64, 95 | log: extern "C" fn(s: *const u8, l: u32), 96 | pid: u64, 97 | fb: FB<'a>, 98 | } 99 | ``` 100 | 101 | Meaning Fomos already handles gracefully Apps designed for a much older version of itself. As long as the OS stays compatible with the old stuff in the context, it can add new functionalities for other App by just appending to the context the new functions (here calloc, cdalloc, store, and input). 102 | 103 | `app_test` precedes the dynamic allocation age ! 104 | 105 | Could that pattern work in the long term ? 106 | 107 | ### How about system calls 108 | 109 | None. Lets try to put everything into _Context_ functions. No voodoo cpu instruction magic. 110 | 111 | > But how do you give back control to the OS ? 112 | 113 | Just 114 | 115 | ```rust 116 | return; 117 | ``` 118 | 119 | > How do you sleep, or wait asynchronously ? 120 | 121 | Just 122 | 123 | ```rust 124 | return; 125 | ``` 126 | 127 | Apps are **cooperative** in Fomos, They can just return (which would exit permanently an app on a classic OS), and assume that they are gonna be called through their only function `start` again soon, maybe even instantly if the "system call" works that way. 128 | 129 | > But an app loses all RAM data everytime it yields that way ! 130 | 131 | No, an app can store anything it wants in Context.store during its execution, and get it back every `start` call. The OS keeps everything in RAM (on the heap). The stack itself is "reset". But it is not more "reset" than it is after any function execution in a normal program. You don't lose anything. In Fomos, apps are merely a single function called multiple times! 132 | 133 | Over simplification of the kernel loop: 134 | 135 | ```rust 136 | loop { 137 | for app in apps.iter_mut() { 138 | app._start(Context::new(...)); 139 | } 140 | } 141 | ``` 142 | 143 | There are a lot of questions without answer yet, but by now you might be curious, what if all the question had an answer in the pattern ? It looks like it could actually work (with a lot of work). 144 | 145 | # Advantages 146 | 147 | A lot of stuff comes free once you accept the premises. 148 | 149 | #### Sandboxing, instrumentation, debugging 150 | 151 | Every functionnality and side effect given to an app goes explicitely through the _Context_. The _Context_ is just a struct, we can wrap or replace anything in it. 152 | Lets instrument an app we'll call `special_app`. Over simplification : 153 | 154 | ```rust 155 | loop { 156 | for normal_app in normal_apps.iter_mut() { 157 | app._start(Context::new(alloc,..)); 158 | } 159 | // special_app alloc instrumentation 160 | fn alloc_log(..){log("allocation detected!"); return alloc(..);} 161 | special_app._start(Context::new(alloc_log,..)); 162 | } 163 | ``` 164 | 165 | #### Restart, sleep, change of hardware 166 | 167 | An app memory lives in its context. The stack is fleeting. It is reset after each yield and doesn't mean much in Fomos. 168 | Since the _Context_ is explicit, it can be stored. A restart _can_ be made completely transparent to an app. 169 | 170 | Pseudo code: 171 | 172 | ```rust 173 | //kernel just started 174 | ... 175 | let app = App::new(..); 176 | let ctx = disk.load(app.id).unwrap_or(Context::new(..)); 177 | loop{ 178 | app._start(ctx); 179 | if restart_request{ 180 | disk.save(app.id, ctx) 181 | break; 182 | } 183 | } 184 | //handle restart 185 | ... 186 | ``` 187 | 188 | Quickload and quicksave of an app complete state is trivial. 189 | Note that some change of hardware could make an app bug. It would be a problem if it was transparent. However, it could be made opaque and obvious, in an opt-in manner, again through the _Context_. 190 | 191 | # Disadvantages 192 | 193 | ### Security 194 | 195 | Right now it is not implemented, any app can casually check the ram of another app ^^. This is going to be a hard problem to solve. I have plans to have data security without context switch, and without giving every damn app its own virtual memory stack. 196 | 197 | ### Cooperative vs preemptive scheduling 198 | 199 | The argument that a cooperative scheduling is doomed to fail is overblown. Apps are already very much cooperative. 200 | For proof, run a version of that on your nice preemptive system : 201 | 202 | ```js 203 | while(true){ 204 | new Thread( () => { 205 | fs.writeFile("/home/"+randomString(),randomString()) 206 | malloc(randomInt()) 207 | curl("http://"+randomString()+".com") 208 | } 209 | } 210 | ``` 211 | 212 | - Blender does a compelling impression of that when you increase the level of details one too many times. Might fill your swap and crash unsaved work on other apps. 213 | - Badly written Webgl websites crash my gpu driver. 214 | 215 | Not only is preemptive scheduling not enough, IMO it is not necessary. Also it is a spectrum. A system can be optimistically cooperative, and turn preemptive pessimistically. 216 | 217 | However the ecosystem is made for preemptive OS. There is friction in doing things differently. 218 | 219 | # Missing 220 | 221 | - Permanent storage (should be easy since virtio is already implemented) 222 | - Gpu support (virgl wip) 223 | - Networking 224 | - A nice abstraction for apps to share data and functionnalities between themselves 225 | 226 | The rest should live in userland. 227 | 228 | # Building 229 | 230 | run 231 | 232 | ```sh 233 | ./build.sh 234 | ``` 235 | 236 | _You might need rust nightly, gcc, qemu with virgl & sdl flag_ 237 | 238 | [More info here](/docs/BUILD.md) 239 | 240 | # Credit 241 | 242 | Heavily inspired by [Philipp Oppermann's blog](https://os.phil-opp.com/). 243 | 244 | Thanks to [darbysauter](https://github.com/darbysauter/myOS) for the advice given. 245 | -------------------------------------------------------------------------------- /app_background/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std-features = ["compiler-builtins-mem"] 3 | build-std = ["core", "compiler_builtins","alloc"] 4 | 5 | [build] 6 | target = "x86_64.json" -------------------------------------------------------------------------------- /app_background/.cargo_x86/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std-features = ["compiler-builtins-mem"] 3 | build-std = ["core", "compiler_builtins","alloc"] 4 | 5 | [build] 6 | target = "x86_64.json" -------------------------------------------------------------------------------- /app_background/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 = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "approx" 13 | version = "0.5.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 16 | dependencies = [ 17 | "num-traits", 18 | ] 19 | 20 | [[package]] 21 | name = "arrform" 22 | version = "0.1.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e7cf566ecc5c9d82b973e81d30babf6583c9b497f86295c952d538c3254ef4e6" 25 | 26 | [[package]] 27 | name = "autocfg" 28 | version = "1.1.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 31 | 32 | [[package]] 33 | name = "bit_field" 34 | version = "0.10.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 37 | 38 | [[package]] 39 | name = "bitflags" 40 | version = "1.3.2" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 43 | 44 | [[package]] 45 | name = "bitfrob" 46 | version = "0.2.3" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "fb18fff2e974c4b95a45f47398bb6ed5b040ccdaac9d41ed045d33dbe960f75f" 49 | 50 | [[package]] 51 | name = "bytemuck" 52 | version = "1.13.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" 55 | 56 | [[package]] 57 | name = "func" 58 | version = "0.1.0" 59 | dependencies = [ 60 | "arrform", 61 | "imagine", 62 | "libm", 63 | "vek", 64 | "x86_64", 65 | ] 66 | 67 | [[package]] 68 | name = "imagine" 69 | version = "0.4.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "c24475037443b4c1d7ae5da440b07ee4eeec46311ed7e5c8cdc97e6ef03948f5" 72 | dependencies = [ 73 | "bitfrob", 74 | "bytemuck", 75 | "miniz_oxide", 76 | "wide", 77 | ] 78 | 79 | [[package]] 80 | name = "libm" 81 | version = "0.2.6" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" 84 | 85 | [[package]] 86 | name = "miniz_oxide" 87 | version = "0.6.2" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 90 | dependencies = [ 91 | "adler", 92 | ] 93 | 94 | [[package]] 95 | name = "num-integer" 96 | version = "0.1.45" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 99 | dependencies = [ 100 | "autocfg", 101 | "num-traits", 102 | ] 103 | 104 | [[package]] 105 | name = "num-traits" 106 | version = "0.2.15" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 109 | dependencies = [ 110 | "autocfg", 111 | "libm", 112 | ] 113 | 114 | [[package]] 115 | name = "rustc_version" 116 | version = "0.4.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 119 | dependencies = [ 120 | "semver", 121 | ] 122 | 123 | [[package]] 124 | name = "rustversion" 125 | version = "1.0.12" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" 128 | 129 | [[package]] 130 | name = "safe_arch" 131 | version = "0.6.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" 134 | dependencies = [ 135 | "bytemuck", 136 | ] 137 | 138 | [[package]] 139 | name = "semver" 140 | version = "1.0.17" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" 143 | 144 | [[package]] 145 | name = "vek" 146 | version = "0.15.10" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "8085882662f9bc47fc8b0cdafa5e19df8f592f650c02b9083da8d45ac9eebd17" 149 | dependencies = [ 150 | "approx", 151 | "num-integer", 152 | "num-traits", 153 | "rustc_version", 154 | ] 155 | 156 | [[package]] 157 | name = "volatile" 158 | version = "0.4.6" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" 161 | 162 | [[package]] 163 | name = "wide" 164 | version = "0.7.8" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" 167 | dependencies = [ 168 | "bytemuck", 169 | "safe_arch", 170 | ] 171 | 172 | [[package]] 173 | name = "x86_64" 174 | version = "0.14.10" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" 177 | dependencies = [ 178 | "bit_field", 179 | "bitflags", 180 | "rustversion", 181 | "volatile", 182 | ] 183 | -------------------------------------------------------------------------------- /app_background/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "func" 3 | version = "0.1.0" 4 | edition = "2021" 5 | [[bin]] 6 | name="func" 7 | test = false 8 | bench = false 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | imagine = { version= "0.4.0"} 13 | x86_64 = { version = "0.14.8" } 14 | arrform = "0.1.1" 15 | vek = { version = "0.15.10", default-features = false, features = ["libm"] } 16 | libm = "0.2.6" 17 | # [dependencies.zune-jpeg] 18 | # version ="0.3.14" 19 | # default-features = false 20 | # features = ["x86"] 21 | 22 | 23 | [features] 24 | default = [] 25 | 26 | [profile.dev] 27 | panic = "abort" 28 | 29 | [profile.release] 30 | panic = "abort" 31 | lto = true 32 | strip = true 33 | codegen-units = 1 34 | 35 | [workspace] 36 | 37 | #RUSTFLAGS="-C relocation-model=pie -C link-arg=-nostartfiles -C link-arg=-pie" 38 | 39 | # RUSTFLAGS="-C relocation-model=pie -C link-arg=-nostartfiles -C link-arg=-pie" cargo build --release --target x86_64-unknown-linux-gnu 40 | 41 | 42 | # CURRENT WORKING with custom target in .cargo 43 | # RUSTFLAGS="-C relocation-model=pie -C link-arg=-pie" cargo build --release -------------------------------------------------------------------------------- /app_background/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /app_background/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | 5 | extern crate alloc; 6 | mod st; 7 | 8 | use st::*; 9 | 10 | use alloc::boxed::Box; 11 | use imagine::pixel_formats::RGBA8888; 12 | 13 | const IMG: &[u8] = include_bytes!("./qr.png"); 14 | 15 | pub struct Store { 16 | pix: imagine::image::Bitmap, 17 | } 18 | 19 | #[no_mangle] 20 | pub extern "C" fn _start(ctx: &mut Context) -> i32 { 21 | unsafe { ALLOCATOR.swap(ctx) }; 22 | unsafe { LOGGER.swap(ctx.log) }; 23 | 24 | // (ctx.log)("back start"); 25 | // x86_64::instructions::interrupts::int3(); 26 | 27 | let store: Box; 28 | if let Some(ptr) = ctx.store.take() { 29 | store = ptr; 30 | } else { 31 | st::log("store not found"); 32 | let pix = imagine::image::Bitmap::try_from_png_bytes(IMG); 33 | if pix.is_none() { 34 | st::log("No pix"); 35 | return -1; 36 | } 37 | store = Box::new(Store { pix: pix.unwrap() }) 38 | } 39 | 40 | let pix = &store.pix; 41 | 42 | let dx = (libm::cosf((ctx.start_time as f32) * 0.001) * 2.0) as usize; 43 | 44 | for y in 0..ctx.fb.h { 45 | for x in 0..ctx.fb.w { 46 | let lx = x as f32 / (ctx.fb.w) as f32; 47 | let ly = y as f32 / (ctx.fb.h) as f32; 48 | let px = (lx * pix.width as f32) as usize; 49 | let py = (ly * pix.height as f32) as usize; 50 | let v = pix.pixels[px + py * pix.width as usize]; 51 | let p = &mut ctx.fb.pixels[x + y * ctx.fb.w]; 52 | p.r = v.r; 53 | p.g = v.g; 54 | p.b = v.b; 55 | } 56 | } 57 | 58 | *ctx.store = Some(store); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /app_background/src/qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ruddle/Fomos/336be2d5bb2733f27e593dec70034a4f59650efd/app_background/src/qr.png -------------------------------------------------------------------------------- /app_background/src/st.rs: -------------------------------------------------------------------------------- 1 | #[panic_handler] 2 | fn panic(info: &core::panic::PanicInfo) -> ! { 3 | x86_64::instructions::interrupts::int3(); 4 | unsafe { 5 | (LOGGER.f)("panic".as_ptr(), 5); 6 | }; 7 | unsafe { 8 | let s = &format!("{:?}", info); 9 | (LOGGER.f)(s.as_ptr(), s.len() as u32) 10 | }; 11 | loop {} 12 | } 13 | 14 | pub static mut LOGGER: Logger = Logger::init(); 15 | 16 | pub fn log(s: &str) { 17 | unsafe { (LOGGER.f)(s.as_ptr(), s.len() as u32) } 18 | } 19 | 20 | type LogFn = extern "C" fn(*const u8, u32); 21 | extern "C" fn nop(s: *const u8, l: u32) {} 22 | pub struct Logger { 23 | pub f: LogFn, 24 | } 25 | impl Logger { 26 | pub const fn init() -> Self { 27 | Self { f: nop } 28 | } 29 | pub fn swap(&mut self, f2: LogFn) { 30 | self.f = f2; 31 | } 32 | } 33 | #[repr(C)] 34 | pub struct Context<'a, T> { 35 | pub version: u8, 36 | pub start_time: u64, 37 | pub log: extern "C" fn(s: *const u8, l: u32), 38 | pub pid: u64, 39 | pub fb: FB<'a>, 40 | pub calloc: extern "C" fn(usize, usize) -> *mut u8, 41 | pub cdalloc: extern "C" fn(*mut u8, usize, usize), 42 | pub store: &'a mut Option>, 43 | pub input: &'a Input, 44 | } 45 | 46 | const HISTORY_SIZE: usize = 64; 47 | 48 | #[repr(C)] 49 | #[derive(Clone, Debug, Copy)] 50 | pub struct InputEvent { 51 | pub trigger: bool, 52 | pub key: usize, 53 | } 54 | #[repr(C)] 55 | pub struct Input { 56 | pub mx: usize, 57 | pub my: usize, 58 | pub keys: [u8; 1024], 59 | pub history_last_index: usize, 60 | pub history_ring: [InputEvent; HISTORY_SIZE], 61 | } 62 | 63 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 64 | #[repr(C)] 65 | pub struct RGBA { 66 | pub r: u8, 67 | pub g: u8, 68 | pub b: u8, 69 | pub a: u8, 70 | } 71 | 72 | #[repr(C)] 73 | pub struct FB<'a> { 74 | pub pixels: &'a mut [RGBA], 75 | pub w: usize, 76 | pub h: usize, 77 | } 78 | 79 | use core::alloc::GlobalAlloc; 80 | 81 | use alloc::{boxed::Box, format}; 82 | 83 | extern "C" fn a_init(size: usize, align: usize) -> *mut u8 { 84 | panic!("") 85 | } 86 | extern "C" fn d_init(ptr: *mut u8, size: usize, align: usize) { 87 | panic!("") 88 | } 89 | #[repr(C)] 90 | pub struct AllocFromCtx { 91 | a: extern "C" fn(usize, usize) -> *mut u8, 92 | d: extern "C" fn(*mut u8, usize, usize), 93 | } 94 | unsafe impl GlobalAlloc for AllocFromCtx { 95 | unsafe fn alloc(&self, layout: alloc::alloc::Layout) -> *mut u8 { 96 | (self.a)(layout.size(), layout.align()) 97 | } 98 | 99 | unsafe fn dealloc(&self, ptr: *mut u8, layout: alloc::alloc::Layout) { 100 | (self.d)(ptr, layout.size(), layout.align()); 101 | } 102 | } 103 | impl AllocFromCtx { 104 | pub const fn init() -> Self { 105 | Self { 106 | a: a_init, 107 | d: d_init, 108 | } 109 | } 110 | pub fn swap(&mut self, ctx: &mut Context) { 111 | let ptr = self; 112 | ptr.a = ctx.calloc; 113 | ptr.d = ctx.cdalloc; 114 | } 115 | } 116 | #[global_allocator] 117 | pub static mut ALLOCATOR: AllocFromCtx = AllocFromCtx::init(); 118 | -------------------------------------------------------------------------------- /app_background/x86_64.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "code-model": "small", 4 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 5 | "arch": "x86_64", 6 | "target-endian": "little", 7 | "target-pointer-width": "64", 8 | "target-c-int-width": "32", 9 | "os": "none", 10 | "executables": true, 11 | "linker-flavor": "ld.lld", 12 | "linker": "rust-lld", 13 | "relocation-model": "pie", 14 | "position-independent-executables": true, 15 | "static-position-independent-executables": true, 16 | "panic-strategy": "abort", 17 | "disable-redzone": true, 18 | "features": "+mmx,+sse,+sse2,+sse3" 19 | } 20 | -------------------------------------------------------------------------------- /app_c/README.md: -------------------------------------------------------------------------------- 1 | # Example of C program for Fomos 2 | 3 | Notice that you can do stuff without any include. 4 | 5 | That is a core idea of Fomos. 6 | 7 | All capabilities of the OS are given explicitly through a single level of abstraction : the `Context` argument. 8 | -------------------------------------------------------------------------------- /app_c/build.sh: -------------------------------------------------------------------------------- 1 | mkdir -p target 2 | gcc -nostdlib -static -fPIE -pie -o ./target/main main.c -------------------------------------------------------------------------------- /app_c/main.c: -------------------------------------------------------------------------------- 1 | typedef struct 2 | { 3 | unsigned char version; 4 | unsigned long long start_time; 5 | void (*log)(const char *, unsigned int); 6 | unsigned long long pid; 7 | 8 | } Context; 9 | 10 | int _start(Context *ctx) 11 | { 12 | char text[] = "[pid:_] Hello from C!"; 13 | text[5] = '0' + ctx->pid; 14 | ctx->log(text, sizeof(text) - 1); 15 | return 0; 16 | } -------------------------------------------------------------------------------- /app_console/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std-features = ["compiler-builtins-mem"] 3 | build-std = ["core", "compiler_builtins","alloc"] 4 | 5 | [build] 6 | target = "x86_64.json" -------------------------------------------------------------------------------- /app_console/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 = "approx" 7 | version = "0.5.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 10 | dependencies = [ 11 | "num-traits", 12 | ] 13 | 14 | [[package]] 15 | name = "arrayvec" 16 | version = "0.7.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "fomoscript" 28 | version = "0.2.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "2ea747010079579506b23ad9ec9d6a33fdf18c5d84dfb65a5a47eab37cb77450" 31 | dependencies = [ 32 | "log", 33 | ] 34 | 35 | [[package]] 36 | name = "func" 37 | version = "0.1.0" 38 | dependencies = [ 39 | "fomoscript", 40 | "noto-sans-mono-bitmap", 41 | "taffy", 42 | "vek", 43 | ] 44 | 45 | [[package]] 46 | name = "libm" 47 | version = "0.2.6" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" 50 | 51 | [[package]] 52 | name = "log" 53 | version = "0.4.20" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 56 | 57 | [[package]] 58 | name = "noto-sans-mono-bitmap" 59 | version = "0.2.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "a27daf9557165efe1d09b52f97393bf6283cadb0a76fbe64a1061e15553a994a" 62 | 63 | [[package]] 64 | name = "num-integer" 65 | version = "0.1.45" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 68 | dependencies = [ 69 | "autocfg", 70 | "num-traits", 71 | ] 72 | 73 | [[package]] 74 | name = "num-traits" 75 | version = "0.2.15" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 78 | dependencies = [ 79 | "autocfg", 80 | "libm", 81 | ] 82 | 83 | [[package]] 84 | name = "rustc_version" 85 | version = "0.4.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 88 | dependencies = [ 89 | "semver", 90 | ] 91 | 92 | [[package]] 93 | name = "semver" 94 | version = "1.0.17" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" 97 | 98 | [[package]] 99 | name = "slotmap" 100 | version = "1.0.6" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" 103 | dependencies = [ 104 | "version_check", 105 | ] 106 | 107 | [[package]] 108 | name = "taffy" 109 | version = "0.3.11" 110 | source = "git+https://github.com/Ruddle/taffy#44b858f3f9d6e2dffb4da719d5bf98e92a75af05" 111 | dependencies = [ 112 | "arrayvec", 113 | "num-traits", 114 | "slotmap", 115 | ] 116 | 117 | [[package]] 118 | name = "vek" 119 | version = "0.15.10" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "8085882662f9bc47fc8b0cdafa5e19df8f592f650c02b9083da8d45ac9eebd17" 122 | dependencies = [ 123 | "approx", 124 | "num-integer", 125 | "num-traits", 126 | "rustc_version", 127 | ] 128 | 129 | [[package]] 130 | name = "version_check" 131 | version = "0.9.4" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 134 | -------------------------------------------------------------------------------- /app_console/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "func" 3 | version = "0.1.0" 4 | edition = "2021" 5 | [[bin]] 6 | name="func" 7 | test = false 8 | bench = false 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | 13 | # x86_64 = { version = "0.14.8" } 14 | vek = { version = "0.15.10", default-features = false, features = ["libm"] } 15 | taffy = { git = "https://github.com/Ruddle/taffy", default-features = false, features = ["alloc","flexbox"] } 16 | fomoscript= "0.2.1" 17 | # [dependencies.zune-jpeg] 18 | # version ="0.3.14" 19 | # default-features = false 20 | # features = ["x86"] 21 | 22 | [dependencies.noto-sans-mono-bitmap] 23 | version = "0.2.0" 24 | default-features = false 25 | features = [ 26 | "regular", 27 | "size_16", 28 | "unicode-basic-latin", 29 | # required for the fallback char '�' 30 | "unicode-specials", 31 | ] 32 | 33 | 34 | 35 | [features] 36 | default = [] 37 | 38 | [profile.dev] 39 | panic = "abort" 40 | 41 | [profile.release] 42 | panic = "abort" 43 | lto = true 44 | strip = true 45 | codegen-units = 1 46 | 47 | [workspace] 48 | 49 | #RUSTFLAGS="-C relocation-model=pie -C link-arg=-nostartfiles -C link-arg=-pie" 50 | 51 | # RUSTFLAGS="-C relocation-model=pie -C link-arg=-nostartfiles -C link-arg=-pie" cargo build --release --target x86_64-unknown-linux-gnu 52 | 53 | 54 | # CURRENT WORKING with custom target in .cargo 55 | # RUSTFLAGS="-C relocation-model=pie -C link-arg=-pie" cargo build --release -------------------------------------------------------------------------------- /app_console/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /app_console/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | #![feature(option_get_or_insert_default)] 5 | extern crate alloc; 6 | mod st; 7 | 8 | use st::*; 9 | 10 | use alloc::{boxed::Box, vec::Vec}; 11 | 12 | use vek::{num_traits::Zero, Vec3}; 13 | 14 | use vek::num_traits::Float; 15 | 16 | use taffy::prelude::*; 17 | 18 | pub struct ConsoleHistory { 19 | atoms: alloc::vec::Vec, 20 | } 21 | impl ConsoleHistory { 22 | pub fn new() -> ConsoleHistory { 23 | ConsoleHistory { atoms: Vec::new() } 24 | } 25 | } 26 | 27 | pub struct Atom { 28 | is_user: bool, 29 | text: alloc::string::String, 30 | } 31 | 32 | enum Mode { 33 | Shell, 34 | FomoscriptREPL, 35 | } 36 | 37 | pub struct Store { 38 | x: usize, 39 | y: usize, 40 | x2: usize, 41 | y2: usize, 42 | resizing: [bool; 4], 43 | moving: Option<(usize, usize)>, 44 | b1: Vec, 45 | b2: Vec, 46 | step: usize, 47 | taffy: Taffy, 48 | input_history_last_index: usize, 49 | console_history: ConsoleHistory, 50 | active: bool, 51 | local: Local, 52 | shift: bool, 53 | altg: bool, 54 | script_ctx: fomoscript::Ctx, 55 | mode: Mode, 56 | } 57 | 58 | fn put(pixel: &mut RGBA, v: Vec3) { 59 | pixel.r = v.x as u8; 60 | pixel.g = v.y as u8; 61 | pixel.b = v.z as u8; 62 | } 63 | 64 | fn get(mut x: isize, mut y: isize, src: &[RGBA], wi: isize, hi: isize) -> Vec3 { 65 | if x < 0 { 66 | x = 0; 67 | } else if x >= wi - 1 { 68 | x = wi - 1; 69 | } 70 | if y < 0 { 71 | y = 0; 72 | } else if y >= hi - 1 { 73 | y = hi - 1; 74 | } 75 | 76 | let (x, y) = (x as usize, y as usize); 77 | let index = x + y * wi as usize; 78 | // st::log(&format!("{} {} {} {}", x, y, wi, hi)); 79 | let r = src[index]; 80 | Vec3::new(r.r as f32, r.g as f32, r.b as f32) 81 | } 82 | 83 | fn getf(mut x: f32, mut y: f32, src: &[RGBA], wi: isize, hi: isize) -> RGBA { 84 | let x0 = x as isize; 85 | let x1 = x0 + 1; 86 | let y0 = y as isize; 87 | let y1 = y0 + 1; 88 | 89 | let fx = x - x0 as f32; 90 | let fy = y - y0 as f32; 91 | 92 | let a = get(x0, y0, src, wi, hi); 93 | let b = get(x1, y0, src, wi, hi); 94 | let c = get(x1, y1, src, wi, hi); 95 | let d = get(x0, y1, src, wi, hi); 96 | 97 | let ax = (1.0 - fx) * a + fx * b; 98 | let ay = (1.0 - fx) * d + fx * c; 99 | 100 | let avg = ax * (1.0 - fy) + ay * fy; 101 | RGBA { 102 | r: avg.x as u8, 103 | g: avg.y as u8, 104 | b: avg.z as u8, 105 | a: 0, 106 | } 107 | } 108 | 109 | fn pt_in_rect(px: usize, py: usize, x: usize, y: usize, x2: usize, y2: usize) -> bool { 110 | px >= x && px <= x2 && py >= y && py <= y2 111 | } 112 | 113 | fn kernel(sigma: f32) -> [f32; S] { 114 | let mut kernel = [0.0; S]; 115 | let mid = S / 2; 116 | 117 | // calculate the Gaussian distribution 118 | let variance = sigma.powi(2); 119 | let factor = 1.0 / (2.0 * 3.141592 * variance); 120 | let mut sum = 0.0; 121 | for i in 0..S { 122 | let x = (i as i32 - mid as i32) as f32; 123 | let value = factor * (-x.powi(2) / (2.0 * variance)).exp(); 124 | kernel[i] = value; 125 | sum += value; 126 | } 127 | sum *= 0.997; 128 | // normalize the kernel 129 | for i in 0..S { 130 | kernel[i] /= sum; 131 | } 132 | 133 | kernel 134 | } 135 | fn blur( 136 | fx: isize, 137 | fy: isize, 138 | src: &[RGBA], 139 | dst: &mut Vec, 140 | wi: isize, 141 | hi: isize, 142 | boxes: &[f32; KSIZE], 143 | ) { 144 | let mut avg: Vec3 = Vec3::zero(); 145 | 146 | for y in 0..hi { 147 | for x in 0..wi { 148 | avg.set_zero(); 149 | for (k, &coef) in boxes.iter().enumerate() { 150 | let dk = k as isize - KH; 151 | let v = get(x + fx * dk, y + fy * dk, src, wi, hi); 152 | avg += v * coef * 0.99; 153 | } 154 | put(&mut dst[(x + y * wi) as usize], avg); 155 | } 156 | } 157 | } 158 | 159 | fn hash(n: usize) -> usize { 160 | // integer hash copied from Hugo Elias 161 | let n = (n << 13) ^ n; 162 | let n = n.wrapping_mul(n.wrapping_mul(n).wrapping_mul(15731) + 789221) + 1376312589; 163 | n 164 | } 165 | 166 | fn hash2(x: usize) -> usize { 167 | let mut x = ((x >> 16) ^ x) * 0x45d9f3b; 168 | x = ((x >> 16) ^ x) * 0x45d9f3b; 169 | (x >> 16) ^ x 170 | } 171 | const KSIZE: usize = 5; 172 | const KH: isize = (KSIZE / 2) as isize; 173 | const DIV: isize = 4; 174 | const ORANGE: RGBA = RGBA { 175 | r: 255, 176 | g: 128, 177 | b: 0, 178 | a: 0, 179 | }; 180 | const GREY3: RGBA = RGBA { 181 | r: 150, 182 | g: 150, 183 | b: 150, 184 | a: 0, 185 | }; 186 | const GREY2: RGBA = RGBA { 187 | r: 80, 188 | g: 80, 189 | b: 80, 190 | a: 0, 191 | }; 192 | const GREY: RGBA = RGBA { 193 | r: 50, 194 | g: 50, 195 | b: 50, 196 | a: 0, 197 | }; 198 | #[no_mangle] 199 | pub extern "C" fn _start(ctx: &mut Context) -> i32 { 200 | unsafe { ALLOCATOR.swap(ctx) }; 201 | unsafe { LOGGER.swap(ctx.log) }; 202 | 203 | let hi = ctx.fb.h as isize / DIV; 204 | let wi = ctx.fb.w as isize / DIV; 205 | 206 | let store = ctx.store.get_or_insert_with(|| { 207 | Box::new({ 208 | Store { 209 | x: 100 + ctx.pid as usize * 100, 210 | y: 100 + ctx.pid as usize * 100, 211 | x2: 1000 + ctx.pid as usize * 100, 212 | y2: 700 + ctx.pid as usize * 100, 213 | resizing: [false; 4], 214 | b1: Vec::with_capacity((wi * hi) as usize), 215 | b2: Vec::with_capacity((wi * hi) as usize), 216 | 217 | step: 0, 218 | moving: None, 219 | taffy: Taffy::new(), 220 | input_history_last_index: ctx.input.history_last_index, 221 | console_history: ConsoleHistory::new(), 222 | active: false, 223 | local: Local::En, 224 | shift: false, 225 | altg: false, 226 | script_ctx: fomoscript::Ctx::new(), 227 | mode: Mode::Shell, 228 | } 229 | }) 230 | }); 231 | 232 | if store.step == 0 { 233 | store.console_history.atoms.push(Atom { 234 | is_user: false, 235 | text: alloc::format!("Welcome to Fomos !"), 236 | }); 237 | store.console_history.atoms.push(Atom { 238 | is_user: true, 239 | text: alloc::format!(">"), 240 | }); 241 | } 242 | 243 | store.step += 1; 244 | let blured1 = &mut store.b1; 245 | let blured2 = &mut store.b2; 246 | 247 | let (src, dst) = if store.step % 2 == 0 { 248 | (blured1, blured2) 249 | } else { 250 | (blured2, blured1) 251 | }; 252 | 253 | if src.len() == 0 { 254 | for y in 0..hi { 255 | for x in 0..wi { 256 | let v = ctx.fb.pixels[((x * DIV) + (y * DIV) * (wi * DIV)) as usize]; 257 | src.push(v); 258 | dst.push(v); 259 | } 260 | } 261 | } else { 262 | for y in 0..hi { 263 | for x in 0..wi { 264 | if true { 265 | //|| (hash2((x + y * wi) as usize) + store.step) % 2 == 0 266 | let v = ctx.fb.pixels[((x * DIV) + (y * DIV) * (wi * DIV)) as usize]; 267 | let s = src[(x + y * wi) as usize]; 268 | let a = 0.03; 269 | let b = 1.0 - a; 270 | let r = (v.r as f32 * a + s.r as f32 * b) as u8; 271 | let g = (v.g as f32 * a + s.g as f32 * b) as u8; 272 | let b = (v.b as f32 * a + s.b as f32 * b) as u8; 273 | 274 | src[(x + y * wi) as usize] = RGBA { r, g, b, a: 0 }; 275 | } 276 | } 277 | } 278 | } 279 | 280 | let boxes = kernel::(1.0); 281 | 282 | let fx = if store.step % 2 == 0 { 1 } else { 0 }; 283 | blur(fx, 1 - fx, src, dst, wi, hi, &boxes); 284 | 285 | // blur(1, 0, &blured2, &mut blured1); 286 | // for y in 0..hi { 287 | // for x in 0..wi { 288 | // avg.set_zero(); 289 | // for (k, coef) in boxes.iter().enumerate() { 290 | // let coef = boxes[k as usize]; 291 | // let v = get(x + (k as isize - KH), y); 292 | // avg += v * coef; 293 | // } 294 | // put(&mut blured1[(x + y * wi) as usize], avg); 295 | // } 296 | // } 297 | 298 | if ctx.input.keys[272] < 128 { 299 | store.resizing = [false; 4]; 300 | store.moving = None; 301 | } 302 | 303 | if ctx.input.keys[272] == 128 { 304 | if (store.x as isize - ctx.input.mx as isize).abs() < 10 { 305 | store.resizing[3] = true; 306 | } 307 | if (store.x2 as isize - ctx.input.mx as isize).abs() < 10 { 308 | store.resizing[1] = true; 309 | } 310 | if (store.y as isize - ctx.input.my as isize).abs() < 10 311 | && store.x <= ctx.input.mx 312 | && store.x2 >= ctx.input.mx 313 | { 314 | store.resizing[0] = true; 315 | } 316 | if (store.y2 as isize - ctx.input.my as isize).abs() < 10 317 | && store.x <= ctx.input.mx 318 | && store.x2 >= ctx.input.mx 319 | { 320 | store.resizing[2] = true; 321 | } 322 | 323 | if !store.resizing.iter().any(|&e| e) { 324 | if store.x <= ctx.input.mx 325 | && store.x2 >= ctx.input.mx 326 | && store.y <= ctx.input.my 327 | && store.y2 >= ctx.input.my 328 | { 329 | store.moving = Some((ctx.input.mx, ctx.input.my)); 330 | store.active = true; 331 | } else { 332 | store.active = false; 333 | } 334 | } 335 | } 336 | 337 | if let Some((x0, y0)) = store.moving.as_mut() { 338 | let dx = ctx.input.mx as isize - *x0 as isize; 339 | let dy = ctx.input.my as isize - *y0 as isize; 340 | 341 | store.x = (store.x as isize + dx) as usize; 342 | store.x2 = (store.x2 as isize + dx) as usize; 343 | store.y = (store.y as isize + dy) as usize; 344 | store.y2 = (store.y2 as isize + dy) as usize; 345 | 346 | *x0 = ctx.input.mx; 347 | *y0 = ctx.input.my; 348 | } 349 | 350 | for (index, &r) in store.resizing.iter().enumerate() { 351 | if r { 352 | match index { 353 | 0 => store.y = ctx.input.my, 354 | 1 => store.x2 = ctx.input.mx, 355 | 2 => store.y2 = ctx.input.my, 356 | 3 => store.x = ctx.input.mx, 357 | _ => {} 358 | } 359 | } 360 | } 361 | 362 | if store.x < 0 || store.x > 10000 { 363 | store.x = 0; 364 | } 365 | if store.y < 0 || store.y > 10000 { 366 | store.y = 0; 367 | } 368 | if store.x2 >= ctx.fb.w { 369 | store.x2 = ctx.fb.w - 1; 370 | } 371 | if store.y2 >= ctx.fb.h { 372 | store.y2 = ctx.fb.h - 1; 373 | } 374 | 375 | let taffy = &mut store.taffy; 376 | taffy.clear(); 377 | 378 | let header_node = taffy 379 | .new_leaf(Style { 380 | size: Size { 381 | width: percent(1.0), 382 | height: points(20.0), 383 | }, 384 | ..Default::default() 385 | }) 386 | .unwrap(); 387 | 388 | let body_node = taffy 389 | .new_leaf(Style { 390 | flex_direction: FlexDirection::Column, 391 | size: Size { 392 | width: percent(1.0), 393 | height: auto(), 394 | }, 395 | // padding: Rect { 396 | // left: points(10.), 397 | // right: points(10.), 398 | // top: points(10.), 399 | // bottom: points(10.), 400 | // }, 401 | flex_grow: 1.0, 402 | ..Default::default() 403 | }) 404 | .unwrap(); 405 | 406 | for i in 0..0 { 407 | let text_node = taffy 408 | .new_leaf(Style { 409 | size: Size { 410 | width: auto(), 411 | height: points(10.), 412 | }, 413 | margin: Rect { 414 | left: points(10.), 415 | right: points(10.), 416 | top: points(10.), 417 | bottom: points(10.), 418 | }, 419 | flex_grow: 1.0, 420 | ..Default::default() 421 | }) 422 | .unwrap(); 423 | 424 | let _ = taffy.add_child(body_node, text_node); 425 | } 426 | 427 | let root_node = taffy 428 | .new_with_children( 429 | Style { 430 | flex_direction: FlexDirection::Column, 431 | size: Size { 432 | width: points(store.x2 as f32 - store.x as f32), 433 | height: points(store.y2 as f32 - store.y as f32), 434 | }, 435 | ..Default::default() 436 | }, 437 | &[header_node, body_node], 438 | ) 439 | .unwrap(); 440 | 441 | // Call compute_layout on the root of your tree to run the layout algorithm 442 | taffy 443 | .compute_layout( 444 | root_node, 445 | Size { 446 | width: points(store.x2 as f32 - store.x as f32), 447 | height: points(store.y2 as f32 - store.y as f32), 448 | }, 449 | ) 450 | .unwrap(); 451 | 452 | let get_rect = |node| { 453 | let l = taffy.layout(node).unwrap(); 454 | let (x, y) = (l.location.x as usize, l.location.y as usize); 455 | let (x2, y2) = (x + l.size.width as usize, y + l.size.height as usize); 456 | (x + store.x, y + store.y, x2 + store.x, y2 + store.y) 457 | }; 458 | 459 | // for i in 0..5 { 460 | // let (cx, cy, cx2, cy2) = get_rect(taffy.child_at_index(body_node, i).unwrap()); 461 | // st::log(&format!("{}: {:?}", i, (cx, cy, cx2, cy2))); 462 | // } 463 | 464 | for y in store.y..=store.y2 { 465 | for x in store.x..=store.x2 { 466 | let p = &mut ctx.fb.pixels[x + y * ctx.fb.w]; 467 | let mut drawn = false; 468 | 469 | // *p = dst[x / div + (y / div) * (ctx.fb.w / div)]; 470 | 471 | // if y - store.y < 20 { 472 | // *p = GREY; 473 | // } 474 | 475 | { 476 | let (cx, cy, cx2, cy2) = get_rect(header_node); 477 | 478 | if pt_in_rect(x, y, cx, cy, cx2, cy2) { 479 | *p = if store.active { GREY2 } else { GREY }; 480 | drawn = true; 481 | } 482 | } 483 | 484 | for i in 0..taffy.child_count(body_node).unwrap() { 485 | let (cx, cy, cx2, cy2) = get_rect(taffy.child_at_index(body_node, i).unwrap()); 486 | if pt_in_rect(x, y, cx, cy, cx2, cy2) { 487 | *p = if i % 2 == 0 { GREY3 } else { ORANGE }; 488 | drawn = true; 489 | } 490 | } 491 | 492 | if (x == store.x) || (x == store.x2) || (y == store.y) || (y == store.y2) { 493 | *p = if store.active { GREY2 } else { GREY }; 494 | if store.moving.is_some() { 495 | *p = ORANGE; 496 | } 497 | drawn = true; 498 | } 499 | 500 | if (x == store.x && store.resizing[3]) 501 | || (x == store.x2 && store.resizing[1]) 502 | || (y == store.y && store.resizing[0]) 503 | || (y == store.y2 && store.resizing[2]) 504 | { 505 | *p = ORANGE; 506 | drawn = true; 507 | } 508 | 509 | if !drawn { 510 | let div = DIV as usize; 511 | // *p = getf(x as f32 / div as f32, y as f32 / div as f32, dst, wi, hi); 512 | *p = dst[x / div + (y / div) * (ctx.fb.w / div)]; 513 | } 514 | } 515 | } 516 | 517 | //Write window title 518 | { 519 | use noto_sans_mono_bitmap::{get_raster, get_raster_width, FontWeight, RasterHeight}; 520 | let s = alloc::format!("app_console [{}]", ctx.pid); 521 | let mut cursor_x = 0; 522 | let padding = 2; 523 | let weight = FontWeight::Regular; 524 | for c in s.chars() { 525 | let width = get_raster_width(weight, RasterHeight::Size16); 526 | 527 | let char_raster = 528 | get_raster(c, weight, RasterHeight::Size16).expect("unsupported char"); 529 | 530 | for (row_i, row) in char_raster.raster().iter().enumerate() { 531 | for (col_i, pixel) in row.iter().enumerate() { 532 | let x = store.x + col_i + padding + cursor_x; 533 | let y = store.y + row_i + padding + 0; 534 | if x <= 0 535 | || x >= ctx.fb.w 536 | || y <= 0 537 | || y >= ctx.fb.h 538 | || x >= store.x2 539 | || y >= store.y2 540 | { 541 | continue; 542 | } 543 | let p = &mut ctx.fb.pixels[x + y * ctx.fb.w]; 544 | p.r = *pixel.max(&p.r); 545 | p.g = *pixel.max(&p.g); 546 | p.b = *pixel.max(&p.b); 547 | } 548 | } 549 | cursor_x += width; 550 | } 551 | } 552 | //Write text buffer 553 | { 554 | use noto_sans_mono_bitmap::{get_raster, get_raster_width, FontWeight, RasterHeight}; 555 | 556 | let mut cursor_x = 0; 557 | let mut cursor_y = 20; 558 | let padding = 2; 559 | let weight = FontWeight::Regular; 560 | 561 | for Atom { is_user, text } in store.console_history.atoms.iter() { 562 | for c in text.chars() { 563 | if (c == '\n') { 564 | cursor_y += 16; 565 | cursor_x = 0; 566 | continue; 567 | } 568 | let width = get_raster_width(weight, RasterHeight::Size16); 569 | 570 | let char_raster = 571 | get_raster(c, weight, RasterHeight::Size16).expect("unsupported char"); 572 | 573 | for (row_i, row) in char_raster.raster().iter().enumerate() { 574 | for (col_i, pixel) in row.iter().enumerate() { 575 | let x = store.x + col_i + padding + cursor_x; 576 | let y = store.y + row_i + padding + cursor_y; 577 | if x <= 0 578 | || x >= ctx.fb.w 579 | || y <= 0 580 | || y >= ctx.fb.h 581 | || x >= store.x2 582 | || y >= store.y2 583 | { 584 | continue; 585 | } 586 | let p = &mut ctx.fb.pixels[x + y * ctx.fb.w]; 587 | 588 | p.r = *pixel.max(&p.r); 589 | p.g = *pixel.max(&p.g); 590 | if *is_user { 591 | p.b = *pixel.max(&p.b); 592 | }; 593 | } 594 | } 595 | cursor_x += width; 596 | } 597 | 598 | cursor_y += 16; 599 | cursor_x = 0; 600 | } 601 | } 602 | 603 | if store.active { 604 | 'new_inputs: for i in (store.input_history_last_index + 1)..=ctx.input.history_last_index { 605 | let InputEvent { trigger, key } = ctx.input.history_ring[i % HISTORY_SIZE]; 606 | 607 | match key { 608 | Key::KeyLeftShift => { 609 | store.shift = trigger; 610 | } 611 | Key::KeyRightAlt => { 612 | store.altg = trigger; 613 | } 614 | _ => {} 615 | } 616 | 617 | if (trigger) { 618 | let last = store.console_history.atoms.last_mut(); 619 | match last { 620 | Some(Atom { is_user, text }) if *is_user => { 621 | log(&alloc::format!("{:?}", key)); 622 | 623 | match key { 624 | Key::KeyEnter if !store.shift => { 625 | if let Mode::FomoscriptREPL = store.mode { 626 | use fomoscript::*; 627 | store.script_ctx.insert_code(&text[4..]); 628 | if let Ok(parent) = store.script_ctx.parse_next_expr() { 629 | let res = eval(&parent, &mut store.script_ctx); 630 | store.console_history.atoms.push(Atom { 631 | is_user: false, 632 | text: alloc::format!("eval: {:?}", res), 633 | }); 634 | } 635 | } else { 636 | match text.as_ref() { 637 | ">repl" => { 638 | use fomoscript::*; 639 | store.mode = Mode::FomoscriptREPL; 640 | store.console_history.atoms.push(Atom { 641 | is_user: false, 642 | text: alloc::format!( 643 | "Call quit() to exit the REPL" 644 | ), 645 | }); 646 | 647 | let store_mode_ptr: *mut Mode = &mut store.mode; 648 | let quit = alloc::rc::Rc::new( 649 | move |a: N, b: N, c: N, d: N| -> N { 650 | let store_mode = 651 | unsafe { &mut *store_mode_ptr }; 652 | *store_mode = Mode::Shell; 653 | N::Unit 654 | }, 655 | ); 656 | store.script_ctx.set_var_absolute( 657 | "quit", 658 | N::FuncNativeDef(Native(quit)), 659 | ); 660 | } 661 | ">pid" => { 662 | store.console_history.atoms.push(Atom { 663 | is_user: false, 664 | text: alloc::format!("pid: {}", ctx.pid), 665 | }); 666 | } 667 | ">time" => { 668 | store.console_history.atoms.push(Atom { 669 | is_user: false, 670 | text: alloc::format!("time: {} ms", ctx.start_time), 671 | }); 672 | } 673 | ">lang en" => { 674 | store.local = Local::En; 675 | store.console_history.atoms.push(Atom { 676 | is_user: false, 677 | text: alloc::format!("ok"), 678 | }); 679 | } 680 | ">lang fr" => { 681 | store.local = Local::Fr; 682 | store.console_history.atoms.push(Atom { 683 | is_user: false, 684 | text: alloc::format!("ok"), 685 | }); 686 | } 687 | ">reset" => { 688 | drop(store); 689 | let old = ctx.store.take().unwrap(); 690 | drop(old); 691 | return 0; 692 | } 693 | ">help" => { 694 | store.console_history.atoms.push(Atom { 695 | is_user: false, 696 | text: alloc::format!( 697 | "commands: 698 | - pid Display the app pid 699 | - time Display the kernel time 700 | - reset Clear the app memory 701 | - lang .. Set key locale (en,fr) 702 | - eval .. Eval fomoscript 703 | - repl launch fomoscript REPL 704 | - help You are here 705 | " 706 | ), 707 | }); 708 | } 709 | _ => { 710 | if text.starts_with(">eval ") { 711 | use crate::alloc::borrow::ToOwned; 712 | use fomoscript::*; 713 | 714 | let res = { 715 | store.script_ctx.insert_code(&text[5..]); 716 | store.script_ctx.set_var_absolute( 717 | "time", 718 | N::Num(ctx.start_time as f64), 719 | ); 720 | store.script_ctx.set_var_absolute( 721 | "pid", 722 | N::Num(ctx.pid as f64), 723 | ); 724 | 725 | { 726 | //TODO find a safe way to share native function to interpreter 727 | let ptr: *mut [RGBA] = ctx.fb.pixels; 728 | let w = ctx.fb.w; 729 | let draw_pixel = alloc::rc::Rc::new( 730 | move |a: N, b: N, c: N, d: N| -> N { 731 | let arr: &mut [RGBA] = 732 | unsafe { &mut *ptr }; 733 | let p = &mut arr[a.as_f64() 734 | as usize 735 | + (b.as_f64() as usize) * w]; 736 | 737 | p.r = (c.as_f64() * 255.0) as u8; 738 | p.g = p.r; 739 | p.b = p.b; 740 | N::Unit 741 | }, 742 | ); 743 | store.script_ctx.set_var_absolute( 744 | "draw", 745 | N::FuncNativeDef(Native(draw_pixel)), 746 | ); 747 | } 748 | let mut res = N::Unit; 749 | while let Ok(parent) = 750 | store.script_ctx.parse_next_expr() 751 | { 752 | res = eval(&parent, &mut store.script_ctx); 753 | } 754 | 755 | log(&alloc::format!("res {:?}", res)); 756 | 757 | res 758 | }; 759 | 760 | store.console_history.atoms.push(Atom { 761 | is_user: false, 762 | text: alloc::format!("eval: {:?}", res), 763 | }); 764 | } else { 765 | store.console_history.atoms.push(Atom { 766 | is_user: false, 767 | text: alloc::format!("unknown command"), 768 | }); 769 | } 770 | } 771 | } 772 | } 773 | 774 | store.console_history.atoms.push(Atom { 775 | is_user: true, 776 | text: alloc::string::String::from( 777 | if let Mode::Shell = store.mode { 778 | ">" 779 | } else { 780 | "fos>" 781 | }, 782 | ), 783 | }); 784 | break 'new_inputs; 785 | } 786 | 787 | Key::KeyBackspace => { 788 | if text.len() > 1 { 789 | text.pop(); 790 | } 791 | } 792 | _ => {} 793 | } 794 | 795 | if let Some(c) = key.char(store.local, store.shift, store.altg) { 796 | *text = alloc::format!("{}{}", text, c) 797 | } else { 798 | match key { 799 | Key::KeyEnter if store.shift => { 800 | *text = alloc::format!("{}\n", text,) 801 | } 802 | _ => {} 803 | } 804 | // store.text_buffer = alloc::format!("{}{:?}", store.text_buffer, key) 805 | } 806 | } 807 | _ => {} 808 | } 809 | } 810 | } 811 | } 812 | store.input_history_last_index = ctx.input.history_last_index; 813 | 814 | return 0; 815 | } 816 | -------------------------------------------------------------------------------- /app_console/x86_64.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "code-model": "small", 4 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 5 | "arch": "x86_64", 6 | "target-endian": "little", 7 | "target-pointer-width": "64", 8 | "target-c-int-width": "32", 9 | "os": "none", 10 | "executables": true, 11 | "linker-flavor": "ld.lld", 12 | "linker": "rust-lld", 13 | "relocation-model": "pie", 14 | "position-independent-executables": true, 15 | "static-position-independent-executables": true, 16 | "panic-strategy": "abort", 17 | "disable-redzone": true, 18 | "features": "+mmx,+sse,+sse2,+sse3" 19 | } 20 | -------------------------------------------------------------------------------- /app_cursor/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std-features = ["compiler-builtins-mem"] 3 | build-std = ["core", "compiler_builtins","alloc"] 4 | 5 | [build] 6 | target = "x86_64.json" -------------------------------------------------------------------------------- /app_cursor/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 = "approx" 7 | version = "0.5.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 10 | dependencies = [ 11 | "num-traits", 12 | ] 13 | 14 | [[package]] 15 | name = "arrform" 16 | version = "0.1.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e7cf566ecc5c9d82b973e81d30babf6583c9b497f86295c952d538c3254ef4e6" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "bit_field" 28 | version = "0.10.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "1.3.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 37 | 38 | [[package]] 39 | name = "func" 40 | version = "0.1.0" 41 | dependencies = [ 42 | "arrform", 43 | "vek", 44 | "x86_64", 45 | ] 46 | 47 | [[package]] 48 | name = "libm" 49 | version = "0.2.6" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" 52 | 53 | [[package]] 54 | name = "num-integer" 55 | version = "0.1.45" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 58 | dependencies = [ 59 | "autocfg", 60 | "num-traits", 61 | ] 62 | 63 | [[package]] 64 | name = "num-traits" 65 | version = "0.2.15" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 68 | dependencies = [ 69 | "autocfg", 70 | "libm", 71 | ] 72 | 73 | [[package]] 74 | name = "rustc_version" 75 | version = "0.4.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 78 | dependencies = [ 79 | "semver", 80 | ] 81 | 82 | [[package]] 83 | name = "rustversion" 84 | version = "1.0.12" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" 87 | 88 | [[package]] 89 | name = "semver" 90 | version = "1.0.17" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" 93 | 94 | [[package]] 95 | name = "vek" 96 | version = "0.15.10" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "8085882662f9bc47fc8b0cdafa5e19df8f592f650c02b9083da8d45ac9eebd17" 99 | dependencies = [ 100 | "approx", 101 | "num-integer", 102 | "num-traits", 103 | "rustc_version", 104 | ] 105 | 106 | [[package]] 107 | name = "volatile" 108 | version = "0.4.6" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" 111 | 112 | [[package]] 113 | name = "x86_64" 114 | version = "0.14.10" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" 117 | dependencies = [ 118 | "bit_field", 119 | "bitflags", 120 | "rustversion", 121 | "volatile", 122 | ] 123 | -------------------------------------------------------------------------------- /app_cursor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "func" 3 | version = "0.1.0" 4 | edition = "2021" 5 | [[bin]] 6 | name="func" 7 | test = false 8 | bench = false 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | x86_64 = { version = "0.14.8" } 13 | arrform = "0.1.1" 14 | vek = { version = "0.15.10", default-features = false, features = ["libm"] } 15 | 16 | # [dependencies.zune-jpeg] 17 | # version ="0.3.14" 18 | # default-features = false 19 | # features = ["x86"] 20 | 21 | 22 | [features] 23 | default = [] 24 | 25 | [profile.dev] 26 | panic = "abort" 27 | 28 | [profile.release] 29 | panic = "abort" 30 | lto = true 31 | strip = true 32 | codegen-units = 1 33 | 34 | [workspace] 35 | 36 | #RUSTFLAGS="-C relocation-model=pie -C link-arg=-nostartfiles -C link-arg=-pie" 37 | 38 | # RUSTFLAGS="-C relocation-model=pie -C link-arg=-nostartfiles -C link-arg=-pie" cargo build --release --target x86_64-unknown-linux-gnu 39 | 40 | 41 | # CURRENT WORKING with custom target in .cargo 42 | # RUSTFLAGS="-C relocation-model=pie -C link-arg=-pie" cargo build --release -------------------------------------------------------------------------------- /app_cursor/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /app_cursor/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | 5 | extern crate alloc; 6 | mod st; 7 | use alloc::boxed::Box; 8 | use st::*; 9 | use vek::Vec2; 10 | 11 | pub struct Store { 12 | x: usize, 13 | y: usize, 14 | xm: usize, 15 | ym: usize, 16 | } 17 | 18 | #[no_mangle] 19 | pub extern "C" fn _start(ctx: &mut Context) -> i32 { 20 | unsafe { ALLOCATOR.swap(ctx) }; 21 | unsafe { LOGGER.swap(ctx.log) }; 22 | 23 | // (ctx.log)("back start"); 24 | // x86_64::instructions::interrupts::int3(); 25 | 26 | let mut store: Box; 27 | if let Some(ptr) = ctx.store.take() { 28 | // st::log("store found"); 29 | store = ptr; 30 | } else { 31 | st::log("store not found"); 32 | 33 | store = Box::new(Store { 34 | x: ctx.input.mx, 35 | y: ctx.input.my, 36 | xm: ctx.input.mx, 37 | ym: ctx.input.my, 38 | }) 39 | } 40 | 41 | let am = Vec2::new(store.xm as f32 + 0.01, store.ym as f32); 42 | let a = Vec2::new(store.x as f32, store.y as f32); 43 | let b = Vec2::new(ctx.input.mx as f32 + 0.01, ctx.input.my as f32 + 0.01); 44 | 45 | fn cro(a: Vec2, b: Vec2) -> f32 { 46 | a.x * b.y - a.y * b.x 47 | } 48 | 49 | fn sd_bezier(p: Vec2, v0: Vec2, v1: Vec2, v2: Vec2) -> f32 { 50 | let mid = (v0 + v2) * 0.5; 51 | let to_v1 = v1 - mid; 52 | let v1 = v1 + to_v1 * 1.; 53 | 54 | let i = v0 - v2; 55 | let j = v2 - v1; 56 | let k = v1 - v0; 57 | let w = j - k; 58 | 59 | let v0 = v0 - p; 60 | let v1 = v1 - p; 61 | let v2 = v2 - p; 62 | 63 | let x = cro(v0, v2); 64 | let y = cro(v1, v0); 65 | let z = cro(v2, v1); 66 | 67 | let s = 2.0 * (y * j + z * k) - x * i; 68 | 69 | let r = (y * z - x * x * 0.25) / s.dot(s); 70 | let t = ((0.5 * x + y + r * s.dot(w)) / (x + y + z)).clamp(0.0, 1.0); 71 | 72 | (v0 + t * (k + k + t * w)).magnitude() 73 | } 74 | 75 | for y in 0..ctx.fb.h { 76 | for x in 0..ctx.fb.w { 77 | let p = &mut ctx.fb.pixels[x + y * ctx.fb.w]; 78 | if (x as i32 - ctx.input.mx as i32).abs() + (y as i32 - ctx.input.my as i32).abs() < 80 79 | { 80 | let pos = Vec2::new(x as f32, y as f32); 81 | let d = sd_bezier(pos, am, a, b); 82 | if d < 3. { 83 | p.r = 255; 84 | 85 | let left_click = ctx.input.keys[0x110]; 86 | if left_click < 128 { 87 | p.g = 255; 88 | p.b = 255; 89 | } else if left_click == 128 { 90 | p.g = 255; 91 | p.b = 0; 92 | } else { 93 | p.g = 0; 94 | p.b = 0; 95 | } 96 | } else if d < 4. { 97 | p.r = 0; 98 | p.g = 0; 99 | p.b = 0; 100 | } 101 | } 102 | } 103 | } 104 | store.xm = store.x; 105 | store.ym = store.y; 106 | store.x = ctx.input.mx; 107 | store.y = ctx.input.my; 108 | *ctx.store = Some(store); 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /app_cursor/src/st.rs: -------------------------------------------------------------------------------- 1 | #[panic_handler] 2 | fn panic(info: &core::panic::PanicInfo) -> ! { 3 | x86_64::instructions::interrupts::int3(); 4 | unsafe { 5 | (LOGGER.f)("panic".as_ptr(), 5); 6 | }; 7 | unsafe { 8 | let s = &format!("{:?}", info); 9 | (LOGGER.f)(s.as_ptr(), s.len() as u32) 10 | }; 11 | loop {} 12 | } 13 | 14 | pub static mut LOGGER: Logger = Logger::init(); 15 | 16 | pub fn log(s: &str) { 17 | unsafe { (LOGGER.f)(s.as_ptr(), s.len() as u32) } 18 | } 19 | 20 | type LogFn = extern "C" fn(*const u8, u32); 21 | extern "C" fn nop(s: *const u8, l: u32) {} 22 | pub struct Logger { 23 | pub f: LogFn, 24 | } 25 | impl Logger { 26 | pub const fn init() -> Self { 27 | Self { f: nop } 28 | } 29 | pub fn swap(&mut self, f2: LogFn) { 30 | self.f = f2; 31 | } 32 | } 33 | #[repr(C)] 34 | pub struct Context<'a, T> { 35 | pub version: u8, 36 | pub start_time: u64, 37 | pub log: extern "C" fn(s: *const u8, l: u32), 38 | pub pid: u64, 39 | pub fb: FB<'a>, 40 | pub calloc: extern "C" fn(usize, usize) -> *mut u8, 41 | pub cdalloc: extern "C" fn(*mut u8, usize, usize), 42 | pub store: &'a mut Option>, 43 | pub input: &'a Input, 44 | } 45 | 46 | const HISTORY_SIZE: usize = 64; 47 | 48 | #[repr(C)] 49 | #[derive(Clone, Debug, Copy)] 50 | pub struct InputEvent { 51 | pub trigger: bool, 52 | pub key: usize, 53 | } 54 | #[repr(C)] 55 | pub struct Input { 56 | pub mx: usize, 57 | pub my: usize, 58 | pub keys: [u8; 1024], 59 | pub history_last_index: usize, 60 | pub history_ring: [InputEvent; HISTORY_SIZE], 61 | } 62 | 63 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 64 | #[repr(C)] 65 | pub struct RGBA { 66 | pub r: u8, 67 | pub g: u8, 68 | pub b: u8, 69 | pub a: u8, 70 | } 71 | 72 | #[repr(C)] 73 | pub struct FB<'a> { 74 | pub pixels: &'a mut [RGBA], 75 | pub w: usize, 76 | pub h: usize, 77 | } 78 | 79 | use core::alloc::GlobalAlloc; 80 | 81 | use alloc::{boxed::Box, format}; 82 | 83 | extern "C" fn a_init(size: usize, align: usize) -> *mut u8 { 84 | panic!("") 85 | } 86 | extern "C" fn d_init(ptr: *mut u8, size: usize, align: usize) { 87 | panic!("") 88 | } 89 | #[repr(C)] 90 | pub struct AllocFromCtx { 91 | a: extern "C" fn(usize, usize) -> *mut u8, 92 | d: extern "C" fn(*mut u8, usize, usize), 93 | } 94 | unsafe impl GlobalAlloc for AllocFromCtx { 95 | unsafe fn alloc(&self, layout: alloc::alloc::Layout) -> *mut u8 { 96 | (self.a)(layout.size(), layout.align()) 97 | } 98 | 99 | unsafe fn dealloc(&self, ptr: *mut u8, layout: alloc::alloc::Layout) { 100 | (self.d)(ptr, layout.size(), layout.align()); 101 | } 102 | } 103 | impl AllocFromCtx { 104 | pub const fn init() -> Self { 105 | Self { 106 | a: a_init, 107 | d: d_init, 108 | } 109 | } 110 | pub fn swap(&mut self, ctx: &mut Context) { 111 | let ptr = self; 112 | ptr.a = ctx.calloc; 113 | ptr.d = ctx.cdalloc; 114 | } 115 | } 116 | #[global_allocator] 117 | pub static mut ALLOCATOR: AllocFromCtx = AllocFromCtx::init(); 118 | -------------------------------------------------------------------------------- /app_cursor/x86_64.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "code-model": "small", 4 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 5 | "arch": "x86_64", 6 | "target-endian": "little", 7 | "target-pointer-width": "64", 8 | "target-c-int-width": "32", 9 | "os": "none", 10 | "executables": true, 11 | "linker-flavor": "ld.lld", 12 | "linker": "rust-lld", 13 | "relocation-model": "pie", 14 | "position-independent-executables": true, 15 | "static-position-independent-executables": true, 16 | "panic-strategy": "abort", 17 | "disable-redzone": true, 18 | "features": "+mmx,+sse,+sse2,+sse3" 19 | } 20 | -------------------------------------------------------------------------------- /app_test/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std-features = ["compiler-builtins-mem"] 3 | build-std = ["core", "compiler_builtins"] 4 | 5 | [build] 6 | target = "x86_64.json" -------------------------------------------------------------------------------- /app_test/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 = "func" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /app_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "func" 3 | version = "0.1.0" 4 | edition = "2021" 5 | [[bin]] 6 | name="func" 7 | test = false 8 | bench = false 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | [profile.dev] 13 | panic = "abort" 14 | 15 | [profile.release] 16 | panic = "abort" 17 | 18 | [workspace] -------------------------------------------------------------------------------- /app_test/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /app_test/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[panic_handler] 5 | fn panic(_info: &core::panic::PanicInfo) -> ! { 6 | loop {} 7 | } 8 | 9 | const TEXT: &str = "Writting from pid: "; 10 | #[repr(C)] 11 | pub struct Context<'a> { 12 | pub version: u8, 13 | start_time: u64, 14 | log: extern "C" fn(s: *const u8, l: u32), 15 | pid: u64, 16 | fb: FB<'a>, 17 | } 18 | 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 20 | #[repr(C)] 21 | pub struct RGBA { 22 | pub r: u8, 23 | pub g: u8, 24 | pub b: u8, 25 | pub a: u8, 26 | } 27 | 28 | #[repr(C)] 29 | pub struct FB<'a> { 30 | pub pixels: &'a mut [RGBA], 31 | pub w: usize, 32 | pub h: usize, 33 | } 34 | 35 | #[no_mangle] 36 | pub extern "C" fn _start(ctx: &mut Context) -> i32 { 37 | ctx.version += 1; 38 | 39 | let mut txt = [0; TEXT.len()]; 40 | let m = &mut txt; 41 | m.copy_from_slice(TEXT.as_bytes()); 42 | m[m.len() - 1] = '0' as u8 + ctx.pid as u8; 43 | 44 | let s = unsafe { core::str::from_utf8_unchecked(&txt) }; 45 | (ctx.log)(s.as_ptr(), s.len() as u32); 46 | 47 | //make thinks blue 48 | // for px in ctx.fb.pixels.iter_mut() { 49 | // px.b = 255; 50 | // } 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /app_test/x86_64.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 | } 16 | -------------------------------------------------------------------------------- /assets/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ruddle/Fomos/336be2d5bb2733f27e593dec70034a4f59650efd/assets/demo.mp4 -------------------------------------------------------------------------------- /bootloader/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | 2 | [unstable] 3 | # enable the unstable artifact-dependencies feature, see 4 | # https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies 5 | bindeps = true 6 | 7 | 8 | -------------------------------------------------------------------------------- /bootloader/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [package] 5 | name = "loader" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [build-dependencies] 10 | bootloader = "0.11.3" 11 | kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none" } 12 | # 13 | [dependencies] 14 | 15 | [workspace] 16 | members = ["kernel"] -------------------------------------------------------------------------------- /bootloader/build.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use bootloader::BootConfig; 4 | 5 | fn main() { 6 | // set by cargo, build scripts should use this directory for output files 7 | let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); 8 | // set by cargo's artifact dependency feature, see 9 | // https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies 10 | let kernel = PathBuf::from(std::env::var_os("CARGO_BIN_FILE_KERNEL_kernel").unwrap()); 11 | 12 | // create an UEFI disk image (optional) 13 | let uefi_path = out_dir.join("uefi.img"); 14 | 15 | let mut conf = BootConfig::default(); 16 | conf.frame_buffer.minimum_framebuffer_width = Some(1200); 17 | bootloader::UefiBoot::new(&kernel) 18 | .set_boot_config(&conf) 19 | .create_disk_image(&uefi_path) 20 | .unwrap(); 21 | 22 | // create a BIOS disk image 23 | let bios_path = out_dir.join("bios.img"); 24 | bootloader::BiosBoot::new(&kernel) 25 | .create_disk_image(&bios_path) 26 | .unwrap(); 27 | 28 | // pass the disk image paths as env variables to the `main.rs` 29 | println!("cargo:rustc-env=UEFI_PATH={}", uefi_path.display()); 30 | println!("cargo:rustc-env=BIOS_PATH={}", bios_path.display()); 31 | } 32 | -------------------------------------------------------------------------------- /bootloader/kernel/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 = "kernel" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /bootloader/kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "kernel" 4 | version = "0.1.0" 5 | edition = "2021" 6 | [[bin]] 7 | name="kernel" 8 | test = false 9 | bench = false 10 | 11 | [dependencies] 12 | bootloader_api = "0.11.3" 13 | bootloader-boot-config = "0.11.3" 14 | conquer-once = { version = "0.3.2", default-features = false } 15 | spinning_top = "0.2.4" 16 | usize_conversions = "0.2.0" 17 | x86_64 = { version = "0.14.8" } 18 | xmas-elf = "0.8.0" 19 | raw-cpuid = "10.2.0" 20 | rand = { version = "0.8.4", default-features = false } 21 | rand_hc = "0.3.1" 22 | uart_16550 = "0.2.18" 23 | log = "0.4.17" 24 | acpi = "4.1.1" 25 | linked_list_allocator = "0.9.0" 26 | iced-x86 = {version = "1.18.0", default-features = false, features= ["decoder", "no_std","nasm"]} 27 | arrayvec = {version="0.7.2", default-features = false} 28 | bitfield = "0.14.0" 29 | crossbeam = {version="0.8", default-features=false, features=["alloc"]} 30 | hashbrown = {version="0.13.2"} 31 | edid-rs = {version="0.1.0", default-features=false, features=["no_std"]} 32 | spin = "0.5.2" 33 | # virtio-drivers = "0.3.0" 34 | [dependencies.noto-sans-mono-bitmap] 35 | version = "0.2.0" 36 | default-features = false 37 | features = [ 38 | "regular", 39 | "size_16", 40 | "unicode-basic-latin", 41 | # required for the fallback char '�' 42 | "unicode-specials", 43 | ] 44 | 45 | [dependencies.lazy_static] 46 | version = "1.0" 47 | features = ["spin_no_std"] 48 | 49 | [dependencies.futures] 50 | version = "0.3.4" 51 | default-features = false 52 | features = ["alloc","async-await"] -------------------------------------------------------------------------------- /bootloader/kernel/src/allocator.rs: -------------------------------------------------------------------------------- 1 | use alloc::alloc::{GlobalAlloc, Layout}; 2 | use core::ptr::null_mut; 3 | extern "C" fn a_init(l: alloc::alloc::Layout) -> *mut u8 { 4 | panic!("") 5 | } 6 | extern "C" fn d_init(ptr: *mut u8, layout: alloc::alloc::Layout) { 7 | panic!("") 8 | } 9 | 10 | #[repr(C)] 11 | pub struct AllocFromCtx { 12 | a: extern "C" fn(alloc::alloc::Layout) -> *mut u8, 13 | d: extern "C" fn(*mut u8, alloc::alloc::Layout), 14 | } 15 | impl AllocFromCtx { 16 | pub fn init() -> Self { 17 | Self { 18 | a: a_init, 19 | d: d_init, 20 | } 21 | } 22 | pub fn new( 23 | a: extern "C" fn(alloc::alloc::Layout) -> *mut u8, 24 | d: extern "C" fn(*mut u8, alloc::alloc::Layout), 25 | ) -> Self { 26 | Self { a, d } 27 | } 28 | } 29 | unsafe impl GlobalAlloc for AllocFromCtx { 30 | unsafe fn alloc(&self, layout: alloc::alloc::Layout) -> *mut u8 { 31 | (self.a)(layout) 32 | } 33 | 34 | unsafe fn dealloc(&self, ptr: *mut u8, layout: alloc::alloc::Layout) { 35 | (self.d)(ptr, layout) 36 | } 37 | } 38 | // #[global_allocator] 39 | // static ALLOCATOR: AllocFromCtx = AllocFromCtx; 40 | 41 | use linked_list_allocator::LockedHeap; 42 | 43 | #[global_allocator] 44 | pub static ALLOCATOR: LockedHeap = LockedHeap::empty(); 45 | 46 | pub const HEAP_START: usize = 0x_4444_4444_0000; 47 | pub const HEAP_SIZE: usize = 128 * 1024 * 1024; // 100 KiB 48 | 49 | use x86_64::{ 50 | structures::paging::{ 51 | mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB, 52 | }, 53 | VirtAddr, 54 | }; 55 | 56 | pub fn init_heap( 57 | mapper: &mut impl Mapper, 58 | frame_allocator: &mut impl FrameAllocator, 59 | ) -> Result<(), MapToError> { 60 | let page_range = { 61 | let heap_start = VirtAddr::new(HEAP_START as u64); 62 | let heap_end = heap_start + HEAP_SIZE - 1u64; 63 | let heap_start_page = Page::containing_address(heap_start); 64 | let heap_end_page = Page::containing_address(heap_end); 65 | Page::range_inclusive(heap_start_page, heap_end_page) 66 | }; 67 | 68 | for page in page_range { 69 | let frame = frame_allocator 70 | .allocate_frame() 71 | .ok_or(MapToError::FrameAllocationFailed)?; 72 | let flags = 73 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE; 74 | unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; 75 | } 76 | 77 | unsafe { 78 | ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); 79 | } 80 | 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /bootloader/kernel/src/app.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | alloc::Layout, 3 | sync::atomic::{AtomicU64, Ordering}, 4 | }; 5 | 6 | use alloc::{boxed::Box, fmt::format, format, string::String, vec::Vec}; 7 | use xmas_elf::{program, sections::SectionData, ElfFile}; 8 | 9 | use crate::{allocator::ALLOCATOR, framebuffer::FBShare, globals, interrupts::global_time_ms}; 10 | 11 | #[repr(C)] 12 | pub struct Context<'a> { 13 | pub version: u8, 14 | pub start_time: u64, 15 | pub log: extern "C" fn(*const u8, u32), 16 | pub pid: u64, 17 | pub fb: FBShare<'a>, 18 | pub calloc: extern "C" fn(usize, usize) -> *mut u8, 19 | pub cdalloc: extern "C" fn(*mut u8, usize, usize), 20 | pub store: &'a mut Option>, 21 | pub input: &'a globals::Input, 22 | } 23 | static mut none: Option> = None; 24 | impl<'a> Context<'a> { 25 | pub fn new( 26 | log: extern "C" fn(*const u8, u32), 27 | fb: FBShare<'a>, 28 | calloc: extern "C" fn(usize, usize) -> *mut u8, 29 | cdalloc: extern "C" fn(*mut u8, usize, usize), 30 | input: &'a globals::Input, 31 | ) -> Context<'a> { 32 | let x = Context { 33 | version: 1, 34 | start_time: global_time_ms(), 35 | log, 36 | pid: 0, 37 | fb, 38 | calloc, 39 | cdalloc, 40 | store: unsafe { &mut none }, 41 | input, 42 | }; 43 | 44 | return x; 45 | } 46 | } 47 | 48 | type FuncType = extern "C" fn(arg: &mut Context) -> i32; 49 | 50 | pub struct App { 51 | pub code: Vec, 52 | pub func: FuncType, 53 | pub pid: u64, 54 | pub store: Option>, 55 | } 56 | impl App { 57 | pub fn new(code: &[u8], show: bool) -> App { 58 | let code = code.to_vec(); 59 | let code = &code[..]; 60 | 61 | // log::info!("loa efl"); 62 | let elf = ElfFile::new(code).expect("elf"); 63 | // log::info!("Elf file loaded at {:#p}", elf.input); 64 | // log::info!("{:#?}", elf.header); 65 | 66 | let mut min_virt = u64::MAX; 67 | let mut max_virt = 0; 68 | 69 | for program_header in elf.program_iter() { 70 | if program_header.mem_size() > 0 { 71 | min_virt = min_virt.min(program_header.virtual_addr()); 72 | max_virt = max_virt.max(program_header.virtual_addr() + program_header.mem_size()); 73 | } 74 | } 75 | 76 | min_virt = 0; 77 | let cap = max_virt as usize; 78 | use core::alloc::GlobalAlloc; 79 | let ptr = unsafe { ALLOCATOR.alloc(Layout::from_size_align_unchecked(cap, 4096)) }; 80 | 81 | let mut owned_code: Vec = unsafe { Vec::from_raw_parts(ptr, 0, cap) }; //Vec::with_capacity((max_virt - min_virt) as usize); 82 | for i in min_virt..max_virt { 83 | owned_code.push(0); 84 | } 85 | 86 | for program_header in elf.program_iter() { 87 | // log::info!("{:?}", program_header); 88 | 89 | if let Ok(program::Type::Load) = program_header.get_type() { 90 | let mut byte = 0; 91 | let seg_offset = (program_header.virtual_addr() - min_virt) as usize; 92 | for e in (program_header.offset() as usize) 93 | ..(program_header.offset() as usize + ((program_header.file_size()) as usize)) 94 | { 95 | owned_code[byte + seg_offset] = code[e]; 96 | byte += 1; 97 | } 98 | 99 | if let Some(rela) = elf.find_section_by_name(".rela.dyn") { 100 | if let Ok(SectionData::Rela64(arr)) = rela.get_data(&elf) { 101 | for r in &arr[0..] { 102 | let off = r.get_offset() as usize; 103 | let add = r.get_addend() as usize; 104 | let typ = r.get_type() as usize; 105 | if off >= program_header.virtual_addr() as usize 106 | && off 107 | < program_header.virtual_addr() as usize 108 | + program_header.mem_size() as usize 109 | { 110 | // log::info!("{:?}", r); 111 | unsafe { 112 | let global_off = owned_code.as_ptr() as u64; 113 | let p64 = owned_code.as_ptr().offset(off as isize) as *mut u64; 114 | p64.write(add as u64 + global_off); 115 | } 116 | 117 | // for k in 0..8 { 118 | // owned_code[off + k] = owned_code[add + k]; 119 | // } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | // log::info!("{:?}", owned_code); 128 | let codep = unsafe { 129 | owned_code 130 | .as_mut_ptr() 131 | .offset((elf.header.pt2.entry_point() - min_virt) as isize) 132 | }; 133 | // log::info!("ptr {:?}", codep); 134 | let codef: FuncType = unsafe { core::intrinsics::transmute(codep) }; 135 | 136 | static NEXT_ID: AtomicU64 = AtomicU64::new(0); 137 | 138 | if show { 139 | let entry = (elf.header.pt2.entry_point() - min_virt) as usize; 140 | let code = &mut owned_code[entry..]; 141 | use iced_x86::{Decoder, DecoderOptions, Formatter, Instruction, NasmFormatter}; 142 | let EXAMPLE_CODE_RIP = entry as u64; 143 | let HEXBYTES_COLUMN_BYTE_LENGTH = 8; 144 | let mut decoder = Decoder::with_ip(64, code, EXAMPLE_CODE_RIP, DecoderOptions::NONE); 145 | 146 | // Formatters: Masm*, Nasm*, Gas* (AT&T) and Intel* (XED). 147 | // For fastest code, see `SpecializedFormatter` which is ~3.3x faster. Use it if formatting 148 | // speed is more important than being able to re-assemble formatted instructions. 149 | let mut formatter = NasmFormatter::new(); 150 | 151 | // Change some options, there are many more 152 | formatter.options_mut().set_digit_separator("`"); 153 | formatter.options_mut().set_first_operand_char_index(10); 154 | 155 | // String implements FormatterOutput 156 | let mut output = String::new(); 157 | 158 | // Initialize this outside the loop because decode_out() writes to every field 159 | let mut instruction = Instruction::default(); 160 | 161 | // The decoder also implements Iterator/IntoIterator so you could use a for loop: 162 | // for instruction in &mut decoder { /* ... */ } 163 | // or collect(): 164 | // let instructions: Vec<_> = decoder.into_iter().collect(); 165 | // but can_decode()/decode_out() is a little faster: 166 | while decoder.can_decode() { 167 | // There's also a decode() method that returns an instruction but that also 168 | // means it copies an instruction (40 bytes): 169 | // instruction = decoder.decode(); 170 | decoder.decode_out(&mut instruction); 171 | 172 | log::info!("{:?}", instruction); 173 | 174 | // Format the instruction ("disassemble" it) 175 | output.clear(); 176 | formatter.format(&instruction, &mut output); 177 | 178 | // Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]" 179 | let mut strbuild = String::new(); 180 | 181 | strbuild = format!("{}{:016X} ", strbuild, instruction.ip()); 182 | let start_index = (instruction.ip() - EXAMPLE_CODE_RIP) as usize; 183 | let instr_bytes = &code[start_index..start_index + instruction.len()]; 184 | for b in instr_bytes.iter() { 185 | strbuild = format!("{}{:02X}", strbuild, b); 186 | } 187 | if instr_bytes.len() < HEXBYTES_COLUMN_BYTE_LENGTH { 188 | for _ in 0..HEXBYTES_COLUMN_BYTE_LENGTH - instr_bytes.len() { 189 | strbuild = format!("{} ", strbuild); 190 | } 191 | } 192 | log::info!("{} {}", strbuild, output); 193 | } 194 | } 195 | 196 | App { 197 | code: owned_code, 198 | func: codef, 199 | pid: NEXT_ID.fetch_add(1, Ordering::Relaxed), 200 | store: None, 201 | } 202 | } 203 | pub fn call(&mut self, arg: &mut Context) -> i32 { 204 | *arg.store = None; 205 | 206 | arg.pid = self.pid; 207 | 208 | let self_store = self.store.take(); 209 | 210 | *arg.store = self_store; 211 | let res = (self.func)(arg); 212 | 213 | self.store = arg.store.take(); 214 | 215 | res 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /bootloader/kernel/src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod virtio_gpu; 2 | pub mod virtio_input; 3 | -------------------------------------------------------------------------------- /bootloader/kernel/src/drivers/virtio_input.rs: -------------------------------------------------------------------------------- 1 | use crate::{task::executor::yield_once, virtio::Virtio}; 2 | 3 | #[repr(C)] 4 | #[derive(Debug)] 5 | struct VirtioInputEvent { 6 | type_: u16, 7 | code: u16, 8 | value: u32, 9 | } 10 | ///Handle the virtio device and export all data to globals::Input 11 | pub async fn drive(mut virtio: Virtio) { 12 | unsafe { 13 | let q = 0; 14 | virtio.queue_select(q); 15 | while let Some(desc_id) = virtio.get_free_desc_id() { 16 | virtio.set_writable_available(desc_id); 17 | } 18 | loop { 19 | while let Some(used) = virtio.next_used() { 20 | let desc = virtio.read_desc(used.id as u16); 21 | let evt = (desc.addr as *const VirtioInputEvent).read_volatile(); 22 | crate::globals::INPUT.update(|input| match evt.type_ { 23 | 0 => { /*no op */ } 24 | 1 => input.handle_incoming_state(evt.code as usize, evt.value != 0), 25 | 2 => { 26 | let d: i32 = core::intrinsics::transmute(evt.value); 27 | match evt.code { 28 | 0 => input.mouse_x = (input.mouse_x as i32 + d).max(0) as usize, 29 | 1 => input.mouse_y = (input.mouse_y as i32 + d).max(0) as usize, 30 | _ => log::error!("virtio_input: unknown event {:?}", evt), 31 | } 32 | } 33 | _ => log::error!("virtio_input: unknown event {:?}", evt), 34 | }); 35 | virtio.set_writable_available(used.id as u16); 36 | } 37 | yield_once().await; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bootloader/kernel/src/framebuffer.rs: -------------------------------------------------------------------------------- 1 | use bootloader_api::info::{FrameBufferInfo, PixelFormat}; 2 | 3 | use core::{fmt, ptr}; 4 | use font_constants::BACKUP_CHAR; 5 | use noto_sans_mono_bitmap::{ 6 | get_raster, get_raster_width, FontWeight, RasterHeight, RasterizedChar, 7 | }; 8 | 9 | /// Additional vertical space between lines 10 | const LINE_SPACING: usize = 2; 11 | /// Additional horizontal space between characters. 12 | const LETTER_SPACING: usize = 0; 13 | 14 | /// Padding from the border. Prevent that font is too close to border. 15 | const BORDER_PADDING: usize = 1; 16 | 17 | /// Constants for the usage of the [`noto_sans_mono_bitmap`] crate. 18 | mod font_constants { 19 | use super::*; 20 | 21 | /// Height of each char raster. The font size is ~0.84% of this. Thus, this is the line height that 22 | /// enables multiple characters to be side-by-side and appear optically in one line in a natural way. 23 | pub const CHAR_RASTER_HEIGHT: RasterHeight = RasterHeight::Size16; 24 | 25 | /// The width of each single symbol of the mono space font. 26 | pub const CHAR_RASTER_WIDTH: usize = get_raster_width(FontWeight::Regular, CHAR_RASTER_HEIGHT); 27 | 28 | /// Backup character if a desired symbol is not available by the font. 29 | /// The '�' character requires the feature "unicode-specials". 30 | pub const BACKUP_CHAR: char = '�'; 31 | 32 | pub const FONT_WEIGHT: FontWeight = FontWeight::Regular; 33 | } 34 | 35 | /// Returns the raster of the given char or the raster of [`font_constants::BACKUP_CHAR`]. 36 | fn get_char_raster(c: char) -> RasterizedChar { 37 | fn get(c: char) -> Option { 38 | get_raster( 39 | c, 40 | font_constants::FONT_WEIGHT, 41 | font_constants::CHAR_RASTER_HEIGHT, 42 | ) 43 | } 44 | get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("Should get raster of backup char.")) 45 | } 46 | 47 | /// Allows logging text to a pixel-based framebuffer. 48 | pub struct FrameBufferWriter { 49 | framebuffer: &'static mut [u8], 50 | info: FrameBufferInfo, 51 | x_pos: usize, 52 | y_pos: usize, 53 | pub level: usize, 54 | } 55 | 56 | impl FrameBufferWriter { 57 | /// Creates a new logger that uses the given framebuffer. 58 | pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { 59 | let mut logger = Self { 60 | framebuffer, 61 | info, 62 | x_pos: 0, 63 | y_pos: 0, 64 | level: 0, 65 | }; 66 | logger.clear(); 67 | logger 68 | } 69 | 70 | fn newline(&mut self) { 71 | self.y_pos += font_constants::CHAR_RASTER_HEIGHT.val() + LINE_SPACING; 72 | self.carriage_return() 73 | } 74 | 75 | fn carriage_return(&mut self) { 76 | self.x_pos = BORDER_PADDING; 77 | } 78 | 79 | /// Erases all text on the screen. Resets `self.x_pos` and `self.y_pos`. 80 | pub fn clear(&mut self) { 81 | self.x_pos = BORDER_PADDING; 82 | self.y_pos = BORDER_PADDING; 83 | self.framebuffer.fill(0); 84 | } 85 | 86 | fn width(&self) -> usize { 87 | self.info.width 88 | } 89 | 90 | fn height(&self) -> usize { 91 | self.info.height 92 | } 93 | 94 | /// Writes a single char to the framebuffer. Takes care of special control characters, such as 95 | /// newlines and carriage returns. 96 | fn write_char(&mut self, c: char) { 97 | match c { 98 | '\n' => self.newline(), 99 | '\r' => self.carriage_return(), 100 | c => { 101 | let new_xpos = self.x_pos + font_constants::CHAR_RASTER_WIDTH; 102 | if new_xpos >= self.width() { 103 | self.newline(); 104 | } 105 | let new_ypos = 106 | self.y_pos + font_constants::CHAR_RASTER_HEIGHT.val() + BORDER_PADDING; 107 | if new_ypos >= self.height() { 108 | self.clear(); 109 | } 110 | self.write_rendered_char(get_char_raster(c)); 111 | } 112 | } 113 | } 114 | 115 | /// Prints a rendered char into the framebuffer. 116 | /// Updates `self.x_pos`. 117 | fn write_rendered_char(&mut self, rendered_char: RasterizedChar) { 118 | for (y, row) in rendered_char.raster().iter().enumerate() { 119 | for (x, byte) in row.iter().enumerate() { 120 | self.write_pixel(self.x_pos + x, self.y_pos + y, *byte); 121 | } 122 | } 123 | self.x_pos += rendered_char.width() + LETTER_SPACING; 124 | } 125 | 126 | fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) { 127 | let pixel_offset = y * self.info.stride + x; 128 | 129 | let r = intensity; 130 | let g = [intensity, 0][self.level]; 131 | let b = [intensity / 2, 0][self.level]; 132 | let color = match self.info.pixel_format { 133 | PixelFormat::Rgb => [r, g, b, 0], 134 | PixelFormat::Bgr => [b, g, r, 0], 135 | PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0], 136 | other => { 137 | // set a supported (but invalid) pixel format before panicking to avoid a double 138 | // panic; it might not be readable though 139 | self.info.pixel_format = PixelFormat::Rgb; 140 | panic!("pixel format {:?} not supported in logger", other) 141 | } 142 | }; 143 | let bytes_per_pixel = self.info.bytes_per_pixel; 144 | let byte_offset = pixel_offset * bytes_per_pixel; 145 | self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)] 146 | .copy_from_slice(&color[..bytes_per_pixel]); 147 | let _ = unsafe { ptr::read_volatile(&self.framebuffer[byte_offset]) }; 148 | } 149 | } 150 | 151 | unsafe impl Send for FrameBufferWriter {} 152 | unsafe impl Sync for FrameBufferWriter {} 153 | 154 | impl fmt::Write for FrameBufferWriter { 155 | fn write_str(&mut self, s: &str) -> fmt::Result { 156 | // for c in s.chars() { 157 | // self.write_char(c); 158 | // } 159 | 160 | Ok(()) 161 | } 162 | } 163 | use alloc::{slice, vec::Vec}; 164 | 165 | use crate::interrupts::global_time_ms; 166 | // extern crate alloc; 167 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 168 | #[repr(C)] 169 | pub struct RGBA { 170 | pub r: u8, 171 | pub g: u8, 172 | pub b: u8, 173 | pub a: u8, 174 | } 175 | #[derive(Clone)] 176 | #[repr(C)] 177 | pub struct FB { 178 | pub pixels: Vec, 179 | pub backbuffer: Vec, 180 | pub w: usize, 181 | pub h: usize, 182 | } 183 | 184 | #[repr(C)] 185 | pub struct FBShare<'a> { 186 | pub pixels: &'a mut [RGBA], 187 | pub w: usize, 188 | pub h: usize, 189 | } 190 | impl FB { 191 | pub fn new(info: &FrameBufferInfo) -> Self { 192 | let w = info.width; 193 | let h = info.height; 194 | let mut pixels = Vec::with_capacity(w * h); 195 | let mut backbuffer = Vec::with_capacity(w * h); 196 | for y in 0..h { 197 | for x in 0..w { 198 | pixels.push(RGBA { 199 | r: x as u8, 200 | g: y as u8, 201 | b: 0, 202 | a: 0, 203 | }); 204 | backbuffer.push(pixels[pixels.len() - 1]); 205 | } 206 | } 207 | FB { 208 | pixels, 209 | w, 210 | h, 211 | backbuffer, 212 | } 213 | } 214 | 215 | pub fn update(&mut self, vec: *mut RGBA, w: usize, h: usize) { 216 | self.pixels = unsafe { Vec::from_raw_parts(vec, h * w, w * h) }; 217 | self.w = w; 218 | self.h = h; 219 | } 220 | 221 | pub fn share(&mut self) -> FBShare { 222 | FBShare { 223 | pixels: &mut self.pixels[..], 224 | w: self.w, 225 | h: self.h, 226 | } 227 | } 228 | 229 | pub fn flush(&mut self, framebuffer: &mut [u8], info: &FrameBufferInfo) { 230 | let mut todraw = &self.pixels; 231 | 232 | let start = global_time_ms(); 233 | 234 | // match info.pixel_format { 235 | // PixelFormat::Bgr => { 236 | // for (idx, &i) in self.pixels.iter().enumerate() { 237 | // self.backbuffer[idx].r = i.b; 238 | // self.backbuffer[idx].g = i.g; 239 | // self.backbuffer[idx].b = i.r; 240 | // } 241 | // // todraw = &self.backbuffer; 242 | // } 243 | // _ => {} 244 | // } 245 | 246 | // let time0 = get_time_ms() - start; 247 | 248 | // let start = get_time_ms(); 249 | framebuffer.copy_from_slice(unsafe { 250 | slice::from_raw_parts(todraw.as_ptr() as *const u8, framebuffer.len()) 251 | }); 252 | // log::info!("step 0 FB {}ms", time0); 253 | // log::info!("step 1 FB {}ms", get_time_ms() - start); 254 | // for y in 0..self.h { 255 | // for x in 0..self.w { 256 | // let RGBA { r, g, b, a } = self.pixels[x + self.w * y]; 257 | // let pixel_offset = y * info.stride + x; 258 | 259 | // let color = match info.pixel_format { 260 | // PixelFormat::Rgb => [r, g, b, 0], 261 | // PixelFormat::Bgr => [b, g, r, 0], 262 | // PixelFormat::U8 => [if (g + b + r) as usize > 200 { 0xf } else { 0 }, 0, 0, 0], 263 | // other => { 264 | // // set a supported (but invalid) pixel format before panicking to avoid a double 265 | // // panic; it might not be readable though 266 | // // info.pixel_format = PixelFormat::Rgb; 267 | // panic!("pixel format {:?} not supported in logger", other) 268 | // } 269 | // }; 270 | // let bytes_per_pixel = info.bytes_per_pixel; 271 | // let byte_offset = pixel_offset * bytes_per_pixel; 272 | // framebuffer[byte_offset..(byte_offset + bytes_per_pixel)] 273 | // .copy_from_slice(&color[..bytes_per_pixel]); 274 | // } 275 | // } 276 | } 277 | 278 | // pub fn set(x: usize, y: usize) 279 | } 280 | -------------------------------------------------------------------------------- /bootloader/kernel/src/gdt.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use x86_64::structures::tss::TaskStateSegment; 3 | use x86_64::VirtAddr; 4 | 5 | use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; 6 | 7 | lazy_static! { 8 | static ref GDT: (GlobalDescriptorTable, Selectors) = { 9 | let mut gdt = GlobalDescriptorTable::new(); 10 | let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); 11 | let data_selector = gdt.add_entry(Descriptor::kernel_data_segment()); 12 | let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); 13 | ( 14 | gdt, 15 | Selectors { 16 | code_selector, 17 | tss_selector, 18 | data_selector, 19 | }, 20 | ) 21 | }; 22 | } 23 | struct Selectors { 24 | code_selector: SegmentSelector, 25 | data_selector: SegmentSelector, 26 | tss_selector: SegmentSelector, 27 | } 28 | pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; 29 | 30 | lazy_static! { 31 | static ref TSS: TaskStateSegment = { 32 | let mut tss = TaskStateSegment::new(); 33 | tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { 34 | const STACK_SIZE: usize = 4*1024 ; //4096 * 5; 35 | static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; 36 | 37 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 38 | let stack_end = stack_start + STACK_SIZE; 39 | stack_end 40 | }; 41 | tss 42 | }; 43 | } 44 | pub fn init() { 45 | use x86_64::instructions::segmentation::{Segment, CS, DS, ES, SS}; 46 | use x86_64::instructions::tables::load_tss; 47 | GDT.0.load(); 48 | unsafe { 49 | // CS::set_reg(code_selector); 50 | DS::set_reg(GDT.1.data_selector); 51 | ES::set_reg(GDT.1.data_selector); 52 | SS::set_reg(GDT.1.data_selector); 53 | CS::set_reg(GDT.1.code_selector); 54 | load_tss(GDT.1.tss_selector); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /bootloader/kernel/src/globals.rs: -------------------------------------------------------------------------------- 1 | use crossbeam::atomic::AtomicCell; 2 | 3 | pub static INPUT: GLOBAL = GLOBAL::new(Input::new()); 4 | pub struct GLOBAL(AtomicCell); 5 | 6 | const HISTORY_SIZE: usize = 64; 7 | impl GLOBAL { 8 | pub const fn new(t: T) -> Self { 9 | Self(AtomicCell::new(t)) 10 | } 11 | ///Might lose data if multiple thread calls it simultaneously 12 | pub fn update(&self, func: F) 13 | where 14 | F: FnOnce(&mut T), 15 | { 16 | let mut v = self.0.load(); 17 | func(&mut v); 18 | self.0.store(v) 19 | } 20 | pub fn read(&self) -> T { 21 | self.0.load() 22 | } 23 | } 24 | #[repr(C)] 25 | #[derive(Clone, Debug, Copy)] 26 | pub struct InputEvent { 27 | pub trigger: bool, 28 | pub key: usize, 29 | } 30 | #[repr(C)] 31 | #[derive(Clone, Debug, Copy)] 32 | pub struct Input { 33 | pub mouse_x: usize, 34 | pub mouse_y: usize, 35 | pub keys: [KeyState; 1024], 36 | pub history_last_index: usize, 37 | pub history_ring: [InputEvent; HISTORY_SIZE], 38 | } 39 | 40 | impl Input { 41 | pub const fn new() -> Self { 42 | Self { 43 | mouse_x: 0, 44 | mouse_y: 0, 45 | keys: [KeyState::Off; 1024], 46 | history_last_index: 0, 47 | history_ring: [InputEvent { 48 | trigger: false, 49 | key: 0, 50 | }; HISTORY_SIZE], 51 | } 52 | } 53 | pub fn step(&mut self) { 54 | for k in self.keys.iter_mut() { 55 | k.step(); 56 | } 57 | } 58 | } 59 | 60 | #[repr(u8)] 61 | #[derive(Clone, Debug, Copy)] 62 | pub enum KeyState { 63 | ///Key is not pressed now 64 | Off = 0, 65 | ///Key is not pressed now, but was last frame 66 | OffFromOn = 1, 67 | ///Key is not pressed now, it was not pressed last frame either, but was during frame (sequence Off -> On -> Off) 68 | OffTransientOn = 2, 69 | OnFromOff = 128, 70 | OnTransientOff = 129, 71 | On = 130, 72 | } 73 | impl Default for KeyState { 74 | fn default() -> Self { 75 | KeyState::Off 76 | } 77 | } 78 | 79 | impl Input { 80 | pub fn handle_incoming_state(&mut self, key: usize, b: bool) { 81 | self.history_last_index += 1; 82 | self.history_ring[self.history_last_index % HISTORY_SIZE] = InputEvent { trigger: b, key }; 83 | self.keys[key].handle_incoming_state(b); 84 | } 85 | } 86 | 87 | impl KeyState { 88 | pub fn handle_incoming_state(&mut self, b: bool) { 89 | *self = match (*self, b) { 90 | (KeyState::Off, true) => KeyState::OnFromOff, 91 | (KeyState::On, false) => KeyState::OffFromOn, 92 | (KeyState::OffFromOn, true) => KeyState::OnTransientOff, 93 | (KeyState::OnFromOff, false) => KeyState::OffTransientOn, 94 | (_, false) => KeyState::Off, 95 | (_, true) => KeyState::On, 96 | } 97 | } 98 | ///To call every kernel loop 99 | pub fn step(&mut self) { 100 | *self = match *self { 101 | KeyState::OffTransientOn => KeyState::Off, 102 | KeyState::OffFromOn => KeyState::Off, 103 | KeyState::OnFromOff => KeyState::On, 104 | KeyState::OnTransientOff => KeyState::On, 105 | _ => *self, 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /bootloader/kernel/src/interrupts.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::AtomicU64; 2 | use core::sync::atomic::Ordering; 3 | use core::task::Waker; 4 | 5 | use crate::gdt; 6 | use crate::globals::GLOBAL; 7 | use crate::ioapic; 8 | 9 | use conquer_once::spin::OnceCell; 10 | use crossbeam::queue::ArrayQueue; 11 | use lazy_static::lazy_static; 12 | use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; 13 | lazy_static! { 14 | static ref IDT: InterruptDescriptorTable = { 15 | let mut idt = InterruptDescriptorTable::new(); 16 | idt.breakpoint.set_handler_fn(breakpoint_handler); 17 | idt.alignment_check.set_handler_fn(alignment_check); 18 | 19 | 20 | idt.invalid_opcode.set_handler_fn(invalid_opcode); 21 | idt.bound_range_exceeded.set_handler_fn(bound_range_exceeded); 22 | idt.general_protection_fault.set_handler_fn(general_protection_fault); 23 | unsafe { 24 | idt.double_fault.set_handler_fn(double_fault_handler) 25 | .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); // new 26 | } 27 | unsafe { 28 | idt.overflow.set_handler_fn(overflow_handler) 29 | .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); // new 30 | } 31 | 32 | idt.invalid_tss.set_handler_fn(invalid_tss_handler); 33 | idt.segment_not_present.set_handler_fn(segment_not_present_handler); 34 | idt.stack_segment_fault.set_handler_fn(stack_segment_fault_handler); 35 | idt.alignment_check.set_handler_fn(alignment_check_handler); 36 | 37 | 38 | 39 | idt.page_fault.set_handler_fn(page_fault_handler); 40 | 41 | 42 | for i in 32..=255{ 43 | idt[i].set_handler_fn(generic_handler); 44 | } 45 | 46 | idt[48].set_handler_fn(lapic_timer); 47 | idt[49].set_handler_fn(lapic_timer2); 48 | 49 | idt[50+0].set_handler_fn(ioapic_handler_0); 50 | idt[50+1].set_handler_fn(ioapic_handler_1); 51 | idt[50+2].set_handler_fn(ioapic_handler_2); 52 | idt[50+3].set_handler_fn(ioapic_handler_3); 53 | idt[50+4].set_handler_fn(ioapic_handler_4); 54 | idt[50+5].set_handler_fn(ioapic_handler_5); 55 | idt[50+6].set_handler_fn(ioapic_handler_6); 56 | idt[50+7].set_handler_fn(ioapic_handler_7); 57 | idt[50+8].set_handler_fn(ioapic_handler_8); 58 | idt[50+9].set_handler_fn(ioapic_handler_9); 59 | idt[50+10].set_handler_fn(ioapic_handler_10); 60 | idt[50+11].set_handler_fn(ioapic_handler_11); 61 | idt[50+12].set_handler_fn(ioapic_handler_12); 62 | idt[50+13].set_handler_fn(ioapic_handler_13); 63 | idt[50+14].set_handler_fn(ioapic_handler_14); 64 | idt[50+15].set_handler_fn(ioapic_handler_15); 65 | idt[50+16].set_handler_fn(ioapic_handler_16); 66 | idt[50+17].set_handler_fn(ioapic_handler_17); 67 | idt[50+18].set_handler_fn(ioapic_handler_18); 68 | idt[50+19].set_handler_fn(ioapic_handler_19); 69 | idt[50+20].set_handler_fn(ioapic_handler_20); 70 | idt[50+21].set_handler_fn(ioapic_handler_21); 71 | idt[50+22].set_handler_fn(ioapic_handler_22); 72 | idt[50+23].set_handler_fn(ioapic_handler_23); 73 | 74 | idt 75 | }; 76 | } 77 | 78 | pub static TIME_MS: AtomicU64 = AtomicU64::new(0); 79 | extern "x86-interrupt" fn lapic_timer(stack_frame: InterruptStackFrame) { 80 | unsafe { 81 | crate::local_apic::LOCAL_APIC.get().unwrap().eoi(); 82 | }; 83 | let ms = 1 + TIME_MS.fetch_add(1, Ordering::Relaxed); 84 | 85 | let mut arr = WAKERS.lock(); 86 | for w in arr.iter_mut() { 87 | if let Some(waker) = w { 88 | waker.wake(); 89 | } 90 | *w = None; 91 | } 92 | WAKER.wake(); 93 | } 94 | 95 | pub fn global_time_ms() -> u64 { 96 | TIME_MS.load(Ordering::Relaxed) 97 | } 98 | 99 | pub fn wait_block(ms: u64) { 100 | let current = TIME_MS.load(Ordering::Relaxed); 101 | loop { 102 | if TIME_MS.load(Ordering::Relaxed) > current + ms { 103 | break; 104 | } else { 105 | x86_64::instructions::nop(); 106 | x86_64::instructions::nop(); 107 | x86_64::instructions::nop(); 108 | x86_64::instructions::nop(); 109 | x86_64::instructions::nop(); 110 | x86_64::instructions::nop(); 111 | x86_64::instructions::nop(); 112 | x86_64::instructions::nop(); 113 | } 114 | } 115 | } 116 | pub async fn a_sleep(ms: u64) { 117 | let timer = Timer::new(ms); 118 | timer.await; 119 | } 120 | pub struct Timer { 121 | stop: u64, 122 | } 123 | 124 | impl Timer { 125 | pub fn new(ms: u64) -> Self { 126 | Timer { 127 | stop: global_time_ms() + ms, 128 | } 129 | } 130 | } 131 | impl futures::future::Future for Timer { 132 | type Output = (); 133 | fn poll( 134 | self: core::pin::Pin<&mut Self>, 135 | cx: &mut core::task::Context<'_>, 136 | ) -> core::task::Poll { 137 | if global_time_ms() >= self.stop { 138 | return core::task::Poll::Ready(()); 139 | } 140 | 141 | let id = add_waker(&cx.waker()); 142 | // WAKER.register(&cx.waker()); 143 | 144 | if global_time_ms() >= self.stop { 145 | core::task::Poll::Ready(()) 146 | } else { 147 | core::task::Poll::Pending 148 | } 149 | } 150 | } 151 | 152 | use spin::Mutex; 153 | 154 | type WAKERS_T = [Option; 128]; 155 | lazy_static! { 156 | pub static ref WAKERS: Mutex = Mutex::new([(); 128].map(|_| None)); 157 | } 158 | 159 | pub fn add_waker(waker: &Waker) -> u64 { 160 | let mut arr = WAKERS.lock(); 161 | for (index, aw) in arr.iter_mut().enumerate() { 162 | if aw.is_none() { 163 | let w = AtomicWaker::new(); 164 | w.register(waker); 165 | *aw = Some(w); 166 | return index as u64; 167 | } 168 | } 169 | 170 | 0 171 | } 172 | 173 | use futures::task::AtomicWaker; 174 | 175 | static WAKER: AtomicWaker = AtomicWaker::new(); 176 | 177 | extern "x86-interrupt" fn lapic_timer2(stack_frame: InterruptStackFrame) { 178 | log::info!("timer2"); 179 | 180 | unsafe { 181 | // crate::local_apic::LocalApic.get().unwrap().eoi(); 182 | }; 183 | } 184 | pub fn init_idt() { 185 | IDT.load(); 186 | } 187 | extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { 188 | log::error!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); 189 | } 190 | 191 | extern "x86-interrupt" fn overflow_handler(stack_frame: InterruptStackFrame) { 192 | log::error!("EXCEPTION: overflow_handler\n{:#?}", stack_frame); 193 | panic!(""); 194 | } 195 | extern "x86-interrupt" fn invalid_tss_handler(stack_frame: InterruptStackFrame, error_code: u64) { 196 | log::error!("EXCEPTION: invalid_tss {}\n{:#?}", error_code, stack_frame); 197 | panic!(""); 198 | } 199 | extern "x86-interrupt" fn segment_not_present_handler( 200 | stack_frame: InterruptStackFrame, 201 | error_code: u64, 202 | ) { 203 | log::error!( 204 | "EXCEPTION: segment_not_present {}\n{:#?}", 205 | error_code, 206 | stack_frame 207 | ); 208 | panic!(""); 209 | } 210 | extern "x86-interrupt" fn stack_segment_fault_handler( 211 | stack_frame: InterruptStackFrame, 212 | error_code: u64, 213 | ) { 214 | log::error!( 215 | "EXCEPTION: stack_segment_fault {}\n{:#?}", 216 | error_code, 217 | stack_frame 218 | ); 219 | panic!(""); 220 | } 221 | extern "x86-interrupt" fn alignment_check_handler( 222 | stack_frame: InterruptStackFrame, 223 | error_code: u64, 224 | ) { 225 | log::error!( 226 | "EXCEPTION: alignment_check {}\n{:#?}", 227 | error_code, 228 | stack_frame 229 | ); 230 | panic!(""); 231 | } 232 | extern "x86-interrupt" fn double_fault_handler( 233 | stack_frame: InterruptStackFrame, 234 | _error_code: u64, 235 | ) -> ! { 236 | panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); 237 | } 238 | 239 | extern "x86-interrupt" fn alignment_check(stack_frame: InterruptStackFrame, _error_code: u64) { 240 | panic!("EXCEPTION: alignment_check{:#?}", stack_frame); 241 | } 242 | extern "x86-interrupt" fn invalid_opcode(stack_frame: InterruptStackFrame) { 243 | panic!("EXCEPTION: invalid_opcode{:#?}", stack_frame); 244 | } 245 | extern "x86-interrupt" fn bound_range_exceeded(stack_frame: InterruptStackFrame) { 246 | panic!("EXCEPTION: bound_range_exceeded{:#?}", stack_frame); 247 | } 248 | 249 | extern "x86-interrupt" fn general_protection_fault( 250 | stack_frame: InterruptStackFrame, 251 | _error_code: u64, 252 | ) { 253 | panic!( 254 | "EXCEPTION: general_protection_fault {}\n{:#?}", 255 | _error_code, stack_frame 256 | ); 257 | } 258 | 259 | use x86_64::structures::idt::PageFaultErrorCode; 260 | 261 | extern "x86-interrupt" fn page_fault_handler( 262 | stack_frame: InterruptStackFrame, 263 | error_code: PageFaultErrorCode, 264 | ) { 265 | use x86_64::registers::control::Cr2; 266 | 267 | log::error!("EXCEPTION: PAGE FAULT"); 268 | log::error!("Accessed Address: {:?}", Cr2::read()); 269 | log::error!("Error Code: {:?}", error_code); 270 | // log::error!("{:#?}", stack_frame); 271 | 272 | panic!("EXCEPTION: PAGE FAULT\n{:#?}", stack_frame); 273 | } 274 | 275 | extern "x86-interrupt" fn ioapic_handler_0(stack_frame: InterruptStackFrame) { 276 | log::info!("______ioapic_handler_0_____"); 277 | } 278 | extern "x86-interrupt" fn ioapic_handler_1(stack_frame: InterruptStackFrame) { 279 | log::info!("______ioapic_handler_1_____"); 280 | let ioa = ioapic::IO_APIC_0.get().expect("IoApic0"); 281 | let n = ioa.read_redtlb(1); 282 | let mut red = ioapic::RedTbl::new(n); 283 | log::info!("{:?}", red); 284 | // let stored = red.store(); 285 | 286 | unsafe { 287 | crate::local_apic::LOCAL_APIC.get().unwrap().eoi(); 288 | }; 289 | } 290 | extern "x86-interrupt" fn ioapic_handler_2(stack_frame: InterruptStackFrame) { 291 | // log::info!("______ioapic_handler_2_____"); 292 | 293 | let ioa = ioapic::IO_APIC_0.get().expect("IoApic0"); 294 | let n = ioa.read_redtlb(2); 295 | let mut red = ioapic::RedTbl::new(n); 296 | 297 | // log::info!("{:?}", red); 298 | let stored = red.store(); 299 | ioa.write_redtlb(2, stored); 300 | unsafe { 301 | crate::local_apic::LOCAL_APIC.get().unwrap().eoi(); 302 | }; 303 | } 304 | extern "x86-interrupt" fn ioapic_handler_3(stack_frame: InterruptStackFrame) { 305 | log::info!("______ioapic_handler_3_____"); 306 | } 307 | extern "x86-interrupt" fn ioapic_handler_4(stack_frame: InterruptStackFrame) { 308 | log::info!("______ioapic_handler_4_____"); 309 | } 310 | extern "x86-interrupt" fn ioapic_handler_5(stack_frame: InterruptStackFrame) { 311 | log::info!("______ioapic_handler_5_____"); 312 | } 313 | extern "x86-interrupt" fn ioapic_handler_6(stack_frame: InterruptStackFrame) { 314 | log::info!("______ioapic_handler_6_____"); 315 | } 316 | extern "x86-interrupt" fn ioapic_handler_7(stack_frame: InterruptStackFrame) { 317 | log::info!("______ioapic_handler_7_____"); 318 | } 319 | extern "x86-interrupt" fn ioapic_handler_8(stack_frame: InterruptStackFrame) { 320 | log::info!("______ioapic_handler_8_____"); 321 | } 322 | extern "x86-interrupt" fn ioapic_handler_9(stack_frame: InterruptStackFrame) { 323 | log::info!("______ioapic_handler_9_____"); 324 | } 325 | extern "x86-interrupt" fn ioapic_handler_10(stack_frame: InterruptStackFrame) { 326 | log::info!("______ioapic_handler_10_____"); 327 | unsafe { 328 | crate::local_apic::LOCAL_APIC.get().unwrap().eoi(); 329 | }; 330 | } 331 | extern "x86-interrupt" fn ioapic_handler_11(stack_frame: InterruptStackFrame) { 332 | log::info!("______ioapic_handler_11_____"); 333 | 334 | unsafe { 335 | crate::local_apic::LOCAL_APIC.get().unwrap().eoi(); 336 | }; 337 | } 338 | extern "x86-interrupt" fn ioapic_handler_12(stack_frame: InterruptStackFrame) { 339 | log::info!("______ioapic_handler_12_____"); 340 | } 341 | extern "x86-interrupt" fn ioapic_handler_13(stack_frame: InterruptStackFrame) { 342 | log::info!("______ioapic_handler_13_____"); 343 | } 344 | extern "x86-interrupt" fn ioapic_handler_14(stack_frame: InterruptStackFrame) { 345 | log::info!("______ioapic_handler_14_____"); 346 | } 347 | extern "x86-interrupt" fn ioapic_handler_15(stack_frame: InterruptStackFrame) { 348 | log::info!("______ioapic_handler_15_____"); 349 | } 350 | extern "x86-interrupt" fn ioapic_handler_16(stack_frame: InterruptStackFrame) { 351 | log::info!("______ioapic_handler_16_____"); 352 | } 353 | extern "x86-interrupt" fn ioapic_handler_17(stack_frame: InterruptStackFrame) { 354 | log::info!("______ioapic_handler_17_____"); 355 | } 356 | extern "x86-interrupt" fn ioapic_handler_18(stack_frame: InterruptStackFrame) { 357 | log::info!("______ioapic_handler_18_____"); 358 | } 359 | extern "x86-interrupt" fn ioapic_handler_19(stack_frame: InterruptStackFrame) { 360 | log::info!("______ioapic_handler_19_____"); 361 | } 362 | extern "x86-interrupt" fn ioapic_handler_20(stack_frame: InterruptStackFrame) { 363 | log::info!("______ioapic_handler_20_____"); 364 | } 365 | extern "x86-interrupt" fn ioapic_handler_21(stack_frame: InterruptStackFrame) { 366 | log::info!("______ioapic_handler_21_____"); 367 | } 368 | extern "x86-interrupt" fn ioapic_handler_22(stack_frame: InterruptStackFrame) { 369 | log::info!("______ioapic_handler_22_____"); 370 | } 371 | extern "x86-interrupt" fn ioapic_handler_23(stack_frame: InterruptStackFrame) { 372 | log::info!("______ioapic_handler_23_____"); 373 | } 374 | extern "x86-interrupt" fn generic_handler(stack_frame: InterruptStackFrame) { 375 | log::info!("______generic_handler_____"); 376 | } 377 | -------------------------------------------------------------------------------- /bootloader/kernel/src/ioapic.rs: -------------------------------------------------------------------------------- 1 | use core::intrinsics::{volatile_load, volatile_store}; 2 | 3 | use conquer_once::spin::OnceCell; 4 | use x86_64::{structures::DescriptorTablePointer, PhysAddr, VirtAddr}; 5 | 6 | use crate::phys_to_virt; 7 | pub static IO_APIC_0: OnceCell = OnceCell::uninit(); 8 | 9 | pub struct IoApic { 10 | virt_address: VirtAddr, 11 | global_system_int: u32, 12 | id: u8, 13 | } 14 | 15 | pub const IOAPICID: u32 = 0; 16 | pub const IOAPICVER: u32 = 1; 17 | impl IoApic { 18 | pub fn init(info: &acpi::platform::interrupt::IoApic) -> &Self { 19 | let this = IO_APIC_0.get_or_init(move || Self { 20 | id: info.id, 21 | virt_address: phys_to_virt(PhysAddr::new(info.address as u64)), 22 | global_system_int: info.global_system_interrupt_base, 23 | }); 24 | this 25 | } 26 | pub unsafe fn set_sel(&self, reg: u32) { 27 | volatile_store(self.virt_address.as_u64() as *mut u32, reg); 28 | } 29 | pub fn read(&self, reg: u32) -> u32 { 30 | unsafe { 31 | self.set_sel(reg); 32 | let sec: VirtAddr = self.virt_address + 0x10_u64; 33 | volatile_load(sec.as_u64() as *const u32) 34 | } 35 | } 36 | pub fn write(&self, reg: u32, value: u32) { 37 | unsafe { 38 | self.set_sel(reg); 39 | let sec: VirtAddr = self.virt_address + 0x10_u64; 40 | volatile_store(sec.as_u64() as *mut u32, value); 41 | } 42 | } 43 | 44 | pub fn read_redtlb(&self, index: u32) -> u64 { 45 | let low = self.read(0x10 + 2 * index) as u64; 46 | let high = self.read(0x10 + 2 * index + 1) as u64; 47 | (high << 32) + low 48 | } 49 | pub fn write_redtlb(&self, index: u32, redtlb: u64) { 50 | let low = self.write(0x10 + 2 * index, (redtlb & 0xffff) as u32); 51 | let high = self.write(0x10 + 2 * index + 1, (redtlb >> 32) as u32); 52 | } 53 | } 54 | #[derive(Clone, Debug)] 55 | pub struct RedTbl { 56 | pub vector: u8, 57 | pub delivery_mode: u8, 58 | pub destination_mode: bool, 59 | pub delivery_status: bool, 60 | pub pin_polarity: bool, 61 | pub remote_irr: bool, 62 | pub trigger_mode: bool, 63 | pub mask: bool, 64 | pub destination: u8, 65 | } 66 | 67 | impl RedTbl { 68 | pub fn new(n: u64) -> Self { 69 | let mut c = n; 70 | let vector = (c & 0xff) as u8; 71 | c >>= 8; 72 | let delivery_mode = (c & 0b11) as u8; 73 | c >>= 2; 74 | let destination_mode = c & 0b1 != 0; 75 | c >>= 1; 76 | let delivery_status = c & 0b1 != 0; 77 | c >>= 1; 78 | let pin_polarity = c & 0b1 != 0; 79 | c >>= 1; 80 | let remote_irr = c & 0b1 != 0; 81 | c >>= 1; 82 | let trigger_mode = c & 0b1 != 0; 83 | c >>= 1; 84 | let mask = c & 0b1 != 0; 85 | c >>= 1; 86 | let destination = (n >> 56) as u8; 87 | Self { 88 | vector, 89 | delivery_mode, 90 | destination_mode, 91 | delivery_status, 92 | pin_polarity, 93 | remote_irr, 94 | trigger_mode, 95 | mask, 96 | destination, 97 | } 98 | } 99 | pub fn store(&self) -> u64 { 100 | let &Self { 101 | vector, 102 | delivery_mode, 103 | destination_mode, 104 | delivery_status, 105 | pin_polarity, 106 | remote_irr, 107 | trigger_mode, 108 | mask, 109 | destination, 110 | } = self; 111 | 112 | let mut r = 0_u64; 113 | r += (destination as u64) << 56; 114 | r += vector as u64; 115 | r += (delivery_mode as u64) << 8; 116 | r += if destination_mode { 1 } else { 0 } << 10; 117 | r += if delivery_status { 1 } else { 0 } << 11; 118 | r += if pin_polarity { 1 } else { 0 } << 12; 119 | r += if remote_irr { 1 } else { 0 } << 13; 120 | r += if trigger_mode { 1 } else { 0 } << 14; 121 | r += if mask { 1 } else { 0 } << 15; 122 | r 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /bootloader/kernel/src/local_apic.rs: -------------------------------------------------------------------------------- 1 | use conquer_once::spin::OnceCell; 2 | use core::intrinsics::{volatile_load, volatile_store}; 3 | use raw_cpuid::{CpuId, CpuIdResult}; 4 | use x86_64::{ 5 | registers::model_specific::Msr, 6 | structures::paging::{FrameAllocator, Size4KiB}, 7 | PhysAddr, VirtAddr, 8 | }; 9 | 10 | use crate::phys_to_virt; 11 | 12 | pub fn cpuid() -> Option { 13 | //TODO: ensure that CPUID exists! https://wiki.osdev.org/CPUID#Checking_CPUID_availability 14 | Some(CpuId::with_cpuid_fn(|a, c| { 15 | let result = unsafe { core::arch::x86_64::__cpuid_count(a, c) }; 16 | CpuIdResult { 17 | eax: result.eax, 18 | ebx: result.ebx, 19 | ecx: result.ecx, 20 | edx: result.edx, 21 | } 22 | })) 23 | } 24 | 25 | pub static LOCAL_APIC: OnceCell = OnceCell::uninit(); 26 | 27 | pub struct LocalApic { 28 | pub virt_address: VirtAddr, 29 | } 30 | 31 | impl LocalApic { 32 | pub unsafe fn init(local_apic_address: PhysAddr) -> &'static Self { 33 | disable_pic(); 34 | 35 | let virtaddr: VirtAddr = phys_to_virt(local_apic_address); 36 | let this = LOCAL_APIC.get_or_init(|| Self { 37 | virt_address: virtaddr, 38 | }); 39 | log::info!("LocalApic {:?}", virtaddr); 40 | 41 | let mut msr = Msr::new(0x1B); 42 | let r = msr.read(); 43 | msr.write(r | (1 << 11)); 44 | 45 | this.write(0xF0, this.read(0xF0) | 0x1FF); 46 | this 47 | } 48 | 49 | unsafe fn read(&self, reg: u32) -> u32 { 50 | volatile_load((self.virt_address.as_u64() + reg as u64) as *const u32) 51 | } 52 | 53 | unsafe fn write(&self, reg: u32, value: u32) { 54 | volatile_store((self.virt_address.as_u64() + reg as u64) as *mut u32, value); 55 | } 56 | 57 | pub fn id(&self) -> u32 { 58 | unsafe { self.read(0x20) } 59 | } 60 | 61 | pub fn version(&self) -> u32 { 62 | unsafe { self.read(0x30) } 63 | } 64 | 65 | pub fn icr(&self) -> u64 { 66 | unsafe { (self.read(0x310) as u64) << 32 | self.read(0x300) as u64 } 67 | } 68 | 69 | pub fn set_icr(&self, value: u64) { 70 | unsafe { 71 | const PENDING: u32 = 1 << 12; 72 | while self.read(0x300) & PENDING == PENDING { 73 | core::hint::spin_loop(); 74 | } 75 | self.write(0x310, (value >> 32) as u32); 76 | self.write(0x300, value as u32); 77 | while self.read(0x300) & PENDING == PENDING { 78 | core::hint::spin_loop(); 79 | } 80 | } 81 | } 82 | 83 | pub fn ipi(&self, apic_id: usize) { 84 | let mut icr = 0x4040; 85 | 86 | icr |= (apic_id as u64) << 56; 87 | 88 | self.set_icr(icr); 89 | } 90 | // Not used just yet, but allows triggering an NMI to another processor. 91 | pub fn ipi_nmi(&self, apic_id: u32) { 92 | let shift = { 56 }; 93 | self.set_icr((u64::from(apic_id) << shift) | (1 << 14) | (0b100 << 8)); 94 | } 95 | 96 | pub unsafe fn eoi(&self) { 97 | self.write(0xB0, 0); 98 | } 99 | /// Reads the Error Status Register. 100 | pub unsafe fn esr(&self) -> u32 { 101 | self.write(0x280, 0); 102 | self.read(0x280) 103 | } 104 | pub unsafe fn lvt_timer(&self) -> u32 { 105 | self.read(0x320) 106 | } 107 | pub unsafe fn set_lvt_timer(&self, value: u32) { 108 | self.write(0x320, value); 109 | } 110 | pub unsafe fn init_count(&self) -> u32 { 111 | self.read(0x380) 112 | } 113 | pub unsafe fn set_init_count(&self, initial_count: u32) { 114 | self.write(0x380, initial_count); 115 | } 116 | pub unsafe fn cur_count(&self) -> u32 { 117 | self.read(0x390) 118 | } 119 | pub unsafe fn div_conf(&self) -> u32 { 120 | self.read(0x3E0) 121 | } 122 | pub unsafe fn set_div_conf(&self, div_conf: u32) { 123 | self.write(0x3E0, div_conf); 124 | } 125 | pub unsafe fn lvt_error(&self) -> u32 { 126 | self.read(0x370) 127 | } 128 | pub unsafe fn set_lvt_error(&self, lvt_error: u32) { 129 | self.write(0x370, lvt_error); 130 | } 131 | unsafe fn setup_error_int(&self) { 132 | let vector = 49u32; 133 | self.set_lvt_error(vector); 134 | } 135 | } 136 | pub unsafe fn disable_pic() { 137 | use x86_64::instructions::port::Port; 138 | let mut wait_port: Port = Port::new(0x80); 139 | let mut wait = || { 140 | wait_port.write(0); 141 | }; 142 | let mut p0c: Port = Port::new(0x20); 143 | let mut p0d: Port = Port::new(0x21); 144 | let mut p1c: Port = Port::new(0xA0); 145 | let mut p1d: Port = Port::new(0xA1); 146 | p0c.write(0x11); 147 | wait(); 148 | p1c.write(0x11); 149 | wait(); 150 | 151 | //OFFSET 152 | p0d.write(0xf0); 153 | wait(); 154 | p1d.write(0xf8); 155 | wait(); 156 | //CHAINING 157 | p0d.write(0x4); 158 | wait(); 159 | p1d.write(0x2); 160 | wait(); 161 | //ICW4_8086 MODE 162 | p0d.write(0x1); 163 | wait(); 164 | p1d.write(0x1); 165 | wait(); 166 | //CLOSE 167 | p0d.write(0xff); 168 | wait(); 169 | p1d.write(0xff); 170 | wait(); 171 | } 172 | -------------------------------------------------------------------------------- /bootloader/kernel/src/logger.rs: -------------------------------------------------------------------------------- 1 | use crate::{framebuffer::FrameBufferWriter, serial::SerialPort}; 2 | use bootloader_api::info::FrameBufferInfo; 3 | use bootloader_boot_config::LevelFilter; 4 | use conquer_once::spin::OnceCell; 5 | use core::fmt::Write; 6 | use log::Level; 7 | use spinning_top::Spinlock; 8 | 9 | /// The global logger instance used for the `log` crate. 10 | pub static LOGGER: OnceCell = OnceCell::uninit(); 11 | 12 | /// A logger instance protected by a spinlock. 13 | pub struct LockedLogger { 14 | framebuffer: Option>, 15 | serial: Option>, 16 | } 17 | 18 | impl LockedLogger { 19 | /// Create a new instance that logs to the given framebuffer. 20 | pub fn new( 21 | framebuffer: &'static mut [u8], 22 | info: FrameBufferInfo, 23 | frame_buffer_logger_status: bool, 24 | serial_logger_status: bool, 25 | ) -> Self { 26 | let framebuffer = match frame_buffer_logger_status { 27 | true => Some(Spinlock::new(FrameBufferWriter::new(framebuffer, info))), 28 | false => None, 29 | }; 30 | 31 | let serial = match serial_logger_status { 32 | true => Some(Spinlock::new(unsafe { SerialPort::init() })), 33 | false => None, 34 | }; 35 | 36 | LockedLogger { 37 | framebuffer, 38 | serial, 39 | } 40 | } 41 | 42 | /// Force-unlocks the logger to prevent a deadlock. 43 | /// 44 | /// ## Safety 45 | /// This method is not memory safe and should be only used when absolutely necessary. 46 | pub unsafe fn force_unlock(&self) { 47 | if let Some(framebuffer) = &self.framebuffer { 48 | unsafe { framebuffer.force_unlock() }; 49 | } 50 | if let Some(serial) = &self.serial { 51 | unsafe { serial.force_unlock() }; 52 | } 53 | } 54 | } 55 | 56 | impl log::Log for LockedLogger { 57 | fn enabled(&self, _metadata: &log::Metadata) -> bool { 58 | true 59 | } 60 | 61 | fn log(&self, record: &log::Record) { 62 | x86_64::instructions::interrupts::without_interrupts(|| { 63 | if let Some(framebuffer) = &self.framebuffer { 64 | let mut framebuffer = framebuffer.lock(); 65 | 66 | if record.level() == Level::Error { 67 | framebuffer.level = 1; 68 | } 69 | 70 | writeln!(framebuffer, "{:5}: {}", record.level(), record.args()).unwrap(); 71 | framebuffer.level = 0; 72 | } 73 | if let Some(serial) = &self.serial { 74 | let mut serial = serial.lock(); 75 | writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap(); 76 | } 77 | }); 78 | } 79 | 80 | fn flush(&self) {} 81 | } 82 | 83 | pub fn init_logger( 84 | framebuffer: &'static mut [u8], 85 | info: FrameBufferInfo, 86 | log_level: LevelFilter, 87 | frame_buffer_logger_status: bool, 88 | serial_logger_status: bool, 89 | ) { 90 | let logger = LOGGER.get_or_init(move || { 91 | LockedLogger::new( 92 | framebuffer, 93 | info, 94 | frame_buffer_logger_status, 95 | serial_logger_status, 96 | ) 97 | }); 98 | log::set_logger(logger).expect("logger already set"); 99 | log::set_max_level(convert_level(log_level)); 100 | log::info!("Framebuffer info: {:?}", info); 101 | } 102 | fn convert_level(level: LevelFilter) -> log::LevelFilter { 103 | match level { 104 | LevelFilter::Off => log::LevelFilter::Off, 105 | LevelFilter::Error => log::LevelFilter::Error, 106 | LevelFilter::Warn => log::LevelFilter::Warn, 107 | LevelFilter::Info => log::LevelFilter::Info, 108 | LevelFilter::Debug => log::LevelFilter::Debug, 109 | LevelFilter::Trace => log::LevelFilter::Trace, 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /bootloader/kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(abi_x86_interrupt)] 4 | #![feature(alloc_error_handler)] 5 | #![feature(core_intrinsics)] 6 | 7 | use acpi::{AcpiHandler, HpetInfo, InterruptModel, PhysicalMapping}; 8 | use alloc::{boxed::Box, fmt, format, slice, string::String, sync::Arc, vec::Vec}; 9 | extern crate alloc; 10 | extern crate edid_rs; 11 | use arrayvec::ArrayVec; 12 | use bitfield::bitfield; 13 | use bootloader_api::{entry_point, info::FrameBufferInfo, BootInfo}; 14 | use bootloader_boot_config::LevelFilter; 15 | use core::{ 16 | alloc::{GlobalAlloc, Layout}, 17 | intrinsics::volatile_set_memory, 18 | panic::PanicInfo, 19 | ptr::{read_volatile, write_volatile, NonNull}, 20 | sync::atomic::{AtomicU64, Ordering}, 21 | }; 22 | use crossbeam::queue::ArrayQueue; 23 | 24 | mod framebuffer; 25 | use framebuffer::FB; 26 | 27 | use conquer_once::spin::OnceCell; 28 | use hashbrown::HashMap; 29 | use x86_64::{ 30 | instructions::hlt, 31 | structures::paging::{ 32 | mapper::MapToError, FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, 33 | PhysFrame, Size1GiB, Size2MiB, Size4KiB, 34 | }, 35 | PhysAddr, VirtAddr, 36 | }; 37 | use xmas_elf::{header::Type, program, sections::SectionData, ElfFile}; 38 | mod allocator; 39 | mod app; 40 | mod drivers; 41 | mod gdt; 42 | mod globals; 43 | mod interrupts; 44 | mod ioapic; 45 | mod local_apic; 46 | mod logger; 47 | mod memory; 48 | mod pci; 49 | mod serial; 50 | mod task; 51 | mod virtio; 52 | /// This function is called on panic. 53 | #[panic_handler] 54 | fn panic(info: &PanicInfo) -> ! { 55 | log::error!("{:?}", info); 56 | loop {} 57 | } 58 | use bootloader_api::config::{BootloaderConfig, Mapping}; 59 | 60 | use crate::{ 61 | allocator::{AllocFromCtx, ALLOCATOR}, 62 | framebuffer::FBShare, 63 | interrupts::{a_sleep, global_time_ms, wait_block, Timer, TIME_MS}, 64 | logger::init_logger, 65 | memory::BootInfoFrameAllocator, 66 | pci::Bar, 67 | task::{ 68 | executor::{qpush, yield_once}, 69 | Task, 70 | }, 71 | virtio::{DeviceType, Virtio}, 72 | }; 73 | 74 | extern "C" fn log_fn(s: *const u8, l: u32) { 75 | unsafe { 76 | let slice = core::slice::from_raw_parts(s, l as usize); 77 | let str_slice = core::str::from_utf8_unchecked(slice); 78 | log::info!("{}", str_slice) 79 | } 80 | } 81 | 82 | extern "C" fn calloc(size: usize, align: usize) -> *mut u8 { 83 | // log::info!("alloc {} {}", size, align); 84 | unsafe { ALLOCATOR.alloc(core::alloc::Layout::from_size_align(size, align).unwrap()) } 85 | } 86 | extern "C" fn cdalloc(ptr: *mut u8, size: usize, align: usize) { 87 | // log::info!("dealloc {:?} {} {}", ptr, size, align); 88 | unsafe { 89 | ALLOCATOR.dealloc( 90 | ptr, 91 | core::alloc::Layout::from_size_align(size, align).unwrap(), 92 | ); 93 | }; 94 | } 95 | 96 | use acpi::AcpiTables; 97 | #[derive(Clone)] 98 | struct AcpiHandlerImpl; 99 | impl AcpiHandler for AcpiHandlerImpl { 100 | unsafe fn map_physical_region( 101 | &self, 102 | physical_address: usize, 103 | size: usize, 104 | ) -> PhysicalMapping { 105 | let s = (size / 4096 + 1) * 4096; 106 | PhysicalMapping::new( 107 | physical_address, 108 | NonNull::new(phys_to_virt(PhysAddr::new(physical_address as u64)).as_mut_ptr()) 109 | .unwrap(), 110 | s, 111 | s, 112 | self.clone(), 113 | ) 114 | } 115 | fn unmap_physical_region(region: &PhysicalMapping) {} 116 | } 117 | const ACPI_HANDLER: AcpiHandlerImpl = AcpiHandlerImpl; 118 | 119 | use spin::Mutex; 120 | pub static MAPPER: OnceCell> = OnceCell::uninit(); 121 | pub static FRAME_ALLOCATOR: OnceCell> = OnceCell::uninit(); 122 | 123 | pub static mut VIRTUAL_MAPPING_OFFSET: VirtAddr = VirtAddr::new_truncate(0); 124 | pub fn phys_to_virt(addr: PhysAddr) -> VirtAddr { 125 | unsafe { VIRTUAL_MAPPING_OFFSET + addr.as_u64() } 126 | } 127 | static OTHER_VIRT: AtomicU64 = AtomicU64::new(0x_5000_0000_0000); 128 | pub fn create_virt_from_phys( 129 | mapper: &mut impl Mapper, 130 | frame_allocator: &mut impl FrameAllocator, 131 | frame: PhysFrame, 132 | ) -> Result> { 133 | let start = VirtAddr::new(OTHER_VIRT.fetch_add(4096, Ordering::Relaxed) as u64); 134 | let page = Page::containing_address(start); 135 | let flags = 136 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE; 137 | unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; 138 | return Ok(page); 139 | } 140 | 141 | pub fn create_identity_virt_from_phys( 142 | mapper: &mut impl Mapper, 143 | frame_allocator: &mut impl FrameAllocator, 144 | ) -> Result> { 145 | let frame = frame_allocator.allocate_frame().unwrap(); 146 | let start = VirtAddr::new(frame.start_address().as_u64()); 147 | let page = Page::containing_address(start); 148 | let flags = 149 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE; 150 | unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; 151 | return Ok(page); 152 | } 153 | 154 | pub fn with_mapper_framealloc(f: FUNC) -> R 155 | where 156 | FUNC: FnOnce(&mut OffsetPageTable, &mut BootInfoFrameAllocator) -> R, 157 | { 158 | let mut mapper = MAPPER.get().unwrap().lock(); 159 | let mut frame_allocator = FRAME_ALLOCATOR.get().unwrap().lock(); 160 | let mapper = &mut *mapper; 161 | let frame_allocator = &mut *frame_allocator; 162 | f(mapper, frame_allocator) 163 | } 164 | 165 | pub fn create_identity_virt_from_phys_n(pages: usize) -> Result> { 166 | with_mapper_framealloc(|mapper, frame_allocator| { 167 | let first_frame = frame_allocator.allocate_frame().unwrap(); 168 | log::info!("first_frame {}", first_frame.start_address().as_u64()); 169 | for i in 1..pages { 170 | let frame = frame_allocator.allocate_frame().unwrap(); 171 | let frame_start = frame.start_address().as_u64(); 172 | 173 | // log::info!("{} : {}", i, frame_start); 174 | if first_frame.start_address().as_u64() + (i as u64) * 4096 != frame_start { 175 | panic!("create_identity_virt_from_phys_n NON CONTIGUOUS, {}", i) 176 | } 177 | } 178 | 179 | for i in 0..pages { 180 | let addr = first_frame.start_address().as_u64() + (i as u64) * 4096; 181 | let frame = PhysFrame::containing_address(PhysAddr::new(addr)); 182 | let page = Page::containing_address(VirtAddr::new(addr)); 183 | let flags = PageTableFlags::PRESENT 184 | | PageTableFlags::WRITABLE 185 | | PageTableFlags::USER_ACCESSIBLE; 186 | unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; 187 | } 188 | 189 | return Ok(Page::containing_address(VirtAddr::new( 190 | first_frame.start_address().as_u64(), 191 | ))); 192 | }) 193 | } 194 | 195 | pub static BOOTLOADER_CONFIG: BootloaderConfig = { 196 | let mut config = BootloaderConfig::new_default(); 197 | config.mappings.physical_memory = Some(Mapping::Dynamic); 198 | config.kernel_stack_size = 128 * 1024; 199 | config 200 | }; 201 | 202 | entry_point!(kernel_main, config = &BOOTLOADER_CONFIG); 203 | 204 | fn kernel_main(boot_info: &'static mut BootInfo) -> ! { 205 | gdt::init(); 206 | interrupts::init_idt(); 207 | 208 | let framebuffer = boot_info.framebuffer.as_mut().unwrap(); 209 | let fbinfo = framebuffer.info(); 210 | 211 | let fbm = framebuffer.buffer_mut(); 212 | let fbm2 = unsafe { 213 | let p = fbm.as_mut_ptr(); 214 | slice::from_raw_parts_mut(p, fbinfo.byte_len) 215 | }; 216 | init_logger(fbm, fbinfo.clone(), LevelFilter::Trace, true, true); 217 | 218 | // x86_64::instructions::interrupts::int3(); 219 | let virtual_full_mapping_offset = VirtAddr::new( 220 | boot_info 221 | .physical_memory_offset 222 | .into_option() 223 | .expect("no physical_memory_offset"), 224 | ); 225 | log::info!("physical_memory_offset {:x}", virtual_full_mapping_offset); 226 | unsafe { 227 | VIRTUAL_MAPPING_OFFSET = virtual_full_mapping_offset; 228 | } 229 | let mapper = unsafe { memory::init(virtual_full_mapping_offset) }; 230 | let frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_regions) }; 231 | MAPPER.init_once(|| Mutex::new(mapper)); 232 | FRAME_ALLOCATOR.init_once(|| Mutex::new(frame_allocator)); 233 | { 234 | log::info!("Complete Bootloader Map physical memory"); 235 | type VirtualMappingPageSize = Size2MiB; // Size2MiB;Size1GiB Size4KiB 236 | 237 | let start_frame: PhysFrame = 238 | PhysFrame::containing_address(PhysAddr::new(0)); 239 | let max_phys = PhysAddr::new(virtual_full_mapping_offset.as_u64() - 1u64); 240 | let max_phys = PhysAddr::new(Size1GiB::SIZE * 64 - 1); 241 | 242 | let end_frame: PhysFrame = PhysFrame::containing_address(max_phys); 243 | 244 | use x86_64::structures::paging::PageSize; 245 | let mut news = 0; 246 | let mut olds = 0; 247 | for frame in PhysFrame::range_inclusive(start_frame, end_frame) { 248 | let page: Page = Page::containing_address( 249 | virtual_full_mapping_offset + frame.start_address().as_u64(), 250 | ); 251 | let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; 252 | match unsafe { 253 | MAPPER.get_unchecked().lock().map_to( 254 | page, 255 | frame, 256 | flags, 257 | &mut *FRAME_ALLOCATOR.get_unchecked().lock(), 258 | ) 259 | } { 260 | Ok(tlb) => { 261 | tlb.flush(); 262 | news += 1; 263 | } 264 | Err(_) => { 265 | olds += 1; 266 | } 267 | }; 268 | } 269 | log::info!("new:{} already_mapped:{}", news, olds); 270 | } 271 | 272 | with_mapper_framealloc(|mapper, frame_allocator| { 273 | allocator::init_heap(mapper, frame_allocator).expect("heap initialization failed"); 274 | }); 275 | 276 | let rsdp_addr = boot_info.rsdp_addr.into_option().expect("no rsdp"); 277 | let acpi_tables = unsafe { AcpiTables::from_rsdp(ACPI_HANDLER, rsdp_addr as usize).unwrap() }; 278 | log::info!("acpi_read"); 279 | 280 | let x = HpetInfo::new(&acpi_tables).expect("hpet"); 281 | // log::info!("{:#?}]", x); 282 | 283 | let pi = acpi_tables.platform_info().expect("platform info"); 284 | 285 | if let InterruptModel::Apic(apic) = pi.interrupt_model { 286 | // log::info!("{:#?}", apic); 287 | 288 | unsafe { 289 | log::info!("init apic"); 290 | let lapic = local_apic::LocalApic::init(PhysAddr::new(apic.local_apic_address)); 291 | log::info!("start apic c"); 292 | let mut freq = 1000_000_000; 293 | if let Some(cpuid) = local_apic::cpuid() { 294 | log::info!("cpuid"); 295 | if let Some(tsc) = cpuid.get_tsc_info() { 296 | log::info!( 297 | "{} {}", 298 | tsc.nominal_frequency(), 299 | tsc.tsc_frequency().unwrap() 300 | ); 301 | freq = tsc.nominal_frequency(); 302 | } else { 303 | } 304 | } 305 | lapic.set_div_conf(0b1011); 306 | log::info!("start apic c"); 307 | lapic.set_lvt_timer((1 << 17) + 48); 308 | let wanted_freq_hz = 1000; 309 | lapic.set_init_count(freq / wanted_freq_hz); 310 | } 311 | 312 | for io_apic in apic.io_apics.iter() { 313 | log::info!("{:x}", io_apic.address); 314 | let mut ioa = ioapic::IoApic::init(io_apic); 315 | let val = ioa.read(ioapic::IOAPICVER); 316 | log::info!("{:x}", val); 317 | for i in 0..24 { 318 | let n = ioa.read_redtlb(i); 319 | let mut red = ioapic::RedTbl::new(n); 320 | red.vector = (50 + i) as u8; 321 | 322 | let stored = red.store(); 323 | 324 | ioa.write_redtlb(i, stored); 325 | } 326 | } 327 | 328 | x86_64::instructions::interrupts::enable(); 329 | 330 | // x86_64::instructions::interrupts::disable(); 331 | } 332 | 333 | { 334 | // aml::AmlContext::new() 335 | } 336 | // .expect("no acpi table"); 337 | 338 | let proc_info = pi.processor_info.expect("processor_info"); 339 | // log::info!("{:?}", pi.power_profile); 340 | // log::info!("{:#?}", pi.interrupt_model); 341 | // log::info!("{:?}", proc_info.boot_processor); 342 | for proc in proc_info.application_processors.iter() { 343 | log::info!("{:?}", proc); 344 | } 345 | 346 | // for ent in mapper.level_4_table().iter().take(30) { 347 | // log::info!("{:?}", ent); 348 | // } 349 | 350 | let pcis = pci::Pcis::new(); 351 | 352 | let mut virtio_devices = Vec::new(); 353 | 354 | { 355 | for (pci_index, pci) in pcis.devs.iter().enumerate() { 356 | let vector_base = 50 + 2 * pci_index; 357 | let status = pci.config_read_u16(pci::PCIConfigRegisters::PCIStatus as u8); 358 | let vendor = pci.config_read_u16(pci::PCIConfigRegisters::PCIVendorID as u8); 359 | let device_id = 360 | pci.config_read_u16(pci::PCIConfigRegisters::PCIDeviceID as u8) as isize - 0x1040; 361 | log::info!( 362 | "{:?} status {} irq:{} ipin:{}, {:x} {} ________________", 363 | pci, 364 | status, 365 | pci.get_irq(), 366 | pci.get_ipin(), 367 | vendor, 368 | device_id, 369 | ); 370 | const VIRTIO_VENDOR_ID: u16 = 0x1af4; 371 | if vendor == VIRTIO_VENDOR_ID { 372 | let virtio = with_mapper_framealloc(|mapper, frame_allocator| { 373 | Virtio::init(pci, mapper, frame_allocator) 374 | }); 375 | if let Some(virtio) = virtio { 376 | virtio_devices.push(virtio); 377 | } 378 | } 379 | } 380 | } 381 | 382 | let mut fb = Box::new(FB::new(&fbinfo)); 383 | // fb.flush(fbm2, &fbinfo); 384 | let fb_clone: *mut FB = &mut *fb; 385 | log::info!("fbclone {:?}", fb_clone); 386 | 387 | { 388 | let mut executor = task::executor::Executor::new(); 389 | let spawner = executor.spawner(); 390 | 391 | for virtio in virtio_devices.into_iter() { 392 | match virtio.device_type { 393 | DeviceType::Input => spawner.run(drivers::virtio_input::drive(virtio)), 394 | DeviceType::Gpu => spawner.run(drivers::virtio_gpu::drive( 395 | virtio, 396 | spawner.clone(), 397 | fb_clone, 398 | )), 399 | } 400 | } 401 | 402 | spawner.run(async move { 403 | use app::*; 404 | let mut apps: Vec = Vec::new(); 405 | let apps_raw = [ 406 | &include_bytes!("../../../app_background/target/x86_64/release/func")[..], 407 | &include_bytes!("../../../app_console/target/x86_64/release/func")[..], 408 | &include_bytes!("../../../app_cursor/target/x86_64/release/func")[..], 409 | // &include_bytes!("../../../app_test/target/x86_64/release/func")[..], 410 | // &include_bytes!("../../../app_c/target/main")[..], 411 | ]; 412 | for app_bytes in apps_raw.iter() { 413 | apps.push(App::new(app_bytes, false)); 414 | } 415 | 416 | loop { 417 | let input = globals::INPUT.read(); 418 | for app in apps.iter_mut() { 419 | let mut arg = Context::new(log_fn, fb.share(), calloc, cdalloc, &input); 420 | app.call(&mut arg); 421 | } 422 | 423 | globals::INPUT.update(|e| e.step()); 424 | yield_once().await; 425 | } 426 | }); 427 | executor.run(); 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /bootloader/kernel/src/memory.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::ArrayVec; 2 | use x86_64::PhysAddr; 3 | use x86_64::{structures::paging::PageTable, VirtAddr}; 4 | 5 | use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PhysFrame, Size4KiB}; 6 | /// Initialize a new OffsetPageTable. 7 | /// 8 | /// This function is unsafe because the caller must guarantee that the 9 | /// complete physical memory is mapped to virtual memory at the passed 10 | /// `physical_memory_offset`. Also, this function must be only called once 11 | /// to avoid aliasing `&mut` references (which is undefined behavior). 12 | pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { 13 | let level_4_table = active_level_4_table(physical_memory_offset); 14 | OffsetPageTable::new(level_4_table, physical_memory_offset) 15 | } 16 | 17 | /// Returns a mutable reference to the active level 4 table. 18 | /// 19 | /// This function is unsafe because the caller must guarantee that the 20 | /// complete physical memory is mapped to virtual memory at the passed 21 | /// `physical_memory_offset`. Also, this function must be only called once 22 | /// to avoid aliasing `&mut` references (which is undefined behavior). 23 | unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { 24 | use x86_64::registers::control::Cr3; 25 | 26 | let (level_4_table_frame, _) = Cr3::read(); 27 | 28 | let phys = level_4_table_frame.start_address(); 29 | 30 | log::info!("level_4_table_frame.start_address {:?}", phys); 31 | let virt = physical_memory_offset + phys.as_u64(); 32 | let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); 33 | 34 | &mut *page_table_ptr // unsafe 35 | } 36 | 37 | pub unsafe fn kern_phy_to_virt(physical_memory_offset: VirtAddr, target_phy: PhysAddr) -> VirtAddr { 38 | let virt = VirtAddr::new(physical_memory_offset.as_u64() + target_phy.as_u64()); 39 | virt 40 | } 41 | 42 | const BUFFER_SIZE_ADVANCE: usize = 32; 43 | use bootloader_api::info::{MemoryRegionKind, MemoryRegions}; 44 | /// A FrameAllocator that returns usable frames from the bootloader's memory map. 45 | pub struct BootInfoFrameAllocator { 46 | memory_map: &'static MemoryRegions, 47 | next: usize, 48 | buf: ArrayVec, 49 | } 50 | unsafe impl Send for BootInfoFrameAllocator {} 51 | 52 | impl BootInfoFrameAllocator { 53 | /// Create a FrameAllocator from the passed memory map. 54 | /// 55 | /// This function is unsafe because the caller must guarantee that the passed 56 | /// memory map is valid. The main requirement is that all frames that are marked 57 | /// as `USABLE` in it are really unused. 58 | pub unsafe fn init(memory_map: &'static MemoryRegions) -> Self { 59 | BootInfoFrameAllocator { 60 | memory_map, 61 | next: 1, 62 | buf: ArrayVec::new(), 63 | } 64 | } 65 | 66 | /// Returns an iterator over the usable frames specified in the memory map. 67 | fn usable_frames(&self) -> impl Iterator { 68 | // get usable regions from memory map 69 | let regions = self.memory_map.iter(); 70 | let usable_regions = regions.filter(|r| r.kind == MemoryRegionKind::Usable); 71 | // map each region to its address range 72 | let addr_ranges = usable_regions.map(|r| r.start..r.end); 73 | // transform to an iterator of frame start addresses 74 | let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); 75 | // create `PhysFrame` types from the start addresses 76 | frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr))) 77 | } 78 | } 79 | 80 | unsafe impl FrameAllocator for BootInfoFrameAllocator { 81 | fn allocate_frame(&mut self) -> Option { 82 | if self.buf.len() == 0 { 83 | for frame in self 84 | .usable_frames() 85 | .skip(self.next - 1) 86 | .take(BUFFER_SIZE_ADVANCE) 87 | { 88 | self.buf.push(frame); 89 | } 90 | 91 | self.buf.reverse(); 92 | self.next += BUFFER_SIZE_ADVANCE; 93 | } 94 | 95 | self.buf.pop() 96 | // let frame = self.usable_frames().nth(self.next); 97 | // self.next += 1; 98 | // frame 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /bootloader/kernel/src/pci.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use alloc::vec::Vec; 4 | use x86_64::{ 5 | instructions::port::{Port, PortGeneric, ReadWriteAccess}, 6 | PhysAddr, 7 | }; 8 | pub enum PCIConfigRegisters { 9 | PCIDeviceID = 0x2, 10 | PCIVendorID = 0x0, 11 | PCIStatus = 0x6, 12 | PCICommand = 0x4, 13 | PCIClassCode = 0xB, 14 | PCISubclass = 0xA, 15 | PCIProgIF = 0x9, 16 | PCIRevisionID = 0x8, 17 | PCIBIST = 0xF, 18 | PCIHeaderType = 0xE, 19 | PCILatencyTimer = 0xD, 20 | PCICacheLineSize = 0xC, 21 | PCIBAR0 = 0x10, 22 | PCIBAR1 = 0x14, 23 | PCIBAR2 = 0x18, 24 | PCIBAR3 = 0x1C, 25 | PCIBAR4 = 0x20, 26 | PCIBAR5 = 0x24, 27 | PCICardbusCISPointer = 0x28, 28 | PCISubsystemID = 0x2E, 29 | PCISubsystemVendorID = 0x2C, 30 | PCIExpansionROMBaseAddress = 0x30, 31 | PCICapabilitiesPointer = 0x34, 32 | PCIMaxLatency = 0x3F, 33 | PCIMinGrant = 0x3E, 34 | PCIInterruptPIN = 0x3D, 35 | PCIInterruptLine = 0x3C, 36 | } 37 | 38 | const port_config_address: PortGeneric = Port::new(0xCF8); 39 | const port_config_data: PortGeneric = Port::new(0xCFC); 40 | const port_config_data_u8: PortGeneric = Port::new(0xCFC); 41 | pub fn config_address(bus: u8, slot: u8, func: u8, off: u8) { 42 | let address: u32 = (((bus as u32) << 16) 43 | | ((slot as u32) << 11) 44 | | ((func as u32) << 8) 45 | | ((off as u32) & 0xfc) 46 | | 0x80000000); 47 | 48 | unsafe { 49 | port_config_address.write(address); 50 | } 51 | } 52 | pub fn config_read_u32(bus: u8, slot: u8, func: u8, off: u8) -> u32 { 53 | config_address(bus, slot, func, off); 54 | let read: u32 = unsafe { port_config_data.read() }; 55 | read 56 | } 57 | 58 | pub fn config_read_u16(bus: u8, slot: u8, func: u8, off: u8) -> u16 { 59 | config_address(bus, slot, func, off); 60 | 61 | let read: u32 = unsafe { port_config_data.read() }; 62 | let read = (read >> ((off & 2) * 8)) & 0xffff; 63 | read as u16 64 | } 65 | 66 | pub fn config_read_u8(bus: u8, slot: u8, func: u8, off: u8) -> u8 { 67 | config_address(bus, slot, func, off); 68 | 69 | // unsafe { port_config_data_u8.read() } 70 | let read: u32 = unsafe { port_config_data.read() }; 71 | let read = (read >> ((off & 3) * 8)) & 0xff; 72 | read as u8 73 | } 74 | 75 | pub fn config_write_u32(bus: u8, slot: u8, func: u8, off: u8, data: u32) { 76 | config_address(bus, slot, func, off); 77 | unsafe { 78 | port_config_data.write(data); 79 | }; 80 | } 81 | 82 | pub fn config_write_u16(bus: u8, slot: u8, func: u8, off: u8, data: u16) { 83 | config_address(bus, slot, func, off); 84 | 85 | let read: u32 = unsafe { port_config_data.read() }; 86 | let val = (read & (!(0xFFFF << ((off & 2) * 8)))) | ((data as u32) << ((off & 2) * 8)); 87 | unsafe { 88 | port_config_data.write(val); 89 | }; 90 | } 91 | 92 | pub fn config_write_u8(bus: u8, slot: u8, func: u8, off: u8, data: u8) { 93 | config_address(bus, slot, func, off); 94 | 95 | let read: u32 = unsafe { port_config_data.read() }; 96 | let val = (read & (!(0xFF << ((off & 3) * 8)))) | ((data as u32) << ((off & 3) * 8)); 97 | unsafe { 98 | port_config_data.write(val); 99 | }; 100 | } 101 | #[derive(Clone)] 102 | pub struct Pci { 103 | pub bus: u8, 104 | pub slot: u8, 105 | pub func: u8, 106 | } 107 | 108 | impl Debug for Pci { 109 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 110 | write!( 111 | f, 112 | "pci:{:02x}:{:02x}:{:02x}", 113 | self.bus, self.slot, self.func 114 | ) 115 | } 116 | } 117 | 118 | #[derive(Clone, Debug, Copy, PartialEq)] 119 | pub enum Bar { 120 | None, 121 | Io(u32), 122 | Mm(PhysAddr), 123 | } 124 | 125 | impl Pci { 126 | pub fn config_read_u8(&self, off: u8) -> u8 { 127 | config_read_u8(self.bus, self.slot, self.func, off as u8) 128 | } 129 | pub fn config_write_u8(&self, off: u8, val: u8) { 130 | config_write_u8(self.bus, self.slot, self.func, off as u8, val) 131 | } 132 | pub fn config_read_u16(&self, off: u8) -> u16 { 133 | config_read_u16(self.bus, self.slot, self.func, off as u8) 134 | } 135 | pub fn config_write_u16(&self, off: u8, val: u16) { 136 | config_write_u16(self.bus, self.slot, self.func, off as u8, val) 137 | } 138 | pub fn config_read_u32(&self, off: u8) -> u32 { 139 | config_read_u32(self.bus, self.slot, self.func, off as u8) 140 | } 141 | 142 | pub fn get_bar(&self, idx: u8) -> Bar { 143 | fn idx_to_enum(idx: u8) -> PCIConfigRegisters { 144 | match idx { 145 | 0 => PCIConfigRegisters::PCIBAR0, 146 | 1 => PCIConfigRegisters::PCIBAR1, 147 | 2 => PCIConfigRegisters::PCIBAR2, 148 | 3 => PCIConfigRegisters::PCIBAR3, 149 | 4 => PCIConfigRegisters::PCIBAR4, 150 | _ => PCIConfigRegisters::PCIBAR5, 151 | } 152 | } 153 | let mut bar = self.config_read_u32(idx_to_enum(idx) as u8) as u64; 154 | if bar == 0 { 155 | return Bar::None; 156 | } 157 | let io = bar & 0x1 != 0; 158 | 159 | if io { 160 | return Bar::Io((bar & 0xFFFFFFFFFFFFFFFC) as u32); 161 | } 162 | 163 | let bit64 = bar & 0x4 != 0; 164 | let not_last = idx < 5; 165 | if bit64 && not_last { 166 | bar |= (config_read_u32( 167 | self.bus, 168 | self.slot, 169 | self.func, 170 | PCIConfigRegisters::PCIBAR0 as u8 + ((idx as u8 + 1) * 4), 171 | ) as u64) 172 | << 32; 173 | } 174 | let masked = bar & 0xFFFFFFFFFFFFFFF0; 175 | if masked == 0 { 176 | return Bar::None; 177 | } 178 | Bar::Mm(PhysAddr::new(masked)) 179 | } 180 | pub fn get_irq(&self) -> u8 { 181 | self.config_read_u8(PCIConfigRegisters::PCIInterruptLine as u8) 182 | } 183 | pub fn get_ipin(&self) -> u8 { 184 | self.config_read_u8(PCIConfigRegisters::PCIInterruptPIN as u8) 185 | } 186 | } 187 | 188 | pub struct Pcis { 189 | pub devs: Vec, 190 | } 191 | 192 | impl Pcis { 193 | pub fn new() -> Self { 194 | let mut devs = Vec::new(); 195 | for bus in 0..=255 { 196 | for slot in 0..32 { 197 | for func in 0..8 { 198 | let vendor = 199 | config_read_u16(bus, slot, func, PCIConfigRegisters::PCIVendorID as u8); 200 | if vendor != 0xFFFF { 201 | let device_id = 202 | config_read_u16(bus, slot, func, PCIConfigRegisters::PCIDeviceID as u8); 203 | let header_type = config_read_u8( 204 | bus, 205 | slot, 206 | func, 207 | PCIConfigRegisters::PCIHeaderType as u8, 208 | ); 209 | // log::trace!( 210 | // "{:x}:{:x}:{:x} vendor: {:x} id:{:x} ht: {:x}", 211 | // bus, 212 | // slot, 213 | // func, 214 | // vendor, 215 | // device_id, 216 | // header_type 217 | // ); 218 | 219 | devs.push(Pci { bus, slot, func }); 220 | if func == 0 && (header_type & 0x80) == 0 { 221 | break; 222 | } 223 | } 224 | } 225 | } 226 | } 227 | Self { devs } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /bootloader/kernel/src/serial.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | pub struct SerialPort { 4 | port: uart_16550::SerialPort, 5 | } 6 | 7 | impl SerialPort { 8 | /// # Safety 9 | /// 10 | /// unsafe because this function must only be called once 11 | pub unsafe fn init() -> Self { 12 | let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) }; 13 | port.init(); 14 | Self { port } 15 | } 16 | } 17 | 18 | impl fmt::Write for SerialPort { 19 | fn write_str(&mut self, s: &str) -> fmt::Result { 20 | self.port.write_str(s).unwrap(); 21 | Ok(()) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bootloader/kernel/src/task/executor.rs: -------------------------------------------------------------------------------- 1 | use super::{Task, TaskId}; 2 | use alloc::collections::VecDeque; 3 | use alloc::{collections::BTreeMap, sync::Arc}; 4 | use crossbeam::queue::ArrayQueue; 5 | use futures::task::AtomicWaker; 6 | use futures::Future; 7 | use lazy_static::lazy_static; 8 | pub struct SimpleExecutor { 9 | task_queue: VecDeque, 10 | } 11 | 12 | impl SimpleExecutor { 13 | pub fn new() -> SimpleExecutor { 14 | SimpleExecutor { 15 | task_queue: VecDeque::new(), 16 | } 17 | } 18 | 19 | pub fn spawn(&mut self, task: Task) { 20 | self.task_queue.push_back(task) 21 | } 22 | } 23 | use core::task::RawWakerVTable; 24 | use core::task::{RawWaker, Waker}; 25 | 26 | fn dummy_raw_waker() -> RawWaker { 27 | fn no_op(_: *const ()) {} 28 | fn clone(_: *const ()) -> RawWaker { 29 | dummy_raw_waker() 30 | } 31 | 32 | let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op); 33 | RawWaker::new(0 as *const (), vtable) 34 | } 35 | 36 | fn dummy_waker() -> Waker { 37 | unsafe { Waker::from_raw(dummy_raw_waker()) } 38 | } 39 | use core::task::{Context, Poll}; 40 | 41 | impl SimpleExecutor { 42 | pub fn run(&mut self) { 43 | while let Some(mut task) = self.task_queue.pop_front() { 44 | let waker = dummy_waker(); 45 | let mut context = Context::from_waker(&waker); 46 | match task.poll(&mut context) { 47 | Poll::Ready(()) => {} // task done 48 | Poll::Pending => self.task_queue.push_back(task), 49 | } 50 | } 51 | } 52 | } 53 | 54 | #[derive(Debug, Clone)] 55 | pub struct Spawner(Arc>); 56 | 57 | impl Spawner { 58 | pub fn run(&self, future: impl Future + 'static) { 59 | self.0.push(Task::new(future)); 60 | } 61 | } 62 | 63 | pub struct Executor { 64 | tasks: BTreeMap, 65 | task_queue: Arc>, 66 | spawn_queue: Arc>, 67 | waker_cache: BTreeMap, 68 | } 69 | pub fn qpush(queue: Arc>, f: impl Future + 'static) { 70 | queue.push(Task::new(f)); 71 | } 72 | 73 | impl Executor { 74 | pub fn new() -> Self { 75 | Executor { 76 | tasks: BTreeMap::new(), 77 | task_queue: Arc::new(ArrayQueue::new(100)), 78 | spawn_queue: Arc::new(ArrayQueue::new(100)), 79 | waker_cache: BTreeMap::new(), 80 | } 81 | } 82 | pub fn spawner(&self) -> Spawner { 83 | Spawner(self.spawn_queue.clone()) 84 | } 85 | } 86 | impl Executor { 87 | pub fn spawn(&mut self, task: Task) { 88 | let task_id = task.id; 89 | if self.tasks.insert(task.id, task).is_some() { 90 | panic!("task with same ID already in tasks"); 91 | } 92 | self.task_queue.push(task_id).expect("queue full"); 93 | } 94 | } 95 | 96 | impl Executor { 97 | fn run_ready_tasks(&mut self) { 98 | // destructure `self` to avoid borrow checker errors 99 | let Self { 100 | tasks, 101 | task_queue, 102 | waker_cache, 103 | spawn_queue, 104 | } = self; 105 | 106 | while let Some(task_id) = task_queue.pop() { 107 | let task = match tasks.get_mut(&task_id) { 108 | Some(task) => task, 109 | None => continue, // task no longer exists 110 | }; 111 | let waker = waker_cache 112 | .entry(task_id) 113 | .or_insert_with(|| TaskWaker::new(task_id, task_queue.clone())); 114 | let mut context = Context::from_waker(waker); 115 | match task.poll(&mut context) { 116 | Poll::Ready(()) => { 117 | // task done -> remove it and its cached waker 118 | tasks.remove(&task_id); 119 | waker_cache.remove(&task_id); 120 | } 121 | Poll::Pending => {} 122 | } 123 | } 124 | } 125 | } 126 | 127 | impl Executor { 128 | pub fn run(&mut self) -> ! { 129 | loop { 130 | while let Some(e) = self.spawn_queue.pop() { 131 | self.spawn(e); 132 | } 133 | 134 | self.run_ready_tasks(); 135 | 136 | x86_64::instructions::interrupts::disable(); 137 | if self.task_queue.is_empty() && self.spawn_queue.is_empty() { 138 | x86_64::instructions::interrupts::enable_and_hlt(); 139 | } else { 140 | x86_64::instructions::interrupts::enable(); 141 | } 142 | while let Some(e) = YIELDERS.pop() { 143 | e.wake(); 144 | } 145 | } 146 | } 147 | } 148 | 149 | struct TaskWaker { 150 | task_id: TaskId, 151 | task_queue: Arc>, 152 | } 153 | impl TaskWaker { 154 | fn wake_task(&self) { 155 | self.task_queue.push(self.task_id).expect("task_queue full"); 156 | } 157 | } 158 | use alloc::task::Wake; 159 | 160 | impl Wake for TaskWaker { 161 | fn wake(self: Arc) { 162 | self.wake_task(); 163 | } 164 | 165 | fn wake_by_ref(self: &Arc) { 166 | self.wake_task(); 167 | } 168 | } 169 | impl TaskWaker { 170 | fn new(task_id: TaskId, task_queue: Arc>) -> Waker { 171 | Waker::from(Arc::new(TaskWaker { 172 | task_id, 173 | task_queue, 174 | })) 175 | } 176 | } 177 | lazy_static! { 178 | pub static ref YIELDERS: ArrayQueue = ArrayQueue::new(100); 179 | } 180 | 181 | pub async fn yield_once() { 182 | let timer = YieldOnce(false); 183 | timer.await; 184 | } 185 | pub struct YieldOnce(bool); 186 | impl futures::future::Future for YieldOnce { 187 | type Output = (); 188 | fn poll( 189 | mut self: core::pin::Pin<&mut Self>, 190 | cx: &mut core::task::Context<'_>, 191 | ) -> core::task::Poll { 192 | if !self.0 { 193 | self.as_mut().0 = true; 194 | let aw = AtomicWaker::new(); 195 | aw.register(&cx.waker()); 196 | YIELDERS.push(aw); 197 | core::task::Poll::Pending 198 | } else { 199 | core::task::Poll::Ready(()) 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /bootloader/kernel/src/task/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use core::{future::Future, pin::Pin}; 3 | 4 | pub mod executor; 5 | 6 | pub struct Task { 7 | id: TaskId, 8 | future: Pin>>, 9 | } 10 | impl Task { 11 | pub fn new(future: impl Future + 'static) -> Task { 12 | Task { 13 | id: TaskId::new(), 14 | future: Box::pin(future), 15 | } 16 | } 17 | } 18 | use core::task::{Context, Poll}; 19 | 20 | impl Task { 21 | fn poll(&mut self, context: &mut Context) -> Poll<()> { 22 | self.future.as_mut().poll(context) 23 | } 24 | } 25 | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 27 | struct TaskId(u64); 28 | use core::sync::atomic::{AtomicU64, Ordering}; 29 | 30 | impl TaskId { 31 | fn new() -> Self { 32 | static NEXT_ID: AtomicU64 = AtomicU64::new(0); 33 | TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bootloader/kernel/src/virtio.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::{read_volatile, write_volatile}; 2 | 3 | use alloc::{fmt, vec::Vec}; 4 | use x86_64::{ 5 | structures::paging::{FrameAllocator, Mapper, Size4KiB}, 6 | PhysAddr, VirtAddr, 7 | }; 8 | 9 | use crate::{ 10 | create_identity_virt_from_phys, 11 | pci::{self, Bar, Pci}, 12 | phys_to_virt, 13 | }; 14 | 15 | pub fn to_bytes(t: &T) -> &[u8] { 16 | unsafe { 17 | let len = core::intrinsics::size_of_val(t); 18 | let ptr: *const u8 = core::intrinsics::transmute(t); 19 | core::slice::from_raw_parts(ptr, len) 20 | } 21 | } 22 | 23 | const MAX_NUM_QUEUE: usize = 256; 24 | pub struct Virtio { 25 | pub pci: Pci, 26 | 27 | pub common: VirtioCap<&'static mut VirtioPciCommonCfg>, 28 | pub device: VirtioCap<&'static mut ()>, 29 | pub notify: VirtioCap, 30 | pub pci_conf: VirtioCap<[u8; 4]>, 31 | 32 | pub step: usize, 33 | pub last_used_idx: [u16; MAX_NUM_QUEUE], 34 | pub device_type: DeviceType, 35 | pub queues_free: Vec, 36 | pub queue_select: u16, 37 | } 38 | pub struct QueueFreeDescs { 39 | free: Vec, 40 | } 41 | impl QueueFreeDescs { 42 | pub fn new(queue_size: u16) -> Self { 43 | let mut free = Vec::with_capacity(queue_size as usize); 44 | for i in 0..queue_size { 45 | free.push(i as u16); 46 | } 47 | Self { free } 48 | } 49 | pub fn get_free(&mut self) -> Option { 50 | self.free.pop() 51 | } 52 | pub fn get_free_twice(&mut self) -> Option<(u16, u16)> { 53 | if self.free.len() >= 2 { 54 | Some((self.free.pop().unwrap(), self.free.pop().unwrap())) 55 | } else { 56 | None 57 | } 58 | } 59 | pub fn set_free(&mut self, desc_id: u16) { 60 | self.free.push(desc_id); 61 | } 62 | } 63 | 64 | #[derive(Clone, Debug)] 65 | pub enum DeviceType { 66 | Input, 67 | Gpu, 68 | } 69 | 70 | const DEVICE_ID_INPUT: isize = 18; 71 | const DEVICE_ID_GPU: isize = 16; 72 | 73 | fn device_id_to_type(id: isize) -> Option { 74 | match id { 75 | DEVICE_ID_INPUT => Some(DeviceType::Input), 76 | DEVICE_ID_GPU => Some(DeviceType::Gpu), 77 | _ => None, 78 | } 79 | } 80 | 81 | impl Virtio { 82 | pub fn init( 83 | pci: &Pci, 84 | 85 | mapper: &mut impl Mapper, 86 | frame_allocator: &mut impl FrameAllocator, 87 | ) -> Option { 88 | let device_id = 89 | pci.config_read_u16(pci::PCIConfigRegisters::PCIDeviceID as u8) as isize - 0x1040; 90 | 91 | let device_type = device_id_to_type(device_id); 92 | if device_type.is_none() { 93 | return None; 94 | } 95 | let device_type = device_type.unwrap(); 96 | 97 | let mut bars = [Bar::None; 6]; 98 | for idx in 0..=5 { 99 | let bar = pci.get_bar(idx); 100 | if bar != Bar::None { 101 | // log::info!("bar {}:{:?}", idx, bar); 102 | } 103 | bars[idx as usize] = bar; 104 | } 105 | 106 | let cap_ptr = pci.config_read_u8(pci::PCIConfigRegisters::PCICapabilitiesPointer as u8); 107 | 108 | let mut current_off = cap_ptr; 109 | const VirtioStatusNone: u8 = 0; 110 | const VirtioStatusAcknowledge: u8 = 1; 111 | const VirtioStatusDriver: u8 = 2; 112 | const VirtioStatusFailed: u8 = 128; 113 | const VirtioStatusFeatureOk: u8 = 8; 114 | const VirtioStatusDriverOk: u8 = 4; 115 | const VirtioStatusNeedsReset: u8 = 64; 116 | 117 | let mut common: Option> = None; 118 | let mut device: Option> = None; 119 | let mut notify: Option> = None; 120 | let mut pci_conf: Option> = None; 121 | loop { 122 | let cap = pci.config_read_u8(current_off); 123 | 124 | //VIRTIO 125 | if cap == 0x9 { 126 | let cap_len = pci.config_read_u8(current_off + 2); 127 | let cfg_type = pci.config_read_u8(current_off + 3); 128 | let bar = pci.config_read_u8(current_off + 4); 129 | let offset = pci.config_read_u32(current_off + 8); 130 | let length = pci.config_read_u32(current_off + 12); 131 | 132 | // log::info!( 133 | // "virtio {} {} {} {} {}", 134 | // cap_len, 135 | // cfg_type, 136 | // bar, 137 | // offset, 138 | // length 139 | // ); 140 | 141 | match cfg_type { 142 | VirtioPciCapCommonCfg => { 143 | if let Bar::Mm(phys) = bars[bar as usize] { 144 | let ptr = phys_to_virt(phys + offset as u64); 145 | // log::info!("common {:?}", ptr.as_ptr::()); 146 | let cfg: &'static mut VirtioPciCommonCfg = 147 | unsafe { &mut *(ptr.as_mut_ptr::()) }; 148 | 149 | let x: u8 = unsafe { read_volatile(ptr.as_ptr()) }; 150 | // log::info!("{}", x); 151 | // log::info!("common {:?}", cfg); 152 | common = Some(VirtioCap::new(cfg, bars[bar as usize], offset, length)); 153 | } 154 | } 155 | VirtioPciCapNotifyCfg => { 156 | let notify_off_multiplier = pci.config_read_u32(current_off + 16); 157 | notify = Some(VirtioCap { 158 | cap: notify_off_multiplier, 159 | bar: bars[bar as usize], 160 | offset, 161 | length, 162 | }); 163 | // log::info!("{:#?}", virtio_caps.notify); 164 | } 165 | VirtioPciCapIsrCfg => {} 166 | VirtioPciCapDeviceCfg => { 167 | if let Bar::Mm(phys) = bars[bar as usize] { 168 | let ptr: VirtAddr = phys_to_virt(phys + offset as u64); 169 | let cfg: &'static mut () = unsafe { &mut *(ptr.as_mut_ptr::<()>()) }; 170 | device = Some(VirtioCap { 171 | cap: cfg, 172 | bar: bars[bar as usize], 173 | offset, 174 | length, 175 | }); 176 | } 177 | } 178 | VirtioPciCapPciCfg => { 179 | let pci_cfg_data = [ 180 | pci.config_read_u8(current_off + 16), 181 | pci.config_read_u8(current_off + 17), 182 | pci.config_read_u8(current_off + 18), 183 | pci.config_read_u8(current_off + 19), 184 | ]; 185 | 186 | pci_conf = Some(VirtioCap { 187 | cap: pci_cfg_data, 188 | bar: bars[bar as usize], 189 | offset, 190 | length, 191 | }); 192 | } 193 | _ => {} 194 | } 195 | } 196 | // if cap == 0x11 && false { 197 | // let line2 = pci.config_read_u32(current_off + 4); 198 | // let table_bir = line2 & 0b111; 199 | // let table_offset = line2 & 0xFFFFFFf8; 200 | // let msg_ctrl = pci.config_read_u16(current_off + 2); 201 | // bitfield! { 202 | // pub struct MsgCtrl(u16); 203 | // impl Debug; 204 | // // The fields default to u16 205 | // pub table_size, _: 10, 0; 206 | // pub reserved, _ : 13, 11; 207 | // pub function_mask , _: 14; 208 | // pub enable , set_enable: 15; 209 | // } 210 | 211 | // let mut msg_ctrl = MsgCtrl(msg_ctrl); 212 | // msg_ctrl.set_enable(true); 213 | 214 | // pci.config_write_u16(current_off + 2, msg_ctrl.0); 215 | // log::info!( 216 | // "MSI-X bir:{} tblo:{} msg_ctrl:{:?}", 217 | // table_bir, 218 | // table_offset, 219 | // msg_ctrl 220 | // ); 221 | 222 | // let line3 = pci.config_read_u32(current_off + 8); 223 | // let pba_bir = line3 & 0b11; 224 | // let pba_offset = line3 & 0xFFFFFFf8; 225 | 226 | // for table_index in 0..=msg_ctrl.table_size() { 227 | // if let Bar::Mm(phys) = bars[table_bir as usize] { 228 | // let virt_bar = create_virt_from_phys( 229 | // &mut mapper, 230 | // &mut frame_allocator, 231 | // PhysFrame::containing_address(phys), 232 | // ) 233 | // .expect("bar"); 234 | 235 | // // let virt_bar = create_virt_from_phys( 236 | // // &mut mapper, 237 | // // &mut frame_allocator, 238 | // // PhysFrame::containing_address(PhysAddr::new(bars[table_bir as usize])), 239 | // // ) 240 | // // .expect("bar"); 241 | 242 | // let ptr: VirtAddr = virt_bar.start_address() 243 | // + (table_offset as u64) 244 | // + (table_index as u64) * 16; 245 | 246 | // use core::intrinsics::{volatile_load, volatile_store}; 247 | 248 | // let table = unsafe { volatile_load(ptr.as_ptr() as *const u128) }; 249 | 250 | // bitfield! { 251 | // pub struct TableEntry(u128); 252 | // impl Debug; 253 | // // The fields default to u16 254 | // u64, address, set_address : 63, 0; 255 | // pub data, set_data : 95, 64; 256 | // pub mask, set_mask : 96; 257 | // pub reserved , _: 127, 97; 258 | 259 | // } 260 | 261 | // let mut table = TableEntry(table); 262 | // log::info!("{:?}", table); 263 | // table.set_data(vector_base as u128 + table_index as u128); 264 | 265 | // table.set_address(0xFEE00000 + (0 << 12)); 266 | // table.set_mask(false); 267 | // unsafe { volatile_store(ptr.as_mut_ptr() as *mut u128, table.0) }; 268 | // // log::info!("{:?}", table); 269 | 270 | // //READ PENDING 271 | // // { 272 | // // let virt_bar = create_virt_from_phys( 273 | // // &mut mapper, 274 | // // &mut frame_allocator, 275 | // // PhysFrame::containing_address(PhysAddr::new( 276 | // // bars[pba_bir as usize], 277 | // // )), 278 | // // ) 279 | // // .expect("bar"); 280 | 281 | // // let ptr: VirtAddr = virt_bar.start_address() 282 | // // + (pba_offset as u64) 283 | // // + (table_index as u64) * 2; 284 | 285 | // // let table = unsafe { volatile_load(ptr.as_ptr() as *const u64) }; 286 | // // if table != 0 { 287 | // // log::info!("pending {:b}", table); 288 | // // } 289 | // // } 290 | // } 291 | 292 | // let command = pci.config_read_u16(4); 293 | // log::info!("com {:b}", command); 294 | // pci.config_write_u16(4, command | 0b11); 295 | 296 | // unsafe { 297 | // crate::local_apic::LocalApic.get().unwrap().eoi(); 298 | // }; 299 | // } 300 | // } 301 | 302 | current_off = pci.config_read_u8(1 + current_off); 303 | 304 | if current_off == 0 { 305 | break; 306 | } 307 | // break; 308 | } 309 | 310 | // log::info!("virtio_caps {:?}", virtio_caps); 311 | 312 | if common.is_none() || pci_conf.is_none() || device.is_none() || notify.is_none() { 313 | return None; 314 | } 315 | 316 | let mut common = common.unwrap(); 317 | let mut pci_conf = pci_conf.unwrap(); 318 | let mut device = device.unwrap(); 319 | let mut notify = notify.unwrap(); 320 | 321 | let cap_common = &mut common.cap; 322 | 323 | unsafe { 324 | let mut queues = Vec::new(); 325 | write_volatile(&mut cap_common.device_status, 0); 326 | 327 | write_volatile( 328 | &mut cap_common.device_status, 329 | read_volatile(&cap_common.device_status) | VirtioStatusAcknowledge, 330 | ); 331 | 332 | write_volatile( 333 | &mut cap_common.device_status, 334 | read_volatile(&cap_common.device_status) | VirtioStatusDriver, 335 | ); 336 | 337 | let current = read_volatile(*cap_common); 338 | log::info!("{:?}", current); 339 | match device_type { 340 | DeviceType::Gpu => { 341 | write_volatile(&mut cap_common.driver_feature, 0b11); 342 | } 343 | _ => { 344 | write_volatile(&mut cap_common.driver_feature, 0); 345 | } 346 | } 347 | 348 | write_volatile( 349 | &mut cap_common.device_status, 350 | read_volatile(&cap_common.device_status) | VirtioStatusFeatureOk, 351 | ); 352 | 353 | if read_volatile(&cap_common.device_status) & VirtioStatusFeatureOk == 0 { 354 | panic!("Cant enable set of feature") 355 | } 356 | 357 | for q in 0..cap_common.num_queues { 358 | write_volatile(&mut cap_common.queue_select, q); 359 | 360 | let q = QueueFreeDescs::new(read_volatile(*cap_common).queue_size); 361 | queues.push(q); 362 | 363 | // log::info!("q{} len {} ", q, cap.queue_size); 364 | 365 | write_volatile( 366 | &mut cap_common.queue_desc, 367 | create_identity_virt_from_phys(mapper, frame_allocator) 368 | .unwrap() 369 | .start_address() 370 | .as_u64(), 371 | ); 372 | 373 | { 374 | let descs = cap_common.queue_desc as *mut Desc; 375 | let qsize = read_volatile(&mut cap_common.queue_size) as isize; 376 | log::info!("qsize {} {}", qsize, cap_common.queue_desc); 377 | for idesc in 0..qsize { 378 | let elem_ptr = descs.offset(idesc); 379 | elem_ptr.write_volatile(Desc { 380 | addr: create_identity_virt_from_phys(mapper, frame_allocator) 381 | .unwrap() 382 | .start_address() 383 | .as_u64(), 384 | // addr: ALLOCATOR.alloc_zeroed( 385 | // Layout::from_size_align_unchecked(4096, 4096), 386 | // ) as u64, 387 | flags: VIRTQ_DESC_F_WRITE, 388 | len: 4096, 389 | next: 0xffff, 390 | }); 391 | } 392 | } 393 | 394 | write_volatile( 395 | &mut cap_common.queue_driver, 396 | create_identity_virt_from_phys(mapper, frame_allocator) 397 | .unwrap() 398 | .start_address() 399 | .as_u64(), 400 | ); 401 | 402 | //DISABLE DEVICE TO DRIVER NOTIFICATION (Interrupt) 403 | (cap_common.queue_driver as *mut u16).write_volatile(1); 404 | 405 | write_volatile( 406 | &mut cap_common.queue_device, 407 | create_identity_virt_from_phys(mapper, frame_allocator) 408 | .unwrap() 409 | .start_address() 410 | .as_u64(), 411 | ); 412 | 413 | // (cap.queue_device as *mut u16).write_volatile(1); 414 | 415 | write_volatile(&mut cap_common.queue_enable, 1); 416 | } 417 | 418 | let cap_device = &mut device.cap; 419 | 420 | match device_type { 421 | DeviceType::Input => { 422 | let conf_ptr: *mut VirtioInputConfig = 423 | core::intrinsics::transmute((*cap_device) as *mut ()); 424 | let rconf = read_volatile(conf_ptr); 425 | let conf: &mut VirtioInputConfig = conf_ptr.as_mut().unwrap(); 426 | write_volatile(&mut conf.select, 1); 427 | let u = read_volatile((&conf.u)); 428 | log::info!( 429 | "name: {:?}", 430 | alloc::str::from_utf8_unchecked( 431 | &u.bitmap[0..read_volatile(&conf.size) as usize] 432 | ) 433 | ); 434 | write_volatile(&mut conf.select, 0); 435 | } 436 | DeviceType::Gpu => { 437 | #[repr(C)] 438 | #[derive(Clone, Debug)] 439 | struct VirtioGpuConfig { 440 | events_read: u32, 441 | events_clear: u32, 442 | num_scanouts: u32, 443 | num_capsets: u32, 444 | } 445 | let conf_ptr: *mut VirtioGpuConfig = 446 | core::intrinsics::transmute((*cap_device) as *mut ()); 447 | let mut rconf = conf_ptr.read_volatile(); 448 | log::info!("{:?}", rconf); 449 | // rconf.events_clear = 1; 450 | // conf_ptr.write_volatile(rconf); 451 | } 452 | _ => {} 453 | } 454 | 455 | write_volatile( 456 | &mut cap_common.device_status, 457 | read_volatile(&cap_common.device_status) | VirtioStatusDriverOk, 458 | ); 459 | 460 | let mut this = Self { 461 | pci: pci.clone(), 462 | last_used_idx: [u16::MAX; MAX_NUM_QUEUE], 463 | step: 0, 464 | 465 | device_type, 466 | queues_free: queues, 467 | 468 | common, 469 | device, 470 | notify, 471 | pci_conf, 472 | queue_select: 0, 473 | }; 474 | this.queue_select(0); 475 | Some(this) 476 | } 477 | } 478 | 479 | pub fn get_free_desc_id(&mut self) -> Option { 480 | self.queues_free[self.queue_select as usize].get_free() 481 | } 482 | pub fn get_free_twice_desc_id(&mut self) -> Option<(u16, u16)> { 483 | self.queues_free[self.queue_select as usize].get_free_twice() 484 | } 485 | pub fn set_free_desc_id(&mut self, desc_id: u16) { 486 | self.queues_free[self.queue_select as usize].set_free(desc_id); 487 | } 488 | 489 | pub fn queue_select(&mut self, q: u16) { 490 | unsafe { 491 | self.queue_select = q; 492 | write_volatile(&mut self.common.cap.queue_select, q); 493 | } 494 | } 495 | pub fn set_available(&mut self, desc_id: u16) { 496 | unsafe { 497 | let queue = read_volatile(self.common.cap); 498 | let driver_idx = (self.common.cap.queue_driver as *mut u8).offset(2) as *mut u16; 499 | let driver_ring_start = (self.common.cap.queue_driver as *mut u8).offset(4) as *mut u16; 500 | let idx = driver_idx.read_volatile(); 501 | let elem_ptr = driver_ring_start.offset(idx as isize % queue.queue_size as isize); 502 | elem_ptr.write_volatile(desc_id); 503 | driver_idx.write_volatile(idx.wrapping_add(1)); 504 | } 505 | } 506 | 507 | pub fn set_writable(&mut self, desc_id: u16) { 508 | unsafe { 509 | let descs = self.common.cap.queue_desc as *mut Desc; 510 | let mut desc = descs.offset(desc_id as isize).read_volatile(); 511 | desc.flags = VIRTQ_DESC_F_WRITE; 512 | desc.len = 4096; 513 | descs.offset(desc_id as isize).write_volatile(desc); 514 | } 515 | } 516 | 517 | pub fn set_writable_available(&mut self, desc_id: u16) { 518 | self.set_writable(desc_id); 519 | self.set_available(desc_id); 520 | } 521 | 522 | pub fn add_request(&mut self, desc_id: u16, desc_next_id: u16, data: T) { 523 | unsafe { 524 | let descs = self.common.cap.queue_desc as *mut Desc; 525 | let mut desc = descs.offset(desc_id as isize).read_volatile(); 526 | desc.len = core::intrinsics::size_of_val(&data) as u32; 527 | // desc.len = data.len() as u32; 528 | let data_ptr = desc.addr as *mut T; 529 | data_ptr.write_volatile(data); 530 | 531 | desc.flags = VIRTQ_DESC_F_NEXT; 532 | desc.next = desc_next_id; 533 | descs.offset(desc_id as isize).write_volatile(desc); 534 | self.set_writable(desc_next_id); 535 | self.set_available(desc_id); 536 | }; 537 | } 538 | 539 | pub fn kick(&mut self, queue_select: u16) { 540 | unsafe { 541 | let queue = read_volatile(self.common.cap); 542 | let VirtioCap { 543 | cap: cap_notify, 544 | bar, 545 | offset: offset_notify, 546 | length, 547 | } = &mut self.notify; 548 | 549 | if let Bar::Mm(addr) = bar { 550 | let queue_notify_address = phys_to_virt(PhysAddr::new( 551 | addr.as_u64() 552 | + (*offset_notify as u64) 553 | + (*cap_notify as u64) * (queue.queue_notify_off as u64), 554 | )); 555 | 556 | // log::info!("kick at {:?}", queue_notify_address); 557 | (queue_notify_address.as_u64() as *mut u16).write_volatile(queue_select); 558 | } 559 | } 560 | } 561 | 562 | pub unsafe fn next_used(&mut self) -> Option { 563 | let queue = read_volatile(self.common.cap); 564 | 565 | let device_idx = (self.common.cap.queue_device as *mut u8).offset(2) as *mut u16; 566 | let idx_next = device_idx.read_volatile(); 567 | let device_ring_start = 568 | (self.common.cap.queue_device as *mut u8).offset(4) as *mut UsedElem; 569 | 570 | let last_used_idx = &mut self.last_used_idx[self.queue_select as usize]; 571 | if last_used_idx.wrapping_add(1) != idx_next { 572 | *last_used_idx = last_used_idx.wrapping_add(1); 573 | let inq_idx = (*last_used_idx as isize) % queue.queue_size as isize; 574 | let elem_ptr = device_ring_start.offset(inq_idx); 575 | let elem = read_volatile(elem_ptr); 576 | Some(elem) 577 | } else { 578 | None 579 | } 580 | } 581 | 582 | pub fn read_desc(&mut self, desc_id: u16) -> Desc { 583 | unsafe { 584 | let descs = self.common.cap.queue_desc as *mut Desc; 585 | descs.offset(desc_id as isize).read_volatile() 586 | } 587 | } 588 | } 589 | 590 | #[repr(C)] 591 | #[derive(Debug, PartialEq)] 592 | pub struct VirtioPciCommonCfg { 593 | // About the whole device. 594 | pub device_feature_select: u32, // read-write 595 | pub device_feature: u32, // read-only for driver 596 | pub driver_feature_select: u32, // read-write 597 | pub driver_feature: u32, // read-write 598 | pub msix_config: u16, // read-write 599 | pub num_queues: u16, // read-only for driver 600 | pub device_status: u8, // read-write 601 | pub config_generation: u8, // read-only for driver 602 | 603 | // About a specific virtqueue. 604 | pub queue_select: u16, // read-write 605 | pub queue_size: u16, // read-write 606 | pub queue_msix_vector: u16, // read-write 607 | pub queue_enable: u16, // read-write 608 | pub queue_notify_off: u16, // read-only for driver 609 | pub queue_desc: u64, // read-write 610 | pub queue_driver: u64, // read-write 611 | pub queue_device: u64, // read-write 612 | } 613 | #[derive(Debug, PartialEq)] 614 | pub struct VirtioCap { 615 | pub cap: T, 616 | pub bar: Bar, 617 | pub offset: u32, 618 | pub length: u32, 619 | } 620 | 621 | impl VirtioCap { 622 | pub fn new(t: T, bar: Bar, offset: u32, length: u32) -> Self { 623 | Self { 624 | cap: t, 625 | bar, 626 | offset, 627 | length, 628 | } 629 | } 630 | } 631 | 632 | const VirtioPciCapCommonCfg: u8 = 1; 633 | const VirtioPciCapNotifyCfg: u8 = 2; 634 | const VirtioPciCapIsrCfg: u8 = 3; 635 | const VirtioPciCapDeviceCfg: u8 = 4; 636 | const VirtioPciCapPciCfg: u8 = 5; 637 | 638 | // Device cfg 639 | #[repr(C)] 640 | #[derive(Debug)] 641 | struct VirtioInputConfig { 642 | select: u8, 643 | subsel: u8, 644 | size: u8, 645 | reserved: [u8; 5], 646 | u: VirtioInputUnion, 647 | } 648 | #[repr(C)] 649 | #[derive(Clone, Copy)] 650 | union VirtioInputUnion { 651 | string: [char; 128], 652 | bitmap: [u8; 128], 653 | abs: VirtioInputAbsInfo, 654 | ids: VirtioInputDevIds, 655 | } 656 | 657 | impl fmt::Debug for VirtioInputUnion { 658 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 659 | unsafe { 660 | f.debug_struct("VirtioInputUnion") 661 | .field("abs", &self.abs) 662 | .field("ids", &self.ids) 663 | .finish() 664 | } 665 | } 666 | } 667 | 668 | #[repr(C)] 669 | #[derive(Debug, Clone, Copy)] 670 | struct VirtioInputAbsInfo { 671 | min: u32, 672 | max: u32, 673 | fuzz: u32, 674 | flat: u32, 675 | resolution: u32, 676 | } 677 | #[repr(C)] 678 | #[derive(Debug, Clone, Copy)] 679 | struct VirtioInputDevIds { 680 | bustype: u16, 681 | vendor: u16, 682 | product: u16, 683 | version: u16, 684 | } 685 | 686 | const VIRTQ_DESC_F_NEXT: u16 = 1; 687 | const VIRTQ_DESC_F_WRITE: u16 = 2; 688 | const VIRTQ_DESC_F_INDIRECT: u16 = 4; 689 | //Queue handle 690 | #[repr(C, align(16))] 691 | #[derive(Clone, Debug, PartialEq)] 692 | pub struct Desc { 693 | pub addr: u64, 694 | pub len: u32, 695 | pub flags: u16, 696 | pub next: u16, 697 | } 698 | 699 | #[repr(C)] 700 | #[derive(Debug)] 701 | pub struct UsedElem { 702 | pub id: u32, 703 | pub len: u32, 704 | } 705 | -------------------------------------------------------------------------------- /bootloader/ovmf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ruddle/Fomos/336be2d5bb2733f27e593dec70034a4f59650efd/bootloader/ovmf -------------------------------------------------------------------------------- /bootloader/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /bootloader/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // read env variables that were set in build script 3 | let uefi_path = env!("UEFI_PATH"); 4 | let bios_path = env!("BIOS_PATH"); 5 | 6 | println!("{}", uefi_path); 7 | 8 | // choose whether to start the UEFI or BIOS image 9 | let uefi = true; 10 | 11 | let mut cmd = std::process::Command::new("qemu-system-x86_64"); 12 | if uefi { 13 | cmd.arg("--enable-kvm"); 14 | cmd.arg("-nodefaults"); 15 | 16 | cmd.arg("-m").arg("600M"); 17 | // cmd.arg("-M").arg("q35"); 18 | // cmd.arg("-device").arg("qemu-xhci"); 19 | // cmd.arg("-device").arg("ahci"); 20 | 21 | cmd.arg("-smp").arg("2"); 22 | //GDB OPTS: 23 | // cmd.arg("-S").arg("-s"); 24 | cmd.arg("-device").arg("virtio-mouse-pci"); 25 | cmd.arg("-device").arg("virtio-keyboard-pci"); 26 | cmd.arg("-nic").arg("user,model=virtio-net-pci"); 27 | 28 | // cmd.arg("-monitor").arg("stdio"); 29 | 30 | // cmd.arg("-device").arg("VGA,vgamem_mb=8"); 31 | // cmd.arg("-device").arg("virtio-vga"); //gl 32 | // on linux guest cmd.arg("-display").arg("gtk,gl=on"); 33 | // cmd.arg("-device").arg("virtio-gpu"); 34 | cmd.arg("-device").arg("virtio-vga-gl"); 35 | cmd.arg("-display").arg("sdl,gl=on"); 36 | 37 | // cmd.arg("-vga").arg("none"); 38 | 39 | // cmd.arg("-device").arg("bochs-display"); 40 | // cmd.arg("-device").arg("qxl-vga"); 41 | 42 | // cmd.arg("-nic").arg("none"); 43 | 44 | cmd.arg("-serial").arg("stdio"); 45 | 46 | cmd.arg("-pflash").arg("./ovmf"); 47 | cmd.arg("-drive") 48 | .arg(format!("format=raw,file={uefi_path}")); 49 | } else { 50 | cmd.arg("-drive") 51 | .arg(format!("format=raw,file={bios_path}")); 52 | } 53 | let mut child = cmd.spawn().unwrap(); 54 | child.wait().unwrap(); 55 | } 56 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd app_background 3 | RUSTFLAGS="-C relocation-model=pie -C link-arg=-pie" cargo build --release 4 | cd .. 5 | cd app_cursor 6 | RUSTFLAGS="-C relocation-model=pie -C link-arg=-pie" cargo build --release 7 | cd .. 8 | cd app_console 9 | RUSTFLAGS="-C relocation-model=pie -C link-arg=-pie" cargo build --release 10 | cd .. 11 | cd app_test 12 | RUSTFLAGS="-C relocation-model=pie -C link-arg=-pie" cargo build --release 13 | cd .. 14 | cd app_c 15 | ./build.sh 16 | cd .. 17 | 18 | cd bootloader 19 | cargo run --release 20 | -------------------------------------------------------------------------------- /docs/BUILD.md: -------------------------------------------------------------------------------- 1 | # How to build and run Fomos in an emulator 2 | 3 | Draft version 4 | 5 | ## Dependencies 6 | 7 | - rust 8 | - qemu 9 | 10 | ### Rust 11 | 12 | [Install Rust](https://www.rust-lang.org/tools/install) 13 | 14 | Then, the nightly version of the compiler is needed: 15 | 16 | ``` 17 | rustup toolchain install nightly 18 | ``` 19 | 20 | Additionnal components might be compiler specific, switch to the nightly version before installing them. 21 | 22 | ``` 23 | rustup default nightly 24 | ``` 25 | 26 | Fomos needs to be cross compiled. It is x86_64 only for now, add the appropriate compiler target: 27 | 28 | ``` 29 | rustup target add x86_64-unknown-none 30 | ``` 31 | 32 | On some system you need specific components. Example for my macbook air 2015. 33 | 34 | ``` 35 | rustup component add rust-src --toolchain nightly-x86_64-apple-darwin 36 | ``` 37 | 38 | ``` 39 | rustup component add llvm-tools 40 | ``` 41 | 42 | The rust compiler will guide you with errors in the terminal while building. 43 | 44 | ### QEMU 45 | 46 | [Install QEMU](https://www.qemu.org/) 47 | 48 | You might need to compile it yourself with the SDL option. You can also remove SDL in the next step otherwise. 49 | 50 | # Build and run 51 | 52 | In the root of the project, execute `./build.sh`. 53 | It should build all the independent apps one by one, and finally build the OS, and run it in qemu. 54 | 55 | There are some qemu launch parameters in `./bootloader/src/main.rs` 56 | 57 | By default they suppose you have a KVM capable machine, and qemu with SDL. 58 | 59 | ### I have a different qemu 60 | 61 | If you do not have qemu with SDL replace 62 | 63 | ```rust 64 | cmd.arg("-device").arg("virtio-vga-gl"); 65 | cmd.arg("-display").arg("sdl,gl=on"); 66 | ``` 67 | 68 | with 69 | 70 | ```rust 71 | cmd.arg("-device").arg("virtio-vga"); 72 | ``` 73 | 74 | ### I don't have KVM 75 | 76 | KVM is linux specific. If you do not have KVM, remove the --enable-kvm option. This makes the emulation extremely slow. Remove that: 77 | 78 | ```rust 79 | cmd.arg("--enable-kvm"); 80 | ``` 81 | 82 | On **macOS**, you can replace KVM with HVF for a fast emulation, add that: 83 | 84 | ```rust 85 | cmd.arg("-accel").arg("hvf"); 86 | ``` 87 | --------------------------------------------------------------------------------