fix: align MMIO mappings to page boundaries for VirtIO capabilities

VirtIO capability structures can start at non-page-aligned offsets within\nBARs. The kernel physmap requires page-aligned addresses. Align the physical\naddress down, adjust the size, and apply the page offset to the returned\npointer. Track the original map pointer/size separately for correct unmapping.

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-05-15 07:23:25 +01:00
parent fba204e2f5
commit ac7c5b1761
@@ -81,6 +81,8 @@ fn ensure_memory_root() -> Result<libredox::Fd> {
pub struct MmioRegion {
ptr: *mut u8,
size: usize,
map_ptr: *mut u8,
map_size: usize,
}
impl MmioRegion {
@@ -89,7 +91,13 @@ impl MmioRegion {
return Err(DriverError::InvalidAddress(phys_addr));
}
let aligned_size = size.next_multiple_of(PAGE_SIZE);
// Align physical address down to page boundary for physmap compatibility.
// The kernel requires page-aligned addresses; VirtIO capabilities can
// start at non-page-aligned offsets within BARs.
let page_offset = (phys_addr as usize) % PAGE_SIZE;
let aligned_phys = phys_addr - page_offset as u64;
let adjusted_size = size + page_offset;
let aligned_size = adjusted_size.next_multiple_of(PAGE_SIZE);
let path = format!("physical@{}", cache.suffix());
let mode = if prot.contains(MmioProt::READ | MmioProt::WRITE) {
@@ -112,7 +120,7 @@ impl MmioRegion {
let mem_fd = root_fd.openat(&path, (O_CLOEXEC | mode) as i32, 0)?;
let map = Map {
offset: phys_addr as usize,
offset: aligned_phys as usize,
size: aligned_size,
flags: mmap_prot | redox_syscall::MapFlags::from_bits_truncate(MAP_SHARED.bits()),
address: 0,
@@ -127,8 +135,10 @@ impl MmioRegion {
})?;
Ok(Self {
ptr: ptr as *mut u8,
size: aligned_size,
ptr: unsafe { (ptr as *mut u8).add(page_offset) },
size,
map_ptr: ptr as *mut u8,
map_size: aligned_size,
})
}
@@ -290,8 +300,8 @@ impl MmioRegion {
impl Drop for MmioRegion {
fn drop(&mut self) {
if !self.ptr.is_null() {
let _ = unsafe { libredox::call::munmap(self.ptr as *mut (), self.size) };
if !self.map_ptr.is_null() {
let _ = unsafe { libredox::call::munmap(self.map_ptr as *mut (), self.map_size) };
}
}
}