From Jérôme Duval <jerome.duval@xxxxxxxxx>:
Jérôme Duval has uploaded this change for review. (
https://review.haiku-os.org/c/haiku/+/2264 ;)
Change subject: kernel: load cpu microcode update if loaded by the bootloader
......................................................................
kernel: load cpu microcode update if loaded by the bootloader
---
M headers/private/kernel/arch/x86/arch_cpu.h
M src/system/kernel/arch/x86/arch_cpu.cpp
2 files changed, 224 insertions(+), 0 deletions(-)
git pull ssh://git.haiku-os.org:22/haiku refs/changes/64/2264/1
diff --git a/headers/private/kernel/arch/x86/arch_cpu.h
b/headers/private/kernel/arch/x86/arch_cpu.h
index 69d849d..0aff703 100644
--- a/headers/private/kernel/arch/x86/arch_cpu.h
+++ b/headers/private/kernel/arch/x86/arch_cpu.h
@@ -34,9 +34,12 @@
// MSR registers (possibly Intel specific)
#define IA32_MSR_TSC 0x10
+#define IA32_MSR_PLATFORM_ID 0x17
#define IA32_MSR_APIC_BASE 0x1b
#define IA32_MSR_SPEC_CTRL 0x48
#define IA32_MSR_PRED_CMD 0x49
+#define IA32_MSR_UCODE_WRITE 0x79 // IA32_BIOS_UPDT_TRIG
+#define IA32_MSR_UCODE_REV 0x8b //
IA32_BIOS_SIGN_ID
#define IA32_MSR_PLATFORM_INFO 0xce
#define IA32_MSR_MPERF 0xe7
#define IA32_MSR_APERF 0xe8
@@ -430,6 +433,7 @@
int stepping;
int model;
int extended_model;
+ uint32 patch_level;
uint32 logical_apic_id;
@@ -447,6 +451,37 @@
} arch_cpu_info;
+// Reference Intel SDM Volume 3 9.11 "Microcode Update Facilities"
+//
https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf
+// 9.11.1 Table 9-7. Microcode Update Field Definitions
+struct intel_microcode_header {
+ uint32 header_version;
+ uint32 update_revision;
+ uint32 date;
+ uint32 processor_signature;
+ uint32 checksum;
+ uint32 loader_revision;
+ uint32 processor_flags;
+ uint32 data_size;
+ uint32 total_size;
+ uint32 reserved[3];
+};
+
+
+struct intel_microcode_extended_signature_header {
+ uint32 extended_signature_count;
+ uint32 extended_checksum;
+ uint32 reserved[3];
+};
+
+
+struct intel_microcode_extended_signature {
+ uint32 processor_signature;
+ uint32 processor_flags;
+ uint32 checksum;
+};
+
+
#define nop() __asm__ ("nop"::)
#define x86_read_cr0() ({ \
diff --git a/src/system/kernel/arch/x86/arch_cpu.cpp
b/src/system/kernel/arch/x86/arch_cpu.cpp
index 7f735b9..e18c76e 100644
--- a/src/system/kernel/arch/x86/arch_cpu.cpp
+++ b/src/system/kernel/arch/x86/arch_cpu.cpp
@@ -41,6 +41,7 @@
#define DUMP_FEATURE_STRING 1
#define DUMP_CPU_TOPOLOGY 1
+#define DUMP_CPU_PATCHLEVEL 1
/* cpu vendor info */
@@ -114,6 +115,11 @@
/* Cache topology information */
static uint32 sCacheSharingMask[CPU_MAX_CACHE_LEVEL];
+static void* sUcodeData = NULL;
+static size_t sUcodeDataSize = 0;
+static struct intel_microcode_header* sLoadedUcodeUpdate;
+static spinlock sUcodeUpdateLock = B_SPINLOCK_INITIALIZER;
+
static status_t
acpi_shutdown(bool rebootSystem)
@@ -880,6 +886,172 @@
static void
+detect_intel_patch_level(cpu_ent* cpu)
+{
+ if (cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_HYPERVISOR) {
+ cpu->arch.patch_level = 0;
+ return;
+ }
+
+ x86_write_msr(IA32_MSR_UCODE_REV, 0);
+ cpuid_info cpuid;
+ get_current_cpuid(&cpuid, 1, 0);
+
+ uint64 value = x86_read_msr(IA32_MSR_UCODE_REV);
+ cpu->arch.patch_level = value >> 32;
+}
+
+
+static void
+detect_amd_patch_level(cpu_ent* cpu)
+{
+ if (cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_HYPERVISOR) {
+ cpu->arch.patch_level = 0;
+ return;
+ }
+
+ uint64 value = x86_read_msr(IA32_MSR_UCODE_REV);
+ cpu->arch.patch_level = value >> 32;
+}
+
+
+static struct intel_microcode_header*
+find_microcode_intel(addr_t data, size_t size, uint32 patchLevel)
+{
+ // 9.11.3 Processor Identification
+ cpuid_info cpuid;
+ get_current_cpuid(&cpuid, 1, 0);
+ uint32 signature = cpuid.regs.eax;
+ // 9.11.4 Platform Identification
+ uint64 platformBits = (x86_read_msr(IA32_MSR_PLATFORM_ID) >> 50) & 0x7;
+ uint64 mask = 1 << platformBits;
+
+ while (size > 0) {
+ if (size < sizeof(struct intel_microcode_header)) {
+ dprintf("find_microcode_intel update is too small for
header\n");
+ break;
+ }
+ struct intel_microcode_header* header =
+ (struct intel_microcode_header*)data;
+
+ uint32 totalSize = header->total_size;
+ uint32 dataSize = header->data_size;
+ if (dataSize == 0) {
+ dataSize = 2000;
+ totalSize = sizeof(struct intel_microcode_header)
+ + dataSize;
+ }
+ if (totalSize > size) {
+ dprintf("find_microcode_intel update is too small for
data\n");
+ break;
+ }
+
+ uint32* dwords = (uint32*)data;
+ // prepare the next update
+ size -= totalSize;
+ data += totalSize;
+
+ if (header->loader_revision != 1) {
+ dprintf("find_microcode_intel incorrect loader
version\n");
+ continue;
+ }
+ // 9.11.6 The microcode update data requires a 16-byte boundary
+ // alignment.
+ if (((addr_t)header % 16) != 0) {
+ dprintf("find_microcode_intel incorrect alignment\n");
+ continue;
+ }
+ uint32 sum = 0;
+ for (uint32 i = 0; i < totalSize / 4; i++) {
+ sum += dwords[i];
+ }
+ if (sum != 0) {
+ dprintf("find_microcode_intel incorrect checksum\n");
+ continue;
+ }
+ if (patchLevel > header->update_revision) {
+ dprintf("find_microcode_intel update_revision is
lower\n");
+ continue;
+ }
+ if (signature == header->processor_signature
+ && (mask & header->processor_flags) != 0) {
+ return header;
+ }
+ if (totalSize <= (sizeof(struct intel_microcode_header) +
dataSize
+ + sizeof(struct
intel_microcode_extended_signature_header))) {
+ continue;
+ }
+ struct intel_microcode_extended_signature_header* extSigHeader =
+ (struct
intel_microcode_extended_signature_header*)((addr_t)header
+ + sizeof(struct intel_microcode_header) +
dataSize);
+ struct intel_microcode_extended_signature* extended_signature =
+ (struct
intel_microcode_extended_signature*)((addr_t)extSigHeader
+ + sizeof(struct
intel_microcode_extended_signature_header));
+ for (uint32 i = 0; i < extSigHeader->extended_signature_count;
i++) {
+ if (signature ==
extended_signature[i].processor_signature
+ && (mask &
extended_signature[i].processor_flags) != 0)
+ return header;
+ }
+ }
+ return NULL;
+}
+
+static void
+load_microcode_intel(int currentCPU, cpu_ent* cpu)
+{
+ // serialize for HT cores
+ if (currentCPU != 0)
+ acquire_spinlock(&sUcodeUpdateLock);
+ detect_intel_patch_level(cpu);
+ uint32 revision = cpu->arch.patch_level;
+ struct intel_microcode_header* update = sLoadedUcodeUpdate;
+ if (update == NULL) {
+ update = find_microcode_intel((addr_t)sUcodeData,
sUcodeDataSize,
+ revision);
+ }
+ if (update != NULL) {
+ addr_t data = (addr_t)update + sizeof(struct
intel_microcode_header);
+ x86_write_msr(IA32_MSR_UCODE_WRITE, data);
+ detect_intel_patch_level(cpu);
+ if (revision == cpu->arch.patch_level) {
+ dprintf("CPU %d: update failed\n", currentCPU);
+ } else {
+ if (sLoadedUcodeUpdate == NULL)
+ sLoadedUcodeUpdate = update;
+ dprintf("CPU %d: updated from revision %" B_PRIu32 " to
%" B_PRIu32
+ "\n", currentCPU, revision,
cpu->arch.patch_level);
+ }
+ } else {
+ dprintf("CPU %d: no update found\n", currentCPU);
+ }
+ if (currentCPU != 0)
+ release_spinlock(&sUcodeUpdateLock);
+}
+
+
+static void
+load_microcode_amd(int currentCPU, cpu_ent* cpu)
+{
+ dprintf("CPU %d: no update found\n", currentCPU);
+}
+
+
+static void
+load_microcode(int currentCPU)
+{
+ if (sUcodeData == NULL)
+ return;
+ cpu_ent* cpu = get_cpu_struct();
+ if ((cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_HYPERVISOR) != 0)
+ return;
+ if (cpu->arch.vendor == VENDOR_INTEL)
+ load_microcode_intel(currentCPU, cpu);
+ else if (cpu->arch.vendor == VENDOR_AMD)
+ load_microcode_amd(currentCPU, cpu);
+}
+
+
+static void
detect_cpu(int currentCPU)
{
cpu_ent* cpu = get_cpu_struct();
@@ -1023,9 +1195,17 @@
detect_cpu_topology(currentCPU, cpu, maxBasicLeaf, maxExtendedLeaf);
+ if (cpu->arch.vendor == VENDOR_INTEL)
+ detect_intel_patch_level(cpu);
+ else if (cpu->arch.vendor == VENDOR_AMD)
+ detect_amd_patch_level(cpu);
+
#if DUMP_FEATURE_STRING
dump_feature_string(currentCPU, cpu);
#endif
+#if DUMP_CPU_PATCHLEVEL
+ dprintf("CPU %d: patch_level %d\n", currentCPU, cpu->arch.patch_level);
+#endif
}
@@ -1136,6 +1316,7 @@
status_t
arch_cpu_init_percpu(kernel_args* args, int cpu)
{
+ load_microcode(cpu);
detect_cpu(cpu);
if (!gCpuIdleFunc) {
@@ -1152,6 +1333,14 @@
status_t
arch_cpu_init(kernel_args* args)
{
+ if (args->arch_args.ucode_data != NULL
+ && args->arch_args.ucode_data_size > 0) {
+ sUcodeData = args->arch_args.ucode_data;
+ sUcodeDataSize = args->arch_args.ucode_data_size;
+ } else {
+ dprintf("CPU: no microcode provided\n");
+ }
+
// init the TSC -> system_time() conversion factors
uint32 conversionFactor = args->arch_args.system_time_cv_factor;
--
To view, visit https://review.haiku-os.org/c/haiku/+/2264
To unsubscribe, or for help writing mail filters, visit
https://review.haiku-os.org/settings
Gerrit-Project: haiku
Gerrit-Branch: master
Gerrit-Change-Id: Ic5fb54cf6c9f489a2d1cdda00f63980c11dcdaeb
Gerrit-Change-Number: 2264
Gerrit-PatchSet: 1
Gerrit-Owner: Jérôme Duval <jerome.duval@xxxxxxxxx>
Gerrit-MessageType: newchange