yggdrasil/src/arch/mod.rs

341 lines
9.9 KiB
Rust

//! Provides architecture/platform-specific implementation details
use core::{ops::DerefMut, time::Duration};
use abi::error::Error;
/// Returns an absolute address to the given symbol
#[macro_export]
macro_rules! absolute_address {
($sym:expr) => {{
let mut _x: usize;
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::asm!("ldr {0}, ={1}", out(reg) _x, sym $sym);
}
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::asm!("movabsq ${1}, {0}", out(reg) _x, sym $sym, options(att_syntax));
}
_x
}};
}
use cfg_if::cfg_if;
use device_api::{
interrupt::{
ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController,
MessageInterruptController,
},
timer::MonotonicTimestampProviderDevice,
ResetDevice,
};
use kernel_util::{
mem::{address::PhysicalAddress, device::RawDeviceMemoryMapping, table::EntryLevel},
sync::IrqGuard,
};
use crate::{
mem::phys::PhysicalMemoryRegion,
task::{sched::CpuQueue, thread::ThreadId, Cpu},
};
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
pub mod aarch64;
pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
} else if #[cfg(target_arch = "x86_64")] {
pub mod x86_64;
pub use x86_64::{X86_64 as ArchitectureImpl, ARCHITECTURE};
} else {
compile_error!("Architecture is not supported");
}
}
/// Architecture-specific lowest level of page mapping
pub type L3 = <ArchitectureImpl as Architecture>::L3;
// Architecture interfaces
/// Describes messages sent from some CPU to others
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u64)]
pub enum CpuMessage {
/// Indicates that the sender CPU entered kernel panic and wants other CPUs to follow
Panic,
/// Indicates that the cores should either halt and wait for the caller to shut the system
/// down, or they should shut down by themselves, depending on the platform
Shutdown,
}
/// Interface for an architecture-specific facilities
#[allow(unused)]
pub trait Architecture {
/// Address, to which "zero" address is mapped in the virtual address space
const KERNEL_VIRT_OFFSET: usize;
/// IRQ number type associated with the architecture
type IrqNumber;
/// Lowest page entry level, usually 4KiB pages
type L3: EntryLevel;
/// Starts up the application processors that may be present in the system.
///
/// # Safety
///
/// Only safe to call once during system init.
unsafe fn start_application_processors(&self) {}
/// Allocates a virtual mapping for the specified physical memory region.
///
/// # Safety
///
/// The caller must ensure the validity of the provided region.
unsafe fn map_device_memory(
&self,
base: PhysicalAddress,
size: usize,
) -> Result<RawDeviceMemoryMapping, Error>;
/// Removes the provided mapping from the kernel's translation tables.
///
/// # Safety
///
/// The caller must ensure the mapping is and will no longer be used.
unsafe fn unmap_device_memory(&self, map: &RawDeviceMemoryMapping);
/// Maps the physical memory regions into the kernel space so they can later be accessed by the
/// kernel
fn map_physical_memory<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
&self,
it: I,
memory_start: PhysicalAddress,
memory_end: PhysicalAddress,
) -> Result<(), Error>;
/// Converts a physical address to a virtual one, so it can be accessed by the kernel
fn virtualize(address: u64) -> usize;
/// Converts a virtual address created by [Architecture::virtualize] back to its physical form
fn physicalize(address: usize) -> u64;
// Architecture intrinsics
/// Suspends CPU until an interrupt is received
fn wait_for_interrupt();
/// Sets the local CPU's interrupt mask.
///
/// # Safety
///
/// Enabling interrupts may lead to unexpected behavior unless the context explicitly expects
/// them.
unsafe fn set_interrupt_mask(mask: bool);
/// Returns the local CPU's interrupt mask
fn interrupt_mask() -> bool;
/// Returns the count of present CPUs, including the BSP
fn cpu_count() -> usize;
/// Adds an external interrupt controller to the system
fn register_external_interrupt_controller(
&self,
intc: &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>,
) -> Result<(), Error> {
Err(Error::NotImplemented)
}
/// Adds a local interrupt controller to the system
fn register_local_interrupt_controller(
&self,
intc: &'static dyn LocalInterruptController<IpiMessage = CpuMessage>,
) -> Result<(), Error> {
Err(Error::NotImplemented)
}
/// Adds a message-signalled interrupt (MSI/MSI-X) controller to the system
fn register_message_interrupt_controller(
&self,
intc: &'static dyn MessageInterruptController,
) -> Result<(), Error> {
Err(Error::NotImplemented)
}
/// Adds a monotonic timer to the system
fn register_monotonic_timer(
&self,
timer: &'static dyn MonotonicTimestampProviderDevice,
) -> Result<(), Error> {
Err(Error::NotImplemented)
}
/// Adds a reset device to the system
fn register_reset_device(&self, reset: &'static dyn ResetDevice) -> Result<(), Error> {
Err(Error::NotImplemented)
}
// TODO only supports 1 extintc per system
/// Returns the primary external interrupt controller
fn external_interrupt_controller(
&'static self,
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
unimplemented!()
}
/// Returns the local interrupt controller
fn local_interrupt_controller(
&'static self,
) -> &'static dyn LocalInterruptController<IpiMessage = CpuMessage> {
unimplemented!()
}
/// Returns the MSI/MSI-X-capable interrupt controller
fn message_interrupt_controller(&'static self) -> &'static dyn MessageInterruptController {
unimplemented!()
}
/// Returns the monotonic timer
fn monotonic_timer(&'static self) -> &'static dyn MonotonicTimestampProviderDevice {
unimplemented!()
}
/// Sends a message to the requested set of CPUs through an interprocessor interrupt.
///
/// # Note
///
/// u64 limits the number of targetable CPUs to (only) 64. Platform-specific implementations
/// may impose narrower restrictions.
///
/// # Safety
///
/// As the call may alter the flow of execution on CPUs, this function is unsafe.
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error> {
Ok(())
}
/// Performs a CPU reset.
///
/// # Safety
///
/// The caller must ensure it is actually safe to reset, i.e. no critical processes will be
/// aborted and no data will be lost.
unsafe fn reset(&self) -> ! {
Self::set_interrupt_mask(true);
loop {
Self::wait_for_interrupt();
}
}
}
/// Interface for accessing local CPU (the CPU of the current context)
pub trait LocalCpuAccess<C: CpuAccess>: DerefMut<Target = C> {
/// Consumes the struct, returning the inner IrqGuard for further use
fn into_guard(self) -> IrqGuard;
}
/// Interface for accessing CPU-local data
pub trait CpuAccess: Sized {
/// Safe wrapper for accessing local CPU
type Local: LocalCpuAccess<Self>;
/// Returns the local CPU wrapper or None, if not yet initialized:
fn try_local() -> Option<Self::Local>;
/// Returns the local CPU wrapper
fn local() -> Self::Local {
Self::try_local().expect("Local CPU has not yet been initialized")
}
/// Returns the ID of the local processor or 0 if the processor has not yet been initialized
fn local_id() -> u32;
/// Initializes the CPU's scheduling queue.
///
/// # Panics
///
/// Will panic, if the initialization has already been performed.
fn init_queue(&mut self, queue: &'static CpuQueue);
/// Returns the CPU's scheduling queue or None if it has not yet been initialized
fn get_queue(&self) -> Option<&'static CpuQueue>;
/// Returns the CPU's scheduling queue or panics if it has not yet been initialized
fn queue(&self) -> &'static CpuQueue {
self.get_queue()
.expect("CPU's queue has not yet been initialized")
}
/// Returns the CPU index
fn id(&self) -> u32;
/// Returns current thread ID or none if idle
fn current_thread_id(&self) -> Option<ThreadId>;
/// Update the current thread ID
unsafe fn set_current_thread_id(&mut self, id: Option<ThreadId>);
}
// External API for architecture specifics
#[no_mangle]
fn __acquire_irq_guard() -> bool {
let mask = ArchitectureImpl::interrupt_mask();
unsafe {
ArchitectureImpl::set_interrupt_mask(true);
}
mask
}
#[no_mangle]
fn __release_irq_guard(mask: bool) {
unsafe {
ArchitectureImpl::set_interrupt_mask(mask);
}
}
#[no_mangle]
fn __cpu_index() -> usize {
Cpu::local().id() as _
}
#[no_mangle]
fn __cpu_count() -> usize {
ArchitectureImpl::cpu_count()
}
#[no_mangle]
fn __virtualize(addr: u64) -> usize {
ArchitectureImpl::virtualize(addr)
}
#[no_mangle]
fn __physicalize(addr: usize) -> u64 {
ArchitectureImpl::physicalize(addr)
}
#[no_mangle]
fn __map_device_pages(
base: PhysicalAddress,
count: usize,
) -> Result<RawDeviceMemoryMapping, Error> {
unsafe { ARCHITECTURE.map_device_memory(base, count) }
}
#[no_mangle]
fn __unmap_device_pages(mapping: &RawDeviceMemoryMapping) {
unsafe { ARCHITECTURE.unmap_device_memory(mapping) }
}
#[no_mangle]
fn __monotonic_timestamp() -> Result<Duration, Error> {
ARCHITECTURE.monotonic_timer().monotonic_timestamp()
}
#[no_mangle]
fn __message_interrupt_controller() -> &'static dyn MessageInterruptController {
ARCHITECTURE.message_interrupt_controller()
}