From ff12b11b9f2769141e0b3449ca82a3a9f8e697d9 Mon Sep 17 00:00:00 2001 From: Arthur Beck Date: Wed, 12 Feb 2025 17:36:35 +0000 Subject: [PATCH] Working even more on making things architecture independent and really leaning into kernel items --- kernel/src/arch_boot_entry/x86.rs | 2 + kernel/src/kernel/arch/example_impl/mod.rs | 110 +++++++++++++++++ kernel/src/kernel/arch/mod.rs | 3 + kernel/src/kernel/arch/x86/constants.rs | 2 +- kernel/src/kernel/arch/x86/interrupts.rs | 90 ++++++++++++-- kernel/src/kernel/arch/x86/mod.rs | 18 +-- kernel/src/kernel/arch/x86/paging.rs | 134 ++++++++++++++++++++- 7 files changed, 335 insertions(+), 24 deletions(-) create mode 100644 kernel/src/kernel/arch/example_impl/mod.rs diff --git a/kernel/src/arch_boot_entry/x86.rs b/kernel/src/arch_boot_entry/x86.rs index d69bbb7..d9b1b95 100644 --- a/kernel/src/arch_boot_entry/x86.rs +++ b/kernel/src/arch_boot_entry/x86.rs @@ -59,9 +59,11 @@ static mut MAGIC: u32 = 0xFFFFFFFF; #[unsafe(link_section = ".start")] #[unsafe(no_mangle)] +#[aphrodite_proc_macros::kernel_item(ArchBootEntry)] extern "C" fn _start() -> ! { unsafe { // Copy values provided by the bootloader out // Aphrodite bootloaders pass values in eax and ebx, however rust doesn't know that it can't overwrite those. + // (if necessary, we'll store all of the registers for other bootloaders and identify which one it is later) // we force using ebx and eax as the output of an empty assembly block to let it know. asm!( "", out("ebx") O, // Bootloader-specific data(ebx) diff --git a/kernel/src/kernel/arch/example_impl/mod.rs b/kernel/src/kernel/arch/example_impl/mod.rs new file mode 100644 index 0000000..27dbf66 --- /dev/null +++ b/kernel/src/kernel/arch/example_impl/mod.rs @@ -0,0 +1,110 @@ +//! An example implementation of an architecture. DO NOT use this module! +//! Everything must be implemented via either kernel items, or for constants +//! making them public. +//! +//! This is commented out for obvious reasons, but make sure to have this at +//! the top of the all files in your arch(with "arch" replaced with the +//! actual architecture, of course): +//! #![cfg(any(target_arch = "arch"))] + +pub mod interrupts { + //! Interrupt-related functions. + + use core::mem::MaybeUninit; + + /// Must be a u16 or castable to a u16. + /// Value used in x86 shown here as an example. + pub const USER_SYSCALL_VECTOR: u16 = 0xA0; + + /// Returns whether interrupts are enabled or not. + #[aphrodite_proc_macros::kernel_item(InterruptsCheck)] + fn interrupts_enabled() -> bool { + false + } + + /// Enables interrupts. + #[aphrodite_proc_macros::kernel_item(InterruptsEnable)] + fn enable_interrupts() { + + } + + /// Disables interrupts. + #[aphrodite_proc_macros::kernel_item(InterruptsDisable)] + fn disable_interrupts() { + + } + + /// Disables interrupts and a value that can be used to restore them + /// with [restore_irq]. + #[aphrodite_proc_macros::kernel_item(InterruptsPop)] + fn pop_irq() -> u64 { + 0 + } + + /// Restores interrupts after a [pop_irq] call. + #[aphrodite_proc_macros::kernel_item(InterruptsRestore)] + fn restore_irq(irq: u64) { + irq; + } + + /// Activates an IDT. + #[aphrodite_proc_macros::kernel_item(ActivateIDT)] + fn activate_idt(idt: Idt) { + idt; + } + + /// An IDT. + #[derive(Clone, Copy)] + pub struct Idt { + vectors: [u16; 256], + funcs: [MaybeUninit; 256], + len: usize, + } + + /// An IDT builder. The only way to create + /// an IDT. + #[derive(Clone, Copy)] + pub struct IdtBuilder { + vectors: [u16; 256], + funcs: [MaybeUninit; 256], + idx: usize, + } + + impl IdtBuilder { + /// Start creating a new IDT. + pub fn new() -> Self { + IdtBuilder { + vectors: [0; 256], + funcs: [MaybeUninit::uninit(); 256], + idx: 0, + } + } + /// Add a function to the IDT. + pub fn add_fn(&mut self, vector: u16, func: fn()) -> &mut Self { + self.vectors[self.idx] = vector; + self.funcs[self.idx].write(func); + self.idx += 1; + self + } + /// Create the IDT from the IDT builder. + pub fn finish(&self) -> Idt { + Idt { + vectors: self.vectors, + funcs: self.funcs, + len: self.idx + } + } + } +} + +pub mod output { + //! Not shown here(see [crate::arch::x86] for an example), but a + //! LOT of output functions must be implemented. Using macros to + //! implement these is HIGHLY recommended. +} + +/// Returns whether paging is available for this architecture. +#[aphrodite_proc_macros::kernel_item(PagingAvailabe)] +pub fn paging_available() -> bool { + true +} \ No newline at end of file diff --git a/kernel/src/kernel/arch/mod.rs b/kernel/src/kernel/arch/mod.rs index 8801ebf..5668ec3 100644 --- a/kernel/src/kernel/arch/mod.rs +++ b/kernel/src/kernel/arch/mod.rs @@ -1,5 +1,8 @@ //! Arch-specific code. This module re-exports all code from the architecture being used. +//! +//! See [example_impl] for everything that has to be implemented by an architecture module. mod x86; +pub mod example_impl; pub use x86::*; \ No newline at end of file diff --git a/kernel/src/kernel/arch/x86/constants.rs b/kernel/src/kernel/arch/x86/constants.rs index da421e5..53cf9b6 100644 --- a/kernel/src/kernel/arch/x86/constants.rs +++ b/kernel/src/kernel/arch/x86/constants.rs @@ -2,4 +2,4 @@ #![cfg(any(target_arch = "x86"))] /// The assembly port number to output debug messages to. -pub const DEBUG_PORT: u16 = 0xE9; \ No newline at end of file +pub(super) const DEBUG_PORT: u16 = 0xE9; \ No newline at end of file diff --git a/kernel/src/kernel/arch/x86/interrupts.rs b/kernel/src/kernel/arch/x86/interrupts.rs index ab69402..67db256 100644 --- a/kernel/src/kernel/arch/x86/interrupts.rs +++ b/kernel/src/kernel/arch/x86/interrupts.rs @@ -1,9 +1,13 @@ //! Provides interrupt-related functions #![cfg(any(target_arch = "x86"))] -use core::arch::asm; +use core::{alloc::{Allocator, Layout}, arch::asm, mem::MaybeUninit}; + +/// The syscall vector. +pub const USER_SYSCALL_VECTOR: u16 = 0xA0; /// Returns whether interrupts are enabled or not. +#[aphrodite_proc_macros::kernel_item(InterruptsCheck)] pub fn interrupts_enabled() -> bool { let flags: u32; unsafe { @@ -16,6 +20,7 @@ pub fn interrupts_enabled() -> bool { } /// Disables interrupts. +#[aphrodite_proc_macros::kernel_item(InterruptsDisable)] pub fn disable_interrupts() { unsafe { asm!("cli") @@ -23,7 +28,8 @@ pub fn disable_interrupts() { } /// Disables interrupts and returns the value of them. -pub fn pop_irq() -> u32 { +#[aphrodite_proc_macros::kernel_item(InterruptsPop)] +pub fn pop_irq() -> u64 { let flags: u32; unsafe { asm!( @@ -32,11 +38,13 @@ pub fn pop_irq() -> u32 { "pop {0:e}", out(reg) flags ) } - flags + flags as u64 } /// Restores interrupts after a [pop_irq] call. -pub fn restore_irq(flags: u32) { +#[aphrodite_proc_macros::kernel_item(InterruptsRestore)] +pub fn restore_irq(flags: u64) { + let flags = flags as u32; unsafe { asm!( "push {0:e}", in(reg) flags @@ -48,21 +56,83 @@ pub fn restore_irq(flags: u32) { } /// The IDTR. Used internally in [load_idt]. +#[repr(packed)] #[repr(C)] struct IDTR { base: *const u8, size: usize } +unsafe impl Send for IDTR {} +unsafe impl Sync for IDTR {} + /// Loads an interrupt descriptor table. -pub fn load_idt(base: *const u8, size: usize) { - let idtr = IDTR { - base, - size - }; +fn load_idt(base: *const u8, size: usize) { + static mut IDTR: MaybeUninit = MaybeUninit::uninit(); + unsafe { + IDTR.write(IDTR { + base, + size + }); + } unsafe { asm!( - "lidt {}", in(reg) &idtr + "lidt {}", in(reg) IDTR.as_ptr() as usize ) } +} + +/// Activate an IDT. +#[aphrodite_proc_macros::kernel_item(ActivateIDT)] +fn activate_idt(idt: Idt, alloc: crate::mem::MemoryMapAlloc) { + let mem = alloc.allocate(unsafe { Layout::from_size_align_unchecked(8*idt.len, 1) }).unwrap().as_mut_ptr(); + for i in 0..idt.len { + let vector = idt.vectors[i]; + let func = unsafe { idt.funcs[i].assume_init() } as usize as u32; + let user_callable = idt.user_callable[i]; + let output: u64 = func & 0b1111111111111111; + + } +} + +#[derive(Clone, Copy)] +pub struct Idt { + vectors: [u16; 256], + funcs: [MaybeUninit; 256], + user_callable: [bool; 256], + len: usize, +} + +#[derive(Clone, Copy)] +pub struct IdtBuilder { + vectors: [u16; 256], + funcs: [MaybeUninit; 256], + user_callable: [bool; 256], + idx: usize, +} + +impl IdtBuilder { + pub fn new() -> Self { + IdtBuilder { + vectors: [0; 256], + funcs: [MaybeUninit::uninit(); 256], + user_callable: [false; 256], + idx: 0, + } + } + pub fn add_fn(&mut self, vector: u16, func: fn(), user_callable: bool) -> &mut Self { + self.vectors[self.idx] = vector; + self.funcs[self.idx].write(func); + self.user_callable[self.idx] = user_callable; + self.idx += 1; + self + } + pub fn finish(&self) -> Idt { + Idt { + vectors: self.vectors, + funcs: self.funcs, + user_callable: self.user_callable, + len: self.idx + } + } } \ No newline at end of file diff --git a/kernel/src/kernel/arch/x86/mod.rs b/kernel/src/kernel/arch/x86/mod.rs index 2311461..dba4213 100644 --- a/kernel/src/kernel/arch/x86/mod.rs +++ b/kernel/src/kernel/arch/x86/mod.rs @@ -11,10 +11,15 @@ pub mod paging; mod constants; -pub use constants::*; +pub(self) use constants::*; use interrupts::{pop_irq, restore_irq}; use ports::{inb, outb}; +#[aphrodite_proc_macros::kernel_item(PagingAvailabe)] +pub fn paging_available() -> bool { + true +} + /// Returns information from the CPUID command in the form /// (ebx, edx, ecx). pub fn cpuid(id: u32) -> (u32, u32, u32) { @@ -160,14 +165,3 @@ pub fn enable_a20() -> bool { return test_a20(); } - -/// Disables paging by clearing bit 31 in the cr0 register. -pub fn disable_paging() { - unsafe { - asm!( - "mov eax, cr0", - "and eax, 01111111111111111111111111111111b", - "mov cr0, eax" - ) - } -} diff --git a/kernel/src/kernel/arch/x86/paging.rs b/kernel/src/kernel/arch/x86/paging.rs index f1cd5df..94137ca 100644 --- a/kernel/src/kernel/arch/x86/paging.rs +++ b/kernel/src/kernel/arch/x86/paging.rs @@ -1 +1,133 @@ -//! Functions and types related to paging. \ No newline at end of file +//! Functions and types related to paging. + +use core::arch::asm; + +use aphrodite_proc_macros::kernel_item; + +pub enum PageDirectoryEntry { + FourMb(u32), + Other(u32), +} + +impl PageDirectoryEntry { + const fn create_fourmb( + mut bits32to22: u16, + bits39to32: u8, + pat: bool, + mut available: u8, + global: bool, + dirty: bool, + accessed: bool, + disable_cache: bool, + write_through: bool, + user: bool, + can_write: bool, + present: bool, + ) -> Self { + let mut out = 0u32; + if present { + out |= 1 << 0; + } + if can_write { + out |= 1 << 1; + } + if user { + out |= 1 << 2; + } + if write_through { + out |= 1 << 3; + } + if disable_cache { + out |= 1 << 4; + } + if accessed { + out |= 1 << 5; + } + if dirty { + out |= 1 << 6; + } + out |= 1 << 7; + if global { + out |= 1 << 8; + } + available &= 0b111; + out |= (available as u32) << 9; + if pat { + out |= 1 << 12; + } + out |= (bits39to32 as u32) << 13; + bits32to22 &= 0b1111111111; + out |= (bits32to22 as u32) << 22; + Self::FourMb(out) + } + + const fn create_other( + mut bits31to12: u32, + pat: bool, + mut available: u8, + global: bool, + accessed: bool, + disable_cache: bool, + write_through: bool, + user: bool, + can_write: bool, + present: bool, + ) -> Self { + let mut out = 0u32; + if present { + out |= 1 << 0; + } + if can_write { + out |= 1 << 1; + } + if user { + out |= 1 << 2; + } + if write_through { + out |= 1 << 3; + } + if disable_cache { + out |= 1 << 4; + } + if accessed { + out |= 1 << 5; + } + if available & 1 != 0 { + out |= 1 << 6; + } + out |= 0 << 7; + if global { + out |= 1 << 8; + } + available &= 0b11110; + out |= (available as u32) << 8; + if pat { + out |= 1 << 12; + } + bits31to12 &= 0b1111111111111111111; + out |= bits31to12 << 13; + Self::Other(out) + } +} + +/// Kind of cursed, but DSTs aren't allowed in statics. +static mut PAGE_DIRECTORY: PageDirectoryEntry = + PageDirectoryEntry::create_other(0, false, 0, false, false, false, false, false, false, false); + +#[kernel_item(PagingInit)] +pub fn initalize_paging() { + +} + + +/// Disables paging by clearing bit 31 in the cr0 register. +#[kernel_item(PagingDeinit)] +pub fn disable_paging() { + unsafe { + asm!( + "mov eax, cr0", + "and eax, 01111111111111111111111111111111b", + "mov cr0, eax" + ) + } +}