Add unsafe with trivial cleanups

This commit is contained in:
Ruihan Li 2025-05-07 23:26:38 +08:00 committed by Junyang Zhang
parent 3bc4424a5b
commit 3f8dbe6990
4 changed files with 66 additions and 54 deletions

View File

@ -78,8 +78,9 @@ impl RootTable {
return Err(ContextTableError::InvalidDeviceId);
}
self.get_or_create_context_table(device)
.map(device, daddr, paddr)?;
let context_table = self.get_or_create_context_table(device);
// SAFETY: The safety is upheld by the caller.
unsafe { context_table.map(device, daddr, paddr)? };
Ok(())
}
@ -93,8 +94,8 @@ impl RootTable {
return Err(ContextTableError::InvalidDeviceId);
}
self.get_or_create_context_table(device)
.unmap(device, daddr)?;
let context_table = self.get_or_create_context_table(device);
context_table.unmap(device, daddr)?;
Ok(())
}
@ -298,23 +299,26 @@ impl ContextTable {
if device.device >= 32 || device.function >= 8 {
return Err(ContextTableError::InvalidDeviceId);
}
trace!(
"Mapping Daddr: {:x?} to Paddr: {:x?} for device: {:x?}",
daddr,
paddr,
device
);
self.get_or_create_page_table(device)
.map(
&(daddr..daddr + PAGE_SIZE),
&(paddr..paddr + PAGE_SIZE),
PageProperty {
flags: PageFlags::RW,
cache: CachePolicy::Uncacheable,
priv_flags: PrivFlags::empty(),
},
)
.unwrap();
let from = daddr..daddr + PAGE_SIZE;
let to = paddr..paddr + PAGE_SIZE;
let prop = PageProperty {
flags: PageFlags::RW,
cache: CachePolicy::Uncacheable,
priv_flags: PrivFlags::empty(),
};
let pt = self.get_or_create_page_table(device);
// SAFETY: The safety is upheld by the caller.
unsafe { pt.map(&from, &to, prop).unwrap() };
Ok(())
}
@ -322,16 +326,19 @@ impl ContextTable {
if device.device >= 32 || device.function >= 8 {
return Err(ContextTableError::InvalidDeviceId);
}
trace!("Unmapping Daddr: {:x?} for device: {:x?}", daddr, device);
let pt = self.get_or_create_page_table(device);
let preempt_guard = disable_preempt();
let mut cursor = pt
.cursor_mut(&preempt_guard, &(daddr..daddr + PAGE_SIZE))
.unwrap();
unsafe {
let result = cursor.take_next(PAGE_SIZE);
debug_assert!(matches!(result, PageTableItem::MappedUntracked { .. }));
}
// SAFETY: This unmaps a page from the context table, which is always safe.
let item = unsafe { cursor.take_next(PAGE_SIZE) };
debug_assert!(matches!(item, PageTableItem::MappedUntracked { .. }));
Ok(())
}
}

View File

@ -30,32 +30,37 @@ pub unsafe fn map(daddr: Daddr, paddr: Paddr) -> Result<(), IommuError> {
let Some(table) = PAGE_TABLE.get() else {
return Err(IommuError::NoIommu);
};
// The page table of all devices is the same. So we can use any device ID.
table
.lock()
.map(PciDeviceLocation::zero(), daddr, paddr)
.map_err(|err| match err {
context_table::ContextTableError::InvalidDeviceId => unreachable!(),
context_table::ContextTableError::ModificationError(err) => {
IommuError::ModificationError(err)
}
})
let mut locked_table = table.lock();
// SAFETY: The safety is upheld by the caller.
let res = unsafe { locked_table.map(PciDeviceLocation::zero(), daddr, paddr) };
match res {
Ok(()) => Ok(()),
Err(context_table::ContextTableError::InvalidDeviceId) => unreachable!(),
Err(context_table::ContextTableError::ModificationError(err)) => {
Err(IommuError::ModificationError(err))
}
}
}
pub fn unmap(daddr: Daddr) -> Result<(), IommuError> {
let Some(table) = PAGE_TABLE.get() else {
return Err(IommuError::NoIommu);
};
// The page table of all devices is the same. So we can use any device ID.
table
.lock()
.unmap(PciDeviceLocation::zero(), daddr)
.map_err(|err| match err {
context_table::ContextTableError::InvalidDeviceId => unreachable!(),
context_table::ContextTableError::ModificationError(err) => {
IommuError::ModificationError(err)
}
})
let mut locked_table = table.lock();
let res = locked_table.unmap(PciDeviceLocation::zero(), daddr);
match res {
Ok(()) => Ok(()),
Err(context_table::ContextTableError::InvalidDeviceId) => unreachable!(),
Err(context_table::ContextTableError::ModificationError(err)) => {
Err(IommuError::ModificationError(err))
}
}
}
pub fn init() {

View File

@ -124,19 +124,16 @@ pub struct PageTableEntry(usize);
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
/// changing the page mapping.
pub unsafe fn activate_page_table(root_paddr: Paddr, root_pt_cache: CachePolicy) {
x86_64::registers::control::Cr3::write(
PhysFrame::from_start_address(x86_64::PhysAddr::new(root_paddr as u64)).unwrap(),
match root_pt_cache {
CachePolicy::Writeback => x86_64::registers::control::Cr3Flags::empty(),
CachePolicy::Writethrough => {
x86_64::registers::control::Cr3Flags::PAGE_LEVEL_WRITETHROUGH
}
CachePolicy::Uncacheable => {
x86_64::registers::control::Cr3Flags::PAGE_LEVEL_CACHE_DISABLE
}
_ => panic!("unsupported cache policy for the root page table"),
},
);
let addr = PhysFrame::from_start_address(x86_64::PhysAddr::new(root_paddr as u64)).unwrap();
let flags = match root_pt_cache {
CachePolicy::Writeback => x86_64::registers::control::Cr3Flags::empty(),
CachePolicy::Writethrough => x86_64::registers::control::Cr3Flags::PAGE_LEVEL_WRITETHROUGH,
CachePolicy::Uncacheable => x86_64::registers::control::Cr3Flags::PAGE_LEVEL_CACHE_DISABLE,
_ => panic!("unsupported cache policy for the root page table"),
};
// SAFETY: The safety is upheld by the caller.
unsafe { x86_64::registers::control::Cr3::write(addr, flags) };
}
pub fn current_page_table_paddr() -> Paddr {

View File

@ -231,13 +231,14 @@ unsafe fn dfs_release_lock<'rcu, E: PageTableEntryTrait, C: PagingConstsTrait>(
let child = cur_node.entry(i);
match child.to_ref() {
Child::PageTableRef(pt) => {
// SAFETY: The caller ensures that the node is locked.
// SAFETY: The caller ensures that the node is locked and the new guard is unique.
let child_node = unsafe { pt.make_guard_unchecked(guard) };
let child_node_va = cur_node_va + i * page_size::<C>(cur_level);
let child_node_va_end = child_node_va + page_size::<C>(cur_level);
let va_start = va_range.start.max(child_node_va);
let va_end = va_range.end.min(child_node_va_end);
// SAFETY: The caller ensures that this sub-tree is locked.
// SAFETY: The caller ensures that all the nodes in the sub-tree are locked and all
// guards are forgotten.
unsafe { dfs_release_lock(guard, child_node, child_node_va, va_start..va_end) };
}
Child::None | Child::Frame(_, _) | Child::Untracked(_, _, _) | Child::PageTable(_) => {}
@ -273,9 +274,11 @@ pub(super) unsafe fn dfs_mark_stray_and_unlock<E: PageTableEntryTrait, C: Paging
let child = sub_tree.entry(i);
match child.to_ref() {
Child::PageTableRef(pt) => {
// SAFETY: The caller ensures that the node is locked.
// SAFETY: The caller ensures that the node is locked and the new guard is unique.
let locked_pt = unsafe { pt.make_guard_unchecked(rcu_guard) };
dfs_mark_stray_and_unlock(rcu_guard, locked_pt);
// SAFETY: The caller ensures that all the nodes in the sub-tree are locked and all
// guards are forgotten.
unsafe { dfs_mark_stray_and_unlock(rcu_guard, locked_pt) };
}
Child::None | Child::Frame(_, _) | Child::Untracked(_, _, _) | Child::PageTable(_) => {}
}