]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c
ArmVirtPkg, OvmfPkg: QemuFwCfgLib: move DMA-related defs to lib class
[mirror_edk2.git] / ArmVirtPkg / Library / QemuFwCfgLib / QemuFwCfgLib.c
CommitLineData
6e2543b0
LE
1/** @file\r
2\r
3 Stateful and implicitly initialized fw_cfg library implementation.\r
4\r
5 Copyright (C) 2013 - 2014, Red Hat, Inc.\r
6 Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>\r
7\r
8 This program and the accompanying materials are licensed and made available\r
9 under the terms and conditions of the BSD License which accompanies this\r
10 distribution. The full text of the license may be found at\r
11 http://opensource.org/licenses/bsd-license.php\r
12\r
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
14 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15**/\r
16\r
7b6745cc
AB
17#include <Uefi.h>\r
18\r
6e2543b0
LE
19#include <Library/BaseLib.h>\r
20#include <Library/BaseMemoryLib.h>\r
953bcbcc 21#include <Library/DebugLib.h>\r
6e2543b0 22#include <Library/IoLib.h>\r
6e2543b0 23#include <Library/QemuFwCfgLib.h>\r
7b6745cc
AB
24#include <Library/UefiBootServicesTableLib.h>\r
25\r
26#include <Protocol/FdtClient.h>\r
6e2543b0
LE
27\r
28STATIC UINTN mFwCfgSelectorAddress;\r
29STATIC UINTN mFwCfgDataAddress;\r
953bcbcc
LE
30STATIC UINTN mFwCfgDmaAddress;\r
31\r
32/**\r
33 Reads firmware configuration bytes into a buffer\r
34\r
35 @param[in] Size Size in bytes to read\r
36 @param[in] Buffer Buffer to store data into (OPTIONAL if Size is 0)\r
37\r
38**/\r
39typedef\r
40VOID (EFIAPI READ_BYTES_FUNCTION) (\r
41 IN UINTN Size,\r
42 IN VOID *Buffer OPTIONAL\r
43 );\r
44\r
45//\r
46// Forward declaration of the two implementations we have.\r
47//\r
48STATIC READ_BYTES_FUNCTION MmioReadBytes;\r
49STATIC READ_BYTES_FUNCTION DmaReadBytes;\r
50\r
51//\r
52// This points to the one we detect at runtime.\r
53//\r
54STATIC READ_BYTES_FUNCTION *InternalQemuFwCfgReadBytes = MmioReadBytes;\r
55\r
6e2543b0 56\r
6e2543b0
LE
57/**\r
58 Returns a boolean indicating if the firmware configuration interface\r
59 is available or not.\r
60\r
61 This function may change fw_cfg state.\r
62\r
63 @retval TRUE The interface is available\r
64 @retval FALSE The interface is not available\r
65\r
66**/\r
67BOOLEAN\r
68EFIAPI\r
69QemuFwCfgIsAvailable (\r
70 VOID\r
71 )\r
72{\r
1cb33be9 73 return (BOOLEAN)(mFwCfgSelectorAddress != 0 && mFwCfgDataAddress != 0);\r
6e2543b0
LE
74}\r
75\r
76\r
77RETURN_STATUS\r
78EFIAPI\r
79QemuFwCfgInitialize (\r
80 VOID\r
81 )\r
82{\r
7b6745cc
AB
83 EFI_STATUS Status;\r
84 FDT_CLIENT_PROTOCOL *FdtClient;\r
85 CONST UINT64 *Reg;\r
cfc8d51c
AB
86 UINT32 RegSize;\r
87 UINTN AddressCells, SizeCells;\r
7b6745cc
AB
88 UINT64 FwCfgSelectorAddress;\r
89 UINT64 FwCfgSelectorSize;\r
90 UINT64 FwCfgDataAddress;\r
91 UINT64 FwCfgDataSize;\r
92 UINT64 FwCfgDmaAddress;\r
93 UINT64 FwCfgDmaSize;\r
94\r
95 Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
96 (VOID **)&FdtClient);\r
97 ASSERT_EFI_ERROR (Status);\r
98\r
99 Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio",\r
cfc8d51c
AB
100 (CONST VOID **)&Reg, &AddressCells, &SizeCells,\r
101 &RegSize);\r
7b6745cc
AB
102 if (EFI_ERROR (Status)) {\r
103 DEBUG ((EFI_D_WARN,\r
104 "%a: No 'qemu,fw-cfg-mmio' compatible DT node found (Status == %r)\n",\r
105 __FUNCTION__, Status));\r
106 return EFI_SUCCESS;\r
107 }\r
108\r
cfc8d51c
AB
109 ASSERT (AddressCells == 2);\r
110 ASSERT (SizeCells == 2);\r
7b6745cc
AB
111 ASSERT (RegSize == 2 * sizeof (UINT64));\r
112\r
113 FwCfgDataAddress = SwapBytes64 (Reg[0]);\r
114 FwCfgDataSize = 8;\r
115 FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;\r
116 FwCfgSelectorSize = 2;\r
117\r
118 //\r
119 // The following ASSERT()s express\r
120 //\r
121 // Address + Size - 1 <= MAX_UINTN\r
122 //\r
123 // for both registers, that is, that the last byte in each MMIO range is\r
124 // expressible as a MAX_UINTN. The form below is mathematically\r
125 // equivalent, and it also prevents any unsigned overflow before the\r
126 // comparison.\r
127 //\r
128 ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);\r
129 ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);\r
130\r
131 mFwCfgSelectorAddress = FwCfgSelectorAddress;\r
132 mFwCfgDataAddress = FwCfgDataAddress;\r
133\r
134 DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,\r
135 FwCfgDataAddress));\r
136\r
137 if (SwapBytes64 (Reg[1]) >= 0x18) {\r
138 FwCfgDmaAddress = FwCfgDataAddress + 0x10;\r
139 FwCfgDmaSize = 0x08;\r
140\r
141 //\r
142 // See explanation above.\r
143 //\r
144 ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);\r
145\r
146 DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));\r
147 } else {\r
148 FwCfgDmaAddress = 0;\r
149 }\r
6e2543b0 150\r
1cb33be9 151 if (QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
152 UINT32 Signature;\r
153\r
154 QemuFwCfgSelectItem (QemuFwCfgItemSignature);\r
155 Signature = QemuFwCfgRead32 ();\r
953bcbcc
LE
156 if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) {\r
157 //\r
158 // For DMA support, we require the DTB to advertise the register, and the\r
159 // feature bitmap (which we read without DMA) to confirm the feature.\r
160 //\r
7b6745cc 161 if (FwCfgDmaAddress != 0) {\r
953bcbcc
LE
162 UINT32 Features;\r
163\r
164 QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);\r
165 Features = QemuFwCfgRead32 ();\r
166 if ((Features & BIT1) != 0) {\r
7b6745cc 167 mFwCfgDmaAddress = FwCfgDmaAddress;\r
953bcbcc
LE
168 InternalQemuFwCfgReadBytes = DmaReadBytes;\r
169 }\r
170 }\r
171 } else {\r
6e2543b0
LE
172 mFwCfgSelectorAddress = 0;\r
173 mFwCfgDataAddress = 0;\r
174 }\r
175 }\r
176 return RETURN_SUCCESS;\r
177}\r
178\r
179\r
180/**\r
181 Selects a firmware configuration item for reading.\r
182\r
183 Following this call, any data read from this item will start from the\r
184 beginning of the configuration item's data.\r
185\r
186 @param[in] QemuFwCfgItem Firmware Configuration item to read\r
187\r
188**/\r
189VOID\r
190EFIAPI\r
191QemuFwCfgSelectItem (\r
192 IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem\r
193 )\r
194{\r
1cb33be9 195 if (QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
196 MmioWrite16 (mFwCfgSelectorAddress, SwapBytes16 ((UINT16)QemuFwCfgItem));\r
197 }\r
198}\r
199\r
200\r
201/**\r
953bcbcc 202 Slow READ_BYTES_FUNCTION.\r
6e2543b0
LE
203**/\r
204STATIC\r
205VOID\r
206EFIAPI\r
953bcbcc 207MmioReadBytes (\r
6e2543b0
LE
208 IN UINTN Size,\r
209 IN VOID *Buffer OPTIONAL\r
210 )\r
211{\r
212 UINTN Left;\r
213 UINT8 *Ptr;\r
214 UINT8 *End;\r
215\r
216#ifdef MDE_CPU_AARCH64\r
217 Left = Size & 7;\r
218#else\r
219 Left = Size & 3;\r
220#endif\r
221\r
222 Size -= Left;\r
223 Ptr = Buffer;\r
224 End = Ptr + Size;\r
225\r
226#ifdef MDE_CPU_AARCH64\r
227 while (Ptr < End) {\r
228 *(UINT64 *)Ptr = MmioRead64 (mFwCfgDataAddress);\r
229 Ptr += 8;\r
230 }\r
231 if (Left & 4) {\r
232 *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
233 Ptr += 4;\r
234 }\r
235#else\r
236 while (Ptr < End) {\r
237 *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
238 Ptr += 4;\r
239 }\r
240#endif\r
241\r
242 if (Left & 2) {\r
243 *(UINT16 *)Ptr = MmioRead16 (mFwCfgDataAddress);\r
244 Ptr += 2;\r
245 }\r
246 if (Left & 1) {\r
247 *Ptr = MmioRead8 (mFwCfgDataAddress);\r
248 }\r
249}\r
250\r
251\r
953bcbcc
LE
252/**\r
253 Fast READ_BYTES_FUNCTION.\r
254**/\r
255STATIC\r
256VOID\r
257EFIAPI\r
258DmaReadBytes (\r
259 IN UINTN Size,\r
260 IN VOID *Buffer OPTIONAL\r
261 )\r
262{\r
263 volatile FW_CFG_DMA_ACCESS Access;\r
264 UINT32 Status;\r
265\r
266 if (Size == 0) {\r
267 return;\r
268 }\r
269\r
270 ASSERT (Size <= MAX_UINT32);\r
271\r
272 Access.Control = SwapBytes32 (FW_CFG_DMA_CTL_READ);\r
273 Access.Length = SwapBytes32 ((UINT32)Size);\r
274 Access.Address = SwapBytes64 ((UINT64)(UINTN)Buffer);\r
275\r
276 //\r
277 // We shouldn't start the transfer before setting up Access.\r
278 //\r
279 MemoryFence ();\r
280\r
281 //\r
282 // This will fire off the transfer.\r
283 //\r
284#ifdef MDE_CPU_AARCH64\r
285 MmioWrite64 (mFwCfgDmaAddress, SwapBytes64 ((UINT64)&Access));\r
286#else\r
287 MmioWrite32 ((UINT32)(mFwCfgDmaAddress + 4), SwapBytes32 ((UINT32)&Access));\r
288#endif\r
289\r
290 //\r
291 // We shouldn't look at Access.Control before starting the transfer.\r
292 //\r
293 MemoryFence ();\r
294\r
295 do {\r
296 Status = SwapBytes32 (Access.Control);\r
297 ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);\r
298 } while (Status != 0);\r
299\r
300 //\r
301 // The caller will want to access the transferred data.\r
302 //\r
303 MemoryFence ();\r
304}\r
305\r
306\r
6e2543b0
LE
307/**\r
308 Reads firmware configuration bytes into a buffer\r
309\r
310 If called multiple times, then the data read will continue at the offset of\r
311 the firmware configuration item where the previous read ended.\r
312\r
313 @param[in] Size Size in bytes to read\r
314 @param[in] Buffer Buffer to store data into\r
315\r
316**/\r
317VOID\r
318EFIAPI\r
319QemuFwCfgReadBytes (\r
320 IN UINTN Size,\r
321 IN VOID *Buffer\r
322 )\r
323{\r
1cb33be9 324 if (QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
325 InternalQemuFwCfgReadBytes (Size, Buffer);\r
326 } else {\r
327 ZeroMem (Buffer, Size);\r
328 }\r
329}\r
330\r
331/**\r
332 Write firmware configuration bytes from a buffer\r
333\r
334 If called multiple times, then the data written will continue at the offset\r
335 of the firmware configuration item where the previous write ended.\r
336\r
337 @param[in] Size Size in bytes to write\r
338 @param[in] Buffer Buffer to read data from\r
339\r
340**/\r
341VOID\r
342EFIAPI\r
343QemuFwCfgWriteBytes (\r
344 IN UINTN Size,\r
345 IN VOID *Buffer\r
346 )\r
347{\r
1cb33be9 348 if (QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
349 UINTN Idx;\r
350\r
351 for (Idx = 0; Idx < Size; ++Idx) {\r
352 MmioWrite8 (mFwCfgDataAddress, ((UINT8 *)Buffer)[Idx]);\r
353 }\r
354 }\r
355}\r
356\r
357\r
358/**\r
359 Reads a UINT8 firmware configuration value\r
360\r
361 @return Value of Firmware Configuration item read\r
362\r
363**/\r
364UINT8\r
365EFIAPI\r
366QemuFwCfgRead8 (\r
367 VOID\r
368 )\r
369{\r
370 UINT8 Result;\r
371\r
372 QemuFwCfgReadBytes (sizeof Result, &Result);\r
373 return Result;\r
374}\r
375\r
376\r
377/**\r
378 Reads a UINT16 firmware configuration value\r
379\r
380 @return Value of Firmware Configuration item read\r
381\r
382**/\r
383UINT16\r
384EFIAPI\r
385QemuFwCfgRead16 (\r
386 VOID\r
387 )\r
388{\r
389 UINT16 Result;\r
390\r
391 QemuFwCfgReadBytes (sizeof Result, &Result);\r
392 return Result;\r
393}\r
394\r
395\r
396/**\r
397 Reads a UINT32 firmware configuration value\r
398\r
399 @return Value of Firmware Configuration item read\r
400\r
401**/\r
402UINT32\r
403EFIAPI\r
404QemuFwCfgRead32 (\r
405 VOID\r
406 )\r
407{\r
408 UINT32 Result;\r
409\r
410 QemuFwCfgReadBytes (sizeof Result, &Result);\r
411 return Result;\r
412}\r
413\r
414\r
415/**\r
416 Reads a UINT64 firmware configuration value\r
417\r
418 @return Value of Firmware Configuration item read\r
419\r
420**/\r
421UINT64\r
422EFIAPI\r
423QemuFwCfgRead64 (\r
424 VOID\r
425 )\r
426{\r
427 UINT64 Result;\r
428\r
429 QemuFwCfgReadBytes (sizeof Result, &Result);\r
430 return Result;\r
431}\r
432\r
433\r
434/**\r
435 Find the configuration item corresponding to the firmware configuration file.\r
436\r
437 @param[in] Name Name of file to look up.\r
438 @param[out] Item Configuration item corresponding to the file, to be passed\r
439 to QemuFwCfgSelectItem ().\r
440 @param[out] Size Number of bytes in the file.\r
441\r
442 @retval RETURN_SUCCESS If file is found.\r
443 @retval RETURN_NOT_FOUND If file is not found.\r
444 @retval RETURN_UNSUPPORTED If firmware configuration is unavailable.\r
445\r
446**/\r
447RETURN_STATUS\r
448EFIAPI\r
449QemuFwCfgFindFile (\r
450 IN CONST CHAR8 *Name,\r
451 OUT FIRMWARE_CONFIG_ITEM *Item,\r
452 OUT UINTN *Size\r
453 )\r
454{\r
455 UINT32 Count;\r
456 UINT32 Idx;\r
457\r
1cb33be9 458 if (!QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
459 return RETURN_UNSUPPORTED;\r
460 }\r
461\r
462 QemuFwCfgSelectItem (QemuFwCfgItemFileDir);\r
463 Count = SwapBytes32 (QemuFwCfgRead32 ());\r
464\r
465 for (Idx = 0; Idx < Count; ++Idx) {\r
466 UINT32 FileSize;\r
467 UINT16 FileSelect;\r
468 CHAR8 FName[QEMU_FW_CFG_FNAME_SIZE];\r
469\r
470 FileSize = QemuFwCfgRead32 ();\r
471 FileSelect = QemuFwCfgRead16 ();\r
472 QemuFwCfgRead16 (); // skip the field called "reserved"\r
473 InternalQemuFwCfgReadBytes (sizeof (FName), FName);\r
474\r
475 if (AsciiStrCmp (Name, FName) == 0) {\r
3f318fbf 476 *Item = (FIRMWARE_CONFIG_ITEM) SwapBytes16 (FileSelect);\r
6e2543b0
LE
477 *Size = SwapBytes32 (FileSize);\r
478 return RETURN_SUCCESS;\r
479 }\r
480 }\r
481\r
482 return RETURN_NOT_FOUND;\r
483}\r
484\r
485\r
486/**\r
487 Determine if S3 support is explicitly enabled.\r
488\r
489 @retval TRUE if S3 support is explicitly enabled.\r
490 FALSE otherwise. This includes unavailability of the firmware\r
491 configuration interface.\r
492**/\r
493BOOLEAN\r
494EFIAPI\r
495QemuFwCfgS3Enabled (\r
496 VOID\r
497 )\r
498{\r
499 return FALSE;\r
500}\r