Merge "Require safety comments in vmbase example." into main
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index 4fe532a..a6f3bfa 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -16,6 +16,8 @@
#![no_main]
#![no_std]
+#![deny(unsafe_op_in_unsafe_fn)]
+#![deny(clippy::undocumented_unsafe_blocks)]
mod exceptions;
mod layout;
@@ -80,8 +82,9 @@
info!("Checking FDT...");
let fdt = dtb_range();
- let fdt =
- unsafe { core::slice::from_raw_parts_mut(fdt.start.0 as *mut u8, fdt.end.0 - fdt.start.0) };
+ let fdt_size = fdt.end.0 - fdt.start.0;
+ // SAFETY: The DTB range is valid, writable memory, and we don't construct any aliases to it.
+ let fdt = unsafe { core::slice::from_raw_parts_mut(fdt.start.0 as *mut u8, fdt_size) };
let fdt = Fdt::from_mut_slice(fdt).unwrap();
info!("FDT passed verification.");
check_fdt(fdt);
@@ -98,6 +101,7 @@
check_data();
check_dice();
+ // SAFETY: This is the only place where `make_pci_root` is called.
let mut pci_root = unsafe { pci_info.make_pci_root() };
check_pci(&mut pci_root);
@@ -123,7 +127,9 @@
fn check_data() {
info!("INITIALISED_DATA: {:?}", INITIALISED_DATA.as_ptr());
+ // SAFETY: We only print the addresses of the static mutable variable, not actually access it.
info!("ZEROED_DATA: {:?}", unsafe { ZEROED_DATA.as_ptr() });
+ // SAFETY: We only print the addresses of the static mutable variable, not actually access it.
info!("MUTABLE_DATA: {:?}", unsafe { MUTABLE_DATA.as_ptr() });
assert_eq!(INITIALISED_DATA[0], 1);
@@ -131,7 +137,11 @@
assert_eq!(INITIALISED_DATA[2], 3);
assert_eq!(INITIALISED_DATA[3], 4);
+ // SAFETY: Nowhere else in the program accesses this static mutable variable, so there is no
+ // chance of concurrent access.
let zeroed_data = unsafe { &mut ZEROED_DATA };
+ // SAFETY: Nowhere else in the program accesses this static mutable variable, so there is no
+ // chance of concurrent access.
let mutable_data = unsafe { &mut MUTABLE_DATA };
for element in zeroed_data.iter() {
diff --git a/vmbase/example/src/pci.rs b/vmbase/example/src/pci.rs
index 6d33215..26bc29b 100644
--- a/vmbase/example/src/pci.rs
+++ b/vmbase/example/src/pci.rs
@@ -120,11 +120,21 @@
struct HalImpl;
+/// SAFETY: See the 'Implementation Safety' comments on methods below for how they fulfill the
+/// safety requirements of the unsafe `Hal` trait.
unsafe impl Hal for HalImpl {
+ /// # Implementation Safety
+ ///
+ /// `dma_alloc` ensures the returned DMA buffer is not aliased with any other allocation or
+ /// reference in the program until it is deallocated by `dma_dealloc` by allocating a unique
+ /// block of memory using `alloc_zeroed`, which is guaranteed to allocate valid, unique and
+ /// zeroed memory. We request an alignment of at least `PAGE_SIZE` from `alloc_zeroed`.
fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull<u8>) {
debug!("dma_alloc: pages={}", pages);
- let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
- // Safe because the layout has a non-zero size.
+ let layout =
+ Layout::from_size_align(pages.checked_mul(PAGE_SIZE).unwrap(), PAGE_SIZE).unwrap();
+ assert_ne!(layout.size(), 0);
+ // SAFETY: We just checked that the layout has a non-zero size.
let vaddr = unsafe { alloc_zeroed(layout) };
let vaddr =
if let Some(vaddr) = NonNull::new(vaddr) { vaddr } else { handle_alloc_error(layout) };
@@ -135,14 +145,19 @@
unsafe fn dma_dealloc(paddr: PhysAddr, vaddr: NonNull<u8>, pages: usize) -> i32 {
debug!("dma_dealloc: paddr={:#x}, pages={}", paddr, pages);
let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();
- // Safe because the memory was allocated by `dma_alloc` above using the same allocator, and
- // the layout is the same as was used then.
+ // SAFETY: The memory was allocated by `dma_alloc` above using the same allocator, and the
+ // layout is the same as was used then.
unsafe {
dealloc(vaddr.as_ptr(), layout);
}
0
}
+ /// # Implementation Safety
+ ///
+ /// The returned pointer must be valid because the `paddr` describes a valid MMIO region, and we
+ /// previously mapped the entire PCI MMIO range. It can't alias any other allocations because
+ /// the PCI MMIO range doesn't overlap with any other memory ranges.
unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8> {
NonNull::new(paddr as _).unwrap()
}