| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| /* |
| * input %esp: return address (not pointer to return address!) |
| * clobber the content of eax, ebx, ecx, edx, esi, edi, and ebp |
| */ |
| |
| #include <cpu/x86/post_code.h> |
| #include <cpu/x86/msr.h> |
| |
| #define CBFS_FILE_MAGIC 0 |
| #define CBFS_FILE_LEN (CBFS_FILE_MAGIC + 8) |
| #define CBFS_FILE_TYPE (CBFS_FILE_LEN + 4) |
| #define CBFS_FILE_CHECKSUM (CBFS_FILE_TYPE + 4) |
| #define CBFS_FILE_OFFSET (CBFS_FILE_CHECKSUM + 4) |
| |
| #define HEADER_VER_OFFSET 0 |
| #define UPDATE_VER_OFFSET 4 |
| #define DATE_OFFSET 8 |
| #define PROCESSOR_SIG_OFFSET 12 |
| #define CHKSUM_OFFSET 16 |
| #define LOADER_REV_OFFSET 20 |
| #define PROCESSOR_FLAG 24 |
| #define DATA_SIZE_OFFSET 28 |
| #define TOTAL_OFFSET 32 |
| #define HEADER_SIZE 48 |
| |
| /* |
| * The microcode header is 48 bytes wide and has the following |
| * structure: |
| * Header Version : 32bit |
| * Update Revision : 32bit |
| * Date : 32bit |
| * Processor Signature : 32bit |
| * Checksum : 32bit |
| * Loader Revision : 32bit |
| * Processor Flags : 32bit |
| * Data Size : 32bit |
| * Total Size : 32bit |
| * Reserved : 96bit |
| * |
| * We only check if the Processor signature and flags match and check |
| * if the revision of the update is newer than what is installed |
| */ |
| |
| .section .text |
| .global update_bsp_microcode |
| |
| update_bsp_microcode: |
| /* Keep return address */ |
| movl %esp, %edx |
| /* find microcodes in cbfs */ |
| leal microcode_name, %esi |
| movl $1f, %esp |
| jmp walkcbfs_asm |
| |
| 1: |
| /* restore return address */ |
| movl %edx, %esp |
| |
| cmpl $0, %eax |
| je end_microcode_update |
| movl CBFS_FILE_OFFSET(%eax), %ebx |
| bswap %ebx |
| addl %eax, %ebx |
| movl %ebx, %esi |
| |
| movl CBFS_FILE_LEN(%eax), %edi |
| bswap %edi |
| addl %esi, %edi |
| |
| /* |
| * Microcode revision -> %ebx |
| * Processor flags -> %ebp |
| * Current installed microcode revision -> %edx |
| */ |
| |
| /* Processor flags |
| * rdmsr 0x17 |
| * pf = 1 << ((msr.hi >> 18) & 7) */ |
| movl $IA32_PLATFORM_ID, %ecx |
| rdmsr |
| shr $18, %edx |
| andl $7, %edx |
| movl $1, %eax |
| /* needs to be %cl for shl */ |
| movl %edx, %ecx |
| shl %cl, %eax |
| movl %eax, %ebp |
| |
| /* Fetch the current microcode revision*/ |
| xorl %eax, %eax |
| xorl %edx, %edx |
| movl $IA32_BIOS_SIGN_ID, %ecx |
| wrmsr |
| movl $0x1, %eax |
| cpuid |
| |
| /* Processor family+model signature=cpuid_eax(1) */ |
| movl %eax, %ebx |
| |
| movl $IA32_BIOS_SIGN_ID, %ecx |
| rdmsr |
| |
| check_microcode_entry: |
| /* Test if header revision is non zero */ |
| cmpl $0, HEADER_VER_OFFSET(%esi) |
| je end_microcode_update |
| |
| /* Processor family+model signature=cpuid_eax(1) */ |
| cmpl PROCESSOR_SIG_OFFSET(%esi), %ebx |
| jne next_entry |
| |
| /* Processor flags */ |
| test PROCESSOR_FLAG(%esi), %ebp |
| jz next_entry |
| |
| /* Check if revision is higher than current */ |
| cmpl UPDATE_VER_OFFSET(%esi), %edx |
| /* Don't upgrade if already greater or equal */ |
| jge end_microcode_update |
| |
| /* Do actual update */ |
| movl %esi, %eax |
| addl $HEADER_SIZE, %eax |
| xorl %edx, %edx |
| movl $IA32_BIOS_UPDT_TRIG, %ecx |
| wrmsr |
| |
| jmp end_microcode_update |
| |
| next_entry: |
| movl TOTAL_OFFSET(%esi), %eax |
| cmpl $0, %eax |
| jne 1f |
| /* Newer microcode updates include a size field, whereas older |
| * containers set it at 0 and are exactly 2048 bytes long */ |
| addl $2048, %esi |
| jmp check_end |
| 1: |
| addl %eax, %esi |
| |
| check_end: |
| cmpl %esi, %edi |
| ja check_microcode_entry |
| |
| end_microcode_update: |
| jmp *%esp |
| |
| microcode_name: |
| .string "cpu_microcode_blob.bin" |
| |
| _update_bsp_microcode_end: |