x86_64: fix FADT field offsets and harden GS base around early boot
- fadt.rs was using sdt.data_address() (after the 36-byte SDT header) with absolute ACPI table offsets, causing PM1a_CNT/STS and FACS addresses to be read 36 bytes past their real location. Under KVM this produced a garbage x_firmware_ctrl value (0x604) and a page fault inside acpi::init. Use the SDT start address instead. - Load GS with the kernel data selector in install_pcr instead of a NULL selector, avoiding undefined GSBASE behavior on KVM. - Add GSBASE diagnostics through start.rs, acpi::init, and tsc::init to catch future GS base corruption early. Author: vasilito <adminpupkin@gmail.com>
This commit is contained in:
+4
-1
@@ -77,7 +77,10 @@ pub fn init(sdt: &Sdt) {
|
|||||||
// offsets (per the ACPI spec); reading them as u32/u64 is
|
// offsets (per the ACPI spec); reading them as u32/u64 is
|
||||||
// safe because all of them are at 4-byte or 8-byte aligned
|
// safe because all of them are at 4-byte or 8-byte aligned
|
||||||
// offsets on x86_64.
|
// offsets on x86_64.
|
||||||
let data = sdt.data_address() as *const u8;
|
// Use the SDT start address, not data_address() (which points past the
|
||||||
|
// 36-byte SDT header). The offsets below are absolute offsets from the
|
||||||
|
// start of the FADT table per ACPI 6.5 §5.2.9.
|
||||||
|
let data = sdt as *const _ as *const u8;
|
||||||
unsafe {
|
unsafe {
|
||||||
// PM1a_CNT is at offset 56 in the FADT (ACPI 6.5 §5.2.9
|
// PM1a_CNT is at offset 56 in the FADT (ACPI 6.5 §5.2.9
|
||||||
// Table 5.6). 32-bit General-Purpose Event Register Block 0
|
// Table 5.6). 32-bit General-Purpose Event Register Block 0
|
||||||
|
|||||||
@@ -150,25 +150,31 @@ pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
|
|||||||
|
|
||||||
// TODO: Enumerate processors in userspace, and then provide an ACPI-independent interface
|
// TODO: Enumerate processors in userspace, and then provide an ACPI-independent interface
|
||||||
// to initialize enumerated processors to userspace?
|
// to initialize enumerated processors to userspace?
|
||||||
|
info!("GSBASE before Madt::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
Madt::init();
|
Madt::init();
|
||||||
|
info!("GSBASE after Madt::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
//TODO: support this on any arch
|
//TODO: support this on any arch
|
||||||
// SPCR must be initialized after MADT for interrupt controllers
|
// SPCR must be initialized after MADT for interrupt controllers
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
spcr::Spcr::init();
|
spcr::Spcr::init();
|
||||||
// TODO: Let userspace setup HPET, and then provide an interface to specify which timer to
|
// TODO: Let userspace setup HPET, and then provide an interface to specify which timer to
|
||||||
// use?
|
// use?
|
||||||
|
info!("GSBASE before Hpet::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
Hpet::init();
|
Hpet::init();
|
||||||
|
info!("GSBASE after Hpet::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
gtdt::Gtdt::init();
|
gtdt::Gtdt::init();
|
||||||
// Phase II: parse the FADT to extract the PM1a_CNT
|
// Phase II: parse the FADT to extract the PM1a_CNT
|
||||||
// and PM1a_STS port addresses used by the S3 entry
|
// and PM1a_STS port addresses used by the S3 entry
|
||||||
// path. Hardware-agnostic — works on any platform
|
// path. Hardware-agnostic — works on any platform
|
||||||
// with a working FADT.
|
// with a working FADT.
|
||||||
|
info!("GSBASE before fadt::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
if let Some(fadt_sdts) = find_sdt("FACP").first() {
|
if let Some(fadt_sdts) = find_sdt("FACP").first() {
|
||||||
fadt::init(fadt_sdts);
|
fadt::init(fadt_sdts);
|
||||||
} else {
|
} else {
|
||||||
warn!("ACPI: no FADT (FACP) found, S3 entry path disabled");
|
warn!("ACPI: no FADT (FACP) found, S3 entry path disabled");
|
||||||
}
|
}
|
||||||
|
info!("GSBASE after fadt::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
// Phase II.X.W: parse the FACS to extract the
|
// Phase II.X.W: parse the FACS to extract the
|
||||||
// xfirmware_waking_vector. This is the address the
|
// xfirmware_waking_vector. This is the address the
|
||||||
// platform firmware jumps to on S3 wake. The kernel's
|
// platform firmware jumps to on S3 wake. The kernel's
|
||||||
@@ -180,7 +186,9 @@ pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
|
|||||||
// field (64-bit) or firmware_ctrl field (32-bit).
|
// field (64-bit) or firmware_ctrl field (32-bit).
|
||||||
// The FADT parser caches the FACS address. We use
|
// The FADT parser caches the FACS address. We use
|
||||||
// the FADT's x_firmware_ctrl to find the FACS SDT.
|
// the FADT's x_firmware_ctrl to find the FACS SDT.
|
||||||
|
info!("GSBASE before facs::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
let facs_addr = fadt::x_firmware_ctrl();
|
let facs_addr = fadt::x_firmware_ctrl();
|
||||||
|
info!("FACS x_firmware_ctrl = {:#x}", facs_addr);
|
||||||
if facs_addr != 0 {
|
if facs_addr != 0 {
|
||||||
// SAFETY: The FACS address is a physical
|
// SAFETY: The FACS address is a physical
|
||||||
// address stored in the FADT. The boot-time page
|
// address stored in the FADT. The boot-time page
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ pub unsafe fn init() -> bool {
|
|||||||
.supp_feats
|
.supp_feats
|
||||||
.contains(KvmFeatureBits::CLOCKSOURCE2 | KvmFeatureBits::CLOCKSOURCE_STABLE)
|
.contains(KvmFeatureBits::CLOCKSOURCE2 | KvmFeatureBits::CLOCKSOURCE_STABLE)
|
||||||
{
|
{
|
||||||
|
let gsbase = x86::msr::rdmsr(x86::msr::IA32_GS_BASE);
|
||||||
|
debug!("tsc::init: IA32_GS_BASE = {:#x}", gsbase);
|
||||||
|
if gsbase == 0 {
|
||||||
|
debug!("tsc::init: GSBASE is zero, skipping KVM TSC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let frame = allocate_frame().expect("failed to allocate timer page");
|
let frame = allocate_frame().expect("failed to allocate timer page");
|
||||||
x86::msr::wrmsr(MSR_KVM_SYSTEM_TIME_NEW, (frame.base().data() as u64) | 1);
|
x86::msr::wrmsr(MSR_KVM_SYSTEM_TIME_NEW, (frame.base().data() as u64) | 1);
|
||||||
let ptr = crate::memory::RmmA::phys_to_virt(frame.base()).data()
|
let ptr = crate::memory::RmmA::phys_to_virt(frame.base()).data()
|
||||||
|
|||||||
@@ -332,9 +332,10 @@ pub unsafe fn install_pcr(pcr_ptr: *mut ProcessorControlRegion) {
|
|||||||
segmentation::load_es(SegmentSelector::from_raw(0));
|
segmentation::load_es(SegmentSelector::from_raw(0));
|
||||||
segmentation::load_fs(SegmentSelector::from_raw(0));
|
segmentation::load_fs(SegmentSelector::from_raw(0));
|
||||||
|
|
||||||
// What happens when GS is loaded with a NULL selector, is undefined on Intel CPUs. However,
|
// Loading GS with a NULL selector leaves the resulting GSBASE undefined on some CPUs and
|
||||||
// GSBASE is set later.
|
// hypervisors (e.g. KVM). Use the kernel data selector so the segment is present, then set
|
||||||
segmentation::load_gs(SegmentSelector::from_raw(0));
|
// the actual base via IA32_GS_BASE.
|
||||||
|
segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
||||||
|
|
||||||
// Ensure that GSBASE always points to the PCR in kernel space.
|
// Ensure that GSBASE always points to the PCR in kernel space.
|
||||||
x86::msr::wrmsr(x86::msr::IA32_GS_BASE, pcr as *mut _ as usize as u64);
|
x86::msr::wrmsr(x86::msr::IA32_GS_BASE, pcr as *mut _ as usize as u64);
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
|||||||
|
|
||||||
// Set up GDT
|
// Set up GDT
|
||||||
gdt::init_bsp(stack_end);
|
gdt::init_bsp(stack_end);
|
||||||
|
info!("GSBASE after init_bsp: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
|
|
||||||
// Set up IDT
|
// Set up IDT
|
||||||
idt::init_bsp();
|
idt::init_bsp();
|
||||||
@@ -108,12 +109,15 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
|||||||
|
|
||||||
// Initialize paging
|
// Initialize paging
|
||||||
paging::init();
|
paging::init();
|
||||||
|
info!("GSBASE after paging::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
crate::arch::alternative::early_init(true);
|
crate::arch::alternative::early_init(true);
|
||||||
|
info!("GSBASE after alternative::early_init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
|
|
||||||
// Set up syscall instruction
|
// Set up syscall instruction
|
||||||
interrupt::syscall::init();
|
interrupt::syscall::init();
|
||||||
|
info!("GSBASE after syscall::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
|
|
||||||
// Setup kernel heap
|
// Setup kernel heap
|
||||||
allocator::init();
|
allocator::init();
|
||||||
@@ -124,17 +128,22 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
|||||||
// Initialize miscellaneous processor features
|
// Initialize miscellaneous processor features
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
crate::arch::misc::init(LogicalCpuId::BSP);
|
crate::arch::misc::init(LogicalCpuId::BSP);
|
||||||
|
info!("GSBASE after misc::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
|
|
||||||
// Initialize devices
|
// Initialize devices
|
||||||
device::init();
|
device::init();
|
||||||
|
info!("GSBASE after device::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
|
|
||||||
// Read ACPI tables, starts APs
|
// Read ACPI tables, starts APs
|
||||||
if cfg!(feature = "acpi") {
|
if cfg!(feature = "acpi") {
|
||||||
|
info!("GSBASE before acpi::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
crate::acpi::init(args.acpi_rsdp());
|
crate::acpi::init(args.acpi_rsdp());
|
||||||
|
info!("GSBASE after acpi::init: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
device::init_after_acpi();
|
device::init_after_acpi();
|
||||||
}
|
}
|
||||||
crate::profiling::init();
|
crate::profiling::init();
|
||||||
|
|
||||||
|
info!("GSBASE before init_noncore: {:#x}", x86::msr::rdmsr(x86::msr::IA32_GS_BASE));
|
||||||
// Initialize all of the non-core devices not otherwise needed to complete initialization
|
// Initialize all of the non-core devices not otherwise needed to complete initialization
|
||||||
device::init_noncore();
|
device::init_noncore();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user