summaryrefslogtreecommitdiffstats
path: root/src/arch/x86/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/x86/core')
-rw-r--r--src/arch/x86/core/mpcall.S197
-rw-r--r--src/arch/x86/core/ucode_mp.S257
2 files changed, 454 insertions, 0 deletions
diff --git a/src/arch/x86/core/mpcall.S b/src/arch/x86/core/mpcall.S
new file mode 100644
index 00000000..f2a3bf90
--- /dev/null
+++ b/src/arch/x86/core/mpcall.S
@@ -0,0 +1,197 @@
+/*
+ * 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
+ *
+ * Multiprocessor functions
+ *
+ */
+
+ .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 DI rdi
+#define SP rsp
+#define if32 if 0
+#define if64 if 1
+#else
+#define codemp code32
+#define DI edi
+#define SP esp
+#define if32 if 1
+#define if64 if 0
+#endif
+
+/* Standard features CPUID leaf */
+#define CPUID_FEATURES 0x00000001
+
+/* x2APIC is supported */
+#define CPUID_FEATURES_ECX_X2APIC 0x00200000
+
+/* Extended topology enumeration CPUID leaf */
+#define CPUID_XT_ENUM 0x0000000b
+
+/*
+ * Call multiprocessor function from C code
+ *
+ * Parameters:
+ * 4(%esp)/%rdi Multiprocessor function
+ * 8(%esp)/%rsi Opaque data pointer
+ */
+ .section ".text.mp_call", "ax", @progbits
+ .codemp
+ .globl mp_call
+mp_call:
+.if64 /* Preserve registers, load incoming parameters into registers */
+ pushq %rax
+ pushq %rcx
+ pushq %rdx
+ pushq %rbx
+ pushq %rsp
+ pushq %rbp
+ pushq %rsi
+ pushq %rdi
+ pushq %r8
+ pushq %r9
+ pushq %r10
+ pushq %r11
+ pushq %r12
+ pushq %r13
+ pushq %r14
+ pushq %r15
+.else
+ pushal
+ movl 36(%esp), %eax
+ movl 40(%esp), %edx
+.endif
+ /* Call multiprocessor function */
+ call mp_jump
+
+.if64 /* Restore registers and return */
+ popq %r15
+ popq %r14
+ popq %r13
+ popq %r12
+ popq %r11
+ popq %r10
+ popq %r9
+ popq %r8
+ popq %rdi
+ popq %rsi
+ popq %rbp
+ leaq 8(%rsp), %rsp /* discard */
+ popq %rbx
+ popq %rdx
+ popq %rcx
+ popq %rax
+.else
+ popal
+.endif
+ ret
+ .size mp_call, . - mp_call
+
+/*
+ * Jump to multiprocessor function
+ *
+ * Parameters:
+ * %eax/%rdi Multiprocessor function
+ * %edx/%rsi Opaque data pointer
+ * %esp/%rsp Stack, or NULL to halt AP upon completion
+ *
+ * Obtain the CPU identifier (i.e. the APIC ID) and perform a tail
+ * call into the specified multiprocessor function.
+ *
+ * This code may run with no stack on an application processor.
+ */
+ .section ".text.mp_jump", "ax", @progbits
+ .codemp
+ .globl mp_jump
+mp_jump:
+.if32 /* Move function parameters to available registers */
+ movl %eax, %edi
+ movl %edx, %esi
+.endif
+
+ /* Get 8-bit APIC ID and x2APIC feature bit */
+ movl $CPUID_FEATURES, %eax
+ cpuid
+ shrl $24, %ebx
+ movl %ebx, %edx
+
+ /* Get 32-bit x2APIC ID if applicable */
+ testl $CPUID_FEATURES_ECX_X2APIC, %ecx
+ jz 1f
+ movl $CPUID_XT_ENUM, %eax
+ xorl %ecx, %ecx
+ cpuid
+1:
+
+.if64 /* Tail call to function */
+ movq %rdi, %rax
+ movq %rsi, %rdi
+ movl %edx, %esi
+ jmp *%rax
+.else
+ movl %esi, %eax
+ jmp *%edi
+.endif
+ .size mp_jump, . - mp_jump
+
+/*
+ * Update maximum CPU identifier
+ *
+ * Parameters:
+ * %eax/%rdi Pointer to shared maximum APIC ID
+ * %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.
+ */
+ .section ".text.mp_update_max_cpuid", "ax", @progbits
+ .codemp
+ .globl mp_update_max_cpuid
+mp_update_max_cpuid:
+.if32 /* Move function parameters to available registers */
+ movl %eax, %edi
+ movl %edx, %esi
+.endif
+ /* Update maximum APIC ID (atomically) */
+ movl (%DI), %eax
+1: cmpl %esi, %eax
+ jae 2f
+ lock cmpxchgl %esi, (%DI)
+ jnz 1b
+2:
+ /* Return to caller (if stack exists), or halt application processor */
+ test %SP, %SP
+ jz 3f
+ ret
+3: cli
+ hlt
+ jmp 3b
+ .size mp_update_max_cpuid, . - mp_update_max_cpuid
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