Working even more on making things architecture independent and really leaning into kernel items

This commit is contained in:
Arthur Beck 2025-02-12 17:36:35 +00:00
parent f6b3f935ef
commit ff12b11b9f
7 changed files with 335 additions and 24 deletions

View file

@ -59,9 +59,11 @@ static mut MAGIC: u32 = 0xFFFFFFFF;
#[unsafe(link_section = ".start")] #[unsafe(link_section = ".start")]
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
#[aphrodite_proc_macros::kernel_item(ArchBootEntry)]
extern "C" fn _start() -> ! { extern "C" fn _start() -> ! {
unsafe { // Copy values provided by the bootloader out 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. // 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. // we force using ebx and eax as the output of an empty assembly block to let it know.
asm!( asm!(
"", out("ebx") O, // Bootloader-specific data(ebx) "", out("ebx") O, // Bootloader-specific data(ebx)

View file

@ -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<fn ()>; 256],
len: usize,
}
/// An IDT builder. The only way to create
/// an IDT.
#[derive(Clone, Copy)]
pub struct IdtBuilder {
vectors: [u16; 256],
funcs: [MaybeUninit<fn ()>; 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
}

View file

@ -1,5 +1,8 @@
//! Arch-specific code. This module re-exports all code from the architecture being used. //! 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; mod x86;
pub mod example_impl;
pub use x86::*; pub use x86::*;

View file

@ -2,4 +2,4 @@
#![cfg(any(target_arch = "x86"))] #![cfg(any(target_arch = "x86"))]
/// The assembly port number to output debug messages to. /// The assembly port number to output debug messages to.
pub const DEBUG_PORT: u16 = 0xE9; pub(super) const DEBUG_PORT: u16 = 0xE9;

View file

@ -1,9 +1,13 @@
//! Provides interrupt-related functions //! Provides interrupt-related functions
#![cfg(any(target_arch = "x86"))] #![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. /// Returns whether interrupts are enabled or not.
#[aphrodite_proc_macros::kernel_item(InterruptsCheck)]
pub fn interrupts_enabled() -> bool { pub fn interrupts_enabled() -> bool {
let flags: u32; let flags: u32;
unsafe { unsafe {
@ -16,6 +20,7 @@ pub fn interrupts_enabled() -> bool {
} }
/// Disables interrupts. /// Disables interrupts.
#[aphrodite_proc_macros::kernel_item(InterruptsDisable)]
pub fn disable_interrupts() { pub fn disable_interrupts() {
unsafe { unsafe {
asm!("cli") asm!("cli")
@ -23,7 +28,8 @@ pub fn disable_interrupts() {
} }
/// Disables interrupts and returns the value of them. /// 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; let flags: u32;
unsafe { unsafe {
asm!( asm!(
@ -32,11 +38,13 @@ pub fn pop_irq() -> u32 {
"pop {0:e}", out(reg) flags "pop {0:e}", out(reg) flags
) )
} }
flags flags as u64
} }
/// Restores interrupts after a [pop_irq] call. /// 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 { unsafe {
asm!( asm!(
"push {0:e}", in(reg) flags "push {0:e}", in(reg) flags
@ -48,21 +56,83 @@ pub fn restore_irq(flags: u32) {
} }
/// The IDTR. Used internally in [load_idt]. /// The IDTR. Used internally in [load_idt].
#[repr(packed)]
#[repr(C)] #[repr(C)]
struct IDTR { struct IDTR {
base: *const u8, base: *const u8,
size: usize size: usize
} }
unsafe impl Send for IDTR {}
unsafe impl Sync for IDTR {}
/// Loads an interrupt descriptor table. /// Loads an interrupt descriptor table.
pub fn load_idt(base: *const u8, size: usize) { fn load_idt(base: *const u8, size: usize) {
let idtr = IDTR { static mut IDTR: MaybeUninit<IDTR> = MaybeUninit::uninit();
unsafe {
IDTR.write(IDTR {
base, base,
size size
}; });
}
unsafe { unsafe {
asm!( 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<fn ()>; 256],
user_callable: [bool; 256],
len: usize,
}
#[derive(Clone, Copy)]
pub struct IdtBuilder {
vectors: [u16; 256],
funcs: [MaybeUninit<fn ()>; 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
}
}
}

View file

@ -11,10 +11,15 @@ pub mod paging;
mod constants; mod constants;
pub use constants::*; pub(self) use constants::*;
use interrupts::{pop_irq, restore_irq}; use interrupts::{pop_irq, restore_irq};
use ports::{inb, outb}; 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 /// Returns information from the CPUID command in the form
/// (ebx, edx, ecx). /// (ebx, edx, ecx).
pub fn cpuid(id: u32) -> (u32, u32, u32) { pub fn cpuid(id: u32) -> (u32, u32, u32) {
@ -160,14 +165,3 @@ pub fn enable_a20() -> bool {
return test_a20(); 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"
)
}
}

View file

@ -1 +1,133 @@
//! Functions and types related to paging. //! 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"
)
}
}