[haiku-commits] haiku: hrev53985 - src/system/kernel/arch/x86 headers/private/kernel/arch/x86 src/system/kernel

  • From: Jérôme Duval <jerome.duval@xxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 16 Mar 2020 02:42:27 -0400 (EDT)

hrev53985 adds 1 changeset to branch 'master'
old head: 4657aba9da7fbe2e633e1a2d0e242566cf888faf
new head: 56bb1bd5c992ca2dd09bb501c44707396273f438
overview: 
https://git.haiku-os.org/haiku/log/?qt=range&q=56bb1bd5c992+%5E4657aba9da7f

----------------------------------------------------------------------------

56bb1bd5c992: kernel: load cpu microcode update if loaded by the bootloader
  
  add optional fields for microcode in kernel_args.
  
  Change-Id: Ic5fb54cf6c9f489a2d1cdda00f63980c11dcdaeb
  Reviewed-on: https://review.haiku-os.org/c/haiku/+/2264
  Reviewed-by: Adrien Destugues <pulkomandy@xxxxxxxxx>

                                   [ Jérôme Duval <jerome.duval@xxxxxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev53985
Commit:      56bb1bd5c992ca2dd09bb501c44707396273f438
URL:         https://git.haiku-os.org/haiku/commit/?id=56bb1bd5c992
Author:      Jérôme Duval <jerome.duval@xxxxxxxxx>
Date:        Thu Feb 20 16:22:16 2020 UTC

----------------------------------------------------------------------------

5 files changed, 246 insertions(+), 3 deletions(-)
headers/private/kernel/arch/x86/arch_cpu.h |  35 +++++
headers/private/kernel/boot/kernel_args.h  |   9 ++
src/system/boot/loader/main.cpp            |   3 +
src/system/kernel/arch/x86/arch_cpu.cpp    | 194 ++++++++++++++++++++++++-
src/system/kernel/main.cpp                 |   8 +-

----------------------------------------------------------------------------

diff --git a/headers/private/kernel/arch/x86/arch_cpu.h 
b/headers/private/kernel/arch/x86/arch_cpu.h
index ed59e3ca29..c2826550b3 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
@@ -431,6 +434,7 @@ typedef struct arch_cpu_info {
        int                                     stepping;
        int                                     model;
        int                                     extended_model;
+       uint32                          patch_level;
 
        uint32                          logical_apic_id;
 
@@ -448,6 +452,37 @@ typedef struct arch_cpu_info {
 } 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/headers/private/kernel/boot/kernel_args.h 
b/headers/private/kernel/boot/kernel_args.h
index 9a6a45568d..d77ef49d5b 100644
--- a/headers/private/kernel/boot/kernel_args.h
+++ b/headers/private/kernel/boot/kernel_args.h
@@ -105,6 +105,15 @@ typedef struct kernel_args {
        // bootsplash data
        FixedWidthPointer<uint8> boot_splash;
 
+       // optional microcode
+       FixedWidthPointer<void> ucode_data;
+       uint32  ucode_data_size;
+
 } _PACKED kernel_args;
 
+
+const size_t kernel_args_size_v1 = sizeof(kernel_args)
+       - sizeof(FixedWidthPointer<void>) - sizeof(uint32);
+
+
 #endif /* KERNEL_BOOT_KERNEL_ARGS_H */
diff --git a/src/system/boot/loader/main.cpp b/src/system/boot/loader/main.cpp
index 8b02173d01..73b66da682 100644
--- a/src/system/boot/loader/main.cpp
+++ b/src/system/boot/loader/main.cpp
@@ -126,6 +126,9 @@ main(stage2_args *args)
 
                        load_modules(args, bootVolume);
 
+                       gKernelArgs.ucode_data = NULL;
+                       gKernelArgs.ucode_data_size = 0;
+
                        // apply boot settings
                        apply_boot_settings();
 
diff --git a/src/system/kernel/arch/x86/arch_cpu.cpp 
b/src/system/kernel/arch/x86/arch_cpu.cpp
index cf833bb1cb..eb2f738dcb 100644
--- a/src/system/kernel/arch/x86/arch_cpu.cpp
+++ b/src/system/kernel/arch/x86/arch_cpu.cpp
@@ -39,8 +39,9 @@
 #include "paging/X86VMTranslationMap.h"
 
 
-#define DUMP_FEATURE_STRING 1
+#define DUMP_FEATURE_STRING    1
 #define DUMP_CPU_TOPOLOGY      1
+#define DUMP_CPU_PATCHLEVEL    1
 
 
 /* cpu vendor info */
@@ -114,6 +115,11 @@ static uint32 sHierarchyShift[CPU_TOPOLOGY_LEVELS];
 /* 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)
@@ -879,6 +885,174 @@ detect_cpu_topology(int currentCPU, cpu_ent* cpu, uint32 
maxBasicLeaf,
 }
 
 
+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);
+               wbinvd();
+               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)
 {
@@ -1023,9 +1197,18 @@ detect_cpu(int currentCPU)
 
        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 %" B_PRIu32 "\n", currentCPU,
+               cpu->arch.patch_level);
+#endif
 }
 
 
@@ -1136,6 +1319,7 @@ detect_amdc1e_noarat()
 status_t
 arch_cpu_init_percpu(kernel_args* args, int cpu)
 {
+       load_microcode(cpu);
        detect_cpu(cpu);
 
        if (!gCpuIdleFunc) {
@@ -1158,6 +1342,14 @@ arch_cpu_init_percpu(kernel_args* args, int cpu)
 status_t
 arch_cpu_init(kernel_args* args)
 {
+       if (args->ucode_data != NULL
+               && args->ucode_data_size > 0) {
+               sUcodeData = args->ucode_data;
+               sUcodeDataSize = 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;
diff --git a/src/system/kernel/main.cpp b/src/system/kernel/main.cpp
index b54a9c2feb..2071f6757f 100644
--- a/src/system/kernel/main.cpp
+++ b/src/system/kernel/main.cpp
@@ -96,7 +96,11 @@ non_boot_cpu_init(void* args, int currentCPU)
 extern "C" int
 _start(kernel_args *bootKernelArgs, int currentCPU)
 {
-       if (bootKernelArgs->kernel_args_size != sizeof(kernel_args)
+       if (bootKernelArgs->version == CURRENT_KERNEL_ARGS_VERSION
+               && bootKernelArgs->kernel_args_size == kernel_args_size_v1) {
+               sKernelArgs.ucode_data = NULL;
+               sKernelArgs.ucode_data_size = 0;
+       } else if (bootKernelArgs->kernel_args_size != sizeof(kernel_args)
                || bootKernelArgs->version != CURRENT_KERNEL_ARGS_VERSION) {
                // This is something we cannot handle right now - release 
kernels
                // should always be able to handle the kernel_args of earlier
@@ -113,7 +117,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
 
        // the passed in kernel args are in a non-allocated range of memory
        if (currentCPU == 0)
-               memcpy(&sKernelArgs, bootKernelArgs, sizeof(kernel_args));
+               memcpy(&sKernelArgs, bootKernelArgs, 
bootKernelArgs->kernel_args_size);
 
        smp_cpu_rendezvous(&sCpuRendezvous2);
 


Other related posts:

  • » [haiku-commits] haiku: hrev53985 - src/system/kernel/arch/x86 headers/private/kernel/arch/x86 src/system/kernel - Jérôme Duval