use core::{fmt, 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::()` /// bytes, but may be bigger. /// /// See `PhysicalMapping::new` for the meaning of each field. pub struct PhysicalMapping where H: AcpiHandler, { physical_start: usize, virtual_start: NonNull, region_length: usize, // Can be equal or larger than size_of::() mapped_length: usize, // Differs from `region_length` if padding is added for alignment handler: H, } impl fmt::Debug for PhysicalMapping where H: AcpiHandler + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PhysicalMapping") .field("physical_start", &self.physical_start) .field("virtual_start", &self.virtual_start) .field("region_length", &self.region_length) .field("mapped_length", &self.mapped_length) .field("handler", &self.handler) .finish() } } impl PhysicalMapping 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::()`. /// - `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, 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 { 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 Send for PhysicalMapping {} impl Deref for PhysicalMapping where H: AcpiHandler, { type Target = T; fn deref(&self) -> &T { unsafe { self.virtual_start.as_ref() } } } impl Drop for PhysicalMapping 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::()`). 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::()`. unsafe fn map_physical_region(&self, physical_address: usize, size: usize) -> PhysicalMapping; /// 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(region: &PhysicalMapping); } #[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() {} fn caller() { test_send_sync::>(); } } }