mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-15 23:17:14 +00:00
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:
parent
c75b07e0fd
commit
312b638220
@ -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());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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> { };
|
||||
);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user