diff options
Diffstat (limited to 'src/handler.rs')
-rw-r--r-- | src/handler.rs | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/src/handler.rs b/src/handler.rs new file mode 100644 index 0000000..6cc0cd0 --- /dev/null +++ b/src/handler.rs @@ -0,0 +1,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>>(); + } + } +} |