Syscall API design

This commit is contained in:
River 2025-06-05 00:19:02 -04:00
parent a6c141dda4
commit 114e87d11f
Signed by: shibedrill
GPG Key ID: 5FE0CB25945EFAA2
26 changed files with 368 additions and 253 deletions

35
Cargo.lock generated
View File

@ -67,26 +67,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 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]] [[package]]
name = "fdt" name = "fdt"
version = "0.2.0-alpha1" version = "0.2.0-alpha1"
@ -103,13 +83,15 @@ name = "gila"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"acpi", "acpi",
"enumflags2", "bitflags",
"fdt", "fdt",
"flagset", "flagset",
"intbits", "intbits",
"lazy_static", "lazy_static",
"limine", "limine",
"lzma-rs", "lzma-rs",
"num-derive",
"num-traits",
"once_cell", "once_cell",
"spin 0.10.0", "spin 0.10.0",
"talc", "talc",
@ -173,6 +155,17 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"

View File

@ -5,13 +5,15 @@ edition = "2024"
[dependencies] [dependencies]
acpi = "5.1.0" acpi = "5.1.0"
enumflags2 = "0.7.11" bitflags = "2.9.1"
fdt = { git = "https://github.com/repnop/fdt", version = "0.2.0-alpha1" } fdt = { git = "https://github.com/repnop/fdt", version = "0.2.0-alpha1" }
flagset = "0.4.7" flagset = "0.4.7"
intbits = "0.2.0" intbits = "0.2.0"
lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] } lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] }
limine = "0.4.0" limine = "0.4.0"
lzma-rs = { git = "https://github.com/glaeqen/lzma-no-std-rs/", version = "0.2.0", default-features = false } 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"] } once_cell = { version = "1.20.3", default-features = false, features = ["alloc", "critical-section"] }
spin = "0.10.0" spin = "0.10.0"
talc = "4.4.2" talc = "4.4.2"

View File

@ -5,7 +5,7 @@ Gila is a microkernel, and almost all functionality of the OS is relegated to
functionality, using a kernel-provided interface, and it is given a "seat" by 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. 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 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 filesystem in the form of a .tar.lzma archive. The kernel reads this file, and
@ -35,14 +35,17 @@ The system, hence, can be configured in two ways:
- 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. initramfs, and all other drivers are included in the root filesystem.
# APIs ## APIs
Processes access services similarly to the way the init process accesses 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 performs the function, and then communicates with it via IPC and shared
memory regions. APIs are to be defined in the standard library. 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: Device driver assignment is performed like so:
@ -55,3 +58,25 @@ Device driver assignment is performed like so:
to the process. to the process.
- The driver then makes its functions available via IPC and shared memory. - The driver then makes its functions available via IPC and shared memory.
- The kernel stores information about the driver to avoid spawning duplicates. - 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.

View File

@ -5,6 +5,7 @@
pub mod x86_64; pub mod x86_64;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub use x86_64::asm; pub use x86_64::asm;
pub use x86_64::paging;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
pub mod aarch64; pub mod aarch64;

View File

@ -4,7 +4,7 @@
use core::arch::asm; use core::arch::asm;
#[allow(clippy::missing_safety_doc)] #[allow(clippy::missing_safety_doc)]
pub unsafe fn halt() { pub fn halt() {
unsafe { unsafe {
asm!("wfi"); asm!("wfi");
} }

View File

@ -6,31 +6,53 @@
use core::arch::asm; use core::arch::asm;
pub unsafe fn cr0() -> u64 { pub fn read_cr0() -> u64 {
let mut cr0: u64; let cr0: u64;
unsafe { asm!("mov {0:r}, cr0", out(reg) cr0) } unsafe { asm!("mov {0:r}, cr0", out(reg) cr0) }
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 { unsafe {
asm!("cli"); asm!("cli");
} }
} }
pub unsafe fn interrupt_enable() { pub fn interrupt_enable() {
unsafe { unsafe {
asm!("sti"); asm!("sti");
} }
} }
pub unsafe fn halt() { pub fn halt() {
unsafe { unsafe {
asm!("hlt"); 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; let result: u8;
unsafe { unsafe {
asm! { asm! {
@ -42,7 +64,7 @@ pub unsafe fn port_read_u8(port: u16) -> u8 {
result result
} }
pub unsafe fn port_read_u16(port: u16) -> u16 { pub fn port_read_u16(port: u16) -> u16 {
let result: u16; let result: u16;
unsafe { unsafe {
asm! { asm! {
@ -54,7 +76,7 @@ pub unsafe fn port_read_u16(port: u16) -> u16 {
result result
} }
pub unsafe fn port_read_u32(port: u16) -> u32 { pub fn port_read_u32(port: u16) -> u32 {
let result: u32; let result: u32;
unsafe { unsafe {
asm! { asm! {
@ -66,7 +88,7 @@ pub unsafe fn port_read_u32(port: u16) -> u32 {
result result
} }
pub unsafe fn port_write_u8(port: u16, data: u8) { pub fn port_write_u8(port: u16, data: u8) {
unsafe { unsafe {
asm! { asm! {
"out dx, al", "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 { unsafe {
asm! { asm! {
"out dx, ax", "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 { unsafe {
asm! { asm! {
"out dx eax", "out dx eax",

View File

@ -5,8 +5,33 @@ use core::arch::asm;
use core::ptr::addr_of_mut; use core::ptr::addr_of_mut;
use intbits::Bits; 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; let mut addr: u128 = 0;
unsafe { unsafe {
asm! { asm! {
@ -14,5 +39,10 @@ pub unsafe fn gdt() -> (u8, u64) {
in("rax") addr_of_mut!(addr), 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)
} }

View File

@ -10,6 +10,7 @@ lazy_static! {
pub static ref IDT: InterruptDescriptorTable = { pub static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new(); let mut idt = InterruptDescriptorTable::new();
idt.double_fault.set_handler_fn(double_fault); idt.double_fault.set_handler_fn(double_fault);
idt.page_fault.set_handler_fn(page_fault);
idt idt
}; };
} }
@ -17,3 +18,24 @@ lazy_static! {
extern "x86-interrupt" fn double_fault(info: InterruptStackFrame, _: u64) -> ! { extern "x86-interrupt" fn double_fault(info: InterruptStackFrame, _: u64) -> ! {
crate::interrupt::double_fault(&format!("{info:#?}")); 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:#?}"))
}
}
}

View File

@ -1,8 +1,8 @@
#![allow(dead_code)] #![allow(dead_code)]
use intbits::Bits; use intbits::Bits;
use x86_64::structures::paging::page_table;
#[repr(packed)]
pub struct PageMapLevel5 { pub struct PageMapLevel5 {
entries: [PageMapLevel5Entry; 512], entries: [PageMapLevel5Entry; 512],
} }
@ -64,7 +64,6 @@ impl PageMapLevel5Entry {
} }
} }
#[repr(packed)]
pub struct PageMapLevel4 { pub struct PageMapLevel4 {
entries: [PageMapLevel4Entry; 512], entries: [PageMapLevel4Entry; 512],
} }
@ -129,13 +128,12 @@ impl PageMapLevel4Entry {
} }
} }
#[repr(packed)]
pub struct PageDirectoryPointerTable { pub struct PageDirectoryPointerTable {
entries: [*mut PageDirectory; 4], entries: [*mut PageDirectory; 4],
} }
pub struct PageDirectoryPointer { pub struct PageDirectoryPointer {
value: u64 value: u64,
} }
impl PageDirectoryPointer { impl PageDirectoryPointer {
@ -255,12 +253,4 @@ impl PageDirectoryEntry {
} }
} }
#[repr(align(4096))] pub use page_table::PageTable;
pub struct PageTable {
entries: [PageTableEntry; 512],
}
#[repr(packed)]
pub struct PageTableEntry {
value: u64,
}

View File

@ -12,7 +12,7 @@ pub struct Serialport {
impl LogSubscriber for Serialport { impl LogSubscriber for Serialport {
fn write(&self, msg: &str) { fn write(&self, msg: &str) {
for c in msg.chars() { for c in msg.chars() {
unsafe { port_write_u8(self.port, c as u8) }; port_write_u8(self.port, c as u8);
} }
} }
} }

View File

@ -10,7 +10,7 @@ use limine::response::{DeviceTreeBlobResponse, RsdpResponse};
use crate::boot::DTB_REQUEST; use crate::boot::DTB_REQUEST;
use crate::device::acpi::RSDP_REQUEST; use crate::device::acpi::RSDP_REQUEST;
use crate::log::{LOGGER, LogLevel}; 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! { lazy_static! {
pub static ref DTB: Option<&'static DeviceTreeBlobResponse> = match DTB_REQUEST.get_response() { pub static ref DTB: Option<&'static DeviceTreeBlobResponse> = match DTB_REQUEST.get_response() {
@ -37,8 +37,11 @@ lazy_static! {
// Just to check // Just to check
pub fn init_statics() { pub fn init_statics() {
let _ = DTB.is_some(); let dtb = DTB.is_some();
let _ = RSDP.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)] #[allow(dead_code)]

View File

@ -4,3 +4,7 @@
pub fn double_fault(info: &str) -> ! { pub fn double_fault(info: &str) -> ! {
panic!("Double fault: {}", info); panic!("Double fault: {}", info);
} }
pub fn page_fault(addr: usize, info: &str) -> ! {
panic!("Page fault at 0x{:X}: {}", addr, info);
}

View File

@ -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 { pub static LOGGER: Logger = Logger {
inner: Mutex::new(LoggerInner::new()), inner: Mutex::new(LoggerInner::new()),
}; };
@ -97,8 +97,8 @@ impl LoggerInner {
} }
} }
/// Calling log will sequentially acquire lock on all logging subscribers // Calling log will sequentially acquire lock on all logging subscribers
/// to write to them with a formatted log message. // to write to them with a formatted log message.
pub fn log(&self, level: LogLevel, msg: &str) { pub fn log(&self, level: LogLevel, msg: &str) {
// Nobody is EVER allowed to call log with the Disabled log level. It is a placeholder. // Nobody is EVER allowed to call log with the Disabled log level. It is a placeholder.
if level == LogLevel::Disabled { if level == LogLevel::Disabled {

View File

@ -19,14 +19,9 @@ mod process;
#[macro_use] #[macro_use]
mod util; 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")] #[cfg(target_arch = "x86_64")]
use arch::x86_64::serial::Serialport; use arch::x86_64::serial::Serialport;
use arch::asm::*;
use boot::{modules::*, params, *}; use boot::{modules::*, params, *};
use constants::*; use constants::*;
use log::*; use log::*;
@ -34,7 +29,6 @@ use memory::alloc::{format, string::*, vec};
use memory::{HHDM_RESPONSE, MEMMAP_REQUEST}; use memory::{HHDM_RESPONSE, MEMMAP_REQUEST};
use params::*; use params::*;
use intbits::{self, Bits};
use limine::memory_map::EntryType; use limine::memory_map::EntryType;
#[allow(unused_imports)] #[allow(unused_imports)]
use lzma_rs::lzma_decompress; use lzma_rs::lzma_decompress;
@ -126,7 +120,7 @@ unsafe extern "C" fn main() -> ! {
let mut unusable: u64 = 0; let mut unusable: u64 = 0;
for entry in mmap_response.entries() { for entry in mmap_response.entries() {
log_msg.push_str(&format!( log_msg.push_str(&format!(
"\n\t\t0x{:x} bytes @ 0x{:x}: {}", "\n\t\t0x{:X} bytes @ 0x{:X}: {}",
entry.length, entry.length,
entry.base, entry.base,
match entry.entry_type { match entry.entry_type {
@ -178,56 +172,14 @@ unsafe extern "C" fn main() -> ! {
panic!("Memory map contains no entries"); panic!("Memory map contains no entries");
} }
log_info!("HHDM offset: 0x{:x}", HHDM_RESPONSE.offset()); 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::<PageMapLevel4Entry>()
);
}
panic!("Bailing"); panic!("Bailing");
#[allow(unreachable_code)] #[allow(unreachable_code)]
loop { loop {
for _i in 0..50000000u64 { for _i in 0..50000000u64 {
unsafe { crate::arch::asm::nop();
core::arch::asm!("nop");
}
} }
core::hint::black_box(()); core::hint::black_box(());
log_trace!("Heartbeat"); log_trace!("Heartbeat");

View File

@ -1,16 +1,12 @@
// Copyright (c) 2025 shibedrill // Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#![allow(unused_imports)]
use enumflags2::*;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use limine::{ use limine::{
request::{ExecutableAddressRequest, HhdmRequest, MemoryMapRequest}, request::{ExecutableAddressRequest, HhdmRequest, MemoryMapRequest},
response::HhdmResponse, response::HhdmResponse,
}; };
use core::alloc::{Allocator, Layout};
use talc::*; use talc::*;
pub extern crate alloc; pub extern crate alloc;
@ -34,36 +30,10 @@ lazy_static! {
.expect("Did not get HHDM response from bootloader"); .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]; static mut ARENA: [u8; 10000] = [0; 10000];
#[global_allocator] #[global_allocator]
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> = Talc::new(unsafe { static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
// if we're in a hosted environment, the Rust runtime may allocate before Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
// main() is called, so we need to initialize the arena automatically .lock();
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<MemoryRegionFlags>,
}
impl MemoryRegion {
// TODO: Memory allocation and virtual addressing
#[allow(unused_variables)]
pub fn new(flags: BitFlags<MemoryRegionFlags>) -> Self {
todo!()
}
}
#[bitflags]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum MemoryRegionFlags {
Readable,
Writable,
Executable,
}

View File

@ -11,6 +11,8 @@ use crate::{LOGGER, LogLevel};
#[panic_handler] #[panic_handler]
pub fn panic(info: &PanicInfo) -> ! { pub fn panic(info: &PanicInfo) -> ! {
log_critical!("Panic in {}: {}", info.location().unwrap(), info.message()); 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 { loop {
unsafe { unsafe {
asm!("nop"); asm!("nop");

View File

@ -1,19 +1,18 @@
// Copyright (c) 2025 shibedrill // Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#![allow(unused_imports, dead_code)] use crate::arch::paging::{PageDirectory, PageTable};
use enumflags2::{BitFlags, bitflags, make_bitflags};
use crate::arch::asm;
use crate::memory::MemoryRegion;
use crate::memory::MemoryRegionFlags;
use crate::memory::alloc; use crate::memory::alloc;
use alloc::string::String; use alloc::string::String;
use alloc::vec::Vec; use alloc::vec::Vec;
use spin::Mutex;
#[allow(dead_code)] #[allow(dead_code)]
pub struct ProcessTable {} pub static PROCESS_TABLE: Mutex<Vec<Process>> = Mutex::new(Vec::new());
// Currently running process. Invalid once the referenced process dies and
// is destructed.
#[allow(dead_code)]
pub static CURRENT_PROCESS: Mutex<Option<&Process>> = Mutex::new(None);
#[allow(dead_code)] #[allow(dead_code)]
pub struct Process { pub struct Process {
@ -21,8 +20,6 @@ pub struct Process {
proc_id: u32, proc_id: u32,
/// ID of the parent process. /// ID of the parent process.
parent_proc_id: u32, parent_proc_id: u32,
/// List of all child processes.
child_proc_ids: Vec<u32>,
/// Human readable name of the process, should be the command. /// Human readable name of the process, should be the command.
name: String, name: String,
/// The base address of the process stack within its memory region. /// 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. /// The length of the stack. This, plus the start address, is the stack size.
stack_length: usize, stack_length: usize,
/// The stack pointer, which is relative to the memory region start. /// The stack pointer, which is relative to the memory region start.
stack_ptr: usize, stack_pointer: usize,
/// The region of memory allocated to the process. /// The page direcory pointing to this process's pages.
mem_region: MemoryRegion, 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<PageTable>,
/// Process priority. Lower number is higher priority. /// Process priority. Lower number is higher priority.
prio: u16, priority: u16,
} }
#[allow(dead_code)] #[allow(dead_code)]
pub unsafe fn context_switch() -> ! { pub fn context_switch() -> ! {
#[allow(unused_unsafe)] loop {}
unsafe {
loop {
asm::halt();
}
}
} }
#[allow(dead_code)] #[allow(dead_code)]
pub enum ProcessState { pub enum ProcessState {
// The process is the one being currently executed.
Running, Running,
// The process is blocked by a syscall.
Waiting, Waiting,
// The process is voluntarily napping until an alarm goes off.
Sleeping, Sleeping,
// The process has been "paused", and can be resumed or killed.
Suspended, 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)
}
}

View File

@ -1,11 +1,13 @@
// Copyright (c) 2025 shibedrill // Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: GPL-3.0-or-later // 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_export]
macro_rules! critical_section { macro_rules! critical_section {
($b:block) => { ($b:block) => {
unsafe { $crate::arch::asm::interrupt_disable(); } $crate::arch::asm::interrupt_disable();
$b $b
unsafe { $crate::arch::asm::interrupt_enable(); } $crate::arch::asm::interrupt_enable();
}; };
} }

View File

@ -4,7 +4,7 @@
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub mod x86_64; pub mod x86_64;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub use x86_64 as current; pub use x86_64::syscall_impl;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
pub mod aarch64; pub mod aarch64;

View File

@ -1,5 +1,4 @@
// Copyright (c) 2025 shibedrill // Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
mod registers_impl; pub mod syscall_impl;
mod syscall_impl;

View File

@ -1,7 +0,0 @@
// Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: GPL-3.0-or-later
#[allow(dead_code)]
pub struct Registers {
// Private fields
}

View File

@ -6,3 +6,131 @@
// The system call API for x86_64. // The system call API for x86_64.
use crate::syscall::*; 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
}

View File

@ -6,4 +6,4 @@
mod arch; mod arch;
pub mod registers; pub mod registers;
pub mod syscall; pub mod syscall;
pub use arch::current::*; pub use arch::*;

View File

@ -4,8 +4,7 @@
// Every architecture MUST implement this as part of the ABI. // Every architecture MUST implement this as part of the ABI.
// Additional registers can be implemented with architecture-specific traits. // Additional registers can be implemented with architecture-specific traits.
#[allow(clippy::missing_safety_doc)] pub trait RegStoreLoad
pub unsafe trait RegStoreLoad
where where
Self: Sized, Self: Sized,
Self: Default, Self: Default,
@ -28,14 +27,14 @@ where
// Get the six argument or return register values. // Get the six argument or return register values.
// This MUST read registers in the same order as set_syscall_args sets them. // 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. // Get the syscall number or error number.
// This MUST NOT zero or otherwise overwrite the register. // 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. // Set the six argument or return register values.
// This MUST write registers in the same order as read_syscall_args reads them. // 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. // Set the syscall number or error number.
// This MUST NOT change any other registers. // 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);
} }

View File

@ -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.
}

41
src/lib/syscall/mod.rs Normal file
View File

@ -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<u64> 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()
}