├── .gitignore ├── .rustfmt.toml ├── .gitattributes ├── src ├── syscall │ ├── nothing.rs │ ├── exit.rs │ ├── invalid.rs │ ├── write.rs │ └── mod.rs ├── synch │ ├── mod.rs │ ├── mutex.rs │ └── spinlock.rs ├── collections │ └── mod.rs ├── arch │ ├── x86 │ │ ├── kernel │ │ │ ├── link_i686.ld │ │ │ ├── entry32.s │ │ │ ├── syscall.rs │ │ │ ├── serial.rs │ │ │ ├── pit.rs │ │ │ ├── start.rs │ │ │ ├── switch.rs │ │ │ ├── vga.rs │ │ │ ├── task.rs │ │ │ ├── processor.rs │ │ │ ├── gdt.rs │ │ │ ├── mod.rs │ │ │ └── irq.rs │ │ ├── mm │ │ │ ├── physicalmem.rs │ │ │ ├── virtualmem.rs │ │ │ └── mod.rs │ │ └── mod.rs │ └── mod.rs ├── console.rs ├── mm │ ├── mod.rs │ ├── linked_list.rs │ ├── buddy.rs │ └── freelist.rs ├── consts.rs ├── macros.rs ├── fd │ ├── stdio.rs │ └── mod.rs ├── main.rs ├── lib.rs ├── logging.rs ├── scheduler │ ├── mod.rs │ ├── task.rs │ └── scheduler.rs ├── io.rs ├── fs │ ├── initrd.rs │ ├── mod.rs │ └── vfs.rs └── errno.rs ├── demo ├── hello ├── hello.c └── Makefile ├── rust-toolchain.toml ├── test.sh ├── test.ps1 ├── .cargo └── config.toml ├── i686-eduos.json ├── .github ├── workflows │ ├── format.yml │ ├── clippy.yml │ └── run.yml └── dependabot.yml ├── x86_64-eduos.json ├── CITATION.cff ├── LICENSE-MIT ├── Cargo.toml ├── README.md ├── Cargo.lock └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | demo/hello filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /src/syscall/nothing.rs: -------------------------------------------------------------------------------- 1 | pub(crate) extern "C" fn sys_nothing() -> i32 { 2 | 0 3 | } 4 | -------------------------------------------------------------------------------- /src/synch/mod.rs: -------------------------------------------------------------------------------- 1 | //! Synchronization primitives 2 | 3 | pub mod mutex; 4 | pub mod spinlock; 5 | -------------------------------------------------------------------------------- /demo/hello: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:193911b90dbe33053fe3c7a69e3c1228d6c3bf1e6bf28d08e440862ae8f01de9 3 | size 15248 4 | -------------------------------------------------------------------------------- /demo/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) 4 | { 5 | printf("Hello World from Linux!\n"); 6 | 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-10-01" 3 | components = [ 4 | "clippy", 5 | "rustfmt", 6 | "rust-src", 7 | "llvm-tools-preview", 8 | ] 9 | -------------------------------------------------------------------------------- /src/syscall/exit.rs: -------------------------------------------------------------------------------- 1 | use crate::logging::*; 2 | use crate::scheduler::*; 3 | 4 | pub(crate) extern "C" fn sys_exit() { 5 | debug!("enter syscall exit"); 6 | do_exit(); 7 | } 8 | -------------------------------------------------------------------------------- /demo/Makefile: -------------------------------------------------------------------------------- 1 | default: hello.o 2 | musl-gcc -fPIE -pie -Wall -static-pie -o hello hello.o 3 | 4 | hello.o: hello.c 5 | musl-gcc -fPIE -pie -Wall -c -o hello.o hello.c 6 | 7 | clean: 8 | rm -rf hello *.o 9 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cargo run 3 | if [ $? -eq 5 ]; then 4 | echo "eduOS-rs runs succesfully within Qemu" 5 | exit 0 6 | else 7 | echo "eduOS-rs isn't able to run within Qemu" 8 | exit 1 9 | fi 10 | -------------------------------------------------------------------------------- /test.ps1: -------------------------------------------------------------------------------- 1 | Invoke-Command -Script { cargo run } -ErrorAction SilentlyContinue 2 | IF( $LASTEXITCODE -EQ 5 ) { 3 | Write-Output "eduOS-rs runs successfully within Qemu" 4 | Exit 0 5 | } else { 6 | Write-Output "eduOS-rs isn't able to run within Qemu" 7 | Exit 1 8 | } 9 | -------------------------------------------------------------------------------- /src/collections/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::irq::*; 2 | 3 | /// `irqsave` guarantees that the call of the closure 4 | /// will be not disturbed by an interrupt 5 | #[inline] 6 | pub fn irqsave(f: F) -> R 7 | where 8 | F: FnOnce() -> R, 9 | { 10 | let irq = irq_nested_disable(); 11 | let ret = f(); 12 | irq_nested_enable(irq); 13 | ret 14 | } 15 | -------------------------------------------------------------------------------- /src/syscall/invalid.rs: -------------------------------------------------------------------------------- 1 | use crate::logging::*; 2 | use crate::scheduler::*; 3 | use core::arch::naked_asm; 4 | 5 | extern "C" fn invalid_syscall(sys_no: u64) -> ! { 6 | error!("Invalid syscall {}", sys_no); 7 | do_exit(); 8 | } 9 | 10 | #[allow(unused_assignments)] 11 | #[unsafe(naked)] 12 | pub(crate) unsafe extern "C" fn sys_invalid() { 13 | naked_asm!("mov rdi, rax", 14 | "call {}", 15 | sym invalid_syscall, 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std = ["core", "alloc"] 3 | build-std-features = ["compiler-builtins-mem"] 4 | 5 | [build] 6 | target = "x86_64-eduos.json" 7 | 8 | [target.i686-eduos] 9 | rustflags = [ 10 | "-C", "link-arg=-Tsrc/arch/x86/kernel/link_i686.ld", "-C", "relocation-model=static" 11 | ] 12 | runner = "qemu-system-x86_64 -display none -serial stdio -smp 1 -m 256M -device isa-debug-exit,iobase=0xf4,iosize=0x04 -kernel" 13 | 14 | [target.x86_64-eduos] 15 | runner = "bootimage runner" 16 | -------------------------------------------------------------------------------- /i686-eduos.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "i686-unknown-none", 3 | "target-endian": "little", 4 | "target-pointer-width": 32, 5 | "target-c-int-width": 32, 6 | "os": "none", 7 | "arch": "x86", 8 | "rustc-abi": "x86-softfloat", 9 | "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", 10 | "features": "-mmx,-sse,+soft-float", 11 | "executables": true, 12 | "linker-flavor": "ld.lld", 13 | "linker": "rust-lld", 14 | "panic-strategy": "abort" 15 | } 16 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/link_i686.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | phys = 0x000000100000; 3 | 4 | SECTIONS 5 | { 6 | kernel_start = phys; 7 | .mboot phys : { 8 | KEEP(*(.mboot)) 9 | KEEP(*(.mboot.*)) 10 | } 11 | .text ALIGN(4) : { 12 | *(.text) 13 | *(.text.*) 14 | } 15 | .rodata ALIGN(4) : { 16 | *(.rodata) 17 | *(.rodata.*) 18 | } 19 | .data ALIGN(4) : { 20 | *(.data) 21 | *(.data.*) 22 | } 23 | .bss ALIGN(4) : { 24 | __bss_start = .; 25 | *(.bss) 26 | *(.bss.*) 27 | __bss_end = .; 28 | } 29 | } -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | //! A wrapper around our serial console. 2 | 3 | #[cfg(not(feature = "vga"))] 4 | use crate::arch::serial; 5 | #[cfg(feature = "vga")] 6 | use crate::arch::vga; 7 | use core::fmt; 8 | 9 | pub struct Console; 10 | 11 | impl fmt::Write for Console { 12 | /// Output a string to each of our console outputs. 13 | fn write_str(&mut self, s: &str) -> fmt::Result { 14 | cfg_if::cfg_if! { 15 | if #[cfg(feature = "vga")] { 16 | vga::VGA_SCREEN.lock().write_str(s) 17 | } else { 18 | serial::COM1.lock().write_str(s) 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Check format 2 | 3 | on: 4 | push: 5 | branches: 6 | - stage9 7 | - main 8 | pull_request: 9 | branches: 10 | - stage9 11 | - main 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ${{ matrix.os }} 17 | 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest] 21 | 22 | steps: 23 | - uses: actions/checkout@v6 24 | with: 25 | submodules: true 26 | - name: Check Cargo availability 27 | run: cargo --version 28 | - name: Check Formatting 29 | run: cargo fmt -- --check 30 | -------------------------------------------------------------------------------- /x86_64-eduos.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "target-endian": "little", 4 | "target-pointer-width": 64, 5 | "target-c-int-width": 32, 6 | "os": "none", 7 | "arch": "x86_64", 8 | "rustc-abi": "x86-softfloat", 9 | "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", 10 | "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float", 11 | "disable-redzone": true, 12 | "executables": true, 13 | "linker-flavor": "ld.lld", 14 | "linker": "rust-lld", 15 | "panic-strategy": "abort" 16 | } 17 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Lankes" 5 | given-names: "Stefan" 6 | orcid: "https://orcid.org/0000-0003-4718-2238" 7 | - family-names: "Klimt" 8 | given-names: "Jonathan" 9 | orcid: "https://orcid.org/0000-0002-5980-2214" 10 | - family-names: "Kröning" 11 | given-names: "Martin" 12 | orcid: "https://orcid.org/0009-0005-0622-4229" 13 | 14 | title: "eduOS-rs - A teaching operating system written in Rust" 15 | version: 0.1.0 16 | doi: 10.5281/zenodo.14675628 17 | date-released: 2025-01-16 18 | url: "https://github.com/RWTH-OS/eduOS-rs" 19 | -------------------------------------------------------------------------------- /src/mm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod freelist; 2 | 3 | use crate::arch; 4 | use crate::arch::mm::get_memory_size; 5 | use crate::arch::processor::shutdown; 6 | use crate::logging::*; 7 | pub(crate) mod buddy; 8 | pub(crate) mod linked_list; 9 | 10 | #[cfg(not(test))] 11 | use alloc::alloc::Layout; 12 | 13 | pub(crate) fn init() { 14 | info!("Memory size {} MByte", get_memory_size() >> 20); 15 | 16 | arch::mm::init(); 17 | } 18 | 19 | #[cfg(not(test))] 20 | #[alloc_error_handler] 21 | pub fn rust_oom(layout: Layout) -> ! { 22 | println!( 23 | "[!!!OOM!!!] Memory allocation of {} bytes failed", 24 | layout.size() 25 | ); 26 | 27 | shutdown(1); 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | name: Clippy 2 | 3 | on: 4 | push: 5 | branches: 6 | - stage9 7 | - main 8 | pull_request: 9 | branches: 10 | - stage9 11 | - main 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ${{ matrix.os }} 17 | 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest] 21 | 22 | steps: 23 | - uses: actions/checkout@v6 24 | with: 25 | submodules: true 26 | - name: Check Cargo availability 27 | run: cargo --version 28 | - name: Check clippy (x86_64) 29 | run: cargo clippy 30 | #- name: Check clippy (i688) 31 | # run: cargo clippy --target i686-eduos.json 32 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "cargo" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | timezone: "Europe/Berlin" 10 | # Automatic rebases cancel pending bors merges 11 | rebase-strategy: "disabled" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | timezone: "Europe/Berlin" 17 | # Automatic rebases cancel pending bors merges 18 | rebase-strategy: "disabled" 19 | -------------------------------------------------------------------------------- /src/consts.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | //! Configuration parameter of the kernel eduOS-rs 4 | 5 | use crate::arch::mm::VirtAddr; 6 | 7 | /// Define the size of the kernel stack 8 | pub(crate) const STACK_SIZE: usize = 0x3000; 9 | 10 | /// Define the size of the interrupt stack 11 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 12 | pub(crate) const INTERRUPT_STACK_SIZE: usize = 0x3000; 13 | 14 | /// Size of a cache line 15 | pub(crate) const CACHE_LINE: usize = 64; 16 | 17 | /// Maximum number of priorities 18 | pub const NO_PRIORITIES: usize = 32; 19 | 20 | /// frequency of the timer interrupt 21 | pub(crate) const TIMER_FREQ: u32 = 100; /* in HZ */ 22 | 23 | /// Entry point of the user tasks 24 | pub const USER_ENTRY: VirtAddr = VirtAddr(0x20000000000u64); 25 | 26 | /// Size of the kernel heap 27 | pub(crate) const HEAP_SIZE: usize = 8 * 1024 * 1024; 28 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Print formatted text to our console. 2 | /// 3 | /// From http://blog.phil-opp.com/rust-os/printing-to-screen.html, but tweaked 4 | /// to work with our APIs. 5 | #[macro_export] 6 | macro_rules! print { 7 | ($($arg:tt)*) => ({ 8 | use core::fmt::Write; 9 | let mut console = $crate::console::Console {}; 10 | console.write_fmt(format_args!($($arg)*)).unwrap(); 11 | }); 12 | } 13 | 14 | /// Print formatted text to our console, followed by a newline. 15 | /// 16 | /// From https://doc.rust-lang.org/nightly/std/macro.println!.html 17 | #[macro_export] 18 | macro_rules! println { 19 | ($fmt:expr) => (print!(concat!($fmt, "\n"))); 20 | ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); 21 | } 22 | 23 | macro_rules! align_down { 24 | ($value:expr, $alignment:expr) => { 25 | $value & !($alignment - 1) 26 | }; 27 | } 28 | 29 | macro_rules! align_up { 30 | ($value:expr, $alignment:expr) => { 31 | align_down!($value + ($alignment - 1), $alignment) 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/syscall/write.rs: -------------------------------------------------------------------------------- 1 | use crate::fd::FileDescriptor; 2 | use crate::logging::*; 3 | 4 | #[repr(C)] 5 | pub struct IoVec { 6 | pub iov_base: *const u8, 7 | pub iov_len: usize, 8 | } 9 | 10 | pub(crate) unsafe extern "C" fn sys_writev( 11 | fd: FileDescriptor, 12 | ptr: *const IoVec, 13 | cnt: i32, 14 | ) -> isize { 15 | debug!("Enter syscall writev"); 16 | let mut len: isize = 0; 17 | let iovec = core::slice::from_raw_parts(ptr, cnt as usize); 18 | 19 | for i in iovec { 20 | let slice = core::slice::from_raw_parts(i.iov_base, i.iov_len); 21 | 22 | let tmp: isize = crate::fd::write(fd, slice).map_or_else( 23 | |e| -num::ToPrimitive::to_isize(&e).unwrap(), 24 | |v| v.try_into().unwrap(), 25 | ); 26 | 27 | len += tmp; 28 | if tmp < i.iov_len as isize { 29 | break; 30 | } 31 | } 32 | 33 | len 34 | } 35 | 36 | pub(crate) unsafe extern "C" fn sys_write(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize { 37 | debug!("Enter syscall write"); 38 | let slice = unsafe { core::slice::from_raw_parts(buf, len) }; 39 | crate::fd::write(fd, slice).map_or_else( 40 | |e| -num::ToPrimitive::to_isize(&e).unwrap(), 41 | |v| v.try_into().unwrap(), 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/entry32.s: -------------------------------------------------------------------------------- 1 | # This is the kernel's entry point. We could either call main here, 2 | # or we can use this to setup the stack or other nice stuff, like 3 | # perhaps setting up the GDT and segments. Please note that interrupts 4 | # are disabled at this point: More on interrupts later! 5 | 6 | .code32 7 | 8 | # We use a special name to map this section at the begin of our kernel 9 | # => Multiboot expects its magic number at the beginning of the kernel. 10 | .section .mboot, "a" 11 | 12 | # This part MUST be 4 byte aligned, so we solve that issue using '.align 4'. 13 | .align 4 14 | mboot: 15 | # Multiboot macros to make a few lines more readable later 16 | .set MULTIBOOT_PAGE_ALIGN, (1 << 0) 17 | .set MULTIBOOT_MEMORY_INFO, (1 << 1) 18 | .set MULTIBOOT_HEADER_MAGIC, 0x1BADB002 19 | .set MULTIBOOT_HEADER_FLAGS, MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO 20 | .set MULTIBOOT_CHECKSUM, -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) 21 | 22 | # This is the GRUB Multiboot header. A boot signature 23 | .4byte MULTIBOOT_HEADER_MAGIC 24 | .4byte MULTIBOOT_HEADER_FLAGS 25 | .4byte MULTIBOOT_CHECKSUM 26 | .4byte 0, 0, 0, 0, 0 # address fields -------------------------------------------------------------------------------- /src/arch/x86/kernel/syscall.rs: -------------------------------------------------------------------------------- 1 | use crate::syscall::SYSHANDLER_TABLE; 2 | use core::arch::naked_asm; 3 | 4 | /// Helper function to save and to restore the register states 5 | /// during a system call. `rax` is the system call identifier. 6 | /// The identifier is used to determine the address of the function, 7 | /// which implements the system call. 8 | #[unsafe(naked)] 9 | pub(crate) extern "C" fn syscall_handler() { 10 | naked_asm!( 11 | // save context, see x86_64 ABI 12 | "push rcx", 13 | "push rdx", 14 | "push rsi", 15 | "push rdi", 16 | "push r8", 17 | "push r9", 18 | "push r10", 19 | "push r11", 20 | // switch to kernel stack 21 | "swapgs", 22 | "mov rcx, rsp", 23 | "rdgsbase rsp", 24 | "push rcx", 25 | // copy 4th argument to rcx to adhere x86_64 ABI 26 | "mov rcx, r10", 27 | "sti", 28 | "call [{sys_handler}+8*rax]", 29 | // restore context, see x86_64 ABI 30 | "cli", 31 | // switch to user stack 32 | "pop rcx", 33 | "mov rsp, rcx", 34 | "swapgs", 35 | "pop r11", 36 | "pop r10", 37 | "pop r9", 38 | "pop r8", 39 | "pop rdi", 40 | "pop rsi", 41 | "pop rdx", 42 | "pop rcx", 43 | "sysretq", 44 | sys_handler = sym SYSHANDLER_TABLE); 45 | } 46 | -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | /// Currently, eduOS supports only x86_64 (64 bit) 2 | /// and x86 (32 bit) code. Both architecture are similar 3 | /// and share the code in the directory x86 4 | 5 | // Implementations for x86_64 and x86. 6 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 7 | pub mod x86; 8 | 9 | // Export our platform-specific modules. 10 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 11 | pub(crate) use self::x86::kernel::{init, processor, register_task, switch::switch}; 12 | 13 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 14 | pub use self::x86::kernel::irq; 15 | 16 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 17 | pub use self::x86::load_application; 18 | 19 | #[cfg(feature = "vga")] 20 | pub(crate) use self::x86::kernel::vga; 21 | 22 | #[cfg(not(feature = "vga"))] 23 | pub(crate) use self::x86::kernel::serial; 24 | 25 | // Export our platform-specific modules. 26 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 27 | pub(crate) use self::x86::mm; 28 | 29 | // Export our platform-specific modules. 30 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 31 | pub(crate) use self::x86::mm::paging::{ 32 | drop_user_space, get_kernel_root_page_table, BasePageSize, PageSize, 33 | }; 34 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/serial.rs: -------------------------------------------------------------------------------- 1 | use crate::synch::spinlock::SpinlockIrqSave; 2 | use core::fmt; 3 | use x86::io::*; 4 | 5 | /// A COM serial port. 6 | pub(crate) struct ComPort { 7 | /// COM ports are identified by the base address of their associated 8 | /// I/O registers. 9 | base_addr: u16, 10 | } 11 | 12 | impl ComPort { 13 | /// Create a new COM port with the specified base address. 14 | const fn new(base_addr: u16) -> Self { 15 | Self { base_addr } 16 | } 17 | 18 | pub fn write_bytes(&mut self, buf: &[u8]) { 19 | unsafe { 20 | // Output each byte of our string. 21 | for &b in buf { 22 | // Write our byte. 23 | outb(self.base_addr, b); 24 | } 25 | } 26 | } 27 | } 28 | 29 | impl fmt::Write for ComPort { 30 | /// Output a string to our COM port. This allows using nice, 31 | /// high-level tools like Rust's `write!` macro. 32 | fn write_str(&mut self, s: &str) -> fmt::Result { 33 | unsafe { 34 | // Output each byte of our string. 35 | for &b in s.as_bytes() { 36 | // Write our byte. 37 | outb(self.base_addr, b); 38 | } 39 | } 40 | 41 | Ok(()) 42 | } 43 | } 44 | 45 | /// Our primary serial port. 46 | pub(crate) static COM1: SpinlockIrqSave = SpinlockIrqSave::new(ComPort::new(0x3F8)); 47 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/pit.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::processor::*; 2 | use crate::consts::*; 3 | use crate::logging::*; 4 | use x86::io::*; 5 | use x86::time::rdtsc; 6 | 7 | const CLOCK_TICK_RATE: u32 = 1193182u32; /* 8254 chip's internal oscillator frequency */ 8 | 9 | unsafe fn wait_some_time() { 10 | let start = rdtsc(); 11 | 12 | mb(); 13 | while rdtsc() - start < 1000000 { 14 | mb(); 15 | } 16 | } 17 | 18 | /// initialize the Programmable Interrupt Controller (PIC) 19 | pub(crate) fn init() { 20 | debug!("initialize timer"); 21 | 22 | let latch = ((CLOCK_TICK_RATE + TIMER_FREQ / 2) / TIMER_FREQ) as u16; 23 | 24 | unsafe { 25 | /* 26 | * Port 0x43 is for initializing the PIT: 27 | * 28 | * 0x34 means the following: 29 | * 0b... (step-by-step binary representation) 30 | * ... 00 - channel 0 31 | * ... 11 - write two values to counter register: 32 | * first low-, then high-byte 33 | * ... 010 - mode number 2: "rate generator" / frequency divider 34 | * ... 0 - binary counter (the alternative is BCD) 35 | */ 36 | outb(0x43, 0x34); 37 | 38 | wait_some_time(); 39 | 40 | /* Port 0x40 is for the counter register of channel 0 */ 41 | 42 | outb(0x40, (latch & 0xFF) as u8); /* low byte */ 43 | 44 | wait_some_time(); 45 | 46 | outb(0x40, (latch >> 8) as u8); /* high byte */ 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/fd/stdio.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "vga"))] 2 | use crate::arch::serial; 3 | #[cfg(feature = "vga")] 4 | use crate::arch::vga; 5 | use crate::fd::IoInterface; 6 | use crate::io; 7 | 8 | #[derive(Debug)] 9 | pub(crate) struct GenericStdin; 10 | 11 | impl IoInterface for GenericStdin {} 12 | 13 | impl GenericStdin { 14 | pub const fn new() -> Self { 15 | Self {} 16 | } 17 | } 18 | 19 | #[derive(Debug)] 20 | pub(crate) struct GenericStdout; 21 | 22 | impl IoInterface for GenericStdout { 23 | fn write(&self, buf: &[u8]) -> io::Result { 24 | cfg_if::cfg_if! { 25 | if #[cfg(feature = "vga")] { 26 | vga::VGA_SCREEN.lock().write_bytes(buf); 27 | } else { 28 | serial::COM1.lock().write_bytes(buf); 29 | } 30 | } 31 | 32 | Ok(buf.len()) 33 | } 34 | } 35 | 36 | impl GenericStdout { 37 | pub const fn new() -> Self { 38 | Self {} 39 | } 40 | } 41 | 42 | #[derive(Debug)] 43 | pub(crate) struct GenericStderr; 44 | 45 | impl IoInterface for GenericStderr { 46 | fn write(&self, buf: &[u8]) -> io::Result { 47 | cfg_if::cfg_if! { 48 | if #[cfg(feature = "vga")] { 49 | vga::VGA_SCREEN.lock().write_bytes(buf); 50 | } else { 51 | serial::COM1.lock().write_bytes(buf); 52 | } 53 | } 54 | 55 | Ok(buf.len()) 56 | } 57 | } 58 | 59 | impl GenericStderr { 60 | pub const fn new() -> Self { 61 | Self {} 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // don't link the Rust standard library 2 | #![cfg_attr(not(test), no_main)] // disable all Rust-level entry points 3 | #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] 4 | 5 | #[macro_use] 6 | extern crate eduos_rs; 7 | extern crate alloc; 8 | 9 | use alloc::string::String; 10 | use eduos_rs::arch; 11 | use eduos_rs::arch::load_application; 12 | use eduos_rs::scheduler; 13 | use eduos_rs::scheduler::task::NORMAL_PRIORITY; 14 | use eduos_rs::{LogLevel, LOGGER}; 15 | 16 | extern "C" fn create_user_foo() { 17 | let path = String::from("/bin/demo"); 18 | 19 | info!("Hello from loader"); 20 | 21 | // load application 22 | if load_application(&path).is_err() { 23 | error!("Unable to load elf64 binary {}", path) 24 | } 25 | } 26 | 27 | extern "C" fn foo() { 28 | println!("hello from task {}", scheduler::get_current_taskid()); 29 | } 30 | 31 | /// This function is the entry point, since the linker looks for a function 32 | /// named `_start` by default. 33 | #[cfg(not(test))] 34 | #[no_mangle] // don't mangle the name of this function 35 | pub extern "C" fn main() -> i32 { 36 | eduos_rs::init(); 37 | 38 | println!("Hello from eduOS-rs!"); 39 | 40 | for _i in 0..2 { 41 | scheduler::spawn(foo, NORMAL_PRIORITY).unwrap(); 42 | } 43 | scheduler::spawn(create_user_foo, NORMAL_PRIORITY).unwrap(); 44 | 45 | // enable interrupts => enable preemptive multitasking 46 | arch::irq::irq_enable(); 47 | 48 | scheduler::reschedule(); 49 | 50 | println!("Shutdown system!"); 51 | 52 | 0 53 | } 54 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "eduos-rs" 3 | version = "0.1.0" 4 | license = "MIT/Apache-2.0" 5 | authors = ["Stefan Lankes "] 6 | edition = "2021" 7 | 8 | [package.metadata.bootimage] 9 | build-command = ["build"] 10 | # The command invoked with the created bootimage (the "{}" will be replaced 11 | # with the path to the bootable disk image) 12 | # Applies to `bootimage run` and `bootimage runner` 13 | run-command = ["qemu-system-x86_64", "-display", "none", "-smp", "1", "-m", "256M", "-serial", "stdio", "-cpu", "qemu64,apic,fsgsbase,rdtscp,xsave,xsaveopt,fxsr", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-drive", "format=raw,file={}"] 14 | # Additional arguments passed to the run command for non-test executables 15 | # Applies to `bootimage run` and `bootimage runner` 16 | run-args = [] 17 | 18 | [features] 19 | default = ["qemu-exit"] 20 | vga = [] 21 | 22 | [dependencies] 23 | bitflags = "2.10" 24 | spinning_top = "0.3" 25 | lock_api = "0" 26 | qemu-exit = { version = "3.0", optional = true } 27 | x86 = { version = "0.52", default-features = false } 28 | cfg-if = "1.0" 29 | num = { version = "0.4", default-features = false } 30 | num-derive = "0.4" 31 | 32 | [target.'cfg(target_arch = "x86_64")'.dependencies.bootloader] 33 | version = "0.9.33" 34 | default-features = false 35 | features = ["recursive_page_table"] 36 | 37 | [dependencies.num-traits] 38 | version = "0.2" 39 | default-features = false 40 | 41 | [dependencies.goblin] 42 | version = "0.10" 43 | default-features = false 44 | features = ["elf64", "elf32", "endian_fd"] 45 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/start.rs: -------------------------------------------------------------------------------- 1 | use crate::arch; 2 | use crate::arch::x86::kernel::processor::shutdown; 3 | 4 | extern "C" { 5 | fn main() -> i32; 6 | } 7 | 8 | #[cfg(target_arch = "x86")] 9 | extern "C" { 10 | static mut __bss_start: u8; 11 | static __bss_end: u8; 12 | } 13 | 14 | /// initialize bss section 15 | #[cfg(target_arch = "x86")] 16 | unsafe fn bss_init() { 17 | core::ptr::write_bytes( 18 | core::ptr::addr_of_mut!(__bss_start), 19 | 0, 20 | core::ptr::addr_of!(__bss_end) as usize - core::ptr::addr_of!(__bss_start) as usize, 21 | ); 22 | } 23 | 24 | #[cfg(not(test))] 25 | unsafe extern "C" fn entry() -> ! { 26 | arch::init(); 27 | 28 | #[cfg(target_arch = "x86")] 29 | bss_init(); 30 | 31 | let ret = main(); 32 | 33 | shutdown(ret) 34 | } 35 | 36 | #[cfg(not(test))] 37 | #[cfg(target_arch = "x86_64")] 38 | #[no_mangle] 39 | pub unsafe extern "C" fn _start(boot_info: &'static bootloader::BootInfo) -> ! { 40 | crate::arch::x86::kernel::BOOT_INFO = Some(boot_info); 41 | 42 | entry(); 43 | } 44 | 45 | #[cfg(not(test))] 46 | #[cfg(target_arch = "x86")] 47 | #[no_mangle] 48 | #[unsafe(naked)] 49 | /// # Safety 50 | /// 51 | /// This function is the entry point of the kernel. 52 | /// The kernel itself should not call this function. 53 | pub unsafe extern "C" fn _start() -> ! { 54 | use crate::arch::mm::{BOOT_STACK, BOOT_STACK_SIZE}; 55 | use core::arch::naked_asm; 56 | 57 | naked_asm!( 58 | "lea esp, {stack}", 59 | "add esp, {offset}", 60 | "jmp {entry}", 61 | stack = sym BOOT_STACK, 62 | offset = const BOOT_STACK_SIZE - 16, 63 | entry = sym entry, 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /src/syscall/mod.rs: -------------------------------------------------------------------------------- 1 | mod exit; 2 | mod invalid; 3 | mod nothing; 4 | mod write; 5 | 6 | use crate::syscall::exit::sys_exit; 7 | use crate::syscall::invalid::sys_invalid; 8 | use crate::syscall::nothing::sys_nothing; 9 | use crate::syscall::write::{sys_write, sys_writev}; 10 | 11 | /// number of the system call `write` 12 | pub const SYSNO_WRITE: usize = 1; 13 | 14 | /// number of the system call `close` 15 | pub const SYSNO_CLOSE: usize = 3; 16 | 17 | pub const SYSNO_IOCTL: usize = 16; 18 | 19 | pub const SYSNO_WRITEV: usize = 20; 20 | 21 | /// number of the system call `exit` 22 | pub const SYSNO_EXIT: usize = 60; 23 | 24 | pub const SYSNO_ARCH_PRCTL: usize = 158; 25 | 26 | /// set pointer to thread ID 27 | pub const SYSNO_SET_TID_ADDRESS: usize = 218; 28 | 29 | /// exit all threads in a process 30 | pub const SYSNO_EXIT_GROUP: usize = 231; 31 | 32 | /// total number of system calls 33 | pub const NO_SYSCALLS: usize = 400; 34 | 35 | #[repr(align(64))] 36 | #[repr(C)] 37 | pub(crate) struct SyscallTable { 38 | handle: [*const usize; NO_SYSCALLS], 39 | } 40 | 41 | impl SyscallTable { 42 | pub const fn new() -> Self { 43 | let mut table = SyscallTable { 44 | handle: [sys_invalid as *const _; NO_SYSCALLS], 45 | }; 46 | 47 | table.handle[SYSNO_WRITE] = sys_write as *const _; 48 | table.handle[SYSNO_CLOSE] = sys_nothing as *const _; 49 | table.handle[SYSNO_IOCTL] = sys_nothing as *const _; 50 | table.handle[SYSNO_WRITEV] = sys_writev as *const _; 51 | table.handle[SYSNO_EXIT] = sys_exit as *const _; 52 | table.handle[SYSNO_ARCH_PRCTL] = sys_nothing as *const _; 53 | table.handle[SYSNO_SET_TID_ADDRESS] = sys_nothing as *const _; 54 | table.handle[SYSNO_EXIT_GROUP] = sys_exit as *const _; 55 | 56 | table 57 | } 58 | } 59 | 60 | unsafe impl Send for SyscallTable {} 61 | unsafe impl Sync for SyscallTable {} 62 | 63 | impl Default for SyscallTable { 64 | fn default() -> Self { 65 | Self::new() 66 | } 67 | } 68 | 69 | pub(crate) static SYSHANDLER_TABLE: SyscallTable = SyscallTable::new(); 70 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(linked_list_cursors)] 2 | #![feature(alloc_error_handler)] 3 | #![feature(abi_x86_interrupt)] 4 | #![feature(specialization)] 5 | #![feature(map_try_insert)] 6 | #![feature(int_lowest_highest_one)] 7 | #![allow(clippy::module_inception)] 8 | #![allow(incomplete_features)] 9 | #![allow(static_mut_refs)] 10 | #![no_std] 11 | 12 | extern crate alloc; 13 | #[cfg(target_arch = "x86_64")] 14 | extern crate x86; 15 | #[macro_use] 16 | extern crate bitflags; 17 | extern crate num_traits; 18 | 19 | // These need to be visible to the linker, so we need to export them. 20 | use crate::arch::processor::shutdown; 21 | use crate::consts::HEAP_SIZE; 22 | use crate::mm::buddy::LockedHeap; 23 | use core::panic::PanicInfo; 24 | pub use logging::*; 25 | 26 | #[macro_use] 27 | pub mod macros; 28 | #[macro_use] 29 | pub mod logging; 30 | pub mod arch; 31 | pub mod collections; 32 | pub mod console; 33 | pub mod consts; 34 | pub mod errno; 35 | pub mod fd; 36 | pub mod fs; 37 | pub mod io; 38 | pub mod mm; 39 | pub mod scheduler; 40 | pub mod synch; 41 | pub mod syscall; 42 | 43 | #[repr(align(256))] 44 | struct Arena([u8; HEAP_SIZE]); 45 | 46 | impl Arena { 47 | pub const fn new() -> Self { 48 | Self([0; HEAP_SIZE]) 49 | } 50 | } 51 | 52 | static mut ARENA: Arena = Arena::new(); 53 | 54 | #[global_allocator] 55 | static ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new(); 56 | 57 | pub fn init() { 58 | unsafe { 59 | crate::ALLOCATOR.init(ARENA.0.as_mut_ptr(), HEAP_SIZE); 60 | } 61 | crate::arch::init(); 62 | crate::mm::init(); 63 | crate::scheduler::init(); 64 | crate::fs::init(); 65 | } 66 | 67 | /// This function is called on panic. 68 | #[cfg(not(test))] 69 | #[panic_handler] 70 | pub fn panic(info: &PanicInfo) -> ! { 71 | print!("[!!!PANIC!!!] "); 72 | 73 | if let Some(location) = info.location() { 74 | print!("{}:{}: ", location.file(), location.line()); 75 | } 76 | 77 | if let Some(message) = info.message().as_str() { 78 | print!("{}", message); 79 | } 80 | 81 | print!("\n"); 82 | 83 | shutdown(1); 84 | } 85 | -------------------------------------------------------------------------------- /src/arch/x86/mm/physicalmem.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x86::kernel::BOOT_INFO; 2 | use crate::arch::x86::mm::paging::{BasePageSize, PageSize}; 3 | use crate::arch::x86::mm::PhysAddr; 4 | use crate::logging::*; 5 | use crate::mm::freelist::{FreeList, FreeListEntry}; 6 | use crate::scheduler::DisabledPreemption; 7 | use core::ops::Deref; 8 | 9 | static mut PHYSICAL_FREE_LIST: FreeList = FreeList::new(); 10 | 11 | pub(crate) fn init() { 12 | unsafe { 13 | let regions = BOOT_INFO.unwrap().memory_map.deref(); 14 | 15 | for i in regions { 16 | if i.region_type == bootloader::bootinfo::MemoryRegionType::Usable { 17 | let entry = FreeListEntry { 18 | start: (i.range.start_frame_number * 0x1000).into(), 19 | end: (i.range.end_frame_number * 0x1000).into(), 20 | }; 21 | 22 | debug!( 23 | "Add free physical regions 0x{:x} - 0x{:x}", 24 | entry.start, entry.end 25 | ); 26 | PHYSICAL_FREE_LIST.list.push_back(entry); 27 | } 28 | } 29 | } 30 | } 31 | 32 | pub fn allocate(size: usize) -> PhysAddr { 33 | assert!(size > 0); 34 | assert!( 35 | size % BasePageSize::SIZE == 0, 36 | "Size {:#X} is not a multiple of {:#X}", 37 | size, 38 | BasePageSize::SIZE 39 | ); 40 | 41 | let _preemption = DisabledPreemption::new(); 42 | let result = unsafe { PHYSICAL_FREE_LIST.allocate(size, None) }; 43 | assert!( 44 | result.is_ok(), 45 | "Could not allocate {:#X} bytes of physical memory", 46 | size 47 | ); 48 | result.unwrap() 49 | } 50 | 51 | pub fn allocate_aligned(size: usize, alignment: usize) -> PhysAddr { 52 | assert!(size > 0); 53 | assert!(alignment > 0); 54 | assert!( 55 | size % alignment == 0, 56 | "Size {:#X} is not a multiple of the given alignment {:#X}", 57 | size, 58 | alignment 59 | ); 60 | assert!( 61 | alignment % BasePageSize::SIZE == 0, 62 | "Alignment {:#X} is not a multiple of {:#X}", 63 | alignment, 64 | BasePageSize::SIZE 65 | ); 66 | 67 | let _preemption = DisabledPreemption::new(); 68 | let result = unsafe { PHYSICAL_FREE_LIST.allocate(size, Some(alignment)) }; 69 | assert!( 70 | result.is_ok(), 71 | "Could not allocate {:#X} bytes of physical memory aligned to {} bytes", 72 | size, 73 | alignment 74 | ); 75 | result.unwrap() 76 | } 77 | 78 | pub fn deallocate(physical_address: PhysAddr, size: usize) { 79 | assert!(size > 0); 80 | assert!( 81 | size % BasePageSize::SIZE == 0, 82 | "Size {:#X} is not a multiple of {:#X}", 83 | size, 84 | BasePageSize::SIZE 85 | ); 86 | 87 | let _preemption = DisabledPreemption::new(); 88 | unsafe { 89 | PHYSICAL_FREE_LIST.deallocate(physical_address, size); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_macros)] 2 | 3 | /// An enum representing the available verbosity levels of the logger. 4 | #[derive(Copy, Clone, PartialEq, PartialOrd)] 5 | pub enum LogLevel { 6 | /// Disable all our put messages 7 | /// 8 | /// Designates without information 9 | Disabled = 0, 10 | /// The "error" level. 11 | /// 12 | /// Designates very serious errors. 13 | Error, 14 | /// The "warn" level. 15 | /// 16 | /// Designates hazardous situations. 17 | Warning, 18 | /// The "info" level. 19 | /// 20 | /// Designates useful information. 21 | Info, 22 | // The "debug" level. 23 | /// 24 | /// Designates lower priority information. 25 | Debug, 26 | } 27 | 28 | /// Data structures to filter kernel messages 29 | pub struct KernelLogger { 30 | pub log_level: LogLevel, 31 | } 32 | 33 | /// default logger to handle kernel messages 34 | pub const LOGGER: KernelLogger = KernelLogger { 35 | log_level: LogLevel::Info, 36 | }; 37 | 38 | /// Print formatted info text to our console, followed by a newline. 39 | #[macro_export] 40 | macro_rules! info { 41 | ($fmt:expr) => ({ 42 | if LOGGER.log_level >= LogLevel::Info { 43 | println!(concat!("[INFO] ", $fmt)); 44 | } 45 | }); 46 | ($fmt:expr, $($arg:tt)*) => ({ 47 | if LOGGER.log_level >= LogLevel::Info { 48 | println!(concat!("[INFO] ", $fmt), $($arg)*); 49 | } 50 | }); 51 | } 52 | 53 | /// Print formatted warnings to our console, followed by a newline. 54 | #[macro_export] 55 | macro_rules! warn { 56 | ($fmt:expr) => ({ 57 | if LOGGER.log_level >= LogLevel::Warning { 58 | println!(concat!("[WARNING] ", $fmt)); 59 | } 60 | }); 61 | ($fmt:expr, $($arg:tt)*) => ({ 62 | if LOGGER.log_level >= LogLevel::Warning { 63 | println!(concat!("[WARNING] ", $fmt), $($arg)*); 64 | } 65 | }); 66 | } 67 | 68 | /// Print formatted warnings to our console, followed by a newline. 69 | #[macro_export] 70 | macro_rules! error { 71 | ($fmt:expr) => ({ 72 | if LOGGER.log_level >= LogLevel::Error { 73 | println!(concat!("[ERROR] ", $fmt)); 74 | } 75 | }); 76 | ($fmt:expr, $($arg:tt)*) => ({ 77 | if LOGGER.log_level >= LogLevel::Error { 78 | println!(concat!("[ERROR] ", $fmt), $($arg)*); 79 | } 80 | }); 81 | } 82 | 83 | /// Print formatted debug messages to our console, followed by a newline. 84 | #[macro_export] 85 | macro_rules! debug { 86 | ($fmt:expr) => ({ 87 | if LOGGER.log_level >= LogLevel::Debug { 88 | println!(concat!("[DEBUG] ", $fmt)); 89 | } 90 | }); 91 | ($fmt:expr, $($arg:tt)*) => ({ 92 | if LOGGER.log_level >= LogLevel::Debug { 93 | println!(concat!("[DEBUG] ", $fmt), $($arg)*); 94 | } 95 | }); 96 | } 97 | -------------------------------------------------------------------------------- /src/fd/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod stdio; 2 | 3 | use crate::io; 4 | use crate::scheduler::get_io_interface; 5 | 6 | pub type FileDescriptor = i32; 7 | 8 | pub const STDIN_FILENO: FileDescriptor = 0; 9 | pub const STDOUT_FILENO: FileDescriptor = 1; 10 | pub const STDERR_FILENO: FileDescriptor = 2; 11 | 12 | /// Enumeration of possible methods to seek within an I/O object. 13 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 14 | pub enum SeekFrom { 15 | /// Set the offset to the provided number of bytes. 16 | Start(usize), 17 | /// Set the offset to the size of this object plus the specified number of bytes. 18 | /// 19 | /// It is possible to seek beyond the end of an object, but it's an error to 20 | /// seek before byte 0. 21 | End(isize), 22 | /// Set the offset to the current position plus the specified number of bytes. 23 | /// 24 | /// It is possible to seek beyond the end of an object, but it's an error to 25 | /// seek before byte 0. 26 | Current(isize), 27 | } 28 | 29 | /// Describes information about a file. 30 | pub struct FileStatus { 31 | /// Size of the file 32 | pub file_size: usize, 33 | } 34 | 35 | #[allow(dead_code)] 36 | pub trait IoInterface: Sync + Send + core::fmt::Debug { 37 | /// `read` attempts to read `len` bytes from the object references 38 | /// by the descriptor 39 | fn read(&self, _buf: &mut [u8]) -> io::Result { 40 | Err(io::Error::ENOSYS) 41 | } 42 | 43 | /// `write` attempts to write `len` bytes to the object references 44 | /// by the descriptor 45 | fn write(&self, _buf: &[u8]) -> io::Result { 46 | Err(io::Error::ENOSYS) 47 | } 48 | 49 | fn seek(&self, _offset: SeekFrom) -> io::Result { 50 | Err(io::Error::ENOSYS) 51 | } 52 | 53 | fn fstat(&self) -> io::Result { 54 | Err(io::Error::ENOSYS) 55 | } 56 | } 57 | 58 | bitflags! { 59 | /// Options for opening files 60 | #[derive(Debug, Copy, Clone)] 61 | pub struct OpenOption: i32 { 62 | const O_RDONLY = 0o0000; 63 | const O_WRONLY = 0o0001; 64 | const O_RDWR = 0o0002; 65 | const O_CREAT = 0o0100; 66 | const O_EXCL = 0o0200; 67 | const O_TRUNC = 0o1000; 68 | const O_APPEND = 0o2000; 69 | const O_DIRECT = 0o40000; 70 | const O_DIRECTORY = 0o200_000; 71 | } 72 | } 73 | 74 | pub(crate) fn read(fd: FileDescriptor, buf: &mut [u8]) -> io::Result { 75 | let obj = get_io_interface(fd)?; 76 | 77 | if buf.is_empty() { 78 | return Ok(0); 79 | } 80 | 81 | obj.read(buf) 82 | } 83 | 84 | pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> io::Result { 85 | let obj = get_io_interface(fd)?; 86 | 87 | if buf.is_empty() { 88 | return Ok(0); 89 | } 90 | 91 | obj.write(buf) 92 | } 93 | 94 | pub(crate) fn fstat(fd: FileDescriptor) -> io::Result { 95 | get_io_interface(fd)?.fstat() 96 | } 97 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/switch.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::mm::VirtAddr; 2 | use crate::arch::x86::kernel::gdt::set_current_kernel_stack; 3 | use core::arch::naked_asm; 4 | 5 | #[cfg(target_arch = "x86_64")] 6 | macro_rules! save_context { 7 | () => { 8 | concat!( 9 | r#" 10 | pushfq 11 | push rax 12 | push rcx 13 | push rdx 14 | push rbx 15 | sub rsp, 8 16 | push rbp 17 | push rsi 18 | push rdi 19 | push r8 20 | push r9 21 | push r10 22 | push r11 23 | push r12 24 | push r13 25 | push r14 26 | push r15 27 | "#, 28 | ) 29 | }; 30 | } 31 | 32 | #[cfg(target_arch = "x86_64")] 33 | macro_rules! restore_context { 34 | () => { 35 | concat!( 36 | r#" 37 | pop r15 38 | pop r14 39 | pop r13 40 | pop r12 41 | pop r11 42 | pop r10 43 | pop r9 44 | pop r8 45 | pop rdi 46 | pop rsi 47 | pop rbp 48 | add rsp, 8 49 | pop rbx 50 | pop rdx 51 | pop rcx 52 | pop rax 53 | popfq 54 | ret 55 | "# 56 | ) 57 | }; 58 | } 59 | 60 | #[cfg(target_arch = "x86_64")] 61 | #[unsafe(naked)] 62 | /// # Safety 63 | /// 64 | /// Only the scheduler itself should call this function to switch the 65 | /// context. `old_stack` is a pointer, where the address to the old 66 | /// stack is stored. `new_stack` provides the stack pointer of the 67 | /// next task. 68 | pub(crate) unsafe extern "C" fn switch(_old_stack: *mut VirtAddr, _new_stack: VirtAddr) { 69 | // rdi = old_stack => the address to store the old rsp 70 | // rsi = new_stack => stack pointer of the new task 71 | 72 | naked_asm!( 73 | save_context!(), 74 | "rdfsbase rax", 75 | "rdgsbase rdx", 76 | "push rax", 77 | "push rdx", 78 | // Store the old `rsp` behind `old_stack` 79 | "mov [rdi], rsp", 80 | // Set `rsp` to `new_stack` 81 | "mov rsp, rsi", 82 | // Set task switched flag 83 | "mov rax, cr0", 84 | "or rax, 8", 85 | "mov cr0, rax", 86 | // set stack pointer in TSS 87 | "call {set_stack}", 88 | "pop r15", 89 | "wrgsbase r15", 90 | "pop r15", 91 | "wrfsbase r15", 92 | restore_context!(), 93 | set_stack = sym set_current_kernel_stack, 94 | ); 95 | } 96 | 97 | #[cfg(target_arch = "x86")] 98 | #[unsafe(naked)] 99 | /// # Safety 100 | /// 101 | /// Only the scheduler itself should call this function to switch the 102 | /// context. `old_stack` is a pointer, where the address to the old 103 | /// stack is stored. `new_stack` provides the stack pointer of the 104 | /// next task. 105 | pub unsafe extern "C" fn switch(_old_stack: *mut usize, _new_stack: usize) { 106 | naked_asm!( 107 | // store all registers 108 | "pushfd", 109 | "pushad", 110 | // switch stack 111 | "mov edi, [esp+10*4]", 112 | "mov [edi], esp", 113 | "mov esp, [esp+11*4]", 114 | // restore registers 115 | "popad", 116 | "popfd", 117 | "ret", 118 | ); 119 | } 120 | -------------------------------------------------------------------------------- /src/mm/linked_list.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | #[derive(Copy, Clone, Debug)] 4 | pub(crate) struct LinkedList { 5 | head: *mut usize, 6 | } 7 | 8 | unsafe impl Send for LinkedList {} 9 | 10 | impl LinkedList { 11 | pub const fn new() -> Self { 12 | Self { 13 | head: core::ptr::null_mut(), 14 | } 15 | } 16 | 17 | /// check if the list is empty 18 | pub fn is_empty(&self) -> bool { 19 | self.head.is_null() 20 | } 21 | 22 | /// add address to the front of the list 23 | pub unsafe fn push(&mut self, item: *mut usize) { 24 | *item = self.head as usize; 25 | self.head = item; 26 | } 27 | 28 | /// Try to remove the first item in the list 29 | pub fn pop(&mut self) -> Option<*mut usize> { 30 | match self.is_empty() { 31 | true => None, 32 | false => { 33 | // Advance head pointer 34 | let item = self.head; 35 | self.head = unsafe { *item as *mut usize }; 36 | Some(item) 37 | } 38 | } 39 | } 40 | 41 | /// Return an iterator over the items in the list 42 | pub fn iter(&self) -> Iter<'_> { 43 | Iter { 44 | curr: self.head, 45 | list: PhantomData, 46 | } 47 | } 48 | 49 | /// Return an mutable iterator over the items in the list 50 | pub fn iter_mut(&mut self) -> IterMut<'_> { 51 | IterMut { 52 | prev: &mut self.head as *mut *mut usize as *mut usize, 53 | curr: self.head, 54 | list: PhantomData, 55 | } 56 | } 57 | } 58 | 59 | /// A simple iterator for the linked list 60 | pub(crate) struct Iter<'a> { 61 | curr: *mut usize, 62 | list: PhantomData<&'a LinkedList>, 63 | } 64 | 65 | impl<'a> Iterator for Iter<'a> { 66 | type Item = *mut usize; 67 | 68 | fn next(&mut self) -> Option { 69 | if self.curr.is_null() { 70 | None 71 | } else { 72 | let item = self.curr; 73 | let next = unsafe { *item as *mut usize }; 74 | self.curr = next; 75 | Some(item) 76 | } 77 | } 78 | } 79 | 80 | /// Represent a mutable node in `LinkedList` 81 | pub(crate) struct ListNode { 82 | prev: *mut usize, 83 | curr: *mut usize, 84 | } 85 | 86 | impl ListNode { 87 | /// Remove the current node from the list 88 | pub fn remove(self) -> *mut usize { 89 | // Skip the current one 90 | unsafe { 91 | *(self.prev) = *(self.curr); 92 | } 93 | self.curr 94 | } 95 | 96 | /// Returns the pointed address 97 | pub fn value(&self) -> *mut usize { 98 | self.curr 99 | } 100 | } 101 | 102 | /// A mutable iterator over the linked list 103 | pub(crate) struct IterMut<'a> { 104 | list: PhantomData<&'a mut LinkedList>, 105 | prev: *mut usize, 106 | curr: *mut usize, 107 | } 108 | 109 | impl<'a> Iterator for IterMut<'a> { 110 | type Item = ListNode; 111 | 112 | fn next(&mut self) -> Option { 113 | if self.curr.is_null() { 114 | None 115 | } else { 116 | let res = ListNode { 117 | prev: self.prev, 118 | curr: self.curr, 119 | }; 120 | self.prev = self.curr; 121 | self.curr = unsafe { *self.curr as *mut usize }; 122 | Some(res) 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /.github/workflows/run.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | schedule: 11 | - cron: '0 0 * * 6' 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ${{ matrix.os }} 17 | 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest,macos-latest,windows-latest] 21 | 22 | steps: 23 | - name: Install QEMU (ubuntu) 24 | if: ${{ matrix.os == 'ubuntu-latest' }} 25 | run: | 26 | sudo apt-get update 27 | sudo apt-get install qemu-system-x86 28 | - name: Install QEMU (macos) 29 | if: ${{ matrix.os == 'macos-latest' }} 30 | run: | 31 | brew update 32 | brew install qemu 33 | - name: Install QEMU (windows) 34 | if: ${{ matrix.os == 'windows-latest' }} 35 | run: | 36 | choco install qemu 37 | echo "$Env:Programfiles\qemu" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 38 | shell: pwsh 39 | - uses: actions/checkout@v6 40 | with: 41 | submodules: true 42 | lfs: true 43 | - name: Check Cargo availability 44 | run: cargo --version 45 | - name: Install bootimage 46 | run: cargo install bootimage 47 | - name: Build (x86_64) 48 | run: 49 | cargo build 50 | # - name: Build (i686) 51 | # run: 52 | # cargo build --target=i686-eduos.json 53 | - name: run (ubuntu) 54 | if: ${{ matrix.os == 'ubuntu-latest' }} 55 | run: ./test.sh 56 | - name: run (macos) 57 | if: ${{ matrix.os == 'macos-latest' }} 58 | run: ./test.sh 59 | - name: run (windows) 60 | if: ${{ matrix.os == 'windows-latest' }} 61 | run: pwsh -command ".\$GITHUB_WORKSPACE\test.ps1" 62 | # - name: Build release for v86 (i686) 63 | # run: 64 | # cargo build --target=i686-eduos.json --release --features vga --no-default-features 65 | # - name: Upload the artifact 66 | # if: ${{ matrix.os == 'ubuntu-latest' }} 67 | # uses: actions/upload-artifact@v4 68 | # with: 69 | # name: i686-eduos-stage9 70 | # path: | 71 | # target/i686-eduos/release/eduos-rs 72 | 73 | # publish: 74 | # name: Upload release 75 | # runs-on: ubuntu-latest 76 | # needs: build 77 | 78 | # steps: 79 | # - uses: dev-drprasad/delete-tag-and-release@v1.1 80 | # with: 81 | # delete_release: true 82 | # tag_name: stage9 83 | # github_token: ${{ secrets.GITHUB_TOKEN }} 84 | 85 | # - name: Get artifacts 86 | # uses: actions/download-artifact@v4 87 | # with: 88 | # name: i686-eduos-stage9 89 | # path: . 90 | 91 | # - name: Rename file 92 | # run: mv eduos-rs i686-eduos-stage9 93 | 94 | # - name: Display structure of downloaded files 95 | # run: ls -R 96 | 97 | # - name: Release to GitHub 98 | # uses: ncipollo/release-action@v1 99 | # with: 100 | # name: Latest Stage6 Release 101 | # tag: stage9 102 | # commit: stage9 103 | # body: ${{ github.event.head_commit.message }} 104 | # artifacts: "i686-eduos-stage9" 105 | # prerelease: true 106 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/vga.rs: -------------------------------------------------------------------------------- 1 | use crate::synch::spinlock::Spinlock; 2 | use core::fmt; 3 | use x86::io::*; 4 | 5 | const CRT_CONTROLLER_ADDRESS_PORT: u16 = 0x3D4; 6 | const CRT_CONTROLLER_DATA_PORT: u16 = 0x3D5; 7 | const CURSOR_START_REGISTER: u8 = 0x0A; 8 | const CURSOR_DISABLE: u8 = 0x20; 9 | 10 | const ATTRIBUTE_BLACK: u8 = 0x00; 11 | const ATTRIBUTE_LIGHTGREY: u8 = 0x07; 12 | const COLS: usize = 80; 13 | const ROWS: usize = 25; 14 | const VGA_BUFFER_ADDRESS: u64 = 0xB8000; 15 | 16 | pub(crate) static VGA_SCREEN: Spinlock = Spinlock::new(VgaScreen::new()); 17 | 18 | #[derive(Clone, Copy)] 19 | #[repr(C, packed)] 20 | pub(crate) struct VgaCharacter { 21 | character: u8, 22 | attribute: u8, 23 | } 24 | 25 | impl VgaCharacter { 26 | const fn new(character: u8, attribute: u8) -> Self { 27 | Self { 28 | character, 29 | attribute, 30 | } 31 | } 32 | } 33 | 34 | pub(crate) struct VgaScreen { 35 | buffer: *mut [[VgaCharacter; COLS]; ROWS], 36 | current_col: usize, 37 | current_row: usize, 38 | is_initialized: bool, 39 | } 40 | 41 | impl VgaScreen { 42 | const fn new() -> Self { 43 | Self { 44 | buffer: VGA_BUFFER_ADDRESS as *mut _, 45 | current_col: 0, 46 | current_row: 0, 47 | is_initialized: false, 48 | } 49 | } 50 | 51 | fn init(&mut self) { 52 | // Disable the cursor. 53 | unsafe { 54 | outb(CRT_CONTROLLER_ADDRESS_PORT, CURSOR_START_REGISTER); 55 | outb(CRT_CONTROLLER_DATA_PORT, CURSOR_DISABLE); 56 | } 57 | 58 | // Clear the screen. 59 | for r in 0..ROWS { 60 | self.clear_row(r); 61 | } 62 | 63 | // Initialization done! 64 | self.is_initialized = true; 65 | } 66 | 67 | #[inline] 68 | fn clear_row(&mut self, row: usize) { 69 | // Overwrite this row by a bogus character in black. 70 | for c in 0..COLS { 71 | unsafe { 72 | (*self.buffer)[row][c] = VgaCharacter::new(0, ATTRIBUTE_BLACK); 73 | } 74 | } 75 | } 76 | 77 | fn write_byte(&mut self, byte: u8) { 78 | if !self.is_initialized { 79 | return; 80 | } 81 | 82 | // Move to the next row if we have a newline character or hit the end of a column. 83 | if byte == b'\n' || self.current_col == COLS { 84 | self.current_col = 0; 85 | self.current_row += 1; 86 | } 87 | 88 | // Check if we have hit the end of the screen rows. 89 | if self.current_row == ROWS { 90 | // Shift all rows up by one line, removing the oldest visible screen row. 91 | for r in 1..ROWS { 92 | for c in 0..COLS { 93 | unsafe { 94 | (*self.buffer)[r - 1][c] = (*self.buffer)[r][c]; 95 | } 96 | } 97 | } 98 | 99 | // Clear the last screen row and write to it next time. 100 | self.clear_row(ROWS - 1); 101 | self.current_row = ROWS - 1; 102 | } 103 | 104 | if byte != b'\n' { 105 | // Put our character into the VGA screen buffer and advance the column counter. 106 | unsafe { 107 | (*self.buffer)[self.current_row][self.current_col] = 108 | VgaCharacter::new(byte, ATTRIBUTE_LIGHTGREY); 109 | } 110 | self.current_col += 1; 111 | } 112 | } 113 | 114 | pub fn write_bytes(&mut self, buf: &[u8]) { 115 | unsafe { 116 | // Output each byte of our string. 117 | for &b in buf { 118 | // Write our byte. 119 | self.write_byte(b); 120 | } 121 | } 122 | } 123 | } 124 | 125 | unsafe impl Send for VgaScreen {} 126 | 127 | impl fmt::Write for VgaScreen { 128 | fn write_str(&mut self, s: &str) -> fmt::Result { 129 | for &b in s.as_bytes() { 130 | self.write_byte(b); 131 | } 132 | 133 | Ok(()) 134 | } 135 | } 136 | 137 | pub(crate) fn init() { 138 | VGA_SCREEN.lock().init(); 139 | } 140 | -------------------------------------------------------------------------------- /src/scheduler/mod.rs: -------------------------------------------------------------------------------- 1 | //! Interface to the scheduler 2 | 3 | mod scheduler; 4 | /// task control block 5 | pub mod task; 6 | 7 | use crate::arch; 8 | use crate::arch::mm::{PhysAddr, VirtAddr}; 9 | use crate::errno::*; 10 | use crate::fd::{FileDescriptor, IoInterface}; 11 | use crate::io; 12 | use crate::scheduler::task::{Task, TaskPriority}; 13 | use alloc::rc::Rc; 14 | use alloc::sync::Arc; 15 | use core::cell::RefCell; 16 | 17 | static mut SCHEDULER: Option = None; 18 | 19 | /// Initialize module, must be called once, and only once 20 | pub(crate) fn init() { 21 | unsafe { 22 | SCHEDULER = Some(scheduler::Scheduler::new()); 23 | } 24 | 25 | arch::register_task(); 26 | } 27 | 28 | /// Create a new kernel task 29 | pub fn spawn(func: extern "C" fn(), prio: TaskPriority) -> Result { 30 | unsafe { SCHEDULER.as_mut().unwrap().spawn(func, prio) } 31 | } 32 | 33 | /// Trigger the scheduler to switch to the next available task 34 | pub fn reschedule() { 35 | unsafe { SCHEDULER.as_mut().unwrap().reschedule() } 36 | } 37 | 38 | /// Timer interrupt call scheduler to switch to the next available task 39 | pub(crate) fn schedule() { 40 | unsafe { SCHEDULER.as_mut().unwrap().schedule() } 41 | } 42 | 43 | /// Terminate the current running task 44 | pub fn do_exit() -> ! { 45 | unsafe { 46 | SCHEDULER.as_mut().unwrap().exit(); 47 | } 48 | } 49 | 50 | /// Terminate the current running task 51 | pub fn abort() -> ! { 52 | unsafe { SCHEDULER.as_mut().unwrap().abort() } 53 | } 54 | 55 | pub(crate) fn get_current_interrupt_stack() -> VirtAddr { 56 | unsafe { SCHEDULER.as_mut().unwrap().get_current_interrupt_stack() } 57 | } 58 | 59 | pub(crate) fn get_root_page_table() -> PhysAddr { 60 | unsafe { SCHEDULER.as_mut().unwrap().get_root_page_table() } 61 | } 62 | 63 | pub(crate) fn set_root_page_table(addr: PhysAddr) { 64 | unsafe { 65 | SCHEDULER.as_mut().unwrap().set_root_page_table(addr); 66 | } 67 | } 68 | 69 | pub(crate) fn block_current_task() -> Rc> { 70 | unsafe { SCHEDULER.as_mut().unwrap().block_current_task() } 71 | } 72 | 73 | pub(crate) fn wakeup_task(task: Rc>) { 74 | unsafe { SCHEDULER.as_mut().unwrap().wakeup_task(task) } 75 | } 76 | 77 | pub(crate) fn get_io_interface(fd: FileDescriptor) -> crate::io::Result> { 78 | let _preemption = DisabledPreemption::new(); 79 | 80 | unsafe { SCHEDULER.as_mut().unwrap().get_io_interface(fd) } 81 | } 82 | 83 | /// Insert IoInterface and create a new FileDescriptor 84 | pub(crate) fn insert_io_interface(obj: Arc) -> io::Result { 85 | let _preemption = DisabledPreemption::new(); 86 | 87 | unsafe { SCHEDULER.as_mut().unwrap().insert_io_interface(obj) } 88 | } 89 | 90 | /// Remove a IO interface, which is named by the file descriptor 91 | pub(crate) fn remove_io_interface(fd: FileDescriptor) -> io::Result> { 92 | let _preemption = DisabledPreemption::new(); 93 | 94 | unsafe { SCHEDULER.as_mut().unwrap().remove_io_interface(fd) } 95 | } 96 | 97 | /// Get the TaskID of the current running task 98 | pub fn get_current_taskid() -> task::TaskId { 99 | unsafe { SCHEDULER.as_ref().unwrap().get_current_taskid() } 100 | } 101 | 102 | pub(crate) struct DisabledPreemption { 103 | irq_enabled: bool, 104 | } 105 | 106 | impl DisabledPreemption { 107 | pub fn new() -> Self { 108 | DisabledPreemption { 109 | irq_enabled: arch::irq::irq_nested_disable(), 110 | } 111 | } 112 | } 113 | 114 | impl Drop for DisabledPreemption { 115 | fn drop(&mut self) { 116 | arch::irq::irq_nested_enable(self.irq_enabled); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/arch/x86/mm/virtualmem.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x86::mm::paging::{BasePageSize, PageSize}; 2 | use crate::arch::x86::mm::VirtAddr; 3 | use crate::mm::freelist::{FreeList, FreeListEntry}; 4 | use crate::scheduler::DisabledPreemption; 5 | 6 | static mut KERNEL_FREE_LIST: FreeList = FreeList::new(); 7 | 8 | /// Start of the virtual memory address space reserved for kernel memory. 9 | /// This also marks the start of the virtual memory address space reserved for the task heap. 10 | pub(crate) const KERNEL_VIRTUAL_MEMORY_START: VirtAddr = VirtAddr(0x8000_0000u64); 11 | 12 | /// End of the virtual memory address space reserved for kernel memory (32 GiB). 13 | /// This also marks the start of the virtual memory address space reserved for the task heap. 14 | pub(crate) const KERNEL_VIRTUAL_MEMORY_END: VirtAddr = VirtAddr(0x800_0000_0000u64); 15 | 16 | /// End of the virtual memory address space reserved for kernel memory (128 TiB). 17 | /// This is the maximum contiguous virtual memory area possible with current x86-64 CPUs, which only support 48-bit 18 | /// linear addressing (in two 47-bit areas). 19 | const TASK_VIRTUAL_MEMORY_END: VirtAddr = VirtAddr(0x8000_0000_0000u64); 20 | 21 | pub(crate) fn init() { 22 | let entry = FreeListEntry { 23 | start: KERNEL_VIRTUAL_MEMORY_START, 24 | end: KERNEL_VIRTUAL_MEMORY_END, 25 | }; 26 | unsafe { 27 | KERNEL_FREE_LIST.list.push_back(entry); 28 | } 29 | } 30 | 31 | #[allow(dead_code)] 32 | pub(crate) fn allocate(size: usize) -> VirtAddr { 33 | assert!(size > 0); 34 | assert!( 35 | size % BasePageSize::SIZE == 0, 36 | "Size {:#X} is not a multiple of {:#X}", 37 | size, 38 | BasePageSize::SIZE 39 | ); 40 | 41 | let _preemption = DisabledPreemption::new(); 42 | let result = unsafe { KERNEL_FREE_LIST.allocate(size, None) }; 43 | assert!( 44 | result.is_ok(), 45 | "Could not allocate {:#X} bytes of virtual memory", 46 | size 47 | ); 48 | result.unwrap() 49 | } 50 | 51 | pub(crate) fn allocate_aligned(size: usize, alignment: usize) -> VirtAddr { 52 | assert!(size > 0); 53 | assert!(alignment > 0); 54 | assert!( 55 | size % alignment == 0, 56 | "Size {:#X} is not a multiple of the given alignment {:#X}", 57 | size, 58 | alignment 59 | ); 60 | assert!( 61 | alignment % BasePageSize::SIZE == 0, 62 | "Alignment {:#X} is not a multiple of {:#X}", 63 | alignment, 64 | BasePageSize::SIZE 65 | ); 66 | 67 | let _preemption = DisabledPreemption::new(); 68 | let result = unsafe { KERNEL_FREE_LIST.allocate(size, Some(alignment)) }; 69 | assert!( 70 | result.is_ok(), 71 | "Could not allocate {:#X} bytes of virtual memory aligned to {} bytes", 72 | size, 73 | alignment 74 | ); 75 | result.unwrap() 76 | } 77 | 78 | pub(crate) fn deallocate(virtual_address: VirtAddr, size: usize) { 79 | assert!( 80 | virtual_address < KERNEL_VIRTUAL_MEMORY_END, 81 | "Virtual address {:#X} is not < KERNEL_VIRTUAL_MEMORY_END", 82 | virtual_address 83 | ); 84 | assert!( 85 | virtual_address % BasePageSize::SIZE == 0, 86 | "Virtual address {:#X} is not a multiple of {:#X}", 87 | virtual_address, 88 | BasePageSize::SIZE 89 | ); 90 | assert!(size > 0); 91 | assert!( 92 | size % BasePageSize::SIZE == 0, 93 | "Size {:#X} is not a multiple of {:#X}", 94 | size, 95 | BasePageSize::SIZE 96 | ); 97 | 98 | let _preemption = DisabledPreemption::new(); 99 | unsafe { 100 | KERNEL_FREE_LIST.deallocate(virtual_address, size); 101 | } 102 | } 103 | 104 | #[allow(dead_code)] 105 | pub(crate) fn task_heap_start() -> VirtAddr { 106 | KERNEL_VIRTUAL_MEMORY_END 107 | } 108 | 109 | #[allow(dead_code)] 110 | pub(crate) fn task_heap_end() -> VirtAddr { 111 | TASK_VIRTUAL_MEMORY_END 112 | } 113 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use alloc::vec::Vec; 3 | use core::{fmt, result}; 4 | use num_derive::{FromPrimitive, ToPrimitive}; 5 | 6 | #[allow(clippy::upper_case_acronyms)] 7 | #[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] 8 | pub enum Error { 9 | ENOENT = crate::errno::ENOENT as isize, 10 | ENOSYS = crate::errno::ENOSYS as isize, 11 | EIO = crate::errno::EIO as isize, 12 | EBADF = crate::errno::EBADF as isize, 13 | EISDIR = crate::errno::EISDIR as isize, 14 | EINVAL = crate::errno::EINVAL as isize, 15 | ETIME = crate::errno::ETIME as isize, 16 | EAGAIN = crate::errno::EAGAIN as isize, 17 | EFAULT = crate::errno::EFAULT as isize, 18 | ENOBUFS = crate::errno::ENOBUFS as isize, 19 | ENOTCONN = crate::errno::ENOTCONN as isize, 20 | ENOTDIR = crate::errno::ENOTDIR as isize, 21 | EMFILE = crate::errno::EMFILE as isize, 22 | EEXIST = crate::errno::EEXIST as isize, 23 | EADDRINUSE = crate::errno::EADDRINUSE as isize, 24 | EOVERFLOW = crate::errno::EOVERFLOW as isize, 25 | ENOTSOCK = crate::errno::ENOTSOCK as isize, 26 | } 27 | 28 | pub type Result = result::Result; 29 | 30 | /// The Read trait allows for reading bytes from a source. 31 | /// 32 | /// The Read trait is derived from Rust's std library. 33 | pub trait Read { 34 | fn read(&mut self, buf: &mut [u8]) -> Result; 35 | 36 | /// Read all bytes until EOF in this source, placing them into buf. 37 | fn read_to_end(&mut self, buf: &mut Vec) -> Result { 38 | let start_len = buf.len(); 39 | 40 | loop { 41 | let mut probe = [0u8; 512]; 42 | 43 | match self.read(&mut probe) { 44 | Ok(0) => return Ok(buf.len() - start_len), 45 | Ok(n) => { 46 | buf.extend_from_slice(&probe[..n]); 47 | } 48 | Err(e) => return Err(e), 49 | } 50 | } 51 | } 52 | 53 | /// Read all bytes until EOF in this source, appending them to `buf`. 54 | /// 55 | /// If successful, this function returns the number of bytes which were read 56 | /// and appended to `buf`. 57 | fn read_to_string(&mut self, buf: &mut String) -> Result { 58 | unsafe { self.read_to_end(buf.as_mut_vec()) } 59 | } 60 | } 61 | 62 | /// The Write trait allows for reading bytes from a source. 63 | /// 64 | /// The Write trait is derived from Rust's std library. 65 | pub trait Write { 66 | fn write(&mut self, buf: &[u8]) -> Result; 67 | 68 | /// Attempts to write an entire buffer into this writer. 69 | fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { 70 | while !buf.is_empty() { 71 | match self.write(buf) { 72 | Ok(0) => { 73 | return Err(Error::EIO); 74 | } 75 | Ok(n) => buf = &buf[n..], 76 | Err(e) => return Err(e), 77 | } 78 | } 79 | 80 | Ok(()) 81 | } 82 | 83 | /// Writes a formatted string into this writer, returning any error encountered. 84 | fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { 85 | // Create a shim which translates a Write to a fmt::Write and saves 86 | // off I/O errors. instead of discarding them 87 | struct Adapter<'a, T: ?Sized> { 88 | inner: &'a mut T, 89 | error: Result<()>, 90 | } 91 | 92 | impl fmt::Write for Adapter<'_, T> { 93 | fn write_str(&mut self, s: &str) -> fmt::Result { 94 | match self.inner.write_all(s.as_bytes()) { 95 | Ok(()) => Ok(()), 96 | Err(e) => { 97 | self.error = Err(e); 98 | Err(fmt::Error) 99 | } 100 | } 101 | } 102 | } 103 | 104 | let mut output = Adapter { 105 | inner: self, 106 | error: Ok(()), 107 | }; 108 | match fmt::write(&mut output, fmt) { 109 | Ok(()) => Ok(()), 110 | Err(..) => { 111 | // check if the error came from the underlying `Write` or not 112 | if output.error.is_err() { 113 | output.error 114 | } else { 115 | Err(Error::EINVAL) 116 | } 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/synch/mutex.rs: -------------------------------------------------------------------------------- 1 | use crate::scheduler::task::*; 2 | use crate::scheduler::{block_current_task, reschedule, wakeup_task}; 3 | use crate::synch::spinlock::*; 4 | use core::cell::UnsafeCell; 5 | use core::marker::Sync; 6 | use core::ops::{Deref, DerefMut, Drop}; 7 | 8 | /// A mutual exclusion primitive useful for protecting shared data 9 | /// 10 | /// This mutex will block threads waiting for the lock to become available. The 11 | /// mutex can also be statically initialized or created via a `new` 12 | /// constructor. Each mutex has a type parameter which represents the data that 13 | /// it is protecting. The data can only be accessed through the RAII guards 14 | /// returned from `lock` and `try_lock`, which guarantees that the data is only 15 | /// ever accessed when the mutex is locked. 16 | /// 17 | /// # Simple examples 18 | /// 19 | /// ``` 20 | /// let mutex = synch::Mutex::new(0); 21 | /// 22 | /// // Modify the data 23 | /// { 24 | /// let mut data = mutex.lock(); 25 | /// *data = 2; 26 | /// } 27 | /// 28 | /// // Read the data 29 | /// let answer = 30 | /// { 31 | /// let data = mutex.lock(); 32 | /// *data 33 | /// }; 34 | /// 35 | /// assert_eq!(answer, 2); 36 | /// ``` 37 | pub struct Mutex { 38 | /// in principle a binary semaphore 39 | value: SpinlockIrqSave, 40 | /// Priority queue of waiting tasks 41 | queue: SpinlockIrqSave, 42 | /// protected data 43 | data: UnsafeCell, 44 | } 45 | 46 | /// A guard to which the protected data can be accessed 47 | /// 48 | /// When the guard falls out of scope it will release the lock. 49 | pub struct MutexGuard<'a, T: ?Sized + 'a> { 50 | value: &'a SpinlockIrqSave, 51 | queue: &'a SpinlockIrqSave, 52 | data: &'a mut T, 53 | } 54 | 55 | // Same unsafe impls as `std::sync::Mutex` 56 | unsafe impl Sync for Mutex {} 57 | unsafe impl Send for Mutex {} 58 | 59 | impl Mutex { 60 | /// Creates a new semaphore with the initial count specified. 61 | /// 62 | /// The count specified can be thought of as a number of resources, and a 63 | /// call to `acquire` or `access` will block until at least one resource is 64 | /// available. It is valid to initialize a semaphore with a negative count. 65 | pub fn new(user_data: T) -> Mutex { 66 | Mutex { 67 | value: SpinlockIrqSave::new(true), 68 | queue: SpinlockIrqSave::new(PriorityTaskQueue::new()), 69 | data: UnsafeCell::new(user_data), 70 | } 71 | } 72 | 73 | /// Consumes this mutex, returning the underlying data. 74 | pub fn into_inner(self) -> T { 75 | // We know statically that there are no outstanding references to 76 | // `self` so there's no need to lock. 77 | let Mutex { data, .. } = self; 78 | data.into_inner() 79 | } 80 | } 81 | 82 | impl Mutex { 83 | fn obtain_lock(&self) { 84 | loop { 85 | let mut count = self.value.lock(); 86 | 87 | if *count { 88 | *count = false; 89 | return; 90 | } else { 91 | self.queue.lock().push(block_current_task()); 92 | // release lock 93 | drop(count); 94 | // switch to the next task 95 | reschedule(); 96 | } 97 | } 98 | } 99 | 100 | pub fn lock(&self) -> MutexGuard<'_, T> { 101 | self.obtain_lock(); 102 | MutexGuard { 103 | value: &self.value, 104 | queue: &self.queue, 105 | data: unsafe { &mut *self.data.get() }, 106 | } 107 | } 108 | } 109 | 110 | impl Default for Mutex { 111 | fn default() -> Mutex { 112 | Mutex::new(Default::default()) 113 | } 114 | } 115 | 116 | impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> { 117 | type Target = T; 118 | fn deref(&self) -> &T { 119 | &*self.data 120 | } 121 | } 122 | 123 | impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> { 124 | fn deref_mut(&mut self) -> &mut T { 125 | &mut *self.data 126 | } 127 | } 128 | 129 | impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> { 130 | /// The dropping of the MutexGuard will release the lock it was created from. 131 | fn drop(&mut self) { 132 | let mut count = self.value.lock(); 133 | *count = true; 134 | 135 | // try to wakeup next task 136 | if let Some(task) = self.queue.lock().pop() { 137 | wakeup_task(task); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/task.rs: -------------------------------------------------------------------------------- 1 | //! Architecture dependent interface to initialize a task 2 | 3 | use crate::arch::mm::VirtAddr; 4 | use crate::consts::*; 5 | use crate::logging::*; 6 | use crate::scheduler::task::*; 7 | use crate::scheduler::{do_exit, get_current_taskid}; 8 | use core::mem::size_of; 9 | use core::ptr::write_bytes; 10 | 11 | #[cfg(target_arch = "x86_64")] 12 | #[repr(C, packed)] 13 | struct State { 14 | /// GS register 15 | gs: u64, 16 | /// FS register 17 | fs: u64, 18 | /// R15 register 19 | r15: u64, 20 | /// R14 register 21 | r14: u64, 22 | /// R13 register 23 | r13: u64, 24 | /// R12 register 25 | r12: u64, 26 | /// R11 register 27 | r11: u64, 28 | /// R10 register 29 | r10: u64, 30 | /// R9 register 31 | r9: u64, 32 | /// R8 register 33 | r8: u64, 34 | /// RDI register 35 | rdi: u64, 36 | /// RSI register 37 | rsi: u64, 38 | /// RBP register 39 | rbp: u64, 40 | /// (pseudo) RSP register 41 | rsp: u64, 42 | /// RBX register 43 | rbx: u64, 44 | /// RDX register 45 | rdx: u64, 46 | /// RCX register 47 | rcx: u64, 48 | /// RAX register 49 | rax: u64, 50 | /// status flags 51 | rflags: u64, 52 | /// instruction pointer 53 | rip: u64, 54 | } 55 | 56 | #[cfg(target_arch = "x86")] 57 | #[repr(C, packed)] 58 | struct State { 59 | /// EDI register 60 | edi: u32, 61 | /// ESI register 62 | esi: u32, 63 | /// EBP register 64 | ebp: u32, 65 | /// (pseudo) ESP register 66 | esp: u32, 67 | /// EBX register 68 | ebx: u32, 69 | /// EDX register 70 | edx: u32, 71 | /// ECX register 72 | ecx: u32, 73 | /// EAX register 74 | eax: u32, 75 | /// status flags 76 | eflags: u32, 77 | /// instruction pointer 78 | eip: u32, 79 | } 80 | 81 | extern "C" fn leave_task() -> ! { 82 | debug!("finish task {}", get_current_taskid()); 83 | 84 | do_exit(); 85 | } 86 | 87 | impl TaskFrame for Task { 88 | #[cfg(target_arch = "x86_64")] 89 | fn create_stack_frame(&mut self, func: extern "C" fn()) { 90 | unsafe { 91 | let mut stack: *mut u64 = ((*self.stack).top()).as_mut_ptr(); 92 | 93 | write_bytes((*self.stack).bottom().as_mut_ptr::(), 0xCD, STACK_SIZE); 94 | 95 | /* Only marker for debugging purposes, ... */ 96 | *stack = 0xDEADBEEFu64; 97 | stack = (stack as usize - size_of::()) as *mut u64; 98 | 99 | /* the first-function-to-be-called's arguments, ... */ 100 | //TODO: add arguments 101 | 102 | /* and the "caller" we shall return to. 103 | * This procedure cleans the task after exit. */ 104 | *stack = (leave_task as *const ()) as u64; 105 | stack = (stack as usize - size_of::()) as *mut u64; 106 | 107 | let state: *mut State = stack as *mut State; 108 | write_bytes(state, 0x00, 1); 109 | 110 | (*state).rsp = (stack as usize + size_of::()) as u64; 111 | (*state).rbp = (*state).rsp + size_of::() as u64; 112 | (*state).gs = ((*self.stack).top()).as_u64(); 113 | 114 | (*state).rip = (func as *const ()) as u64; 115 | (*state).rflags = 0x1202u64; 116 | 117 | /* Set the task's stack pointer entry to the stack we have crafted right now. */ 118 | self.last_stack_pointer = VirtAddr(stack as u64); 119 | } 120 | } 121 | 122 | #[cfg(target_arch = "x86")] 123 | fn create_stack_frame(&mut self, func: extern "C" fn()) { 124 | unsafe { 125 | let mut stack: *mut u32 = ((*self.stack).top()).as_mut_ptr(); 126 | 127 | write_bytes((*self.stack).bottom().as_mut_ptr::(), 0xCD, STACK_SIZE); 128 | 129 | /* Only marker for debugging purposes, ... */ 130 | *stack = 0xDEADBEEFu32; 131 | stack = (stack as usize - size_of::()) as *mut u32; 132 | 133 | /* the first-function-to-be-called's arguments, ... */ 134 | //TODO: add arguments 135 | 136 | /* and the "caller" we shall return to. 137 | * This procedure cleans the task after exit. */ 138 | *stack = (leave_task as *const ()) as u32; 139 | stack = (stack as usize - size_of::()) as *mut u32; 140 | 141 | let state: *mut State = stack as *mut State; 142 | write_bytes(state, 0x00, 1); 143 | 144 | (*state).esp = (stack as usize + size_of::()) as u32; 145 | (*state).ebp = (*state).esp + size_of::() as u32; 146 | 147 | (*state).eip = (func as *const ()) as u32; 148 | (*state).eflags = 0x1002u32; 149 | 150 | /* Set the task's stack pointer entry to the stack we have crafted right now. */ 151 | self.last_stack_pointer = stack as usize; 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/arch/x86/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod kernel; 2 | pub mod mm; 3 | 4 | use self::mm::paging; 5 | use self::mm::paging::{BasePageSize, PageSize, PageTableEntryFlags}; 6 | use self::mm::physicalmem; 7 | use crate::consts::*; 8 | use crate::fs; 9 | use crate::io::{self, Read}; 10 | use crate::logging::*; 11 | use alloc::string::String; 12 | use alloc::vec::Vec; 13 | use core::ptr::write_bytes; 14 | use core::slice; 15 | use goblin::elf::program_header::{PT_DYNAMIC, PT_GNU_RELRO, PT_LOAD}; 16 | use goblin::elf64::dynamic::{DT_RELA, DT_RELASZ}; 17 | use goblin::elf64::reloc::{R_386_GLOB_DAT, R_386_RELATIVE}; 18 | use goblin::{elf, elf64}; 19 | use x86::controlregs; 20 | 21 | pub fn load_application(path: &String) -> io::Result<()> { 22 | debug!("Try to load application!"); 23 | unsafe { 24 | controlregs::cr3_write(paging::create_usr_pgd().as_u64()); 25 | } 26 | 27 | let mut file = fs::File::open(path)?; 28 | let len = file.len()?; 29 | debug!("File has a size of {} bytes", len); 30 | let mut buffer: Vec = Vec::new(); 31 | 32 | buffer.resize(len, 0); 33 | file.read(&mut buffer)?; 34 | let elf = match elf::Elf::parse(&buffer) { 35 | Ok(n) => n, 36 | _ => return Err(io::Error::EINVAL), 37 | }; 38 | drop(file); // close file 39 | debug!("elf information: {:#?}", &elf); 40 | 41 | if elf.is_lib { 42 | error!("Error: File is an ELF library"); 43 | return Err(io::Error::EINVAL); 44 | } 45 | 46 | if !elf.is_64 { 47 | error!("Error: File isn't a 64bit ELF executable"); 48 | return Err(io::Error::EINVAL); 49 | } 50 | 51 | if elf.libraries.len() > 0 { 52 | error!( 53 | "Error: File depends on following libraries: {:?}", 54 | elf.libraries 55 | ); 56 | return Err(io::Error::EINVAL); 57 | } 58 | 59 | // Determine the memory size of the executable 60 | let vstart: usize = 0; 61 | let mut exec_size: usize = 0; 62 | for i in &elf.program_headers { 63 | if i.p_type == PT_LOAD { 64 | /*if vstart == 0 { 65 | vstart = i.p_vaddr as usize; 66 | }*/ 67 | 68 | exec_size = align_up!( 69 | i.p_vaddr as usize - vstart + i.p_memsz as usize, 70 | BasePageSize::SIZE 71 | ); 72 | } 73 | } 74 | debug!("Virtual start address 0x{:x}", vstart); 75 | debug!("Memory size 0x{:x} {}", exec_size, len); 76 | 77 | if exec_size == 0 { 78 | error!("Error: unable to find PT_LOAD",); 79 | return Err(io::Error::EINVAL); 80 | } 81 | 82 | let physical_address = physicalmem::allocate(exec_size); 83 | paging::map::( 84 | USER_ENTRY, 85 | physical_address, 86 | exec_size / BasePageSize::SIZE, 87 | PageTableEntryFlags::WRITABLE | PageTableEntryFlags::USER_ACCESSIBLE, 88 | ); 89 | 90 | unsafe { 91 | write_bytes(USER_ENTRY.as_mut_ptr() as *mut u8, 0x00, exec_size); 92 | } 93 | 94 | let mut rela_addr: u64 = 0; 95 | let mut relasz: u64 = 0; 96 | //let mut relaent: u64 = 0; 97 | for i in &elf.program_headers { 98 | if i.p_type == PT_LOAD { 99 | debug!("Load code for address 0x{:x}", i.p_vaddr); 100 | 101 | let mem = (USER_ENTRY.as_usize() + i.p_vaddr as usize - vstart) as *mut u8; 102 | let mem_slice = unsafe { slice::from_raw_parts_mut(mem, i.p_filesz as usize) }; 103 | 104 | mem_slice[0..i.p_filesz as usize].clone_from_slice( 105 | &buffer[(i.p_offset as usize)..(i.p_offset + i.p_filesz) as usize], 106 | ); 107 | } else if i.p_type == PT_GNU_RELRO { 108 | debug!( 109 | "PT_GNU_RELRO at 0x{:x} (size 0x{:x})", 110 | i.p_vaddr, i.p_filesz 111 | ); 112 | } else if i.p_type == PT_DYNAMIC { 113 | debug!("PT_DYNAMIC at 0x{:x} (size 0x{:x})", i.p_vaddr, i.p_filesz); 114 | 115 | let mem = (USER_ENTRY.as_u64() + i.p_vaddr as u64 - vstart as u64) as *mut u8; 116 | let r#dyn = unsafe { elf::dynamic::dyn64::from_raw(0, mem as usize) }; 117 | 118 | for j in r#dyn { 119 | if j.d_tag == DT_RELA { 120 | rela_addr = USER_ENTRY.as_u64() + j.d_val; 121 | } else if j.d_tag == DT_RELASZ { 122 | relasz = j.d_val; 123 | } /*else if j.d_tag == DT_RELAENT { 124 | relaent = j.d_val; 125 | }*/ 126 | } 127 | } 128 | } 129 | 130 | let rela = unsafe { 131 | elf64::reloc::from_raw_rela(rela_addr as *const elf64::reloc::Rela, relasz as usize) 132 | }; 133 | for j in rela { 134 | let offset = (USER_ENTRY.as_usize() - vstart + j.r_offset as usize) as *mut u64; 135 | 136 | if (j.r_info & 0xF) == R_386_RELATIVE as u64 { 137 | unsafe { 138 | *offset = (USER_ENTRY.as_usize() as i64 - vstart as i64 + j.r_addend) as u64; 139 | } 140 | } else if (j.r_info & 0xF) == R_386_GLOB_DAT as u64 { 141 | } else { 142 | error!("Unsupported relocation type {}", j.r_info & 0xF); 143 | } 144 | } 145 | 146 | let entry = elf.entry as usize - vstart as usize + USER_ENTRY.as_usize(); 147 | 148 | // free temporary buffer 149 | drop(buffer); 150 | 151 | debug!("jump to user land at 0x{:x}", entry); 152 | unsafe { 153 | self::kernel::jump_to_user_land(entry); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/synch/spinlock.rs: -------------------------------------------------------------------------------- 1 | use crate::arch; 2 | use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; 3 | use lock_api::{GuardSend, RawMutex, RawMutexFair}; 4 | 5 | /// A [fair] [ticket lock]. 6 | /// 7 | /// [fair]: https://en.wikipedia.org/wiki/Unbounded_nondeterminism 8 | /// [ticket lock]: https://en.wikipedia.org/wiki/Ticket_lock 9 | pub struct RawSpinlock { 10 | queue: AtomicUsize, 11 | dequeue: AtomicUsize, 12 | } 13 | 14 | unsafe impl RawMutex for RawSpinlock { 15 | #[allow(clippy::declare_interior_mutable_const)] 16 | const INIT: Self = Self { 17 | queue: AtomicUsize::new(0), 18 | dequeue: AtomicUsize::new(0), 19 | }; 20 | 21 | type GuardMarker = GuardSend; 22 | 23 | #[inline] 24 | fn lock(&self) { 25 | let ticket = self.queue.fetch_add(1, Ordering::Relaxed); 26 | while self.dequeue.load(Ordering::Acquire) != ticket { 27 | core::hint::spin_loop(); 28 | } 29 | } 30 | 31 | #[inline] 32 | fn try_lock(&self) -> bool { 33 | let ticket = self 34 | .queue 35 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |ticket| { 36 | if self.dequeue.load(Ordering::Acquire) == ticket { 37 | Some(ticket + 1) 38 | } else { 39 | None 40 | } 41 | }); 42 | 43 | ticket.is_ok() 44 | } 45 | 46 | #[inline] 47 | unsafe fn unlock(&self) { 48 | self.dequeue.fetch_add(1, Ordering::Release); 49 | } 50 | 51 | #[inline] 52 | fn is_locked(&self) -> bool { 53 | let ticket = self.dequeue.load(Ordering::Relaxed); 54 | self.dequeue.load(Ordering::Relaxed) != ticket 55 | } 56 | } 57 | 58 | unsafe impl RawMutexFair for RawSpinlock { 59 | #[inline] 60 | unsafe fn unlock_fair(&self) { 61 | unsafe { self.unlock() } 62 | } 63 | 64 | #[inline] 65 | unsafe fn bump(&self) { 66 | let ticket = self.queue.load(Ordering::Relaxed); 67 | let serving = self.dequeue.load(Ordering::Relaxed); 68 | if serving + 1 != ticket { 69 | unsafe { 70 | self.unlock_fair(); 71 | self.lock(); 72 | } 73 | } 74 | } 75 | } 76 | 77 | /// A [`lock_api::Mutex`] based on [`RawSpinlockMutex`]. 78 | pub type Spinlock = lock_api::Mutex; 79 | 80 | /// A [`lock_api::MutexGuard`] based on [`RawSpinlockMutex`]. 81 | pub type SpinlockGuard<'a, T> = lock_api::MutexGuard<'a, RawSpinlock, T>; 82 | 83 | /// A [fair] irqsave [ticket lock]. 84 | /// 85 | /// [fair]: https://en.wikipedia.org/wiki/Unbounded_nondeterminism 86 | /// [ticket lock]: https://en.wikipedia.org/wiki/Ticket_lock 87 | pub struct RawSpinlockIrqSave { 88 | queue: AtomicUsize, 89 | dequeue: AtomicUsize, 90 | irq: AtomicBool, 91 | } 92 | 93 | unsafe impl RawMutex for RawSpinlockIrqSave { 94 | #[allow(clippy::declare_interior_mutable_const)] 95 | const INIT: Self = Self { 96 | queue: AtomicUsize::new(0), 97 | dequeue: AtomicUsize::new(0), 98 | irq: AtomicBool::new(false), 99 | }; 100 | 101 | type GuardMarker = GuardSend; 102 | 103 | #[inline] 104 | fn lock(&self) { 105 | let irq = arch::irq::irq_nested_disable(); 106 | let ticket = self.queue.fetch_add(1, Ordering::Relaxed); 107 | 108 | while self.dequeue.load(Ordering::Acquire) != ticket { 109 | arch::irq::irq_nested_enable(irq); 110 | core::hint::spin_loop(); 111 | arch::irq::irq_nested_disable(); 112 | } 113 | 114 | self.irq.store(irq, Ordering::SeqCst); 115 | } 116 | 117 | #[inline] 118 | fn try_lock(&self) -> bool { 119 | let irq = arch::irq::irq_nested_disable(); 120 | let ticket = self 121 | .queue 122 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |ticket| { 123 | if self.dequeue.load(Ordering::Acquire) == ticket { 124 | self.irq.store(irq, Ordering::SeqCst); 125 | Some(ticket + 1) 126 | } else { 127 | arch::irq::irq_nested_enable(irq); 128 | None 129 | } 130 | }); 131 | 132 | ticket.is_ok() 133 | } 134 | 135 | #[inline] 136 | unsafe fn unlock(&self) { 137 | let irq = self.irq.swap(false, Ordering::SeqCst); 138 | self.dequeue.fetch_add(1, Ordering::Release); 139 | arch::irq::irq_nested_enable(irq); 140 | } 141 | 142 | #[inline] 143 | fn is_locked(&self) -> bool { 144 | let ticket = self.dequeue.load(Ordering::Relaxed); 145 | self.dequeue.load(Ordering::Relaxed) != ticket 146 | } 147 | } 148 | 149 | unsafe impl RawMutexFair for RawSpinlockIrqSave { 150 | #[inline] 151 | unsafe fn unlock_fair(&self) { 152 | unsafe { self.unlock() } 153 | } 154 | 155 | #[inline] 156 | unsafe fn bump(&self) { 157 | let irq = arch::irq::irq_nested_disable(); 158 | let ticket = self.queue.load(Ordering::Relaxed); 159 | let serving = self.dequeue.load(Ordering::Relaxed); 160 | if serving + 1 != ticket { 161 | unsafe { 162 | self.unlock_fair(); 163 | self.lock(); 164 | } 165 | } 166 | arch::irq::irq_nested_enable(irq); 167 | } 168 | } 169 | 170 | /// A [`lock_api::Mutex`] based on [`RawSpinlockMutex`]. 171 | pub type SpinlockIrqSave = lock_api::Mutex; 172 | 173 | /// A [`lock_api::MutexGuard`] based on [`RawSpinlockMutex`]. 174 | pub type SpinlockIrqSaveGuard<'a, T> = lock_api::MutexGuard<'a, RawSpinlockIrqSave, T>; 175 | -------------------------------------------------------------------------------- /src/arch/x86/mm/mod.rs: -------------------------------------------------------------------------------- 1 | // Original version written by Colin Finck, RWTH Aachen University 2 | 3 | pub mod paging; 4 | pub mod physicalmem; 5 | pub mod virtualmem; 6 | 7 | use crate::arch::x86::kernel::BOOT_INFO; 8 | use crate::consts::INTERRUPT_STACK_SIZE; 9 | use crate::scheduler::task::Stack; 10 | use bootloader::bootinfo::MemoryRegionType; 11 | use core::convert::TryInto; 12 | use core::ops::Deref; 13 | #[cfg(target_arch = "x86")] 14 | pub use x86::bits32::paging::VAddr as VirtAddr; 15 | #[cfg(target_arch = "x86_64")] 16 | pub use x86::bits64::paging::PAddr as PhysAddr; 17 | #[cfg(target_arch = "x86_64")] 18 | pub use x86::bits64::paging::VAddr as VirtAddr; 19 | 20 | #[allow(dead_code)] 21 | #[derive(Copy, Clone)] 22 | pub(crate) struct BootStack { 23 | start: VirtAddr, 24 | end: VirtAddr, 25 | ist_start: VirtAddr, 26 | ist_end: VirtAddr, 27 | } 28 | 29 | impl BootStack { 30 | pub const fn new( 31 | start: VirtAddr, 32 | end: VirtAddr, 33 | ist_start: VirtAddr, 34 | ist_end: VirtAddr, 35 | ) -> Self { 36 | Self { 37 | start, 38 | end, 39 | ist_start, 40 | ist_end, 41 | } 42 | } 43 | } 44 | 45 | impl Stack for BootStack { 46 | fn top(&self) -> VirtAddr { 47 | cfg_if::cfg_if! { 48 | if #[cfg(target_arch = "x86")] { 49 | self.end - 16u32 50 | } else { 51 | self.end - 16u64 52 | } 53 | } 54 | } 55 | 56 | fn bottom(&self) -> VirtAddr { 57 | self.start 58 | } 59 | 60 | fn interrupt_top(&self) -> VirtAddr { 61 | cfg_if::cfg_if! { 62 | if #[cfg(target_arch = "x86")] { 63 | self.ist_end - 16u32 64 | } else { 65 | self.ist_end - 16u64 66 | } 67 | } 68 | } 69 | 70 | fn interrupt_bottom(&self) -> VirtAddr { 71 | self.ist_start 72 | } 73 | } 74 | 75 | #[cfg(target_arch = "x86_64")] 76 | pub(crate) fn get_boot_stack() -> BootStack { 77 | use crate::arch::x86::kernel::BOOT_INFO; 78 | use bootloader::bootinfo::MemoryRegionType; 79 | use core::ops::Deref; 80 | 81 | unsafe { 82 | let regions = BOOT_INFO.unwrap().memory_map.deref(); 83 | 84 | for i in regions { 85 | if i.region_type == MemoryRegionType::KernelStack { 86 | return BootStack::new( 87 | VirtAddr(i.range.start_frame_number * 0x1000), 88 | VirtAddr(i.range.end_frame_number * 0x1000), 89 | VirtAddr((BOOT_IST_STACK.0.as_ptr() as usize).try_into().unwrap()), 90 | VirtAddr( 91 | (BOOT_IST_STACK.0.as_ptr() as usize + INTERRUPT_STACK_SIZE) 92 | .try_into() 93 | .unwrap(), 94 | ), 95 | ); 96 | } 97 | } 98 | 99 | panic!("Unable to determine the kernel stack"); 100 | } 101 | } 102 | 103 | #[allow(dead_code)] 104 | pub(crate) fn is_kernel(addr: VirtAddr) -> bool { 105 | unsafe { 106 | let regions = BOOT_INFO.unwrap().memory_map.deref(); 107 | 108 | for i in regions { 109 | if i.region_type == MemoryRegionType::Kernel 110 | && addr >= VirtAddr(i.range.start_frame_number * 0x1000) 111 | && addr <= VirtAddr(i.range.end_frame_number * 0x1000) 112 | { 113 | return true; 114 | } 115 | 116 | if i.region_type == MemoryRegionType::KernelStack 117 | && addr >= VirtAddr(i.range.start_frame_number * 0x1000) 118 | && addr <= VirtAddr(i.range.end_frame_number * 0x1000) 119 | { 120 | return true; 121 | } 122 | } 123 | } 124 | 125 | false 126 | } 127 | 128 | pub(crate) fn get_memory_size() -> usize { 129 | let mut sz: u64 = 0; 130 | 131 | unsafe { 132 | let regions = BOOT_INFO.unwrap().memory_map.deref(); 133 | 134 | for i in regions { 135 | match i.region_type { 136 | MemoryRegionType::Usable 137 | | MemoryRegionType::InUse 138 | | MemoryRegionType::Kernel 139 | | MemoryRegionType::KernelStack 140 | | MemoryRegionType::PageTable 141 | | MemoryRegionType::Bootloader 142 | | MemoryRegionType::FrameZero 143 | | MemoryRegionType::BootInfo 144 | | MemoryRegionType::Package => { 145 | sz += (i.range.end_frame_number - i.range.start_frame_number) * 0x1000 146 | } 147 | _ => {} 148 | } 149 | } 150 | } 151 | 152 | sz.try_into().unwrap() 153 | } 154 | 155 | pub(crate) fn init() { 156 | paging::init(); 157 | physicalmem::init(); 158 | virtualmem::init(); 159 | } 160 | 161 | #[repr(C, align(64))] 162 | pub(crate) struct Aligned(T); 163 | 164 | impl Aligned { 165 | /// Constructor. 166 | pub const fn new(t: T) -> Self { 167 | Self(t) 168 | } 169 | } 170 | 171 | #[cfg(target_arch = "x86")] 172 | pub(crate) const BOOT_STACK_SIZE: usize = 0x10000; 173 | #[cfg(target_arch = "x86")] 174 | #[link_section = ".data"] 175 | pub(crate) static mut BOOT_STACK: Aligned<[u8; BOOT_STACK_SIZE]> = 176 | Aligned::new([0; BOOT_STACK_SIZE]); 177 | pub(crate) static mut BOOT_IST_STACK: Aligned<[u8; INTERRUPT_STACK_SIZE]> = 178 | Aligned::new([0; INTERRUPT_STACK_SIZE]); 179 | 180 | #[cfg(target_arch = "x86")] 181 | pub(crate) fn get_boot_stack() -> BootStack { 182 | BootStack::new( 183 | unsafe { VirtAddr((BOOT_STACK.0.as_ptr() as usize).try_into().unwrap()) }, 184 | unsafe { 185 | VirtAddr( 186 | (BOOT_STACK.0.as_ptr() as usize + BOOT_STACK_SIZE) 187 | .try_into() 188 | .unwrap(), 189 | ) 190 | }, 191 | unsafe { VirtAddr((BOOT_IST_STACK.0.as_ptr() as usize).try_into().unwrap()) }, 192 | unsafe { 193 | VirtAddr( 194 | (BOOT_IST_STACK.0.as_ptr() as usize + INTERRUPT_STACK_SIZE) 195 | .try_into() 196 | .unwrap(), 197 | ) 198 | }, 199 | ) 200 | } 201 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::mm::get_boot_stack; 2 | use crate::arch::x86::kernel::syscall_handler; 3 | use crate::logging::*; 4 | use crate::scheduler::task::Stack; 5 | use core::arch::asm; 6 | #[cfg(feature = "qemu-exit")] 7 | use qemu_exit::QEMUExit; 8 | use x86::controlregs::*; 9 | use x86::cpuid::*; 10 | use x86::msr::*; 11 | 12 | // MSR EFER bits 13 | #[allow(dead_code)] 14 | const EFER_SCE: u64 = 1 << 0; 15 | #[allow(dead_code)] 16 | const EFER_LME: u64 = 1 << 8; 17 | #[allow(dead_code)] 18 | const EFER_LMA: u64 = 1 << 10; 19 | #[allow(dead_code)] 20 | const EFER_NXE: u64 = 1 << 11; 21 | #[allow(dead_code)] 22 | const EFER_SVME: u64 = 1 << 12; 23 | #[allow(dead_code)] 24 | const EFER_LMSLE: u64 = 1 << 13; 25 | #[allow(dead_code)] 26 | const EFER_FFXSR: u64 = 1 << 14; 27 | #[allow(dead_code)] 28 | const EFER_TCE: u64 = 1 << 15; 29 | 30 | static mut PHYSICAL_ADDRESS_BITS: u8 = 0; 31 | static mut LINEAR_ADDRESS_BITS: u8 = 0; 32 | static mut SUPPORTS_1GIB_PAGES: bool = false; 33 | 34 | /// Force strict CPU ordering, serializes load and store operations. 35 | #[inline(always)] 36 | pub(crate) fn mb() { 37 | unsafe { 38 | asm!("mfence", options(preserves_flags, nostack)); 39 | } 40 | } 41 | 42 | #[allow(dead_code)] 43 | #[inline(always)] 44 | pub(crate) fn halt() { 45 | unsafe { 46 | asm!("hlt", options(nomem, nostack)); 47 | } 48 | } 49 | 50 | #[allow(unused_variables)] 51 | #[no_mangle] 52 | pub(crate) extern "C" fn shutdown(error_code: i32) -> ! { 53 | #[cfg(feature = "qemu-exit")] 54 | { 55 | let code = if error_code == 0 { 5 } else { 1 }; 56 | 57 | // shutdown, works like Qemu's shutdown command 58 | let qemu_exit_handle = qemu_exit::X86::new(0xf4, code); 59 | qemu_exit_handle.exit_success(); 60 | } 61 | 62 | #[cfg(not(feature = "qemu-exit"))] 63 | loop { 64 | unsafe { 65 | x86::halt(); 66 | } 67 | } 68 | } 69 | 70 | pub(crate) fn supports_1gib_pages() -> bool { 71 | unsafe { SUPPORTS_1GIB_PAGES } 72 | } 73 | 74 | pub(crate) fn get_linear_address_bits() -> u8 { 75 | unsafe { LINEAR_ADDRESS_BITS } 76 | } 77 | 78 | pub(crate) fn get_physical_address_bits() -> u8 { 79 | unsafe { PHYSICAL_ADDRESS_BITS } 80 | } 81 | 82 | pub(crate) fn init() { 83 | debug!("enable supported processor features"); 84 | 85 | let cpuid = CpuId::new(); 86 | let mut cr0 = unsafe { cr0() }; 87 | 88 | // be sure that AM, NE and MP is enabled 89 | cr0 |= Cr0::CR0_ALIGNMENT_MASK; 90 | cr0 |= Cr0::CR0_NUMERIC_ERROR; 91 | cr0 |= Cr0::CR0_MONITOR_COPROCESSOR; 92 | // enable cache 93 | cr0 &= !(Cr0::CR0_CACHE_DISABLE | Cr0::CR0_NOT_WRITE_THROUGH); 94 | 95 | debug!("set CR0 to {:?}", cr0); 96 | 97 | unsafe { cr0_write(cr0) }; 98 | 99 | let mut cr4 = unsafe { cr4() }; 100 | 101 | let has_pge = match cpuid.get_feature_info() { 102 | Some(finfo) => finfo.has_pge(), 103 | None => false, 104 | }; 105 | 106 | if has_pge { 107 | cr4 |= Cr4::CR4_ENABLE_GLOBAL_PAGES; 108 | } 109 | 110 | let has_fsgsbase = match cpuid.get_extended_feature_info() { 111 | Some(efinfo) => efinfo.has_fsgsbase(), 112 | None => false, 113 | }; 114 | 115 | if has_fsgsbase { 116 | cr4 |= Cr4::CR4_ENABLE_FSGSBASE; 117 | } else { 118 | panic!("eduOS-rs requires the CPU feature FSGSBASE"); 119 | } 120 | 121 | let has_mce = match cpuid.get_feature_info() { 122 | Some(finfo) => finfo.has_mce(), 123 | None => false, 124 | }; 125 | 126 | if has_mce { 127 | cr4 |= Cr4::CR4_ENABLE_MACHINE_CHECK; // enable machine check exceptions 128 | } 129 | 130 | // disable performance monitoring counter 131 | // allow the usage of rdtsc in user space 132 | cr4 &= !(Cr4::CR4_ENABLE_PPMC | Cr4::CR4_TIME_STAMP_DISABLE); 133 | 134 | debug!("set CR4 to {:?}", cr4); 135 | 136 | unsafe { cr4_write(cr4) }; 137 | 138 | let has_syscall = match cpuid.get_extended_processor_and_feature_identifiers() { 139 | Some(finfo) => finfo.has_syscall_sysret(), 140 | None => false, 141 | }; 142 | 143 | if !has_syscall { 144 | panic!("Syscall support is missing"); 145 | } 146 | 147 | // enable support of syscall and sysret 148 | unsafe { 149 | wrmsr(IA32_EFER, rdmsr(IA32_EFER) | EFER_LMA | EFER_SCE | EFER_NXE); 150 | wrmsr(IA32_STAR, (0x1Bu64 << 48) | (0x08u64 << 32)); 151 | wrmsr(IA32_LSTAR, (syscall_handler as usize).try_into().unwrap()); 152 | wrmsr(IA32_FMASK, 1 << 9); // clear IF flag during system call 153 | 154 | // reset GS registers 155 | wrmsr(IA32_GS_BASE, 0); 156 | asm!("wrgsbase {}", in(reg) get_boot_stack().top().as_u64(), options(preserves_flags, nomem, nostack)); 157 | } 158 | 159 | // determin processor features 160 | let extended_feature_info = cpuid 161 | .get_processor_capacity_feature_info() 162 | .expect("CPUID Capacity Feature Info is not available!"); 163 | unsafe { 164 | PHYSICAL_ADDRESS_BITS = extended_feature_info.physical_address_bits(); 165 | LINEAR_ADDRESS_BITS = extended_feature_info.linear_address_bits(); 166 | SUPPORTS_1GIB_PAGES = cpuid 167 | .get_extended_processor_and_feature_identifiers() 168 | .expect("CPUID Extended Processor and Feature Info is not available!") 169 | .has_1gib_pages(); 170 | } 171 | 172 | if supports_1gib_pages() { 173 | info!("System supports 1GiB pages"); 174 | } 175 | debug!("Physical address bits {}", get_physical_address_bits()); 176 | debug!("Linear address bits {}", get_linear_address_bits()); 177 | debug!("CR0: {:?}", cr0); 178 | debug!("CR4: {:?}", cr4); 179 | } 180 | -------------------------------------------------------------------------------- /src/fs/initrd.rs: -------------------------------------------------------------------------------- 1 | //! Implements basic functions to realize a simple in-memory file system 2 | 3 | use crate::fd::OpenOption; 4 | use crate::fs::SeekFrom; 5 | use crate::io; 6 | use crate::synch::spinlock::*; 7 | use alloc::sync::Arc; 8 | use alloc::vec::Vec; 9 | use core::ops::{Deref, DerefMut}; 10 | use spinning_top::RwSpinlock; 11 | 12 | #[derive(Debug)] 13 | pub(crate) struct RomHandle { 14 | /// Position within the file 15 | pos: Spinlock, 16 | /// File content 17 | data: Arc>, 18 | } 19 | 20 | impl RomHandle { 21 | pub fn new(slice: &'static [u8]) -> Self { 22 | RomHandle { 23 | pos: Spinlock::new(0), 24 | data: Arc::new(RwSpinlock::new(slice)), 25 | } 26 | } 27 | 28 | pub fn get_handle(&self, _opt: OpenOption) -> RomHandle { 29 | RomHandle { 30 | pos: Spinlock::new(0), 31 | data: self.data.clone(), 32 | } 33 | } 34 | 35 | pub fn read(&self, buf: &mut [u8]) -> io::Result { 36 | let vec = self.data.read(); 37 | let mut pos_guard = self.pos.lock(); 38 | let pos = *pos_guard; 39 | 40 | if pos >= vec.len() { 41 | return Ok(0); 42 | } 43 | 44 | let len; 45 | if vec.len() - pos < buf.len() { 46 | len = vec.len() - pos 47 | } else { 48 | len = buf.len() 49 | } 50 | 51 | buf[0..len].clone_from_slice(&vec[pos..pos + len]); 52 | *pos_guard = pos + len; 53 | 54 | Ok(len) 55 | } 56 | 57 | pub fn seek(&self, style: SeekFrom) -> io::Result { 58 | let mut pos_guard = self.pos.lock(); 59 | 60 | match style { 61 | SeekFrom::Start(n) => { 62 | *pos_guard = n; 63 | Ok(n) 64 | } 65 | SeekFrom::End(n) => { 66 | let guard = self.data.read(); 67 | let data = guard.len() as isize + n; 68 | if data >= 0 { 69 | *pos_guard = data as usize; 70 | Ok(data as usize) 71 | } else { 72 | Err(io::Error::EINVAL) 73 | } 74 | } 75 | SeekFrom::Current(n) => { 76 | let pos = *pos_guard as isize + n; 77 | if pos >= 0 { 78 | *pos_guard = pos as usize; 79 | Ok(pos as usize) 80 | } else { 81 | Err(io::Error::EINVAL) 82 | } 83 | } 84 | } 85 | } 86 | 87 | pub fn len(&self) -> usize { 88 | let guard = self.data.read(); 89 | guard.len() as usize 90 | } 91 | } 92 | 93 | impl Clone for RomHandle { 94 | fn clone(&self) -> Self { 95 | RomHandle { 96 | pos: Spinlock::new(*self.pos.lock()), 97 | data: self.data.clone(), 98 | } 99 | } 100 | } 101 | 102 | #[derive(Debug)] 103 | pub(crate) struct RamHandle { 104 | /// Is the file writeable? 105 | writeable: bool, 106 | /// Position within the file 107 | pos: Spinlock, 108 | /// File content 109 | data: Arc>>, 110 | } 111 | 112 | impl RamHandle { 113 | pub fn new(writeable: bool) -> Self { 114 | RamHandle { 115 | writeable: writeable, 116 | pos: Spinlock::new(0), 117 | data: Arc::new(RwSpinlock::new(Vec::new())), 118 | } 119 | } 120 | 121 | pub fn read(&self, buf: &mut [u8]) -> io::Result { 122 | let guard = self.data.read(); 123 | let vec = guard.deref(); 124 | let mut pos_guard = self.pos.lock(); 125 | let pos = *pos_guard; 126 | 127 | if pos >= vec.len() { 128 | return Ok(0); 129 | } 130 | 131 | let len; 132 | if vec.len() - pos < buf.len() { 133 | len = vec.len() - pos 134 | } else { 135 | len = buf.len() 136 | } 137 | 138 | buf[0..len].clone_from_slice(&vec[pos..pos + len]); 139 | *pos_guard = pos + len; 140 | 141 | Ok(len) 142 | } 143 | 144 | pub fn write(&self, buf: &[u8]) -> io::Result { 145 | if self.writeable == false { 146 | return Err(io::Error::EBADF); 147 | } 148 | 149 | let mut guard = self.data.write(); 150 | let vec = guard.deref_mut(); 151 | let mut pos_guard = self.pos.lock(); 152 | let pos = *pos_guard; 153 | 154 | if pos + buf.len() > vec.len() { 155 | vec.resize(pos + buf.len(), 0); 156 | } 157 | 158 | vec[pos..pos + buf.len()].clone_from_slice(buf); 159 | *pos_guard = pos + buf.len(); 160 | 161 | Ok(buf.len()) 162 | } 163 | 164 | pub fn seek(&self, style: SeekFrom) -> io::Result { 165 | let mut pos_guard = self.pos.lock(); 166 | 167 | match style { 168 | SeekFrom::Start(n) => { 169 | *pos_guard = n as usize; 170 | Ok(n) 171 | } 172 | SeekFrom::End(n) => { 173 | let guard = self.data.read(); 174 | let vec = guard.deref(); 175 | let data = vec.len() as isize + n; 176 | if data >= 0 { 177 | *pos_guard = data as usize; 178 | Ok(data as usize) 179 | } else { 180 | Err(io::Error::EINVAL) 181 | } 182 | } 183 | SeekFrom::Current(n) => { 184 | let pos = *pos_guard as isize + n; 185 | if pos >= 0 { 186 | *pos_guard = pos as usize; 187 | Ok(pos as usize) 188 | } else { 189 | Err(io::Error::EINVAL) 190 | } 191 | } 192 | } 193 | } 194 | 195 | pub fn write_str(&self, s: &str) -> core::fmt::Result { 196 | if self.writeable == false { 197 | return Err(core::fmt::Error); 198 | } 199 | 200 | let mut guard = self.data.write(); 201 | let vec = guard.deref_mut(); 202 | let mut pos_guard = self.pos.lock(); 203 | let pos = *pos_guard; 204 | 205 | if pos + s.len() > vec.len() { 206 | vec.resize(pos + s.len(), 0); 207 | } 208 | 209 | vec[pos..pos + s.len()].clone_from_slice(s.as_bytes()); 210 | *pos_guard = pos + s.len(); 211 | 212 | Ok(()) 213 | } 214 | 215 | pub fn get_handle(&self, opt: OpenOption) -> RamHandle { 216 | RamHandle { 217 | writeable: opt.contains(OpenOption::O_RDWR), 218 | pos: Spinlock::new(0), 219 | data: self.data.clone(), 220 | } 221 | } 222 | 223 | pub fn len(&self) -> usize { 224 | let guard = self.data.read(); 225 | let ref vec: &Vec = guard.deref(); 226 | vec.len() as usize 227 | } 228 | } 229 | 230 | impl Clone for RamHandle { 231 | fn clone(&self) -> Self { 232 | RamHandle { 233 | writeable: self.writeable, 234 | pos: Spinlock::new(*self.pos.lock()), 235 | data: self.data.clone(), 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | //! Definition a simple virtual file system 2 | 3 | #![allow(dead_code)] 4 | 5 | mod initrd; 6 | mod vfs; 7 | 8 | use crate::errno::*; 9 | use crate::fd::{self, FileDescriptor, OpenOption}; 10 | use crate::fd::{IoInterface, SeekFrom}; 11 | use crate::fs::vfs::Fs; 12 | use crate::io; 13 | use crate::logging::*; 14 | use crate::scheduler::{insert_io_interface, remove_io_interface}; 15 | use alloc::string::{String, ToString}; 16 | use alloc::sync::Arc; 17 | use alloc::vec::Vec; 18 | use core::include_bytes; 19 | 20 | static DEMO: &[u8] = include_bytes!("../../demo/hello"); 21 | 22 | /// Type of the VfsNode 23 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 24 | pub(crate) enum NodeKind { 25 | /// Node represent a file 26 | File, 27 | /// Node represent a directory 28 | Directory, 29 | } 30 | 31 | /// VfsNode represents an internal node of the virtual file system. 32 | trait VfsNode: core::fmt::Debug + core::marker::Send + core::marker::Sync { 33 | /// Determines the current node type 34 | fn get_kind(&self) -> NodeKind; 35 | } 36 | 37 | /// VfsNodeFile represents a file node of the virtual file system. 38 | trait VfsNodeFile: VfsNode + core::fmt::Debug + core::marker::Send + core::marker::Sync { 39 | /// Create an IO interface to the current file 40 | fn get_handle(&self, _opt: OpenOption) -> Result>; 41 | } 42 | 43 | /// VfsNodeDirectory represents a directory node of the virtual file system. 44 | trait VfsNodeDirectory: VfsNode + core::fmt::Debug + core::marker::Send + core::marker::Sync { 45 | /// Helper function to create a new dirctory node 46 | fn traverse_mkdir(&mut self, _components: &mut Vec<&str>) -> Result<()>; 47 | 48 | /// Helper function to print the current state of the file system 49 | fn traverse_lsdir(&self, _tabs: String) -> Result<()>; 50 | 51 | /// Helper function to open a file 52 | fn traverse_open( 53 | &mut self, 54 | _components: &mut Vec<&str>, 55 | _flags: OpenOption, 56 | ) -> Result>; 57 | 58 | /// Mound memory region as file 59 | fn traverse_mount(&mut self, _components: &mut Vec<&str>, slice: &'static [u8]) -> Result<()>; 60 | } 61 | 62 | /// The trait `Vfs` specifies all operation on the virtual file systems. 63 | trait Vfs: core::fmt::Debug + core::marker::Send + core::marker::Sync { 64 | /// Create a directory node at the location `path`. 65 | fn mkdir(&mut self, path: &String) -> Result<()>; 66 | 67 | /// Print the current state of the file system 68 | fn lsdir(&self) -> Result<()>; 69 | 70 | /// Open a file with the path `path`. 71 | /// `path` must be an absolute path to the file, while `flags` defined 72 | fn open(&mut self, path: &str, flags: OpenOption) -> Result>; 73 | 74 | /// Mound memory region as file 75 | fn mount(&mut self, path: &String, slice: &'static [u8]) -> Result<()>; 76 | } 77 | 78 | /// Entrypoint of the file system 79 | static mut VFS_ROOT: Option = None; 80 | 81 | /// List the current state of file system 82 | pub fn lsdir() -> Result<()> { 83 | unsafe { VFS_ROOT.as_mut().unwrap().lsdir() } 84 | } 85 | 86 | /// Create a directory with the path `path`. 87 | /// `path` must be a absolete path to the direcory. 88 | pub fn mkdir(path: &String) -> Result<()> { 89 | unsafe { VFS_ROOT.as_mut().unwrap().mkdir(path) } 90 | } 91 | 92 | /// Open a file with the path `path`. 93 | /// `path` must be an absolute path to the file, while `flags` defined 94 | /// if the file is writeable or created on demand. 95 | pub fn open(name: &str, flags: OpenOption) -> io::Result { 96 | debug!("Open {}, {:?}", name, flags); 97 | 98 | let fs = unsafe { VFS_ROOT.as_mut().unwrap() }; 99 | if let Ok(file) = fs.open(name, flags) { 100 | let fd = insert_io_interface(file)?; 101 | Ok(fd) 102 | } else { 103 | Err(io::Error::EINVAL) 104 | } 105 | } 106 | 107 | /// Mount slice to to `path` 108 | pub fn mount(path: &String, slice: &'static [u8]) -> Result<()> { 109 | unsafe { VFS_ROOT.as_mut().unwrap().mount(path, slice) } 110 | } 111 | 112 | /// Help function to check if the argument is an abolute path 113 | fn check_path(path: &str) -> bool { 114 | if let Some(pos) = path.find('/') { 115 | if pos == 0 { 116 | return true; 117 | } 118 | } 119 | 120 | false 121 | } 122 | 123 | #[derive(Debug)] 124 | pub struct File { 125 | fd: FileDescriptor, 126 | path: String, 127 | } 128 | 129 | impl File { 130 | /// Attempts to create a file in read-write mode. 131 | pub fn create(path: &str) -> io::Result { 132 | let fd = open(path, OpenOption::O_RDWR | OpenOption::O_CREAT)?; 133 | 134 | Ok(File { 135 | fd, 136 | path: path.to_string(), 137 | }) 138 | } 139 | 140 | /// Attempts to open a file in read-write mode. 141 | pub fn open(path: &str) -> io::Result { 142 | let fd = open(path, OpenOption::O_RDWR)?; 143 | 144 | Ok(File { 145 | fd, 146 | path: path.to_string(), 147 | }) 148 | } 149 | 150 | pub fn len(&self) -> io::Result { 151 | let fstat = fd::fstat(self.fd)?; 152 | Ok(fstat.file_size) 153 | } 154 | } 155 | 156 | impl crate::io::Read for File { 157 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 158 | fd::read(self.fd, buf) 159 | } 160 | } 161 | 162 | impl crate::io::Write for File { 163 | fn write(&mut self, buf: &[u8]) -> io::Result { 164 | fd::write(self.fd, buf) 165 | } 166 | } 167 | 168 | impl Drop for File { 169 | fn drop(&mut self) { 170 | let _ = remove_io_interface(self.fd); 171 | } 172 | } 173 | 174 | pub(crate) fn init() { 175 | let mut root = Fs::new(); 176 | 177 | root.mkdir(&String::from("/bin")).unwrap(); 178 | root.mkdir(&String::from("/dev")).unwrap(); 179 | 180 | if DEMO.len() > 0 { 181 | info!( 182 | "Found mountable file at 0x{:x} (len 0x{:x})", 183 | DEMO.as_ptr() as u64, 184 | DEMO.len() 185 | ); 186 | root.mount(&String::from("/bin/demo"), &DEMO) 187 | .expect("Unable to mount file"); 188 | } 189 | 190 | root.lsdir().unwrap(); 191 | //info!("root {:?}", root); 192 | unsafe { 193 | VFS_ROOT = Some(root); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/mm/buddy.rs: -------------------------------------------------------------------------------- 1 | use crate::mm::linked_list; 2 | use crate::synch::spinlock::Spinlock; 3 | use core::alloc::GlobalAlloc; 4 | use core::alloc::Layout; 5 | use core::cmp::{max, min}; 6 | use core::fmt; 7 | use core::ptr::NonNull; 8 | 9 | /// Minimal size of an allocated memory block 10 | const MIN_ALLOC_SIZE: usize = 128; 11 | 12 | #[derive(Debug)] 13 | pub(crate) enum AllocatorError { 14 | OutOfMemory, 15 | TooBig, 16 | } 17 | 18 | #[repr(align(64))] 19 | pub(crate) struct BuddySystem { 20 | free_list: [linked_list::LinkedList; ORDER], 21 | } 22 | 23 | impl BuddySystem { 24 | /// Constructs an empty buddy system. 25 | pub const fn new() -> Self { 26 | Self { 27 | free_list: [linked_list::LinkedList::new(); ORDER], 28 | } 29 | } 30 | 31 | /// Initialize buddy system. `start` specifies the 32 | /// start address of the heap, while `len` specifies# 33 | /// the heap size. 34 | pub unsafe fn init(&mut self, start: *mut u8, len: usize) { 35 | assert!((len & (len - 1)) == 0, "Heap size isn't a power of two"); 36 | let order: usize = len.trailing_zeros().try_into().unwrap(); 37 | assert!(order <= ORDER, "ORDER isn't large enough"); 38 | 39 | unsafe { 40 | self.free_list[order].push(start as *mut usize); 41 | } 42 | } 43 | 44 | /// Allocates memory as described by the given `layout`. 45 | /// 46 | /// Returns as result a pointer to newly-allocated memory, 47 | /// or an error, which describes the reason of the error. 48 | pub fn alloc(&mut self, layout: Layout) -> Result, AllocatorError> { 49 | let size = max( 50 | layout.size().next_power_of_two(), 51 | max(layout.align(), MIN_ALLOC_SIZE), 52 | ); 53 | let order: usize = size.trailing_zeros().try_into().unwrap(); 54 | 55 | if order >= ORDER { 56 | // size of the memory allocation is too large 57 | return Err(AllocatorError::TooBig); 58 | } 59 | 60 | for i in order..self.free_list.len() { 61 | // Find the first non-empty list, which handles a block of 62 | // memory with a equal or a larger size as the requested size 63 | if !self.free_list[i].is_empty() { 64 | // Split larger blocks in two buddies 65 | for j in (order + 1..i + 1).rev() { 66 | if let Some(block) = self.free_list[j].pop() { 67 | unsafe { 68 | self.free_list[j - 1] 69 | .push((block as usize + (1 << (j - 1))) as *mut usize); 70 | self.free_list[j - 1].push(block); 71 | } 72 | } else { 73 | return Err(AllocatorError::OutOfMemory); 74 | } 75 | } 76 | 77 | if let Some(addr) = self.free_list[order].pop() { 78 | return Ok(NonNull::new(addr as *mut u8).unwrap()); 79 | } else { 80 | return Err(AllocatorError::OutOfMemory); 81 | } 82 | } 83 | } 84 | 85 | Err(AllocatorError::OutOfMemory) 86 | } 87 | 88 | /// Deallocates the block of memory at the given `ptr` pointer with the given layout. 89 | pub fn dealloc(&mut self, ptr: NonNull, layout: Layout) { 90 | let size = max( 91 | layout.size().next_power_of_two(), 92 | max(layout.align(), MIN_ALLOC_SIZE), 93 | ); 94 | let order: usize = size.trailing_zeros().try_into().unwrap(); 95 | 96 | unsafe { 97 | // add block to free list 98 | self.free_list[order].push(ptr.as_ptr() as *mut usize); 99 | 100 | // Try to merge two buddies to one buddy 101 | let mut current_ptr = ptr.as_ptr() as usize; 102 | let mut current_order = order; 103 | 104 | 'outer: while current_order < self.free_list.len() - 1 { 105 | let block_size = 1 << current_order; 106 | 107 | // the list is unordered => check all nodes to find a buddy 108 | for block in self.free_list[current_order].iter_mut() { 109 | let buddy = block.value() as usize; 110 | if buddy == current_ptr + block_size || buddy == current_ptr - block_size { 111 | // remove current block from the list 112 | block.remove(); 113 | // the first node of the list includes `ptr` 114 | self.free_list[current_order].pop().unwrap(); 115 | // merge buddies 116 | current_ptr = min(current_ptr, buddy); 117 | current_order += 1; 118 | self.free_list[current_order].push(current_ptr as *mut usize); 119 | continue 'outer; 120 | } 121 | } 122 | 123 | // no buddy merged => leave while loop 124 | break; 125 | } 126 | } 127 | } 128 | } 129 | 130 | impl fmt::Debug for BuddySystem { 131 | /// Print all elements of the lists 132 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 133 | for order in (0..self.free_list.len()).rev() { 134 | if !self.free_list[order].is_empty() { 135 | write!(f, "Block size {:>8}: ", 1 << order)?; 136 | 137 | for block in self.free_list[order].iter() { 138 | write!(f, "0x{:x} ", block as usize)?; 139 | } 140 | 141 | writeln!(f)?; 142 | } 143 | } 144 | 145 | Ok(()) 146 | } 147 | } 148 | 149 | /// A memory allocator that can be registered as default allocator through 150 | /// the #[global_allocator] attribute. 151 | pub(crate) struct LockedHeap(Spinlock>); 152 | 153 | impl LockedHeap { 154 | /// Constructs an empty buddy system 155 | pub const fn new() -> Self { 156 | LockedHeap(Spinlock::new(BuddySystem::::new())) 157 | } 158 | 159 | pub unsafe fn init(&self, start: *mut u8, len: usize) { 160 | unsafe { 161 | self.0.lock().init(start, len); 162 | } 163 | } 164 | } 165 | 166 | impl fmt::Debug for LockedHeap { 167 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 168 | self.0.lock().fmt(f) 169 | } 170 | } 171 | 172 | unsafe impl GlobalAlloc for LockedHeap { 173 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 174 | self.0 175 | .lock() 176 | .alloc(layout) 177 | .ok() 178 | .map_or(core::ptr::null_mut(), |allocation| allocation.as_ptr()) 179 | } 180 | 181 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 182 | self.0.lock().dealloc(NonNull::new_unchecked(ptr), layout) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Actions Status](https://github.com/RWTH-OS/eduOS-rs/workflows/Build/badge.svg) 2 | 3 | # eduOS-rs - A teaching operating system written in Rust 4 | 5 | ## Introduction 6 | 7 | eduOS-rs is a Unix-like operating system based on a monolithic architecture for educational purposes. 8 | It is developed for the course [Operating Systems][acsos] at RWTH Aachen University. 9 | eduOS-rs is derived from following tutorials and software distributions: 10 | 11 | 1. Philipp Oppermann's [excellent series of blog posts][opp]. 12 | 2. Erik Kidd's [toyos-rs][kidd], which is an extension of Philipp Opermann's kernel. 13 | 3. The original version of [eduOS][stlankes], which was the old teaching kernel written in C. 14 | 4. eduOS-rs' uses a memory allocator, which is derived from the [buddy_system_allocator][buddy]. 15 | 5. The first version of paging is derived from a version, which was developed by [Colin Finck][colin]. 16 | 17 | [opp]: http://blog.phil-opp.com/ 18 | [kidd]: http://www.randomhacks.net/bare-metal-rust/ 19 | [stlankes]: http://rwth-os.github.io/eduOS/ 20 | [rust-barebones-kernel]: https://github.com/thepowersgang/rust-barebones-kernel 21 | [acsos]: https://www.acs.eonerc.rwth-aachen.de/cms/e-on-erc-acs/studium/lehrveranstaltungen/~xwyom/grundgebiete-der-informatik-4-betriebs/?lidx=1 22 | [colin]: https://github.com/ColinFinck 23 | [buddy]: https://github.com/rcore-os/buddy_system_allocator 24 | 25 | ## Requirements to build eduOS-rs 26 | eduOS-rs is tested under Linux, macOS, and Windows. 27 | 28 | ### macOS 29 | Apple's *Command Line Tools* must be installed. 30 | The Command Line Tool package gives macOS terminal users many commonly used tools and compilers, that are usually found in default Linux installations. 31 | Following terminal command installs these tools without Apple's IDE Xcode: 32 | 33 | ```sh 34 | $ xcode-select --install 35 | ``` 36 | 37 | In addition, *Qemu* must be installed. 38 | Please use [Homebrew](https://brew.sh) as package manager to install Qemu. 39 | 40 | ```sh 41 | $ brew install qemu 42 | ``` 43 | 44 | ### Windows 45 | To build eduOS-rs you have to install _Qemu_ and a git client. 46 | Please use [Chocolatey](https://chocolatey.org) as package manager to install Qemu and git. 47 | 48 | ```sh 49 | $ choco install qemu git 50 | ``` 51 | 52 | ### Linux 53 | Linux users should install common developer tools. 54 | For instance, on Ubuntu 22.04 the following command installs the required tools: 55 | 56 | ```sh 57 | $ apt-get install -y git qemu-system-x86 build-essential 58 | ``` 59 | 60 | ### Common for macOS, Windows and Linux 61 | This project uses Rustup to set its Rust toolchain. 62 | Follow the instructions to [install Rust using Rustup](https://www.rust-lang.org/tools/install). 63 | 64 | In addition, the tool [bootimage](https://github.com/rust-osdev/bootimage) is required, which creates a bootable diskimage. 65 | Please install the tool with following command. 66 | 67 | ```sh 68 | $ cargo install bootimage 69 | ``` 70 | 71 | ## Building 72 | 73 | eduOS-rs is able to run within [Qemu](https://www.qemu.org), which is a generic and open source machine emulator and virtualizer. 74 | 75 | After cloning the repository, you can run the kernel with following command: 76 | 77 | ```sh 78 | $ cargo run 79 | ``` 80 | 81 | ## Overview of all branches 82 | 83 | Step by step (here branch by branch) the operating system design will be introduced. 84 | This tutorial shows the steps to develop from a minimal kernel to a Unix-like computer operating system. 85 | Currently, following stages of development are available: 86 | 87 | 0. stage0 - Smallest HelloWorld of the World 88 | 89 | Description of loading a minimal 64bit kernel 90 | 91 | 1. stage1 - Cooperative/non-preemptive multitasking 92 | 93 | Introduction into a simple form of multitasking, where no interrupts are required. 94 | 95 | 2. stage2 - Priority-based cooperative/non-preemptive multitasking 96 | 97 | Introduction into a simple form of priority-based multitasking, where no interrupts are required. 98 | 99 | 3. stage3 - Synchronization primitives 100 | 101 | Introduce basic synchronization primitives 102 | 103 | 4. stage 4 - Preemptive multitasking 104 | 105 | Introduction into preemptive multitasking and interrupt handling 106 | 107 | 5. stage 5 - Support of user-level tasks 108 | 109 | Add support of user-level tasks with an small interface for basic system calls 110 | 111 | 6. stage 6 - Support of paging 112 | 113 | Add support of paging and a simple demo for process creation 114 | 115 | 7. stage 7 - Basic IO interface 116 | 117 | Add basic support of file descriptors 118 | 119 | 8. stage 8 - Integration of an in-memory file system 120 | 121 | Introduce a virtual file system with an in-memory file system as example file system. 122 | 123 | 9. stage9 - Run Linux application as common process 124 | 125 | Start a simple Linux application (_HelloWorld_) on top of eduOS-rs. The application is a _position-independent executable_ (PIE) and use [musl-libc](http://www.musl-libc.org) as standard C library. The FPU support is disabled. 126 | 127 | ## Useful Links 128 | 129 | 1. [http://www.gnu.org/software/grub/manual/multiboot/](http://www.gnu.org/software/grub/manual/multiboot/) 130 | 2. [http://www.osdever.net/tutorials/view/brans-kernel-development-tutorial](http://www.osdever.net/tutorials/view/brans-kernel-development-tutorial) 131 | 3. [https://www.acs.eonerc.rwth-aachen.de/cms/e-on-erc-acs/studium/lehrveranstaltungen/~xwyom/grundgebiete-der-informatik-4-betriebs/?lidx=1](https://www.acs.eonerc.rwth-aachen.de/cms/e-on-erc-acs/studium/lehrveranstaltungen/~xwyom/grundgebiete-der-informatik-4-betriebs/?lidx=1) 132 | 4. [http://rwth-os.github.io/eduOS/](http://rwth-os.github.io/eduOS/) 133 | 5. [https://intermezzos.github.io](https://intermezzos.github.io) 134 | 135 | ## License 136 | 137 | Licensed under either of 138 | 139 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 140 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 141 | 142 | at your option. 143 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/gdt.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::mm::get_boot_stack; 2 | use crate::arch::mm::VirtAddr; 3 | use crate::scheduler; 4 | use crate::scheduler::task::Stack; 5 | use core::mem; 6 | use x86::bits64::segmentation::*; 7 | use x86::bits64::task::*; 8 | use x86::controlregs::cr3_write; 9 | use x86::dtables::{self, DescriptorTablePointer}; 10 | use x86::segmentation::*; 11 | use x86::Ring; 12 | 13 | const GDT_NULL: usize = 0; 14 | const GDT_KERNEL_CODE: usize = 1; 15 | const GDT_KERNEL_DATA: usize = 2; 16 | const GDT_USER32_CODE: usize = 3; 17 | const GDT_USER32_DATA: usize = 4; 18 | #[cfg(target_arch = "x86_64")] 19 | const GDT_USER64_CODE: usize = 5; 20 | #[cfg(target_arch = "x86_64")] 21 | const GDT_FIRST_TSS: usize = 6; 22 | #[cfg(target_arch = "x86")] 23 | const GDT_FIRST_TSS: usize = 5; 24 | 25 | // fox x86_64 is a TSS descriptor twice larger than a code/data descriptor 26 | #[cfg(target_arch = "x86_64")] 27 | const TSS_ENTRIES: usize = 2; 28 | #[cfg(target_arch = "x86")] 29 | const TSS_ENTRIES: usize = 1; 30 | const GDT_ENTRIES: usize = GDT_FIRST_TSS + TSS_ENTRIES; 31 | 32 | // thread_local on a static mut, signals that the value of this static may 33 | // change depending on the current thread. 34 | static mut GDT: [Descriptor; GDT_ENTRIES] = [Descriptor::NULL; GDT_ENTRIES]; 35 | static mut TSS: Tss = Tss::from(TaskStateSegment::new()); 36 | 37 | // workaround to use the new repr(align) feature 38 | // currently, it is only supported by structs 39 | // => map all task state segments in a struct 40 | #[repr(align(128))] 41 | pub(crate) struct Tss(TaskStateSegment); 42 | 43 | impl Tss { 44 | #[allow(dead_code)] 45 | pub const fn into(self) -> TaskStateSegment { 46 | self.0 47 | } 48 | 49 | pub const fn from(x: TaskStateSegment) -> Self { 50 | Tss(x) 51 | } 52 | } 53 | 54 | /// This will setup the special GDT 55 | /// pointer, set up the entries in our GDT, and then 56 | /// finally to load the new GDT and to update the 57 | /// new segment registers 58 | pub(crate) fn init() { 59 | #[cfg(target_arch = "x86_64")] 60 | let limit = 0; 61 | #[cfg(target_arch = "x86")] 62 | let limit = 0xFFFF_FFFF; 63 | 64 | unsafe { 65 | // The NULL descriptor is always the first entry. 66 | GDT[GDT_NULL] = Descriptor::NULL; 67 | 68 | #[cfg(target_arch = "x86_64")] 69 | { 70 | // The second entry is a 64bit Code Segment in kernel-space (Ring 0). 71 | // All other parameters are ignored. 72 | GDT[GDT_KERNEL_CODE] = 73 | DescriptorBuilder::code_descriptor(0, limit, CodeSegmentType::ExecuteRead) 74 | .present() 75 | .dpl(Ring::Ring0) 76 | .l() 77 | .finish(); 78 | } 79 | #[cfg(target_arch = "x86")] 80 | { 81 | // The second entry is a 32bit Code Segment in kernel-space (Ring 0). 82 | // All other parameters are ignored. 83 | GDT[GDT_KERNEL_CODE] = 84 | DescriptorBuilder::code_descriptor(0, limit, CodeSegmentType::ExecuteRead) 85 | .present() 86 | .dpl(Ring::Ring0) 87 | .db() 88 | .limit_granularity_4kb() 89 | .finish(); 90 | } 91 | 92 | // The third entry is a Data Segment in kernel-space (Ring 0). 93 | // All other parameters are ignored. 94 | GDT[GDT_KERNEL_DATA] = DescriptorBuilder::data_descriptor(0, 0, DataSegmentType::ReadWrite) 95 | .present() 96 | .dpl(Ring::Ring0) 97 | .finish(); 98 | 99 | /* 100 | * Create code segment for 32bit user-space applications (ring 3) 101 | */ 102 | GDT[GDT_USER32_CODE] = 103 | DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead) 104 | .present() 105 | .dpl(Ring::Ring3) 106 | .finish(); 107 | 108 | /* 109 | * Create data segment for 32bit user-space applications (ring 3) 110 | */ 111 | GDT[GDT_USER32_DATA] = DescriptorBuilder::data_descriptor(0, 0, DataSegmentType::ReadWrite) 112 | .present() 113 | .dpl(Ring::Ring3) 114 | .finish(); 115 | 116 | /* 117 | * Create code segment for 64bit user-space applications (ring 3) 118 | */ 119 | #[cfg(target_arch = "x86_64")] 120 | { 121 | GDT[GDT_USER64_CODE] = 122 | DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead) 123 | .present() 124 | .dpl(Ring::Ring3) 125 | .l() 126 | .finish(); 127 | } 128 | 129 | /* 130 | * Create TSS for each core (we use these segments for task switching) 131 | */ 132 | #[cfg(target_arch = "x86_64")] 133 | { 134 | let base = &TSS.0 as *const _ as u64; 135 | let tss_descriptor: Descriptor64 = 136 | >::tss_descriptor( 137 | base, 138 | base + mem::size_of::() as u64 - 1, 139 | true, 140 | ) 141 | .present() 142 | .dpl(Ring::Ring0) 143 | .finish(); 144 | 145 | GDT[GDT_FIRST_TSS..GDT_FIRST_TSS + TSS_ENTRIES] 146 | .copy_from_slice(&mem::transmute::( 147 | tss_descriptor, 148 | )); 149 | 150 | TSS.0.rsp[0] = get_boot_stack().interrupt_top().into(); 151 | } 152 | #[cfg(target_arch = "x86")] 153 | { 154 | let base = &TSS.0 as *const _ as u64; 155 | let tss_descriptor: Descriptor = 156 | >::tss_descriptor( 157 | base, 158 | base + mem::size_of::() as u64 - 1, 159 | true, 160 | ) 161 | .present() 162 | .dpl(Ring::Ring0) 163 | .finish(); 164 | 165 | /* set default values */ 166 | TSS.0.eflags = 0x1202; 167 | TSS.0.ss0 = 0x10; // data segment 168 | TSS.0.esp0 = get_boot_stack().interrupt_top().into(); 169 | TSS.0.cs = 0x0b; 170 | 171 | GDT[GDT_FIRST_TSS] = tss_descriptor; 172 | } 173 | 174 | // load GDT 175 | let gdtr = DescriptorTablePointer::new(&GDT); 176 | dtables::lgdt(&gdtr); 177 | 178 | // Reload the segment descriptors 179 | load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16, Ring::Ring0)); 180 | load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0)); 181 | } 182 | } 183 | 184 | #[cfg(target_arch = "x86_64")] 185 | #[inline(always)] 186 | unsafe fn set_kernel_stack(stack: VirtAddr) { 187 | TSS.0.rsp[0] = stack.as_u64(); 188 | } 189 | 190 | #[cfg(target_arch = "x86")] 191 | #[inline(always)] 192 | unsafe fn set_kernel_stack(stack: VirtAddr) { 193 | TSS.0.esp = stack.as_u32(); 194 | } 195 | 196 | pub(crate) unsafe extern "C" fn set_current_kernel_stack() { 197 | cr3_write(scheduler::get_root_page_table().as_u64()); 198 | set_kernel_stack(scheduler::get_current_interrupt_stack()); 199 | } 200 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/mod.rs: -------------------------------------------------------------------------------- 1 | mod gdt; 2 | pub mod irq; 3 | mod pit; 4 | pub(crate) mod processor; 5 | #[cfg(not(feature = "vga"))] 6 | pub(crate) mod serial; 7 | #[cfg(target_arch = "x86_64")] 8 | mod start; 9 | pub(crate) mod switch; 10 | mod syscall; 11 | pub(crate) mod task; 12 | #[cfg(feature = "vga")] 13 | pub(crate) mod vga; 14 | 15 | use crate::arch::x86::kernel::syscall::syscall_handler; 16 | use crate::consts::USER_ENTRY; 17 | use bootloader::BootInfo; 18 | use core::arch::{asm, naked_asm}; 19 | 20 | #[cfg(target_arch = "x86_64")] 21 | pub(crate) static mut BOOT_INFO: Option<&'static BootInfo> = None; 22 | 23 | #[cfg(target_arch = "x86")] 24 | core::arch::global_asm!(include_str!("entry32.s")); 25 | 26 | #[unsafe(naked)] 27 | unsafe extern "C" fn __jump_to_user_land(ds: usize, stack: usize, cs: usize, entry: usize) -> ! { 28 | naked_asm!("swapgs", "push rdi", "push rsi", "pushf", "push rdx", "push rcx", "iretq",) 29 | } 30 | 31 | /// Helper function to jump into the user space 32 | /// 33 | /// # Safety 34 | /// 35 | /// Be sure the the user-level function mapped into the user space. 36 | pub(crate) unsafe fn jump_to_user_land(func: usize) -> ! { 37 | __jump_to_user_land( 38 | 0x23, 39 | USER_ENTRY.as_usize() + 0x400000usize, 40 | 0x2b, 41 | USER_ENTRY.as_usize() | func, 42 | ) 43 | } 44 | 45 | pub fn register_task() { 46 | let sel: u16 = 6u16 << 3; 47 | 48 | unsafe { 49 | asm!("ltr ax", in("ax") sel, options(nostack, nomem)); 50 | } 51 | } 52 | 53 | /// This macro can be used to call system functions from user-space 54 | #[macro_export] 55 | macro_rules! syscall { 56 | ($arg0:expr) => { 57 | arch::x86::kernel::syscall0($arg0 as u64) 58 | }; 59 | 60 | ($arg0:expr, $arg1:expr) => { 61 | arch::x86::kernel::syscall1($arg0 as u64, $arg1 as u64) 62 | }; 63 | 64 | ($arg0:expr, $arg1:expr, $arg2:expr) => { 65 | arch::x86::kernel::syscall2($arg0 as u64, $arg1 as u64, $arg2 as u64) 66 | }; 67 | 68 | ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => { 69 | arch::x86::kernel::syscall3($arg0 as u64, $arg1 as u64, $arg2 as u64, $arg3 as u64) 70 | }; 71 | 72 | ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr) => { 73 | arch::x86::kernel::syscall4( 74 | $arg0 as u64, 75 | $arg1 as u64, 76 | $arg2 as u64, 77 | $arg3 as u64, 78 | $arg4 as u64, 79 | ) 80 | }; 81 | 82 | ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => { 83 | arch::x86::kernel::syscall5( 84 | $arg0 as u64, 85 | $arg1 as u64, 86 | $arg2 as u64, 87 | $arg3 as u64, 88 | $arg4 as u64, 89 | $arg5 as u64, 90 | ) 91 | }; 92 | 93 | ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr) => { 94 | arch::x86::kernel::syscall6( 95 | $arg0 as u64, 96 | $arg1 as u64, 97 | $arg2 as u64, 98 | $arg3 as u64, 99 | $arg4 as u64, 100 | $arg5 as u64, 101 | $arg6 as u64, 102 | ) 103 | }; 104 | 105 | ($arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr, $arg7:expr) => { 106 | arch::x86::kernel::syscall7( 107 | $arg0 as u64, 108 | $arg1 as u64, 109 | $arg2 as u64, 110 | $arg3 as u64, 111 | $arg4 as u64, 112 | $arg5 as u64, 113 | $arg6 as u64, 114 | $arg7 as u64, 115 | ) 116 | }; 117 | } 118 | 119 | #[inline(always)] 120 | #[allow(unused_mut)] 121 | pub fn syscall0(arg0: u64) -> u64 { 122 | let mut ret: u64; 123 | unsafe { 124 | asm!("syscall", 125 | inlateout("rax") arg0 => ret, 126 | lateout("rcx") _, 127 | lateout("r11") _, 128 | options(preserves_flags, nostack) 129 | ); 130 | } 131 | ret 132 | } 133 | 134 | #[inline(always)] 135 | #[allow(unused_mut)] 136 | pub fn syscall1(arg0: u64, arg1: u64) -> u64 { 137 | let mut ret: u64; 138 | unsafe { 139 | asm!("syscall", 140 | inlateout("rax") arg0 => ret, 141 | in("rdi") arg1, 142 | lateout("rcx") _, 143 | lateout("r11") _, 144 | options(preserves_flags, nostack) 145 | ); 146 | } 147 | ret 148 | } 149 | 150 | #[inline(always)] 151 | #[allow(unused_mut)] 152 | pub fn syscall2(arg0: u64, arg1: u64, arg2: u64) -> u64 { 153 | let mut ret: u64; 154 | unsafe { 155 | asm!("syscall", 156 | inlateout("rax") arg0 => ret, 157 | in("rdi") arg1, 158 | in("rsi") arg2, 159 | lateout("rcx") _, 160 | lateout("r11") _, 161 | options(preserves_flags, nostack) 162 | ); 163 | } 164 | ret 165 | } 166 | 167 | #[inline(always)] 168 | #[allow(unused_mut)] 169 | pub fn syscall3(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> u64 { 170 | let mut ret: u64; 171 | unsafe { 172 | asm!("syscall", 173 | inlateout("rax") arg0 => ret, 174 | in("rdi") arg1, 175 | in("rsi") arg2, 176 | in("rdx") arg3, 177 | lateout("rcx") _, 178 | lateout("r11") _, 179 | options(preserves_flags, nostack) 180 | ); 181 | } 182 | ret 183 | } 184 | 185 | #[inline(always)] 186 | #[allow(unused_mut)] 187 | pub fn syscall4(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> u64 { 188 | let mut ret: u64; 189 | unsafe { 190 | asm!("syscall", 191 | inlateout("rax") arg0 => ret, 192 | in("rdi") arg1, 193 | in("rsi") arg2, 194 | in("rdx") arg3, 195 | in("r10") arg4, 196 | lateout("rcx") _, 197 | lateout("r11") _, 198 | options(preserves_flags, nostack) 199 | ); 200 | } 201 | ret 202 | } 203 | 204 | #[inline(always)] 205 | #[allow(unused_mut)] 206 | pub fn syscall5(arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { 207 | let mut ret: u64; 208 | unsafe { 209 | asm!("syscall", 210 | inlateout("rax") arg0 => ret, 211 | in("rdi") arg1, 212 | in("rsi") arg2, 213 | in("rdx") arg3, 214 | in("r10") arg4, 215 | in("r8") arg5, 216 | lateout("rcx") _, 217 | lateout("r11") _, 218 | options(preserves_flags, nostack) 219 | ); 220 | } 221 | ret 222 | } 223 | 224 | #[inline(always)] 225 | #[allow(unused_mut)] 226 | pub fn syscall6( 227 | arg0: u64, 228 | arg1: u64, 229 | arg2: u64, 230 | arg3: u64, 231 | arg4: u64, 232 | arg5: u64, 233 | arg6: u64, 234 | ) -> u64 { 235 | let mut ret: u64; 236 | unsafe { 237 | asm!("syscall", 238 | inlateout("rax") arg0 => ret, 239 | in("rdi") arg1, 240 | in("rsi") arg2, 241 | in("rdx") arg3, 242 | in("r10") arg4, 243 | in("r8") arg5, 244 | in("r9") arg6, 245 | lateout("rcx") _, 246 | lateout("r11") _, 247 | options(preserves_flags, nostack) 248 | ); 249 | } 250 | ret 251 | } 252 | 253 | /// Initialize module, must be called once, and only once 254 | pub(crate) fn init() { 255 | processor::init(); 256 | gdt::init(); 257 | irq::init(); 258 | pit::init(); 259 | 260 | #[cfg(feature = "vga")] 261 | vga::init(); 262 | } 263 | -------------------------------------------------------------------------------- /src/scheduler/task.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::arch; 4 | use crate::arch::mm::PhysAddr; 5 | use crate::arch::mm::VirtAddr; 6 | use crate::arch::{BasePageSize, PageSize}; 7 | use crate::consts::*; 8 | use crate::fd::stdio::{GenericStderr, GenericStdin, GenericStdout}; 9 | use crate::fd::{FileDescriptor, IoInterface, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; 10 | use crate::logging::*; 11 | use alloc::boxed::Box; 12 | use alloc::collections::{BTreeMap, VecDeque}; 13 | use alloc::rc::Rc; 14 | use alloc::sync::Arc; 15 | use core::cell::RefCell; 16 | use core::fmt; 17 | 18 | /// The status of the task - used for scheduling 19 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 20 | pub(crate) enum TaskStatus { 21 | Invalid, 22 | Ready, 23 | Running, 24 | Blocked, 25 | Finished, 26 | Idle, 27 | } 28 | 29 | /// Unique identifier for a task (i.e. `pid`). 30 | #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] 31 | pub struct TaskId(u32); 32 | 33 | impl TaskId { 34 | pub const fn into(self) -> u32 { 35 | self.0 36 | } 37 | 38 | pub const fn from(x: u32) -> Self { 39 | TaskId(x) 40 | } 41 | } 42 | 43 | impl alloc::fmt::Display for TaskId { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> alloc::fmt::Result { 45 | write!(f, "{}", self.0) 46 | } 47 | } 48 | 49 | /// Priority of a task 50 | #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] 51 | pub struct TaskPriority(u8); 52 | 53 | impl TaskPriority { 54 | pub const fn into(self) -> u8 { 55 | self.0 56 | } 57 | 58 | pub const fn from(x: u8) -> Self { 59 | TaskPriority(x) 60 | } 61 | } 62 | 63 | impl alloc::fmt::Display for TaskPriority { 64 | fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { 65 | write!(f, "{}", self.0) 66 | } 67 | } 68 | 69 | pub const REALTIME_PRIORITY: TaskPriority = TaskPriority::from(NO_PRIORITIES as u8 - 1); 70 | pub const HIGH_PRIORITY: TaskPriority = TaskPriority::from(24); 71 | pub const NORMAL_PRIORITY: TaskPriority = TaskPriority::from(16); 72 | pub const LOW_PRIORITY: TaskPriority = TaskPriority::from(0); 73 | 74 | /// Realize a priority queue for tasks 75 | pub(crate) struct PriorityTaskQueue { 76 | queues: [VecDeque>>; NO_PRIORITIES], 77 | prio_bitmap: usize, 78 | } 79 | 80 | impl PriorityTaskQueue { 81 | /// Creates an empty priority queue for tasks 82 | pub const fn new() -> PriorityTaskQueue { 83 | const VALUE: VecDeque>> = VecDeque::new(); 84 | 85 | PriorityTaskQueue { 86 | queues: [VALUE; NO_PRIORITIES], 87 | prio_bitmap: 0, 88 | } 89 | } 90 | 91 | /// Add a task by its priority to the queue 92 | pub fn push(&mut self, task: Rc>) { 93 | let i: usize = task.borrow().prio.into().into(); 94 | //assert!(i < NO_PRIORITIES, "Priority {} is too high", i); 95 | 96 | self.prio_bitmap |= 1 << i; 97 | self.queues[i].push_back(task.clone()); 98 | } 99 | 100 | fn pop_from_queue(&mut self, queue_index: usize) -> Option>> { 101 | let task = self.queues[queue_index].pop_front(); 102 | if self.queues[queue_index].is_empty() { 103 | self.prio_bitmap &= !(1 << queue_index); 104 | } 105 | 106 | task 107 | } 108 | 109 | /// Pop the task with the highest priority from the queue 110 | pub fn pop(&mut self) -> Option>> { 111 | if let Some(i) = self.prio_bitmap.highest_one() { 112 | return self.pop_from_queue(i.try_into().unwrap()); 113 | } 114 | 115 | None 116 | } 117 | 118 | /// Pop the next task, which has a higher or the same priority as `prio` 119 | pub fn pop_with_prio(&mut self, prio: TaskPriority) -> Option>> { 120 | if let Some(i) = self.prio_bitmap.highest_one() { 121 | let i: usize = i.try_into().unwrap(); 122 | if i >= prio.into().into() { 123 | return self.pop_from_queue(i); 124 | } 125 | } 126 | 127 | None 128 | } 129 | } 130 | 131 | #[allow(dead_code)] 132 | pub(crate) trait Stack { 133 | fn top(&self) -> VirtAddr; 134 | fn bottom(&self) -> VirtAddr; 135 | fn interrupt_top(&self) -> VirtAddr; 136 | fn interrupt_bottom(&self) -> VirtAddr; 137 | } 138 | 139 | #[derive(Copy, Clone)] 140 | #[repr(C, align(64))] 141 | pub(crate) struct TaskStack { 142 | buffer: [u8; STACK_SIZE], 143 | ist_buffer: [u8; INTERRUPT_STACK_SIZE], 144 | } 145 | 146 | impl Default for TaskStack { 147 | fn default() -> Self { 148 | Self::new() 149 | } 150 | } 151 | 152 | impl TaskStack { 153 | pub const fn new() -> TaskStack { 154 | TaskStack { 155 | buffer: [0; STACK_SIZE], 156 | ist_buffer: [0; INTERRUPT_STACK_SIZE], 157 | } 158 | } 159 | } 160 | 161 | impl Stack for TaskStack { 162 | fn top(&self) -> VirtAddr { 163 | VirtAddr::from(self.buffer.as_ptr() as usize + STACK_SIZE - 16) 164 | } 165 | 166 | fn bottom(&self) -> VirtAddr { 167 | VirtAddr::from(self.buffer.as_ptr() as usize) 168 | } 169 | 170 | fn interrupt_top(&self) -> VirtAddr { 171 | VirtAddr::from(self.ist_buffer.as_ptr() as usize + INTERRUPT_STACK_SIZE - 16) 172 | } 173 | 174 | fn interrupt_bottom(&self) -> VirtAddr { 175 | VirtAddr::from(self.ist_buffer.as_ptr() as usize) 176 | } 177 | } 178 | 179 | /// A task control block, which identifies either a process or a thread 180 | #[repr(align(64))] 181 | pub(crate) struct Task { 182 | /// The ID of this context 183 | pub id: TaskId, 184 | /// Task Priority 185 | pub prio: TaskPriority, 186 | /// Status of a task, e.g. if the task is ready or blocked 187 | pub status: TaskStatus, 188 | /// Last stack pointer before a context switch to another task 189 | pub last_stack_pointer: VirtAddr, 190 | /// Stack of the task 191 | pub stack: Box, 192 | /// Physical address of the 1st level page table 193 | pub root_page_table: PhysAddr, 194 | /// Mapping between file descriptor and the referenced IO interface 195 | pub fd_map: BTreeMap>, 196 | } 197 | 198 | impl Task { 199 | pub fn new_idle(id: TaskId) -> Task { 200 | Task { 201 | id, 202 | prio: LOW_PRIORITY, 203 | status: TaskStatus::Idle, 204 | last_stack_pointer: VirtAddr::zero(), 205 | stack: Box::new(crate::arch::mm::get_boot_stack()), 206 | root_page_table: arch::get_kernel_root_page_table(), 207 | fd_map: BTreeMap::new(), 208 | } 209 | } 210 | 211 | pub fn new(id: TaskId, status: TaskStatus, prio: TaskPriority) -> Task { 212 | let mut fd_map: BTreeMap> = BTreeMap::new(); 213 | fd_map 214 | .try_insert(STDIN_FILENO, Arc::new(GenericStdin::new())) 215 | .unwrap(); 216 | fd_map 217 | .try_insert(STDOUT_FILENO, Arc::new(GenericStdout::new())) 218 | .unwrap(); 219 | fd_map 220 | .try_insert(STDERR_FILENO, Arc::new(GenericStderr::new())) 221 | .unwrap(); 222 | 223 | Task { 224 | id, 225 | prio, 226 | status, 227 | last_stack_pointer: VirtAddr::zero(), 228 | stack: Box::new(TaskStack::new()), 229 | root_page_table: arch::get_kernel_root_page_table(), 230 | fd_map, 231 | } 232 | } 233 | } 234 | 235 | pub(crate) trait TaskFrame { 236 | /// Create the initial stack frame for a new task 237 | fn create_stack_frame(&mut self, func: extern "C" fn()); 238 | } 239 | 240 | impl Drop for Task { 241 | fn drop(&mut self) { 242 | if self.root_page_table != arch::get_kernel_root_page_table() { 243 | debug!( 244 | "Deallocate page table 0x{:x} of task {}", 245 | self.root_page_table, self.id 246 | ); 247 | arch::mm::physicalmem::deallocate(self.root_page_table, BasePageSize::SIZE); 248 | } 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.5.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 10 | 11 | [[package]] 12 | name = "bit_field" 13 | version = "0.10.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "1.3.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 22 | 23 | [[package]] 24 | name = "bitflags" 25 | version = "2.10.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 28 | 29 | [[package]] 30 | name = "bootloader" 31 | version = "0.9.33" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "7bdfddac270bbdd45903296bc1caf29a7fdce6b326aaf0bbab7f04c5f98b7447" 34 | 35 | [[package]] 36 | name = "cfg-if" 37 | version = "1.0.4" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 40 | 41 | [[package]] 42 | name = "eduos-rs" 43 | version = "0.1.0" 44 | dependencies = [ 45 | "bitflags 2.10.0", 46 | "bootloader", 47 | "cfg-if", 48 | "goblin", 49 | "lock_api", 50 | "num", 51 | "num-derive", 52 | "num-traits", 53 | "qemu-exit", 54 | "spinning_top", 55 | "x86", 56 | ] 57 | 58 | [[package]] 59 | name = "goblin" 60 | version = "0.10.4" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "4db6758c546e6f81f265638c980e5e84dfbda80cfd8e89e02f83454c8e8124bd" 63 | dependencies = [ 64 | "log", 65 | "plain", 66 | "scroll", 67 | ] 68 | 69 | [[package]] 70 | name = "lock_api" 71 | version = "0.4.14" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 74 | dependencies = [ 75 | "scopeguard", 76 | ] 77 | 78 | [[package]] 79 | name = "log" 80 | version = "0.4.29" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 83 | 84 | [[package]] 85 | name = "num" 86 | version = "0.4.3" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" 89 | dependencies = [ 90 | "num-complex", 91 | "num-integer", 92 | "num-iter", 93 | "num-rational", 94 | "num-traits", 95 | ] 96 | 97 | [[package]] 98 | name = "num-complex" 99 | version = "0.4.6" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 102 | dependencies = [ 103 | "num-traits", 104 | ] 105 | 106 | [[package]] 107 | name = "num-derive" 108 | version = "0.4.2" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 111 | dependencies = [ 112 | "proc-macro2", 113 | "quote", 114 | "syn", 115 | ] 116 | 117 | [[package]] 118 | name = "num-integer" 119 | version = "0.1.46" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 122 | dependencies = [ 123 | "num-traits", 124 | ] 125 | 126 | [[package]] 127 | name = "num-iter" 128 | version = "0.1.45" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 131 | dependencies = [ 132 | "autocfg", 133 | "num-integer", 134 | "num-traits", 135 | ] 136 | 137 | [[package]] 138 | name = "num-rational" 139 | version = "0.4.2" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 142 | dependencies = [ 143 | "num-integer", 144 | "num-traits", 145 | ] 146 | 147 | [[package]] 148 | name = "num-traits" 149 | version = "0.2.19" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 152 | dependencies = [ 153 | "autocfg", 154 | ] 155 | 156 | [[package]] 157 | name = "plain" 158 | version = "0.2.3" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 161 | 162 | [[package]] 163 | name = "proc-macro2" 164 | version = "1.0.103" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 167 | dependencies = [ 168 | "unicode-ident", 169 | ] 170 | 171 | [[package]] 172 | name = "qemu-exit" 173 | version = "3.0.2" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "8bb0fd6580eeed0103c054e3fba2c2618ff476943762f28a645b63b8692b21c9" 176 | 177 | [[package]] 178 | name = "quote" 179 | version = "1.0.42" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 182 | dependencies = [ 183 | "proc-macro2", 184 | ] 185 | 186 | [[package]] 187 | name = "raw-cpuid" 188 | version = "10.7.0" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" 191 | dependencies = [ 192 | "bitflags 1.3.2", 193 | ] 194 | 195 | [[package]] 196 | name = "scopeguard" 197 | version = "1.2.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 200 | 201 | [[package]] 202 | name = "scroll" 203 | version = "0.13.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" 206 | dependencies = [ 207 | "scroll_derive", 208 | ] 209 | 210 | [[package]] 211 | name = "scroll_derive" 212 | version = "0.13.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "ed76efe62313ab6610570951494bdaa81568026e0318eaa55f167de70eeea67d" 215 | dependencies = [ 216 | "proc-macro2", 217 | "quote", 218 | "syn", 219 | ] 220 | 221 | [[package]] 222 | name = "spinning_top" 223 | version = "0.3.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" 226 | dependencies = [ 227 | "lock_api", 228 | ] 229 | 230 | [[package]] 231 | name = "syn" 232 | version = "2.0.111" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 235 | dependencies = [ 236 | "proc-macro2", 237 | "quote", 238 | "unicode-ident", 239 | ] 240 | 241 | [[package]] 242 | name = "unicode-ident" 243 | version = "1.0.22" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 246 | 247 | [[package]] 248 | name = "x86" 249 | version = "0.52.0" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" 252 | dependencies = [ 253 | "bit_field", 254 | "bitflags 1.3.2", 255 | "raw-cpuid", 256 | ] 257 | -------------------------------------------------------------------------------- /src/fs/vfs.rs: -------------------------------------------------------------------------------- 1 | //! Implements a simple virtual file system 2 | 3 | use crate::errno::*; 4 | use crate::fd::OpenOption; 5 | use crate::fd::{FileStatus, IoInterface}; 6 | use crate::fs::initrd::{RamHandle, RomHandle}; 7 | use crate::fs::{check_path, NodeKind, SeekFrom, Vfs, VfsNode, VfsNodeDirectory, VfsNodeFile}; 8 | use crate::io; 9 | use crate::logging::*; 10 | use crate::synch::spinlock::*; 11 | use alloc::boxed::Box; 12 | use alloc::collections::BTreeMap; 13 | use alloc::string::String; 14 | use alloc::sync::Arc; 15 | use alloc::vec::Vec; 16 | use core::any::Any; 17 | use core::fmt; 18 | 19 | #[derive(Debug)] 20 | struct VfsDirectory { 21 | /// in principle, a map with all entries of the current directory 22 | children: BTreeMap>, 23 | } 24 | 25 | impl VfsDirectory { 26 | pub fn new() -> Self { 27 | VfsDirectory { 28 | children: BTreeMap::new(), 29 | } 30 | } 31 | 32 | fn get_mut(&mut self, name: &String) -> Option<&mut T> { 33 | if let Some(b) = self.children.get_mut(name) { 34 | return b.downcast_mut::(); 35 | } 36 | None 37 | } 38 | 39 | fn get(&mut self, name: &String) -> Option<&T> { 40 | if let Some(b) = self.children.get_mut(name) { 41 | return b.downcast_ref::(); 42 | } 43 | None 44 | } 45 | } 46 | 47 | impl VfsNode for VfsDirectory { 48 | /// Returns the node type 49 | fn get_kind(&self) -> NodeKind { 50 | NodeKind::Directory 51 | } 52 | } 53 | 54 | impl VfsNodeDirectory for VfsDirectory { 55 | fn traverse_mkdir(&mut self, components: &mut Vec<&str>) -> Result<()> { 56 | if let Some(component) = components.pop() { 57 | let node_name = String::from(component); 58 | 59 | { 60 | if let Some(directory) = self.get_mut::(&node_name) { 61 | return directory.traverse_mkdir(components); 62 | } 63 | } 64 | 65 | let mut directory = Box::new(VfsDirectory::new()); 66 | let result = directory.traverse_mkdir(components); 67 | self.children.insert(node_name, directory); 68 | 69 | result 70 | } else { 71 | Ok(()) 72 | } 73 | } 74 | 75 | fn traverse_lsdir(&self, mut tabs: String) -> Result<()> { 76 | tabs.push_str(" "); 77 | for (name, node) in self.children.iter() { 78 | if let Some(directory) = node.downcast_ref::() { 79 | info!("{}{} ({:?})", tabs, name, self.get_kind()); 80 | directory.traverse_lsdir(tabs.clone())?; 81 | } else if let Some(file) = node.downcast_ref::() { 82 | info!("{}{} ({:?})", tabs, name, file.get_kind()); 83 | } else { 84 | info!("{}{} (Unknown))", tabs, name); 85 | } 86 | } 87 | 88 | Ok(()) 89 | } 90 | 91 | fn traverse_open( 92 | &mut self, 93 | components: &mut Vec<&str>, 94 | flags: OpenOption, 95 | ) -> Result> { 96 | if let Some(component) = components.pop() { 97 | let node_name = String::from(component); 98 | 99 | if components.is_empty() == true { 100 | // reach endpoint => reach file 101 | if let Some(file) = self.get_mut::(&node_name) { 102 | return file.get_handle(flags); 103 | } 104 | } 105 | 106 | if components.is_empty() == true { 107 | if flags.contains(OpenOption::O_CREAT) { 108 | // Create file on demand 109 | let file = Box::new(VfsFile::new()); 110 | let result = file.get_handle(flags); 111 | self.children.insert(node_name, file); 112 | 113 | result 114 | } else { 115 | Err(Error::InvalidArgument) 116 | } 117 | } else { 118 | // traverse to the directories to the endpoint 119 | if let Some(directory) = self.get_mut::(&node_name) { 120 | directory.traverse_open(components, flags) 121 | } else { 122 | Err(Error::InvalidArgument) 123 | } 124 | } 125 | } else { 126 | Err(Error::InvalidArgument) 127 | } 128 | } 129 | 130 | fn traverse_mount(&mut self, components: &mut Vec<&str>, slice: &'static [u8]) -> Result<()> { 131 | if let Some(component) = components.pop() { 132 | let node_name = String::from(component); 133 | 134 | if components.is_empty() == true { 135 | // Create file on demand 136 | let file = Box::new(VfsFile::new_from_rom(slice)); 137 | self.children.insert(node_name, file); 138 | 139 | Ok(()) 140 | } else { 141 | // traverse to the directories to the endpoint 142 | if let Some(directory) = self.get_mut::(&node_name) { 143 | directory.traverse_mount(components, slice) 144 | } else { 145 | Err(Error::InvalidArgument) 146 | } 147 | } 148 | } else { 149 | Err(Error::InvalidArgument) 150 | } 151 | } 152 | } 153 | 154 | /// Enumeration of possible methods to seek within an I/O object. 155 | #[derive(Debug, Clone)] 156 | enum DataHandle { 157 | RAM(RamHandle), 158 | ROM(RomHandle), 159 | } 160 | 161 | #[derive(Debug, Clone)] 162 | struct VfsFile { 163 | /// File content 164 | data: DataHandle, 165 | } 166 | 167 | impl VfsFile { 168 | pub fn new() -> Self { 169 | VfsFile { 170 | data: DataHandle::RAM(RamHandle::new(true)), 171 | } 172 | } 173 | 174 | pub fn new_from_rom(slice: &'static [u8]) -> Self { 175 | VfsFile { 176 | data: DataHandle::ROM(RomHandle::new(slice)), 177 | } 178 | } 179 | } 180 | 181 | impl VfsNode for VfsFile { 182 | fn get_kind(&self) -> NodeKind { 183 | NodeKind::File 184 | } 185 | } 186 | 187 | impl VfsNodeFile for VfsFile { 188 | fn get_handle(&self, opt: OpenOption) -> Result> { 189 | match self.data { 190 | DataHandle::RAM(ref data) => Ok(Arc::new(VfsFile { 191 | data: DataHandle::RAM(data.get_handle(opt)), 192 | })), 193 | DataHandle::ROM(ref data) => Ok(Arc::new(VfsFile { 194 | data: DataHandle::ROM(data.get_handle(opt)), 195 | })), 196 | } 197 | } 198 | } 199 | 200 | impl fmt::Write for VfsFile { 201 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 202 | match self.data { 203 | DataHandle::RAM(ref mut data) => data.write_str(s), 204 | _ => Err(core::fmt::Error), 205 | } 206 | } 207 | } 208 | 209 | impl IoInterface for VfsFile { 210 | fn read(&self, buf: &mut [u8]) -> io::Result { 211 | match self.data { 212 | DataHandle::RAM(ref data) => data.read(buf), 213 | DataHandle::ROM(ref data) => data.read(buf), 214 | } 215 | } 216 | 217 | fn write(&self, buf: &[u8]) -> io::Result { 218 | match self.data { 219 | DataHandle::RAM(ref data) => data.write(buf), 220 | _ => Err(io::Error::EBADF), 221 | } 222 | } 223 | 224 | fn seek(&self, style: SeekFrom) -> io::Result { 225 | match self.data { 226 | DataHandle::RAM(ref data) => data.seek(style), 227 | DataHandle::ROM(ref data) => data.seek(style), 228 | } 229 | } 230 | 231 | fn fstat(&self) -> io::Result { 232 | let file_size = match self.data { 233 | DataHandle::RAM(ref data) => data.len(), 234 | DataHandle::ROM(ref data) => data.len(), 235 | }; 236 | 237 | Ok(FileStatus { file_size }) 238 | } 239 | } 240 | 241 | /// Entrypoint of the in-memory file system 242 | #[derive(Debug)] 243 | pub(crate) struct Fs { 244 | handle: Spinlock, 245 | } 246 | 247 | impl Fs { 248 | pub fn new() -> Fs { 249 | Fs { 250 | handle: Spinlock::new(VfsDirectory::new()), 251 | } 252 | } 253 | } 254 | 255 | impl Vfs for Fs { 256 | fn mkdir(&mut self, path: &String) -> Result<()> { 257 | if check_path(path) { 258 | let mut components: Vec<&str> = path.split("/").collect(); 259 | 260 | components.reverse(); 261 | components.pop(); 262 | 263 | self.handle.lock().traverse_mkdir(&mut components) 264 | } else { 265 | Err(Error::InvalidFsPath) 266 | } 267 | } 268 | 269 | fn lsdir(&self) -> Result<()> { 270 | info!("/"); 271 | 272 | self.handle.lock().traverse_lsdir(String::from("")) 273 | } 274 | 275 | fn open(&mut self, path: &str, flags: OpenOption) -> Result> { 276 | if check_path(path) { 277 | let mut components: Vec<&str> = path.split("/").collect(); 278 | 279 | components.reverse(); 280 | components.pop(); 281 | 282 | self.handle.lock().traverse_open(&mut components, flags) 283 | } else { 284 | Err(Error::InvalidFsPath) 285 | } 286 | } 287 | 288 | /// Mound memory region as file 289 | fn mount(&mut self, path: &String, slice: &'static [u8]) -> Result<()> { 290 | if check_path(path) { 291 | let mut components: Vec<&str> = path.split("/").collect(); 292 | 293 | components.reverse(); 294 | components.pop(); 295 | 296 | self.handle.lock().traverse_mount(&mut components, slice) 297 | } else { 298 | Err(Error::InvalidFsPath) 299 | } 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/scheduler/scheduler.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::drop_user_space; 2 | use crate::arch::mm::{PhysAddr, VirtAddr}; 3 | use crate::arch::switch; 4 | use crate::collections::irqsave; 5 | use crate::consts::*; 6 | use crate::errno::*; 7 | use crate::fd::{FileDescriptor, IoInterface}; 8 | use crate::io; 9 | use crate::logging::*; 10 | use crate::scheduler::task::*; 11 | use alloc::collections::{BTreeMap, VecDeque}; 12 | use alloc::rc::Rc; 13 | use alloc::sync::Arc; 14 | use core::cell::RefCell; 15 | use core::sync::atomic::{AtomicU32, Ordering}; 16 | 17 | static TID_COUNTER: AtomicU32 = AtomicU32::new(0); 18 | 19 | pub(crate) struct Scheduler { 20 | /// task id which is currently running 21 | current_task: Rc>, 22 | /// task id of the idle task 23 | idle_task: Rc>, 24 | /// queue of tasks, which are ready 25 | ready_queue: PriorityTaskQueue, 26 | /// queue of tasks, which are finished and can be released 27 | finished_tasks: VecDeque, 28 | /// map between task id and task control block 29 | tasks: BTreeMap>>, 30 | } 31 | 32 | impl Scheduler { 33 | pub fn new() -> Scheduler { 34 | let tid = TaskId::from(TID_COUNTER.fetch_add(1, Ordering::SeqCst)); 35 | let idle_task = Rc::new(RefCell::new(Task::new_idle(tid))); 36 | let mut tasks = BTreeMap::new(); 37 | 38 | tasks.insert(tid, idle_task.clone()); 39 | 40 | Scheduler { 41 | current_task: idle_task.clone(), 42 | idle_task: idle_task.clone(), 43 | ready_queue: PriorityTaskQueue::new(), 44 | finished_tasks: VecDeque::::new(), 45 | tasks, 46 | } 47 | } 48 | 49 | fn get_tid(&self) -> TaskId { 50 | loop { 51 | let id = TaskId::from(TID_COUNTER.fetch_add(1, Ordering::SeqCst)); 52 | 53 | if !self.tasks.contains_key(&id) { 54 | return id; 55 | } 56 | } 57 | } 58 | 59 | pub fn spawn(&mut self, func: extern "C" fn(), prio: TaskPriority) -> Result { 60 | let closure = || { 61 | let prio_number: usize = prio.into().into(); 62 | 63 | if prio_number >= NO_PRIORITIES { 64 | return Err(Error::BadPriority); 65 | } 66 | 67 | // Create the new task. 68 | let tid = self.get_tid(); 69 | let task = Rc::new(RefCell::new(Task::new(tid, TaskStatus::Ready, prio))); 70 | 71 | task.borrow_mut().create_stack_frame(func); 72 | 73 | // Add it to the task lists. 74 | self.ready_queue.push(task.clone()); 75 | self.tasks.insert(tid, task); 76 | 77 | info!("Creating task {}", tid); 78 | 79 | Ok(tid) 80 | }; 81 | 82 | irqsave(closure) 83 | } 84 | 85 | fn cleanup(&mut self) { 86 | // destroy user space 87 | drop_user_space(); 88 | 89 | self.current_task.borrow_mut().status = TaskStatus::Finished; 90 | } 91 | 92 | pub fn exit(&mut self) -> ! { 93 | let closure = || { 94 | if self.current_task.borrow().status != TaskStatus::Idle { 95 | info!("finish task with id {}", self.current_task.borrow().id); 96 | self.cleanup(); 97 | } else { 98 | panic!("unable to terminate idle task"); 99 | } 100 | }; 101 | 102 | irqsave(closure); 103 | 104 | self.reschedule(); 105 | 106 | // we should never reach this point 107 | panic!("exit failed!"); 108 | } 109 | 110 | pub fn abort(&mut self) -> ! { 111 | let closure = || { 112 | if self.current_task.borrow().status != TaskStatus::Idle { 113 | info!("abort task with id {}", self.current_task.borrow().id); 114 | self.cleanup(); 115 | } else { 116 | panic!("unable to terminate idle task"); 117 | } 118 | }; 119 | 120 | irqsave(closure); 121 | 122 | self.reschedule(); 123 | 124 | // we should never reach this point 125 | panic!("abort failed!"); 126 | } 127 | 128 | pub fn block_current_task(&mut self) -> Rc> { 129 | let closure = || { 130 | if self.current_task.borrow().status == TaskStatus::Running { 131 | debug!("block task {}", self.current_task.borrow().id); 132 | 133 | self.current_task.borrow_mut().status = TaskStatus::Blocked; 134 | self.current_task.clone() 135 | } else { 136 | panic!("unable to block task {}", self.current_task.borrow().id); 137 | } 138 | }; 139 | 140 | irqsave(closure) 141 | } 142 | 143 | pub fn wakeup_task(&mut self, task: Rc>) { 144 | let closure = || { 145 | if task.borrow().status == TaskStatus::Blocked { 146 | debug!("wakeup task {}", task.borrow().id); 147 | 148 | task.borrow_mut().status = TaskStatus::Ready; 149 | self.ready_queue.push(task.clone()); 150 | } 151 | }; 152 | 153 | irqsave(closure); 154 | } 155 | 156 | pub(crate) fn insert_io_interface( 157 | &mut self, 158 | io_interface: Arc, 159 | ) -> io::Result { 160 | let new_fd = || -> io::Result { 161 | let mut fd: FileDescriptor = 0; 162 | loop { 163 | if !self.current_task.borrow().fd_map.contains_key(&fd) { 164 | break Ok(fd); 165 | } else if fd == FileDescriptor::MAX { 166 | break Err(io::Error::EOVERFLOW); 167 | } 168 | 169 | fd = fd.saturating_add(1); 170 | } 171 | }; 172 | 173 | let fd = new_fd()?; 174 | self.current_task 175 | .borrow_mut() 176 | .fd_map 177 | .insert(fd, io_interface.clone()); 178 | 179 | Ok(fd) 180 | } 181 | 182 | pub fn remove_io_interface(&self, fd: FileDescriptor) -> io::Result> { 183 | self.current_task 184 | .borrow_mut() 185 | .fd_map 186 | .remove(&fd) 187 | .ok_or(io::Error::EBADF) 188 | } 189 | 190 | pub(crate) fn get_io_interface( 191 | &self, 192 | fd: FileDescriptor, 193 | ) -> crate::io::Result> { 194 | let closure = || { 195 | if let Some(io_interface) = self.current_task.borrow().fd_map.get(&fd) { 196 | Ok(io_interface.clone()) 197 | } else { 198 | Err(crate::io::Error::ENOENT) 199 | } 200 | }; 201 | 202 | irqsave(closure) 203 | } 204 | 205 | pub fn get_current_taskid(&self) -> TaskId { 206 | irqsave(|| self.current_task.borrow().id) 207 | } 208 | 209 | /// Determines the start address of the stack 210 | #[no_mangle] 211 | pub fn get_current_interrupt_stack(&self) -> VirtAddr { 212 | irqsave(|| (*self.current_task.borrow().stack).interrupt_top()) 213 | } 214 | 215 | pub fn get_root_page_table(&self) -> PhysAddr { 216 | self.current_task.borrow().root_page_table 217 | } 218 | 219 | pub fn set_root_page_table(&self, addr: PhysAddr) { 220 | self.current_task.borrow_mut().root_page_table = addr; 221 | } 222 | 223 | pub fn schedule(&mut self) { 224 | // do we have finished tasks? => drop tasks => deallocate implicitly the stack 225 | if let Some(id) = self.finished_tasks.pop_front() { 226 | if self.tasks.remove(&id).is_none() { 227 | info!("Unable to drop task {}", id); 228 | } 229 | } 230 | 231 | // Get information about the current task. 232 | let (current_id, current_stack_pointer, current_prio, current_status) = { 233 | let mut borrowed = self.current_task.borrow_mut(); 234 | ( 235 | borrowed.id, 236 | &mut borrowed.last_stack_pointer as *mut VirtAddr, 237 | borrowed.prio, 238 | borrowed.status, 239 | ) 240 | }; 241 | 242 | // do we have a task, which is ready? 243 | let mut next_task; 244 | if current_status == TaskStatus::Running { 245 | next_task = self.ready_queue.pop_with_prio(current_prio); 246 | } else { 247 | next_task = self.ready_queue.pop(); 248 | } 249 | 250 | if next_task.is_none() 251 | && current_status != TaskStatus::Running 252 | && current_status != TaskStatus::Idle 253 | { 254 | debug!("Switch to idle task"); 255 | // current task isn't able to run and no other task available 256 | // => switch to the idle task 257 | next_task = Some(self.idle_task.clone()); 258 | } 259 | 260 | if let Some(new_task) = next_task { 261 | let (new_id, new_stack_pointer) = { 262 | let mut borrowed = new_task.borrow_mut(); 263 | borrowed.status = TaskStatus::Running; 264 | (borrowed.id, borrowed.last_stack_pointer) 265 | }; 266 | 267 | if current_status == TaskStatus::Running { 268 | debug!("Add task {} to ready queue", current_id); 269 | self.current_task.borrow_mut().status = TaskStatus::Ready; 270 | self.ready_queue.push(self.current_task.clone()); 271 | } else if current_status == TaskStatus::Finished { 272 | debug!("Task {} finished", current_id); 273 | self.current_task.borrow_mut().status = TaskStatus::Invalid; 274 | // release the task later, because the stack is required 275 | // to call the function "switch" 276 | // => push id to a queue and release the task later 277 | self.finished_tasks.push_back(current_id); 278 | } 279 | 280 | debug!( 281 | "Switching task from {} to {} (stack {:#X} => {:#X})", 282 | current_id, 283 | new_id, 284 | unsafe { *current_stack_pointer }, 285 | new_stack_pointer 286 | ); 287 | 288 | self.current_task = new_task; 289 | 290 | unsafe { 291 | switch(current_stack_pointer, new_stack_pointer); 292 | } 293 | } 294 | } 295 | 296 | pub fn reschedule(&mut self) { 297 | irqsave(|| self.schedule()); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /src/mm/freelist.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::mm::{PhysAddr, VirtAddr}; 2 | use crate::logging::*; 3 | use alloc::collections::linked_list::LinkedList; 4 | use core::cmp::Ordering; 5 | 6 | #[derive(Copy, Clone, Debug)] 7 | pub(crate) enum FreeListError { 8 | NoValidEntry, 9 | } 10 | 11 | pub(crate) struct FreeListEntry< 12 | T: core::marker::Copy 13 | + core::cmp::PartialEq 14 | + core::cmp::PartialOrd 15 | + core::fmt::UpperHex 16 | + core::ops::Sub 17 | + core::ops::BitAnd 18 | + core::ops::Add 19 | + core::ops::AddAssign 20 | + core::ops::Add, 21 | > { 22 | pub start: T, 23 | pub end: T, 24 | } 25 | 26 | impl< 27 | T: core::marker::Copy 28 | + core::cmp::PartialEq 29 | + core::cmp::PartialOrd 30 | + core::fmt::UpperHex 31 | + core::ops::Sub 32 | + core::ops::BitAnd 33 | + core::ops::Add 34 | + core::ops::AddAssign 35 | + core::ops::Add, 36 | > FreeListEntry 37 | { 38 | pub const fn new(start: T, end: T) -> Self { 39 | FreeListEntry { start, end } 40 | } 41 | } 42 | 43 | pub(crate) struct FreeList< 44 | T: core::marker::Copy 45 | + core::cmp::PartialEq 46 | + core::cmp::PartialOrd 47 | + core::fmt::UpperHex 48 | + core::ops::Sub 49 | + core::ops::BitAnd 50 | + core::ops::Add 51 | + core::ops::AddAssign 52 | + core::ops::Add, 53 | > { 54 | pub list: LinkedList>, 55 | } 56 | 57 | impl< 58 | T: core::marker::Copy 59 | + core::cmp::PartialEq 60 | + core::cmp::PartialOrd 61 | + core::fmt::UpperHex 62 | + core::ops::Sub 63 | + core::ops::BitAnd 64 | + core::ops::Add 65 | + core::ops::AddAssign 66 | + core::ops::Add, 67 | > FreeList 68 | { 69 | pub const fn new() -> Self { 70 | Self { 71 | list: LinkedList::new(), 72 | } 73 | } 74 | 75 | pub fn deallocate(&mut self, address: T, size: usize) { 76 | debug!( 77 | "Deallocating {} bytes at {:#X} from Free List {:#X}", 78 | size, address, self as *const Self as usize 79 | ); 80 | 81 | let end = address + size; 82 | let mut cursor = self.list.cursor_front_mut(); 83 | 84 | while let Some(node) = cursor.current() { 85 | let (region_start, region_end) = (node.start, node.end); 86 | 87 | if region_start == end { 88 | // The deallocated memory extends this free memory region to the left. 89 | node.start = address; 90 | 91 | // Check if it can even reunite with the previous region. 92 | if let Some(prev_node) = cursor.peek_prev() { 93 | let prev_region_end = prev_node.end; 94 | 95 | if prev_region_end == address { 96 | // It can reunite, so let the current region span over the reunited region and move the duplicate node 97 | // into the pool for deletion or reuse. 98 | prev_node.end = region_end; 99 | cursor.remove_current(); 100 | } 101 | } 102 | 103 | return; 104 | } else if region_end == address { 105 | node.end = end; 106 | 107 | // Check if it can even reunite with the next region. 108 | if let Some(next_node) = cursor.peek_next() { 109 | let next_region_start = next_node.start; 110 | 111 | if next_region_start == end { 112 | // It can reunite, so let the current region span over the reunited region and move the duplicate node 113 | // into the pool for deletion or reuse. 114 | next_node.start = region_start; 115 | cursor.remove_current(); 116 | } 117 | } 118 | 119 | return; 120 | } else if end < region_start { 121 | // The deallocated memory does not extend any memory region and needs an own entry in the Free List. 122 | // Get that entry from the node pool. 123 | // We search the list from low to high addresses and insert us before the first entry that has a 124 | // higher address than us. 125 | let new_entry = FreeListEntry::new(address, end); 126 | cursor.insert_before(new_entry); 127 | return; 128 | } 129 | 130 | cursor.move_next(); 131 | } 132 | 133 | // We could not find an entry with a higher address than us. 134 | // So we become the new last entry in the list. Get that entry from the node pool. 135 | let new_element = FreeListEntry::new(address, end); 136 | self.list.push_back(new_element); 137 | } 138 | } 139 | 140 | #[cfg(not(target_os = "none"))] 141 | #[test] 142 | fn add_element() { 143 | let mut freelist = FreeList::new(); 144 | let entry = FreeListEntry::new(0x10000, 0x100000); 145 | 146 | freelist.list.push_back(entry); 147 | 148 | let mut cursor = freelist.list.cursor_front_mut(); 149 | 150 | while let Some(node) = cursor.peek_next() { 151 | assert!(node.start != 0x1000); 152 | assert!(node.end != 0x10000); 153 | 154 | cursor.move_next(); 155 | } 156 | } 157 | 158 | #[cfg(not(target_os = "none"))] 159 | #[test] 160 | fn allocate() { 161 | let mut freelist = FreeList::new(); 162 | let entry = FreeListEntry::new(0x10000, 0x100000); 163 | 164 | freelist.list.push_back(entry); 165 | let addr = freelist.allocate(0x1000, None); 166 | 167 | assert_eq!(addr.unwrap(), 0x10000); 168 | 169 | let mut cursor = freelist.list.cursor_front_mut(); 170 | while let Some(node) = cursor.current() { 171 | assert_eq!(node.start, 0x11000); 172 | assert_eq!(node.end, 0x100000); 173 | 174 | cursor.move_next(); 175 | } 176 | 177 | let addr = freelist.allocate(0x1000, Some(0x2000)); 178 | let mut cursor = freelist.list.cursor_front_mut(); 179 | assert!(cursor.current().is_some()); 180 | if let Some(node) = cursor.current() { 181 | assert_eq!(node.start, 0x11000); 182 | } 183 | 184 | cursor.move_next(); 185 | assert!(cursor.current().is_some()); 186 | if let Some(node) = cursor.current() { 187 | assert_eq!(node.start, 0x13000); 188 | } 189 | } 190 | 191 | #[cfg(not(target_os = "none"))] 192 | #[test] 193 | fn deallocate() { 194 | let mut freelist = FreeList::new(); 195 | let entry = FreeListEntry::new(0x10000, 0x100000); 196 | 197 | freelist.list.push_back(entry); 198 | let addr = freelist.allocate(0x1000, None); 199 | freelist.deallocate(addr.unwrap(), 0x1000); 200 | 201 | let mut cursor = freelist.list.cursor_front_mut(); 202 | while let Some(node) = cursor.current() { 203 | assert_eq!(node.start, 0x10000); 204 | assert_eq!(node.end, 0x100000); 205 | 206 | cursor.move_next(); 207 | } 208 | } 209 | 210 | impl FreeList { 211 | pub fn allocate( 212 | &mut self, 213 | size: usize, 214 | alignment: Option, 215 | ) -> Result { 216 | debug!( 217 | "Allocating {} bytes from Free List {:#X}", 218 | size, self as *const Self as usize 219 | ); 220 | 221 | let new_size = if let Some(align) = alignment { 222 | size + align 223 | } else { 224 | size 225 | }; 226 | 227 | // Find a region in the Free List that has at least the requested size. 228 | let mut cursor = self.list.cursor_front_mut(); 229 | while let Some(node) = cursor.current() { 230 | let (region_start, region_size) = (node.start, node.end - node.start); 231 | 232 | match region_size.as_usize().cmp(&new_size) { 233 | Ordering::Greater => { 234 | // We have found a region that is larger than the requested size. 235 | // Return the address to the beginning of that region and shrink the region by that size. 236 | if let Some(align) = alignment { 237 | let new_addr = PhysAddr::from(align_up!(region_start.as_usize(), align)); 238 | node.start += (new_addr - region_start) + size as u64; 239 | if new_addr != region_start { 240 | let new_entry = FreeListEntry::new(region_start, new_addr); 241 | cursor.insert_before(new_entry); 242 | } 243 | return Ok(new_addr); 244 | } else { 245 | node.start += size as u64; 246 | return Ok(region_start); 247 | } 248 | } 249 | Ordering::Equal => { 250 | // We have found a region that has exactly the requested size. 251 | // Return the address to the beginning of that region and move the node into the pool for deletion or reuse. 252 | if let Some(align) = alignment { 253 | let new_addr = PhysAddr::from(align_up!(region_start.as_usize(), align)); 254 | if new_addr != region_start { 255 | node.end = new_addr; 256 | } 257 | return Ok(new_addr); 258 | } else { 259 | cursor.remove_current(); 260 | return Ok(region_start); 261 | } 262 | } 263 | Ordering::Less => {} 264 | } 265 | 266 | cursor.move_next(); 267 | } 268 | 269 | Err(FreeListError::NoValidEntry) 270 | } 271 | } 272 | 273 | impl FreeList { 274 | pub fn allocate( 275 | &mut self, 276 | size: usize, 277 | alignment: Option, 278 | ) -> Result { 279 | debug!( 280 | "Allocating {} bytes from Free List {:#X}", 281 | size, self as *const Self as usize 282 | ); 283 | 284 | let new_size = if let Some(align) = alignment { 285 | size + align 286 | } else { 287 | size 288 | }; 289 | 290 | // Find a region in the Free List that has at least the requested size. 291 | let mut cursor = self.list.cursor_front_mut(); 292 | while let Some(node) = cursor.current() { 293 | let (region_start, region_size) = (node.start, node.end - node.start); 294 | 295 | match region_size.as_usize().cmp(&new_size) { 296 | Ordering::Greater => { 297 | // We have found a region that is larger than the requested size. 298 | // Return the address to the beginning of that region and shrink the region by that size. 299 | if let Some(align) = alignment { 300 | let new_addr = VirtAddr::from(align_up!(region_start.as_usize(), align)); 301 | node.start += (new_addr - region_start) + size as u64; 302 | if new_addr != region_start { 303 | let new_entry = FreeListEntry::new(region_start, new_addr); 304 | cursor.insert_before(new_entry); 305 | } 306 | return Ok(new_addr); 307 | } else { 308 | node.start += size as u64; 309 | return Ok(region_start); 310 | } 311 | } 312 | Ordering::Equal => { 313 | // We have found a region that has exactly the requested size. 314 | // Return the address to the beginning of that region and move the node into the pool for deletion or reuse. 315 | if let Some(align) = alignment { 316 | let new_addr = VirtAddr::from(align_up!(region_start.as_usize(), align)); 317 | if new_addr != region_start { 318 | node.end = new_addr; 319 | } 320 | return Ok(new_addr); 321 | } else { 322 | cursor.remove_current(); 323 | return Ok(region_start); 324 | } 325 | } 326 | Ordering::Less => {} 327 | } 328 | 329 | cursor.move_next(); 330 | } 331 | 332 | Err(FreeListError::NoValidEntry) 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/errno.rs: -------------------------------------------------------------------------------- 1 | //! Basic error handling 2 | 3 | use core::{fmt, result}; 4 | 5 | pub type Result = result::Result; 6 | 7 | /// Possible errors of eduOS-rs 8 | #[derive(Debug, Clone)] 9 | pub enum Error { 10 | /// Usage of a invalid priority 11 | BadPriority, 12 | BadFsKind, 13 | BadFsOperation, 14 | BadFsPermission, 15 | InvalidFsPath, 16 | InvalidArgument, 17 | } 18 | 19 | impl fmt::Display for Error { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | match *self { 22 | Error::BadPriority => write!(f, "Invalid priority number"), 23 | Error::BadFsKind => write!(f, "Bad file system kind"), 24 | Error::BadFsOperation => write!(f, "Bad file system operation"), 25 | Error::BadFsPermission => write!(f, "Bad file permission"), 26 | Error::InvalidFsPath => write!(f, "Invalid file system path"), 27 | Error::InvalidArgument => write!(f, "Inavlid argument"), 28 | } 29 | } 30 | } 31 | 32 | /// Operation not permitted 33 | pub const EPERM: i32 = 1; 34 | 35 | /// No such file or directory 36 | pub const ENOENT: i32 = 2; 37 | 38 | /// No such process 39 | pub const ESRCH: i32 = 3; 40 | 41 | /// Interrupted system call 42 | pub const EINTR: i32 = 4; 43 | 44 | /// I/O error 45 | pub const EIO: i32 = 5; 46 | 47 | /// No such device or address 48 | pub const ENXIO: i32 = 6; 49 | 50 | /// Argument list too long 51 | pub const E2BIG: i32 = 7; 52 | 53 | /// Exec format error 54 | pub const ENOEXEC: i32 = 8; 55 | 56 | /// Bad file number 57 | pub const EBADF: i32 = 9; 58 | 59 | /// No child processes 60 | pub const ECHILD: i32 = 10; 61 | 62 | /// Try again 63 | pub const EAGAIN: i32 = 11; 64 | 65 | /// Out of memory 66 | pub const ENOMEM: i32 = 12; 67 | 68 | /// Permission denied 69 | pub const EACCES: i32 = 13; 70 | 71 | /// Bad address 72 | pub const EFAULT: i32 = 14; 73 | 74 | /// Block device required 75 | pub const ENOTBLK: i32 = 15; 76 | 77 | /// Device or resource busy 78 | pub const EBUSY: i32 = 16; 79 | 80 | /// File exists 81 | pub const EEXIST: i32 = 17; 82 | 83 | /// Cross-device link 84 | pub const EXDEV: i32 = 18; 85 | 86 | /// No such device 87 | pub const ENODEV: i32 = 19; 88 | 89 | /// Not a directory 90 | pub const ENOTDIR: i32 = 20; 91 | 92 | /// Is a directory 93 | pub const EISDIR: i32 = 21; 94 | 95 | /// Invalid argument 96 | pub const EINVAL: i32 = 22; 97 | 98 | /// File table overflow 99 | pub const ENFILE: i32 = 23; 100 | 101 | /// Too many open files 102 | pub const EMFILE: i32 = 24; 103 | 104 | /// Not a typewriter 105 | pub const ENOTTY: i32 = 25; 106 | 107 | /// Text file busy 108 | pub const ETXTBSY: i32 = 26; 109 | 110 | /// File too large 111 | pub const EFBIG: i32 = 27; 112 | 113 | /// No space left on device 114 | pub const ENOSPC: i32 = 28; 115 | 116 | /// Illegal seek 117 | pub const ESPIPE: i32 = 29; 118 | 119 | /// Read-only file system 120 | pub const EROFS: i32 = 30; 121 | 122 | /// Too many links 123 | pub const EMLINK: i32 = 31; 124 | 125 | /// Broken pipe 126 | pub const EPIPE: i32 = 32; 127 | 128 | /// Math argument out of domain of func 129 | pub const EDOM: i32 = 33; 130 | 131 | /// Math result not representable 132 | pub const ERANGE: i32 = 34; 133 | 134 | /// Resource deadlock would occur 135 | pub const EDEADLK: i32 = 35; 136 | 137 | /// File name too long 138 | pub const ENAMETOOLONG: i32 = 36; 139 | 140 | /// No record locks available 141 | pub const ENOLCK: i32 = 37; 142 | 143 | /// Function not implemented 144 | pub const ENOSYS: i32 = 38; 145 | 146 | /// Directory not empty 147 | pub const ENOTEMPTY: i32 = 39; 148 | 149 | /// Too many symbolic links encountered 150 | pub const ELOOP: i32 = 40; 151 | 152 | /// Operation would block 153 | pub const EWOULDBLOCK: i32 = EAGAIN; 154 | 155 | /// No message of desired type 156 | pub const ENOMSG: i32 = 42; 157 | 158 | /// Identifier removed 159 | pub const EIDRM: i32 = 43; 160 | 161 | /// Channel number out of range 162 | pub const ECHRNG: i32 = 44; 163 | 164 | /// Level 2 not synchronized 165 | pub const EL2NSYNC: i32 = 45; 166 | 167 | /// Level 3 halted 168 | pub const EL3HLT: i32 = 46; 169 | 170 | /// Level 3 reset 171 | pub const EL3RST: i32 = 47; 172 | 173 | /// Link number out of range 174 | pub const ELNRNG: i32 = 48; 175 | 176 | /// Protocol driver not attached 177 | pub const EUNATCH: i32 = 49; 178 | 179 | /// No CSI structure available 180 | pub const ENOCSI: i32 = 50; 181 | 182 | /// Level 2 halted 183 | pub const EL2HLT: i32 = 51; 184 | 185 | /// Invalid exchange 186 | pub const EBADE: i32 = 52; 187 | 188 | /// Invalid request descriptor 189 | pub const EBADR: i32 = 53; 190 | 191 | /// Exchange full 192 | pub const EXFULL: i32 = 54; 193 | 194 | /// No anode 195 | pub const ENOANO: i32 = 55; 196 | 197 | /// Invalid request code 198 | pub const EBADRQC: i32 = 56; 199 | 200 | /// Invalid slot 201 | pub const EBADSLT: i32 = 57; 202 | 203 | pub const EDEADLOCK: i32 = EDEADLK; 204 | 205 | /// Bad font file format 206 | pub const EBFONT: i32 = 59; 207 | 208 | /// Device not a stream 209 | pub const ENOSTR: i32 = 60; 210 | 211 | /// No data available 212 | pub const ENODATA: i32 = 61; 213 | 214 | /// Timer expired 215 | pub const ETIME: i32 = 62; 216 | 217 | /// Out of streams resources 218 | pub const ENOSR: i32 = 63; 219 | 220 | /// Machine is not on the network 221 | pub const ENONET: i32 = 64; 222 | 223 | /// Package not installed 224 | pub const ENOPKG: i32 = 65; 225 | 226 | /// Object is remote 227 | pub const EREMOTE: i32 = 66; 228 | 229 | /// Link has been severed 230 | pub const ENOLINK: i32 = 67; 231 | 232 | /// Advertise error 233 | pub const EADV: i32 = 68; 234 | 235 | /// Srmount error 236 | pub const ESRMNT: i32 = 69; 237 | 238 | /// Communication error on send 239 | pub const ECOMM: i32 = 70; 240 | 241 | /// Protocol error 242 | pub const EPROTO: i32 = 71; 243 | 244 | /// Multihop attempted 245 | pub const EMULTIHOP: i32 = 72; 246 | 247 | /// RFS specific error 248 | pub const EDOTDOT: i32 = 73; 249 | 250 | /// Not a data message 251 | pub const EBADMSG: i32 = 74; 252 | 253 | /// Value too large for defined data type 254 | pub const EOVERFLOW: i32 = 75; 255 | 256 | /// Name not unique on network 257 | pub const ENOTUNIQ: i32 = 76; 258 | 259 | /// File descriptor in bad state 260 | pub const EBADFD: i32 = 77; 261 | 262 | /// Remote address changed 263 | pub const EREMCHG: i32 = 78; 264 | 265 | /// Can not access a needed shared library 266 | pub const ELIBACC: i32 = 79; 267 | 268 | /// Accessing a corrupted shared library 269 | pub const ELIBBAD: i32 = 80; 270 | 271 | /// .lib section in a.out corrupted 272 | pub const ELIBSCN: i32 = 81; 273 | 274 | /// Attempting to link in too many shared libraries 275 | pub const ELIBMAX: i32 = 82; 276 | 277 | /// Cannot exec a shared library directly 278 | pub const ELIBEXEC: i32 = 83; 279 | 280 | /// Illegal byte sequence 281 | pub const EILSEQ: i32 = 84; 282 | 283 | /// Interrupted system call should be restarted 284 | pub const ERESTART: i32 = 85; 285 | 286 | /// Streams pipe error 287 | pub const ESTRPIPE: i32 = 86; 288 | 289 | /// Too many users 290 | pub const EUSERS: i32 = 87; 291 | 292 | /// Socket operation on non-socket 293 | pub const ENOTSOCK: i32 = 88; 294 | 295 | /// Destination address required 296 | pub const EDESTADDRREQ: i32 = 89; 297 | 298 | /// Message too long 299 | pub const EMSGSIZE: i32 = 90; 300 | 301 | /// Protocol wrong type for socket 302 | pub const EPROTOTYPE: i32 = 91; 303 | 304 | /// Protocol not available 305 | pub const ENOPROTOOPT: i32 = 92; 306 | 307 | /// Protocol not supported 308 | pub const EPROTONOSUPPORT: i32 = 93; 309 | 310 | /// Socket type not supported 311 | pub const ESOCKTNOSUPPORT: i32 = 94; 312 | 313 | /// Operation not supported on transport endpoint 314 | pub const EOPNOTSUPP: i32 = 95; 315 | 316 | /// Protocol family not supported 317 | pub const EPFNOSUPPORT: i32 = 96; 318 | 319 | /// Address family not supported by protocol 320 | pub const EAFNOSUPPORT: i32 = 97; 321 | 322 | /// Address already in use 323 | pub const EADDRINUSE: i32 = 98; 324 | 325 | /// Cannot assign requested address 326 | pub const EADDRNOTAVAIL: i32 = 99; 327 | 328 | /// Network is down 329 | pub const ENETDOWN: i32 = 100; 330 | 331 | /// Network is unreachable 332 | pub const ENETUNREACH: i32 = 101; 333 | 334 | /// Network dropped connection because of reset 335 | pub const ENETRESET: i32 = 102; 336 | 337 | /// Software caused connection abort 338 | pub const ECONNABORTED: i32 = 103; 339 | 340 | /// Connection reset by peer 341 | pub const ECONNRESET: i32 = 104; 342 | 343 | /// No buffer space available 344 | pub const ENOBUFS: i32 = 105; 345 | 346 | /// Transport endpoint is already connected 347 | pub const EISCONN: i32 = 106; 348 | 349 | /// Transport endpoint is not connected 350 | pub const ENOTCONN: i32 = 107; 351 | 352 | /// Cannot send after transport endpoint shutdown 353 | pub const ESHUTDOWN: i32 = 108; 354 | 355 | /// Too many references: cannot splice 356 | pub const ETOOMANYREFS: i32 = 109; 357 | 358 | /// Connection timed out 359 | pub const ETIMEDOUT: i32 = 110; 360 | 361 | /// Connection refused 362 | pub const ECONNREFUSED: i32 = 111; 363 | 364 | /// Host is down 365 | pub const EHOSTDOWN: i32 = 112; 366 | 367 | /// No route to host 368 | pub const EHOSTUNREACH: i32 = 113; 369 | 370 | /// Operation already in progress 371 | pub const EALREADY: i32 = 114; 372 | 373 | /// Operation now in progress 374 | pub const EINPROGRESS: i32 = 115; 375 | 376 | /// Stale file handle 377 | pub const ESTALE: i32 = 116; 378 | 379 | /// Structure needs cleaning 380 | pub const EUCLEAN: i32 = 117; 381 | 382 | /// Not a XENIX named type file 383 | pub const ENOTNAM: i32 = 118; 384 | 385 | /// No XENIX semaphores available 386 | pub const ENAVAIL: i32 = 119; 387 | 388 | /// Is a named type file 389 | pub const EISNAM: i32 = 120; 390 | 391 | /// Remote I/O error 392 | pub const EREMOTEIO: i32 = 121; 393 | 394 | /// Quota exceeded 395 | pub const EDQUOT: i32 = 122; 396 | 397 | /// No medium found 398 | pub const ENOMEDIUM: i32 = 123; 399 | 400 | /// Wrong medium type 401 | pub const EMEDIUMTYPE: i32 = 124; 402 | 403 | /// Operation Canceled 404 | pub const ECANCELED: i32 = 125; 405 | 406 | /// Required key not available 407 | pub const ENOKEY: i32 = 126; 408 | 409 | /// Key has expired 410 | pub const EKEYEXPIRED: i32 = 127; 411 | 412 | /// Key has been revoked 413 | pub const EKEYREVOKED: i32 = 128; 414 | 415 | /// Key was rejected by service 416 | pub const EKEYREJECTED: i32 = 129; 417 | 418 | /// Robust mutexes: Owner died 419 | pub const EOWNERDEAD: i32 = 130; 420 | 421 | /// Robust mutexes: State not recoverable 422 | pub const ENOTRECOVERABLE: i32 = 131; 423 | 424 | /// Robust mutexes: Operation not possible due to RF-kill 425 | pub const ERFKILL: i32 = 132; 426 | 427 | /// Robust mutexes: Memory page has hardware error 428 | pub const EHWPOISON: i32 = 133; 429 | -------------------------------------------------------------------------------- /src/arch/x86/kernel/irq.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x86::mm::paging::page_fault_handler; 2 | use crate::logging::*; 3 | use crate::scheduler::*; 4 | use crate::synch::spinlock::*; 5 | use core::arch::asm; 6 | use core::fmt; 7 | use x86::bits64::paging::VAddr; 8 | use x86::dtables::{lidt, DescriptorTablePointer}; 9 | use x86::io::*; 10 | use x86::segmentation::{SegmentSelector, SystemDescriptorTypes64}; 11 | use x86::Ring; 12 | 13 | /// Maximum possible number of interrupts 14 | const IDT_ENTRIES: usize = 256; 15 | const KERNEL_CODE_SELECTOR: SegmentSelector = SegmentSelector::new(1, Ring::Ring0); 16 | 17 | /// Enable Interrupts 18 | pub fn irq_enable() { 19 | unsafe { asm!("sti", options(nomem, nostack, preserves_flags)) }; 20 | } 21 | 22 | /// Disable Interrupts 23 | pub fn irq_disable() { 24 | unsafe { asm!("cli", options(nomem, nostack, preserves_flags)) }; 25 | } 26 | 27 | /// Determines, if the interrupt flags (IF) is set 28 | pub fn is_irq_enabled() -> bool { 29 | let eflags: usize; 30 | 31 | unsafe { asm!("pushf; pop {}", lateout(reg) eflags, options(nomem, nostack, preserves_flags)) }; 32 | if (eflags & (1usize << 9)) != 0 { 33 | return true; 34 | } 35 | 36 | false 37 | } 38 | 39 | /// Disable IRQs (nested) 40 | /// 41 | /// Disable IRQs when unsure if IRQs were enabled at all. 42 | /// This function together with irq_nested_enable can be used 43 | /// in situations when interrupts shouldn't be activated if they 44 | /// were not activated before calling this function. 45 | pub fn irq_nested_disable() -> bool { 46 | let was_enabled = is_irq_enabled(); 47 | irq_disable(); 48 | was_enabled 49 | } 50 | 51 | /// Enable IRQs (nested) 52 | /// 53 | /// Can be used in conjunction with irq_nested_disable() to only enable 54 | /// interrupts again if they were enabled before. 55 | pub fn irq_nested_enable(was_enabled: bool) { 56 | if was_enabled { 57 | irq_enable(); 58 | } 59 | } 60 | 61 | #[inline(always)] 62 | pub(crate) fn send_eoi_to_slave() { 63 | /* 64 | * If the IDT entry that was invoked was greater-than-or-equal to 40 65 | * and lower than 48 (meaning IRQ8 - 15), then we need to 66 | * send an EOI to the slave controller of the PIC 67 | */ 68 | unsafe { 69 | outb(0xA0, 0x20); 70 | } 71 | } 72 | 73 | #[inline(always)] 74 | pub(crate) fn send_eoi_to_master() { 75 | /* 76 | * In either case, we need to send an EOI to the master 77 | * interrupt controller of the PIC, too 78 | */ 79 | unsafe { 80 | outb(0x20, 0x20); 81 | } 82 | } 83 | 84 | // Create isr entries, where the number after the 85 | // pseudo error code represents following interrupts: 86 | // 0: Divide By Zero Exception 87 | // 1: Debug Exception 88 | // 2: Non Maskable Interrupt Exception 89 | // 3: Int 3 Exception 90 | // 4: INTO Exception 91 | // 5: Out of Bounds Exception 92 | // 6: Invalid Opcode Exception 93 | // 7: Coprocessor Not Available Exception 94 | 95 | extern "x86-interrupt" fn divide_by_zero_exception(stack_frame: ExceptionStackFrame) { 96 | info!( 97 | "Task {} receive a Divide By Zero Exception: {:#?}", 98 | get_current_taskid(), 99 | stack_frame 100 | ); 101 | send_eoi_to_master(); 102 | abort(); 103 | } 104 | 105 | extern "x86-interrupt" fn debug_exception(stack_frame: ExceptionStackFrame) { 106 | info!( 107 | "Task {} receive a Debug Exception: {:#?}", 108 | get_current_taskid(), 109 | stack_frame 110 | ); 111 | send_eoi_to_master(); 112 | abort(); 113 | } 114 | 115 | extern "x86-interrupt" fn nmi_exception(stack_frame: ExceptionStackFrame) { 116 | info!( 117 | "Task {} receive a Non Maskable Interrupt Exception: {:#?}", 118 | get_current_taskid(), 119 | stack_frame 120 | ); 121 | send_eoi_to_master(); 122 | abort(); 123 | } 124 | 125 | extern "x86-interrupt" fn int3_exception(stack_frame: ExceptionStackFrame) { 126 | info!( 127 | "Task {} receive a Int 3 Exception: {:#?}", 128 | get_current_taskid(), 129 | stack_frame 130 | ); 131 | send_eoi_to_master(); 132 | abort(); 133 | } 134 | 135 | extern "x86-interrupt" fn int0_exception(stack_frame: ExceptionStackFrame) { 136 | info!( 137 | "Task {} receive a INT0 Exception: {:#?}", 138 | get_current_taskid(), 139 | stack_frame 140 | ); 141 | send_eoi_to_master(); 142 | abort(); 143 | } 144 | 145 | extern "x86-interrupt" fn out_of_bound_exception(stack_frame: ExceptionStackFrame) { 146 | info!( 147 | "Task {} receive a Out of Bounds Exception: {:#?}", 148 | get_current_taskid(), 149 | stack_frame 150 | ); 151 | send_eoi_to_master(); 152 | abort(); 153 | } 154 | 155 | extern "x86-interrupt" fn invalid_opcode_exception(stack_frame: ExceptionStackFrame) { 156 | info!( 157 | "Task {} receive a Invalid Opcode Exception: {:#?}", 158 | get_current_taskid(), 159 | stack_frame 160 | ); 161 | send_eoi_to_master(); 162 | abort(); 163 | } 164 | 165 | extern "x86-interrupt" fn no_coprocessor_exception(stack_frame: ExceptionStackFrame) { 166 | info!( 167 | "Task {} receive a Coprocessor Not Available Exception: {:#?}", 168 | get_current_taskid(), 169 | stack_frame 170 | ); 171 | send_eoi_to_master(); 172 | abort(); 173 | } 174 | 175 | // 8: Double Fault Exception (With Error Code!) 176 | 177 | extern "x86-interrupt" fn double_fault_exception( 178 | stack_frame: ExceptionStackFrame, 179 | error_code: u64, 180 | ) { 181 | info!( 182 | "Task {} receive a Double Fault Exception: {:#?}, error_code {}", 183 | get_current_taskid(), 184 | stack_frame, 185 | error_code 186 | ); 187 | send_eoi_to_master(); 188 | abort(); 189 | } 190 | 191 | // 9: Coprocessor Segment Overrun Exception 192 | 193 | extern "x86-interrupt" fn overrun_exception(stack_frame: ExceptionStackFrame) { 194 | info!( 195 | "Task {} receive a Coprocessor Segment Overrun Exception: {:#?}", 196 | get_current_taskid(), 197 | stack_frame 198 | ); 199 | send_eoi_to_master(); 200 | abort(); 201 | } 202 | 203 | // 10: Bad TSS Exception (With Error Code!) 204 | // 11: Segment Not Present Exception (With Error Code!) 205 | // 12: Stack Fault Exception (With Error Code!) 206 | // 13: General Protection Fault Exception (With Error Code!) 207 | // 14: Page Fault Exception (With Error Code!) 208 | 209 | extern "x86-interrupt" fn bad_tss_exception(stack_frame: ExceptionStackFrame, error_code: u64) { 210 | info!( 211 | "Task {} receive a Bad TSS Exception: {:#?}, error_code 0x{:x}", 212 | get_current_taskid(), 213 | stack_frame, 214 | error_code 215 | ); 216 | send_eoi_to_master(); 217 | abort(); 218 | } 219 | 220 | extern "x86-interrupt" fn not_present_exception(stack_frame: ExceptionStackFrame, error_code: u64) { 221 | info!( 222 | "Task {} receive a Segment Not Present Exception: {:#?}, error_code 0x{:x}", 223 | get_current_taskid(), 224 | stack_frame, 225 | error_code 226 | ); 227 | send_eoi_to_master(); 228 | abort(); 229 | } 230 | 231 | extern "x86-interrupt" fn stack_fault_exception(stack_frame: ExceptionStackFrame, error_code: u64) { 232 | info!( 233 | "Task {} receive a Stack Fault Exception: {:#?}, error_code 0x{:x}", 234 | get_current_taskid(), 235 | stack_frame, 236 | error_code 237 | ); 238 | send_eoi_to_master(); 239 | abort(); 240 | } 241 | 242 | extern "x86-interrupt" fn general_protection_exception( 243 | stack_frame: ExceptionStackFrame, 244 | error_code: u64, 245 | ) { 246 | info!( 247 | "Task {} receive a General Protection Exception: {:#?}, error_code 0x{:x}", 248 | get_current_taskid(), 249 | stack_frame, 250 | error_code 251 | ); 252 | send_eoi_to_master(); 253 | abort(); 254 | } 255 | 256 | // 15: Reserved Exception 257 | // 16: Floating Point Exception 258 | // 17: Alignment Check Exception 259 | // 18: Machine Check Exception 260 | // 19-31: Reserved 261 | 262 | extern "x86-interrupt" fn floating_point_exception(stack_frame: ExceptionStackFrame) { 263 | info!( 264 | "Task {} receive a Floating Point Exception: {:#?}", 265 | get_current_taskid(), 266 | stack_frame 267 | ); 268 | send_eoi_to_master(); 269 | abort(); 270 | } 271 | 272 | extern "x86-interrupt" fn alignment_check_exception(stack_frame: ExceptionStackFrame) { 273 | info!( 274 | "Task {} receive a Alignment Check Exception: {:#?}", 275 | get_current_taskid(), 276 | stack_frame 277 | ); 278 | send_eoi_to_master(); 279 | abort(); 280 | } 281 | 282 | extern "x86-interrupt" fn machine_check_exception(stack_frame: ExceptionStackFrame) { 283 | info!( 284 | "Task {} receive a Machine Check Exception: {:#?}", 285 | get_current_taskid(), 286 | stack_frame 287 | ); 288 | send_eoi_to_master(); 289 | abort(); 290 | } 291 | 292 | extern "x86-interrupt" fn reserved_exception(stack_frame: ExceptionStackFrame) { 293 | info!( 294 | "Task {} receive a reserved exception: {:#?}", 295 | get_current_taskid(), 296 | stack_frame 297 | ); 298 | send_eoi_to_master(); 299 | abort(); 300 | } 301 | 302 | extern "x86-interrupt" fn unhandled_irq1(stack_frame: ExceptionStackFrame) { 303 | info!( 304 | "Task {} receive unknown interrupt: {:#?}", 305 | get_current_taskid(), 306 | stack_frame 307 | ); 308 | send_eoi_to_master(); 309 | abort(); 310 | } 311 | 312 | extern "x86-interrupt" fn unhandled_irq2(stack_frame: ExceptionStackFrame) { 313 | info!( 314 | "Task {} receive unknown interrupt: {:#?}", 315 | get_current_taskid(), 316 | stack_frame 317 | ); 318 | send_eoi_to_slave(); 319 | send_eoi_to_master(); 320 | abort(); 321 | } 322 | 323 | extern "x86-interrupt" fn timer_handler(stack_frame: ExceptionStackFrame) { 324 | debug!( 325 | "Task {} receive timer interrupt!\n{:#?}", 326 | get_current_taskid(), 327 | stack_frame 328 | ); 329 | 330 | send_eoi_to_master(); 331 | schedule(); 332 | } 333 | 334 | /// An interrupt gate descriptor. 335 | /// 336 | /// See Intel manual 3a for details, specifically section "6.14.1 64-Bit Mode 337 | /// IDT" and "Figure 6-7. 64-Bit IDT Gate Descriptors". 338 | #[derive(Debug, Clone, Copy)] 339 | #[repr(C, packed)] 340 | struct IdtEntry { 341 | /// Lower 16 bits of ISR. 342 | pub base_lo: u16, 343 | /// Segment selector. 344 | pub selector: SegmentSelector, 345 | /// This must always be zero. 346 | pub ist_index: u8, 347 | /// Flags. 348 | pub flags: u8, 349 | /// The upper 48 bits of ISR (the last 16 bits must be zero). 350 | pub base_hi: u64, 351 | /// Must be zero. 352 | pub reserved1: u16, 353 | } 354 | 355 | #[allow(dead_code)] 356 | enum Type { 357 | InterruptGate, 358 | TrapGate, 359 | } 360 | 361 | impl Type { 362 | pub fn pack(self) -> u8 { 363 | match self { 364 | Type::InterruptGate => SystemDescriptorTypes64::InterruptGate as u8, 365 | Type::TrapGate => SystemDescriptorTypes64::TrapGate as u8, 366 | } 367 | } 368 | } 369 | 370 | impl IdtEntry { 371 | /// A "missing" IdtEntry. 372 | /// 373 | /// If the CPU tries to invoke a missing interrupt, it will instead 374 | /// send a General Protection fault (13), with the interrupt number and 375 | /// some other data stored in the error code. 376 | pub const MISSING: IdtEntry = IdtEntry { 377 | base_lo: 0, 378 | selector: SegmentSelector::from_raw(0), 379 | ist_index: 0, 380 | flags: 0, 381 | base_hi: 0, 382 | reserved1: 0, 383 | }; 384 | 385 | /// Create a new IdtEntry pointing at `handler`, which must be a function 386 | /// with interrupt calling conventions. (This must be currently defined in 387 | /// assembly language.) The `gdt_code_selector` value must be the offset of 388 | /// code segment entry in the GDT. 389 | /// 390 | /// The "Present" flag set, which is the most common case. If you need 391 | /// something else, you can construct it manually. 392 | pub fn new( 393 | handler: VAddr, 394 | gdt_code_selector: SegmentSelector, 395 | dpl: Ring, 396 | ty: Type, 397 | ist_index: u8, 398 | ) -> IdtEntry { 399 | assert!(ist_index < 0b1000); 400 | IdtEntry { 401 | base_lo: ((handler.as_usize() as u64) & 0xFFFF) as u16, 402 | base_hi: handler.as_usize() as u64 >> 16, 403 | selector: gdt_code_selector, 404 | ist_index, 405 | flags: dpl as u8 | ty.pack() | (1 << 7), 406 | reserved1: 0, 407 | } 408 | } 409 | } 410 | 411 | static INTERRUPT_HANDLER: SpinlockIrqSave = 412 | SpinlockIrqSave::new(InteruptHandler::new()); 413 | 414 | struct InteruptHandler { 415 | /// An Interrupt Descriptor Table which specifies how to respond to each 416 | /// interrupt. 417 | idt: [IdtEntry; IDT_ENTRIES], 418 | } 419 | 420 | impl InteruptHandler { 421 | pub const fn new() -> InteruptHandler { 422 | InteruptHandler { 423 | idt: [IdtEntry::MISSING; IDT_ENTRIES], 424 | } 425 | } 426 | 427 | #[allow(dead_code)] 428 | pub fn add_handler( 429 | &mut self, 430 | int_no: usize, 431 | func: extern "x86-interrupt" fn(ExceptionStackFrame), 432 | ) { 433 | if int_no < IDT_ENTRIES { 434 | self.idt[int_no] = IdtEntry::new( 435 | VAddr::from_usize(func as usize), 436 | KERNEL_CODE_SELECTOR, 437 | Ring::Ring0, 438 | Type::InterruptGate, 439 | 0, 440 | ); 441 | } else { 442 | info!("unable to add handler for interrupt {}", int_no); 443 | } 444 | } 445 | 446 | #[allow(dead_code)] 447 | pub fn remove_handler(&mut self, int_no: usize) { 448 | if int_no < IDT_ENTRIES { 449 | if int_no < 40 { 450 | self.idt[int_no] = IdtEntry::new( 451 | VAddr::from_usize(unhandled_irq1 as usize), 452 | KERNEL_CODE_SELECTOR, 453 | Ring::Ring0, 454 | Type::InterruptGate, 455 | 0, 456 | ); 457 | } else { 458 | // send eoi to the master and to the slave 459 | self.idt[int_no] = IdtEntry::new( 460 | VAddr::from_usize(unhandled_irq2 as usize), 461 | KERNEL_CODE_SELECTOR, 462 | Ring::Ring0, 463 | Type::InterruptGate, 464 | 0, 465 | ); 466 | } 467 | } else { 468 | info!("unable to remove handler for interrupt {}", int_no); 469 | } 470 | } 471 | 472 | pub unsafe fn load_idt(&mut self) { 473 | self.idt[0] = IdtEntry::new( 474 | VAddr::from_usize(divide_by_zero_exception as usize), 475 | KERNEL_CODE_SELECTOR, 476 | Ring::Ring0, 477 | Type::InterruptGate, 478 | 0, 479 | ); 480 | self.idt[1] = IdtEntry::new( 481 | VAddr::from_usize(debug_exception as usize), 482 | KERNEL_CODE_SELECTOR, 483 | Ring::Ring0, 484 | Type::InterruptGate, 485 | 0, 486 | ); 487 | self.idt[2] = IdtEntry::new( 488 | VAddr::from_usize(nmi_exception as usize), 489 | KERNEL_CODE_SELECTOR, 490 | Ring::Ring0, 491 | Type::InterruptGate, 492 | 0, 493 | ); 494 | self.idt[3] = IdtEntry::new( 495 | VAddr::from_usize(int3_exception as usize), 496 | KERNEL_CODE_SELECTOR, 497 | Ring::Ring0, 498 | Type::InterruptGate, 499 | 0, 500 | ); 501 | self.idt[4] = IdtEntry::new( 502 | VAddr::from_usize(int0_exception as usize), 503 | KERNEL_CODE_SELECTOR, 504 | Ring::Ring0, 505 | Type::InterruptGate, 506 | 0, 507 | ); 508 | self.idt[5] = IdtEntry::new( 509 | VAddr::from_usize(out_of_bound_exception as usize), 510 | KERNEL_CODE_SELECTOR, 511 | Ring::Ring0, 512 | Type::InterruptGate, 513 | 0, 514 | ); 515 | self.idt[6] = IdtEntry::new( 516 | VAddr::from_usize(invalid_opcode_exception as usize), 517 | KERNEL_CODE_SELECTOR, 518 | Ring::Ring0, 519 | Type::InterruptGate, 520 | 0, 521 | ); 522 | self.idt[7] = IdtEntry::new( 523 | VAddr::from_usize(no_coprocessor_exception as usize), 524 | KERNEL_CODE_SELECTOR, 525 | Ring::Ring0, 526 | Type::InterruptGate, 527 | 0, 528 | ); 529 | self.idt[8] = IdtEntry::new( 530 | VAddr::from_usize(double_fault_exception as usize), 531 | KERNEL_CODE_SELECTOR, 532 | Ring::Ring0, 533 | Type::InterruptGate, 534 | 0, 535 | ); 536 | self.idt[9] = IdtEntry::new( 537 | VAddr::from_usize(overrun_exception as usize), 538 | KERNEL_CODE_SELECTOR, 539 | Ring::Ring0, 540 | Type::InterruptGate, 541 | 0, 542 | ); 543 | self.idt[10] = IdtEntry::new( 544 | VAddr::from_usize(bad_tss_exception as usize), 545 | KERNEL_CODE_SELECTOR, 546 | Ring::Ring0, 547 | Type::InterruptGate, 548 | 0, 549 | ); 550 | self.idt[11] = IdtEntry::new( 551 | VAddr::from_usize(not_present_exception as usize), 552 | KERNEL_CODE_SELECTOR, 553 | Ring::Ring0, 554 | Type::InterruptGate, 555 | 0, 556 | ); 557 | self.idt[12] = IdtEntry::new( 558 | VAddr::from_usize(stack_fault_exception as usize), 559 | KERNEL_CODE_SELECTOR, 560 | Ring::Ring0, 561 | Type::InterruptGate, 562 | 0, 563 | ); 564 | self.idt[13] = IdtEntry::new( 565 | VAddr::from_usize(general_protection_exception as usize), 566 | KERNEL_CODE_SELECTOR, 567 | Ring::Ring0, 568 | Type::InterruptGate, 569 | 0, 570 | ); 571 | self.idt[14] = IdtEntry::new( 572 | VAddr::from_usize(page_fault_handler as usize), 573 | KERNEL_CODE_SELECTOR, 574 | Ring::Ring0, 575 | Type::InterruptGate, 576 | 0, 577 | ); 578 | self.idt[15] = IdtEntry::new( 579 | VAddr::from_usize(reserved_exception as usize), 580 | KERNEL_CODE_SELECTOR, 581 | Ring::Ring0, 582 | Type::InterruptGate, 583 | 0, 584 | ); 585 | self.idt[16] = IdtEntry::new( 586 | VAddr::from_usize(floating_point_exception as usize), 587 | KERNEL_CODE_SELECTOR, 588 | Ring::Ring0, 589 | Type::InterruptGate, 590 | 0, 591 | ); 592 | self.idt[17] = IdtEntry::new( 593 | VAddr::from_usize(alignment_check_exception as usize), 594 | KERNEL_CODE_SELECTOR, 595 | Ring::Ring0, 596 | Type::InterruptGate, 597 | 0, 598 | ); 599 | self.idt[18] = IdtEntry::new( 600 | VAddr::from_usize(machine_check_exception as usize), 601 | KERNEL_CODE_SELECTOR, 602 | Ring::Ring0, 603 | Type::InterruptGate, 604 | 0, 605 | ); 606 | for i in 19..32 { 607 | self.idt[i] = IdtEntry::new( 608 | VAddr::from_usize(reserved_exception as usize), 609 | KERNEL_CODE_SELECTOR, 610 | Ring::Ring0, 611 | Type::InterruptGate, 612 | 0, 613 | ); 614 | } 615 | self.idt[32] = IdtEntry::new( 616 | VAddr::from_usize(timer_handler as usize), 617 | KERNEL_CODE_SELECTOR, 618 | Ring::Ring0, 619 | Type::InterruptGate, 620 | 0, 621 | ); 622 | 623 | // send only eoi to the master 624 | for i in 33..40 { 625 | self.idt[i] = IdtEntry::new( 626 | VAddr::from_usize(unhandled_irq1 as usize), 627 | KERNEL_CODE_SELECTOR, 628 | Ring::Ring0, 629 | Type::InterruptGate, 630 | 0, 631 | ); 632 | } 633 | // send eoi to the master and to the slave 634 | for i in 40..IDT_ENTRIES { 635 | self.idt[i] = IdtEntry::new( 636 | VAddr::from_usize(unhandled_irq2 as usize), 637 | KERNEL_CODE_SELECTOR, 638 | Ring::Ring0, 639 | Type::InterruptGate, 640 | 0, 641 | ); 642 | } 643 | 644 | let idtr = DescriptorTablePointer::new(&self.idt); 645 | lidt(&idtr); 646 | } 647 | } 648 | 649 | /// Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This 650 | /// is a problem in protected mode, because IDT entry 8 is a 651 | /// Double Fault! Without remapping, every time IRQ0 fires, 652 | /// you get a Double Fault Exception, which is NOT what's 653 | /// actually happening. We send commands to the Programmable 654 | /// Interrupt Controller (PICs - also called the 8259's) in 655 | /// order to make IRQ0 to 15 be remapped to IDT entries 32 to 656 | /// 47 657 | unsafe fn irq_remap() { 658 | outb(0x20, 0x11); 659 | outb(0xA0, 0x11); 660 | outb(0x21, 0x20); 661 | outb(0xA1, 0x28); 662 | outb(0x21, 0x04); 663 | outb(0xA1, 0x02); 664 | outb(0x21, 0x01); 665 | outb(0xA1, 0x01); 666 | outb(0x21, 0x00); 667 | outb(0xA1, 0x00); 668 | } 669 | 670 | pub(crate) fn init() { 671 | debug!("initialize interrupt descriptor table"); 672 | 673 | unsafe { 674 | irq_remap(); 675 | 676 | // load address of the IDT 677 | INTERRUPT_HANDLER.lock().load_idt(); 678 | } 679 | } 680 | 681 | // derived from hilipp Oppermann's blog 682 | // => https://github.com/phil-opp/blog_os/blob/master/src/interrupts/mod.rs 683 | 684 | /// Represents the exception stack frame pushed by the CPU on exception entry. 685 | #[repr(C)] 686 | pub(crate) struct ExceptionStackFrame { 687 | /// This value points to the instruction that should be executed when the interrupt 688 | /// handler returns. For most interrupts, this value points to the instruction immediately 689 | /// following the last executed instruction. However, for some exceptions (e.g., page faults), 690 | /// this value points to the faulting instruction, so that the instruction is restarted on 691 | /// return. See the documentation of the `Idt` fields for more details. 692 | pub instruction_pointer: u64, 693 | /// The code segment selector, padded with zeros. 694 | pub code_segment: u64, 695 | /// The flags register before the interrupt handler was invoked. 696 | pub cpu_flags: u64, 697 | /// The stack pointer at the time of the interrupt. 698 | pub stack_pointer: u64, 699 | /// The stack segment descriptor at the time of the interrupt (often zero in 64-bit mode). 700 | pub stack_segment: u64, 701 | } 702 | 703 | impl fmt::Debug for ExceptionStackFrame { 704 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 705 | struct Hex(u64); 706 | impl fmt::Debug for Hex { 707 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 708 | write!(f, "{:#x}", self.0) 709 | } 710 | } 711 | 712 | let mut s = f.debug_struct("ExceptionStackFrame"); 713 | s.field("instruction_pointer", &Hex(self.instruction_pointer)); 714 | s.field("code_segment", &Hex(self.code_segment)); 715 | s.field("cpu_flags", &Hex(self.cpu_flags)); 716 | s.field("stack_pointer", &Hex(self.stack_pointer)); 717 | s.field("stack_segment", &Hex(self.stack_segment)); 718 | s.finish() 719 | } 720 | } 721 | --------------------------------------------------------------------------------