| ;; @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 28h | |
| 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 | |
| ; | |
| SAVE_RCX | |
| ; | |
| ; Get FspInfoHeader address | |
| ; | |
| CALL_RDI ASM_PFX(AsmGetFspInfoHeaderNoStack) | |
| mov rsi, rax | |
| LOAD_RCX | |
| cmp rcx, 0 | |
| jnz ParamValid | |
| ; | |
| ; Fall back to default UPD | |
| ; | |
| xor rcx, rcx | |
| mov ecx, DWORD [rax + 01Ch] ; Read FsptImageBaseAddress | |
| add ecx, DWORD [rax + 024h] ; Get Cfg Region base address = FsptImageBaseAddress + CfgRegionOffset | |
| SAVE_RCX | |
| ParamValid: | |
| 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 $ | |