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