├── userland ├── .gitignore ├── Cargo.toml ├── .vscode │ └── settings.json ├── rust-toolchain.toml ├── kados_syscall │ ├── .cargo │ │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── Cargo.lock ├── src ├── arch │ ├── mod.rs │ └── x86_64 │ │ ├── cpu_local.rs │ │ ├── time.rs │ │ ├── gdt.rs │ │ ├── syscall.rs │ │ └── mod.rs ├── userland │ ├── mod.rs │ ├── syscall │ │ └── syscall_impl │ │ │ ├── mod.rs │ │ │ ├── time.rs │ │ │ ├── sys.rs │ │ │ ├── mem.rs │ │ │ ├── signal.rs │ │ │ └── task.rs │ ├── buffer.rs │ └── elf.rs ├── mem │ ├── paging │ │ ├── mod.rs │ │ ├── table.rs │ │ └── mapper.rs │ ├── consts.rs │ ├── mod.rs │ └── addr_space.rs ├── fs │ ├── devfs │ │ ├── mod.rs │ │ ├── urandom.rs │ │ ├── null.rs │ │ ├── input.rs │ │ ├── fb.rs │ │ └── socket.rs │ ├── initramfs │ │ ├── symlink.rs │ │ ├── file.rs │ │ ├── dir.rs │ │ ├── root.rs │ │ └── mod.rs │ ├── pipe.rs │ ├── path.rs │ └── mod.rs ├── util │ ├── mod.rs │ ├── ctypes.rs │ ├── stack.rs │ ├── error.rs │ ├── ringbuffer.rs │ ├── lock.rs │ └── errno.rs ├── main.rs ├── task │ ├── group.rs │ ├── wait_queue.rs │ └── signal.rs ├── logging.rs ├── serial.rs ├── god_mode.rs ├── backtrace.rs └── vga_text.rs ├── .cargo ├── debug.toml ├── release.toml ├── config.toml ├── x86_64-kados.json ├── runner_debug.sh ├── runner_release.sh └── linker.ld ├── rust-toolchain.toml ├── .gitignore ├── conf ├── limine.cfg └── grub.cfg ├── .vscode ├── settings.json └── launch.json ├── README.md ├── LICENSE ├── Cargo.toml └── deps.sh /userland/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod x86_64; 2 | 3 | pub use self::x86_64::*; 4 | -------------------------------------------------------------------------------- /src/userland/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod buffer; 2 | pub mod elf; 3 | pub mod syscall; 4 | -------------------------------------------------------------------------------- /src/mem/paging/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mapper; 2 | pub mod table; 3 | pub mod units; 4 | -------------------------------------------------------------------------------- /.cargo/debug.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-none] 2 | runner = ["bash", ".cargo/runner_debug.sh"] 3 | -------------------------------------------------------------------------------- /userland/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["kash", "kados_syscall"] 3 | resolver = "3" 4 | -------------------------------------------------------------------------------- /.cargo/release.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-none] 2 | runner = ["bash", ".cargo/runner_release.sh"] 3 | -------------------------------------------------------------------------------- /userland/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "x86_64-unknown-linux-musl", 3 | } -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = ["x86_64-unknown-none"] 4 | components = ["rust-src"] 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /initramfs 3 | /initramfs_old 4 | /extern 5 | *.objdump 6 | *.map 7 | strace_vi.txt 8 | kados.map 9 | /extern_old -------------------------------------------------------------------------------- /userland/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = ["x86_64-unknown-linux-musl"] 4 | components = ["rust-src"] 5 | -------------------------------------------------------------------------------- /conf/limine.cfg: -------------------------------------------------------------------------------- 1 | TIMEOUT=0 2 | VERBOSE=yes 3 | 4 | : k4dos 5 | PROTOCOL=limine 6 | KASLR=no 7 | RESOLUTION=640x400x32 8 | KERNEL_PATH=boot:///k4dos -------------------------------------------------------------------------------- /src/userland/syscall/syscall_impl/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fs; 2 | pub mod mem; 3 | pub mod signal; 4 | pub mod sys; 5 | pub mod task; 6 | pub mod time; 7 | -------------------------------------------------------------------------------- /conf/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | GRUB_TERMINAL=console 4 | 5 | menuentry "K4DOS" { 6 | multiboot2 /boot/k4dos 7 | boot 8 | } -------------------------------------------------------------------------------- /userland/kados_syscall/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-linux-musl" 3 | 4 | [target.'cfg(target_os = "linux")'] 5 | rustflags = ["-C", "linker=rust-lld", "-C", "relocation-model=static"] -------------------------------------------------------------------------------- /userland/kados_syscall/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "kados_syscall" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /userland/kados_syscall/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kados_syscall" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /userland/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 = "kados_syscall" 7 | version = "0.1.0" 8 | 9 | [[package]] 10 | name = "kash" 11 | version = "0.1.0" 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "lldb.displayFormat": "auto", 3 | "lldb.dereferencePointers": true, 4 | "lldb.consoleMode": "commands", 5 | "lldb.showDisassembly": "auto", 6 | "rust-analyzer.check.command": "clippy", 7 | "editor.formatOnSave": true, 8 | "rust-analyzer.cargo.target": "x86_64-unknown-none", 9 | } -------------------------------------------------------------------------------- /src/fs/devfs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fb; 2 | pub mod input; 3 | pub mod null; 4 | pub mod socket; 5 | pub mod tty; 6 | pub mod urandom; 7 | 8 | pub fn init() { 9 | self::tty::init(); 10 | self::null::init(); 11 | self::urandom::init(); 12 | self::fb::init(); 13 | self::input::init(); 14 | self::socket::init(); 15 | } 16 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-none" 3 | rustflags = ["-Cforce-frame-pointers=yes"] 4 | 5 | [unstable] 6 | build-std = ["core", "compiler_builtins", "alloc"] 7 | build-std-features = ["compiler-builtins-mem"] 8 | 9 | [alias] 10 | qemu = "run --release --config .cargo/release.toml" 11 | qemu-debug = "run --config .cargo/debug.toml" 12 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod error; 3 | pub mod ctypes; 4 | pub mod errno; 5 | pub mod lock; 6 | pub mod ringbuffer; 7 | pub mod stack; 8 | 9 | pub use self::error::*; 10 | pub use self::lock::*; 11 | 12 | #[inline] 13 | pub const fn align_down(val: usize, align: usize) -> usize { 14 | val / align * align 15 | } 16 | #[inline] 17 | pub const fn align_up(val: usize, align: usize) -> usize { 18 | val.div_ceil(align) * align 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "custom", 7 | "name": "Attach to gdbserver", 8 | "initCommands": [ 9 | "platform select remote-gdb-server" 10 | ], 11 | "targetCreateCommands": [ 12 | "target create ${workspaceFolder}/target/iso_root/k4dos" 13 | ], 14 | "processCreateCommands": [ 15 | "gdb-remote 127.0.0.1:1234" 16 | ] 17 | }, 18 | ] 19 | } -------------------------------------------------------------------------------- /src/fs/initramfs/symlink.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | 3 | use crate::{ 4 | fs::{path::PathBuf, FsNode, Stat, Symlink}, 5 | util::KResult, 6 | }; 7 | 8 | pub struct InitRamFsSymlink { 9 | pub(crate) name: String, 10 | pub(crate) dst: PathBuf, 11 | pub(crate) stat: Stat, 12 | } 13 | 14 | impl Symlink for InitRamFsSymlink { 15 | fn link_location(&self) -> KResult { 16 | Ok(self.dst.clone()) 17 | } 18 | 19 | fn stat(&self) -> KResult { 20 | Ok(self.stat) 21 | } 22 | } 23 | 24 | impl FsNode for InitRamFsSymlink { 25 | fn get_name(&self) -> String { 26 | self.name.clone() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/arch/x86_64/cpu_local.rs: -------------------------------------------------------------------------------- 1 | use x86::msr::{rdmsr, IA32_GS_BASE}; 2 | use x86_64::structures::{gdt::GlobalDescriptorTable, tss::TaskStateSegment}; 3 | 4 | pub struct CpuLocalData { 5 | pub kernel_sp: usize, 6 | pub gdt: GlobalDescriptorTable, 7 | } 8 | 9 | #[repr(C, packed)] 10 | pub struct Kpcr { 11 | pub tss: TaskStateSegment, 12 | pub cpu_local: &'static mut CpuLocalData, 13 | pub user_rsp0_tmp: usize, 14 | } 15 | 16 | pub fn get_kpcr() -> &'static mut Kpcr { 17 | unsafe { &mut *(rdmsr(IA32_GS_BASE) as *mut _) } 18 | } 19 | 20 | pub fn get_tss() -> &'static mut TaskStateSegment { 21 | unsafe { &mut *(rdmsr(IA32_GS_BASE) as *mut _) } 22 | } 23 | -------------------------------------------------------------------------------- /src/userland/syscall/syscall_impl/time.rs: -------------------------------------------------------------------------------- 1 | pub const ITIMER_REAL: usize = 0; 2 | pub const ITIMER_VIRTUAL: usize = 1; 3 | pub const ITIMER_PROF: usize = 2; 4 | 5 | #[derive(Default, PartialEq)] 6 | #[repr(C)] 7 | pub struct TimeVal { 8 | pub tv_sec: i64, 9 | pub tv_usec: i64, 10 | } 11 | 12 | #[derive(Default, PartialEq)] 13 | #[repr(C)] 14 | pub struct ITimerVal { 15 | pub it_interval: TimeVal, 16 | pub it_value: TimeVal, 17 | } 18 | 19 | #[derive(Clone, Copy, Default, PartialEq)] 20 | #[repr(C)] 21 | pub struct TimeSpec { 22 | pub tv_sec: isize, 23 | pub tv_nsec: isize, 24 | } 25 | 26 | impl TimeSpec { 27 | pub const fn zero() -> Self { 28 | TimeSpec { 29 | tv_sec: 0, 30 | tv_nsec: 0, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/userland/syscall/syscall_impl/sys.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | mem::addr::VirtAddr, 3 | userland::{buffer::UserBufferWriter, syscall::SyscallHandler}, 4 | util::KResult, 5 | }; 6 | 7 | const UTS_FIELD_LEN: usize = 65; 8 | 9 | impl SyscallHandler<'_> { 10 | pub fn sys_uname(&mut self, buf: VirtAddr) -> KResult { 11 | let mut writer = UserBufferWriter::from_vaddr(buf, UTS_FIELD_LEN * 6); 12 | writer.write_bytes_or_zeros(b"Linux", UTS_FIELD_LEN)?; 13 | writer.write_bytes_or_zeros(b"", UTS_FIELD_LEN)?; 14 | writer.write_bytes_or_zeros(b"4.0.0", UTS_FIELD_LEN)?; 15 | writer.write_bytes_or_zeros(b"K4DOS", UTS_FIELD_LEN)?; 16 | writer.write_bytes_or_zeros(b"", UTS_FIELD_LEN)?; 17 | writer.write_bytes_or_zeros(b"", UTS_FIELD_LEN)?; 18 | Ok(0) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.cargo/x86_64-kados.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none-elf", 3 | "target-endian": "little", 4 | "target-pointer-width": "64", 5 | "target-c-int-width": "32", 6 | "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", 7 | "arch": "x86_64", 8 | "os": "none", 9 | "env": "", 10 | "vendor": "unknown", 11 | "linker": "rust-lld", 12 | "linker-flavor": "ld.lld", 13 | "pre-link-args": { 14 | "ld.lld": [ 15 | "--script=.cargo/linker.ld" 16 | ] 17 | }, 18 | "features": "-mmx,-sse,+soft-float", 19 | "executables": true, 20 | "relocation-model": "pic", 21 | "code-model": "kernel", 22 | "disable-redzone": true, 23 | "frame-pointer": "always", 24 | "no-default-libraries": true, 25 | "position-independent-executables": false, 26 | "tls-model": "global-dynamic" 27 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # k4dos 2 | 3 | K4DOS is a hobby operating system written in Rust. It current supports x86_64 architecture and aims for Linux ABI compatibility (running unmodified Linux programs). 4 | 5 | ## k4dos running NetHack 6 | 7 | ![image](https://user-images.githubusercontent.com/58794204/227795203-976c46ac-1c75-4125-89d8-4377397deff0.png) 8 | 9 | ## k4dos running FreeDOOM 10 | 11 | ![image](https://user-images.githubusercontent.com/58794204/227990608-a0dec362-8624-4869-84ea-9db5dd170a14.png) 12 | 13 | ## k4dos running [RustyRays](https://github.com/clstatham/rustyrays/tree/k4dos_port), my own ray tracer 14 | 15 | ![image](https://user-images.githubusercontent.com/58794204/228302199-c4a6e4ea-590d-4453-8987-1374773342b3.png) 16 | 17 | ## Building 18 | 19 | I haven't set up a proper build system for this to work on other people's machines yet. Contact me or open a PR if you'd like to help with that. 20 | For now, this repo exists to present the source code to others who might want to learn from it. 21 | 22 | ## License 23 | 24 | MIT licensed (see LICENSE file) 25 | -------------------------------------------------------------------------------- /src/util/ctypes.rs: -------------------------------------------------------------------------------- 1 | #[allow(non_camel_case_types)] 2 | pub type c_int16 = i16; 3 | #[allow(non_camel_case_types)] 4 | pub type c_int32 = i32; 5 | #[allow(non_camel_case_types)] 6 | pub type c_int64 = i64; 7 | #[allow(non_camel_case_types)] 8 | pub type c_uint32 = u32; 9 | #[allow(non_camel_case_types)] 10 | pub type c_uint64 = u64; 11 | 12 | #[allow(non_camel_case_types)] 13 | pub type c_int = c_int32; 14 | #[allow(non_camel_case_types)] 15 | pub type c_uint = c_uint32; 16 | #[allow(non_camel_case_types)] 17 | pub type c_short = c_int16; 18 | #[allow(non_camel_case_types)] 19 | pub type c_long = c_int64; 20 | #[allow(non_camel_case_types)] 21 | pub type c_ulong = c_uint64; 22 | 23 | #[allow(non_camel_case_types)] 24 | pub type c_time = c_int64; 25 | #[allow(non_camel_case_types)] 26 | pub type c_suseconds = c_int64; 27 | #[allow(non_camel_case_types)] 28 | pub type c_clockid = c_int; 29 | #[allow(non_camel_case_types)] 30 | pub type c_nfds = c_ulong; 31 | #[allow(non_camel_case_types)] 32 | pub type c_size = c_ulong; 33 | #[allow(non_camel_case_types)] 34 | pub type c_off = c_uint64; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Connor Statham 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/fs/devfs/urandom.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use x86::random::rdrand_slice; 3 | 4 | use crate::{ 5 | fs::{initramfs::get_root, path::Path, File, FsNode, INode}, 6 | userland::buffer::UserBufferWriter, 7 | }; 8 | 9 | pub fn init() { 10 | get_root() 11 | .unwrap() 12 | .lookup(Path::new("dev"), true) 13 | .unwrap() 14 | .as_dir() 15 | .unwrap() 16 | .insert(INode::File(Arc::new(URandom))); 17 | } 18 | 19 | pub struct URandom; 20 | 21 | impl FsNode for URandom { 22 | fn get_name(&self) -> alloc::string::String { 23 | "urandom".into() 24 | } 25 | } 26 | 27 | impl File for URandom { 28 | fn read( 29 | &self, 30 | _offset: usize, 31 | buf: crate::userland::buffer::UserBufferMut, 32 | _options: &crate::fs::opened_file::OpenFlags, 33 | ) -> crate::util::KResult { 34 | let mut bytes = alloc::vec![0u8; buf.len()]; 35 | unsafe { 36 | rdrand_slice(&mut bytes); 37 | } 38 | let mut writer = UserBufferWriter::from_buf(buf); 39 | writer.write_bytes(&bytes)?; 40 | Ok(writer.written_len()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature( 4 | lang_items, 5 | abi_x86_interrupt, 6 | ptr_internals, 7 | slice_pattern, 8 | map_try_insert, 9 | iter_advance_by, 10 | alloc_error_handler 11 | )] 12 | #![allow(internal_features)] 13 | #![allow(clippy::missing_safety_doc, clippy::uninlined_format_args)] 14 | #![deny(unsafe_op_in_unsafe_fn)] 15 | // #![warn(clippy::unwrap_used)] 16 | 17 | extern crate alloc; 18 | 19 | #[macro_use] 20 | pub mod vga_text; 21 | #[macro_use] 22 | pub mod serial; 23 | pub mod arch; 24 | pub mod backtrace; 25 | pub mod fs; 26 | pub mod logging; 27 | pub mod mem; 28 | pub mod task; 29 | pub mod userland; 30 | pub mod util; 31 | #[macro_use] 32 | pub mod graphics; 33 | pub mod god_mode; 34 | 35 | use mem::addr::VirtAddr; 36 | 37 | use spin::Once; 38 | use x86_64::instructions::hlt; 39 | 40 | pub static PHYSICAL_OFFSET: Once = Once::new(); 41 | 42 | #[inline] 43 | pub fn phys_offset() -> VirtAddr { 44 | unsafe { VirtAddr::new_unchecked(*PHYSICAL_OFFSET.get().unwrap()) } 45 | } 46 | 47 | #[unsafe(no_mangle)] 48 | pub extern "C" fn start() -> ! { 49 | arch::arch_main(); 50 | 51 | hcf(); 52 | } 53 | 54 | pub fn hcf() -> ! { 55 | loop { 56 | hlt(); 57 | core::hint::spin_loop(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "k4dos" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [[bin]] 7 | name = "k4dos" 8 | test = false 9 | bench = false 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | limine = "0.3.1" 15 | volatile = "0.2.6" # DO NOT CHANGE 16 | spin = "0.9.8" 17 | bit_field = "0.10.2" 18 | x86 = "0.52.0" 19 | x86_64 = "0.15.2" 20 | log = "0.4" 21 | pic8259 = "0.11" 22 | bitflags = "2.6.0" 23 | uart_16550 = "0.3.2" 24 | crossbeam-utils = { version = "0.8.20", default-features = false } 25 | atomic_refcell = "0.1.13" 26 | buddy_system_allocator = { version = "0.11.0", features = [] } 27 | pc-keyboard = "0.8.0" 28 | xmas-elf = "0.8" 29 | rustc-demangle = "0.1.24" 30 | arrayvec = { version = "0.7.6", default-features = false } 31 | x2apic = "0.4.3" 32 | elfloader = "0.16.0" 33 | bitvec = { version = "1.0.1", default-features = false } 34 | embedded-graphics = "0.8.1" 35 | smoltcp = { version = "0.12.0", default-features = false, features = [ 36 | "alloc", 37 | "proto-ipv4", 38 | "socket", 39 | "socket-raw", 40 | "socket-udp", 41 | "socket-tcp", 42 | "proto-dhcpv4", 43 | "medium-ethernet", 44 | ] } 45 | lazy_static = { version = "1.5.0", features = ["spin_no_std"] } 46 | embedded-profiling = { version = "0.3.0", features = ["proc-macros"] } 47 | 48 | [profile.release] 49 | debug = 1 50 | -------------------------------------------------------------------------------- /src/mem/consts.rs: -------------------------------------------------------------------------------- 1 | use super::addr::VirtAddr; 2 | 3 | pub const PAGE_SHIFT: usize = 12; 4 | pub const PAGE_SIZE: usize = 0x1000; 5 | pub const PAGE_TABLE_ENTRIES: usize = 512; 6 | pub const L4_SHIFT: usize = 39; 7 | pub const L3_SHIFT: usize = 30; 8 | pub const L2_SHIFT: usize = 21; 9 | pub const L1_SHIFT: usize = 12; 10 | 11 | pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE * 32; 12 | pub const USER_STACK_SIZE: usize = PAGE_SIZE * 64; 13 | 14 | /// The maximum canonical virtual address in low (user) address space. 15 | /// All user virtual addresses are less than this value. 16 | pub const MAX_LOW_VADDR: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_7000_0000_0000) }; 17 | 18 | /// The minimum canonical virtual address in high (kernel) address space. 19 | /// All kernel virtual addresses are greater than or equal to this value. 20 | pub const MIN_HIGH_VADDR: VirtAddr = unsafe { VirtAddr::new_unchecked(0xffff_8000_0000_0000) }; 21 | 22 | pub const USER_VALLOC_BASE: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_000a_0000_0000) }; 23 | pub const USER_VALLOC_END: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_0fff_0000_0000) }; 24 | pub const USER_STACK_TOP: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_0fff_ffff_e000) }; 25 | pub const USER_STACK_BOTTOM: VirtAddr = USER_STACK_TOP.const_sub(USER_STACK_SIZE); 26 | 27 | pub const KERNEL_HEAP_START: VirtAddr = unsafe { VirtAddr::new_unchecked(0xFFFF_FE80_0000_0000) }; 28 | pub const KERNEL_HEAP_SIZE: usize = 1024 * 1024 * 1024; // 1024 MiB 29 | -------------------------------------------------------------------------------- /src/task/group.rs: -------------------------------------------------------------------------------- 1 | use alloc::{ 2 | sync::{Arc, Weak}, 3 | vec::Vec, 4 | }; 5 | 6 | use crate::util::IrqMutex; 7 | 8 | use super::{get_scheduler, signal::Signal, Task}; 9 | 10 | pub type PgId = i32; 11 | 12 | pub struct TaskGroup { 13 | pgid: PgId, 14 | tasks: Vec>, 15 | } 16 | 17 | impl TaskGroup { 18 | pub(super) fn new(pgid: PgId) -> Arc> { 19 | Arc::new(IrqMutex::new(TaskGroup { 20 | pgid, 21 | tasks: Vec::new(), 22 | })) 23 | } 24 | 25 | pub fn pgid(&self) -> PgId { 26 | self.pgid 27 | } 28 | 29 | pub fn set_pgid(&mut self, pgid: PgId) { 30 | self.pgid = pgid 31 | } 32 | 33 | pub fn add(&mut self, task: Weak) { 34 | self.tasks.push(task) 35 | } 36 | 37 | pub fn remove(&mut self, task: &Weak) { 38 | self.tasks.retain(|p| !Weak::ptr_eq(p, task)); 39 | if self.tasks.is_empty() { 40 | get_scheduler().task_groups.lock().remove(&self.pgid); 41 | } 42 | } 43 | 44 | pub fn gc_dropped_processes(&mut self) { 45 | self.tasks.retain(|task| task.upgrade().is_some()); 46 | if self.tasks.is_empty() { 47 | get_scheduler().task_groups.lock().remove(&self.pgid); 48 | } 49 | } 50 | 51 | pub fn signal(&mut self, signal: Signal) { 52 | for task in self.tasks.iter() { 53 | get_scheduler().send_signal_to(task.upgrade().unwrap(), signal); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/util/stack.rs: -------------------------------------------------------------------------------- 1 | use core::mem::size_of; 2 | 3 | use super::align_down; 4 | 5 | pub struct Stack<'a> { 6 | ptr: &'a mut usize, 7 | } 8 | 9 | impl<'a> Stack<'a> { 10 | pub fn new(ptr: &'a mut usize) -> Self { 11 | Self { ptr } 12 | } 13 | 14 | pub fn skip_by(&mut self, by: usize) { 15 | *self.ptr -= by; 16 | } 17 | 18 | pub unsafe fn offset<'b, T: Sized>(&mut self) -> &'b mut T { 19 | self.skip_by(size_of::()); 20 | unsafe { &mut *(*self.ptr as *mut T) } 21 | } 22 | 23 | pub fn top(&self) -> usize { 24 | *self.ptr 25 | } 26 | 27 | pub unsafe fn push_bytes(&mut self, bytes: &[u8]) { 28 | self.skip_by(bytes.len()); 29 | 30 | unsafe { (*self.ptr as *mut u8).copy_from(bytes.as_ptr(), bytes.len()) }; 31 | } 32 | 33 | pub unsafe fn push(&mut self, value: T) { 34 | self.skip_by(size_of::()); 35 | unsafe { (*self.ptr as *mut T).write(value) }; 36 | } 37 | 38 | pub fn pop_by(&mut self, by: usize) { 39 | *self.ptr += by; 40 | } 41 | 42 | pub unsafe fn pop_bytes(&mut self, len: usize) -> &[u8] { 43 | let x = unsafe { core::slice::from_raw_parts(*self.ptr as *const u8, len) }; 44 | self.pop_by(len); 45 | x 46 | } 47 | 48 | pub unsafe fn pop<'b, T: Sized>(&mut self) -> &'b mut T { 49 | let x = unsafe { &mut *(*self.ptr as *mut T) }; 50 | self.pop_by(size_of::()); 51 | x 52 | } 53 | 54 | pub fn align_down(&mut self, align: usize) { 55 | *self.ptr = align_down(*self.ptr, align) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.cargo/runner_debug.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # This script will be executed by `cargo run`. 4 | 5 | set -xe 6 | 7 | LIMINE_GIT_URL="https://github.com/limine-bootloader/limine.git" 8 | 9 | # Cargo passes the path to the built executable as the first argument. 10 | KERNEL=$1 11 | 12 | 13 | # Clone the `limine` repository if we don't have it yet. 14 | if [ ! -d target/limine ]; then 15 | git clone $LIMINE_GIT_URL --depth=1 --branch v3.0-branch-binary target/limine 16 | fi 17 | 18 | # Make sure we have an up-to-date version of the bootloader. 19 | cd target/limine 20 | git fetch 21 | make 22 | cd - 23 | 24 | # Copy the needed files into an ISO image. 25 | mkdir -p target/iso_root 26 | cp $KERNEL conf/limine.cfg target/limine/limine{.sys,-cd.bin,-cd-efi.bin} target/iso_root 27 | 28 | 29 | xorriso -as mkisofs \ 30 | -b limine-cd.bin \ 31 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 32 | --efi-boot limine-cd-efi.bin \ 33 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 34 | target/iso_root -o $KERNEL.iso 35 | 36 | # For the image to be bootable on BIOS systems, we must run `limine-deploy` on it. 37 | target/limine/limine-deploy $KERNEL.iso 38 | 39 | # Run the created image with QEMU. 40 | # -machine q35 -cpu EPYC 41 | echo "Running in debug mode." >&2 42 | qemu-system-x86_64 \ 43 | -machine q35 -cpu EPYC -M smm=off \ 44 | -D target/log.txt -d int,guest_errors -no-reboot -no-shutdown \ 45 | -s -S \ 46 | -serial stdio \ 47 | -serial file:target/fb_log.txt \ 48 | -m 4G \ 49 | -cdrom $KERNEL.iso >&2 50 | -------------------------------------------------------------------------------- /src/fs/devfs/null.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::ToOwned, sync::Arc}; 2 | use spin::Once; 3 | 4 | use crate::{ 5 | fs::{ 6 | initramfs::get_root, opened_file::OpenFlags, File, FileMode, FsNode, INode, Stat, S_IFCHR, 7 | }, 8 | userland::buffer::UserBufferWriter, 9 | }; 10 | 11 | static DEV_NULL: Once> = Once::new(); 12 | 13 | pub fn init() { 14 | let null = Arc::new(NullDevice); 15 | get_root() 16 | .unwrap() 17 | .root_dir() 18 | .lookup("dev") 19 | .unwrap() 20 | .as_dir() 21 | .unwrap() 22 | .insert(INode::File(null.clone())); 23 | DEV_NULL.call_once(|| null); 24 | } 25 | 26 | pub struct NullDevice; 27 | 28 | impl FsNode for NullDevice { 29 | fn get_name(&self) -> alloc::string::String { 30 | "null".to_owned() 31 | } 32 | } 33 | 34 | impl File for NullDevice { 35 | fn write( 36 | &self, 37 | _offset: usize, 38 | buf: crate::userland::buffer::UserBuffer<'_>, 39 | _options: &OpenFlags, 40 | ) -> crate::util::KResult { 41 | Ok(buf.len()) 42 | } 43 | 44 | fn read( 45 | &self, 46 | _offset: usize, 47 | buf: crate::userland::buffer::UserBufferMut, 48 | _options: &OpenFlags, 49 | ) -> crate::util::KResult { 50 | let mut writer = UserBufferWriter::from_buf(buf); 51 | writer.write_bytes(&[0x05])?; // EOF 52 | Ok(writer.written_len()) 53 | } 54 | 55 | fn stat(&self) -> crate::util::KResult { 56 | Ok(Stat { 57 | inode_no: 4, 58 | mode: FileMode(S_IFCHR), 59 | ..Stat::zeroed() 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.cargo/runner_release.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # This script will be executed by `cargo run`. 4 | 5 | set -xe 6 | 7 | LIMINE_GIT_URL="https://github.com/limine-bootloader/limine.git" 8 | 9 | # Cargo passes the path to the built executable as the first argument. 10 | KERNEL=$1 11 | 12 | 13 | # Clone the `limine` repository if we don't have it yet. 14 | if [ ! -d target/limine ]; then 15 | git clone $LIMINE_GIT_URL --depth=1 --branch v3.0-branch-binary target/limine 16 | fi 17 | 18 | # Make sure we have an up-to-date version of the bootloader. 19 | cd target/limine 20 | git fetch 21 | make 22 | cd - 23 | 24 | # Copy the needed files into an ISO image. 25 | mkdir -p target/iso_root 26 | cp $KERNEL conf/limine.cfg target/limine/limine{.sys,-cd.bin,-cd-efi.bin} target/iso_root 27 | 28 | 29 | xorriso -as mkisofs \ 30 | -b limine-cd.bin \ 31 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 32 | --efi-boot limine-cd-efi.bin \ 33 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 34 | target/iso_root -o $KERNEL.iso 35 | 36 | # For the image to be bootable on BIOS systems, we must run `limine-deploy` on it. 37 | target/limine/limine-deploy $KERNEL.iso 38 | 39 | # Run the created image with QEMU. 40 | # -machine q35 -cpu EPYC 41 | echo "Running in release mode." >&2 42 | qemu-system-x86_64 \ 43 | -M smm=off \ 44 | -machine q35 -cpu EPYC \ 45 | -D target/log.txt -d int,guest_errors -no-reboot -no-shutdown \ 46 | -s \ 47 | -serial stdio \ 48 | -serial file:target/fb_log.txt \ 49 | -m 4G \ 50 | -cdrom $KERNEL.iso >&2 51 | -------------------------------------------------------------------------------- /src/fs/devfs/input.rs: -------------------------------------------------------------------------------- 1 | //! TODO: make this more like an actual linux keyboard device file 2 | 3 | use alloc::sync::Arc; 4 | use pc_keyboard::{KeyEvent, KeyState}; 5 | use spin::{mutex::SpinMutex, Once}; 6 | 7 | use crate::{ 8 | fs::{initramfs::get_root, opened_file::OpenFlags, File, FsNode, INode}, 9 | userland::buffer::{UserBufferMut, UserBufferWriter}, 10 | util::KResult, 11 | }; 12 | 13 | pub static KBD_DEVICE: Once> = Once::new(); 14 | 15 | pub fn init() { 16 | let kbd = Arc::new(KbdDevice::new()); 17 | get_root() 18 | .unwrap() 19 | .root_dir() 20 | .lookup("dev") 21 | .unwrap() 22 | .as_dir() 23 | .unwrap() 24 | .insert(INode::File(kbd.clone())); 25 | KBD_DEVICE.call_once(|| kbd); 26 | } 27 | 28 | pub struct KbdDevice { 29 | keys_pressed: Arc>, 30 | } 31 | 32 | impl KbdDevice { 33 | pub fn new() -> Self { 34 | Self { 35 | keys_pressed: Arc::new(SpinMutex::new([0u8; 256])), 36 | } 37 | } 38 | 39 | pub fn handle_kbd_irq(&self, key_evt: &KeyEvent) { 40 | match key_evt.state { 41 | KeyState::Down => self.keys_pressed.lock()[key_evt.code as u8 as usize] = 1, 42 | KeyState::Up => self.keys_pressed.lock()[key_evt.code as u8 as usize] = 0, 43 | _ => {} 44 | } 45 | } 46 | } 47 | 48 | impl Default for KbdDevice { 49 | fn default() -> Self { 50 | Self::new() 51 | } 52 | } 53 | 54 | impl FsNode for KbdDevice { 55 | fn get_name(&self) -> alloc::string::String { 56 | "kbd".into() 57 | } 58 | } 59 | 60 | impl File for KbdDevice { 61 | fn read(&self, _offset: usize, buf: UserBufferMut, _options: &OpenFlags) -> KResult { 62 | let mut writer = UserBufferWriter::from_buf(buf); 63 | let keys = *self.keys_pressed.lock(); 64 | writer.write(keys) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/util/error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{Debug, Display}; 2 | 3 | use super::errno::Errno; 4 | 5 | pub type KResult = Result>; 6 | 7 | #[derive(Clone, Default, Debug)] 8 | pub struct KError<'a> { 9 | pub(crate) msg: Option>, 10 | pub(crate) errno: Option, 11 | } 12 | 13 | impl<'a> KError<'a> { 14 | pub fn msg(&self) -> Option> { 15 | self.msg 16 | } 17 | 18 | pub fn errno(&self) -> Option { 19 | self.errno 20 | } 21 | } 22 | 23 | impl Display for KError<'_> { 24 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 25 | match (self.errno, self.msg) { 26 | (Some(errno), Some(msg)) => write!(f, "{:?}: {}", errno, msg), 27 | (Some(errno), None) => write!(f, "{:?}", errno), 28 | (None, Some(msg)) => write!(f, "{}", msg), 29 | (None, None) => write!(f, "Unknown error"), 30 | } 31 | } 32 | } 33 | 34 | #[macro_export] 35 | macro_rules! kerror { 36 | ($e:ident) => { 37 | $crate::util::error::KError { 38 | errno: Some($crate::util::errno::Errno::$e), 39 | msg: None, 40 | } 41 | }; 42 | ($e:ident, $($tts:tt)*) => { 43 | $crate::util::error::KError { 44 | errno: Some($crate::util::errno::Errno::$e), 45 | msg: Some(format_args!($($tts)*)), 46 | } 47 | }; 48 | ($($tts:tt)*) => { 49 | $crate::util::error::KError { 50 | errno: None, 51 | msg: Some(format_args!($($tts)*)), 52 | } 53 | }; 54 | } 55 | 56 | #[macro_export] 57 | macro_rules! kbail { 58 | ($e:ident) => { 59 | return Err($crate::kerror!($e)) 60 | }; 61 | ($e:ident, $($tts:tt)*) => { 62 | return Err($crate::kerror!($e, $($tts)*)) 63 | }; 64 | ($($tts:tt)*) => { 65 | return Err($crate::kerror!($($tts)*)) 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /src/fs/initramfs/file.rs: -------------------------------------------------------------------------------- 1 | use alloc::{string::String, vec, vec::Vec}; 2 | 3 | use crate::{ 4 | fs::{opened_file::OpenFlags, File, FileMode, FsNode, Stat, S_IFREG}, 5 | userland::buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter}, 6 | util::{lock::IrqMutex, KResult}, 7 | }; 8 | 9 | pub struct InitRamFsFile { 10 | pub name: IrqMutex, 11 | pub(super) data: IrqMutex>, 12 | pub(super) stat: IrqMutex, 13 | } 14 | 15 | impl InitRamFsFile { 16 | pub fn new(name: String, inode_no: usize) -> InitRamFsFile { 17 | InitRamFsFile { 18 | name: IrqMutex::new(name), 19 | data: IrqMutex::new(Vec::new()), 20 | stat: IrqMutex::new(Stat { 21 | inode_no, 22 | mode: FileMode::new(S_IFREG | 0o644), 23 | ..Stat::zeroed() 24 | }), 25 | } 26 | } 27 | } 28 | 29 | impl FsNode for InitRamFsFile { 30 | fn get_name(&self) -> String { 31 | self.name.lock().clone() 32 | } 33 | } 34 | 35 | impl File for InitRamFsFile { 36 | fn read(&self, offset: usize, buf: UserBufferMut<'_>, _options: &OpenFlags) -> KResult { 37 | let lock = self.data.lock(); 38 | if offset > lock.len() { 39 | return Ok(0); 40 | } 41 | let mut writer = UserBufferWriter::from_buf(buf); 42 | writer.write_bytes(&lock[offset..]) 43 | } 44 | 45 | fn write(&self, offset: usize, buf: UserBuffer<'_>, options: &OpenFlags) -> KResult { 46 | let mut reader = UserBufferReader::from_buf(buf); 47 | let mut data = self.data.lock(); 48 | let data_len = data.len(); 49 | if data.is_empty() { 50 | data.extend_from_slice(&vec![0u8; offset + reader.remaining_len()]); 51 | } else if offset + reader.remaining_len() < isize::MAX as usize 52 | && (offset + reader.remaining_len() > data_len || options.contains(OpenFlags::O_APPEND)) 53 | { 54 | data.push(0u8); 55 | } 56 | 57 | reader.read_bytes(&mut data[offset..]) 58 | } 59 | 60 | fn stat(&self) -> KResult { 61 | Ok(*self.stat.lock()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/userland/syscall/syscall_impl/mem.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fs::opened_file::FileDesc, 3 | mem::addr::VirtAddr, 4 | task::{ 5 | current_task, 6 | vmem::{MMapFlags, MMapProt}, 7 | }, 8 | userland::syscall::SyscallHandler, 9 | util::KResult, 10 | }; 11 | 12 | impl SyscallHandler<'_> { 13 | pub fn sys_mmap( 14 | &mut self, 15 | addr: VirtAddr, 16 | size: usize, 17 | prot: MMapProt, 18 | flags: MMapFlags, 19 | fd: FileDesc, 20 | offset: usize, 21 | ) -> KResult { 22 | if fd as isize != -1 { 23 | todo!("mmap file"); 24 | } 25 | 26 | let current = current_task(); 27 | let vmem = current.vmem(); 28 | current 29 | .arch_mut() 30 | .address_space 31 | .with_mapper(|mut mapper| { 32 | vmem.lock() 33 | .mmap(addr, size, prot, flags, fd, offset, &mut mapper) 34 | }) 35 | .map(|addr| addr.value() as isize) 36 | } 37 | 38 | pub fn sys_mprotect(&mut self, addr: VirtAddr, size: usize, prot: MMapProt) -> KResult { 39 | current_task().vmem().lock().mprotect(addr, size, prot)?; 40 | Ok(0) 41 | } 42 | 43 | // pub fn sys_brk(&mut self, addr: VirtAddr) -> KResult { 44 | // let current = current_task(); 45 | // let new_addr = current 46 | // .arch_mut() 47 | // .address_space 48 | // .with_mapper(|mut mapper| current.vmem().lock().brk(&mut mapper, addr))?; 49 | // Ok(new_addr.value() as isize) 50 | // } 51 | 52 | pub fn sys_munmap(&mut self, addr: VirtAddr, size: usize) -> KResult { 53 | let current = current_task(); 54 | current.arch_mut().address_space.with_mapper(|mut mapper| { 55 | current.vmem().lock().munmap(&mut mapper, addr, addr + size) 56 | })?; 57 | Ok(0) 58 | } 59 | 60 | pub fn sys_mremap(&mut self, addr: VirtAddr, old_size: usize, size: usize) -> KResult { 61 | let current = current_task(); 62 | let new_addr = current.arch_mut().address_space.with_mapper(|mut mapper| { 63 | current 64 | .vmem() 65 | .lock() 66 | .mremap(addr, old_size, size, &mut mapper) 67 | })?; 68 | Ok(new_addr.value() as isize) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/arch/x86_64/time.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | use x86::io::{inb, outb}; 4 | 5 | use crate::{userland::syscall::syscall_impl::time::TimeSpec, util::IrqMutex}; 6 | 7 | const PIT_FREQUENCY_HZ: usize = 1000; 8 | pub const PIT_DIVIDEND: usize = 1193182; 9 | 10 | static UPTIME_RAW: AtomicUsize = AtomicUsize::new(0); 11 | static UPTIME_SEC: AtomicUsize = AtomicUsize::new(0); 12 | 13 | pub static EPOCH: AtomicUsize = AtomicUsize::new(usize::MAX); 14 | pub static RT_CLOCK: IrqMutex = IrqMutex::new(TimeSpec::zero()); 15 | 16 | pub fn get_uptime_ns() -> usize { 17 | let ts = get_rt_clock(); 18 | (ts.tv_sec * 1000000000 + ts.tv_nsec) as usize 19 | } 20 | 21 | pub fn get_uptime_ms() -> usize { 22 | get_uptime_ns() / 1000000 23 | } 24 | 25 | pub fn get_rt_clock() -> TimeSpec { 26 | *RT_CLOCK.lock() 27 | } 28 | 29 | pub fn get_pit_count() -> u16 { 30 | unsafe { 31 | outb(0x43, 0); 32 | 33 | let lower = inb(0x40) as u16; 34 | let higher = inb(0x40) as u16; 35 | 36 | (higher << 8) | lower 37 | } 38 | } 39 | 40 | pub fn set_reload_value(new_count: u16) { 41 | unsafe { 42 | outb(0x43, 0x34); 43 | outb(0x40, new_count as u8); 44 | outb(0x40, (new_count >> 8) as u8); 45 | } 46 | } 47 | 48 | pub fn set_pit_frequency(frequency: usize) { 49 | let mut new_divisor = PIT_DIVIDEND / frequency; 50 | 51 | if PIT_DIVIDEND % frequency > frequency / 2 { 52 | new_divisor += 1; 53 | } 54 | 55 | set_reload_value(new_divisor as u16); 56 | } 57 | 58 | pub fn pit_irq() { 59 | { 60 | let interval = TimeSpec { 61 | tv_sec: 0, 62 | tv_nsec: (1000000000 / PIT_FREQUENCY_HZ) as isize, 63 | }; 64 | 65 | let mut clk = RT_CLOCK.lock(); 66 | 67 | if clk.tv_nsec + interval.tv_nsec > 999999999 { 68 | let diff = (clk.tv_nsec + interval.tv_nsec) - 1000000000; 69 | 70 | clk.tv_nsec = diff; 71 | clk.tv_sec += 1; 72 | } else { 73 | clk.tv_nsec += interval.tv_nsec; 74 | } 75 | } 76 | 77 | let value = UPTIME_RAW.fetch_add(1, Ordering::Relaxed); 78 | if value % PIT_FREQUENCY_HZ == 0 { 79 | UPTIME_SEC.fetch_add(1, Ordering::Relaxed); 80 | } 81 | } 82 | 83 | pub fn init(boot_time: i64) { 84 | EPOCH.store(boot_time as usize, Ordering::SeqCst); 85 | RT_CLOCK.lock().tv_sec = boot_time as isize; 86 | set_pit_frequency(PIT_FREQUENCY_HZ); 87 | } 88 | -------------------------------------------------------------------------------- /src/task/wait_queue.rs: -------------------------------------------------------------------------------- 1 | use alloc::{collections::VecDeque, sync::Arc}; 2 | 3 | use crate::{ 4 | arch, kbail, 5 | util::{IrqMutex, KResult}, 6 | }; 7 | 8 | use super::{current_task, get_scheduler, Task, TaskState}; 9 | 10 | pub struct WaitQueue { 11 | pub(super) queue: IrqMutex>>, 12 | } 13 | 14 | impl Default for WaitQueue { 15 | fn default() -> Self { 16 | Self::new() 17 | } 18 | } 19 | 20 | impl WaitQueue { 21 | pub const fn new() -> WaitQueue { 22 | WaitQueue { 23 | queue: IrqMutex::new(VecDeque::new()), 24 | } 25 | } 26 | 27 | pub fn sleep_signalable_until( 28 | &self, 29 | timeout: Option, 30 | mut sleep_if_none: F, 31 | ) -> KResult 32 | where 33 | F: FnMut() -> KResult>, 34 | { 35 | let start_time = arch::time::get_uptime_ms(); 36 | loop { 37 | let current = current_task(); 38 | let scheduler = get_scheduler(); 39 | current.set_state(TaskState::Waiting); 40 | { 41 | let mut q_lock = self.queue.lock(); 42 | if !q_lock.iter().any(|t| t.pid == current.pid) { 43 | q_lock.push_back(current.clone()); 44 | } 45 | } 46 | 47 | if current.has_pending_signals() { 48 | scheduler.resume_task(current.clone()); 49 | self.queue.lock().retain(|t| t.pid != current.pid); 50 | kbail!( 51 | EINTR, 52 | "sleep_signalable_until(): interrupted by pending signals" 53 | ); 54 | } 55 | 56 | let ret_value = match sleep_if_none() { 57 | Ok(Some(ret_val)) => Some(Ok(ret_val)), 58 | Ok(None) => None, 59 | Err(err) => Some(Err(err)), 60 | }; 61 | 62 | let current = current_task(); 63 | if let Some(ret_val) = ret_value { 64 | scheduler.resume_task(current.clone()); 65 | self.queue.lock().retain(|t| t.pid != current.pid); 66 | return ret_val; 67 | } 68 | 69 | scheduler.sleep(timeout)?; 70 | 71 | if let Some(timeout) = timeout { 72 | if arch::time::get_uptime_ms() >= start_time + timeout { 73 | kbail!(EINTR, "sleep_signalable_until(): timeout reached"); 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | use log::Level; 2 | use log::Log; 3 | 4 | use crate::serial0_print; 5 | use crate::serial0_println; 6 | use crate::task::SCHEDULER; 7 | 8 | struct KaDOSLogger; 9 | 10 | impl Log for KaDOSLogger { 11 | fn enabled(&self, _metadata: &log::Metadata) -> bool { 12 | true 13 | } 14 | 15 | fn log(&self, record: &log::Record) { 16 | match record.level() { 17 | Level::Debug => serial0_print!("\x1b[1;36m"), // cyan 18 | Level::Error => serial0_print!("\x1b[1;31m"), // red 19 | Level::Info => serial0_print!("\x1b[1;32m"), // green 20 | Level::Warn => serial0_print!("\x1b[1;33m"), // yellow 21 | Level::Trace => serial0_print!("\x1b[1;37m"), // white 22 | } 23 | if let Some(sched) = SCHEDULER.get() { 24 | if let Some(current) = sched.current_task_opt() { 25 | serial0_print!( 26 | "[{}]\t[{}] - {}", 27 | record.level(), 28 | current.pid().as_usize(), 29 | record.args() 30 | ); 31 | } else { 32 | serial0_print!("[{}]\t{}", record.level(), record.args()); 33 | } 34 | } else { 35 | serial0_print!("[{}]\t{}", record.level(), record.args()); 36 | } 37 | serial0_println!("\x1b[0m"); // reset color 38 | } 39 | 40 | fn flush(&self) {} 41 | } 42 | 43 | struct KaDOSProfiler; 44 | 45 | impl embedded_profiling::EmbeddedProfiler for KaDOSProfiler { 46 | fn read_clock(&self) -> embedded_profiling::EPInstant { 47 | let uptime = crate::arch::x86_64::time::get_uptime_ns(); 48 | embedded_profiling::EPInstant::from_ticks(uptime as u32) 49 | } 50 | 51 | fn log_snapshot(&self, snapshot: &embedded_profiling::EPSnapshot) { 52 | crate::serial0_println!("{}", snapshot); 53 | } 54 | } 55 | 56 | pub fn init() { 57 | log::set_logger(&KaDOSLogger).expect("error setting logger"); 58 | let level = if let Some(level) = option_env!("RUST_LOG") 59 | .or(option_env!("KADOS_LOG")) 60 | .or(option_env!("K4DOS_LOG")) 61 | { 62 | match level { 63 | "error" => log::LevelFilter::Error, 64 | "warn" => log::LevelFilter::Warn, 65 | "info" => log::LevelFilter::Info, 66 | "debug" => log::LevelFilter::Debug, 67 | "trace" => log::LevelFilter::Trace, 68 | _ => log::LevelFilter::Info, 69 | } 70 | } else { 71 | log::LevelFilter::Info 72 | }; 73 | log::set_max_level(level); 74 | 75 | unsafe { embedded_profiling::set_profiler(&KaDOSProfiler).expect("error setting profiler") }; 76 | } 77 | -------------------------------------------------------------------------------- /deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ -z "$@" ]]; then 6 | echo "Usage: deps.sh [COMMANDS]..." 7 | echo "Available commands:" 8 | echo "download Downloads Busybox" 9 | echo "menuconfig Configures Busybox with menuconfig" 10 | echo "allnoconfig Configures Busybox with allnoconfig" 11 | echo "defconfig Configures Busybox with its default config (currently doesn't build!)" 12 | echo "clean Cleans the output directory" 13 | echo "build Builds Busybox" 14 | echo "makeimg Creates the initramfs image for K4DOS" 15 | echo "kash Builds Kash, the K4DOS shell" 16 | exit 17 | fi 18 | 19 | STARTDIR=$(pwd) 20 | 21 | mkdir -p extern 22 | 23 | cd extern 24 | if [[ $@ =~ "download" ]]; 25 | then 26 | echo 27 | echo "!!!IMPORTANT!!!" 28 | echo "If you just want a prebuilt image, go to the following link to download one and put it in a new folder in this directory called initramfs" 29 | echo "The prebuilt image currently contains preconfigured and prebuilt distributions of Busybox, FreeDOOM, NetHack, and RustyRays." 30 | echo "Busybox, FreeDOOM, Doom-Generic, and NetHack are subject to their own copyright and license terms." 31 | echo "https://drive.google.com/file/d/1Yl8Ei1toRCmoHbNVxOxamXrGS_s4xvf6/view" 32 | echo "Press ENTER to continue, or Ctrl-C to cancel." 33 | read 34 | 35 | echo "Downloading Busybox." 36 | git clone git://busybox.net/busybox.git 37 | echo "Installing musl with pacman and creating symlinks. (will sudo)" 38 | sudo pacman -S musl 39 | sudo ln -s /usr/bin/ar /usr/bin/musl-ar 40 | sudo ln -s /usr/bin/strip /usr/bin/musl-strip 41 | echo "Now run 'deps.sh menuconfig' and load kados.config, located in the same directory as this script" 42 | fi 43 | 44 | 45 | BUSYBOXDIR=$STARTDIR/extern/busybox 46 | cd busybox 47 | if [[ $@ =~ "menuconfig" ]]; 48 | then 49 | make menuconfig 50 | fi 51 | if [[ $@ =~ "defconfig" ]]; 52 | then 53 | make defconfig 54 | fi 55 | 56 | if [[ $@ =~ "allnoconfig" ]]; 57 | then 58 | make allnoconfig 59 | fi 60 | if [[ $@ =~ "clean" ]]; 61 | then 62 | make clean 63 | fi 64 | if [[ $@ =~ "build" ]]; 65 | then 66 | time make -j6 67 | make install 68 | fi 69 | if [[ $@ =~ "kash" ]]; 70 | then 71 | cd $STARTDIR/userland/kash 72 | cargo build 73 | cd - 74 | fi 75 | if [[ $@ =~ "makeimg" ]]; 76 | then 77 | cd $STARTDIR 78 | mkdir -p initramfs/busybox_fs 79 | cd $STARTDIR 80 | cd initramfs/busybox_fs 81 | rm -f ../initramfs_busybox 82 | cp -r $BUSYBOXDIR/_install/* ./ 83 | cp -r $BUSYBOXDIR/busybox_unstripped ./bin/busybox 84 | mkdir -p dev 85 | find . | cpio -ov --format=newc > ../initramfs 86 | fi 87 | 88 | 89 | cd $STARTDIR 90 | echo "Done." -------------------------------------------------------------------------------- /.cargo/linker.ld: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(start) 3 | OUTPUT_ARCH(i386:x86-64) 4 | OUTPUT_FORMAT(elf64-x86-64) 5 | 6 | KERNEL_BASE = 0xffffffff80000000; 7 | 8 | SECTIONS { 9 | . = KERNEL_BASE + SIZEOF_HEADERS; 10 | 11 | .hash : { *(.hash) } 12 | .gnu.hash : { *(.gnu.hash) } 13 | .dynsym : { *(.dynsym) } 14 | .dynstr : { *(.dynstr) } 15 | .rela : { *(.rela*) } 16 | .rodata : { *(.rodata .rodata.*) } 17 | .note.gnu.build-id : { *(.note.gnu.build-id) } 18 | .eh_frame_hdr : { 19 | PROVIDE(__eh_frame_hdr = .); 20 | KEEP(*(.eh_frame_hdr)) 21 | PROVIDE(__eh_frame_hdr_end = .); 22 | } 23 | .eh_frame : { 24 | PROVIDE(__eh_frame = .); 25 | KEEP(*(.eh_frame)) 26 | PROVIDE(__eh_frame_end = .); 27 | } 28 | .gcc_except_table : { KEEP(*(.gcc_except_table .gcc_except_table.*)) } 29 | 30 | . += CONSTANT(MAXPAGESIZE); 31 | 32 | .plt : { *(.plt .plt.*) } 33 | .text : { *(.text .text.*) } 34 | 35 | . += CONSTANT(MAXPAGESIZE); 36 | 37 | .tdata : { *(.tdata .tdata.*) } 38 | .tbss : { *(.tbss .tbss.*) } 39 | 40 | .data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) } 41 | .dynamic : { *(.dynamic) } 42 | 43 | . = DATA_SEGMENT_RELRO_END(0, .); 44 | 45 | .got : { *(.got .got.*) } 46 | .got.plt : { *(.got.plt .got.plt.*) } 47 | .data : { *(.data .data.*) } 48 | .bss : { *(.bss .bss.*) *(COMMON) } 49 | 50 | . = DATA_SEGMENT_END(.); 51 | 52 | .comment 0 : { *(.comment) } 53 | .debug 0 : { *(.debug) } 54 | .debug_abbrev 0 : { *(.debug_abbrev) } 55 | .debug_aranges 0 : { *(.debug_aranges) } 56 | .debug_frame 0 : { *(.debug_frame) } 57 | .debug_funcnames 0 : { *(.debug_funcnames) } 58 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 59 | .debug_line 0 : { *(.debug_line) } 60 | .debug_loc 0 : { *(.debug_loc) } 61 | .debug_macinfo 0 : { *(.debug_macinfo) } 62 | .debug_pubnames 0 : { *(.debug_pubnames) } 63 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 64 | .debug_ranges 0 : { *(.debug_ranges) } 65 | .debug_sfnames 0 : { *(.debug_sfnames) } 66 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 67 | .debug_str 0 : { *(.debug_str) } 68 | .debug_typenames 0 : { *(.debug_typenames) } 69 | .debug_varnames 0 : { *(.debug_varnames) } 70 | .debug_weaknames 0 : { *(.debug_weaknames) } 71 | .line 0 : { *(.line) } 72 | .shstrtab 0 : { *(.shstrtab) } 73 | .strtab 0 : { *(.strtab) } 74 | .symtab 0 : { *(.symtab) } 75 | } -------------------------------------------------------------------------------- /src/mem/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use paging::units::MemoryUnit; 3 | use spin::Once; 4 | use x86_64::structures::paging::PageTableFlags; 5 | 6 | use crate::{ 7 | kerror, 8 | task::{get_scheduler, Task}, 9 | util::{IrqMutex, KResult}, 10 | }; 11 | 12 | use self::{ 13 | addr_space::AddressSpace, 14 | allocator::{alloc_kernel_pages_at, GLOBAL_ALLOC}, 15 | consts::{KERNEL_HEAP_SIZE, KERNEL_HEAP_START, PAGE_SIZE}, 16 | paging::{ 17 | mapper::Mapper, 18 | units::{MappedPages, Page}, 19 | }, 20 | }; 21 | 22 | pub mod addr; 23 | pub mod addr_space; 24 | pub mod allocator; 25 | pub mod consts; 26 | pub mod paging; 27 | 28 | pub static KERNEL_ADDR_SPACE: Once> = Once::new(); 29 | 30 | pub fn remap_kernel() -> KResult<&'static IrqMutex> { 31 | let active = AddressSpace::current(); 32 | log::info!("Active page table at {:?}", active.cr3()); 33 | let new_space = AddressSpace::new()?; 34 | 35 | // and that's all we gotta do, because Offset Page Tables RULE! 36 | 37 | new_space.switch(); 38 | log::info!("Switched to new page table at {:?}", new_space.cr3()); 39 | Ok(KERNEL_ADDR_SPACE.call_once(|| IrqMutex::new(new_space))) 40 | } 41 | 42 | pub fn init_heap(kernel_mapper: &mut Mapper) -> KResult { 43 | let heap_ap = alloc_kernel_pages_at( 44 | Page::containing_address(KERNEL_HEAP_START), 45 | KERNEL_HEAP_SIZE / PAGE_SIZE, 46 | )?; 47 | let heap_mp = kernel_mapper.map( 48 | heap_ap, 49 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE, 50 | )?; 51 | unsafe { 52 | GLOBAL_ALLOC.lock().add_to_heap( 53 | heap_mp.pages().start_address().value(), 54 | heap_mp.pages().end_address().value(), 55 | ); 56 | } 57 | Ok(heap_mp) 58 | } 59 | 60 | pub fn kernel_addr_space_scope() -> KResult { 61 | KernelAddrSpaceGuard::new() 62 | } 63 | 64 | pub fn with_kernel_addr_space R>(f: F) -> R { 65 | let _guard = kernel_addr_space_scope().expect("Failed to switch to kernel address space"); 66 | f() 67 | } 68 | 69 | #[must_use = "KernelAddrSpaceGuard restores previous address space on drop"] 70 | pub struct KernelAddrSpaceGuard { 71 | _private: (), 72 | current_task: Option>, 73 | } 74 | 75 | impl KernelAddrSpaceGuard { 76 | pub fn new() -> KResult { 77 | let _kernel_addr_space = KERNEL_ADDR_SPACE 78 | .get() 79 | .ok_or(kerror!("KERNEL_ADDR_SPACE not initialized"))? 80 | .try_lock()?; 81 | 82 | _kernel_addr_space.switch(); 83 | 84 | Ok(Self { 85 | _private: (), 86 | current_task: get_scheduler().current_task_opt(), 87 | }) 88 | } 89 | } 90 | 91 | impl Drop for KernelAddrSpaceGuard { 92 | fn drop(&mut self) { 93 | if let Some(task) = self.current_task.as_ref() { 94 | task.arch_mut().address_space.switch(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/userland/syscall/syscall_impl/signal.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | kbail, kerror, 3 | mem::addr::VirtAddr, 4 | task::{ 5 | current_task, get_scheduler, 6 | signal::{SigAction, SignalMask, DEFAULT_ACTIONS, SIG_DFL, SIG_ERR, SIG_IGN}, 7 | TaskId, 8 | }, 9 | userland::syscall::SyscallHandler, 10 | util::{ctypes::c_int, error::KResult}, 11 | }; 12 | 13 | impl SyscallHandler<'_> { 14 | pub fn sys_rt_sigprocmask( 15 | &mut self, 16 | how: usize, 17 | set: VirtAddr, 18 | mut oldset: VirtAddr, 19 | length: usize, 20 | ) -> KResult { 21 | if length != 8 { 22 | log::warn!("sys_rt_sigprocmask: length != 8"); 23 | } 24 | 25 | let how = match how { 26 | 0 => SignalMask::Block, 27 | 1 => SignalMask::Unblock, 28 | 2 => SignalMask::Set, 29 | _ => kbail!(EINVAL, "sys_rt_sigprocmask(): invalid how"), 30 | }; 31 | 32 | current_task().set_signal_mask(how, set, &mut oldset, length)?; 33 | 34 | Ok(0) 35 | } 36 | 37 | pub fn sys_rt_sigaction( 38 | &mut self, 39 | signum: c_int, 40 | act: VirtAddr, 41 | oldact: VirtAddr, 42 | ) -> KResult { 43 | if oldact != VirtAddr::null() { 44 | let action = current_task().signals.lock().get_action(signum); 45 | let action = match action { 46 | SigAction::Ignore => SIG_IGN, 47 | SigAction::Terminate => SIG_ERR, // todo? 48 | SigAction::Handler { handler } => handler as usize, 49 | }; 50 | unsafe { oldact.write_user(action) }?; 51 | } 52 | if act != VirtAddr::null() { 53 | let handler = unsafe { act.read_user::() }?; 54 | let new_action = match handler { 55 | SIG_IGN => SigAction::Ignore, 56 | SIG_DFL => match DEFAULT_ACTIONS.get(signum as usize) { 57 | Some(def) => *def, 58 | None => { 59 | kbail!(EINVAL, "sys_rt_sigaction(): invalid signal number"); 60 | } 61 | }, 62 | _ => SigAction::Handler { 63 | handler: unsafe { core::mem::transmute::(handler) }, 64 | }, 65 | }; 66 | 67 | current_task() 68 | .signals 69 | .lock() 70 | .set_action(signum, new_action)?; 71 | } 72 | 73 | Ok(0) 74 | } 75 | 76 | pub fn sys_rt_sigreturn(&mut self) -> KResult { 77 | get_scheduler().restore_signaled_user_stack(self.frame); 78 | kbail!(EINTR, "sys_rt_sigreturn(): should not return") 79 | } 80 | 81 | pub fn sys_kill(&mut self, pid: TaskId, signum: c_int) -> KResult { 82 | let sched = get_scheduler(); 83 | 84 | sched.send_signal_to( 85 | sched 86 | .find_task(pid) 87 | .ok_or(kerror!(ESRCH, "sys_kill(): pid not found"))?, 88 | signum, 89 | ); 90 | Ok(0) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/arch/x86_64/gdt.rs: -------------------------------------------------------------------------------- 1 | use core::alloc::Layout; 2 | 3 | use alloc::alloc::alloc_zeroed; 4 | 5 | use lazy_static::lazy_static; 6 | use x86::msr::{wrmsr, IA32_GS_BASE}; 7 | use x86_64::{ 8 | instructions::tables::load_tss, 9 | registers::segmentation::{Segment, CS, DS, ES, FS, GS, SS}, 10 | structures::{ 11 | gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}, 12 | tss::TaskStateSegment, 13 | }, 14 | }; 15 | 16 | use crate::mem::consts::KERNEL_STACK_SIZE; 17 | 18 | use super::cpu_local::{get_kpcr, get_tss, CpuLocalData, Kpcr}; 19 | 20 | pub const KERNEL_CS_IDX: u16 = 1; 21 | pub const KERNEL_DS_IDX: u16 = 2; 22 | pub const TSS_IDX: u16 = 3; 23 | pub const USER_DS_IDX: u16 = 5; 24 | pub const USER_CS_IDX: u16 = 6; 25 | 26 | static mut STACK: [u8; KERNEL_STACK_SIZE] = [0; KERNEL_STACK_SIZE]; 27 | 28 | lazy_static! { 29 | static ref BOOT_GDT: (GlobalDescriptorTable, [SegmentSelector; 2]) = { 30 | let mut gdt = GlobalDescriptorTable::new(); 31 | let kernel_code_sel = gdt.append(Descriptor::kernel_code_segment()); 32 | let kernel_data_sel = gdt.append(Descriptor::kernel_data_segment()); 33 | (gdt, [kernel_code_sel, kernel_data_sel]) 34 | }; 35 | } 36 | 37 | pub fn init_boot() { 38 | unsafe { 39 | BOOT_GDT.0.load(); 40 | CS::set_reg(BOOT_GDT.1[0]); 41 | DS::set_reg(BOOT_GDT.1[1]); 42 | ES::set_reg(BOOT_GDT.1[1]); 43 | FS::set_reg(BOOT_GDT.1[1]); 44 | 45 | GS::set_reg(BOOT_GDT.1[1]); 46 | 47 | SS::set_reg(BOOT_GDT.1[1]); 48 | } 49 | } 50 | 51 | pub fn init() { 52 | unsafe { 53 | let kpcr_layout = Layout::new::(); 54 | let kpcr_ptr = alloc_zeroed(kpcr_layout) as *mut Kpcr; 55 | wrmsr(IA32_GS_BASE, kpcr_ptr as u64); 56 | 57 | let tls_layout = Layout::new::(); 58 | let tls_ptr = alloc_zeroed(tls_layout) as *mut CpuLocalData; 59 | get_kpcr().cpu_local = &mut *tls_ptr; 60 | } 61 | 62 | let tss = get_tss(); 63 | *tss = TaskStateSegment::new(); 64 | 65 | tss.privilege_stack_table[0] = x86_64::VirtAddr::new( 66 | unsafe { 67 | #[allow(static_mut_refs)] 68 | STACK.as_mut_ptr() 69 | } as u64 70 | + KERNEL_STACK_SIZE as u64, 71 | ); 72 | 73 | let gdt = &mut get_kpcr().cpu_local.gdt; 74 | *gdt = GlobalDescriptorTable::new(); 75 | // kernel code 76 | let kernel_cs_sel = gdt.append(Descriptor::kernel_code_segment()); 77 | // kernel data 78 | let kernel_ds_sel = gdt.append(Descriptor::kernel_data_segment()); 79 | // TSS 80 | let tss_sel = gdt.append(Descriptor::tss_segment(tss)); 81 | // user data (syscall) 82 | let _user_ds_sel = gdt.append(Descriptor::user_data_segment()); 83 | // user code 84 | let _user_cs_sel = gdt.append(Descriptor::user_code_segment()); 85 | 86 | gdt.load(); 87 | 88 | unsafe { 89 | CS::set_reg(kernel_cs_sel); 90 | DS::set_reg(kernel_ds_sel); 91 | ES::set_reg(kernel_ds_sel); 92 | SS::set_reg(kernel_ds_sel); 93 | // FS::set_reg(kernel_ds_sel); 94 | // GS::set_reg(kernel_ds_sel); 95 | 96 | load_tss(tss_sel); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/util/ringbuffer.rs: -------------------------------------------------------------------------------- 1 | //! The ringbuffer from Kerla. 2 | 3 | use core::{cmp::min, mem::MaybeUninit, ops::Range, slice}; 4 | 5 | pub struct RingBuffer { 6 | buf: [MaybeUninit; CAP], 7 | rp: usize, 8 | wp: usize, 9 | full: bool, 10 | } 11 | 12 | impl Default for RingBuffer { 13 | fn default() -> Self { 14 | RingBuffer { 15 | buf: unsafe { MaybeUninit::uninit().assume_init() }, 16 | rp: 0, 17 | wp: 0, 18 | full: false, 19 | } 20 | } 21 | } 22 | 23 | impl RingBuffer { 24 | pub fn new() -> RingBuffer { 25 | Self::default() 26 | } 27 | 28 | pub fn is_writable(&self) -> bool { 29 | !self.full 30 | } 31 | 32 | pub fn is_readable(&self) -> bool { 33 | self.full || self.rp != self.wp 34 | } 35 | 36 | pub fn push(&mut self, data: T) -> Result<(), T> 37 | where 38 | T: Copy, 39 | { 40 | if self.push_slice(&[data]) == 0 { 41 | Err(data) 42 | } else { 43 | Ok(()) 44 | } 45 | } 46 | 47 | pub fn pop(&mut self) -> Option 48 | where 49 | T: Copy, 50 | { 51 | self.pop_slice(1).map(|slice| slice[0]) 52 | } 53 | 54 | pub fn push_slice(&mut self, data: &[T]) -> usize 55 | where 56 | T: Copy, 57 | { 58 | if !self.is_writable() || data.is_empty() { 59 | return 0; 60 | } 61 | 62 | let written_len = if self.wp >= self.rp { 63 | let free1 = self.wp..CAP; 64 | let free2 = 0..self.rp; 65 | let src1 = &data[..min(data.len(), free1.len())]; 66 | let src2 = &data[src1.len()..min(data.len(), src1.len() + free2.len())]; 67 | let dst1 = free1.start..(free1.start + src1.len()); 68 | let dst2 = free2.start..(free2.start + src2.len()); 69 | self.slice_mut(dst1).copy_from_slice(src1); 70 | self.slice_mut(dst2).copy_from_slice(src2); 71 | src1.len() + src2.len() 72 | } else { 73 | let free = self.wp..self.rp; 74 | let src = &data[..min(data.len(), free.len())]; 75 | let dst = free.start..(free.start + src.len()); 76 | self.slice_mut(dst).copy_from_slice(src); 77 | src.len() 78 | }; 79 | 80 | self.wp = (self.wp + written_len) % CAP; 81 | self.full = self.wp == self.rp; 82 | written_len 83 | } 84 | 85 | pub fn pop_slice(&mut self, len: usize) -> Option<&[T]> { 86 | if !self.is_readable() { 87 | return None; 88 | } 89 | 90 | let range = if self.rp < self.wp { 91 | self.rp..min(self.rp + len, self.wp) 92 | } else { 93 | self.wp..min(self.wp + len, CAP) 94 | }; 95 | 96 | self.rp = (self.rp + range.len()) % CAP; 97 | self.full = false; 98 | Some(self.slice(range)) 99 | } 100 | 101 | fn slice(&self, range: Range) -> &[T] { 102 | debug_assert!(range.end <= CAP); 103 | unsafe { 104 | let ptr = self.buf.as_ptr() as *const T; 105 | slice::from_raw_parts(ptr.add(range.start), range.end - range.start) 106 | } 107 | } 108 | 109 | fn slice_mut(&mut self, range: Range) -> &mut [T] { 110 | debug_assert!(range.end <= CAP); 111 | unsafe { 112 | let ptr = self.buf.as_mut_ptr() as *mut T; 113 | slice::from_raw_parts_mut(ptr.add(range.start), range.end - range.start) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/fs/initramfs/dir.rs: -------------------------------------------------------------------------------- 1 | use alloc::{ 2 | string::String, 3 | sync::{Arc, Weak}, 4 | vec::Vec, 5 | }; 6 | 7 | use crate::{ 8 | fs::{ 9 | alloc_inode_no, DirEntry, Directory, FileMode, FileRef, FileType, FsNode, INode, Stat, 10 | S_IFDIR, 11 | }, 12 | kerror, 13 | util::{lock::IrqMutex, KResult}, 14 | }; 15 | 16 | pub struct DirInner { 17 | pub children: Vec, 18 | pub stat: Stat, 19 | pub name: String, 20 | } 21 | 22 | pub struct InitRamFsDir { 23 | pub(super) parent: Weak, 24 | pub(super) inner: IrqMutex, 25 | } 26 | 27 | impl InitRamFsDir { 28 | pub fn new(name: String, inode_no: usize) -> InitRamFsDir { 29 | InitRamFsDir { 30 | parent: Weak::new(), 31 | inner: IrqMutex::new(DirInner { 32 | name, 33 | children: Vec::new(), 34 | stat: Stat { 35 | inode_no, 36 | mode: FileMode::new(S_IFDIR | 0o755), 37 | ..Stat::zeroed() 38 | }, 39 | }), 40 | } 41 | } 42 | 43 | pub fn add_dir(&self, name: String) -> Arc { 44 | let dir = Arc::new(InitRamFsDir::new(name, alloc_inode_no())); 45 | self.inner.lock().children.push(INode::Dir(dir.clone())); 46 | dir 47 | } 48 | 49 | pub fn add_file(&self, file: FileRef) { 50 | self.inner.lock().children.push(INode::File(file.clone())); 51 | } 52 | 53 | pub fn parent_dir(&self) -> Option> { 54 | self.parent.upgrade() 55 | } 56 | } 57 | 58 | impl Directory for InitRamFsDir { 59 | fn insert(&self, inode: INode) { 60 | self.inner.lock().children.push(inode); 61 | } 62 | 63 | fn lookup(&self, name: &str) -> KResult { 64 | let inode = self 65 | .inner 66 | .lock() 67 | .children 68 | .iter() 69 | .find(|child| child.get_name() == *name) 70 | .cloned() 71 | .ok_or(kerror!(ENOENT, "lookup(): not found"))?; 72 | Ok(inode) 73 | } 74 | 75 | fn stat(&self) -> KResult { 76 | Ok(self.inner.lock().stat) 77 | } 78 | 79 | fn readdir(&self, index: usize) -> KResult> { 80 | let entry = self 81 | .inner 82 | .lock() 83 | .children 84 | .get(index) 85 | .map(|entry| match entry { 86 | INode::Pipe(_) => unreachable!("Pipes should be in PipeFs"), 87 | INode::Dir(dir) => DirEntry { 88 | inode_no: dir.stat().unwrap().inode_no, 89 | file_type: FileType::Directory, 90 | name: dir.get_name(), 91 | }, 92 | INode::File(file) => DirEntry { 93 | inode_no: file.stat().unwrap().inode_no, 94 | file_type: FileType::Directory, 95 | name: file.get_name(), 96 | }, 97 | INode::Symlink(link) => DirEntry { 98 | inode_no: link.stat().unwrap().inode_no, 99 | file_type: FileType::Link, 100 | name: link.get_name(), 101 | }, 102 | }); 103 | 104 | Ok(entry) 105 | } 106 | 107 | fn unlink(&self, name: &str) -> KResult<()> { 108 | self.inner 109 | .lock() 110 | .children 111 | .retain(|child| child.get_name() != name); 112 | Ok(()) 113 | } 114 | } 115 | 116 | impl FsNode for InitRamFsDir { 117 | fn get_name(&self) -> String { 118 | self.inner.lock().name.clone() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/fs/pipe.rs: -------------------------------------------------------------------------------- 1 | use alloc::{format, sync::Arc, vec::Vec}; 2 | 3 | use crate::{ 4 | kerror, 5 | task::wait_queue::WaitQueue, 6 | userland::buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter}, 7 | util::{ringbuffer::RingBuffer, IrqMutex, KResult}, 8 | }; 9 | 10 | use super::{opened_file::FileDesc, path::Path, File, FsNode}; 11 | 12 | pub static PIPE_FS: PipeFs = PipeFs::new(); 13 | 14 | pub struct Pipe { 15 | wait_queue: WaitQueue, 16 | ringbuffer: Arc>>, 17 | read_fd: FileDesc, 18 | write_fd: FileDesc, 19 | } 20 | 21 | impl Pipe { 22 | pub fn new(read_fd: FileDesc, write_fd: FileDesc) -> Self { 23 | Self { 24 | wait_queue: WaitQueue::new(), 25 | ringbuffer: Arc::new(IrqMutex::new(RingBuffer::new())), 26 | read_fd, 27 | write_fd, 28 | } 29 | } 30 | 31 | pub fn read_pipe(&self, buf: UserBufferMut<'_>) -> KResult { 32 | let mut writer = UserBufferWriter::from_buf(buf); 33 | let mut ringbuffer = self.wait_queue.sleep_signalable_until(None, || { 34 | let ringbuffer = self.ringbuffer.try_lock(); 35 | if let Ok(ringbuffer) = ringbuffer { 36 | if ringbuffer.is_readable() { 37 | Ok(Some(ringbuffer)) 38 | } else { 39 | Ok(None) 40 | } 41 | } else { 42 | Ok(None) 43 | } 44 | })?; 45 | while let Some(byte) = ringbuffer.pop() { 46 | writer.write(byte)?; 47 | } 48 | Ok(writer.written_len()) 49 | } 50 | 51 | pub fn write_pipe(&self, buf: UserBuffer<'_>) -> KResult { 52 | let mut reader = UserBufferReader::from_buf(buf); 53 | let mut ringbuffer = self.wait_queue.sleep_signalable_until(None, || { 54 | let ringbuffer = self.ringbuffer.try_lock(); 55 | if let Ok(ringbuffer) = ringbuffer { 56 | if ringbuffer.is_writable() { 57 | Ok(Some(ringbuffer)) 58 | } else { 59 | Ok(None) 60 | } 61 | } else { 62 | Ok(None) 63 | } 64 | })?; 65 | while let Ok(byte) = reader.read::() { 66 | ringbuffer.push(byte).ok(); 67 | } 68 | Ok(reader.read_len()) 69 | } 70 | 71 | pub fn read_fd(&self) -> FileDesc { 72 | self.read_fd 73 | } 74 | 75 | pub fn write_fd(&self) -> FileDesc { 76 | self.write_fd 77 | } 78 | } 79 | 80 | impl FsNode for Pipe { 81 | fn get_name(&self) -> alloc::string::String { 82 | format!("pipe_{}_{}", self.write_fd, self.read_fd) 83 | } 84 | } 85 | 86 | impl File for Pipe { 87 | fn read( 88 | &self, 89 | _offset: usize, 90 | buf: UserBufferMut, 91 | _options: &super::opened_file::OpenFlags, 92 | ) -> KResult { 93 | self.read_pipe(buf) 94 | } 95 | 96 | fn write( 97 | &self, 98 | _offset: usize, 99 | buf: UserBuffer<'_>, 100 | _options: &super::opened_file::OpenFlags, 101 | ) -> KResult { 102 | self.write_pipe(buf) 103 | } 104 | } 105 | 106 | pub struct PipeFs { 107 | pipes: IrqMutex>>, 108 | } 109 | 110 | impl PipeFs { 111 | #[allow(clippy::new_without_default)] 112 | pub const fn new() -> Self { 113 | Self { 114 | pipes: IrqMutex::new(Vec::new()), 115 | } 116 | } 117 | 118 | pub fn insert(&self, pipe: Arc) { 119 | self.pipes.lock().push(pipe); 120 | } 121 | 122 | pub fn lookup(&self, path: &Path) -> KResult> { 123 | self.pipes 124 | .lock() 125 | .iter() 126 | .find(|pipe| pipe.get_name() == path.pipe_name().unwrap()) 127 | .cloned() 128 | .ok_or(kerror!(ENOENT, "pipe does not exist")) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /userland/kados_syscall/src/lib.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | 3 | use consts::*; 4 | 5 | pub mod consts; 6 | 7 | #[no_mangle] 8 | #[inline(always)] 9 | #[allow(clippy::missing_safety_doc)] 10 | pub extern "C" fn syscall0(n: usize) -> isize { 11 | let ret: isize; 12 | unsafe { 13 | asm!("syscall", in("rax") n, lateout("rax") ret); 14 | } 15 | ret 16 | } 17 | 18 | #[no_mangle] 19 | #[inline(always)] 20 | #[allow(clippy::missing_safety_doc)] 21 | pub extern "C" fn syscall1(n: usize, a1: usize) -> isize { 22 | let ret: isize; 23 | unsafe { 24 | asm!("syscall", in("rax") n, in("rdi") a1, lateout("rax") ret); 25 | } 26 | ret 27 | } 28 | 29 | #[no_mangle] 30 | #[inline(always)] 31 | #[allow(clippy::missing_safety_doc)] 32 | pub extern "C" fn syscall2(n: usize, a1: usize, a2: usize) -> isize { 33 | let ret: isize; 34 | unsafe { 35 | asm!("syscall", in("rax") n, in("rdi") a1, in("rsi") a2, lateout("rax") ret); 36 | } 37 | ret 38 | } 39 | 40 | #[no_mangle] 41 | #[inline(always)] 42 | #[allow(clippy::missing_safety_doc)] 43 | pub extern "C" fn syscall3(n: usize, a1: usize, a2: usize, a3: usize) -> isize { 44 | let ret: isize; 45 | unsafe { 46 | asm!("syscall", in("rax") n, in("rdi") a1, in("rsi") a2, in("rdx") a3, lateout("rax") ret); 47 | } 48 | ret 49 | } 50 | 51 | #[no_mangle] 52 | #[inline(always)] 53 | #[allow(clippy::missing_safety_doc)] 54 | pub extern "C" fn syscall4(n: usize, a1: usize, a2: usize, a3: usize, a4: usize) -> isize { 55 | let ret: isize; 56 | unsafe { 57 | asm!("syscall", in("rax") n, in("rdi") a1, in("rsi") a2, in("rdx") a3, in("r10") a4, lateout("rax") ret); 58 | } 59 | ret 60 | } 61 | 62 | #[no_mangle] 63 | #[inline(always)] 64 | #[allow(clippy::missing_safety_doc)] 65 | pub extern "C" fn syscall5( 66 | n: usize, 67 | a1: usize, 68 | a2: usize, 69 | a3: usize, 70 | a4: usize, 71 | a5: usize, 72 | ) -> isize { 73 | let ret: isize; 74 | unsafe { 75 | asm!("syscall", in("rax") n, in("rdi") a1, in("rsi") a2, in("rdx") a3, in("r10") a4, in("r8") a5, lateout("rax") ret); 76 | } 77 | ret 78 | } 79 | 80 | #[no_mangle] 81 | #[inline(always)] 82 | #[allow(clippy::missing_safety_doc)] 83 | pub extern "C" fn syscall6( 84 | n: usize, 85 | a1: usize, 86 | a2: usize, 87 | a3: usize, 88 | a4: usize, 89 | a5: usize, 90 | a6: usize, 91 | ) -> isize { 92 | let ret: isize; 93 | unsafe { 94 | asm!("syscall", in("rax") n, in("rdi") a1, in("rsi") a2, in("rdx") a3, in("r10") a4, in("r8") a5, in("r9") a6, lateout("rax") ret); 95 | } 96 | ret 97 | } 98 | 99 | pub fn sys_arch_prctl(code: i32, address: usize) -> SyscallResult { 100 | syscall_result(syscall2(SYS_ARCH_PRCTL, code as usize, address)) 101 | } 102 | 103 | pub fn sys_set_tid_address(address: usize) -> SyscallResult { 104 | syscall_result(syscall1(SYS_SET_TID_ADDRESS, address)) 105 | } 106 | 107 | pub fn sys_write(fd: i32, address: usize, len: usize) -> SyscallResult { 108 | syscall_result(syscall3(SYS_WRITE, fd as usize, address, len)) 109 | } 110 | 111 | pub fn sys_writev(fd: i32, iov_base: usize, iov_count: usize) -> SyscallResult { 112 | syscall_result(syscall3(SYS_WRITEV, fd as usize, iov_base, iov_count)) 113 | } 114 | 115 | pub fn sys_read(fd: i32, address: usize, len: usize) -> SyscallResult { 116 | syscall_result(syscall3(SYS_READ, fd as usize, address, len)) 117 | } 118 | 119 | pub fn sys_fork() -> SyscallResult { 120 | syscall_result(syscall0(SYS_FORK)) 121 | } 122 | 123 | pub fn sys_wait4(pid: i32, status_addr: usize, options: i32, rusage_addr: usize) -> SyscallResult { 124 | syscall_result(syscall4( 125 | SYS_WAIT4, 126 | pid as usize, 127 | status_addr, 128 | options as usize, 129 | rusage_addr, 130 | )) 131 | } 132 | 133 | pub fn sys_execve(path_addr: usize, argv_addr: usize, envp_addr: usize) -> SyscallResult { 134 | syscall_result(syscall3(SYS_EXECVE, path_addr, argv_addr, envp_addr)) 135 | } 136 | 137 | pub fn sys_getcwd(buf_addr: usize, buf_len: usize) -> SyscallResult { 138 | syscall_result(syscall2(SYS_GETCWD, buf_addr, buf_len)) 139 | } 140 | -------------------------------------------------------------------------------- /src/fs/devfs/fb.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use spin::Once; 3 | 4 | use crate::{ 5 | fs::{initramfs::get_root, opened_file::OpenFlags, File, FsNode, INode}, 6 | graphics::fb, 7 | kerror, 8 | mem::addr::VirtAddr, 9 | userland::buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter}, 10 | util::KResult, 11 | }; 12 | 13 | pub static DEV_FB0: Once> = Once::new(); 14 | 15 | pub fn init() { 16 | let fb0 = Arc::new(FbDevice); 17 | get_root() 18 | .unwrap() 19 | .root_dir() 20 | .lookup("dev") 21 | .unwrap() 22 | .as_dir() 23 | .unwrap() 24 | .insert(INode::File(fb0.clone())); 25 | DEV_FB0.call_once(|| fb0); 26 | } 27 | 28 | pub struct FbDevice; 29 | 30 | impl FsNode for FbDevice { 31 | fn get_name(&self) -> alloc::string::String { 32 | "fb0".into() 33 | } 34 | } 35 | 36 | impl File for FbDevice { 37 | fn read(&self, offset: usize, buf: UserBufferMut, _options: &OpenFlags) -> KResult { 38 | assert_eq!(offset % 4, 0); 39 | let buf_len = buf.len(); 40 | assert_eq!(buf_len % 4, 0); 41 | let mut writer = UserBufferWriter::from_buf(buf); 42 | let mut fb = fb(); 43 | let mem = fb.frame_mut(); 44 | let start = offset / 4; 45 | let len = buf_len / 4; 46 | let end = (start + len).min(mem.len()); 47 | let mem = mem[start..end].iter().flat_map(|pixel| pixel.to_le_bytes()); 48 | for byte in mem { 49 | writer.write(byte)?; 50 | } 51 | Ok((end - start) * 4) 52 | } 53 | 54 | fn write(&self, offset: usize, buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult { 55 | assert_eq!(offset % 4, 0); 56 | let buf_len = buf.len(); 57 | assert_eq!(buf_len % 4, 0); 58 | let mut reader = UserBufferReader::from_buf(buf); 59 | let mut fb = fb(); 60 | let mem = fb.frame_mut(); 61 | let mut i = 0; 62 | while (offset / 4 + i) < mem.len() && i < buf_len / 4 { 63 | let pixel = reader.read::()?; 64 | // let pixel = u32::from_le_bytes([byte0, byte1, byte2, byte3]); 65 | mem[offset / 4 + i] = pixel; 66 | i += 1; 67 | } 68 | fb.present(); 69 | Ok(i * 4) 70 | } 71 | 72 | fn ioctl(&self, cmd: usize, arg: usize) -> KResult { 73 | const FBIOGET_VSCREENINFO: usize = 0x4600; 74 | // const FBIOGET_FSCREENINFO: usize = 0x4602; 75 | match cmd { 76 | FBIOGET_VSCREENINFO => { 77 | let fb = fb(); 78 | let info = FbVarScreenInfo { 79 | xres: fb.width() as u32, 80 | yres: fb.height() as u32, 81 | xres_virtual: fb.width() as u32, 82 | yres_virtual: fb.height() as u32, 83 | bpp: fb.bpp() as u32, 84 | ..FbVarScreenInfo::default() 85 | }; 86 | unsafe { VirtAddr::new(arg).write_user(info) }?; 87 | } 88 | // FBIOGET_FSCREENINFO => { 89 | 90 | // } 91 | _ => return Err(kerror!(EINVAL, "ioctl(): unknown cmd")), 92 | } 93 | Ok(0) 94 | } 95 | } 96 | 97 | #[repr(C)] 98 | #[derive(Clone, Copy, Default)] 99 | struct FbBitField { 100 | offset: u32, 101 | length: u32, 102 | msb_right: u32, 103 | } 104 | 105 | #[repr(C)] 106 | #[derive(Clone, Copy, Default)] 107 | struct FbVarScreenInfo { 108 | xres: u32, 109 | yres: u32, 110 | xres_virtual: u32, 111 | yres_virtual: u32, 112 | xoffset: u32, 113 | yoffset: u32, 114 | bpp: u32, 115 | grayscale: u32, 116 | red: FbBitField, 117 | green: FbBitField, 118 | blue: FbBitField, 119 | transp: FbBitField, 120 | nonstd: u32, 121 | activate: u32, 122 | height_mm: u32, 123 | width_mm: u32, 124 | accel_flags: u32, 125 | pixclock: u32, 126 | left_margin: u32, 127 | right_margin: u32, 128 | upper_margin: u32, 129 | lower_margin: u32, 130 | hsync_len: u32, 131 | vsync_len: u32, 132 | sync: u32, 133 | vmode: u32, 134 | rotate: u32, 135 | colorspace: u32, 136 | reserved: [u32; 4], 137 | } 138 | -------------------------------------------------------------------------------- /src/fs/devfs/socket.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | use crate::{ 4 | fs::{File, FsNode}, 5 | kerror, 6 | mem::addr::VirtAddr, 7 | util::{KError, KResult}, 8 | }; 9 | 10 | pub fn init() {} 11 | 12 | #[repr(C)] 13 | #[non_exhaustive] 14 | pub enum Domain { 15 | Unix = 0, 16 | Inet = 2, 17 | } 18 | 19 | impl TryFrom for Domain { 20 | type Error = KError<'static>; 21 | fn try_from(value: usize) -> Result { 22 | match value { 23 | 0 => Ok(Self::Unix), 24 | 2 => Ok(Self::Inet), 25 | _ => Err(kerror!(EINVAL, "invalid socket domain")), 26 | } 27 | } 28 | } 29 | 30 | #[repr(C)] 31 | #[non_exhaustive] 32 | pub enum SocketType { 33 | Stream = 1, 34 | Datagram = 2, 35 | Raw = 3, 36 | SeqPacket = 5, 37 | Packet = 10, 38 | } 39 | 40 | impl TryFrom for SocketType { 41 | type Error = KError<'static>; 42 | fn try_from(value: usize) -> Result { 43 | match value { 44 | 1 => Ok(Self::Stream), 45 | 2 => Ok(Self::Datagram), 46 | 3 => Ok(Self::Raw), 47 | 5 => Ok(Self::SeqPacket), 48 | 10 => Ok(Self::Packet), 49 | _ => Err(kerror!(EINVAL, "invalid socket type")), 50 | } 51 | } 52 | } 53 | 54 | #[repr(C)] 55 | #[non_exhaustive] 56 | pub enum Protocol { 57 | Ipv4 = 4, 58 | Tcp = 6, 59 | Udp = 17, 60 | } 61 | 62 | impl TryFrom for Protocol { 63 | type Error = KError<'static>; 64 | fn try_from(value: usize) -> Result { 65 | match value { 66 | 4 => Ok(Self::Ipv4), 67 | 6 => Ok(Self::Tcp), 68 | 17 => Ok(Self::Udp), 69 | _ => Err(kerror!(EINVAL, "invalid socket protocol")), 70 | } 71 | } 72 | } 73 | 74 | pub struct Socket { 75 | pub id: usize, 76 | // pub handle: SocketHandle, 77 | pub domain: Domain, 78 | pub typ: SocketType, 79 | pub protocol: Protocol, 80 | } 81 | 82 | static NEXT_ID: AtomicUsize = AtomicUsize::new(0); 83 | 84 | impl Socket { 85 | pub fn alloc_id() -> usize { 86 | NEXT_ID.fetch_add(1, Ordering::SeqCst) 87 | } 88 | 89 | // pub fn new() -> Self { 90 | // Self { 91 | // id: Self::alloc_id(), 92 | // handle: (), 93 | // domain: (), 94 | // typ: (), 95 | // protocol: (), 96 | // } 97 | // } 98 | } 99 | 100 | impl FsNode for Socket { 101 | fn get_name(&self) -> alloc::string::String { 102 | alloc::format!("socket{}", self.id) 103 | } 104 | } 105 | 106 | #[derive(Clone, Copy)] 107 | #[repr(C, packed)] 108 | pub struct SockAddrInet { 109 | family: u16, 110 | port: [u8; 2], 111 | addr: [u8; 4], 112 | zero: [u8; 8], 113 | } 114 | 115 | pub fn read_sockaddr(addr: VirtAddr, len: usize) -> KResult { 116 | let family = unsafe { addr.read_volatile::()? }; 117 | let sockaddr = match Domain::try_from(family as usize)? { 118 | Domain::Inet => { 119 | if len < core::mem::size_of::() { 120 | return Err(kerror!(EINVAL, "read_sockaddr(): buffer overflow")); 121 | } 122 | 123 | unsafe { addr.read_volatile::()? } 124 | } 125 | Domain::Unix => { 126 | todo!() 127 | } 128 | }; 129 | Ok(sockaddr) 130 | } 131 | 132 | pub fn write_sockaddr( 133 | sockaddr: SockAddrInet, 134 | dst: Option, 135 | socklen: Option, 136 | ) -> KResult<()> { 137 | if let Some(dst) = dst { 138 | unsafe { dst.write_volatile(sockaddr) }?; 139 | } 140 | if let Some(socklen) = socklen { 141 | unsafe { socklen.write_volatile(core::mem::size_of::() as u32) }?; 142 | } 143 | Ok(()) 144 | } 145 | 146 | impl File for Socket { 147 | fn ioctl(&self, cmd: usize, _arg: usize) -> KResult { 148 | const FIONBIO: usize = 0x5421; 149 | match cmd { 150 | FIONBIO => { 151 | // todo: set/clear non block flag 152 | } 153 | _ => return Err(kerror!(EINVAL, "ioctl(): unknown cmd for socket")), 154 | } 155 | Ok(0) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/serial.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | 3 | use uart_16550::SerialPort; 4 | use x86::io::inb; 5 | 6 | use crate::util::lock::IrqMutex; 7 | 8 | pub const SERIAL0_IOPORT: u16 = 0x3f8; 9 | pub const SERIAL1_IOPORT: u16 = 0x2f8; 10 | pub const SERIAL2_IOPORT: u16 = 0x3e8; 11 | 12 | lazy_static! { 13 | pub static ref SERIAL0: IrqMutex = { 14 | let mut serial_port = unsafe { SerialPort::new(SERIAL0_IOPORT) }; 15 | serial_port.init(); 16 | 17 | IrqMutex::new(serial_port) 18 | }; 19 | pub static ref SERIAL1: IrqMutex = { 20 | let mut serial_port = unsafe { SerialPort::new(SERIAL1_IOPORT) }; 21 | serial_port.init(); 22 | IrqMutex::new(serial_port) 23 | }; 24 | pub static ref SERIAL2: IrqMutex = { 25 | let mut serial_port = unsafe { SerialPort::new(SERIAL2_IOPORT) }; 26 | serial_port.init(); 27 | IrqMutex::new(serial_port) 28 | }; 29 | } 30 | 31 | #[doc(hidden)] 32 | #[allow(unreachable_code)] 33 | pub fn _print0(args: ::core::fmt::Arguments) { 34 | use core::fmt::Write; 35 | // #[cfg(debug_assertions)] 36 | x86_64::instructions::interrupts::without_interrupts(|| { 37 | SERIAL0 38 | .lock() 39 | .write_fmt(args) 40 | .expect("Printing to serial failed"); 41 | }); 42 | } 43 | 44 | /// Prints to the host through the logging serial interface. 45 | #[macro_export] 46 | macro_rules! serial0_print { 47 | ($($arg:tt)*) => { 48 | $crate::serial::_print0(format_args!($($arg)*)) 49 | }; 50 | } 51 | 52 | /// Prints to the host through the logging serial interface, appending a newline. 53 | #[macro_export] 54 | macro_rules! serial0_println { 55 | () => ($crate::serial0_print!("\n")); 56 | ($fmt:expr) => ($crate::serial0_print!(concat!($fmt, "\n"))); 57 | ($fmt:expr, $($arg:tt)*) => ($crate::serial0_print!( 58 | concat!($fmt, "\n"), $($arg)*)); 59 | } 60 | 61 | #[doc(hidden)] 62 | #[allow(unreachable_code)] 63 | pub fn _print1(args: ::core::fmt::Arguments) { 64 | use core::fmt::Write; 65 | // #[cfg(debug_assertions)] 66 | x86_64::instructions::interrupts::without_interrupts(|| { 67 | SERIAL1 68 | .lock() 69 | .write_fmt(args) 70 | .expect("Printing to serial failed"); 71 | }); 72 | } 73 | 74 | #[inline] 75 | pub fn serial1_recv() -> Option { 76 | // #[cfg(debug_assertions)] 77 | unsafe { 78 | let line_sts = inb(SERIAL1_IOPORT + 5); 79 | if line_sts & 0x1 != 0 { 80 | return Some(inb(SERIAL1_IOPORT)); 81 | } 82 | None 83 | } 84 | // #[cfg(not(debug_assertions))] 85 | // None 86 | 87 | // }) 88 | } 89 | 90 | /// Prints to the host through the user serial interface. 91 | #[macro_export] 92 | macro_rules! serial1_print { 93 | ($($arg:tt)*) => { 94 | $crate::serial::_print1(format_args!($($arg)*)) 95 | }; 96 | } 97 | 98 | /// Prints to the host through the user serial interface, appending a newline. 99 | #[macro_export] 100 | macro_rules! serial1_println { 101 | () => ($crate::serial1_print!("\r\n")); 102 | ($fmt:expr) => ($crate::serial1_print!(concat!($fmt, "\r\n"))); 103 | ($fmt:expr, $($arg:tt)*) => ($crate::serial1_print!( 104 | concat!($fmt, "\r\n"), $($arg)*)); 105 | } 106 | 107 | #[doc(hidden)] 108 | #[allow(unreachable_code)] 109 | pub fn _print2(args: ::core::fmt::Arguments) { 110 | use core::fmt::Write; 111 | // #[cfg(debug_assertions)] 112 | x86_64::instructions::interrupts::without_interrupts(|| { 113 | SERIAL2 114 | .lock() 115 | .write_fmt(args) 116 | .expect("Printing to serial failed"); 117 | }); 118 | } 119 | 120 | /// Prints to the host through the TTY logging serial interface. 121 | #[macro_export] 122 | macro_rules! serial2_print { 123 | ($($arg:tt)*) => { 124 | $crate::serial::_print2(format_args!($($arg)*)) 125 | }; 126 | } 127 | 128 | /// Prints to the host through the TTY logging serial interface, appending a newline. 129 | #[macro_export] 130 | macro_rules! serial2_println { 131 | () => ($crate::serial2_print!("\n")); 132 | ($fmt:expr) => ($crate::serial2_print!(concat!($fmt, "\n"))); 133 | ($fmt:expr, $($arg:tt)*) => ($crate::serial2_print!( 134 | concat!($fmt, "\n"), $($arg)*)); 135 | } 136 | -------------------------------------------------------------------------------- /src/arch/x86_64/syscall.rs: -------------------------------------------------------------------------------- 1 | use core::mem::offset_of; 2 | 3 | use x86::msr::{rdmsr, wrmsr}; 4 | 5 | use crate::mem::kernel_addr_space_scope; 6 | use crate::userland::syscall::{ 7 | QUIET_SYSCALLS, SyscallHandler, errno_to_isize, syscall_name_by_number, 8 | }; 9 | 10 | use super::gdt::{KERNEL_CS_IDX, USER_DS_IDX}; 11 | use super::idt::InterruptFrame; 12 | 13 | #[macro_export] 14 | macro_rules! push_regs { 15 | () => { 16 | " 17 | // push scratch regs 18 | push rcx 19 | push rdx 20 | push rdi 21 | push rsi 22 | push r8 23 | push r9 24 | push r10 25 | push r11 26 | 27 | // push preserved regs 28 | push rbx 29 | push rbp 30 | push r12 31 | push r13 32 | push r14 33 | push r15 34 | " 35 | }; 36 | } 37 | 38 | #[macro_export] 39 | macro_rules! pop_regs { 40 | () => { 41 | " 42 | // pop preserved regs 43 | pop r15 44 | pop r14 45 | pop r13 46 | pop r12 47 | pop rbp 48 | pop rbx 49 | 50 | // pop scratch regs 51 | pop r11 52 | pop r10 53 | pop r9 54 | pop r8 55 | pop rsi 56 | pop rdi 57 | pop rdx 58 | pop rcx 59 | 60 | pop rax 61 | " 62 | }; 63 | } 64 | 65 | #[unsafe(naked)] 66 | pub unsafe extern "C" fn syscall_entry() { 67 | use x86_64::structures::tss::TaskStateSegment; 68 | { 69 | core::arch::naked_asm!( 70 | concat!( 71 | " 72 | cli 73 | swapgs 74 | mov gs:[{off} + {sp}], rsp 75 | mov rsp, gs:[{off} + {ksp}] 76 | push qword ptr {ss_sel} 77 | push qword ptr gs:[{off} + {sp}] 78 | push r11 79 | push qword ptr {cs_sel} 80 | push rcx 81 | 82 | push rax 83 | ", 84 | push_regs!(), 85 | " 86 | mov rdi, rsp 87 | cld 88 | call x64_handle_syscall 89 | cli 90 | ", 91 | pop_regs!(), 92 | " 93 | test dword ptr [rsp + 4], 0xFFFF8000 94 | jnz 2f 95 | 96 | pop rcx 97 | add rsp, 8 98 | pop r11 99 | pop qword ptr gs:[{off} + {sp}] 100 | mov rsp, gs:[{off} + {sp}] 101 | // pop rsp 102 | cli 103 | swapgs 104 | sysretq 105 | 2: 106 | xor rcx, rcx 107 | xor r11, r11 108 | cli 109 | swapgs 110 | iretq 111 | " 112 | ), 113 | off = const(0), 114 | sp = const(offset_of!(crate::arch::cpu_local::Kpcr, user_rsp0_tmp)), 115 | ksp = const(offset_of!(TaskStateSegment, privilege_stack_table)), 116 | ss_sel = const((crate::arch::gdt::USER_DS_IDX << 3) | 3), 117 | cs_sel = const((crate::arch::gdt::USER_CS_IDX << 3) | 3), 118 | ) 119 | } 120 | } 121 | 122 | #[unsafe(no_mangle)] 123 | unsafe extern "C" fn x64_handle_syscall(ctx: *mut InterruptFrame) -> isize { 124 | let context = unsafe { core::ptr::read(ctx) }; 125 | handle_syscall( 126 | context.rdi, 127 | context.rsi, 128 | context.rdx, 129 | context.r10, 130 | context.r8, 131 | context.r9, 132 | context.rax, 133 | ctx, 134 | ) 135 | } 136 | 137 | #[allow(clippy::too_many_arguments)] 138 | fn handle_syscall( 139 | a1: usize, 140 | a2: usize, 141 | a3: usize, 142 | a4: usize, 143 | a5: usize, 144 | a6: usize, 145 | n: usize, 146 | frame: *mut InterruptFrame, 147 | ) -> isize { 148 | let mut handler = SyscallHandler { 149 | frame: unsafe { &mut *frame }, 150 | }; 151 | 152 | let guard = kernel_addr_space_scope().unwrap(); 153 | let res = handler.dispatch(a1, a2, a3, a4, a5, a6, n); 154 | drop(guard); 155 | 156 | if let Err(ref err) = res { 157 | if !QUIET_SYSCALLS.contains(&n) { 158 | log::error!( 159 | "Syscall handler for `{}` returned Err: {}", 160 | syscall_name_by_number(n), 161 | err 162 | ); 163 | } 164 | } 165 | let retval = errno_to_isize(&res); 166 | handler.frame.rax = retval as usize; 167 | retval 168 | } 169 | 170 | /// # Safety 171 | /// This writes several MSR registers. 172 | pub unsafe fn init() { 173 | let kernel_cs_offset = (KERNEL_CS_IDX as u64) << 3; 174 | let user_ds_offset = (USER_DS_IDX as u64) << 3; 175 | let mut star = 0u64; 176 | star |= (user_ds_offset - 8) << 48; 177 | star |= kernel_cs_offset << 32; 178 | unsafe { 179 | wrmsr(x86::msr::IA32_STAR, star); 180 | wrmsr(x86::msr::IA32_LSTAR, syscall_entry as *const u8 as u64); 181 | wrmsr(x86::msr::IA32_FMASK, 0x200); 182 | 183 | wrmsr(x86::msr::IA32_CSTAR, 0); 184 | 185 | wrmsr(x86::msr::IA32_EFER, rdmsr(x86::msr::IA32_EFER) | 1); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/task/signal.rs: -------------------------------------------------------------------------------- 1 | use bitvec::prelude::*; 2 | 3 | use crate::{ 4 | kbail, 5 | util::{ctypes::c_int, error::KResult}, 6 | }; 7 | 8 | pub type Signal = c_int; 9 | #[allow(unused)] 10 | pub const SIGHUP: Signal = 1; 11 | #[allow(unused)] 12 | pub const SIGINT: Signal = 2; 13 | #[allow(unused)] 14 | pub const SIGQUIT: Signal = 3; 15 | #[allow(unused)] 16 | pub const SIGILL: Signal = 4; 17 | #[allow(unused)] 18 | pub const SIGTRAP: Signal = 5; 19 | #[allow(unused)] 20 | pub const SIGABRT: Signal = 6; 21 | #[allow(unused)] 22 | pub const SIGBUS: Signal = 7; 23 | #[allow(unused)] 24 | pub const SIGFPE: Signal = 8; 25 | #[allow(unused)] 26 | pub const SIGKILL: Signal = 9; 27 | #[allow(unused)] 28 | pub const SIGUSR1: Signal = 10; 29 | #[allow(unused)] 30 | pub const SIGSEGV: Signal = 11; 31 | #[allow(unused)] 32 | pub const SIGUSR2: Signal = 12; 33 | #[allow(unused)] 34 | pub const SIGPIPE: Signal = 13; 35 | #[allow(unused)] 36 | pub const SIGALRM: Signal = 14; 37 | #[allow(unused)] 38 | pub const SIGTERM: Signal = 15; 39 | #[allow(unused)] 40 | pub const SIGSTKFLT: Signal = 16; 41 | #[allow(unused)] 42 | pub const SIGCHLD: Signal = 17; 43 | #[allow(unused)] 44 | pub const SIGCONT: Signal = 18; 45 | #[allow(unused)] 46 | pub const SIGSTOP: Signal = 19; 47 | #[allow(unused)] 48 | pub const SIGTSTP: Signal = 20; 49 | #[allow(unused)] 50 | pub const SIGTTIN: Signal = 21; 51 | #[allow(unused)] 52 | pub const SIGTTOU: Signal = 22; 53 | #[allow(unused)] 54 | pub const SIGURG: Signal = 23; 55 | #[allow(unused)] 56 | pub const SIGXCPU: Signal = 24; 57 | #[allow(unused)] 58 | pub const SIGXFSZ: Signal = 25; 59 | #[allow(unused)] 60 | pub const SIGVTALRM: Signal = 26; 61 | #[allow(unused)] 62 | pub const SIGPROF: Signal = 27; 63 | #[allow(unused)] 64 | pub const SIGWINCH: Signal = 28; 65 | #[allow(unused)] 66 | pub const SIGIO: Signal = 29; 67 | #[allow(unused)] 68 | pub const SIGPWR: Signal = 30; 69 | #[allow(unused)] 70 | pub const SIGSYS: Signal = 31; 71 | 72 | const SIGMAX: c_int = 32; 73 | 74 | pub const SIG_DFL: usize = 0; 75 | pub const SIG_IGN: usize = 1; 76 | pub const SIG_ERR: usize = usize::MAX; 77 | 78 | #[derive(Clone, Copy, PartialEq, Eq)] 79 | pub enum SigAction { 80 | Ignore, 81 | Terminate, 82 | Handler { handler: fn() }, 83 | } 84 | 85 | pub const DEFAULT_ACTIONS: [SigAction; SIGMAX as usize] = [ 86 | /* (unused) */ SigAction::Ignore, 87 | /* SIGHUP */ SigAction::Terminate, 88 | /* SIGINT */ SigAction::Terminate, 89 | /* SIGQUIT */ SigAction::Terminate, 90 | /* SIGILL */ SigAction::Terminate, 91 | /* SIGTRAP */ SigAction::Ignore, 92 | /* SIGABRT */ SigAction::Terminate, 93 | /* SIGBUS */ SigAction::Terminate, 94 | /* SIGFPE */ SigAction::Terminate, 95 | /* SIGKILL */ SigAction::Terminate, 96 | /* SIGUSR1 */ SigAction::Ignore, 97 | /* SIGSEGV */ SigAction::Terminate, 98 | /* SIGUSR2 */ SigAction::Ignore, 99 | /* SIGPIPE */ SigAction::Ignore, 100 | /* SIGALRM */ SigAction::Ignore, 101 | /* SIGTERM */ SigAction::Terminate, 102 | /* SIGSTKFLT */ SigAction::Ignore, 103 | /* SIGCHLD */ SigAction::Ignore, 104 | /* SIGCONT */ SigAction::Terminate, 105 | /* SIGSTOP */ SigAction::Ignore, 106 | /* SIGTSTP */ SigAction::Ignore, 107 | /* SIGTTIN */ SigAction::Ignore, 108 | /* SIGTTOU */ SigAction::Ignore, 109 | /* SIGURG */ SigAction::Ignore, 110 | /* SIGXCPU */ SigAction::Ignore, 111 | /* SIGXFSZ */ SigAction::Ignore, 112 | /* SIGVTALRM */ SigAction::Ignore, 113 | /* SIGPROF */ SigAction::Ignore, 114 | /* SIGWINCH */ SigAction::Ignore, 115 | /* SIGIO */ SigAction::Ignore, 116 | /* SIGPWR */ SigAction::Ignore, 117 | /* SIGSYS */ SigAction::Ignore, 118 | ]; 119 | 120 | #[derive(Clone)] 121 | pub struct SignalDelivery { 122 | pending: u32, 123 | actions: [SigAction; SIGMAX as usize], 124 | } 125 | 126 | impl Default for SignalDelivery { 127 | fn default() -> Self { 128 | Self::new() 129 | } 130 | } 131 | 132 | impl SignalDelivery { 133 | pub fn new() -> SignalDelivery { 134 | SignalDelivery { 135 | pending: 0, 136 | actions: DEFAULT_ACTIONS, 137 | } 138 | } 139 | 140 | pub fn get_action(&self, signal: Signal) -> SigAction { 141 | self.actions[signal as usize] 142 | } 143 | 144 | pub fn set_action(&mut self, signal: Signal, action: SigAction) -> KResult<()> { 145 | if signal > SIGMAX { 146 | kbail!(EINVAL, "set_action(): signal out of range"); 147 | } 148 | 149 | self.actions[signal as usize] = action; 150 | Ok(()) 151 | } 152 | 153 | pub fn is_pending(&self) -> bool { 154 | self.pending != 0 155 | } 156 | 157 | pub fn signal(&mut self, signal: Signal) { 158 | self.pending |= 1 << signal 159 | } 160 | 161 | pub fn pop_pending(&mut self) -> Option<(Signal, SigAction)> { 162 | if self.pending == 0 { 163 | return None; 164 | } 165 | 166 | let signal = self.pending.trailing_zeros(); 167 | self.pending &= !(1 << signal); 168 | Some((signal as Signal, self.actions[signal as usize])) 169 | } 170 | } 171 | 172 | pub type SigSet = BitArray<[u8; 8], LocalBits>; 173 | 174 | #[derive(Clone, Copy, Debug)] 175 | pub enum SignalMask { 176 | Block, 177 | Unblock, 178 | Set, 179 | } 180 | -------------------------------------------------------------------------------- /src/god_mode.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::ToOwned, collections::VecDeque, string::String, sync::Arc}; 2 | use spin::Once; 3 | use x86_64::instructions::interrupts; 4 | 5 | use crate::{ 6 | mem::{ 7 | addr::PhysAddr, 8 | allocator::{GLOBAL_ALLOC, KERNEL_FRAME_ALLOCATOR}, 9 | consts::{KERNEL_HEAP_SIZE, PAGE_SIZE}, 10 | }, 11 | task::{get_scheduler, Task, TaskId}, 12 | util::{align_down, BlockingMutex}, 13 | }; 14 | 15 | pub static GOD_MODE_TASK: Once> = Once::new(); 16 | 17 | pub static GOD_MODE_FIFO: Once>> = Once::new(); 18 | 19 | pub fn init() { 20 | GOD_MODE_FIFO.call_once(|| BlockingMutex::new(VecDeque::new())); 21 | let sched = get_scheduler(); 22 | GOD_MODE_TASK.call_once(|| Task::new_kernel(sched, god_mode_repl, true)); 23 | sched.push_runnable(GOD_MODE_TASK.get().unwrap().clone(), false); 24 | } 25 | 26 | fn read_cmd() -> String { 27 | let mut cmd = String::new(); 28 | loop { 29 | let mut lock = GOD_MODE_FIFO.get().unwrap().try_lock(); 30 | while let Ok(Some(ch)) = lock.as_mut().map(|lock| lock.pop_front()) { 31 | if ch == b'\n' || ch == b'\r' { 32 | drop(lock); 33 | return cmd; 34 | } 35 | let st = core::str::from_utf8(&[ch]).unwrap().to_owned(); 36 | cmd.push_str(&st); 37 | serial1_print!("{}", st); 38 | } 39 | drop(lock); 40 | interrupts::enable_and_hlt(); 41 | } 42 | } 43 | 44 | pub fn god_mode_repl() { 45 | loop { 46 | serial1_print!("\ngodmode > "); 47 | let cmd = read_cmd(); 48 | let mut args = cmd.split_whitespace(); 49 | let cmd = if let Some(cmd) = args.next() { 50 | cmd 51 | } else { 52 | continue; 53 | }; 54 | 55 | serial1_println!(); 56 | log::warn!("God said: {}", cmd); 57 | match cmd { 58 | "f" | "frames" => { 59 | serial1_println!("Dumping free physical memory."); 60 | let fa = KERNEL_FRAME_ALLOCATOR.get().unwrap().lock(); 61 | let mut total_space_pages = 0; 62 | for area in fa.free_regions() { 63 | total_space_pages += area.size_in_pages(); 64 | serial1_println!("Free chunk at {:?}", area); 65 | } 66 | serial1_println!("Total free pages: {}", total_space_pages); 67 | serial1_println!("Total free bytes: {}", total_space_pages * PAGE_SIZE); 68 | } 69 | "vm" | "vmem" => { 70 | let pid = if let Some(Ok(pid)) = args.next().map(|arg| arg.parse()) { 71 | TaskId::new(pid) 72 | } else { 73 | serial1_println!("Invalid argument. Specify a PID to inspect vmem of."); 74 | continue; 75 | }; 76 | let sched = get_scheduler(); 77 | let task = if let Some(task) = sched.find_task(pid) { 78 | task 79 | } else { 80 | serial1_println!("Invalid argument. PID not found."); 81 | continue; 82 | }; 83 | serial1_println!( 84 | "Dumping virtual memory of pid {}. Check serial0 (stdio).", 85 | pid.as_usize() 86 | ); 87 | task.vmem().lock().log(); 88 | } 89 | "x" | "examine" => { 90 | let start = if let Some(Ok(start)) = 91 | args.next().map(|arg| usize::from_str_radix(arg, 16)) 92 | { 93 | align_down(start, PAGE_SIZE) 94 | } else { 95 | serial1_println!("Invalid argument. Specify the address to dump the frame of."); 96 | continue; 97 | }; 98 | 99 | let ptr = PhysAddr::new(start).as_hhdm_virt().as_raw_ptr::(); 100 | 101 | let max_i = PAGE_SIZE / core::mem::size_of::(); 102 | serial1_println!("Dumping frame at {:#x}.", start); 103 | for i in 0..max_i / 4 { 104 | let i = i * 4; 105 | serial1_print!("{:#016x} >> ", start + i * core::mem::size_of::()); 106 | for j in 0..4 { 107 | let offset = i + j; 108 | serial1_print!("{:016x} ", unsafe { ptr.add(offset).read_volatile() }); 109 | } 110 | serial1_println!(); 111 | } 112 | } 113 | "h" | "heap" => { 114 | let lock = GLOBAL_ALLOC.try_lock(); 115 | if let Some(lock) = lock { 116 | serial1_println!("Kernel heap size: {:#08x}", KERNEL_HEAP_SIZE); 117 | serial1_println!( 118 | "Kernel heap usage (actual): {:#08x} ({:.4}%)", 119 | lock.stats_alloc_actual(), 120 | lock.stats_alloc_actual() as f64 / KERNEL_HEAP_SIZE as f64 * 100.0 121 | ); 122 | serial1_println!( 123 | "Kernel heap usage (user): {:#08x} ({:.4}%)", 124 | lock.stats_alloc_user(), 125 | lock.stats_alloc_user() as f64 / KERNEL_HEAP_SIZE as f64 * 100.0 126 | ); 127 | } else { 128 | serial1_println!("Error locking global allocator."); 129 | } 130 | } 131 | _ => {} 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/util/lock.rs: -------------------------------------------------------------------------------- 1 | use core::mem::ManuallyDrop; 2 | use core::ops::{Deref, DerefMut}; 3 | 4 | use spin::mutex::{SpinMutex, SpinMutexGuard}; 5 | use x86::current::rflags::{self, RFlags}; 6 | use x86_64::instructions::interrupts; 7 | 8 | use crate::backtrace; 9 | use crate::task::wait_queue::WaitQueue; 10 | 11 | use super::error::KResult; 12 | 13 | pub struct SavedInterruptStatus { 14 | rflags: RFlags, 15 | } 16 | 17 | impl SavedInterruptStatus { 18 | pub fn save() -> SavedInterruptStatus { 19 | SavedInterruptStatus { 20 | rflags: rflags::read(), 21 | } 22 | } 23 | } 24 | 25 | impl Drop for SavedInterruptStatus { 26 | fn drop(&mut self) { 27 | rflags::set(rflags::read() | (self.rflags & rflags::RFlags::FLAGS_IF)); 28 | } 29 | } 30 | 31 | pub struct BlockingMutex { 32 | queue: WaitQueue, 33 | inner: IrqMutex, 34 | } 35 | 36 | impl BlockingMutex { 37 | pub const fn new(value: T) -> BlockingMutex { 38 | BlockingMutex { 39 | queue: WaitQueue::new(), 40 | inner: IrqMutex::new(value), 41 | } 42 | } 43 | } 44 | 45 | impl BlockingMutex { 46 | pub fn get_mut(&mut self) -> &mut T { 47 | self.inner.get_mut() 48 | } 49 | 50 | pub fn try_lock(&self) -> KResult> { 51 | if self.inner.is_locked() { 52 | Err(kerror!("Cannot relock BlockingMutex")) // todo: more verbose error message 53 | } else { 54 | Ok(BlockingMutexGuard { 55 | inner: ManuallyDrop::new(self.inner.lock()), 56 | }) 57 | } 58 | } 59 | 60 | pub fn lock(&self) -> KResult> { 61 | let guard = self.queue.sleep_signalable_until(None, || { 62 | if let Ok(guard) = self.inner.try_lock() { 63 | Ok(Some(BlockingMutexGuard { 64 | inner: ManuallyDrop::new(guard), 65 | })) 66 | } else { 67 | Ok(None) 68 | } 69 | })?; 70 | Ok(guard) 71 | } 72 | 73 | pub fn is_locked(&self) -> bool { 74 | self.inner.is_locked() 75 | } 76 | 77 | /// # Safety 78 | /// See `spin::SpinMutex::force_unlock()` 79 | pub unsafe fn force_unlock(&self) { 80 | unsafe { self.inner.force_unlock() }; 81 | } 82 | } 83 | 84 | unsafe impl Sync for BlockingMutex {} 85 | unsafe impl Send for BlockingMutex {} 86 | 87 | pub struct BlockingMutexGuard<'a, T: ?Sized> { 88 | inner: ManuallyDrop>, 89 | } 90 | 91 | impl Drop for BlockingMutexGuard<'_, T> { 92 | fn drop(&mut self) { 93 | unsafe { 94 | ManuallyDrop::drop(&mut self.inner); 95 | } 96 | } 97 | } 98 | 99 | impl Deref for BlockingMutexGuard<'_, T> { 100 | type Target = T; 101 | fn deref(&self) -> &T { 102 | &self.inner 103 | } 104 | } 105 | 106 | impl DerefMut for BlockingMutexGuard<'_, T> { 107 | fn deref_mut(&mut self) -> &mut T { 108 | &mut self.inner 109 | } 110 | } 111 | 112 | pub struct IrqMutex { 113 | inner: SpinMutex, 114 | } 115 | 116 | impl IrqMutex { 117 | pub const fn new(value: T) -> IrqMutex { 118 | IrqMutex { 119 | inner: SpinMutex::new(value), 120 | } 121 | } 122 | } 123 | 124 | impl IrqMutex { 125 | pub fn get_mut(&mut self) -> &mut T { 126 | self.inner.get_mut() 127 | } 128 | 129 | pub fn try_lock(&self) -> KResult> { 130 | if self.inner.is_locked() { 131 | Err(kerror!("Cannot relock IrqMutex")) // todo: more verbose error message 132 | } else { 133 | Ok(self.lock()) 134 | } 135 | } 136 | 137 | pub fn lock(&self) -> IrqMutexGuard<'_, T> { 138 | if self.inner.is_locked() { 139 | serial0_println!( 140 | "WARNING: Tried to relock IrqMutex of {}", 141 | core::any::type_name::() 142 | ); 143 | backtrace::unwind_stack().ok(); 144 | } 145 | 146 | let saved_intr_status = SavedInterruptStatus::save(); 147 | interrupts::disable(); 148 | 149 | let guard = self.inner.lock(); 150 | 151 | IrqMutexGuard { 152 | inner: ManuallyDrop::new(guard), 153 | saved_intr_status: ManuallyDrop::new(saved_intr_status), 154 | } 155 | } 156 | 157 | pub fn is_locked(&self) -> bool { 158 | self.inner.is_locked() 159 | } 160 | 161 | /// # Safety 162 | /// See `spin::SpinMutex::force_unlock()` 163 | pub unsafe fn force_unlock(&self) { 164 | unsafe { self.inner.force_unlock() }; 165 | } 166 | } 167 | 168 | unsafe impl Sync for IrqMutex {} 169 | unsafe impl Send for IrqMutex {} 170 | 171 | pub struct IrqMutexGuard<'a, T: ?Sized> { 172 | inner: ManuallyDrop>, 173 | saved_intr_status: ManuallyDrop, 174 | } 175 | 176 | impl Drop for IrqMutexGuard<'_, T> { 177 | fn drop(&mut self) { 178 | unsafe { 179 | ManuallyDrop::drop(&mut self.inner); 180 | } 181 | 182 | unsafe { 183 | ManuallyDrop::drop(&mut self.saved_intr_status); 184 | } 185 | } 186 | } 187 | 188 | impl Deref for IrqMutexGuard<'_, T> { 189 | type Target = T; 190 | fn deref(&self) -> &T { 191 | &self.inner 192 | } 193 | } 194 | 195 | impl DerefMut for IrqMutexGuard<'_, T> { 196 | fn deref_mut(&mut self) -> &mut T { 197 | &mut self.inner 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/mem/paging/table.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::Debug, 3 | ops::{Index, IndexMut}, 4 | }; 5 | 6 | use x86_64::{registers::control::Cr3, structures::paging::PageTableFlags}; 7 | 8 | use crate::{ 9 | kbail, 10 | mem::{ 11 | addr::{PhysAddr, VirtAddr}, 12 | allocator::alloc_kernel_frames, 13 | consts::{PAGE_SIZE, PAGE_TABLE_ENTRIES}, 14 | }, 15 | util::KResult, 16 | }; 17 | 18 | use super::units::{Frame, MemoryUnit}; 19 | 20 | fn frame_to_table(frame: Frame) -> *mut PageTable { 21 | let virt = crate::phys_offset() + frame.start_address().value(); 22 | virt.as_raw_ptr_mut() 23 | } 24 | 25 | #[derive(Clone, Copy)] 26 | #[repr(transparent)] 27 | pub struct PageTableEntry { 28 | data: usize, 29 | } 30 | 31 | impl PageTableEntry { 32 | const ADDRESS_MASK: usize = 0x000f_ffff_ffff_f000; 33 | const FLAGS_MASK: usize = 0x01ff; 34 | 35 | #[allow(clippy::new_without_default)] 36 | pub const fn new() -> Self { 37 | PageTableEntry { data: 0 } 38 | } 39 | 40 | pub const fn is_unused(&self) -> bool { 41 | self.data == 0 42 | } 43 | 44 | pub fn set_unused(&mut self) { 45 | self.data = 0 46 | } 47 | 48 | pub const fn flags(&self) -> PageTableFlags { 49 | PageTableFlags::from_bits_truncate(self.data as u64) 50 | } 51 | 52 | pub fn addr(&self) -> PhysAddr { 53 | PhysAddr::new(self.data & Self::ADDRESS_MASK) 54 | } 55 | 56 | pub fn frame(&self) -> Option { 57 | if !self.flags().contains(PageTableFlags::PRESENT) 58 | || self.flags().contains(PageTableFlags::HUGE_PAGE) 59 | { 60 | None 61 | } else { 62 | Some(Frame::containing_address(self.addr())) 63 | } 64 | } 65 | 66 | pub fn set_addr(&mut self, addr: PhysAddr, flags: PageTableFlags) { 67 | assert!(addr.is_aligned(PAGE_SIZE)); 68 | 69 | self.data = addr.value() | flags.bits() as usize; 70 | } 71 | 72 | pub fn set_frame(&mut self, frame: Frame, flags: PageTableFlags) { 73 | self.set_addr(frame.start_address(), flags) 74 | } 75 | 76 | pub fn set_flags(&mut self, flags: PageTableFlags) { 77 | self.data &= !Self::FLAGS_MASK; 78 | self.data |= flags.bits() as usize; 79 | } 80 | } 81 | 82 | impl Debug for PageTableEntry { 83 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 84 | f.debug_struct("PageTableEntry") 85 | .field("addr", &self.addr()) 86 | .field("flags", &self.flags()) 87 | .finish() 88 | } 89 | } 90 | 91 | #[repr(C, align(4096))] 92 | #[derive(Clone)] 93 | pub struct PageTable { 94 | pub(super) entries: [PageTableEntry; PAGE_TABLE_ENTRIES], 95 | } 96 | 97 | impl PageTable { 98 | #[allow(clippy::new_without_default)] 99 | #[inline] 100 | pub const fn new() -> Self { 101 | Self { 102 | entries: [PageTableEntry::new(); PAGE_TABLE_ENTRIES], 103 | } 104 | } 105 | 106 | #[inline] 107 | pub fn zero(&mut self) { 108 | for entry in self.entries.iter_mut() { 109 | entry.set_unused(); 110 | } 111 | } 112 | 113 | pub fn next_table<'b>(&self, index: usize) -> Option<&'b PageTable> { 114 | let ptr = frame_to_table(self[index].frame()?); 115 | Some(unsafe { &*ptr }) 116 | } 117 | 118 | pub fn next_table_mut<'b>(&self, index: usize) -> Option<&'b mut PageTable> { 119 | let ptr = frame_to_table(self[index].frame()?); 120 | Some(unsafe { &mut *ptr }) 121 | } 122 | 123 | pub fn next_table_create<'b>( 124 | &mut self, 125 | index: usize, 126 | insert_flags: PageTableFlags, 127 | ) -> KResult<&'b mut PageTable> { 128 | let entry = &mut self[index]; 129 | let created; 130 | if entry.is_unused() { 131 | match alloc_kernel_frames(1) { 132 | Ok(frame) => { 133 | entry.set_frame(frame.start(), insert_flags); 134 | created = true; 135 | } 136 | Err(_e) => { 137 | kbail!("Could not allocate frame for new page table") 138 | } 139 | } 140 | } else { 141 | entry.set_flags(entry.flags() | insert_flags); 142 | created = false; 143 | } 144 | 145 | let page_table = match self.next_table_mut(index) { 146 | Some(pt) => pt, 147 | None => { 148 | kbail!("Could not get mutable reference to new page table, likely due to huge page") 149 | } 150 | }; 151 | 152 | if created { 153 | page_table.zero(); 154 | } 155 | 156 | Ok(page_table) 157 | } 158 | } 159 | 160 | impl Index for PageTable { 161 | type Output = PageTableEntry; 162 | fn index(&self, index: usize) -> &Self::Output { 163 | &self.entries[index] 164 | } 165 | } 166 | 167 | impl IndexMut for PageTable { 168 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 169 | &mut self.entries[index] 170 | } 171 | } 172 | 173 | impl Debug for PageTable { 174 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 175 | writeln!( 176 | f, 177 | "PageTable[{:?}]", 178 | VirtAddr::new(self as *const _ as usize).as_hhdm_phys() 179 | )?; 180 | for (i, entry) in self.entries.iter().enumerate() { 181 | if !entry.is_unused() { 182 | writeln!(f, "{:>3}: {:>16?} | {:?}", i, entry.addr(), entry.flags())?; 183 | } 184 | } 185 | Ok(()) 186 | } 187 | } 188 | 189 | pub fn active_table() -> &'static mut PageTable { 190 | let cr3 = PhysAddr::new(Cr3::read().0.start_address().as_u64() as usize); 191 | unsafe { &mut *cr3.as_hhdm_virt().as_raw_ptr_mut() } 192 | } 193 | -------------------------------------------------------------------------------- /src/fs/path.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc}; 2 | 3 | use super::INode; 4 | 5 | #[derive(Debug, Eq, PartialEq, Hash)] 6 | pub struct Path { 7 | path: str, 8 | } 9 | 10 | impl Path { 11 | pub fn new(path: &str) -> &Path { 12 | let path = if path == "/" { 13 | path 14 | } else { 15 | path.trim_end_matches('/') 16 | }; 17 | unsafe { &*(path as *const str as *const Path) } 18 | } 19 | 20 | pub fn as_str(&self) -> &str { 21 | &self.path 22 | } 23 | 24 | pub fn is_empty(&self) -> bool { 25 | self.path.is_empty() 26 | } 27 | 28 | pub fn is_absolute(&self) -> bool { 29 | self.path.starts_with('/') 30 | && !self 31 | .components() 32 | .any(|comp| matches!(comp, ".." | "." | "")) 33 | } 34 | 35 | pub fn is_pipe(&self) -> bool { 36 | self.path.starts_with("pipe:") 37 | } 38 | 39 | pub fn pipe_name(&self) -> Option<&str> { 40 | if self.is_pipe() { 41 | Some(&self.path[5..]) 42 | } else { 43 | None 44 | } 45 | } 46 | 47 | pub fn components(&self) -> Components<'_> { 48 | let path = if self.path.starts_with('/') { 49 | &self.path[1..] 50 | } else { 51 | &self.path 52 | }; 53 | 54 | Components { path } 55 | } 56 | 57 | pub fn parent_and_basename(&self) -> Option<(&Path, &str)> { 58 | if &self.path == "/" { 59 | return None; 60 | } 61 | 62 | if let Some(slash_idx) = self.path.rfind('/') { 63 | let parent_dir = if slash_idx == 0 { 64 | Path::new("/") 65 | } else { 66 | Path::new(&self.path[..slash_idx]) 67 | }; 68 | 69 | let basename = &self.path[(slash_idx + 1)..]; 70 | Some((parent_dir, basename)) 71 | } else { 72 | Some((Path::new("."), &self.path)) 73 | } 74 | } 75 | } 76 | 77 | impl AsRef for Path { 78 | fn as_ref(&self) -> &Path { 79 | self 80 | } 81 | } 82 | 83 | impl AsRef for str { 84 | fn as_ref(&self) -> &Path { 85 | Path::new(self) 86 | } 87 | } 88 | 89 | impl core::fmt::Display for Path { 90 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 91 | write!(f, "{}", &self.path) 92 | } 93 | } 94 | 95 | pub struct Components<'a> { 96 | path: &'a str, 97 | } 98 | 99 | impl<'a> Iterator for Components<'a> { 100 | type Item = &'a str; 101 | fn next(&mut self) -> Option { 102 | if self.path.is_empty() { 103 | return None; 104 | } 105 | 106 | let (path_str, next_start) = match self.path.find('/') { 107 | Some(slash_idx) => (&self.path[..slash_idx], slash_idx + 1), 108 | None => (self.path, self.path.len()), 109 | }; 110 | 111 | self.path = &self.path[next_start..]; 112 | Some(path_str) 113 | } 114 | } 115 | 116 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 117 | pub struct PathBuf { 118 | path: String, 119 | } 120 | 121 | impl PathBuf { 122 | pub fn new() -> PathBuf { 123 | PathBuf { 124 | path: String::new(), 125 | } 126 | } 127 | 128 | pub fn as_path(&self) -> &Path { 129 | Path::new(&self.path) 130 | } 131 | 132 | pub fn pop(&mut self) { 133 | if let Some((index, _)) = self.path.char_indices().rfind(|(_, ch)| *ch == '/') { 134 | self.path.truncate(index); 135 | } 136 | } 137 | 138 | pub fn push>(&mut self, path: P) { 139 | let path = path.as_ref(); 140 | let path_str = if path.as_str() == "/" { 141 | "/" 142 | } else { 143 | path.as_str().trim_end_matches('/') 144 | }; 145 | 146 | if path.is_absolute() { 147 | self.path = path_str.to_owned() 148 | } else { 149 | if self.path != "/" { 150 | self.path.push('/'); 151 | } 152 | self.path.push_str(path_str) 153 | } 154 | } 155 | } 156 | 157 | impl Default for PathBuf { 158 | fn default() -> Self { 159 | PathBuf::new() 160 | } 161 | } 162 | 163 | impl core::ops::Deref for PathBuf { 164 | type Target = Path; 165 | fn deref(&self) -> &Self::Target { 166 | self.as_path() 167 | } 168 | } 169 | 170 | impl AsRef for PathBuf { 171 | fn as_ref(&self) -> &Path { 172 | self.as_path() 173 | } 174 | } 175 | 176 | impl From<&Path> for PathBuf { 177 | fn from(value: &Path) -> Self { 178 | PathBuf { 179 | path: value.path.to_owned(), 180 | } 181 | } 182 | } 183 | 184 | impl From for PathBuf { 185 | fn from(value: String) -> Self { 186 | // TODO: check if this is a valid path 187 | PathBuf { path: value } 188 | } 189 | } 190 | 191 | impl From<&str> for PathBuf { 192 | fn from(value: &str) -> Self { 193 | // TODO: check if this is a valid path 194 | PathBuf { 195 | path: value.to_owned(), 196 | } 197 | } 198 | } 199 | 200 | #[derive(Clone)] 201 | pub struct PathComponent { 202 | pub parent_dir: Option>, 203 | pub name: Arc, 204 | pub inode: INode, 205 | } 206 | 207 | impl PathComponent { 208 | pub fn resolve_abs_path(&self) -> PathBuf { 209 | let path = if self.parent_dir.is_some() { 210 | let mut path = self.name.as_ref().to_owned(); 211 | let mut parent_dir = self.parent_dir.clone(); 212 | while let Some(ref path_comp) = parent_dir { 213 | path = path_comp.name.as_ref().to_owned() + "/" + &path; 214 | parent_dir = path_comp.parent_dir.clone(); 215 | } 216 | 217 | debug_assert!(path.starts_with('/')); 218 | path 219 | } else { 220 | "/".to_owned() 221 | }; 222 | 223 | PathBuf::from(path) 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/fs/initramfs/root.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc}; 2 | 3 | use crate::{ 4 | fs::{ 5 | path::{Path, PathComponent}, 6 | pipe::PIPE_FS, 7 | DirRef, INode, 8 | }, 9 | kbail, 10 | util::KResult, 11 | }; 12 | 13 | use super::dir::InitRamFsDir; 14 | 15 | const MAX_SYMLINK_FOLLOW_DEPTH: usize = 20; 16 | 17 | #[derive(Clone)] 18 | pub struct RootFs { 19 | root_path: PathComponent, 20 | cwd_path: PathComponent, 21 | } 22 | 23 | impl RootFs { 24 | pub fn new(root: Arc) -> RootFs { 25 | let root_path = PathComponent { 26 | parent_dir: None, 27 | name: Arc::new(String::new()), 28 | inode: INode::Dir(root), 29 | }; 30 | RootFs { 31 | root_path: root_path.clone(), 32 | cwd_path: root_path, 33 | } 34 | } 35 | 36 | pub fn cwd_path(&self) -> &PathComponent { 37 | &self.cwd_path 38 | } 39 | 40 | pub fn root_dir(&self) -> DirRef { 41 | self.root_path.inode.as_dir().unwrap().clone() 42 | } 43 | 44 | pub fn cwd_dir(&self) -> DirRef { 45 | self.cwd_path.inode.as_dir().unwrap().clone() 46 | } 47 | 48 | pub fn chdir(&mut self, path: &Path) -> KResult<()> { 49 | self.cwd_path = self.lookup_path(path, true)?; 50 | Ok(()) 51 | } 52 | 53 | pub fn lookup(&self, path: &Path, follow_symlinks: bool) -> KResult { 54 | if path.is_pipe() { 55 | return PIPE_FS.lookup(path).map(INode::Pipe); 56 | } 57 | self.lookup_path(path, follow_symlinks) 58 | .map(|cmp| cmp.inode.clone()) 59 | } 60 | 61 | pub fn lookup_path(&self, path: &Path, follow_symlinks: bool) -> KResult { 62 | if path.is_empty() { 63 | kbail!(ENOENT, "lookup_path(): empty path"); 64 | } 65 | 66 | let lookup_from = if path.is_absolute() { 67 | self.root_path.clone() 68 | } else { 69 | self.cwd_path.clone() 70 | }; 71 | 72 | self.do_lookup_path( 73 | &lookup_from, 74 | path, 75 | follow_symlinks, 76 | MAX_SYMLINK_FOLLOW_DEPTH, 77 | ) 78 | } 79 | 80 | fn do_lookup_path( 81 | &self, 82 | lookup_from: &PathComponent, 83 | path: &Path, 84 | follow_symlinks: bool, 85 | symlink_follow_limit: usize, 86 | ) -> KResult { 87 | let mut parent = lookup_from.clone(); 88 | let mut components = path.components().peekable(); 89 | while let Some(name) = components.next() { 90 | let path_comp = match name { 91 | "." => continue, 92 | ".." => parent 93 | .parent_dir 94 | .as_deref() 95 | .unwrap_or(&self.root_path) 96 | .clone(), 97 | _ => { 98 | let inode = parent.inode.as_dir()?.lookup(name)?; 99 | PathComponent { 100 | parent_dir: Some(Box::new(parent.clone())), 101 | name: Arc::new(name.to_owned()), 102 | inode, 103 | } 104 | } 105 | }; 106 | 107 | if components.peek().is_some() { 108 | parent = match &path_comp.inode { 109 | INode::Dir(_) => path_comp, 110 | INode::Pipe(_) => { 111 | unreachable!("Pipes should be contained in PipeFs, not RootFs") 112 | } 113 | INode::Symlink(link) if follow_symlinks => { 114 | if symlink_follow_limit == 0 { 115 | kbail!(ELOOP, "lookup_path(): maximum symlink depth reached"); 116 | } 117 | let dst = link.link_location()?; 118 | let follow_from = if dst.is_absolute() { 119 | &self.root_path 120 | } else { 121 | &parent 122 | }; 123 | 124 | let dst_path = self.do_lookup_path( 125 | follow_from, 126 | &dst, 127 | follow_symlinks, 128 | symlink_follow_limit - 1, 129 | )?; 130 | 131 | match dst_path.inode { 132 | INode::Dir(_) => dst_path, 133 | _ => { 134 | kbail!(ENOTDIR, "lookup_path(): not a directory"); 135 | } 136 | } 137 | } 138 | INode::Symlink(_) => { 139 | kbail!(ENOTDIR, "lookup_path(): not a directory"); 140 | } 141 | INode::File(_) => { 142 | kbail!(ENOTDIR, "lookup_path(): not a directory"); 143 | } 144 | } 145 | } else { 146 | match &path_comp.inode { 147 | INode::Symlink(link) if follow_symlinks => { 148 | if symlink_follow_limit == 0 { 149 | kbail!(ELOOP, "lookup_path(): maximum symlink depth reached"); 150 | } 151 | let dst = link.link_location()?; 152 | let follow_from = if dst.is_absolute() { 153 | &self.root_path 154 | } else { 155 | &parent 156 | }; 157 | 158 | return self.do_lookup_path( 159 | follow_from, 160 | &dst, 161 | follow_symlinks, 162 | symlink_follow_limit - 1, 163 | ); 164 | } 165 | _ => return Ok(path_comp), 166 | } 167 | } 168 | } 169 | 170 | Ok(parent) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/mem/paging/mapper.rs: -------------------------------------------------------------------------------- 1 | use x86::tlb; 2 | use x86_64::structures::paging::PageTableFlags; 3 | 4 | use crate::{ 5 | mem::{ 6 | addr::{PhysAddr, VirtAddr}, 7 | allocator::alloc_kernel_frames, 8 | }, 9 | util::KResult, 10 | }; 11 | 12 | use super::{ 13 | table::PageTable, 14 | units::{AllocatedFrames, AllocatedPages, Frame, MappedPages, MemoryUnit, Page}, 15 | }; 16 | 17 | #[derive(Debug)] 18 | #[must_use = "Changes to page tables must be flushed or ignored."] 19 | pub struct PageFlush(Page); 20 | 21 | impl PageFlush { 22 | pub fn new(page: Page) -> Self { 23 | PageFlush(page) 24 | } 25 | 26 | pub fn ignore(self) {} 27 | 28 | pub fn flush(self) { 29 | unsafe { tlb::flush(self.0.start_address().value()) } 30 | } 31 | } 32 | 33 | #[derive(Debug)] 34 | pub struct Mapper<'a> { 35 | p4: &'a mut PageTable, 36 | } 37 | 38 | impl<'a> Mapper<'a> { 39 | pub fn new(p4: &'a mut PageTable) -> Self { 40 | Self { p4 } 41 | } 42 | 43 | pub fn into_inner(self) -> &'a mut PageTable { 44 | self.p4 45 | } 46 | 47 | pub fn translate(&self, addr: VirtAddr) -> Option<(PhysAddr, PageTableFlags)> { 48 | let p3 = self.p4.next_table(addr.p4_index())?; 49 | let p2 = p3.next_table(addr.p3_index())?; 50 | let p1 = p2.next_table(addr.p2_index())?; 51 | let entry = p1[addr.p1_index()]; 52 | 53 | Some((entry.addr(), entry.flags())) 54 | } 55 | 56 | pub fn map_to_single( 57 | &mut self, 58 | page: Page, 59 | frame: Frame, 60 | flags: PageTableFlags, 61 | ) -> KResult<()> { 62 | let mut insert_flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; 63 | if flags.contains(PageTableFlags::USER_ACCESSIBLE) { 64 | insert_flags |= PageTableFlags::USER_ACCESSIBLE; 65 | } 66 | let addr = page.start_address(); 67 | 68 | let p3 = self.p4.next_table_create(addr.p4_index(), insert_flags)?; 69 | let p2 = p3.next_table_create(addr.p3_index(), insert_flags)?; 70 | let p1 = p2.next_table_create(addr.p2_index(), insert_flags)?; 71 | let entry = &mut p1[addr.p1_index()]; 72 | if !entry.is_unused() { 73 | unsafe { self.unmap_single(page) }; 74 | // log::error!( 75 | // "Attempt to remap {:?} to {:?} with {:?}", 76 | // page, 77 | // frame, 78 | // flags 79 | // ); 80 | // log::error!( 81 | // "-- {:?} already mapped to {:?}, with {:?}", 82 | // page, 83 | // entry.frame(), 84 | // entry.flags() 85 | // ); 86 | // kbail!("Page already mapped to different frame"); 87 | } 88 | entry.set_frame(frame, flags); 89 | unsafe { tlb::flush(addr.value()) } 90 | Ok(()) 91 | } 92 | 93 | pub fn map_to( 94 | &mut self, 95 | pages: AllocatedPages, 96 | frames: AllocatedFrames, 97 | flags: PageTableFlags, 98 | ) -> KResult { 99 | assert_eq!( 100 | pages.size_in_pages(), 101 | frames.size_in_pages(), 102 | "Number of pages must equal number of frames" 103 | ); 104 | for (page, frame) in pages.iter().zip(frames.iter()) { 105 | self.map_to_single(page, frame, flags)?; 106 | } 107 | 108 | Ok(unsafe { MappedPages::assume_mapped(pages, frames, flags) }) 109 | } 110 | 111 | pub fn map(&mut self, pages: AllocatedPages, flags: PageTableFlags) -> KResult { 112 | let frames = alloc_kernel_frames(pages.size_in_pages())?; 113 | self.map_to(pages, frames, flags) 114 | } 115 | 116 | pub fn set_flags(&mut self, mp: &mut MappedPages, flags: PageTableFlags) { 117 | for page in mp.pages().iter() { 118 | let addr = page.start_address(); 119 | // these unwraps should be safe since we know the pages are already mapped 120 | let p3 = self.p4.next_table_mut(addr.p4_index()).unwrap(); 121 | let p2 = p3.next_table_mut(addr.p3_index()).unwrap(); 122 | let p1 = p2.next_table_mut(addr.p2_index()).unwrap(); 123 | p1[addr.p1_index()].set_flags(flags); 124 | unsafe { tlb::flush(addr.value()) }; 125 | } 126 | mp.flags = flags; 127 | } 128 | 129 | pub unsafe fn unmap(&mut self, mp: MappedPages) -> (AllocatedPages, AllocatedFrames) { 130 | for page in mp.pages().iter() { 131 | let addr = page.start_address(); 132 | // these unwraps should be safe since we know the pages are already mapped 133 | let p3 = self.p4.next_table_mut(addr.p4_index()).unwrap(); 134 | let p2 = p3.next_table_mut(addr.p3_index()).unwrap(); 135 | let p1 = p2.next_table_mut(addr.p2_index()).unwrap(); 136 | p1[addr.p1_index()].set_unused(); 137 | unsafe { tlb::flush(addr.value()) }; 138 | } 139 | let MappedPages { pages, frames, .. } = mp; 140 | (pages, frames) 141 | } 142 | 143 | pub unsafe fn unmap_single(&mut self, page: Page) -> Option { 144 | let addr = page.start_address(); 145 | // these unwraps should be safe since we know the pages are already mapped 146 | let p3 = self.p4.next_table_mut(addr.p4_index())?; 147 | let p2 = p3.next_table_mut(addr.p3_index())?; 148 | let p1 = p2.next_table_mut(addr.p2_index())?; 149 | let old_frame = p1[addr.p1_index()].frame(); 150 | p1[addr.p1_index()].set_unused(); 151 | unsafe { tlb::flush(addr.value()) }; 152 | old_frame 153 | } 154 | 155 | pub unsafe fn set_flags_single(&mut self, page: Page, flags: PageTableFlags) { 156 | let addr = page.start_address(); 157 | // these unwraps should be safe since we know the pages are already mapped 158 | let p3 = self.p4.next_table_mut(addr.p4_index()).unwrap(); 159 | let p2 = p3.next_table_mut(addr.p3_index()).unwrap(); 160 | let p1 = p2.next_table_mut(addr.p2_index()).unwrap(); 161 | p1[addr.p1_index()].set_flags(flags); 162 | unsafe { tlb::flush(addr.value()) }; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/mem/addr_space.rs: -------------------------------------------------------------------------------- 1 | use x86_64::{ 2 | registers::control::{Cr3, Cr3Flags}, 3 | structures::paging::{PageTableFlags, PhysFrame}, 4 | }; 5 | 6 | use crate::{util::KResult, vga_text}; 7 | 8 | use super::{ 9 | addr::{PhysAddr, VirtAddr}, 10 | allocator::alloc_kernel_frames, 11 | consts::PAGE_TABLE_ENTRIES, 12 | paging::{ 13 | mapper::Mapper, 14 | table::{active_table, PageTable}, 15 | units::{AllocatedFrames, Frame, FrameRange, MemoryUnit, Page}, 16 | }, 17 | }; 18 | 19 | pub struct AddressSpace { 20 | cr3: AllocatedFrames, 21 | } 22 | 23 | impl AddressSpace { 24 | pub fn new() -> KResult { 25 | let cr3 = unsafe { 26 | let frame = alloc_kernel_frames(1)?; 27 | let phys_addr = frame.start_address(); 28 | let mut virt_addr = phys_addr.as_hhdm_virt(); 29 | 30 | let page_table: &mut PageTable = virt_addr.deref_mut()?; 31 | let active_table = active_table(); 32 | 33 | // zero out lower half of virtual address space 34 | for i in 0..256 { 35 | page_table[i].set_unused(); 36 | } 37 | 38 | // copy kernel mappings 39 | for i in 256..512 { 40 | page_table[i] = active_table[i]; 41 | } 42 | 43 | frame 44 | }; 45 | 46 | let mut this = Self { cr3 }; 47 | this.with_mapper(|mut mapper| { 48 | mapper.map_to_single( 49 | Page::containing_address(unsafe { 50 | VirtAddr::new_unchecked(vga_text::VGA_BUFFER_START_PADDR) 51 | }), 52 | Frame::containing_address(unsafe { 53 | PhysAddr::new_unchecked(vga_text::VGA_BUFFER_START_PADDR) 54 | }), 55 | PageTableFlags::PRESENT 56 | | PageTableFlags::WRITABLE 57 | | PageTableFlags::NO_EXECUTE 58 | | PageTableFlags::USER_ACCESSIBLE, 59 | ) 60 | })?; 61 | 62 | Ok(this) 63 | } 64 | 65 | pub fn current() -> Self { 66 | let cr3 = Cr3::read().0.start_address().as_u64() as usize; 67 | let cr3 = PhysAddr::new(cr3); 68 | let cr3 = unsafe { 69 | AllocatedFrames::assume_allocated(FrameRange::new( 70 | Frame::containing_address(cr3), 71 | Frame::containing_address(cr3), 72 | )) 73 | }; 74 | Self { cr3 } 75 | } 76 | 77 | pub fn cr3(&self) -> PhysAddr { 78 | self.cr3.start_address() 79 | } 80 | 81 | pub fn switch(&self) { 82 | unsafe { 83 | Cr3::write( 84 | PhysFrame::containing_address(x86_64::PhysAddr::new( 85 | self.cr3.start_address().value() as u64, 86 | )), 87 | Cr3Flags::empty(), 88 | ); 89 | } 90 | } 91 | 92 | pub fn temporarily_switch(&self) -> TmpAddrSpaceGuard { 93 | let previous = Self::current(); 94 | self.switch(); 95 | TmpAddrSpaceGuard { previous } 96 | } 97 | 98 | pub fn with_mapper(&mut self, f: impl FnOnce(Mapper) -> R) -> R { 99 | let mut addr = self.cr3.start_address().as_hhdm_virt(); 100 | let table = unsafe { addr.deref_mut().unwrap() }; 101 | let mapper = Mapper::new(table); 102 | let _guard = self.temporarily_switch(); 103 | f(mapper) 104 | // _guard is dropped here 105 | } 106 | 107 | pub fn map_two( 108 | first: &mut Self, 109 | second: &mut Self, 110 | f: impl FnOnce(Mapper, Mapper) -> R, 111 | ) -> R { 112 | let mut addr = first.cr3.start_address().as_hhdm_virt(); 113 | let table = unsafe { addr.deref_mut().unwrap() }; 114 | let mapper = Mapper::new(table); 115 | 116 | let mut addr = second.cr3.start_address().as_hhdm_virt(); 117 | let table = unsafe { addr.deref_mut().unwrap() }; 118 | let other_mapper = Mapper::new(table); 119 | 120 | f(mapper, other_mapper) 121 | } 122 | 123 | pub fn is_active(&self) -> bool { 124 | self.cr3.start_address().value() == Cr3::read().0.start_address().as_u64() as usize 125 | } 126 | 127 | pub fn fork(&mut self, set_cow: bool) -> KResult { 128 | assert!(self.is_active(), "Can only fork the active address space"); 129 | let mut new = AddressSpace::new()?; 130 | 131 | let mut insert_flags = PageTableFlags::PRESENT | PageTableFlags::USER_ACCESSIBLE; 132 | 133 | if !set_cow { 134 | insert_flags |= PageTableFlags::WRITABLE; 135 | } 136 | 137 | Self::map_two(self, &mut new, |my_mapper, new_mapper| -> KResult<()> { 138 | let my_p4 = my_mapper.into_inner(); 139 | let new_p4 = new_mapper.into_inner(); 140 | 141 | for p4_idx in 0..256 { 142 | let my_entry = &my_p4[p4_idx]; 143 | if my_entry.is_unused() { 144 | continue; 145 | } 146 | let my_p3 = my_p4.next_table(p4_idx).unwrap(); 147 | let new_p3 = new_p4.next_table_create(p4_idx, insert_flags)?; 148 | for p3_idx in 0..PAGE_TABLE_ENTRIES { 149 | let my_entry = &my_p3[p3_idx]; 150 | if my_entry.is_unused() { 151 | continue; 152 | } 153 | let my_p2 = my_p3.next_table(p3_idx).unwrap(); 154 | let new_p2 = new_p3.next_table_create(p3_idx, insert_flags)?; 155 | 156 | for p2_idx in 0..PAGE_TABLE_ENTRIES { 157 | let my_entry = &my_p2[p2_idx]; 158 | if my_entry.is_unused() { 159 | continue; 160 | } 161 | let my_p1 = my_p2.next_table(p2_idx).unwrap(); 162 | let new_p1 = new_p2.next_table_create(p2_idx, insert_flags)?; 163 | 164 | for p1_idx in 0..PAGE_TABLE_ENTRIES { 165 | let my_entry = &my_p1[p1_idx]; 166 | if my_entry.is_unused() { 167 | continue; 168 | } 169 | let mut flags = my_entry.flags(); 170 | 171 | if set_cow { 172 | flags.remove(PageTableFlags::WRITABLE); 173 | } 174 | 175 | new_p1[p1_idx].set_frame(my_entry.frame().unwrap(), flags); 176 | } 177 | } 178 | } 179 | } 180 | 181 | Ok(()) 182 | })?; 183 | 184 | Ok(new) 185 | } 186 | } 187 | 188 | #[must_use = "TmpAddrSpaceGuard restores previous address space on drop"] 189 | pub struct TmpAddrSpaceGuard { 190 | previous: AddressSpace, 191 | } 192 | 193 | impl Drop for TmpAddrSpaceGuard { 194 | fn drop(&mut self) { 195 | self.previous.switch(); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/backtrace.rs: -------------------------------------------------------------------------------- 1 | use core::{alloc::Layout, mem::size_of, panic::PanicInfo}; 2 | 3 | use alloc::vec::Vec; 4 | use spin::Once; 5 | use x86_64::instructions::interrupts; 6 | use xmas_elf::{ 7 | ElfFile, 8 | sections::{SectionData, ShType}, 9 | symbol_table::Entry, 10 | }; 11 | 12 | use crate::{ 13 | fb_println, 14 | graphics::FRAMEBUFFER, 15 | kerror, 16 | mem::{addr::VirtAddr, addr_space::AddressSpace, consts::PAGE_SIZE}, 17 | task::get_scheduler, 18 | userland::elf::SymTabEntry, 19 | util::{KResult, SavedInterruptStatus}, 20 | }; 21 | 22 | pub static KERNEL_ELF: Once> = Once::new(); 23 | 24 | fn print_symbol(rip: usize, symtab: &Option>, depth: usize) { 25 | if let Some(symbol_table) = symtab { 26 | let mut name = None; 27 | for data in symbol_table { 28 | let st_value = data.value as usize; 29 | let st_size = data.size as usize; 30 | 31 | if rip >= st_value && rip < (st_value + st_size) { 32 | name = Some(data.name.clone()); 33 | } 34 | } 35 | 36 | if let Some(name) = name { 37 | serial0_println!("{:>2}: 0x{:016x} - {}", depth, rip, name); 38 | } else { 39 | serial0_println!( 40 | "{:>2}: 0x{:016x} - (symbol not found)", 41 | depth, 42 | rip 43 | ); 44 | } 45 | } else { 46 | serial0_println!("{:>2}: 0x{:016x} - (no symbol table)", depth, rip); 47 | } 48 | } 49 | 50 | pub fn unwind_user_stack_from(mut rbp: usize, mut rip: usize) { 51 | let _guard = SavedInterruptStatus::save(); 52 | interrupts::disable(); 53 | let mut addr_space = AddressSpace::current(); 54 | 55 | if rbp == 0 { 56 | serial0_println!(""); 57 | return; 58 | } 59 | 60 | let current = get_scheduler().current_task_opt(); 61 | let symtab = if let Some(current) = current { 62 | let s = current.arch_mut().symtab.clone(); 63 | if s.is_none() { 64 | serial0_println!( 65 | "Warning: Couldn't find symbol table for pid {}", 66 | current.pid().as_usize() 67 | ); 68 | } 69 | s 70 | } else { 71 | serial0_println!("Warning: Couldn't lock current scheduler task"); 72 | None 73 | }; 74 | 75 | serial0_println!("---BEGIN BACKTRACE---"); 76 | print_symbol(rip, &symtab, 0); 77 | for depth in 1..17 { 78 | if let Some(rip_rbp) = rbp.checked_add(size_of::()) { 79 | let rip_rbp = unsafe { VirtAddr::new_unchecked(rip_rbp) }; 80 | let translated = addr_space.with_mapper(|mapper| mapper.translate(rip_rbp)); 81 | if rip_rbp.value() < PAGE_SIZE || translated.is_none() { 82 | serial0_println!("{:>2}: ", depth); 83 | break; 84 | } 85 | 86 | rip = unsafe { rip_rbp.read::().unwrap_or(0) }; 87 | if rip == 0 || rbp == 0 { 88 | break; 89 | } 90 | 91 | unsafe { 92 | rbp = *(rbp as *const usize); 93 | } 94 | 95 | print_symbol(rip, &symtab, depth); 96 | } else { 97 | break; 98 | } 99 | } 100 | serial0_println!("---END BACKTRACE---"); 101 | } 102 | 103 | pub fn unwind_stack() -> KResult<()> { 104 | let _guard = SavedInterruptStatus::save(); 105 | interrupts::disable(); 106 | let mut addr_space = AddressSpace::current(); 107 | 108 | let kernel_elf = KERNEL_ELF 109 | .get() 110 | .ok_or(kerror!("KERNEL_ELF not initialized"))?; 111 | let mut symbol_table = None; 112 | 113 | for section in kernel_elf.section_iter() { 114 | if section.get_type() == Ok(ShType::SymTab) { 115 | let section_data = section 116 | .get_data(kernel_elf) 117 | .map_err(|_| kerror!("Failed to get kernel section data"))?; 118 | 119 | if let SectionData::SymbolTable64(symtab) = section_data { 120 | symbol_table = Some(symtab); 121 | } 122 | } 123 | } 124 | 125 | let symbol_table = symbol_table.ok_or(kerror!("No symbol table available"))?; 126 | let mut rbp: usize; 127 | unsafe { 128 | core::arch::asm!("mov {}, rbp", out(reg) rbp); 129 | } 130 | 131 | if rbp == 0 { 132 | serial0_println!(""); 133 | return Ok(()); 134 | } 135 | 136 | serial0_println!("---BEGIN BACKTRACE---"); 137 | for depth in 0..16 { 138 | if let Some(rip_rbp) = rbp.checked_add(size_of::()) { 139 | let rip_rbp = unsafe { VirtAddr::new_unchecked(rip_rbp) }; 140 | let translated = addr_space.with_mapper(|mapper| mapper.translate(rip_rbp)); 141 | if translated.is_none() { 142 | serial0_println!("{:>2}: ", depth); 143 | break; 144 | } 145 | 146 | let rip = unsafe { rip_rbp.read::().unwrap_or(0) }; 147 | if rip == 0 || rbp == 0 { 148 | break; 149 | } 150 | 151 | unsafe { 152 | rbp = *(rbp as *const usize); 153 | } 154 | 155 | let mut name = None; 156 | for data in symbol_table { 157 | let st_value = data.value() as usize; 158 | let st_size = data.size() as usize; 159 | 160 | if rip >= st_value && rip < (st_value + st_size) { 161 | let mangled_name = data.get_name(kernel_elf).unwrap_or(""); 162 | name = Some(rustc_demangle::demangle(mangled_name)); 163 | } 164 | } 165 | 166 | if let Some(name) = name { 167 | serial0_println!("{:>2}: 0x{:016x} - {}", depth, rip, name); 168 | } else { 169 | serial0_println!("{:>2}: 0x{:016x} - ", depth, rip); 170 | } 171 | } else { 172 | break; 173 | } 174 | } 175 | serial0_println!("---END BACKTRACE---"); 176 | 177 | Ok(()) 178 | } 179 | 180 | #[panic_handler] 181 | fn rust_panic(info: &PanicInfo) -> ! { 182 | interrupts::disable(); 183 | let panic_msg = info.message(); 184 | 185 | serial0_println!("Panicked at '{}'", panic_msg); 186 | if FRAMEBUFFER.get().is_some() { 187 | fb_println!("Panicked at '{}'", panic_msg); 188 | } 189 | 190 | if let Some(panic_location) = info.location() { 191 | serial0_println!("{}", panic_location); 192 | if FRAMEBUFFER.get().is_some() { 193 | fb_println!("{}", panic_location); 194 | } 195 | } 196 | 197 | match unwind_stack() { 198 | Ok(()) => {} 199 | Err(e) => crate::serial::_print0(format_args!("Error unwinding stack: {:?}\n", e.msg())), 200 | } 201 | 202 | crate::hcf(); 203 | } 204 | 205 | #[allow(non_snake_case)] 206 | #[unsafe(no_mangle)] 207 | extern "C" fn _Unwind_Resume(unwind_context_ptr: usize) -> ! { 208 | serial0_println!("{:#x}", unwind_context_ptr); 209 | crate::hcf(); 210 | } 211 | 212 | #[lang = "eh_personality"] 213 | #[unsafe(no_mangle)] 214 | extern "C" fn rust_eh_personality() -> ! { 215 | serial0_println!("Poisoned function `rust_eh_personality` was called."); 216 | crate::hcf() 217 | } 218 | 219 | #[alloc_error_handler] 220 | fn handle_alloc_error(layout: Layout) -> ! { 221 | panic!("Alloc Error for layout {:?}", layout) 222 | } 223 | -------------------------------------------------------------------------------- /src/util/errno.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 2 | #[repr(i32)] 3 | #[allow(unused)] 4 | #[allow(clippy::upper_case_acronyms)] 5 | pub enum Errno { 6 | /// Operation not permitted 7 | EPERM = 1, 8 | /// No such file or directory 9 | ENOENT = 2, 10 | /// No such process 11 | ESRCH = 3, 12 | /// Interrupted system call 13 | EINTR = 4, 14 | /// I/O error 15 | EIO = 5, 16 | /// No such device or address 17 | ENXIO = 6, 18 | /// Argument list too long 19 | E2BIG = 7, 20 | /// Exec format error 21 | ENOEXEC = 8, 22 | /// Bad file number 23 | EBADF = 9, 24 | /// No child processes 25 | ECHILD = 10, 26 | /// Resource temporarily unavailable 27 | EAGAIN = 11, 28 | /// Cannot allocate memory 29 | ENOMEM = 12, 30 | /// Permission denied 31 | EACCES = 13, 32 | /// Bad address 33 | EFAULT = 14, 34 | /// Block device required 35 | ENOTBLK = 15, 36 | /// Device or resource busy 37 | EBUSY = 16, 38 | /// File exists 39 | EEXIST = 17, 40 | /// Invalid cross-device link 41 | EXDEV = 18, 42 | /// No such device 43 | ENODEV = 19, 44 | /// Not a directory 45 | ENOTDIR = 20, 46 | /// Is a directory 47 | EISDIR = 21, 48 | /// Invalid argument 49 | EINVAL = 22, 50 | /// Too many open files in system 51 | ENFILE = 23, 52 | /// Too many open files 53 | EMFILE = 24, 54 | /// Inappropriate ioctl for device 55 | ENOTTY = 25, 56 | /// Text file busy 57 | ETXTBSY = 26, 58 | /// File too large 59 | EFBIG = 27, 60 | /// No space left on device 61 | ENOSPC = 28, 62 | /// Illegal seek 63 | ESPIPE = 29, 64 | /// Read-only file system 65 | EROFS = 30, 66 | /// Too many links 67 | EMLINK = 31, 68 | /// Broken pipe 69 | EPIPE = 32, 70 | /// Math argument out of domain of func 71 | EDOM = 33, 72 | /// Math result not representable 73 | ERANGE = 34, 74 | /// Resource deadlock would occur 75 | EDEADLK = 35, 76 | /// File name too long 77 | ENAMETOOLONG = 36, 78 | /// No locks available 79 | ENOLCK = 37, 80 | /// Function not implemented 81 | ENOSYS = 38, 82 | /// Directory not empty 83 | ENOTEMPTY = 39, 84 | /// Too many levels of symbolic links 85 | ELOOP = 40, 86 | /// No message of desired type 87 | ENOMSG = 42, 88 | /// Identifier removed 89 | EIDRM = 43, 90 | /// Channel number out of range 91 | ECHRNG = 44, 92 | /// Level 2 not synchronized 93 | EL2NSYNC = 45, 94 | /// Level 3 halted 95 | EL3HLT = 46, 96 | /// Level 3 reset 97 | EL3RST = 47, 98 | /// Link number out of range 99 | ELNRNG = 48, 100 | /// Protocol driver not attached 101 | EUNATCH = 49, 102 | /// No CSI structure available 103 | ENOCSI = 50, 104 | /// Level 2 halted 105 | EL2HLT = 51, 106 | /// Invalid exchange 107 | EBADE = 52, 108 | /// Invalid request descriptor 109 | EBADR = 53, 110 | /// Exchange full 111 | EXFULL = 54, 112 | /// No anode 113 | ENOANO = 55, 114 | /// Invalid request code 115 | EBADRQC = 56, 116 | /// Invalid slot 117 | EBADSLT = 57, 118 | /// Bad font file format 119 | EBFONT = 59, 120 | /// Device not a stream 121 | ENOSTR = 60, 122 | /// No data available 123 | ENODATA = 61, 124 | /// Timer expired 125 | ETIME = 62, 126 | /// Out of streams resources 127 | ENOSR = 63, 128 | /// Machine is not on the network 129 | ENONET = 64, 130 | /// Package not installed 131 | ENOPKG = 65, 132 | /// Object is remote 133 | EREMOTE = 66, 134 | /// Link has been severed 135 | ENOLINK = 67, 136 | /// Advertise error 137 | EADV = 68, 138 | /// Srmount error 139 | ESRMNT = 69, 140 | /// Communication error on send 141 | ECOMM = 70, 142 | /// Protocol error 143 | EPROTO = 71, 144 | /// Multihop attempted 145 | EMULTIHOP = 72, 146 | /// RFS specific error 147 | EDOTDOT = 73, 148 | /// Not a data message 149 | EBADMSG = 74, 150 | /// Value too large for defined data type 151 | EOVERFLOW = 75, 152 | /// Name not unique on network 153 | ENOTUNIQ = 76, 154 | /// File descriptor in bad state 155 | EBADFD = 77, 156 | /// Remote address changed 157 | EREMCHG = 78, 158 | /// Can not access a needed shared library 159 | ELIBACC = 79, 160 | /// Accessing a corrupted shared library 161 | ELIBBAD = 80, 162 | /// .lib section in a.out corrupted 163 | ELIBSCN = 81, 164 | /// Attempting to link in too many shared libraries 165 | ELIBMAX = 82, 166 | /// Cannot exec a shared library directly 167 | ELIBEXEC = 83, 168 | /// Illegal byte sequence 169 | EILSEQ = 84, 170 | /// Interrupted system call should be restarted 171 | ERESTART = 85, 172 | /// Streams pipe error 173 | ESTRPIPE = 86, 174 | /// Too many users 175 | EUSERS = 87, 176 | /// Socket operation on non-socket 177 | ENOTSOCK = 88, 178 | /// Destination address required 179 | EDESTADDRREQ = 89, 180 | /// Message too long 181 | EMSGSIZE = 90, 182 | /// Protocol wrong type for socket 183 | EPROTOTYPE = 91, 184 | /// Protocol not available 185 | ENOPROTOOPT = 92, 186 | /// Protocol not supported 187 | EPROTONOSUPPORT = 93, 188 | /// Socket type not supported 189 | ESOCKTNOSUPPORT = 94, 190 | /// Operation not supported on transport endpoint 191 | EOPNOTSUPP = 95, 192 | /// Protocol family not supported 193 | EPFNOSUPPORT = 96, 194 | /// Address family not supported by protocol 195 | EAFNOSUPPORT = 97, 196 | /// Address already in use 197 | EADDRINUSE = 98, 198 | /// Cannot assign requested address 199 | EADDRNOTAVAIL = 99, 200 | /// Network is down 201 | ENETDOWN = 100, 202 | /// Network is unreachable 203 | ENETUNREACH = 101, 204 | /// Network dropped connection because of reset 205 | ENETRESET = 102, 206 | /// Software caused connection abort 207 | ECONNABORTED = 103, 208 | /// Connection reset by peer 209 | ECONNRESET = 104, 210 | /// No buffer space available 211 | ENOBUFS = 105, 212 | /// Transport endpoint is already connected 213 | EISCONN = 106, 214 | /// Transport endpoint is not connected 215 | ENOTCONN = 107, 216 | /// Cannot send after transport endpoint shutdown 217 | ESHUTDOWN = 108, 218 | /// Too many references: cannot splice 219 | ETOOMANYREFS = 109, 220 | /// Connection timed out 221 | ETIMEDOUT = 110, 222 | /// Connection refused 223 | ECONNREFUSED = 111, 224 | /// Host is down 225 | EHOSTDOWN = 112, 226 | /// No route to host 227 | EHOSTUNREACH = 113, 228 | /// Operation already in progress 229 | EALREADY = 114, 230 | /// Operation now in progress 231 | EINPROGRESS = 115, 232 | /// Stale file handle 233 | ESTALE = 116, 234 | /// Structure needs cleaning 235 | EUCLEAN = 117, 236 | /// Not a XENIX named type file 237 | ENOTNAM = 118, 238 | /// No XENIX semaphores available 239 | ENAVAIL = 119, 240 | /// Is a named type file 241 | EISNAM = 120, 242 | /// Remote I/O error 243 | EREMOTEIO = 121, 244 | /// Quota exceeded 245 | EDQUOT = 122, 246 | /// No medium found 247 | ENOMEDIUM = 123, 248 | /// Wrong medium type 249 | EMEDIUMTYPE = 124, 250 | /// Operation canceled 251 | ECANCELED = 125, 252 | /// Required key not available 253 | ENOKEY = 126, 254 | /// Key has expired 255 | EKEYEXPIRED = 127, 256 | /// Key has been revoked 257 | EKEYREVOKED = 128, 258 | /// Key was rejected by service 259 | EKEYREJECTED = 129, 260 | /// Owner died 261 | EOWNERDEAD = 130, 262 | /// State not recoverable 263 | ENOTRECOVERABLE = 131, 264 | /// Operation not possible due to RF-kill 265 | ERFKILL = 132, 266 | /// Memory page has hardware error 267 | EHWPOISON = 133, 268 | } 269 | -------------------------------------------------------------------------------- /src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use limine::request::{ 3 | BootTimeRequest, FramebufferRequest, HhdmRequest, KernelFileRequest, MemoryMapRequest, 4 | StackSizeRequest, 5 | }; 6 | 7 | use spin::Once; 8 | use x86::{ 9 | controlregs::{self, Cr0, Cr4}, 10 | cpuid::{CpuId, FeatureInfo}, 11 | }; 12 | use x86_64::instructions::interrupts; 13 | 14 | use crate::{ 15 | backtrace, 16 | fs::{ 17 | self, 18 | initramfs::get_root, 19 | opened_file::{OpenFlags, OpenedFile}, 20 | path::Path, 21 | }, 22 | god_mode::GOD_MODE_FIFO, 23 | graphics, 24 | mem::{ 25 | self, 26 | allocator::{KERNEL_FRAME_ALLOCATOR, KERNEL_PAGE_ALLOCATOR}, 27 | consts::KERNEL_STACK_SIZE, 28 | }, 29 | serial::serial1_recv, 30 | task::{current_task, get_scheduler, Task}, 31 | }; 32 | 33 | pub mod cpu_local; 34 | pub mod gdt; 35 | pub mod idt; 36 | pub mod syscall; 37 | pub mod task; 38 | pub mod time; 39 | 40 | static HHDM: HhdmRequest = HhdmRequest::new(); 41 | static _STACK: StackSizeRequest = StackSizeRequest::new().with_size(KERNEL_STACK_SIZE as u64); 42 | static BOOT_TIME: BootTimeRequest = BootTimeRequest::new(); 43 | static FB_REQUEST: FramebufferRequest = FramebufferRequest::new(); 44 | static MEM_MAP: MemoryMapRequest = MemoryMapRequest::new(); 45 | static KERNEL_FILE: KernelFileRequest = KernelFileRequest::new(); 46 | 47 | pub static CPUID_FEATURE_INFO: Once = Once::new(); 48 | 49 | pub fn get_cpuid_feature_info() -> &'static FeatureInfo { 50 | CPUID_FEATURE_INFO.call_once(|| { 51 | CpuId::new() 52 | .get_feature_info() 53 | .expect("Error getting CPUID feature info") 54 | }) 55 | } 56 | 57 | pub fn arch_main() { 58 | interrupts::disable(); 59 | 60 | let kernel_file = KERNEL_FILE 61 | .get_response() 62 | .expect("Error getting kernel binary from Limine"); 63 | let kernel_file = kernel_file.file(); 64 | let kernel_file_base = kernel_file.addr(); 65 | let kernel_file_len = kernel_file.size() as usize; 66 | let kernel_file_data = 67 | unsafe { core::slice::from_raw_parts(kernel_file_base, kernel_file_len) }; 68 | backtrace::KERNEL_ELF.call_once(|| { 69 | xmas_elf::ElfFile::new(kernel_file_data).expect("Error parsing kernel ELF file data") 70 | }); 71 | 72 | crate::PHYSICAL_OFFSET.call_once(|| { 73 | HHDM.get_response() 74 | .expect("Error getting HHDM response from Limine") 75 | .offset() as usize 76 | }); 77 | 78 | let memmap = MEM_MAP 79 | .get_response() 80 | .expect("Error getting memory map response from Limine") 81 | .entries(); 82 | 83 | crate::logging::init(); 84 | log::info!("Logger initialized."); 85 | 86 | log::info!("Setting up time structures."); 87 | let boot_time = BOOT_TIME 88 | .get_response() 89 | .expect("Error getting boot time response from Limine") 90 | .boot_time(); 91 | time::init(boot_time.as_secs() as i64); 92 | 93 | log::info!("Initializing FPU mechanisms."); 94 | let features = get_cpuid_feature_info(); 95 | assert!(features.has_fxsave_fxstor(), "FXSAVE/FXRSTOR not available"); 96 | assert!(features.has_mmx(), "MMX not available"); 97 | assert!(features.has_fpu(), "FPU not available"); 98 | assert!(features.has_sse(), "SSE not available"); 99 | unsafe { 100 | // enable FXSAVE and FXRSTOR 101 | controlregs::cr4_write(controlregs::cr4() | Cr4::CR4_ENABLE_SSE | Cr4::CR4_UNMASKED_SSE); 102 | log::trace!("CR4_ENABLE_SSE and CR4_UNMASKED_SSE set."); 103 | 104 | // controlregs::xcr0_write( 105 | // controlregs::xcr0() 106 | // | Xcr0::XCR0_SSE_STATE 107 | // | Xcr0::XCR0_FPU_MMX_STATE 108 | // | Xcr0::XCR0_AVX_STATE, 109 | // ); 110 | // log::trace!("XCR0_SSE_STATE, XCR0_FPU_MMX_STATE, and XCR0_AVX_STATE set."); 111 | controlregs::cr0_write(controlregs::cr0() & !Cr0::CR0_EMULATE_COPROCESSOR); 112 | log::trace!("CR0_EMULATE_COPROCESSOR cleared."); 113 | controlregs::cr0_write(controlregs::cr0() | Cr0::CR0_MONITOR_COPROCESSOR); 114 | log::trace!("CR0_MONITOR_COPROCESSOR set."); 115 | } 116 | 117 | log::info!("Initializing boot GDT."); 118 | gdt::init_boot(); 119 | 120 | // let fb_tag = boot_info.framebuffer_tag().expect("No multiboot2 framebuffer tag found"); 121 | let fb_resp = FB_REQUEST 122 | .get_response() 123 | .expect("Error getting framebuffer response from Limine"); 124 | log::info!("Initializing kernel frame and page allocators."); 125 | mem::allocator::init(memmap).expect("Error initializing kernel frame and page allocators"); 126 | 127 | log::info!("Remapping kernel to new page table."); 128 | let kernel_addr_space = mem::remap_kernel().expect("Error remapping kernel"); 129 | 130 | log::info!("Setting up kernel heap."); 131 | let _heap_mp = kernel_addr_space 132 | .lock() 133 | .with_mapper(|mut mapper| mem::init_heap(&mut mapper).expect("Error setting up heap")); 134 | 135 | log::info!("Converting kernel frame and page allocators to use heap."); 136 | { 137 | KERNEL_FRAME_ALLOCATOR 138 | .get() 139 | .expect("Error getting kernel frame allocator") 140 | .lock() 141 | .convert_to_heap_allocated(); 142 | KERNEL_PAGE_ALLOCATOR 143 | .get() 144 | .expect("Error getting kernel page allocator") 145 | .lock() 146 | .convert_to_heap_allocated(); 147 | } 148 | 149 | log::info!("Initializing VGA graphics."); 150 | 151 | graphics::init(fb_resp).expect("Error initializing VGA graphics"); 152 | 153 | log::info!("Setting up syscalls."); 154 | unsafe { 155 | syscall::init(); 156 | } 157 | 158 | log::info!("Loading GDT."); 159 | gdt::init(); 160 | 161 | log::info!("Loading IDT."); 162 | idt::init(); 163 | 164 | log::info!("Initializing filesystems."); 165 | fs::initramfs::init().expect("Error initializing initramfs"); 166 | 167 | log::info!("Initializing task scheduler."); 168 | crate::task::init(); 169 | 170 | log::info!("Starting init process."); 171 | 172 | let sched = get_scheduler(); 173 | 174 | fs::devfs::init(); 175 | 176 | log::info!("Welcome to K4DOS!"); 177 | 178 | { 179 | let task = Task::new_kernel(sched, poll_serial1, true); 180 | sched.push_runnable(task, true); 181 | } 182 | 183 | // god_mode::init(); 184 | 185 | loop { 186 | interrupts::enable_and_hlt(); 187 | } 188 | } 189 | 190 | pub fn startup_init() { 191 | let exe = "/bin/sh"; 192 | let file = get_root() 193 | .unwrap() 194 | .lookup(Path::new(exe), true) 195 | .unwrap() 196 | .as_file() 197 | .unwrap() 198 | .clone(); 199 | 200 | let current = current_task(); 201 | let mut files = current.opened_files.lock(); 202 | 203 | let console = get_root() 204 | .unwrap() 205 | .lookup_path(Path::new("/dev/tty"), true) 206 | .unwrap(); 207 | 208 | // stdin 209 | files 210 | .open_with_fd( 211 | 0, 212 | Arc::new(OpenedFile::new(console.clone(), OpenFlags::O_RDONLY, 0)), 213 | OpenFlags::O_RDONLY | OpenFlags::O_CLOEXEC, 214 | ) 215 | .unwrap(); 216 | // stdout 217 | files 218 | .open_with_fd( 219 | 1, 220 | Arc::new(OpenedFile::new(console.clone(), OpenFlags::O_WRONLY, 0)), 221 | OpenFlags::O_WRONLY | OpenFlags::O_CLOEXEC, 222 | ) 223 | .unwrap(); 224 | // stderr 225 | files 226 | .open_with_fd( 227 | 2, 228 | Arc::new(OpenedFile::new(console, OpenFlags::O_WRONLY, 0)), 229 | OpenFlags::O_WRONLY | OpenFlags::O_CLOEXEC, 230 | ) 231 | .unwrap(); 232 | drop(files); 233 | current 234 | .exec(file, &[exe.as_bytes()], &[b"FOO=bar"]) 235 | .unwrap(); 236 | } 237 | 238 | fn poll_serial1() { 239 | loop { 240 | let c = serial1_recv(); 241 | if let Some(c) = c { 242 | // TTY.get().unwrap().input_char(c); 243 | loop { 244 | if let Ok(mut lock) = GOD_MODE_FIFO.get().unwrap().try_lock() { 245 | lock.push_back(c); 246 | drop(lock); 247 | break; 248 | } 249 | // sched.preempt(); 250 | interrupts::enable_and_hlt(); 251 | } 252 | } 253 | interrupts::enable_and_hlt(); 254 | } 255 | } 256 | 257 | pub fn idle() { 258 | loop { 259 | interrupts::enable_and_hlt(); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/fs/initramfs/mod.rs: -------------------------------------------------------------------------------- 1 | use core::iter::Peekable; 2 | 3 | use alloc::{ 4 | string::{String, ToString}, 5 | sync::{Arc, Weak}, 6 | vec::Vec, 7 | }; 8 | use spin::Once; 9 | 10 | use crate::{ 11 | fs::{ 12 | DirRef, FileMode, FileSize, FsNode, INode, Stat, 13 | initramfs::{ 14 | dir::{DirInner, InitRamFsDir}, 15 | file::InitRamFsFile, 16 | symlink::InitRamFsSymlink, 17 | }, 18 | path::{Components, Path, PathBuf}, 19 | }, 20 | kbail, kerror, 21 | util::{IrqMutex, KResult, align_up}, 22 | }; 23 | 24 | use self::root::RootFs; 25 | 26 | pub mod dir; 27 | pub mod file; 28 | pub mod root; 29 | pub mod symlink; 30 | 31 | pub struct ByteParser<'a> { 32 | buffer: &'a [u8], 33 | current: usize, 34 | } 35 | 36 | impl<'a> ByteParser<'a> { 37 | pub fn new(buffer: &'a [u8]) -> ByteParser<'a> { 38 | ByteParser { buffer, current: 0 } 39 | } 40 | 41 | pub fn remaining(&self) -> &[u8] { 42 | &self.buffer[self.current..] 43 | } 44 | pub fn remaining_len(&self) -> usize { 45 | self.buffer.len() - self.current 46 | } 47 | 48 | pub fn skip(&mut self, len: usize) -> KResult<()> { 49 | if self.current + len > self.buffer.len() { 50 | kbail!("skip(): out of bounds"); 51 | } 52 | 53 | self.current += len; 54 | Ok(()) 55 | } 56 | 57 | pub fn skip_until_alignment(&mut self, align: usize) -> KResult<()> { 58 | let next = align_up(self.current, align); 59 | if next > self.buffer.len() { 60 | kbail!("skip_until_alignment(): out of bounds"); 61 | } 62 | 63 | self.current = next; 64 | Ok(()) 65 | } 66 | 67 | pub fn consume_bytes(&mut self, len: usize) -> KResult<&'a [u8]> { 68 | if self.current + len > self.buffer.len() { 69 | kbail!("consume_bytes(): out of bounds"); 70 | } 71 | 72 | self.current += len; 73 | Ok(&self.buffer[self.current - len..self.current]) 74 | } 75 | } 76 | 77 | fn parse_str_field(bytes: &[u8]) -> KResult<&str> { 78 | core::str::from_utf8(bytes).map_err(|_e| kerror!("parse_str_field(): UTF-8 parsing error")) 79 | } 80 | 81 | fn parse_hex_field(bytes: &[u8]) -> KResult { 82 | usize::from_str_radix(parse_str_field(bytes)?, 16) 83 | .map_err(|_e| kerror!("parse_hex_field(): int parsing error")) 84 | } 85 | 86 | pub static INITRAM_FS: Once> = Once::new(); 87 | 88 | pub fn init() -> KResult<()> { 89 | INITRAM_FS.call_once(|| { 90 | let image = include_bytes!(concat!( 91 | env!("CARGO_MANIFEST_DIR"), 92 | "/initramfs/initramfs.img" 93 | )); 94 | if image.is_empty() { 95 | panic!("initramfs not embedded"); 96 | } 97 | 98 | log::info!("Parsing initramfs..."); 99 | Arc::new(InitRamFs::parse(image.as_slice()).expect("error parsing initramfs")) 100 | }); 101 | Ok(()) 102 | } 103 | 104 | pub fn get_root() -> Option<&'static RootFs> { 105 | Some(&INITRAM_FS.get()?.root) 106 | } 107 | 108 | pub struct InitRamFs { 109 | root: RootFs, 110 | } 111 | 112 | impl InitRamFs { 113 | pub fn parse(fs_image: &[u8]) -> KResult { 114 | let mut image = ByteParser::new(fs_image); 115 | let mut n_files = 0; 116 | let mut loaded_size = 0; 117 | let root = Arc::new(InitRamFsDir::new(String::new(), 2)); 118 | loop { 119 | if image.remaining_len() == 0 { 120 | break; 121 | } 122 | let magic = image.consume_bytes(6).and_then(parse_hex_field)?; 123 | if magic != 0x070701 { 124 | log::error!( 125 | "initramfs: invalid magic (expected {:#x}, got {:#x})", 126 | 0x070701, 127 | magic 128 | ); 129 | kbail!("parse(): invalid magic"); 130 | } 131 | 132 | let ino = parse_hex_field(image.consume_bytes(8)?)?; 133 | let mode = FileMode::new(parse_hex_field(image.consume_bytes(8)?)? as u32); 134 | let _uid = parse_hex_field(image.consume_bytes(8)?)?; 135 | let _gid = parse_hex_field(image.consume_bytes(8)?)?; 136 | let _nlink = parse_hex_field(image.consume_bytes(8)?)?; 137 | let _mtime = parse_hex_field(image.consume_bytes(8)?)?; 138 | let filesize = parse_hex_field(image.consume_bytes(8)?)?; 139 | let _dev_major = parse_hex_field(image.consume_bytes(8)?)?; 140 | let _dev_minor = parse_hex_field(image.consume_bytes(8)?)?; 141 | 142 | image.skip(16)?; 143 | 144 | let path_len = parse_hex_field(image.consume_bytes(8)?)?; 145 | if path_len == 0 { 146 | kbail!("parse(): path_len is 0"); 147 | } 148 | 149 | image.skip(8)?; 150 | 151 | let mut path = parse_str_field(image.consume_bytes(path_len - 1)?)?; 152 | 153 | if path.starts_with("./") { 154 | path = &path[1..]; 155 | } 156 | if path == "TRAILER!!!" { 157 | break; 158 | } 159 | 160 | if path.is_empty() { 161 | kbail!("parse(): empty path"); 162 | } 163 | image.skip(1)?; 164 | image.skip_until_alignment(4)?; 165 | 166 | let components = Path::new(path).components().peekable(); 167 | 168 | fn walk( 169 | mut components_peekable: Peekable, 170 | dir: DirRef, 171 | ) -> Option<(DirRef, String)> { 172 | let next = components_peekable.next(); 173 | next?; 174 | if components_peekable.peek().is_none() { 175 | return Some((dir, next.unwrap().to_string())); 176 | } 177 | 178 | let dir_clone = dir.clone(); 179 | if let Ok(child) = dir.lookup(next.unwrap()) { 180 | if let INode::Dir(next_dir) = child { 181 | return walk(components_peekable, next_dir.clone()); 182 | } else { 183 | return Some((dir_clone, child.get_name())); 184 | } 185 | } 186 | None 187 | } 188 | 189 | if path == "." { 190 | image.skip_until_alignment(4)?; 191 | continue; 192 | } 193 | 194 | let walk_result = walk(components, root.clone()); 195 | let (parent_dir, filename) = if let Some((parent, name)) = walk_result { 196 | (parent, name) 197 | } else { 198 | image.consume_bytes(filesize)?; 199 | image.skip_until_alignment(4)?; 200 | continue; 201 | }; 202 | 203 | let data = image.consume_bytes(filesize)?; 204 | if mode.is_symbolic_link() { 205 | let inode = INode::Symlink(Arc::new(InitRamFsSymlink { 206 | name: filename.clone(), 207 | dst: PathBuf::from(core::str::from_utf8(data).unwrap()), 208 | stat: Stat { 209 | inode_no: ino, 210 | mode, 211 | ..Stat::zeroed() 212 | }, 213 | })); 214 | parent_dir.insert(inode); 215 | } else if mode.is_directory() { 216 | let inode = INode::Dir(Arc::new(InitRamFsDir { 217 | parent: Weak::new(), 218 | inner: IrqMutex::new(DirInner { 219 | children: Vec::new(), 220 | stat: Stat { 221 | inode_no: ino, 222 | mode, 223 | ..Stat::zeroed() 224 | }, 225 | name: filename.clone(), 226 | }), 227 | })); 228 | parent_dir.insert(inode); 229 | } else if mode.is_regular_file() { 230 | let file = InitRamFsFile { 231 | name: IrqMutex::new(filename.clone()), 232 | data: IrqMutex::new(data.to_vec()), 233 | stat: IrqMutex::new(Stat { 234 | inode_no: ino, 235 | mode, 236 | size: FileSize(filesize as isize), 237 | ..Stat::zeroed() 238 | }), 239 | }; 240 | parent_dir.insert(INode::File(Arc::new(file))); 241 | } 242 | 243 | image.skip_until_alignment(4)?; 244 | n_files += 1; 245 | loaded_size += data.len(); 246 | } 247 | 248 | log::info!( 249 | "initramfs: found {} files taking up {} bytes", 250 | n_files, 251 | loaded_size 252 | ); 253 | 254 | Ok(InitRamFs { 255 | root: RootFs::new(root), 256 | }) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | use alloc::{string::String, sync::Arc}; 4 | use bitflags::bitflags; 5 | 6 | use crate::{ 7 | kerror, 8 | task::wait_queue::WaitQueue, 9 | userland::buffer::{UserBuffer, UserBufferMut}, 10 | util::{ctypes::c_short, KResult}, 11 | }; 12 | 13 | use self::{opened_file::OpenFlags, path::PathBuf, pipe::Pipe}; 14 | 15 | pub mod devfs; 16 | pub mod initramfs; 17 | pub mod opened_file; 18 | pub mod path; 19 | pub mod pipe; 20 | 21 | pub type FileRef = Arc; 22 | pub type DirRef = Arc; 23 | pub type SymlinkRef = Arc; 24 | 25 | pub static POLL_WAIT_QUEUE: WaitQueue = WaitQueue::new(); 26 | 27 | pub fn alloc_inode_no() -> usize { 28 | // Inode #1 is reserved for the root dir. 29 | static NEXT_INODE_NO: AtomicUsize = AtomicUsize::new(2); 30 | 31 | NEXT_INODE_NO.fetch_add(1, Ordering::AcqRel) 32 | } 33 | 34 | bitflags! { 35 | #[derive(Debug)] 36 | pub struct PollStatus: c_short { 37 | const POLLIN = 0x001; 38 | const POLLPRI = 0x002; 39 | const POLLOUT = 0x004; 40 | const POLLERR = 0x008; 41 | const POLLHUP = 0x010; 42 | const POLLNVAL = 0x020; 43 | const POLLRDNORM = 0x040; 44 | const POLLRDBAND = 0x080; 45 | const POLLWRNORM = 0x100; 46 | const POLLWRBAND = 0x200; 47 | } 48 | } 49 | 50 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 51 | #[repr(u8)] 52 | #[non_exhaustive] 53 | pub enum FileType { 54 | Directory = 4, 55 | Regular = 8, 56 | Link = 10, 57 | } 58 | 59 | /// for readdir(3) 60 | pub struct DirEntry { 61 | pub inode_no: usize, 62 | pub file_type: FileType, 63 | pub name: String, 64 | } 65 | 66 | /// The device file's ID. 67 | #[derive(Debug, Copy, Clone)] 68 | #[repr(transparent)] 69 | pub struct DevId(usize); 70 | 71 | /// The number of hard links. 72 | #[derive(Debug, Copy, Clone)] 73 | #[repr(transparent)] 74 | pub struct NLink(usize); 75 | 76 | /// The file size in bytes. 77 | #[derive(Debug, Copy, Clone)] 78 | #[repr(transparent)] 79 | pub struct FileSize(pub isize); 80 | 81 | /// The user ID. 82 | #[derive(Debug, Copy, Clone)] 83 | #[repr(transparent)] 84 | pub struct UId(u32); 85 | 86 | /// The Group ID. 87 | #[derive(Debug, Copy, Clone)] 88 | #[repr(transparent)] 89 | pub struct GId(u32); 90 | 91 | /// The size in bytes of a block file file system I/O operations. 92 | #[derive(Debug, Copy, Clone)] 93 | #[repr(transparent)] 94 | pub struct BlockSize(isize); 95 | 96 | /// The number of blocks. 97 | #[derive(Debug, Copy, Clone)] 98 | #[repr(transparent)] 99 | pub struct BlockCount(isize); 100 | 101 | #[derive(Debug, Copy, Clone)] 102 | #[repr(transparent)] 103 | pub struct Time(isize); 104 | 105 | #[derive(Debug, Copy, Clone)] 106 | #[repr(C, packed)] 107 | pub struct Stat { 108 | pub dev: DevId, 109 | pub inode_no: usize, 110 | pub nlink: NLink, 111 | pub mode: FileMode, 112 | pub uid: UId, 113 | pub gid: GId, 114 | pub pad0: u32, 115 | pub rdev: DevId, 116 | pub size: FileSize, 117 | pub blksize: BlockSize, 118 | pub blocks: BlockCount, 119 | pub atime: Time, 120 | pub mtime: Time, 121 | pub ctime: Time, 122 | } 123 | 124 | impl Stat { 125 | pub fn zeroed() -> Stat { 126 | Stat { 127 | dev: DevId(0), 128 | inode_no: 0, 129 | mode: FileMode(0), 130 | nlink: NLink(0), 131 | uid: UId(0), 132 | gid: GId(0), 133 | pad0: 0, 134 | rdev: DevId(0), 135 | size: FileSize(0), 136 | blksize: BlockSize(0), 137 | blocks: BlockCount(0), 138 | atime: Time(0), 139 | mtime: Time(0), 140 | ctime: Time(0), 141 | } 142 | } 143 | } 144 | 145 | pub const S_IFMT: u32 = 0o170000; 146 | pub const S_IFCHR: u32 = 0o020000; 147 | pub const S_IFDIR: u32 = 0o040000; 148 | pub const S_IFREG: u32 = 0o100000; 149 | pub const S_IFLNK: u32 = 0o120000; 150 | 151 | pub const O_ACCMODE: u32 = 0o3; 152 | 153 | // FIXME: OpenFlags also define these values. 154 | #[allow(unused)] 155 | pub const O_RDONLY: u32 = 0o0; 156 | pub const O_WRONLY: u32 = 0o1; 157 | pub const O_RDWR: u32 = 0o2; 158 | 159 | #[derive(Debug, Clone, Copy)] 160 | #[repr(transparent)] 161 | pub struct FileMode(u32); 162 | 163 | impl FileMode { 164 | pub fn new(val: u32) -> FileMode { 165 | FileMode(val) 166 | } 167 | 168 | pub fn access_mode(self) -> u32 { 169 | self.0 & O_ACCMODE 170 | } 171 | 172 | pub fn is_directory(self) -> bool { 173 | (self.0 & S_IFMT) == S_IFDIR 174 | } 175 | 176 | pub fn is_regular_file(self) -> bool { 177 | (self.0 & S_IFMT) == S_IFREG 178 | } 179 | 180 | pub fn is_symbolic_link(self) -> bool { 181 | (self.0 & S_IFMT) == S_IFLNK 182 | } 183 | } 184 | 185 | pub trait FsNode { 186 | fn get_name(&self) -> String; 187 | } 188 | 189 | pub trait File: FsNode { 190 | /// `open(2)`. 191 | fn open(&self, _options: &OpenFlags) -> KResult> { 192 | Ok(None) 193 | } 194 | 195 | /// `stat(2)`. 196 | fn stat(&self) -> KResult { 197 | Err(kerror!(EBADF, "stat(): not implemented")) 198 | } 199 | 200 | /// `readlink(2)`. 201 | fn readlink(&self) -> KResult { 202 | // "EINVAL - The named file is not a symbolic link." -- readlink(2) 203 | Err(kerror!(EINVAL, "readlink(): not a symbolic link")) 204 | } 205 | 206 | /// `poll(2)` and `select(2)`. 207 | fn poll(&self) -> KResult { 208 | Err(kerror!(EBADF, "poll(): not implemented")) 209 | } 210 | 211 | /// `ioctl(2)`. 212 | fn ioctl(&self, _cmd: usize, _arg: usize) -> KResult { 213 | Err(kerror!(EBADF, "ioctl(): not implemented")) 214 | } 215 | 216 | /// `read(2)`. 217 | fn read(&self, _offset: usize, _buf: UserBufferMut, _options: &OpenFlags) -> KResult { 218 | Err(kerror!(EBADF, "read(): not implemented")) 219 | } 220 | 221 | /// `write(2)`. 222 | fn write(&self, _offset: usize, _buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult { 223 | Err(kerror!(EBADF, "write(): not implemented")) 224 | } 225 | } 226 | 227 | pub trait Symlink: FsNode { 228 | fn link_location(&self) -> KResult; 229 | fn stat(&self) -> KResult; 230 | fn fsync(&self) -> KResult<()> { 231 | Ok(()) 232 | } 233 | } 234 | 235 | pub trait Directory: FsNode { 236 | fn insert(&self, inode: INode); 237 | 238 | /// Looks for an existing file. 239 | fn lookup(&self, name: &str) -> KResult; 240 | /// `stat(2)`. 241 | fn stat(&self) -> KResult; 242 | /// `fsync(2)`. 243 | fn fsync(&self) -> KResult<()> { 244 | Ok(()) 245 | } 246 | /// `readlink(2)`. 247 | fn readlink(&self) -> KResult { 248 | // "EINVAL - The named file is not a symbolic link." -- readlink(2) 249 | Err(kerror!(EINVAL, "readlink(): not a symbolic link")) 250 | } 251 | 252 | fn readdir(&self, index: usize) -> KResult>; 253 | 254 | fn unlink(&self, name: &str) -> KResult<()>; 255 | } 256 | 257 | #[derive(Clone)] 258 | pub enum INode { 259 | File(FileRef), 260 | Dir(DirRef), 261 | Symlink(SymlinkRef), 262 | Pipe(Arc), 263 | } 264 | 265 | impl INode { 266 | pub fn is_file(&self) -> bool { 267 | matches!(self, INode::File(_)) 268 | } 269 | 270 | pub fn is_dir(&self) -> bool { 271 | matches!(self, INode::Dir(_)) 272 | } 273 | 274 | pub fn is_symlink(&self) -> bool { 275 | matches!(self, INode::Symlink(_)) 276 | } 277 | 278 | pub fn is_pipe(&self) -> bool { 279 | matches!(self, INode::Pipe(_)) 280 | } 281 | 282 | pub fn as_file(&self) -> KResult<&FileRef> { 283 | match self { 284 | INode::File(file) => Ok(file), 285 | _ => Err(kerror!(EINVAL, "as_file(): not a file")), 286 | } 287 | } 288 | 289 | pub fn stat(&self) -> KResult { 290 | match self { 291 | INode::Dir(d) => d.stat(), 292 | INode::File(d) => d.stat(), 293 | INode::Symlink(d) => d.stat(), 294 | INode::Pipe(p) => p.stat(), 295 | } 296 | } 297 | 298 | pub fn as_dir(&self) -> KResult<&DirRef> { 299 | match self { 300 | INode::Dir(d) => Ok(d), 301 | _ => Err(kerror!(ENOTDIR, "as_dir(): not a directory")), 302 | } 303 | } 304 | 305 | pub fn as_symlink(&self) -> KResult<&SymlinkRef> { 306 | match self { 307 | INode::Symlink(s) => Ok(s), 308 | _ => Err(kerror!(EINVAL, "as_symlink(): not a symbolic link")), 309 | } 310 | } 311 | 312 | pub fn as_pipe(&self) -> KResult<&Arc> { 313 | match self { 314 | INode::Pipe(p) => Ok(p), 315 | _ => Err(kerror!(EINVAL, "as_pipe(): not a pipe")), 316 | } 317 | } 318 | } 319 | 320 | impl FsNode for INode { 321 | fn get_name(&self) -> String { 322 | match self { 323 | INode::Dir(d) => d.get_name(), 324 | INode::File(f) => f.get_name(), 325 | INode::Symlink(l) => l.get_name(), 326 | INode::Pipe(p) => p.get_name(), 327 | } 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /src/userland/syscall/syscall_impl/task.rs: -------------------------------------------------------------------------------- 1 | use core::{mem::size_of, ops::Add}; 2 | 3 | use alloc::{sync::Arc, vec::Vec}; 4 | use bitflags::bitflags; 5 | 6 | use crate::{ 7 | arch::time, 8 | fs::path::Path, 9 | kbail, kerror, 10 | mem::addr::VirtAddr, 11 | task::{current_task, get_scheduler, group::PgId, Task, TaskId, TaskState, JOIN_WAIT_QUEUE}, 12 | userland::{buffer::CStr, syscall::SyscallHandler}, 13 | util::{ctypes::c_int, errno::Errno, KResult}, 14 | }; 15 | 16 | use super::time::TimeSpec; 17 | 18 | const ARG_MAX: usize = 512; 19 | const ARG_LEN_MAX: usize = 4096; 20 | const ENV_MAX: usize = 512; 21 | const ENV_LEN_MAX: usize = 4096; 22 | 23 | impl SyscallHandler<'_> { 24 | pub fn sys_arch_prctl(&mut self, code: i32, uaddr: VirtAddr) -> KResult { 25 | arch_prctl(current_task(), code, uaddr)?; 26 | Ok(0) 27 | } 28 | 29 | pub fn sys_fork(&mut self) -> KResult { 30 | let current = current_task(); 31 | let _guard = current.arch_mut().address_space.temporarily_switch(); 32 | let child = current.fork(); 33 | Ok(child.pid().as_usize() as isize) 34 | } 35 | 36 | pub fn sys_clone( 37 | &mut self, 38 | _clone_flags: usize, 39 | user_stack: VirtAddr, 40 | r8: usize, 41 | args: VirtAddr, 42 | r9: usize, 43 | entry_point: VirtAddr, 44 | ) -> KResult { 45 | let child = current_task().clone_process(entry_point, user_stack, args, r8, r9, self.frame); 46 | Ok(child.pid().as_usize() as isize) 47 | } 48 | 49 | pub fn sys_execve( 50 | &mut self, 51 | path: &Path, 52 | argv_addr: VirtAddr, 53 | envp_addr: VirtAddr, 54 | ) -> KResult { 55 | let current = current_task(); 56 | let _guard = current.arch_mut().address_space.temporarily_switch(); 57 | log::debug!("Statting path {}", path); 58 | let exefile = current 59 | .root_fs 60 | .lock() 61 | .lookup(path, true)? 62 | .as_file()? 63 | .clone(); 64 | 65 | let mut argv = Vec::new(); 66 | for i in 0..ARG_MAX { 67 | let ptr = argv_addr.add(i * size_of::()); 68 | let str_ptr = unsafe { ptr.read_user::() }?; 69 | if str_ptr != 0 { 70 | argv.push(CStr::new(VirtAddr::new(str_ptr), ARG_LEN_MAX, false)?); 71 | } else { 72 | break; 73 | } 74 | } 75 | 76 | let mut envp = Vec::new(); 77 | for i in 0..ENV_MAX { 78 | let ptr = envp_addr.add(i * size_of::()); 79 | let str_ptr = unsafe { ptr.read_user::() }?; 80 | if str_ptr != 0 { 81 | envp.push(CStr::new(VirtAddr::new(str_ptr), ENV_LEN_MAX, false)?); 82 | } else { 83 | break; 84 | } 85 | } 86 | let argv: Vec<&[u8]> = argv.as_slice().iter().map(|s| s.as_bytes()).collect(); 87 | let envp: Vec<&[u8]> = envp.as_slice().iter().map(|s| s.as_bytes()).collect(); 88 | current.exec(exefile, &argv, &envp)?; 89 | Ok(0) 90 | } 91 | 92 | pub fn sys_exit(&mut self, status: c_int) -> KResult { 93 | get_scheduler().exit_current(status); 94 | Ok(0) 95 | } 96 | 97 | pub fn sys_set_tid_address(&mut self, _addr: VirtAddr) -> KResult { 98 | // todo: use addr 99 | Ok(current_task().pid().as_usize() as isize) 100 | } 101 | 102 | pub fn sys_getpid(&mut self) -> KResult { 103 | Ok(current_task().pid().as_usize() as isize) 104 | } 105 | 106 | pub fn sys_getppid(&mut self) -> KResult { 107 | Ok(current_task().ppid().as_usize() as isize) 108 | } 109 | 110 | pub fn sys_getpgid(&mut self, pid: TaskId) -> KResult { 111 | if pid.as_usize() == 0 { 112 | Ok(current_task().pgid().unwrap() as isize) 113 | } else { 114 | Ok(get_scheduler() 115 | .find_task(pid) 116 | .ok_or(kerror!(ESRCH, "sys_getpgid(): task not found"))? 117 | .pgid() 118 | .unwrap() as isize) 119 | } 120 | } 121 | 122 | pub fn sys_setpgid(&mut self, pid: TaskId, pgid: PgId) -> KResult { 123 | if pid.as_usize() == 0 { 124 | current_task() 125 | .group 126 | .borrow_mut() 127 | .upgrade() 128 | .unwrap() 129 | .lock() 130 | .set_pgid(pgid); 131 | } else { 132 | get_scheduler() 133 | .find_task(pid) 134 | .ok_or(kerror!(ESRCH, "sys_setpgid(): task not found"))? 135 | .group 136 | .borrow_mut() 137 | .upgrade() 138 | .unwrap() 139 | .lock() 140 | .set_pgid(pgid); 141 | } 142 | Ok(0) 143 | } 144 | } 145 | 146 | bitflags! { 147 | pub struct WaitOptions: c_int { 148 | const WNOHANG = 1; 149 | const WUNTRACED = 2; 150 | } 151 | } 152 | 153 | impl SyscallHandler<'_> { 154 | pub fn sys_wait4( 155 | &mut self, 156 | pid: TaskId, 157 | status: VirtAddr, 158 | options: WaitOptions, 159 | _rusage: VirtAddr, // could be null 160 | ) -> KResult { 161 | let (got_pid, status_val) = JOIN_WAIT_QUEUE.sleep_signalable_until(None, || { 162 | let current = current_task(); 163 | let children = current.children.lock(); 164 | if children.is_empty() { 165 | kbail!(ECHILD, "sys_wait4(): all subprocesses have exited"); 166 | } 167 | for child in children.iter() { 168 | if pid.as_usize() as isize > 0 && pid != child.pid() { 169 | continue; 170 | } 171 | 172 | if pid.as_usize() == 0 { 173 | todo!() 174 | } 175 | 176 | if let TaskState::ExitedWith(status_val) = child.get_state() { 177 | return Ok(Some((child.pid(), status_val))); 178 | } 179 | } 180 | 181 | if options.contains(WaitOptions::WNOHANG) { 182 | return Ok(Some((TaskId::new(0), 0))); 183 | } 184 | 185 | Ok(None) 186 | })?; 187 | 188 | log::debug!("wait4: status = {status_val}"); 189 | current_task() 190 | .children 191 | .lock() 192 | .retain(|p| p.pid() != got_pid); 193 | 194 | if status.value() != 0 { 195 | unsafe { status.write_user::(status_val) }?; 196 | } 197 | 198 | Ok(got_pid.as_usize() as isize) 199 | } 200 | 201 | pub fn sys_nanosleep(&mut self, req: VirtAddr, _rem: VirtAddr) -> KResult { 202 | let req = unsafe { req.read_user::() }?; 203 | assert_eq!(req.tv_nsec % 1000000, 0); 204 | let duration = req.tv_sec * 1000 + req.tv_nsec / 1000000; 205 | #[allow(clippy::redundant_guards)] 206 | match get_scheduler().sleep(Some(duration as usize)) { 207 | Ok(_) => {} 208 | Err(e) if e.errno == Some(Errno::EINTR) => { 209 | todo!() 210 | // return Err(KError::Errno { errno: Errno::EINTR, msg }) 211 | } 212 | Err(_) => { 213 | todo!() 214 | } 215 | } 216 | Ok(0) 217 | } 218 | 219 | pub fn sys_clock_gettime(&mut self, clk_id: usize, tp: VirtAddr) -> KResult { 220 | match clk_id { 221 | 0 => { 222 | let ts = time::get_rt_clock(); 223 | unsafe { tp.write_user(ts) }?; 224 | } 225 | 1 => { 226 | let ts = time::get_rt_clock(); 227 | unsafe { tp.write_user(ts) }?; 228 | } 229 | 2 => { 230 | let current = current_task(); 231 | let delta_ns = time::get_uptime_ns() - current.start_time.get().unwrap() * 1000000; 232 | let delta_sec = delta_ns / 1000000000; 233 | let ts = TimeSpec { 234 | tv_sec: delta_sec as isize, 235 | tv_nsec: (delta_ns % 1000000000) as isize, 236 | }; 237 | unsafe { tp.write_user(ts) }?; 238 | } 239 | 3 => { 240 | let current = current_task(); 241 | let delta_ns = time::get_uptime_ns() - current.start_time.get().unwrap() * 1000000; 242 | let delta_sec = delta_ns / 1000000000; 243 | let ts = TimeSpec { 244 | tv_sec: delta_sec as isize, 245 | tv_nsec: (delta_ns % 1000000000) as isize, 246 | }; 247 | unsafe { tp.write_user(ts) }?; 248 | } 249 | _ => unreachable!(), 250 | } 251 | 252 | Ok(0) 253 | } 254 | } 255 | 256 | fn arch_prctl(current_task: Arc, code: i32, addr: VirtAddr) -> KResult<()> { 257 | const ARCH_SET_FS: i32 = 0x1002; 258 | 259 | match code { 260 | ARCH_SET_FS => { 261 | current_task.arch_mut().set_fsbase(addr); 262 | } 263 | _ => kbail!(EINVAL, "arch_prctl(): unknown code"), 264 | } 265 | 266 | Ok(()) 267 | } 268 | -------------------------------------------------------------------------------- /src/userland/buffer.rs: -------------------------------------------------------------------------------- 1 | use core::{mem::size_of, ops::Add}; 2 | 3 | use alloc::string::{String, ToString}; 4 | 5 | use crate::{ 6 | kbail, kerror, 7 | mem::{addr::VirtAddr, addr_space::TmpAddrSpaceGuard}, 8 | task::current_task, 9 | util::{align_up, error::KResult}, 10 | }; 11 | 12 | #[allow(dead_code)] 13 | enum Inner<'a> { 14 | Slice(&'a [u8]), 15 | User { base: VirtAddr, len: usize }, 16 | } 17 | 18 | pub struct UserBuffer<'a> { 19 | inner: Inner<'a>, 20 | } 21 | 22 | impl<'a> UserBuffer<'a> { 23 | pub fn from_vaddr(vaddr: VirtAddr, len: usize) -> UserBuffer<'static> { 24 | UserBuffer { 25 | inner: Inner::User { base: vaddr, len }, 26 | } 27 | } 28 | 29 | pub fn from_slice(slc: &'a [u8]) -> UserBuffer<'a> { 30 | UserBuffer { 31 | inner: Inner::Slice(slc), 32 | } 33 | } 34 | 35 | #[allow(clippy::len_without_is_empty)] 36 | pub fn len(&self) -> usize { 37 | match &self.inner { 38 | Inner::Slice(slice) => slice.len(), 39 | Inner::User { len, .. } => *len, 40 | } 41 | } 42 | } 43 | 44 | #[allow(dead_code)] 45 | enum InnerMut<'a> { 46 | Slice(&'a mut [u8]), 47 | User { base: VirtAddr, len: usize }, 48 | } 49 | 50 | pub struct UserBufferMut<'a> { 51 | inner: InnerMut<'a>, 52 | } 53 | 54 | impl<'a> UserBufferMut<'a> { 55 | pub fn from_slice(slice: &'a mut [u8]) -> UserBufferMut<'a> { 56 | UserBufferMut { 57 | inner: InnerMut::Slice(slice), 58 | } 59 | } 60 | 61 | pub fn from_vaddr(vaddr: VirtAddr, len: usize) -> UserBufferMut<'static> { 62 | UserBufferMut { 63 | inner: InnerMut::User { base: vaddr, len }, 64 | } 65 | } 66 | 67 | #[allow(clippy::len_without_is_empty)] 68 | pub fn len(&self) -> usize { 69 | match &self.inner { 70 | InnerMut::Slice(slice) => slice.len(), 71 | InnerMut::User { len, .. } => *len, 72 | } 73 | } 74 | } 75 | 76 | pub unsafe fn user_strncpy_rust(dst: *mut u8, src: *const u8, max_len: usize) -> usize { 77 | let mut read_len = 0usize; 78 | loop { 79 | let byte = unsafe { src.add(read_len).read_volatile() }; 80 | if byte == b'\0' || read_len > max_len { 81 | break; 82 | } 83 | unsafe { dst.add(read_len).write_volatile(byte) }; 84 | read_len += 1; 85 | } 86 | read_len 87 | } 88 | 89 | pub struct CStr { 90 | string: String, 91 | } 92 | 93 | impl CStr { 94 | pub fn new(vaddr: VirtAddr, max_len: usize, is_user: bool) -> KResult { 95 | vaddr.align_ok::()?; 96 | if is_user { 97 | (vaddr + max_len).user_ok()?; 98 | } 99 | let mut tmp = alloc::vec![0; max_len]; 100 | let guard = current_task().arch_mut().address_space.temporarily_switch(); 101 | let read_len = unsafe { user_strncpy_rust(tmp.as_mut_ptr(), vaddr.as_raw_ptr(), max_len) }; 102 | drop(guard); 103 | let string = core::str::from_utf8(&tmp[..read_len]) 104 | .map_err(|_| kerror!(EINVAL, "UserCStr: UTF-8 parsing error"))? 105 | .to_string(); 106 | Ok(CStr { string }) 107 | } 108 | 109 | pub fn as_bytes(&self) -> &[u8] { 110 | self.string.as_bytes() 111 | } 112 | 113 | pub fn as_str(&self) -> &str { 114 | &self.string 115 | } 116 | } 117 | 118 | pub struct UserBufferReader<'a> { 119 | buf: UserBuffer<'a>, 120 | pos: usize, 121 | #[allow(dead_code)] 122 | guard: TmpAddrSpaceGuard, 123 | } 124 | 125 | impl<'a> UserBufferReader<'a> { 126 | pub fn from_vaddr(buf: VirtAddr, len: usize) -> UserBufferReader<'a> { 127 | let guard = current_task().arch_mut().address_space.temporarily_switch(); 128 | UserBufferReader { 129 | buf: UserBuffer::from_vaddr(buf, len), 130 | pos: 0, 131 | guard, 132 | } 133 | } 134 | 135 | pub fn from_buf(buf: UserBuffer<'a>) -> UserBufferReader<'a> { 136 | let guard = current_task().arch_mut().address_space.temporarily_switch(); 137 | UserBufferReader { buf, pos: 0, guard } 138 | } 139 | 140 | pub fn read_len(&self) -> usize { 141 | self.pos 142 | } 143 | 144 | pub fn skip(&mut self, len: usize) -> KResult<()> { 145 | self.check_remaining_len(len)?; 146 | self.pos += len; 147 | Ok(()) 148 | } 149 | 150 | pub fn read_bytes(&mut self, dst: &mut [u8]) -> KResult { 151 | let read_len = core::cmp::min(dst.len(), self.remaining_len()); 152 | if read_len == 0 { 153 | return Ok(0); 154 | } 155 | 156 | match &self.buf.inner { 157 | Inner::Slice(src) => { 158 | dst[..read_len].copy_from_slice(&src[self.pos..(self.pos + read_len)]) 159 | } 160 | Inner::User { base, .. } => unsafe { 161 | base.add(self.pos).read_bytes(&mut dst[..read_len])?; 162 | }, 163 | } 164 | 165 | self.pos += read_len; 166 | 167 | Ok(read_len) 168 | } 169 | 170 | pub fn read(&mut self) -> KResult { 171 | self.check_remaining_len(size_of::())?; 172 | 173 | let val = match &self.buf.inner { 174 | Inner::Slice(src) => { 175 | // this could cause a page fault if the inner slice of the buffer isn't mapped to the current page table! 176 | unsafe { *(src.as_ptr().add(self.pos) as *const T) } 177 | } 178 | Inner::User { base, .. } => unsafe { base.add(self.pos).read()? }, 179 | }; 180 | 181 | self.pos += size_of::(); 182 | 183 | Ok(val) 184 | } 185 | 186 | fn check_remaining_len(&self, len: usize) -> KResult<()> { 187 | if len <= self.remaining_len() { 188 | Ok(()) 189 | } else { 190 | kbail!(EINVAL, "check_remaining_len(): len out of bounds"); 191 | } 192 | } 193 | 194 | pub fn remaining_len(&self) -> usize { 195 | self.buf.len() - self.pos 196 | } 197 | } 198 | 199 | pub struct UserBufferWriter<'a> { 200 | buf: UserBufferMut<'a>, 201 | pos: usize, 202 | #[allow(dead_code)] 203 | guard: TmpAddrSpaceGuard, 204 | } 205 | 206 | impl<'a> UserBufferWriter<'a> { 207 | pub fn from_vaddr(buf: VirtAddr, len: usize) -> UserBufferWriter<'a> { 208 | let guard = current_task().arch_mut().address_space.temporarily_switch(); 209 | UserBufferWriter { 210 | buf: UserBufferMut::from_vaddr(buf, len), 211 | pos: 0, 212 | guard, 213 | } 214 | } 215 | 216 | pub fn from_buf(buf: UserBufferMut<'a>) -> UserBufferWriter<'a> { 217 | let guard = current_task().arch_mut().address_space.temporarily_switch(); 218 | UserBufferWriter { buf, pos: 0, guard } 219 | } 220 | 221 | pub fn written_len(&self) -> usize { 222 | self.pos 223 | } 224 | 225 | pub fn write(&mut self, value: T) -> KResult { 226 | let bytes = 227 | unsafe { core::slice::from_raw_parts(&value as *const T as *const u8, size_of::()) }; 228 | self.write_bytes(bytes) 229 | } 230 | 231 | pub fn write_bytes(&mut self, src: &[u8]) -> KResult { 232 | let copy_len = core::cmp::min(self.remaining_len(), src.len()); 233 | if copy_len == 0 { 234 | return Ok(0); 235 | } 236 | self.check_remaining_len(copy_len)?; 237 | 238 | match &mut self.buf.inner { 239 | InnerMut::Slice(dst) => { 240 | dst[self.pos..(self.pos + copy_len)].copy_from_slice(&src[..copy_len]); 241 | } 242 | InnerMut::User { base, .. } => { 243 | unsafe { base.add(self.pos).write_bytes(&src[..copy_len]) }?; 244 | } 245 | } 246 | 247 | self.pos += copy_len; 248 | Ok(copy_len) 249 | } 250 | 251 | pub fn skip_until_alignment(&mut self, alignment: usize) -> KResult<()> { 252 | let new_pos = align_up(self.pos, alignment); 253 | self.check_remaining_len(new_pos - self.pos)?; 254 | self.pos = new_pos; 255 | Ok(()) 256 | } 257 | 258 | pub fn fill(&mut self, value: u8, len: usize) -> KResult<()> { 259 | self.check_remaining_len(len)?; 260 | 261 | match &mut self.buf.inner { 262 | InnerMut::Slice(dst) => { 263 | dst[self.pos..(self.pos + len)].fill(value); 264 | } 265 | InnerMut::User { base, .. } => { 266 | unsafe { base.add(self.pos).fill(value, len) }?; 267 | } 268 | } 269 | 270 | self.pos += len; 271 | Ok(()) 272 | } 273 | 274 | pub fn write_bytes_or_zeros(&mut self, buf: &[u8], max_len: usize) -> KResult<()> { 275 | let zero_start = core::cmp::min(buf.len(), max_len); 276 | self.check_remaining_len(zero_start)?; 277 | self.write_bytes(&buf[..zero_start])?; 278 | self.fill(0, max_len - zero_start)?; 279 | Ok(()) 280 | } 281 | 282 | fn check_remaining_len(&self, len: usize) -> KResult<()> { 283 | if len <= self.remaining_len() { 284 | Ok(()) 285 | } else { 286 | kbail!(EINVAL, "check_remaining_len(): len out of bounds"); 287 | } 288 | } 289 | 290 | pub fn remaining_len(&self) -> usize { 291 | self.buf.len() - self.pos 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/vga_text.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Add; 2 | 3 | use lazy_static::lazy_static; 4 | 5 | #[allow(dead_code)] 6 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 7 | #[repr(u8)] 8 | pub enum Color { 9 | Black = 0, 10 | Blue = 1, 11 | Green = 2, 12 | Cyan = 3, 13 | Red = 4, 14 | Magenta = 5, 15 | Brown = 6, 16 | LightGray = 7, 17 | DarkGray = 8, 18 | LightBlue = 9, 19 | LightGreen = 10, 20 | LightCyan = 11, 21 | LightRed = 12, 22 | Pink = 13, 23 | Yellow = 14, 24 | White = 15, 25 | } 26 | 27 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 28 | #[repr(transparent)] 29 | pub struct ColorCode(u8); 30 | 31 | impl ColorCode { 32 | pub fn new(foreground: Color, background: Color) -> ColorCode { 33 | ColorCode(((background as u8) << 4) | (foreground as u8)) 34 | } 35 | 36 | pub fn background(self) -> Color { 37 | unsafe { core::mem::transmute((self.0 >> 4) & 0xf) } 38 | } 39 | 40 | pub fn foreground(self) -> Color { 41 | unsafe { core::mem::transmute(self.0 & 0xf) } 42 | } 43 | } 44 | 45 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 46 | #[repr(C)] 47 | struct ScreenChar { 48 | ascii_character: u8, 49 | color_code: ColorCode, 50 | } 51 | 52 | pub const VGA_BUFFER_START_PADDR: usize = 0xb8000; 53 | pub const BUFFER_HEIGHT: usize = 25; 54 | pub const BUFFER_WIDTH: usize = 80; 55 | 56 | #[repr(transparent)] 57 | struct Buffer { 58 | chars: [[volatile::Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], 59 | } 60 | 61 | pub struct Writer { 62 | x: usize, 63 | y: usize, 64 | color_code: ColorCode, 65 | buffer: &'static mut Buffer, 66 | } 67 | 68 | impl Writer { 69 | pub fn write_byte(&mut self, byte: u8) { 70 | match byte { 71 | 0x8 => self.backspace(), 72 | b'\n' => self.new_line(), 73 | b'\r' => self.x = 0, 74 | byte => { 75 | if self.x >= BUFFER_WIDTH - 1 { 76 | self.new_line(); 77 | } 78 | 79 | let row = self.y; 80 | let col = self.x; 81 | 82 | let color_code = self.color_code; 83 | self.buffer.chars[row][col].write(ScreenChar { 84 | ascii_character: byte, 85 | color_code, 86 | }); 87 | self.move_right(); 88 | } 89 | } 90 | self.cursor_color_hook(); 91 | } 92 | 93 | fn cursor_color_hook(&mut self) { 94 | // let cursor = self.buffer.chars[self.y][self.x].read(); 95 | // for y in 0..BUFFER_HEIGHT { 96 | // for x in 0..BUFFER_WIDTH { 97 | // let chr = self.buffer.chars[y][x].read(); 98 | // if y == self.y && x == self.x { 99 | // self.buffer.chars[y][x].write(ScreenChar { ascii_character: cursor.ascii_character, color_code: ColorCode::new(Color::White, Color::Cyan) }); 100 | // } else { 101 | // self.buffer.chars[y][x].write(ScreenChar { ascii_character: chr.ascii_character, color_code: self.color_code }); 102 | // } 103 | // } 104 | // } 105 | } 106 | 107 | pub fn backspace(&mut self) { 108 | let row = self.y; 109 | let col = self.x.saturating_sub(1); 110 | let color_code = self.color_code; 111 | self.buffer.chars[row][col].write(ScreenChar { 112 | ascii_character: b' ', 113 | color_code, 114 | }); 115 | self.x = col; 116 | self.cursor_color_hook(); 117 | } 118 | 119 | pub fn write_string(&mut self, s: &str) { 120 | for byte in s.bytes() { 121 | // match byte { 122 | // 0x20..=0x7e | b'\n' | b'\r' | 0x8 => self.write_byte(byte), 123 | // // _ => self.write_byte(0xfe), 124 | // _ => {}, 125 | // } 126 | self.write_byte(byte) 127 | } 128 | } 129 | 130 | fn new_line(&mut self) { 131 | if self.y >= BUFFER_HEIGHT - 1 { 132 | for row in 1..BUFFER_HEIGHT { 133 | for col in 0..BUFFER_WIDTH { 134 | let character = self.buffer.chars[row][col].read(); 135 | self.buffer.chars[row - 1][col].write(character); 136 | } 137 | } 138 | self.y = BUFFER_HEIGHT - 1; 139 | self.clear_row(self.y); 140 | self.x = 0; 141 | } else { 142 | self.y += 1; 143 | self.x = 0; 144 | } 145 | self.cursor_color_hook(); 146 | } 147 | 148 | fn clear_row(&mut self, row: usize) { 149 | let blank = ScreenChar { 150 | ascii_character: b' ', 151 | color_code: self.color_code, 152 | }; 153 | for col in 0..BUFFER_WIDTH { 154 | self.buffer.chars[row][col].write(blank); 155 | } 156 | self.cursor_color_hook(); 157 | } 158 | fn clear_until_end(&mut self) { 159 | let blank = ScreenChar { 160 | ascii_character: b' ', 161 | color_code: self.color_code, 162 | }; 163 | for col in self.x..BUFFER_WIDTH { 164 | self.buffer.chars[self.y][col].write(blank); 165 | } 166 | for row in self.y + 1..BUFFER_HEIGHT { 167 | self.clear_row(row); 168 | } 169 | self.cursor_color_hook(); 170 | } 171 | fn clear_until_beginning(&mut self) { 172 | let blank = ScreenChar { 173 | ascii_character: b' ', 174 | color_code: self.color_code, 175 | }; 176 | for col in 0..self.x { 177 | self.buffer.chars[self.y][col].write(blank); 178 | } 179 | for row in 0..self.y - 1 { 180 | self.clear_row(row); 181 | } 182 | self.cursor_color_hook(); 183 | } 184 | fn clear_until_eol(&mut self) { 185 | let blank = ScreenChar { 186 | ascii_character: b' ', 187 | color_code: self.color_code, 188 | }; 189 | for col in self.x..BUFFER_WIDTH { 190 | self.buffer.chars[self.y][col].write(blank); 191 | } 192 | self.cursor_color_hook(); 193 | } 194 | fn clear_from_bol(&mut self) { 195 | let blank = ScreenChar { 196 | ascii_character: b' ', 197 | color_code: self.color_code, 198 | }; 199 | for col in 0..self.x { 200 | self.buffer.chars[self.y][col].write(blank); 201 | } 202 | self.cursor_color_hook(); 203 | } 204 | fn clear_line(&mut self) { 205 | self.clear_row(self.y); 206 | } 207 | fn clear_all(&mut self) { 208 | for row in 0..BUFFER_HEIGHT { 209 | self.clear_row(row) 210 | } 211 | self.cursor_color_hook(); 212 | } 213 | fn move_up(&mut self) { 214 | let new_y = self.y.saturating_sub(1); 215 | let mut new_x = self.x; 216 | while new_x > 0 && self.buffer.chars[new_y][new_x].read().ascii_character == b' ' { 217 | new_x -= 1; 218 | } 219 | self.y = new_y; 220 | self.x = new_x; 221 | self.cursor_color_hook(); 222 | } 223 | fn move_down(&mut self) { 224 | let new_y = self.y.add(1).min(BUFFER_HEIGHT - 1); 225 | let mut new_x = self.x; 226 | while new_x > 0 && self.buffer.chars[new_y][new_x].read().ascii_character == b' ' { 227 | new_x -= 1; 228 | } 229 | self.y = new_y; 230 | self.x = new_x; 231 | self.cursor_color_hook(); 232 | } 233 | fn move_left(&mut self) { 234 | self.x = self.x.saturating_sub(1); 235 | self.cursor_color_hook(); 236 | } 237 | fn move_right(&mut self) { 238 | self.x = self.x.add(1).min(BUFFER_WIDTH - 1); 239 | self.cursor_color_hook(); 240 | } 241 | } 242 | 243 | impl core::fmt::Write for Writer { 244 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 245 | self.write_string(s); 246 | Ok(()) 247 | } 248 | } 249 | 250 | lazy_static! { 251 | pub static ref WRITER: spin::Mutex = spin::Mutex::new(Writer { 252 | y: 0, 253 | x: 0, 254 | color_code: ColorCode::new(Color::White, Color::Black), 255 | buffer: unsafe { &mut *(VGA_BUFFER_START_PADDR as *mut Buffer) }, 256 | }); 257 | } 258 | 259 | pub fn clear_screen() { 260 | WRITER.lock().clear_all() 261 | } 262 | 263 | pub fn backspace() { 264 | WRITER.lock().backspace() 265 | } 266 | 267 | pub fn set_color_code(color_code: ColorCode) { 268 | WRITER.lock().color_code = color_code; 269 | } 270 | 271 | pub fn get_color_code() -> ColorCode { 272 | WRITER.lock().color_code 273 | } 274 | 275 | pub fn set_cursor_x(x: usize) { 276 | WRITER.lock().x = x.min(BUFFER_WIDTH - 1); 277 | } 278 | 279 | pub fn set_cursor_y(y: usize) { 280 | WRITER.lock().y = y.min(BUFFER_HEIGHT - 1); 281 | } 282 | 283 | pub fn set_cursor_xy(xy: (usize, usize)) { 284 | set_cursor_x(xy.0.min(BUFFER_WIDTH - 1)); 285 | set_cursor_y(xy.1.min(BUFFER_HEIGHT - 1)); 286 | } 287 | 288 | pub fn cursor_xy() -> (usize, usize) { 289 | let writer = WRITER.lock(); 290 | (writer.x, writer.y) 291 | } 292 | 293 | pub fn write_byte(byte: u8) { 294 | WRITER.lock().write_byte(byte); 295 | } 296 | 297 | pub fn clear_until_end() { 298 | WRITER.lock().clear_until_end(); 299 | } 300 | 301 | pub fn clear_until_beginning() { 302 | WRITER.lock().clear_until_beginning(); 303 | } 304 | 305 | pub fn clear_from_bol() { 306 | WRITER.lock().clear_from_bol(); 307 | } 308 | 309 | pub fn clear_until_eol() { 310 | WRITER.lock().clear_until_eol(); 311 | } 312 | 313 | pub fn clear_line() { 314 | WRITER.lock().clear_line(); 315 | } 316 | 317 | pub fn move_up() { 318 | WRITER.lock().move_up(); 319 | } 320 | 321 | pub fn move_down() { 322 | WRITER.lock().move_down(); 323 | } 324 | 325 | pub fn move_left() { 326 | WRITER.lock().move_left(); 327 | } 328 | 329 | pub fn move_right() { 330 | WRITER.lock().move_right(); 331 | } 332 | 333 | #[macro_export] 334 | macro_rules! vga_print { 335 | ($($arg:tt)*) => ($crate::vga_text::_vga_print(format_args!($($arg)*))); 336 | } 337 | 338 | #[macro_export] 339 | macro_rules! vga_println { 340 | () => ($crate::vga_print!("\n")); 341 | ($($arg:tt)*) => ($crate::vga_print!("{}\n", format_args!($($arg)*))); 342 | } 343 | 344 | #[doc(hidden)] 345 | pub fn _vga_print(args: core::fmt::Arguments) { 346 | use core::fmt::Write; 347 | x86_64::instructions::interrupts::without_interrupts(|| { 348 | WRITER.lock().write_fmt(args).unwrap(); 349 | }); 350 | } 351 | -------------------------------------------------------------------------------- /src/userland/elf.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::ToOwned, string::String, vec::Vec}; 2 | use elfloader::{ElfBinary, ElfLoader, Entry}; 3 | use x86::random::rdrand_slice; 4 | 5 | use xmas_elf::{ 6 | program::Type, 7 | sections::{SectionData, ShType}, 8 | }; 9 | 10 | use crate::{ 11 | fs::{initramfs::get_root, opened_file::OpenFlags, path::Path, FileRef}, 12 | kerror, 13 | mem::{ 14 | addr::VirtAddr, addr_space::AddressSpace, allocator::alloc_kernel_frames, consts::PAGE_SIZE, 15 | }, 16 | task::vmem::{MMapFlags, MMapKind, MMapProt, Vmem}, 17 | userland::buffer::UserBufferMut, 18 | util::{align_up, KResult}, 19 | }; 20 | 21 | pub fn gen_stack_canary() -> [u8; 16] { 22 | let mut random_bytes = [0u8; 16]; 23 | unsafe { rdrand_slice(&mut random_bytes) }; 24 | random_bytes 25 | } 26 | 27 | #[repr(u64)] 28 | #[derive(Debug, Copy, Clone)] 29 | pub enum AuxvType { 30 | AtNull = 0, 31 | AtPhdr = 3, 32 | AtPhEnt = 4, 33 | AtPhNum = 5, 34 | AtEntry = 9, 35 | } 36 | 37 | #[derive(Clone)] 38 | pub struct SymTabEntry { 39 | pub name: String, 40 | pub value: u64, 41 | pub size: u64, 42 | } 43 | 44 | pub struct UserlandEntry { 45 | pub entry_point: VirtAddr, 46 | pub vmem: Vmem, 47 | pub fsbase: Option, 48 | pub addr_space: AddressSpace, 49 | pub hdr: [(AuxvType, usize); 4], 50 | pub symtab: Option>, 51 | } 52 | 53 | pub fn load_elf(file: FileRef) -> KResult { 54 | let len = file.stat()?.size.0 as usize; 55 | let current = AddressSpace::current(); 56 | // let mut buf = alloc::vec![0u8; len]; 57 | let mut addr_space = AddressSpace::new()?; 58 | addr_space.switch(); 59 | let frames = alloc_kernel_frames(align_up(len, PAGE_SIZE) / PAGE_SIZE)?; 60 | 61 | // let mp = addr_space.mapper().map(pages, PageTableFlags::PRESENT | PageTableFlags::WRITABLE)?; 62 | let buf = unsafe { 63 | core::slice::from_raw_parts_mut( 64 | frames.start_address().as_hhdm_virt().as_raw_ptr_mut(), 65 | frames.size_in_bytes(), 66 | ) 67 | }; 68 | let ubuf = UserBufferMut::from_slice(buf); 69 | file.read(0, ubuf, &OpenFlags::empty())?; 70 | current.switch(); 71 | let elf = ElfBinary::new(buf).map_err(|_e| kerror!("load_elf(): elf loader error"))?; 72 | 73 | let mut start_of_image = usize::MAX; 74 | let mut end_of_image = 0; 75 | for hdr in elf.program_headers() { 76 | if hdr.get_type().unwrap() == xmas_elf::program::Type::Load { 77 | end_of_image = end_of_image.max((hdr.virtual_addr() + hdr.mem_size()) as usize); 78 | start_of_image = end_of_image.min(hdr.virtual_addr() as usize); 79 | } 80 | } 81 | let mut symbol_table = None; 82 | for section in elf.file.section_iter() { 83 | if section.get_type() == Ok(ShType::SymTab) { 84 | let section_data = section.get_data(&elf.file); 85 | if let Ok(ref _section_data @ SectionData::SymbolTable64(symtab)) = section_data { 86 | symbol_table = Some( 87 | symtab 88 | .iter() 89 | .map(|e| SymTabEntry { 90 | name: e.get_name(&elf.file).unwrap().to_owned(), 91 | size: e.size(), 92 | value: e.value(), 93 | }) 94 | .collect::>(), 95 | ); 96 | } 97 | } 98 | } 99 | if symbol_table.is_none() { 100 | log::warn!("Couldn't get symbol table for ELF."); 101 | } 102 | log::debug!( 103 | "ELF loaded into memory at {:#x} .. {:#x}", 104 | start_of_image, 105 | end_of_image 106 | ); 107 | if elf.is_pie() { 108 | log::warn!("It's a PIE"); 109 | } 110 | 111 | let mut vmem = Vmem::new(); 112 | 113 | let load_offset = 114 | if elf.file.header.pt2.type_().as_type() == xmas_elf::header::Type::SharedObject { 115 | 0x40000000 116 | } else { 117 | 0 118 | }; 119 | 120 | let entry_point = VirtAddr::new(elf.entry_point() as usize + load_offset); 121 | 122 | log::debug!("Entry point: {:?}", entry_point); 123 | 124 | addr_space.switch(); 125 | let mut loader = KadosElfLoader { 126 | vmem: &mut vmem, 127 | addr_space: &mut addr_space, 128 | base_addr: usize::MAX, 129 | load_offset, 130 | file: file.clone(), 131 | entry_point, 132 | }; 133 | 134 | elf.load(&mut loader).unwrap(); 135 | 136 | let p2 = elf.file.header.pt2; 137 | log::debug!("Base address at {:?}", VirtAddr::new(loader.base_addr)); 138 | let hdr = [ 139 | (AuxvType::AtPhdr, p2.ph_offset() as usize + loader.base_addr), 140 | (AuxvType::AtPhEnt, p2.ph_entry_size() as usize), 141 | (AuxvType::AtPhNum, p2.ph_count() as usize), 142 | (AuxvType::AtEntry, p2.entry_point() as usize), 143 | ]; 144 | 145 | if let Some(ref symtab) = symbol_table { 146 | for sym in symtab.iter() { 147 | if sym.name == "__stack_chk_fail" { 148 | // unsafe { 149 | // *(sym.value as *mut u8) = 0xc3; // "ret" instruction 150 | // } 151 | log::warn!("SSP is ON for this binary!"); 152 | break; 153 | } 154 | } 155 | } 156 | current.switch(); 157 | 158 | log::debug!("ELF load complete."); 159 | Ok(UserlandEntry { 160 | entry_point: loader.entry_point, 161 | vmem, 162 | fsbase: None, 163 | addr_space, 164 | hdr, 165 | symtab: symbol_table, 166 | }) 167 | } 168 | 169 | struct KadosElfLoader<'a> { 170 | vmem: &'a mut Vmem, 171 | addr_space: &'a mut AddressSpace, 172 | base_addr: usize, 173 | load_offset: usize, 174 | file: FileRef, 175 | entry_point: VirtAddr, 176 | } 177 | 178 | impl ElfLoader for KadosElfLoader<'_> { 179 | fn allocate( 180 | &mut self, 181 | load_headers: elfloader::LoadableHeaders, 182 | ) -> Result<(), elfloader::ElfLoaderErr> { 183 | for header in load_headers { 184 | if header.get_type().unwrap() == Type::Load { 185 | let start = VirtAddr::new(header.virtual_addr() as usize + self.load_offset) 186 | .align_down(PAGE_SIZE); 187 | let mem_end = VirtAddr::new( 188 | header.virtual_addr() as usize + header.mem_size() as usize + self.load_offset, 189 | ) 190 | .align_up(PAGE_SIZE); 191 | if start.value() < self.base_addr { 192 | self.base_addr = start.value(); 193 | } 194 | let flags = MMapFlags::empty(); 195 | let mut prot = MMapProt::PROT_WRITE; 196 | if header.flags().is_execute() { 197 | prot.insert(MMapProt::PROT_EXEC); 198 | } 199 | let kind = MMapKind::File { 200 | file: self.file.clone(), 201 | offset: header.offset() as usize, 202 | size: header.file_size() as usize, 203 | }; 204 | log::debug!("Mapping region {:?} .. {:?}", start, mem_end); 205 | self.addr_space.with_mapper(|mut mapper| { 206 | self.vmem 207 | .map_area(start, mem_end, flags, prot, kind, &mut mapper) 208 | .unwrap(); 209 | }); 210 | } else if header.get_type().unwrap() == Type::Interp { 211 | let ld = get_root() 212 | .unwrap() 213 | .lookup(Path::new("/usr/lib/ld.so"), true) 214 | .unwrap() 215 | .as_file() 216 | .unwrap() 217 | .clone(); 218 | let res = load_elf(ld).unwrap(); 219 | self.entry_point = res.entry_point; 220 | } 221 | } 222 | 223 | Ok(()) 224 | } 225 | 226 | fn load( 227 | &mut self, 228 | flags: elfloader::Flags, 229 | base: elfloader::VAddr, 230 | region: &[u8], 231 | ) -> Result<(), elfloader::ElfLoaderErr> { 232 | let region_start = VirtAddr::new(base as usize + self.load_offset); 233 | let region_end = region_start + region.len(); 234 | let area = self 235 | .vmem 236 | .area_containing_mut(region_start, region_end) 237 | .unwrap(); 238 | let mut prot = MMapProt::empty(); 239 | if flags.is_read() { 240 | prot |= MMapProt::PROT_READ; 241 | } 242 | if flags.is_write() { 243 | prot |= MMapProt::PROT_WRITE; 244 | } 245 | if flags.is_execute() { 246 | prot |= MMapProt::PROT_EXEC; 247 | } 248 | // this should be safe since the pages should already be mapped and writable in allocate() 249 | unsafe { region_start.write_bytes(region).unwrap() }; 250 | // set the correct protections now 251 | area.prot = prot; 252 | Ok(()) 253 | } 254 | 255 | fn tls( 256 | &mut self, 257 | tdata_start: elfloader::VAddr, 258 | _tdata_length: u64, 259 | _total_size: u64, 260 | _align: u64, 261 | ) -> Result<(), elfloader::ElfLoaderErr> { 262 | log::warn!( 263 | "TLS section found at {:?}", 264 | VirtAddr::new(tdata_start as usize) 265 | ); 266 | Ok(()) 267 | } 268 | 269 | fn relocate( 270 | &mut self, 271 | entry: elfloader::RelocationEntry, 272 | ) -> Result<(), elfloader::ElfLoaderErr> { 273 | use elfloader::arch::x86_64::RelocationTypes; 274 | match entry.rtype { 275 | elfloader::RelocationType::x86_64(rtype) => match rtype { 276 | RelocationTypes::R_AMD64_RELATIVE => { 277 | let reloc_value = entry.addend.unwrap() as usize + self.load_offset; 278 | log::trace!( 279 | "Applying relocation R_AMD64_RELATIVE at location {:#x} -> {:#x}", 280 | entry.offset, 281 | reloc_value 282 | ); 283 | unsafe { 284 | *((entry.offset + self.load_offset as u64) as *mut usize) = reloc_value; 285 | } 286 | } 287 | rtype => { 288 | log::error!("Unsupported relocation type: {:?}", rtype); 289 | return Err(elfloader::ElfLoaderErr::UnsupportedRelocationEntry); 290 | } 291 | }, 292 | _ => return Err(elfloader::ElfLoaderErr::UnsupportedArchitecture), 293 | } 294 | Ok(()) 295 | } 296 | } 297 | --------------------------------------------------------------------------------