]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c
ArmVirtPkg, OvmfPkg: retire QemuFwCfgS3Enabled() from QemuFwCfgLib
[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
e8ae381f
LE
45/**\r
46 Writes bytes from a buffer to firmware configuration\r
47\r
48 @param[in] Size Size in bytes to write\r
49 @param[in] Buffer Buffer to transfer data from (OPTIONAL if Size is 0)\r
50\r
51**/\r
52typedef\r
53VOID (EFIAPI WRITE_BYTES_FUNCTION) (\r
54 IN UINTN Size,\r
55 IN VOID *Buffer OPTIONAL\r
56 );\r
57\r
7fcb7354
LE
58/**\r
59 Skips bytes in firmware configuration\r
60\r
61 @param[in] Size Size in bytes to skip\r
62\r
63**/\r
64typedef\r
65VOID (EFIAPI SKIP_BYTES_FUNCTION) (\r
66 IN UINTN Size\r
67 );\r
68\r
953bcbcc
LE
69//\r
70// Forward declaration of the two implementations we have.\r
71//\r
72STATIC READ_BYTES_FUNCTION MmioReadBytes;\r
e8ae381f 73STATIC WRITE_BYTES_FUNCTION MmioWriteBytes;\r
7fcb7354 74STATIC SKIP_BYTES_FUNCTION MmioSkipBytes;\r
953bcbcc 75STATIC READ_BYTES_FUNCTION DmaReadBytes;\r
e8ae381f 76STATIC WRITE_BYTES_FUNCTION DmaWriteBytes;\r
7fcb7354 77STATIC SKIP_BYTES_FUNCTION DmaSkipBytes;\r
953bcbcc
LE
78\r
79//\r
e8ae381f 80// These correspond to the implementation we detect at runtime.\r
953bcbcc
LE
81//\r
82STATIC READ_BYTES_FUNCTION *InternalQemuFwCfgReadBytes = MmioReadBytes;\r
e8ae381f 83STATIC WRITE_BYTES_FUNCTION *InternalQemuFwCfgWriteBytes = MmioWriteBytes;\r
7fcb7354 84STATIC SKIP_BYTES_FUNCTION *InternalQemuFwCfgSkipBytes = MmioSkipBytes;\r
953bcbcc 85\r
6e2543b0 86\r
6e2543b0
LE
87/**\r
88 Returns a boolean indicating if the firmware configuration interface\r
89 is available or not.\r
90\r
91 This function may change fw_cfg state.\r
92\r
93 @retval TRUE The interface is available\r
94 @retval FALSE The interface is not available\r
95\r
96**/\r
97BOOLEAN\r
98EFIAPI\r
99QemuFwCfgIsAvailable (\r
100 VOID\r
101 )\r
102{\r
1cb33be9 103 return (BOOLEAN)(mFwCfgSelectorAddress != 0 && mFwCfgDataAddress != 0);\r
6e2543b0
LE
104}\r
105\r
106\r
107RETURN_STATUS\r
108EFIAPI\r
109QemuFwCfgInitialize (\r
110 VOID\r
111 )\r
112{\r
7b6745cc
AB
113 EFI_STATUS Status;\r
114 FDT_CLIENT_PROTOCOL *FdtClient;\r
115 CONST UINT64 *Reg;\r
cfc8d51c
AB
116 UINT32 RegSize;\r
117 UINTN AddressCells, SizeCells;\r
7b6745cc
AB
118 UINT64 FwCfgSelectorAddress;\r
119 UINT64 FwCfgSelectorSize;\r
120 UINT64 FwCfgDataAddress;\r
121 UINT64 FwCfgDataSize;\r
122 UINT64 FwCfgDmaAddress;\r
123 UINT64 FwCfgDmaSize;\r
124\r
125 Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
126 (VOID **)&FdtClient);\r
127 ASSERT_EFI_ERROR (Status);\r
128\r
129 Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio",\r
cfc8d51c
AB
130 (CONST VOID **)&Reg, &AddressCells, &SizeCells,\r
131 &RegSize);\r
7b6745cc
AB
132 if (EFI_ERROR (Status)) {\r
133 DEBUG ((EFI_D_WARN,\r
134 "%a: No 'qemu,fw-cfg-mmio' compatible DT node found (Status == %r)\n",\r
135 __FUNCTION__, Status));\r
136 return EFI_SUCCESS;\r
137 }\r
138\r
cfc8d51c
AB
139 ASSERT (AddressCells == 2);\r
140 ASSERT (SizeCells == 2);\r
7b6745cc
AB
141 ASSERT (RegSize == 2 * sizeof (UINT64));\r
142\r
143 FwCfgDataAddress = SwapBytes64 (Reg[0]);\r
144 FwCfgDataSize = 8;\r
145 FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;\r
146 FwCfgSelectorSize = 2;\r
147\r
148 //\r
149 // The following ASSERT()s express\r
150 //\r
151 // Address + Size - 1 <= MAX_UINTN\r
152 //\r
153 // for both registers, that is, that the last byte in each MMIO range is\r
154 // expressible as a MAX_UINTN. The form below is mathematically\r
155 // equivalent, and it also prevents any unsigned overflow before the\r
156 // comparison.\r
157 //\r
158 ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);\r
159 ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);\r
160\r
161 mFwCfgSelectorAddress = FwCfgSelectorAddress;\r
162 mFwCfgDataAddress = FwCfgDataAddress;\r
163\r
164 DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,\r
165 FwCfgDataAddress));\r
166\r
167 if (SwapBytes64 (Reg[1]) >= 0x18) {\r
168 FwCfgDmaAddress = FwCfgDataAddress + 0x10;\r
169 FwCfgDmaSize = 0x08;\r
170\r
171 //\r
172 // See explanation above.\r
173 //\r
174 ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);\r
175\r
176 DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));\r
177 } else {\r
178 FwCfgDmaAddress = 0;\r
179 }\r
6e2543b0 180\r
1cb33be9 181 if (QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
182 UINT32 Signature;\r
183\r
184 QemuFwCfgSelectItem (QemuFwCfgItemSignature);\r
185 Signature = QemuFwCfgRead32 ();\r
953bcbcc
LE
186 if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) {\r
187 //\r
188 // For DMA support, we require the DTB to advertise the register, and the\r
189 // feature bitmap (which we read without DMA) to confirm the feature.\r
190 //\r
7b6745cc 191 if (FwCfgDmaAddress != 0) {\r
953bcbcc
LE
192 UINT32 Features;\r
193\r
194 QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);\r
195 Features = QemuFwCfgRead32 ();\r
d61a5f45 196 if ((Features & FW_CFG_F_DMA) != 0) {\r
7b6745cc 197 mFwCfgDmaAddress = FwCfgDmaAddress;\r
953bcbcc 198 InternalQemuFwCfgReadBytes = DmaReadBytes;\r
e8ae381f 199 InternalQemuFwCfgWriteBytes = DmaWriteBytes;\r
7fcb7354 200 InternalQemuFwCfgSkipBytes = DmaSkipBytes;\r
953bcbcc
LE
201 }\r
202 }\r
203 } else {\r
6e2543b0
LE
204 mFwCfgSelectorAddress = 0;\r
205 mFwCfgDataAddress = 0;\r
206 }\r
207 }\r
208 return RETURN_SUCCESS;\r
209}\r
210\r
211\r
212/**\r
213 Selects a firmware configuration item for reading.\r
214\r
215 Following this call, any data read from this item will start from the\r
216 beginning of the configuration item's data.\r
217\r
218 @param[in] QemuFwCfgItem Firmware Configuration item to read\r
219\r
220**/\r
221VOID\r
222EFIAPI\r
223QemuFwCfgSelectItem (\r
224 IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem\r
225 )\r
226{\r
1cb33be9 227 if (QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
228 MmioWrite16 (mFwCfgSelectorAddress, SwapBytes16 ((UINT16)QemuFwCfgItem));\r
229 }\r
230}\r
231\r
232\r
233/**\r
953bcbcc 234 Slow READ_BYTES_FUNCTION.\r
6e2543b0
LE
235**/\r
236STATIC\r
237VOID\r
238EFIAPI\r
953bcbcc 239MmioReadBytes (\r
6e2543b0
LE
240 IN UINTN Size,\r
241 IN VOID *Buffer OPTIONAL\r
242 )\r
243{\r
244 UINTN Left;\r
245 UINT8 *Ptr;\r
246 UINT8 *End;\r
247\r
248#ifdef MDE_CPU_AARCH64\r
249 Left = Size & 7;\r
250#else\r
251 Left = Size & 3;\r
252#endif\r
253\r
254 Size -= Left;\r
255 Ptr = Buffer;\r
256 End = Ptr + Size;\r
257\r
258#ifdef MDE_CPU_AARCH64\r
259 while (Ptr < End) {\r
260 *(UINT64 *)Ptr = MmioRead64 (mFwCfgDataAddress);\r
261 Ptr += 8;\r
262 }\r
263 if (Left & 4) {\r
264 *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
265 Ptr += 4;\r
266 }\r
267#else\r
268 while (Ptr < End) {\r
269 *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
270 Ptr += 4;\r
271 }\r
272#endif\r
273\r
274 if (Left & 2) {\r
275 *(UINT16 *)Ptr = MmioRead16 (mFwCfgDataAddress);\r
276 Ptr += 2;\r
277 }\r
278 if (Left & 1) {\r
279 *Ptr = MmioRead8 (mFwCfgDataAddress);\r
280 }\r
281}\r
282\r
283\r
953bcbcc 284/**\r
4175356f
LE
285 Transfer an array of bytes, or skip a number of bytes, using the DMA\r
286 interface.\r
287\r
288 @param[in] Size Size in bytes to transfer or skip.\r
289\r
290 @param[in,out] Buffer Buffer to read data into or write data from. Ignored,\r
291 and may be NULL, if Size is zero, or Control is\r
292 FW_CFG_DMA_CTL_SKIP.\r
293\r
294 @param[in] Control One of the following:\r
295 FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.\r
296 FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.\r
297 FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.\r
953bcbcc
LE
298**/\r
299STATIC\r
300VOID\r
4175356f
LE
301DmaTransferBytes (\r
302 IN UINTN Size,\r
303 IN OUT VOID *Buffer OPTIONAL,\r
304 IN UINT32 Control\r
953bcbcc
LE
305 )\r
306{\r
307 volatile FW_CFG_DMA_ACCESS Access;\r
308 UINT32 Status;\r
309\r
4175356f
LE
310 ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||\r
311 Control == FW_CFG_DMA_CTL_SKIP);\r
312\r
953bcbcc
LE
313 if (Size == 0) {\r
314 return;\r
315 }\r
316\r
317 ASSERT (Size <= MAX_UINT32);\r
318\r
4175356f 319 Access.Control = SwapBytes32 (Control);\r
953bcbcc
LE
320 Access.Length = SwapBytes32 ((UINT32)Size);\r
321 Access.Address = SwapBytes64 ((UINT64)(UINTN)Buffer);\r
322\r
323 //\r
324 // We shouldn't start the transfer before setting up Access.\r
325 //\r
326 MemoryFence ();\r
327\r
328 //\r
329 // This will fire off the transfer.\r
330 //\r
331#ifdef MDE_CPU_AARCH64\r
332 MmioWrite64 (mFwCfgDmaAddress, SwapBytes64 ((UINT64)&Access));\r
333#else\r
334 MmioWrite32 ((UINT32)(mFwCfgDmaAddress + 4), SwapBytes32 ((UINT32)&Access));\r
335#endif\r
336\r
337 //\r
338 // We shouldn't look at Access.Control before starting the transfer.\r
339 //\r
340 MemoryFence ();\r
341\r
342 do {\r
343 Status = SwapBytes32 (Access.Control);\r
344 ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);\r
345 } while (Status != 0);\r
346\r
347 //\r
348 // The caller will want to access the transferred data.\r
349 //\r
350 MemoryFence ();\r
351}\r
352\r
353\r
4175356f
LE
354/**\r
355 Fast READ_BYTES_FUNCTION.\r
356**/\r
357STATIC\r
358VOID\r
359EFIAPI\r
360DmaReadBytes (\r
361 IN UINTN Size,\r
362 IN VOID *Buffer OPTIONAL\r
363 )\r
364{\r
365 DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_READ);\r
366}\r
367\r
368\r
6e2543b0
LE
369/**\r
370 Reads firmware configuration bytes into a buffer\r
371\r
372 If called multiple times, then the data read will continue at the offset of\r
373 the firmware configuration item where the previous read ended.\r
374\r
375 @param[in] Size Size in bytes to read\r
376 @param[in] Buffer Buffer to store data into\r
377\r
378**/\r
379VOID\r
380EFIAPI\r
381QemuFwCfgReadBytes (\r
382 IN UINTN Size,\r
383 IN VOID *Buffer\r
384 )\r
385{\r
1cb33be9 386 if (QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
387 InternalQemuFwCfgReadBytes (Size, Buffer);\r
388 } else {\r
389 ZeroMem (Buffer, Size);\r
390 }\r
391}\r
392\r
e8ae381f
LE
393\r
394/**\r
395 Slow WRITE_BYTES_FUNCTION.\r
396**/\r
397STATIC\r
398VOID\r
399EFIAPI\r
400MmioWriteBytes (\r
401 IN UINTN Size,\r
402 IN VOID *Buffer OPTIONAL\r
403 )\r
404{\r
405 UINTN Idx;\r
406\r
407 for (Idx = 0; Idx < Size; ++Idx) {\r
408 MmioWrite8 (mFwCfgDataAddress, ((UINT8 *)Buffer)[Idx]);\r
409 }\r
410}\r
411\r
412\r
413/**\r
414 Fast WRITE_BYTES_FUNCTION.\r
415**/\r
416STATIC\r
417VOID\r
418EFIAPI\r
419DmaWriteBytes (\r
420 IN UINTN Size,\r
421 IN VOID *Buffer OPTIONAL\r
422 )\r
423{\r
424 DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_WRITE);\r
425}\r
426\r
427\r
6e2543b0
LE
428/**\r
429 Write firmware configuration bytes from a buffer\r
430\r
431 If called multiple times, then the data written will continue at the offset\r
432 of the firmware configuration item where the previous write ended.\r
433\r
434 @param[in] Size Size in bytes to write\r
435 @param[in] Buffer Buffer to read data from\r
436\r
437**/\r
438VOID\r
439EFIAPI\r
440QemuFwCfgWriteBytes (\r
441 IN UINTN Size,\r
442 IN VOID *Buffer\r
443 )\r
444{\r
1cb33be9 445 if (QemuFwCfgIsAvailable ()) {\r
e8ae381f 446 InternalQemuFwCfgWriteBytes (Size, Buffer);\r
6e2543b0
LE
447 }\r
448}\r
449\r
450\r
7fcb7354
LE
451/**\r
452 Slow SKIP_BYTES_FUNCTION.\r
453**/\r
454STATIC\r
455VOID\r
456EFIAPI\r
457MmioSkipBytes (\r
458 IN UINTN Size\r
459 )\r
460{\r
461 UINTN ChunkSize;\r
462 UINT8 SkipBuffer[256];\r
463\r
464 //\r
465 // Emulate the skip by reading data in chunks, and throwing it away. The\r
466 // implementation below doesn't affect the static data footprint for client\r
467 // modules. Large skips are not expected, therefore this fallback is not\r
468 // performance critical. The size of SkipBuffer is thought not to exert a\r
469 // large pressure on the stack.\r
470 //\r
471 while (Size > 0) {\r
472 ChunkSize = MIN (Size, sizeof SkipBuffer);\r
473 MmioReadBytes (ChunkSize, SkipBuffer);\r
474 Size -= ChunkSize;\r
475 }\r
476}\r
477\r
478\r
479/**\r
480 Fast SKIP_BYTES_FUNCTION.\r
481**/\r
482STATIC\r
483VOID\r
484EFIAPI\r
485DmaSkipBytes (\r
486 IN UINTN Size\r
487 )\r
488{\r
489 DmaTransferBytes (Size, NULL, FW_CFG_DMA_CTL_SKIP);\r
490}\r
491\r
492\r
493/**\r
494 Skip bytes in the firmware configuration item.\r
495\r
496 Increase the offset of the firmware configuration item without transferring\r
497 bytes between the item and a caller-provided buffer. Subsequent read, write\r
498 or skip operations will commence at the increased offset.\r
499\r
500 @param[in] Size Number of bytes to skip.\r
501**/\r
502VOID\r
503EFIAPI\r
504QemuFwCfgSkipBytes (\r
505 IN UINTN Size\r
506 )\r
507{\r
508 if (QemuFwCfgIsAvailable ()) {\r
509 InternalQemuFwCfgSkipBytes (Size);\r
510 }\r
511}\r
512\r
513\r
6e2543b0
LE
514/**\r
515 Reads a UINT8 firmware configuration value\r
516\r
517 @return Value of Firmware Configuration item read\r
518\r
519**/\r
520UINT8\r
521EFIAPI\r
522QemuFwCfgRead8 (\r
523 VOID\r
524 )\r
525{\r
526 UINT8 Result;\r
527\r
528 QemuFwCfgReadBytes (sizeof Result, &Result);\r
529 return Result;\r
530}\r
531\r
532\r
533/**\r
534 Reads a UINT16 firmware configuration value\r
535\r
536 @return Value of Firmware Configuration item read\r
537\r
538**/\r
539UINT16\r
540EFIAPI\r
541QemuFwCfgRead16 (\r
542 VOID\r
543 )\r
544{\r
545 UINT16 Result;\r
546\r
547 QemuFwCfgReadBytes (sizeof Result, &Result);\r
548 return Result;\r
549}\r
550\r
551\r
552/**\r
553 Reads a UINT32 firmware configuration value\r
554\r
555 @return Value of Firmware Configuration item read\r
556\r
557**/\r
558UINT32\r
559EFIAPI\r
560QemuFwCfgRead32 (\r
561 VOID\r
562 )\r
563{\r
564 UINT32 Result;\r
565\r
566 QemuFwCfgReadBytes (sizeof Result, &Result);\r
567 return Result;\r
568}\r
569\r
570\r
571/**\r
572 Reads a UINT64 firmware configuration value\r
573\r
574 @return Value of Firmware Configuration item read\r
575\r
576**/\r
577UINT64\r
578EFIAPI\r
579QemuFwCfgRead64 (\r
580 VOID\r
581 )\r
582{\r
583 UINT64 Result;\r
584\r
585 QemuFwCfgReadBytes (sizeof Result, &Result);\r
586 return Result;\r
587}\r
588\r
589\r
590/**\r
591 Find the configuration item corresponding to the firmware configuration file.\r
592\r
593 @param[in] Name Name of file to look up.\r
594 @param[out] Item Configuration item corresponding to the file, to be passed\r
595 to QemuFwCfgSelectItem ().\r
596 @param[out] Size Number of bytes in the file.\r
597\r
598 @retval RETURN_SUCCESS If file is found.\r
599 @retval RETURN_NOT_FOUND If file is not found.\r
600 @retval RETURN_UNSUPPORTED If firmware configuration is unavailable.\r
601\r
602**/\r
603RETURN_STATUS\r
604EFIAPI\r
605QemuFwCfgFindFile (\r
606 IN CONST CHAR8 *Name,\r
607 OUT FIRMWARE_CONFIG_ITEM *Item,\r
608 OUT UINTN *Size\r
609 )\r
610{\r
611 UINT32 Count;\r
612 UINT32 Idx;\r
613\r
1cb33be9 614 if (!QemuFwCfgIsAvailable ()) {\r
6e2543b0
LE
615 return RETURN_UNSUPPORTED;\r
616 }\r
617\r
618 QemuFwCfgSelectItem (QemuFwCfgItemFileDir);\r
619 Count = SwapBytes32 (QemuFwCfgRead32 ());\r
620\r
621 for (Idx = 0; Idx < Count; ++Idx) {\r
622 UINT32 FileSize;\r
623 UINT16 FileSelect;\r
624 CHAR8 FName[QEMU_FW_CFG_FNAME_SIZE];\r
625\r
626 FileSize = QemuFwCfgRead32 ();\r
627 FileSelect = QemuFwCfgRead16 ();\r
628 QemuFwCfgRead16 (); // skip the field called "reserved"\r
629 InternalQemuFwCfgReadBytes (sizeof (FName), FName);\r
630\r
631 if (AsciiStrCmp (Name, FName) == 0) {\r
3f318fbf 632 *Item = (FIRMWARE_CONFIG_ITEM) SwapBytes16 (FileSelect);\r
6e2543b0
LE
633 *Size = SwapBytes32 (FileSize);\r
634 return RETURN_SUCCESS;\r
635 }\r
636 }\r
637\r
638 return RETURN_NOT_FOUND;\r
639}\r