veridian_kernel/arch/x86_64/msr.rs
1//! Model-Specific Register (MSR) read/write primitives.
2//!
3//! Extracted from `apic.rs` so that other modules (e.g. `pat.rs`) can
4//! access MSRs without duplicating inline assembly.
5
6/// Read a 64-bit Model-Specific Register.
7pub fn rdmsr(msr: u32) -> u64 {
8 let (low, high): (u32, u32);
9 // SAFETY: RDMSR reads the MSR specified by ECX. The caller passes a
10 // well-known MSR address. This is a privileged, read-only operation
11 // with no side effects beyond returning the MSR value.
12 unsafe {
13 core::arch::asm!(
14 "rdmsr",
15 in("ecx") msr,
16 out("eax") low,
17 out("edx") high,
18 options(nomem, nostack, preserves_flags),
19 );
20 }
21 (low as u64) | ((high as u64) << 32)
22}
23
24/// Write a 64-bit value to a Model-Specific Register.
25pub fn wrmsr(msr: u32, value: u64) {
26 let low = value as u32;
27 let high = (value >> 32) as u32;
28 // SAFETY: WRMSR writes to the MSR specified by ECX. The caller passes a
29 // well-known MSR address and a valid value. This is a privileged operation
30 // that modifies CPU configuration.
31 unsafe {
32 core::arch::asm!(
33 "wrmsr",
34 in("ecx") msr,
35 in("eax") low,
36 in("edx") high,
37 options(nomem, nostack, preserves_flags),
38 );
39 }
40}
41
42/// Translate a physical address to its virtual address using the
43/// bootloader's physical memory mapping.
44///
45/// The bootloader maps all physical memory at a dynamic offset in the
46/// higher-half virtual address space. MMIO regions like the Local APIC
47/// (0xFEE0_0000) and I/O APIC (0xFEC0_0000) are not identity-mapped,
48/// so we must add the physical memory offset to access them.
49///
50/// Returns `None` if boot info or the physical memory offset is unavailable.
51pub fn phys_to_virt(phys: usize) -> Option<usize> {
52 // SAFETY: BOOT_INFO is a static mut written once during early boot
53 // (before any concurrency) and read-only afterwards. We are in
54 // single-threaded kernel init context.
55 #[allow(static_mut_refs)]
56 let boot_info = unsafe { crate::arch::x86_64::boot::BOOT_INFO.as_ref()? };
57 let offset = boot_info.physical_memory_offset.into_option()?;
58 Some(offset as usize + phys)
59}