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