summaryrefslogtreecommitdiffstats
path: root/src/arch/x86/core/ucode_mp.S
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/x86/core/ucode_mp.S')
-rw-r--r--src/arch/x86/core/ucode_mp.S257
1 files changed, 257 insertions, 0 deletions
diff --git a/src/arch/x86/core/ucode_mp.S b/src/arch/x86/core/ucode_mp.S
new file mode 100644
index 00000000..808e881e
--- /dev/null
+++ b/src/arch/x86/core/ucode_mp.S
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Microcode updates
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+/* Selectively assemble code for 32-bit/64-bit builds */
+#if defined ( __x86_64__ ) && ! defined ( PLATFORM_pcbios )
+#define codemp code64
+#define AX rax
+#define BX rbx
+#define CX rcx
+#define DX rdx
+#define SI rsi
+#define DI rdi
+#define BP rbp
+#define SP rsp
+#define if32 if 0
+#define if64 if 1
+#else
+#define codemp code32
+#define AX eax
+#define BX ebx
+#define CX ecx
+#define DX edx
+#define SI esi
+#define DI edi
+#define BP ebp
+#define SP esp
+#define if32 if 1
+#define if64 if 0
+#endif
+
+/* Standard features CPUID leaf */
+#define CPUID_FEATURES 0x00000001
+
+/* BIOS update signature MSR */
+#define MSR_BIOS_SIGN_ID 0x0000008b
+
+/** Microcode update control layout
+ *
+ * This must match the layout of struct ucode_control.
+ */
+ .struct 0
+CONTROL_DESC:
+ .space 8
+CONTROL_STATUS:
+ .space 8
+CONTROL_TRIGGER_MSR:
+ .space 4
+CONTROL_APIC_MAX:
+ .space 4
+CONTROL_APIC_UNEXPECTED:
+ .space 4
+CONTROL_APIC_MASK:
+ .space 4
+CONTROL_APIC_TEST:
+ .space 4
+CONTROL_VER_CLEAR:
+ .space 1
+CONTROL_VER_HIGH:
+ .space 1
+CONTROL_LEN:
+
+/* We use register %ebp/%rbp to hold the address of the update control */
+#define CONTROL BP
+
+/* Microcode update descriptor layout
+ *
+ * This must match the layout of struct ucode_descriptor.
+ */
+ .struct 0
+DESC_SIGNATURE:
+ .space 4
+DESC_VERSION:
+ .space 4
+DESC_ADDRESS:
+ .space 8
+DESC_LEN:
+
+/* We use register %esi/%rsi to hold the address of the descriptor */
+#define DESC SI
+
+/** Microcode update status report layout
+ *
+ * This must match the layout of struct ucode_status.
+ */
+ .struct 0
+STATUS_SIGNATURE:
+ .space 4
+STATUS_ID:
+ .space 4
+STATUS_BEFORE:
+ .space 4
+STATUS_AFTER:
+ .space 4
+STATUS_LEN:
+ .equ LOG2_STATUS_LEN, 4
+ .if ( 1 << LOG2_STATUS_LEN ) - STATUS_LEN
+ .error "LOG2_STATUS_LEN value is incorrect"
+ .endif
+
+/* We use register %edi/%rdi to hold the address of the status report */
+#define STATUS DI
+
+/*
+ * Update microcode
+ *
+ * Parameters:
+ * %eax/%rdi Microcode update structure
+ * %edx/%rsi CPU identifier (APIC ID)
+ * %esp/%rsp Stack, or NULL to halt AP upon completion
+ *
+ * This code may run with no stack on an application processor (AP).
+ * All values must be held in registers, and no subroutine calls are
+ * possible. No firmware routines may be called.
+ *
+ * Since cpuid/rdmsr/wrmsr require the use of %eax, %ebx, %ecx, and
+ * %edx, we have essentially only three registers available for
+ * long-term state.
+ */
+ .text
+ .globl ucode_update
+ .codemp
+ .section ".text.ucode_update", "ax", @progbits
+ucode_update:
+
+.if64 /* Get input parameters */
+ movq %rdi, %CONTROL
+ movl %esi, %edx
+.else
+ movl %eax, %CONTROL
+.endif
+ /* Check against maximum expected APIC ID */
+ cmpl CONTROL_APIC_MAX(%CONTROL), %edx
+ jbe 1f
+ movl %edx, CONTROL_APIC_UNEXPECTED(%CONTROL)
+ jmp done
+1:
+ /* Calculate per-CPU status report buffer address */
+ mov %DX, %STATUS
+ shl $LOG2_STATUS_LEN, %STATUS
+ add CONTROL_STATUS(%CONTROL), %STATUS
+
+ /* Report APIC ID */
+ movl %edx, STATUS_ID(%STATUS)
+
+ /* Get and report CPU signature */
+ movl $CPUID_FEATURES, %eax
+ cpuid
+ movl %eax, STATUS_SIGNATURE(%STATUS)
+
+ /* Check APIC ID mask */
+ movl STATUS_ID(%STATUS), %eax
+ andl CONTROL_APIC_MASK(%CONTROL), %eax
+ cmpl CONTROL_APIC_TEST(%CONTROL), %eax
+ jne done
+
+ /* Clear BIOS_SIGN_ID MSR if applicable */
+ movl $MSR_BIOS_SIGN_ID, %ecx
+ xorl %eax, %eax
+ xorl %edx, %edx
+ testb $0xff, CONTROL_VER_CLEAR(%CONTROL)
+ jz 1f
+ wrmsr
+1:
+ /* Get CPU signature to repopulate BIOS_SIGN_ID MSR (for Intel) */
+ movl $CPUID_FEATURES, %eax
+ cpuid
+
+ /* Get initial microcode version */
+ movl $MSR_BIOS_SIGN_ID, %ecx
+ rdmsr
+ testb $0xff, CONTROL_VER_HIGH(%CONTROL)
+ jz 1f
+ movl %edx, %eax
+1: movl %eax, STATUS_BEFORE(%STATUS)
+
+ /* Get start of descriptor list */
+ mov CONTROL_DESC(%CONTROL), %DESC
+ sub $DESC_LEN, %DESC
+
+1: /* Walk update descriptor list to find a matching CPU signature */
+ add $DESC_LEN, %DESC
+ movl DESC_SIGNATURE(%DESC), %eax
+ testl %eax, %eax
+ jz noload
+ cmpl STATUS_SIGNATURE(%STATUS), %eax
+ jne 1b
+
+ /* Compare (signed) microcode versions */
+ movl STATUS_BEFORE(%STATUS), %eax
+ cmpl DESC_VERSION(%DESC), %eax
+ jge noload
+
+ /* Load microcode update */
+ movl CONTROL_TRIGGER_MSR(%CONTROL), %ecx
+ movl (DESC_ADDRESS + 0)(%DESC), %eax
+ movl (DESC_ADDRESS + 4)(%DESC), %edx
+ wrmsr
+
+noload: /* Clear BIOS_SIGN_ID MSR if applicable */
+ movl $MSR_BIOS_SIGN_ID, %ecx
+ xorl %eax, %eax
+ xorl %edx, %edx
+ testb $0xff, CONTROL_VER_CLEAR(%CONTROL)
+ jz 1f
+ wrmsr
+1:
+ /* Get CPU signature to repopulate BIOS_SIGN_ID MSR (for Intel) */
+ movl $CPUID_FEATURES, %eax
+ cpuid
+
+ /* Get and report final microcode version */
+ movl $MSR_BIOS_SIGN_ID, %ecx
+ rdmsr
+ testb $0xff, CONTROL_VER_HIGH(%CONTROL)
+ jz 1f
+ movl %edx, %eax
+1: movl %eax, STATUS_AFTER(%STATUS)
+
+done: /* Return to caller (if stack exists), or halt application processor */
+ test %SP, %SP
+ jz 1f
+ ret
+1: cli
+ hlt
+ jmp 1b
+ .size ucode_update, . - ucode_update