OvmfPkg/QemuFwCfgLib: Add option to dynamic alloc FW_CFG_DMA Access
[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
76 ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
77 Control == FW_CFG_DMA_CTL_SKIP);
78
79 if (Size == 0) {
80 return;
81 }
82
83 Access = &LocalAccess;
84
85 Access->Control = SwapBytes32 (Control);
86 Access->Length = SwapBytes32 (Size);
87 Access->Address = SwapBytes64 ((UINTN)Buffer);
88
89 //
90 // Delimit the transfer from (a) modifications to Access, (b) in case of a
91 // write, from writes to Buffer by the caller.
92 //
93 MemoryFence ();
94
95 //
96 // Start the transfer.
97 //
98 AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32);
99 AccessLow = (UINT32)(UINTN)Access;
100 IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh));
101 IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow));
102
103 //
104 // Don't look at Access.Control before starting the transfer.
105 //
106 MemoryFence ();
107
108 //
109 // Wait for the transfer to complete.
110 //
111 do {
112 Status = SwapBytes32 (Access->Control);
113 ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);
114 } while (Status != 0);
115
116 //
117 // After a read, the caller will want to use Buffer.
118 //
119 MemoryFence ();
120 }
121
122
123 /**
124 Reads firmware configuration bytes into a buffer
125
126 @param[in] Size - Size in bytes to read
127 @param[in] Buffer - Buffer to store data into (OPTIONAL if Size is 0)
128
129 **/
130 VOID
131 EFIAPI
132 InternalQemuFwCfgReadBytes (
133 IN UINTN Size,
134 IN VOID *Buffer OPTIONAL
135 )
136 {
137 if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
138 InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_READ);
139 return;
140 }
141 IoReadFifo8 (FW_CFG_IO_DATA, Size, Buffer);
142 }
143
144
145 /**
146 Reads firmware configuration bytes into a buffer
147
148 If called multiple times, then the data read will
149 continue at the offset of the firmware configuration
150 item where the previous read ended.
151
152 @param[in] Size - Size in bytes to read
153 @param[in] Buffer - Buffer to store data into
154
155 **/
156 VOID
157 EFIAPI
158 QemuFwCfgReadBytes (
159 IN UINTN Size,
160 IN VOID *Buffer
161 )
162 {
163 if (InternalQemuFwCfgIsAvailable ()) {
164 InternalQemuFwCfgReadBytes (Size, Buffer);
165 } else {
166 ZeroMem (Buffer, Size);
167 }
168 }
169
170 /**
171 Write firmware configuration bytes from a buffer
172
173 If called multiple times, then the data written will
174 continue at the offset of the firmware configuration
175 item where the previous write ended.
176
177 @param[in] Size - Size in bytes to write
178 @param[in] Buffer - Buffer to read data from
179
180 **/
181 VOID
182 EFIAPI
183 QemuFwCfgWriteBytes (
184 IN UINTN Size,
185 IN VOID *Buffer
186 )
187 {
188 if (InternalQemuFwCfgIsAvailable ()) {
189 if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
190 InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_WRITE);
191 return;
192 }
193 IoWriteFifo8 (FW_CFG_IO_DATA, Size, Buffer);
194 }
195 }
196
197
198 /**
199 Skip bytes in the firmware configuration item.
200
201 Increase the offset of the firmware configuration item without transferring
202 bytes between the item and a caller-provided buffer. Subsequent read, write
203 or skip operations will commence at the increased offset.
204
205 @param[in] Size Number of bytes to skip.
206 **/
207 VOID
208 EFIAPI
209 QemuFwCfgSkipBytes (
210 IN UINTN Size
211 )
212 {
213 UINTN ChunkSize;
214 UINT8 SkipBuffer[256];
215
216 if (!InternalQemuFwCfgIsAvailable ()) {
217 return;
218 }
219
220 if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
221 InternalQemuFwCfgDmaBytes ((UINT32)Size, NULL, FW_CFG_DMA_CTL_SKIP);
222 return;
223 }
224
225 //
226 // Emulate the skip by reading data in chunks, and throwing it away. The
227 // implementation below is suitable even for phases where RAM or dynamic
228 // allocation is not available or appropriate. It also doesn't affect the
229 // static data footprint for client modules. Large skips are not expected,
230 // therefore this fallback is not performance critical. The size of
231 // SkipBuffer is thought not to exert a large pressure on the stack in any
232 // phase.
233 //
234 while (Size > 0) {
235 ChunkSize = MIN (Size, sizeof SkipBuffer);
236 IoReadFifo8 (FW_CFG_IO_DATA, ChunkSize, SkipBuffer);
237 Size -= ChunkSize;
238 }
239 }
240
241
242 /**
243 Reads a UINT8 firmware configuration value
244
245 @return Value of Firmware Configuration item read
246
247 **/
248 UINT8
249 EFIAPI
250 QemuFwCfgRead8 (
251 VOID
252 )
253 {
254 UINT8 Result;
255
256 QemuFwCfgReadBytes (sizeof (Result), &Result);
257
258 return Result;
259 }
260
261
262 /**
263 Reads a UINT16 firmware configuration value
264
265 @return Value of Firmware Configuration item read
266
267 **/
268 UINT16
269 EFIAPI
270 QemuFwCfgRead16 (
271 VOID
272 )
273 {
274 UINT16 Result;
275
276 QemuFwCfgReadBytes (sizeof (Result), &Result);
277
278 return Result;
279 }
280
281
282 /**
283 Reads a UINT32 firmware configuration value
284
285 @return Value of Firmware Configuration item read
286
287 **/
288 UINT32
289 EFIAPI
290 QemuFwCfgRead32 (
291 VOID
292 )
293 {
294 UINT32 Result;
295
296 QemuFwCfgReadBytes (sizeof (Result), &Result);
297
298 return Result;
299 }
300
301
302 /**
303 Reads a UINT64 firmware configuration value
304
305 @return Value of Firmware Configuration item read
306
307 **/
308 UINT64
309 EFIAPI
310 QemuFwCfgRead64 (
311 VOID
312 )
313 {
314 UINT64 Result;
315
316 QemuFwCfgReadBytes (sizeof (Result), &Result);
317
318 return Result;
319 }
320
321
322 /**
323 Find the configuration item corresponding to the firmware configuration file.
324
325 @param[in] Name - Name of file to look up.
326 @param[out] Item - Configuration item corresponding to the file, to be passed
327 to QemuFwCfgSelectItem ().
328 @param[out] Size - Number of bytes in the file.
329
330 @return RETURN_SUCCESS If file is found.
331 RETURN_NOT_FOUND If file is not found.
332 RETURN_UNSUPPORTED If firmware configuration is unavailable.
333
334 **/
335 RETURN_STATUS
336 EFIAPI
337 QemuFwCfgFindFile (
338 IN CONST CHAR8 *Name,
339 OUT FIRMWARE_CONFIG_ITEM *Item,
340 OUT UINTN *Size
341 )
342 {
343 UINT32 Count;
344 UINT32 Idx;
345
346 if (!InternalQemuFwCfgIsAvailable ()) {
347 return RETURN_UNSUPPORTED;
348 }
349
350 QemuFwCfgSelectItem (QemuFwCfgItemFileDir);
351 Count = SwapBytes32 (QemuFwCfgRead32 ());
352
353 for (Idx = 0; Idx < Count; ++Idx) {
354 UINT32 FileSize;
355 UINT16 FileSelect;
356 UINT16 FileReserved;
357 CHAR8 FName[QEMU_FW_CFG_FNAME_SIZE];
358
359 FileSize = QemuFwCfgRead32 ();
360 FileSelect = QemuFwCfgRead16 ();
361 FileReserved = QemuFwCfgRead16 ();
362 (VOID) FileReserved; /* Force a do-nothing reference. */
363 InternalQemuFwCfgReadBytes (sizeof (FName), FName);
364
365 if (AsciiStrCmp (Name, FName) == 0) {
366 *Item = SwapBytes16 (FileSelect);
367 *Size = SwapBytes32 (FileSize);
368 return RETURN_SUCCESS;
369 }
370 }
371
372 return RETURN_NOT_FOUND;
373 }