1use crate::sealed::Sealed;
4use crate::structures::paging::page_table::PageTableLevel;
5use crate::structures::paging::PageTableIndex;
6use crate::VirtAddr;
7use core::fmt;
8#[cfg(feature = "step_trait")]
9use core::iter::Step;
10use core::marker::PhantomData;
11use core::ops::{Add, AddAssign, Sub, SubAssign};
12
13pub trait PageSize: Copy + Eq + PartialOrd + Ord + Sealed {
15 const SIZE: u64;
17
18 const DEBUG_STR: &'static str;
20}
21
22pub trait NotGiantPageSize: PageSize {}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub enum Size4KiB {}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub enum Size2MiB {}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum Size1GiB {}
38
39impl PageSize for Size4KiB {
40 const SIZE: u64 = 4096;
41 const DEBUG_STR: &'static str = "4KiB";
42}
43
44impl NotGiantPageSize for Size4KiB {}
45
46impl Sealed for super::Size4KiB {}
47
48impl PageSize for Size2MiB {
49 const SIZE: u64 = Size4KiB::SIZE * 512;
50 const DEBUG_STR: &'static str = "2MiB";
51}
52
53impl NotGiantPageSize for Size2MiB {}
54
55impl Sealed for super::Size2MiB {}
56
57impl PageSize for Size1GiB {
58 const SIZE: u64 = Size2MiB::SIZE * 512;
59 const DEBUG_STR: &'static str = "1GiB";
60}
61
62impl Sealed for super::Size1GiB {}
63
64#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
66#[repr(C)]
67pub struct Page<S: PageSize = Size4KiB> {
68 start_address: VirtAddr,
69 size: PhantomData<S>,
70}
71
72impl<S: PageSize> Page<S> {
73 pub const SIZE: u64 = S::SIZE;
75
76 #[inline]
80 #[rustversion::attr(since(1.61), const)]
81 pub fn from_start_address(address: VirtAddr) -> Result<Self, AddressNotAligned> {
82 if !address.is_aligned_u64(S::SIZE) {
83 return Err(AddressNotAligned);
84 }
85 Ok(Page::containing_address(address))
86 }
87
88 #[inline]
94 #[rustversion::attr(since(1.61), const)]
95 pub unsafe fn from_start_address_unchecked(start_address: VirtAddr) -> Self {
96 Page {
97 start_address,
98 size: PhantomData,
99 }
100 }
101
102 #[inline]
104 #[rustversion::attr(since(1.61), const)]
105 pub fn containing_address(address: VirtAddr) -> Self {
106 Page {
107 start_address: address.align_down_u64(S::SIZE),
108 size: PhantomData,
109 }
110 }
111
112 #[inline]
114 #[rustversion::attr(since(1.61), const)]
115 pub fn start_address(self) -> VirtAddr {
116 self.start_address
117 }
118
119 #[inline]
121 #[rustversion::attr(since(1.61), const)]
122 pub fn size(self) -> u64 {
123 S::SIZE
124 }
125
126 #[inline]
128 #[rustversion::attr(since(1.61), const)]
129 pub fn p4_index(self) -> PageTableIndex {
130 self.start_address().p4_index()
131 }
132
133 #[inline]
135 #[rustversion::attr(since(1.61), const)]
136 pub fn p3_index(self) -> PageTableIndex {
137 self.start_address().p3_index()
138 }
139
140 #[inline]
142 #[rustversion::attr(since(1.61), const)]
143 pub fn page_table_index(self, level: PageTableLevel) -> PageTableIndex {
144 self.start_address().page_table_index(level)
145 }
146
147 #[inline]
149 #[rustversion::attr(since(1.61), const)]
150 pub fn range(start: Self, end: Self) -> PageRange<S> {
151 PageRange { start, end }
152 }
153
154 #[inline]
156 #[rustversion::attr(since(1.61), const)]
157 pub fn range_inclusive(start: Self, end: Self) -> PageRangeInclusive<S> {
158 PageRangeInclusive { start, end }
159 }
160
161 #[cfg(any(feature = "instructions", feature = "step_trait"))]
163 pub(crate) fn steps_between_impl(start: &Self, end: &Self) -> (usize, Option<usize>) {
164 use core::convert::TryFrom;
165
166 if let Some(steps) =
167 VirtAddr::steps_between_u64(&start.start_address(), &end.start_address())
168 {
169 let steps = steps / S::SIZE;
170 let steps = usize::try_from(steps).ok();
171 (steps.unwrap_or(usize::MAX), steps)
172 } else {
173 (0, None)
174 }
175 }
176
177 #[cfg(any(feature = "instructions", feature = "step_trait"))]
179 pub(crate) fn forward_checked_impl(start: Self, count: usize) -> Option<Self> {
180 use core::convert::TryFrom;
181
182 let count = u64::try_from(count).ok()?.checked_mul(S::SIZE)?;
183 let start_address = VirtAddr::forward_checked_u64(start.start_address, count)?;
184 Some(Self {
185 start_address,
186 size: PhantomData,
187 })
188 }
189}
190
191impl<S: NotGiantPageSize> Page<S> {
192 #[inline]
194 #[rustversion::attr(since(1.61), const)]
195 pub fn p2_index(self) -> PageTableIndex {
196 self.start_address().p2_index()
197 }
198}
199
200impl Page<Size1GiB> {
201 #[inline]
203 #[rustversion::attr(since(1.61), const)]
204 pub fn from_page_table_indices_1gib(
205 p4_index: PageTableIndex,
206 p3_index: PageTableIndex,
207 ) -> Self {
208 let mut addr = 0;
209 addr |= p4_index.into_u64() << 39;
210 addr |= p3_index.into_u64() << 30;
211 Page::containing_address(VirtAddr::new_truncate(addr))
212 }
213}
214
215impl Page<Size2MiB> {
216 #[inline]
218 #[rustversion::attr(since(1.61), const)]
219 pub fn from_page_table_indices_2mib(
220 p4_index: PageTableIndex,
221 p3_index: PageTableIndex,
222 p2_index: PageTableIndex,
223 ) -> Self {
224 let mut addr = 0;
225 addr |= p4_index.into_u64() << 39;
226 addr |= p3_index.into_u64() << 30;
227 addr |= p2_index.into_u64() << 21;
228 Page::containing_address(VirtAddr::new_truncate(addr))
229 }
230}
231
232impl Page<Size4KiB> {
233 #[inline]
235 #[rustversion::attr(since(1.61), const)]
236 pub fn from_page_table_indices(
237 p4_index: PageTableIndex,
238 p3_index: PageTableIndex,
239 p2_index: PageTableIndex,
240 p1_index: PageTableIndex,
241 ) -> Self {
242 let mut addr = 0;
243 addr |= p4_index.into_u64() << 39;
244 addr |= p3_index.into_u64() << 30;
245 addr |= p2_index.into_u64() << 21;
246 addr |= p1_index.into_u64() << 12;
247 Page::containing_address(VirtAddr::new_truncate(addr))
248 }
249
250 #[inline]
252 pub const fn p1_index(self) -> PageTableIndex {
253 self.start_address.p1_index()
254 }
255}
256
257impl<S: PageSize> fmt::Debug for Page<S> {
258 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259 f.write_fmt(format_args!(
260 "Page[{}]({:#x})",
261 S::DEBUG_STR,
262 self.start_address().as_u64()
263 ))
264 }
265}
266
267impl<S: PageSize> Add<u64> for Page<S> {
268 type Output = Self;
269 #[inline]
270 fn add(self, rhs: u64) -> Self::Output {
271 Page::containing_address(self.start_address() + rhs * S::SIZE)
272 }
273}
274
275impl<S: PageSize> AddAssign<u64> for Page<S> {
276 #[inline]
277 fn add_assign(&mut self, rhs: u64) {
278 *self = *self + rhs;
279 }
280}
281
282impl<S: PageSize> Sub<u64> for Page<S> {
283 type Output = Self;
284 #[inline]
285 fn sub(self, rhs: u64) -> Self::Output {
286 Page::containing_address(self.start_address() - rhs * S::SIZE)
287 }
288}
289
290impl<S: PageSize> SubAssign<u64> for Page<S> {
291 #[inline]
292 fn sub_assign(&mut self, rhs: u64) {
293 *self = *self - rhs;
294 }
295}
296
297impl<S: PageSize> Sub<Self> for Page<S> {
298 type Output = u64;
299 #[inline]
300 fn sub(self, rhs: Self) -> Self::Output {
301 (self.start_address - rhs.start_address) / S::SIZE
302 }
303}
304
305#[cfg(feature = "step_trait")]
306impl<S: PageSize> Step for Page<S> {
307 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
308 Self::steps_between_impl(start, end)
309 }
310
311 fn forward_checked(start: Self, count: usize) -> Option<Self> {
312 Self::forward_checked_impl(start, count)
313 }
314
315 fn backward_checked(start: Self, count: usize) -> Option<Self> {
316 use core::convert::TryFrom;
317
318 let count = u64::try_from(count).ok()?.checked_mul(S::SIZE)?;
319 let start_address = VirtAddr::backward_checked_u64(start.start_address, count)?;
320 Some(Self {
321 start_address,
322 size: PhantomData,
323 })
324 }
325}
326
327#[derive(Clone, Copy, PartialEq, Eq, Hash)]
329#[repr(C)]
330pub struct PageRange<S: PageSize = Size4KiB> {
331 pub start: Page<S>,
333 pub end: Page<S>,
335}
336
337impl<S: PageSize> PageRange<S> {
338 #[inline]
340 pub fn is_empty(&self) -> bool {
341 self.start >= self.end
342 }
343
344 #[inline]
346 pub fn len(&self) -> u64 {
347 if !self.is_empty() {
348 self.end - self.start
349 } else {
350 0
351 }
352 }
353
354 #[inline]
356 pub fn size(&self) -> u64 {
357 S::SIZE * self.len()
358 }
359}
360
361impl<S: PageSize> Iterator for PageRange<S> {
362 type Item = Page<S>;
363
364 #[inline]
365 fn next(&mut self) -> Option<Self::Item> {
366 if self.start < self.end {
367 let page = self.start;
368 self.start += 1;
369 Some(page)
370 } else {
371 None
372 }
373 }
374}
375
376impl PageRange<Size2MiB> {
377 #[inline]
379 pub fn as_4kib_page_range(self) -> PageRange<Size4KiB> {
380 PageRange {
381 start: Page::containing_address(self.start.start_address()),
382 end: Page::containing_address(self.end.start_address()),
383 }
384 }
385}
386
387impl<S: PageSize> fmt::Debug for PageRange<S> {
388 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389 f.debug_struct("PageRange")
390 .field("start", &self.start)
391 .field("end", &self.end)
392 .finish()
393 }
394}
395
396#[derive(Clone, Copy, PartialEq, Eq, Hash)]
398#[repr(C)]
399pub struct PageRangeInclusive<S: PageSize = Size4KiB> {
400 pub start: Page<S>,
402 pub end: Page<S>,
404}
405
406impl<S: PageSize> PageRangeInclusive<S> {
407 #[inline]
409 pub fn is_empty(&self) -> bool {
410 self.start > self.end
411 }
412
413 #[inline]
415 pub fn len(&self) -> u64 {
416 if !self.is_empty() {
417 self.end - self.start + 1
418 } else {
419 0
420 }
421 }
422
423 #[inline]
425 pub fn size(&self) -> u64 {
426 S::SIZE * self.len()
427 }
428}
429
430impl<S: PageSize> Iterator for PageRangeInclusive<S> {
431 type Item = Page<S>;
432
433 #[inline]
434 fn next(&mut self) -> Option<Self::Item> {
435 if self.start <= self.end {
436 let page = self.start;
437
438 let max_page_addr = VirtAddr::new(u64::MAX) - (S::SIZE - 1);
442 if self.start.start_address() < max_page_addr {
443 self.start += 1;
444 } else {
445 self.end -= 1;
446 }
447 Some(page)
448 } else {
449 None
450 }
451 }
452}
453
454impl<S: PageSize> fmt::Debug for PageRangeInclusive<S> {
455 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456 f.debug_struct("PageRangeInclusive")
457 .field("start", &self.start)
458 .field("end", &self.end)
459 .finish()
460 }
461}
462
463#[derive(Debug)]
465pub struct AddressNotAligned;
466
467impl fmt::Display for AddressNotAligned {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 write!(f, "the given address was not sufficiently aligned")
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476
477 fn test_is_hash<T: core::hash::Hash>() {}
478
479 #[test]
480 pub fn test_page_is_hash() {
481 test_is_hash::<Page<Size4KiB>>();
482 test_is_hash::<Page<Size2MiB>>();
483 test_is_hash::<Page<Size1GiB>>();
484 }
485
486 #[test]
487 pub fn test_page_ranges() {
488 let page_size = Size4KiB::SIZE;
489 let number = 1000;
490
491 let start_addr = VirtAddr::new(0xdead_beaf);
492 let start: Page = Page::containing_address(start_addr);
493 let end = start + number;
494
495 let mut range = Page::range(start, end);
496 for i in 0..number {
497 assert_eq!(
498 range.next(),
499 Some(Page::containing_address(start_addr + page_size * i))
500 );
501 }
502 assert_eq!(range.next(), None);
503
504 let mut range_inclusive = Page::range_inclusive(start, end);
505 for i in 0..=number {
506 assert_eq!(
507 range_inclusive.next(),
508 Some(Page::containing_address(start_addr + page_size * i))
509 );
510 }
511 assert_eq!(range_inclusive.next(), None);
512 }
513
514 #[test]
515 pub fn test_page_range_inclusive_overflow() {
516 let page_size = Size4KiB::SIZE;
517 let number = 1000;
518
519 let start_addr = VirtAddr::new(u64::MAX).align_down(page_size) - number * page_size;
520 let start: Page = Page::containing_address(start_addr);
521 let end = start + number;
522
523 let mut range_inclusive = Page::range_inclusive(start, end);
524 for i in 0..=number {
525 assert_eq!(
526 range_inclusive.next(),
527 Some(Page::containing_address(start_addr + page_size * i))
528 );
529 }
530 assert_eq!(range_inclusive.next(), None);
531 }
532
533 #[test]
534 pub fn test_page_range_len() {
535 let start_addr = VirtAddr::new(0xdead_beaf);
536 let start = Page::<Size4KiB>::containing_address(start_addr);
537 let end = start + 50;
538
539 let range = PageRange { start, end };
540 assert_eq!(range.len(), 50);
541
542 let range_inclusive = PageRangeInclusive { start, end };
543 assert_eq!(range_inclusive.len(), 51);
544 }
545
546 #[test]
547 #[cfg(feature = "step_trait")]
548 fn page_step_forward() {
549 let test_cases = [
550 (0, 0, Some(0)),
551 (0, 1, Some(0x1000)),
552 (0x1000, 1, Some(0x2000)),
553 (0x7fff_ffff_f000, 1, Some(0xffff_8000_0000_0000)),
554 (0xffff_8000_0000_0000, 1, Some(0xffff_8000_0000_1000)),
555 (0xffff_ffff_ffff_f000, 1, None),
556 #[cfg(target_pointer_width = "64")]
557 (0x7fff_ffff_f000, 0x1_2345_6789, Some(0xffff_9234_5678_8000)),
558 #[cfg(target_pointer_width = "64")]
559 (0x7fff_ffff_f000, 0x8_0000_0000, Some(0xffff_ffff_ffff_f000)),
560 #[cfg(target_pointer_width = "64")]
561 (0x7fff_fff0_0000, 0x8_0000_00ff, Some(0xffff_ffff_ffff_f000)),
562 #[cfg(target_pointer_width = "64")]
563 (0x7fff_fff0_0000, 0x8_0000_0100, None),
564 #[cfg(target_pointer_width = "64")]
565 (0x7fff_ffff_f000, 0x8_0000_0001, None),
566 (0, 0x10_0000, Some(0x1_0000_0000)),
569 ];
570 for (start, count, result) in test_cases {
571 let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
572 let result = result
573 .map(|result| Page::<Size4KiB>::from_start_address(VirtAddr::new(result)).unwrap());
574 assert_eq!(Step::forward_checked(start, count), result);
575 }
576 }
577
578 #[test]
579 #[cfg(feature = "step_trait")]
580 fn page_step_backwards() {
581 let test_cases = [
582 (0, 0, Some(0)),
583 (0, 1, None),
584 (0x1000, 1, Some(0)),
585 (0xffff_8000_0000_0000, 1, Some(0x7fff_ffff_f000)),
586 (0xffff_8000_0000_1000, 1, Some(0xffff_8000_0000_0000)),
587 #[cfg(target_pointer_width = "64")]
588 (0xffff_9234_5678_8000, 0x1_2345_6789, Some(0x7fff_ffff_f000)),
589 #[cfg(target_pointer_width = "64")]
590 (0xffff_8000_0000_0000, 0x8_0000_0000, Some(0)),
591 #[cfg(target_pointer_width = "64")]
592 (0xffff_8000_0000_0000, 0x7_ffff_ff01, Some(0xff000)),
593 #[cfg(target_pointer_width = "64")]
594 (0xffff_8000_0000_0000, 0x8_0000_0001, None),
595 (0x1_0000_0000, 0x10_0000, Some(0)),
598 ];
599 for (start, count, result) in test_cases {
600 let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
601 let result = result
602 .map(|result| Page::<Size4KiB>::from_start_address(VirtAddr::new(result)).unwrap());
603 assert_eq!(Step::backward_checked(start, count), result);
604 }
605 }
606
607 #[test]
608 #[cfg(feature = "step_trait")]
609 fn page_steps_between() {
610 let test_cases = [
611 (0, 0, 0, Some(0)),
612 (0, 0x1000, 1, Some(1)),
613 (0x1000, 0, 0, None),
614 (0x1000, 0x1000, 0, Some(0)),
615 (0x7fff_ffff_f000, 0xffff_8000_0000_0000, 1, Some(1)),
616 (0xffff_8000_0000_0000, 0x7fff_ffff_f000, 0, None),
617 (0xffff_8000_0000_0000, 0xffff_8000_0000_0000, 0, Some(0)),
618 (0xffff_8000_0000_0000, 0xffff_8000_0000_1000, 1, Some(1)),
619 (0xffff_8000_0000_1000, 0xffff_8000_0000_0000, 0, None),
620 (0xffff_8000_0000_1000, 0xffff_8000_0000_1000, 0, Some(0)),
621 (
624 0x0000_0000_0000,
625 0x0001_0000_0000,
626 0x10_0000,
627 Some(0x10_0000),
628 ),
629 #[cfg(target_pointer_width = "64")]
636 (
637 0x0000_0000_0000,
638 0x1000_0000_0000,
639 0x1_0000_0000,
640 Some(0x1_0000_0000),
641 ),
642 #[cfg(not(target_pointer_width = "64"))]
643 (0x0000_0000_0000, 0x1000_0000_0000, usize::MAX, None),
644 ];
645 for (start, end, lower, upper) in test_cases {
646 let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
647 let end = Page::from_start_address(VirtAddr::new(end)).unwrap();
648 assert_eq!(Step::steps_between(&start, &end), (lower, upper));
649 }
650 }
651}