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>>();
        }
    }
}