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 <[email protected]>
This commit is contained in:
@@ -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() ({ \
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user