-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
To get the Time Stamp Counter's frequency, hw relied on a complex and incomplete algorithm. Since this is a one-time initialization issue, move TSC calibration to bootstrap and implement it using the ACPI timer. Issue genodelabs#5215
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
#include <hw/memory_consts.h> | ||
#include <hw/spec/x86_64/acpi.h> | ||
#include <hw/spec/x86_64/apic.h> | ||
#include <hw/spec/x86_64/x86_64.h> | ||
|
||
using namespace Genode; | ||
|
||
|
@@ -66,6 +67,34 @@ static Hw::Acpi_rsdp search_rsdp(addr_t area, addr_t area_size) | |
} | ||
|
||
|
||
static uint32_t calibrate_tsc_frequency(addr_t fadt_addr) | ||
{ | ||
const unsigned Tsc_fixed_value = 2400; | ||
uint32_t default_freq = Tsc_fixed_value * 1000; | ||
|
||
uint32_t const sleep_ms = 10; | ||
|
||
if (!fadt_addr) { | ||
Genode::error("FADT not found, returning default TSC value"); | ||
This comment has been minimized.
Sorry, something went wrong. |
||
return default_freq; | ||
} | ||
|
||
Hw::Acpi_fadt fadt(reinterpret_cast<Hw::Acpi_generic *>(fadt_addr)); | ||
|
||
uint32_t val = fadt.calibrate_freq_khz(sleep_ms, []() { | ||
return Hw::Tsc::rdtsc(); | ||
}); | ||
|
||
if (!val) | ||
{ | ||
This comment has been minimized.
Sorry, something went wrong.
chelmuth
|
||
Genode::error("Unable to calibrate TSC, returning default value"); | ||
return default_freq; | ||
} | ||
|
||
return val; | ||
} | ||
|
||
|
||
Bootstrap::Platform::Board::Board() | ||
: | ||
core_mmio(Memory_region { 0, 0x1000 }, | ||
|
@@ -250,6 +279,8 @@ Bootstrap::Platform::Board::Board() | |
cpus = !cpus ? 1 : max_cpus; | ||
} | ||
|
||
info.tsc_frequency = calibrate_tsc_frequency(info.acpi_fadt); | ||
|
||
/* copy 16 bit boot code for AP CPUs and for ACPI resume */ | ||
addr_t ap_code_size = (addr_t)&_start - (addr_t)&_ap; | ||
memcpy((void *)AP_BOOT_CODE_PAGE, &_ap, ap_code_size); | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -22,8 +22,8 @@ | |||
namespace Hw { | ||||
struct Cpu_memory_map; | ||||
struct Virtualization_support; | ||||
class Vendor; | ||||
class Lapic; | ||||
class Vendor; | ||||
struct Tsc; | ||||
} | ||||
|
||||
|
||||
|
@@ -107,172 +107,23 @@ class Hw::Vendor | |||
}; | ||||
|
||||
|
||||
class Hw::Lapic | ||||
struct Hw::Tsc | ||||
{ | ||||
private: | ||||
static bool _has_tsc_dl() | ||||
static Genode::uint64_t rdtsc() | ||||
{ | ||||
using Cpu = Hw::X86_64_cpu; | ||||
|
||||
Cpu::Cpuid_1_ecx::access_t ecx = Cpu::Cpuid_1_ecx::read(); | ||||
return (bool)Cpu::Cpuid_1_ecx::Tsc_deadline::get(ecx); | ||||
Genode::uint32_t low, high; | ||||
asm volatile("rdtsc" : "=a"(low), "=d"(high)); | ||||
return (Genode::uint64_t)(high) << 32 | low; | ||||
} | ||||
This comment has been minimized.
Sorry, something went wrong.
chelmuth
|
while ((read_pm_tmr() - initial) < (acpi_timer_freq * sleep_ms / 1000)) |
will produce different values depending on the cycle count of the
pause
instruction, I wonder if this is significant for this use case?
For the use case of passing the TSC value into the VMM via Vcpu_state
, I would argue that the lack of serialization does not matter because the value that is passed up to the VMM is fairly imprecise anyway.
If anyone disagrees with this analysis I am happy to add proper serialization.
From https://stackoverflow.com/questions/51844886/is-lfence-serializing-on-amd-processors and https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=be261ffce6f13229dad50f59c5e491f933d3167f it appears that lfence
might not be serializing on AMD, whereas cpuid
could taint the measurement with a VM exit when running hw
in a virtualized environment.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
chelmuth
Jan 21, 2025
I comprehend the combination of pause and rdtsc in the calibration-loop use case. On the other hand, the rdtsc() function (like Trace::timestamp()) appears like an independent function that it is not because of the serialization requirement. We should at least add a bold note in the documentation of the function how to use it in other use cases. Or maybe just add the lfence?
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
atopia
Jan 21, 2025
Author
Owner
I have summed up the discussion in genodelabs#5430 and after consideration decided to set up dispatch serializing lfence
on AMD processors and serialize rdtsc
access in d904299. While I stand by my interpretation that pause
should serialize the second rdtsc
, the potential out of order execution of the first rdtsc
at the start of the calibration loop and having to add a bunch of warning comments didn't seem the right way to go.
4 comments
on commit 272e77a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you tested this implementation on x260 and your Nano, but how does the calibration affects test runs in Qemu on systems with varying loads? In the past we at least had issues with the TSC calibration loop of the Fiasco kernel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late reply, comments on my clone of the repo keep going to my private email address and I haven't found a per-repo setting to fix this (I tried some months ago and now I get notifications on repo pushes to @genode-labs.com but still not for comments).
Anyway, I had tested the implementation on the x260. For reasons I don't have the means to debug right now, even without the PIT loop, hw does not boot on the X1 Nano Gen 2.
When running the calibration in Qemu alongside artificial load, in maybe 10-20% of the tests the TSC frequency measured inside Qemu goes up significantly, nearly up to 25% (e.g. 1996968kHz vs. 2477338kHz).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late reply, comments on my clone of the repo keep going to my private email address and I haven't found a per-repo setting to fix this (I tried some months ago and now I get notifications on repo pushes to @genode-labs.com but still not for comments).
There exists a simple and reliable fix: Use only the @genode-labs.com email address for your GitHub genodelabs collaboration account and, thus, clearly distinguish this collaboration from private matters. In my opinion there's no room left for discussing this matter further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, I had tested the implementation on the x260. For reasons I don't have the means to debug right now, even without the PIT loop, hw does not boot on the X1 Nano Gen 2.
Looking forward to future findings and will test on Tigerlake myself once the commits entered staging.
When running the calibration in Qemu alongside artificial load, in maybe 10-20% of the tests the TSC frequency measured inside Qemu goes up significantly, nearly up to 25% (e.g. 1996968kHz vs. 2477338kHz).
As long as we stay with limited accuracy and nothing just hangs, I'd be okay with the results.
For more clarity, I suggest to change the error message to a warning like follows. (Maybe move before line 75?)