From 02b7878229b605a25ea0e21cc5e3e4e2d300b84e Mon Sep 17 00:00:00 2001 From: Benjamin Lamowski Date: Tue, 21 Jan 2025 16:26:52 +0100 Subject: [PATCH] hw: always serialize rdtsc reads While implementing TSC calibration in #5215, the issue of properly serializing TSC reads came up. Some learnings of the discussion were noted in #5430. Using `cpuid` for serialization as in Trace::timestamp() is portable, but will cause VM exits on VMX and SVM and is therefore unsuitable to retain a roughly working calibration loop while running virtualized. On the other hand on most AMD systems, dispatch serializing `lfence` needs to be explicitly enabled via a non-architectural MSR. Enable setting up dispatch serializing lfence on AMD systems and always serialize rdtsc accesses in Hw::Tsc::rdtsc() for maximum reliability. Issues #5215, #5430 --- .../src/bootstrap/spec/x86_64/platform.cc | 44 ++++++++++++++++++- .../base-hw/src/include/hw/spec/x86_64/cpu.h | 6 +++ .../src/include/hw/spec/x86_64/x86_64.h | 14 +++++- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc b/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc index 77aeec6e5b..8ec2e444ac 100644 --- a/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc +++ b/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc @@ -131,7 +131,7 @@ static void disable_pit() PIT_MODE = 0x43, }; - /** + /* * Disable PIT timer channel. This is necessary since BIOS sets up * channel 0 to fire periodically. */ @@ -141,6 +141,35 @@ static void disable_pit() } +/* + * Enable dispatch serializing lfence instruction on AMD processors + * + * See Software techniques for managing speculation on AMD processors + * Revision 5.09.23 + * Mitigation G-2 + */ +static void amd_enable_serializing_lfence() +{ + using Cpu = Hw::X86_64_cpu; + + if (Hw::Vendor::get_vendor_id() != Hw::Vendor::Vendor_id::AMD) + return; + + unsigned const family = Hw::Vendor::get_family(); + + /* + * In family 0Fh and 11h, lfence is always dispatch serializing and + * "AMD plans support for this MSR and access to this bit for all future + * processors." from family 14h on. + */ + if ((family == 0x10) || (family == 0x12) || (family >= 0x14)) { + Cpu::Amd_lfence::access_t amd_lfence = Cpu::Amd_lfence::read(); + Cpu::Amd_lfence::Enable_dispatch_serializing::set(amd_lfence); + Cpu::Amd_lfence::write(amd_lfence); + } +} + + Bootstrap::Platform::Board::Board() : core_mmio(Memory_region { 0, 0x1000 }, @@ -325,6 +354,14 @@ Bootstrap::Platform::Board::Board() cpus = !cpus ? 1 : max_cpus; } + /* + * Enable serializing lfence on supported AMD processors + * + * For APs this will be set up later, but we need it already to obtain + * the most acurate results when calibrating the TSC frequency. + */ + amd_enable_serializing_lfence(); + auto r = calibrate_lapic_frequency(info.acpi_fadt); info.lapic_freq_khz = r.freq_khz; info.lapic_div = r.div; @@ -401,9 +438,12 @@ unsigned Bootstrap::Platform::enable_mmu() if (board.cpus <= 1) return (unsigned)cpu_id; - if (!Cpu::IA32_apic_base::Bsp::get(lapic_msr)) + if (!Cpu::IA32_apic_base::Bsp::get(lapic_msr)) { /* AP - done */ + /* enable serializing lfence on supported AMD processors. */ + amd_enable_serializing_lfence(); return (unsigned)cpu_id; + } /* BSP - we're primary CPU - wake now all other CPUs */ diff --git a/repos/base-hw/src/include/hw/spec/x86_64/cpu.h b/repos/base-hw/src/include/hw/spec/x86_64/cpu.h index 324f37af67..f538c9e65d 100644 --- a/repos/base-hw/src/include/hw/spec/x86_64/cpu.h +++ b/repos/base-hw/src/include/hw/spec/x86_64/cpu.h @@ -118,6 +118,12 @@ struct Hw::X86_64_cpu /* AMD host save physical address */ X86_64_MSR_REGISTER(Amd_vm_hsavepa, 0xC0010117); + + /* Non-architectural MSR used to make lfence serializing */ + X86_64_MSR_REGISTER(Amd_lfence, 0xC0011029, + struct Enable_dispatch_serializing : Bitfield<1, 1> { }; /* Enable lfence dispatch serializing */ + ) + X86_64_MSR_REGISTER(Platform_id, 0x17, struct Bus_ratio : Bitfield<8, 5> { }; /* Bus ratio on Core 2, see SDM 19.7.3 */ ); diff --git a/repos/base-hw/src/include/hw/spec/x86_64/x86_64.h b/repos/base-hw/src/include/hw/spec/x86_64/x86_64.h index c451fac22c..6bca48fe9a 100644 --- a/repos/base-hw/src/include/hw/spec/x86_64/x86_64.h +++ b/repos/base-hw/src/include/hw/spec/x86_64/x86_64.h @@ -109,10 +109,22 @@ public: struct Hw::Tsc { + /* + * Provide serialized access to the Timestamp Counter + * + * See #5430 for more information. + */ static Genode::uint64_t rdtsc() { Genode::uint32_t low, high; - asm volatile("rdtsc" : "=a"(low), "=d"(high)); + asm volatile( + "lfence;" + "rdtsc;" + "lfence;" + : "=a"(low), "=d"(high) + : + : "memory" + ); return (Genode::uint64_t)(high) << 32 | low; }