]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c
ArmVirtPkg/FdtClientDxe: report address and size cell count directly
[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
56//\r
57// Communication structure for DmaReadBytes(). All fields are encoded in big\r
58// endian.\r
59//\r
60#pragma pack (1)\r
61typedef struct {\r
62 UINT32 Control;\r
63 UINT32 Length;\r
64 UINT64 Address;\r
65} FW_CFG_DMA_ACCESS;\r
66#pragma pack ()\r
67\r
68//\r
69// Macros for the FW_CFG_DMA_ACCESS.Control bitmap (in native encoding).\r
70//\r
71#define FW_CFG_DMA_CTL_ERROR BIT0\r
72#define FW_CFG_DMA_CTL_READ BIT1\r
73#define FW_CFG_DMA_CTL_SKIP BIT2\r
74#define FW_CFG_DMA_CTL_SELECT BIT3\r
6e2543b0
LE
75\r
76\r
77/**\r
78 Returns a boolean indicating if the firmware configuration interface is\r
79 available for library-internal purposes.\r
80\r
81 This function never changes fw_cfg state.\r
82\r
83 @retval TRUE The interface is available internally.\r
84 @retval FALSE The interface is not available internally.\r
85**/\r
86BOOLEAN\r
87EFIAPI\r
88InternalQemuFwCfgIsAvailable (\r
89 VOID\r
90 )\r
91{\r
92 return (BOOLEAN)(mFwCfgSelectorAddress != 0 && mFwCfgDataAddress != 0);\r
93}\r
94\r
95\r
96/**\r
97 Returns a boolean indicating if the firmware configuration interface\r
98 is available or not.\r
99\r
100 This function may change fw_cfg state.\r
101\r
102 @retval TRUE The interface is available\r
103 @retval FALSE The interface is not available\r
104\r
105**/\r
106BOOLEAN\r
107EFIAPI\r
108QemuFwCfgIsAvailable (\r
109 VOID\r
110 )\r
111{\r
112 return InternalQemuFwCfgIsAvailable ();\r
113}\r
114\r
115\r
116RETURN_STATUS\r
117EFIAPI\r
118QemuFwCfgInitialize (\r
119 VOID\r
120 )\r
121{\r
7b6745cc
AB
122 EFI_STATUS Status;\r
123 FDT_CLIENT_PROTOCOL *FdtClient;\r
124 CONST UINT64 *Reg;\r
cfc8d51c
AB
125 UINT32 RegSize;\r
126 UINTN AddressCells, SizeCells;\r
7b6745cc
AB
127 UINT64 FwCfgSelectorAddress;\r
128 UINT64 FwCfgSelectorSize;\r
129 UINT64 FwCfgDataAddress;\r
130 UINT64 FwCfgDataSize;\r
131 UINT64 FwCfgDmaAddress;\r
132 UINT64 FwCfgDmaSize;\r
133\r
134 Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
135 (VOID **)&FdtClient);\r
136 ASSERT_EFI_ERROR (Status);\r
137\r
138 Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio",\r
cfc8d51c
AB
139 (CONST VOID **)&Reg, &AddressCells, &SizeCells,\r
140 &RegSize);\r
7b6745cc
AB
141 if (EFI_ERROR (Status)) {\r
142 DEBUG ((EFI_D_WARN,\r
143 "%a: No 'qemu,fw-cfg-mmio' compatible DT node found (Status == %r)\n",\r
144 __FUNCTION__, Status));\r
145 return EFI_SUCCESS;\r
146 }\r
147\r
cfc8d51c
AB
148 ASSERT (AddressCells == 2);\r
149 ASSERT (SizeCells == 2);\r
7b6745cc
AB
150 ASSERT (RegSize == 2 * sizeof (UINT64));\r
151\r
152 FwCfgDataAddress = SwapBytes64 (Reg[0]);\r
153 FwCfgDataSize = 8;\r
154 FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;\r
155 FwCfgSelectorSize = 2;\r
156\r
157 //\r
158 // The following ASSERT()s express\r
159 //\r
160 // Address + Size - 1 <= MAX_UINTN\r
161 //\r
162 // for both registers, that is, that the last byte in each MMIO range is\r
163 // expressible as a MAX_UINTN. The form below is mathematically\r
164 // equivalent, and it also prevents any unsigned overflow before the\r
165 // comparison.\r
166 //\r
167 ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);\r
168 ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);\r
169\r
170 mFwCfgSelectorAddress = FwCfgSelectorAddress;\r
171 mFwCfgDataAddress = FwCfgDataAddress;\r
172\r
173 DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,\r
174 FwCfgDataAddress));\r
175\r
176 if (SwapBytes64 (Reg[1]) >= 0x18) {\r
177 FwCfgDmaAddress = FwCfgDataAddress + 0x10;\r
178 FwCfgDmaSize = 0x08;\r
179\r
180 //\r
181 // See explanation above.\r
182 //\r
183 ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);\r
184\r
185 DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));\r
186 } else {\r
187 FwCfgDmaAddress = 0;\r
188 }\r
6e2543b0
LE
189\r
190 if (InternalQemuFwCfgIsAvailable ()) {\r
191 UINT32 Signature;\r
192\r
193 QemuFwCfgSelectItem (QemuFwCfgItemSignature);\r
194 Signature = QemuFwCfgRead32 ();\r
953bcbcc
LE
195 if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) {\r
196 //\r
197 // For DMA support, we require the DTB to advertise the register, and the\r
198 // feature bitmap (which we read without DMA) to confirm the feature.\r
199 //\r
7b6745cc 200 if (FwCfgDmaAddress != 0) {\r
953bcbcc
LE
201 UINT32 Features;\r
202\r
203 QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);\r
204 Features = QemuFwCfgRead32 ();\r
205 if ((Features & BIT1) != 0) {\r
7b6745cc 206 mFwCfgDmaAddress = FwCfgDmaAddress;\r
953bcbcc
LE
207 InternalQemuFwCfgReadBytes = DmaReadBytes;\r
208 }\r
209 }\r
210 } else {\r
6e2543b0
LE
211 mFwCfgSelectorAddress = 0;\r
212 mFwCfgDataAddress = 0;\r
213 }\r
214 }\r
215 return RETURN_SUCCESS;\r
216}\r
217\r
218\r
219/**\r
220 Selects a firmware configuration item for reading.\r
221\r
222 Following this call, any data read from this item will start from the\r
223 beginning of the configuration item's data.\r
224\r
225 @param[in] QemuFwCfgItem Firmware Configuration item to read\r
226\r
227**/\r
228VOID\r
229EFIAPI\r
230QemuFwCfgSelectItem (\r
231 IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem\r
232 )\r
233{\r
234 if (InternalQemuFwCfgIsAvailable ()) {\r
235 MmioWrite16 (mFwCfgSelectorAddress, SwapBytes16 ((UINT16)QemuFwCfgItem));\r
236 }\r
237}\r
238\r
239\r
240/**\r
953bcbcc 241 Slow READ_BYTES_FUNCTION.\r
6e2543b0
LE
242**/\r
243STATIC\r
244VOID\r
245EFIAPI\r
953bcbcc 246MmioReadBytes (\r
6e2543b0
LE
247 IN UINTN Size,\r
248 IN VOID *Buffer OPTIONAL\r
249 )\r
250{\r
251 UINTN Left;\r
252 UINT8 *Ptr;\r
253 UINT8 *End;\r
254\r
255#ifdef MDE_CPU_AARCH64\r
256 Left = Size & 7;\r
257#else\r
258 Left = Size & 3;\r
259#endif\r
260\r
261 Size -= Left;\r
262 Ptr = Buffer;\r
263 End = Ptr + Size;\r
264\r
265#ifdef MDE_CPU_AARCH64\r
266 while (Ptr < End) {\r
267 *(UINT64 *)Ptr = MmioRead64 (mFwCfgDataAddress);\r
268 Ptr += 8;\r
269 }\r
270 if (Left & 4) {\r
271 *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
272 Ptr += 4;\r
273 }\r
274#else\r
275 while (Ptr < End) {\r
276 *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
277 Ptr += 4;\r
278 }\r
279#endif\r
280\r
281 if (Left & 2) {\r
282 *(UINT16 *)Ptr = MmioRead16 (mFwCfgDataAddress);\r
283 Ptr += 2;\r
284 }\r
285 if (Left & 1) {\r
286 *Ptr = MmioRead8 (mFwCfgDataAddress);\r
287 }\r
288}\r
289\r
290\r
953bcbcc
LE
291/**\r
292 Fast READ_BYTES_FUNCTION.\r
293**/\r
294STATIC\r
295VOID\r
296EFIAPI\r
297DmaReadBytes (\r
298 IN UINTN Size,\r
299 IN VOID *Buffer OPTIONAL\r
300 )\r
301{\r
302 volatile FW_CFG_DMA_ACCESS Access;\r
303 UINT32 Status;\r
304\r
305 if (Size == 0) {\r
306 return;\r
307 }\r
308\r
309 ASSERT (Size <= MAX_UINT32);\r
310\r
311 Access.Control = SwapBytes32 (FW_CFG_DMA_CTL_READ);\r
312 Access.Length = SwapBytes32 ((UINT32)Size);\r
313 Access.Address = SwapBytes64 ((UINT64)(UINTN)Buffer);\r
314\r
315 //\r
316 // We shouldn't start the transfer before setting up Access.\r
317 //\r
318 MemoryFence ();\r
319\r
320 //\r
321 // This will fire off the transfer.\r
322 //\r
323#ifdef MDE_CPU_AARCH64\r
324 MmioWrite64 (mFwCfgDmaAddress, SwapBytes64 ((UINT64)&Access));\r
325#else\r
326 MmioWrite32 ((UINT32)(mFwCfgDmaAddress + 4), SwapBytes32 ((UINT32)&Access));\r
327#endif\r
328\r
329 //\r
330 // We shouldn't look at Access.Control before starting the transfer.\r
331 //\r
332 MemoryFence ();\r
333\r
334 do {\r
335 Status = SwapBytes32 (Access.Control);\r
336 ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);\r
337 } while (Status != 0);\r
338\r
339 //\r
340 // The caller will want to access the transferred data.\r
341 //\r
342 MemoryFence ();\r
343}\r
344\r
345\r
6e2543b0
LE
346/**\r
347 Reads firmware configuration bytes into a buffer\r
348\r
349 If called multiple times, then the data read will continue at the offset of\r
350 the firmware configuration item where the previous read ended.\r
351\r
352 @param[in] Size Size in bytes to read\r
353 @param[in] Buffer Buffer to store data into\r
354\r
355**/\r
356VOID\r
357EFIAPI\r
358QemuFwCfgReadBytes (\r
359 IN UINTN Size,\r
360 IN VOID *Buffer\r
361 )\r
362{\r
363 if (InternalQemuFwCfgIsAvailable ()) {\r
364 InternalQemuFwCfgReadBytes (Size, Buffer);\r
365 } else {\r
366 ZeroMem (Buffer, Size);\r
367 }\r
368}\r
369\r
370/**\r
371 Write firmware configuration bytes from a buffer\r
372\r
373 If called multiple times, then the data written will continue at the offset\r
374 of the firmware configuration item where the previous write ended.\r
375\r
376 @param[in] Size Size in bytes to write\r
377 @param[in] Buffer Buffer to read data from\r
378\r
379**/\r
380VOID\r
381EFIAPI\r
382QemuFwCfgWriteBytes (\r
383 IN UINTN Size,\r
384 IN VOID *Buffer\r
385 )\r
386{\r
387 if (InternalQemuFwCfgIsAvailable ()) {\r
388 UINTN Idx;\r
389\r
390 for (Idx = 0; Idx < Size; ++Idx) {\r
391 MmioWrite8 (mFwCfgDataAddress, ((UINT8 *)Buffer)[Idx]);\r
392 }\r
393 }\r
394}\r
395\r
396\r
397/**\r
398 Reads a UINT8 firmware configuration value\r
399\r
400 @return Value of Firmware Configuration item read\r
401\r
402**/\r
403UINT8\r
404EFIAPI\r
405QemuFwCfgRead8 (\r
406 VOID\r
407 )\r
408{\r
409 UINT8 Result;\r
410\r
411 QemuFwCfgReadBytes (sizeof Result, &Result);\r
412 return Result;\r
413}\r
414\r
415\r
416/**\r
417 Reads a UINT16 firmware configuration value\r
418\r
419 @return Value of Firmware Configuration item read\r
420\r
421**/\r
422UINT16\r
423EFIAPI\r
424QemuFwCfgRead16 (\r
425 VOID\r
426 )\r
427{\r
428 UINT16 Result;\r
429\r
430 QemuFwCfgReadBytes (sizeof Result, &Result);\r
431 return Result;\r
432}\r
433\r
434\r
435/**\r
436 Reads a UINT32 firmware configuration value\r
437\r
438 @return Value of Firmware Configuration item read\r
439\r
440**/\r
441UINT32\r
442EFIAPI\r
443QemuFwCfgRead32 (\r
444 VOID\r
445 )\r
446{\r
447 UINT32 Result;\r
448\r
449 QemuFwCfgReadBytes (sizeof Result, &Result);\r
450 return Result;\r
451}\r
452\r
453\r
454/**\r
455 Reads a UINT64 firmware configuration value\r
456\r
457 @return Value of Firmware Configuration item read\r
458\r
459**/\r
460UINT64\r
461EFIAPI\r
462QemuFwCfgRead64 (\r
463 VOID\r
464 )\r
465{\r
466 UINT64 Result;\r
467\r
468 QemuFwCfgReadBytes (sizeof Result, &Result);\r
469 return Result;\r
470}\r
471\r
472\r
473/**\r
474 Find the configuration item corresponding to the firmware configuration file.\r
475\r
476 @param[in] Name Name of file to look up.\r
477 @param[out] Item Configuration item corresponding to the file, to be passed\r
478 to QemuFwCfgSelectItem ().\r
479 @param[out] Size Number of bytes in the file.\r
480\r
481 @retval RETURN_SUCCESS If file is found.\r
482 @retval RETURN_NOT_FOUND If file is not found.\r
483 @retval RETURN_UNSUPPORTED If firmware configuration is unavailable.\r
484\r
485**/\r
486RETURN_STATUS\r
487EFIAPI\r
488QemuFwCfgFindFile (\r
489 IN CONST CHAR8 *Name,\r
490 OUT FIRMWARE_CONFIG_ITEM *Item,\r
491 OUT UINTN *Size\r
492 )\r
493{\r
494 UINT32 Count;\r
495 UINT32 Idx;\r
496\r
497 if (!InternalQemuFwCfgIsAvailable ()) {\r
498 return RETURN_UNSUPPORTED;\r
499 }\r
500\r
501 QemuFwCfgSelectItem (QemuFwCfgItemFileDir);\r
502 Count = SwapBytes32 (QemuFwCfgRead32 ());\r
503\r
504 for (Idx = 0; Idx < Count; ++Idx) {\r
505 UINT32 FileSize;\r
506 UINT16 FileSelect;\r
507 CHAR8 FName[QEMU_FW_CFG_FNAME_SIZE];\r
508\r
509 FileSize = QemuFwCfgRead32 ();\r
510 FileSelect = QemuFwCfgRead16 ();\r
511 QemuFwCfgRead16 (); // skip the field called "reserved"\r
512 InternalQemuFwCfgReadBytes (sizeof (FName), FName);\r
513\r
514 if (AsciiStrCmp (Name, FName) == 0) {\r
3f318fbf 515 *Item = (FIRMWARE_CONFIG_ITEM) SwapBytes16 (FileSelect);\r
6e2543b0
LE
516 *Size = SwapBytes32 (FileSize);\r
517 return RETURN_SUCCESS;\r
518 }\r
519 }\r
520\r
521 return RETURN_NOT_FOUND;\r
522}\r
523\r
524\r
525/**\r
526 Determine if S3 support is explicitly enabled.\r
527\r
528 @retval TRUE if S3 support is explicitly enabled.\r
529 FALSE otherwise. This includes unavailability of the firmware\r
530 configuration interface.\r
531**/\r
532BOOLEAN\r
533EFIAPI\r
534QemuFwCfgS3Enabled (\r
535 VOID\r
536 )\r
537{\r
538 return FALSE;\r
539}\r