]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
Omap35xxPkg: remove bogus UncachedMemoryAllocationLib dependency
[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
LE
8\r
9 This program and the accompanying materials are licensed and made available\r
10 under the terms and conditions of the BSD License which accompanies this\r
11 distribution. The full text of the license may be found at\r
12 http://opensource.org/licenses/bsd-license.php\r
13\r
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
15 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
16**/\r
17\r
09719a01
BS
18#include <Uefi.h>\r
19\r
20#include <Protocol/IoMmu.h>\r
21\r
22#include <Library/BaseLib.h>\r
f6c909ae 23#include <Library/IoLib.h>\r
2ce7e221
LE
24#include <Library/DebugLib.h>\r
25#include <Library/QemuFwCfgLib.h>\r
09719a01
BS
26#include <Library/UefiBootServicesTableLib.h>\r
27#include <Library/MemEncryptSevLib.h>\r
2ce7e221 28\r
5297c0bf
LE
29#include "QemuFwCfgLibInternal.h"\r
30\r
2ce7e221 31STATIC BOOLEAN mQemuFwCfgSupported = FALSE;\r
2c8dcbc6 32STATIC BOOLEAN mQemuFwCfgDmaSupported;\r
2ce7e221 33\r
09719a01 34STATIC EDKII_IOMMU_PROTOCOL *mIoMmuProtocol;\r
2ce7e221
LE
35\r
36/**\r
37 Returns a boolean indicating if the firmware configuration interface\r
38 is available or not.\r
39\r
40 This function may change fw_cfg state.\r
41\r
42 @retval TRUE The interface is available\r
43 @retval FALSE The interface is not available\r
44\r
45**/\r
46BOOLEAN\r
47EFIAPI\r
48QemuFwCfgIsAvailable (\r
49 VOID\r
50 )\r
51{\r
52 return InternalQemuFwCfgIsAvailable ();\r
53}\r
54\r
55\r
56RETURN_STATUS\r
57EFIAPI\r
58QemuFwCfgInitialize (\r
59 VOID\r
60 )\r
61{\r
62 UINT32 Signature;\r
63 UINT32 Revision;\r
64\r
65 //\r
66 // Enable the access routines while probing to see if it is supported.\r
2c8dcbc6 67 // For probing we always use the IO Port (IoReadFifo8()) access method.\r
2ce7e221
LE
68 //\r
69 mQemuFwCfgSupported = TRUE;\r
2c8dcbc6 70 mQemuFwCfgDmaSupported = FALSE;\r
2ce7e221
LE
71\r
72 QemuFwCfgSelectItem (QemuFwCfgItemSignature);\r
73 Signature = QemuFwCfgRead32 ();\r
74 DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature));\r
75 QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);\r
76 Revision = QemuFwCfgRead32 ();\r
77 DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision));\r
78 if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||\r
79 (Revision < 1)\r
80 ) {\r
81 DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n"));\r
82 mQemuFwCfgSupported = FALSE;\r
83 return RETURN_SUCCESS;\r
84 }\r
85\r
2c8dcbc6
LE
86 if ((Revision & FW_CFG_F_DMA) == 0) {\r
87 DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));\r
88 } else {\r
89 mQemuFwCfgDmaSupported = TRUE;\r
90 DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));\r
91 }\r
09719a01
BS
92\r
93 if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {\r
94 EFI_STATUS Status;\r
95\r
96 //\r
97 // IoMmuDxe driver must have installed the IOMMU protocol. If we are not\r
98 // able to locate the protocol then something must have gone wrong.\r
99 //\r
f6c909ae
BS
100 Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL,\r
101 (VOID **)&mIoMmuProtocol);\r
09719a01
BS
102 if (EFI_ERROR (Status)) {\r
103 DEBUG ((DEBUG_ERROR,\r
104 "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n",\r
105 gEfiCallerBaseName, __FUNCTION__));\r
106 ASSERT (FALSE);\r
107 CpuDeadLoop ();\r
108 }\r
109 }\r
110\r
2ce7e221
LE
111 return RETURN_SUCCESS;\r
112}\r
113\r
114\r
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
BS
155AllocFwCfgDmaAccessBuffer (\r
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
f6c909ae
BS
167 Size = sizeof (FW_CFG_DMA_ACCESS);\r
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
172 // BusMasterCommomBuffer64, the buffer must be allocated using the IOMMU\r
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
BS
183 if (EFI_ERROR (Status)) {\r
184 DEBUG ((DEBUG_ERROR,\r
f6c909ae
BS
185 "%a:%a failed to allocate FW_CFG_DMA_ACCESS\n", gEfiCallerBaseName,\r
186 __FUNCTION__));\r
09719a01
BS
187 ASSERT (FALSE);\r
188 CpuDeadLoop ();\r
189 }\r
190\r
f6c909ae
BS
191 //\r
192 // Map the host buffer with BusMasterCommonBuffer64\r
193 //\r
194 Status = mIoMmuProtocol->Map (\r
195 mIoMmuProtocol,\r
196 EdkiiIoMmuOperationBusMasterCommonBuffer64,\r
197 HostAddress,\r
198 &Size,\r
199 &DmaAddress,\r
200 &Mapping\r
201 );\r
202 if (EFI_ERROR (Status)) {\r
203 mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);\r
204 DEBUG ((DEBUG_ERROR,\r
205 "%a:%a failed to Map() FW_CFG_DMA_ACCESS\n", gEfiCallerBaseName,\r
206 __FUNCTION__));\r
207 ASSERT (FALSE);\r
208 CpuDeadLoop ();\r
209 }\r
210\r
211 if (Size < sizeof (FW_CFG_DMA_ACCESS)) {\r
212 mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
213 mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);\r
214 DEBUG ((DEBUG_ERROR,\r
215 "%a:%a failed to Map() - requested 0x%Lx got 0x%Lx\n", gEfiCallerBaseName,\r
216 __FUNCTION__, (UINT64)sizeof (FW_CFG_DMA_ACCESS), (UINT64)Size));\r
217 ASSERT (FALSE);\r
218 CpuDeadLoop ();\r
219 }\r
220\r
221 *Access = HostAddress;\r
222 *MapInfo = Mapping;\r
09719a01
BS
223}\r
224\r
225/**\r
f6c909ae
BS
226 Function is to used for freeing the Access buffer allocated using\r
227 AllocFwCfgDmaAccessBuffer()\r
228\r
229**/\r
230STATIC\r
231VOID\r
232FreeFwCfgDmaAccessBuffer (\r
233 IN VOID *Access,\r
234 IN VOID *Mapping\r
235 )\r
236{\r
237 UINTN NumPages;\r
238 EFI_STATUS Status;\r
239\r
240 NumPages = EFI_SIZE_TO_PAGES (sizeof (FW_CFG_DMA_ACCESS));\r
09719a01 241\r
f6c909ae
BS
242 Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
243 if (EFI_ERROR (Status)) {\r
244 DEBUG ((DEBUG_ERROR,\r
245 "%a:%a failed to UnMap() Mapping 0x%Lx\n", gEfiCallerBaseName,\r
246 __FUNCTION__, (UINT64)(UINTN)Mapping));\r
247 ASSERT (FALSE);\r
248 CpuDeadLoop ();\r
249 }\r
250\r
251 Status = mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, Access);\r
252 if (EFI_ERROR (Status)) {\r
253 DEBUG ((DEBUG_ERROR,\r
254 "%a:%a failed to Free() 0x%Lx\n", gEfiCallerBaseName, __FUNCTION__,\r
255 (UINT64)(UINTN)Access));\r
256 ASSERT (FALSE);\r
257 CpuDeadLoop ();\r
258 }\r
259}\r
260\r
261/**\r
262 Function is used for mapping host address to device address. The buffer must\r
263 be unmapped with UnmapDmaDataBuffer ().\r
09719a01
BS
264\r
265**/\r
f6c909ae 266STATIC\r
09719a01 267VOID\r
f6c909ae
BS
268MapFwCfgDmaDataBuffer (\r
269 IN BOOLEAN IsWrite,\r
270 IN VOID *HostAddress,\r
271 IN UINT32 Size,\r
272 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
273 OUT VOID **MapInfo\r
09719a01
BS
274 )\r
275{\r
f6c909ae
BS
276 EFI_STATUS Status;\r
277 UINTN NumberOfBytes;\r
278 VOID *Mapping;\r
279 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
280\r
281 NumberOfBytes = Size;\r
282 Status = mIoMmuProtocol->Map (\r
283 mIoMmuProtocol,\r
284 (IsWrite ?\r
285 EdkiiIoMmuOperationBusMasterRead64 :\r
286 EdkiiIoMmuOperationBusMasterWrite64),\r
287 HostAddress,\r
288 &NumberOfBytes,\r
289 &PhysicalAddress,\r
290 &Mapping\r
291 );\r
292 if (EFI_ERROR (Status)) {\r
293 DEBUG ((DEBUG_ERROR,\r
294 "%a:%a failed to Map() Address 0x%Lx Size 0x%Lx\n", gEfiCallerBaseName,\r
295 __FUNCTION__, (UINT64)(UINTN)HostAddress, (UINT64)Size));\r
296 ASSERT (FALSE);\r
297 CpuDeadLoop ();\r
298 }\r
09719a01 299\r
f6c909ae
BS
300 if (NumberOfBytes < Size) {\r
301 mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
302 DEBUG ((DEBUG_ERROR,\r
303 "%a:%a failed to Map() - requested 0x%x got 0x%Lx\n", gEfiCallerBaseName,\r
304 __FUNCTION__, Size, (UINT64)NumberOfBytes));\r
305 ASSERT (FALSE);\r
306 CpuDeadLoop ();\r
307 }\r
09719a01 308\r
f6c909ae
BS
309 *DeviceAddress = PhysicalAddress;\r
310 *MapInfo = Mapping;\r
311}\r
312\r
313STATIC\r
314VOID\r
315UnmapFwCfgDmaDataBuffer (\r
316 IN VOID *Mapping\r
317 )\r
318{\r
319 EFI_STATUS Status;\r
320\r
321 Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
09719a01
BS
322 if (EFI_ERROR (Status)) {\r
323 DEBUG ((DEBUG_ERROR,\r
f6c909ae
BS
324 "%a:%a failed to UnMap() Mapping 0x%Lx\n", gEfiCallerBaseName,\r
325 __FUNCTION__, (UINT64)(UINTN)Mapping));\r
09719a01
BS
326 ASSERT (FALSE);\r
327 CpuDeadLoop ();\r
328 }\r
f6c909ae 329}\r
09719a01 330\r
f6c909ae
BS
331/**\r
332 Transfer an array of bytes, or skip a number of bytes, using the DMA\r
333 interface.\r
334\r
335 @param[in] Size Size in bytes to transfer or skip.\r
336\r
337 @param[in,out] Buffer Buffer to read data into or write data from. Ignored,\r
338 and may be NULL, if Size is zero, or Control is\r
339 FW_CFG_DMA_CTL_SKIP.\r
340\r
341 @param[in] Control One of the following:\r
342 FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.\r
343 FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.\r
344 FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.\r
345**/\r
346VOID\r
347InternalQemuFwCfgDmaBytes (\r
348 IN UINT32 Size,\r
349 IN OUT VOID *Buffer OPTIONAL,\r
350 IN UINT32 Control\r
351 )\r
352{\r
353 volatile FW_CFG_DMA_ACCESS LocalAccess;\r
354 volatile FW_CFG_DMA_ACCESS *Access;\r
355 UINT32 AccessHigh, AccessLow;\r
356 UINT32 Status;\r
357 VOID *AccessMapping, *DataMapping;\r
358 VOID *DataBuffer;\r
359\r
360 ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||\r
361 Control == FW_CFG_DMA_CTL_SKIP);\r
362\r
363 if (Size == 0) {\r
364 return;\r
365 }\r
366\r
367 Access = &LocalAccess;\r
368 AccessMapping = NULL;\r
369 DataMapping = NULL;\r
370 DataBuffer = Buffer;\r
371\r
372 //\r
373 // When SEV is enabled, map Buffer to DMA address before issuing the DMA\r
374 // request\r
375 //\r
376 if (MemEncryptSevIsEnabled ()) {\r
377 VOID *AccessBuffer;\r
378 EFI_PHYSICAL_ADDRESS DataBufferAddress;\r
379\r
380 //\r
381 // Allocate DMA Access buffer\r
382 //\r
383 AllocFwCfgDmaAccessBuffer (&AccessBuffer, &AccessMapping);\r
384\r
385 Access = AccessBuffer;\r
386\r
387 //\r
388 // Map actual data buffer\r
389 //\r
390 if (Control != FW_CFG_DMA_CTL_SKIP) {\r
391 MapFwCfgDmaDataBuffer (\r
392 Control == FW_CFG_DMA_CTL_WRITE,\r
393 Buffer,\r
394 Size,\r
395 &DataBufferAddress,\r
396 &DataMapping\r
397 );\r
398\r
399 DataBuffer = (VOID *) (UINTN) DataBufferAddress;\r
400 }\r
401 }\r
402\r
403 Access->Control = SwapBytes32 (Control);\r
404 Access->Length = SwapBytes32 (Size);\r
405 Access->Address = SwapBytes64 ((UINTN)DataBuffer);\r
406\r
407 //\r
408 // Delimit the transfer from (a) modifications to Access, (b) in case of a\r
409 // write, from writes to Buffer by the caller.\r
410 //\r
411 MemoryFence ();\r
412\r
413 //\r
414 // Start the transfer.\r
415 //\r
416 AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32);\r
417 AccessLow = (UINT32)(UINTN)Access;\r
418 IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh));\r
419 IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow));\r
420\r
421 //\r
422 // Don't look at Access.Control before starting the transfer.\r
423 //\r
424 MemoryFence ();\r
425\r
426 //\r
427 // Wait for the transfer to complete.\r
428 //\r
429 do {\r
430 Status = SwapBytes32 (Access->Control);\r
431 ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);\r
432 } while (Status != 0);\r
433\r
434 //\r
435 // After a read, the caller will want to use Buffer.\r
436 //\r
437 MemoryFence ();\r
438\r
439 //\r
440 // If Access buffer was dynamically allocated then free it.\r
441 //\r
442 if (AccessMapping != NULL) {\r
443 FreeFwCfgDmaAccessBuffer ((VOID *)Access, AccessMapping);\r
444 }\r
445\r
446 //\r
447 // If DataBuffer was mapped then unmap it.\r
448 //\r
449 if (DataMapping != NULL) {\r
450 UnmapFwCfgDmaDataBuffer (DataMapping);\r
451 }\r
09719a01 452}\r