kernel_api/memory/
mod.rs

1//! Provides primitives for interfacing with raw memory (such as [pages](`Page`) and [frames](`Frame`)), as well as
2//! interfaces for memory related kernel modules to implement (such as [`BackingAllocator`](allocator::PhysicalAllocator))
3#![stable(feature = "kernel_core_api", since = "1.0.0")]
4
5#[cfg(feature = "full")]
6pub mod allocator;
7#[cfg(feature = "full")]
8pub mod heap;
9mod type_ops;
10#[cfg(feature = "full")]
11pub mod r#virtual;
12#[cfg(all(not(feature = "use_std"), feature = "full"))]
13pub mod mapping;
14#[cfg(feature = "full")]
15pub mod physical;
16
17/// The error returned when an allocation was unsuccessful
18#[derive(Debug, Copy, Clone, Eq, PartialEq)]
19#[stable(feature = "kernel_core_api", since = "1.0.0")]
20pub struct AllocError;
21
22const PAGE_SIZE: usize = 4096;
23const PAGE_MAP_OFFSET: usize = 0xffff_8000_0000_0000;
24
25/// A memory frame
26#[stable(feature = "kernel_core_api", since = "1.0.0")]
27#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
28#[repr(transparent)]
29pub struct Frame {
30    base: PhysicalAddress<PAGE_SIZE>
31}
32
33/// A memory page
34#[stable(feature = "kernel_core_api", since = "1.0.0")]
35#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
36#[repr(transparent)]
37pub struct Page {
38    base: VirtualAddress<PAGE_SIZE>
39}
40
41/// A physical memory address of alignment `ALIGN`
42// todo: replace ALIGN with a NonZero<usize>
43#[stable(feature = "kernel_core_api", since = "1.0.0")]
44#[derive(Debug, Copy, Clone, Eq, Ord)]
45#[repr(transparent)]
46pub struct PhysicalAddress<const ALIGN: usize = 1> {
47    #[unstable(feature = "kernel_memory_addr_access", issue = "none")]
48    pub addr: usize
49}
50
51/// A virtual memory address of alignment `ALIGN`
52#[stable(feature = "kernel_core_api", since = "1.0.0")]
53#[derive(Debug, Copy, Clone, Eq, Ord)]
54#[repr(transparent)]
55pub struct VirtualAddress<const ALIGN: usize = 1> {
56    #[unstable(feature = "kernel_memory_addr_access", issue = "none")]
57    pub addr: usize
58}
59
60impl<const ALIGN: usize> PhysicalAddress<ALIGN> {
61    /// Creates a new [`PhysicalAddress`], panicking if the alignment is incorrect
62    #[stable(feature = "kernel_core_api", since = "1.0.0")]
63    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
64    #[track_caller]
65    pub const fn new(addr: usize) -> Self {
66        let unaligned: PhysicalAddress = PhysicalAddress { addr };
67        let aligned = unaligned.align_down();
68
69        if aligned.addr != unaligned.addr { panic!("Address not aligned"); }
70
71        aligned
72    }
73
74    /// Converts a [`PhysicalAddress`] into a [`VirtualAddress`] via the physical page map region
75    #[unstable(feature = "kernel_physical_page_offset", issue = "1")]
76    pub const fn to_virtual(self) -> VirtualAddress<ALIGN> {
77        VirtualAddress {
78            addr: self.addr + PAGE_MAP_OFFSET
79        }
80    }
81
82    /// Forces a [`PhysicalAddress`] to have a specific alignment
83    /// # Safety
84    /// The address must be already aligned to the new alignment
85    #[stable(feature = "kernel_core_api", since = "1.0.0")]
86    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
87    pub const unsafe fn align_unchecked<const NEW_ALIGN: usize>(self) -> PhysicalAddress<NEW_ALIGN> {
88        PhysicalAddress {
89            .. self
90        }
91    }
92
93    /// Returns the [`PhysicalAddress`] less than or equal to `self` with the given alignment
94    #[stable(feature = "kernel_core_api", since = "1.0.0")]
95    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
96    pub const fn align_down<const NEW_ALIGN: usize>(self) -> PhysicalAddress<NEW_ALIGN> {
97        PhysicalAddress {
98            addr: self.addr & !(NEW_ALIGN - 1)
99        }
100    }
101
102    /// Returns the [`PhysicalAddress`] greater than or equal to `self` with the given alignment
103    #[stable(feature = "kernel_core_api", since = "1.0.0")]
104    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
105    pub const fn align_up<const NEW_ALIGN: usize>(self) -> PhysicalAddress<NEW_ALIGN> {
106        // FIXME(const): use normal add implementation
107        let a: PhysicalAddress = PhysicalAddress {
108            addr: self.addr + NEW_ALIGN - 1
109        };
110        a.align_down()
111    }
112
113    /// Returns the [`PhysicalAddress`] less than or equal to `self` with the given runtime alignment
114    #[unstable(feature = "kernel_address_alignment_runtime", issue = "none")]
115    pub const fn align_down_runtime(self, new_alignment: usize) -> PhysicalAddress<1> {
116        PhysicalAddress {
117            addr: self.addr & !(new_alignment - 1)
118        }
119    }
120
121    /// Returns the [`PhysicalAddress`] greater than or equal to `self` with the given runtime alignment
122    #[unstable(feature = "kernel_address_alignment_runtime", issue = "none")]
123    pub const fn align_up_runtime(self, new_alignment: usize) -> PhysicalAddress<1> {
124        let a: PhysicalAddress = PhysicalAddress {
125            addr: self.addr + new_alignment - 1
126        };
127        a.align_down_runtime(new_alignment)
128    }
129}
130
131impl Frame {
132    /// Converts a [`Frame`] into a [`Page`] via the physical page map region
133    #[unstable(feature = "kernel_physical_page_offset", issue = "1")]
134    pub const fn to_page(&self) -> Page {
135        Page {
136            base: self.base.to_virtual()
137        }
138    }
139
140    /// Returns the zero frame
141    #[unstable(feature = "kernel_frame_zero", issue = "none")]
142    pub const fn zero() -> Frame {
143        Frame::new(PhysicalAddress::new(0))
144    }
145
146    /// Creates a [`Frame`] using `base` as the first address within it
147    #[stable(feature = "kernel_core_api", since = "1.0.0")]
148    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
149    pub const fn new(base: PhysicalAddress<PAGE_SIZE>) -> Self {
150        Self { base }
151    }
152
153    /// Attempts to subtract `rhs` number of pages, returning `None` if overflow would occur
154    #[stable(feature = "kernel_core_api", since = "1.0.0")]
155    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
156    pub const fn checked_sub(&self, rhs: usize) -> Option<Self> {
157        // FIXME(const): Option::map
158        match self.base.addr.checked_sub(rhs * PAGE_SIZE) {
159            Some(addr) => Some(Self {
160                base: PhysicalAddress::new(addr)
161            }),
162            None => None
163        }
164    }
165
166    /// Returns the first address within the [`Frame`]
167    #[stable(feature = "kernel_core_api", since = "1.0.0")]
168    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
169    pub const fn start(&self) -> PhysicalAddress<PAGE_SIZE> {
170        self.base
171    }
172
173    /// Returns the address one after the end of the [`Frame`]
174    #[stable(feature = "kernel_core_api", since = "1.0.0")]
175    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
176    pub const fn end(&self) -> PhysicalAddress<PAGE_SIZE> {
177        // FIXME(const): use normal add implementation
178        PhysicalAddress::<4096>::new(self.base.addr + PAGE_SIZE)
179    }
180}
181
182impl<const ALIGN: usize> VirtualAddress<ALIGN> {
183    /// Creates a new [`VirtualAddress`], panicking if the alignment is incorrect
184    #[stable(feature = "kernel_core_api", since = "1.0.0")]
185    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
186    #[track_caller]
187    pub const fn new(addr: usize) -> Self {
188        let unaligned: VirtualAddress = VirtualAddress { addr };
189        let aligned = unaligned.align_down();
190
191        if aligned.addr != unaligned.addr { panic!("Address not aligned"); }
192
193        aligned
194    }
195
196    /// Converts a [`VirtualAddress`] into a raw pointer
197    #[stable(feature = "kernel_core_api", since = "1.0.0")]
198    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
199    pub const fn as_ptr(self) -> *mut u8 {
200        self.addr as _
201    }
202
203    /// Forces a [`VirtualAddress`] to have a specific alignment
204    /// # Safety
205    /// The address must be already aligned to the new alignment
206    #[stable(feature = "kernel_core_api", since = "1.0.0")]
207    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
208    pub const unsafe fn align_unchecked<const NEW_ALIGN: usize>(self) -> VirtualAddress<NEW_ALIGN> {
209        VirtualAddress {
210            .. self
211        }
212    }
213
214    /// Returns the [`VirtualAddress`] less than or equal to `self` with the given alignment
215    #[stable(feature = "kernel_core_api", since = "1.0.0")]
216    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
217    pub const fn align_down<const NEW_ALIGN: usize>(self) -> VirtualAddress<NEW_ALIGN> {
218        VirtualAddress {
219            addr: self.addr & !(NEW_ALIGN - 1)
220        }
221    }
222
223    /// Returns the [`VirtualAddress`] greater than or equal to `self` with the given alignment
224    #[stable(feature = "kernel_core_api", since = "1.0.0")]
225    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
226    pub const fn align_up<const NEW_ALIGN: usize>(self) -> VirtualAddress<NEW_ALIGN> {
227        // FIXME: const ops
228        let a: VirtualAddress = VirtualAddress {
229            addr: self.addr + NEW_ALIGN - 1
230        };
231        a.align_down()
232    }
233
234    /// Returns the [`VirtualAddress`] less than or equal to `self` with the given runtime alignment
235    #[unstable(feature = "kernel_address_alignment_runtime", issue = "none")]
236    pub const fn align_down_runtime(self, new_alignment: usize) -> VirtualAddress<1> {
237        VirtualAddress {
238            addr: self.addr & !(new_alignment - 1)
239        }
240    }
241
242    /// Returns the [`VirtualAddress`] greater than or equal to `self` with the given runtime alignment
243    #[unstable(feature = "kernel_address_alignment_runtime", issue = "none")]
244    pub const fn align_up_runtime(self, new_alignment: usize) -> VirtualAddress<1> {
245        let a: VirtualAddress = VirtualAddress {
246            addr: self.addr + new_alignment - 1
247        };
248        a.align_down_runtime(new_alignment)
249    }
250
251    #[track_caller]
252    #[unstable(feature = "kernel_address_alignment_runtime", issue = "none")]
253    pub const fn aligned<const N: usize>(self) -> VirtualAddress<N> {
254        VirtualAddress::new(self.addr)
255    }
256}
257
258impl Page {
259    /// Converts a [`Page`] into a raw pointer pointing to the first address within the page
260    #[stable(feature = "kernel_core_api", since = "1.0.0")]
261    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
262    pub const fn as_ptr(&self) -> *mut u8 {
263        self.base.as_ptr()
264    }
265
266    /// Creates a [`Page`] using `base` as the first address within it
267    #[stable(feature = "kernel_core_api", since = "1.0.0")]
268    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
269    pub const fn new(base: VirtualAddress<PAGE_SIZE>) -> Self {
270        Self { base }
271    }
272
273    /// Returns the first address within the [`Page`]
274    #[stable(feature = "kernel_core_api", since = "1.0.0")]
275    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
276    pub const fn start(&self) -> VirtualAddress<PAGE_SIZE> {
277        self.base
278    }
279
280    /// Returns the address one after the end of the [`Page`]
281    #[stable(feature = "kernel_core_api", since = "1.0.0")]
282    #[rustc_const_stable(feature = "kernel_core_api", since = "1.0.0")]
283    pub const fn end(&self) -> VirtualAddress<PAGE_SIZE> {
284        // FIXME(const): use normal add implementation
285        VirtualAddress::<4096>::new(self.base.addr + PAGE_SIZE)
286    }
287}
288
289#[stable(feature = "kernel_core_api", since = "1.0.0")]
290impl<T: ?Sized> From<*mut T> for VirtualAddress<1> {
291    fn from(value: *mut T) -> Self {
292        VirtualAddress { addr: value as *mut u8 as usize }
293    }
294}
295
296#[stable(feature = "kernel_core_api", since = "1.0.0")]
297impl<T: ?Sized> From<*const T> for VirtualAddress<1> {
298    fn from(value: *const T) -> Self {
299        VirtualAddress { addr: value as *const u8 as usize }
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use super::*;
306
307    #[test]
308    fn align_down() {
309        let unaligned: VirtualAddress = VirtualAddress { addr: 0x1567 };
310        let aligned = unaligned.align_down::<4096>();
311        assert_eq!(aligned.addr, 0x1000);
312
313        let unaligned: VirtualAddress = VirtualAddress { addr: 0x2000 };
314        let aligned = unaligned.align_down::<4096>();
315        assert_eq!(aligned.addr, 0x2000);
316
317        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x1567 };
318        let aligned = unaligned.align_down::<4096>();
319        assert_eq!(aligned.addr, 0x1000);
320
321        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x2000 };
322        let aligned = unaligned.align_down::<4096>();
323        assert_eq!(aligned.addr, 0x2000);
324    }
325
326    #[test]
327    fn align_up() {
328        let unaligned: VirtualAddress = VirtualAddress { addr: 0x1567 };
329        let aligned = unaligned.align_up::<4096>();
330        assert_eq!(aligned.addr, 0x2000);
331
332        let unaligned: VirtualAddress = VirtualAddress { addr: 0x2000 };
333        let aligned = unaligned.align_up::<4096>();
334        assert_eq!(aligned.addr, 0x2000);
335
336        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x1567 };
337        let aligned = unaligned.align_up::<4096>();
338        assert_eq!(aligned.addr, 0x2000);
339
340        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x2000 };
341        let aligned = unaligned.align_up::<4096>();
342        assert_eq!(aligned.addr, 0x2000);
343    }
344}