feat: devices via caps (BootInfo, init assigns devices, drivers map MMIO/DMA themselves, IRQ-driven input); drop boot device wiring and info pages; headless display fallback
This commit is contained in:
@@ -7,7 +7,7 @@ use domain::{error::KernelError, resource::Resource};
|
||||
use platform::Platform;
|
||||
use types::{
|
||||
address::PhysicalAddress,
|
||||
bounded::PageCount,
|
||||
bounded::{CapSlot, PageCount},
|
||||
id::{IrqId, ProcessId, RegionId},
|
||||
permission::{Permission, PermissionSet},
|
||||
};
|
||||
@@ -23,6 +23,13 @@ pub struct BoardDevice {
|
||||
pub irq: Option<IrqId>,
|
||||
}
|
||||
|
||||
/// 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 device that is misaligned, overlaps the frame pool or
|
||||
@@ -45,28 +52,33 @@ impl<P: Platform> Kernel<P> {
|
||||
|
||||
/// 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.
|
||||
/// init can hand them on. Writes where each device's caps went into
|
||||
/// `placed` (one entry per device, in order) and returns the
|
||||
/// SystemControl slot, so boot can tell init.
|
||||
pub fn grant_boot_authority(
|
||||
&mut self,
|
||||
init: ProcessId,
|
||||
devices: &[BoardDevice],
|
||||
) -> Result<(), KernelError> {
|
||||
placed: &mut [Option<BoardCaps>],
|
||||
) -> Result<CapSlot, 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 {
|
||||
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)?;
|
||||
self.grant_cap(init, Resource::Memory(region), mmio)?;
|
||||
if let Some(irq) = device.irq {
|
||||
self.grant_cap(init, Resource::Interrupt(irq), control)?;
|
||||
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 });
|
||||
}
|
||||
}
|
||||
self.grant_cap(init, Resource::SystemControl, control)?;
|
||||
Ok(())
|
||||
self.grant_cap(init, Resource::SystemControl, control)
|
||||
}
|
||||
|
||||
/// The device region that starts at `base`.
|
||||
|
||||
@@ -24,7 +24,7 @@ pub mod proofs;
|
||||
mod smp;
|
||||
pub mod syscall;
|
||||
|
||||
pub use board::BoardDevice;
|
||||
pub use board::{BoardCaps, BoardDevice};
|
||||
pub use syscall::{SyscallOutcome, SyscallResult};
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -30,7 +30,8 @@ fn board() -> [BoardDevice; 2] {
|
||||
fn booted() -> (Kernel<MockPlatform>, ProcessId) {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
k.register_board(init, &board()).unwrap();
|
||||
k.grant_boot_authority(init, &board()).unwrap();
|
||||
k.grant_boot_authority(init, &board(), &mut [None; 2])
|
||||
.unwrap();
|
||||
(k, init)
|
||||
}
|
||||
|
||||
@@ -147,7 +148,8 @@ fn boot_authority_keeps_existing_slots() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let before = k.cap_slots_for_test(init);
|
||||
k.register_board(init, &board()).unwrap();
|
||||
k.grant_boot_authority(init, &board()).unwrap();
|
||||
k.grant_boot_authority(init, &board(), &mut [None; 2])
|
||||
.unwrap();
|
||||
let after = k.cap_slots_for_test(init);
|
||||
for (b, a) in before.iter().zip(after.iter()) {
|
||||
assert!(!b || *a, "an existing slot was disturbed");
|
||||
@@ -196,7 +198,7 @@ fn init_can_route_a_board_irq() {
|
||||
fn granting_needs_a_registered_board() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
assert!(matches!(
|
||||
k.grant_boot_authority(init, &board()),
|
||||
k.grant_boot_authority(init, &board(), &mut [None; 2]),
|
||||
Err(KernelError::NoSuchObject)
|
||||
));
|
||||
}
|
||||
@@ -207,7 +209,7 @@ fn granting_needs_the_regions_to_be_inits() {
|
||||
let other = k.create_process_internal().unwrap();
|
||||
k.register_board(other, &board()).unwrap();
|
||||
assert!(matches!(
|
||||
k.grant_boot_authority(init, &board()),
|
||||
k.grant_boot_authority(init, &board(), &mut [None; 2]),
|
||||
Err(KernelError::NoSuchObject)
|
||||
));
|
||||
}
|
||||
@@ -321,3 +323,24 @@ fn region_address_rejects_other_objects() {
|
||||
let e = error_of(k.handle_syscall(Syscall::RegionAddress { region: endpoint }));
|
||||
assert!(matches!(e, KernelError::WrongObject));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boot_authority_reports_where_each_cap_went() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
k.register_board(init, &board()).unwrap();
|
||||
let mut placed = [None; 2];
|
||||
let control = k.grant_boot_authority(init, &board(), &mut placed).unwrap();
|
||||
for (d, caps) in board().iter().zip(placed) {
|
||||
let caps = caps.expect("every device reported");
|
||||
let region = k.device_region_at(d.base).unwrap();
|
||||
assert_eq!(
|
||||
caps.region,
|
||||
slot_holding(&k, init, Resource::Memory(region))
|
||||
);
|
||||
assert_eq!(
|
||||
caps.irq,
|
||||
Some(slot_holding(&k, init, Resource::Interrupt(d.irq.unwrap())))
|
||||
);
|
||||
}
|
||||
assert_eq!(control, slot_holding(&k, init, Resource::SystemControl));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user