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