Skip to content

Commit ffe34b0

Browse files
authored
Merge pull request #529 from adavis628/master
Add page attribute table support
2 parents a675195 + 2486164 commit ffe34b0

File tree

4 files changed

+149
-26
lines changed

4 files changed

+149
-26
lines changed

src/registers/model_specific.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ pub struct UCet;
6767
#[derive(Debug)]
6868
pub struct SCet;
6969

70+
/// IA32_PAT: Page Attribute Table.
71+
#[derive(Debug)]
72+
pub struct Pat;
73+
7074
impl Efer {
7175
/// The underlying model specific register.
7276
pub const MSR: Msr = Msr(0xC000_0080);
@@ -112,6 +116,22 @@ impl SCet {
112116
pub const MSR: Msr = Msr(0x6A2);
113117
}
114118

119+
impl Pat {
120+
/// The underlying model specific register.
121+
pub const MSR: Msr = Msr(0x277);
122+
/// The default PAT configuration following a power up or reset of the processor.
123+
pub const DEFAULT: [PatMemoryType; 8] = [
124+
PatMemoryType::WriteBack,
125+
PatMemoryType::WriteThrough,
126+
PatMemoryType::Uncacheable,
127+
PatMemoryType::StrongUncacheable,
128+
PatMemoryType::WriteBack,
129+
PatMemoryType::WriteThrough,
130+
PatMemoryType::Uncacheable,
131+
PatMemoryType::StrongUncacheable,
132+
];
133+
}
134+
115135
bitflags! {
116136
/// Flags of the Extended Feature Enable Register.
117137
#[repr(transparent)]
@@ -161,6 +181,43 @@ bitflags! {
161181
}
162182
}
163183

184+
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
185+
/// Memory types used in the [PAT](Pat).
186+
#[repr(u8)]
187+
pub enum PatMemoryType {
188+
/// Uncacheable (UC).
189+
StrongUncacheable = 0x00,
190+
/// Uses a write combining (WC) cache policy.
191+
WriteCombining = 0x01,
192+
/// Uses a write through (WT) cache policy.
193+
WriteThrough = 0x04,
194+
/// Uses a write protected (WP) cache policy.
195+
WriteProtected = 0x05,
196+
/// Uses a write back (WB) cache policy.
197+
WriteBack = 0x06,
198+
/// Same as strong uncacheable, but can be overridden to be write combining by MTRRs (UC-).
199+
Uncacheable = 0x07,
200+
}
201+
impl PatMemoryType {
202+
/// Converts from bits, returning `None` if the value is invalid.
203+
pub const fn from_bits(bits: u8) -> Option<Self> {
204+
match bits {
205+
0x00 => Some(Self::StrongUncacheable),
206+
0x01 => Some(Self::WriteCombining),
207+
0x04 => Some(Self::WriteThrough),
208+
0x05 => Some(Self::WriteProtected),
209+
0x06 => Some(Self::WriteBack),
210+
0x07 => Some(Self::Uncacheable),
211+
_ => None,
212+
}
213+
}
214+
215+
/// Gets the underlying bits.
216+
pub const fn bits(self) -> u8 {
217+
self as u8
218+
}
219+
}
220+
164221
#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
165222
mod x86_64 {
166223
use super::*;
@@ -636,4 +693,36 @@ mod x86_64 {
636693
Self::write(flags, legacy_bitmap);
637694
}
638695
}
696+
697+
impl Pat {
698+
/// Reads IA32_PAT.
699+
///
700+
/// The PAT must be supported on the CPU, otherwise a general protection exception will
701+
/// occur. Support can be detected using the `cpuid` instruction.
702+
#[inline]
703+
pub fn read() -> [PatMemoryType; 8] {
704+
unsafe { Self::MSR.read() }
705+
.to_ne_bytes()
706+
.map(|bits| PatMemoryType::from_bits(bits).unwrap())
707+
}
708+
709+
/// Writes IA32_PAT.
710+
///
711+
/// The PAT must be supported on the CPU, otherwise a general protection exception will
712+
/// occur. Support can be detected using the `cpuid` instruction.
713+
///
714+
/// # Safety
715+
///
716+
/// All affected pages must be flushed from the TLB. Processor caches may also need to be
717+
/// flushed. Additionally, all pages that map to a given frame must have the same memory
718+
/// type.
719+
#[inline]
720+
pub unsafe fn write(table: [PatMemoryType; 8]) {
721+
let bits = u64::from_ne_bytes(table.map(PatMemoryType::bits));
722+
let mut msr = Self::MSR;
723+
unsafe {
724+
msr.write(bits);
725+
}
726+
}
727+
}
639728
}

src/structures/paging/mapper/mapped_page_table.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,8 @@ impl<P: PageTableFrameMapping> Mapper<Size4KiB> for MappedPageTable<'_, P> {
421421

422422
let frame = p1_entry.frame().map_err(|err| match err {
423423
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
424-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
424+
#[allow(deprecated)]
425+
FrameError::HugeFrame => unreachable!(),
425426
})?;
426427

427428
p1_entry.set_unused();
@@ -711,6 +712,9 @@ impl<P: PageTableFrameMapping> PageTableWalker<P> {
711712
&self,
712713
entry: &'b PageTableEntry,
713714
) -> Result<&'b PageTable, PageTableWalkError> {
715+
if entry.flags().contains(PageTableFlags::HUGE_PAGE) {
716+
return Err(PageTableWalkError::MappedToHugePage);
717+
}
714718
let page_table_ptr = self
715719
.page_table_frame_mapping
716720
.frame_to_pointer(entry.frame()?);
@@ -729,6 +733,9 @@ impl<P: PageTableFrameMapping> PageTableWalker<P> {
729733
&self,
730734
entry: &'b mut PageTableEntry,
731735
) -> Result<&'b mut PageTable, PageTableWalkError> {
736+
if entry.flags().contains(PageTableFlags::HUGE_PAGE) {
737+
return Err(PageTableWalkError::MappedToHugePage);
738+
}
732739
let page_table_ptr = self
733740
.page_table_frame_mapping
734741
.frame_to_pointer(entry.frame()?);
@@ -832,7 +839,8 @@ impl From<FrameError> for PageTableWalkError {
832839
#[inline]
833840
fn from(err: FrameError) -> Self {
834841
match err {
835-
FrameError::HugeFrame => PageTableWalkError::MappedToHugePage,
842+
#[allow(deprecated)]
843+
FrameError::HugeFrame => unreachable!(),
836844
FrameError::FrameNotPresent => PageTableWalkError::NotMapped,
837845
}
838846
}

src/structures/paging/mapper/recursive_page_table.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,13 @@ impl Mapper<Size1GiB> for RecursivePageTable<'_> {
322322
let p4 = &mut self.p4;
323323
let p4_entry = &p4[page.p4_index()];
324324

325+
if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
326+
return Err(UnmapError::ParentEntryHugePage);
327+
}
325328
p4_entry.frame().map_err(|err| match err {
326329
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
327-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
330+
#[allow(deprecated)]
331+
FrameError::HugeFrame => unreachable!(),
328332
})?;
329333

330334
let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) };
@@ -441,16 +445,24 @@ impl Mapper<Size2MiB> for RecursivePageTable<'_> {
441445
) -> Result<(PhysFrame<Size2MiB>, MapperFlush<Size2MiB>), UnmapError> {
442446
let p4 = &mut self.p4;
443447
let p4_entry = &p4[page.p4_index()];
448+
if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
449+
return Err(UnmapError::ParentEntryHugePage);
450+
}
444451
p4_entry.frame().map_err(|err| match err {
445452
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
446-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
453+
#[allow(deprecated)]
454+
FrameError::HugeFrame => unreachable!(),
447455
})?;
448456

449457
let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) };
450458
let p3_entry = &p3[page.p3_index()];
459+
if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
460+
return Err(UnmapError::ParentEntryHugePage);
461+
}
451462
p3_entry.frame().map_err(|err| match err {
452463
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
453-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
464+
#[allow(deprecated)]
465+
FrameError::HugeFrame => unreachable!(),
454466
})?;
455467

456468
let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) };
@@ -596,31 +608,44 @@ impl Mapper<Size4KiB> for RecursivePageTable<'_> {
596608
) -> Result<(PhysFrame<Size4KiB>, MapperFlush<Size4KiB>), UnmapError> {
597609
let p4 = &mut self.p4;
598610
let p4_entry = &p4[page.p4_index()];
611+
if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
612+
return Err(UnmapError::ParentEntryHugePage);
613+
}
599614
p4_entry.frame().map_err(|err| match err {
600615
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
601-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
616+
#[allow(deprecated)]
617+
FrameError::HugeFrame => unreachable!(),
602618
})?;
603619

604620
let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) };
605621
let p3_entry = &p3[page.p3_index()];
622+
if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
623+
return Err(UnmapError::ParentEntryHugePage);
624+
}
606625
p3_entry.frame().map_err(|err| match err {
607626
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
608-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
627+
#[allow(deprecated)]
628+
FrameError::HugeFrame => unreachable!(),
609629
})?;
610630

611631
let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) };
612632
let p2_entry = &p2[page.p2_index()];
633+
if p2_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
634+
return Err(UnmapError::ParentEntryHugePage);
635+
}
613636
p2_entry.frame().map_err(|err| match err {
614637
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
615-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
638+
#[allow(deprecated)]
639+
FrameError::HugeFrame => unreachable!(),
616640
})?;
617641

618642
let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) };
619643
let p1_entry = &mut p1[page.p1_index()];
620644

621645
let frame = p1_entry.frame().map_err(|err| match err {
622646
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
623-
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
647+
#[allow(deprecated)]
648+
FrameError::HugeFrame => unreachable!(),
624649
})?;
625650

626651
p1_entry.set_unused();
@@ -818,9 +843,6 @@ impl Translate for RecursivePageTable<'_> {
818843
if p1_entry.is_unused() {
819844
return TranslateResult::NotMapped;
820845
}
821-
if p1_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
822-
panic!("level 1 entry has huge page bit set")
823-
}
824846

825847
let frame = match PhysFrame::from_start_address(p1_entry.addr()) {
826848
Ok(frame) => frame,
@@ -890,6 +912,9 @@ impl CleanUp for RecursivePageTable<'_> {
890912
!(level == PageTableLevel::Four && *i == recursive_index.into())
891913
})
892914
{
915+
if entry.flags().contains(PageTableFlags::HUGE_PAGE) {
916+
continue;
917+
}
893918
if let Ok(frame) = entry.frame() {
894919
let start = VirtAddr::forward_checked_impl(
895920
table_addr,

src/structures/paging/page_table.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use bitflags::bitflags;
1515
pub enum FrameError {
1616
/// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame.
1717
FrameNotPresent,
18-
/// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame
19-
/// as return type, so a huge frame can't be returned.
18+
#[deprecated = "`HugeFrame` is no longer returned by the `frame` method"]
19+
/// The entry does have the `HUGE_PAGE` flag set.
2020
HugeFrame,
2121
}
2222

@@ -63,16 +63,12 @@ impl PageTableEntry {
6363
/// Returns the following errors:
6464
///
6565
/// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set.
66-
/// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the
67-
/// `addr` function must be used)
6866
#[inline]
6967
pub fn frame(&self) -> Result<PhysFrame, FrameError> {
70-
if !self.flags().contains(PageTableFlags::PRESENT) {
71-
Err(FrameError::FrameNotPresent)
72-
} else if self.flags().contains(PageTableFlags::HUGE_PAGE) {
73-
Err(FrameError::HugeFrame)
74-
} else {
68+
if self.flags().contains(PageTableFlags::PRESENT) {
7569
Ok(PhysFrame::containing_address(self.addr()))
70+
} else {
71+
Err(FrameError::FrameNotPresent)
7672
}
7773
}
7874

@@ -86,7 +82,6 @@ impl PageTableEntry {
8682
/// Map the entry to the specified physical frame with the specified flags.
8783
#[inline]
8884
pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) {
89-
assert!(!flags.contains(PageTableFlags::HUGE_PAGE));
9085
self.set_addr(frame.start_address(), flags)
9186
}
9287

@@ -128,17 +123,21 @@ bitflags! {
128123
/// Controls whether accesses from userspace (i.e. ring 3) are permitted.
129124
const USER_ACCESSIBLE = 1 << 2;
130125
/// If this bit is set, a “write-through” policy is used for the cache, else a “write-back”
131-
/// policy is used.
126+
/// policy is used. This referred to as the page-level write-through (PWT) bit.
132127
const WRITE_THROUGH = 1 << 3;
133-
/// Disables caching for the pointed entry is cacheable.
128+
/// Disables caching for the pointed entry if it is cacheable. This referred to as the
129+
/// page-level cache disable (PCD) bit.
134130
const NO_CACHE = 1 << 4;
135131
/// Set by the CPU when the mapped frame or page table is accessed.
136132
const ACCESSED = 1 << 5;
137133
/// Set by the CPU on a write to the mapped frame.
138134
const DIRTY = 1 << 6;
139-
/// Specifies that the entry maps a huge frame instead of a page table. Only allowed in
140-
/// P2 or P3 tables.
135+
/// Specifies that the entry maps a huge frame instead of a page table. This is the same bit
136+
/// as `PAT_4KIB_PAGE`.
141137
const HUGE_PAGE = 1 << 7;
138+
/// This is the PAT bit for page table entries that point to 4KiB pages. This is the same
139+
/// bit as `HUGE_PAGE`.
140+
const PAT_4KIB_PAGE = 1 << 7;
142141
/// Indicates that the mapping is present in all address spaces, so it isn't flushed from
143142
/// the TLB on an address space switch.
144143
const GLOBAL = 1 << 8;
@@ -148,6 +147,8 @@ bitflags! {
148147
const BIT_10 = 1 << 10;
149148
/// Available to the OS, can be used to store additional data, e.g. custom flags.
150149
const BIT_11 = 1 << 11;
150+
/// This is the PAT bit for page table entries that point to huge pages.
151+
const PAT_HUGE_PAGE = 1 << 12;
151152
/// Available to the OS, can be used to store additional data, e.g. custom flags.
152153
const BIT_52 = 1 << 52;
153154
/// Available to the OS, can be used to store additional data, e.g. custom flags.

0 commit comments

Comments
 (0)