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);