diff --git a/src/cpu/intel/microcode/microcode_asm.S b/src/cpu/intel/microcode/microcode_asm.S new file mode 100644 index 0000000000..ef85760269 --- /dev/null +++ b/src/cpu/intel/microcode/microcode_asm.S @@ -0,0 +1,162 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2018 Arthur Heymans + * + * 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; version 2 of the License. + * + * 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. + */ + +/* + * input %esp: return address (not pointer to return address!) + * clobber the content of eax, ebx, ecx, edx, esi, edi, and ebp + */ + +#include +#include +#include + +#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 family+model signature=cpuid_eax(1) */ + movl $1, %eax + cpuid + movl %eax, %ebx + + /* 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 + 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 */ + cmpl PROCESSOR_FLAG(%esi), %ebp + jne 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: