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