Skip to main content

kernel_api/memory/
mod.rs

1//! Provides primitives for interfacing with memory.
2//! 
3//! TODO(doc): 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//!
17//! # Physical memory
18//!
19//! TODO(doc).
20//!
21//! # Virtual memory
22//!
23//! TODO(doc).
24//!
25//! # Heap memory
26//!
27//! TODO(doc).
28
29use core::cell::UnsafeCell;
30use core::fmt::{Debug, Formatter};
31use core::ops::Deref;
32use core::iter::Step;
33use core::fmt;
34#[cfg(feature = "full")] use core::slice;
35#[cfg(feature = "full")] use core::ops::Range;
36#[cfg(feature = "full")] use core::marker::PhantomData;
37#[cfg(feature = "full")] use core::mem::{ManuallyDrop, MaybeUninit};
38#[cfg(feature = "full")] use crate::allocator::{DynPmm, Pmm};
39
40mod type_ops;
41
42pub mod asan;
43
44/// The number of bytes in the smallest sized page for the current architecture
45pub const PAGE_SIZE: usize = const {
46    if cfg!(doc) { 0 }
47    else if cfg!(target_arch = "x86_64") { 4096 }
48    else { panic!("unsupported arch") }
49};
50
51const PAGE_MAP_OFFSET: usize = 0xffff_8000_0000_0000;
52
53/// A virtual memory page.
54#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
55pub struct RawPage {
56    inner: VirtualAddress,
57}
58
59impl RawPage {
60	/// Creates a new `RawPage` with the given address.
61	///
62    /// # Panics
63    ///
64    /// If `addr` is not aligned to a multiple of [`PAGE_SIZE`].
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use kernel_api::memory::{RawPage, PAGE_SIZE};
70    ///
71    /// let page = RawPage::new(PAGE_SIZE * 5);
72    /// ```
73    ///
74    /// Creating a misaligned page will panic:
75    /// ```should_panic
76    /// use kernel_api::memory::RawPage;
77    ///
78    /// let page = RawPage::new(3);
79    /// ```
80    #[track_caller]
81	#[must_use]
82    pub const fn new(addr: usize) -> Self {
83        assert!(addr.is_multiple_of(PAGE_SIZE), "unaligned `RawPage`");
84
85        Self { inner: VirtualAddress::new(addr) }
86    }
87}
88
89const impl Deref for RawPage {
90    type Target = VirtualAddress;
91
92    fn deref(&self) -> &Self::Target {
93        &self.inner
94    }
95}
96
97/// A physical memory frame.
98#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
99pub struct RawFrame {
100    inner: PhysicalAddress,
101}
102
103impl RawFrame {
104	/// Creates a new `RawFrame` with the given address.
105	///
106	/// # Panics
107    ///
108    /// If `addr` is not aligned to a multiple of [`PAGE_SIZE`].
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use kernel_api::memory::{RawFrame, PAGE_SIZE};
114    ///
115    /// let frame = RawFrame::new(PAGE_SIZE * 5);
116    /// ```
117    ///
118    /// Creating a misaligned frame will panic:
119    /// ```should_panic
120    /// use kernel_api::memory::RawFrame;
121    ///
122    /// let frame = RawFrame::new(3);
123    /// ```
124    #[track_caller]
125	#[must_use]
126    pub const fn new(addr: usize) -> Self {
127        assert!(addr.is_multiple_of(PAGE_SIZE), "unaligned `RawFrame`");
128
129        Self { inner: PhysicalAddress::new(addr) }
130    }
131
132	/// Checked integer subtraction.
133    ///
134    /// Computes the frame `count` frames below `self`, returning
135	/// `None` if overflow occurred.
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// use kernel_api::memory::{RawFrame, PAGE_SIZE};
141    ///
142    /// let frame = RawFrame::new(PAGE_SIZE * 2);
143    /// assert_eq!(frame.checked_sub(1), Some(RawFrame::new(PAGE_SIZE)));
144    /// assert_eq!(frame.checked_sub(5), None);
145    /// ```
146	#[must_use = "this returns the result of the operation, without modifying the original"]
147    pub const fn checked_sub(self, count: usize) -> Option<Self> {
148        self.addr.checked_sub(count * PAGE_SIZE)
149                .map(Self::new)
150    }
151}
152
153const impl Deref for RawFrame {
154    type Target = PhysicalAddress;
155
156    fn deref(&self) -> &Self::Target {
157        &self.inner
158    }
159}
160
161/// An owned region of physical memory.
162///
163/// If the region is allocated from conventional memory, then the `RAM` parameter is set,
164/// and the allocated memory can be directly accessed through [`get`](`Frames::get`) and
165/// [`get_mut`](`Frames::get_mut`) via the [page map region](`self#page-map-region`).
166///
167/// # Examples
168///
169/// ```
170/// use std::num::NonZero;
171/// use kernel_api::allocator::highmem;
172/// use kernel_api::memory::Frames;
173///
174/// // allocate some memory from `highmem`
175/// let mut mem = highmem().allocate_one()?;
176///
177/// // initialise the memory with zeroes
178/// let slice = mem.write_filled(0);
179///
180/// // once initialised, the memory can be accessed as a slice
181/// slice[1] = 42;
182/// info!("{}", slice[0]);
183///
184/// // the memory is deallocated when dropped
185/// drop(mem);
186/// # Ok::<(), kernel_api::allocator::AllocError>::(())
187/// ```
188// INVARIANT: `raw` must be an unaliased range of frames allocated by `pmm`
189#[cfg(feature = "full")]
190#[repr(C)]
191pub struct Frames<const RAM: bool, T = MaybeUninit<u8>> {
192    raw: Range<RawFrame>,
193	pmm: DynPmm<'static, RAM>,
194    _phantom: PhantomData<[T]>,
195}
196
197#[cfg(feature = "full")]
198impl<const RAM: bool, T> Debug for Frames<RAM, T> {
199    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
200        f.debug_struct("Frames")
201                .field("raw", &self.raw)
202                .field("pmm", &self.pmm)
203                .finish()
204    }
205}
206
207#[cfg(feature = "full")]
208impl<const RAM: bool, T> Frames<RAM, T> {
209	/// Returns the physical memory allocator used for the underlying allocation.
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// use kernel_api::allocator::highmem;
215    ///
216    /// let mem = highmem().allocate_one()?;
217    /// let allocator = mem.pmm(); // this is a reference to the `highmem` allocator
218    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
219    /// ```
220    #[must_use]
221	pub const fn pmm(&self) -> &DynPmm<'static, RAM> { &self.pmm }
222
223	/// Returns the number of allocated frames.
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// use kernel_api::allocator::highmem;
229    ///
230    /// let mem = highmem().allocate_one()?;
231    /// assert_eq!(mem.count(), 1);
232    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
233    /// ```
234    #[must_use]
235    pub const fn count(&self) -> usize {
236        self.raw.end - self.raw.start
237    }
238
239	/// Returns the first frame of the allocation.
240    ///
241    /// # Examples
242    ///
243    /// ```
244    /// use kernel_api::allocator::highmem;
245    ///
246    /// let mem = highmem().allocate_one()?;
247    /// info!("allocation starts at {:?}", mem.base());
248    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
249    /// ```
250    #[must_use]
251    pub const fn base(&self) -> RawFrame {
252        self.raw.start
253    }
254
255	/// Returns the two [`RawFrame`]s that span the allocation.
256	///
257	/// The returned range is half-open, which means that the end frame points *one past* the last frame of the allocation.
258	/// This way, a zero sized allocation is represented by two equal frames, and the difference between the two frames
259	/// represents the number of frames of the allocation.
260    ///
261    /// # Examples
262    ///
263    /// ```
264    /// use kernel_api::allocator::highmem;
265    /// use core::ops::Range;
266    ///
267    /// let mem = highmem().allocate_one()?;
268    /// let Range { start, end } = mem.as_frame_range();
269    ///
270    /// info!("allocation starts at {start:?}");
271    /// assert_eq!(start + 1, end);
272    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
273    /// ```
274    #[must_use]
275	pub fn as_frame_range(&self) -> Range<RawFrame> {
276		self.raw.clone()
277	}
278
279	/// Equivalent to [`base()`](`Self::base`) but taking a raw pointer for `self`.
280	///
281	/// # Safety
282	///
283	/// Behavior is undefined if any of the following conditions are violated:
284	///
285	/// - `self` must be valid for reads.
286	/// - `self` must be properly aligned.
287	/// - `self` must point to a properly initialized value of `Frames`.
288    ///
289    /// # Examples
290    ///
291    /// See examples for [`base()`](Self::base).
292    #[expect(rustdoc::missing_doc_code_examples, reason = "example reference to other function")]
293    #[must_use]
294    pub const unsafe fn base_raw(self: *const Self) -> RawFrame {
295		// SAFETY: Safety requirements upheld by caller
296        unsafe { (*self).raw.start }
297    }
298
299    #[must_use]
300    pub(crate) fn into_raw(self) -> (Range<RawFrame>, DynPmm<'static, RAM>) {
301        let this = ManuallyDrop::new(self);
302	    (this.raw.clone(), this.pmm)
303    }
304
305	/// Creates a `Frames` directly from its raw components.
306	///
307	/// # Safety
308	///
309	/// `raw` must be an unaliased region of memory allocated by the allocator `pmm`.
310    ///
311    /// # Examples
312    ///
313    /// ```
314    /// use kernel_api::allocator::highmem;
315    /// use kernel_api::memory::Frames;
316    ///
317    /// // manually allocate some memory
318    /// let allocator = highmem();
319    /// let start = allocator.allocate_one_raw()?;
320    ///
321    /// // wrap it in `Frames` so it deallocates on drop
322    /// let frames = unsafe {
323    ///     Frames::from_raw(
324    ///         start..(start + 1),
325    ///         allocator,
326    ///     )
327    /// };
328    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
329    /// ```
330    #[must_use]
331    pub const unsafe fn from_raw(raw: Range<RawFrame>, pmm: DynPmm<'static, RAM>) -> Self {
332        Self {
333            raw,
334	        pmm,
335            _phantom: PhantomData
336        }
337    }
338}
339
340#[cfg(feature = "full")]
341impl<T> Frames<false, T> {
342	/// # Safety
343	///
344	/// See [`Frames::from_raw`].
345	pub(crate) const unsafe fn from_raw_tuple<const RAM: bool>((raw, pmm): (Range<RawFrame>, DynPmm<'static, RAM>)) -> Self {
346		Self {
347			raw,
348			pmm: pmm.into(),
349			_phantom: PhantomData
350		}
351	}
352}
353
354#[cfg(feature = "full")]
355impl<const RAM: bool, T> Frames<RAM, MaybeUninit<T>> {
356	/// Casts the underlying type pointed to by the `Frames`.
357    ///
358    /// This allows using a `Frames` object directly as a fixed size array for storing data with a
359    /// lower alignment requirement than a page.
360    ///
361    /// # Examples
362    ///
363    /// ```
364    /// use kernel_api::allocator::highmem;
365    ///
366    /// let mem = highmem().allocate_one()?;
367    /// let mem = mem.cast::<u32>();
368    /// // `mem` can now be used as a `[u32]`
369    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
370    /// ```
371	#[must_use]
372    pub fn cast<U>(self) -> Frames<RAM, MaybeUninit<U>> {
373	    let (raw, pmm) = self.into_raw();
374	    Frames {
375		    raw,
376		    pmm,
377		    _phantom: PhantomData
378	    }
379    }
380}
381
382#[cfg(feature = "full")]
383impl<T> Frames<true, MaybeUninit<T>> {
384	/// Fills the memory region with elements by cloning `value`, returning a mutable reference to the now
385	/// initialized contents of memory.
386	/// Any previously initialized elements will not be dropped.
387	///
388	/// This is similar to [`slice::fill`].
389	///
390	/// # Panics
391	///
392	/// This function will panic if any call to `Clone` panics.
393	///
394	/// If such a panic occurs, any elements previously initialized during this operation will be
395	/// dropped.
396    ///
397    /// # Examples
398    ///
399    /// ```
400    /// use kernel_api::allocator::highmem;
401    /// use kernel_api::memory::PAGE_SIZE;
402    ///
403    /// let mem = highmem().allocate_one()?;
404    /// let initialized = mem.write_filled(1);
405    /// assert_eq!(initialized, &[1; PAGE_SIZE]);
406    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
407    /// ```
408    pub fn write_filled(&mut self, value: T) -> &mut [T] where T: Clone {
409        self.get_mut().write_filled(value)
410    }
411
412	/// Fills the memory region with elements returned by calling a closure for each index, returning a mutable
413	/// reference to the now initialized contents of memory.
414	/// Any previously initialized elements will not be dropped.
415	///
416	/// This method uses a closure to create new values. If you'd rather `Clone` a given value, use
417	/// [`write_filled`](`Frames::write_filled`). If you want to use the `Default` trait to generate values, you can
418	/// pass [`|_| Default::default()`][Default::default] as the argument.
419	///
420	/// # Panics
421	///
422	/// This function will panic if any call to the provided closure panics.
423	///
424	/// If such a panic occurs, any elements previously initialized during this operation will be
425	/// dropped.
426    ///
427    /// # Examples
428    ///
429    /// ```
430    /// use kernel_api::allocator::highmem;
431    /// use kernel_api::memory::PAGE_SIZE;
432    ///
433    /// let mem = highmem().allocate_one()?;
434    /// let initialized = mem.write_with(|idx| 1u8.wrapping_add(idx as u8));
435    /// assert_eq!(&initialized[..5], &[1, 2, 3, 4, 5]);
436    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
437    /// ```
438    pub fn write_filled_with(&mut self, f: impl FnMut(usize) -> T) -> &mut [T] {
439        self.get_mut().write_with(f)
440    }
441
442	/// Fills the memory region with elements by cloning `value`, consuming the `Frames`
443	/// and returning a new object of the initialized type.
444	/// Any previously initialized elements will not be dropped.
445	///
446	/// This is similar to [`slice::fill`].
447	///
448	/// # Panics
449	///
450	/// This function will panic if any call to `Clone` panics.
451	///
452	/// If such a panic occurs, any elements previously initialized during this operation will be
453	/// dropped.
454    ///
455    /// # Examples
456    ///
457    /// ```
458    /// use kernel_api::allocator::highmem;
459    /// use kernel_api::memory::PAGE_SIZE;
460    ///
461    /// let mem = highmem().allocate_one()?;
462    /// let mem = mem.into_filed(1);
463    /// let initialized = mem.get();
464    /// assert_eq!(initialized, &[1; PAGE_SIZE]);
465    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
466    /// ```
467    pub fn into_filed(mut self, value: T) -> Frames<true, T> where T: Clone {
468        self.write_filled(value);
469	    let (raw, pmm) = self.into_raw();
470	    Frames {
471		    raw,
472		    pmm,
473		    _phantom: PhantomData
474	    }
475    }
476
477	/// Fills the memory region with elements returned by calling a closure for each index, consuming the `Frames`
478	/// and returning a new object of the initialized type.
479	/// Any previously initialized elements will not be dropped.
480	///
481	/// This method uses a closure to create new values. If you'd rather `Clone` a given value, use
482	/// [`write_filled`](`Frames::write_filled`). If you want to use the `Default` trait to generate values, you can
483	/// pass [`|_| Default::default()`][Default::default] as the argument.
484	///
485	/// # Panics
486	///
487	/// This function will panic if any call to the provided closure panics.
488	///
489	/// If such a panic occurs, any elements previously initialized during this operation will be
490	/// dropped.
491    ///
492    /// # Examples
493    ///
494    /// ```
495    /// use kernel_api::allocator::highmem;
496    /// use kernel_api::memory::PAGE_SIZE;
497    ///
498    /// let mem = highmem().allocate_one()?;
499    /// let mem = mem.into_filed_with(|idx| 1u8.wrapping_add(idx as u8));
500    /// let initialized = mem.get();
501    /// assert_eq!(&initialized[..5], &[1, 2, 3, 4, 5]);
502    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
503    /// ```
504    pub fn into_filed_with(mut self, f: impl FnMut(usize) -> T) -> Frames<true, T> {
505        self.write_filled_with(f);
506	    let (raw, pmm) = self.into_raw();
507	    Frames {
508		    raw,
509		    pmm,
510		    _phantom: PhantomData
511	    }
512    }
513}
514
515#[cfg(feature = "full")]
516impl<T> Frames<true, T> {
517	/// Returns a slice to the underlying memory.
518    ///
519    /// This will fail to compile if `T` is a type with higher alignment requirements than a page
520    /// guarantees.
521    ///
522    /// # Examples
523    ///
524    /// ```
525    /// use core::mem::MaybeUninit;
526    /// use kernel_api::allocator::highmem;
527    ///
528    /// let mem = highmem().allocate_one()?;
529    /// let buffer: &[MaybeUninit<u8>] = mem.get();
530    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
531    /// ```
532    ///
533    /// ```compile_fail
534    /// use kernel_api::allocator::highmem;
535    ///
536    /// #[repr(align(8192))]
537    /// struct HighAlignment(u64);
538    ///
539    /// let mem = highmem().allocate_one()?
540    /// let mem = mem.cast::<HighAlignment>();
541    /// mem.get();
542    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
543    /// ```
544	#[must_use]
545	pub const fn get(&self) -> &[T] {
546        const { assert!(align_of::<T>() <= 4096, "Accessing a page as a type requires pages to be aligned enough for the type") };
547        let base = self.raw.start.to_virtual().as_ptr();
548		// SAFETY: the backing memory must either be initialized to get a `Frames<T>`, or `T = MaybeUninit`,
549		//  alignment is checked above, and the pointer always points to a valid place in the page map
550		//  region since `RAM = true`
551        unsafe {
552            slice::from_raw_parts(base.cast_const().cast(), self.count() * PAGE_SIZE / size_of::<T>())
553        }
554    }
555
556	/// Returns a mutable slice to the underlying memory.
557    ///
558    /// This will fail to compile if `T` is a type with higher alignment requirements than a page
559    /// guarantees.
560    ///
561    /// # Examples
562    ///
563    /// ```
564    /// use core::mem::MaybeUninit;
565    /// use kernel_api::allocator::highmem;
566    ///
567    /// let mem = highmem().allocate_one()?;
568    /// let buffer: &mut [MaybeUninit<u8>] = mem.get_mut();
569    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
570    /// ```
571    ///
572    /// ```compile_fail
573    /// use kernel_api::allocator::highmem;
574    ///
575    /// #[repr(align(8192))]
576    /// struct HighAlignment(u64);
577    ///
578    /// let mem = highmem().allocate_one()?
579    /// let mut mem = mem.cast::<HighAlignment>();
580    /// mem.get_mut();
581    /// # Ok::<(), kernel_api::allocator::AllocError>::(())
582    /// ```
583	#[must_use]
584	pub const fn get_mut(&mut self) -> &mut [T] {
585        const { assert!(align_of::<T>() <= 4096, "Accessing a page as a type requires pages to be aligned enough for the type") };
586        let base = self.raw.start.to_virtual().as_ptr();
587		// SAFETY: the backing memory must either be initialized to get a `Frames<T>`, or `T = MaybeUninit`,
588		//  alignment is checked above, the pointer always points to a valid place in the page map
589		//  region since `RAM = true`, and the memory is uniquely owned by the `Frames` object and so can't
590		//  be aliased
591        unsafe {
592            slice::from_raw_parts_mut(base.cast(), self.count() * PAGE_SIZE / size_of::<T>())
593        }
594    }
595}
596
597#[cfg(feature = "full")]
598impl<const RAM: bool, T> Drop for Frames<RAM, T> {
599    fn drop(&mut self) {
600	    // SAFETY: invariant of `Frames` that frames are allocated by `self.pmm`
601        unsafe {
602	        self.pmm.deallocate_raw(self.raw.start, self.raw.clone().count().try_into().unwrap());
603        }
604    }
605}
606
607/// A physical memory address.
608#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
609#[repr(transparent)]
610pub struct PhysicalAddress {
611    /// The underlying address
612    pub addr: usize
613}
614
615/// A virtual memory address.
616#[derive(Debug, Copy, Clone, Eq, Ord, Hash, PartialOrd, PartialEq)]
617#[repr(transparent)]
618pub struct VirtualAddress {
619    /// The underlying address
620    pub addr: usize
621}
622
623impl PhysicalAddress {
624    /// Creates a new `PhysicalAddress` with the given address.
625    ///
626    /// # Examples
627    ///
628    /// ```
629    /// use kernel_api::memory::PhysicalAddress;
630    ///
631    /// let addr = PhysicalAddress::new(0);
632    /// ```
633    #[track_caller]
634    #[must_use]
635    pub const fn new(addr: usize) -> Self {
636        Self { addr }
637    }
638
639    /// Converts a `PhysicalAddress` into a [`VirtualAddress`] via the physical page map region.
640    ///
641    /// The returned `VirtualAddress` is only safe to access if the `PhysicalAddress` points into conventional
642    /// RAM.
643    ///
644    /// This is the physical-to-virtual counterpart to [`to_physical()`](VirtualAddress::to_physical).
645    ///
646    /// # Examples
647    ///
648    /// ```
649    /// use kernel_api::memory::PhysicalAddress;
650    ///
651    /// let phys = PhysicalAddress::new(0x1000);
652    /// let virt = phys.to_virtual();
653    /// // if there was conventional RAM at addr 0x1000,
654    /// // `virt` could now be accessed to write to that memory
655    /// ```
656    #[must_use = "this returns the result of the operation, without modifying the original"]
657    pub const fn to_virtual(self) -> VirtualAddress {
658        VirtualAddress::new(self.addr + PAGE_MAP_OFFSET)
659    }
660
661    /// Returns the closest [`RawFrame`] at or below the current address.
662    ///
663    /// # Examples
664    ///
665    /// ```
666    /// use kernel_api::memory::{PhysicalAddress, PAGE_SIZE};
667    ///
668    /// let address = PhysicalAddress::new(0x1);
669    /// let frame = address.align_down_to_frame();
670    /// assert!(address >= *frame);
671    /// assert!((*frame).is_aligned_to(PAGE_SIZE));
672    ///
673    /// // If the address is already aligned, then the value will remain unchanged.
674    /// let address = PhysicalAddress::new(PAGE_SIZE);
675    /// let frame = address.align_down_to_frame();
676    /// assert!(address == *frame);
677    /// ```
678    #[must_use = "this returns the result of the operation, without modifying the original"]
679    pub const fn align_down_to_frame(self) -> RawFrame {
680        let aligned = Self {
681            addr: self.addr & !(PAGE_SIZE - 1)
682        };
683        RawFrame { inner: aligned }
684    }
685
686    /// Returns the closest [`RawFrame`] at or above the current address.
687    ///
688    /// # Examples
689    ///
690    /// ```
691    /// use kernel_api::memory::{PhysicalAddress, PAGE_SIZE};
692    ///
693    /// let address = PhysicalAddress::new(0x1);
694    /// let frame = address.align_up_to_frame();
695    /// assert!(address <= *frame);
696    /// assert!((*frame).is_aligned_to(PAGE_SIZE));
697    ///
698    /// // If the address is already aligned, then the value will remain unchanged.
699    /// let address = PhysicalAddress::new(PAGE_SIZE);
700    /// let frame = address.align_up_to_frame();
701    /// assert!(address == *frame);
702    /// ```
703    #[must_use = "this returns the result of the operation, without modifying the original"]
704    pub const fn align_up_to_frame(self) -> RawFrame {
705        let addr: Self = self + PAGE_SIZE - 1usize;
706        addr.align_down_to_frame()
707    }
708
709    /// Returns `true` if the address is aligned to `align`.
710    ///
711    /// # Panics
712    /// 
713    /// If `align` is not a power of two.
714    ///
715    /// # Examples
716    ///
717    /// ```
718    /// use kernel_api::memory::PhysicalAddress;
719    ///
720    /// let addr = PhysicalAddress::new(64);
721    /// assert_eq!(addr.is_aligned_to(32), true);
722    /// assert_eq!(addr.is_aligned_to(128), false);
723    /// ```
724    #[cfg_attr(debug_assertions, track_caller)]
725    #[must_use]
726    pub const fn is_aligned_to(self, align: usize) -> bool {
727        debug_assert!(align.is_power_of_two(), "alignment must be power of 2");
728
729        self.addr & (align - 1) == 0
730    }
731}
732
733impl VirtualAddress {
734    /// Creates a new `VirtualAddress` with the given address.
735    ///
736    /// # Examples
737    ///
738    /// ```
739    /// use kernel_api::memory::VirtualAddress;
740    ///
741    /// let addr = VirtualAddress::new(0);
742    /// ```
743    #[track_caller]
744    #[must_use]
745    pub const fn new(addr: usize) -> Self {
746        Self { addr }
747    }
748
749    /// Converts a `VirtualAddress` into a raw pointer.
750    ///
751    /// # Examples
752    ///
753    /// ```
754    /// use kernel_api::memory::VirtualAddress;
755    ///
756    /// let addr = VirtualAddress::new(0x1000);
757    /// let ptr = addr.as_ptr();
758    /// // if `addr` came from allocated memory, it can be accessed through `ptr`
759    /// // unsafe { *ptr = 5 };
760    /// ```
761    #[inline]
762    #[must_use]
763    pub const fn as_ptr(self) -> *mut u8 {
764	    core::ptr::with_exposed_provenance_mut(self.addr)
765    }
766
767    /// Returns `true` if the address is in the upper half of the address space, i.e. [kernelspace](crate::address_space#kernel-address-space).
768    ///
769    /// # Examples
770    ///
771    /// ```
772    /// use kernel_api::memory::VirtualAddress;
773    ///
774    /// let addr = VirtualAddress::new(usize::MAX);
775    /// assert_eq!(addr.is_higher_half(), true);
776    /// ```
777    #[must_use]
778    pub const fn is_higher_half(self) -> bool {
779        self.addr.cast_signed() < 0
780    }
781
782    /// Returns the closest [`RawPage`] at or below the current address.
783    ///
784    /// # Examples
785    ///
786    /// ```
787    /// use kernel_api::memory::{VirtualAddress, PAGE_SIZE};
788    ///
789    /// let address = VirtualAddress::new(0x1);
790    /// let page = address.align_down_to_page();
791    /// assert!(address >= *page);
792    /// assert!((*page).is_aligned_to(PAGE_SIZE));
793    ///
794    /// // If the address is already aligned, then the value will remain unchanged.
795    /// let address = VirtualAddress::new(PAGE_SIZE);
796    /// let page = address.align_down_to_page();
797    /// assert!(address == *page);
798    /// ```
799    #[must_use = "this returns the result of the operation, without modifying the original"]
800    pub const fn align_down_to_page(self) -> RawPage {
801        let aligned = Self {
802            addr: self.addr & !(PAGE_SIZE - 1)
803        };
804        RawPage { inner: aligned }
805    }
806
807    /// Returns the closest [`RawPage`] at or above the current address.
808    ///
809    /// # Examples
810    ///
811    /// ```
812    /// use kernel_api::memory::{VirtualAddress, PAGE_SIZE};
813    ///
814    /// let address = VirtualAddress::new(0x1);
815    /// let page = address.align_up_to_page();
816    /// assert!(address <= *page);
817    /// assert!((*page).is_aligned_to(PAGE_SIZE));
818    ///
819    /// // If the address is already aligned, then the value will remain unchanged.
820    /// let address = VirtualAddress::new(PAGE_SIZE);
821    /// let page = address.align_up_to_page();
822    /// assert!(address == *page);
823    /// ```
824    #[must_use = "this returns the result of the operation, without modifying the original"]
825    pub const fn align_up_to_page(self) -> RawPage {
826        let addr: Self = self + PAGE_SIZE - 1usize;
827        addr.align_down_to_page()
828    }
829
830    /// Returns `true` if the address is aligned to `align`.
831    ///
832    /// # Panics
833    ///
834    /// If `align` is not a power of two.
835    ///
836    /// # Examples
837    ///
838    /// ```
839    /// use kernel_api::memory::VirtualAddress;
840    ///
841    /// let addr = VirtualAddress::new(64);
842    /// assert_eq!(addr.is_aligned_to(32), true);
843    /// assert_eq!(addr.is_aligned_to(128), false);
844    /// ```
845    #[cfg_attr(debug_assertions, track_caller)]
846    #[must_use]
847    pub const fn is_aligned_to(self, align: usize) -> bool {
848        debug_assert!(align.is_power_of_two(), "alignment must be power of 2");
849
850        self.addr & (align - 1) == 0
851    }
852
853    /// Computes `self + rhs`, saturating when the address reaches [`usize::MAX`].
854    ///
855    /// # Examples
856    ///
857    /// ```
858    /// use kernel_api::memory::VirtualAddress;
859    ///
860    /// let a = VirtualAddress::new(0x1000);
861    /// assert_eq!(a.saturating_add(1), Some(VirtualAddress::new(0x1001)));
862    /// assert_eq!(a.saturating_add(usize::MAX), None);
863    /// ```
864    #[must_use = "this returns the result of the operation, without modifying the original"]
865    pub const fn saturating_add(self, rhs: usize) -> Self {
866        Self::new(self.addr.saturating_add(rhs))
867    }
868
869    /// Computes `self - rhs` saturating when the address reaches 0.
870    ///
871    /// # Examples
872    ///
873    /// ```
874    /// use kernel_api::memory::VirtualAddress;
875    ///
876    /// let a = VirtualAddress::new(0x1001);
877    /// assert_eq!(a.saturating_sub(1), Some(VirtualAddress::new(0x1000)));
878    /// assert_eq!(a.saturating_sub(usize::MAX), None);
879    /// ```
880    #[must_use = "this returns the result of the operation, without modifying the original"]
881    pub const fn saturating_sub(self, rhs: usize) -> Self {
882        Self::new(self.addr.saturating_sub(rhs))
883    }
884
885	/// Converts an address in the physical page map region into an [`PhysicalAddress`].
886    ///
887    /// This is the virtual-to-physical counterpart to [`to_virtual()`](PhysicalAddress::to_virtual).
888	///
889	/// # Panics
890	/// 
891	/// Panics if the address is not in the physical page map region on a best effort basis.
892    ///
893    /// # Examples
894    ///
895    /// ```should_panic
896    /// use kernel_api::memory::VirtualAddress;
897    ///
898    /// let virt = VirtualAddress::new(0);
899    ///
900    /// // converting non physical page map addresses may panic
901    /// let phys = virt.to_physical();
902    /// ```
903	#[must_use = "this returns the result of the operation, without modifying the original"]
904	pub const fn to_physical(self) -> PhysicalAddress {
905		PhysicalAddress::new(self.addr - PAGE_MAP_OFFSET)
906	}
907}
908
909#[cfg(feature = "full")]
910pub struct EpochGuard {
911    phantom: PhantomData<UnsafeCell<()>>,
912}
913
914#[cfg(feature = "full")]
915impl EpochGuard {
916    /// # Safety
917    ///
918    /// This cpu must be in an epoch.
919    #[doc(hidden)]
920    pub unsafe fn new() -> Self {
921        Self { phantom: PhantomData }
922    }
923}
924
925impl<T: ?Sized> From<*mut T> for VirtualAddress {
926    fn from(value: *mut T) -> Self {
927        Self { addr: value.expose_provenance() }
928    }
929}
930
931impl<T: ?Sized> From<*const T> for VirtualAddress {
932    fn from(value: *const T) -> Self {
933        Self { addr: value.expose_provenance() }
934    }
935}
936
937const impl From<RawFrame> for PhysicalAddress {
938    fn from(value: RawFrame) -> Self {
939        value.inner
940    }
941}
942
943const impl From<RawPage> for VirtualAddress {
944    fn from(value: RawPage) -> Self {
945        value.inner
946    }
947}
948
949impl Step for PhysicalAddress {
950    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
951        Step::steps_between(&start.addr, &end.addr)
952    }
953
954    fn forward_checked(start: Self, count: usize) -> Option<Self> {
955        Step::forward_checked(start.addr, count).map(Self::new)
956    }
957
958	fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
959		let (val, overflow) = Step::forward_overflowing(start.addr, count);
960		(Self::new(val), overflow)
961	}
962
963	fn backward_checked(start: Self, count: usize) -> Option<Self> {
964	    Step::backward_checked(start.addr, count).map(Self::new)
965	}
966
967	fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
968		let (val, overflow) = Step::backward_overflowing(start.addr, count);
969		(Self::new(val), overflow)
970	}
971}
972
973impl Step for VirtualAddress {
974    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
975        Step::steps_between(&start.addr, &end.addr)
976    }
977
978    fn forward_checked(start: Self, count: usize) -> Option<Self> {
979        Step::forward_checked(start.addr, count).map(Self::new)
980    }
981
982	fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
983		let (val, overflow) = Step::forward_overflowing(start.addr, count);
984		(Self::new(val), overflow)
985	}
986
987	fn backward_checked(start: Self, count: usize) -> Option<Self> {
988	    Step::backward_checked(start.addr, count).map(Self::new)
989	}
990
991	fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
992		let (val, overflow) = Step::backward_overflowing(start.addr, count);
993		(Self::new(val), overflow)
994	}
995}
996
997impl Step for RawFrame {
998    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
999        Step::steps_between(&(start.addr / PAGE_SIZE), &(end.addr / PAGE_SIZE))
1000    }
1001
1002    fn forward_checked(start: Self, count: usize) -> Option<Self> {
1003        Some(Self::new(
1004            Step::forward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
1005        ))
1006    }
1007
1008	fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
1009		let count = count.wrapping_mul(PAGE_SIZE);
1010		let (val, overflow) = Step::forward_overflowing(start.addr, count);
1011		(Self::new(val), overflow)
1012	}
1013
1014	fn backward_checked(start: Self, count: usize) -> Option<Self> {
1015	    Some(Self::new(
1016	        Step::backward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
1017	    ))
1018	}
1019
1020	fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
1021		let count = count.wrapping_mul(PAGE_SIZE);
1022		let (val, overflow) = Step::backward_overflowing(start.addr, count);
1023		(Self::new(val), overflow)
1024	}
1025}
1026
1027impl Step for RawPage {
1028    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
1029        Step::steps_between(&start.addr, &end.addr)
1030    }
1031
1032    fn forward_checked(start: Self, count: usize) -> Option<Self> {
1033        Some(Self::new(
1034            Step::forward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
1035        ))
1036    }
1037
1038	fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
1039		let count = count.wrapping_mul(PAGE_SIZE);
1040		let (val, overflow) = Step::forward_overflowing(start.addr, count);
1041		(Self::new(val), overflow)
1042	}
1043
1044	fn backward_checked(start: Self, count: usize) -> Option<Self> {
1045	    Some(Self::new(
1046	        Step::backward_checked(start.addr, count.checked_mul(PAGE_SIZE)?)?
1047	    ))
1048	}
1049
1050	fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
1051		let count = count.wrapping_mul(PAGE_SIZE);
1052		let (val, overflow) = Step::backward_overflowing(start.addr, count);
1053		(Self::new(val), overflow)
1054	}
1055}
1056
1057impl fmt::LowerHex for VirtualAddress {
1058    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1059        fmt::LowerHex::fmt(&self.addr, f)
1060    }
1061}
1062
1063impl fmt::LowerHex for PhysicalAddress {
1064    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1065        fmt::LowerHex::fmt(&self.addr, f)
1066    }
1067}
1068
1069impl fmt::LowerHex for RawPage {
1070    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1071        fmt::LowerHex::fmt(&self.addr, f)
1072    }
1073}
1074
1075impl fmt::LowerHex for RawFrame {
1076    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1077        fmt::LowerHex::fmt(&self.addr, f)
1078    }
1079}