abyss/
addressing.rs

1//! Physical and Virtual Memory Addressing Schemes.
2//!
3//! This module provides abstractions for virtual address and physical
4//! address. In the keos kernel, kernel virtual memory is directly mapped to
5//! physical memory. The first page of kernel virtual memory maps to the first
6//! frame of physical memory, the second page maps to the second frame, and so
7//! on. This direct mapping allows the kernel to calculate the physical address
8//! from the kernel virtual address with simple arithmetic operations, by adding
9//! or subtracting a constant offset.
10//!
11//! The module defines three primary types for memory addresses: [`Kva`] for
12//! kernel virtual address, [`Va`] for virtual address, and [`Pa`] for
13//! physical address. These types are equipped with methods to facilitate
14//! address manipulation, conversion between virtual and physical addresses, and
15//! safe arithmetic operations on addresses.
16//!
17//! In this abstraction, the kernel can seamlessly perform address calculations
18//! and fastly convert between physical and kernel virtual adresses.
19//!
20//! Both [`Pa`], [`Va`] and [`Kva`] support arithmetic operations (addition,
21//! subtraction, bitwise operations), which allow straightforward address
22//! arithmetic. The types can be safely cast between virtual and physical
23//! addresses, and the operations maintain the integrity of the memory model.
24//!
25//! ## Arithmetic Operations
26//!
27//! Both `Pa` and `Kva` types implement various arithmetic operations such as
28//! addition, subtraction, and bitwise operations. These operations allow for
29//! easy manipulation of addresses, such as incrementing or decrementing by
30//! a specific number of bytes or performing logical operations on addresses.
31//!
32//! ## Example Usage:
33//!
34//! ```
35//! // Create a physical address
36//! let pa = Pa::new(0x1234_5678_9abc_0000).unwrap();
37//!
38//! // Convert to kernel virtual address
39//! let kva = pa.into_kva();
40//!
41//! // Perform arithmetic on addresses
42//! let next_pa = pa + 0x1000; // Move to the next page
43//! ```
44
45const VA_TO_PA_OFF: usize = 0xffff000000000000 | (510 << 39);
46
47/// The size of a single page in memory, in bytes.
48///
49/// This constant represents the size of a memory page, which is 4 KiB
50/// (kilobytes). It is commonly used in memory management to divide memory into
51/// pages for efficient allocation, paging, and address translation.
52///
53/// This value is crucial when working with memory in the kernel, as it
54/// determines how memory is accessed, mapped, and managed. It is used in
55/// conjunction with other constants (e.g., page shift, page mask)
56/// to handle operations like page table indexing, virtual-to-physical address
57/// translation, and memory page allocation.
58///
59/// ## Example:
60/// ```
61/// let next_page = address + PAGE_SIZE;
62/// ```
63pub const PAGE_SIZE: usize = 0x1000;
64
65/// The shift amount to get the page index from a given address.
66///
67/// This constant represents the number of bits to shift a memory address to
68/// obtain the page index. It is used to determine which page a given address
69/// belongs to by shifting the address to the right. This value corresponds to
70/// the log2 of the page size (which is typically 12 bits for a 4 KB page size).
71///
72/// ## Example:
73/// ```
74/// let frame_number = address >> PAGE_SHIFT;
75/// ```
76pub const PAGE_SHIFT: usize = 12; // 12 bits (log2 of 4 KB)
77
78/// A mask for extracting the offset within a page from a given address.
79///
80/// This constant is used to calculate the offset within a page from a given
81/// address. By using the page size and the corresponding shift value, this mask
82/// allows you to calculate the exact byte offset within the page.
83/// It is commonly used in address translation and memory management to
84/// determine the location of a byte relative to the start of the page.
85///
86/// ## Example:
87/// ```
88/// let offset_within_page = address & PAGE_MASK;  // Get the byte offset within the page
89/// ```
90pub const PAGE_MASK: usize = 0xfff;
91
92/// Represents a physical address.
93///
94/// The `Pa` (Physical Address) struct is a wrapper around the `usize` type,
95/// which represents a physical address in memory. It is used to handle
96/// addresses that correspond directly to the hardware memory locations in the
97/// physical address space.
98///
99/// This struct provides methods to:
100/// - Create a new physical address with validation.
101/// - Convert a physical address to a virtual address.
102/// - Convert a physical address to a raw `usize` type.
103///
104/// ## Example:
105/// ```
106/// let pa = Pa::new(0x1234_5678_9ABC_DEF0).unwrap();
107/// let kva = pa.into_kva(); // Convert to a kernel virtual address.
108/// ```
109#[repr(transparent)]
110#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
111pub struct Pa(usize);
112
113impl Pa {
114    /// The physical address `0`.
115    ///
116    /// This constant represents the special address `0`.
117    pub const ZERO: Self = Self(0);
118
119    /// Creates a new physical address if the address is valid.
120    ///
121    /// This method attempts to create a new [`Pa`] instance by validating the
122    /// provided physical address. The address must be less than
123    /// `0xffff_0000_0000_0000`, which ensures it falls within the valid
124    /// physical address range.
125    ///
126    /// # Arguments
127    /// - `addr`: A `usize` representing the physical address.
128    ///
129    /// # Returns
130    /// - `Some(Pa)` if the address is valid.
131    /// - `None` if the address is outside the valid range.
132    ///
133    /// ## Example:
134    /// ```rust
135    /// let pa = Pa::new(0x1234_5678_9ABC_DEF0);
136    /// ```
137    #[inline]
138    pub const fn new(addr: usize) -> Option<Self> {
139        if addr < 0xffff_0000_0000_0000 {
140            Some(Self(addr))
141        } else {
142            None
143        }
144    }
145
146    /// Cast the physical address into a raw `usize`.
147    ///
148    /// This method allows the physical address to be cast into a raw `usize`
149    /// value, which can be used for low-level operations like pointer
150    /// arithmetic or addressing.
151    ///
152    /// # Returns
153    /// - The underlying `usize` value representing the physical address.
154    #[inline]
155    pub const fn into_usize(self) -> usize {
156        self.0
157    }
158
159    /// Convert the physical address to a virtual address.
160    ///
161    /// This method allows you to convert a [`Pa`] (physical address) to a
162    /// [`Kva`] (kernel virtual address). The conversion uses a fixed offset
163    /// to transform the physical address into a corresponding kernel virtual
164    /// address.
165    ///
166    /// # Returns
167    /// - The corresponding kernel virtual address as a [`Kva`] instance.
168    ///
169    /// ## Example:
170    /// ```rust
171    /// let pa = Pa::new(0x1234_5678_9ABC_DEF0).unwrap();
172    /// let va = pa.into_kva();  // Convert the physical address to kernel virtual address
173    /// ```
174    #[inline]
175    pub const fn into_kva(self) -> Kva {
176        Kva(self.0 + VA_TO_PA_OFF)
177    }
178
179    /// Align down the physical address to the page boundary.
180    pub const fn page_down(self) -> Self {
181        Self(self.0 & !PAGE_MASK)
182    }
183
184    /// Align up to the physical address to the page boundary.
185    pub const fn page_up(self) -> Self {
186        Self((self.0 + PAGE_MASK) & !PAGE_MASK)
187    }
188
189    /// Extracts the page offset from the physical address.
190    ///
191    /// This method retrieves the lower bits of the address that represent the
192    /// offset within a memory page. The offset is useful when working with
193    /// memory operations that need to determine the position within a page.
194    ///
195    /// # Returns
196    /// - The offset within the page as a `usize`.
197    ///
198    /// # Example
199    /// ```
200    /// let pa = Pa::new(0x1234_5678).unwrap();
201    /// let offset = pa.offset();
202    /// assert_eq!(offset, 0x678); // Example offset within the page
203    /// ```
204    #[inline]
205    pub const fn offset(self) -> usize {
206        self.0 & PAGE_MASK
207    }
208}
209
210/// Represents a kernel virtual address.
211///
212/// The [`Kva`] (Kernel Virtual Address) struct is a lightweight wrapper around
213/// a `usize` value that represents an address in the kernel's virtual address
214/// space. It provides utility methods for address validation, conversion, and
215/// alignment.
216///
217/// This abstraction ensures that kernel addresses are used safely and
218/// consistently, reducing the risk of incorrect memory access.
219#[repr(transparent)]
220#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
221pub struct Kva(usize);
222
223impl Kva {
224    /// Creates a new kernel virtual address if the address is valid.
225    ///
226    /// This method validates the given address to ensure it falls within the
227    /// valid kernel virtual address range. If the address is valid, it
228    /// returns a `Some(Kva)`, otherwise, it returns `None`.
229    ///
230    /// # Arguments
231    /// - `addr`: A `usize` representing the virtual address.
232    ///
233    /// # Returns
234    /// - `Some(Kva)` if the address is within the valid kernel address space.
235    /// - `None` if the address is outside the valid range.
236    ///
237    /// # Example
238    /// ```
239    /// let kva = Kva::new(0xFFFF_8000_1234_5678);
240    /// assert!(kva.is_some()); // Valid kernel virtual address
241    ///
242    /// let invalid_kva = Kva::new(0x1234_5678);
243    /// assert!(invalid_kva.is_none()); // Invalid kernel address
244    /// ```
245    #[inline(always)]
246    pub const fn new(addr: usize) -> Option<Self> {
247        match addr & 0xffff_8000_0000_0000 {
248            0xffff_8000_0000_0000 => Some(Self(addr)),
249            _ => None,
250        }
251    }
252
253    /// Returns the raw `usize` representation of the virtual address.
254    ///
255    /// This function extracts the underlying `usize` value from the [`Kva`]
256    /// struct, allowing it to be used in low-level operations.
257    ///
258    /// # Returns
259    /// - The virtual address as a `usize` value.
260    ///
261    /// # Example
262    /// ```
263    /// let kva = Kva::new(0xFFFF_8000_1234_5678).unwrap();
264    /// let raw_addr = kva.into_usize();
265    /// assert_eq!(raw_addr, 0xFFFF_8000_1234_5678);
266    /// ```
267    #[inline]
268    pub const fn into_usize(self) -> usize {
269        self.0
270    }
271
272    /// Converts the virtual address to a physical address.
273    ///
274    /// This method maps a [`Kva`] (Kernel Virtual Address) to a [`Pa`]
275    /// (Physical Address) by subtracting a fixed offset. This operation
276    /// utilizes that the virtual address follows a known mapping pattern
277    /// for kernel memory.
278    ///
279    /// # Returns
280    /// - The corresponding physical address as a [`Pa`] instance.
281    ///
282    /// # Example
283    /// ```
284    /// let kva = Kva::new(0xFFFF_8000_1234_5678).unwrap();
285    /// let pa = kva.into_pa();
286    /// ```
287    #[inline]
288    pub const fn into_pa(self) -> Pa {
289        Pa(self.0 - VA_TO_PA_OFF)
290    }
291
292    /// Aligns the virtual address down to the nearest page boundary.
293    ///
294    /// This method clears the lower bits of the address to ensure it is
295    /// page-aligned downwards, meaning the address will be rounded down to
296    /// the start of the current memory page.
297    ///
298    /// # Returns
299    /// - A new [`Kva`] instance representing the aligned address.
300    ///
301    /// # Example
302    /// ```
303    /// let kva = Kva::new(0xFFFF_8000_1234_5678).unwrap();
304    /// let aligned = kva.page_down();
305    /// assert_eq!(aligned.into_usize(), 0xFFFF_8000_1234_5000); // Example of alignment
306    /// ```
307    #[inline]
308    pub const fn page_down(self) -> Self {
309        Self(self.0 & !PAGE_MASK)
310    }
311
312    /// Aligns the virtual address up to the nearest page boundary.
313    ///
314    /// This method rounds the address up to the next page boundary by adding
315    /// [`PAGE_MASK`] and clearing the lower bits. This ensures that the
316    /// address is aligned to the start of the next memory page.
317    ///
318    /// # Returns
319    /// - A new [`Kva`] instance representing the aligned address.
320    ///
321    /// # Example
322    /// ```
323    /// let kva = Kva::new(0xFFFF_8000_1234_5678).unwrap();
324    /// let aligned = kva.page_up();
325    /// assert_eq!(aligned.into_usize(), 0xFFFF_8000_1234_6000); // Example of alignment
326    /// ```
327    #[inline]
328    pub const fn page_up(self) -> Self {
329        Self((self.0 + PAGE_MASK) & !PAGE_MASK)
330    }
331
332    /// Extracts the page offset from the virtual address.
333    ///
334    /// This method retrieves the lower bits of the address that represent the
335    /// offset within a memory page. The offset is useful when working with
336    /// memory operations that need to determine the position within a page.
337    ///
338    /// # Returns
339    /// - The offset within the page as a `usize`.
340    ///
341    /// # Example
342    /// ```
343    /// let kva = Kva::new(0xFFFF_8000_1234_5678).unwrap();
344    /// let offset = kva.offset();
345    /// assert_eq!(offset, 0x678); // Example offset within the page
346    /// ```
347    #[inline]
348    pub const fn offset(self) -> usize {
349        self.0 & PAGE_MASK
350    }
351
352    /// Converts the kernel virtual address into a general virtual address.
353    ///
354    /// This method allows converting a [`Kva`] into a [`Va`] (a more generic
355    /// virtual address type), maintaining the same underlying address value
356    /// but changing its type representation.
357    ///
358    /// # Returns
359    /// - The equivalent [`Va`] instance.
360    ///
361    /// # Example
362    /// ```
363    /// let kva = Kva::new(0xFFFF_8000_1234_5678).unwrap();
364    /// let va = kva.into_va();
365    /// ```
366    #[inline]
367    pub const fn into_va(self) -> Va {
368        Va(self.0)
369    }
370}
371
372/// Represents a virtual address.
373///
374/// The [`Va`] (Virtual Address) struct represents an address in the virtual
375/// memory space used by the kernel or user-space applications.
376///
377/// This abstraction provides utility methods for validation, alignment, and
378/// address manipulation, ensuring safe and consistent handling of virtual
379/// addresses.
380#[repr(transparent)]
381#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
382pub struct Va(usize);
383
384impl Va {
385    /// Creates a new virtual address if the address is valid.
386    ///
387    /// This method checks whether the given address falls within the valid
388    /// virtual address range. If it does, a `Some(Va)` is returned;
389    /// otherwise, `None` is returned.
390    ///
391    /// # Arguments
392    /// - `addr`: A `usize` representing the virtual address.
393    ///
394    /// # Returns
395    /// - `Some(Va)`: If the address is within the valid virtual memory range.
396    /// - `None`: If the address is invalid.
397    ///
398    /// # Example
399    /// ```
400    /// let va = Va::new(0xFFFF_8000_1234_5678);
401    /// assert!(va.is_some()); // Valid virtual address
402    ///
403    /// let invalid_va = Va::new(0xFFFF_7000_1234_5678);
404    /// assert!(invalid_va.is_none()); // Invalid virtual address
405    /// ```
406    #[inline(always)]
407    pub const fn new(addr: usize) -> Option<Self> {
408        match addr & 0xffff_8000_0000_0000 {
409            m if m == 0xffff_8000_0000_0000 || m == 0 => Some(Self(addr)),
410            _ => None,
411        }
412    }
413
414    /// Returns the raw `usize` representation of the virtual address.
415    ///
416    /// This method allows extracting the underlying `usize` value, enabling
417    /// low-level operations or conversions between address types.
418    ///
419    /// # Returns
420    /// - The virtual address as a `usize`.
421    ///
422    /// # Example
423    /// ```
424    /// let va = Va::new(0xFFFF_8000_1234_5678).unwrap();
425    /// let raw_addr = va.into_usize();
426    /// assert_eq!(raw_addr, 0xFFFF_8000_1234_5678);
427    /// ```
428    #[inline]
429    pub const fn into_usize(self) -> usize {
430        self.0
431    }
432
433    /// Aligns the virtual address down to the nearest page boundary.
434    ///
435    /// This method ensures that the address is aligned to the start of its
436    /// memory page by clearing the lower bits that represent the page
437    /// offset.
438    ///
439    /// # Returns
440    /// - A new [`Va`] instance representing the aligned address.
441    ///
442    /// # Example
443    /// ```
444    /// let va = Va::new(0xFFFF_8000_1234_5678).unwrap();
445    /// let aligned = va.page_down();
446    /// assert_eq!(aligned.into_usize(), 0xFFFF_8000_1234_5000); // Example alignment
447    /// ```
448    #[inline]
449    pub const fn page_down(self) -> Self {
450        Self(self.0 & !PAGE_MASK)
451    }
452
453    /// Aligns the virtual address up to the nearest page boundary.
454    ///
455    /// This method rounds up the address to the next memory page.
456    ///
457    /// # Returns
458    /// - A new [`Va`] instance representing the aligned address.
459    ///
460    /// # Example
461    /// ```
462    /// let va = Va::new(0xFFFF_8000_1234_5678).unwrap();
463    /// let aligned = va.page_up();
464    /// assert_eq!(aligned.into_usize(), 0xFFFF_8000_1234_6000); // Example alignment
465    /// ```
466    #[inline]
467    pub const fn page_up(self) -> Self {
468        Self((self.0 + PAGE_MASK) & !PAGE_MASK)
469    }
470
471    /// Extracts the offset within the memory page from the virtual address.
472    ///
473    /// This method retrieves the lower bits of the address that indicate the
474    /// position within a page, which is useful for memory management and
475    /// page-related operations.
476    ///
477    /// # Returns
478    /// - The offset within the page as a `usize`.
479    ///
480    /// # Example
481    /// ```
482    /// let va = Va::new(0xFFFF_8000_1234_5678).unwrap();
483    /// let offset = va.offset();
484    /// assert_eq!(offset, 0x678); // Example offset within the page
485    /// ```
486    #[inline]
487    pub const fn offset(self) -> usize {
488        self.0 & PAGE_MASK
489    }
490}
491
492macro_rules! impl_arith {
493    ($t: ty) => {
494        impl core::ops::Add<usize> for $t {
495            type Output = Self;
496
497            fn add(self, other: usize) -> Self::Output {
498                Self(self.0 + other)
499            }
500        }
501        impl core::ops::AddAssign<usize> for $t {
502            fn add_assign(&mut self, other: usize) {
503                self.0 = self.0 + other
504            }
505        }
506        impl core::ops::Sub<usize> for $t {
507            type Output = Self;
508
509            fn sub(self, other: usize) -> Self::Output {
510                Self(self.0 - other)
511            }
512        }
513        impl core::ops::Sub<Self> for $t {
514            type Output = usize;
515
516            fn sub(self, other: Self) -> Self::Output {
517                self.0 - other.0
518            }
519        }
520        impl core::ops::SubAssign<usize> for $t {
521            fn sub_assign(&mut self, other: usize) {
522                self.0 = self.0 - other
523            }
524        }
525        impl core::ops::BitOr<usize> for $t {
526            type Output = Self;
527
528            fn bitor(self, other: usize) -> Self {
529                Self(self.0 | other)
530            }
531        }
532        impl core::ops::BitOrAssign<usize> for $t {
533            fn bitor_assign(&mut self, other: usize) {
534                self.0 = self.0 | other;
535            }
536        }
537        impl core::ops::BitAnd<usize> for $t {
538            type Output = Self;
539
540            fn bitand(self, other: usize) -> Self {
541                Self(self.0 & other)
542            }
543        }
544        impl core::ops::BitAndAssign<usize> for $t {
545            fn bitand_assign(&mut self, other: usize) {
546                self.0 = self.0 & other;
547            }
548        }
549    };
550}
551
552impl_arith!(Kva);
553impl_arith!(Va);
554impl_arith!(Pa);
555
556impl core::fmt::Debug for Kva {
557    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
558        write!(f, "Kva(0x{:x})", self.0)
559    }
560}
561impl core::fmt::Display for Kva {
562    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
563        write!(f, "Kva(0x{:x})", self.0)
564    }
565}
566impl core::fmt::Debug for Va {
567    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
568        write!(f, "Va(0x{:x})", self.0)
569    }
570}
571impl core::fmt::Display for Va {
572    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
573        write!(f, "Va(0x{:x})", self.0)
574    }
575}
576impl core::fmt::Debug for Pa {
577    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
578        write!(f, "Pa(0x{:x})", self.0)
579    }
580}
581impl core::fmt::Display for Pa {
582    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
583        write!(f, "Pa(0x{:x})", self.0)
584    }
585}