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}