]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Sec/AmdSev.c
6af38aaf46ee6cb085b62e702edd1add5799d252
[mirror_edk2.git] / OvmfPkg / Sec / AmdSev.c
1 /** @file
2 File defines the Sec routines for the AMD SEV
3
4 Copyright (c) 2021, Advanced Micro Devices, Inc. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <Library/BaseLib.h>
11 #include <Library/DebugLib.h>
12 #include <Library/MemEncryptSevLib.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <Register/Amd/Ghcb.h>
15 #include <Register/Amd/Msr.h>
16
17 #include "AmdSev.h"
18
19 /**
20 Handle an SEV-ES/GHCB protocol check failure.
21
22 Notify the hypervisor using the VMGEXIT instruction that the SEV-ES guest
23 wishes to be terminated.
24
25 @param[in] ReasonCode Reason code to provide to the hypervisor for the
26 termination request.
27
28 **/
29 VOID
30 SevEsProtocolFailure (
31 IN UINT8 ReasonCode
32 )
33 {
34 MSR_SEV_ES_GHCB_REGISTER Msr;
35
36 //
37 // Use the GHCB MSR Protocol to request termination by the hypervisor
38 //
39 Msr.GhcbPhysicalAddress = 0;
40 Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
41 Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
42 Msr.GhcbTerminate.ReasonCode = ReasonCode;
43 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
44
45 AsmVmgExit ();
46
47 ASSERT (FALSE);
48 CpuDeadLoop ();
49 }
50
51 /**
52 Determine if SEV-SNP is active.
53
54 @retval TRUE SEV-SNP is enabled
55 @retval FALSE SEV-SNP is not enabled
56
57 **/
58 BOOLEAN
59 SevSnpIsEnabled (
60 VOID
61 )
62 {
63 MSR_SEV_STATUS_REGISTER Msr;
64
65 //
66 // Read the SEV_STATUS MSR to determine whether SEV-SNP is active.
67 //
68 Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
69
70 //
71 // Check MSR_0xC0010131 Bit 2 (Sev-Snp Enabled)
72 //
73 if (Msr.Bits.SevSnpBit) {
74 return TRUE;
75 }
76
77 return FALSE;
78 }
79
80 /**
81 Register the GHCB GPA
82
83 */
84 STATIC
85 VOID
86 SevSnpGhcbRegister (
87 EFI_PHYSICAL_ADDRESS Address
88 )
89 {
90 MSR_SEV_ES_GHCB_REGISTER Msr;
91
92 //
93 // Use the GHCB MSR Protocol to request to register the GPA.
94 //
95 Msr.GhcbPhysicalAddress = Address & ~EFI_PAGE_MASK;
96 Msr.GhcbGpaRegister.Function = GHCB_INFO_GHCB_GPA_REGISTER_REQUEST;
97 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
98
99 AsmVmgExit ();
100
101 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
102
103 //
104 // If hypervisor responded with a different GPA than requested then fail.
105 //
106 if ((Msr.GhcbGpaRegister.Function != GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE) ||
107 ((Msr.GhcbPhysicalAddress & ~EFI_PAGE_MASK) != Address))
108 {
109 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
110 }
111 }
112
113 /**
114 Verify that Hypervisor supports the SNP feature.
115
116 */
117 STATIC
118 BOOLEAN
119 HypervisorSnpFeatureCheck (
120 VOID
121 )
122 {
123 MSR_SEV_ES_GHCB_REGISTER Msr;
124 UINT64 Features;
125
126 //
127 // Use the GHCB MSR Protocol to query the hypervisor capabilities
128 //
129 Msr.GhcbPhysicalAddress = 0;
130 Msr.GhcbHypervisorFeatures.Function = GHCB_HYPERVISOR_FEATURES_REQUEST;
131 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
132
133 AsmVmgExit ();
134
135 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
136
137 Features = RShiftU64 (Msr.GhcbPhysicalAddress, 12);
138
139 if ((Msr.GhcbHypervisorFeatures.Function != GHCB_HYPERVISOR_FEATURES_RESPONSE) ||
140 (!(Features & GHCB_HV_FEATURES_SNP)))
141 {
142 return FALSE;
143 }
144
145 return TRUE;
146 }
147
148 /**
149 Validate the SEV-ES/GHCB protocol level.
150
151 Verify that the level of SEV-ES/GHCB protocol supported by the hypervisor
152 and the guest intersect. If they don't intersect, request termination.
153
154 **/
155 VOID
156 SevEsProtocolCheck (
157 VOID
158 )
159 {
160 MSR_SEV_ES_GHCB_REGISTER Msr;
161 GHCB *Ghcb;
162
163 //
164 // Use the GHCB MSR Protocol to obtain the GHCB SEV-ES Information for
165 // protocol checking
166 //
167 Msr.GhcbPhysicalAddress = 0;
168 Msr.GhcbInfo.Function = GHCB_INFO_SEV_INFO_GET;
169 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
170
171 AsmVmgExit ();
172
173 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
174
175 if (Msr.GhcbInfo.Function != GHCB_INFO_SEV_INFO) {
176 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
177 }
178
179 if (Msr.GhcbProtocol.SevEsProtocolMin > Msr.GhcbProtocol.SevEsProtocolMax) {
180 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
181 }
182
183 if ((Msr.GhcbProtocol.SevEsProtocolMin > GHCB_VERSION_MAX) ||
184 (Msr.GhcbProtocol.SevEsProtocolMax < GHCB_VERSION_MIN))
185 {
186 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
187 }
188
189 //
190 // We cannot use the MemEncryptSevSnpIsEnabled () because the
191 // ProcessLibraryConstructorList () is not called yet.
192 //
193 if (SevSnpIsEnabled ()) {
194 //
195 // Check if hypervisor supports the SNP feature
196 //
197 if (!HypervisorSnpFeatureCheck ()) {
198 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
199 }
200
201 //
202 // Unlike the SEV-ES guest, the SNP requires that GHCB GPA must be
203 // registered with the Hypervisor before the use. This can be done
204 // using the new VMGEXIT defined in the GHCB v2. Register the GPA
205 // before it is used.
206 //
207 SevSnpGhcbRegister ((EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase));
208 }
209
210 //
211 // SEV-ES protocol checking succeeded, set the initial GHCB address
212 //
213 Msr.GhcbPhysicalAddress = FixedPcdGet32 (PcdOvmfSecGhcbBase);
214 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
215
216 Ghcb = Msr.Ghcb;
217 SetMem (Ghcb, FixedPcdGet32 (PcdOvmfSecGhcbSize), 0);
218
219 //
220 // Set the version to the maximum that can be supported
221 //
222 Ghcb->ProtocolVersion = MIN (Msr.GhcbProtocol.SevEsProtocolMax, GHCB_VERSION_MAX);
223 Ghcb->GhcbUsage = GHCB_STANDARD_USAGE;
224 }
225
226 /**
227 Determine if the SEV is active.
228
229 During the early booting, GuestType is set in the work area. Verify that it
230 is an SEV guest.
231
232 @retval TRUE SEV is enabled
233 @retval FALSE SEV is not enabled
234
235 **/
236 BOOLEAN
237 IsSevGuest (
238 VOID
239 )
240 {
241 OVMF_WORK_AREA *WorkArea;
242
243 //
244 // Ensure that the size of the Confidential Computing work area header
245 // is same as what is provided through a fixed PCD.
246 //
247 ASSERT (
248 (UINTN)FixedPcdGet32 (PcdOvmfConfidentialComputingWorkAreaHeader) ==
249 sizeof (CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER)
250 );
251
252 WorkArea = (OVMF_WORK_AREA *)FixedPcdGet32 (PcdOvmfWorkAreaBase);
253
254 return ((WorkArea != NULL) && (WorkArea->Header.GuestType == CcGuestTypeAmdSev));
255 }
256
257 /**
258 Determine if SEV-ES is active.
259
260 During early booting, SEV-ES support code will set a flag to indicate that
261 SEV-ES is enabled. Return the value of this flag as an indicator that SEV-ES
262 is enabled.
263
264 @retval TRUE SEV-ES is enabled
265 @retval FALSE SEV-ES is not enabled
266
267 **/
268 BOOLEAN
269 SevEsIsEnabled (
270 VOID
271 )
272 {
273 SEC_SEV_ES_WORK_AREA *SevEsWorkArea;
274
275 if (!IsSevGuest ()) {
276 return FALSE;
277 }
278
279 SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *)FixedPcdGet32 (PcdSevEsWorkAreaBase);
280
281 return ((SevEsWorkArea->SevStatusMsrValue & BIT1) != 0);
282 }
283
284 /**
285 Validate System RAM used for decompressing the PEI and DXE firmware volumes
286 when SEV-SNP is active. The PCDs SecValidatedStart and SecValidatedEnd are
287 set in OvmfPkg/FvmainCompactScratchEnd.fdf.inc.
288
289 **/
290 VOID
291 SecValidateSystemRam (
292 VOID
293 )
294 {
295 PHYSICAL_ADDRESS Start, End;
296
297 if (IsSevGuest () && SevSnpIsEnabled ()) {
298 Start = (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecValidatedStart);
299 End = (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecValidatedEnd);
300
301 MemEncryptSevSnpPreValidateSystemRam (Start, EFI_SIZE_TO_PAGES ((UINTN)(End - Start)));
302 }
303 }