From 32327c09859de8ab627c942fd15624c7d7cf470b Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Mon, 27 Jan 2025 19:53:52 +0100 Subject: [PATCH 1/5] feat(msr): add IA32_APIC_BASE support --- src/registers/model_specific.rs | 105 ++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index fa1902c9..c7815117 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -71,6 +71,10 @@ pub struct SCet; #[derive(Debug)] pub struct Pat; +/// IA32_APIC_BASE: status and location of the local APIC +#[derive(Debug)] +pub struct Apic; + impl Efer { /// The underlying model specific register. pub const MSR: Msr = Msr(0xC000_0080); @@ -132,6 +136,11 @@ impl Pat { ]; } +impl Apic { + /// The underlying model specific register. + pub const MSR: Msr = Msr(0x1B); +} + bitflags! { /// Flags of the Extended Feature Enable Register. #[repr(transparent)] @@ -218,6 +227,31 @@ impl PatMemoryType { } } +bitflags! { + /// Flags for the Advanced Programmable Interrupt Controler Base Register. + #[repr(transparent)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] + pub struct ApicFlags: u64 { + // bits 0 - 7 are reserved. + /// Indicates whether the current processor is the bootstrap processor + const BSP = 1 << 8; + // bits 9 - 10 are reserved. + /// Enables or disables the local Apic + const LAPIC_ENABLE = 1 << 11; + /// Specifies the base address of the APIC registers. This 24-bit value is extended by 12 bits at the low end to form the base address. + const APIC_BASE = 0b111111111111111111111111 << 12; + // bits 36-63 reserved + } +} + +impl ApicFlags { + /// Returns the physical address of the apic registers + #[inline] + pub fn address(&self) -> u64 { + self.bits() & 0b11111111111111111111000000000000 + } +} + #[cfg(all(feature = "instructions", target_arch = "x86_64"))] mod x86_64 { use super::*; @@ -725,4 +759,75 @@ mod x86_64 { } } } + + impl Apic { + /// Reads the IA32_APIC_BASE. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + #[inline] + pub fn read() -> ApicFlags { + ApicFlags::from_bits_truncate(Self::read_raw()) + } + + /// Reads the raw IA32_APIC_BASE. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + #[inline] + pub fn read_raw() -> u64 { + unsafe { Self::MSR.read() } + } + + /// Writes the IA32_APIC_BASE preserving reserved values. + /// + /// Preserves the value of reserved fields. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + #[inline] + pub fn write(flags: ApicFlags) { + let old_value = Self::read_raw(); + let reserved = old_value & !(ApicFlags::all().bits()); + let new_value = reserved | flags.bits(); + + unsafe { + Self::write_raw(new_value); + } + } + + /// Writes the IA32_APIC_BASE flags. + /// + /// Does not preserve any bits, including reserved fields. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + /// + /// ## Safety + /// + /// Unsafe because it's possible to set reserved bits to `1`. + #[inline] + pub unsafe fn write_raw(flags: u64) { + let mut msr = Self::MSR; + unsafe { + msr.write(flags); + } + } + + /// Update IA32_APIC_BASE flags. + /// + /// Preserves the value of reserved fields. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + #[inline] + pub fn update(f: F) + where + F: FnOnce(&mut ApicFlags), + { + let mut flags = Self::read(); + f(&mut flags); + Self::write(flags); + } + } } From 6286c5180a1f3794ddd78465d2d27e0ecaecfbe2 Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 08:51:38 +0100 Subject: [PATCH 2/5] feat(msr): add X2APIC enable functionality --- src/registers/model_specific.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index c7815117..70dd49aa 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -235,7 +235,10 @@ bitflags! { // bits 0 - 7 are reserved. /// Indicates whether the current processor is the bootstrap processor const BSP = 1 << 8; - // bits 9 - 10 are reserved. + // bit 9 is reserved. + /// Places the local APIC in the x2APIC mode. Processor support for x2APIC feature can be + /// detected using the `cpuid` instruction. (CPUID.(EAX=1):ECX.21) + const X2APIC_ENABLE = 1 << 10; /// Enables or disables the local Apic const LAPIC_ENABLE = 1 << 11; /// Specifies the base address of the APIC registers. This 24-bit value is extended by 12 bits at the low end to form the base address. From 7a97e484a970dac13007a470bf29ec25100de503 Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 09:57:29 +0100 Subject: [PATCH 3/5] refactor(msr): handle apic base as seperate value --- src/registers/model_specific.rs | 63 +++++++++++++-------------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 70dd49aa..47d0d4e3 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -241,17 +241,6 @@ bitflags! { const X2APIC_ENABLE = 1 << 10; /// Enables or disables the local Apic const LAPIC_ENABLE = 1 << 11; - /// Specifies the base address of the APIC registers. This 24-bit value is extended by 12 bits at the low end to form the base address. - const APIC_BASE = 0b111111111111111111111111 << 12; - // bits 36-63 reserved - } -} - -impl ApicFlags { - /// Returns the physical address of the apic registers - #[inline] - pub fn address(&self) -> u64 { - self.bits() & 0b11111111111111111111000000000000 } } @@ -262,7 +251,9 @@ mod x86_64 { use crate::registers::rflags::RFlags; use crate::structures::gdt::SegmentSelector; use crate::structures::paging::Page; + use crate::structures::paging::PhysFrame; use crate::structures::paging::Size4KiB; + use crate::PhysAddr; use crate::PrivilegeLevel; use bit_field::BitField; use core::convert::TryInto; @@ -769,8 +760,9 @@ mod x86_64 { /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will /// occur. Support can be detected using the `cpuid` instruction. #[inline] - pub fn read() -> ApicFlags { - ApicFlags::from_bits_truncate(Self::read_raw()) + pub fn read() -> (PhysFrame, ApicFlags) { + let (frame, flags) = Self::read_raw(); + (frame, ApicFlags::from_bits_truncate(flags)) } /// Reads the raw IA32_APIC_BASE. @@ -778,8 +770,12 @@ mod x86_64 { /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will /// occur. Support can be detected using the `cpuid` instruction. #[inline] - pub fn read_raw() -> u64 { - unsafe { Self::MSR.read() } + pub fn read_raw() -> (PhysFrame, u64) { + let raw = unsafe { Self::MSR.read() }; + // extract bits 32 - 51 (incl.) + let addr = PhysAddr::new((raw >> 32) & 0xFFFFF); + let frame = PhysFrame::containing_address(addr); + (frame, raw) } /// Writes the IA32_APIC_BASE preserving reserved values. @@ -788,14 +784,18 @@ mod x86_64 { /// /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will /// occur. Support can be detected using the `cpuid` instruction. + /// + /// ## Safety + /// + /// Unsafe because changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules. #[inline] - pub fn write(flags: ApicFlags) { - let old_value = Self::read_raw(); - let reserved = old_value & !(ApicFlags::all().bits()); - let new_value = reserved | flags.bits(); + pub unsafe fn write(frame: PhysFrame, flags: ApicFlags) { + let (_, old_flags) = Self::read_raw(); + let reserved = old_flags & !(ApicFlags::all().bits()); + let new_flags = reserved | flags.bits(); unsafe { - Self::write_raw(new_value); + Self::write_raw(frame, new_flags); } } @@ -808,29 +808,14 @@ mod x86_64 { /// /// ## Safety /// - /// Unsafe because it's possible to set reserved bits to `1`. + /// Unsafe because it's possible to set reserved bits to `1` and changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules. #[inline] - pub unsafe fn write_raw(flags: u64) { + pub unsafe fn write_raw(frame: PhysFrame, flags: u64) { + let addr = frame.start_address(); let mut msr = Self::MSR; unsafe { - msr.write(flags); + msr.write(flags | addr.as_u64()); } } - - /// Update IA32_APIC_BASE flags. - /// - /// Preserves the value of reserved fields. - /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. - #[inline] - pub fn update(f: F) - where - F: FnOnce(&mut ApicFlags), - { - let mut flags = Self::read(); - f(&mut flags); - Self::write(flags); - } } } From 753388d6895c16dfb71e60fd03a722619ac4b41d Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 12:19:28 +0100 Subject: [PATCH 4/5] fix(msr): properly mask APIC_BASE --- src/registers/model_specific.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 47d0d4e3..c6b299e3 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -773,7 +773,7 @@ mod x86_64 { pub fn read_raw() -> (PhysFrame, u64) { let raw = unsafe { Self::MSR.read() }; // extract bits 32 - 51 (incl.) - let addr = PhysAddr::new((raw >> 32) & 0xFFFFF); + let addr = PhysAddr::new(raw & 0x_000F_FFFF_FFFF_F000); let frame = PhysFrame::containing_address(addr); (frame, raw) } From 9e38b0879147ed3ff4ea3fb1bc709449ee4b774d Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 21:36:35 +0100 Subject: [PATCH 5/5] style(msr): address cosmetic changes --- src/registers/model_specific.rs | 42 +++++++++++++-------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index c6b299e3..f8dbb71f 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -72,8 +72,10 @@ pub struct SCet; pub struct Pat; /// IA32_APIC_BASE: status and location of the local APIC +/// +/// IA32_APIC_BASE must be supported on the CPU, otherwise, a general protection exception will occur. Support can be detected using the `cpuid` instruction. #[derive(Debug)] -pub struct Apic; +pub struct ApicBase; impl Efer { /// The underlying model specific register. @@ -136,7 +138,7 @@ impl Pat { ]; } -impl Apic { +impl ApicBase { /// The underlying model specific register. pub const MSR: Msr = Msr(0x1B); } @@ -231,7 +233,7 @@ bitflags! { /// Flags for the Advanced Programmable Interrupt Controler Base Register. #[repr(transparent)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] - pub struct ApicFlags: u64 { + pub struct ApicBaseFlags: u64 { // bits 0 - 7 are reserved. /// Indicates whether the current processor is the bootstrap processor const BSP = 1 << 8; @@ -754,44 +756,35 @@ mod x86_64 { } } - impl Apic { - /// Reads the IA32_APIC_BASE. - /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. + impl ApicBase { + /// Reads the IA32_APIC_BASE MSR. #[inline] - pub fn read() -> (PhysFrame, ApicFlags) { + pub fn read() -> (PhysFrame, ApicBaseFlags) { let (frame, flags) = Self::read_raw(); - (frame, ApicFlags::from_bits_truncate(flags)) + (frame, ApicBaseFlags::from_bits_truncate(flags)) } - /// Reads the raw IA32_APIC_BASE. - /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. + /// Reads the raw IA32_APIC_BASE MSR. #[inline] pub fn read_raw() -> (PhysFrame, u64) { let raw = unsafe { Self::MSR.read() }; - // extract bits 32 - 51 (incl.) - let addr = PhysAddr::new(raw & 0x_000F_FFFF_FFFF_F000); + // extract bits 12 - 51 (incl.) + let addr = PhysAddr::new_truncate(raw); let frame = PhysFrame::containing_address(addr); (frame, raw) } - /// Writes the IA32_APIC_BASE preserving reserved values. + /// Writes the IA32_APIC_BASE MSR preserving reserved values. /// /// Preserves the value of reserved fields. /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. - /// /// ## Safety /// /// Unsafe because changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules. #[inline] - pub unsafe fn write(frame: PhysFrame, flags: ApicFlags) { + pub unsafe fn write(frame: PhysFrame, flags: ApicBaseFlags) { let (_, old_flags) = Self::read_raw(); - let reserved = old_flags & !(ApicFlags::all().bits()); + let reserved = old_flags & !(ApicBaseFlags::all().bits()); let new_flags = reserved | flags.bits(); unsafe { @@ -799,13 +792,10 @@ mod x86_64 { } } - /// Writes the IA32_APIC_BASE flags. + /// Writes the IA32_APIC_BASE MSR flags. /// /// Does not preserve any bits, including reserved fields. /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. - /// /// ## Safety /// /// Unsafe because it's possible to set reserved bits to `1` and changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules.