1 ;------------------------------------------------------------------------------
3 ; Relocate the SMBASE on a hot-added CPU when it services its first SMI.
5 ; Copyright (c) 2020, Red Hat, Inc.
7 ; SPDX-License-Identifier: BSD-2-Clause-Patent
9 ; The routine runs on the hot-added CPU in the following "big real mode",
10 ; 16-bit environment; per "SMI HANDLER EXECUTION ENVIRONMENT" in the Intel SDM
11 ; (table "Processor Register Initialization in SMM"):
13 ; - CS selector: 0x3000 (most significant 16 bits of SMM_DEFAULT_SMBASE).
15 ; - CS limit: 0xFFFF_FFFF.
17 ; - CS base: SMM_DEFAULT_SMBASE (0x3_0000).
19 ; - IP: SMM_HANDLER_OFFSET (0x8000).
21 ; - ES, SS, DS, FS, GS selectors: 0.
23 ; - ES, SS, DS, FS, GS limits: 0xFFFF_FFFF.
25 ; - ES, SS, DS, FS, GS bases: 0.
27 ; - Operand-size and address-size override prefixes can be used to access the
28 ; address space beyond 1MB.
29 ;------------------------------------------------------------------------------
35 ; Bring in SMM_DEFAULT_SMBASE from
36 ; "MdePkg/Include/Register/Intel/SmramSaveStateMap.h".
38 SMM_DEFAULT_SMBASE: equ 0x3_0000
41 ; Field offsets in FIRST_SMI_HANDLER_CONTEXT, which resides at
44 ApicIdGate: equ 0 ; UINT64
45 NewSmbase: equ 8 ; UINT32
46 AboutToLeaveSmm: equ 12 ; UINT8
49 ; SMRAM Save State Map field offsets, per the AMD (not Intel) layout that QEMU
50 ; implements. Relative to SMM_DEFAULT_SMBASE.
52 SaveStateRevId: equ 0xFEFC ; UINT32
53 SaveStateSmbase: equ 0xFEF8 ; UINT32
54 SaveStateSmbase64: equ 0xFF00 ; UINT32
57 ; CPUID constants, from "MdePkg/Include/Register/Intel/Cpuid.h".
59 CPUID_SIGNATURE: equ 0x00
60 CPUID_EXTENDED_TOPOLOGY: equ 0x0B
61 CPUID_VERSION_INFO: equ 0x01
63 GLOBAL ASM_PFX (mFirstSmiHandler) ; UINT8[]
64 GLOBAL ASM_PFX (mFirstSmiHandlerSize) ; UINT16
66 ASM_PFX (mFirstSmiHandler):
68 ; Get our own APIC ID first, so we can contend for ApicIdGate.
70 ; This basically reimplements GetInitialApicId() from
71 ; "UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c".
73 mov eax, CPUID_SIGNATURE
75 cmp eax, CPUID_EXTENDED_TOPOLOGY
76 jb GetApicIdFromVersionInfo
78 mov eax, CPUID_EXTENDED_TOPOLOGY
82 jz GetApicIdFromVersionInfo
85 ; EDX has the APIC ID, save it to ESI.
90 GetApicIdFromVersionInfo:
91 mov eax, CPUID_VERSION_INFO
95 ; EBX has the APIC ID, save it to ESI.
101 ; See if ApicIdGate shows our own APIC ID. If so, swap it to MAX_UINT64
102 ; (close the gate), and advance. Otherwise, keep knocking.
104 ; InterlockedCompareExchange64():
105 ; - Value := &FIRST_SMI_HANDLER_CONTEXT.ApicIdGate
106 ; - CompareValue (EDX:EAX) := APIC ID (from ESI)
107 ; - ExchangeValue (ECX:EBX) := MAX_UINT64
113 lock cmpxchg8b [ds : dword (SMM_DEFAULT_SMBASE + ApicIdGate)]
120 ; Update the SMBASE field in the SMRAM Save State Map.
122 ; First, calculate the address of the SMBASE field, based on the SMM Revision
123 ; ID; store the result in EBX.
125 mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + SaveStateRevId)]
127 jz LegacySaveStateMap
129 mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase64
133 mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase
137 ; Load the new SMBASE value into EAX.
139 mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + NewSmbase)]
141 ; Save it to the SMBASE field whose address we calculated in EBX.
143 mov dword [ds : dword ebx], eax
145 ; Set AboutToLeaveSmm.
147 mov byte [ds : dword (SMM_DEFAULT_SMBASE + AboutToLeaveSmm)], 1
149 ; We're done; leave SMM and continue to the pen.
153 ASM_PFX (mFirstSmiHandlerSize):
154 dw $ - ASM_PFX (mFirstSmiHandler)