95 lines
3.3 KiB
Rust
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)
|
|
}
|
|
}
|