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