forked from rust-osdev/acpi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhandler.rs
More file actions
160 lines (142 loc) · 5.45 KB
/
handler.rs
File metadata and controls
160 lines (142 loc) · 5.45 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use core::{fmt, ops::Deref, pin::Pin, 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.
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> fmt::Debug for PhysicalMapping<H, T>
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<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.
///
/// ### Safety
///
/// The caller must ensure that the physical memory can be safely mapped.
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 get(&self) -> Pin<&T> {
unsafe { Pin::new_unchecked(self.virtual_start.as_ref()) }
}
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
T: Unpin,
H: AcpiHandler,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { self.virtual_start.as_ref() }
}
}
impl<H, T> DerefMut for PhysicalMapping<H, T>
where
H: AcpiHandler,
{
fn deref_mut(&mut self) -> &mut T {
unsafe { self.virtual_start.as_mut() }
}
}
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>>();
}
}
}