diff --git a/Cargo.lock b/Cargo.lock index d60f3b3..22aa050 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,26 +67,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" -[[package]] -name = "enumflags2" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" -dependencies = [ - "enumflags2_derive", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "fdt" version = "0.2.0-alpha1" @@ -103,13 +83,15 @@ name = "gila" version = "0.3.0" dependencies = [ "acpi", - "enumflags2", + "bitflags", "fdt", "flagset", "intbits", "lazy_static", "limine", "lzma-rs", + "num-derive", + "num-traits", "once_cell", "spin 0.10.0", "talc", @@ -173,6 +155,17 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-traits" version = "0.2.19" diff --git a/Cargo.toml b/Cargo.toml index 8c7ee02..d0cfc45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,13 +5,15 @@ edition = "2024" [dependencies] acpi = "5.1.0" -enumflags2 = "0.7.11" +bitflags = "2.9.1" fdt = { git = "https://github.com/repnop/fdt", version = "0.2.0-alpha1" } flagset = "0.4.7" intbits = "0.2.0" lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] } limine = "0.4.0" lzma-rs = { git = "https://github.com/glaeqen/lzma-no-std-rs/", version = "0.2.0", default-features = false } +num-derive = "0.4.2" +num-traits = { version = "0.2.19", default-features = false } once_cell = { version = "1.20.3", default-features = false, features = ["alloc", "critical-section"] } spin = "0.10.0" talc = "4.4.2" diff --git a/DESIGN.md b/DESIGN.md index fd4ac97..03fe8cf 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,11 +1,11 @@ # Design Outline -Gila is a microkernel, and almost all functionality of the OS is relegated to -"server" processes. A server is a process that provides a specific -functionality, using a kernel-provided interface, and it is given a "seat" by +Gila is a microkernel, and almost all functionality of the OS is relegated to +"server" processes. A server is a process that provides a specific +functionality, using a kernel-provided interface, and it is given a "seat" by the kernel which processes may query to reference it and invoke its functions. -# Boot Process +## Boot Process Gila initializes as a bare kernel, with the bootloader providing an init RAM filesystem in the form of a .tar.lzma archive. The kernel reads this file, and @@ -32,17 +32,20 @@ The benefit of this approach is threefold: The system, hence, can be configured in two ways: - The drivers can all be included in the initramfs for diskless operation. -- The bare minimum drivers needed for disk access are included in the +- The bare minimum drivers needed for disk access are included in the initramfs, and all other drivers are included in the root filesystem. -# APIs +## APIs Processes access services similarly to the way the init process accesses -data from the file system. The process requests the ID of the server that +data from the file system. The process requests the ID of the server that performs the function, and then communicates with it via IPC and shared memory regions. APIs are to be defined in the standard library. -# Device Drivers +Conventions for local procedure calls and message busing will be worked on +soon. + +## Device Drivers Device driver assignment is performed like so: @@ -54,4 +57,26 @@ Device driver assignment is performed like so: into the process's memory space, and returns information on the new mappings to the process. - The driver then makes its functions available via IPC and shared memory. -- The kernel stores information about the driver to avoid spawning duplicates. \ No newline at end of file +- The kernel stores information about the driver to avoid spawning duplicates. + +## Servers vs. Shared Libraries + +Servers and shared libraries serve similar purposes: They make some +functionality usable by any process without code duplication. However, there +are times when processes and developers should prefer one or the other. + +A server should be used when: + +- The function must somehow acquire a mutually exclusive lock on a resource. +- The function should complete asynchronously. + +A shared library should be used when: + +- No resources involved need to be mutually exclusive. +- The function is non-blocking and synchronous. + +Hence, servers are very important for things like disk drivers and file +system drivers, where non-synchronized writes could cause data loss. It should +also be noted that libraries *can*, and often will, call local procedures +from servers. The functionality of calling a procedure will be handled by the +standard library and shared across processes. diff --git a/src/kernel/arch/mod.rs b/src/kernel/arch/mod.rs index c894aa2..f9bc616 100644 --- a/src/kernel/arch/mod.rs +++ b/src/kernel/arch/mod.rs @@ -5,6 +5,7 @@ pub mod x86_64; #[cfg(target_arch = "x86_64")] pub use x86_64::asm; +pub use x86_64::paging; #[cfg(target_arch = "aarch64")] pub mod aarch64; diff --git a/src/kernel/arch/riscv64/asm.rs b/src/kernel/arch/riscv64/asm.rs index d763d5a..1570968 100644 --- a/src/kernel/arch/riscv64/asm.rs +++ b/src/kernel/arch/riscv64/asm.rs @@ -4,7 +4,7 @@ use core::arch::asm; #[allow(clippy::missing_safety_doc)] -pub unsafe fn halt() { +pub fn halt() { unsafe { asm!("wfi"); } diff --git a/src/kernel/arch/x86_64/asm.rs b/src/kernel/arch/x86_64/asm.rs index 38913b5..30fc7e8 100644 --- a/src/kernel/arch/x86_64/asm.rs +++ b/src/kernel/arch/x86_64/asm.rs @@ -6,31 +6,53 @@ use core::arch::asm; -pub unsafe fn cr0() -> u64 { - let mut cr0: u64; +pub fn read_cr0() -> u64 { + let cr0: u64; unsafe { asm!("mov {0:r}, cr0", out(reg) cr0) } cr0 } -pub unsafe fn interrupt_disable() { +pub fn read_cr2() -> usize { + let cr2: usize; + unsafe { asm!("mov {0:r}, cr2", out(reg) cr2) } + cr2 +} + +pub fn read_cr3() -> u64 { + let cr3: u64; + unsafe { asm!("mov {0:r}, cr3", out(reg) cr3) } + cr3 +} + +pub fn write_cr3(val: u64) { + unsafe { asm!("mov cr3, {0:r}", in(reg) val) } +} + +pub fn interrupt_disable() { unsafe { asm!("cli"); } } -pub unsafe fn interrupt_enable() { +pub fn interrupt_enable() { unsafe { asm!("sti"); } } -pub unsafe fn halt() { +pub fn halt() { unsafe { asm!("hlt"); } } -pub unsafe fn port_read_u8(port: u16) -> u8 { +pub fn nop() { + unsafe { + asm!("nop"); + } +} + +pub fn port_read_u8(port: u16) -> u8 { let result: u8; unsafe { asm! { @@ -42,7 +64,7 @@ pub unsafe fn port_read_u8(port: u16) -> u8 { result } -pub unsafe fn port_read_u16(port: u16) -> u16 { +pub fn port_read_u16(port: u16) -> u16 { let result: u16; unsafe { asm! { @@ -54,7 +76,7 @@ pub unsafe fn port_read_u16(port: u16) -> u16 { result } -pub unsafe fn port_read_u32(port: u16) -> u32 { +pub fn port_read_u32(port: u16) -> u32 { let result: u32; unsafe { asm! { @@ -66,7 +88,7 @@ pub unsafe fn port_read_u32(port: u16) -> u32 { result } -pub unsafe fn port_write_u8(port: u16, data: u8) { +pub fn port_write_u8(port: u16, data: u8) { unsafe { asm! { "out dx, al", @@ -76,7 +98,7 @@ pub unsafe fn port_write_u8(port: u16, data: u8) { } } -pub unsafe fn port_write_u16(port: u16, data: u16) { +pub fn port_write_u16(port: u16, data: u16) { unsafe { asm! { "out dx, ax", @@ -86,7 +108,7 @@ pub unsafe fn port_write_u16(port: u16, data: u16) { } } -pub unsafe fn port_write_u32(port: u16, data: u32) { +pub fn port_write_u32(port: u16, data: u32) { unsafe { asm! { "out dx eax", diff --git a/src/kernel/arch/x86_64/gdt.rs b/src/kernel/arch/x86_64/gdt.rs index af82180..74a594a 100644 --- a/src/kernel/arch/x86_64/gdt.rs +++ b/src/kernel/arch/x86_64/gdt.rs @@ -5,8 +5,33 @@ use core::arch::asm; use core::ptr::addr_of_mut; use intbits::Bits; +use x86_64::structures::gdt::*; -pub unsafe fn gdt() -> (u8, u64) { +#[allow(dead_code)] +pub trait GdtEntryRead { + fn base(&self) -> usize; + fn limit(&self) -> usize; + fn flags(&self) -> u8; + fn access(&self) -> u8; +} + +impl GdtEntryRead for Entry { + fn base(&self) -> usize { + ((self.raw().bits(56..=63) << 24) & self.raw().bits(16..=39)) as usize + } + fn limit(&self) -> usize { + ((self.raw().bits(48..=51) << 16) & self.raw().bits(16..=31)) as usize + } + fn access(&self) -> u8 { + self.raw().bits(40..=47) as u8 + } + fn flags(&self) -> u8 { + self.raw().bits(52..=55) as u8 + } +} + +#[allow(dead_code)] +pub fn read_gdt() -> GlobalDescriptorTable { let mut addr: u128 = 0; unsafe { asm! { @@ -14,5 +39,10 @@ pub unsafe fn gdt() -> (u8, u64) { in("rax") addr_of_mut!(addr), } } - (addr.bits(0..=15) as u8, addr.bits(16..=79) as u64) + // x86 gdtr: Lower 2 8-bit bytes (lower 16 bits) is the size of the GDT, + // in 8-bit bytes, minus one. + // Bits 16 through 79 are the virtual address of the GDT. + let (limit, addr) = (addr.bits(0..=15) as usize + 1, addr.bits(16..=79) as u64); + let gdt_raw: &[u64] = unsafe { core::slice::from_raw_parts(addr as *const u64, limit / 8) }; + GlobalDescriptorTable::from_raw_entries(gdt_raw) } diff --git a/src/kernel/arch/x86_64/interrupts.rs b/src/kernel/arch/x86_64/interrupts.rs index 73d1149..a38f919 100644 --- a/src/kernel/arch/x86_64/interrupts.rs +++ b/src/kernel/arch/x86_64/interrupts.rs @@ -10,6 +10,7 @@ lazy_static! { pub static ref IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); idt.double_fault.set_handler_fn(double_fault); + idt.page_fault.set_handler_fn(page_fault); idt }; } @@ -17,3 +18,24 @@ lazy_static! { extern "x86-interrupt" fn double_fault(info: InterruptStackFrame, _: u64) -> ! { crate::interrupt::double_fault(&format!("{info:#?}")); } + +extern "x86-interrupt" fn page_fault(info: InterruptStackFrame, errcode: PageFaultErrorCode) { + if errcode.contains(PageFaultErrorCode::USER_MODE) { + // Fault occurred in usermode. Non fatal. + todo!() + } else { + // Fault occurred in kernel mode. This is possibly fatal. + // This is recoverable if we simply hit an unavailable page, + // but unrecoverable if we hit a nonexistent or invalid page. + if errcode.contains(PageFaultErrorCode::PROTECTION_VIOLATION) + | errcode.contains(PageFaultErrorCode::MALFORMED_TABLE) + { + let addr = unsafe { + let a: usize; + core::arch::asm! {"mov {}, cr2", out(reg) a}; + a + }; + crate::interrupt::page_fault(addr, &format!("{info:#?}")) + } + } +} diff --git a/src/kernel/arch/x86_64/paging.rs b/src/kernel/arch/x86_64/paging.rs index 4f15a67..c86c864 100644 --- a/src/kernel/arch/x86_64/paging.rs +++ b/src/kernel/arch/x86_64/paging.rs @@ -1,8 +1,8 @@ #![allow(dead_code)] use intbits::Bits; +use x86_64::structures::paging::page_table; -#[repr(packed)] pub struct PageMapLevel5 { entries: [PageMapLevel5Entry; 512], } @@ -64,7 +64,6 @@ impl PageMapLevel5Entry { } } -#[repr(packed)] pub struct PageMapLevel4 { entries: [PageMapLevel4Entry; 512], } @@ -129,13 +128,12 @@ impl PageMapLevel4Entry { } } -#[repr(packed)] pub struct PageDirectoryPointerTable { entries: [*mut PageDirectory; 4], } pub struct PageDirectoryPointer { - value: u64 + value: u64, } impl PageDirectoryPointer { @@ -255,12 +253,4 @@ impl PageDirectoryEntry { } } -#[repr(align(4096))] -pub struct PageTable { - entries: [PageTableEntry; 512], -} - -#[repr(packed)] -pub struct PageTableEntry { - value: u64, -} +pub use page_table::PageTable; diff --git a/src/kernel/arch/x86_64/serial.rs b/src/kernel/arch/x86_64/serial.rs index f684cd8..d2e03e4 100644 --- a/src/kernel/arch/x86_64/serial.rs +++ b/src/kernel/arch/x86_64/serial.rs @@ -12,7 +12,7 @@ pub struct Serialport { impl LogSubscriber for Serialport { fn write(&self, msg: &str) { for c in msg.chars() { - unsafe { port_write_u8(self.port, c as u8) }; + port_write_u8(self.port, c as u8); } } } diff --git a/src/kernel/device/mod.rs b/src/kernel/device/mod.rs index fc8f47f..109da1b 100644 --- a/src/kernel/device/mod.rs +++ b/src/kernel/device/mod.rs @@ -10,7 +10,7 @@ use limine::response::{DeviceTreeBlobResponse, RsdpResponse}; use crate::boot::DTB_REQUEST; use crate::device::acpi::RSDP_REQUEST; use crate::log::{LOGGER, LogLevel}; -use crate::{format, log_info, log_trace, log_warning}; +use crate::{format, log_error, log_info, log_trace, log_warning}; lazy_static! { pub static ref DTB: Option<&'static DeviceTreeBlobResponse> = match DTB_REQUEST.get_response() { @@ -37,8 +37,11 @@ lazy_static! { // Just to check pub fn init_statics() { - let _ = DTB.is_some(); - let _ = RSDP.is_some(); + let dtb = DTB.is_some(); + let rdsp = RSDP.is_some(); + if !dtb & !rdsp { + log_error!("Device: Neither DTB nor ACPI available, booted system will be useless") + } } #[allow(dead_code)] diff --git a/src/kernel/interrupt/mod.rs b/src/kernel/interrupt/mod.rs index 407267d..7d8ee53 100644 --- a/src/kernel/interrupt/mod.rs +++ b/src/kernel/interrupt/mod.rs @@ -4,3 +4,7 @@ pub fn double_fault(info: &str) -> ! { panic!("Double fault: {}", info); } + +pub fn page_fault(addr: usize, info: &str) -> ! { + panic!("Page fault at 0x{:X}: {}", addr, info); +} diff --git a/src/kernel/log.rs b/src/kernel/log.rs index 1135ad7..9c2c70c 100644 --- a/src/kernel/log.rs +++ b/src/kernel/log.rs @@ -67,7 +67,7 @@ impl Logger { } } -/// The logger exists for the entire lifetime of the kernel. +// The logger exists for the entire lifetime of the kernel. pub static LOGGER: Logger = Logger { inner: Mutex::new(LoggerInner::new()), }; @@ -97,8 +97,8 @@ impl LoggerInner { } } - /// Calling log will sequentially acquire lock on all logging subscribers - /// to write to them with a formatted log message. + // Calling log will sequentially acquire lock on all logging subscribers + // to write to them with a formatted log message. pub fn log(&self, level: LogLevel, msg: &str) { // Nobody is EVER allowed to call log with the Disabled log level. It is a placeholder. if level == LogLevel::Disabled { diff --git a/src/kernel/main.rs b/src/kernel/main.rs index 23e3c7c..fb8f38f 100644 --- a/src/kernel/main.rs +++ b/src/kernel/main.rs @@ -19,14 +19,9 @@ mod process; #[macro_use] mod util; -use core::arch::asm; - -use arch::x86_64::gdt::gdt; -use arch::x86_64::paging::{self, PageDirectory, PageMapLevel4Entry, PageMapLevel5, PageMapLevel5Entry}; #[cfg(target_arch = "x86_64")] use arch::x86_64::serial::Serialport; -use arch::asm::*; use boot::{modules::*, params, *}; use constants::*; use log::*; @@ -34,7 +29,6 @@ use memory::alloc::{format, string::*, vec}; use memory::{HHDM_RESPONSE, MEMMAP_REQUEST}; use params::*; -use intbits::{self, Bits}; use limine::memory_map::EntryType; #[allow(unused_imports)] use lzma_rs::lzma_decompress; @@ -126,7 +120,7 @@ unsafe extern "C" fn main() -> ! { let mut unusable: u64 = 0; for entry in mmap_response.entries() { log_msg.push_str(&format!( - "\n\t\t0x{:x} bytes @ 0x{:x}: {}", + "\n\t\t0x{:X} bytes @ 0x{:X}: {}", entry.length, entry.base, match entry.entry_type { @@ -178,56 +172,14 @@ unsafe extern "C" fn main() -> ! { panic!("Memory map contains no entries"); } - log_info!("HHDM offset: 0x{:x}", HHDM_RESPONSE.offset()); - - #[cfg(target_arch = "x86_64")] - { - log_trace!( - "Boot: Protected mode enable: {}", - unsafe { cr0() & 0b1 } == 1 - ); - log_trace!( - "Boot: Paging enable: {}", - unsafe { cr0() & (0b1 << 31) } == 1 - ); - let pd_addr = { - let cr3: u64; - unsafe { - asm! { - "mov {}, cr3", - out(reg) cr3, - } - } - log_trace!("Boot: cr3 value: 0b{:b}", cr3); - cr3.bits(12..=31) - }; - log_trace!("Boot: Physical address of page directory: 0x{:x}", pd_addr); - log_trace!( - "Boot: Virtual address of page directory: 0x{:x}", - pd_addr + HHDM_RESPONSE.offset() - ); - log_trace!( - "Boot: GDT: 0x{:x} bytes @ 0x{:x}", - unsafe { gdt().0 }, - unsafe { gdt().1 } - ); - log_trace!("Boot: GDT: 0x{:x}", unsafe { - *((gdt().1 + 0x0) as *const u64) - }); - log_trace!( - "Size of page directory: {}", - size_of::() - ); - } + log_info!("HHDM offset: 0x{:X}", HHDM_RESPONSE.offset()); panic!("Bailing"); #[allow(unreachable_code)] loop { for _i in 0..50000000u64 { - unsafe { - core::arch::asm!("nop"); - } + crate::arch::asm::nop(); } core::hint::black_box(()); log_trace!("Heartbeat"); diff --git a/src/kernel/memory.rs b/src/kernel/memory.rs index 30e77c0..1da72f0 100644 --- a/src/kernel/memory.rs +++ b/src/kernel/memory.rs @@ -1,16 +1,12 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: GPL-3.0-or-later -#![allow(unused_imports)] - -use enumflags2::*; use lazy_static::lazy_static; use limine::{ request::{ExecutableAddressRequest, HhdmRequest, MemoryMapRequest}, response::HhdmResponse, }; -use core::alloc::{Allocator, Layout}; use talc::*; pub extern crate alloc; @@ -34,36 +30,10 @@ lazy_static! { .expect("Did not get HHDM response from bootloader"); } +// TODO: 10kb kernel heap. Need to figure out how to make this less stupid... static mut ARENA: [u8; 10000] = [0; 10000]; #[global_allocator] -static ALLOCATOR: Talck, ClaimOnOom> = Talc::new(unsafe { - // if we're in a hosted environment, the Rust runtime may allocate before - // main() is called, so we need to initialize the arena automatically - ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) -}) -.lock(); - -#[allow(dead_code)] -pub struct MemoryRegion { - start_address: usize, - end_address: usize, - flags: BitFlags, -} - -impl MemoryRegion { - // TODO: Memory allocation and virtual addressing - #[allow(unused_variables)] - pub fn new(flags: BitFlags) -> Self { - todo!() - } -} - -#[bitflags] -#[repr(u8)] -#[derive(Clone, Copy)] -pub enum MemoryRegionFlags { - Readable, - Writable, - Executable, -} +static ALLOCATOR: Talck, ClaimOnOom> = + Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) }) + .lock(); diff --git a/src/kernel/panic.rs b/src/kernel/panic.rs index a6d8903..ea7b578 100644 --- a/src/kernel/panic.rs +++ b/src/kernel/panic.rs @@ -11,6 +11,8 @@ use crate::{LOGGER, LogLevel}; #[panic_handler] pub fn panic(info: &PanicInfo) -> ! { log_critical!("Panic in {}: {}", info.location().unwrap(), info.message()); + // TODO: If any userspace facilities are still working, *attempt* to flush panic info and + // logs to disk or whatever else. Then kill all processes and reboot. loop { unsafe { asm!("nop"); diff --git a/src/kernel/process.rs b/src/kernel/process.rs index 805b773..ea8ce6b 100644 --- a/src/kernel/process.rs +++ b/src/kernel/process.rs @@ -1,19 +1,18 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: GPL-3.0-or-later -#![allow(unused_imports, dead_code)] - -use enumflags2::{BitFlags, bitflags, make_bitflags}; - -use crate::arch::asm; -use crate::memory::MemoryRegion; -use crate::memory::MemoryRegionFlags; +use crate::arch::paging::{PageDirectory, PageTable}; use crate::memory::alloc; use alloc::string::String; use alloc::vec::Vec; +use spin::Mutex; #[allow(dead_code)] -pub struct ProcessTable {} +pub static PROCESS_TABLE: Mutex> = Mutex::new(Vec::new()); +// Currently running process. Invalid once the referenced process dies and +// is destructed. +#[allow(dead_code)] +pub static CURRENT_PROCESS: Mutex> = Mutex::new(None); #[allow(dead_code)] pub struct Process { @@ -21,8 +20,6 @@ pub struct Process { proc_id: u32, /// ID of the parent process. parent_proc_id: u32, - /// List of all child processes. - child_proc_ids: Vec, /// Human readable name of the process, should be the command. name: String, /// The base address of the process stack within its memory region. @@ -30,62 +27,30 @@ pub struct Process { /// The length of the stack. This, plus the start address, is the stack size. stack_length: usize, /// The stack pointer, which is relative to the memory region start. - stack_ptr: usize, - /// The region of memory allocated to the process. - mem_region: MemoryRegion, + stack_pointer: usize, + /// The page direcory pointing to this process's pages. + page_directory: PageDirectory, + /// All pages owned by the process. + /// When creating a process, we should create one read-write data page, + /// with execution disabled, and one read-only executable page. + page_tables: Vec, /// Process priority. Lower number is higher priority. - prio: u16, + priority: u16, } #[allow(dead_code)] -pub unsafe fn context_switch() -> ! { - #[allow(unused_unsafe)] - unsafe { - loop { - asm::halt(); - } - } +pub fn context_switch() -> ! { + loop {} } #[allow(dead_code)] pub enum ProcessState { + // The process is the one being currently executed. Running, + // The process is blocked by a syscall. Waiting, + // The process is voluntarily napping until an alarm goes off. Sleeping, + // The process has been "paused", and can be resumed or killed. Suspended, } - -// Interprocess communication system: -// Processes communicate through "sessions" which are mediated by the kernel. -// Sessions can only be opened if two processes both request a session with -// each other (mutual operation). Submitting a session request is a blocking -// system call that will return either once the other process accepts, or the -// kernel denies the request due to missing capability, or the other process -// not existing yet. -pub struct ProcessSession { - proc_id_a: u32, - proc_id_b: u32, - shared_mem: MemoryRegion, -} - -impl ProcessSession { - pub fn new(a: ProcessSessionRequest, b: ProcessSessionRequest) -> Self { - ProcessSession { - proc_id_a: a.proc_id, - proc_id_b: b.proc_id, - shared_mem: MemoryRegion::new( - MemoryRegionFlags::Readable | MemoryRegionFlags::Writable, - ), - } - } -} - -pub struct ProcessSessionRequest { - proc_id: u32, -} - -impl ProcessSessionRequest { - pub fn accept(self, b: ProcessSessionRequest) -> ProcessSession { - ProcessSession::new(self, b) - } -} diff --git a/src/kernel/util.rs b/src/kernel/util.rs index 5e0c156..1211e41 100644 --- a/src/kernel/util.rs +++ b/src/kernel/util.rs @@ -1,11 +1,13 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: GPL-3.0-or-later +/// Critical section: Mark a section of code as critical. +/// Interrupts will be disabled until the section completes. #[macro_export] macro_rules! critical_section { ($b:block) => { - unsafe { $crate::arch::asm::interrupt_disable(); } + $crate::arch::asm::interrupt_disable(); $b - unsafe { $crate::arch::asm::interrupt_enable(); } + $crate::arch::asm::interrupt_enable(); }; } diff --git a/src/lib/arch/mod.rs b/src/lib/arch/mod.rs index 4973b91..55133cf 100644 --- a/src/lib/arch/mod.rs +++ b/src/lib/arch/mod.rs @@ -4,7 +4,7 @@ #[cfg(target_arch = "x86_64")] pub mod x86_64; #[cfg(target_arch = "x86_64")] -pub use x86_64 as current; +pub use x86_64::syscall_impl; #[cfg(target_arch = "aarch64")] pub mod aarch64; diff --git a/src/lib/arch/x86_64/mod.rs b/src/lib/arch/x86_64/mod.rs index 676936f..d465cc0 100644 --- a/src/lib/arch/x86_64/mod.rs +++ b/src/lib/arch/x86_64/mod.rs @@ -1,5 +1,4 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: GPL-3.0-or-later -mod registers_impl; -mod syscall_impl; +pub mod syscall_impl; diff --git a/src/lib/arch/x86_64/registers_impl.rs b/src/lib/arch/x86_64/registers_impl.rs deleted file mode 100644 index bc31ab2..0000000 --- a/src/lib/arch/x86_64/registers_impl.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2025 shibedrill -// SPDX-License-Identifier: GPL-3.0-or-later - -#[allow(dead_code)] -pub struct Registers { - // Private fields -} diff --git a/src/lib/arch/x86_64/syscall_impl.rs b/src/lib/arch/x86_64/syscall_impl.rs index 32a1701..e7dd5bd 100644 --- a/src/lib/arch/x86_64/syscall_impl.rs +++ b/src/lib/arch/x86_64/syscall_impl.rs @@ -6,3 +6,131 @@ // The system call API for x86_64. use crate::syscall::*; + +#[inline(always)] +pub fn caller_syscall_0(call: u64) -> u64 { + let returncode: u64; + unsafe { + core::arch::asm!( + "syscall", + inlateout("rax") call => returncode, + out("rcx") _, + out("r11") _, + options(nostack, preserves_flags) + ); + } + returncode +} +#[inline(always)] +pub fn caller_syscall_1(call: u64, one: u64) -> u64 { + let returncode: u64; + unsafe { + core::arch::asm!( + "syscall", + inlateout("rax") call => returncode, + in("rdi") one, + out("rcx") _, + out("r11") _, + options(nostack, preserves_flags) + ); + } + returncode +} +#[inline(always)] +pub fn caller_syscall_2(call: u64, one: u64, two: u64) -> u64 { + let returncode: u64; + unsafe { + core::arch::asm!( + "syscall", + inlateout("rax") call => returncode, + in("rdi") one, + in("rsi") two, + out("rcx") _, + out("r11") _, + options(nostack, preserves_flags) + ); + } + returncode +} +#[inline(always)] +pub fn caller_syscall_3(call: u64, one: u64, two: u64, three: u64) -> u64 { + let returncode: u64; + unsafe { + core::arch::asm!( + "syscall", + inlateout("rax") call => returncode, + in("rdi") one, + in("rsi") two, + in("rdx") three, + out("rcx") _, + out("r11") _, + options(nostack, preserves_flags) + ); + } + returncode +} +#[inline(always)] +pub fn caller_syscall_4(call: u64, one: u64, two: u64, three: u64, four: u64) -> u64 { + let returncode: u64; + unsafe { + core::arch::asm!( + "syscall", + inlateout("rax") call => returncode, + in("rdi") one, + in("rsi") two, + in("rdx") three, + in("r10") four, + out("rcx") _, + out("r11") _, + options(nostack, preserves_flags) + ); + } + returncode +} +#[inline(always)] +pub fn caller_syscall_5(call: u64, one: u64, two: u64, three: u64, four: u64, five: u64) -> u64 { + let returncode: u64; + unsafe { + core::arch::asm!( + "syscall", + inlateout("rax") call => returncode, + in("rdi") one, + in("rsi") two, + in("rdx") three, + in("r10") four, + in("r8") five, + out("rcx") _, + out("r11") _, + options(nostack, preserves_flags) + ); + } + returncode +} +#[inline(always)] +pub fn caller_syscall_6( + call: u64, + one: u64, + two: u64, + three: u64, + four: u64, + five: u64, + six: u64, +) -> u64 { + let returncode: u64; + unsafe { + core::arch::asm!( + "syscall", + inlateout("rax") call => returncode, + in("rdi") one, + in("rsi") two, + in("rdx") three, + in("r10") four, + in("r8") five, + in("r9") six, + out("rcx") _, + out("r11") _, + options(nostack, preserves_flags) + ); + } + returncode +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs index b554558..cb22907 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -6,4 +6,4 @@ mod arch; pub mod registers; pub mod syscall; -pub use arch::current::*; +pub use arch::*; diff --git a/src/lib/registers.rs b/src/lib/registers.rs index 8371060..6a2ce23 100644 --- a/src/lib/registers.rs +++ b/src/lib/registers.rs @@ -4,8 +4,7 @@ // Every architecture MUST implement this as part of the ABI. // Additional registers can be implemented with architecture-specific traits. -#[allow(clippy::missing_safety_doc)] -pub unsafe trait RegStoreLoad +pub trait RegStoreLoad where Self: Sized, Self: Default, @@ -28,14 +27,14 @@ where // Get the six argument or return register values. // This MUST read registers in the same order as set_syscall_args sets them. - fn read_syscall_args(&self) -> [usize; 6]; + fn read_syscall_args(&self) -> [u64; 6]; // Get the syscall number or error number. // This MUST NOT zero or otherwise overwrite the register. - fn read_syscall_num(&self) -> usize; + fn read_syscall_num(&self) -> u64; // Set the six argument or return register values. // This MUST write registers in the same order as read_syscall_args reads them. - fn set_syscall_args(&mut self, new_values: [usize; 6]); + fn set_syscall_args(&mut self, new_values: [u64; 6]); // Set the syscall number or error number. // This MUST NOT change any other registers. - fn set_syscall_num(&mut self, new_value: usize); + fn set_syscall_num(&mut self, new_value: u64); } diff --git a/src/lib/syscall.rs b/src/lib/syscall.rs deleted file mode 100644 index 4941c71..0000000 --- a/src/lib/syscall.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2025 shibedrill -// SPDX-License-Identifier: GPL-3.0-or-later - -#![allow(dead_code)] - -// TODO: Implement a nice API for system calls. -// I don't want to have to define the argument/return value registers twice -// per architecture. How do I make this work? - -pub enum SyscallArgs { - Args0(), - Args1(usize), - Args2(usize, usize), - Args3(usize, usize, usize), - Args4(usize, usize, usize, usize), - Args5(usize, usize, usize, usize, usize), - Args6(usize, usize, usize, usize, usize, usize), -} - -#[repr(usize)] -pub enum SyscallError { - Ok, // No error. - Unspecified, // Unspecified error occurred. - SyscallNotExist, // System call does not exist. - ProcessNotExist, // The process mentioned does not exist. - PermissionDenied, // The process lacks capabilities. - Aborted, // The kernel gave up on a blocking request. -} diff --git a/src/lib/syscall/mod.rs b/src/lib/syscall/mod.rs new file mode 100644 index 0000000..e19235b --- /dev/null +++ b/src/lib/syscall/mod.rs @@ -0,0 +1,41 @@ +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; + +use crate::arch; + +#[derive(FromPrimitive, Debug)] +pub enum Syscall { + Exit, + Pid, + Message, + MemMap, + MemUnmap, + Resolve, + Spawn, + Version, +} + +#[derive(FromPrimitive, Debug)] +pub enum SyscallStatus { + Success, + NoSuchCall, + NoSuchProcess, + NoSuchServer, + NoPermission, + OutOfMemory, + Aborted, + Unspecified, + Unknown, +} + +pub struct UnknownStatus {} + +impl From for SyscallStatus { + fn from(value: u64) -> Self { + SyscallStatus::from_u64(value).unwrap_or(Self::Unknown) + } +} + +pub fn exit(code: u64) -> SyscallStatus { + arch::syscall_impl::caller_syscall_1(Syscall::Exit as u64, code).into() +}