Skip to main content

kernel_api/memory/
mod.rs

1//! Provides primitives for interfacing with memory
2//! 
3//! TODO: memory map overview?
4//! 
5//! # Page map region
6//! 
7//! All conventional memory (i.e. not MMIO, ACPI firmware data, etc.) is mapped into the "page map
8//! region", meaning that the kernel can directly access it without having to create a
9//! [`Mapping`](crate::mapping::Mapping) first. This is useful for writing to userspace in a different
10//! address space, or for storing allocator metadata.
11//! 
12//! Physical memory owned through a [`Frames<true>`] can be directly accessed via [`Frames::get()`] and
13//! [`Frames::get_mut()`]. Raw [`PhysicalAddress`]es can be converted to a [`VirtualAddress`] in the page
14//! map region by calling [`PhysicalAddress::to_virtual`]. **The returned address is only safe to access
15//! if the [`PhysicalAddress`] pointed to conventional memory.**
16
17use core::fmt::{Debug, Formatter};
18use core::ops::Deref;
19use core::iter::Step;
20use core::fmt;
21#[cfg(feature = "full")] use core::slice;
22#[cfg(feature = "full")] use core::ops::Range;
23#[cfg(feature = "full")] use core::marker::PhantomData;
24#[cfg(feature = "full")] use core::mem::{ManuallyDrop, MaybeUninit};
25#[cfg(feature = "full")] use core::ptr::addr_of;
26#[cfg(feature = "full")] use crate::allocator::{DynPmm, Pmm};
27
28mod type_ops;
29
30pub mod asan;
31
32/// The number of bytes in the smallest sized page for the current architecture
33pub const PAGE_SIZE: usize = const {
34    if cfg!(doc) { 0 }
35    else if cfg!(target_arch = "x86_64") { 4096 }
36    else { panic!("unsupported arch") }
37};
38
39const PAGE_MAP_OFFSET: usize = 0xffff_8000_0000_0000;
40
41#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
42#[must_use = "must be explicitly deallocated to not leak memory"]
43pub struct RawPage {
44    inner: VirtualAddress,
45}
46
47impl RawPage {
48    #[track_caller]
49    pub fn new(addr: usize) -> Self {
50        if addr % PAGE_SIZE != 0 { panic!("unaligned `RawPage`") };
51        RawPage { inner: VirtualAddress::new(addr) }
52    }
53}
54
55impl const Deref for RawPage {
56    type Target = VirtualAddress;
57
58    fn deref(&self) -> &Self::Target {
59        &self.inner
60    }
61}
62
63#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
64#[must_use = "must be explicitly deallocated to not leak memory"]
65pub struct RawFrame {
66    inner: PhysicalAddress,
67}
68
69impl RawFrame {
70    #[track_caller]
71    pub const fn new(addr: usize) -> Self {
72        if addr % PAGE_SIZE != 0 { panic!("unaligned `RawFrame`") };
73        RawFrame { inner: PhysicalAddress::new(addr) }
74    }
75    
76    pub const fn checked_sub(self, count: usize) -> Option<Self> {
77        self.addr.checked_sub(count * PAGE_SIZE)
78                .map(RawFrame::new)
79    }
80}
81
82impl const Deref for RawFrame {
83    type Target = PhysicalAddress;
84
85    fn deref(&self) -> &Self::Target {
86        &self.inner
87    }
88}
89
90/// An owned region of physical memory
91#[cfg(feature = "full")]
92#[repr(C)]
93pub struct Frames<const RAM: bool, T = MaybeUninit<u8>> {
94    raw: Range<RawFrame>,
95	pmm: DynPmm<'static, RAM>,
96    _phantom: PhantomData<[T]>,
97}
98
99#[cfg(feature = "full")]
100impl<const RAM: bool, T> Debug for Frames<RAM, T> {
101    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
102        f.debug_struct("Frames")
103                .field("raw", &self.raw)
104                .field("pmm", &self.pmm)
105                .finish()
106    }
107}
108
109#[cfg(feature = "full")]
110impl<const RAM: bool, T> Frames<RAM, T> {
111	pub fn pmm(&self) -> &DynPmm<'static, RAM> { &self.pmm }
112
113    pub fn count(&self) -> usize {
114        self.raw.end - self.raw.start
115    }
116    
117    pub fn base(&self) -> RawFrame {
118        self.raw.start
119    }
120
121	pub fn as_frame_range(&self) -> Range<RawFrame> {
122		self.raw.clone()
123	}
124
125    pub unsafe fn base_raw(self: *const Self) -> RawFrame {
126        unsafe { *addr_of!((*self).raw.start) }
127    }
128
129    pub(crate) fn into_raw(self) -> (Range<RawFrame>, DynPmm<'static, RAM>) {
130        let this = ManuallyDrop::new(self);
131	    (this.raw.clone(), this.pmm)
132    }
133
134    pub unsafe fn from_raw(raw: Range<RawFrame>, pmm: DynPmm<'static, RAM>) -> Self {
135        Self {
136            raw,
137	        pmm,
138            _phantom: PhantomData
139        }
140    }
141}
142
143#[cfg(feature = "full")]
144impl<T> Frames<false, T> {
145	pub(crate) unsafe fn from_raw_tuple<const RAM: bool>((raw, pmm): (Range<RawFrame>, DynPmm<'static, RAM>)) -> Self {
146		Self {
147			raw,
148			pmm: pmm.into(),
149			_phantom: PhantomData
150		}
151	}
152}
153
154#[cfg(feature = "full")]
155impl<const RAM: bool, T> Frames<RAM, MaybeUninit<T>> {
156    pub fn cast<U>(self) -> Frames<RAM, MaybeUninit<U>> {
157	    let (raw, pmm) = self.into_raw();
158	    Frames {
159		    raw,
160		    pmm,
161		    _phantom: PhantomData
162	    }
163    }
164}
165
166#[cfg(feature = "full")]
167impl<T> Frames<true, MaybeUninit<T>> {
168    pub fn write_filled(&mut self, value: T) -> &mut [T] where T: Clone {
169        self.get_mut().write_filled(value)
170    }
171    
172    pub fn write_filled_with(&mut self, f: impl FnMut(usize) -> T) -> &mut [T] {
173        self.get_mut().write_with(f)
174    }
175
176    pub fn into_filed(mut self, value: T) -> Frames<true, T> where T: Clone {
177        self.write_filled(value);
178	    let (raw, pmm) = self.into_raw();
179	    Frames {
180		    raw,
181		    pmm,
182		    _phantom: PhantomData
183	    }
184    }
185
186    pub fn into_filed_with(mut self, f: impl FnMut(usize) -> T) -> Frames<true, T> {
187        self.write_filled_with(f);
188	    let (raw, pmm) = self.into_raw();
189	    Frames {
190		    raw,
191		    pmm,
192		    _phantom: PhantomData
193	    }
194    }
195}
196
197#[cfg(feature = "full")]
198impl<T> Frames<true, T> {
199    pub fn get(&self) -> &[T] {
200        assert!(align_of::<T>() <= 4096);
201        let base = self.raw.start.to_virtual().as_ptr();
202        unsafe {
203            slice::from_raw_parts(base.cast_const().cast(), self.count() * PAGE_SIZE / size_of::<T>())
204        }
205    }
206
207    pub fn get_mut(&mut self) -> &mut [T] {
208        assert!(align_of::<T>() <= 4096);
209        let base = self.raw.start.to_virtual().as_ptr();
210        unsafe {
211            slice::from_raw_parts_mut(base.cast(), self.count() * PAGE_SIZE / size_of::<T>())
212        }
213    }
214}
215
216#[cfg(feature = "full")]
217impl<const RAM: bool, T> Drop for Frames<RAM, T> {
218    fn drop(&mut self) {
219        unsafe {
220	        self.pmm.deallocate_raw(self.raw.start, self.raw.clone().count().try_into().unwrap());
221        }
222    }
223}
224
225/// A physical memory address
226#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
227#[repr(transparent)]
228pub struct PhysicalAddress {
229    /// The underlying address
230    pub addr: usize
231}
232
233/// A virtual memory address
234#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
235#[repr(transparent)]
236pub struct VirtualAddress {
237    /// The underlying address
238    pub addr: usize
239}
240
241impl PhysicalAddress {
242    /// Creates a new [`PhysicalAddress`]
243    #[track_caller]
244    pub const fn new(addr: usize) -> Self {
245        Self { addr }
246    }
247
248    /// Converts an [`PhysicalAddress`] into an [`VirtualAddress`] via the physical page map region
249    ///
250    /// The returned [`VirtualAddress`] is only safe to access if the [`PhysicalAddress`] points into conventional
251    /// RAM
252    pub const fn to_virtual(self) -> VirtualAddress {
253        VirtualAddress::new(self.addr + PAGE_MAP_OFFSET)
254    }
255
256    /// Returns the closest [`RawFrame`] at or below the current address
257    pub const fn align_down_to_frame(self) -> RawFrame {
258        let aligned = PhysicalAddress {
259            addr: self.addr & !(PAGE_SIZE - 1)
260        };
261        RawFrame { inner: aligned }
262    }
263
264    /// Returns the closest [`RawFrame`] at or above the current address
265    pub const fn align_up_to_frame(self) -> RawFrame {
266        let a: PhysicalAddress = self + PAGE_SIZE - 1usize;
267        a.align_down_to_frame()
268    }
269
270    /// Returns `true` if the [`PhysicalAddress`] is aligned to `align`
271    ///
272    /// # Panics
273    /// 
274    /// If `align` is not a power of two
275    #[cfg_attr(debug_assertions, track_caller)]
276    pub const fn aligned_to(self, align: usize) -> bool {
277        #[cfg(debug_assertions)] if !align.is_power_of_two() { panic!("alignment must be power of 2") }
278        self.addr & (align - 1) == 0
279    }
280}
281
282impl VirtualAddress {
283    /// Returns `true` if the [`VirtualAddress`] is in the upper half of the address space, i.e. kernelspace
284    pub const fn is_higher_half(self) -> bool {
285        (self.addr as isize) < 0
286    }
287
288    /// Creates a new [`VirtualAddress`]
289    #[track_caller]
290    pub const fn new(addr: usize) -> Self {
291        Self { addr }
292    }
293
294    /// Converts a [`VirtualAddress`] into a raw pointer
295    #[inline]
296    pub const fn as_ptr(self) -> *mut u8 {
297        self.addr as _
298    }
299
300    /// Returns the closest [`RawPage`] at or below the current address
301    pub const fn align_down_to_page(self) -> RawPage {
302        let aligned = VirtualAddress {
303            addr: self.addr & !(PAGE_SIZE - 1)
304        };
305        RawPage { inner: aligned }
306    }
307
308    /// Returns the closest [`RawPage`] at or above the current address
309    pub const fn align_up_to_page(self) -> RawPage {
310        let a: VirtualAddress = self + PAGE_SIZE - 1usize;
311        a.align_down_to_page()
312    }
313
314    /// Returns `true` if the [`PhysicalAddress`] is aligned to `align`
315    ///
316    /// # Panics
317    ///
318    /// If `align` is not a power of two
319    #[cfg_attr(debug_assertions, track_caller)]
320    pub const fn aligned_to(self, align: usize) -> bool {
321        #[cfg(debug_assertions)] if !align.is_power_of_two() { panic!("alignment must be power of 2") }
322        self.addr & (align - 1) == 0
323    }
324
325    /// Computed `self + rhs` saturating when the [`VirtualAddress`] reaches [`usize::MAX`]
326    pub const fn saturating_add(self, rhs: usize) -> Self {
327        VirtualAddress::new(self.addr.saturating_add(rhs))
328    }
329
330    /// Computed `self - rhs` saturating when the [`VirtualAddress`] reaches 0
331    pub const fn saturating_sub(self, rhs: usize) -> Self {
332        VirtualAddress::new(self.addr.saturating_sub(rhs))
333    }
334
335	/// Converts an [`VirtualAddress`] in the physical page map region into an [`PhysicalAddress`]
336	///
337	/// # Panics
338	/// 
339	/// Panics if the address is not in the physical page map region on a best effort basis
340	pub const fn to_physical(self) -> PhysicalAddress {
341		PhysicalAddress::new(self.addr - PAGE_MAP_OFFSET)
342	}
343}
344
345impl<T: ?Sized> From<*mut T> for VirtualAddress {
346    fn from(value: *mut T) -> Self {
347        VirtualAddress { addr: value as *mut u8 as usize }
348    }
349}
350
351impl<T: ?Sized> From<*const T> for VirtualAddress {
352    fn from(value: *const T) -> Self {
353        VirtualAddress { addr: value as *const u8 as usize }
354    }
355}
356
357impl const From<RawFrame> for PhysicalAddress {
358    fn from(value: RawFrame) -> Self {
359        value.inner
360    }
361}
362
363impl const From<RawPage> for VirtualAddress {
364    fn from(value: RawPage) -> Self {
365        value.inner
366    }
367}
368
369impl Step for PhysicalAddress {
370    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
371        Step::steps_between(&start.addr, &end.addr)
372    }
373
374    fn forward_checked(start: Self, count: usize) -> Option<Self> {
375        Some(PhysicalAddress::new(
376            Step::forward_checked(start.addr, count)?
377        ))
378    }
379
380    fn backward_checked(start: Self, count: usize) -> Option<Self> {
381        Some(PhysicalAddress::new(
382            Step::backward_checked(start.addr, count)?
383        ))
384    }
385}
386
387impl Step for VirtualAddress {
388    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
389        Step::steps_between(&start.addr, &end.addr)
390    }
391
392    fn forward_checked(start: Self, count: usize) -> Option<Self> {
393        Some(VirtualAddress::new(
394            Step::forward_checked(start.addr, count)?
395        ))
396    }
397
398    fn backward_checked(start: Self, count: usize) -> Option<Self> {
399        Some(VirtualAddress::new(
400            Step::backward_checked(start.addr, count)?
401        ))
402    }
403}
404
405impl Step for RawFrame {
406    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
407        Step::steps_between(&(start.addr / PAGE_SIZE), &(end.addr / PAGE_SIZE))
408    }
409
410    fn forward_checked(start: Self, count: usize) -> Option<Self> {
411        Some(RawFrame::new(
412            Step::forward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
413        ))
414    }
415
416    fn backward_checked(start: Self, count: usize) -> Option<Self> {
417        Some(RawFrame::new(
418            Step::backward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
419        ))
420    }
421}
422
423impl Step for RawPage {
424    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
425        Step::steps_between(&start.addr, &end.addr)
426    }
427
428    fn forward_checked(start: Self, count: usize) -> Option<Self> {
429        Some(RawPage::new(
430            Step::forward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
431        ))
432    }
433
434    fn backward_checked(start: Self, count: usize) -> Option<Self> {
435        Some(RawPage::new(
436            Step::backward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
437        ))
438    }
439}
440
441impl fmt::LowerHex for VirtualAddress {
442    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
443        fmt::LowerHex::fmt(&self.addr, f)
444    }
445}
446
447impl fmt::LowerHex for PhysicalAddress {
448    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
449        fmt::LowerHex::fmt(&self.addr, f)
450    }
451}
452
453impl fmt::LowerHex for RawPage {
454    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
455        fmt::LowerHex::fmt(&self.addr, f)
456    }
457}
458
459impl fmt::LowerHex for RawFrame {
460    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
461        fmt::LowerHex::fmt(&self.addr, f)
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use super::*;
468
469    #[test]
470    fn align_down() {
471        let unaligned: VirtualAddress = VirtualAddress { addr: 0x1567 };
472        let aligned = unaligned.align_down::<4096>();
473        assert_eq!(aligned.addr, 0x1000);
474
475        let unaligned: VirtualAddress = VirtualAddress { addr: 0x2000 };
476        let aligned = unaligned.align_down::<4096>();
477        assert_eq!(aligned.addr, 0x2000);
478
479        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x1567 };
480        let aligned = unaligned.align_down::<4096>();
481        assert_eq!(aligned.addr, 0x1000);
482
483        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x2000 };
484        let aligned = unaligned.align_down::<4096>();
485        assert_eq!(aligned.addr, 0x2000);
486    }
487
488    #[test]
489    fn align_up() {
490        let unaligned: VirtualAddress = VirtualAddress { addr: 0x1567 };
491        let aligned = unaligned.align_up::<4096>();
492        assert_eq!(aligned.addr, 0x2000);
493
494        let unaligned: VirtualAddress = VirtualAddress { addr: 0x2000 };
495        let aligned = unaligned.align_up::<4096>();
496        assert_eq!(aligned.addr, 0x2000);
497
498        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x1567 };
499        let aligned = unaligned.align_up::<4096>();
500        assert_eq!(aligned.addr, 0x2000);
501
502        let unaligned: PhysicalAddress = PhysicalAddress { addr: 0x2000 };
503        let aligned = unaligned.align_up::<4096>();
504        assert_eq!(aligned.addr, 0x2000);
505    }
506}