;; @file | |
; Provide FSP API entry points. | |
; | |
; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> | |
; SPDX-License-Identifier: BSD-2-Clause-Patent | |
;; | |
SECTION .text | |
%include "SaveRestoreSseAvxNasm.inc" | |
%include "MicrocodeLoadNasm.inc" | |
; | |
; Following are fixed PCDs | |
; | |
extern ASM_PFX(PcdGet32 (PcdTemporaryRamBase)) | |
extern ASM_PFX(PcdGet32 (PcdTemporaryRamSize)) | |
extern ASM_PFX(PcdGet32 (PcdFspReservedBufferSize)) | |
; | |
; Following functions will be provided in PlatformSecLib | |
; | |
extern ASM_PFX(AsmGetFspBaseAddress) | |
extern ASM_PFX(AsmGetFspInfoHeaderNoStack) | |
;extern ASM_PFX(LoadMicrocode) ; @todo: needs a weak implementation | |
extern ASM_PFX(SecPlatformInit) ; @todo: needs a weak implementation | |
extern ASM_PFX(SecCarInit) | |
; | |
; Define the data length that we saved on the stack top | |
; | |
DATA_LEN_OF_PER0 EQU 18h | |
DATA_LEN_OF_MCUD EQU 18h | |
DATA_LEN_AT_STACK_TOP EQU (DATA_LEN_OF_PER0 + DATA_LEN_OF_MCUD + 4) | |
; | |
; @todo: These structures are moved from MicrocodeLoadNasm.inc to avoid | |
; build error. This needs to be fixed later on. | |
; | |
struc MicrocodeHdr | |
.MicrocodeHdrVersion: resd 1 | |
.MicrocodeHdrRevision: resd 1 | |
.MicrocodeHdrDate: resd 1 | |
.MicrocodeHdrProcessor: resd 1 | |
.MicrocodeHdrChecksum: resd 1 | |
.MicrocodeHdrLoader: resd 1 | |
.MicrocodeHdrFlags: resd 1 | |
.MicrocodeHdrDataSize: resd 1 | |
.MicrocodeHdrTotalSize: resd 1 | |
.MicrocodeHdrRsvd: resd 3 | |
.size: | |
endstruc | |
struc ExtSigHdr | |
.ExtSigHdrCount: resd 1 | |
.ExtSigHdrChecksum: resd 1 | |
.ExtSigHdrRsvd: resd 3 | |
.size: | |
endstruc | |
struc ExtSig | |
.ExtSigProcessor: resd 1 | |
.ExtSigFlags: resd 1 | |
.ExtSigChecksum: resd 1 | |
.size: | |
endstruc | |
struc LoadMicrocodeParamsFsp24 | |
; FSP_UPD_HEADER { | |
.FspUpdHeaderSignature: resd 2 | |
.FspUpdHeaderRevision: resb 1 | |
.FspUpdHeaderReserved: resb 23 | |
; } | |
; FSPT_ARCH2_UPD { | |
.FsptArchRevision: resb 1 | |
.FsptArchReserved: resb 3 | |
.FsptArchLength: resd 1 | |
.FspDebugHandler resq 1 | |
.FspTemporaryRamSize: resd 1 ; Supported only if ArchRevison is >= 3 | |
.FsptArchUpd: resd 3 | |
; } | |
; FSPT_CORE_UPD { | |
.MicrocodeCodeAddr: resq 1 | |
.MicrocodeCodeSize: resq 1 | |
.CodeRegionBase: resq 1 | |
.CodeRegionSize: resq 1 | |
; } | |
.size: | |
endstruc | |
%macro CALL_RDI 1 | |
mov rdi, %%ReturnAddress | |
jmp %1 | |
%%ReturnAddress: | |
%endmacro | |
; | |
; @todo: The strong/weak implementation does not work. | |
; This needs to be reviewed later. | |
; | |
;------------------------------------------------------------------------------ | |
; | |
;;global ASM_PFX(SecPlatformInitDefault) | |
;ASM_PFX(SecPlatformInitDefault): | |
; ; Inputs: | |
; ; ymm7 -> Return address | |
; ; Outputs: | |
; ; rax -> 0 - Successful, Non-zero - Failed. | |
; ; Register Usage: | |
; ; rax is cleared and rbp is used for return address. | |
; ; All others reserved. | |
; | |
; ; Save return address to RBP | |
; LOAD_RBP | |
; | |
; xor rax, rax | |
;Exit1: | |
; jmp rbp | |
;------------------------------------------------------------------------------ | |
global ASM_PFX(LoadMicrocodeDefault) | |
ASM_PFX(LoadMicrocodeDefault): | |
; Inputs: | |
; rcx -> LoadMicrocodeParams pointer | |
; Register Usage: | |
; All are destroyed | |
; Assumptions: | |
; No memory available, stack is hard-coded and used for return address | |
; Executed by SBSP and NBSP | |
; Beginning of microcode update region starts on paragraph boundary | |
; | |
; Save return address to RBP | |
; | |
LOAD_RBP | |
test rsp, rsp | |
jz ParamError | |
test rcx, rcx | |
jz ParamError | |
mov rsp, rcx | |
; | |
; If microcode already loaded before this function, exit this function with SUCCESS. | |
; | |
mov ecx, MSR_IA32_BIOS_SIGN_ID | |
xor eax, eax ; Clear EAX | |
xor edx, edx ; Clear EDX | |
wrmsr ; Load 0 to MSR at 8Bh | |
mov eax, 1 | |
cpuid | |
mov ecx, MSR_IA32_BIOS_SIGN_ID | |
rdmsr ; Get current microcode signature | |
xor rax, rax | |
test edx, edx | |
jnz Exit2 | |
; skip loading Microcode if the MicrocodeCodeSize is zero | |
; and report error if size is less than 2k | |
; first check UPD header revision | |
cmp byte [rsp + LoadMicrocodeParamsFsp24.FspUpdHeaderRevision], 2 | |
jb ParamError | |
cmp byte [rsp + LoadMicrocodeParamsFsp24.FsptArchRevision], 2 | |
jb ParamError | |
; UPD structure is compliant with FSP spec 2.4 | |
mov rax, qword [rsp + LoadMicrocodeParamsFsp24.MicrocodeCodeSize] | |
test rax, rax | |
jz Exit2 | |
cmp rax, 0800h | |
jl ParamError | |
mov rsi, qword [rsp + LoadMicrocodeParamsFsp24.MicrocodeCodeAddr] | |
test rsi, rsi | |
jnz CheckMainHeader | |
ParamError: | |
mov rax, 08000000000000002h | |
jmp Exit2 | |
CheckMainHeader: | |
; Get processor signature and platform ID from the installed processor | |
; and save into registers for later use | |
; ebx = processor signature | |
; edx = platform ID | |
mov eax, 1 | |
cpuid | |
mov ebx, eax | |
mov ecx, MSR_IA32_PLATFORM_ID | |
rdmsr | |
mov ecx, edx | |
shr ecx, 50-32 ; shift (50d-32d=18d=0x12) bits | |
and ecx, 7h ; platform id at bit[52..50] | |
mov edx, 1 | |
shl edx, cl | |
; Current register usage | |
; esp -> stack with parameters | |
; esi -> microcode update to check | |
; ebx = processor signature | |
; edx = platform ID | |
; Check for valid microcode header | |
; Minimal test checking for header version and loader version as 1 | |
mov eax, dword 1 | |
cmp dword [esi + MicrocodeHdr.MicrocodeHdrVersion], eax | |
jne AdvanceFixedSize | |
cmp dword [esi + MicrocodeHdr.MicrocodeHdrLoader], eax | |
jne AdvanceFixedSize | |
; Check if signature and plaform ID match | |
cmp ebx, dword [esi + MicrocodeHdr.MicrocodeHdrProcessor] | |
jne LoadMicrocodeDefault1 | |
test edx, dword [esi + MicrocodeHdr.MicrocodeHdrFlags ] | |
jnz LoadMicrocode ; Jif signature and platform ID match | |
LoadMicrocodeDefault1: | |
; Check if extended header exists | |
; First check if MicrocodeHdrTotalSize and MicrocodeHdrDataSize are valid | |
xor rax, rax | |
cmp dword [esi + MicrocodeHdr.MicrocodeHdrTotalSize], eax | |
je NextMicrocode | |
cmp dword [esi + MicrocodeHdr.MicrocodeHdrDataSize], eax | |
je NextMicrocode | |
; Then verify total size - sizeof header > data size | |
mov ecx, dword [esi + MicrocodeHdr.MicrocodeHdrTotalSize] | |
sub ecx, MicrocodeHdr.size | |
cmp ecx, dword [esi + MicrocodeHdr.MicrocodeHdrDataSize] | |
jng NextMicrocode ; Jif extended header does not exist | |
; Set edi -> extended header | |
mov edi, esi | |
add edi, MicrocodeHdr.size | |
add edi, dword [esi + MicrocodeHdr.MicrocodeHdrDataSize] | |
; Get count of extended structures | |
mov ecx, dword [edi + ExtSigHdr.ExtSigHdrCount] | |
; Move pointer to first signature structure | |
add edi, ExtSigHdr.size | |
CheckExtSig: | |
; Check if extended signature and platform ID match | |
cmp dword [edi + ExtSig.ExtSigProcessor], ebx | |
jne LoadMicrocodeDefault2 | |
test dword [edi + ExtSig.ExtSigFlags], edx | |
jnz LoadMicrocode ; Jif signature and platform ID match | |
LoadMicrocodeDefault2: | |
; Check if any more extended signatures exist | |
add edi, ExtSig.size | |
loop CheckExtSig | |
NextMicrocode: | |
; Advance just after end of this microcode | |
xor rax, rax | |
cmp dword [esi + MicrocodeHdr.MicrocodeHdrTotalSize], eax | |
je LoadMicrocodeDefault3 | |
add esi, dword [esi + MicrocodeHdr.MicrocodeHdrTotalSize] | |
jmp CheckAddress | |
LoadMicrocodeDefault3: | |
add esi, dword 2048 | |
jmp CheckAddress | |
AdvanceFixedSize: | |
; Advance by 4X dwords | |
add esi, dword 1024 | |
CheckAddress: | |
; Check UPD header revision | |
cmp byte [rsp + LoadMicrocodeParamsFsp24.FspUpdHeaderRevision], 2 | |
jb ParamError | |
cmp byte [rsp + LoadMicrocodeParamsFsp24.FsptArchRevision], 2 | |
jb ParamError | |
; UPD structure is compliant with FSP spec 2.4 | |
; Is automatic size detection ? | |
mov rax, qword [rsp + LoadMicrocodeParamsFsp24.MicrocodeCodeSize] | |
mov rcx, 0ffffffffffffffffh | |
cmp rax, rcx | |
jz LoadMicrocodeDefault4 | |
; Address >= microcode region address + microcode region size? | |
add rax, qword [rsp + LoadMicrocodeParamsFsp24.MicrocodeCodeAddr] | |
cmp rsi, rax | |
jae Done ;Jif address is outside of microcode region | |
jmp CheckMainHeader | |
LoadMicrocodeDefault4: | |
; Is valid Microcode start point ? | |
cmp dword [esi + MicrocodeHdr.MicrocodeHdrVersion], 0ffffffffh | |
jz Done | |
jmp CheckMainHeader | |
LoadMicrocode: | |
; EAX contains the linear address of the start of the Update Data | |
; EDX contains zero | |
; ECX contains 79h (IA32_BIOS_UPDT_TRIG) | |
; Start microcode load with wrmsr | |
mov eax, esi | |
add eax, MicrocodeHdr.size | |
xor edx, edx | |
mov ecx, MSR_IA32_BIOS_UPDT_TRIG | |
wrmsr | |
mov eax, 1 | |
cpuid | |
Done: | |
mov ecx, MSR_IA32_BIOS_SIGN_ID | |
xor eax, eax ; Clear EAX | |
xor edx, edx ; Clear EDX | |
wrmsr ; Load 0 to MSR at 8Bh | |
mov eax, 1 | |
cpuid | |
mov ecx, MSR_IA32_BIOS_SIGN_ID | |
rdmsr ; Get current microcode signature | |
xor eax, eax | |
test edx, edx | |
jnz Exit2 | |
mov rax, 0800000000000000Eh | |
Exit2: | |
jmp rbp | |
global ASM_PFX(EstablishStackFsp) | |
ASM_PFX(EstablishStackFsp): | |
; | |
; Save parameter pointer in rdx | |
; | |
mov rdx, rcx | |
; | |
; Enable FSP STACK | |
; | |
mov rax, ASM_PFX(PcdGet32 (PcdTemporaryRamBase)) | |
mov esp, DWORD[rax] | |
LOAD_TEMPORARY_RAM_SIZE rax | |
add esp, eax | |
sub esp, 4 | |
mov dword[esp], DATA_LEN_OF_MCUD ; Size of the data region | |
sub esp, 4 | |
mov dword[esp], 4455434Dh ; Signature of the data region 'MCUD' | |
; check UPD structure revision (rdx + 8) | |
cmp byte [rdx + LoadMicrocodeParamsFsp24.FspUpdHeaderRevision], 2 | |
jb ParamError1 | |
cmp byte [rdx + LoadMicrocodeParamsFsp24.FsptArchRevision], 2 | |
jnb Fsp24UpdHeader | |
ParamError1: | |
mov rax, 08000000000000002h | |
jmp EstablishStackFspExit | |
Fsp24UpdHeader: | |
; UPD structure is compliant with FSP spec 2.4 | |
xor rax, rax | |
mov rax, qword [rdx + LoadMicrocodeParamsFsp24.CodeRegionSize] ; Code size sizeof(FSPT_UPD_COMMON) + 18h | |
sub rsp, 8 | |
mov qword[rsp], rax | |
mov rax, qword [rdx + LoadMicrocodeParamsFsp24.CodeRegionBase] ; Code base sizeof(FSPT_UPD_COMMON) + 10h | |
sub rsp, 8 | |
mov qword[rsp], rax | |
mov rax, qword [rdx + LoadMicrocodeParamsFsp24.MicrocodeCodeSize] ; Microcode size sizeof(FSPT_UPD_COMMON) + 8h | |
sub rsp, 8 | |
mov qword[rsp], rax | |
mov rax, qword [rdx + LoadMicrocodeParamsFsp24.MicrocodeCodeAddr] ; Microcode base sizeof(FSPT_UPD_COMMON) + 0h | |
sub rsp, 8 | |
mov qword[rsp], rax | |
ContinueAfterUpdPush: | |
; | |
; Save API entry/exit timestamp into stack | |
; | |
sub esp, 4 | |
mov dword[esp], DATA_LEN_OF_PER0 ; Size of the data region | |
sub esp, 4 | |
mov dword[esp], 30524550h ; Signature of the data region 'PER0' | |
rdtsc | |
sub esp, 4 | |
mov dword[esp], edx | |
sub esp, 4 | |
mov dword[esp], eax | |
LOAD_TS rax | |
push rax | |
; | |
; Terminator for the data on stack | |
; | |
push 0 | |
; | |
; Set ECX/EDX to the BootLoader temporary memory range | |
; | |
mov rcx, ASM_PFX(PcdGet32 (PcdTemporaryRamBase)) | |
mov edx, [ecx] | |
LOAD_TEMPORARY_RAM_SIZE rcx | |
add edx, ecx | |
mov rcx, ASM_PFX(PcdGet32 (PcdFspReservedBufferSize)) | |
sub edx, [ecx] | |
mov rcx, ASM_PFX(PcdGet32 (PcdTemporaryRamBase)) | |
mov ecx, [ecx] | |
cmp ecx, edx ; If PcdFspReservedBufferSize >= PcdTemporaryRamSize, then error. | |
jb EstablishStackFspSuccess | |
mov rax, 08000000000000003h ; EFI_UNSUPPORTED | |
jmp EstablishStackFspExit | |
EstablishStackFspSuccess: | |
xor rax, rax | |
EstablishStackFspExit: | |
RET_YMM | |
;---------------------------------------------------------------------------- | |
; TempRamInit API | |
; | |
; This FSP API will load the microcode update, enable code caching for the | |
; region specified by the boot loader and also setup a temporary stack to be | |
; used till main memory is initialized. | |
; | |
;---------------------------------------------------------------------------- | |
global ASM_PFX(TempRamInitApi) | |
ASM_PFX(TempRamInitApi): | |
; | |
; Ensure both SSE and AVX are enabled | |
; | |
ENABLE_SSE | |
ENABLE_AVX | |
; | |
; Save RBP, RBX, RSI, RDI and RSP in YMM7, YMM8 and YMM6 | |
; | |
SAVE_REGS | |
; | |
; Save BFV address in YMM9 | |
; | |
SAVE_BFV rbp | |
; | |
; Save timestamp into YMM6 | |
; | |
rdtsc | |
shl rdx, 32 | |
or rax, rdx | |
SAVE_TS rax | |
; | |
; Save Input Parameter in YMM10 | |
; | |
cmp rcx, 0 | |
jnz ParamValid | |
; | |
; Fall back to default UPD | |
; | |
CALL_RDI ASM_PFX(AsmGetFspInfoHeaderNoStack) | |
xor rcx, rcx | |
mov ecx, DWORD [rax + 01Ch] ; Read FsptImageBaseAddress | |
add ecx, DWORD [rax + 024h] ; Get Cfg Region base address = FsptImageBaseAddress + CfgRegionOffset | |
ParamValid: | |
SAVE_RCX | |
mov rdx, ASM_PFX(PcdGet32 (PcdTemporaryRamSize)) | |
mov edx, DWORD [rdx] | |
; | |
; Read Fsp Arch2 revision | |
; | |
cmp byte [ecx + LoadMicrocodeParamsFsp24.FsptArchRevision], 3 | |
jb UseTemporaryRamSizePcd | |
; | |
; Read ARCH2 UPD input value. | |
; | |
mov ebx, DWORD [ecx + LoadMicrocodeParamsFsp24.FspTemporaryRamSize] | |
; | |
; As per spec, if Bootloader pass zero, use Fsp defined Size | |
; | |
cmp ebx, 0 | |
jz UseTemporaryRamSizePcd | |
xor rax, rax | |
mov ax, WORD [rsi + 020h] ; Read ImageAttribute | |
test ax, 16 ; check if Bit4 is set | |
jnz ConsumeInputConfiguration | |
; | |
; Sometimes user may change input value even if it is not supported | |
; return error if input is Non-Zero and not same as PcdTemporaryRamSize. | |
; | |
cmp ebx, edx | |
je UseTemporaryRamSizePcd | |
mov rax, 08000000000000002h ; RETURN_INVALID_PARAMETER | |
jmp TempRamInitExit | |
ConsumeInputConfiguration: | |
; | |
; Read ARCH2 UPD value and Save. | |
; Only low-32 bits of rbx/rdx holds the temporary ram size. | |
; | |
SAVE_TEMPORARY_RAM_SIZE rbx | |
jmp GotTemporaryRamSize | |
UseTemporaryRamSizePcd: | |
SAVE_TEMPORARY_RAM_SIZE rdx | |
GotTemporaryRamSize: | |
; | |
; Sec Platform Init | |
; | |
CALL_YMM ASM_PFX(SecPlatformInit) | |
test rax, rax | |
jnz TempRamInitExit | |
; Load microcode | |
LOAD_RCX | |
CALL_YMM ASM_PFX(LoadMicrocodeDefault) | |
SAVE_UCODE_STATUS rax ; Save microcode return status in SLOT 0 in YMM9 (upper 128bits). | |
; @note If return value rax is not 0, microcode did not load, but continue and attempt to boot. | |
; Call Sec CAR Init | |
LOAD_RCX | |
CALL_YMM ASM_PFX(SecCarInit) | |
test rax, rax | |
jnz TempRamInitExit | |
LOAD_RCX | |
CALL_YMM ASM_PFX(EstablishStackFsp) | |
test rax, rax | |
jnz TempRamInitExit | |
LOAD_UCODE_STATUS rax ; Restore microcode status if no CAR init error from SLOT 0 in YMM9 (upper 128bits). | |
TempRamInitExit: | |
mov bl, al ; save al data in bl | |
mov al, 07Fh ; API exit postcode 7f | |
out 080h, al | |
mov al, bl ; restore al data from bl | |
; | |
; Load RBP, RBX, RSI, RDI and RSP from YMM7, YMM8 and YMM6 | |
; | |
LOAD_REGS | |
LOAD_BFV rbp | |
ret | |
;---------------------------------------------------------------------------- | |
; Module Entrypoint API | |
;---------------------------------------------------------------------------- | |
global ASM_PFX(_ModuleEntryPoint) | |
ASM_PFX(_ModuleEntryPoint): | |
jmp $ | |