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}