region/
lock.rs

1use crate::{os, util, Result};
2
3/// Locks one or more memory regions to RAM.
4///
5/// The memory pages within the address range is guaranteed to stay in RAM
6/// except for specials cases, such as hibernation and memory starvation. It
7/// returns a [`LockGuard`], which [`unlock`]s the affected regions once
8/// dropped.
9///
10/// # Parameters
11///
12/// - The range is `[address, address + size)`
13/// - The address is rounded down to the closest page boundary.
14/// - The size may not be zero.
15/// - The size is rounded up to the closest page boundary, relative to the
16///   address.
17///
18/// # Errors
19///
20/// - If an interaction with the underlying operating system fails, an error
21/// will be returned.
22/// - If size is zero,
23/// [`Error::InvalidParameter`](crate::Error::InvalidParameter) will be
24/// returned.
25///
26/// # Examples
27///
28/// ```
29/// # fn main() -> region::Result<()> {
30/// let data = [0; 100];
31/// let _guard = region::lock(data.as_ptr(), data.len())?;
32/// # Ok(())
33/// # }
34/// ```
35#[inline]
36pub fn lock<T>(address: *const T, size: usize) -> Result<LockGuard> {
37  let (address, size) = util::round_to_page_boundaries(address, size)?;
38  os::lock(address.cast(), size).map(|_| LockGuard::new(address, size))
39}
40
41/// Unlocks one or more memory regions from RAM.
42///
43/// If possible, prefer to use [`lock`] combined with the [`LockGuard`].
44///
45/// # Parameters
46///
47/// - The range is `[address, address + size)`
48/// - The address is rounded down to the closest page boundary.
49/// - The size may not be zero.
50/// - The size is rounded up to the closest page boundary, relative to the
51///   address.
52///
53/// # Errors
54///
55/// - If an interaction with the underlying operating system fails, an error
56/// will be returned.
57/// - If size is zero,
58/// [`Error::InvalidParameter`](crate::Error::InvalidParameter) will be
59/// returned.
60#[inline]
61pub fn unlock<T>(address: *const T, size: usize) -> Result<()> {
62  let (address, size) = util::round_to_page_boundaries(address, size)?;
63  os::unlock(address.cast(), size)
64}
65
66/// A RAII implementation of a scoped lock.
67///
68/// When this structure is dropped (falls out of scope), the virtual lock will be
69/// released.
70#[must_use]
71pub struct LockGuard {
72  address: *const (),
73  size: usize,
74}
75
76impl LockGuard {
77  #[inline(always)]
78  fn new<T>(address: *const T, size: usize) -> Self {
79    Self {
80      address: address.cast(),
81      size,
82    }
83  }
84}
85
86impl Drop for LockGuard {
87  #[inline]
88  fn drop(&mut self) {
89    let result = os::unlock(self.address, self.size);
90    debug_assert!(result.is_ok(), "unlocking region: {:?}", result);
91  }
92}
93
94unsafe impl Send for LockGuard {}
95unsafe impl Sync for LockGuard {}
96
97#[cfg(test)]
98mod tests {
99  use super::*;
100  use crate::tests::util::alloc_pages;
101  use crate::{page, Protection};
102
103  #[test]
104  fn lock_mapped_pages_succeeds() -> Result<()> {
105    let map = alloc_pages(&[Protection::READ_WRITE]);
106    let _guard = lock(map.as_ptr(), page::size())?;
107    Ok(())
108  }
109
110  #[test]
111  fn unlock_mapped_pages_succeeds() -> Result<()> {
112    let map = alloc_pages(&[Protection::READ_WRITE]);
113    std::mem::forget(lock(map.as_ptr(), page::size())?);
114    unlock(map.as_ptr(), page::size())
115  }
116}