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}