[haiku-commits] Change in haiku[master]: kernel: load cpu microcode update if loaded by the bootloader

  • From: Gerrit <review@xxxxxxxxxxxxxxxxxxx>
  • To: waddlesplash <waddlesplash@xxxxxxxxx>, haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 22 Feb 2020 11:41:11 +0000

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

Other related posts:

  • » [haiku-commits] Change in haiku[master]: kernel: load cpu microcode update if loaded by the bootloader - Gerrit