| ;------------------------------------------------------------------------------ | |
| ; @file | |
| ; Relocate the SMBASE on a hot-added CPU when it services its first SMI. | |
| ; | |
| ; Copyright (c) 2020, Red Hat, Inc. | |
| ; | |
| ; SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ; | |
| ; The routine runs on the hot-added CPU in the following "big real mode", | |
| ; 16-bit environment; per "SMI HANDLER EXECUTION ENVIRONMENT" in the Intel SDM | |
| ; (table "Processor Register Initialization in SMM"): | |
| ; | |
| ; - CS selector: 0x3000 (most significant 16 bits of SMM_DEFAULT_SMBASE). | |
| ; | |
| ; - CS limit: 0xFFFF_FFFF. | |
| ; | |
| ; - CS base: SMM_DEFAULT_SMBASE (0x3_0000). | |
| ; | |
| ; - IP: SMM_HANDLER_OFFSET (0x8000). | |
| ; | |
| ; - ES, SS, DS, FS, GS selectors: 0. | |
| ; | |
| ; - ES, SS, DS, FS, GS limits: 0xFFFF_FFFF. | |
| ; | |
| ; - ES, SS, DS, FS, GS bases: 0. | |
| ; | |
| ; - Operand-size and address-size override prefixes can be used to access the | |
| ; address space beyond 1MB. | |
| ;------------------------------------------------------------------------------ | |
| SECTION .data | |
| BITS 16 | |
| ; | |
| ; Bring in SMM_DEFAULT_SMBASE from | |
| ; "MdePkg/Include/Register/Intel/SmramSaveStateMap.h". | |
| ; | |
| SMM_DEFAULT_SMBASE: equ 0x3_0000 | |
| ; | |
| ; Field offsets in FIRST_SMI_HANDLER_CONTEXT, which resides at | |
| ; SMM_DEFAULT_SMBASE. | |
| ; | |
| ApicIdGate: equ 0 ; UINT64 | |
| NewSmbase: equ 8 ; UINT32 | |
| AboutToLeaveSmm: equ 12 ; UINT8 | |
| ; | |
| ; SMRAM Save State Map field offsets, per the AMD (not Intel) layout that QEMU | |
| ; implements. Relative to SMM_DEFAULT_SMBASE. | |
| ; | |
| SaveStateRevId: equ 0xFEFC ; UINT32 | |
| SaveStateSmbase: equ 0xFEF8 ; UINT32 | |
| SaveStateSmbase64: equ 0xFF00 ; UINT32 | |
| ; | |
| ; CPUID constants, from "MdePkg/Include/Register/Intel/Cpuid.h". | |
| ; | |
| CPUID_SIGNATURE: equ 0x00 | |
| CPUID_EXTENDED_TOPOLOGY: equ 0x0B | |
| CPUID_VERSION_INFO: equ 0x01 | |
| GLOBAL ASM_PFX (mFirstSmiHandler) ; UINT8[] | |
| GLOBAL ASM_PFX (mFirstSmiHandlerSize) ; UINT16 | |
| ASM_PFX (mFirstSmiHandler): | |
| ; | |
| ; Get our own APIC ID first, so we can contend for ApicIdGate. | |
| ; | |
| ; This basically reimplements GetInitialApicId() from | |
| ; "UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c". | |
| ; | |
| mov eax, CPUID_SIGNATURE | |
| cpuid | |
| cmp eax, CPUID_EXTENDED_TOPOLOGY | |
| jb GetApicIdFromVersionInfo | |
| mov eax, CPUID_EXTENDED_TOPOLOGY | |
| mov ecx, 0 | |
| cpuid | |
| test ebx, 0xFFFF | |
| jz GetApicIdFromVersionInfo | |
| ; | |
| ; EDX has the APIC ID, save it to ESI. | |
| ; | |
| mov esi, edx | |
| jmp KnockOnGate | |
| GetApicIdFromVersionInfo: | |
| mov eax, CPUID_VERSION_INFO | |
| cpuid | |
| shr ebx, 24 | |
| ; | |
| ; EBX has the APIC ID, save it to ESI. | |
| ; | |
| mov esi, ebx | |
| KnockOnGate: | |
| ; | |
| ; See if ApicIdGate shows our own APIC ID. If so, swap it to MAX_UINT64 | |
| ; (close the gate), and advance. Otherwise, keep knocking. | |
| ; | |
| ; InterlockedCompareExchange64(): | |
| ; - Value := &FIRST_SMI_HANDLER_CONTEXT.ApicIdGate | |
| ; - CompareValue (EDX:EAX) := APIC ID (from ESI) | |
| ; - ExchangeValue (ECX:EBX) := MAX_UINT64 | |
| ; | |
| mov edx, 0 | |
| mov eax, esi | |
| mov ecx, 0xFFFF_FFFF | |
| mov ebx, 0xFFFF_FFFF | |
| lock cmpxchg8b [ds : dword (SMM_DEFAULT_SMBASE + ApicIdGate)] | |
| jz ApicIdMatch | |
| pause | |
| jmp KnockOnGate | |
| ApicIdMatch: | |
| ; | |
| ; Update the SMBASE field in the SMRAM Save State Map. | |
| ; | |
| ; First, calculate the address of the SMBASE field, based on the SMM Revision | |
| ; ID; store the result in EBX. | |
| ; | |
| mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + SaveStateRevId)] | |
| test eax, 0xFFFF | |
| jz LegacySaveStateMap | |
| mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase64 | |
| jmp UpdateSmbase | |
| LegacySaveStateMap: | |
| mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase | |
| UpdateSmbase: | |
| ; | |
| ; Load the new SMBASE value into EAX. | |
| ; | |
| mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + NewSmbase)] | |
| ; | |
| ; Save it to the SMBASE field whose address we calculated in EBX. | |
| ; | |
| mov dword [ds : dword ebx], eax | |
| ; | |
| ; Set AboutToLeaveSmm. | |
| ; | |
| mov byte [ds : dword (SMM_DEFAULT_SMBASE + AboutToLeaveSmm)], 1 | |
| ; | |
| ; We're done; leave SMM and continue to the pen. | |
| ; | |
| rsm | |
| ASM_PFX (mFirstSmiHandlerSize): | |
| dw $ - ASM_PFX (mFirstSmiHandler) |