feat(kernel): SystemControl cap for halt, board device table + boot caps to init, RegionAddress syscall, idle loop services irqs
This commit is contained in:
79
crates/kernel/src/board.rs
Normal file
79
crates/kernel/src/board.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
//! The authority init starts with. The board describes its devices; the
|
||||
//! kernel turns each into capabilities (an MMIO region and an IRQ line)
|
||||
//! and adds one SystemControl capability. Init then decides who gets what;
|
||||
//! nothing else is privileged by its process id.
|
||||
|
||||
use domain::{error::KernelError, resource::Resource};
|
||||
use platform::Platform;
|
||||
use types::{
|
||||
address::PhysicalAddress,
|
||||
bounded::PageCount,
|
||||
id::{IrqId, ProcessId, RegionId},
|
||||
permission::{Permission, PermissionSet},
|
||||
};
|
||||
|
||||
use crate::Kernel;
|
||||
|
||||
/// A device the board exposes to userspace: its MMIO window and, if it
|
||||
/// raises interrupts, its IRQ line.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BoardDevice {
|
||||
pub base: PhysicalAddress,
|
||||
pub pages: PageCount,
|
||||
pub irq: Option<IrqId>,
|
||||
}
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
/// Creates one device region per board device, owned by `init`. Fails
|
||||
/// on the first device that is misaligned, overlaps the frame pool or
|
||||
/// another device, or names an IRQ the kernel cannot route.
|
||||
pub fn register_board(
|
||||
&mut self,
|
||||
init: ProcessId,
|
||||
devices: &[BoardDevice],
|
||||
) -> Result<(), KernelError> {
|
||||
for device in devices {
|
||||
if let Some(irq) = device.irq
|
||||
&& !self.irqs.is_valid(irq)
|
||||
{
|
||||
return Err(KernelError::InvalidIrq(irq));
|
||||
}
|
||||
self.create_device_region(init, device.base, device.pages)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gives `init` a capability to every registered device's region and
|
||||
/// IRQ line, then one SystemControl capability. All carry Delegate so
|
||||
/// init can hand them on. Done separately from `register_board` so
|
||||
/// boot code can wire drivers to the regions first.
|
||||
pub fn grant_boot_authority(
|
||||
&mut self,
|
||||
init: ProcessId,
|
||||
devices: &[BoardDevice],
|
||||
) -> Result<(), KernelError> {
|
||||
let delegate = PermissionSet::empty().with(Permission::Delegate);
|
||||
let control = delegate.with(Permission::Execute);
|
||||
let mmio = delegate.with(Permission::Read).with(Permission::Write);
|
||||
for device in devices {
|
||||
let region = self
|
||||
.device_region_at(device.base)
|
||||
.filter(|id| self.regions.get(*id).is_some_and(|r| r.owner() == init))
|
||||
.ok_or(KernelError::NoSuchObject)?;
|
||||
self.grant_cap(init, Resource::Memory(region), mmio)?;
|
||||
if let Some(irq) = device.irq {
|
||||
self.grant_cap(init, Resource::Interrupt(irq), control)?;
|
||||
}
|
||||
}
|
||||
self.grant_cap(init, Resource::SystemControl, control)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The device region that starts at `base`.
|
||||
pub fn device_region_at(&self, base: PhysicalAddress) -> Option<RegionId> {
|
||||
self.regions
|
||||
.iter()
|
||||
.find(|(_, r)| r.backing() == domain::memory::RegionBacking::Device && r.base() == base)
|
||||
.map(|(id, _)| id)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user