1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
use core::{ops::Deref, ptr::NonNull};
/// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by
/// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::<T>()`
/// bytes, but may be bigger.
///
/// See `PhysicalMapping::new` for the meaning of each field.
#[derive(Debug)]
pub struct PhysicalMapping<H, T>
where
H: AcpiHandler,
{
physical_start: usize,
virtual_start: NonNull<T>,
region_length: usize, // Can be equal or larger than size_of::<T>()
mapped_length: usize, // Differs from `region_length` if padding is added for alignment
handler: H,
}
impl<H, T> PhysicalMapping<H, T>
where
H: AcpiHandler,
{
/// Construct a new `PhysicalMapping`.
///
/// - `physical_start` should be the physical address of the structure to be mapped.
/// - `virtual_start` should be the corresponding virtual address of that structure. It may differ from the
/// start of the region mapped due to requirements of the paging system. It must be a valid, non-null
/// pointer.
/// - `region_length` should be the number of bytes requested to be mapped. It must be equal to or larger than
/// `size_of::<T>()`.
/// - `mapped_length` should be the number of bytes mapped to fulfill the request. It may be equal to or larger
/// than `region_length`, due to requirements of the paging system or other reasoning.
/// - `handler` should be the same `AcpiHandler` that created the mapping. When the `PhysicalMapping` is
/// dropped, it will be used to unmap the structure.
pub unsafe fn new(
physical_start: usize,
virtual_start: NonNull<T>,
region_length: usize,
mapped_length: usize,
handler: H,
) -> Self {
Self { physical_start, virtual_start, region_length, mapped_length, handler }
}
pub fn physical_start(&self) -> usize {
self.physical_start
}
pub fn virtual_start(&self) -> NonNull<T> {
self.virtual_start
}
pub fn region_length(&self) -> usize {
self.region_length
}
pub fn mapped_length(&self) -> usize {
self.mapped_length
}
pub fn handler(&self) -> &H {
&self.handler
}
}
unsafe impl<H: AcpiHandler + Send, T: Send> Send for PhysicalMapping<H, T> {}
impl<H, T> Deref for PhysicalMapping<H, T>
where
H: AcpiHandler,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { self.virtual_start.as_ref() }
}
}
impl<H, T> Drop for PhysicalMapping<H, T>
where
H: AcpiHandler,
{
fn drop(&mut self) {
H::unmap_physical_region(self)
}
}
/// An implementation of this trait must be provided to allow `acpi` to access platform-specific
/// functionality, such as mapping regions of physical memory. You are free to implement these
/// however you please, as long as they conform to the documentation of each function. The handler is stored in
/// every `PhysicalMapping` so it's able to unmap itself when dropped, so this type needs to be something you can
/// clone/move about freely (e.g. a reference, wrapper over `Rc`, marker struct, etc.).
pub trait AcpiHandler: Clone {
/// Given a physical address and a size, map a region of physical memory that contains `T` (note: the passed
/// size may be larger than `size_of::<T>()`). The address is not neccessarily page-aligned, so the
/// implementation may need to map more than `size` bytes. The virtual address the region is mapped to does not
/// matter, as long as it is accessible to `acpi`.
///
/// See the documentation on `PhysicalMapping::new` for an explanation of each field on the `PhysicalMapping`
/// return type.
///
/// ## Safety
///
/// - `physical_address` must point to a valid `T` in physical memory.
/// - `size` must be at least `size_of::<T>()`.
unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T>;
/// Unmap the given physical mapping. This is called when a `PhysicalMapping` is dropped, you should **not** manually call this.
///
/// Note: A reference to the handler used to construct `region` can be acquired by calling [`PhysicalMapping::handler`].
fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(dead_code)]
fn test_send_sync() {
// verify that PhysicalMapping implements Send and Sync
fn test_send_sync<T: Send>() {}
fn caller<H: AcpiHandler + Send, T: Send>() {
test_send_sync::<PhysicalMapping<H, T>>();
}
}
}