dbebd36b18530dbebac6f7fc1e7a1ea4fd4c2966
[mirror_edk2.git] / OvmfPkg / Library / QemuFwCfgLib / QemuFwCfgLib.c
1 /** @file
2
3 Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
4 Copyright (C) 2013, Red Hat, Inc.
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include "Uefi.h"
18 #include <Library/BaseLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/IoLib.h>
22 #include <Library/QemuFwCfgLib.h>
23 #include <Library/MemoryAllocationLib.h>
24 #include <Library/UefiBootServicesTableLib.h>
25
26 #include "QemuFwCfgLibInternal.h"
27
28
29 /**
30 Selects a firmware configuration item for reading.
31
32 Following this call, any data read from this item will start from
33 the beginning of the configuration item's data.
34
35 @param[in] QemuFwCfgItem - Firmware Configuration item to read
36
37 **/
38 VOID
39 EFIAPI
40 QemuFwCfgSelectItem (
41 IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem
42 )
43 {
44 DEBUG ((EFI_D_INFO, "Select Item: 0x%x\n", (UINT16)(UINTN) QemuFwCfgItem));
45 IoWrite16 (FW_CFG_IO_SELECTOR, (UINT16)(UINTN) QemuFwCfgItem);
46 }
47
48
49 /**
50 Transfer an array of bytes, or skip a number of bytes, using the DMA
51 interface.
52
53 @param[in] Size Size in bytes to transfer or skip.
54
55 @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
56 and may be NULL, if Size is zero, or Control is
57 FW_CFG_DMA_CTL_SKIP.
58
59 @param[in] Control One of the following:
60 FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
61 FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
62 FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
63 **/
64 VOID
65 InternalQemuFwCfgDmaBytes (
66 IN UINT32 Size,
67 IN OUT VOID *Buffer OPTIONAL,
68 IN UINT32 Control
69 )
70 {
71 volatile FW_CFG_DMA_ACCESS LocalAccess;
72 volatile FW_CFG_DMA_ACCESS *Access;
73 UINT32 AccessHigh, AccessLow;
74 UINT32 Status;
75 UINT32 NumPages;
76 VOID *DmaBuffer, *BounceBuffer;
77
78 ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
79 Control == FW_CFG_DMA_CTL_SKIP);
80
81 if (Size == 0) {
82 return;
83 }
84
85 //
86 // When SEV is enabled then allocate DMA bounce buffer
87 //
88 if (InternalQemuFwCfgSevIsEnabled ()) {
89 UINTN TotalSize;
90
91 TotalSize = sizeof (*Access);
92 //
93 // Skip operation does not need buffer
94 //
95 if (Control != FW_CFG_DMA_CTL_SKIP) {
96 TotalSize += Size;
97 }
98
99 //
100 // Allocate SEV DMA buffer
101 //
102 NumPages = (UINT32)EFI_SIZE_TO_PAGES (TotalSize);
103 InternalQemuFwCfgSevDmaAllocateBuffer (&BounceBuffer, NumPages);
104
105 Access = BounceBuffer;
106 DmaBuffer = (UINT8*)BounceBuffer + sizeof (*Access);
107
108 //
109 // Decrypt data from encrypted guest buffer into DMA buffer
110 //
111 if (Control == FW_CFG_DMA_CTL_WRITE) {
112 CopyMem (DmaBuffer, Buffer, Size);
113 }
114 } else {
115 Access = &LocalAccess;
116 DmaBuffer = Buffer;
117 BounceBuffer = NULL;
118 }
119
120 Access->Control = SwapBytes32 (Control);
121 Access->Length = SwapBytes32 (Size);
122 Access->Address = SwapBytes64 ((UINTN)DmaBuffer);
123
124 //
125 // Delimit the transfer from (a) modifications to Access, (b) in case of a
126 // write, from writes to Buffer by the caller.
127 //
128 MemoryFence ();
129
130 //
131 // Start the transfer.
132 //
133 AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32);
134 AccessLow = (UINT32)(UINTN)Access;
135 IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh));
136 IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow));
137
138 //
139 // Don't look at Access.Control before starting the transfer.
140 //
141 MemoryFence ();
142
143 //
144 // Wait for the transfer to complete.
145 //
146 do {
147 Status = SwapBytes32 (Access->Control);
148 ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);
149 } while (Status != 0);
150
151 //
152 // After a read, the caller will want to use Buffer.
153 //
154 MemoryFence ();
155
156 //
157 // If Bounce buffer was allocated then copy the data into guest buffer and
158 // free the bounce buffer
159 //
160 if (BounceBuffer != NULL) {
161 //
162 // Encrypt the data from DMA buffer into guest buffer
163 //
164 if (Control == FW_CFG_DMA_CTL_READ) {
165 CopyMem (Buffer, DmaBuffer, Size);
166 }
167
168 InternalQemuFwCfgSevDmaFreeBuffer (BounceBuffer, NumPages);
169 }
170 }
171
172
173 /**
174 Reads firmware configuration bytes into a buffer
175
176 @param[in] Size - Size in bytes to read
177 @param[in] Buffer - Buffer to store data into (OPTIONAL if Size is 0)
178
179 **/
180 VOID
181 EFIAPI
182 InternalQemuFwCfgReadBytes (
183 IN UINTN Size,
184 IN VOID *Buffer OPTIONAL
185 )
186 {
187 if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
188 InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_READ);
189 return;
190 }
191 IoReadFifo8 (FW_CFG_IO_DATA, Size, Buffer);
192 }
193
194
195 /**
196 Reads firmware configuration bytes into a buffer
197
198 If called multiple times, then the data read will
199 continue at the offset of the firmware configuration
200 item where the previous read ended.
201
202 @param[in] Size - Size in bytes to read
203 @param[in] Buffer - Buffer to store data into
204
205 **/
206 VOID
207 EFIAPI
208 QemuFwCfgReadBytes (
209 IN UINTN Size,
210 IN VOID *Buffer
211 )
212 {
213 if (InternalQemuFwCfgIsAvailable ()) {
214 InternalQemuFwCfgReadBytes (Size, Buffer);
215 } else {
216 ZeroMem (Buffer, Size);
217 }
218 }
219
220 /**
221 Write firmware configuration bytes from a buffer
222
223 If called multiple times, then the data written will
224 continue at the offset of the firmware configuration
225 item where the previous write ended.
226
227 @param[in] Size - Size in bytes to write
228 @param[in] Buffer - Buffer to read data from
229
230 **/
231 VOID
232 EFIAPI
233 QemuFwCfgWriteBytes (
234 IN UINTN Size,
235 IN VOID *Buffer
236 )
237 {
238 if (InternalQemuFwCfgIsAvailable ()) {
239 if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
240 InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_WRITE);
241 return;
242 }
243 IoWriteFifo8 (FW_CFG_IO_DATA, Size, Buffer);
244 }
245 }
246
247
248 /**
249 Skip bytes in the firmware configuration item.
250
251 Increase the offset of the firmware configuration item without transferring
252 bytes between the item and a caller-provided buffer. Subsequent read, write
253 or skip operations will commence at the increased offset.
254
255 @param[in] Size Number of bytes to skip.
256 **/
257 VOID
258 EFIAPI
259 QemuFwCfgSkipBytes (
260 IN UINTN Size
261 )
262 {
263 UINTN ChunkSize;
264 UINT8 SkipBuffer[256];
265
266 if (!InternalQemuFwCfgIsAvailable ()) {
267 return;
268 }
269
270 if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
271 InternalQemuFwCfgDmaBytes ((UINT32)Size, NULL, FW_CFG_DMA_CTL_SKIP);
272 return;
273 }
274
275 //
276 // Emulate the skip by reading data in chunks, and throwing it away. The
277 // implementation below is suitable even for phases where RAM or dynamic
278 // allocation is not available or appropriate. It also doesn't affect the
279 // static data footprint for client modules. Large skips are not expected,
280 // therefore this fallback is not performance critical. The size of
281 // SkipBuffer is thought not to exert a large pressure on the stack in any
282 // phase.
283 //
284 while (Size > 0) {
285 ChunkSize = MIN (Size, sizeof SkipBuffer);
286 IoReadFifo8 (FW_CFG_IO_DATA, ChunkSize, SkipBuffer);
287 Size -= ChunkSize;
288 }
289 }
290
291
292 /**
293 Reads a UINT8 firmware configuration value
294
295 @return Value of Firmware Configuration item read
296
297 **/
298 UINT8
299 EFIAPI
300 QemuFwCfgRead8 (
301 VOID
302 )
303 {
304 UINT8 Result;
305
306 QemuFwCfgReadBytes (sizeof (Result), &Result);
307
308 return Result;
309 }
310
311
312 /**
313 Reads a UINT16 firmware configuration value
314
315 @return Value of Firmware Configuration item read
316
317 **/
318 UINT16
319 EFIAPI
320 QemuFwCfgRead16 (
321 VOID
322 )
323 {
324 UINT16 Result;
325
326 QemuFwCfgReadBytes (sizeof (Result), &Result);
327
328 return Result;
329 }
330
331
332 /**
333 Reads a UINT32 firmware configuration value
334
335 @return Value of Firmware Configuration item read
336
337 **/
338 UINT32
339 EFIAPI
340 QemuFwCfgRead32 (
341 VOID
342 )
343 {
344 UINT32 Result;
345
346 QemuFwCfgReadBytes (sizeof (Result), &Result);
347
348 return Result;
349 }
350
351
352 /**
353 Reads a UINT64 firmware configuration value
354
355 @return Value of Firmware Configuration item read
356
357 **/
358 UINT64
359 EFIAPI
360 QemuFwCfgRead64 (
361 VOID
362 )
363 {
364 UINT64 Result;
365
366 QemuFwCfgReadBytes (sizeof (Result), &Result);
367
368 return Result;
369 }
370
371
372 /**
373 Find the configuration item corresponding to the firmware configuration file.
374
375 @param[in] Name - Name of file to look up.
376 @param[out] Item - Configuration item corresponding to the file, to be passed
377 to QemuFwCfgSelectItem ().
378 @param[out] Size - Number of bytes in the file.
379
380 @return RETURN_SUCCESS If file is found.
381 RETURN_NOT_FOUND If file is not found.
382 RETURN_UNSUPPORTED If firmware configuration is unavailable.
383
384 **/
385 RETURN_STATUS
386 EFIAPI
387 QemuFwCfgFindFile (
388 IN CONST CHAR8 *Name,
389 OUT FIRMWARE_CONFIG_ITEM *Item,
390 OUT UINTN *Size
391 )
392 {
393 UINT32 Count;
394 UINT32 Idx;
395
396 if (!InternalQemuFwCfgIsAvailable ()) {
397 return RETURN_UNSUPPORTED;
398 }
399
400 QemuFwCfgSelectItem (QemuFwCfgItemFileDir);
401 Count = SwapBytes32 (QemuFwCfgRead32 ());
402
403 for (Idx = 0; Idx < Count; ++Idx) {
404 UINT32 FileSize;
405 UINT16 FileSelect;
406 UINT16 FileReserved;
407 CHAR8 FName[QEMU_FW_CFG_FNAME_SIZE];
408
409 FileSize = QemuFwCfgRead32 ();
410 FileSelect = QemuFwCfgRead16 ();
411 FileReserved = QemuFwCfgRead16 ();
412 (VOID) FileReserved; /* Force a do-nothing reference. */
413 InternalQemuFwCfgReadBytes (sizeof (FName), FName);
414
415 if (AsciiStrCmp (Name, FName) == 0) {
416 *Item = SwapBytes16 (FileSelect);
417 *Size = SwapBytes32 (FileSize);
418 return RETURN_SUCCESS;
419 }
420 }
421
422 return RETURN_NOT_FOUND;
423 }