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)
|
// MSR registers (possibly Intel specific)
|
||||||
#define IA32_MSR_TSC 0x10
|
#define IA32_MSR_TSC 0x10
|
||||||
|
#define IA32_MSR_PLATFORM_ID 0x17
|
||||||
#define IA32_MSR_APIC_BASE 0x1b
|
#define IA32_MSR_APIC_BASE 0x1b
|
||||||
#define IA32_MSR_SPEC_CTRL 0x48
|
#define IA32_MSR_SPEC_CTRL 0x48
|
||||||
#define IA32_MSR_PRED_CMD 0x49
|
#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_PLATFORM_INFO 0xce
|
||||||
#define IA32_MSR_MPERF 0xe7
|
#define IA32_MSR_MPERF 0xe7
|
||||||
#define IA32_MSR_APERF 0xe8
|
#define IA32_MSR_APERF 0xe8
|
||||||
@@ -431,6 +434,7 @@ typedef struct arch_cpu_info {
|
|||||||
int stepping;
|
int stepping;
|
||||||
int model;
|
int model;
|
||||||
int extended_model;
|
int extended_model;
|
||||||
|
uint32 patch_level;
|
||||||
|
|
||||||
uint32 logical_apic_id;
|
uint32 logical_apic_id;
|
||||||
|
|
||||||
@@ -448,6 +452,37 @@ typedef struct arch_cpu_info {
|
|||||||
} 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 nop() __asm__ ("nop"::)
|
||||||
|
|
||||||
#define x86_read_cr0() ({ \
|
#define x86_read_cr0() ({ \
|
||||||
|
|||||||
@@ -105,6 +105,15 @@ typedef struct kernel_args {
|
|||||||
// bootsplash data
|
// bootsplash data
|
||||||
FixedWidthPointer<uint8> boot_splash;
|
FixedWidthPointer<uint8> boot_splash;
|
||||||
|
|
||||||
|
// optional microcode
|
||||||
|
FixedWidthPointer<void> ucode_data;
|
||||||
|
uint32 ucode_data_size;
|
||||||
|
|
||||||
} _PACKED kernel_args;
|
} _PACKED kernel_args;
|
||||||
|
|
||||||
|
|
||||||
|
const size_t kernel_args_size_v1 = sizeof(kernel_args)
|
||||||
|
- sizeof(FixedWidthPointer<void>) - sizeof(uint32);
|
||||||
|
|
||||||
|
|
||||||
#endif /* KERNEL_BOOT_KERNEL_ARGS_H */
|
#endif /* KERNEL_BOOT_KERNEL_ARGS_H */
|
||||||
|
|||||||
@@ -126,6 +126,9 @@ main(stage2_args *args)
|
|||||||
|
|
||||||
load_modules(args, bootVolume);
|
load_modules(args, bootVolume);
|
||||||
|
|
||||||
|
gKernelArgs.ucode_data = NULL;
|
||||||
|
gKernelArgs.ucode_data_size = 0;
|
||||||
|
|
||||||
// apply boot settings
|
// apply boot settings
|
||||||
apply_boot_settings();
|
apply_boot_settings();
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,9 @@
|
|||||||
#include "paging/X86VMTranslationMap.h"
|
#include "paging/X86VMTranslationMap.h"
|
||||||
|
|
||||||
|
|
||||||
#define DUMP_FEATURE_STRING 1
|
#define DUMP_FEATURE_STRING 1
|
||||||
#define DUMP_CPU_TOPOLOGY 1
|
#define DUMP_CPU_TOPOLOGY 1
|
||||||
|
#define DUMP_CPU_PATCHLEVEL 1
|
||||||
|
|
||||||
|
|
||||||
/* cpu vendor info */
|
/* cpu vendor info */
|
||||||
@@ -114,6 +115,11 @@ static uint32 sHierarchyShift[CPU_TOPOLOGY_LEVELS];
|
|||||||
/* Cache topology information */
|
/* Cache topology information */
|
||||||
static uint32 sCacheSharingMask[CPU_MAX_CACHE_LEVEL];
|
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
|
static status_t
|
||||||
acpi_shutdown(bool rebootSystem)
|
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
|
static void
|
||||||
detect_cpu(int currentCPU)
|
detect_cpu(int currentCPU)
|
||||||
{
|
{
|
||||||
@@ -1023,9 +1197,18 @@ detect_cpu(int currentCPU)
|
|||||||
|
|
||||||
detect_cpu_topology(currentCPU, cpu, maxBasicLeaf, maxExtendedLeaf);
|
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
|
#if DUMP_FEATURE_STRING
|
||||||
dump_feature_string(currentCPU, cpu);
|
dump_feature_string(currentCPU, cpu);
|
||||||
#endif
|
#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
|
status_t
|
||||||
arch_cpu_init_percpu(kernel_args* args, int cpu)
|
arch_cpu_init_percpu(kernel_args* args, int cpu)
|
||||||
{
|
{
|
||||||
|
load_microcode(cpu);
|
||||||
detect_cpu(cpu);
|
detect_cpu(cpu);
|
||||||
|
|
||||||
if (!gCpuIdleFunc) {
|
if (!gCpuIdleFunc) {
|
||||||
@@ -1158,6 +1342,14 @@ arch_cpu_init_percpu(kernel_args* args, int cpu)
|
|||||||
status_t
|
status_t
|
||||||
arch_cpu_init(kernel_args* args)
|
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
|
// init the TSC -> system_time() conversion factors
|
||||||
|
|
||||||
uint32 conversionFactor = args->arch_args.system_time_cv_factor;
|
uint32 conversionFactor = args->arch_args.system_time_cv_factor;
|
||||||
|
|||||||
@@ -96,7 +96,11 @@ non_boot_cpu_init(void* args, int currentCPU)
|
|||||||
extern "C" int
|
extern "C" int
|
||||||
_start(kernel_args *bootKernelArgs, int currentCPU)
|
_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) {
|
|| bootKernelArgs->version != CURRENT_KERNEL_ARGS_VERSION) {
|
||||||
// This is something we cannot handle right now - release kernels
|
// This is something we cannot handle right now - release kernels
|
||||||
// should always be able to handle the kernel_args of earlier
|
// 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
|
// the passed in kernel args are in a non-allocated range of memory
|
||||||
if (currentCPU == 0)
|
if (currentCPU == 0)
|
||||||
memcpy(&sKernelArgs, bootKernelArgs, sizeof(kernel_args));
|
memcpy(&sKernelArgs, bootKernelArgs, bootKernelArgs->kernel_args_size);
|
||||||
|
|
||||||
smp_cpu_rendezvous(&sCpuRendezvous2);
|
smp_cpu_rendezvous(&sCpuRendezvous2);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user