1 | ;------------------------------------------------------------------------------
|
---|
2 | ; @file
|
---|
3 | ; Relocate the SMBASE on a hot-added CPU when it services its first SMI.
|
---|
4 | ;
|
---|
5 | ; Copyright (c) 2020, Red Hat, Inc.
|
---|
6 | ;
|
---|
7 | ; SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
8 | ;
|
---|
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"):
|
---|
12 | ;
|
---|
13 | ; - CS selector: 0x3000 (most significant 16 bits of SMM_DEFAULT_SMBASE).
|
---|
14 | ;
|
---|
15 | ; - CS limit: 0xFFFF_FFFF.
|
---|
16 | ;
|
---|
17 | ; - CS base: SMM_DEFAULT_SMBASE (0x3_0000).
|
---|
18 | ;
|
---|
19 | ; - IP: SMM_HANDLER_OFFSET (0x8000).
|
---|
20 | ;
|
---|
21 | ; - ES, SS, DS, FS, GS selectors: 0.
|
---|
22 | ;
|
---|
23 | ; - ES, SS, DS, FS, GS limits: 0xFFFF_FFFF.
|
---|
24 | ;
|
---|
25 | ; - ES, SS, DS, FS, GS bases: 0.
|
---|
26 | ;
|
---|
27 | ; - Operand-size and address-size override prefixes can be used to access the
|
---|
28 | ; address space beyond 1MB.
|
---|
29 | ;------------------------------------------------------------------------------
|
---|
30 |
|
---|
31 | SECTION .data
|
---|
32 | BITS 16
|
---|
33 |
|
---|
34 | ;
|
---|
35 | ; Bring in SMM_DEFAULT_SMBASE from
|
---|
36 | ; "MdePkg/Include/Register/Intel/SmramSaveStateMap.h".
|
---|
37 | ;
|
---|
38 | SMM_DEFAULT_SMBASE: equ 0x3_0000
|
---|
39 |
|
---|
40 | ;
|
---|
41 | ; Field offsets in FIRST_SMI_HANDLER_CONTEXT, which resides at
|
---|
42 | ; SMM_DEFAULT_SMBASE.
|
---|
43 | ;
|
---|
44 | ApicIdGate: equ 0 ; UINT64
|
---|
45 | NewSmbase: equ 8 ; UINT32
|
---|
46 | AboutToLeaveSmm: equ 12 ; UINT8
|
---|
47 |
|
---|
48 | ;
|
---|
49 | ; SMRAM Save State Map field offsets, per the AMD (not Intel) layout that QEMU
|
---|
50 | ; implements. Relative to SMM_DEFAULT_SMBASE.
|
---|
51 | ;
|
---|
52 | SaveStateRevId: equ 0xFEFC ; UINT32
|
---|
53 | SaveStateSmbase: equ 0xFEF8 ; UINT32
|
---|
54 | SaveStateSmbase64: equ 0xFF00 ; UINT32
|
---|
55 |
|
---|
56 | ;
|
---|
57 | ; CPUID constants, from "MdePkg/Include/Register/Intel/Cpuid.h".
|
---|
58 | ;
|
---|
59 | CPUID_SIGNATURE: equ 0x00
|
---|
60 | CPUID_EXTENDED_TOPOLOGY: equ 0x0B
|
---|
61 | CPUID_VERSION_INFO: equ 0x01
|
---|
62 |
|
---|
63 | GLOBAL ASM_PFX (mFirstSmiHandler) ; UINT8[]
|
---|
64 | GLOBAL ASM_PFX (mFirstSmiHandlerSize) ; UINT16
|
---|
65 |
|
---|
66 | ASM_PFX (mFirstSmiHandler):
|
---|
67 | ;
|
---|
68 | ; Get our own APIC ID first, so we can contend for ApicIdGate.
|
---|
69 | ;
|
---|
70 | ; This basically reimplements GetInitialApicId() from
|
---|
71 | ; "UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c".
|
---|
72 | ;
|
---|
73 | mov eax, CPUID_SIGNATURE
|
---|
74 | cpuid
|
---|
75 | cmp eax, CPUID_EXTENDED_TOPOLOGY
|
---|
76 | jb GetApicIdFromVersionInfo
|
---|
77 |
|
---|
78 | mov eax, CPUID_EXTENDED_TOPOLOGY
|
---|
79 | mov ecx, 0
|
---|
80 | cpuid
|
---|
81 | test ebx, 0xFFFF
|
---|
82 | jz GetApicIdFromVersionInfo
|
---|
83 |
|
---|
84 | ;
|
---|
85 | ; EDX has the APIC ID, save it to ESI.
|
---|
86 | ;
|
---|
87 | mov esi, edx
|
---|
88 | jmp KnockOnGate
|
---|
89 |
|
---|
90 | GetApicIdFromVersionInfo:
|
---|
91 | mov eax, CPUID_VERSION_INFO
|
---|
92 | cpuid
|
---|
93 | shr ebx, 24
|
---|
94 | ;
|
---|
95 | ; EBX has the APIC ID, save it to ESI.
|
---|
96 | ;
|
---|
97 | mov esi, ebx
|
---|
98 |
|
---|
99 | KnockOnGate:
|
---|
100 | ;
|
---|
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.
|
---|
103 | ;
|
---|
104 | ; InterlockedCompareExchange64():
|
---|
105 | ; - Value := &FIRST_SMI_HANDLER_CONTEXT.ApicIdGate
|
---|
106 | ; - CompareValue (EDX:EAX) := APIC ID (from ESI)
|
---|
107 | ; - ExchangeValue (ECX:EBX) := MAX_UINT64
|
---|
108 | ;
|
---|
109 | mov edx, 0
|
---|
110 | mov eax, esi
|
---|
111 | mov ecx, 0xFFFF_FFFF
|
---|
112 | mov ebx, 0xFFFF_FFFF
|
---|
113 | lock cmpxchg8b [ds : dword (SMM_DEFAULT_SMBASE + ApicIdGate)]
|
---|
114 | jz ApicIdMatch
|
---|
115 | pause
|
---|
116 | jmp KnockOnGate
|
---|
117 |
|
---|
118 | ApicIdMatch:
|
---|
119 | ;
|
---|
120 | ; Update the SMBASE field in the SMRAM Save State Map.
|
---|
121 | ;
|
---|
122 | ; First, calculate the address of the SMBASE field, based on the SMM Revision
|
---|
123 | ; ID; store the result in EBX.
|
---|
124 | ;
|
---|
125 | mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + SaveStateRevId)]
|
---|
126 | test eax, 0xFFFF
|
---|
127 | jz LegacySaveStateMap
|
---|
128 |
|
---|
129 | mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase64
|
---|
130 | jmp UpdateSmbase
|
---|
131 |
|
---|
132 | LegacySaveStateMap:
|
---|
133 | mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase
|
---|
134 |
|
---|
135 | UpdateSmbase:
|
---|
136 | ;
|
---|
137 | ; Load the new SMBASE value into EAX.
|
---|
138 | ;
|
---|
139 | mov eax, dword [ds : dword (SMM_DEFAULT_SMBASE + NewSmbase)]
|
---|
140 | ;
|
---|
141 | ; Save it to the SMBASE field whose address we calculated in EBX.
|
---|
142 | ;
|
---|
143 | mov dword [ds : dword ebx], eax
|
---|
144 | ;
|
---|
145 | ; Set AboutToLeaveSmm.
|
---|
146 | ;
|
---|
147 | mov byte [ds : dword (SMM_DEFAULT_SMBASE + AboutToLeaveSmm)], 1
|
---|
148 | ;
|
---|
149 | ; We're done; leave SMM and continue to the pen.
|
---|
150 | ;
|
---|
151 | rsm
|
---|
152 |
|
---|
153 | ASM_PFX (mFirstSmiHandlerSize):
|
---|
154 | dw $ - ASM_PFX (mFirstSmiHandler)
|
---|