]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe
[mirror_edk2.git] / OvmfPkg / Library / QemuFwCfgLib / QemuFwCfgDxe.c
CommitLineData
2ce7e221
LE
1/** @file\r
2\r
3 Stateful and implicitly initialized fw_cfg library implementation.\r
4\r
5 Copyright (C) 2013, Red Hat, Inc.\r
6 Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>\r
09719a01 7 Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>\r
2ce7e221 8\r
b26f0cf9 9 SPDX-License-Identifier: BSD-2-Clause-Patent\r
2ce7e221
LE
10**/\r
11\r
09719a01
BS
12#include <Uefi.h>\r
13\r
14#include <Protocol/IoMmu.h>\r
15\r
16#include <Library/BaseLib.h>\r
d431d833 17#include <Library/BaseMemoryLib.h>\r
f6c909ae 18#include <Library/IoLib.h>\r
2ce7e221
LE
19#include <Library/DebugLib.h>\r
20#include <Library/QemuFwCfgLib.h>\r
09719a01 21#include <Library/UefiBootServicesTableLib.h>\r
07c721fe 22#include <Library/MemEncryptTdxLib.h>\r
09719a01 23#include <Library/MemEncryptSevLib.h>\r
2ce7e221 24\r
5297c0bf
LE
25#include "QemuFwCfgLibInternal.h"\r
26\r
ac0a286f
MK
27STATIC BOOLEAN mQemuFwCfgSupported = FALSE;\r
28STATIC BOOLEAN mQemuFwCfgDmaSupported;\r
2ce7e221 29\r
ac0a286f 30STATIC EDKII_IOMMU_PROTOCOL *mIoMmuProtocol;\r
2ce7e221
LE
31\r
32/**\r
33 Returns a boolean indicating if the firmware configuration interface\r
34 is available or not.\r
35\r
36 This function may change fw_cfg state.\r
37\r
38 @retval TRUE The interface is available\r
39 @retval FALSE The interface is not available\r
40\r
41**/\r
42BOOLEAN\r
43EFIAPI\r
44QemuFwCfgIsAvailable (\r
45 VOID\r
46 )\r
47{\r
48 return InternalQemuFwCfgIsAvailable ();\r
49}\r
50\r
2ce7e221
LE
51RETURN_STATUS\r
52EFIAPI\r
53QemuFwCfgInitialize (\r
54 VOID\r
55 )\r
56{\r
ac0a286f
MK
57 UINT32 Signature;\r
58 UINT32 Revision;\r
2ce7e221
LE
59\r
60 //\r
61 // Enable the access routines while probing to see if it is supported.\r
2c8dcbc6 62 // For probing we always use the IO Port (IoReadFifo8()) access method.\r
2ce7e221 63 //\r
ac0a286f 64 mQemuFwCfgSupported = TRUE;\r
2c8dcbc6 65 mQemuFwCfgDmaSupported = FALSE;\r
2ce7e221
LE
66\r
67 QemuFwCfgSelectItem (QemuFwCfgItemSignature);\r
68 Signature = QemuFwCfgRead32 ();\r
70d5086c 69 DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));\r
2ce7e221
LE
70 QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);\r
71 Revision = QemuFwCfgRead32 ();\r
70d5086c 72 DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));\r
2ce7e221
LE
73 if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||\r
74 (Revision < 1)\r
ac0a286f
MK
75 )\r
76 {\r
70d5086c 77 DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));\r
2ce7e221
LE
78 mQemuFwCfgSupported = FALSE;\r
79 return RETURN_SUCCESS;\r
80 }\r
81\r
2c8dcbc6
LE
82 if ((Revision & FW_CFG_F_DMA) == 0) {\r
83 DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));\r
84 } else {\r
85 mQemuFwCfgDmaSupported = TRUE;\r
86 DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));\r
87 }\r
09719a01 88\r
07c721fe 89 if (mQemuFwCfgDmaSupported && (MemEncryptSevIsEnabled () || (MemEncryptTdxIsEnabled ()))) {\r
ac0a286f 90 EFI_STATUS Status;\r
09719a01
BS
91\r
92 //\r
93 // IoMmuDxe driver must have installed the IOMMU protocol. If we are not\r
94 // able to locate the protocol then something must have gone wrong.\r
95 //\r
ac0a286f
MK
96 Status = gBS->LocateProtocol (\r
97 &gEdkiiIoMmuProtocolGuid,\r
98 NULL,\r
99 (VOID **)&mIoMmuProtocol\r
100 );\r
09719a01 101 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
102 DEBUG ((\r
103 DEBUG_ERROR,\r
07c721fe 104 "QemuFwCfgDma %a:%a Failed to locate IOMMU protocol.\n",\r
ac0a286f
MK
105 gEfiCallerBaseName,\r
106 __FUNCTION__\r
107 ));\r
09719a01
BS
108 ASSERT (FALSE);\r
109 CpuDeadLoop ();\r
110 }\r
111 }\r
112\r
2ce7e221
LE
113 return RETURN_SUCCESS;\r
114}\r
115\r
2ce7e221
LE
116/**\r
117 Returns a boolean indicating if the firmware configuration interface is\r
118 available for library-internal purposes.\r
119\r
120 This function never changes fw_cfg state.\r
121\r
122 @retval TRUE The interface is available internally.\r
123 @retval FALSE The interface is not available internally.\r
124**/\r
125BOOLEAN\r
2ce7e221
LE
126InternalQemuFwCfgIsAvailable (\r
127 VOID\r
128 )\r
129{\r
130 return mQemuFwCfgSupported;\r
131}\r
2c8dcbc6
LE
132\r
133/**\r
134 Returns a boolean indicating whether QEMU provides the DMA-like access method\r
135 for fw_cfg.\r
136\r
137 @retval TRUE The DMA-like access method is available.\r
138 @retval FALSE The DMA-like access method is unavailable.\r
139**/\r
140BOOLEAN\r
141InternalQemuFwCfgDmaIsAvailable (\r
142 VOID\r
143 )\r
144{\r
145 return mQemuFwCfgDmaSupported;\r
146}\r
09719a01
BS
147\r
148/**\r
f6c909ae
BS
149 Function is used for allocating a bi-directional FW_CFG_DMA_ACCESS used\r
150 between Host and device to exchange the information. The buffer must be free'd\r
151 using FreeFwCfgDmaAccessBuffer ().\r
09719a01
BS
152\r
153**/\r
f6c909ae 154STATIC\r
09719a01 155VOID\r
f6c909ae 156AllocFwCfgDmaAccessBuffer (\r
ac0a286f
MK
157 OUT VOID **Access,\r
158 OUT VOID **MapInfo\r
09719a01
BS
159 )\r
160{\r
f6c909ae
BS
161 UINTN Size;\r
162 UINTN NumPages;\r
163 EFI_STATUS Status;\r
164 VOID *HostAddress;\r
165 EFI_PHYSICAL_ADDRESS DmaAddress;\r
166 VOID *Mapping;\r
09719a01 167\r
ac0a286f 168 Size = sizeof (FW_CFG_DMA_ACCESS);\r
f6c909ae 169 NumPages = EFI_SIZE_TO_PAGES (Size);\r
09719a01 170\r
f6c909ae
BS
171 //\r
172 // As per UEFI spec, in order to map a host address with\r
a2e75595 173 // BusMasterCommonBuffer64, the buffer must be allocated using the IOMMU\r
f6c909ae
BS
174 // AllocateBuffer()\r
175 //\r
09719a01 176 Status = mIoMmuProtocol->AllocateBuffer (\r
f6c909ae
BS
177 mIoMmuProtocol,\r
178 AllocateAnyPages,\r
179 EfiBootServicesData,\r
180 NumPages,\r
181 &HostAddress,\r
182 EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE\r
183 );\r
09719a01 184 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
185 DEBUG ((\r
186 DEBUG_ERROR,\r
187 "%a:%a failed to allocate FW_CFG_DMA_ACCESS\n",\r
188 gEfiCallerBaseName,\r
189 __FUNCTION__\r
190 ));\r
09719a01
BS
191 ASSERT (FALSE);\r
192 CpuDeadLoop ();\r
193 }\r
194\r
d431d833
LE
195 //\r
196 // Avoid exposing stale data even temporarily: zero the area before mapping\r
197 // it.\r
198 //\r
199 ZeroMem (HostAddress, Size);\r
200\r
f6c909ae
BS
201 //\r
202 // Map the host buffer with BusMasterCommonBuffer64\r
203 //\r
204 Status = mIoMmuProtocol->Map (\r
205 mIoMmuProtocol,\r
206 EdkiiIoMmuOperationBusMasterCommonBuffer64,\r
207 HostAddress,\r
208 &Size,\r
209 &DmaAddress,\r
210 &Mapping\r
211 );\r
212 if (EFI_ERROR (Status)) {\r
213 mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);\r
ac0a286f
MK
214 DEBUG ((\r
215 DEBUG_ERROR,\r
216 "%a:%a failed to Map() FW_CFG_DMA_ACCESS\n",\r
217 gEfiCallerBaseName,\r
218 __FUNCTION__\r
219 ));\r
f6c909ae
BS
220 ASSERT (FALSE);\r
221 CpuDeadLoop ();\r
222 }\r
223\r
224 if (Size < sizeof (FW_CFG_DMA_ACCESS)) {\r
225 mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
226 mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);\r
ac0a286f
MK
227 DEBUG ((\r
228 DEBUG_ERROR,\r
229 "%a:%a failed to Map() - requested 0x%Lx got 0x%Lx\n",\r
230 gEfiCallerBaseName,\r
231 __FUNCTION__,\r
232 (UINT64)sizeof (FW_CFG_DMA_ACCESS),\r
233 (UINT64)Size\r
234 ));\r
f6c909ae
BS
235 ASSERT (FALSE);\r
236 CpuDeadLoop ();\r
237 }\r
238\r
ac0a286f 239 *Access = HostAddress;\r
f6c909ae 240 *MapInfo = Mapping;\r
09719a01
BS
241}\r
242\r
243/**\r
f6c909ae
BS
244 Function is to used for freeing the Access buffer allocated using\r
245 AllocFwCfgDmaAccessBuffer()\r
246\r
247**/\r
248STATIC\r
249VOID\r
250FreeFwCfgDmaAccessBuffer (\r
ac0a286f
MK
251 IN VOID *Access,\r
252 IN VOID *Mapping\r
f6c909ae
BS
253 )\r
254{\r
255 UINTN NumPages;\r
256 EFI_STATUS Status;\r
257\r
258 NumPages = EFI_SIZE_TO_PAGES (sizeof (FW_CFG_DMA_ACCESS));\r
09719a01 259\r
f6c909ae
BS
260 Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
261 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
262 DEBUG ((\r
263 DEBUG_ERROR,\r
264 "%a:%a failed to UnMap() Mapping 0x%Lx\n",\r
265 gEfiCallerBaseName,\r
266 __FUNCTION__,\r
267 (UINT64)(UINTN)Mapping\r
268 ));\r
f6c909ae
BS
269 ASSERT (FALSE);\r
270 CpuDeadLoop ();\r
271 }\r
272\r
273 Status = mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, Access);\r
274 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
275 DEBUG ((\r
276 DEBUG_ERROR,\r
277 "%a:%a failed to Free() 0x%Lx\n",\r
278 gEfiCallerBaseName,\r
279 __FUNCTION__,\r
280 (UINT64)(UINTN)Access\r
281 ));\r
f6c909ae
BS
282 ASSERT (FALSE);\r
283 CpuDeadLoop ();\r
284 }\r
285}\r
286\r
287/**\r
288 Function is used for mapping host address to device address. The buffer must\r
289 be unmapped with UnmapDmaDataBuffer ().\r
09719a01
BS
290\r
291**/\r
f6c909ae 292STATIC\r
09719a01 293VOID\r
f6c909ae
BS
294MapFwCfgDmaDataBuffer (\r
295 IN BOOLEAN IsWrite,\r
296 IN VOID *HostAddress,\r
297 IN UINT32 Size,\r
298 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
299 OUT VOID **MapInfo\r
09719a01
BS
300 )\r
301{\r
ac0a286f
MK
302 EFI_STATUS Status;\r
303 UINTN NumberOfBytes;\r
304 VOID *Mapping;\r
305 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
f6c909ae
BS
306\r
307 NumberOfBytes = Size;\r
ac0a286f
MK
308 Status = mIoMmuProtocol->Map (\r
309 mIoMmuProtocol,\r
310 (IsWrite ?\r
311 EdkiiIoMmuOperationBusMasterRead64 :\r
312 EdkiiIoMmuOperationBusMasterWrite64),\r
313 HostAddress,\r
314 &NumberOfBytes,\r
315 &PhysicalAddress,\r
316 &Mapping\r
317 );\r
f6c909ae 318 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
319 DEBUG ((\r
320 DEBUG_ERROR,\r
321 "%a:%a failed to Map() Address 0x%Lx Size 0x%Lx\n",\r
322 gEfiCallerBaseName,\r
323 __FUNCTION__,\r
324 (UINT64)(UINTN)HostAddress,\r
325 (UINT64)Size\r
326 ));\r
f6c909ae
BS
327 ASSERT (FALSE);\r
328 CpuDeadLoop ();\r
329 }\r
09719a01 330\r
f6c909ae
BS
331 if (NumberOfBytes < Size) {\r
332 mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
ac0a286f
MK
333 DEBUG ((\r
334 DEBUG_ERROR,\r
335 "%a:%a failed to Map() - requested 0x%x got 0x%Lx\n",\r
336 gEfiCallerBaseName,\r
337 __FUNCTION__,\r
338 Size,\r
339 (UINT64)NumberOfBytes\r
340 ));\r
f6c909ae
BS
341 ASSERT (FALSE);\r
342 CpuDeadLoop ();\r
343 }\r
09719a01 344\r
f6c909ae 345 *DeviceAddress = PhysicalAddress;\r
ac0a286f 346 *MapInfo = Mapping;\r
f6c909ae
BS
347}\r
348\r
349STATIC\r
350VOID\r
351UnmapFwCfgDmaDataBuffer (\r
352 IN VOID *Mapping\r
353 )\r
354{\r
355 EFI_STATUS Status;\r
356\r
357 Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
09719a01 358 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
359 DEBUG ((\r
360 DEBUG_ERROR,\r
361 "%a:%a failed to UnMap() Mapping 0x%Lx\n",\r
362 gEfiCallerBaseName,\r
363 __FUNCTION__,\r
364 (UINT64)(UINTN)Mapping\r
365 ));\r
09719a01
BS
366 ASSERT (FALSE);\r
367 CpuDeadLoop ();\r
368 }\r
f6c909ae 369}\r
09719a01 370\r
f6c909ae
BS
371/**\r
372 Transfer an array of bytes, or skip a number of bytes, using the DMA\r
373 interface.\r
374\r
375 @param[in] Size Size in bytes to transfer or skip.\r
376\r
377 @param[in,out] Buffer Buffer to read data into or write data from. Ignored,\r
378 and may be NULL, if Size is zero, or Control is\r
379 FW_CFG_DMA_CTL_SKIP.\r
380\r
381 @param[in] Control One of the following:\r
382 FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.\r
383 FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.\r
384 FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.\r
385**/\r
386VOID\r
387InternalQemuFwCfgDmaBytes (\r
ac0a286f
MK
388 IN UINT32 Size,\r
389 IN OUT VOID *Buffer OPTIONAL,\r
390 IN UINT32 Control\r
f6c909ae
BS
391 )\r
392{\r
ac0a286f
MK
393 volatile FW_CFG_DMA_ACCESS LocalAccess;\r
394 volatile FW_CFG_DMA_ACCESS *Access;\r
395 UINT32 AccessHigh, AccessLow;\r
396 UINT32 Status;\r
397 VOID *AccessMapping, *DataMapping;\r
398 VOID *DataBuffer;\r
399\r
400 ASSERT (\r
401 Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||\r
402 Control == FW_CFG_DMA_CTL_SKIP\r
403 );\r
f6c909ae
BS
404\r
405 if (Size == 0) {\r
406 return;\r
407 }\r
408\r
ac0a286f 409 Access = &LocalAccess;\r
f6c909ae 410 AccessMapping = NULL;\r
ac0a286f
MK
411 DataMapping = NULL;\r
412 DataBuffer = Buffer;\r
f6c909ae
BS
413\r
414 //\r
07c721fe 415 // When SEV or TDX is enabled, map Buffer to DMA address before issuing the DMA\r
f6c909ae
BS
416 // request\r
417 //\r
07c721fe 418 if (MemEncryptSevIsEnabled () || MemEncryptTdxIsEnabled ()) {\r
f6c909ae
BS
419 VOID *AccessBuffer;\r
420 EFI_PHYSICAL_ADDRESS DataBufferAddress;\r
421\r
422 //\r
423 // Allocate DMA Access buffer\r
424 //\r
425 AllocFwCfgDmaAccessBuffer (&AccessBuffer, &AccessMapping);\r
426\r
427 Access = AccessBuffer;\r
428\r
429 //\r
430 // Map actual data buffer\r
431 //\r
432 if (Control != FW_CFG_DMA_CTL_SKIP) {\r
433 MapFwCfgDmaDataBuffer (\r
434 Control == FW_CFG_DMA_CTL_WRITE,\r
435 Buffer,\r
436 Size,\r
437 &DataBufferAddress,\r
438 &DataMapping\r
439 );\r
440\r
ac0a286f 441 DataBuffer = (VOID *)(UINTN)DataBufferAddress;\r
f6c909ae
BS
442 }\r
443 }\r
444\r
445 Access->Control = SwapBytes32 (Control);\r
446 Access->Length = SwapBytes32 (Size);\r
447 Access->Address = SwapBytes64 ((UINTN)DataBuffer);\r
448\r
449 //\r
450 // Delimit the transfer from (a) modifications to Access, (b) in case of a\r
451 // write, from writes to Buffer by the caller.\r
452 //\r
453 MemoryFence ();\r
454\r
455 //\r
456 // Start the transfer.\r
457 //\r
458 AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32);\r
459 AccessLow = (UINT32)(UINTN)Access;\r
ac0a286f 460 IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh));\r
f6c909ae
BS
461 IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow));\r
462\r
463 //\r
464 // Don't look at Access.Control before starting the transfer.\r
465 //\r
466 MemoryFence ();\r
467\r
468 //\r
469 // Wait for the transfer to complete.\r
470 //\r
471 do {\r
472 Status = SwapBytes32 (Access->Control);\r
473 ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);\r
474 } while (Status != 0);\r
475\r
476 //\r
477 // After a read, the caller will want to use Buffer.\r
478 //\r
479 MemoryFence ();\r
480\r
481 //\r
482 // If Access buffer was dynamically allocated then free it.\r
483 //\r
484 if (AccessMapping != NULL) {\r
485 FreeFwCfgDmaAccessBuffer ((VOID *)Access, AccessMapping);\r
486 }\r
487\r
488 //\r
489 // If DataBuffer was mapped then unmap it.\r
490 //\r
491 if (DataMapping != NULL) {\r
492 UnmapFwCfgDmaDataBuffer (DataMapping);\r
493 }\r
09719a01 494}\r