]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/CpuHotplugSmm/FirstSmiHandler.nasm
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / OvmfPkg / CpuHotplugSmm / FirstSmiHandler.nasm
CommitLineData
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
31SECTION .data\r
32BITS 16\r
33\r
34;\r
35; Bring in SMM_DEFAULT_SMBASE from\r
36; "MdePkg/Include/Register/Intel/SmramSaveStateMap.h".\r
37;\r
38SMM_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
44ApicIdGate: equ 0 ; UINT64\r
45NewSmbase: equ 8 ; UINT32\r
46AboutToLeaveSmm: 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
52SaveStateRevId: equ 0xFEFC ; UINT32\r
53SaveStateSmbase: equ 0xFEF8 ; UINT32\r
54SaveStateSmbase64: equ 0xFF00 ; UINT32\r
55\r
56;\r
57; CPUID constants, from "MdePkg/Include/Register/Intel/Cpuid.h".\r
58;\r
59CPUID_SIGNATURE: equ 0x00\r
60CPUID_EXTENDED_TOPOLOGY: equ 0x0B\r
61CPUID_VERSION_INFO: equ 0x01\r
62\r
63GLOBAL ASM_PFX (mFirstSmiHandler) ; UINT8[]\r
64GLOBAL ASM_PFX (mFirstSmiHandlerSize) ; UINT16\r
65\r
66ASM_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
90GetApicIdFromVersionInfo:\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
99KnockOnGate:\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
118ApicIdMatch:\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
132LegacySaveStateMap:\r
133 mov ebx, SMM_DEFAULT_SMBASE + SaveStateSmbase\r
134\r
135UpdateSmbase:\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
153ASM_PFX (mFirstSmiHandlerSize):\r
154 dw $ - ASM_PFX (mFirstSmiHandler)\r