341 lines
9.9 KiB
Rust
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()
|
|
}
|