3 Stateful and implicitly initialized fw_cfg library implementation.
5 Copyright (C) 2013, Red Hat, Inc.
6 Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
7 Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
9 SPDX-License-Identifier: BSD-2-Clause-Patent
14 #include <Protocol/IoMmu.h>
16 #include <Library/BaseLib.h>
17 #include <Library/BaseMemoryLib.h>
18 #include <Library/IoLib.h>
19 #include <Library/DebugLib.h>
20 #include <Library/QemuFwCfgLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/MemEncryptTdxLib.h>
23 #include <Library/MemEncryptSevLib.h>
25 #include "QemuFwCfgLibInternal.h"
27 STATIC BOOLEAN mQemuFwCfgSupported
= FALSE
;
28 STATIC BOOLEAN mQemuFwCfgDmaSupported
;
30 STATIC EDKII_IOMMU_PROTOCOL
*mIoMmuProtocol
;
33 Returns a boolean indicating if the firmware configuration interface
36 This function may change fw_cfg state.
38 @retval TRUE The interface is available
39 @retval FALSE The interface is not available
44 QemuFwCfgIsAvailable (
48 return InternalQemuFwCfgIsAvailable ();
61 // Enable the access routines while probing to see if it is supported.
62 // For probing we always use the IO Port (IoReadFifo8()) access method.
64 mQemuFwCfgSupported
= TRUE
;
65 mQemuFwCfgDmaSupported
= FALSE
;
67 QemuFwCfgSelectItem (QemuFwCfgItemSignature
);
68 Signature
= QemuFwCfgRead32 ();
69 DEBUG ((DEBUG_INFO
, "FW CFG Signature: 0x%x\n", Signature
));
70 QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion
);
71 Revision
= QemuFwCfgRead32 ();
72 DEBUG ((DEBUG_INFO
, "FW CFG Revision: 0x%x\n", Revision
));
73 if ((Signature
!= SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
77 DEBUG ((DEBUG_INFO
, "QemuFwCfg interface not supported.\n"));
78 mQemuFwCfgSupported
= FALSE
;
79 return RETURN_SUCCESS
;
82 if ((Revision
& FW_CFG_F_DMA
) == 0) {
83 DEBUG ((DEBUG_INFO
, "QemuFwCfg interface (IO Port) is supported.\n"));
85 mQemuFwCfgDmaSupported
= TRUE
;
86 DEBUG ((DEBUG_INFO
, "QemuFwCfg interface (DMA) is supported.\n"));
89 if (mQemuFwCfgDmaSupported
&& (MemEncryptSevIsEnabled () || (MemEncryptTdxIsEnabled ()))) {
93 // IoMmuDxe driver must have installed the IOMMU protocol. If we are not
94 // able to locate the protocol then something must have gone wrong.
96 Status
= gBS
->LocateProtocol (
97 &gEdkiiIoMmuProtocolGuid
,
99 (VOID
**)&mIoMmuProtocol
101 if (EFI_ERROR (Status
)) {
104 "QemuFwCfgDma %a:%a Failed to locate IOMMU protocol.\n",
113 return RETURN_SUCCESS
;
117 Returns a boolean indicating if the firmware configuration interface is
118 available for library-internal purposes.
120 This function never changes fw_cfg state.
122 @retval TRUE The interface is available internally.
123 @retval FALSE The interface is not available internally.
126 InternalQemuFwCfgIsAvailable (
130 return mQemuFwCfgSupported
;
134 Returns a boolean indicating whether QEMU provides the DMA-like access method
137 @retval TRUE The DMA-like access method is available.
138 @retval FALSE The DMA-like access method is unavailable.
141 InternalQemuFwCfgDmaIsAvailable (
145 return mQemuFwCfgDmaSupported
;
149 Function is used for allocating a bi-directional FW_CFG_DMA_ACCESS used
150 between Host and device to exchange the information. The buffer must be free'd
151 using FreeFwCfgDmaAccessBuffer ().
156 AllocFwCfgDmaAccessBuffer (
165 EFI_PHYSICAL_ADDRESS DmaAddress
;
168 Size
= sizeof (FW_CFG_DMA_ACCESS
);
169 NumPages
= EFI_SIZE_TO_PAGES (Size
);
172 // As per UEFI spec, in order to map a host address with
173 // BusMasterCommonBuffer64, the buffer must be allocated using the IOMMU
176 Status
= mIoMmuProtocol
->AllocateBuffer (
182 EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE
184 if (EFI_ERROR (Status
)) {
187 "%a:%a failed to allocate FW_CFG_DMA_ACCESS\n",
196 // Avoid exposing stale data even temporarily: zero the area before mapping
199 ZeroMem (HostAddress
, Size
);
202 // Map the host buffer with BusMasterCommonBuffer64
204 Status
= mIoMmuProtocol
->Map (
206 EdkiiIoMmuOperationBusMasterCommonBuffer64
,
212 if (EFI_ERROR (Status
)) {
213 mIoMmuProtocol
->FreeBuffer (mIoMmuProtocol
, NumPages
, HostAddress
);
216 "%a:%a failed to Map() FW_CFG_DMA_ACCESS\n",
224 if (Size
< sizeof (FW_CFG_DMA_ACCESS
)) {
225 mIoMmuProtocol
->Unmap (mIoMmuProtocol
, Mapping
);
226 mIoMmuProtocol
->FreeBuffer (mIoMmuProtocol
, NumPages
, HostAddress
);
229 "%a:%a failed to Map() - requested 0x%Lx got 0x%Lx\n",
232 (UINT64
)sizeof (FW_CFG_DMA_ACCESS
),
239 *Access
= HostAddress
;
244 Function is to used for freeing the Access buffer allocated using
245 AllocFwCfgDmaAccessBuffer()
250 FreeFwCfgDmaAccessBuffer (
258 NumPages
= EFI_SIZE_TO_PAGES (sizeof (FW_CFG_DMA_ACCESS
));
260 Status
= mIoMmuProtocol
->Unmap (mIoMmuProtocol
, Mapping
);
261 if (EFI_ERROR (Status
)) {
264 "%a:%a failed to UnMap() Mapping 0x%Lx\n",
267 (UINT64
)(UINTN
)Mapping
273 Status
= mIoMmuProtocol
->FreeBuffer (mIoMmuProtocol
, NumPages
, Access
);
274 if (EFI_ERROR (Status
)) {
277 "%a:%a failed to Free() 0x%Lx\n",
280 (UINT64
)(UINTN
)Access
288 Function is used for mapping host address to device address. The buffer must
289 be unmapped with UnmapDmaDataBuffer ().
294 MapFwCfgDmaDataBuffer (
296 IN VOID
*HostAddress
,
298 OUT EFI_PHYSICAL_ADDRESS
*DeviceAddress
,
305 EFI_PHYSICAL_ADDRESS PhysicalAddress
;
307 NumberOfBytes
= Size
;
308 Status
= mIoMmuProtocol
->Map (
311 EdkiiIoMmuOperationBusMasterRead64
:
312 EdkiiIoMmuOperationBusMasterWrite64
),
318 if (EFI_ERROR (Status
)) {
321 "%a:%a failed to Map() Address 0x%Lx Size 0x%Lx\n",
324 (UINT64
)(UINTN
)HostAddress
,
331 if (NumberOfBytes
< Size
) {
332 mIoMmuProtocol
->Unmap (mIoMmuProtocol
, Mapping
);
335 "%a:%a failed to Map() - requested 0x%x got 0x%Lx\n",
339 (UINT64
)NumberOfBytes
345 *DeviceAddress
= PhysicalAddress
;
351 UnmapFwCfgDmaDataBuffer (
357 Status
= mIoMmuProtocol
->Unmap (mIoMmuProtocol
, Mapping
);
358 if (EFI_ERROR (Status
)) {
361 "%a:%a failed to UnMap() Mapping 0x%Lx\n",
364 (UINT64
)(UINTN
)Mapping
372 Transfer an array of bytes, or skip a number of bytes, using the DMA
375 @param[in] Size Size in bytes to transfer or skip.
377 @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
378 and may be NULL, if Size is zero, or Control is
381 @param[in] Control One of the following:
382 FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
383 FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
384 FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
387 InternalQemuFwCfgDmaBytes (
389 IN OUT VOID
*Buffer OPTIONAL
,
393 volatile FW_CFG_DMA_ACCESS LocalAccess
;
394 volatile FW_CFG_DMA_ACCESS
*Access
;
395 UINT32 AccessHigh
, AccessLow
;
397 VOID
*AccessMapping
, *DataMapping
;
401 Control
== FW_CFG_DMA_CTL_WRITE
|| Control
== FW_CFG_DMA_CTL_READ
||
402 Control
== FW_CFG_DMA_CTL_SKIP
409 Access
= &LocalAccess
;
410 AccessMapping
= NULL
;
415 // When SEV or TDX is enabled, map Buffer to DMA address before issuing the DMA
418 if (MemEncryptSevIsEnabled () || MemEncryptTdxIsEnabled ()) {
420 EFI_PHYSICAL_ADDRESS DataBufferAddress
;
423 // Allocate DMA Access buffer
425 AllocFwCfgDmaAccessBuffer (&AccessBuffer
, &AccessMapping
);
427 Access
= AccessBuffer
;
430 // Map actual data buffer
432 if (Control
!= FW_CFG_DMA_CTL_SKIP
) {
433 MapFwCfgDmaDataBuffer (
434 Control
== FW_CFG_DMA_CTL_WRITE
,
441 DataBuffer
= (VOID
*)(UINTN
)DataBufferAddress
;
445 Access
->Control
= SwapBytes32 (Control
);
446 Access
->Length
= SwapBytes32 (Size
);
447 Access
->Address
= SwapBytes64 ((UINTN
)DataBuffer
);
450 // Delimit the transfer from (a) modifications to Access, (b) in case of a
451 // write, from writes to Buffer by the caller.
456 // Start the transfer.
458 AccessHigh
= (UINT32
)RShiftU64 ((UINTN
)Access
, 32);
459 AccessLow
= (UINT32
)(UINTN
)Access
;
460 IoWrite32 (FW_CFG_IO_DMA_ADDRESS
, SwapBytes32 (AccessHigh
));
461 IoWrite32 (FW_CFG_IO_DMA_ADDRESS
+ 4, SwapBytes32 (AccessLow
));
464 // Don't look at Access.Control before starting the transfer.
469 // Wait for the transfer to complete.
472 Status
= SwapBytes32 (Access
->Control
);
473 ASSERT ((Status
& FW_CFG_DMA_CTL_ERROR
) == 0);
474 } while (Status
!= 0);
477 // After a read, the caller will want to use Buffer.
482 // If Access buffer was dynamically allocated then free it.
484 if (AccessMapping
!= NULL
) {
485 FreeFwCfgDmaAccessBuffer ((VOID
*)Access
, AccessMapping
);
489 // If DataBuffer was mapped then unmap it.
491 if (DataMapping
!= NULL
) {
492 UnmapFwCfgDmaDataBuffer (DataMapping
);