diff --git a/kernel/src/utils/mod.rs b/kernel/src/utils/mod.rs index 5390225c5..2b78ab1f6 100644 --- a/kernel/src/utils/mod.rs +++ b/kernel/src/utils/mod.rs @@ -8,9 +8,11 @@ pub mod bitmap_allocator; pub mod fw_meta; pub mod immut_after_init; pub mod memory_region; +pub mod scoped; pub mod util; pub use memory_region::MemoryRegion; +pub use scoped::{ScopedMut, ScopedRef}; pub use util::{ align_down, align_up, halt, is_aligned, overlap, page_align_up, page_offset, zero_mem_region, }; diff --git a/kernel/src/utils/scoped.rs b/kernel/src/utils/scoped.rs new file mode 100644 index 000000000..417ad797e --- /dev/null +++ b/kernel/src/utils/scoped.rs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) Microsoft Corporation +// +// Author: Jon Lange (jlange@microsoft.com) + +use core::ops::{Deref, DerefMut}; + +/// `ScopedRef` and `ScopedMut` are designed to solve the problem of managing +/// lifetimes of references created from pointers. Normally, when a reference +/// is created from a pointer (such as with `ptr::as_ref()`), it is associated +/// with the static lifetime, and as a result, the compiler is unable to +/// determine whether the reference will live long enough for its intended +/// use. While functions like `ptr::as_ref()` can associate the reference with +/// a lifetime, the compiler cannot usefully use this information to enfoce +/// lifetime checks on pointers generated in this way because although every +/// reference can be bound to a lifetime, a reference does not by itself own a +/// lifetime, and without an owning lifetime, the compiler has no way to know +/// when the lifetime to which the reference is bound goes out of scope. +/// The `ScopedRef` and `ScopedMut` objects solve this by creating a new object +/// every time a pointer is converted to a reference, so there is an actual +/// object with an associated lifetime that the compiler can use to ensure that +/// the reference remains valid. + +#[derive(Debug)] +pub struct ScopedRef<'a, T> { + inner: &'a T, +} + +impl ScopedRef<'_, T> { + /// Generates a new `ScopedRef` from a pointer. + /// + /// # Safety + /// + /// This is a dereference of a raw pointer, and no correctness checks are + /// performed. + pub unsafe fn new(ptr: *const T) -> Option { + unsafe { ptr.as_ref().map(|inner| Self { inner }) } + } +} + +impl AsRef for ScopedRef<'_, T> { + fn as_ref(&self) -> &T { + self.inner + } +} + +impl Deref for ScopedRef<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + self.as_ref() + } +} + +impl Drop for ScopedRef<'_, T> { + fn drop(&mut self) {} +} + +#[derive(Debug)] +pub struct ScopedMut<'a, T> { + inner: &'a mut T, +} + +impl ScopedMut<'_, T> { + /// Generates a new `ScopedMut` from a pointer. + /// + /// # Safety + /// + /// This is a dereference of a raw pointer, and no correctness checks are + /// performed. + pub unsafe fn new(ptr: *mut T) -> Option { + unsafe { ptr.as_mut().map(|inner| Self { inner }) } + } +} + +impl AsRef for ScopedMut<'_, T> { + fn as_ref(&self) -> &T { + self.inner + } +} + +impl AsMut for ScopedMut<'_, T> { + fn as_mut(&mut self) -> &mut T { + self.inner + } +} + +impl Deref for ScopedMut<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + self.as_ref() + } +} + +impl DerefMut for ScopedMut<'_, T> { + fn deref_mut(&mut self) -> &mut T { + self.as_mut() + } +} + +impl Drop for ScopedMut<'_, T> { + fn drop(&mut self) {} +}