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