1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
;------------------------------------------------------------------------------
; @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)
|