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