VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c@ 101291

Last change on this file since 101291 was 101291, checked in by vboxsync, 14 months ago

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette