Advance firmware and IOMMU support

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-15 12:57:45 +01:00
parent 01ce7a649b
commit 9dd372ad14
7 changed files with 518 additions and 134 deletions
@@ -24,6 +24,7 @@ pub mod opcode {
pub const QUERY: u16 = 0x0000;
pub const CREATE_DOMAIN: u16 = 0x0001;
pub const DESTROY_DOMAIN: u16 = 0x0002;
pub const INIT_UNITS: u16 = 0x0003;
pub const MAP: u16 = 0x0010;
pub const UNMAP: u16 = 0x0011;
pub const ASSIGN_DEVICE: u16 = 0x0020;
@@ -213,6 +214,26 @@ impl IommuScheme {
(1..u16::MAX).find(|domain_id| !self.domains.contains_key(domain_id))
}
fn ensure_unit_initialized(&mut self, unit_index: usize) -> core::result::Result<(), i32> {
let Some(unit) = self.units.get_mut(unit_index) else {
return Err(ENODEV as i32);
};
if unit.initialized() {
return Ok(());
}
unit.init().map_err(|err| {
log::error!(
"iommu: failed to initialize unit {} at MMIO {:#x}: {}",
unit_index,
unit.info().mmio_base,
err
);
EIO as i32
})
}
fn root_listing(&self) -> Vec<u8> {
let mut listing = String::from("control\n");
for (index, unit) in self.units.iter().enumerate() {
@@ -310,6 +331,49 @@ impl IommuScheme {
self.device_assignments.len() as u64,
self.units.iter().filter(|unit| unit.initialized()).count() as u64,
),
opcode::INIT_UNITS => {
let requested_index = if request.arg0 == u32::MAX {
None
} else {
Some(request.arg0 as usize)
};
let mut initialized_now = 0u32;
let mut attempted = 0u64;
for index in 0..self.units.len() {
if requested_index.is_some() && requested_index != Some(index) {
continue;
}
attempted += 1;
let was_initialized = self
.units
.get(index)
.map(|unit| unit.initialized())
.unwrap_or(false);
if let Err(errno) = self.ensure_unit_initialized(index) {
return IommuResponse::error(request.opcode, errno);
}
if !was_initialized {
initialized_now = initialized_now.saturating_add(1);
}
}
let initialized_total =
self.units.iter().filter(|unit| unit.initialized()).count() as u64;
IommuResponse::success(
request.opcode,
initialized_now,
attempted,
initialized_total,
requested_index
.map(|index| index as u64)
.unwrap_or(u64::MAX),
)
}
opcode::CREATE_DOMAIN => {
let domain_id = if request.arg0 == 0 {
match self.next_domain_id() {
@@ -364,6 +428,9 @@ impl IommuScheme {
if requested_index.is_some() && requested_index != Some(index) {
continue;
}
if !unit.initialized() {
continue;
}
match unit.drain_events() {
Ok(events) => {
if let Some(event) = events.first() {
@@ -485,9 +552,14 @@ impl IommuScheme {
Err(errno) => return IommuResponse::error(request.opcode, errno),
};
if let Err(errno) = self.ensure_unit_initialized(unit_index) {
return IommuResponse::error(request.opcode, errno);
}
let Some(domain) = self.domains.get(&domain_id) else {
return IommuResponse::error(request.opcode, ENOENT as i32);
};
let Some(unit) = self.units.get_mut(unit_index) else {
return IommuResponse::error(request.opcode, ENODEV as i32);
};
@@ -824,6 +896,26 @@ mod tests {
assert_eq!(query_response.arg1, 1);
}
#[test]
fn init_units_on_empty_scheme_is_a_noop_success() {
let mut scheme = IommuScheme::new();
let control = scheme
.open("control", 0, 0, 0)
.unwrap_or_else(|err| panic!("open control failed: {err}"))
.unwrap_or_else(|| panic!("control open returned no handle"));
let request = IommuRequest::new(opcode::INIT_UNITS, u32::MAX, 0, 0, 0);
scheme
.write(control, &request.to_bytes())
.unwrap_or_else(|err| panic!("init units write failed: {err}"));
let response = read_response(&mut scheme, control);
assert_eq!(response.status, 0);
assert_eq!(response.arg0, 0);
assert_eq!(response.arg1, 0);
assert_eq!(response.arg2, 0);
}
#[test]
fn domain_handle_can_map_pages() {
let mut scheme = IommuScheme::new();