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