base-hw: export Intel TSC platform information

Export TSC invariant and the frequency reported on Intel processors.
Currently lacks TSC calibration and reports a fixed frequency on AMD.

Ref #4826
This commit is contained in:
Benjamin Lamowski 2023-01-13 14:31:12 +01:00 committed by Christian Helmuth
parent c75b07e0fd
commit 312b638220
3 changed files with 192 additions and 2 deletions

View File

@ -52,11 +52,15 @@ void Platform::_init_additional_platform_info(Xml_generator &xml)
xml.attribute("pitch", boot_fb.pitch);
});
});
xml.node("hardware", [&] () {
xml.node("hardware", [&]() {
xml.node("features", [&] () {
xml.attribute("svm", Hw::Virtualization_support::has_svm());
xml.attribute("vmx", false);
});
xml.node("tsc", [&]() {
xml.attribute("invariant", Hw::Lapic::invariant_tsc());
xml.attribute("freq_khz", Hw::Lapic::tsc_freq());
});
});
}

View File

@ -117,6 +117,17 @@ struct Hw::X86_64_cpu
/* AMD host save physical address */
X86_64_MSR_REGISTER(Amd_vm_hsavepa, 0xC0010117);
X86_64_MSR_REGISTER(Platform_id, 0x17,
struct Bus_ratio : Bitfield<8, 5> { }; /* Bus ratio on Core 2, see SDM 19.7.3 */
);
X86_64_MSR_REGISTER(Platform_info, 0xCE,
struct Ratio : Bitfield< 8, 8> { }; /* Maximum Non-Turbo Ratio (R/O) */
);
X86_64_MSR_REGISTER(Fsb_freq, 0xCD,
struct Speed : Bitfield< 0, 3> { }; /* Scaleable Bus Speed (R/O) */
);
X86_64_MSR_REGISTER(Ia32_efer, 0xC0000080,
struct Svme : Bitfield< 12, 1> { }; /* Secure Virtual Machine Enable */
@ -129,10 +140,17 @@ struct Hw::X86_64_cpu
*/
X86_64_MSR_REGISTER(Ia32_tsc_aux, 0xc0000103);
X86_64_CPUID_REGISTER(Cpuid_0_eax, 0, eax);
X86_64_CPUID_REGISTER(Cpuid_0_ebx, 0, ebx);
X86_64_CPUID_REGISTER(Cpuid_0_ecx, 0, ecx);
X86_64_CPUID_REGISTER(Cpuid_0_edx, 0, edx);
X86_64_CPUID_REGISTER(Cpuid_1_eax, 1, eax);
X86_64_CPUID_REGISTER(Cpuid_1_ecx, 1, ecx,
struct Tsc_deadline : Bitfield<24, 1> { };
);
X86_64_CPUID_REGISTER(Cpuid_1_edx, 1, edx,
struct Pat : Bitfield<16, 1> { };
);
@ -140,10 +158,18 @@ struct Hw::X86_64_cpu
/* Number of address space identifiers (ASID) */
X86_64_CPUID_REGISTER(Amd_nasid, 0x8000000A, ebx);
X86_64_CPUID_REGISTER(Cpuid_15_eax, 15, eax);
X86_64_CPUID_REGISTER(Cpuid_15_ebx, 15, ebx);
X86_64_CPUID_REGISTER(Cpuid_15_ecx, 15, ecx);
X86_64_CPUID_REGISTER(Cpuid_8000000A_edx, 0x8000000A, edx,
struct Np : Bitfield<0, 1> { }; /* Nested paging */
);
X86_64_CPUID_REGISTER(Cpuid_80000007_eax, 0x80000007, eax,
struct Invariant_tsc : Bitfield<2, 1> { }; /* Invariant TSC */
);
X86_64_CPUID_REGISTER(Cpuid_80000001_ecx, 0x80000001, ecx,
struct Svm : Bitfield<2, 1> { };
);

View File

@ -22,6 +22,7 @@ namespace Hw {
struct Cpu_memory_map;
struct Virtualization_support;
class Vendor;
class Lapic;
}
@ -40,7 +41,7 @@ struct Hw::Cpu_memory_map
}
};
struct Hw::Vendor
class Hw::Vendor
{
private:
static constexpr char const *const vendor_string[] = {
@ -85,6 +86,165 @@ public:
return Vendor_id(v);
}
static unsigned get_family()
{
using Cpu = Hw::X86_64_cpu;
Cpu::Cpuid_1_eax::access_t eax = Cpu::Cpuid_1_eax::read();
return ((eax >> 8 & 0xf) + (eax >> 20 & 0xff)) & 0xff;
}
static unsigned get_model()
{
using Cpu = Hw::X86_64_cpu;
Cpu::Cpuid_1_eax::access_t eax = Cpu::Cpuid_1_eax::read();
return ((eax >> 4 & 0xf) + (eax >> 12 & 0xf0)) & 0xff;
}
};
class Hw::Lapic
{
private:
static bool _has_tsc_dl()
{
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);
}
/*
* Adapted from Christian Prochaska's and Alexander Boettcher's
* implementation for Nova.
*
* For details, see Vol. 3A of the Intel SDM:
* 19.7.3 Determining the Processor Base Frequency
*/
static unsigned _read_tsc_freq()
{
using Cpu = Hw::X86_64_cpu;
unsigned freq_tsc = 0U;
if (Vendor::get_vendor_id() != Vendor::INTEL)
return 0;
unsigned const model = Vendor::get_model();
unsigned const family = Vendor::get_family();
enum
{
Cpu_id_clock = 0x15
};
Cpu::Cpuid_0_eax::access_t eax = Cpu::Cpuid_0_eax::read();
if (eax >= Cpu_id_clock) {
Cpu::Cpuid_15_eax::access_t eax = Cpu::Cpuid_15_eax::read();
Cpu::Cpuid_15_ebx::access_t ebx = Cpu::Cpuid_15_ebx::read();
Cpu::Cpuid_15_ecx::access_t ecx = Cpu::Cpuid_15_ecx::read();
if (eax && ebx) {
if (ecx)
return static_cast<unsigned>(
((Genode::uint64_t)(ecx) * ebx) / eax / 1000
);
if (family == 6) {
if (model == 0x5c) /* Goldmont */
freq_tsc = static_cast<unsigned>((19200ull * ebx) / eax);
if (model == 0x55) /* Xeon */
freq_tsc = static_cast<unsigned>((25000ull * ebx) / eax);
}
if (!freq_tsc && family >= 6)
freq_tsc = static_cast<unsigned>((24000ull * ebx) / eax);
if (freq_tsc)
return freq_tsc;
}
if (family != 6)
return 0;
if (model == 0x2a ||
model == 0x2d || /* Sandy Bridge */
model >= 0x3a) /* Ivy Bridge and later */
{
Cpu::Platform_info::access_t platform_info = Cpu::Platform_info::read();
Genode::uint64_t ratio = Cpu::Platform_info::Ratio::get(platform_info);
freq_tsc = static_cast<unsigned>(ratio * 100000);
} else if (model == 0x1a ||
model == 0x1e ||
model == 0x1f ||
model == 0x2e || /* Nehalem */
model == 0x25 ||
model == 0x2c ||
model == 0x2f) /* Xeon Westmere */
{
Cpu::Platform_info::access_t platform_info = Cpu::Platform_info::read();
Genode::uint64_t ratio = Cpu::Platform_info::Ratio::get(platform_info);
freq_tsc = static_cast<unsigned>(ratio * 133330);
} else if (model == 0x17 || model == 0xf) { /* Core 2 */
Cpu::Fsb_freq::access_t fsb_freq = Cpu::Fsb_freq::read();
Genode::uint64_t freq_bus = Cpu::Fsb_freq::Speed::get(fsb_freq);
switch (freq_bus) {
case 0b101: freq_bus = 100000; break;
case 0b001: freq_bus = 133330; break;
case 0b011: freq_bus = 166670; break;
case 0b010: freq_bus = 200000; break;
case 0b000: freq_bus = 266670; break;
case 0b100: freq_bus = 333330; break;
case 0b110: freq_bus = 400000; break;
default: freq_bus = 0; break;
}
Cpu::Platform_id::access_t platform_id = Cpu::Platform_id::read();
Genode::uint64_t ratio = Cpu::Platform_id::Bus_ratio::get(platform_id);
freq_tsc = static_cast<unsigned>(freq_bus * ratio);
}
}
return freq_tsc;
}
static unsigned _measure_tsc_freq()
{
Genode::warning("TSC calibration not yet implemented, using fixed value");
/* TODO: implement TSC calibration on AMD */
return 1000000U;
}
public:
static Genode::uint64_t rdtsc()
{
Genode::uint32_t low, high;
asm volatile("rdtsc" : "=a"(low), "=d"(high));
return (Genode::uint64_t)(high) << 32 | low;
}
static bool invariant_tsc()
{
using Cpu = Hw::X86_64_cpu;
Cpu::Cpuid_80000007_eax::access_t eax =
Cpu::Cpuid_80000007_eax::read();
return Cpu::Cpuid_80000007_eax::Invariant_tsc::get(eax);
}
static unsigned tsc_freq()
{
unsigned freq = _read_tsc_freq();
if (freq)
return freq;
else
return _measure_tsc_freq();
}
};
struct Hw::Virtualization_support