x86_64/structures/paging/
frame.rs

1//! Abstractions for default-sized and huge physical memory frames.
2
3use super::page::AddressNotAligned;
4use crate::structures::paging::page::{PageSize, Size4KiB};
5use crate::PhysAddr;
6use core::fmt;
7use core::marker::PhantomData;
8use core::ops::{Add, AddAssign, Sub, SubAssign};
9
10/// A physical memory frame.
11#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(C)]
13pub struct PhysFrame<S: PageSize = Size4KiB> {
14    // TODO: Make private when our minimum supported stable Rust version is 1.61
15    pub(crate) start_address: PhysAddr,
16    size: PhantomData<S>,
17}
18
19impl<S: PageSize> PhysFrame<S> {
20    /// Returns the frame that starts at the given virtual address.
21    ///
22    /// Returns an error if the address is not correctly aligned (i.e. is not a valid frame start).
23    #[inline]
24    #[rustversion::attr(since(1.61), const)]
25    pub fn from_start_address(address: PhysAddr) -> Result<Self, AddressNotAligned> {
26        if !address.is_aligned_u64(S::SIZE) {
27            return Err(AddressNotAligned);
28        }
29
30        // SAFETY: correct address alignment is checked above
31        Ok(unsafe { PhysFrame::from_start_address_unchecked(address) })
32    }
33
34    /// Returns the frame that starts at the given virtual address.
35    ///
36    /// ## Safety
37    ///
38    /// The address must be correctly aligned.
39    #[inline]
40    #[rustversion::attr(since(1.61), const)]
41    pub unsafe fn from_start_address_unchecked(start_address: PhysAddr) -> Self {
42        PhysFrame {
43            start_address,
44            size: PhantomData,
45        }
46    }
47
48    /// Returns the frame that contains the given physical address.
49    #[inline]
50    #[rustversion::attr(since(1.61), const)]
51    pub fn containing_address(address: PhysAddr) -> Self {
52        PhysFrame {
53            start_address: address.align_down_u64(S::SIZE),
54            size: PhantomData,
55        }
56    }
57
58    /// Returns the start address of the frame.
59    #[inline]
60    #[rustversion::attr(since(1.61), const)]
61    pub fn start_address(self) -> PhysAddr {
62        self.start_address
63    }
64
65    /// Returns the size the frame (4KB, 2MB or 1GB).
66    #[inline]
67    #[rustversion::attr(since(1.61), const)]
68    pub fn size(self) -> u64 {
69        S::SIZE
70    }
71
72    /// Returns a range of frames, exclusive `end`.
73    #[inline]
74    #[rustversion::attr(since(1.61), const)]
75    pub fn range(start: PhysFrame<S>, end: PhysFrame<S>) -> PhysFrameRange<S> {
76        PhysFrameRange { start, end }
77    }
78
79    /// Returns a range of frames, inclusive `end`.
80    #[inline]
81    #[rustversion::attr(since(1.61), const)]
82    pub fn range_inclusive(start: PhysFrame<S>, end: PhysFrame<S>) -> PhysFrameRangeInclusive<S> {
83        PhysFrameRangeInclusive { start, end }
84    }
85}
86
87impl<S: PageSize> fmt::Debug for PhysFrame<S> {
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        f.write_fmt(format_args!(
90            "PhysFrame[{}]({:#x})",
91            S::DEBUG_STR,
92            self.start_address().as_u64()
93        ))
94    }
95}
96
97impl<S: PageSize> Add<u64> for PhysFrame<S> {
98    type Output = Self;
99    #[inline]
100    fn add(self, rhs: u64) -> Self::Output {
101        PhysFrame::containing_address(self.start_address() + rhs * S::SIZE)
102    }
103}
104
105impl<S: PageSize> AddAssign<u64> for PhysFrame<S> {
106    #[inline]
107    fn add_assign(&mut self, rhs: u64) {
108        *self = *self + rhs;
109    }
110}
111
112impl<S: PageSize> Sub<u64> for PhysFrame<S> {
113    type Output = Self;
114    #[inline]
115    fn sub(self, rhs: u64) -> Self::Output {
116        PhysFrame::containing_address(self.start_address() - rhs * S::SIZE)
117    }
118}
119
120impl<S: PageSize> SubAssign<u64> for PhysFrame<S> {
121    #[inline]
122    fn sub_assign(&mut self, rhs: u64) {
123        *self = *self - rhs;
124    }
125}
126
127impl<S: PageSize> Sub<PhysFrame<S>> for PhysFrame<S> {
128    type Output = u64;
129    #[inline]
130    fn sub(self, rhs: PhysFrame<S>) -> Self::Output {
131        (self.start_address - rhs.start_address) / S::SIZE
132    }
133}
134
135/// An range of physical memory frames, exclusive the upper bound.
136#[derive(Clone, Copy, PartialEq, Eq, Hash)]
137#[repr(C)]
138pub struct PhysFrameRange<S: PageSize = Size4KiB> {
139    /// The start of the range, inclusive.
140    pub start: PhysFrame<S>,
141    /// The end of the range, exclusive.
142    pub end: PhysFrame<S>,
143}
144
145impl<S: PageSize> PhysFrameRange<S> {
146    /// Returns whether the range contains no frames.
147    #[inline]
148    pub fn is_empty(&self) -> bool {
149        self.start >= self.end
150    }
151
152    /// Returns the number of frames in the range.
153    #[inline]
154    pub fn len(&self) -> u64 {
155        if !self.is_empty() {
156            self.end - self.start
157        } else {
158            0
159        }
160    }
161
162    /// Returns the size in bytes of all frames within the range.
163    #[inline]
164    pub fn size(&self) -> u64 {
165        S::SIZE * self.len()
166    }
167}
168
169impl<S: PageSize> Iterator for PhysFrameRange<S> {
170    type Item = PhysFrame<S>;
171
172    #[inline]
173    fn next(&mut self) -> Option<Self::Item> {
174        if self.start < self.end {
175            let frame = self.start;
176            self.start += 1;
177            Some(frame)
178        } else {
179            None
180        }
181    }
182}
183
184impl<S: PageSize> fmt::Debug for PhysFrameRange<S> {
185    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        f.debug_struct("PhysFrameRange")
187            .field("start", &self.start)
188            .field("end", &self.end)
189            .finish()
190    }
191}
192
193/// An range of physical memory frames, inclusive the upper bound.
194#[derive(Clone, Copy, PartialEq, Eq, Hash)]
195#[repr(C)]
196pub struct PhysFrameRangeInclusive<S: PageSize = Size4KiB> {
197    /// The start of the range, inclusive.
198    pub start: PhysFrame<S>,
199    /// The start of the range, inclusive.
200    pub end: PhysFrame<S>,
201}
202
203impl<S: PageSize> PhysFrameRangeInclusive<S> {
204    /// Returns whether the range contains no frames.
205    #[inline]
206    pub fn is_empty(&self) -> bool {
207        self.start > self.end
208    }
209
210    /// Returns the number of frames in the range.
211    #[inline]
212    pub fn len(&self) -> u64 {
213        if !self.is_empty() {
214            self.end - self.start + 1
215        } else {
216            0
217        }
218    }
219
220    /// Returns the size in bytes of all frames within the range.
221    #[inline]
222    pub fn size(&self) -> u64 {
223        S::SIZE * self.len()
224    }
225}
226
227impl<S: PageSize> Iterator for PhysFrameRangeInclusive<S> {
228    type Item = PhysFrame<S>;
229
230    #[inline]
231    fn next(&mut self) -> Option<Self::Item> {
232        if self.start <= self.end {
233            let frame = self.start;
234            self.start += 1;
235            Some(frame)
236        } else {
237            None
238        }
239    }
240}
241
242impl<S: PageSize> fmt::Debug for PhysFrameRangeInclusive<S> {
243    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244        f.debug_struct("PhysFrameRangeInclusive")
245            .field("start", &self.start)
246            .field("end", &self.end)
247            .finish()
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254    #[test]
255    pub fn test_frame_range_len() {
256        let start_addr = PhysAddr::new(0xdead_beaf);
257        let start = PhysFrame::<Size4KiB>::containing_address(start_addr);
258        let end = start + 50;
259
260        let range = PhysFrameRange { start, end };
261        assert_eq!(range.len(), 50);
262
263        let range_inclusive = PhysFrameRangeInclusive { start, end };
264        assert_eq!(range_inclusive.len(), 51);
265    }
266}