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