Files
k-os/crates/kernel/src/board.rs

95 lines
3.3 KiB
Rust

//! The authority init starts with: per-device MMIO and IRQ capabilities plus one SystemControl.
use domain::{error::KernelError, resource::Resource};
use platform::Platform;
use types::{
address::PhysicalAddress,
bounded::{CapSlot, 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>,
}
/// Where `grant_boot_authority` put init's machine-wide capabilities.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BootCaps {
pub system_control: CapSlot,
pub console: CapSlot,
}
/// Where `grant_boot_authority` put one device's capabilities in init.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BoardCaps {
pub region: CapSlot,
pub irq: Option<CapSlot>,
}
impl<P: Platform> Kernel<P> {
/// Creates one device region per board device, owned by `init`; fails on the first invalid one.
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` (with Delegate) caps to every device region and IRQ, then SystemControl.
/// Writes each device's slots into `placed` in order; returns the SystemControl
/// and Console slots.
pub fn grant_boot_authority(
&mut self,
init: ProcessId,
devices: &[BoardDevice],
placed: &mut [Option<BoardCaps>],
) -> Result<BootCaps, KernelError> {
let delegate = PermissionSet::empty().with(Permission::Delegate);
let control = delegate.with(Permission::Execute);
let mmio = delegate.with(Permission::Read).with(Permission::Write);
for (i, device) in devices.iter().enumerate() {
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)?;
let region = self.grant_cap(init, Resource::Memory(region), mmio)?;
let irq = match device.irq {
Some(irq) => Some(self.grant_cap(init, Resource::Interrupt(irq), control)?),
None => None,
};
if let Some(slot) = placed.get_mut(i) {
*slot = Some(BoardCaps { region, irq });
}
}
Ok(BootCaps {
system_control: self.grant_cap(init, Resource::SystemControl, control)?,
console: self.grant_cap(init, Resource::Console, delegate.with(Permission::Write))?,
})
}
/// 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)
}
}