summaryrefslogtreecommitdiff
path: root/src/handler.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/handler.rs')
-rw-r--r--src/handler.rs128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/handler.rs b/src/handler.rs
new file mode 100644
index 0000000..6cc0cd0
--- /dev/null
+++ b/src/handler.rs
@@ -0,0 +1,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>>();
+ }
+ }
+}