rsdp 2.0.1

Zero-allocation library for locating and parsing the RSDP, the first ACPI table
Documentation
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>>();
        }
    }
}