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