diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/OvmfPkg/Library | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/OvmfPkg/Library')
155 files changed, 30548 insertions, 0 deletions
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.c b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.c new file mode 100644 index 000000000..2ed1baeeb --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.c @@ -0,0 +1,210 @@ +/** @file
+ ACPI Timer implements one instance of Timer Library.
+
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <IndustryStandard/Acpi.h>
+
+#include "AcpiTimerLib.h"
+
+//
+// The ACPI Time is a 24-bit counter
+//
+#define ACPI_TIMER_COUNT_SIZE BIT24
+
+/**
+ Stalls the CPU for at least the given number of ticks.
+
+ Stalls the CPU for at least the given number of ticks. It's invoked by
+ MicroSecondDelay() and NanoSecondDelay().
+
+ @param Delay A period of time to delay in ticks.
+
+**/
+VOID
+InternalAcpiDelay (
+ IN UINT32 Delay
+ )
+{
+ UINT32 Ticks;
+ UINT32 Times;
+
+ Times = Delay >> 22;
+ Delay &= BIT22 - 1;
+ do {
+ //
+ // The target timer count is calculated here
+ //
+ Ticks = InternalAcpiGetTimerTick () + Delay;
+ Delay = BIT22;
+ //
+ // Wait until time out
+ // Delay >= 2^23 could not be handled by this function
+ // Timer wrap-arounds are handled correctly by this function
+ //
+ while (((Ticks - InternalAcpiGetTimerTick ()) & BIT23) == 0) {
+ CpuPause ();
+ }
+ } while (Times-- > 0);
+}
+
+/**
+ Stalls the CPU for at least the given number of microseconds.
+
+ Stalls the CPU for the number of microseconds specified by MicroSeconds.
+
+ @param MicroSeconds The minimum number of microseconds to delay.
+
+ @return MicroSeconds
+
+**/
+UINTN
+EFIAPI
+MicroSecondDelay (
+ IN UINTN MicroSeconds
+ )
+{
+ InternalAcpiDelay (
+ (UINT32)DivU64x32 (
+ MultU64x32 (
+ MicroSeconds,
+ ACPI_TIMER_FREQUENCY
+ ),
+ 1000000u
+ )
+ );
+ return MicroSeconds;
+}
+
+/**
+ Stalls the CPU for at least the given number of nanoseconds.
+
+ Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
+
+ @param NanoSeconds The minimum number of nanoseconds to delay.
+
+ @return NanoSeconds
+
+**/
+UINTN
+EFIAPI
+NanoSecondDelay (
+ IN UINTN NanoSeconds
+ )
+{
+ InternalAcpiDelay (
+ (UINT32)DivU64x32 (
+ MultU64x32 (
+ NanoSeconds,
+ ACPI_TIMER_FREQUENCY
+ ),
+ 1000000000u
+ )
+ );
+ return NanoSeconds;
+}
+
+/**
+ Retrieves the current value of a 64-bit free running performance counter.
+
+ Retrieves the current value of a 64-bit free running performance counter. The
+ counter can either count up by 1 or count down by 1. If the physical
+ performance counter counts by a larger increment, then the counter values
+ must be translated. The properties of the counter can be retrieved from
+ GetPerformanceCounterProperties().
+
+ @return The current value of the free running performance counter.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounter (
+ VOID
+ )
+{
+ return (UINT64)InternalAcpiGetTimerTick ();
+}
+
+/**
+ Retrieves the 64-bit frequency in Hz and the range of performance counter
+ values.
+
+ If StartValue is not NULL, then the value that the performance counter starts
+ with immediately after is it rolls over is returned in StartValue. If
+ EndValue is not NULL, then the value that the performance counter end with
+ immediately before it rolls over is returned in EndValue. The 64-bit
+ frequency of the performance counter in Hz is always returned. If StartValue
+ is less than EndValue, then the performance counter counts up. If StartValue
+ is greater than EndValue, then the performance counter counts down. For
+ example, a 64-bit free running counter that counts up would have a StartValue
+ of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter
+ that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.
+
+ @param StartValue The value the performance counter starts with when it
+ rolls over.
+ @param EndValue The value that the performance counter ends with before
+ it rolls over.
+
+ @return The frequency in Hz.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounterProperties (
+ OUT UINT64 *StartValue, OPTIONAL
+ OUT UINT64 *EndValue OPTIONAL
+ )
+{
+ if (StartValue != NULL) {
+ *StartValue = 0;
+ }
+
+ if (EndValue != NULL) {
+ *EndValue = ACPI_TIMER_COUNT_SIZE - 1;
+ }
+
+ return ACPI_TIMER_FREQUENCY;
+}
+
+/**
+ Converts elapsed ticks of performance counter to time in nanoseconds.
+
+ This function converts the elapsed ticks of running performance counter to
+ time value in unit of nanoseconds.
+
+ @param Ticks The number of elapsed ticks of running performance counter.
+
+ @return The elapsed time in nanoseconds.
+
+**/
+UINT64
+EFIAPI
+GetTimeInNanoSecond (
+ IN UINT64 Ticks
+ )
+{
+ UINT64 NanoSeconds;
+ UINT32 Remainder;
+
+ //
+ // Ticks
+ // Time = --------- x 1,000,000,000
+ // Frequency
+ //
+ NanoSeconds = MultU64x32 (DivU64x32Remainder (Ticks, ACPI_TIMER_FREQUENCY, &Remainder), 1000000000u);
+
+ //
+ // Frequency < 0x100000000, so Remainder < 0x100000000, then (Remainder * 1,000,000,000)
+ // will not overflow 64-bit.
+ //
+ NanoSeconds += DivU64x32 (MultU64x32 ((UINT64) Remainder, 1000000000u), ACPI_TIMER_FREQUENCY);
+
+ return NanoSeconds;
+}
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.h b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.h new file mode 100644 index 000000000..0ac990284 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.h @@ -0,0 +1,23 @@ +/** @file
+ Internal definitions for ACPI Timer Library
+
+ Copyright (C) 2014, Gabriel L. Somlo <somlo@cmu.edu>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _ACPI_TIMER_LIB_INTERNAL_H_
+#define _ACPI_TIMER_LIB_INTERNAL_H_
+
+/**
+ Internal function to read the current tick counter of ACPI.
+
+ @return The tick counter read.
+
+**/
+UINT32
+InternalAcpiGetTimerTick (
+ VOID
+ );
+
+#endif // _ACPI_TIMER_LIB_INTERNAL_H_
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.c b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.c new file mode 100644 index 000000000..7c593e8be --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.c @@ -0,0 +1,103 @@ +/** @file
+ Provide constructor and GetTick for Base instance of ACPI Timer Library
+
+ Copyright (C) 2014, Gabriel L. Somlo <somlo@cmu.edu>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PciLib.h>
+#include <OvmfPlatforms.h>
+
+//
+// Cached ACPI Timer IO Address
+//
+STATIC UINT32 mAcpiTimerIoAddr;
+
+/**
+ The constructor function caches the ACPI tick counter address, and,
+ if necessary, enables ACPI IO space.
+
+ @retval EFI_SUCCESS The constructor always returns RETURN_SUCCESS.
+
+**/
+RETURN_STATUS
+EFIAPI
+AcpiTimerLibConstructor (
+ VOID
+ )
+{
+ UINT16 HostBridgeDevId;
+ UINTN Pmba;
+ UINT32 PmbaAndVal;
+ UINT32 PmbaOrVal;
+ UINTN AcpiCtlReg;
+ UINT8 AcpiEnBit;
+
+ //
+ // Query Host Bridge DID to determine platform type
+ //
+ HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
+ switch (HostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
+ PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK;
+ PmbaOrVal = PIIX4_PMBA_VALUE;
+ AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC);
+ AcpiEnBit = PIIX4_PMREGMISC_PMIOSE;
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
+ PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK;
+ PmbaOrVal = ICH9_PMBASE_VALUE;
+ AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL);
+ AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN;
+ break;
+ default:
+ DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
+ __FUNCTION__, HostBridgeDevId));
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // Check to see if the Power Management Base Address is already enabled
+ //
+ if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) {
+ //
+ // If the Power Management Base Address is not programmed,
+ // then program it now.
+ //
+ PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal);
+
+ //
+ // Enable PMBA I/O port decodes
+ //
+ PciOr8 (AcpiCtlReg, AcpiEnBit);
+ }
+
+ mAcpiTimerIoAddr = (PciRead32 (Pmba) & ~PMBA_RTE) + ACPI_TIMER_OFFSET;
+ return RETURN_SUCCESS;
+}
+
+/**
+ Internal function to read the current tick counter of ACPI.
+
+ Read the current ACPI tick counter using the counter address cached
+ by this instance's constructor.
+
+ @return The tick counter read.
+
+**/
+UINT32
+InternalAcpiGetTimerTick (
+ VOID
+ )
+{
+ //
+ // Return the current ACPI timer value.
+ //
+ return IoRead32 (mAcpiTimerIoAddr);
+}
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf new file mode 100644 index 000000000..21bf79c33 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf @@ -0,0 +1,32 @@ +## @file
+# Base ACPI Timer Library Instance.
+#
+# Copyright (C) 2014, Gabriel L. Somlo <somlo@cmu.edu>
+# Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BaseAcpiTimerLib
+ FILE_GUID = FB648CF5-91BE-4737-9023-FD807AC6D96D
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TimerLib|PEI_CORE PEIM DXE_CORE
+ CONSTRUCTOR = AcpiTimerLibConstructor
+
+[Sources]
+ AcpiTimerLib.c
+ AcpiTimerLib.h
+ BaseAcpiTimerLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ PciLib
+ IoLib
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.c b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.c new file mode 100644 index 000000000..f927e2718 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.c @@ -0,0 +1,32 @@ +/** @file
+ Provide InternalAcpiGetTimerTick for the bhyve instance of the
+ Base ACPI Timer Library
+
+ Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
+ Copyright (C) 2014, Gabriel L. Somlo <somlo@cmu.edu>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/IoLib.h>
+#include <OvmfPlatforms.h>
+
+/**
+ Internal function to read the current tick counter of ACPI.
+
+ Read the current ACPI tick counter using the counter address cached
+ by this instance's constructor.
+
+ @return The tick counter read.
+
+**/
+UINT32
+InternalAcpiGetTimerTick (
+ VOID
+ )
+{
+ //
+ // Return the current ACPI timer value.
+ //
+ return IoRead32 (BHYVE_ACPI_TIMER_IO_ADDR);
+}
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.inf b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.inf new file mode 100644 index 000000000..14b7479e6 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.inf @@ -0,0 +1,30 @@ +## @file
+# Base ACPI Timer Library Instance for Bhyve.
+#
+# Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
+# Copyright (C) 2014, Gabriel L. Somlo <somlo@cmu.edu>
+# Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BaseAcpiTimerLibBhyve
+ FILE_GUID = A5E3B247-7302-11EA-9C04-3CECEF0C1C08
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TimerLib
+
+[Sources]
+ AcpiTimerLib.c
+ AcpiTimerLib.h
+ BaseAcpiTimerLibBhyve.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ IoLib
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.c b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.c new file mode 100644 index 000000000..52f3ea2db --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.c @@ -0,0 +1,121 @@ +/** @file
+ Provide constructor and GetTick for BaseRom instance of ACPI Timer Library
+
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
+ Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PciLib.h>
+#include <OvmfPlatforms.h>
+
+/**
+ The constructor function enables ACPI IO space.
+
+ If ACPI I/O space not enabled, this function will enable it.
+ It will always return RETURN_SUCCESS.
+
+ @retval EFI_SUCCESS The constructor always returns RETURN_SUCCESS.
+
+**/
+RETURN_STATUS
+EFIAPI
+AcpiTimerLibConstructor (
+ VOID
+ )
+{
+ UINT16 HostBridgeDevId;
+ UINTN Pmba;
+ UINT32 PmbaAndVal;
+ UINT32 PmbaOrVal;
+ UINTN AcpiCtlReg;
+ UINT8 AcpiEnBit;
+
+ //
+ // Query Host Bridge DID to determine platform type
+ //
+ HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
+ switch (HostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
+ PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK;
+ PmbaOrVal = PIIX4_PMBA_VALUE;
+ AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC);
+ AcpiEnBit = PIIX4_PMREGMISC_PMIOSE;
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
+ PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK;
+ PmbaOrVal = ICH9_PMBASE_VALUE;
+ AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL);
+ AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN;
+ break;
+ default:
+ DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
+ __FUNCTION__, HostBridgeDevId));
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // Check to see if the Power Management Base Address is already enabled
+ //
+ if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) {
+ //
+ // If the Power Management Base Address is not programmed,
+ // then program it now.
+ //
+ PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal);
+
+ //
+ // Enable PMBA I/O port decodes
+ //
+ PciOr8 (AcpiCtlReg, AcpiEnBit);
+ }
+
+ return RETURN_SUCCESS;
+}
+
+/**
+ Internal function to read the current tick counter of ACPI.
+
+ Dynamically compute the address of the ACPI tick counter based on the
+ properties of the underlying platform, to avoid relying on global variables.
+
+ @return The tick counter read.
+
+**/
+UINT32
+InternalAcpiGetTimerTick (
+ VOID
+ )
+{
+ UINT16 HostBridgeDevId;
+ UINTN Pmba;
+
+ //
+ // Query Host Bridge DID to determine platform type
+ //
+ HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
+ switch (HostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
+ break;
+ default:
+ DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
+ __FUNCTION__, HostBridgeDevId));
+ ASSERT (FALSE);
+ return 0;
+ }
+
+ //
+ // Read PMBA to read and return the current ACPI timer value.
+ //
+ return IoRead32 ((PciRead32 (Pmba) & ~PMBA_RTE) + ACPI_TIMER_OFFSET);
+}
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf new file mode 100644 index 000000000..8f9bedbfe --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf @@ -0,0 +1,31 @@ +## @file
+# BaseRom ACPI Timer Library Instance.
+#
+# Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BaseRomAcpiTimerLib
+ FILE_GUID = CDD9D74F-213E-4c28-98F7-8B4A167DB936
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TimerLib|SEC
+ CONSTRUCTOR = AcpiTimerLibConstructor
+
+[Sources]
+ AcpiTimerLib.c
+ AcpiTimerLib.h
+ BaseRomAcpiTimerLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ PciLib
+ IoLib
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.c b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.c new file mode 100644 index 000000000..09076c0ad --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.c @@ -0,0 +1,83 @@ +/** @file
+ Provide constructor and GetTick for Dxe instance of ACPI Timer Library
+
+ Copyright (C) 2014, Gabriel L. Somlo <somlo@cmu.edu>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciLib.h>
+#include <OvmfPlatforms.h>
+
+//
+// Cached ACPI Timer IO Address
+//
+STATIC UINT32 mAcpiTimerIoAddr;
+
+/**
+ The constructor function caches the ACPI tick counter address
+
+ At the time this constructor runs (DXE_CORE or later), ACPI IO space
+ has already been enabled by either PlatformPei or by the "Base"
+ instance of this library.
+ In order to avoid querying the underlying platform type during each
+ tick counter read operation, we cache the counter address during
+ initialization of this instance of the Timer Library.
+
+ @retval EFI_SUCCESS The constructor always returns RETURN_SUCCESS.
+
+**/
+RETURN_STATUS
+EFIAPI
+AcpiTimerLibConstructor (
+ VOID
+ )
+{
+ UINT16 HostBridgeDevId;
+ UINTN Pmba;
+
+ //
+ // Query Host Bridge DID to determine platform type
+ //
+ HostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
+ switch (HostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
+ break;
+ default:
+ DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
+ __FUNCTION__, HostBridgeDevId));
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+ }
+
+ mAcpiTimerIoAddr = (PciRead32 (Pmba) & ~PMBA_RTE) + ACPI_TIMER_OFFSET;
+
+ return RETURN_SUCCESS;
+}
+
+/**
+ Internal function to read the current tick counter of ACPI.
+
+ Read the current ACPI tick counter using the counter address cached
+ by this instance's constructor.
+
+ @return The tick counter read.
+
+**/
+UINT32
+InternalAcpiGetTimerTick (
+ VOID
+ )
+{
+ //
+ // Return the current ACPI timer value.
+ //
+ return IoRead32 (mAcpiTimerIoAddr);
+}
diff --git a/roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.inf b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.inf new file mode 100644 index 000000000..e29872add --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.inf @@ -0,0 +1,35 @@ +## @file
+# DXE ACPI Timer Library Instance.
+#
+# Copyright (C) 2014, Gabriel L. Somlo <somlo@cmu.edu>
+# Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeAcpiTimerLib
+ FILE_GUID = 52DECA02-2EE8-4EAA-8EAD-1AB83F8A5955
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TimerLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION SMM_CORE
+ CONSTRUCTOR = AcpiTimerLibConstructor
+
+[Sources]
+ AcpiTimerLib.c
+ AcpiTimerLib.h
+ DxeAcpiTimerLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
+
+[LibraryClasses]
+ BaseLib
+ PciLib
+ IoLib
diff --git a/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf new file mode 100644 index 000000000..7c44d0952 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf @@ -0,0 +1,51 @@ +## @file
+# Library provides the helper functions for SEV guest
+#
+# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = MemEncryptSevLib
+ FILE_GUID = c1594631-3888-4be4-949f-9c630dbc842b
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = MemEncryptSevLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[Sources.X64]
+ MemEncryptSevLibInternal.c
+ X64/MemEncryptSevLib.c
+ X64/VirtualMemory.c
+ X64/VirtualMemory.h
+
+[Sources.IA32]
+ Ia32/MemEncryptSevLib.c
+ MemEncryptSevLibInternal.c
+
+[LibraryClasses]
+ BaseLib
+ CacheMaintenanceLib
+ CpuLib
+ DebugLib
+ MemoryAllocationLib
+ PcdLib
+
+[FeaturePcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
diff --git a/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c new file mode 100644 index 000000000..b4f6e5738 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c @@ -0,0 +1,84 @@ +/** @file
+
+ Secure Encrypted Virtualization (SEV) library helper function
+
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Register/Amd/Cpuid.h>
+#include <Register/Amd/Msr.h>
+#include <Register/Cpuid.h>
+
+/**
+ This function clears memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+ @param[in] Flush Flush the caches before clearing the bit
+ (mostly TRUE except MMIO addresses)
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Clearing the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptSevClearPageEncMask (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages,
+ IN BOOLEAN Flush
+ )
+{
+ //
+ // Memory encryption bit is not accessible in 32-bit mode
+ //
+ return RETURN_UNSUPPORTED;
+}
+
+/**
+ This function sets memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+ @param[in] Flush Flush the caches before setting the bit
+ (mostly TRUE except MMIO addresses)
+
+ @retval RETURN_SUCCESS The attributes were set for the memory
+ region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptSevSetPageEncMask (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages,
+ IN BOOLEAN Flush
+ )
+{
+ //
+ // Memory encryption bit is not accessible in 32-bit mode
+ //
+ return RETURN_UNSUPPORTED;
+}
diff --git a/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c new file mode 100644 index 000000000..02b8eb225 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c @@ -0,0 +1,155 @@ +/** @file
+
+ Secure Encrypted Virtualization (SEV) library helper function
+
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/PcdLib.h>
+#include <Register/Amd/Cpuid.h>
+#include <Register/Amd/Msr.h>
+#include <Register/Cpuid.h>
+#include <Register/QemuSmramSaveStateMap.h>
+#include <Register/SmramSaveStateMap.h>
+#include <Uefi/UefiBaseType.h>
+
+STATIC BOOLEAN mSevStatus = FALSE;
+STATIC BOOLEAN mSevEsStatus = FALSE;
+STATIC BOOLEAN mSevStatusChecked = FALSE;
+
+/**
+ Reads and sets the status of SEV features.
+
+ **/
+STATIC
+VOID
+EFIAPI
+InternalMemEncryptSevStatus (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ MSR_SEV_STATUS_REGISTER Msr;
+ CPUID_MEMORY_ENCRYPTION_INFO_EAX Eax;
+
+ //
+ // Check if memory encryption leaf exist
+ //
+ AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= CPUID_MEMORY_ENCRYPTION_INFO) {
+ //
+ // CPUID Fn8000_001F[EAX] Bit 1 (Sev supported)
+ //
+ AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, &Eax.Uint32, NULL, NULL, NULL);
+
+ if (Eax.Bits.SevBit) {
+ //
+ // Check MSR_0xC0010131 Bit 0 (Sev Enabled)
+ //
+ Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
+ if (Msr.Bits.SevBit) {
+ mSevStatus = TRUE;
+ }
+
+ //
+ // Check MSR_0xC0010131 Bit 1 (Sev-Es Enabled)
+ //
+ if (Msr.Bits.SevEsBit) {
+ mSevEsStatus = TRUE;
+ }
+ }
+ }
+
+ mSevStatusChecked = TRUE;
+}
+
+/**
+ Returns a boolean to indicate whether SEV-ES is enabled.
+
+ @retval TRUE SEV-ES is enabled
+ @retval FALSE SEV-ES is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevEsIsEnabled (
+ VOID
+ )
+{
+ if (!mSevStatusChecked) {
+ InternalMemEncryptSevStatus ();
+ }
+
+ return mSevEsStatus;
+}
+
+/**
+ Returns a boolean to indicate whether SEV is enabled.
+
+ @retval TRUE SEV is enabled
+ @retval FALSE SEV is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevIsEnabled (
+ VOID
+ )
+{
+ if (!mSevStatusChecked) {
+ InternalMemEncryptSevStatus ();
+ }
+
+ return mSevStatus;
+}
+
+
+/**
+ Locate the page range that covers the initial (pre-SMBASE-relocation) SMRAM
+ Save State Map.
+
+ @param[out] BaseAddress The base address of the lowest-address page that
+ covers the initial SMRAM Save State Map.
+
+ @param[out] NumberOfPages The number of pages in the page range that covers
+ the initial SMRAM Save State Map.
+
+ @retval RETURN_SUCCESS BaseAddress and NumberOfPages have been set on
+ output.
+
+ @retval RETURN_UNSUPPORTED SMM is unavailable.
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptSevLocateInitialSmramSaveStateMapPages (
+ OUT UINTN *BaseAddress,
+ OUT UINTN *NumberOfPages
+ )
+{
+ UINTN MapStart;
+ UINTN MapEnd;
+ UINTN MapPagesStart; // MapStart rounded down to page boundary
+ UINTN MapPagesEnd; // MapEnd rounded up to page boundary
+ UINTN MapPagesSize; // difference between MapPagesStart and MapPagesEnd
+
+ if (!FeaturePcdGet (PcdSmmSmramRequire)) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ MapStart = SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET;
+ MapEnd = MapStart + sizeof (QEMU_SMRAM_SAVE_STATE_MAP);
+ MapPagesStart = MapStart & ~(UINTN)EFI_PAGE_MASK;
+ MapPagesEnd = ALIGN_VALUE (MapEnd, EFI_PAGE_SIZE);
+ MapPagesSize = MapPagesEnd - MapPagesStart;
+
+ ASSERT ((MapPagesSize & EFI_PAGE_MASK) == 0);
+
+ *BaseAddress = MapPagesStart;
+ *NumberOfPages = MapPagesSize >> EFI_PAGE_SHIFT;
+
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c new file mode 100644 index 000000000..cf0921e21 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c @@ -0,0 +1,90 @@ +/** @file
+
+ Secure Encrypted Virtualization (SEV) library helper function
+
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Register/Amd/Cpuid.h>
+#include <Register/Amd/Msr.h>
+#include <Register/Cpuid.h>
+
+#include "VirtualMemory.h"
+
+/**
+ This function clears memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+ @param[in] Flush Flush the caches before clearing the bit
+ (mostly TRUE except MMIO addresses)
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Clearing the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptSevClearPageEncMask (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages,
+ IN BOOLEAN Flush
+ )
+{
+ return InternalMemEncryptSevSetMemoryDecrypted (
+ Cr3BaseAddress,
+ BaseAddress,
+ EFI_PAGES_TO_SIZE (NumPages),
+ Flush
+ );
+}
+
+/**
+ This function sets memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+ @param[in] Flush Flush the caches before setting the bit
+ (mostly TRUE except MMIO addresses)
+
+ @retval RETURN_SUCCESS The attributes were set for the memory
+ region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptSevSetPageEncMask (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages,
+ IN BOOLEAN Flush
+ )
+{
+ return InternalMemEncryptSevSetMemoryEncrypted (
+ Cr3BaseAddress,
+ BaseAddress,
+ EFI_PAGES_TO_SIZE (NumPages),
+ Flush
+ );
+}
diff --git a/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c new file mode 100644 index 000000000..5e110c84f --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c @@ -0,0 +1,897 @@ +/** @file
+
+ Virtual Memory Management Services to set or clear the memory encryption bit
+
+ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
+
+**/
+
+#include <Library/CpuLib.h>
+#include <Register/Amd/Cpuid.h>
+#include <Register/Cpuid.h>
+
+#include "VirtualMemory.h"
+
+STATIC BOOLEAN mAddressEncMaskChecked = FALSE;
+STATIC UINT64 mAddressEncMask;
+STATIC PAGE_TABLE_POOL *mPageTablePool = NULL;
+
+typedef enum {
+ SetCBit,
+ ClearCBit
+} MAP_RANGE_MODE;
+
+/**
+ Get the memory encryption mask
+
+ @param[out] EncryptionMask contains the pte mask.
+
+**/
+STATIC
+UINT64
+GetMemEncryptionAddressMask (
+ VOID
+ )
+{
+ UINT64 EncryptionMask;
+ CPUID_MEMORY_ENCRYPTION_INFO_EBX Ebx;
+
+ if (mAddressEncMaskChecked) {
+ return mAddressEncMask;
+ }
+
+ //
+ // CPUID Fn8000_001F[EBX] Bit 0:5 (memory encryption bit position)
+ //
+ AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, NULL, &Ebx.Uint32, NULL, NULL);
+ EncryptionMask = LShiftU64 (1, Ebx.Bits.PtePosBits);
+
+ mAddressEncMask = EncryptionMask & PAGING_1G_ADDRESS_MASK_64;
+ mAddressEncMaskChecked = TRUE;
+
+ return mAddressEncMask;
+}
+
+/**
+ Initialize a buffer pool for page table use only.
+
+ To reduce the potential split operation on page table, the pages reserved for
+ page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
+ at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
+ initialized with number of pages greater than or equal to the given
+ PoolPages.
+
+ Once the pages in the pool are used up, this method should be called again to
+ reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't
+ happen often in practice.
+
+ @param[in] PoolPages The least page number of the pool to be created.
+
+ @retval TRUE The pool is initialized successfully.
+ @retval FALSE The memory is out of resource.
+**/
+STATIC
+BOOLEAN
+InitializePageTablePool (
+ IN UINTN PoolPages
+ )
+{
+ VOID *Buffer;
+
+ //
+ // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
+ // header.
+ //
+ PoolPages += 1; // Add one page for header.
+ PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
+ PAGE_TABLE_POOL_UNIT_PAGES;
+ Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
+ if (Buffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
+ return FALSE;
+ }
+
+ //
+ // Link all pools into a list for easier track later.
+ //
+ if (mPageTablePool == NULL) {
+ mPageTablePool = Buffer;
+ mPageTablePool->NextPool = mPageTablePool;
+ } else {
+ ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
+ mPageTablePool->NextPool = Buffer;
+ mPageTablePool = Buffer;
+ }
+
+ //
+ // Reserve one page for pool header.
+ //
+ mPageTablePool->FreePages = PoolPages - 1;
+ mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
+
+ return TRUE;
+}
+
+/**
+ This API provides a way to allocate memory for page table.
+
+ This API can be called more than once to allocate memory for page tables.
+
+ Allocates the number of 4KB pages and returns a pointer to the allocated
+ buffer. The buffer returned is aligned on a 4KB boundary.
+
+ If Pages is 0, then NULL is returned.
+ If there is not enough memory remaining to satisfy the request, then NULL is
+ returned.
+
+ @param Pages The number of 4 KB pages to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+STATIC
+VOID *
+EFIAPI
+AllocatePageTableMemory (
+ IN UINTN Pages
+ )
+{
+ VOID *Buffer;
+
+ if (Pages == 0) {
+ return NULL;
+ }
+
+ //
+ // Renew the pool if necessary.
+ //
+ if (mPageTablePool == NULL ||
+ Pages > mPageTablePool->FreePages) {
+ if (!InitializePageTablePool (Pages)) {
+ return NULL;
+ }
+ }
+
+ Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
+
+ mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
+ mPageTablePool->FreePages -= Pages;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: Buffer=0x%Lx Pages=%ld\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ Buffer,
+ Pages
+ ));
+
+ return Buffer;
+}
+
+
+/**
+ Split 2M page to 4K.
+
+ @param[in] PhysicalAddress Start physical address the 2M page
+ covered.
+ @param[in, out] PageEntry2M Pointer to 2M page entry.
+ @param[in] StackBase Stack base address.
+ @param[in] StackSize Stack size.
+
+**/
+STATIC
+VOID
+Split2MPageTo4K (
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN OUT UINT64 *PageEntry2M,
+ IN PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ )
+{
+ PHYSICAL_ADDRESS PhysicalAddress4K;
+ UINTN IndexOfPageTableEntries;
+ PAGE_TABLE_4K_ENTRY *PageTableEntry, *PageTableEntry1;
+ UINT64 AddressEncMask;
+
+ PageTableEntry = AllocatePageTableMemory(1);
+
+ PageTableEntry1 = PageTableEntry;
+
+ AddressEncMask = GetMemEncryptionAddressMask ();
+
+ ASSERT (PageTableEntry != NULL);
+ ASSERT (*PageEntry2M & AddressEncMask);
+
+ PhysicalAddress4K = PhysicalAddress;
+ for (IndexOfPageTableEntries = 0;
+ IndexOfPageTableEntries < 512;
+ (IndexOfPageTableEntries++,
+ PageTableEntry++,
+ PhysicalAddress4K += SIZE_4KB)) {
+ //
+ // Fill in the Page Table entries
+ //
+ PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask;
+ PageTableEntry->Bits.ReadWrite = 1;
+ PageTableEntry->Bits.Present = 1;
+ if ((PhysicalAddress4K >= StackBase) &&
+ (PhysicalAddress4K < StackBase + StackSize)) {
+ //
+ // Set Nx bit for stack.
+ //
+ PageTableEntry->Bits.Nx = 1;
+ }
+ }
+
+ //
+ // Fill in 2M page entry.
+ //
+ *PageEntry2M = ((UINT64)(UINTN)PageTableEntry1 |
+ IA32_PG_P | IA32_PG_RW | AddressEncMask);
+}
+
+/**
+ Set one page of page table pool memory to be read-only.
+
+ @param[in] PageTableBase Base address of page table (CR3).
+ @param[in] Address Start address of a page to be set as read-only.
+ @param[in] Level4Paging Level 4 paging flag.
+
+**/
+STATIC
+VOID
+SetPageTablePoolReadOnly (
+ IN UINTN PageTableBase,
+ IN EFI_PHYSICAL_ADDRESS Address,
+ IN BOOLEAN Level4Paging
+ )
+{
+ UINTN Index;
+ UINTN EntryIndex;
+ UINT64 AddressEncMask;
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;
+ UINT64 *PageTable;
+ UINT64 *NewPageTable;
+ UINT64 PageAttr;
+ UINT64 LevelSize[5];
+ UINT64 LevelMask[5];
+ UINTN LevelShift[5];
+ UINTN Level;
+ UINT64 PoolUnitSize;
+
+ ASSERT (PageTableBase != 0);
+
+ //
+ // Since the page table is always from page table pool, which is always
+ // located at the boundary of PcdPageTablePoolAlignment, we just need to
+ // set the whole pool unit to be read-only.
+ //
+ Address = Address & PAGE_TABLE_POOL_ALIGN_MASK;
+
+ LevelShift[1] = PAGING_L1_ADDRESS_SHIFT;
+ LevelShift[2] = PAGING_L2_ADDRESS_SHIFT;
+ LevelShift[3] = PAGING_L3_ADDRESS_SHIFT;
+ LevelShift[4] = PAGING_L4_ADDRESS_SHIFT;
+
+ LevelMask[1] = PAGING_4K_ADDRESS_MASK_64;
+ LevelMask[2] = PAGING_2M_ADDRESS_MASK_64;
+ LevelMask[3] = PAGING_1G_ADDRESS_MASK_64;
+ LevelMask[4] = PAGING_1G_ADDRESS_MASK_64;
+
+ LevelSize[1] = SIZE_4KB;
+ LevelSize[2] = SIZE_2MB;
+ LevelSize[3] = SIZE_1GB;
+ LevelSize[4] = SIZE_512GB;
+
+ AddressEncMask = GetMemEncryptionAddressMask() &
+ PAGING_1G_ADDRESS_MASK_64;
+ PageTable = (UINT64 *)(UINTN)PageTableBase;
+ PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE;
+
+ for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) {
+ Index = ((UINTN)RShiftU64 (Address, LevelShift[Level]));
+ Index &= PAGING_PAE_INDEX_MASK;
+
+ PageAttr = PageTable[Index];
+ if ((PageAttr & IA32_PG_PS) == 0) {
+ //
+ // Go to next level of table.
+ //
+ PageTable = (UINT64 *)(UINTN)(PageAttr & ~AddressEncMask &
+ PAGING_4K_ADDRESS_MASK_64);
+ continue;
+ }
+
+ if (PoolUnitSize >= LevelSize[Level]) {
+ //
+ // Clear R/W bit if current page granularity is not larger than pool unit
+ // size.
+ //
+ if ((PageAttr & IA32_PG_RW) != 0) {
+ while (PoolUnitSize > 0) {
+ //
+ // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in
+ // one page (2MB). Then we don't need to update attributes for pages
+ // crossing page directory. ASSERT below is for that purpose.
+ //
+ ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64));
+
+ PageTable[Index] &= ~(UINT64)IA32_PG_RW;
+ PoolUnitSize -= LevelSize[Level];
+
+ ++Index;
+ }
+ }
+
+ break;
+
+ } else {
+ //
+ // The smaller granularity of page must be needed.
+ //
+ ASSERT (Level > 1);
+
+ NewPageTable = AllocatePageTableMemory (1);
+ ASSERT (NewPageTable != NULL);
+
+ PhysicalAddress = PageAttr & LevelMask[Level];
+ for (EntryIndex = 0;
+ EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64);
+ ++EntryIndex) {
+ NewPageTable[EntryIndex] = PhysicalAddress | AddressEncMask |
+ IA32_PG_P | IA32_PG_RW;
+ if (Level > 2) {
+ NewPageTable[EntryIndex] |= IA32_PG_PS;
+ }
+ PhysicalAddress += LevelSize[Level - 1];
+ }
+
+ PageTable[Index] = (UINT64)(UINTN)NewPageTable | AddressEncMask |
+ IA32_PG_P | IA32_PG_RW;
+ PageTable = NewPageTable;
+ }
+ }
+}
+
+/**
+ Prevent the memory pages used for page table from been overwritten.
+
+ @param[in] PageTableBase Base address of page table (CR3).
+ @param[in] Level4Paging Level 4 paging flag.
+
+**/
+STATIC
+VOID
+EnablePageTableProtection (
+ IN UINTN PageTableBase,
+ IN BOOLEAN Level4Paging
+ )
+{
+ PAGE_TABLE_POOL *HeadPool;
+ PAGE_TABLE_POOL *Pool;
+ UINT64 PoolSize;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ if (mPageTablePool == NULL) {
+ return;
+ }
+
+ //
+ // SetPageTablePoolReadOnly might update mPageTablePool. It's safer to
+ // remember original one in advance.
+ //
+ HeadPool = mPageTablePool;
+ Pool = HeadPool;
+ do {
+ Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;
+ PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);
+
+ //
+ // The size of one pool must be multiple of PAGE_TABLE_POOL_UNIT_SIZE,
+ // which is one of page size of the processor (2MB by default). Let's apply
+ // the protection to them one by one.
+ //
+ while (PoolSize > 0) {
+ SetPageTablePoolReadOnly(PageTableBase, Address, Level4Paging);
+ Address += PAGE_TABLE_POOL_UNIT_SIZE;
+ PoolSize -= PAGE_TABLE_POOL_UNIT_SIZE;
+ }
+
+ Pool = Pool->NextPool;
+ } while (Pool != HeadPool);
+
+}
+
+
+/**
+ Split 1G page to 2M.
+
+ @param[in] PhysicalAddress Start physical address the 1G page
+ covered.
+ @param[in, out] PageEntry1G Pointer to 1G page entry.
+ @param[in] StackBase Stack base address.
+ @param[in] StackSize Stack size.
+
+**/
+STATIC
+VOID
+Split1GPageTo2M (
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN OUT UINT64 *PageEntry1G,
+ IN PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ )
+{
+ PHYSICAL_ADDRESS PhysicalAddress2M;
+ UINTN IndexOfPageDirectoryEntries;
+ PAGE_TABLE_ENTRY *PageDirectoryEntry;
+ UINT64 AddressEncMask;
+
+ PageDirectoryEntry = AllocatePageTableMemory(1);
+
+ AddressEncMask = GetMemEncryptionAddressMask ();
+ ASSERT (PageDirectoryEntry != NULL);
+ ASSERT (*PageEntry1G & GetMemEncryptionAddressMask ());
+ //
+ // Fill in 1G page entry.
+ //
+ *PageEntry1G = ((UINT64)(UINTN)PageDirectoryEntry |
+ IA32_PG_P | IA32_PG_RW | AddressEncMask);
+
+ PhysicalAddress2M = PhysicalAddress;
+ for (IndexOfPageDirectoryEntries = 0;
+ IndexOfPageDirectoryEntries < 512;
+ (IndexOfPageDirectoryEntries++,
+ PageDirectoryEntry++,
+ PhysicalAddress2M += SIZE_2MB)) {
+ if ((PhysicalAddress2M < StackBase + StackSize) &&
+ ((PhysicalAddress2M + SIZE_2MB) > StackBase)) {
+ //
+ // Need to split this 2M page that covers stack range.
+ //
+ Split2MPageTo4K (
+ PhysicalAddress2M,
+ (UINT64 *)PageDirectoryEntry,
+ StackBase,
+ StackSize
+ );
+ } else {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | AddressEncMask;
+ PageDirectoryEntry->Bits.ReadWrite = 1;
+ PageDirectoryEntry->Bits.Present = 1;
+ PageDirectoryEntry->Bits.MustBe1 = 1;
+ }
+ }
+}
+
+
+/**
+ Set or Clear the memory encryption bit
+
+ @param[in] PagetablePoint Page table entry pointer (PTE).
+ @param[in] Mode Set or Clear encryption bit
+
+**/
+STATIC VOID
+SetOrClearCBit(
+ IN OUT UINT64* PageTablePointer,
+ IN MAP_RANGE_MODE Mode
+ )
+{
+ UINT64 AddressEncMask;
+
+ AddressEncMask = GetMemEncryptionAddressMask ();
+
+ if (Mode == SetCBit) {
+ *PageTablePointer |= AddressEncMask;
+ } else {
+ *PageTablePointer &= ~AddressEncMask;
+ }
+
+}
+
+/**
+ Check the WP status in CR0 register. This bit is used to lock or unlock write
+ access to pages marked as read-only.
+
+ @retval TRUE Write protection is enabled.
+ @retval FALSE Write protection is disabled.
+**/
+STATIC
+BOOLEAN
+IsReadOnlyPageWriteProtected (
+ VOID
+ )
+{
+ return ((AsmReadCr0 () & BIT16) != 0);
+}
+
+
+/**
+ Disable Write Protect on pages marked as read-only.
+**/
+STATIC
+VOID
+DisableReadOnlyPageWriteProtect (
+ VOID
+ )
+{
+ AsmWriteCr0 (AsmReadCr0() & ~BIT16);
+}
+
+/**
+ Enable Write Protect on pages marked as read-only.
+**/
+VOID
+EnableReadOnlyPageWriteProtect (
+ VOID
+ )
+{
+ AsmWriteCr0 (AsmReadCr0() | BIT16);
+}
+
+
+/**
+ This function either sets or clears memory encryption bit for the memory
+ region specified by PhysicalAddress and Length from the current page table
+ context.
+
+ The function iterates through the PhysicalAddress one page at a time, and set
+ or clears the memory encryption mask in the page table. If it encounters
+ that a given physical address range is part of large page then it attempts to
+ change the attribute at one go (based on size), otherwise it splits the
+ large pages into smaller (e.g 2M page into 4K pages) and then try to set or
+ clear the encryption bit on the smallest page size.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Length The length of memory region
+ @param[in] Mode Set or Clear mode
+ @param[in] CacheFlush Flush the caches before applying the
+ encryption mask
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute
+ is not supported
+**/
+
+STATIC
+RETURN_STATUS
+EFIAPI
+SetMemoryEncDec (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Length,
+ IN MAP_RANGE_MODE Mode,
+ IN BOOLEAN CacheFlush
+ )
+{
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageUpperDirectoryPointerEntry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
+ PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
+ PAGE_TABLE_ENTRY *PageDirectory2MEntry;
+ PAGE_TABLE_4K_ENTRY *PageTableEntry;
+ UINT64 PgTableMask;
+ UINT64 AddressEncMask;
+ BOOLEAN IsWpEnabled;
+ RETURN_STATUS Status;
+
+ //
+ // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.
+ //
+ PageMapLevel4Entry = NULL;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ Cr3BaseAddress,
+ PhysicalAddress,
+ (UINT64)Length,
+ (Mode == SetCBit) ? "Encrypt" : "Decrypt",
+ (UINT32)CacheFlush
+ ));
+
+ //
+ // Check if we have a valid memory encryption mask
+ //
+ AddressEncMask = GetMemEncryptionAddressMask ();
+ if (!AddressEncMask) {
+ return RETURN_ACCESS_DENIED;
+ }
+
+ PgTableMask = AddressEncMask | EFI_PAGE_MASK;
+
+ if (Length == 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // We are going to change the memory encryption attribute from C=0 -> C=1 or
+ // vice versa Flush the caches to ensure that data is written into memory
+ // with correct C-bit
+ //
+ if (CacheFlush) {
+ WriteBackInvalidateDataCacheRange((VOID*) (UINTN)PhysicalAddress, Length);
+ }
+
+ //
+ // Make sure that the page table is changeable.
+ //
+ IsWpEnabled = IsReadOnlyPageWriteProtected ();
+ if (IsWpEnabled) {
+ DisableReadOnlyPageWriteProtect ();
+ }
+
+ Status = EFI_SUCCESS;
+
+ while (Length)
+ {
+ //
+ // If Cr3BaseAddress is not specified then read the current CR3
+ //
+ if (Cr3BaseAddress == 0) {
+ Cr3BaseAddress = AsmReadCr3();
+ }
+
+ PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask);
+ PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress);
+ if (!PageMapLevel4Entry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PML4 for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+
+ PageDirectory1GEntry = (VOID *)(
+ (PageMapLevel4Entry->Bits.PageTableBaseAddress <<
+ 12) & ~PgTableMask
+ );
+ PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress);
+ if (!PageDirectory1GEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PDPE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+
+ //
+ // If the MustBe1 bit is not 1, it's not actually a 1GB entry
+ //
+ if (PageDirectory1GEntry->Bits.MustBe1) {
+ //
+ // Valid 1GB page
+ // If we have at least 1GB to go, we can just update this entry
+ //
+ if (!(PhysicalAddress & (BIT30 - 1)) && Length >= BIT30) {
+ SetOrClearCBit(&PageDirectory1GEntry->Uint64, Mode);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: updated 1GB entry for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ PhysicalAddress += BIT30;
+ Length -= BIT30;
+ } else {
+ //
+ // We must split the page
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: splitting 1GB page for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Split1GPageTo2M (
+ (UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress << 30,
+ (UINT64 *)PageDirectory1GEntry,
+ 0,
+ 0
+ );
+ continue;
+ }
+ } else {
+ //
+ // Actually a PDP
+ //
+ PageUpperDirectoryPointerEntry =
+ (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory1GEntry;
+ PageDirectory2MEntry =
+ (VOID *)(
+ (PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress <<
+ 12) & ~PgTableMask
+ );
+ PageDirectory2MEntry += PDE_OFFSET(PhysicalAddress);
+ if (!PageDirectory2MEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PDE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+ //
+ // If the MustBe1 bit is not a 1, it's not a 2MB entry
+ //
+ if (PageDirectory2MEntry->Bits.MustBe1) {
+ //
+ // Valid 2MB page
+ // If we have at least 2MB left to go, we can just update this entry
+ //
+ if (!(PhysicalAddress & (BIT21-1)) && Length >= BIT21) {
+ SetOrClearCBit (&PageDirectory2MEntry->Uint64, Mode);
+ PhysicalAddress += BIT21;
+ Length -= BIT21;
+ } else {
+ //
+ // We must split up this page into 4K pages
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: splitting 2MB page for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Split2MPageTo4K (
+ (UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress << 21,
+ (UINT64 *)PageDirectory2MEntry,
+ 0,
+ 0
+ );
+ continue;
+ }
+ } else {
+ PageDirectoryPointerEntry =
+ (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory2MEntry;
+ PageTableEntry =
+ (VOID *)(
+ (PageDirectoryPointerEntry->Bits.PageTableBaseAddress <<
+ 12) & ~PgTableMask
+ );
+ PageTableEntry += PTE_OFFSET(PhysicalAddress);
+ if (!PageTableEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PTE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+ SetOrClearCBit (&PageTableEntry->Uint64, Mode);
+ PhysicalAddress += EFI_PAGE_SIZE;
+ Length -= EFI_PAGE_SIZE;
+ }
+ }
+ }
+
+ //
+ // Protect the page table by marking the memory used for page table to be
+ // read-only.
+ //
+ if (IsWpEnabled) {
+ EnablePageTableProtection ((UINTN)PageMapLevel4Entry, TRUE);
+ }
+
+ //
+ // Flush TLB
+ //
+ CpuFlushTlb();
+
+Done:
+ //
+ // Restore page table write protection, if any.
+ //
+ if (IsWpEnabled) {
+ EnableReadOnlyPageWriteProtect ();
+ }
+
+ return Status;
+}
+
+/**
+ This function clears memory encryption bit for the memory region specified by
+ PhysicalAddress and Length from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Length The length of memory region
+ @param[in] Flush Flush the caches before applying the
+ encryption mask
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Clearing the memory encyrption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+InternalMemEncryptSevSetMemoryDecrypted (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Length,
+ IN BOOLEAN Flush
+ )
+{
+
+ return SetMemoryEncDec (
+ Cr3BaseAddress,
+ PhysicalAddress,
+ Length,
+ ClearCBit,
+ Flush
+ );
+}
+
+/**
+ This function sets memory encryption bit for the memory region specified by
+ PhysicalAddress and Length from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Length The length of memory region
+ @param[in] Flush Flush the caches before applying the
+ encryption mask
+
+ @retval RETURN_SUCCESS The attributes were set for the memory
+ region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+InternalMemEncryptSevSetMemoryEncrypted (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Length,
+ IN BOOLEAN Flush
+ )
+{
+ return SetMemoryEncDec (
+ Cr3BaseAddress,
+ PhysicalAddress,
+ Length,
+ SetCBit,
+ Flush
+ );
+}
diff --git a/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h new file mode 100644 index 000000000..26d26cd92 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h @@ -0,0 +1,237 @@ +/** @file
+
+ Virtual Memory Management Services to set or clear the memory encryption bit
+
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
+
+**/
+
+#ifndef __VIRTUAL_MEMORY__
+#define __VIRTUAL_MEMORY__
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Uefi.h>
+
+#define SYS_CODE64_SEL 0x38
+
+#pragma pack(1)
+
+//
+// Page-Map Level-4 Offset (PML4) and
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
+//
+
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1 = Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Reserved:1; // Reserved
+ UINT64 MustBeZero:2; // Must Be Zero
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PageTableBaseAddress:40; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // No Execute bit
+ } Bits;
+ UINT64 Uint64;
+} PAGE_MAP_AND_DIRECTORY_POINTER;
+
+//
+// Page Table Entry 4KB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1 = Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by
+ // processor on access to page
+ UINT64 PAT:1; //
+ UINT64 Global:1; // 0 = Not global page, 1 = global page
+ // TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PageTableBaseAddress:40; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code,
+ // 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_4K_ENTRY;
+
+//
+// Page Table Entry 2MB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1=Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by
+ // processor on access to page
+ UINT64 MustBe1:1; // Must be 1
+ UINT64 Global:1; // 0 = Not global page, 1 = global page
+ // TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PAT:1; //
+ UINT64 MustBeZero:8; // Must be zero;
+ UINT64 PageTableBaseAddress:31; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code,
+ // 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_ENTRY;
+
+//
+// Page Table Entry 1GB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1 = Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by
+ // processor on access to page
+ UINT64 MustBe1:1; // Must be 1
+ UINT64 Global:1; // 0 = Not global page, 1 = global page
+ // TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PAT:1; //
+ UINT64 MustBeZero:17; // Must be zero;
+ UINT64 PageTableBaseAddress:22; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code,
+ // 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_1G_ENTRY;
+
+#pragma pack()
+
+#define IA32_PG_P BIT0
+#define IA32_PG_RW BIT1
+#define IA32_PG_PS BIT7
+
+#define PAGING_PAE_INDEX_MASK 0x1FF
+
+#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
+#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
+#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
+
+#define PAGING_L1_ADDRESS_SHIFT 12
+#define PAGING_L2_ADDRESS_SHIFT 21
+#define PAGING_L3_ADDRESS_SHIFT 30
+#define PAGING_L4_ADDRESS_SHIFT 39
+
+#define PAGING_PML4E_NUMBER 4
+
+#define PAGETABLE_ENTRY_MASK ((1UL << 9) - 1)
+#define PML4_OFFSET(x) ( (x >> 39) & PAGETABLE_ENTRY_MASK)
+#define PDP_OFFSET(x) ( (x >> 30) & PAGETABLE_ENTRY_MASK)
+#define PDE_OFFSET(x) ( (x >> 21) & PAGETABLE_ENTRY_MASK)
+#define PTE_OFFSET(x) ( (x >> 12) & PAGETABLE_ENTRY_MASK)
+#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
+
+#define PAGE_TABLE_POOL_ALIGNMENT BASE_2MB
+#define PAGE_TABLE_POOL_UNIT_SIZE SIZE_2MB
+#define PAGE_TABLE_POOL_UNIT_PAGES \
+ EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE)
+#define PAGE_TABLE_POOL_ALIGN_MASK \
+ (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1))
+
+typedef struct {
+ VOID *NextPool;
+ UINTN Offset;
+ UINTN FreePages;
+} PAGE_TABLE_POOL;
+
+
+
+/**
+ This function clears memory encryption bit for the memory region specified by
+ PhysicalAddress and Length from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Length The length of memory region
+ @param[in] Flush Flush the caches before applying the
+ encryption mask
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Clearing the memory encyrption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+InternalMemEncryptSevSetMemoryDecrypted (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Length,
+ IN BOOLEAN Flush
+ );
+
+/**
+ This function sets memory encryption bit for the memory region specified by
+ PhysicalAddress and Length from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Length The length of memory region
+ @param[in] Flush Flush the caches before applying the
+ encryption mask
+
+ @retval RETURN_SUCCESS The attributes were set for the memory
+ region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+InternalMemEncryptSevSetMemoryEncrypted (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Length,
+ IN BOOLEAN Flush
+ );
+
+#endif
diff --git a/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c b/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c new file mode 100644 index 000000000..c6f2c7265 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c @@ -0,0 +1,1010 @@ +/** @file
+ Work with PCI capabilities in PCI config space.
+
+ Provides functions to parse capabilities lists, and to locate, describe, read
+ and write capabilities. PCI config space access is abstracted away.
+
+ Copyright (C) 2018, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <IndustryStandard/PciExpress21.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include "BasePciCapLib.h"
+
+
+/**
+ Compare a standalone PCI_CAP_KEY against a PCI_CAP containing an embedded
+ PCI_CAP_KEY.
+
+ @param[in] PciCapKey Pointer to the bare PCI_CAP_KEY.
+
+ @param[in] PciCap Pointer to the PCI_CAP with the embedded PCI_CAP_KEY.
+
+ @retval <0 If PciCapKey compares less than PciCap->Key.
+
+ @retval 0 If PciCapKey compares equal to PciCap->Key.
+
+ @retval >0 If PciCapKey compares greater than PciCap->Key.
+**/
+STATIC
+INTN
+EFIAPI
+ComparePciCapKey (
+ IN CONST VOID *PciCapKey,
+ IN CONST VOID *PciCap
+ )
+{
+ CONST PCI_CAP_KEY *Key1;
+ CONST PCI_CAP_KEY *Key2;
+
+ Key1 = PciCapKey;
+ Key2 = &((CONST PCI_CAP *)PciCap)->Key;
+
+ if (Key1->Domain < Key2->Domain) {
+ return -1;
+ }
+ if (Key1->Domain > Key2->Domain) {
+ return 1;
+ }
+ if (Key1->CapId < Key2->CapId) {
+ return -1;
+ }
+ if (Key1->CapId > Key2->CapId) {
+ return 1;
+ }
+ if (Key1->Instance < Key2->Instance) {
+ return -1;
+ }
+ if (Key1->Instance > Key2->Instance) {
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ Compare two PCI_CAP objects based on PCI_CAP.Key.
+
+ @param[in] PciCap1 Pointer to the first PCI_CAP.
+
+ @param[in] PciCap2 Pointer to the second PCI_CAP.
+
+ @retval <0 If PciCap1 compares less than PciCap2.
+
+ @retval 0 If PciCap1 compares equal to PciCap2.
+
+ @retval >0 If PciCap1 compares greater than PciCap2.
+**/
+STATIC
+INTN
+EFIAPI
+ComparePciCap (
+ IN CONST VOID *PciCap1,
+ IN CONST VOID *PciCap2
+ )
+{
+ CONST PCI_CAP_KEY *PciCap1Key;
+
+ PciCap1Key = &((CONST PCI_CAP *)PciCap1)->Key;
+ return ComparePciCapKey (PciCap1Key, PciCap2);
+}
+
+
+/**
+ Compare the standalone UINT16 config space offset of a capability header
+ against a PCI_CAP containing an embedded Offset.
+
+ @param[in] CapHdrOffset Pointer to the bare UINT16 config space offset.
+
+ @param[in] PciCap Pointer to the PCI_CAP with the embedded Offset.
+
+ @retval <0 If CapHdrOffset compares less than PciCap->Offset.
+
+ @retval 0 If CapHdrOffset compares equal to PciCap->Offset.
+
+ @retval >0 If CapHdrOffset compares greater than PciCap->Offset.
+**/
+STATIC
+INTN
+EFIAPI
+ComparePciCapOffsetKey (
+ IN CONST VOID *CapHdrOffset,
+ IN CONST VOID *PciCap
+ )
+{
+ UINT16 Offset1;
+ UINT16 Offset2;
+
+ Offset1 = *(CONST UINT16 *)CapHdrOffset;
+ Offset2 = ((CONST PCI_CAP *)PciCap)->Offset;
+ //
+ // Note: both Offset1 and Offset2 are promoted to INT32 below, and the
+ // subtraction takes place between INT32 values.
+ //
+ return Offset1 - Offset2;
+}
+
+
+/**
+ Compare two PCI_CAP objects based on PCI_CAP.Offset.
+
+ @param[in] PciCap1 Pointer to the first PCI_CAP.
+
+ @param[in] PciCap2 Pointer to the second PCI_CAP.
+
+ @retval <0 If PciCap1 compares less than PciCap2.
+
+ @retval 0 If PciCap1 compares equal to PciCap2.
+
+ @retval >0 If PciCap1 compares greater than PciCap2.
+**/
+STATIC
+INTN
+EFIAPI
+ComparePciCapOffset (
+ IN CONST VOID *PciCap1,
+ IN CONST VOID *PciCap2
+ )
+{
+ UINT16 Offset1;
+ UINT16 Offset2;
+
+ Offset1 = ((CONST PCI_CAP *)PciCap1)->Offset;
+ Offset2 = ((CONST PCI_CAP *)PciCap2)->Offset;
+ //
+ // Note: both Offset1 and Offset2 are promoted to INT32 below, and the
+ // subtraction takes place between INT32 values.
+ //
+ return Offset1 - Offset2;
+}
+
+
+/**
+ Insert a new instance of the PCI capability given by (Domain, CapId) in
+ CapList.
+
+ @param[in,out] CapList The PCI_CAP_LIST into which the new PCI_CAP
+ should be inserted. CapList will own the new
+ PCI_CAP structure.
+
+ @param[in,out] CapHdrOffsets Link the new PCI_CAP structure into the
+ (non-owning) CapHdrOffsets collection as well.
+ CapHdrOffsets orders the PCI_CAP structures
+ based on the PCI_CAP.Offset member, and enables
+ the calculation of PCI_CAP.MaxSizeHint.
+
+ @param[in] Domain Whether the capability is normal or extended.
+
+ @param[in] CapId Capability ID (specific to Domain).
+
+ @param[in] Offset Config space offset at which the standard
+ header of the capability starts. The caller is
+ responsible for ensuring that Offset be DWORD
+ aligned. The caller is also responsible for
+ ensuring that Offset be within the config space
+ identified by Domain.
+
+ @param[in] Version The version number of the capability. The
+ caller is responsible for passing 0 as Version
+ if Domain is PciCapNormal.
+
+ @retval RETURN_SUCCESS Insertion successful.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval RETURN_DEVICE_ERROR A PCI_CAP with Offset is already linked by
+ CapHdrOffsets. This indicates a loop in the
+ capabilities list being parsed.
+**/
+STATIC
+RETURN_STATUS
+InsertPciCap (
+ IN OUT PCI_CAP_LIST *CapList,
+ IN OUT ORDERED_COLLECTION *CapHdrOffsets,
+ IN PCI_CAP_DOMAIN Domain,
+ IN UINT16 CapId,
+ IN UINT16 Offset,
+ IN UINT8 Version
+ )
+{
+ PCI_CAP *PciCap;
+ RETURN_STATUS Status;
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;
+ PCI_CAP *InstanceZero;
+
+ ASSERT ((Offset & 0x3) == 0);
+ ASSERT (Offset < (Domain == PciCapNormal ?
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET));
+ ASSERT (Domain == PciCapExtended || Version == 0);
+
+ //
+ // Set InstanceZero to suppress incorrect compiler/analyzer warnings.
+ //
+ InstanceZero = NULL;
+
+ //
+ // Allocate PciCap, and populate it assuming it is the first occurrence of
+ // (Domain, CapId). Note that PciCap->MaxSizeHint is not assigned the final
+ // value just yet.
+ //
+ PciCap = AllocatePool (sizeof *PciCap);
+ if (PciCap == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+ PciCap->Key.Domain = Domain;
+ PciCap->Key.CapId = CapId;
+ PciCap->Key.Instance = 0;
+ PciCap->NumInstancesUnion.NumInstances = 1;
+ PciCap->Offset = Offset;
+ PciCap->MaxSizeHint = 0;
+ PciCap->Version = Version;
+
+ //
+ // Add PciCap to CapList.
+ //
+ Status = OrderedCollectionInsert (CapList->Capabilities, &PciCapEntry,
+ PciCap);
+ if (RETURN_ERROR (Status)) {
+ if (Status == RETURN_OUT_OF_RESOURCES) {
+ goto FreePciCap;
+ }
+ ASSERT (Status == RETURN_ALREADY_STARTED);
+ //
+ // PciCap is not the first instance of (Domain, CapId). Add it as a new
+ // instance, taking the current instance count from Instance#0. Note that
+ // we don't bump the instance count maintained in Instance#0 just yet, to
+ // keep rollback on errors simple.
+ //
+ InstanceZero = OrderedCollectionUserStruct (PciCapEntry);
+ PciCap->Key.Instance = InstanceZero->NumInstancesUnion.NumInstances;
+ PciCap->NumInstancesUnion.InstanceZero = InstanceZero;
+
+ ASSERT (PciCap->Key.Instance > 0);
+ Status = OrderedCollectionInsert (CapList->Capabilities, &PciCapEntry,
+ PciCap);
+ if (Status == RETURN_OUT_OF_RESOURCES) {
+ goto FreePciCap;
+ }
+ }
+ //
+ // At this point, PciCap has been inserted in CapList->Capabilities, either
+ // with Instance==0 or with Instance>0. PciCapEntry is the iterator that
+ // links PciCap.
+ //
+ ASSERT_RETURN_ERROR (Status);
+
+ //
+ // Link PciCap into CapHdrOffsets too, to order it globally based on config
+ // space offset. Note that partial overlaps between capability headers is not
+ // possible: Offset is DWORD aligned, normal capability headers are 16-bit
+ // wide, and extended capability headers are 32-bit wide. Therefore any two
+ // capability headers either are distinct or start at the same offset
+ // (implying a loop in the respective capabilities list).
+ //
+ Status = OrderedCollectionInsert (CapHdrOffsets, NULL, PciCap);
+ if (RETURN_ERROR (Status)) {
+ if (Status == RETURN_ALREADY_STARTED) {
+ //
+ // Loop found; map return status accordingly.
+ //
+ Status = RETURN_DEVICE_ERROR;
+ }
+ goto DeletePciCapFromCapList;
+ }
+
+ //
+ // Now we can bump the instance count maintained in Instance#0, if PciCap is
+ // not the first instance of (Domain, CapId).
+ //
+ if (PciCap->Key.Instance > 0) {
+ //
+ // Suppress invalid "nullptr dereference" compiler/analyzer warnings: the
+ // only way for "PciCap->Key.Instance" to be positive here is for it to
+ // have been assigned *from* dereferencing "InstanceZero" above.
+ //
+ ASSERT (InstanceZero != NULL);
+
+ InstanceZero->NumInstancesUnion.NumInstances++;
+ }
+ return RETURN_SUCCESS;
+
+DeletePciCapFromCapList:
+ OrderedCollectionDelete (CapList->Capabilities, PciCapEntry, NULL);
+
+FreePciCap:
+ FreePool (PciCap);
+
+ return Status;
+}
+
+
+/**
+ Calculate the MaxSizeHint member for a PCI_CAP object.
+
+ CalculatePciCapMaxSizeHint() may only be called once all capability instances
+ have been successfully processed by InsertPciCap().
+
+ @param[in,out] PciCap The PCI_CAP object for which to calculate the
+ MaxSizeHint member. The caller is responsible for
+ passing a PCI_CAP object that has been created by a
+ successful invocation of InsertPciCap().
+
+ @param[in] NextPciCap If NextPciCap is NULL, then the caller is responsible
+ for PciCap to represent the capability instance with
+ the highest header offset in all config space. If
+ NextPciCap is not NULL, then the caller is responsible
+ for (a) having created NextPciCap with a successful
+ invocation of InsertPciCap(), and (b) NextPciCap being
+ the direct successor of PciCap in config space offset
+ order, as ordered by ComparePciCapOffset().
+**/
+STATIC
+VOID
+CalculatePciCapMaxSizeHint (
+ IN OUT PCI_CAP *PciCap,
+ IN PCI_CAP *NextPciCap OPTIONAL
+ )
+{
+ UINT16 ConfigSpaceSize;
+
+ ConfigSpaceSize = (PciCap->Key.Domain == PciCapNormal ?
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
+ //
+ // The following is guaranteed by the interface contract on
+ // CalculatePciCapMaxSizeHint().
+ //
+ ASSERT (NextPciCap == NULL || PciCap->Offset < NextPciCap->Offset);
+ //
+ // The following is guaranteed by the interface contract on InsertPciCap().
+ //
+ ASSERT (PciCap->Offset < ConfigSpaceSize);
+ //
+ // Thus we can safely subtract PciCap->Offset from either of
+ // - ConfigSpaceSize
+ // - and NextPciCap->Offset (if NextPciCap is not NULL).
+ //
+ // PciCap extends from PciCap->Offset to NextPciCap->Offset (if any), except
+ // it cannot cross config space boundary.
+ //
+ if (NextPciCap == NULL || NextPciCap->Offset >= ConfigSpaceSize) {
+ PciCap->MaxSizeHint = ConfigSpaceSize - PciCap->Offset;
+ return;
+ }
+ PciCap->MaxSizeHint = NextPciCap->Offset - PciCap->Offset;
+}
+
+
+/**
+ Debug dump a PCI_CAP_LIST object at the DEBUG_VERBOSE level.
+
+ @param[in] CapList The PCI_CAP_LIST object to dump.
+**/
+STATIC
+VOID
+EFIAPI
+DebugDumpPciCapList (
+ IN PCI_CAP_LIST *CapList
+ )
+{
+ DEBUG_CODE_BEGIN ();
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;
+
+ for (PciCapEntry = OrderedCollectionMin (CapList->Capabilities);
+ PciCapEntry != NULL;
+ PciCapEntry = OrderedCollectionNext (PciCapEntry)) {
+ PCI_CAP *PciCap;
+ RETURN_STATUS Status;
+ PCI_CAP_INFO Info;
+
+ PciCap = OrderedCollectionUserStruct (PciCapEntry);
+ Status = PciCapGetInfo (PciCap, &Info);
+ //
+ // PciCapGetInfo() cannot fail in this library instance.
+ //
+ ASSERT_RETURN_ERROR (Status);
+
+ DEBUG ((DEBUG_VERBOSE,
+ "%a:%a: %a 0x%04x %03u/%03u v0x%x @0x%03x+0x%03x\n", gEfiCallerBaseName,
+ __FUNCTION__, (Info.Domain == PciCapNormal ? "Norm" : "Extd"),
+ Info.CapId, Info.Instance, Info.NumInstances, Info.Version, Info.Offset,
+ Info.MaxSizeHint));
+ }
+ DEBUG_CODE_END ();
+}
+
+
+/**
+ Empty a collection of PCI_CAP structures, optionally releasing the referenced
+ PCI_CAP structures themselves. Release the collection at last.
+
+ @param[in,out] PciCapCollection The collection to empty and release.
+
+ @param[in] FreePciCap TRUE if the PCI_CAP structures linked by
+ PciCapCollection should be released. When
+ FALSE, the caller is responsible for
+ retaining at least one reference to each
+ PCI_CAP structure originally linked by
+ PciCapCollection.
+**/
+STATIC
+VOID
+EmptyAndUninitPciCapCollection (
+ IN OUT ORDERED_COLLECTION *PciCapCollection,
+ IN BOOLEAN FreePciCap
+ )
+{
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;
+ ORDERED_COLLECTION_ENTRY *NextEntry;
+
+ for (PciCapEntry = OrderedCollectionMin (PciCapCollection);
+ PciCapEntry != NULL;
+ PciCapEntry = NextEntry) {
+ PCI_CAP *PciCap;
+
+ NextEntry = OrderedCollectionNext (PciCapEntry);
+ OrderedCollectionDelete (PciCapCollection, PciCapEntry, (VOID **)&PciCap);
+ if (FreePciCap) {
+ FreePool (PciCap);
+ }
+ }
+ OrderedCollectionUninit (PciCapCollection);
+}
+
+
+/**
+ Parse the capabilities lists (both normal and extended, as applicable) of a
+ PCI device.
+
+ If the PCI device has no capabilities, that per se will not fail
+ PciCapListInit(); an empty capabilities list will be represented.
+
+ If the PCI device is found to be PCI Express, then an attempt will be made to
+ parse the extended capabilities list as well. If the first extended config
+ space access -- via PciDevice->ReadConfig() with SourceOffset=0x100 and
+ Size=4 -- fails, that per se will not fail PciCapListInit(); the device will
+ be assumed to have no extended capabilities.
+
+ @param[in] PciDevice Implementation-specific unique representation of the
+ PCI device in the PCI hierarchy.
+
+ @param[out] CapList Opaque data structure that holds an in-memory
+ representation of the parsed capabilities lists of
+ PciDevice.
+
+ @retval RETURN_SUCCESS The capabilities lists have been parsed from
+ config space.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval RETURN_DEVICE_ERROR A loop or some other kind of invalid pointer
+ was detected in the capabilities lists of
+ PciDevice.
+
+ @return Error codes propagated from
+ PciDevice->ReadConfig().
+**/
+RETURN_STATUS
+EFIAPI
+PciCapListInit (
+ IN PCI_CAP_DEV *PciDevice,
+ OUT PCI_CAP_LIST **CapList
+ )
+{
+ PCI_CAP_LIST *OutCapList;
+ RETURN_STATUS Status;
+ ORDERED_COLLECTION *CapHdrOffsets;
+ UINT16 PciStatusReg;
+ BOOLEAN DeviceIsExpress;
+ ORDERED_COLLECTION_ENTRY *OffsetEntry;
+
+ //
+ // Allocate the output structure.
+ //
+ OutCapList = AllocatePool (sizeof *OutCapList);
+ if (OutCapList == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+ //
+ // The OutCapList->Capabilities collection owns the PCI_CAP structures and
+ // orders them based on PCI_CAP.Key.
+ //
+ OutCapList->Capabilities = OrderedCollectionInit (ComparePciCap,
+ ComparePciCapKey);
+ if (OutCapList->Capabilities == NULL) {
+ Status = RETURN_OUT_OF_RESOURCES;
+ goto FreeOutCapList;
+ }
+
+ //
+ // The (temporary) CapHdrOffsets collection only references PCI_CAP
+ // structures, and orders them based on PCI_CAP.Offset.
+ //
+ CapHdrOffsets = OrderedCollectionInit (ComparePciCapOffset,
+ ComparePciCapOffsetKey);
+ if (CapHdrOffsets == NULL) {
+ Status = RETURN_OUT_OF_RESOURCES;
+ goto FreeCapabilities;
+ }
+
+ //
+ // Whether the device is PCI Express depends on the normal capability with
+ // identifier EFI_PCI_CAPABILITY_ID_PCIEXP.
+ //
+ DeviceIsExpress = FALSE;
+
+ //
+ // Check whether a normal capabilities list is present. If there's none,
+ // that's not an error; we'll just return OutCapList->Capabilities empty.
+ //
+ Status = PciDevice->ReadConfig (PciDevice, PCI_PRIMARY_STATUS_OFFSET,
+ &PciStatusReg, sizeof PciStatusReg);
+ if (RETURN_ERROR (Status)) {
+ goto FreeCapHdrOffsets;
+ }
+ if ((PciStatusReg & EFI_PCI_STATUS_CAPABILITY) != 0) {
+ UINT8 NormalCapHdrOffset;
+
+ //
+ // Fetch the start offset of the normal capabilities list.
+ //
+ Status = PciDevice->ReadConfig (PciDevice, PCI_CAPBILITY_POINTER_OFFSET,
+ &NormalCapHdrOffset, sizeof NormalCapHdrOffset);
+ if (RETURN_ERROR (Status)) {
+ goto FreeCapHdrOffsets;
+ }
+
+ //
+ // Traverse the normal capabilities list.
+ //
+ NormalCapHdrOffset &= 0xFC;
+ while (NormalCapHdrOffset > 0) {
+ EFI_PCI_CAPABILITY_HDR NormalCapHdr;
+
+ Status = PciDevice->ReadConfig (PciDevice, NormalCapHdrOffset,
+ &NormalCapHdr, sizeof NormalCapHdr);
+ if (RETURN_ERROR (Status)) {
+ goto FreeCapHdrOffsets;
+ }
+
+ Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapNormal,
+ NormalCapHdr.CapabilityID, NormalCapHdrOffset, 0);
+ if (RETURN_ERROR (Status)) {
+ goto FreeCapHdrOffsets;
+ }
+
+ if (NormalCapHdr.CapabilityID == EFI_PCI_CAPABILITY_ID_PCIEXP) {
+ DeviceIsExpress = TRUE;
+ }
+ NormalCapHdrOffset = NormalCapHdr.NextItemPtr & 0xFC;
+ }
+ }
+
+ //
+ // If the device has been found PCI Express, attempt to traverse the extended
+ // capabilities list. It starts right after the normal config space.
+ //
+ if (DeviceIsExpress) {
+ UINT16 ExtendedCapHdrOffset;
+
+ ExtendedCapHdrOffset = PCI_MAX_CONFIG_OFFSET;
+ while (ExtendedCapHdrOffset > 0) {
+ PCI_EXPRESS_EXTENDED_CAPABILITIES_HEADER ExtendedCapHdr;
+
+ Status = PciDevice->ReadConfig (PciDevice, ExtendedCapHdrOffset,
+ &ExtendedCapHdr, sizeof ExtendedCapHdr);
+ //
+ // If the first extended config space access fails, assume the device has
+ // no extended capabilities. If the first extended config space access
+ // succeeds but we read an "all bits zero" extended capability header,
+ // that means (by spec) the device has no extended capabilities.
+ //
+ if (ExtendedCapHdrOffset == PCI_MAX_CONFIG_OFFSET &&
+ (RETURN_ERROR (Status) ||
+ IsZeroBuffer (&ExtendedCapHdr, sizeof ExtendedCapHdr))) {
+ break;
+ }
+ if (RETURN_ERROR (Status)) {
+ goto FreeCapHdrOffsets;
+ }
+
+ Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapExtended,
+ (UINT16)ExtendedCapHdr.CapabilityId, ExtendedCapHdrOffset,
+ (UINT8)ExtendedCapHdr.CapabilityVersion);
+ if (RETURN_ERROR (Status)) {
+ goto FreeCapHdrOffsets;
+ }
+
+ ExtendedCapHdrOffset = ExtendedCapHdr.NextCapabilityOffset & 0xFFC;
+ if (ExtendedCapHdrOffset > 0 &&
+ ExtendedCapHdrOffset < PCI_MAX_CONFIG_OFFSET) {
+ //
+ // Invalid capability pointer.
+ //
+ Status = RETURN_DEVICE_ERROR;
+ goto FreeCapHdrOffsets;
+ }
+ }
+ }
+
+ //
+ // Both capabilities lists have been parsed; compute the PCI_CAP.MaxSizeHint
+ // members if at least one capability has been found. In parallel, evacuate
+ // the CapHdrOffsets collection.
+ //
+ // At first, set OffsetEntry to the iterator of the PCI_CAP object with the
+ // lowest Offset (if such exists).
+ //
+ OffsetEntry = OrderedCollectionMin (CapHdrOffsets);
+ if (OffsetEntry != NULL) {
+ ORDERED_COLLECTION_ENTRY *NextOffsetEntry;
+ PCI_CAP *PciCap;
+
+ //
+ // Initialize NextOffsetEntry to the iterator of the PCI_CAP object with
+ // the second lowest Offset (if such exists).
+ //
+ NextOffsetEntry = OrderedCollectionNext (OffsetEntry);
+ //
+ // Calculate MaxSizeHint for all PCI_CAP objects except the one with the
+ // highest Offset.
+ //
+ while (NextOffsetEntry != NULL) {
+ PCI_CAP *NextPciCap;
+
+ OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);
+ NextPciCap = OrderedCollectionUserStruct (NextOffsetEntry);
+ CalculatePciCapMaxSizeHint (PciCap, NextPciCap);
+
+ OffsetEntry = NextOffsetEntry;
+ NextOffsetEntry = OrderedCollectionNext (OffsetEntry);
+ }
+ //
+ // Calculate MaxSizeHint for the PCI_CAP object with the highest Offset.
+ //
+ OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);
+ CalculatePciCapMaxSizeHint (PciCap, NULL);
+ }
+ ASSERT (OrderedCollectionIsEmpty (CapHdrOffsets));
+ OrderedCollectionUninit (CapHdrOffsets);
+
+ DebugDumpPciCapList (OutCapList);
+ *CapList = OutCapList;
+ return RETURN_SUCCESS;
+
+FreeCapHdrOffsets:
+ EmptyAndUninitPciCapCollection (CapHdrOffsets, FALSE);
+
+FreeCapabilities:
+ EmptyAndUninitPciCapCollection (OutCapList->Capabilities, TRUE);
+
+FreeOutCapList:
+ FreePool (OutCapList);
+
+ ASSERT (RETURN_ERROR (Status));
+ DEBUG ((DEBUG_ERROR, "%a:%a: %r\n", gEfiCallerBaseName, __FUNCTION__,
+ Status));
+ return Status;
+}
+
+
+/**
+ Free the resources used by CapList.
+
+ @param[in] CapList The PCI_CAP_LIST object to free, originally produced by
+ PciCapListInit().
+**/
+VOID
+EFIAPI
+PciCapListUninit (
+ IN PCI_CAP_LIST *CapList
+ )
+{
+ EmptyAndUninitPciCapCollection (CapList->Capabilities, TRUE);
+ FreePool (CapList);
+}
+
+
+/**
+ Locate a capability instance in the parsed capabilities lists.
+
+ @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().
+
+ @param[in] Domain Distinguishes whether CapId is 8-bit wide and
+ interpreted in normal config space, or 16-bit wide and
+ interpreted in extended config space. Capability ID
+ definitions are relative to domain.
+
+ @param[in] CapId Capability identifier to look up.
+
+ @param[in] Instance Domain and CapId may identify a multi-instance
+ capability. When Instance is zero, the first instance of
+ the capability is located (in list traversal order --
+ which may not mean increasing config space offset
+ order). Higher Instance values locate subsequent
+ instances of the same capability (in list traversal
+ order).
+
+ @param[out] Cap The capability instance that matches the search
+ criteria. Cap is owned by CapList and becomes invalid
+ when CapList is freed with PciCapListUninit().
+ PciCapListFindCap() may be called with Cap set to NULL,
+ in order to test the existence of a specific capability
+ instance.
+
+ @retval RETURN_SUCCESS The capability instance identified by (Domain,
+ CapId, Instance) has been found.
+
+ @retval RETURN_NOT_FOUND The requested (Domain, CapId, Instance) capability
+ instance does not exist.
+**/
+RETURN_STATUS
+EFIAPI
+PciCapListFindCap (
+ IN PCI_CAP_LIST *CapList,
+ IN PCI_CAP_DOMAIN Domain,
+ IN UINT16 CapId,
+ IN UINT16 Instance,
+ OUT PCI_CAP **Cap OPTIONAL
+ )
+{
+ PCI_CAP_KEY Key;
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;
+
+ Key.Domain = Domain;
+ Key.CapId = CapId;
+ Key.Instance = Instance;
+
+ PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);
+ if (PciCapEntry == NULL) {
+ return RETURN_NOT_FOUND;
+ }
+ if (Cap != NULL) {
+ *Cap = OrderedCollectionUserStruct (PciCapEntry);
+ }
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Locate the first instance of the capability given by (Domain, CapId) such
+ that the instance's Version is greater than or equal to MinVersion.
+
+ This is a convenience function that may save client code calls to
+ PciCapListFindCap() and PciCapGetInfo().
+
+ @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().
+
+ @param[in] Domain Distinguishes whether CapId is 8-bit wide and
+ interpreted in normal config space, or 16-bit wide and
+ interpreted in extended config space. Capability ID
+ definitions are relative to domain.
+
+ @param[in] CapId Capability identifier to look up.
+
+ @param[in] MinVersion The minimum version that the capability instance is
+ required to have. Note that all capability instances
+ in Domain=PciCapNormal have Version=0.
+
+ @param[out] Cap The first capability instance that matches the search
+ criteria. Cap is owned by CapList and becomes invalid
+ when CapList is freed with PciCapListUninit().
+ PciCapListFindCapVersion() may be called with Cap set
+ to NULL, in order just to test whether the search
+ criteria are satisfiable.
+
+ @retval RETURN_SUCCESS The first capability instance matching (Domain,
+ CapId, MinVersion) has been located.
+
+ @retval RETURN_NOT_FOUND No capability instance matches (Domain, CapId,
+ MinVersion).
+**/
+RETURN_STATUS
+EFIAPI
+PciCapListFindCapVersion (
+ IN PCI_CAP_LIST *CapList,
+ IN PCI_CAP_DOMAIN Domain,
+ IN UINT16 CapId,
+ IN UINT8 MinVersion,
+ OUT PCI_CAP **Cap OPTIONAL
+ )
+{
+ PCI_CAP_KEY Key;
+ ORDERED_COLLECTION_ENTRY *PciCapEntry;
+
+ //
+ // Start the version checks at Instance#0 of (Domain, CapId).
+ //
+ Key.Domain = Domain;
+ Key.CapId = CapId;
+ Key.Instance = 0;
+
+ for (PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);
+ PciCapEntry != NULL;
+ PciCapEntry = OrderedCollectionNext (PciCapEntry)) {
+ PCI_CAP *PciCap;
+
+ PciCap = OrderedCollectionUserStruct (PciCapEntry);
+ //
+ // PCI_CAP.Key ordering keeps instances of the same (Domain, CapId)
+ // adjacent to each other, so stop searching if either Domain or CapId
+ // changes.
+ //
+ if (PciCap->Key.Domain != Domain || PciCap->Key.CapId != CapId) {
+ break;
+ }
+ if (PciCap->Version >= MinVersion) {
+ //
+ // Match found.
+ //
+ if (Cap != NULL) {
+ *Cap = PciCap;
+ }
+ return RETURN_SUCCESS;
+ }
+ }
+ return RETURN_NOT_FOUND;
+}
+
+
+/**
+ Get information about a PCI Capability instance.
+
+ @param[in] Cap The capability instance to get info about, located with
+ PciCapListFindCap*().
+
+ @param[out] Info A PCI_CAP_INFO structure that describes the properties of
+ Cap.
+
+ @retval RETURN_SUCCESS Fields of Info have been set.
+
+ @return Unspecified error codes, if filling in Info failed
+ for some reason.
+**/
+RETURN_STATUS
+EFIAPI
+PciCapGetInfo (
+ IN PCI_CAP *Cap,
+ OUT PCI_CAP_INFO *Info
+ )
+{
+ PCI_CAP *InstanceZero;
+
+ ASSERT (Info != NULL);
+
+ InstanceZero = (Cap->Key.Instance == 0 ? Cap :
+ Cap->NumInstancesUnion.InstanceZero);
+
+ Info->Domain = Cap->Key.Domain;
+ Info->CapId = Cap->Key.CapId;
+ Info->NumInstances = InstanceZero->NumInstancesUnion.NumInstances;
+ Info->Instance = Cap->Key.Instance;
+ Info->Offset = Cap->Offset;
+ Info->MaxSizeHint = Cap->MaxSizeHint;
+ Info->Version = Cap->Version;
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Read a slice of a capability instance.
+
+ The function performs as few config space accesses as possible (without
+ attempting 64-bit wide accesses). PciCapRead() performs bounds checking on
+ SourceOffsetInCap and Size, and only invokes PciDevice->ReadConfig() if the
+ requested transfer falls within Cap.
+
+ @param[in] PciDevice Implementation-specific unique representation
+ of the PCI device in the PCI hierarchy.
+
+ @param[in] Cap The capability instance to read, located with
+ PciCapListFindCap*().
+
+ @param[in] SourceOffsetInCap Source offset relative to the capability
+ header to start reading from. A zero value
+ refers to the first byte of the capability
+ header.
+
+ @param[out] DestinationBuffer Buffer to store the read data to.
+
+ @param[in] Size The number of bytes to transfer.
+
+ @retval RETURN_SUCCESS Size bytes have been transferred from Cap to
+ DestinationBuffer.
+
+ @retval RETURN_BAD_BUFFER_SIZE Reading Size bytes starting from
+ SourceOffsetInCap would not (entirely) be
+ contained within Cap, as suggested by
+ PCI_CAP_INFO.MaxSizeHint. No bytes have been
+ read.
+
+ @return Error codes propagated from
+ PciDevice->ReadConfig(). Fewer than Size
+ bytes may have been read.
+**/
+RETURN_STATUS
+EFIAPI
+PciCapRead (
+ IN PCI_CAP_DEV *PciDevice,
+ IN PCI_CAP *Cap,
+ IN UINT16 SourceOffsetInCap,
+ OUT VOID *DestinationBuffer,
+ IN UINT16 Size
+ )
+{
+ //
+ // Note: all UINT16 values are promoted to INT32 below, and addition and
+ // comparison take place between INT32 values.
+ //
+ if (SourceOffsetInCap + Size > Cap->MaxSizeHint) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+ return PciDevice->ReadConfig (PciDevice, Cap->Offset + SourceOffsetInCap,
+ DestinationBuffer, Size);
+}
+
+
+/**
+ Write a slice of a capability instance.
+
+ The function performs as few config space accesses as possible (without
+ attempting 64-bit wide accesses). PciCapWrite() performs bounds checking on
+ DestinationOffsetInCap and Size, and only invokes PciDevice->WriteConfig() if
+ the requested transfer falls within Cap.
+
+ @param[in] PciDevice Implementation-specific unique
+ representation of the PCI device in the
+ PCI hierarchy.
+
+ @param[in] Cap The capability instance to write, located
+ with PciCapListFindCap*().
+
+ @param[in] DestinationOffsetInCap Destination offset relative to the
+ capability header to start writing at. A
+ zero value refers to the first byte of the
+ capability header.
+
+ @param[in] SourceBuffer Buffer to read the data to be stored from.
+
+ @param[in] Size The number of bytes to transfer.
+
+ @retval RETURN_SUCCESS Size bytes have been transferred from
+ SourceBuffer to Cap.
+
+ @retval RETURN_BAD_BUFFER_SIZE Writing Size bytes starting at
+ DestinationOffsetInCap would not (entirely)
+ be contained within Cap, as suggested by
+ PCI_CAP_INFO.MaxSizeHint. No bytes have been
+ written.
+
+ @return Error codes propagated from
+ PciDevice->WriteConfig(). Fewer than Size
+ bytes may have been written.
+**/
+RETURN_STATUS
+EFIAPI
+PciCapWrite (
+ IN PCI_CAP_DEV *PciDevice,
+ IN PCI_CAP *Cap,
+ IN UINT16 DestinationOffsetInCap,
+ IN VOID *SourceBuffer,
+ IN UINT16 Size
+ )
+{
+ //
+ // Note: all UINT16 values are promoted to INT32 below, and addition and
+ // comparison take place between INT32 values.
+ //
+ if (DestinationOffsetInCap + Size > Cap->MaxSizeHint) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+ return PciDevice->WriteConfig (PciDevice,
+ Cap->Offset + DestinationOffsetInCap, SourceBuffer,
+ Size);
+}
diff --git a/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h b/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h new file mode 100644 index 000000000..e84dab62c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h @@ -0,0 +1,54 @@ +/** @file
+ Work with PCI capabilities in PCI config space -- internal type definitions.
+
+ Copyright (C) 2018, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef __BASE_PCI_CAP_LIB_H__
+#define __BASE_PCI_CAP_LIB_H__
+
+#include <Library/OrderedCollectionLib.h>
+
+#include <Library/PciCapLib.h>
+
+//
+// Structure that uniquely identifies a capability instance and serves as key
+// for insertion and lookup.
+//
+typedef struct {
+ PCI_CAP_DOMAIN Domain;
+ UINT16 CapId;
+ UINT16 Instance;
+} PCI_CAP_KEY;
+
+//
+// In Instance==0 PCI_CAP objects, store NumInstances directly. In Instance>0
+// PCI_CAP objects, link Instance#0 of the same (Domain, CapId). This way
+// NumInstances needs maintenance in one object only, per (Domain, CapId) pair.
+//
+typedef union {
+ UINT16 NumInstances;
+ PCI_CAP *InstanceZero;
+} PCI_CAP_NUM_INSTANCES;
+
+//
+// Complete the incomplete PCI_CAP structure here.
+//
+struct PCI_CAP {
+ PCI_CAP_KEY Key;
+ PCI_CAP_NUM_INSTANCES NumInstancesUnion;
+ UINT16 Offset;
+ UINT16 MaxSizeHint;
+ UINT8 Version;
+};
+
+//
+// Complete the incomplete PCI_CAP_LIST structure here.
+//
+struct PCI_CAP_LIST {
+ ORDERED_COLLECTION *Capabilities;
+};
+
+#endif // __BASE_PCI_CAP_LIB_H__
diff --git a/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf b/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf new file mode 100644 index 000000000..a513014f2 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf @@ -0,0 +1,32 @@ +## @file
+# Work with PCI capabilities in PCI config space.
+#
+# Provides functions to parse capabilities lists, and to locate, describe, read
+# and write capabilities. PCI config space access is abstracted away.
+#
+# Copyright (C) 2018, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = BasePciCapLib
+ FILE_GUID = 6957540D-F7B5-4D5B-BEE4-FC14114DCD3C
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PciCapLib
+
+[Sources]
+ BasePciCapLib.h
+ BasePciCapLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ OrderedCollectionLib
diff --git a/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c b/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c new file mode 100644 index 000000000..81f2a3eb0 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c @@ -0,0 +1,220 @@ +/** @file
+ Plug a PciSegmentLib backend into PciCapLib, for config space access.
+
+ Copyright (C) 2018, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <IndustryStandard/Pci23.h>
+
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PciSegmentLib.h>
+
+#include "BasePciCapPciSegmentLib.h"
+
+
+/**
+ Read the config space of a given PCI device (both normal and extended).
+
+ SegmentDevReadConfig() performs as few config space accesses as possible
+ (without attempting 64-bit wide accesses).
+
+ @param[in] PciDevice Implementation-specific unique representation
+ of the PCI device in the PCI hierarchy.
+
+ @param[in] SourceOffset Source offset in the config space of the PCI
+ device to start reading from.
+
+ @param[out] DestinationBuffer Buffer to store the read data to.
+
+ @param[in] Size The number of bytes to transfer.
+
+ @retval RETURN_SUCCESS Size bytes have been transferred from config
+ space to DestinationBuffer.
+
+ @retval RETURN_UNSUPPORTED Accessing Size bytes from SourceOffset exceeds
+ the config space limit of the PCI device.
+ Although PCI_CAP_DEV_READ_CONFIG allows reading
+ fewer than Size bytes in this case,
+ SegmentDevReadConfig() will read none.
+**/
+STATIC
+RETURN_STATUS
+EFIAPI
+SegmentDevReadConfig (
+ IN PCI_CAP_DEV *PciDevice,
+ IN UINT16 SourceOffset,
+ OUT VOID *DestinationBuffer,
+ IN UINT16 Size
+ )
+{
+ SEGMENT_DEV *SegmentDev;
+ UINT16 ConfigSpaceSize;
+ UINT64 SourceAddress;
+
+ SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
+ ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
+ //
+ // Note that all UINT16 variables below are promoted to INT32, and the
+ // addition and the comparison is carried out in INT32.
+ //
+ if (SourceOffset + Size > ConfigSpaceSize) {
+ return RETURN_UNSUPPORTED;
+ }
+ SourceAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,
+ SegmentDev->BusNr, SegmentDev->DeviceNr,
+ SegmentDev->FunctionNr, SourceOffset);
+ PciSegmentReadBuffer (SourceAddress, Size, DestinationBuffer);
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Write the config space of a given PCI device (both normal and extended).
+
+ SegmentDevWriteConfig() performs as few config space accesses as possible
+ (without attempting 64-bit wide accesses).
+
+ @param[in] PciDevice Implementation-specific unique representation
+ of the PCI device in the PCI hierarchy.
+
+ @param[in] DestinationOffset Destination offset in the config space of the
+ PCI device to start writing at.
+
+ @param[in] SourceBuffer Buffer to read the data to be stored from.
+
+ @param[in] Size The number of bytes to transfer.
+
+ @retval RETURN_SUCCESS Size bytes have been transferred from
+ SourceBuffer to config space.
+
+ @retval RETURN_UNSUPPORTED Accessing Size bytes at DestinationOffset exceeds
+ the config space limit of the PCI device.
+ Although PCI_CAP_DEV_WRITE_CONFIG allows writing
+ fewer than Size bytes in this case,
+ SegmentDevWriteConfig() will write none.
+**/
+STATIC
+RETURN_STATUS
+EFIAPI
+SegmentDevWriteConfig (
+ IN PCI_CAP_DEV *PciDevice,
+ IN UINT16 DestinationOffset,
+ IN VOID *SourceBuffer,
+ IN UINT16 Size
+ )
+{
+ SEGMENT_DEV *SegmentDev;
+ UINT16 ConfigSpaceSize;
+ UINT64 DestinationAddress;
+
+ SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
+ ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
+ PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
+ //
+ // Note that all UINT16 variables below are promoted to INT32, and the
+ // addition and the comparison is carried out in INT32.
+ //
+ if (DestinationOffset + Size > ConfigSpaceSize) {
+ return RETURN_UNSUPPORTED;
+ }
+ DestinationAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,
+ SegmentDev->BusNr, SegmentDev->DeviceNr,
+ SegmentDev->FunctionNr, DestinationOffset);
+ PciSegmentWriteBuffer (DestinationAddress, Size, SourceBuffer);
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function
+ quadruplet. The config space accessors are based upon PciSegmentLib.
+
+ @param[in] MaxDomain If MaxDomain is PciCapExtended, then
+ PciDevice->ReadConfig() and PciDevice->WriteConfig()
+ will delegate extended config space accesses too to
+ PciSegmentReadBuffer() and PciSegmentWriteBuffer(),
+ respectively. Otherwise, PciDevice->ReadConfig() and
+ PciDevice->WriteConfig() will reject accesses to
+ extended config space with RETURN_UNSUPPORTED, without
+ calling PciSegmentReadBuffer() or
+ PciSegmentWriteBuffer(). By setting MaxDomain to
+ PciCapNormal, the platform can prevent undefined
+ PciSegmentLib behavior when the PCI root bridge under
+ the PCI device at Segment:Bus:Device.Function doesn't
+ support extended config space.
+
+ @param[in] Segment 16-bit wide segment number.
+
+ @param[in] Bus 8-bit wide bus number.
+
+ @param[in] Device 5-bit wide device number.
+
+ @param[in] Function 3-bit wide function number.
+
+ @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.
+ PciDevice can be passed to the PciCapLib APIs.
+
+ @retval RETURN_SUCCESS PciDevice has been constructed and output.
+
+ @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the
+ permitted number of bits.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+**/
+RETURN_STATUS
+EFIAPI
+PciCapPciSegmentDeviceInit (
+ IN PCI_CAP_DOMAIN MaxDomain,
+ IN UINT16 Segment,
+ IN UINT8 Bus,
+ IN UINT8 Device,
+ IN UINT8 Function,
+ OUT PCI_CAP_DEV **PciDevice
+ )
+{
+ SEGMENT_DEV *SegmentDev;
+
+ if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ SegmentDev = AllocatePool (sizeof *SegmentDev);
+ if (SegmentDev == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ SegmentDev->Signature = SEGMENT_DEV_SIG;
+ SegmentDev->MaxDomain = MaxDomain;
+ SegmentDev->SegmentNr = Segment;
+ SegmentDev->BusNr = Bus;
+ SegmentDev->DeviceNr = Device;
+ SegmentDev->FunctionNr = Function;
+ SegmentDev->BaseDevice.ReadConfig = SegmentDevReadConfig;
+ SegmentDev->BaseDevice.WriteConfig = SegmentDevWriteConfig;
+
+ *PciDevice = &SegmentDev->BaseDevice;
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Free the resources used by PciDevice.
+
+ @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by
+ PciCapPciSegmentDeviceInit().
+**/
+VOID
+EFIAPI
+PciCapPciSegmentDeviceUninit (
+ IN PCI_CAP_DEV *PciDevice
+ )
+{
+ SEGMENT_DEV *SegmentDev;
+
+ SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
+ FreePool (SegmentDev);
+}
diff --git a/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h b/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h new file mode 100644 index 000000000..a806bf320 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h @@ -0,0 +1,41 @@ +/** @file
+ Plug a PciSegmentLib backend into PciCapLib, for config space access --
+ internal macro and type definitions.
+
+ Copyright (C) 2018, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__
+#define __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__
+
+#include <Library/DebugLib.h>
+
+#include <Library/PciCapPciSegmentLib.h>
+
+#define SEGMENT_DEV_SIG SIGNATURE_64 ('P', 'C', 'P', 'S', 'G', 'M', 'N', 'T')
+
+typedef struct {
+ //
+ // Signature identifying the derived class.
+ //
+ UINT64 Signature;
+ //
+ // Members added by the derived class, specific to the use of PciSegmentLib.
+ //
+ PCI_CAP_DOMAIN MaxDomain;
+ UINT16 SegmentNr;
+ UINT8 BusNr;
+ UINT8 DeviceNr;
+ UINT8 FunctionNr;
+ //
+ // Base class.
+ //
+ PCI_CAP_DEV BaseDevice;
+} SEGMENT_DEV;
+
+#define SEGMENT_DEV_FROM_PCI_CAP_DEV(PciDevice) \
+ CR (PciDevice, SEGMENT_DEV, BaseDevice, SEGMENT_DEV_SIG)
+
+#endif // __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__
diff --git a/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf b/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf new file mode 100644 index 000000000..8d41a35f8 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf @@ -0,0 +1,29 @@ +## @file
+# Plug a PciSegmentLib backend into PciCapLib, for config space access.
+#
+# Copyright (C) 2018, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = BasePciCapPciSegmentLib
+ FILE_GUID = ED011855-AA31-43B9-ACC0-BF45A05C5985
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PciCapPciSegmentLib
+
+[Sources]
+ BasePciCapPciSegmentLib.h
+ BasePciCapPciSegmentLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ MemoryAllocationLib
+ PciSegmentLib
diff --git a/roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.c b/roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.c new file mode 100644 index 000000000..2c45ad70c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.c @@ -0,0 +1,426 @@ +/** @file
+
+ Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2015, Nahanni Systems.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Uefi.h"
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/BhyveFwCtlLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#define FW_PORT 0x510
+#define FW_IPORT 0x511
+
+/* Transport protocol basic operations */
+#define OP_NULL 1
+#define OP_ECHO 2
+#define OP_GET 3
+#define OP_GET_LEN 4
+#define OP_SET 5
+
+/* Transport protocol error returns */
+#define T_ESUCCESS 0
+#define T_ENOENT 2
+#define T_E2BIG 7
+#define T_EMSGSIZE 40
+
+#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
+
+STATIC CONST CHAR8 mBhyveSig[4] = { 'B', 'H', 'Y', 'V' };
+
+STATIC BOOLEAN mBhyveFwCtlSupported = FALSE;
+
+STATIC INTN mBhyveFwCtlTxid = 0xa5;
+
+/* XXX Maybe a better inbuilt version of this ? */
+struct BIoVec {
+ VOID *Base;
+ UINTN Len;
+};
+
+struct MsgRxHdr {
+ UINT32 Sz;
+ UINT32 Op;
+ UINT32 TxId;
+ UINT32 Err;
+};
+
+STATIC
+RETURN_STATUS
+EFIAPI
+BhyveFwCtl_CvtErr (
+ IN UINT32 errno
+ )
+{
+ RETURN_STATUS Status;
+
+ switch (errno) {
+ case T_ESUCCESS:
+ Status = RETURN_SUCCESS;
+ break;
+ case T_ENOENT:
+ Status = RETURN_NOT_FOUND;
+ break;
+ case T_E2BIG:
+ Status = RETURN_INVALID_PARAMETER;
+ break;
+ case T_EMSGSIZE:
+ Status = RETURN_BUFFER_TOO_SMALL;
+ break;
+ default:
+ Status = RETURN_PROTOCOL_ERROR;
+ break;
+ }
+
+ return Status;
+}
+
+STATIC
+UINTN
+EFIAPI
+BIov_WLen (
+ IN struct BIoVec b[]
+ )
+{
+ UINTN i;
+ UINTN tLen;
+
+ tLen = 0;
+
+ if (b != NULL) {
+ for (i = 0; b[i].Base != NULL; i++)
+ tLen += ROUNDUP (b[i].Len, sizeof(UINT32));
+ }
+
+ return tLen;
+}
+
+/**
+ Utility to send 1-3 bhyes of input as a 4-byte value
+ with trailing zeroes.
+ **/
+STATIC
+UINT32
+BIov_Send_Rem (
+ IN UINT32 *Data,
+ IN UINTN Len
+ )
+{
+ union {
+ UINT8 c[4];
+ UINT32 w;
+ } u;
+ UINT8 *cdata;
+ UINTN i;
+
+ cdata = (UINT8 *)Data;
+ u.w = 0;
+
+ for (i = 0; i < Len; i++)
+ u.c[i] = *cdata++;
+
+ return u.w;
+}
+
+/**
+ Send a block of data out the i/o port as 4-byte quantities,
+ appending trailing zeroes on the last if required.
+ **/
+STATIC
+VOID
+BIov_Send (
+ IN char *Data,
+ IN UINTN Len
+ )
+{
+ UINT32 *LData;
+
+ LData = (UINT32 *)Data;
+
+ while (Len > sizeof(UINT32)) {
+ IoWrite32 (FW_PORT, *LData++);
+ Len -= sizeof(UINT32);
+ }
+
+ if (Len > 0) {
+ IoWrite32 (FW_PORT, BIov_Send_Rem (LData, Len));
+ }
+}
+
+/**
+ Send data described by an array of iovecs out the i/o port.
+ **/
+STATIC
+VOID
+BIov_SendAll (
+ IN struct BIoVec b[]
+ )
+{
+ INTN i;
+
+ if (b != NULL) {
+ for (i = 0; b[i].Base; i++) {
+ BIov_Send (b[i].Base, b[i].Len);
+ }
+ }
+}
+
+/**
+ Prepend the transport header to a block of data and send.
+ **/
+STATIC
+VOID
+EFIAPI
+BhyveFwCtl_MsgSend(
+ IN UINTN OpCode,
+ IN struct BIoVec Data[]
+ )
+{
+ struct BIoVec hIov[4];
+ UINT32 Hdr[3];
+ UINTN i;
+
+ /* Set up header as an iovec */
+ for (i = 0; i < 3; i++) {
+ hIov[i].Base = &Hdr[i];
+ hIov[i].Len = sizeof(Hdr[0]);
+ }
+ hIov[i].Base = NULL;
+ hIov[i].Len = 0;
+
+ /* Initialize header */
+ Hdr[0] = BIov_WLen (hIov) + BIov_WLen (Data);
+ Hdr[1] = OpCode;
+ Hdr[2] = mBhyveFwCtlTxid;
+
+ /* Send header and data */
+ BIov_SendAll (hIov);
+ BIov_SendAll (Data);
+}
+
+/**
+ Read a transport response and optional data from the i/o port.
+ **/
+STATIC
+RETURN_STATUS
+EFIAPI
+BhyveFwCtl_MsgRecv(
+ OUT struct MsgRxHdr *Rhdr,
+ OUT struct BIoVec Data[]
+ )
+{
+ RETURN_STATUS Status;
+ UINT32 *Dp;
+ UINT32 Rd;
+ UINTN remLen;
+ INTN oLen, xLen;
+
+ Rd = IoRead32 (FW_PORT);
+ if (Rd < sizeof(struct MsgRxHdr)) {
+ ;
+ }
+
+ /* Read in header and setup initial error */
+ Rhdr->Sz = Rd;
+ Rhdr->Op = IoRead32 (FW_PORT);
+ Rhdr->TxId = IoRead32 (FW_PORT);
+ Rhdr->Err = IoRead32 (FW_PORT);
+
+ /* Convert transport errno into UEFI error status */
+ Status = BhyveFwCtl_CvtErr(Rhdr->Err);
+
+ remLen = Rd - sizeof(struct MsgRxHdr);
+ xLen = 0;
+
+ /*
+ * A few cases to handle:
+ * - the user didn't supply a read buffer
+ * - the buffer is too small for the response
+ * - the response is zero-length
+ */
+ if (Data != NULL) {
+ Dp = (UINT32 *)Data[0].Base;
+ oLen = remLen;
+ if (remLen > Data[0].Len) {
+ Status = RETURN_BUFFER_TOO_SMALL;
+ xLen = remLen - Data[0].Len;
+ oLen = remLen = Data[0].Len;
+ }
+ while (remLen > 0) {
+ *Dp++ = IoRead32 (FW_PORT);
+ remLen -= sizeof(UINT32);
+ }
+ Data[0].Len = oLen;
+ } else {
+ /* No user data, but data returned - drop */
+ if (remLen > 0) {
+ Status = RETURN_BUFFER_TOO_SMALL;
+ xLen = remLen;
+ }
+ }
+
+ /* Drop additional data */
+ while (xLen > 0) {
+ (void) IoRead32 (FW_PORT);
+ xLen -= sizeof(UINT32);
+ }
+
+ return Status;
+}
+
+
+STATIC
+RETURN_STATUS
+EFIAPI
+BhyveFwCtl_Msg(
+ IN UINTN OpCode,
+ IN struct BIoVec Sdata[],
+ OUT struct BIoVec Rdata[]
+ )
+{
+ struct MsgRxHdr Rh;
+ RETURN_STATUS Status;
+
+ Status = RETURN_SUCCESS;
+
+ BhyveFwCtl_MsgSend (OpCode, Sdata);
+ Status = BhyveFwCtl_MsgRecv (&Rh, Rdata);
+
+ mBhyveFwCtlTxid++;
+
+ return Status;
+}
+
+STATIC
+RETURN_STATUS
+EFIAPI
+BhyveFwCtlGetLen (
+ IN CONST CHAR8 *Name,
+ IN OUT UINTN *Size
+ )
+{
+ struct BIoVec Req[2], Resp[2];
+ RETURN_STATUS Status;
+
+ Req[0].Base = (VOID *)Name;
+ Req[0].Len = AsciiStrLen (Name) + 1;
+ Req[1].Base = NULL;
+
+ Resp[0].Base = Size;
+ Resp[0].Len = sizeof(UINTN);
+ Resp[1].Base = NULL;
+
+ Status = BhyveFwCtl_Msg (OP_GET_LEN, Req, Resp);
+
+ return Status;
+}
+
+#define FMAXSZ 1024
+STATIC struct {
+ UINT64 fSize;
+ UINT32 fData[FMAXSZ];
+} FwGetvalBuf;
+
+STATIC
+RETURN_STATUS
+EFIAPI
+BhyveFwCtlGetVal (
+ IN CONST CHAR8 *Name,
+ OUT VOID *Item,
+ IN OUT UINTN *Size
+ )
+{
+ struct BIoVec Req[2], Resp[2];
+ RETURN_STATUS Status;
+
+ /* Make sure temp buffer is larger than passed-in size */
+ if (*Size > sizeof(FwGetvalBuf.fData))
+ return RETURN_INVALID_PARAMETER;
+
+ Req[0].Base = (VOID *)Name;
+ Req[0].Len = AsciiStrLen(Name) + 1;
+ Req[1].Base = NULL;
+
+ Resp[0].Base = &FwGetvalBuf;
+ Resp[0].Len = sizeof(UINT64) + *Size;
+ Resp[1].Base = NULL;
+
+ Status = BhyveFwCtl_Msg (OP_GET, Req, Resp);
+
+ /*
+ * Copy out data on success (or on a truncated message).
+ * XXX This step can be eliminted with Msg() supporting
+ * multiple iovecs.
+ */
+ if ((Status == RETURN_SUCCESS) || (Status == RETURN_BUFFER_TOO_SMALL)) {
+ *Size = FwGetvalBuf.fSize;
+ CopyMem (Item, FwGetvalBuf.fData, *Size);
+ }
+
+ return Status;
+}
+
+/**
+ Front end to the internal GET_LEN and GET protocols
+ **/
+RETURN_STATUS
+EFIAPI
+BhyveFwCtlGet (
+ IN CONST CHAR8 *Name,
+ OUT VOID *Item,
+ IN OUT UINTN *Size
+ )
+{
+ RETURN_STATUS Status;
+
+ if (mBhyveFwCtlSupported == FALSE)
+ return RETURN_UNSUPPORTED;
+
+ if (Item == NULL) {
+ Status = BhyveFwCtlGetLen (Name, Size);
+ } else {
+ Status = BhyveFwCtlGetVal (Name, Item, Size);
+ }
+
+ return Status;
+}
+
+
+/**
+ Library initialization. Probe the host to see if the f/w ctl
+ interface is supported.
+ **/
+RETURN_STATUS
+EFIAPI
+BhyveFwCtlInitialize (
+ VOID
+ )
+{
+ UINTN i;
+ UINT8 ch;
+
+ DEBUG ((DEBUG_INFO, "FwCtlInitialize\n"));
+
+ IoWrite16 (FW_PORT, 0x0000);
+ for (i = 0; i < 4; i++) {
+ ch = IoRead8 (FW_IPORT);
+ if (ch != mBhyveSig[i]) {
+ DEBUG ((DEBUG_INFO, "Host f/w sig mismatch %c/%c\n", ch, mBhyveSig[i]));
+ return RETURN_SUCCESS;
+ }
+ }
+
+ mBhyveFwCtlSupported = TRUE;
+
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.inf b/roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.inf new file mode 100644 index 000000000..4643de381 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.inf @@ -0,0 +1,41 @@ +## @file
+#
+# Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
+# Copyright (C) 2015 Nahanni Systems
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BhyveFwCtlLib
+ FILE_GUID = fd982666-67f9-11e5-a42a-0025908602f6
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = BhyveFwCtlLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER
+
+ CONSTRUCTOR = BhyveFwCtlInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = X64
+#
+
+[Sources]
+ BhyveFwCtlLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ IoLib
+ MemoryAllocationLib
+ IntrinsicLib
+
diff --git a/roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf b/roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf new file mode 100644 index 000000000..4a8c0bac2 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf @@ -0,0 +1,40 @@ +## @file
+# An instance of the PCI Library that is based on both the PCI CF8 Library and
+# the PCI Express Library.
+#
+# This PciLib instance caches the OVMF platform type (I440FX vs. Q35) in
+# its entry point function, then delegates function calls to one of the
+# PciCf8Lib or PciExpressLib "backends" as appropriate.
+#
+# Copyright (C) 2016, Red Hat, Inc.
+#
+# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxePciLibI440FxQ35
+ FILE_GUID = 5360bff6-3911-4495-ae3c-b02ff004b585
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PciLib|DXE_DRIVER DXE_RUNTIME_DRIVER SMM_CORE DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION
+ CONSTRUCTOR = InitializeConfigAccessMethod
+
+# VALID_ARCHITECTURES = IA32 X64
+
+[Sources]
+ PciLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ PcdLib
+ PciCf8Lib
+ PciExpressLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
diff --git a/roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/PciLib.c b/roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/PciLib.c new file mode 100644 index 000000000..1839f11f3 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/PciLib.c @@ -0,0 +1,1223 @@ +/** @file
+ PCI Library functions that use
+ (a) I/O ports 0xCF8 and 0xCFC to perform PCI Configuration cycles, layering
+ on top of one PCI CF8 Library instance; or
+ (b) PCI Library functions that use the 256 MB PCI Express MMIO window to
+ perform PCI Configuration cycles, layering on PCI Express Library.
+
+ The decision is made in the entry point function, based on the OVMF platform
+ type, and then adhered to during the lifetime of the client module.
+
+ Copyright (C) 2016, Red Hat, Inc.
+
+ Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include <Base.h>
+
+#include <IndustryStandard/Q35MchIch9.h>
+
+#include <Library/PciLib.h>
+#include <Library/PciCf8Lib.h>
+#include <Library/PciExpressLib.h>
+#include <Library/PcdLib.h>
+
+STATIC BOOLEAN mRunningOnQ35;
+
+RETURN_STATUS
+EFIAPI
+InitializeConfigAccessMethod (
+ VOID
+ )
+{
+ mRunningOnQ35 = (PcdGet16 (PcdOvmfHostBridgePciDevId) ==
+ INTEL_Q35_MCH_DEVICE_ID);
+ return RETURN_SUCCESS;
+}
+
+/**
+ Registers a PCI device so PCI configuration registers may be accessed after
+ SetVirtualAddressMap().
+
+ Registers the PCI device specified by Address so all the PCI configuration registers
+ associated with that PCI device may be accessed after SetVirtualAddressMap() is called.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+
+ @retval RETURN_SUCCESS The PCI device was registered for runtime access.
+ @retval RETURN_UNSUPPORTED An attempt was made to call this function
+ after ExitBootServices().
+ @retval RETURN_UNSUPPORTED The resources required to access the PCI device
+ at runtime could not be mapped.
+ @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
+ complete the registration.
+
+**/
+RETURN_STATUS
+EFIAPI
+PciRegisterForRuntimeAccess (
+ IN UINTN Address
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressRegisterForRuntimeAccess (Address) :
+ PciCf8RegisterForRuntimeAccess (Address);
+}
+
+/**
+ Reads an 8-bit PCI configuration register.
+
+ Reads and returns the 8-bit PCI configuration register specified by Address.
+ This function must guarantee that all PCI read and write operations are
+ serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+
+ @return The read value from the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciRead8 (
+ IN UINTN Address
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressRead8 (Address) :
+ PciCf8Read8 (Address);
+}
+
+/**
+ Writes an 8-bit PCI configuration register.
+
+ Writes the 8-bit PCI configuration register specified by Address with the
+ value specified by Value. Value is returned. This function must guarantee
+ that all PCI read and write operations are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param Value The value to write.
+
+ @return The value written to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciWrite8 (
+ IN UINTN Address,
+ IN UINT8 Value
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressWrite8 (Address, Value) :
+ PciCf8Write8 (Address, Value);
+}
+
+/**
+ Performs a bitwise OR of an 8-bit PCI configuration register with
+ an 8-bit value.
+
+ Reads the 8-bit PCI configuration register specified by Address, performs a
+ bitwise OR between the read result and the value specified by
+ OrData, and writes the result to the 8-bit PCI configuration register
+ specified by Address. The value written to the PCI configuration register is
+ returned. This function must guarantee that all PCI read and write operations
+ are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param OrData The value to OR with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciOr8 (
+ IN UINTN Address,
+ IN UINT8 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressOr8 (Address, OrData) :
+ PciCf8Or8 (Address, OrData);
+}
+
+/**
+ Performs a bitwise AND of an 8-bit PCI configuration register with an 8-bit
+ value.
+
+ Reads the 8-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData, and
+ writes the result to the 8-bit PCI configuration register specified by
+ Address. The value written to the PCI configuration register is returned.
+ This function must guarantee that all PCI read and write operations are
+ serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param AndData The value to AND with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciAnd8 (
+ IN UINTN Address,
+ IN UINT8 AndData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressAnd8 (Address, AndData) :
+ PciCf8And8 (Address, AndData);
+}
+
+/**
+ Performs a bitwise AND of an 8-bit PCI configuration register with an 8-bit
+ value, followed a bitwise OR with another 8-bit value.
+
+ Reads the 8-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData,
+ performs a bitwise OR between the result of the AND operation and
+ the value specified by OrData, and writes the result to the 8-bit PCI
+ configuration register specified by Address. The value written to the PCI
+ configuration register is returned. This function must guarantee that all PCI
+ read and write operations are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param AndData The value to AND with the PCI configuration register.
+ @param OrData The value to OR with the result of the AND operation.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciAndThenOr8 (
+ IN UINTN Address,
+ IN UINT8 AndData,
+ IN UINT8 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressAndThenOr8 (Address, AndData, OrData) :
+ PciCf8AndThenOr8 (Address, AndData, OrData);
+}
+
+/**
+ Reads a bit field of a PCI configuration register.
+
+ Reads the bit field in an 8-bit PCI configuration register. The bit field is
+ specified by the StartBit and the EndBit. The value of the bit field is
+ returned.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If StartBit is greater than 7, then ASSERT().
+ If EndBit is greater than 7, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+
+ @param Address The PCI configuration register to read.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..7.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..7.
+
+ @return The value of the bit field read from the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciBitFieldRead8 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldRead8 (Address, StartBit, EndBit) :
+ PciCf8BitFieldRead8 (Address, StartBit, EndBit);
+}
+
+/**
+ Writes a bit field to a PCI configuration register.
+
+ Writes Value to the bit field of the PCI configuration register. The bit
+ field is specified by the StartBit and the EndBit. All other bits in the
+ destination PCI configuration register are preserved. The new value of the
+ 8-bit register is returned.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If StartBit is greater than 7, then ASSERT().
+ If EndBit is greater than 7, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If Value is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..7.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..7.
+ @param Value The new value of the bit field.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciBitFieldWrite8 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT8 Value
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldWrite8 (Address, StartBit, EndBit, Value) :
+ PciCf8BitFieldWrite8 (Address, StartBit, EndBit, Value);
+}
+
+/**
+ Reads a bit field in an 8-bit PCI configuration, performs a bitwise OR, and
+ writes the result back to the bit field in the 8-bit port.
+
+ Reads the 8-bit PCI configuration register specified by Address, performs a
+ bitwise OR between the read result and the value specified by
+ OrData, and writes the result to the 8-bit PCI configuration register
+ specified by Address. The value written to the PCI configuration register is
+ returned. This function must guarantee that all PCI read and write operations
+ are serialized. Extra left bits in OrData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If StartBit is greater than 7, then ASSERT().
+ If EndBit is greater than 7, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..7.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..7.
+ @param OrData The value to OR with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciBitFieldOr8 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT8 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldOr8 (Address, StartBit, EndBit, OrData) :
+ PciCf8BitFieldOr8 (Address, StartBit, EndBit, OrData);
+}
+
+/**
+ Reads a bit field in an 8-bit PCI configuration register, performs a bitwise
+ AND, and writes the result back to the bit field in the 8-bit register.
+
+ Reads the 8-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData, and
+ writes the result to the 8-bit PCI configuration register specified by
+ Address. The value written to the PCI configuration register is returned.
+ This function must guarantee that all PCI read and write operations are
+ serialized. Extra left bits in AndData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If StartBit is greater than 7, then ASSERT().
+ If EndBit is greater than 7, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..7.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..7.
+ @param AndData The value to AND with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciBitFieldAnd8 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT8 AndData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldAnd8 (Address, StartBit, EndBit, AndData) :
+ PciCf8BitFieldAnd8 (Address, StartBit, EndBit, AndData);
+}
+
+/**
+ Reads a bit field in an 8-bit port, performs a bitwise AND followed by a
+ bitwise OR, and writes the result back to the bit field in the
+ 8-bit port.
+
+ Reads the 8-bit PCI configuration register specified by Address, performs a
+ bitwise AND followed by a bitwise OR between the read result and
+ the value specified by AndData, and writes the result to the 8-bit PCI
+ configuration register specified by Address. The value written to the PCI
+ configuration register is returned. This function must guarantee that all PCI
+ read and write operations are serialized. Extra left bits in both AndData and
+ OrData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If StartBit is greater than 7, then ASSERT().
+ If EndBit is greater than 7, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+ If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..7.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..7.
+ @param AndData The value to AND with the PCI configuration register.
+ @param OrData The value to OR with the result of the AND operation.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciBitFieldAndThenOr8 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT8 AndData,
+ IN UINT8 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldAndThenOr8 (Address, StartBit, EndBit, AndData, OrData) :
+ PciCf8BitFieldAndThenOr8 (Address, StartBit, EndBit, AndData, OrData);
+}
+
+/**
+ Reads a 16-bit PCI configuration register.
+
+ Reads and returns the 16-bit PCI configuration register specified by Address.
+ This function must guarantee that all PCI read and write operations are
+ serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+
+ @return The read value from the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciRead16 (
+ IN UINTN Address
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressRead16 (Address) :
+ PciCf8Read16 (Address);
+}
+
+/**
+ Writes a 16-bit PCI configuration register.
+
+ Writes the 16-bit PCI configuration register specified by Address with the
+ value specified by Value. Value is returned. This function must guarantee
+ that all PCI read and write operations are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param Value The value to write.
+
+ @return The value written to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciWrite16 (
+ IN UINTN Address,
+ IN UINT16 Value
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressWrite16 (Address, Value) :
+ PciCf8Write16 (Address, Value);
+}
+
+/**
+ Performs a bitwise OR of a 16-bit PCI configuration register with
+ a 16-bit value.
+
+ Reads the 16-bit PCI configuration register specified by Address, performs a
+ bitwise OR between the read result and the value specified by
+ OrData, and writes the result to the 16-bit PCI configuration register
+ specified by Address. The value written to the PCI configuration register is
+ returned. This function must guarantee that all PCI read and write operations
+ are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param OrData The value to OR with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciOr16 (
+ IN UINTN Address,
+ IN UINT16 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressOr16 (Address, OrData) :
+ PciCf8Or16 (Address, OrData);
+}
+
+/**
+ Performs a bitwise AND of a 16-bit PCI configuration register with a 16-bit
+ value.
+
+ Reads the 16-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData, and
+ writes the result to the 16-bit PCI configuration register specified by
+ Address. The value written to the PCI configuration register is returned.
+ This function must guarantee that all PCI read and write operations are
+ serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param AndData The value to AND with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciAnd16 (
+ IN UINTN Address,
+ IN UINT16 AndData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressAnd16 (Address, AndData) :
+ PciCf8And16 (Address, AndData);
+}
+
+/**
+ Performs a bitwise AND of a 16-bit PCI configuration register with a 16-bit
+ value, followed a bitwise OR with another 16-bit value.
+
+ Reads the 16-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData,
+ performs a bitwise OR between the result of the AND operation and
+ the value specified by OrData, and writes the result to the 16-bit PCI
+ configuration register specified by Address. The value written to the PCI
+ configuration register is returned. This function must guarantee that all PCI
+ read and write operations are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param AndData The value to AND with the PCI configuration register.
+ @param OrData The value to OR with the result of the AND operation.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciAndThenOr16 (
+ IN UINTN Address,
+ IN UINT16 AndData,
+ IN UINT16 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressAndThenOr16 (Address, AndData, OrData) :
+ PciCf8AndThenOr16 (Address, AndData, OrData);
+}
+
+/**
+ Reads a bit field of a PCI configuration register.
+
+ Reads the bit field in a 16-bit PCI configuration register. The bit field is
+ specified by the StartBit and the EndBit. The value of the bit field is
+ returned.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+ If StartBit is greater than 15, then ASSERT().
+ If EndBit is greater than 15, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+
+ @param Address The PCI configuration register to read.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..15.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..15.
+
+ @return The value of the bit field read from the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciBitFieldRead16 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldRead16 (Address, StartBit, EndBit) :
+ PciCf8BitFieldRead16 (Address, StartBit, EndBit);
+}
+
+/**
+ Writes a bit field to a PCI configuration register.
+
+ Writes Value to the bit field of the PCI configuration register. The bit
+ field is specified by the StartBit and the EndBit. All other bits in the
+ destination PCI configuration register are preserved. The new value of the
+ 16-bit register is returned.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+ If StartBit is greater than 15, then ASSERT().
+ If EndBit is greater than 15, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If Value is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..15.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..15.
+ @param Value The new value of the bit field.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciBitFieldWrite16 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT16 Value
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldWrite16 (Address, StartBit, EndBit, Value) :
+ PciCf8BitFieldWrite16 (Address, StartBit, EndBit, Value);
+}
+
+/**
+ Reads a bit field in a 16-bit PCI configuration, performs a bitwise OR, and
+ writes the result back to the bit field in the 16-bit port.
+
+ Reads the 16-bit PCI configuration register specified by Address, performs a
+ bitwise OR between the read result and the value specified by
+ OrData, and writes the result to the 16-bit PCI configuration register
+ specified by Address. The value written to the PCI configuration register is
+ returned. This function must guarantee that all PCI read and write operations
+ are serialized. Extra left bits in OrData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+ If StartBit is greater than 15, then ASSERT().
+ If EndBit is greater than 15, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..15.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..15.
+ @param OrData The value to OR with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciBitFieldOr16 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT16 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldOr16 (Address, StartBit, EndBit, OrData) :
+ PciCf8BitFieldOr16 (Address, StartBit, EndBit, OrData);
+}
+
+/**
+ Reads a bit field in a 16-bit PCI configuration register, performs a bitwise
+ AND, and writes the result back to the bit field in the 16-bit register.
+
+ Reads the 16-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData, and
+ writes the result to the 16-bit PCI configuration register specified by
+ Address. The value written to the PCI configuration register is returned.
+ This function must guarantee that all PCI read and write operations are
+ serialized. Extra left bits in AndData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+ If StartBit is greater than 15, then ASSERT().
+ If EndBit is greater than 15, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..15.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..15.
+ @param AndData The value to AND with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciBitFieldAnd16 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT16 AndData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldAnd16 (Address, StartBit, EndBit, AndData) :
+ PciCf8BitFieldAnd16 (Address, StartBit, EndBit, AndData);
+}
+
+/**
+ Reads a bit field in a 16-bit port, performs a bitwise AND followed by a
+ bitwise OR, and writes the result back to the bit field in the
+ 16-bit port.
+
+ Reads the 16-bit PCI configuration register specified by Address, performs a
+ bitwise AND followed by a bitwise OR between the read result and
+ the value specified by AndData, and writes the result to the 16-bit PCI
+ configuration register specified by Address. The value written to the PCI
+ configuration register is returned. This function must guarantee that all PCI
+ read and write operations are serialized. Extra left bits in both AndData and
+ OrData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 16-bit boundary, then ASSERT().
+ If StartBit is greater than 15, then ASSERT().
+ If EndBit is greater than 15, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+ If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..15.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..15.
+ @param AndData The value to AND with the PCI configuration register.
+ @param OrData The value to OR with the result of the AND operation.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciBitFieldAndThenOr16 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT16 AndData,
+ IN UINT16 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldAndThenOr16 (Address, StartBit, EndBit, AndData, OrData) :
+ PciCf8BitFieldAndThenOr16 (Address, StartBit, EndBit, AndData, OrData);
+}
+
+/**
+ Reads a 32-bit PCI configuration register.
+
+ Reads and returns the 32-bit PCI configuration register specified by Address.
+ This function must guarantee that all PCI read and write operations are
+ serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+
+ @return The read value from the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciRead32 (
+ IN UINTN Address
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressRead32 (Address) :
+ PciCf8Read32 (Address);
+}
+
+/**
+ Writes a 32-bit PCI configuration register.
+
+ Writes the 32-bit PCI configuration register specified by Address with the
+ value specified by Value. Value is returned. This function must guarantee
+ that all PCI read and write operations are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param Value The value to write.
+
+ @return The value written to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciWrite32 (
+ IN UINTN Address,
+ IN UINT32 Value
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressWrite32 (Address, Value) :
+ PciCf8Write32 (Address, Value);
+}
+
+/**
+ Performs a bitwise OR of a 32-bit PCI configuration register with
+ a 32-bit value.
+
+ Reads the 32-bit PCI configuration register specified by Address, performs a
+ bitwise OR between the read result and the value specified by
+ OrData, and writes the result to the 32-bit PCI configuration register
+ specified by Address. The value written to the PCI configuration register is
+ returned. This function must guarantee that all PCI read and write operations
+ are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param OrData The value to OR with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciOr32 (
+ IN UINTN Address,
+ IN UINT32 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressOr32 (Address, OrData) :
+ PciCf8Or32 (Address, OrData);
+}
+
+/**
+ Performs a bitwise AND of a 32-bit PCI configuration register with a 32-bit
+ value.
+
+ Reads the 32-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData, and
+ writes the result to the 32-bit PCI configuration register specified by
+ Address. The value written to the PCI configuration register is returned.
+ This function must guarantee that all PCI read and write operations are
+ serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param AndData The value to AND with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciAnd32 (
+ IN UINTN Address,
+ IN UINT32 AndData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressAnd32 (Address, AndData) :
+ PciCf8And32 (Address, AndData);
+}
+
+/**
+ Performs a bitwise AND of a 32-bit PCI configuration register with a 32-bit
+ value, followed a bitwise OR with another 32-bit value.
+
+ Reads the 32-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData,
+ performs a bitwise OR between the result of the AND operation and
+ the value specified by OrData, and writes the result to the 32-bit PCI
+ configuration register specified by Address. The value written to the PCI
+ configuration register is returned. This function must guarantee that all PCI
+ read and write operations are serialized.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+ @param Address The address that encodes the PCI Bus, Device, Function and
+ Register.
+ @param AndData The value to AND with the PCI configuration register.
+ @param OrData The value to OR with the result of the AND operation.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciAndThenOr32 (
+ IN UINTN Address,
+ IN UINT32 AndData,
+ IN UINT32 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressAndThenOr32 (Address, AndData, OrData) :
+ PciCf8AndThenOr32 (Address, AndData, OrData);
+}
+
+/**
+ Reads a bit field of a PCI configuration register.
+
+ Reads the bit field in a 32-bit PCI configuration register. The bit field is
+ specified by the StartBit and the EndBit. The value of the bit field is
+ returned.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+ If StartBit is greater than 31, then ASSERT().
+ If EndBit is greater than 31, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+
+ @param Address The PCI configuration register to read.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..31.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..31.
+
+ @return The value of the bit field read from the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciBitFieldRead32 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldRead32 (Address, StartBit, EndBit) :
+ PciCf8BitFieldRead32 (Address, StartBit, EndBit);
+}
+
+/**
+ Writes a bit field to a PCI configuration register.
+
+ Writes Value to the bit field of the PCI configuration register. The bit
+ field is specified by the StartBit and the EndBit. All other bits in the
+ destination PCI configuration register are preserved. The new value of the
+ 32-bit register is returned.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+ If StartBit is greater than 31, then ASSERT().
+ If EndBit is greater than 31, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If Value is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..31.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..31.
+ @param Value The new value of the bit field.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciBitFieldWrite32 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT32 Value
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldWrite32 (Address, StartBit, EndBit, Value) :
+ PciCf8BitFieldWrite32 (Address, StartBit, EndBit, Value);
+}
+
+/**
+ Reads a bit field in a 32-bit PCI configuration, performs a bitwise OR, and
+ writes the result back to the bit field in the 32-bit port.
+
+ Reads the 32-bit PCI configuration register specified by Address, performs a
+ bitwise OR between the read result and the value specified by
+ OrData, and writes the result to the 32-bit PCI configuration register
+ specified by Address. The value written to the PCI configuration register is
+ returned. This function must guarantee that all PCI read and write operations
+ are serialized. Extra left bits in OrData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+ If StartBit is greater than 31, then ASSERT().
+ If EndBit is greater than 31, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..31.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..31.
+ @param OrData The value to OR with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciBitFieldOr32 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT32 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldOr32 (Address, StartBit, EndBit, OrData) :
+ PciCf8BitFieldOr32 (Address, StartBit, EndBit, OrData);
+}
+
+/**
+ Reads a bit field in a 32-bit PCI configuration register, performs a bitwise
+ AND, and writes the result back to the bit field in the 32-bit register.
+
+ Reads the 32-bit PCI configuration register specified by Address, performs a
+ bitwise AND between the read result and the value specified by AndData, and
+ writes the result to the 32-bit PCI configuration register specified by
+ Address. The value written to the PCI configuration register is returned.
+ This function must guarantee that all PCI read and write operations are
+ serialized. Extra left bits in AndData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+ If StartBit is greater than 31, then ASSERT().
+ If EndBit is greater than 31, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..31.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..31.
+ @param AndData The value to AND with the PCI configuration register.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciBitFieldAnd32 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT32 AndData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldAnd32 (Address, StartBit, EndBit, AndData) :
+ PciCf8BitFieldAnd32 (Address, StartBit, EndBit, AndData);
+}
+
+/**
+ Reads a bit field in a 32-bit port, performs a bitwise AND followed by a
+ bitwise OR, and writes the result back to the bit field in the
+ 32-bit port.
+
+ Reads the 32-bit PCI configuration register specified by Address, performs a
+ bitwise AND followed by a bitwise OR between the read result and
+ the value specified by AndData, and writes the result to the 32-bit PCI
+ configuration register specified by Address. The value written to the PCI
+ configuration register is returned. This function must guarantee that all PCI
+ read and write operations are serialized. Extra left bits in both AndData and
+ OrData are stripped.
+
+ If Address > 0x0FFFFFFF, then ASSERT().
+ If Address is not aligned on a 32-bit boundary, then ASSERT().
+ If StartBit is greater than 31, then ASSERT().
+ If EndBit is greater than 31, then ASSERT().
+ If EndBit is less than StartBit, then ASSERT().
+ If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+ If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+ @param Address The PCI configuration register to write.
+ @param StartBit The ordinal of the least significant bit in the bit field.
+ Range 0..31.
+ @param EndBit The ordinal of the most significant bit in the bit field.
+ Range 0..31.
+ @param AndData The value to AND with the PCI configuration register.
+ @param OrData The value to OR with the result of the AND operation.
+
+ @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciBitFieldAndThenOr32 (
+ IN UINTN Address,
+ IN UINTN StartBit,
+ IN UINTN EndBit,
+ IN UINT32 AndData,
+ IN UINT32 OrData
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressBitFieldAndThenOr32 (Address, StartBit, EndBit, AndData, OrData) :
+ PciCf8BitFieldAndThenOr32 (Address, StartBit, EndBit, AndData, OrData);
+}
+
+/**
+ Reads a range of PCI configuration registers into a caller supplied buffer.
+
+ Reads the range of PCI configuration registers specified by StartAddress and
+ Size into the buffer specified by Buffer. This function only allows the PCI
+ configuration registers from a single PCI function to be read. Size is
+ returned. When possible 32-bit PCI configuration read cycles are used to read
+ from StartAddress to StartAddress + Size. Due to alignment restrictions, 8-bit
+ and 16-bit PCI configuration read cycles may be used at the beginning and the
+ end of the range.
+
+ If StartAddress > 0x0FFFFFFF, then ASSERT().
+ If ((StartAddress & 0xFFF) + Size) > 0x1000, then ASSERT().
+ If Size > 0 and Buffer is NULL, then ASSERT().
+
+ @param StartAddress The starting address that encodes the PCI Bus, Device,
+ Function and Register.
+ @param Size The size in bytes of the transfer.
+ @param Buffer The pointer to a buffer receiving the data read.
+
+ @return Size
+
+**/
+UINTN
+EFIAPI
+PciReadBuffer (
+ IN UINTN StartAddress,
+ IN UINTN Size,
+ OUT VOID *Buffer
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressReadBuffer (StartAddress, Size, Buffer) :
+ PciCf8ReadBuffer (StartAddress, Size, Buffer);
+}
+
+/**
+ Copies the data in a caller supplied buffer to a specified range of PCI
+ configuration space.
+
+ Writes the range of PCI configuration registers specified by StartAddress and
+ Size from the buffer specified by Buffer. This function only allows the PCI
+ configuration registers from a single PCI function to be written. Size is
+ returned. When possible 32-bit PCI configuration write cycles are used to
+ write from StartAddress to StartAddress + Size. Due to alignment restrictions,
+ 8-bit and 16-bit PCI configuration write cycles may be used at the beginning
+ and the end of the range.
+
+ If StartAddress > 0x0FFFFFFF, then ASSERT().
+ If ((StartAddress & 0xFFF) + Size) > 0x1000, then ASSERT().
+ If Size > 0 and Buffer is NULL, then ASSERT().
+
+ @param StartAddress The starting address that encodes the PCI Bus, Device,
+ Function and Register.
+ @param Size The size in bytes of the transfer.
+ @param Buffer The pointer to a buffer containing the data to write.
+
+ @return Size written to StartAddress.
+
+**/
+UINTN
+EFIAPI
+PciWriteBuffer (
+ IN UINTN StartAddress,
+ IN UINTN Size,
+ IN VOID *Buffer
+ )
+{
+ return mRunningOnQ35 ?
+ PciExpressWriteBuffer (StartAddress, Size, Buffer) :
+ PciCf8WriteBuffer (StartAddress, Size, Buffer);
+}
diff --git a/roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.c b/roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.c new file mode 100644 index 000000000..9c890013e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.c @@ -0,0 +1,97 @@ +/** @file
+ OVMF platform customization for EMU Variable FVB driver
+
+ Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PiDxe.h"
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PlatformFvbLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+
+/**
+ This function will be called following a call to the
+ EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL Read function.
+
+ @param[in] This The EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+ @param[in] Lba The starting logical block index
+ from which to read.
+ @param[in] Offset Offset into the block at which to begin reading.
+ @param[in] NumBytes The number of bytes read.
+ @param[in] Buffer Pointer to the buffer that was read, and will be
+ returned to the caller.
+
+**/
+VOID
+EFIAPI
+PlatformFvbDataRead (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+}
+
+
+/**
+ This function will be called following a call to the
+ EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL Write function.
+
+ @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+ @param[in] Lba The starting logical block index to written to.
+ @param[in] Offset Offset into the block at which to begin writing.
+ @param[in] NumBytes The number of bytes written.
+ @param[in] Buffer Pointer to the buffer that was written.
+
+**/
+VOID
+EFIAPI
+PlatformFvbDataWritten (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+ STATIC EFI_EVENT EventToSignal = NULL;
+
+ if (!EfiAtRuntime ()) {
+ if (EventToSignal == NULL) {
+ EventToSignal = (EFI_EVENT)(UINTN) PcdGet64 (PcdEmuVariableEvent);
+ }
+ if (EventToSignal != NULL) {
+ gBS->SignalEvent (EventToSignal);
+ }
+ }
+}
+
+
+/**
+ This function will be called following a call to the
+ EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL Erase function.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL
+ instance.
+ @param List The variable argument list as documented for
+ the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL Erase
+ function.
+
+**/
+VOID
+EFIAPI
+PlatformFvbBlocksErased (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN VA_LIST List
+ )
+{
+}
+
+
diff --git a/roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf b/roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf new file mode 100644 index 000000000..4eb0f506e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf @@ -0,0 +1,43 @@ +## @file
+# OVMF platform customization for EMU Variable FVB driver
+#
+# This library handles hooks for the EMU Variable FVB driver.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = EmuVariableFvbLib
+ FILE_GUID = 8a6062ed-7140-4a74-b4ea-fe900e79e24b
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformFvbLib|DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ EmuVariableFvbLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent
+
diff --git a/roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c b/roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c new file mode 100644 index 000000000..114db7e84 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c @@ -0,0 +1,277 @@ +/** @file
+ Generic implementation of QemuLoadImageLib library class interface.
+
+ Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Base.h>
+#include <Guid/QemuKernelLoaderFsMedia.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuLoadImageLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+
+#pragma pack (1)
+typedef struct {
+ EFI_DEVICE_PATH_PROTOCOL FilePathHeader;
+ CHAR16 FilePath[ARRAY_SIZE (L"kernel")];
+} KERNEL_FILE_DEVPATH;
+
+typedef struct {
+ VENDOR_DEVICE_PATH VenMediaNode;
+ KERNEL_FILE_DEVPATH FileNode;
+ EFI_DEVICE_PATH_PROTOCOL EndNode;
+} KERNEL_VENMEDIA_FILE_DEVPATH;
+#pragma pack ()
+
+STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
+ {
+ {
+ MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
+ { sizeof (VENDOR_DEVICE_PATH) }
+ },
+ QEMU_KERNEL_LOADER_FS_MEDIA_GUID
+ }, {
+ {
+ MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
+ { sizeof (KERNEL_FILE_DEVPATH) }
+ },
+ L"kernel",
+ }, {
+ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
+ }
+};
+
+/**
+ Download the kernel, the initial ramdisk, and the kernel command line from
+ QEMU's fw_cfg. The kernel will be instructed via its command line to load
+ the initrd from the same Simple FileSystem where the kernel was loaded from.
+
+ @param[out] ImageHandle The image handle that was allocated for
+ loading the image
+
+ @retval EFI_SUCCESS The image was loaded successfully.
+ @retval EFI_NOT_FOUND Kernel image was not found.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
+ @retval EFI_ACCESS_DENIED The underlying LoadImage boot service call
+ returned EFI_SECURITY_VIOLATION, and the image
+ was unloaded again.
+
+ @return Error codes from any of the underlying
+ functions.
+**/
+EFI_STATUS
+EFIAPI
+QemuLoadKernelImage (
+ OUT EFI_HANDLE *ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE KernelImageHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
+ UINTN CommandLineSize;
+ CHAR8 *CommandLine;
+ UINTN InitrdSize;
+
+ //
+ // Load the image. This should call back into the QEMU EFI loader file system.
+ //
+ Status = gBS->LoadImage (
+ FALSE, // BootPolicy: exact match required
+ gImageHandle, // ParentImageHandle
+ (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
+ NULL, // SourceBuffer
+ 0, // SourceSize
+ &KernelImageHandle
+ );
+ switch (Status) {
+ case EFI_SUCCESS:
+ break;
+
+ case EFI_SECURITY_VIOLATION:
+ //
+ // In this case, the image was loaded but failed to authenticate.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto UnloadImage;
+
+ default:
+ DEBUG ((Status == EFI_NOT_FOUND ? DEBUG_INFO : DEBUG_ERROR,
+ "%a: LoadImage(): %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+
+ //
+ // Construct the kernel command line.
+ //
+ Status = gBS->OpenProtocol (
+ KernelImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&KernelLoadedImage,
+ gImageHandle, // AgentHandle
+ NULL, // ControllerHandle
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
+ CommandLineSize = (UINTN)QemuFwCfgRead32 ();
+
+ if (CommandLineSize == 0) {
+ KernelLoadedImage->LoadOptionsSize = 0;
+ } else {
+ CommandLine = AllocatePool (CommandLineSize);
+ if (CommandLine == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UnloadImage;
+ }
+
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
+ QemuFwCfgReadBytes (CommandLineSize, CommandLine);
+
+ //
+ // Verify NUL-termination of the command line.
+ //
+ if (CommandLine[CommandLineSize - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
+ __FUNCTION__));
+ Status = EFI_PROTOCOL_ERROR;
+ goto FreeCommandLine;
+ }
+
+ //
+ // Drop the terminating NUL, convert to UTF-16.
+ //
+ KernelLoadedImage->LoadOptionsSize = (UINT32)((CommandLineSize - 1) * 2);
+ }
+
+ QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
+ InitrdSize = (UINTN)QemuFwCfgRead32 ();
+
+ if (InitrdSize > 0) {
+ //
+ // Append ' initrd=initrd' in UTF-16.
+ //
+ KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
+ }
+
+ if (KernelLoadedImage->LoadOptionsSize == 0) {
+ KernelLoadedImage->LoadOptions = NULL;
+ } else {
+ //
+ // NUL-terminate in UTF-16.
+ //
+ KernelLoadedImage->LoadOptionsSize += 2;
+
+ KernelLoadedImage->LoadOptions = AllocatePool (
+ KernelLoadedImage->LoadOptionsSize);
+ if (KernelLoadedImage->LoadOptions == NULL) {
+ KernelLoadedImage->LoadOptionsSize = 0;
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeCommandLine;
+ }
+
+ UnicodeSPrintAsciiFormat (
+ KernelLoadedImage->LoadOptions,
+ KernelLoadedImage->LoadOptionsSize,
+ "%a%a",
+ (CommandLineSize == 0) ? "" : CommandLine,
+ (InitrdSize == 0) ? "" : " initrd=initrd"
+ );
+ DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
+ (CHAR16 *)KernelLoadedImage->LoadOptions));
+ }
+
+ *ImageHandle = KernelImageHandle;
+ return EFI_SUCCESS;
+
+FreeCommandLine:
+ if (CommandLineSize > 0) {
+ FreePool (CommandLine);
+ }
+UnloadImage:
+ gBS->UnloadImage (KernelImageHandle);
+
+ return Status;
+}
+
+/**
+ Transfer control to a kernel image loaded with QemuLoadKernelImage ()
+
+ @param[in,out] ImageHandle Handle of image to be started. May assume a
+ different value on return if the image was
+ reloaded.
+
+ @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
+ or the image has already been initialized with
+ StartImage
+ @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
+ image should not be started.
+
+ @return Error codes returned by the started image
+**/
+EFI_STATUS
+EFIAPI
+QemuStartKernelImage (
+ IN OUT EFI_HANDLE *ImageHandle
+ )
+{
+ return gBS->StartImage (
+ *ImageHandle,
+ NULL, // ExitDataSize
+ NULL // ExitData
+ );
+}
+
+/**
+ Unloads an image loaded with QemuLoadKernelImage ().
+
+ @param ImageHandle Handle that identifies the image to be
+ unloaded.
+
+ @retval EFI_SUCCESS The image has been unloaded.
+ @retval EFI_UNSUPPORTED The image has been started, and does not
+ support unload.
+ @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
+
+ @return Exit code from the image's unload function.
+**/
+EFI_STATUS
+EFIAPI
+QemuUnloadKernelImage (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&KernelLoadedImage,
+ gImageHandle, // AgentHandle
+ NULL, // ControllerHandle
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (KernelLoadedImage->LoadOptions != NULL) {
+ FreePool (KernelLoadedImage->LoadOptions);
+ KernelLoadedImage->LoadOptions = NULL;
+ }
+ KernelLoadedImage->LoadOptionsSize = 0;
+
+ return gBS->UnloadImage (ImageHandle);
+}
diff --git a/roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf b/roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf new file mode 100644 index 000000000..b262cb926 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf @@ -0,0 +1,38 @@ +## @file
+# Generic implementation of QemuLoadImageLib library class interface.
+#
+# Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = GenericQemuLoadImageLib
+ FILE_GUID = 9e3e28da-c7b5-4f85-841a-84e6a9a1f1a0
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuLoadImageLib|DXE_DRIVER
+
+[Sources]
+ GenericQemuLoadImageLib.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ MemoryAllocationLib
+ PrintLib
+ QemuFwCfgLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gEfiDevicePathProtocolGuid
+ gEfiLoadedImageProtocolGuid
+
+[Guids]
+ gQemuKernelLoaderFsMediaGuid
diff --git a/roms/edk2/OvmfPkg/Library/LoadLinuxLib/Ia32/JumpToKernel.nasm b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/Ia32/JumpToKernel.nasm new file mode 100644 index 000000000..ae5f5eb03 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/Ia32/JumpToKernel.nasm @@ -0,0 +1,43 @@ +;------------------------------------------------------------------------------
+;
+; Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+;
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+ SECTION .text
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; JumpToKernel (
+; VOID *KernelStart,
+; VOID *KernelBootParams
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(JumpToKernel)
+ASM_PFX(JumpToKernel):
+
+ mov esi, [esp + 8]
+ call DWORD [esp + 4]
+ ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; JumpToUefiKernel (
+; EFI_HANDLE ImageHandle,
+; EFI_SYSTEM_TABLE *SystemTable,
+; VOID *KernelBootParams,
+; VOID *KernelStart
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(JumpToUefiKernel)
+ASM_PFX(JumpToUefiKernel):
+
+ mov eax, [esp + 12]
+ mov eax, [eax + 0x264]
+ add eax, [esp + 16]
+ jmp eax
+
diff --git a/roms/edk2/OvmfPkg/Library/LoadLinuxLib/Linux.c b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/Linux.c new file mode 100644 index 000000000..c5022f663 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/Linux.c @@ -0,0 +1,664 @@ +/** @file
+
+ Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LoadLinuxLib.h"
+
+
+/**
+ A simple check of the kernel setup image
+
+ An assumption is made that the size of the data is at least the
+ size of struct boot_params.
+
+ @param[in] KernelSetup - The kernel setup image
+
+ @retval EFI_SUCCESS - The kernel setup looks valid and supported
+ @retval EFI_INVALID_PARAMETER - KernelSetup was NULL
+ @retval EFI_UNSUPPORTED - The kernel setup is not valid or supported
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BasicKernelSetupCheck (
+ IN VOID *KernelSetup
+ )
+{
+ return LoadLinuxCheckKernelSetup(KernelSetup, sizeof (struct boot_params));
+}
+
+
+EFI_STATUS
+EFIAPI
+LoadLinuxCheckKernelSetup (
+ IN VOID *KernelSetup,
+ IN UINTN KernelSetupSize
+ )
+{
+ struct boot_params *Bp;
+
+ if (KernelSetup == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (KernelSetupSize < sizeof (*Bp)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Bp = (struct boot_params*) KernelSetup;
+
+ if ((Bp->hdr.signature != 0xAA55) || // Check boot sector signature
+ (Bp->hdr.header != SETUP_HDR) ||
+ (Bp->hdr.version < 0x205) || // We only support relocatable kernels
+ (!Bp->hdr.relocatable_kernel)
+ ) {
+ return EFI_UNSUPPORTED;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+
+UINTN
+EFIAPI
+LoadLinuxGetKernelSize (
+ IN VOID *KernelSetup,
+ IN UINTN KernelSize
+ )
+{
+ struct boot_params *Bp;
+
+ if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {
+ return 0;
+ }
+
+ Bp = (struct boot_params*) KernelSetup;
+
+ if (Bp->hdr.version > 0x20a) {
+ return Bp->hdr.init_size;
+ } else {
+ //
+ // Add extra size for kernel decompression
+ //
+ return 3 * KernelSize;
+ }
+}
+
+
+VOID*
+EFIAPI
+LoadLinuxAllocateKernelSetupPages (
+ IN UINTN Pages
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ Address = BASE_1GB;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiLoaderData,
+ Pages,
+ &Address
+ );
+ if (!EFI_ERROR (Status)) {
+ return (VOID*)(UINTN) Address;
+ } else {
+ return NULL;
+ }
+}
+
+EFI_STATUS
+EFIAPI
+LoadLinuxInitializeKernelSetup (
+ IN VOID *KernelSetup
+ )
+{
+ EFI_STATUS Status;
+ UINTN SetupEnd;
+ struct boot_params *Bp;
+
+ Status = BasicKernelSetupCheck (KernelSetup);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Bp = (struct boot_params*) KernelSetup;
+
+ SetupEnd = 0x202 + (Bp->hdr.jump & 0xff);
+
+ //
+ // Clear all but the setup_header
+ //
+ ZeroMem (KernelSetup, 0x1f1);
+ ZeroMem (((UINT8 *)KernelSetup) + SetupEnd, 4096 - SetupEnd);
+ DEBUG ((DEBUG_INFO, "Cleared kernel setup 0-0x1f1, 0x%Lx-0x1000\n",
+ (UINT64)SetupEnd));
+
+ return EFI_SUCCESS;
+}
+
+VOID*
+EFIAPI
+LoadLinuxAllocateKernelPages (
+ IN VOID *KernelSetup,
+ IN UINTN Pages
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS KernelAddress;
+ UINT32 Loop;
+ struct boot_params *Bp;
+
+ if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {
+ return NULL;
+ }
+
+ Bp = (struct boot_params*) KernelSetup;
+
+ for (Loop = 1; Loop < 512; Loop++) {
+ KernelAddress = MultU64x32 (
+ 2 * Bp->hdr.kernel_alignment,
+ Loop
+ );
+ Status = gBS->AllocatePages (
+ AllocateAddress,
+ EfiLoaderData,
+ Pages,
+ &KernelAddress
+ );
+ if (!EFI_ERROR (Status)) {
+ return (VOID*)(UINTN) KernelAddress;
+ }
+ }
+
+ return NULL;
+}
+
+
+VOID*
+EFIAPI
+LoadLinuxAllocateCommandLinePages (
+ IN UINTN Pages
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ Address = 0xa0000;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiLoaderData,
+ Pages,
+ &Address
+ );
+ if (!EFI_ERROR (Status)) {
+ return (VOID*)(UINTN) Address;
+ } else {
+ return NULL;
+ }
+}
+
+
+VOID*
+EFIAPI
+LoadLinuxAllocateInitrdPages (
+ IN VOID *KernelSetup,
+ IN UINTN Pages
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ struct boot_params *Bp;
+
+ if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {
+ return NULL;
+ }
+
+ Bp = (struct boot_params*) KernelSetup;
+
+ Address = (EFI_PHYSICAL_ADDRESS)(UINTN) Bp->hdr.ramdisk_max;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiLoaderData,
+ Pages,
+ &Address
+ );
+ if (!EFI_ERROR (Status)) {
+ return (VOID*)(UINTN) Address;
+ } else {
+ return NULL;
+ }
+}
+
+
+STATIC
+VOID
+SetupLinuxMemmap (
+ IN OUT struct boot_params *Bp
+ )
+{
+ EFI_STATUS Status;
+ UINT8 TmpMemoryMap[1];
+ UINTN MapKey;
+ UINTN DescriptorSize;
+ UINT32 DescriptorVersion;
+ UINTN MemoryMapSize;
+ EFI_MEMORY_DESCRIPTOR *MemoryMap;
+ EFI_MEMORY_DESCRIPTOR *MemoryMapPtr;
+ UINTN Index;
+ struct efi_info *Efi;
+ struct e820_entry *LastE820;
+ struct e820_entry *E820;
+ UINTN E820EntryCount;
+ EFI_PHYSICAL_ADDRESS LastEndAddr;
+
+ //
+ // Get System MemoryMapSize
+ //
+ MemoryMapSize = sizeof (TmpMemoryMap);
+ Status = gBS->GetMemoryMap (
+ &MemoryMapSize,
+ (EFI_MEMORY_DESCRIPTOR *)TmpMemoryMap,
+ &MapKey,
+ &DescriptorSize,
+ &DescriptorVersion
+ );
+ ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+ //
+ // Enlarge space here, because we will allocate pool now.
+ //
+ MemoryMapSize += EFI_PAGE_SIZE;
+ Status = gBS->AllocatePool (
+ EfiLoaderData,
+ MemoryMapSize,
+ (VOID **) &MemoryMap
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Get System MemoryMap
+ //
+ Status = gBS->GetMemoryMap (
+ &MemoryMapSize,
+ MemoryMap,
+ &MapKey,
+ &DescriptorSize,
+ &DescriptorVersion
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ LastE820 = NULL;
+ E820 = &Bp->e820_map[0];
+ E820EntryCount = 0;
+ LastEndAddr = 0;
+ MemoryMapPtr = MemoryMap;
+ for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {
+ UINTN E820Type = 0;
+
+ if (MemoryMap->NumberOfPages == 0) {
+ continue;
+ }
+
+ switch(MemoryMap->Type) {
+ case EfiReservedMemoryType:
+ case EfiRuntimeServicesCode:
+ case EfiRuntimeServicesData:
+ case EfiMemoryMappedIO:
+ case EfiMemoryMappedIOPortSpace:
+ case EfiPalCode:
+ E820Type = E820_RESERVED;
+ break;
+
+ case EfiUnusableMemory:
+ E820Type = E820_UNUSABLE;
+ break;
+
+ case EfiACPIReclaimMemory:
+ E820Type = E820_ACPI;
+ break;
+
+ case EfiLoaderCode:
+ case EfiLoaderData:
+ case EfiBootServicesCode:
+ case EfiBootServicesData:
+ case EfiConventionalMemory:
+ E820Type = E820_RAM;
+ break;
+
+ case EfiACPIMemoryNVS:
+ E820Type = E820_NVS;
+ break;
+
+ default:
+ DEBUG ((
+ DEBUG_ERROR,
+ "Invalid EFI memory descriptor type (0x%x)!\n",
+ MemoryMap->Type
+ ));
+ continue;
+ }
+
+ if ((LastE820 != NULL) &&
+ (LastE820->type == (UINT32) E820Type) &&
+ (MemoryMap->PhysicalStart == LastEndAddr)) {
+ LastE820->size += EFI_PAGES_TO_SIZE ((UINTN) MemoryMap->NumberOfPages);
+ LastEndAddr += EFI_PAGES_TO_SIZE ((UINTN) MemoryMap->NumberOfPages);
+ } else {
+ if (E820EntryCount >= ARRAY_SIZE (Bp->e820_map)) {
+ break;
+ }
+ E820->type = (UINT32) E820Type;
+ E820->addr = MemoryMap->PhysicalStart;
+ E820->size = EFI_PAGES_TO_SIZE ((UINTN) MemoryMap->NumberOfPages);
+ LastE820 = E820;
+ LastEndAddr = E820->addr + E820->size;
+ E820++;
+ E820EntryCount++;
+ }
+
+ //
+ // Get next item
+ //
+ MemoryMap = (EFI_MEMORY_DESCRIPTOR *)((UINTN)MemoryMap + DescriptorSize);
+ }
+ Bp->e820_entries = (UINT8) E820EntryCount;
+
+ Efi = &Bp->efi_info;
+ Efi->efi_systab = (UINT32)(UINTN) gST;
+ Efi->efi_memdesc_size = (UINT32) DescriptorSize;
+ Efi->efi_memdesc_version = DescriptorVersion;
+ Efi->efi_memmap = (UINT32)(UINTN) MemoryMapPtr;
+ Efi->efi_memmap_size = (UINT32) MemoryMapSize;
+#ifdef MDE_CPU_IA32
+ Efi->efi_loader_signature = SIGNATURE_32 ('E', 'L', '3', '2');
+#else
+ Efi->efi_systab_hi = (UINT32) (((UINT64)(UINTN) gST) >> 32);
+ Efi->efi_memmap_hi = (UINT32) (((UINT64)(UINTN) MemoryMapPtr) >> 32);
+ Efi->efi_loader_signature = SIGNATURE_32 ('E', 'L', '6', '4');
+#endif
+
+ gBS->ExitBootServices (gImageHandle, MapKey);
+}
+
+
+EFI_STATUS
+EFIAPI
+LoadLinuxSetCommandLine (
+ IN OUT VOID *KernelSetup,
+ IN CHAR8 *CommandLine
+ )
+{
+ EFI_STATUS Status;
+ struct boot_params *Bp;
+
+ Status = BasicKernelSetupCheck (KernelSetup);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Bp = (struct boot_params*) KernelSetup;
+
+ Bp->hdr.cmd_line_ptr = (UINT32)(UINTN) CommandLine;
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+LoadLinuxSetInitrd (
+ IN OUT VOID *KernelSetup,
+ IN VOID *Initrd,
+ IN UINTN InitrdSize
+ )
+{
+ EFI_STATUS Status;
+ struct boot_params *Bp;
+
+ Status = BasicKernelSetupCheck (KernelSetup);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Bp = (struct boot_params*) KernelSetup;
+
+ Bp->hdr.ramdisk_start = (UINT32)(UINTN) Initrd;
+ Bp->hdr.ramdisk_len = (UINT32) InitrdSize;
+
+ return EFI_SUCCESS;
+}
+
+
+STATIC VOID
+FindBits (
+ unsigned long Mask,
+ UINT8 *Pos,
+ UINT8 *Size
+ )
+{
+ UINT8 First, Len;
+
+ First = 0;
+ Len = 0;
+
+ if (Mask) {
+ while (!(Mask & 0x1)) {
+ Mask = Mask >> 1;
+ First++;
+ }
+
+ while (Mask & 0x1) {
+ Mask = Mask >> 1;
+ Len++;
+ }
+ }
+ *Pos = First;
+ *Size = Len;
+}
+
+
+STATIC
+EFI_STATUS
+SetupGraphicsFromGop (
+ struct screen_info *Si,
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop
+ )
+{
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
+ EFI_STATUS Status;
+ UINTN Size;
+
+ Status = Gop->QueryMode(Gop, Gop->Mode->Mode, &Size, &Info);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ /* We found a GOP */
+
+ /* EFI framebuffer */
+ Si->orig_video_isVGA = 0x70;
+
+ Si->orig_x = 0;
+ Si->orig_y = 0;
+ Si->orig_video_page = 0;
+ Si->orig_video_mode = 0;
+ Si->orig_video_cols = 0;
+ Si->orig_video_lines = 0;
+ Si->orig_video_ega_bx = 0;
+ Si->orig_video_points = 0;
+
+ Si->lfb_base = (UINT32) Gop->Mode->FrameBufferBase;
+ Si->lfb_size = (UINT32) Gop->Mode->FrameBufferSize;
+ Si->lfb_width = (UINT16) Info->HorizontalResolution;
+ Si->lfb_height = (UINT16) Info->VerticalResolution;
+ Si->pages = 1;
+ Si->vesapm_seg = 0;
+ Si->vesapm_off = 0;
+
+ if (Info->PixelFormat == PixelRedGreenBlueReserved8BitPerColor) {
+ Si->lfb_depth = 32;
+ Si->red_size = 8;
+ Si->red_pos = 0;
+ Si->green_size = 8;
+ Si->green_pos = 8;
+ Si->blue_size = 8;
+ Si->blue_pos = 16;
+ Si->rsvd_size = 8;
+ Si->rsvd_pos = 24;
+ Si->lfb_linelength = (UINT16) (Info->PixelsPerScanLine * 4);
+
+ } else if (Info->PixelFormat == PixelBlueGreenRedReserved8BitPerColor) {
+ Si->lfb_depth = 32;
+ Si->red_size = 8;
+ Si->red_pos = 16;
+ Si->green_size = 8;
+ Si->green_pos = 8;
+ Si->blue_size = 8;
+ Si->blue_pos = 0;
+ Si->rsvd_size = 8;
+ Si->rsvd_pos = 24;
+ Si->lfb_linelength = (UINT16) (Info->PixelsPerScanLine * 4);
+ } else if (Info->PixelFormat == PixelBitMask) {
+ FindBits(Info->PixelInformation.RedMask,
+ &Si->red_pos, &Si->red_size);
+ FindBits(Info->PixelInformation.GreenMask,
+ &Si->green_pos, &Si->green_size);
+ FindBits(Info->PixelInformation.BlueMask,
+ &Si->blue_pos, &Si->blue_size);
+ FindBits(Info->PixelInformation.ReservedMask,
+ &Si->rsvd_pos, &Si->rsvd_size);
+ Si->lfb_depth = Si->red_size + Si->green_size +
+ Si->blue_size + Si->rsvd_size;
+ Si->lfb_linelength = (UINT16) ((Info->PixelsPerScanLine * Si->lfb_depth) / 8);
+ } else {
+ Si->lfb_depth = 4;
+ Si->red_size = 0;
+ Si->red_pos = 0;
+ Si->green_size = 0;
+ Si->green_pos = 0;
+ Si->blue_size = 0;
+ Si->blue_pos = 0;
+ Si->rsvd_size = 0;
+ Si->rsvd_pos = 0;
+ Si->lfb_linelength = Si->lfb_width / 2;
+ }
+
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+SetupGraphics (
+ IN OUT struct boot_params *Bp
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
+
+ ZeroMem ((VOID*)&Bp->screen_info, sizeof(Bp->screen_info));
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiGraphicsOutputProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (!EFI_ERROR (Status)) {
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiGraphicsOutputProtocolGuid,
+ (VOID*) &Gop
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ Status = SetupGraphicsFromGop (&Bp->screen_info, Gop);
+ if (!EFI_ERROR (Status)) {
+ FreePool (HandleBuffer);
+ return EFI_SUCCESS;
+ }
+ }
+
+ FreePool (HandleBuffer);
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+STATIC
+EFI_STATUS
+SetupLinuxBootParams (
+ IN OUT struct boot_params *Bp
+ )
+{
+ SetupGraphics (Bp);
+
+ SetupLinuxMemmap (Bp);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+LoadLinux (
+ IN VOID *Kernel,
+ IN OUT VOID *KernelSetup
+ )
+{
+ EFI_STATUS Status;
+ struct boot_params *Bp;
+
+ Status = BasicKernelSetupCheck (KernelSetup);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Bp = (struct boot_params *) KernelSetup;
+
+ if (Bp->hdr.version < 0x205 || !Bp->hdr.relocatable_kernel) {
+ //
+ // We only support relocatable kernels
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ InitLinuxDescriptorTables ();
+
+ Bp->hdr.code32_start = (UINT32)(UINTN) Kernel;
+ if (Bp->hdr.version >= 0x20c && Bp->hdr.handover_offset &&
+ (Bp->hdr.xloadflags & (sizeof (UINTN) == 4 ? BIT2 : BIT3))) {
+ DEBUG ((DEBUG_INFO, "Jumping to kernel EFI handover point at ofs %x\n", Bp->hdr.handover_offset));
+
+ DisableInterrupts ();
+ JumpToUefiKernel ((VOID*) gImageHandle, (VOID*) gST, KernelSetup, Kernel);
+ }
+
+ //
+ // Old kernels without EFI handover protocol
+ //
+ SetupLinuxBootParams (KernelSetup);
+
+ DEBUG ((DEBUG_INFO, "Jumping to kernel\n"));
+ DisableInterrupts ();
+ SetLinuxDescriptorTables ();
+ JumpToKernel (Kernel, (VOID*) KernelSetup);
+
+ return EFI_SUCCESS;
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LinuxGdt.c b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LinuxGdt.c new file mode 100644 index 000000000..d90e999d8 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LinuxGdt.c @@ -0,0 +1,175 @@ +/** @file
+ Initialize GDT for Linux.
+
+ Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LoadLinuxLib.h"
+
+
+//
+// Local structure definitions
+//
+
+#pragma pack (1)
+
+//
+// Global Descriptor Entry structures
+//
+
+typedef struct _GDT_ENTRY {
+ UINT16 Limit15_0;
+ UINT16 Base15_0;
+ UINT8 Base23_16;
+ UINT8 Type;
+ UINT8 Limit19_16_and_flags;
+ UINT8 Base31_24;
+} GDT_ENTRY;
+
+typedef
+struct _GDT_ENTRIES {
+ GDT_ENTRY Null;
+ GDT_ENTRY Null2;
+ GDT_ENTRY Linear;
+ GDT_ENTRY LinearCode;
+ GDT_ENTRY TaskSegment;
+ GDT_ENTRY Spare4;
+ GDT_ENTRY Spare5;
+} GDT_ENTRIES;
+
+#pragma pack ()
+
+STATIC GDT_ENTRIES *mGdt = NULL;
+
+//
+// Global descriptor table (GDT) Template
+//
+STATIC GDT_ENTRIES GdtTemplate = {
+ //
+ // Null
+ //
+ {
+ 0x0, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x0, // type
+ 0x0, // limit 19:16, flags
+ 0x0, // base 31:24
+ },
+ //
+ // Null2
+ //
+ {
+ 0x0, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x0, // type
+ 0x0, // limit 19:16, flags
+ 0x0, // base 31:24
+ },
+ //
+ // Linear
+ //
+ {
+ 0x0FFFF, // limit 0xFFFFF
+ 0x0, // base 0
+ 0x0,
+ 0x09A, // present, ring 0, data, expand-up, writable
+ 0x0CF, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // LinearCode
+ //
+ {
+ 0x0FFFF, // limit 0xFFFFF
+ 0x0, // base 0
+ 0x0,
+ 0x092, // present, ring 0, data, expand-up, writable
+ 0x0CF, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // TaskSegment
+ //
+ {
+ 0x0, // limit 0
+ 0x0, // base 0
+ 0x0,
+ 0x089, // ?
+ 0x080, // ?
+ 0x0,
+ },
+ //
+ // Spare4
+ //
+ {
+ 0x0, // limit 0
+ 0x0, // base 0
+ 0x0,
+ 0x0, // present, ring 0, data, expand-up, writable
+ 0x0, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // Spare5
+ //
+ {
+ 0x0, // limit 0
+ 0x0, // base 0
+ 0x0,
+ 0x0, // present, ring 0, data, expand-up, writable
+ 0x0, // page-granular, 32-bit
+ 0x0,
+ },
+};
+
+/**
+ Initialize Global Descriptor Table.
+
+**/
+VOID
+InitLinuxDescriptorTables (
+ VOID
+ )
+{
+ //
+ // Allocate Runtime Data for the GDT
+ //
+ mGdt = AllocateRuntimePool (sizeof (GdtTemplate) + 8);
+ ASSERT (mGdt != NULL);
+ mGdt = ALIGN_POINTER (mGdt, 8);
+
+ //
+ // Initialize all GDT entries
+ //
+ CopyMem (mGdt, &GdtTemplate, sizeof (GdtTemplate));
+
+}
+
+/**
+ Initialize Global Descriptor Table.
+
+**/
+VOID
+SetLinuxDescriptorTables (
+ VOID
+ )
+{
+ IA32_DESCRIPTOR GdtPtr;
+ IA32_DESCRIPTOR IdtPtr;
+
+ //
+ // Write GDT register
+ //
+ GdtPtr.Base = (UINT32)(UINTN)(VOID*) mGdt;
+ GdtPtr.Limit = (UINT16) (sizeof (GdtTemplate) - 1);
+ AsmWriteGdtr (&GdtPtr);
+
+ IdtPtr.Base = (UINT32) 0;
+ IdtPtr.Limit = (UINT16) 0;
+ AsmWriteIdtr (&IdtPtr);
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.h b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.h new file mode 100644 index 000000000..daaecb615 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.h @@ -0,0 +1,53 @@ +/** @file
+ Boot UEFI Linux.
+
+ Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _LOAD_LINUX_LIB_INCLUDED_
+#define _LOAD_LINUX_LIB_INCLUDED_
+
+#include <Uefi.h>
+#include <Library/LoadLinuxLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <IndustryStandard/LinuxBzimage.h>
+
+#include <Protocol/GraphicsOutput.h>
+
+VOID
+EFIAPI
+JumpToKernel (
+ VOID *KernelStart,
+ VOID *KernelBootParams
+ );
+
+VOID
+EFIAPI
+JumpToUefiKernel (
+ EFI_HANDLE ImageHandle,
+ EFI_SYSTEM_TABLE *SystemTable,
+ VOID *KernelBootParams,
+ VOID *KernelStart
+ );
+
+VOID
+InitLinuxDescriptorTables (
+ VOID
+ );
+
+VOID
+SetLinuxDescriptorTables (
+ VOID
+ );
+
+#endif
+
diff --git a/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf new file mode 100644 index 000000000..cc643373e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf @@ -0,0 +1,43 @@ +## @file
+#
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = LoadLinuxLib
+ FILE_GUID = eaec1915-65a0-43a9-bf0b-a76438da61db
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = LoadLinuxLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources.common]
+ Linux.c
+ LinuxGdt.c
+ LoadLinuxLib.h
+
+[Sources.IA32]
+ Ia32/JumpToKernel.nasm
+
+[Sources.X64]
+ X64/JumpToKernel.nasm
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ MemoryAllocationLib
+ BaseMemoryLib
+
diff --git a/roms/edk2/OvmfPkg/Library/LoadLinuxLib/X64/JumpToKernel.nasm b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/X64/JumpToKernel.nasm new file mode 100644 index 000000000..6e37f21db --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LoadLinuxLib/X64/JumpToKernel.nasm @@ -0,0 +1,87 @@ +;------------------------------------------------------------------------------
+;
+; Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+;
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+ DEFAULT REL
+ SECTION .text
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; JumpToKernel (
+; VOID *KernelStart, // rcx
+; VOID *KernelBootParams // rdx
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(JumpToKernel)
+ASM_PFX(JumpToKernel):
+
+ ; Set up for executing kernel. BP in %esi, entry point on the stack
+ ; (64-bit when the 'ret' will use it as 32-bit, but we're little-endian)
+ mov rsi, rdx
+ push rcx
+
+ ; Jump into the compatibility mode CS
+ push 0x10
+ lea rax, [.0]
+ push rax
+ DB 0x48, 0xcb ; retfq
+
+.0:
+ ; Now in compatibility mode.
+
+ DB 0xb8, 0x18, 0x0, 0x0, 0x0 ; movl $0x18, %eax
+ DB 0x8e, 0xd8 ; movl %eax, %ds
+ DB 0x8e, 0xc0 ; movl %eax, %es
+ DB 0x8e, 0xe0 ; movl %eax, %fs
+ DB 0x8e, 0xe8 ; movl %eax, %gs
+ DB 0x8e, 0xd0 ; movl %eax, %ss
+
+ ; Disable paging
+ DB 0xf, 0x20, 0xc0 ; movl %cr0, %eax
+ DB 0xf, 0xba, 0xf8, 0x1f ; btcl $31, %eax
+ DB 0xf, 0x22, 0xc0 ; movl %eax, %cr0
+
+ ; Disable long mode in EFER
+ DB 0xb9, 0x80, 0x0, 0x0, 0xc0 ; movl $0x0c0000080, %ecx
+ DB 0xf, 0x32 ; rdmsr
+ DB 0xf, 0xba, 0xf8, 0x8 ; btcl $8, %eax
+ DB 0xf, 0x30 ; wrmsr
+
+ ; Disable PAE
+ DB 0xf, 0x20, 0xe0 ; movl %cr4, %eax
+ DB 0xf, 0xba, 0xf8, 0x5 ; btcl $5, %eax
+ DB 0xf, 0x22, 0xe0 ; movl %eax, %cr4
+
+ DB 0x31, 0xed ; xor %ebp, %ebp
+ DB 0x31, 0xff ; xor %edi, %edi
+ DB 0x31, 0xdb ; xor %ebx, %ebx
+ DB 0xc3 ; ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; JumpToUefiKernel (
+; EFI_HANDLE ImageHandle, // rcx
+; EFI_SYSTEM_TABLE *SystemTable, // rdx
+; VOID *KernelBootParams // r8
+; VOID *KernelStart, // r9
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(JumpToUefiKernel)
+ASM_PFX(JumpToUefiKernel):
+
+ mov rdi, rcx
+ mov rsi, rdx
+ mov rdx, r8
+ xor rax, rax
+ mov eax, [r8 + 0x264]
+ add r9, rax
+ add r9, 0x200
+ call r9
+ ret
+
diff --git a/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBase.c b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBase.c new file mode 100644 index 000000000..00f03525c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBase.c @@ -0,0 +1,36 @@ +/** @file
+
+ Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+#include <Library/DebugLib.h>
+#include <LockBoxLib.h>
+
+/**
+ Allocates a buffer of type EfiACPIMemoryNVS.
+
+ Allocates the number bytes specified by AllocationSize of type
+ EfiACPIMemoryNVS and returns a pointer to the allocated buffer.
+ If AllocationSize is 0, then a valid buffer of 0 size is
+ returned. If there is not enough memory remaining to satisfy
+ the request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateAcpiNvsPool (
+ IN UINTN AllocationSize
+ )
+{
+ ASSERT_EFI_ERROR (RETURN_UNSUPPORTED);
+ return NULL;
+}
diff --git a/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf new file mode 100644 index 000000000..e0461d4da --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf @@ -0,0 +1,41 @@ +## @file
+#
+# Library implementing the LockBox interface for OVMF
+#
+# Copyright (C) 2013, Red Hat, Inc.
+# Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = LockBoxBaseLib
+ FILE_GUID = 17CA9B37-5BAB-492C-A09C-7121FBE34CE6
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = LockBoxLib
+
+ CONSTRUCTOR = LockBoxLibInitialize
+
+[Sources]
+ LockBoxBase.c
+ LockBoxLib.c
+ LockBoxLib.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
+
+[FeaturePcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
diff --git a/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxe.c b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxe.c new file mode 100644 index 000000000..b28ad4d2d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxe.c @@ -0,0 +1,141 @@ +/** @file
+
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+#include <Protocol/LockBox.h>
+#include <LockBoxLib.h>
+
+/**
+ Allocate memory below 4G memory address.
+
+ This function allocates memory below 4G memory address.
+
+ @param MemoryType Memory type of memory to allocate.
+ @param Size Size of memory to allocate.
+
+ @return Allocated address for output.
+
+**/
+STATIC
+VOID *
+AllocateMemoryBelow4G (
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Size
+ )
+{
+ UINTN Pages;
+ EFI_PHYSICAL_ADDRESS Address;
+ EFI_STATUS Status;
+ VOID* Buffer;
+ UINTN AllocRemaining;
+
+ Pages = EFI_SIZE_TO_PAGES (Size);
+ Address = 0xffffffff;
+
+ //
+ // Since we need to use gBS->AllocatePages to get a buffer below
+ // 4GB, there is a good chance that space will be wasted for very
+ // small allocation. We keep track of unused portions of the page
+ // allocations, and use these to allocate memory for small buffers.
+ //
+ ASSERT (mLockBoxGlobal->Signature == LOCK_BOX_GLOBAL_SIGNATURE);
+ if ((UINTN) mLockBoxGlobal->SubPageRemaining >= Size) {
+ Buffer = (VOID*)(UINTN) mLockBoxGlobal->SubPageBuffer;
+ mLockBoxGlobal->SubPageBuffer += (UINT32) Size;
+ mLockBoxGlobal->SubPageRemaining -= (UINT32) Size;
+ return Buffer;
+ }
+
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ MemoryType,
+ Pages,
+ &Address
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Buffer = (VOID *) (UINTN) Address;
+ ZeroMem (Buffer, EFI_PAGES_TO_SIZE (Pages));
+
+ AllocRemaining = EFI_PAGES_TO_SIZE (Pages) - Size;
+ if (AllocRemaining > (UINTN) mLockBoxGlobal->SubPageRemaining) {
+ mLockBoxGlobal->SubPageBuffer = (UINT32) (Address + Size);
+ mLockBoxGlobal->SubPageRemaining = (UINT32) AllocRemaining;
+ }
+
+ return Buffer;
+}
+
+
+/**
+ Allocates a buffer of type EfiACPIMemoryNVS.
+
+ Allocates the number bytes specified by AllocationSize of type
+ EfiACPIMemoryNVS and returns a pointer to the allocated buffer.
+ If AllocationSize is 0, then a valid buffer of 0 size is
+ returned. If there is not enough memory remaining to satisfy
+ the request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateAcpiNvsPool (
+ IN UINTN AllocationSize
+ )
+{
+ return AllocateMemoryBelow4G (EfiACPIMemoryNVS, AllocationSize);
+}
+
+
+EFI_STATUS
+EFIAPI
+LockBoxDxeLibInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ VOID *Interface;
+
+ Status = LockBoxLibInitialize ();
+ if (!EFI_ERROR (Status)) {
+ if (QemuFwCfgS3Enabled ()) {
+ //
+ // When S3 enabled, the first driver run with this library linked will
+ // have this library constructor to install LockBox protocol on the
+ // ImageHandle. As other drivers may have gEfiLockBoxProtocolGuid
+ // dependency, the first driver should run before them.
+ //
+ Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface);
+ if (EFI_ERROR (Status)) {
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEfiLockBoxProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+ }
+
+ return Status;
+}
diff --git a/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxeLib.inf b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxeLib.inf new file mode 100644 index 000000000..38bcc5770 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxeLib.inf @@ -0,0 +1,47 @@ +## @file
+#
+# Library implementing the LockBox interface for OVMF
+#
+# Copyright (C) 2013, Red Hat, Inc.
+# Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = LockBoxDxeLib
+ FILE_GUID = f61c9a34-2e18-44ce-af2f-21a998e64fda
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = LockBoxLib
+
+ CONSTRUCTOR = LockBoxDxeLibInitialize
+
+[Sources]
+ LockBoxDxe.c
+ LockBoxLib.c
+ LockBoxLib.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ UefiBootServicesTableLib
+ QemuFwCfgLib
+ QemuFwCfgS3Lib
+
+[Protocols]
+ gEfiLockBoxProtocolGuid ## SOMETIMES_PRODUCES
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
+
+[FeaturePcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
diff --git a/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.c b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.c new file mode 100644 index 000000000..1f36b5cc0 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.c @@ -0,0 +1,377 @@ +/** @file
+
+ Library implementing the LockBox interface for OVMF
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/LockBoxLib.h>
+#include <Library/PcdLib.h>
+#include <LockBoxLib.h>
+
+#pragma pack(1)
+typedef struct {
+ EFI_GUID Guid;
+ EFI_PHYSICAL_ADDRESS OrigAddress;
+ EFI_PHYSICAL_ADDRESS CopyAddress;
+ UINT32 Size;
+ UINT64 Attributes;
+} LOCK_BOX_ENTRY;
+#pragma pack()
+
+LOCK_BOX_GLOBAL *mLockBoxGlobal = NULL;
+STATIC LOCK_BOX_ENTRY *StartOfEntries = NULL;
+STATIC LOCK_BOX_ENTRY *EndOfEntries = NULL;
+
+RETURN_STATUS
+EFIAPI
+LockBoxLibInitialize (
+ VOID
+ )
+{
+ UINTN NumEntries;
+
+ ASSERT (!FeaturePcdGet (PcdSmmSmramRequire));
+
+ if (PcdGet32 (PcdOvmfLockBoxStorageSize) < sizeof (LOCK_BOX_GLOBAL)) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ mLockBoxGlobal = (LOCK_BOX_GLOBAL *)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase);
+ StartOfEntries = ((LOCK_BOX_ENTRY *) (mLockBoxGlobal + 1));
+ NumEntries = ((PcdGet32 (PcdOvmfLockBoxStorageSize) - sizeof (LOCK_BOX_GLOBAL)) /
+ sizeof (LOCK_BOX_ENTRY));
+ EndOfEntries = StartOfEntries + NumEntries;
+ if (mLockBoxGlobal->Signature != LOCK_BOX_GLOBAL_SIGNATURE) {
+ //
+ // Note: This code depends on the lock box being cleared in early
+ // PEI before usage, so the SubPageBuffer and SubPageRemaining
+ // fields don't need to be set to 0.
+ //
+ mLockBoxGlobal->Signature = LOCK_BOX_GLOBAL_SIGNATURE;
+ }
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Find LockBox entry based on GUID.
+
+ @param[in] Guid The GUID to search for.
+
+ @return Address of the LOCK_BOX_ENTRY found.
+
+ If NULL, then the item was not found, and there is no space
+ left to store a new item.
+
+ If non-NULL and LOCK_BOX_ENTRY.Size == 0, then the item was not
+ found, but a new item can be inserted at the returned location.
+
+ If non-NULL and LOCK_BOX_ENTRY.Size > 0, then the item was found.
+**/
+STATIC
+LOCK_BOX_ENTRY *
+EFIAPI
+FindHeaderByGuid (
+ IN CONST EFI_GUID *Guid
+ )
+{
+ LOCK_BOX_ENTRY *Header;
+
+ for (Header = StartOfEntries; Header < EndOfEntries; Header++) {
+ if (Header->Size == 0 || CompareGuid (Guid, &Header->Guid)) {
+ return Header;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ This function will save confidential information to lockbox.
+
+ @param Guid the guid to identify the confidential information
+ @param Buffer the address of the confidential information
+ @param Length the length of the confidential information
+
+ @retval RETURN_SUCCESS the information is saved successfully.
+ @retval RETURN_INVALID_PARAMETER the Guid is NULL, or Buffer is NULL, or
+ Length is 0
+ @retval RETURN_ALREADY_STARTED the requested GUID already exist.
+ @retval RETURN_OUT_OF_RESOURCES no enough resource to save the information.
+ @retval RETURN_ACCESS_DENIED it is too late to invoke this interface
+ @retval RETURN_NOT_STARTED it is too early to invoke this interface
+ @retval RETURN_UNSUPPORTED the service is not supported by
+ implementaion.
+**/
+RETURN_STATUS
+EFIAPI
+SaveLockBox (
+ IN GUID *Guid,
+ IN VOID *Buffer,
+ IN UINTN Length
+ )
+{
+ LOCK_BOX_ENTRY *Header;
+ VOID *CopyBuffer;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Guid=%g Buffer=%p Length=0x%x\n", __FUNCTION__,
+ Guid, Buffer, (UINT32) Length));
+
+ if (Guid == NULL || Buffer == NULL || Length == 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (Length > 0xFFFFFFFF) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ Header = FindHeaderByGuid (Guid);
+ if (Header == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ if (Header->Size > 0) {
+ return RETURN_ALREADY_STARTED;
+ }
+
+ CopyBuffer = AllocateAcpiNvsPool (Length);
+ if (CopyBuffer == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ //
+ // overwrite the current terminator header with new metadata
+ //
+ CopyGuid (&Header->Guid, Guid);
+ Header->OrigAddress = (UINTN) Buffer;
+ Header->CopyAddress = (UINTN) CopyBuffer;
+ Header->Size = (UINT32) Length;
+ Header->Attributes = 0;
+
+ //
+ // copy contents
+ //
+ CopyMem (CopyBuffer, Buffer, Length);
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ This function will set lockbox attributes.
+
+ @param Guid the guid to identify the confidential information
+ @param Attributes the attributes of the lockbox
+
+ @retval RETURN_SUCCESS the information is saved successfully.
+ @retval RETURN_INVALID_PARAMETER attributes is invalid.
+ @retval RETURN_NOT_FOUND the requested GUID not found.
+ @retval RETURN_ACCESS_DENIED it is too late to invoke this interface
+ @retval RETURN_NOT_STARTED it is too early to invoke this interface
+ @retval RETURN_UNSUPPORTED the service is not supported by
+ implementaion.
+**/
+RETURN_STATUS
+EFIAPI
+SetLockBoxAttributes (
+ IN GUID *Guid,
+ IN UINT64 Attributes
+ )
+{
+ LOCK_BOX_ENTRY *Header;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Guid=%g Attributes=0x%Lx\n", __FUNCTION__, Guid,
+ Attributes));
+
+ if (Guid == NULL) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ Header = FindHeaderByGuid (Guid);
+ if (!Header || Header->Size == 0) {
+ return RETURN_NOT_FOUND;
+ }
+ Header->Attributes = Attributes;
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ This function will update confidential information to lockbox.
+
+ @param Guid the guid to identify the original confidential information
+ @param Offset the offset of the original confidential information
+ @param Buffer the address of the updated confidential information
+ @param Length the length of the updated confidential information
+
+ @retval RETURN_SUCCESS the information is saved successfully.
+ @retval RETURN_INVALID_PARAMETER the Guid is NULL, or Buffer is NULL, or
+ Length is 0.
+ @retval RETURN_NOT_FOUND the requested GUID not found.
+ @retval RETURN_BUFFER_TOO_SMALL for lockbox without attribute
+ LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY, the
+ original buffer to too small to hold new
+ information.
+ @retval RETURN_OUT_OF_RESOURCES for lockbox with attribute
+ LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY, no
+ enough resource to save the information.
+ @retval RETURN_ACCESS_DENIED it is too late to invoke this interface
+ @retval RETURN_NOT_STARTED it is too early to invoke this interface
+ @retval RETURN_UNSUPPORTED the service is not supported by
+ implementaion.
+**/
+RETURN_STATUS
+EFIAPI
+UpdateLockBox (
+ IN GUID *Guid,
+ IN UINTN Offset,
+ IN VOID *Buffer,
+ IN UINTN Length
+ )
+{
+ LOCK_BOX_ENTRY *Header;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Guid=%g Offset=0x%x Length=0x%x\n", __FUNCTION__,
+ Guid, (UINT32) Offset, (UINT32) Length));
+
+ if (Guid == NULL || Buffer == NULL || Length == 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ Header = FindHeaderByGuid (Guid);
+ if (!Header || Header->Size == 0) {
+ return RETURN_NOT_FOUND;
+ }
+
+ if (Header->Size < Offset ||
+ Length > Header->Size - Offset) {
+ return RETURN_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem ((UINT8 *)(UINTN) (Header->CopyAddress) + Offset, Buffer, Length);
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ This function will restore confidential information from lockbox.
+
+ @param Guid the guid to identify the confidential information
+ @param Buffer the address of the restored confidential information
+ NULL means restored to original address, Length MUST be NULL at
+ same time.
+ @param Length the length of the restored confidential information
+
+ @retval RETURN_SUCCESS the information is restored successfully.
+ @retval RETURN_INVALID_PARAMETER the Guid is NULL, or one of Buffer and
+ Length is NULL.
+ @retval RETURN_WRITE_PROTECTED Buffer and Length are NULL, but the LockBox
+ has no LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
+ attribute.
+ @retval RETURN_BUFFER_TOO_SMALL the Length is too small to hold the
+ confidential information.
+ @retval RETURN_NOT_FOUND the requested GUID not found.
+ @retval RETURN_NOT_STARTED it is too early to invoke this interface
+ @retval RETURN_ACCESS_DENIED not allow to restore to the address
+ @retval RETURN_UNSUPPORTED the service is not supported by
+ implementaion.
+**/
+RETURN_STATUS
+EFIAPI
+RestoreLockBox (
+ IN GUID *Guid,
+ IN VOID *Buffer, OPTIONAL
+ IN OUT UINTN *Length OPTIONAL
+ )
+{
+ LOCK_BOX_ENTRY *Header;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Guid=%g Buffer=%p\n", __FUNCTION__, Guid,
+ Buffer));
+
+ if ((Guid == NULL) ||
+ ((Buffer == NULL) && (Length != NULL)) ||
+ ((Buffer != NULL) && (Length == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Header = FindHeaderByGuid (Guid);
+ if (!Header || Header->Size == 0) {
+ return RETURN_NOT_FOUND;
+ }
+
+ if (Buffer == NULL) {
+ if (!(Header->Attributes & LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE)) {
+ return RETURN_WRITE_PROTECTED;
+ }
+ if (Header->OrigAddress + (Header->Size - 1) > MAX_ADDRESS) {
+ return RETURN_UNSUPPORTED;
+ }
+ Buffer = (VOID *)(UINTN) Header->OrigAddress;
+ }
+
+ //
+ // Set RestoreLength
+ //
+ if (Length != NULL) {
+ if (Header->Size > *Length) {
+ //
+ // Input buffer is too small to hold all data.
+ //
+ *Length = Header->Size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ *Length = Header->Size;
+ }
+
+ CopyMem (Buffer, (VOID*)(UINTN) Header->CopyAddress, Header->Size);
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ This function will restore confidential information from all lockbox which
+ have RestoreInPlace attribute.
+
+ @retval RETURN_SUCCESS the information is restored successfully.
+ @retval RETURN_NOT_STARTED it is too early to invoke this interface
+ @retval RETURN_UNSUPPORTED the service is not supported by
+ implementaion.
+**/
+RETURN_STATUS
+EFIAPI
+RestoreAllLockBoxInPlace (
+ VOID
+ )
+{
+ LOCK_BOX_ENTRY *Header;
+
+ for (Header = StartOfEntries;
+ Header < EndOfEntries && Header->Size > 0;
+ Header++) {
+ if (Header->Attributes & LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE) {
+ VOID *Buffer;
+
+ if (Header->OrigAddress + (Header->Size - 1) > MAX_ADDRESS) {
+ return RETURN_UNSUPPORTED;
+ }
+ Buffer = (VOID *)(UINTN) Header->OrigAddress;
+ CopyMem (Buffer, (VOID*)(UINTN)Header->CopyAddress, Header->Size);
+ DEBUG ((DEBUG_VERBOSE, "%a: Guid=%g Buffer=%p\n", __FUNCTION__,
+ &Header->Guid, Buffer));
+ }
+ }
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.h b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.h new file mode 100644 index 000000000..d654ea639 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.h @@ -0,0 +1,54 @@ +/** @file
+
+ Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __LOCK_BOX_LIB_IMPL_H__
+#define __LOCK_BOX_LIB_IMPL_H__
+
+#pragma pack(1)
+
+typedef struct {
+ UINT32 Signature;
+ UINT32 SubPageBuffer;
+ UINT32 SubPageRemaining;
+} LOCK_BOX_GLOBAL;
+
+#define LOCK_BOX_GLOBAL_SIGNATURE SIGNATURE_32('L', 'B', 'G', 'S')
+
+extern LOCK_BOX_GLOBAL *mLockBoxGlobal;
+
+#pragma pack()
+
+/**
+ Allocates a buffer of type EfiACPIMemoryNVS.
+
+ Allocates the number bytes specified by AllocationSize of type
+ EfiACPIMemoryNVS and returns a pointer to the allocated buffer.
+ If AllocationSize is 0, then a valid buffer of 0 size is
+ returned. If there is not enough memory remaining to satisfy
+ the request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateAcpiNvsPool (
+ IN UINTN AllocationSize
+ );
+
+
+RETURN_STATUS
+EFIAPI
+LockBoxLibInitialize (
+ VOID
+ );
+
+
+#endif
diff --git a/roms/edk2/OvmfPkg/Library/NvVarsFileLib/FsAccess.c b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/FsAccess.c new file mode 100644 index 000000000..2ba03522f --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/FsAccess.c @@ -0,0 +1,508 @@ +/** @file
+ File System Access for NvVarsFileLib
+
+ Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvVarsFileLib.h"
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+/**
+ Open the NvVars file for reading or writing
+
+ @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
+ @param[in] ReadingFile - TRUE: open the file for reading. FALSE: writing
+ @param[out] NvVarsFile - If EFI_SUCCESS is returned, then this is updated
+ with the opened NvVars file.
+
+ @return EFI_SUCCESS if the file was opened
+
+**/
+EFI_STATUS
+GetNvVarsFile (
+ IN EFI_HANDLE FsHandle,
+ IN BOOLEAN ReadingFile,
+ OUT EFI_FILE_HANDLE *NvVarsFile
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+ EFI_FILE_HANDLE Root;
+
+ //
+ // Get the FileSystem protocol on that handle
+ //
+ Status = gBS->HandleProtocol (
+ FsHandle,
+ &gEfiSimpleFileSystemProtocolGuid,
+ (VOID **)&Fs
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the volume (the root directory)
+ //
+ Status = Fs->OpenVolume (Fs, &Root);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Attempt to open the NvVars file in the root directory
+ //
+ Status = Root->Open (
+ Root,
+ NvVarsFile,
+ L"NvVars",
+ ReadingFile ?
+ EFI_FILE_MODE_READ :
+ (
+ EFI_FILE_MODE_CREATE |
+ EFI_FILE_MODE_READ |
+ EFI_FILE_MODE_WRITE
+ ),
+ 0
+ );
+
+ return Status;
+}
+
+
+/**
+ Open the NvVars file for reading or writing
+
+ @param[in] File - The file to inspect
+ @param[out] Exists - Returns whether the file exists
+ @param[out] Size - Returns the size of the file
+ (0 if the file does not exist)
+
+**/
+VOID
+NvVarsFileReadCheckup (
+ IN EFI_FILE_HANDLE File,
+ OUT BOOLEAN *Exists,
+ OUT UINTN *Size
+ )
+{
+ EFI_FILE_INFO *FileInfo;
+
+ *Exists = FALSE;
+ *Size = 0;
+
+ FileInfo = FileHandleGetInfo (File);
+ if (FileInfo == NULL) {
+ return;
+ }
+
+ if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
+ FreePool (FileInfo);
+ return;
+ }
+
+ *Exists = TRUE;
+ *Size = (UINTN) FileInfo->FileSize;
+
+ FreePool (FileInfo);
+}
+
+
+/**
+ Open the NvVars file for reading or writing
+
+ @param[in] File - The file to inspect
+ @param[out] Exists - Returns whether the file exists
+ @param[out] Size - Returns the size of the file
+ (0 if the file does not exist)
+
+**/
+EFI_STATUS
+FileHandleEmpty (
+ IN EFI_FILE_HANDLE File
+ )
+{
+ EFI_STATUS Status;
+ EFI_FILE_INFO *FileInfo;
+
+ //
+ // Retrieve the FileInfo structure
+ //
+ FileInfo = FileHandleGetInfo (File);
+ if (FileInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If the path is a directory, then return an error
+ //
+ if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
+ FreePool (FileInfo);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If the file size is already 0, then it is empty, so
+ // we can return success.
+ //
+ if (FileInfo->FileSize == 0) {
+ FreePool (FileInfo);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Set the file size to 0.
+ //
+ FileInfo->FileSize = 0;
+ Status = FileHandleSetInfo (File, FileInfo);
+
+ FreePool (FileInfo);
+
+ return Status;
+}
+
+
+/**
+ Reads a file to a newly allocated buffer
+
+ @param[in] File - The file to read
+ @param[in] ReadSize - The size of data to read from the file
+
+ @return Pointer to buffer allocated to hold the file
+ contents. NULL if an error occurred.
+
+**/
+VOID*
+FileHandleReadToNewBuffer (
+ IN EFI_FILE_HANDLE FileHandle,
+ IN UINTN ReadSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN ActualReadSize;
+ VOID *FileContents;
+
+ ActualReadSize = ReadSize;
+ FileContents = AllocatePool (ReadSize);
+ if (FileContents != NULL) {
+ Status = FileHandleRead (
+ FileHandle,
+ &ReadSize,
+ FileContents
+ );
+ if (EFI_ERROR (Status) || (ActualReadSize != ReadSize)) {
+ FreePool (FileContents);
+ return NULL;
+ }
+ }
+
+ return FileContents;
+}
+
+
+/**
+ Reads the contents of the NvVars file on the file system
+
+ @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
+
+ @return EFI_STATUS based on the success or failure of the file read
+
+**/
+EFI_STATUS
+ReadNvVarsFile (
+ IN EFI_HANDLE FsHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_FILE_HANDLE File;
+ UINTN FileSize;
+ BOOLEAN FileExists;
+ VOID *FileContents;
+ EFI_HANDLE SerializedVariables;
+
+ Status = GetNvVarsFile (FsHandle, TRUE, &File);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "FsAccess.c: Could not open NV Variables file on this file system\n"));
+ return Status;
+ }
+
+ NvVarsFileReadCheckup (File, &FileExists, &FileSize);
+ if (FileSize == 0) {
+ FileHandleClose (File);
+ return EFI_UNSUPPORTED;
+ }
+
+ FileContents = FileHandleReadToNewBuffer (File, FileSize);
+ if (FileContents == NULL) {
+ FileHandleClose (File);
+ return EFI_UNSUPPORTED;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "FsAccess.c: Read %Lu bytes from NV Variables file\n",
+ (UINT64)FileSize
+ ));
+
+ Status = SerializeVariablesNewInstanceFromBuffer (
+ &SerializedVariables,
+ FileContents,
+ FileSize
+ );
+ if (!RETURN_ERROR (Status)) {
+ Status = SerializeVariablesSetSerializedVariables (SerializedVariables);
+ }
+
+ FreePool (FileContents);
+ FileHandleClose (File);
+
+ return Status;
+}
+
+
+/**
+ Writes a variable to indicate that the NV variables
+ have been loaded from the file system.
+
+**/
+STATIC
+VOID
+SetNvVarsVariable (
+ VOID
+ )
+{
+ BOOLEAN VarData;
+ UINTN Size;
+
+ //
+ // Write a variable to indicate we've already loaded the
+ // variable data. If it is found, we skip the loading on
+ // subsequent attempts.
+ //
+ Size = sizeof (VarData);
+ VarData = TRUE;
+ gRT->SetVariable (
+ L"NvVars",
+ &gEfiSimpleFileSystemProtocolGuid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ Size,
+ (VOID*) &VarData
+ );
+}
+
+
+/**
+ Loads the non-volatile variables from the NvVars file on the
+ given file system.
+
+ @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
+
+ @return EFI_STATUS based on the success or failure of load operation
+
+**/
+EFI_STATUS
+LoadNvVarsFromFs (
+ EFI_HANDLE FsHandle
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN VarData;
+ UINTN Size;
+
+ DEBUG ((DEBUG_INFO, "FsAccess.c: LoadNvVarsFromFs\n"));
+
+ //
+ // We write a variable to indicate we've already loaded the
+ // variable data. If it is found, we skip the loading.
+ //
+ // This is relevant if the non-volatile variable have been
+ // able to survive a reboot operation. In that case, we don't
+ // want to re-load the file as it would overwrite newer changes
+ // made to the variables.
+ //
+ Size = sizeof (VarData);
+ VarData = TRUE;
+ Status = gRT->GetVariable (
+ L"NvVars",
+ &gEfiSimpleFileSystemProtocolGuid,
+ NULL,
+ &Size,
+ (VOID*) &VarData
+ );
+ if (Status == EFI_SUCCESS) {
+ DEBUG ((DEBUG_INFO, "NV Variables were already loaded\n"));
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Attempt to restore the variables from the NvVars file.
+ //
+ Status = ReadNvVarsFile (FsHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "Error while restoring NV variable data\n"));
+ return Status;
+ }
+
+ //
+ // Write a variable to indicate we've already loaded the
+ // variable data. If it is found, we skip the loading on
+ // subsequent attempts.
+ //
+ SetNvVarsVariable();
+
+ DEBUG ((
+ DEBUG_INFO,
+ "FsAccess.c: Read NV Variables file (size=%Lu)\n",
+ (UINT64)Size
+ ));
+
+ return Status;
+}
+
+
+STATIC
+RETURN_STATUS
+EFIAPI
+IterateVariablesCallbackAddAllNvVariables (
+ IN VOID *Context,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_HANDLE Instance;
+
+ Instance = (EFI_HANDLE) Context;
+
+ //
+ // Only save non-volatile variables
+ //
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
+ return RETURN_SUCCESS;
+ }
+
+ return SerializeVariablesAddVariable (
+ Instance,
+ VariableName,
+ VendorGuid,
+ Attributes,
+ DataSize,
+ Data
+ );
+}
+
+
+/**
+ Saves the non-volatile variables into the NvVars file on the
+ given file system.
+
+ @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
+
+ @return EFI_STATUS based on the success or failure of load operation
+
+**/
+EFI_STATUS
+SaveNvVarsToFs (
+ EFI_HANDLE FsHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_FILE_HANDLE File;
+ UINTN WriteSize;
+ UINTN VariableDataSize;
+ VOID *VariableData;
+ EFI_HANDLE SerializedVariables;
+
+ SerializedVariables = NULL;
+
+ Status = SerializeVariablesNewInstance (&SerializedVariables);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = SerializeVariablesIterateSystemVariables (
+ IterateVariablesCallbackAddAllNvVariables,
+ (VOID*) SerializedVariables
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ VariableData = NULL;
+ VariableDataSize = 0;
+ Status = SerializeVariablesToBuffer (
+ SerializedVariables,
+ NULL,
+ &VariableDataSize
+ );
+ if (Status == RETURN_BUFFER_TOO_SMALL) {
+ VariableData = AllocatePool (VariableDataSize);
+ if (VariableData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ Status = SerializeVariablesToBuffer (
+ SerializedVariables,
+ VariableData,
+ &VariableDataSize
+ );
+ }
+ }
+
+ SerializeVariablesFreeInstance (SerializedVariables);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open the NvVars file for writing.
+ //
+ Status = GetNvVarsFile (FsHandle, FALSE, &File);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "FsAccess.c: Unable to open file to saved NV Variables\n"));
+ return Status;
+ }
+
+ //
+ // Empty the starting file contents.
+ //
+ Status = FileHandleEmpty (File);
+ if (EFI_ERROR (Status)) {
+ FileHandleClose (File);
+ return Status;
+ }
+
+ WriteSize = VariableDataSize;
+ Status = FileHandleWrite (File, &WriteSize, VariableData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FileHandleClose (File);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Write a variable to indicate we've already loaded the
+ // variable data. If it is found, we skip the loading on
+ // subsequent attempts.
+ //
+ SetNvVarsVariable();
+
+ DEBUG ((DEBUG_INFO, "Saved NV Variables to NvVars file\n"));
+ }
+
+ return Status;
+
+}
+
+
diff --git a/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.c b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.c new file mode 100644 index 000000000..65ab6a06e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.c @@ -0,0 +1,77 @@ +/** @file
+ Save Non-Volatile Variables to a file system.
+
+ Copyright (c) 2009, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "NvVarsFileLib.h"
+#include <Library/DebugLib.h>
+#include <Library/NvVarsFileLib.h>
+
+EFI_HANDLE mNvVarsFileLibFsHandle = NULL;
+
+
+/**
+ Attempts to connect the NvVarsFileLib to the specified file system.
+
+ @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
+
+ @return The EFI_STATUS while attempting to connect the NvVarsFileLib
+ to the file system instance.
+ @retval EFI_SUCCESS - The given file system was connected successfully
+
+**/
+EFI_STATUS
+EFIAPI
+ConnectNvVarsToFileSystem (
+ IN EFI_HANDLE FsHandle
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // We might fail to load the variable, since the file system initially
+ // will not have the NvVars file.
+ //
+ LoadNvVarsFromFs (FsHandle);
+
+ //
+ // We must be able to save the variables successfully to the file system
+ // to have connected successfully.
+ //
+ Status = SaveNvVarsToFs (FsHandle);
+ if (!EFI_ERROR (Status)) {
+ mNvVarsFileLibFsHandle = FsHandle;
+ }
+
+ return Status;
+}
+
+
+/**
+ Update non-volatile variables stored on the file system.
+
+ @return The EFI_STATUS while attempting to update the variable on
+ the connected file system.
+ @retval EFI_SUCCESS - The non-volatile variables were saved to the disk
+ @retval EFI_NOT_STARTED - A file system has not been connected
+
+**/
+EFI_STATUS
+EFIAPI
+UpdateNvVarsOnFileSystem (
+ )
+{
+ if (mNvVarsFileLibFsHandle == NULL) {
+ //
+ // A file system had not been connected to the library.
+ //
+ return EFI_NOT_STARTED;
+ } else {
+ return SaveNvVarsToFs (mNvVarsFileLibFsHandle);
+ }
+}
+
+
diff --git a/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.h b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.h new file mode 100644 index 000000000..d602ae1fa --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.h @@ -0,0 +1,55 @@ +/** @file
+ Save Non-Volatile Variables to a file system.
+
+ Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __NV_VARS_FILE_LIB_INSTANCE__
+#define __NV_VARS_FILE_LIB_INSTANCE__
+
+#include <Uefi.h>
+
+#include <Guid/FileInfo.h>
+
+#include <Protocol/SimpleFileSystem.h>
+
+#include <Library/BaseLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/SerializeVariablesLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+/**
+ Loads the non-volatile variables from the NvVars file on the
+ given file system.
+
+ @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
+
+ @return EFI_STATUS based on the success or failure of load operation
+
+**/
+EFI_STATUS
+LoadNvVarsFromFs (
+ EFI_HANDLE FsHandle
+ );
+
+
+/**
+ Saves the non-volatile variables into the NvVars file on the
+ given file system.
+
+ @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
+
+ @return EFI_STATUS based on the success or failure of load operation
+
+**/
+EFI_STATUS
+SaveNvVarsToFs (
+ EFI_HANDLE FsHandle
+ );
+
+#endif
+
diff --git a/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.inf b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.inf new file mode 100644 index 000000000..8cda78d0d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.inf @@ -0,0 +1,56 @@ +## @file
+# NvVarsFileLib
+#
+# This library saves and restores non-volatile variables in a
+# file within a file system.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = NvVarsFileLib
+ FILE_GUID = 9172fe8b-9a36-40f8-bba5-5e57a44390bd
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = NvVarsFileLib|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ FsAccess.c
+ NvVarsFileLib.c
+ NvVarsFileLib.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ FileHandleLib
+ MemoryAllocationLib
+ SerializeVariablesLib
+
+[Protocols]
+ gEfiSimpleFileSystemProtocolGuid ## CONSUMES
+
+
+[Guids]
+ gEfiFileInfoGuid
+
+[Depex]
+ gEfiVariableWriteArchProtocolGuid
+
diff --git a/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridge.h b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridge.h new file mode 100644 index 000000000..134d74112 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridge.h @@ -0,0 +1,69 @@ +/** @file
+ Header file of OVMF instance of PciHostBridgeLib.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+PCI_ROOT_BRIDGE *
+ScanForRootBridges (
+ UINTN *NumberOfRootBridges
+);
+
+/**
+ Initialize a PCI_ROOT_BRIDGE structure.
+
+ @param[in] Supports Supported attributes.
+
+ @param[in] Attributes Initial attributes.
+
+ @param[in] AllocAttributes Allocation attributes.
+
+ @param[in] RootBusNumber The bus number to store in RootBus.
+
+ @param[in] MaxSubBusNumber The inclusive maximum bus number that can be
+ assigned to any subordinate bus found behind any
+ PCI bridge hanging off this root bus.
+
+ The caller is repsonsible for ensuring that
+ RootBusNumber <= MaxSubBusNumber. If
+ RootBusNumber equals MaxSubBusNumber, then the
+ root bus has no room for subordinate buses.
+
+ @param[in] Io IO aperture.
+
+ @param[in] Mem MMIO aperture.
+
+ @param[in] MemAbove4G MMIO aperture above 4G.
+
+ @param[in] PMem Prefetchable MMIO aperture.
+
+ @param[in] PMemAbove4G Prefetchable MMIO aperture above 4G.
+
+ @param[out] RootBus The PCI_ROOT_BRIDGE structure (allocated by the
+ caller) that should be filled in by this
+ function.
+
+ @retval EFI_SUCCESS Initialization successful. A device path
+ consisting of an ACPI device path node, with
+ UID = RootBusNumber, has been allocated and
+ linked into RootBus.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+**/
+EFI_STATUS
+InitRootBridge (
+ IN UINT64 Supports,
+ IN UINT64 Attributes,
+ IN UINT64 AllocAttributes,
+ IN UINT8 RootBusNumber,
+ IN UINT8 MaxSubBusNumber,
+ IN PCI_ROOT_BRIDGE_APERTURE *Io,
+ IN PCI_ROOT_BRIDGE_APERTURE *Mem,
+ IN PCI_ROOT_BRIDGE_APERTURE *MemAbove4G,
+ IN PCI_ROOT_BRIDGE_APERTURE *PMem,
+ IN PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G,
+ OUT PCI_ROOT_BRIDGE *RootBus
+ );
diff --git a/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.c b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.c new file mode 100644 index 000000000..e850f7d18 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.c @@ -0,0 +1,443 @@ +/** @file
+ OVMF's instance of the PCI Host Bridge Library.
+
+ Copyright (C) 2016, Red Hat, Inc.
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <PiDxe.h>
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/Q35MchIch9.h>
+
+#include <Protocol/PciHostBridgeResourceAllocation.h>
+#include <Protocol/PciRootBridgeIo.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PciHostBridgeLib.h>
+#include <Library/PciLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include "PciHostBridge.h"
+
+
+#pragma pack(1)
+typedef struct {
+ ACPI_HID_DEVICE_PATH AcpiDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL EndDevicePath;
+} OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH;
+#pragma pack ()
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED
+CHAR16 *mPciHostBridgeLibAcpiAddressSpaceTypeStr[] = {
+ L"Mem", L"I/O", L"Bus"
+};
+
+
+STATIC
+CONST
+OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH mRootBridgeDevicePathTemplate = {
+ {
+ {
+ ACPI_DEVICE_PATH,
+ ACPI_DP,
+ {
+ (UINT8) (sizeof(ACPI_HID_DEVICE_PATH)),
+ (UINT8) ((sizeof(ACPI_HID_DEVICE_PATH)) >> 8)
+ }
+ },
+ EISA_PNP_ID(0x0A03), // HID
+ 0 // UID
+ },
+
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ END_DEVICE_PATH_LENGTH,
+ 0
+ }
+ }
+};
+
+STATIC PCI_ROOT_BRIDGE_APERTURE mNonExistAperture = { MAX_UINT64, 0 };
+
+/**
+ Initialize a PCI_ROOT_BRIDGE structure.
+
+ @param[in] Supports Supported attributes.
+
+ @param[in] Attributes Initial attributes.
+
+ @param[in] AllocAttributes Allocation attributes.
+
+ @param[in] RootBusNumber The bus number to store in RootBus.
+
+ @param[in] MaxSubBusNumber The inclusive maximum bus number that can be
+ assigned to any subordinate bus found behind any
+ PCI bridge hanging off this root bus.
+
+ The caller is repsonsible for ensuring that
+ RootBusNumber <= MaxSubBusNumber. If
+ RootBusNumber equals MaxSubBusNumber, then the
+ root bus has no room for subordinate buses.
+
+ @param[in] Io IO aperture.
+
+ @param[in] Mem MMIO aperture.
+
+ @param[in] MemAbove4G MMIO aperture above 4G.
+
+ @param[in] PMem Prefetchable MMIO aperture.
+
+ @param[in] PMemAbove4G Prefetchable MMIO aperture above 4G.
+
+ @param[out] RootBus The PCI_ROOT_BRIDGE structure (allocated by the
+ caller) that should be filled in by this
+ function.
+
+ @retval EFI_SUCCESS Initialization successful. A device path
+ consisting of an ACPI device path node, with
+ UID = RootBusNumber, has been allocated and
+ linked into RootBus.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+**/
+EFI_STATUS
+InitRootBridge (
+ IN UINT64 Supports,
+ IN UINT64 Attributes,
+ IN UINT64 AllocAttributes,
+ IN UINT8 RootBusNumber,
+ IN UINT8 MaxSubBusNumber,
+ IN PCI_ROOT_BRIDGE_APERTURE *Io,
+ IN PCI_ROOT_BRIDGE_APERTURE *Mem,
+ IN PCI_ROOT_BRIDGE_APERTURE *MemAbove4G,
+ IN PCI_ROOT_BRIDGE_APERTURE *PMem,
+ IN PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G,
+ OUT PCI_ROOT_BRIDGE *RootBus
+ )
+{
+ OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH *DevicePath;
+
+ //
+ // Be safe if other fields are added to PCI_ROOT_BRIDGE later.
+ //
+ ZeroMem (RootBus, sizeof *RootBus);
+
+ RootBus->Segment = 0;
+
+ RootBus->Supports = Supports;
+ RootBus->Attributes = Attributes;
+
+ RootBus->DmaAbove4G = FALSE;
+
+ RootBus->AllocationAttributes = AllocAttributes;
+ RootBus->Bus.Base = RootBusNumber;
+ RootBus->Bus.Limit = MaxSubBusNumber;
+ CopyMem (&RootBus->Io, Io, sizeof (*Io));
+ CopyMem (&RootBus->Mem, Mem, sizeof (*Mem));
+ CopyMem (&RootBus->MemAbove4G, MemAbove4G, sizeof (*MemAbove4G));
+ CopyMem (&RootBus->PMem, PMem, sizeof (*PMem));
+ CopyMem (&RootBus->PMemAbove4G, PMemAbove4G, sizeof (*PMemAbove4G));
+
+ RootBus->NoExtendedConfigSpace = (PcdGet16 (PcdOvmfHostBridgePciDevId) !=
+ INTEL_Q35_MCH_DEVICE_ID);
+
+ DevicePath = AllocateCopyPool (sizeof mRootBridgeDevicePathTemplate,
+ &mRootBridgeDevicePathTemplate);
+ if (DevicePath == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ DevicePath->AcpiDevicePath.UID = RootBusNumber;
+ RootBus->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;
+
+ DEBUG ((DEBUG_INFO,
+ "%a: populated root bus %d, with room for %d subordinate bus(es)\n",
+ __FUNCTION__, RootBusNumber, MaxSubBusNumber - RootBusNumber));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Uninitialize a PCI_ROOT_BRIDGE structure set up with InitRootBridge().
+
+ param[in] RootBus The PCI_ROOT_BRIDGE structure, allocated by the caller and
+ initialized with InitRootBridge(), that should be
+ uninitialized. This function doesn't free RootBus.
+**/
+STATIC
+VOID
+UninitRootBridge (
+ IN PCI_ROOT_BRIDGE *RootBus
+ )
+{
+ FreePool (RootBus->DevicePath);
+}
+
+
+/**
+ Return all the root bridge instances in an array.
+
+ @param Count Return the count of root bridge instances.
+
+ @return All the root bridge instances in an array.
+ The array should be passed into PciHostBridgeFreeRootBridges()
+ when it's not used.
+**/
+PCI_ROOT_BRIDGE *
+EFIAPI
+PciHostBridgeGetRootBridges (
+ UINTN *Count
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ UINT64 ExtraRootBridges;
+ PCI_ROOT_BRIDGE *Bridges;
+ UINTN Initialized;
+ UINTN LastRootBridgeNumber;
+ UINTN RootBridgeNumber;
+ UINT64 Attributes;
+ UINT64 AllocationAttributes;
+ PCI_ROOT_BRIDGE_APERTURE Io;
+ PCI_ROOT_BRIDGE_APERTURE Mem;
+ PCI_ROOT_BRIDGE_APERTURE MemAbove4G;
+
+ if (PcdGetBool (PcdPciDisableBusEnumeration)) {
+ return ScanForRootBridges (Count);
+ }
+
+ ZeroMem (&Io, sizeof (Io));
+ ZeroMem (&Mem, sizeof (Mem));
+ ZeroMem (&MemAbove4G, sizeof (MemAbove4G));
+
+ Attributes = EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO |
+ EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO |
+ EFI_PCI_ATTRIBUTE_ISA_IO_16 |
+ EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |
+ EFI_PCI_ATTRIBUTE_VGA_MEMORY |
+ EFI_PCI_ATTRIBUTE_VGA_IO_16 |
+ EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;
+
+ AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;
+ if (PcdGet64 (PcdPciMmio64Size) > 0) {
+ AllocationAttributes |= EFI_PCI_HOST_BRIDGE_MEM64_DECODE;
+ MemAbove4G.Base = PcdGet64 (PcdPciMmio64Base);
+ MemAbove4G.Limit = PcdGet64 (PcdPciMmio64Base) +
+ PcdGet64 (PcdPciMmio64Size) - 1;
+ } else {
+ CopyMem (&MemAbove4G, &mNonExistAperture, sizeof (mNonExistAperture));
+ }
+
+ Io.Base = PcdGet64 (PcdPciIoBase);
+ Io.Limit = PcdGet64 (PcdPciIoBase) + (PcdGet64 (PcdPciIoSize) - 1);
+ Mem.Base = PcdGet64 (PcdPciMmio32Base);
+ Mem.Limit = PcdGet64 (PcdPciMmio32Base) + (PcdGet64 (PcdPciMmio32Size) - 1);
+
+ *Count = 0;
+
+ //
+ // QEMU provides the number of extra root buses, shortening the exhaustive
+ // search below. If there is no hint, the feature is missing.
+ //
+ Status = QemuFwCfgFindFile ("etc/extra-pci-roots", &FwCfgItem, &FwCfgSize);
+ if (EFI_ERROR (Status) || FwCfgSize != sizeof ExtraRootBridges) {
+ ExtraRootBridges = 0;
+ } else {
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, &ExtraRootBridges);
+
+ if (ExtraRootBridges > PCI_MAX_BUS) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid count of extra root buses (%Lu) "
+ "reported by QEMU\n", __FUNCTION__, ExtraRootBridges));
+ return NULL;
+ }
+ DEBUG ((DEBUG_INFO, "%a: %Lu extra root buses reported by QEMU\n",
+ __FUNCTION__, ExtraRootBridges));
+ }
+
+ //
+ // Allocate the "main" root bridge, and any extra root bridges.
+ //
+ Bridges = AllocatePool ((1 + (UINTN)ExtraRootBridges) * sizeof *Bridges);
+ if (Bridges == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));
+ return NULL;
+ }
+ Initialized = 0;
+
+ //
+ // The "main" root bus is always there.
+ //
+ LastRootBridgeNumber = 0;
+
+ //
+ // Scan all other root buses. If function 0 of any device on a bus returns a
+ // VendorId register value different from all-bits-one, then that bus is
+ // alive.
+ //
+ for (RootBridgeNumber = 1;
+ RootBridgeNumber <= PCI_MAX_BUS && Initialized < ExtraRootBridges;
+ ++RootBridgeNumber) {
+ UINTN Device;
+
+ for (Device = 0; Device <= PCI_MAX_DEVICE; ++Device) {
+ if (PciRead16 (PCI_LIB_ADDRESS (RootBridgeNumber, Device, 0,
+ PCI_VENDOR_ID_OFFSET)) != MAX_UINT16) {
+ break;
+ }
+ }
+ if (Device <= PCI_MAX_DEVICE) {
+ //
+ // Found the next root bus. We can now install the *previous* one,
+ // because now we know how big a bus number range *that* one has, for any
+ // subordinate buses that might exist behind PCI bridges hanging off it.
+ //
+ Status = InitRootBridge (
+ Attributes,
+ Attributes,
+ AllocationAttributes,
+ (UINT8) LastRootBridgeNumber,
+ (UINT8) (RootBridgeNumber - 1),
+ &Io,
+ &Mem,
+ &MemAbove4G,
+ &mNonExistAperture,
+ &mNonExistAperture,
+ &Bridges[Initialized]
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeBridges;
+ }
+ ++Initialized;
+ LastRootBridgeNumber = RootBridgeNumber;
+ }
+ }
+
+ //
+ // Install the last root bus (which might be the only, ie. main, root bus, if
+ // we've found no extra root buses).
+ //
+ Status = InitRootBridge (
+ Attributes,
+ Attributes,
+ AllocationAttributes,
+ (UINT8) LastRootBridgeNumber,
+ PCI_MAX_BUS,
+ &Io,
+ &Mem,
+ &MemAbove4G,
+ &mNonExistAperture,
+ &mNonExistAperture,
+ &Bridges[Initialized]
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeBridges;
+ }
+ ++Initialized;
+
+ *Count = Initialized;
+ return Bridges;
+
+FreeBridges:
+ while (Initialized > 0) {
+ --Initialized;
+ UninitRootBridge (&Bridges[Initialized]);
+ }
+
+ FreePool (Bridges);
+ return NULL;
+}
+
+
+/**
+ Free the root bridge instances array returned from
+ PciHostBridgeGetRootBridges().
+
+ @param The root bridge instances array.
+ @param The count of the array.
+**/
+VOID
+EFIAPI
+PciHostBridgeFreeRootBridges (
+ PCI_ROOT_BRIDGE *Bridges,
+ UINTN Count
+ )
+{
+ if (Bridges == NULL && Count == 0) {
+ return;
+ }
+ ASSERT (Bridges != NULL && Count > 0);
+
+ do {
+ --Count;
+ UninitRootBridge (&Bridges[Count]);
+ } while (Count > 0);
+
+ FreePool (Bridges);
+}
+
+
+/**
+ Inform the platform that the resource conflict happens.
+
+ @param HostBridgeHandle Handle of the Host Bridge.
+ @param Configuration Pointer to PCI I/O and PCI memory resource
+ descriptors. The Configuration contains the resources
+ for all the root bridges. The resource for each root
+ bridge is terminated with END descriptor and an
+ additional END is appended indicating the end of the
+ entire resources. The resource descriptor field
+ values follow the description in
+ EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
+ .SubmitResources().
+**/
+VOID
+EFIAPI
+PciHostBridgeResourceConflict (
+ EFI_HANDLE HostBridgeHandle,
+ VOID *Configuration
+ )
+{
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
+ UINTN RootBridgeIndex;
+ DEBUG ((DEBUG_ERROR, "PciHostBridge: Resource conflict happens!\n"));
+
+ RootBridgeIndex = 0;
+ Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;
+ while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {
+ DEBUG ((DEBUG_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));
+ for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {
+ ASSERT (Descriptor->ResType <
+ ARRAY_SIZE (mPciHostBridgeLibAcpiAddressSpaceTypeStr)
+ );
+ DEBUG ((DEBUG_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",
+ mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],
+ Descriptor->AddrLen, Descriptor->AddrRangeMax
+ ));
+ if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {
+ DEBUG ((DEBUG_ERROR, " Granularity/SpecificFlag = %ld / %02x%s\n",
+ Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,
+ ((Descriptor->SpecificFlag &
+ EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE
+ ) != 0) ? L" (Prefetchable)" : L""
+ ));
+ }
+ }
+ //
+ // Skip the END descriptor for root bridge
+ //
+ ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);
+ Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(
+ (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1
+ );
+ }
+}
diff --git a/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.inf b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.inf new file mode 100644 index 000000000..6ec9ec751 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.inf @@ -0,0 +1,53 @@ +## @file
+# OVMF's instance of the PCI Host Bridge Library.
+#
+# Copyright (C) 2016, Red Hat, Inc.
+# Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PciHostBridgeLib
+ FILE_GUID = 9F2BC05E-51EA-4AED-9A3E-7699641734E8
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PciHostBridgeLib
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ PciHostBridgeLib.c
+ XenSupport.c
+ PciHostBridge.h
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ PciLib
+ QemuFwCfgLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciIoBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciIoSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Base
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Size
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Base
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration
diff --git a/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/XenSupport.c b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/XenSupport.c new file mode 100644 index 000000000..e161f1437 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PciHostBridgeLib/XenSupport.c @@ -0,0 +1,470 @@ +/** @file
+ Scan the entire PCI bus for root bridges to support OVMF above Xen.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <PiDxe.h>
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/Q35MchIch9.h>
+
+#include <Protocol/PciHostBridgeResourceAllocation.h>
+#include <Protocol/PciRootBridgeIo.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PciHostBridgeLib.h>
+#include <Library/PciLib.h>
+#include "PciHostBridge.h"
+
+STATIC
+VOID
+PcatPciRootBridgeBarExisted (
+ IN UINTN Address,
+ OUT UINT32 *OriginalValue,
+ OUT UINT32 *Value
+ )
+{
+ //
+ // Preserve the original value
+ //
+ *OriginalValue = PciRead32 (Address);
+
+ //
+ // Disable timer interrupt while the BAR is probed
+ //
+ DisableInterrupts ();
+
+ PciWrite32 (Address, 0xFFFFFFFF);
+ *Value = PciRead32 (Address);
+ PciWrite32 (Address, *OriginalValue);
+
+ //
+ // Enable interrupt
+ //
+ EnableInterrupts ();
+}
+
+#define PCI_COMMAND_DECODE ((UINT16)(EFI_PCI_COMMAND_IO_SPACE | \
+ EFI_PCI_COMMAND_MEMORY_SPACE))
+STATIC
+VOID
+PcatPciRootBridgeDecodingDisable (
+ IN UINTN Address
+ )
+{
+ UINT16 Value;
+
+ Value = PciRead16 (Address);
+ if (Value & PCI_COMMAND_DECODE) {
+ PciWrite16 (Address, Value & ~(UINT32)PCI_COMMAND_DECODE);
+ }
+}
+
+STATIC
+VOID
+PcatPciRootBridgeParseBars (
+ IN UINT16 Command,
+ IN UINTN Bus,
+ IN UINTN Device,
+ IN UINTN Function,
+ IN UINTN BarOffsetBase,
+ IN UINTN BarOffsetEnd,
+ IN PCI_ROOT_BRIDGE_APERTURE *Io,
+ IN PCI_ROOT_BRIDGE_APERTURE *Mem,
+ IN PCI_ROOT_BRIDGE_APERTURE *MemAbove4G
+
+)
+{
+ UINT32 OriginalValue;
+ UINT32 Value;
+ UINT32 OriginalUpperValue;
+ UINT32 UpperValue;
+ UINT64 Mask;
+ UINTN Offset;
+ UINT64 Base;
+ UINT64 Length;
+ UINT64 Limit;
+ PCI_ROOT_BRIDGE_APERTURE *MemAperture;
+
+ // Disable address decoding for every device before OVMF starts sizing it
+ PcatPciRootBridgeDecodingDisable (
+ PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET)
+ );
+
+ for (Offset = BarOffsetBase; Offset < BarOffsetEnd; Offset += sizeof (UINT32)) {
+ PcatPciRootBridgeBarExisted (
+ PCI_LIB_ADDRESS (Bus, Device, Function, Offset),
+ &OriginalValue, &Value
+ );
+ if (Value == 0) {
+ continue;
+ }
+ if ((Value & BIT0) == BIT0) {
+ //
+ // IO Bar
+ //
+ if (Command & EFI_PCI_COMMAND_IO_SPACE) {
+ Mask = 0xfffffffc;
+ Base = OriginalValue & Mask;
+ Length = ((~(Value & Mask)) & Mask) + 0x04;
+ if (!(Value & 0xFFFF0000)) {
+ Length &= 0x0000FFFF;
+ }
+ Limit = Base + Length - 1;
+
+ if (Base < Limit) {
+ if (Io->Base > Base) {
+ Io->Base = Base;
+ }
+ if (Io->Limit < Limit) {
+ Io->Limit = Limit;
+ }
+ }
+ }
+ } else {
+ //
+ // Mem Bar
+ //
+ if (Command & EFI_PCI_COMMAND_MEMORY_SPACE) {
+
+ Mask = 0xfffffff0;
+ Base = OriginalValue & Mask;
+ Length = Value & Mask;
+
+ if ((Value & (BIT1 | BIT2)) == 0) {
+ //
+ // 32bit
+ //
+ Length = ((~Length) + 1) & 0xffffffff;
+
+ MemAperture = Mem;
+ } else {
+ //
+ // 64bit
+ //
+ Offset += 4;
+ PcatPciRootBridgeBarExisted (
+ PCI_LIB_ADDRESS (Bus, Device, Function, Offset),
+ &OriginalUpperValue,
+ &UpperValue
+ );
+
+ Base = Base | LShiftU64 ((UINT64) OriginalUpperValue, 32);
+ Length = Length | LShiftU64 ((UINT64) UpperValue, 32);
+ Length = (~Length) + 1;
+
+ if (Base < BASE_4GB) {
+ MemAperture = Mem;
+ } else {
+ MemAperture = MemAbove4G;
+ }
+ }
+
+ Limit = Base + Length - 1;
+ if (Base < Limit) {
+ if (MemAperture->Base > Base) {
+ MemAperture->Base = Base;
+ }
+ if (MemAperture->Limit < Limit) {
+ MemAperture->Limit = Limit;
+ }
+ }
+ }
+ }
+ }
+}
+
+STATIC PCI_ROOT_BRIDGE_APERTURE mNonExistAperture = { MAX_UINT64, 0 };
+
+PCI_ROOT_BRIDGE *
+ScanForRootBridges (
+ UINTN *NumberOfRootBridges
+ )
+{
+ UINTN PrimaryBus;
+ UINTN SubBus;
+ UINT8 Device;
+ UINT8 Function;
+ UINTN NumberOfDevices;
+ UINTN Address;
+ PCI_TYPE01 Pci;
+ UINT64 Attributes;
+ UINT64 Base;
+ UINT64 Limit;
+ UINT64 Value;
+ PCI_ROOT_BRIDGE_APERTURE Io, Mem, MemAbove4G, *MemAperture;
+ PCI_ROOT_BRIDGE *RootBridges;
+ UINTN BarOffsetEnd;
+
+
+ *NumberOfRootBridges = 0;
+ RootBridges = NULL;
+
+ //
+ // After scanning all the PCI devices on the PCI root bridge's primary bus,
+ // update the Primary Bus Number for the next PCI root bridge to be this PCI
+ // root bridge's subordinate bus number + 1.
+ //
+ for (PrimaryBus = 0; PrimaryBus <= PCI_MAX_BUS; PrimaryBus = SubBus + 1) {
+ SubBus = PrimaryBus;
+ Attributes = 0;
+
+ ZeroMem (&Io, sizeof (Io));
+ ZeroMem (&Mem, sizeof (Mem));
+ ZeroMem (&MemAbove4G, sizeof (MemAbove4G));
+ Io.Base = Mem.Base = MemAbove4G.Base = MAX_UINT64;
+ //
+ // Scan all the PCI devices on the primary bus of the PCI root bridge
+ //
+ for (Device = 0, NumberOfDevices = 0; Device <= PCI_MAX_DEVICE; Device++) {
+
+ for (Function = 0; Function <= PCI_MAX_FUNC; Function++) {
+
+ //
+ // Compute the PCI configuration address of the PCI device to probe
+ //
+ Address = PCI_LIB_ADDRESS (PrimaryBus, Device, Function, 0);
+
+ //
+ // Read the Vendor ID from the PCI Configuration Header
+ //
+ if (PciRead16 (Address) == MAX_UINT16) {
+ if (Function == 0) {
+ //
+ // If the PCI Configuration Read fails, or a PCI device does not
+ // exist, then skip this entire PCI device
+ //
+ break;
+ } else {
+ //
+ // If PCI function != 0, VendorId == 0xFFFF, we continue to search
+ // PCI function.
+ //
+ continue;
+ }
+ }
+
+ //
+ // Read the entire PCI Configuration Header
+ //
+ PciReadBuffer (Address, sizeof (Pci), &Pci);
+
+ //
+ // Increment the number of PCI device found on the primary bus of the
+ // PCI root bridge
+ //
+ NumberOfDevices++;
+
+ //
+ // Look for devices with the VGA Palette Snoop enabled in the COMMAND
+ // register of the PCI Config Header
+ //
+ if ((Pci.Hdr.Command & EFI_PCI_COMMAND_VGA_PALETTE_SNOOP) != 0) {
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;
+ }
+
+ BarOffsetEnd = 0;
+
+ //
+ // PCI-PCI Bridge
+ //
+ if (IS_PCI_BRIDGE (&Pci)) {
+ //
+ // Get the Bus range that the PPB is decoding
+ //
+ if (Pci.Bridge.SubordinateBus > SubBus) {
+ //
+ // If the subordinate bus number of the PCI-PCI bridge is greater
+ // than the PCI root bridge's current subordinate bus number,
+ // then update the PCI root bridge's subordinate bus number
+ //
+ SubBus = Pci.Bridge.SubordinateBus;
+ }
+
+ //
+ // Get the I/O range that the PPB is decoding
+ //
+ Value = Pci.Bridge.IoBase & 0x0f;
+ Base = ((UINT32) Pci.Bridge.IoBase & 0xf0) << 8;
+ Limit = (((UINT32) Pci.Bridge.IoLimit & 0xf0) << 8) | 0x0fff;
+ if (Value == BIT0) {
+ Base |= ((UINT32) Pci.Bridge.IoBaseUpper16 << 16);
+ Limit |= ((UINT32) Pci.Bridge.IoLimitUpper16 << 16);
+ }
+ if (Base < Limit) {
+ if (Io.Base > Base) {
+ Io.Base = Base;
+ }
+ if (Io.Limit < Limit) {
+ Io.Limit = Limit;
+ }
+ }
+
+ //
+ // Get the Memory range that the PPB is decoding
+ //
+ Base = ((UINT32) Pci.Bridge.MemoryBase & 0xfff0) << 16;
+ Limit = (((UINT32) Pci.Bridge.MemoryLimit & 0xfff0) << 16) | 0xfffff;
+ if (Base < Limit) {
+ if (Mem.Base > Base) {
+ Mem.Base = Base;
+ }
+ if (Mem.Limit < Limit) {
+ Mem.Limit = Limit;
+ }
+ }
+
+ //
+ // Get the Prefetchable Memory range that the PPB is decoding
+ // and merge it into Memory range
+ //
+ Value = Pci.Bridge.PrefetchableMemoryBase & 0x0f;
+ Base = ((UINT32) Pci.Bridge.PrefetchableMemoryBase & 0xfff0) << 16;
+ Limit = (((UINT32) Pci.Bridge.PrefetchableMemoryLimit & 0xfff0)
+ << 16) | 0xfffff;
+ MemAperture = &Mem;
+ if (Value == BIT0) {
+ Base |= LShiftU64 (Pci.Bridge.PrefetchableBaseUpper32, 32);
+ Limit |= LShiftU64 (Pci.Bridge.PrefetchableLimitUpper32, 32);
+ MemAperture = &MemAbove4G;
+ }
+ if (Base < Limit) {
+ if (MemAperture->Base > Base) {
+ MemAperture->Base = Base;
+ }
+ if (MemAperture->Limit < Limit) {
+ MemAperture->Limit = Limit;
+ }
+ }
+
+ //
+ // Look at the PPB Configuration for legacy decoding attributes
+ //
+ if ((Pci.Bridge.BridgeControl & EFI_PCI_BRIDGE_CONTROL_ISA)
+ == EFI_PCI_BRIDGE_CONTROL_ISA) {
+ Attributes |= EFI_PCI_ATTRIBUTE_ISA_IO;
+ Attributes |= EFI_PCI_ATTRIBUTE_ISA_IO_16;
+ Attributes |= EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO;
+ }
+ if ((Pci.Bridge.BridgeControl & EFI_PCI_BRIDGE_CONTROL_VGA)
+ == EFI_PCI_BRIDGE_CONTROL_VGA) {
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_MEMORY;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_IO;
+ if ((Pci.Bridge.BridgeControl & EFI_PCI_BRIDGE_CONTROL_VGA_16)
+ != 0) {
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_IO_16;
+ }
+ }
+
+ BarOffsetEnd = OFFSET_OF (PCI_TYPE01, Bridge.Bar[2]);
+ } else {
+ //
+ // Parse the BARs of the PCI device to get what I/O Ranges, Memory
+ // Ranges, and Prefetchable Memory Ranges the device is decoding
+ //
+ if ((Pci.Hdr.HeaderType & HEADER_LAYOUT_CODE) == HEADER_TYPE_DEVICE) {
+ BarOffsetEnd = OFFSET_OF (PCI_TYPE00, Device.Bar[6]);
+ }
+ }
+
+ PcatPciRootBridgeParseBars (
+ Pci.Hdr.Command,
+ PrimaryBus,
+ Device,
+ Function,
+ OFFSET_OF (PCI_TYPE00, Device.Bar),
+ BarOffsetEnd,
+ &Io,
+ &Mem, &MemAbove4G
+ );
+
+ //
+ // See if the PCI device is an IDE controller
+ //
+ if (IS_CLASS2 (&Pci, PCI_CLASS_MASS_STORAGE,
+ PCI_CLASS_MASS_STORAGE_IDE)) {
+ if (Pci.Hdr.ClassCode[0] & 0x80) {
+ Attributes |= EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO;
+ Attributes |= EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO;
+ }
+ if (Pci.Hdr.ClassCode[0] & 0x01) {
+ Attributes |= EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO;
+ }
+ if (Pci.Hdr.ClassCode[0] & 0x04) {
+ Attributes |= EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO;
+ }
+ }
+
+ //
+ // See if the PCI device is a legacy VGA controller or
+ // a standard VGA controller
+ //
+ if (IS_CLASS2 (&Pci, PCI_CLASS_OLD, PCI_CLASS_OLD_VGA) ||
+ IS_CLASS2 (&Pci, PCI_CLASS_DISPLAY, PCI_CLASS_DISPLAY_VGA)
+ ) {
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_MEMORY;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_IO;
+ Attributes |= EFI_PCI_ATTRIBUTE_VGA_IO_16;
+ }
+
+ //
+ // See if the PCI Device is a PCI - ISA or PCI - EISA
+ // or ISA_POSITIVE_DECODE Bridge device
+ //
+ if (Pci.Hdr.ClassCode[2] == PCI_CLASS_BRIDGE) {
+ if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA ||
+ Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_EISA ||
+ Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA_PDECODE) {
+ Attributes |= EFI_PCI_ATTRIBUTE_ISA_IO;
+ Attributes |= EFI_PCI_ATTRIBUTE_ISA_IO_16;
+ Attributes |= EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO;
+ }
+ }
+
+ //
+ // If this device is not a multi function device, then skip the rest
+ // of this PCI device
+ //
+ if (Function == 0 && !IS_PCI_MULTI_FUNC (&Pci)) {
+ break;
+ }
+ }
+ }
+
+ //
+ // If at least one PCI device was found on the primary bus of this PCI
+ // root bridge, then the PCI root bridge exists.
+ //
+ if (NumberOfDevices > 0) {
+ RootBridges = ReallocatePool (
+ (*NumberOfRootBridges) * sizeof (PCI_ROOT_BRIDGE),
+ (*NumberOfRootBridges + 1) * sizeof (PCI_ROOT_BRIDGE),
+ RootBridges
+ );
+ ASSERT (RootBridges != NULL);
+ InitRootBridge (
+ Attributes, Attributes, 0,
+ (UINT8) PrimaryBus, (UINT8) SubBus,
+ &Io, &Mem, &MemAbove4G, &mNonExistAperture, &mNonExistAperture,
+ &RootBridges[*NumberOfRootBridges]
+ );
+ RootBridges[*NumberOfRootBridges].ResourceAssigned = TRUE;
+ //
+ // Increment the index for the next PCI Root Bridge
+ //
+ (*NumberOfRootBridges)++;
+ }
+ }
+
+ return RootBridges;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf b/roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf new file mode 100644 index 000000000..43ea7998d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf @@ -0,0 +1,60 @@ +## @file
+# Register a status code handler for printing the Boot Manager's LoadImage()
+# and StartImage() preparations, and return codes, to the UEFI console.
+#
+# This feature enables users that are not accustomed to analyzing the firmware
+# log to glean some information about UEFI boot option processing (loading and
+# starting).
+#
+# This library instance filters out (ignores) status codes that are not
+# reported by the containing firmware module. The intent is to link this
+# library instance into BdsDxe via PlatformBootManagerLib (which BdsDxe depends
+# upon), then catch only those status codes that BdsDxe reports (which happens
+# via UefiBootManagerLib). Status codes reported by other modules (such as
+# UiApp), via UefiBootManagerLib or otherwise, are meant to be ignored.
+#
+# Copyright (C) 2019, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = PlatformBmPrintScLib
+ FILE_GUID = 3417c705-903e-41a7-9485-3fafebf60917
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformBmPrintScLib|DXE_DRIVER
+
+[Sources]
+ StatusCodeHandler.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ PcdLib
+ PrintLib
+ UefiBootManagerLib
+ UefiBootServicesTableLib
+ UefiLib
+ UefiRuntimeServicesTableLib
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderLoad ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderStart ## CONSUMES
+
+[Protocols]
+ gEfiRscHandlerProtocolGuid ## CONSUMES
+
+[Guids]
+ gEfiGlobalVariableGuid ## CONSUMES
+ gEfiStatusCodeSpecificDataGuid ## CONSUMES
+
+[Depex.common.DXE_DRIVER]
+ gEfiRscHandlerProtocolGuid AND gEfiVariableArchProtocolGuid
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c b/roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c new file mode 100644 index 000000000..594ab105e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c @@ -0,0 +1,304 @@ +/** @file
+ Register a status code handler for printing the Boot Manager's LoadImage()
+ and StartImage() preparations, and return codes, to the UEFI console.
+
+ This feature enables users that are not accustomed to analyzing the firmware
+ log to glean some information about UEFI boot option processing (loading and
+ starting).
+
+ This library instance filters out (ignores) status codes that are not
+ reported by the containing firmware module. The intent is to link this
+ library instance into BdsDxe via PlatformBootManagerLib (which BdsDxe depends
+ upon), then catch only those status codes that BdsDxe reports (which happens
+ via UefiBootManagerLib). Status codes reported by other modules (such as
+ UiApp), via UefiBootManagerLib or otherwise, are meant to be ignored.
+
+ Copyright (C) 2019, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Protocol/ReportStatusCodeHandler.h>
+
+#include <Guid/GlobalVariable.h>
+#include <Guid/StatusCodeDataTypeId.h>
+
+#include <Pi/PiStatusCode.h>
+
+
+//
+// Convenience variables for the status codes that are relevant for LoadImage()
+// and StartImage() preparations and return codes.
+//
+STATIC EFI_STATUS_CODE_VALUE mLoadPrep;
+STATIC EFI_STATUS_CODE_VALUE mLoadFail;
+STATIC EFI_STATUS_CODE_VALUE mStartPrep;
+STATIC EFI_STATUS_CODE_VALUE mStartFail;
+
+
+/**
+ Handle status codes reported through ReportStatusCodeLib /
+ EFI_STATUS_CODE_PROTOCOL.ReportStatusCode(). Format matching status codes to
+ the system console.
+
+ The highest TPL at which this handler can be registered with
+ EFI_RSC_HANDLER_PROTOCOL.Register() is TPL_CALLBACK. That's because
+ HandleStatusCode() uses the UEFI variable services.
+
+ The parameter list of this function precisely matches that of
+ EFI_STATUS_CODE_PROTOCOL.ReportStatusCode().
+
+ The return status of this function is ignored by the caller, but the function
+ still returns sensible codes:
+
+ @retval EFI_SUCCESS The status code has been processed; either
+ as a no-op, due to filtering, or by
+ formatting it to the system console.
+
+ @retval EFI_INVALID_PARAMETER Unknown or malformed contents have been
+ detected in Data.
+
+ @retval EFI_INCOMPATIBLE_VERSION Unexpected UEFI variable behavior has been
+ encountered.
+
+ @return Error codes propagated from underlying
+ services.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+HandleStatusCode (
+ IN EFI_STATUS_CODE_TYPE CodeType,
+ IN EFI_STATUS_CODE_VALUE Value,
+ IN UINT32 Instance,
+ IN EFI_GUID *CallerId,
+ IN EFI_STATUS_CODE_DATA *Data
+ )
+{
+ UINTN VariableSize;
+ UINT16 BootCurrent;
+ EFI_STATUS Status;
+ CHAR16 BootOptionName[ARRAY_SIZE (L"Boot####")];
+ EFI_BOOT_MANAGER_LOAD_OPTION BmBootOption;
+ BOOLEAN DevPathStringIsDynamic;
+ CHAR16 *DevPathString;
+
+ //
+ // Ignore all status codes that are irrelevant for LoadImage() and
+ // StartImage() preparations and return codes.
+ //
+ if (Value != mLoadPrep && Value != mLoadFail &&
+ Value != mStartPrep && Value != mStartFail) {
+ return EFI_SUCCESS;
+ }
+ //
+ // Ignore status codes that are not reported by the same containing module.
+ //
+ if (!CompareGuid (CallerId, &gEfiCallerIdGuid)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Sanity-check Data in case of failure reports.
+ //
+ if ((Value == mLoadFail || Value == mStartFail) &&
+ (Data == NULL ||
+ Data->HeaderSize != sizeof *Data ||
+ Data->Size != sizeof (EFI_RETURN_STATUS_EXTENDED_DATA) - sizeof *Data ||
+ !CompareGuid (&Data->Type, &gEfiStatusCodeSpecificDataGuid))) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: malformed Data\n", gEfiCallerBaseName,
+ __FUNCTION__));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the number of the Boot#### option that the status code applies to.
+ //
+ VariableSize = sizeof BootCurrent;
+ Status = gRT->GetVariable (EFI_BOOT_CURRENT_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid, NULL /* Attributes */,
+ &VariableSize, &BootCurrent);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to get %g:\"%s\": %r\n",
+ gEfiCallerBaseName, __FUNCTION__, &gEfiGlobalVariableGuid,
+ EFI_BOOT_CURRENT_VARIABLE_NAME, Status));
+ return Status;
+ }
+ if (VariableSize != sizeof BootCurrent) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: got %Lu bytes for %g:\"%s\", expected %Lu\n",
+ gEfiCallerBaseName, __FUNCTION__, (UINT64)VariableSize,
+ &gEfiGlobalVariableGuid, EFI_BOOT_CURRENT_VARIABLE_NAME,
+ (UINT64)sizeof BootCurrent));
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+
+ //
+ // Get the Boot#### option that the status code applies to.
+ //
+ UnicodeSPrint (BootOptionName, sizeof BootOptionName, L"Boot%04x",
+ BootCurrent);
+ Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BmBootOption);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a: EfiBootManagerVariableToLoadOption(\"%s\"): %r\n",
+ gEfiCallerBaseName, __FUNCTION__, BootOptionName, Status));
+ return Status;
+ }
+
+ //
+ // Format the device path.
+ //
+ DevPathStringIsDynamic = TRUE;
+ DevPathString = ConvertDevicePathToText (
+ BmBootOption.FilePath,
+ FALSE, // DisplayOnly
+ FALSE // AllowShortcuts
+ );
+ if (DevPathString == NULL) {
+ DevPathStringIsDynamic = FALSE;
+ DevPathString = L"<out of memory while formatting device path>";
+ }
+
+ //
+ // Print the message to the console.
+ //
+ if (Value == mLoadPrep || Value == mStartPrep) {
+ Print (
+ L"%a: %a %s \"%s\" from %s\n",
+ gEfiCallerBaseName,
+ Value == mLoadPrep ? "loading" : "starting",
+ BootOptionName,
+ BmBootOption.Description,
+ DevPathString
+ );
+ } else {
+ Print (
+ L"%a: failed to %a %s \"%s\" from %s: %r\n",
+ gEfiCallerBaseName,
+ Value == mLoadFail ? "load" : "start",
+ BootOptionName,
+ BmBootOption.Description,
+ DevPathString,
+ ((EFI_RETURN_STATUS_EXTENDED_DATA *)Data)->ReturnStatus
+ );
+ }
+
+ //
+ // Done.
+ //
+ if (DevPathStringIsDynamic) {
+ FreePool (DevPathString);
+ }
+ EfiBootManagerFreeLoadOption (&BmBootOption);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Unregister HandleStatusCode() at ExitBootServices().
+
+ (See EFI_RSC_HANDLER_PROTOCOL in Volume 3 of the Platform Init spec.)
+
+ @param[in] Event Event whose notification function is being invoked.
+
+ @param[in] Context Pointer to EFI_RSC_HANDLER_PROTOCOL, originally looked up
+ when HandleStatusCode() was registered.
+**/
+STATIC
+VOID
+EFIAPI
+UnregisterAtExitBootServices (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
+
+ StatusCodeRouter = Context;
+ StatusCodeRouter->Unregister (HandleStatusCode);
+}
+
+
+/**
+ Register a status code handler for printing the Boot Manager's LoadImage()
+ and StartImage() preparations, and return codes, to the UEFI console.
+
+ @retval EFI_SUCCESS The status code handler has been successfully
+ registered.
+
+ @return Error codes propagated from boot services and from
+ EFI_RSC_HANDLER_PROTOCOL.
+**/
+EFI_STATUS
+EFIAPI
+PlatformBmPrintScRegisterHandler (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
+ EFI_EVENT ExitBootEvent;
+
+ Status = gBS->LocateProtocol (&gEfiRscHandlerProtocolGuid,
+ NULL /* Registration */, (VOID **)&StatusCodeRouter);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Set the EFI_STATUS_CODE_VALUE convenience variables.
+ //
+ mLoadPrep = PcdGet32 (PcdProgressCodeOsLoaderLoad);
+ mLoadFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
+ EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR);
+ mStartPrep = PcdGet32 (PcdProgressCodeOsLoaderStart);
+ mStartFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
+ EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED);
+
+ //
+ // Register the handler callback.
+ //
+ Status = StatusCodeRouter->Register (HandleStatusCode, TPL_CALLBACK);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to register status code handler: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return Status;
+ }
+
+ //
+ // Status code reporting and routing/handling extend into OS runtime. Since
+ // we don't want our handler to survive the BDS phase, we have to unregister
+ // the callback at ExitBootServices(). (See EFI_RSC_HANDLER_PROTOCOL in
+ // Volume 3 of the Platform Init spec.)
+ //
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
+ TPL_CALLBACK, // NotifyTpl
+ UnregisterAtExitBootServices, // NotifyFunction
+ StatusCodeRouter, // NotifyContext
+ &ExitBootEvent // Event
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // We have to unregister the callback right now, and fail the function.
+ //
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to create ExitBootServices() event: "
+ "%r\n", gEfiCallerBaseName, __FUNCTION__, Status));
+ StatusCodeRouter->Unregister (HandleStatusCode);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c new file mode 100644 index 000000000..3c55ec9bd --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c @@ -0,0 +1,1740 @@ +/** @file
+ Platform BDS customizations.
+
+ Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "BdsPlatform.h"
+#include <Guid/RootBridgesConnectedEventGroup.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Library/PlatformBmPrintScLib.h>
+#include <Library/Tcg2PhysicalPresenceLib.h>
+#include <Library/XenPlatformLib.h>
+
+
+//
+// Global data
+//
+
+VOID *mEfiDevPathNotifyReg;
+EFI_EVENT mEfiDevPathEvent;
+VOID *mEmuVariableEventReg;
+EFI_EVENT mEmuVariableEvent;
+UINT16 mHostBridgeDevId;
+
+//
+// Table of host IRQs matching PCI IRQs A-D
+// (for configuring PCI Interrupt Line register)
+//
+CONST UINT8 PciHostIrqs[] = {
+ 0x0a, 0x0a, 0x0b, 0x0b
+};
+
+//
+// Type definitions
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *PROTOCOL_INSTANCE_CALLBACK)(
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ );
+
+/**
+ @param[in] Handle - Handle of PCI device instance
+ @param[in] PciIo - PCI IO protocol instance
+ @param[in] Pci - PCI Header register block
+**/
+typedef
+EFI_STATUS
+(EFIAPI *VISIT_PCI_INSTANCE_CALLBACK)(
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN PCI_TYPE00 *Pci
+ );
+
+
+//
+// Function prototypes
+//
+
+EFI_STATUS
+VisitAllInstancesOfProtocol (
+ IN EFI_GUID *Id,
+ IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+VisitAllPciInstancesOfProtocol (
+ IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction
+ );
+
+VOID
+InstallDevicePathCallback (
+ VOID
+ );
+
+VOID
+PlatformRegisterFvBootOption (
+ EFI_GUID *FileGuid,
+ CHAR16 *Description,
+ UINT32 Attributes
+ )
+{
+ EFI_STATUS Status;
+ INTN OptionIndex;
+ EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ Status = gBS->HandleProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **) &LoadedImage
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
+ DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);
+ ASSERT (DevicePath != NULL);
+ DevicePath = AppendDevicePathNode (
+ DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
+ );
+ ASSERT (DevicePath != NULL);
+
+ Status = EfiBootManagerInitializeLoadOption (
+ &NewOption,
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ Attributes,
+ Description,
+ DevicePath,
+ NULL,
+ 0
+ );
+ ASSERT_EFI_ERROR (Status);
+ FreePool (DevicePath);
+
+ BootOptions = EfiBootManagerGetLoadOptions (
+ &BootOptionCount, LoadOptionTypeBoot
+ );
+
+ OptionIndex = EfiBootManagerFindLoadOption (
+ &NewOption, BootOptions, BootOptionCount
+ );
+
+ if (OptionIndex == -1) {
+ Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);
+ ASSERT_EFI_ERROR (Status);
+ }
+ EfiBootManagerFreeLoadOption (&NewOption);
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+}
+
+/**
+ Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options
+ whose device paths do not resolve exactly to an FvFile in the system.
+
+ This removes any boot options that point to binaries built into the firmware
+ and have become stale due to any of the following:
+ - DXEFV's base address or size changed (historical),
+ - DXEFV's FvNameGuid changed,
+ - the FILE_GUID of the pointed-to binary changed,
+ - the referenced binary is no longer built into the firmware.
+
+ EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only
+ avoids exact duplicates.
+**/
+VOID
+RemoveStaleFvFileOptions (
+ VOID
+ )
+{
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+ UINTN Index;
+
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,
+ LoadOptionTypeBoot);
+
+ for (Index = 0; Index < BootOptionCount; ++Index) {
+ EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;
+ EFI_STATUS Status;
+ EFI_HANDLE FvHandle;
+
+ //
+ // If the device path starts with neither MemoryMapped(...) nor Fv(...),
+ // then keep the boot option.
+ //
+ Node1 = BootOptions[Index].FilePath;
+ if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&
+ DevicePathSubType (Node1) == HW_MEMMAP_DP) &&
+ !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {
+ continue;
+ }
+
+ //
+ // If the second device path node is not FvFile(...), then keep the boot
+ // option.
+ //
+ Node2 = NextDevicePathNode (Node1);
+ if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {
+ continue;
+ }
+
+ //
+ // Locate the Firmware Volume2 protocol instance that is denoted by the
+ // boot option. If this lookup fails (i.e., the boot option references a
+ // firmware volume that doesn't exist), then we'll proceed to delete the
+ // boot option.
+ //
+ SearchNode = Node1;
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,
+ &SearchNode, &FvHandle);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // The firmware volume was found; now let's see if it contains the FvFile
+ // identified by GUID.
+ //
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;
+ UINTN BufferSize;
+ EFI_FV_FILETYPE FoundType;
+ EFI_FV_FILE_ATTRIBUTES FileAttributes;
+ UINT32 AuthenticationStatus;
+
+ Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,
+ (VOID **)&FvProtocol);
+ ASSERT_EFI_ERROR (Status);
+
+ FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;
+ //
+ // Buffer==NULL means we request metadata only: BufferSize, FoundType,
+ // FileAttributes.
+ //
+ Status = FvProtocol->ReadFile (
+ FvProtocol,
+ &FvFileNode->FvFileName, // NameGuid
+ NULL, // Buffer
+ &BufferSize,
+ &FoundType,
+ &FileAttributes,
+ &AuthenticationStatus
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // The FvFile was found. Keep the boot option.
+ //
+ continue;
+ }
+ }
+
+ //
+ // Delete the boot option.
+ //
+ Status = EfiBootManagerDeleteLoadOptionVariable (
+ BootOptions[Index].OptionNumber, LoadOptionTypeBoot);
+ DEBUG_CODE (
+ CHAR16 *DevicePathString;
+
+ DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,
+ FALSE, FALSE);
+ DEBUG ((
+ EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_VERBOSE,
+ "%a: removing stale Boot#%04x %s: %r\n",
+ __FUNCTION__,
+ (UINT32)BootOptions[Index].OptionNumber,
+ DevicePathString == NULL ? L"<unavailable>" : DevicePathString,
+ Status
+ ));
+ if (DevicePathString != NULL) {
+ FreePool (DevicePathString);
+ }
+ );
+ }
+
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+}
+
+VOID
+PlatformRegisterOptionsAndKeys (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Enter;
+ EFI_INPUT_KEY F2;
+ EFI_INPUT_KEY Esc;
+ EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
+
+ //
+ // Register ENTER as CONTINUE key
+ //
+ Enter.ScanCode = SCAN_NULL;
+ Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
+ Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Map F2 to Boot Manager Menu
+ //
+ F2.ScanCode = SCAN_F2;
+ F2.UnicodeChar = CHAR_NULL;
+ Esc.ScanCode = SCAN_ESC;
+ Esc.UnicodeChar = CHAR_NULL;
+ Status = EfiBootManagerGetBootManagerMenu (&BootOption);
+ ASSERT_EFI_ERROR (Status);
+ Status = EfiBootManagerAddKeyOptionVariable (
+ NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
+ );
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+ Status = EfiBootManagerAddKeyOptionVariable (
+ NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
+ );
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+}
+
+EFI_STATUS
+EFIAPI
+ConnectRootBridge (
+ IN EFI_HANDLE RootBridgeHandle,
+ IN VOID *Instance,
+ IN VOID *Context
+ );
+
+STATIC
+EFI_STATUS
+EFIAPI
+ConnectVirtioPciRng (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ );
+
+STATIC
+VOID
+SaveS3BootScript (
+ VOID
+ );
+
+//
+// BDS Platform Functions
+//
+/**
+ Do the platform init, can be customized by OEM/IBV
+
+ Possible things that can be done in PlatformBootManagerBeforeConsole:
+
+ > Update console variable: 1. include hot-plug devices;
+ > 2. Clear ConIn and add SOL for AMT
+ > Register new Driver#### or Boot####
+ > Register new Key####: e.g.: F12
+ > Signal ReadyToLock event
+ > Authentication action: 1. connect Auth devices;
+ > 2. Identify auto logon user.
+**/
+VOID
+EFIAPI
+PlatformBootManagerBeforeConsole (
+ VOID
+ )
+{
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+ UINT16 FrontPageTimeout;
+ RETURN_STATUS PcdStatus;
+
+ DEBUG ((DEBUG_INFO, "PlatformBootManagerBeforeConsole\n"));
+ InstallDevicePathCallback ();
+
+ VisitAllInstancesOfProtocol (&gEfiPciRootBridgeIoProtocolGuid,
+ ConnectRootBridge, NULL);
+
+ //
+ // Signal the ACPI platform driver that it can download QEMU ACPI tables.
+ //
+ EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);
+
+ //
+ // We can't signal End-of-Dxe earlier than this. Namely, End-of-Dxe triggers
+ // the preparation of S3 system information. That logic has a hard dependency
+ // on the presence of the FACS ACPI table. Since our ACPI tables are only
+ // installed after PCI enumeration completes, we must not trigger the S3 save
+ // earlier, hence we can't signal End-of-Dxe earlier.
+ //
+ EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);
+
+ if (QemuFwCfgS3Enabled ()) {
+ //
+ // Save the boot script too. Note that this will require us to emit the
+ // DxeSmmReadyToLock event just below, which in turn locks down SMM.
+ //
+ SaveS3BootScript ();
+ }
+
+ //
+ // Prevent further changes to LockBoxes or SMRAM.
+ //
+ Handle = NULL;
+ Status = gBS->InstallProtocolInterface (&Handle,
+ &gEfiDxeSmmReadyToLockProtocolGuid, EFI_NATIVE_INTERFACE,
+ NULL);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Dispatch deferred images after EndOfDxe event and ReadyToLock
+ // installation.
+ //
+ EfiBootManagerDispatchDeferredImages ();
+
+ PlatformInitializeConsole (
+ XenDetected() ? gXenPlatformConsole : gPlatformConsole);
+
+ FrontPageTimeout = GetFrontPageTimeoutFromQemu ();
+ PcdStatus = PcdSet16S (PcdPlatformBootTimeOut, FrontPageTimeout);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ //
+ // Reflect the PCD in the standard Timeout variable.
+ //
+ Status = gRT->SetVariable (
+ EFI_TIME_OUT_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ (EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS),
+ sizeof FrontPageTimeout,
+ &FrontPageTimeout
+ );
+ DEBUG ((
+ EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE,
+ "%a: SetVariable(%s, %u): %r\n",
+ __FUNCTION__,
+ EFI_TIME_OUT_VARIABLE_NAME,
+ FrontPageTimeout,
+ Status
+ ));
+
+ PlatformRegisterOptionsAndKeys ();
+
+ //
+ // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL
+ // instances on Virtio PCI RNG devices.
+ //
+ VisitAllInstancesOfProtocol (&gEfiPciIoProtocolGuid, ConnectVirtioPciRng,
+ NULL);
+}
+
+
+EFI_STATUS
+EFIAPI
+ConnectRootBridge (
+ IN EFI_HANDLE RootBridgeHandle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Make the PCI bus driver connect the root bridge, non-recursively. This
+ // will produce a number of child handles with PciIo on them.
+ //
+ Status = gBS->ConnectController (
+ RootBridgeHandle, // ControllerHandle
+ NULL, // DriverImageHandle
+ NULL, // RemainingDevicePath -- produce all
+ // children
+ FALSE // Recursive
+ );
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+ConnectVirtioPciRng (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+ UINT16 VendorId;
+ UINT16 DeviceId;
+ UINT8 RevisionId;
+ BOOLEAN Virtio10;
+ UINT16 SubsystemId;
+
+ PciIo = Instance;
+
+ //
+ // Read and check VendorId.
+ //
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,
+ 1, &VendorId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ if (VendorId != VIRTIO_VENDOR_ID) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Read DeviceId and RevisionId.
+ //
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_DEVICE_ID_OFFSET,
+ 1, &DeviceId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,
+ 1, &RevisionId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // From DeviceId and RevisionId, determine whether the device is a
+ // modern-only Virtio 1.0 device. In case of Virtio 1.0, DeviceId can
+ // immediately be restricted to VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and
+ // SubsystemId will only play a sanity-check role. Otherwise, DeviceId can
+ // only be sanity-checked, and SubsystemId will decide.
+ //
+ if (DeviceId == 0x1040 + VIRTIO_SUBSYSTEM_ENTROPY_SOURCE &&
+ RevisionId >= 0x01) {
+ Virtio10 = TRUE;
+ } else if (DeviceId >= 0x1000 && DeviceId <= 0x103F && RevisionId == 0x00) {
+ Virtio10 = FALSE;
+ } else {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Read and check SubsystemId as dictated by Virtio10.
+ //
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,
+ PCI_SUBSYSTEM_ID_OFFSET, 1, &SubsystemId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ if ((Virtio10 && SubsystemId >= 0x40) ||
+ (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE)) {
+ Status = gBS->ConnectController (
+ Handle, // ControllerHandle
+ NULL, // DriverImageHandle -- connect all drivers
+ NULL, // RemainingDevicePath -- produce all child handles
+ FALSE // Recursive -- don't follow child handles
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ }
+ return EFI_SUCCESS;
+
+Error:
+ DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));
+ return Status;
+}
+
+
+/**
+ Add IsaKeyboard to ConIn; add IsaSerial to ConOut, ConIn, ErrOut.
+
+ @param[in] DeviceHandle Handle of the LPC Bridge device.
+
+ @retval EFI_SUCCESS Console devices on the LPC bridge have been added to
+ ConOut, ConIn, and ErrOut.
+
+ @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing
+ from DeviceHandle.
+**/
+EFI_STATUS
+PrepareLpcBridgeDevicePath (
+ IN EFI_HANDLE DeviceHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ CHAR16 *DevPathStr;
+
+ DevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ TempDevicePath = DevicePath;
+
+ //
+ // Register Keyboard
+ //
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gPnpPs2KeyboardDeviceNode);
+
+ EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);
+
+ //
+ // Register COM1
+ //
+ DevicePath = TempDevicePath;
+ gPnp16550ComPortDeviceNode.UID = 0;
+
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);
+
+ //
+ // Print Device Path
+ //
+ DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+ if (DevPathStr != NULL) {
+ DEBUG((
+ DEBUG_INFO,
+ "BdsPlatform.c+%d: COM%d DevPath: %s\n",
+ __LINE__,
+ gPnp16550ComPortDeviceNode.UID + 1,
+ DevPathStr
+ ));
+ FreePool(DevPathStr);
+ }
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
+
+ //
+ // Register COM2
+ //
+ DevicePath = TempDevicePath;
+ gPnp16550ComPortDeviceNode.UID = 1;
+
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);
+
+ //
+ // Print Device Path
+ //
+ DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+ if (DevPathStr != NULL) {
+ DEBUG((
+ DEBUG_INFO,
+ "BdsPlatform.c+%d: COM%d DevPath: %s\n",
+ __LINE__,
+ gPnp16550ComPortDeviceNode.UID + 1,
+ DevPathStr
+ ));
+ FreePool(DevPathStr);
+ }
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+GetGopDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *PciDevicePath,
+ OUT EFI_DEVICE_PATH_PROTOCOL **GopDevicePath
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+ EFI_HANDLE PciDeviceHandle;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempPciDevicePath;
+ UINTN GopHandleCount;
+ EFI_HANDLE *GopHandleBuffer;
+
+ if (PciDevicePath == NULL || GopDevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Initialize the GopDevicePath to be PciDevicePath
+ //
+ *GopDevicePath = PciDevicePath;
+ TempPciDevicePath = PciDevicePath;
+
+ Status = gBS->LocateDevicePath (
+ &gEfiDevicePathProtocolGuid,
+ &TempPciDevicePath,
+ &PciDeviceHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Try to connect this handle, so that GOP driver could start on this
+ // device and create child handles with GraphicsOutput Protocol installed
+ // on them, then we get device paths of these child handles and select
+ // them as possible console device.
+ //
+ gBS->ConnectController (PciDeviceHandle, NULL, NULL, FALSE);
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiGraphicsOutputProtocolGuid,
+ NULL,
+ &GopHandleCount,
+ &GopHandleBuffer
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Add all the child handles as possible Console Device
+ //
+ for (Index = 0; Index < GopHandleCount; Index++) {
+ Status = gBS->HandleProtocol (GopHandleBuffer[Index],
+ &gEfiDevicePathProtocolGuid, (VOID*)&TempDevicePath);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ if (CompareMem (
+ PciDevicePath,
+ TempDevicePath,
+ GetDevicePathSize (PciDevicePath) - END_DEVICE_PATH_LENGTH
+ ) == 0) {
+ //
+ // In current implementation, we only enable one of the child handles
+ // as console device, i.e. sotre one of the child handle's device
+ // path to variable "ConOut"
+ // In future, we could select all child handles to be console device
+ //
+
+ *GopDevicePath = TempDevicePath;
+
+ //
+ // Delete the PCI device's path that added by
+ // GetPlugInPciVgaDevicePath(). Add the integrity GOP device path.
+ //
+ EfiBootManagerUpdateConsoleVariable (ConOutDev, NULL, PciDevicePath);
+ EfiBootManagerUpdateConsoleVariable (ConOutDev, TempDevicePath, NULL);
+ }
+ }
+ gBS->FreePool (GopHandleBuffer);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Add PCI display to ConOut.
+
+ @param[in] DeviceHandle Handle of the PCI display device.
+
+ @retval EFI_SUCCESS The PCI display device has been added to ConOut.
+
+ @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing
+ from DeviceHandle.
+**/
+EFI_STATUS
+PreparePciDisplayDevicePath (
+ IN EFI_HANDLE DeviceHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;
+
+ DevicePath = NULL;
+ GopDevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ GetGopDevicePath (DevicePath, &GopDevicePath);
+ DevicePath = GopDevicePath;
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Add PCI Serial to ConOut, ConIn, ErrOut.
+
+ @param[in] DeviceHandle Handle of the PCI serial device.
+
+ @retval EFI_SUCCESS The PCI serial device has been added to ConOut, ConIn,
+ ErrOut.
+
+ @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing
+ from DeviceHandle.
+**/
+EFI_STATUS
+PreparePciSerialDevicePath (
+ IN EFI_HANDLE DeviceHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ DevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+VisitAllInstancesOfProtocol (
+ IN EFI_GUID *Id,
+ IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+ VOID *Instance;
+
+ //
+ // Start to check all the PciIo to find all possible device
+ //
+ HandleCount = 0;
+ HandleBuffer = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ Id,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (HandleBuffer[Index], Id, &Instance);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ Status = (*CallBackFunction) (
+ HandleBuffer[Index],
+ Instance,
+ Context
+ );
+ }
+
+ gBS->FreePool (HandleBuffer);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+VisitingAPciInstance (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ PciIo = (EFI_PCI_IO_PROTOCOL*) Instance;
+
+ //
+ // Check for all PCI device
+ //
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint32,
+ 0,
+ sizeof (Pci) / sizeof (UINT32),
+ &Pci
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return (*(VISIT_PCI_INSTANCE_CALLBACK)(UINTN) Context) (
+ Handle,
+ PciIo,
+ &Pci
+ );
+
+}
+
+
+
+EFI_STATUS
+VisitAllPciInstances (
+ IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction
+ )
+{
+ return VisitAllInstancesOfProtocol (
+ &gEfiPciIoProtocolGuid,
+ VisitingAPciInstance,
+ (VOID*)(UINTN) CallBackFunction
+ );
+}
+
+
+/**
+ Do platform specific PCI Device check and add them to
+ ConOut, ConIn, ErrOut.
+
+ @param[in] Handle - Handle of PCI device instance
+ @param[in] PciIo - PCI IO protocol instance
+ @param[in] Pci - PCI Header register block
+
+ @retval EFI_SUCCESS - PCI Device check and Console variable update
+ successfully.
+ @retval EFI_STATUS - PCI Device check or Console variable update fail.
+
+**/
+EFI_STATUS
+EFIAPI
+DetectAndPreparePlatformPciDevicePath (
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN PCI_TYPE00 *Pci
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_DEVICE_ENABLE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Here we decide whether it is LPC Bridge
+ //
+ if ((IS_PCI_LPC (Pci)) ||
+ ((IS_PCI_ISA_PDECODE (Pci)) &&
+ (Pci->Hdr.VendorId == 0x8086) &&
+ (Pci->Hdr.DeviceId == 0x7000)
+ )
+ ) {
+ //
+ // Add IsaKeyboard to ConIn,
+ // add IsaSerial to ConOut, ConIn, ErrOut
+ //
+ DEBUG ((DEBUG_INFO, "Found LPC Bridge device\n"));
+ PrepareLpcBridgeDevicePath (Handle);
+ return EFI_SUCCESS;
+ }
+ //
+ // Here we decide which Serial device to enable in PCI bus
+ //
+ if (IS_PCI_16550SERIAL (Pci)) {
+ //
+ // Add them to ConOut, ConIn, ErrOut.
+ //
+ DEBUG ((DEBUG_INFO, "Found PCI 16550 SERIAL device\n"));
+ PreparePciSerialDevicePath (Handle);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Here we decide which display device to enable in PCI bus
+ //
+ if (IS_PCI_DISPLAY (Pci)) {
+ //
+ // Add them to ConOut.
+ //
+ DEBUG ((DEBUG_INFO, "Found PCI display device\n"));
+ PreparePciDisplayDevicePath (Handle);
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+/**
+ Connect the predefined platform default console device.
+
+ Always try to find and enable PCI display devices.
+
+ @param[in] PlatformConsole Predefined platform default console device array.
+**/
+VOID
+PlatformInitializeConsole (
+ IN PLATFORM_CONSOLE_CONNECT_ENTRY *PlatformConsole
+ )
+{
+ UINTN Index;
+
+ //
+ // Do platform specific PCI Device check and add them to ConOut, ConIn,
+ // ErrOut
+ //
+ VisitAllPciInstances (DetectAndPreparePlatformPciDevicePath);
+
+ //
+ // Have chance to connect the platform default console,
+ // the platform default console is the minimum device group
+ // the platform should support
+ //
+ for (Index = 0; PlatformConsole[Index].DevicePath != NULL; ++Index) {
+ //
+ // Update the console variable with the connect type
+ //
+ if ((PlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) {
+ EfiBootManagerUpdateConsoleVariable (ConIn,
+ PlatformConsole[Index].DevicePath, NULL);
+ }
+ if ((PlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) {
+ EfiBootManagerUpdateConsoleVariable (ConOut,
+ PlatformConsole[Index].DevicePath, NULL);
+ }
+ if ((PlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) {
+ EfiBootManagerUpdateConsoleVariable (ErrOut,
+ PlatformConsole[Index].DevicePath, NULL);
+ }
+ }
+}
+
+
+/**
+ Configure PCI Interrupt Line register for applicable devices
+ Ported from SeaBIOS, src/fw/pciinit.c, *_pci_slot_get_irq()
+
+ @param[in] Handle - Handle of PCI device instance
+ @param[in] PciIo - PCI IO protocol instance
+ @param[in] PciHdr - PCI Header register block
+
+ @retval EFI_SUCCESS - PCI Interrupt Line register configured successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+SetPciIntLine (
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN PCI_TYPE00 *PciHdr
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevPathNode;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ UINTN RootSlot;
+ UINTN Idx;
+ UINT8 IrqLine;
+ EFI_STATUS Status;
+ UINT32 RootBusNumber;
+
+ Status = EFI_SUCCESS;
+
+ if (PciHdr->Device.InterruptPin != 0) {
+
+ DevPathNode = DevicePathFromHandle (Handle);
+ ASSERT (DevPathNode != NULL);
+ DevPath = DevPathNode;
+
+ RootBusNumber = 0;
+ if (DevicePathType (DevPathNode) == ACPI_DEVICE_PATH &&
+ DevicePathSubType (DevPathNode) == ACPI_DP &&
+ ((ACPI_HID_DEVICE_PATH *)DevPathNode)->HID == EISA_PNP_ID(0x0A03)) {
+ RootBusNumber = ((ACPI_HID_DEVICE_PATH *)DevPathNode)->UID;
+ }
+
+ //
+ // Compute index into PciHostIrqs[] table by walking
+ // the device path and adding up all device numbers
+ //
+ Status = EFI_NOT_FOUND;
+ RootSlot = 0;
+ Idx = PciHdr->Device.InterruptPin - 1;
+ while (!IsDevicePathEnd (DevPathNode)) {
+ if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH &&
+ DevicePathSubType (DevPathNode) == HW_PCI_DP) {
+
+ Idx += ((PCI_DEVICE_PATH *)DevPathNode)->Device;
+
+ //
+ // Unlike SeaBIOS, which starts climbing from the leaf device
+ // up toward the root, we traverse the device path starting at
+ // the root moving toward the leaf node.
+ // The slot number of the top-level parent bridge is needed for
+ // Q35 cases with more than 24 slots on the root bus.
+ //
+ if (Status != EFI_SUCCESS) {
+ Status = EFI_SUCCESS;
+ RootSlot = ((PCI_DEVICE_PATH *)DevPathNode)->Device;
+ }
+ }
+
+ DevPathNode = NextDevicePathNode (DevPathNode);
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (RootBusNumber == 0 && RootSlot == 0) {
+ DEBUG((
+ DEBUG_ERROR,
+ "%a: PCI host bridge (00:00.0) should have no interrupts!\n",
+ __FUNCTION__
+ ));
+ ASSERT (FALSE);
+ }
+
+ //
+ // Final PciHostIrqs[] index calculation depends on the platform
+ // and should match SeaBIOS src/fw/pciinit.c *_pci_slot_get_irq()
+ //
+ switch (mHostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ Idx -= 1;
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ //
+ // SeaBIOS contains the following comment:
+ // "Slots 0-24 rotate slot:pin mapping similar to piix above, but
+ // with a different starting index - see q35-acpi-dsdt.dsl.
+ //
+ // Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)"
+ //
+ if (RootSlot > 24) {
+ //
+ // in this case, subtract back out RootSlot from Idx
+ // (SeaBIOS never adds it to begin with, but that would make our
+ // device path traversal loop above too awkward)
+ //
+ Idx -= RootSlot;
+ }
+ break;
+ default:
+ ASSERT (FALSE); // should never get here
+ }
+ Idx %= ARRAY_SIZE (PciHostIrqs);
+ IrqLine = PciHostIrqs[Idx];
+
+ DEBUG_CODE_BEGIN ();
+ {
+ CHAR16 *DevPathString;
+ STATIC CHAR16 Fallback[] = L"<failed to convert>";
+ UINTN Segment, Bus, Device, Function;
+
+ DevPathString = ConvertDevicePathToText (DevPath, FALSE, FALSE);
+ if (DevPathString == NULL) {
+ DevPathString = Fallback;
+ }
+ Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
+ ASSERT_EFI_ERROR (Status);
+
+ DEBUG ((DEBUG_VERBOSE, "%a: [%02x:%02x.%x] %s -> 0x%02x\n", __FUNCTION__,
+ (UINT32)Bus, (UINT32)Device, (UINT32)Function, DevPathString,
+ IrqLine));
+
+ if (DevPathString != Fallback) {
+ FreePool (DevPathString);
+ }
+ }
+ DEBUG_CODE_END ();
+
+ //
+ // Set PCI Interrupt Line register for this device to PciHostIrqs[Idx]
+ //
+ Status = PciIo->Pci.Write (
+ PciIo,
+ EfiPciIoWidthUint8,
+ PCI_INT_LINE_OFFSET,
+ 1,
+ &IrqLine
+ );
+ }
+
+ return Status;
+}
+
+
+VOID
+PciAcpiInitialization (
+ )
+{
+ UINTN Pmba;
+
+ //
+ // Query Host Bridge DID to determine platform type
+ //
+ mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
+ switch (mHostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
+ //
+ // 00:01.0 ISA Bridge (PIIX4) LNK routing targets
+ //
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // A
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // B
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // C
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // D
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
+ //
+ // 00:1f.0 LPC Bridge (Q35) LNK routing targets
+ //
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), 0x0a); // A
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), 0x0a); // B
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), 0x0b); // C
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), 0x0b); // D
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), 0x0a); // E
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), 0x0a); // F
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), 0x0b); // G
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), 0x0b); // H
+ break;
+ default:
+ if (XenDetected ()) {
+ //
+ // There is no PCI bus in this case.
+ //
+ return;
+ }
+ DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
+ __FUNCTION__, mHostBridgeDevId));
+ ASSERT (FALSE);
+ return;
+ }
+
+ //
+ // Initialize PCI_INTERRUPT_LINE for applicable present PCI devices
+ //
+ VisitAllPciInstances (SetPciIntLine);
+
+ //
+ // Set ACPI SCI_EN bit in PMCNTRL
+ //
+ IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);
+}
+
+EFI_STATUS
+EFIAPI
+ConnectRecursivelyIfPciMassStorage (
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *Instance,
+ IN PCI_TYPE00 *PciHeader
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ CHAR16 *DevPathStr;
+
+ //
+ // Recognize PCI Mass Storage, and Xen PCI devices
+ //
+ if (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ||
+ (XenDetected() && IS_CLASS2 (PciHeader, 0xFF, 0x80))) {
+ DevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Print Device Path
+ //
+ DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+ if (DevPathStr != NULL) {
+ DEBUG((
+ DEBUG_INFO,
+ "Found %s device: %s\n",
+ (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ?
+ L"Mass Storage" :
+ L"Xen"
+ ),
+ DevPathStr
+ ));
+ FreePool(DevPathStr);
+ }
+
+ Status = gBS->ConnectController (Handle, NULL, NULL, TRUE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This notification function is invoked when the
+ EMU Variable FVB has been changed.
+
+ @param Event The event that occurred
+ @param Context For EFI compatibility. Not used.
+
+**/
+VOID
+EFIAPI
+EmuVariablesUpdatedCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ DEBUG ((DEBUG_INFO, "EmuVariablesUpdatedCallback\n"));
+ UpdateNvVarsOnFileSystem ();
+}
+
+
+EFI_STATUS
+EFIAPI
+VisitingFileSystemInstance (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ STATIC BOOLEAN ConnectedToFileSystem = FALSE;
+ RETURN_STATUS PcdStatus;
+
+ if (ConnectedToFileSystem) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = ConnectNvVarsToFileSystem (Handle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ConnectedToFileSystem = TRUE;
+ mEmuVariableEvent =
+ EfiCreateProtocolNotifyEvent (
+ &gEfiDevicePathProtocolGuid,
+ TPL_CALLBACK,
+ EmuVariablesUpdatedCallback,
+ NULL,
+ &mEmuVariableEventReg
+ );
+ PcdStatus = PcdSet64S (PcdEmuVariableEvent,
+ (UINT64)(UINTN) mEmuVariableEvent);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ return EFI_SUCCESS;
+}
+
+
+VOID
+PlatformBdsRestoreNvVarsFromHardDisk (
+ )
+{
+ VisitAllPciInstances (ConnectRecursivelyIfPciMassStorage);
+ VisitAllInstancesOfProtocol (
+ &gEfiSimpleFileSystemProtocolGuid,
+ VisitingFileSystemInstance,
+ NULL
+ );
+
+}
+
+/**
+ Connect with predefined platform connect sequence.
+
+ The OEM/IBV can customize with their own connect sequence.
+**/
+VOID
+PlatformBdsConnectSequence (
+ VOID
+ )
+{
+ UINTN Index;
+ RETURN_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "PlatformBdsConnectSequence\n"));
+
+ Index = 0;
+
+ //
+ // Here we can get the customized platform connect sequence
+ // Notes: we can connect with new variable which record the
+ // last time boots connect device path sequence
+ //
+ while (gPlatformConnectSequence[Index] != NULL) {
+ //
+ // Build the platform boot option
+ //
+ EfiBootManagerConnectDevicePath (gPlatformConnectSequence[Index], NULL);
+ Index++;
+ }
+
+ Status = ConnectDevicesFromQemu ();
+ if (RETURN_ERROR (Status)) {
+ //
+ // Just use the simple policy to connect all devices
+ //
+ DEBUG ((DEBUG_INFO, "EfiBootManagerConnectAll\n"));
+ EfiBootManagerConnectAll ();
+ }
+}
+
+/**
+ Save the S3 boot script.
+
+ Note that DxeSmmReadyToLock must be signaled after this function returns;
+ otherwise the script wouldn't be saved actually.
+**/
+STATIC
+VOID
+SaveS3BootScript (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_S3_SAVE_STATE_PROTOCOL *BootScript;
+ STATIC CONST UINT8 Info[] = { 0xDE, 0xAD, 0xBE, 0xEF };
+
+ Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, NULL,
+ (VOID **) &BootScript);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Despite the opcode documentation in the PI spec, the protocol
+ // implementation embeds a deep copy of the info in the boot script, rather
+ // than storing just a pointer to runtime or NVS storage.
+ //
+ Status = BootScript->Write(BootScript, EFI_BOOT_SCRIPT_INFORMATION_OPCODE,
+ (UINT32) sizeof Info,
+ (EFI_PHYSICAL_ADDRESS)(UINTN) &Info);
+ ASSERT_EFI_ERROR (Status);
+}
+
+
+/**
+ Do the platform specific action after the console is ready
+
+ Possible things that can be done in PlatformBootManagerAfterConsole:
+
+ > Console post action:
+ > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
+ > Signal console ready platform customized event
+ > Run diagnostics like memory testing
+ > Connect certain devices
+ > Dispatch aditional option roms
+ > Special boot: e.g.: USB boot, enter UI
+**/
+VOID
+EFIAPI
+PlatformBootManagerAfterConsole (
+ VOID
+ )
+{
+ EFI_BOOT_MODE BootMode;
+
+ DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole\n"));
+
+ if (PcdGetBool (PcdOvmfFlashVariablesEnable)) {
+ DEBUG ((DEBUG_INFO, "PlatformBdsPolicyBehavior: not restoring NvVars "
+ "from disk since flash variables appear to be supported.\n"));
+ } else {
+ //
+ // Try to restore variables from the hard disk early so
+ // they can be used for the other BDS connect operations.
+ //
+ PlatformBdsRestoreNvVarsFromHardDisk ();
+ }
+
+ //
+ // Get current Boot Mode
+ //
+ BootMode = GetBootModeHob ();
+ DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));
+
+ //
+ // Go the different platform policy with different boot mode
+ // Notes: this part code can be change with the table policy
+ //
+ ASSERT (BootMode == BOOT_WITH_FULL_CONFIGURATION);
+
+ //
+ // Logo show
+ //
+ BootLogoEnableLogo ();
+
+ //
+ // Set PCI Interrupt Line registers and ACPI SCI_EN
+ //
+ PciAcpiInitialization ();
+
+ //
+ // Process TPM PPI request
+ //
+ Tcg2PhysicalPresenceLibProcessRequest (NULL);
+
+ //
+ // Process QEMU's -kernel command line option
+ //
+ TryRunningQemuKernel ();
+
+ //
+ // Perform some platform specific connect sequence
+ //
+ PlatformBdsConnectSequence ();
+
+ EfiBootManagerRefreshAllBootOption ();
+
+ //
+ // Register UEFI Shell
+ //
+ PlatformRegisterFvBootOption (
+ &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
+ );
+
+ RemoveStaleFvFileOptions ();
+ SetBootOrderFromQemu ();
+
+ PlatformBmPrintScRegisterHandler ();
+}
+
+/**
+ This notification function is invoked when an instance of the
+ EFI_DEVICE_PATH_PROTOCOL is produced.
+
+ @param Event The event that occurred
+ @param Context For EFI compatibility. Not used.
+
+**/
+VOID
+EFIAPI
+NotifyDevPath (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ EFI_DEVICE_PATH_PROTOCOL *DevPathNode;
+ ATAPI_DEVICE_PATH *Atapi;
+
+ //
+ // Examine all new handles
+ //
+ for (;;) {
+ //
+ // Get the next handle
+ //
+ BufferSize = sizeof (Handle);
+ Status = gBS->LocateHandle (
+ ByRegisterNotify,
+ NULL,
+ mEfiDevPathNotifyReg,
+ &BufferSize,
+ &Handle
+ );
+
+ //
+ // If not found, we're done
+ //
+ if (EFI_NOT_FOUND == Status) {
+ break;
+ }
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ //
+ // Get the DevicePath protocol on that handle
+ //
+ Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevPathNode);
+ ASSERT_EFI_ERROR (Status);
+
+ while (!IsDevicePathEnd (DevPathNode)) {
+ //
+ // Find the handler to dump this device path node
+ //
+ if (
+ (DevicePathType(DevPathNode) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType(DevPathNode) == MSG_ATAPI_DP)
+ ) {
+ Atapi = (ATAPI_DEVICE_PATH*) DevPathNode;
+ PciOr16 (
+ PCI_LIB_ADDRESS (
+ 0,
+ 1,
+ 1,
+ (Atapi->PrimarySecondary == 1) ? 0x42: 0x40
+ ),
+ BIT15
+ );
+ }
+
+ //
+ // Next device path node
+ //
+ DevPathNode = NextDevicePathNode (DevPathNode);
+ }
+ }
+
+ return;
+}
+
+
+VOID
+InstallDevicePathCallback (
+ VOID
+ )
+{
+ DEBUG ((DEBUG_INFO, "Registered NotifyDevPath Event\n"));
+ mEfiDevPathEvent = EfiCreateProtocolNotifyEvent (
+ &gEfiDevicePathProtocolGuid,
+ TPL_CALLBACK,
+ NotifyDevPath,
+ NULL,
+ &mEfiDevPathNotifyReg
+ );
+}
+
+/**
+ This function is called each second during the boot manager waits the
+ timeout.
+
+ @param TimeoutRemain The remaining timeout.
+**/
+VOID
+EFIAPI
+PlatformBootManagerWaitCallback (
+ UINT16 TimeoutRemain
+ )
+{
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;
+ UINT16 TimeoutInitial;
+
+ TimeoutInitial = PcdGet16 (PcdPlatformBootTimeOut);
+
+ //
+ // If PcdPlatformBootTimeOut is set to zero, then we consider
+ // that no progress update should be enacted (since we'd only
+ // ever display a one-shot progress of either 0% or 100%).
+ //
+ if (TimeoutInitial == 0) {
+ return;
+ }
+
+ Black.Raw = 0x00000000;
+ White.Raw = 0x00FFFFFF;
+
+ BootLogoUpdateProgress (
+ White.Pixel,
+ Black.Pixel,
+ L"Start boot option",
+ White.Pixel,
+ (TimeoutInitial - TimeoutRemain) * 100 / TimeoutInitial,
+ 0
+ );
+}
+
+/**
+ The function is called when no boot option could be launched,
+ including platform recovery options and options pointing to applications
+ built into firmware volumes.
+
+ If this function returns, BDS attempts to enter an infinite loop.
+**/
+VOID
+EFIAPI
+PlatformBootManagerUnableToBoot (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
+ UINTN Index;
+
+ //
+ // BootManagerMenu doesn't contain the correct information when return status
+ // is EFI_NOT_FOUND.
+ //
+ Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ //
+ // Normally BdsDxe does not print anything to the system console, but this is
+ // a last resort -- the end-user will likely not see any DEBUG messages
+ // logged in this situation.
+ //
+ // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn
+ // here to see if it makes sense to request and wait for a keypress.
+ //
+ if (gST->ConIn != NULL) {
+ AsciiPrint (
+ "%a: No bootable option or device was found.\n"
+ "%a: Press any key to enter the Boot Manager Menu.\n",
+ gEfiCallerBaseName,
+ gEfiCallerBaseName
+ );
+ Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (Index == 0);
+
+ //
+ // Drain any queued keys.
+ //
+ while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {
+ //
+ // just throw away Key
+ //
+ }
+ }
+
+ for (;;) {
+ EfiBootManagerBoot (&BootManagerMenu);
+ }
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h new file mode 100644 index 000000000..153e21510 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h @@ -0,0 +1,190 @@ +/** @file
+ Platform BDS customizations include file.
+
+ Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+Module Name:
+
+ BdsPlatform.h
+
+Abstract:
+
+ Head file for BDS Platform specific code
+
+**/
+
+#ifndef _PLATFORM_SPECIFIC_BDS_PLATFORM_H_
+#define _PLATFORM_SPECIFIC_BDS_PLATFORM_H_
+
+
+#include <PiDxe.h>
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/Acpi.h>
+#include <IndustryStandard/SmBios.h>
+#include <IndustryStandard/PeImage.h>
+#include <IndustryStandard/Virtio095.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/BootLogoLib.h>
+#include <Library/HobLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/NvVarsFileLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+#include <Library/QemuBootOrderLib.h>
+
+#include <Protocol/Decompress.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/S3SaveState.h>
+#include <Protocol/DxeSmmReadyToLock.h>
+#include <Protocol/LoadedImage.h>
+
+#include <Guid/Acpi.h>
+#include <Guid/SmBios.h>
+#include <Guid/HobList.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/EventGroup.h>
+#include <Guid/DebugAgentGuid.h>
+
+#include <OvmfPlatforms.h>
+
+extern EFI_DEVICE_PATH_PROTOCOL *gPlatformConnectSequence[];
+extern ACPI_HID_DEVICE_PATH gPnpPs2KeyboardDeviceNode;
+extern ACPI_HID_DEVICE_PATH gPnp16550ComPortDeviceNode;
+extern UART_DEVICE_PATH gUartDeviceNode;
+extern VENDOR_DEVICE_PATH gTerminalTypeDeviceNode;
+
+#define PCI_DEVICE_PATH_NODE(Func, Dev) \
+ { \
+ { \
+ HARDWARE_DEVICE_PATH, \
+ HW_PCI_DP, \
+ { \
+ (UINT8) (sizeof (PCI_DEVICE_PATH)), \
+ (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8) \
+ } \
+ }, \
+ (Func), \
+ (Dev) \
+ }
+
+#define PNPID_DEVICE_PATH_NODE(PnpId) \
+ { \
+ { \
+ ACPI_DEVICE_PATH, \
+ ACPI_DP, \
+ { \
+ (UINT8) (sizeof (ACPI_HID_DEVICE_PATH)), \
+ (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8) \
+ }, \
+ }, \
+ EISA_PNP_ID((PnpId)), \
+ 0 \
+ }
+
+#define gPciIsaBridge \
+ PCI_DEVICE_PATH_NODE(0, 0x1f)
+
+#define gP2PBridge \
+ PCI_DEVICE_PATH_NODE(0, 0x1e)
+
+#define gPnpPs2Keyboard \
+ PNPID_DEVICE_PATH_NODE(0x0303)
+
+#define gPnp16550ComPort \
+ PNPID_DEVICE_PATH_NODE(0x0501)
+
+#define gUart \
+ { \
+ { \
+ MESSAGING_DEVICE_PATH, \
+ MSG_UART_DP, \
+ { \
+ (UINT8) (sizeof (UART_DEVICE_PATH)), \
+ (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8) \
+ } \
+ }, \
+ 0, \
+ 115200, \
+ 8, \
+ 1, \
+ 1 \
+ }
+
+#define gPcAnsiTerminal \
+ { \
+ { \
+ MESSAGING_DEVICE_PATH, \
+ MSG_VENDOR_DP, \
+ { \
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)), \
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) \
+ } \
+ }, \
+ DEVICE_PATH_MESSAGING_PC_ANSI \
+ }
+
+#define gEndEntire \
+ { \
+ END_DEVICE_PATH_TYPE, \
+ END_ENTIRE_DEVICE_PATH_SUBTYPE, \
+ { \
+ END_DEVICE_PATH_LENGTH, \
+ 0 \
+ } \
+ }
+
+#define PCI_CLASS_SCC 0x07
+#define PCI_SUBCLASS_SERIAL 0x00
+#define PCI_IF_16550 0x02
+#define IS_PCI_16550SERIAL(_p) IS_CLASS3 (_p, PCI_CLASS_SCC, PCI_SUBCLASS_SERIAL, PCI_IF_16550)
+#define IS_PCI_ISA_PDECODE(_p) IS_CLASS3 (_p, PCI_CLASS_BRIDGE, PCI_CLASS_BRIDGE_ISA_PDECODE, 0)
+
+typedef struct {
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ UINTN ConnectType;
+} PLATFORM_CONSOLE_CONNECT_ENTRY;
+
+#define CONSOLE_OUT BIT0
+#define CONSOLE_IN BIT1
+#define STD_ERROR BIT2
+extern PLATFORM_CONSOLE_CONNECT_ENTRY gPlatformConsole[];
+extern PLATFORM_CONSOLE_CONNECT_ENTRY gXenPlatformConsole[];
+
+//
+// Platform BDS Functions
+//
+
+VOID
+PlatformInitializeConsole (
+ IN PLATFORM_CONSOLE_CONNECT_ENTRY *PlatformConsole
+ );
+
+/**
+ Loads and boots UEFI Linux via the FwCfg interface.
+
+ @retval EFI_NOT_FOUND - The Linux kernel was not found
+
+**/
+EFI_STATUS
+TryRunningQemuKernel (
+ VOID
+ );
+
+#endif // _PLATFORM_SPECIFIC_BDS_PLATFORM_H_
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf new file mode 100644 index 000000000..e470b9a6a --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -0,0 +1,85 @@ +## @file
+# Platform BDS customizations library.
+#
+# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformBootManagerLib
+ FILE_GUID = FB65006C-AC9F-4992-AD80-184B2BDBBD83
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformBootManagerLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ BdsPlatform.c
+ PlatformData.c
+ QemuKernel.c
+ BdsPlatform.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SourceLevelDebugPkg/SourceLevelDebugPkg.dec
+ OvmfPkg/OvmfPkg.dec
+ SecurityPkg/SecurityPkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ BaseMemoryLib
+ DebugLib
+ PcdLib
+ UefiBootManagerLib
+ BootLogoLib
+ DevicePathLib
+ PciLib
+ NvVarsFileLib
+ QemuFwCfgLib
+ QemuFwCfgS3Lib
+ QemuLoadImageLib
+ QemuBootOrderLib
+ ReportStatusCodeLib
+ UefiLib
+ PlatformBmPrintScLib
+ Tcg2PhysicalPresenceLib
+ XenPlatformLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
+ gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits ## CONSUMES
+
+[Pcd.IA32, Pcd.X64]
+ gEfiMdePkgTokenSpaceGuid.PcdFSBClock
+
+[Protocols]
+ gEfiDecompressProtocolGuid
+ gEfiPciRootBridgeIoProtocolGuid
+ gEfiS3SaveStateProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+ gEfiDxeSmmReadyToLockProtocolGuid # PROTOCOL SOMETIMES_PRODUCED
+ gEfiLoadedImageProtocolGuid # PROTOCOL SOMETIMES_PRODUCED
+ gEfiFirmwareVolume2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+
+[Guids]
+ gEfiEndOfDxeEventGroupGuid
+ gEfiGlobalVariableGuid
+ gRootBridgesConnectedEventGroupGuid
+ gUefiShellFileGuid
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformData.c b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformData.c new file mode 100644 index 000000000..2858c3dfd --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformData.c @@ -0,0 +1,213 @@ +/** @file
+ Defined the platform specific device path which will be used by
+ platform Bbd to perform the platform policy connect.
+
+ Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "BdsPlatform.h"
+#include <Guid/QemuRamfb.h>
+#include <Guid/SerialPortLibVendor.h>
+
+//
+// Vendor UART Device Path structure
+//
+#pragma pack (1)
+typedef struct {
+ VENDOR_DEVICE_PATH VendorHardware;
+ UART_DEVICE_PATH Uart;
+ VENDOR_DEVICE_PATH TerminalType;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} VENDOR_UART_DEVICE_PATH;
+#pragma pack ()
+
+//
+// USB Keyboard Device Path structure
+//
+#pragma pack (1)
+typedef struct {
+ USB_CLASS_DEVICE_PATH Keyboard;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} USB_KEYBOARD_DEVICE_PATH;
+#pragma pack ()
+
+//
+// QemuRamfb Device Path structure
+//
+#pragma pack (1)
+typedef struct {
+ VENDOR_DEVICE_PATH Vendor;
+ ACPI_ADR_DEVICE_PATH AcpiAdr;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} VENDOR_RAMFB_DEVICE_PATH;
+#pragma pack ()
+
+ACPI_HID_DEVICE_PATH gPnpPs2KeyboardDeviceNode = gPnpPs2Keyboard;
+ACPI_HID_DEVICE_PATH gPnp16550ComPortDeviceNode = gPnp16550ComPort;
+UART_DEVICE_PATH gUartDeviceNode = gUart;
+VENDOR_DEVICE_PATH gTerminalTypeDeviceNode = gPcAnsiTerminal;
+
+//
+// Platform specific keyboard device path
+//
+
+
+//
+// Debug Agent UART Device Path
+//
+VENDOR_UART_DEVICE_PATH gDebugAgentUartDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ EFI_DEBUG_AGENT_GUID,
+ },
+ {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_UART_DP,
+ {
+ (UINT8) (sizeof (UART_DEVICE_PATH)),
+ (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0, // Reserved
+ 0, // BaudRate - Default
+ 0, // DataBits - Default
+ 0, // Parity - Default
+ 0, // StopBits - Default
+ },
+ gPcAnsiTerminal,
+ gEndEntire
+};
+
+STATIC USB_KEYBOARD_DEVICE_PATH gUsbKeyboardDevicePath = {
+ {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_USB_CLASS_DP,
+ {
+ (UINT8)sizeof (USB_CLASS_DEVICE_PATH),
+ (UINT8)(sizeof (USB_CLASS_DEVICE_PATH) >> 8)
+ }
+ },
+ 0xFFFF, // VendorId: any
+ 0xFFFF, // ProductId: any
+ 3, // DeviceClass: HID
+ 1, // DeviceSubClass: boot
+ 1 // DeviceProtocol: keyboard
+ },
+ gEndEntire
+};
+
+STATIC VENDOR_RAMFB_DEVICE_PATH gQemuRamfbDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ QEMU_RAMFB_GUID,
+ },
+ {
+ {
+ ACPI_DEVICE_PATH,
+ ACPI_ADR_DP,
+ {
+ (UINT8) (sizeof (ACPI_ADR_DEVICE_PATH)),
+ (UINT8) ((sizeof (ACPI_ADR_DEVICE_PATH)) >> 8)
+ }
+ },
+ ACPI_DISPLAY_ADR (
+ 1, // DeviceIdScheme
+ 0, // HeadId
+ 0, // NonVgaOutput
+ 1, // BiosCanDetect
+ 0, // VendorInfo
+ ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL, // Type
+ 0, // Port
+ 0 // Index
+ ),
+ },
+ gEndEntire
+};
+
+STATIC VENDOR_UART_DEVICE_PATH gXenConsoleDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ EDKII_SERIAL_PORT_LIB_VENDOR_GUID
+ },
+ {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_UART_DP,
+ {
+ (UINT8) (sizeof (UART_DEVICE_PATH)),
+ (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0,
+ FixedPcdGet64 (PcdUartDefaultBaudRate),
+ FixedPcdGet8 (PcdUartDefaultDataBits),
+ FixedPcdGet8 (PcdUartDefaultParity),
+ FixedPcdGet8 (PcdUartDefaultStopBits),
+ },
+ gPcAnsiTerminal,
+ gEndEntire
+};
+
+//
+// Predefined platform default console device path
+//
+PLATFORM_CONSOLE_CONNECT_ENTRY gPlatformConsole[] = {
+ {
+ (EFI_DEVICE_PATH_PROTOCOL *) &gDebugAgentUartDevicePath,
+ (CONSOLE_OUT | CONSOLE_IN | STD_ERROR)
+ },
+ {
+ (EFI_DEVICE_PATH_PROTOCOL *)&gUsbKeyboardDevicePath,
+ CONSOLE_IN
+ },
+ {
+ (EFI_DEVICE_PATH_PROTOCOL *)&gQemuRamfbDevicePath,
+ CONSOLE_OUT
+ },
+ {
+ NULL,
+ 0
+ }
+};
+
+PLATFORM_CONSOLE_CONNECT_ENTRY gXenPlatformConsole[] = {
+ {
+ (EFI_DEVICE_PATH_PROTOCOL *)&gXenConsoleDevicePath,
+ (CONSOLE_OUT | CONSOLE_IN | STD_ERROR)
+ },
+ {
+ NULL,
+ 0
+ }
+};
+
+//
+// Predefined platform connect sequence
+//
+EFI_DEVICE_PATH_PROTOCOL *gPlatformConnectSequence[] = { NULL };
+
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c new file mode 100644 index 000000000..c62559217 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c @@ -0,0 +1,50 @@ +/** @file
+
+ Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuLoadImageLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/UefiLib.h>
+
+
+EFI_STATUS
+TryRunningQemuKernel (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE KernelImageHandle;
+
+ Status = QemuLoadKernelImage (&KernelImageHandle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Signal the EVT_SIGNAL_READY_TO_BOOT event
+ //
+ EfiSignalEventReadyToBoot();
+
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE,
+ (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
+
+ //
+ // Start the image.
+ //
+ Status = QemuStartKernelImage (&KernelImageHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: QemuStartKernelImage(): %r\n", __FUNCTION__,
+ Status));
+ }
+
+ QemuUnloadKernelImage (KernelImageHandle);
+
+ return Status;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.c b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.c new file mode 100644 index 000000000..3b94630ac --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.c @@ -0,0 +1,1660 @@ +/** @file
+ Platform BDS customizations.
+
+ Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
+ Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "BdsPlatform.h"
+#include <Guid/RootBridgesConnectedEventGroup.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Library/PlatformBmPrintScLib.h>
+#include <Library/Tcg2PhysicalPresenceLib.h>
+
+#include <Protocol/BlockIo.h>
+
+//
+// Global data
+//
+
+VOID *mEfiDevPathNotifyReg;
+EFI_EVENT mEfiDevPathEvent;
+VOID *mEmuVariableEventReg;
+EFI_EVENT mEmuVariableEvent;
+UINT16 mHostBridgeDevId;
+
+//
+// Table of host IRQs matching PCI IRQs A-D
+// (for configuring PCI Interrupt Line register)
+//
+CONST UINT8 PciHostIrqs[] = {
+ 0x0a, 0x0a, 0x0b, 0x0b
+};
+
+//
+// Type definitions
+//
+
+typedef
+EFI_STATUS
+(EFIAPI *PROTOCOL_INSTANCE_CALLBACK)(
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ );
+
+/**
+ @param[in] Handle - Handle of PCI device instance
+ @param[in] PciIo - PCI IO protocol instance
+ @param[in] Pci - PCI Header register block
+**/
+typedef
+EFI_STATUS
+(EFIAPI *VISIT_PCI_INSTANCE_CALLBACK)(
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN PCI_TYPE00 *Pci
+ );
+
+
+//
+// Function prototypes
+//
+
+EFI_STATUS
+VisitAllInstancesOfProtocol (
+ IN EFI_GUID *Id,
+ IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+VisitAllPciInstancesOfProtocol (
+ IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction
+ );
+
+VOID
+InstallDevicePathCallback (
+ VOID
+ );
+
+VOID
+PlatformRegisterFvBootOption (
+ EFI_GUID *FileGuid,
+ CHAR16 *Description,
+ UINT32 Attributes
+ )
+{
+ EFI_STATUS Status;
+ INTN OptionIndex;
+ EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ Status = gBS->HandleProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **) &LoadedImage
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
+ DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);
+ ASSERT (DevicePath != NULL);
+ DevicePath = AppendDevicePathNode (
+ DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
+ );
+ ASSERT (DevicePath != NULL);
+
+ Status = EfiBootManagerInitializeLoadOption (
+ &NewOption,
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ Attributes,
+ Description,
+ DevicePath,
+ NULL,
+ 0
+ );
+ ASSERT_EFI_ERROR (Status);
+ FreePool (DevicePath);
+
+ BootOptions = EfiBootManagerGetLoadOptions (
+ &BootOptionCount, LoadOptionTypeBoot
+ );
+
+ OptionIndex = EfiBootManagerFindLoadOption (
+ &NewOption, BootOptions, BootOptionCount
+ );
+
+ if (OptionIndex == -1) {
+ Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);
+ ASSERT_EFI_ERROR (Status);
+ }
+ EfiBootManagerFreeLoadOption (&NewOption);
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+}
+
+/**
+ Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options
+ whose device paths do not resolve exactly to an FvFile in the system.
+
+ This removes any boot options that point to binaries built into the firmware
+ and have become stale due to any of the following:
+ - DXEFV's base address or size changed (historical),
+ - DXEFV's FvNameGuid changed,
+ - the FILE_GUID of the pointed-to binary changed,
+ - the referenced binary is no longer built into the firmware.
+
+ EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only
+ avoids exact duplicates.
+**/
+VOID
+RemoveStaleFvFileOptions (
+ VOID
+ )
+{
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+ UINTN Index;
+
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,
+ LoadOptionTypeBoot);
+
+ for (Index = 0; Index < BootOptionCount; ++Index) {
+ EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;
+ EFI_STATUS Status;
+ EFI_HANDLE FvHandle;
+
+ //
+ // If the device path starts with neither MemoryMapped(...) nor Fv(...),
+ // then keep the boot option.
+ //
+ Node1 = BootOptions[Index].FilePath;
+ if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&
+ DevicePathSubType (Node1) == HW_MEMMAP_DP) &&
+ !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {
+ continue;
+ }
+
+ //
+ // If the second device path node is not FvFile(...), then keep the boot
+ // option.
+ //
+ Node2 = NextDevicePathNode (Node1);
+ if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {
+ continue;
+ }
+
+ //
+ // Locate the Firmware Volume2 protocol instance that is denoted by the
+ // boot option. If this lookup fails (i.e., the boot option references a
+ // firmware volume that doesn't exist), then we'll proceed to delete the
+ // boot option.
+ //
+ SearchNode = Node1;
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,
+ &SearchNode, &FvHandle);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // The firmware volume was found; now let's see if it contains the FvFile
+ // identified by GUID.
+ //
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;
+ UINTN BufferSize;
+ EFI_FV_FILETYPE FoundType;
+ EFI_FV_FILE_ATTRIBUTES FileAttributes;
+ UINT32 AuthenticationStatus;
+
+ Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,
+ (VOID **)&FvProtocol);
+ ASSERT_EFI_ERROR (Status);
+
+ FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;
+ //
+ // Buffer==NULL means we request metadata only: BufferSize, FoundType,
+ // FileAttributes.
+ //
+ Status = FvProtocol->ReadFile (
+ FvProtocol,
+ &FvFileNode->FvFileName, // NameGuid
+ NULL, // Buffer
+ &BufferSize,
+ &FoundType,
+ &FileAttributes,
+ &AuthenticationStatus
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // The FvFile was found. Keep the boot option.
+ //
+ continue;
+ }
+ }
+
+ //
+ // Delete the boot option.
+ //
+ Status = EfiBootManagerDeleteLoadOptionVariable (
+ BootOptions[Index].OptionNumber, LoadOptionTypeBoot);
+ DEBUG_CODE (
+ CHAR16 *DevicePathString;
+
+ DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,
+ FALSE, FALSE);
+ DEBUG ((
+ EFI_ERROR (Status) ? EFI_D_WARN : DEBUG_VERBOSE,
+ "%a: removing stale Boot#%04x %s: %r\n",
+ __FUNCTION__,
+ (UINT32)BootOptions[Index].OptionNumber,
+ DevicePathString == NULL ? L"<unavailable>" : DevicePathString,
+ Status
+ ));
+ if (DevicePathString != NULL) {
+ FreePool (DevicePathString);
+ }
+ );
+ }
+
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+}
+
+VOID
+PlatformRegisterOptionsAndKeys (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Enter;
+ EFI_INPUT_KEY F2;
+ EFI_INPUT_KEY Esc;
+ EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
+
+ //
+ // Register ENTER as CONTINUE key
+ //
+ Enter.ScanCode = SCAN_NULL;
+ Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
+ Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Map F2 to Boot Manager Menu
+ //
+ F2.ScanCode = SCAN_F2;
+ F2.UnicodeChar = CHAR_NULL;
+ Esc.ScanCode = SCAN_ESC;
+ Esc.UnicodeChar = CHAR_NULL;
+ Status = EfiBootManagerGetBootManagerMenu (&BootOption);
+ ASSERT_EFI_ERROR (Status);
+ Status = EfiBootManagerAddKeyOptionVariable (
+ NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
+ );
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+ Status = EfiBootManagerAddKeyOptionVariable (
+ NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
+ );
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+}
+
+EFI_STATUS
+EFIAPI
+ConnectRootBridge (
+ IN EFI_HANDLE RootBridgeHandle,
+ IN VOID *Instance,
+ IN VOID *Context
+ );
+
+STATIC
+EFI_STATUS
+EFIAPI
+ConnectVirtioPciRng (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ );
+
+STATIC
+VOID
+SaveS3BootScript (
+ VOID
+ );
+
+//
+// BDS Platform Functions
+//
+/**
+ Do the platform init, can be customized by OEM/IBV
+
+ Possible things that can be done in PlatformBootManagerBeforeConsole:
+
+ > Update console variable: 1. include hot-plug devices;
+ > 2. Clear ConIn and add SOL for AMT
+ > Register new Driver#### or Boot####
+ > Register new Key####: e.g.: F12
+ > Signal ReadyToLock event
+ > Authentication action: 1. connect Auth devices;
+ > 2. Identify auto logon user.
+**/
+VOID
+EFIAPI
+PlatformBootManagerBeforeConsole (
+ VOID
+ )
+{
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "PlatformBootManagerBeforeConsole\n"));
+ InstallDevicePathCallback ();
+
+ VisitAllInstancesOfProtocol (&gEfiPciRootBridgeIoProtocolGuid,
+ ConnectRootBridge, NULL);
+
+ //
+ // Signal the ACPI platform driver that it can download QEMU ACPI tables.
+ //
+ EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);
+
+ //
+ // We can't signal End-of-Dxe earlier than this. Namely, End-of-Dxe triggers
+ // the preparation of S3 system information. That logic has a hard dependency
+ // on the presence of the FACS ACPI table. Since our ACPI tables are only
+ // installed after PCI enumeration completes, we must not trigger the S3 save
+ // earlier, hence we can't signal End-of-Dxe earlier.
+ //
+ EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);
+
+ //
+ // Prevent further changes to LockBoxes or SMRAM.
+ //
+ Handle = NULL;
+ Status = gBS->InstallProtocolInterface (&Handle,
+ &gEfiDxeSmmReadyToLockProtocolGuid, EFI_NATIVE_INTERFACE,
+ NULL);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Dispatch deferred images after EndOfDxe event and ReadyToLock
+ // installation.
+ //
+ EfiBootManagerDispatchDeferredImages ();
+
+ PlatformInitializeConsole (gPlatformConsole);
+
+ PlatformRegisterOptionsAndKeys ();
+
+ //
+ // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL
+ // instances on Virtio PCI RNG devices.
+ //
+ VisitAllInstancesOfProtocol (&gEfiPciIoProtocolGuid, ConnectVirtioPciRng,
+ NULL);
+}
+
+
+EFI_STATUS
+EFIAPI
+ConnectRootBridge (
+ IN EFI_HANDLE RootBridgeHandle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Make the PCI bus driver connect the root bridge, non-recursively. This
+ // will produce a number of child handles with PciIo on them.
+ //
+ Status = gBS->ConnectController (
+ RootBridgeHandle, // ControllerHandle
+ NULL, // DriverImageHandle
+ NULL, // RemainingDevicePath -- produce all
+ // children
+ FALSE // Recursive
+ );
+ return Status;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+ConnectVirtioPciRng (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_STATUS Status;
+ UINT16 VendorId;
+ UINT16 DeviceId;
+ UINT8 RevisionId;
+ BOOLEAN Virtio10;
+ UINT16 SubsystemId;
+
+ PciIo = Instance;
+
+ //
+ // Read and check VendorId.
+ //
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,
+ 1, &VendorId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ if (VendorId != VIRTIO_VENDOR_ID) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Read DeviceId and RevisionId.
+ //
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_DEVICE_ID_OFFSET,
+ 1, &DeviceId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,
+ 1, &RevisionId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // From DeviceId and RevisionId, determine whether the device is a
+ // modern-only Virtio 1.0 device. In case of Virtio 1.0, DeviceId can
+ // immediately be restricted to VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and
+ // SubsystemId will only play a sanity-check role. Otherwise, DeviceId can
+ // only be sanity-checked, and SubsystemId will decide.
+ //
+ if (DeviceId == 0x1040 + VIRTIO_SUBSYSTEM_ENTROPY_SOURCE &&
+ RevisionId >= 0x01) {
+ Virtio10 = TRUE;
+ } else if (DeviceId >= 0x1000 && DeviceId <= 0x103F && RevisionId == 0x00) {
+ Virtio10 = FALSE;
+ } else {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Read and check SubsystemId as dictated by Virtio10.
+ //
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,
+ PCI_SUBSYSTEM_ID_OFFSET, 1, &SubsystemId);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ if ((Virtio10 && SubsystemId >= 0x40) ||
+ (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE)) {
+ Status = gBS->ConnectController (
+ Handle, // ControllerHandle
+ NULL, // DriverImageHandle -- connect all drivers
+ NULL, // RemainingDevicePath -- produce all child handles
+ FALSE // Recursive -- don't follow child handles
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ }
+ return EFI_SUCCESS;
+
+Error:
+ DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));
+ return Status;
+}
+
+
+/**
+ Add IsaKeyboard to ConIn; add IsaSerial to ConOut, ConIn, ErrOut.
+
+ @param[in] DeviceHandle Handle of the LPC Bridge device.
+
+ @retval EFI_SUCCESS Console devices on the LPC bridge have been added to
+ ConOut, ConIn, and ErrOut.
+
+ @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing
+ from DeviceHandle.
+**/
+EFI_STATUS
+PrepareLpcBridgeDevicePath (
+ IN EFI_HANDLE DeviceHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ CHAR16 *DevPathStr;
+
+ DevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ TempDevicePath = DevicePath;
+
+ //
+ // Register Keyboard
+ //
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gPnpPs2KeyboardDeviceNode);
+
+ EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);
+
+ //
+ // Register COM1
+ //
+ DevicePath = TempDevicePath;
+ gPnp16550ComPortDeviceNode.UID = 0;
+
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);
+
+ //
+ // Print Device Path
+ //
+ DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+ if (DevPathStr != NULL) {
+ DEBUG((
+ DEBUG_INFO,
+ "BdsPlatform.c+%d: COM%d DevPath: %s\n",
+ __LINE__,
+ gPnp16550ComPortDeviceNode.UID + 1,
+ DevPathStr
+ ));
+ FreePool(DevPathStr);
+ }
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
+
+ // Don't register COM2 which can be used for DBG instead so keep it clean
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+GetGopDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *PciDevicePath,
+ OUT EFI_DEVICE_PATH_PROTOCOL **GopDevicePath
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+ EFI_HANDLE PciDeviceHandle;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempPciDevicePath;
+ UINTN GopHandleCount;
+ EFI_HANDLE *GopHandleBuffer;
+
+ if (PciDevicePath == NULL || GopDevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Initialize the GopDevicePath to be PciDevicePath
+ //
+ *GopDevicePath = PciDevicePath;
+ TempPciDevicePath = PciDevicePath;
+
+ Status = gBS->LocateDevicePath (
+ &gEfiDevicePathProtocolGuid,
+ &TempPciDevicePath,
+ &PciDeviceHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Try to connect this handle, so that GOP driver could start on this
+ // device and create child handles with GraphicsOutput Protocol installed
+ // on them, then we get device paths of these child handles and select
+ // them as possible console device.
+ //
+ gBS->ConnectController (PciDeviceHandle, NULL, NULL, FALSE);
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiGraphicsOutputProtocolGuid,
+ NULL,
+ &GopHandleCount,
+ &GopHandleBuffer
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Add all the child handles as possible Console Device
+ //
+ for (Index = 0; Index < GopHandleCount; Index++) {
+ Status = gBS->HandleProtocol (GopHandleBuffer[Index],
+ &gEfiDevicePathProtocolGuid, (VOID*)&TempDevicePath);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ if (CompareMem (
+ PciDevicePath,
+ TempDevicePath,
+ GetDevicePathSize (PciDevicePath) - END_DEVICE_PATH_LENGTH
+ ) == 0) {
+ //
+ // In current implementation, we only enable one of the child handles
+ // as console device, i.e. sotre one of the child handle's device
+ // path to variable "ConOut"
+ // In future, we could select all child handles to be console device
+ //
+
+ *GopDevicePath = TempDevicePath;
+
+ //
+ // Delete the PCI device's path that added by
+ // GetPlugInPciVgaDevicePath(). Add the integrity GOP device path.
+ //
+ EfiBootManagerUpdateConsoleVariable (ConOutDev, NULL, PciDevicePath);
+ EfiBootManagerUpdateConsoleVariable (ConOutDev, TempDevicePath, NULL);
+ }
+ }
+ gBS->FreePool (GopHandleBuffer);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Add PCI display to ConOut.
+
+ @param[in] DeviceHandle Handle of the PCI display device.
+
+ @retval EFI_SUCCESS The PCI display device has been added to ConOut.
+
+ @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing
+ from DeviceHandle.
+**/
+EFI_STATUS
+PreparePciDisplayDevicePath (
+ IN EFI_HANDLE DeviceHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;
+
+ DevicePath = NULL;
+ GopDevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ GetGopDevicePath (DevicePath, &GopDevicePath);
+ DevicePath = GopDevicePath;
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Add PCI Serial to ConOut, ConIn, ErrOut.
+
+ @param[in] DeviceHandle Handle of the PCI serial device.
+
+ @retval EFI_SUCCESS The PCI serial device has been added to ConOut, ConIn,
+ ErrOut.
+
+ @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing
+ from DeviceHandle.
+**/
+EFI_STATUS
+PreparePciSerialDevicePath (
+ IN EFI_HANDLE DeviceHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ DevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);
+ DevicePath = AppendDevicePathNode (DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+VisitAllInstancesOfProtocol (
+ IN EFI_GUID *Id,
+ IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+ VOID *Instance;
+
+ //
+ // Start to check all the PciIo to find all possible device
+ //
+ HandleCount = 0;
+ HandleBuffer = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ Id,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (HandleBuffer[Index], Id, &Instance);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ Status = (*CallBackFunction) (
+ HandleBuffer[Index],
+ Instance,
+ Context
+ );
+ }
+
+ gBS->FreePool (HandleBuffer);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+VisitingAPciInstance (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ PciIo = (EFI_PCI_IO_PROTOCOL*) Instance;
+
+ //
+ // Check for all PCI device
+ //
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint32,
+ 0,
+ sizeof (Pci) / sizeof (UINT32),
+ &Pci
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return (*(VISIT_PCI_INSTANCE_CALLBACK)(UINTN) Context) (
+ Handle,
+ PciIo,
+ &Pci
+ );
+
+}
+
+
+
+EFI_STATUS
+VisitAllPciInstances (
+ IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction
+ )
+{
+ return VisitAllInstancesOfProtocol (
+ &gEfiPciIoProtocolGuid,
+ VisitingAPciInstance,
+ (VOID*)(UINTN) CallBackFunction
+ );
+}
+
+
+/**
+ Do platform specific PCI Device check and add them to
+ ConOut, ConIn, ErrOut.
+
+ @param[in] Handle - Handle of PCI device instance
+ @param[in] PciIo - PCI IO protocol instance
+ @param[in] Pci - PCI Header register block
+
+ @retval EFI_SUCCESS - PCI Device check and Console variable update
+ successfully.
+ @retval EFI_STATUS - PCI Device check or Console variable update fail.
+
+**/
+EFI_STATUS
+EFIAPI
+DetectAndPreparePlatformPciDevicePath (
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN PCI_TYPE00 *Pci
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_DEVICE_ENABLE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Here we decide whether it is LPC Bridge
+ //
+ if ((IS_PCI_LPC (Pci)) ||
+ ((IS_PCI_ISA_PDECODE (Pci)) &&
+ (Pci->Hdr.VendorId == 0x8086) &&
+ (Pci->Hdr.DeviceId == 0x7000)
+ )
+ ) {
+ //
+ // Add IsaKeyboard to ConIn,
+ // add IsaSerial to ConOut, ConIn, ErrOut
+ //
+ DEBUG ((DEBUG_INFO, "Found LPC Bridge device\n"));
+ PrepareLpcBridgeDevicePath (Handle);
+ return EFI_SUCCESS;
+ }
+ //
+ // Here we decide which Serial device to enable in PCI bus
+ //
+ if (IS_PCI_16550SERIAL (Pci)) {
+ //
+ // Add them to ConOut, ConIn, ErrOut.
+ //
+ DEBUG ((DEBUG_INFO, "Found PCI 16550 SERIAL device\n"));
+ PreparePciSerialDevicePath (Handle);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Here we decide which display device to enable in PCI bus
+ //
+ if (IS_PCI_DISPLAY (Pci)) {
+ //
+ // Add them to ConOut.
+ //
+ DEBUG ((DEBUG_INFO, "Found PCI display device\n"));
+ PreparePciDisplayDevicePath (Handle);
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+/**
+ Connect the predefined platform default console device.
+
+ Always try to find and enable PCI display devices.
+
+ @param[in] PlatformConsole Predefined platform default console device array.
+**/
+VOID
+PlatformInitializeConsole (
+ IN PLATFORM_CONSOLE_CONNECT_ENTRY *PlatformConsole
+ )
+{
+ UINTN Index;
+
+ //
+ // Do platform specific PCI Device check and add them to ConOut, ConIn,
+ // ErrOut
+ //
+ VisitAllPciInstances (DetectAndPreparePlatformPciDevicePath);
+
+ //
+ // Have chance to connect the platform default console,
+ // the platform default console is the minimum device group
+ // the platform should support
+ //
+ for (Index = 0; PlatformConsole[Index].DevicePath != NULL; ++Index) {
+ //
+ // Update the console variable with the connect type
+ //
+ if ((PlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) {
+ EfiBootManagerUpdateConsoleVariable (ConIn,
+ PlatformConsole[Index].DevicePath, NULL);
+ }
+ if ((PlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) {
+ EfiBootManagerUpdateConsoleVariable (ConOut,
+ PlatformConsole[Index].DevicePath, NULL);
+ }
+ if ((PlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) {
+ EfiBootManagerUpdateConsoleVariable (ErrOut,
+ PlatformConsole[Index].DevicePath, NULL);
+ }
+ }
+}
+
+
+/**
+ Configure PCI Interrupt Line register for applicable devices
+ Ported from SeaBIOS, src/fw/pciinit.c, *_pci_slot_get_irq()
+
+ @param[in] Handle - Handle of PCI device instance
+ @param[in] PciIo - PCI IO protocol instance
+ @param[in] PciHdr - PCI Header register block
+
+ @retval EFI_SUCCESS - PCI Interrupt Line register configured successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+SetPciIntLine (
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN PCI_TYPE00 *PciHdr
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevPathNode;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ UINTN RootSlot;
+ UINTN Idx;
+ UINT8 IrqLine;
+ EFI_STATUS Status;
+ UINT32 RootBusNumber;
+
+ Status = EFI_SUCCESS;
+
+ if (PciHdr->Device.InterruptPin != 0) {
+
+ DevPathNode = DevicePathFromHandle (Handle);
+ ASSERT (DevPathNode != NULL);
+ DevPath = DevPathNode;
+
+ RootBusNumber = 0;
+ if (DevicePathType (DevPathNode) == ACPI_DEVICE_PATH &&
+ DevicePathSubType (DevPathNode) == ACPI_DP &&
+ ((ACPI_HID_DEVICE_PATH *)DevPathNode)->HID == EISA_PNP_ID(0x0A03)) {
+ RootBusNumber = ((ACPI_HID_DEVICE_PATH *)DevPathNode)->UID;
+ }
+
+ //
+ // Compute index into PciHostIrqs[] table by walking
+ // the device path and adding up all device numbers
+ //
+ Status = EFI_NOT_FOUND;
+ RootSlot = 0;
+ Idx = PciHdr->Device.InterruptPin - 1;
+ while (!IsDevicePathEnd (DevPathNode)) {
+ if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH &&
+ DevicePathSubType (DevPathNode) == HW_PCI_DP) {
+
+ Idx += ((PCI_DEVICE_PATH *)DevPathNode)->Device;
+
+ //
+ // Unlike SeaBIOS, which starts climbing from the leaf device
+ // up toward the root, we traverse the device path starting at
+ // the root moving toward the leaf node.
+ // The slot number of the top-level parent bridge is needed for
+ // Q35 cases with more than 24 slots on the root bus.
+ //
+ if (Status != EFI_SUCCESS) {
+ Status = EFI_SUCCESS;
+ RootSlot = ((PCI_DEVICE_PATH *)DevPathNode)->Device;
+ }
+ }
+
+ DevPathNode = NextDevicePathNode (DevPathNode);
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (RootBusNumber == 0 && RootSlot == 0) {
+ DEBUG((
+ DEBUG_ERROR,
+ "%a: PCI host bridge (00:00.0) should have no interrupts!\n",
+ __FUNCTION__
+ ));
+ ASSERT (FALSE);
+ }
+
+ //
+ // Final PciHostIrqs[] index calculation depends on the platform
+ // and should match SeaBIOS src/fw/pciinit.c *_pci_slot_get_irq()
+ //
+ switch (mHostBridgeDevId) {
+ case 0x1275: // BHYVE
+ case INTEL_82441_DEVICE_ID:
+ Idx -= 1;
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ //
+ // SeaBIOS contains the following comment:
+ // "Slots 0-24 rotate slot:pin mapping similar to piix above, but
+ // with a different starting index - see q35-acpi-dsdt.dsl.
+ //
+ // Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)"
+ //
+ if (RootSlot > 24) {
+ //
+ // in this case, subtract back out RootSlot from Idx
+ // (SeaBIOS never adds it to begin with, but that would make our
+ // device path traversal loop above too awkward)
+ //
+ Idx -= RootSlot;
+ }
+ break;
+ default:
+ ASSERT (FALSE); // should never get here
+ }
+ Idx %= ARRAY_SIZE (PciHostIrqs);
+ IrqLine = PciHostIrqs[Idx];
+
+ DEBUG_CODE_BEGIN ();
+ {
+ CHAR16 *DevPathString;
+ STATIC CHAR16 Fallback[] = L"<failed to convert>";
+ UINTN Segment, Bus, Device, Function;
+
+ DevPathString = ConvertDevicePathToText (DevPath, FALSE, FALSE);
+ if (DevPathString == NULL) {
+ DevPathString = Fallback;
+ }
+ Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
+ ASSERT_EFI_ERROR (Status);
+
+ DEBUG ((DEBUG_VERBOSE, "%a: [%02x:%02x.%x] %s -> 0x%02x\n", __FUNCTION__,
+ (UINT32)Bus, (UINT32)Device, (UINT32)Function, DevPathString,
+ IrqLine));
+
+ if (DevPathString != Fallback) {
+ FreePool (DevPathString);
+ }
+ }
+ DEBUG_CODE_END ();
+
+ //
+ // Set PCI Interrupt Line register for this device to PciHostIrqs[Idx]
+ //
+ Status = PciIo->Pci.Write (
+ PciIo,
+ EfiPciIoWidthUint8,
+ PCI_INT_LINE_OFFSET,
+ 1,
+ &IrqLine
+ );
+ }
+
+ return Status;
+}
+
+
+VOID
+PciAcpiInitialization (
+ )
+{
+ UINTN Pmba;
+
+ //
+ // Query Host Bridge DID to determine platform type
+ //
+ mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
+ switch (mHostBridgeDevId) {
+ case 0x1275: // BHYVE
+ case INTEL_82441_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
+ //
+ // 00:01.0 ISA Bridge (PIIX4) LNK routing targets
+ //
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // A
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // B
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // C
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // D
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
+ //
+ // 00:1f.0 LPC Bridge (Q35) LNK routing targets
+ //
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), 0x0a); // A
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), 0x0a); // B
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), 0x0b); // C
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), 0x0b); // D
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), 0x0a); // E
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), 0x0a); // F
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), 0x0b); // G
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), 0x0b); // H
+ break;
+ default:
+ DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
+ __FUNCTION__, mHostBridgeDevId));
+ ASSERT (FALSE);
+ return;
+ }
+
+ //
+ // Initialize PCI_INTERRUPT_LINE for applicable present PCI devices
+ //
+ VisitAllPciInstances (SetPciIntLine);
+
+ //
+ // Set ACPI SCI_EN bit in PMCNTRL
+ //
+ IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);
+}
+
+EFI_STATUS
+EFIAPI
+ConnectRecursivelyIfPciMassStorage (
+ IN EFI_HANDLE Handle,
+ IN EFI_PCI_IO_PROTOCOL *Instance,
+ IN PCI_TYPE00 *PciHeader
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ CHAR16 *DevPathStr;
+
+ //
+ // Recognize PCI Mass Storage, and Xen PCI devices
+ //
+ if (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE)) {
+ DevicePath = NULL;
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID*)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Print Device Path
+ //
+ DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+ if (DevPathStr != NULL) {
+ DEBUG((
+ DEBUG_INFO,
+ "Found %s device: %s\n",
+ (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ?
+ L"Mass Storage" :
+ L"Xen"
+ ),
+ DevPathStr
+ ));
+ FreePool(DevPathStr);
+ }
+
+ Status = gBS->ConnectController (Handle, NULL, NULL, TRUE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This notification function is invoked when the
+ EMU Variable FVB has been changed.
+
+ @param Event The event that occurred
+ @param Context For EFI compatibility. Not used.
+
+**/
+VOID
+EFIAPI
+EmuVariablesUpdatedCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ DEBUG ((DEBUG_INFO, "EmuVariablesUpdatedCallback\n"));
+ UpdateNvVarsOnFileSystem ();
+}
+
+
+EFI_STATUS
+EFIAPI
+VisitingFileSystemInstance (
+ IN EFI_HANDLE Handle,
+ IN VOID *Instance,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ STATIC BOOLEAN ConnectedToFileSystem = FALSE;
+ RETURN_STATUS PcdStatus;
+
+ if (ConnectedToFileSystem) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = ConnectNvVarsToFileSystem (Handle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ConnectedToFileSystem = TRUE;
+ mEmuVariableEvent =
+ EfiCreateProtocolNotifyEvent (
+ &gEfiDevicePathProtocolGuid,
+ TPL_CALLBACK,
+ EmuVariablesUpdatedCallback,
+ NULL,
+ &mEmuVariableEventReg
+ );
+ PcdStatus = PcdSet64S (PcdEmuVariableEvent,
+ (UINT64)(UINTN) mEmuVariableEvent);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ return EFI_SUCCESS;
+}
+
+
+VOID
+PlatformBdsRestoreNvVarsFromHardDisk (
+ )
+{
+ VisitAllPciInstances (ConnectRecursivelyIfPciMassStorage);
+ VisitAllInstancesOfProtocol (
+ &gEfiSimpleFileSystemProtocolGuid,
+ VisitingFileSystemInstance,
+ NULL
+ );
+
+}
+
+/**
+ Connect with predefined platform connect sequence.
+
+ The OEM/IBV can customize with their own connect sequence.
+**/
+VOID
+PlatformBdsConnectSequence (
+ VOID
+ )
+{
+ UINTN Index;
+
+ DEBUG ((DEBUG_INFO, "PlatformBdsConnectSequence\n"));
+
+ Index = 0;
+
+ //
+ // Here we can get the customized platform connect sequence
+ // Notes: we can connect with new variable which record the
+ // last time boots connect device path sequence
+ //
+ while (gPlatformConnectSequence[Index] != NULL) {
+ //
+ // Build the platform boot option
+ //
+ EfiBootManagerConnectDevicePath (gPlatformConnectSequence[Index], NULL);
+ Index++;
+ }
+
+ //
+ // Just use the simple policy to connect all devices
+ //
+ DEBUG ((DEBUG_INFO, "EfiBootManagerConnectAll\n"));
+ EfiBootManagerConnectAll ();
+}
+
+/**
+ Save the S3 boot script.
+
+ Note that DxeSmmReadyToLock must be signaled after this function returns;
+ otherwise the script wouldn't be saved actually.
+**/
+#if defined(__GNUC__)
+__attribute__((unused))
+#endif
+STATIC
+VOID
+SaveS3BootScript (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_S3_SAVE_STATE_PROTOCOL *BootScript;
+ STATIC CONST UINT8 Info[] = { 0xDE, 0xAD, 0xBE, 0xEF };
+
+ Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, NULL,
+ (VOID **) &BootScript);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Despite the opcode documentation in the PI spec, the protocol
+ // implementation embeds a deep copy of the info in the boot script, rather
+ // than storing just a pointer to runtime or NVS storage.
+ //
+ Status = BootScript->Write(BootScript, EFI_BOOT_SCRIPT_INFORMATION_OPCODE,
+ (UINT32) sizeof Info,
+ (EFI_PHYSICAL_ADDRESS)(UINTN) &Info);
+ ASSERT_EFI_ERROR (Status);
+}
+
+
+/**
+ Do the platform specific action after the console is ready
+
+ Possible things that can be done in PlatformBootManagerAfterConsole:
+
+ > Console post action:
+ > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
+ > Signal console ready platform customized event
+ > Run diagnostics like memory testing
+ > Connect certain devices
+ > Dispatch aditional option roms
+ > Special boot: e.g.: USB boot, enter UI
+**/
+VOID
+EFIAPI
+PlatformBootManagerAfterConsole (
+ VOID
+ )
+{
+ EFI_BOOT_MODE BootMode;
+
+ DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole\n"));
+
+ if (PcdGetBool (PcdOvmfFlashVariablesEnable)) {
+ DEBUG ((DEBUG_INFO, "PlatformBdsPolicyBehavior: not restoring NvVars "
+ "from disk since flash variables appear to be supported.\n"));
+ } else {
+ //
+ // Try to restore variables from the hard disk early so
+ // they can be used for the other BDS connect operations.
+ //
+ /* XXX Calling this causes Keyboard to be removed from ConIn which
+ results in unresponsive guest boot loaders in the GUI. Restore it
+ when we figure out what is needed to get NvVars storage done
+ properly.
+ */
+ /*PlatformBdsRestoreNvVarsFromHardDisk ();*/
+ }
+
+ //
+ // Get current Boot Mode
+ //
+ BootMode = GetBootModeHob ();
+ DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));
+
+ //
+ // Go the different platform policy with different boot mode
+ // Notes: this part code can be change with the table policy
+ //
+ ASSERT (BootMode == BOOT_WITH_FULL_CONFIGURATION);
+
+ //
+ // Logo show
+ //
+ BootLogoEnableLogo ();
+
+ //
+ // Set PCI Interrupt Line registers and ACPI SCI_EN
+ //
+ PciAcpiInitialization ();
+
+ //
+ // Process TPM PPI request
+ //
+ Tcg2PhysicalPresenceLibProcessRequest (NULL);
+
+ //
+ // Perform some platform specific connect sequence
+ //
+ PlatformBdsConnectSequence ();
+
+ EfiBootManagerRefreshAllBootOption ();
+
+ //
+ // Register UEFI Shell
+ //
+ PlatformRegisterFvBootOption (
+ &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
+ );
+
+ RemoveStaleFvFileOptions ();
+
+ PlatformBmPrintScRegisterHandler ();
+}
+
+/**
+ This notification function is invoked when an instance of the
+ EFI_DEVICE_PATH_PROTOCOL is produced.
+
+ @param Event The event that occurred
+ @param Context For EFI compatibility. Not used.
+
+**/
+VOID
+EFIAPI
+NotifyDevPath (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ EFI_DEVICE_PATH_PROTOCOL *DevPathNode;
+ ATAPI_DEVICE_PATH *Atapi;
+
+ //
+ // Examine all new handles
+ //
+ for (;;) {
+ //
+ // Get the next handle
+ //
+ BufferSize = sizeof (Handle);
+ Status = gBS->LocateHandle (
+ ByRegisterNotify,
+ NULL,
+ mEfiDevPathNotifyReg,
+ &BufferSize,
+ &Handle
+ );
+
+ //
+ // If not found, we're done
+ //
+ if (EFI_NOT_FOUND == Status) {
+ break;
+ }
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ //
+ // Get the DevicePath protocol on that handle
+ //
+ Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevPathNode);
+ ASSERT_EFI_ERROR (Status);
+
+ while (!IsDevicePathEnd (DevPathNode)) {
+ //
+ // Find the handler to dump this device path node
+ //
+ if (
+ (DevicePathType(DevPathNode) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType(DevPathNode) == MSG_ATAPI_DP)
+ ) {
+ Atapi = (ATAPI_DEVICE_PATH*) DevPathNode;
+ PciOr16 (
+ PCI_LIB_ADDRESS (
+ 0,
+ 1,
+ 1,
+ (Atapi->PrimarySecondary == 1) ? 0x42: 0x40
+ ),
+ BIT15
+ );
+ }
+
+ //
+ // Next device path node
+ //
+ DevPathNode = NextDevicePathNode (DevPathNode);
+ }
+ }
+
+ return;
+}
+
+
+VOID
+InstallDevicePathCallback (
+ VOID
+ )
+{
+ DEBUG ((DEBUG_INFO, "Registered NotifyDevPath Event\n"));
+ mEfiDevPathEvent = EfiCreateProtocolNotifyEvent (
+ &gEfiDevicePathProtocolGuid,
+ TPL_CALLBACK,
+ NotifyDevPath,
+ NULL,
+ &mEfiDevPathNotifyReg
+ );
+}
+
+/**
+ This function is called each second during the boot manager waits the
+ timeout.
+
+ @param TimeoutRemain The remaining timeout.
+**/
+VOID
+EFIAPI
+PlatformBootManagerWaitCallback (
+ UINT16 TimeoutRemain
+ )
+{
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;
+ UINT16 Timeout;
+
+ Timeout = PcdGet16 (PcdPlatformBootTimeOut);
+
+ Black.Raw = 0x00000000;
+ White.Raw = 0x00FFFFFF;
+
+ BootLogoUpdateProgress (
+ White.Pixel,
+ Black.Pixel,
+ L"Start boot option",
+ White.Pixel,
+ (Timeout - TimeoutRemain) * 100 / Timeout,
+ 0
+ );
+}
+
+/**
+ The function is called when no boot option could be launched,
+ including platform recovery options and options pointing to applications
+ built into firmware volumes.
+
+ If this function returns, BDS attempts to enter an infinite loop.
+**/
+VOID
+EFIAPI
+PlatformBootManagerUnableToBoot (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
+ UINTN Index;
+
+ //
+ // BootManagerMenu doesn't contain the correct information when return status
+ // is EFI_NOT_FOUND.
+ //
+ Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+ //
+ // Normally BdsDxe does not print anything to the system console, but this is
+ // a last resort -- the end-user will likely not see any DEBUG messages
+ // logged in this situation.
+ //
+ // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn
+ // here to see if it makes sense to request and wait for a keypress.
+ //
+ if (gST->ConIn != NULL) {
+ AsciiPrint (
+ "%a: No bootable option or device was found.\n"
+ "%a: Press any key to enter the Boot Manager Menu.\n",
+ gEfiCallerBaseName,
+ gEfiCallerBaseName
+ );
+ Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (Index == 0);
+
+ //
+ // Drain any queued keys.
+ //
+ while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {
+ //
+ // just throw away Key
+ //
+ }
+ }
+
+ for (;;) {
+ EfiBootManagerBoot (&BootManagerMenu);
+ }
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.h b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.h new file mode 100644 index 000000000..382fe191a --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.h @@ -0,0 +1,190 @@ +/** @file
+ Platform BDS customizations include file.
+
+ Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
+ Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+Module Name:
+
+ BdsPlatform.h
+
+Abstract:
+
+ Head file for BDS Platform specific code
+
+**/
+
+#ifndef _PLATFORM_SPECIFIC_BDS_PLATFORM_H_
+#define _PLATFORM_SPECIFIC_BDS_PLATFORM_H_
+
+
+#include <PiDxe.h>
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/Acpi.h>
+#include <IndustryStandard/SmBios.h>
+#include <IndustryStandard/PeImage.h>
+#include <IndustryStandard/Virtio095.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/BootLogoLib.h>
+#include <Library/HobLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/NvVarsFileLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+#include <Library/QemuBootOrderLib.h>
+
+#include <Protocol/Decompress.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/S3SaveState.h>
+#include <Protocol/DxeSmmReadyToLock.h>
+#include <Protocol/LoadedImage.h>
+
+#include <Guid/Acpi.h>
+#include <Guid/SmBios.h>
+#include <Guid/HobList.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/EventGroup.h>
+#include <Guid/DebugAgentGuid.h>
+
+#include <OvmfPlatforms.h>
+
+extern EFI_DEVICE_PATH_PROTOCOL *gPlatformConnectSequence[];
+extern ACPI_HID_DEVICE_PATH gPnpPs2KeyboardDeviceNode;
+extern ACPI_HID_DEVICE_PATH gPnp16550ComPortDeviceNode;
+extern UART_DEVICE_PATH gUartDeviceNode;
+extern VENDOR_DEVICE_PATH gTerminalTypeDeviceNode;
+
+#define PCI_DEVICE_PATH_NODE(Func, Dev) \
+ { \
+ { \
+ HARDWARE_DEVICE_PATH, \
+ HW_PCI_DP, \
+ { \
+ (UINT8) (sizeof (PCI_DEVICE_PATH)), \
+ (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8) \
+ } \
+ }, \
+ (Func), \
+ (Dev) \
+ }
+
+#define PNPID_DEVICE_PATH_NODE(PnpId) \
+ { \
+ { \
+ ACPI_DEVICE_PATH, \
+ ACPI_DP, \
+ { \
+ (UINT8) (sizeof (ACPI_HID_DEVICE_PATH)), \
+ (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8) \
+ }, \
+ }, \
+ EISA_PNP_ID((PnpId)), \
+ 0 \
+ }
+
+#define gPciIsaBridge \
+ PCI_DEVICE_PATH_NODE(0, 0x1f)
+
+#define gP2PBridge \
+ PCI_DEVICE_PATH_NODE(0, 0x1e)
+
+#define gPnpPs2Keyboard \
+ PNPID_DEVICE_PATH_NODE(0x0303)
+
+#define gPnp16550ComPort \
+ PNPID_DEVICE_PATH_NODE(0x0501)
+
+#define gUart \
+ { \
+ { \
+ MESSAGING_DEVICE_PATH, \
+ MSG_UART_DP, \
+ { \
+ (UINT8) (sizeof (UART_DEVICE_PATH)), \
+ (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8) \
+ } \
+ }, \
+ 0, \
+ 115200, \
+ 8, \
+ 1, \
+ 1 \
+ }
+
+#define gPcAnsiTerminal \
+ { \
+ { \
+ MESSAGING_DEVICE_PATH, \
+ MSG_VENDOR_DP, \
+ { \
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)), \
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) \
+ } \
+ }, \
+ DEVICE_PATH_MESSAGING_PC_ANSI \
+ }
+
+#define gEndEntire \
+ { \
+ END_DEVICE_PATH_TYPE, \
+ END_ENTIRE_DEVICE_PATH_SUBTYPE, \
+ { \
+ END_DEVICE_PATH_LENGTH, \
+ 0 \
+ } \
+ }
+
+#define PCI_CLASS_SCC 0x07
+#define PCI_SUBCLASS_SERIAL 0x00
+#define PCI_IF_16550 0x02
+#define IS_PCI_16550SERIAL(_p) IS_CLASS3 (_p, PCI_CLASS_SCC, PCI_SUBCLASS_SERIAL, PCI_IF_16550)
+#define IS_PCI_ISA_PDECODE(_p) IS_CLASS3 (_p, PCI_CLASS_BRIDGE, PCI_CLASS_BRIDGE_ISA_PDECODE, 0)
+
+typedef struct {
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ UINTN ConnectType;
+} PLATFORM_CONSOLE_CONNECT_ENTRY;
+
+#define CONSOLE_OUT BIT0
+#define CONSOLE_IN BIT1
+#define STD_ERROR BIT2
+extern PLATFORM_CONSOLE_CONNECT_ENTRY gPlatformConsole[];
+
+//
+// Platform BDS Functions
+//
+
+VOID
+PlatformInitializeConsole (
+ IN PLATFORM_CONSOLE_CONNECT_ENTRY *PlatformConsole
+ );
+
+/**
+ Loads and boots UEFI Linux via the FwCfg interface.
+
+ @retval EFI_NOT_FOUND - The Linux kernel was not found
+
+**/
+EFI_STATUS
+TryRunningQemuKernel (
+ VOID
+ );
+
+#endif // _PLATFORM_SPECIFIC_BDS_PLATFORM_H_
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformBootManagerLibBhyve.inf b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformBootManagerLibBhyve.inf new file mode 100644 index 000000000..c1fb5119e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformBootManagerLibBhyve.inf @@ -0,0 +1,75 @@ +## @file
+# Platform BDS customizations library.
+#
+# Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformBootManagerLibBhyve
+ FILE_GUID = b0de5f0d-f676-42da-8d00-86997ee0dbb4
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformBootManagerLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ BdsPlatform.c
+ PlatformData.c
+ BdsPlatform.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SourceLevelDebugPkg/SourceLevelDebugPkg.dec
+ OvmfPkg/OvmfPkg.dec
+ SecurityPkg/SecurityPkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ BaseMemoryLib
+ DebugLib
+ PcdLib
+ UefiBootManagerLib
+ BootLogoLib
+ DevicePathLib
+ PciLib
+ NvVarsFileLib
+ ReportStatusCodeLib
+ UefiLib
+ PlatformBmPrintScLib
+ Tcg2PhysicalPresenceLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
+ gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
+
+[Pcd.IA32, Pcd.X64]
+ gEfiMdePkgTokenSpaceGuid.PcdFSBClock
+
+[Protocols]
+ gEfiDecompressProtocolGuid
+ gEfiPciRootBridgeIoProtocolGuid
+ gEfiS3SaveStateProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+ gEfiDxeSmmReadyToLockProtocolGuid # PROTOCOL SOMETIMES_PRODUCED
+ gEfiLoadedImageProtocolGuid # PROTOCOL SOMETIMES_PRODUCED
+ gEfiFirmwareVolume2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+
+[Guids]
+ gEfiEndOfDxeEventGroupGuid
+ gRootBridgesConnectedEventGroupGuid
+ gUefiShellFileGuid
diff --git a/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformData.c b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformData.c new file mode 100644 index 000000000..73674d536 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformData.c @@ -0,0 +1,171 @@ +/** @file
+ Defined the platform specific device path which will be used by
+ platform Bbd to perform the platform policy connect.
+
+ Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
+ Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "BdsPlatform.h"
+#include <Guid/QemuRamfb.h>
+
+//
+// Debug Agent UART Device Path structure
+//
+#pragma pack(1)
+typedef struct {
+ VENDOR_DEVICE_PATH VendorHardware;
+ UART_DEVICE_PATH Uart;
+ VENDOR_DEVICE_PATH TerminalType;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} VENDOR_UART_DEVICE_PATH;
+#pragma pack()
+
+//
+// USB Keyboard Device Path structure
+//
+#pragma pack (1)
+typedef struct {
+ USB_CLASS_DEVICE_PATH Keyboard;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} USB_KEYBOARD_DEVICE_PATH;
+#pragma pack ()
+
+//
+// QemuRamfb Device Path structure
+//
+#pragma pack (1)
+typedef struct {
+ VENDOR_DEVICE_PATH Vendor;
+ ACPI_ADR_DEVICE_PATH AcpiAdr;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} VENDOR_RAMFB_DEVICE_PATH;
+#pragma pack ()
+
+ACPI_HID_DEVICE_PATH gPnpPs2KeyboardDeviceNode = gPnpPs2Keyboard;
+ACPI_HID_DEVICE_PATH gPnp16550ComPortDeviceNode = gPnp16550ComPort;
+UART_DEVICE_PATH gUartDeviceNode = gUart;
+VENDOR_DEVICE_PATH gTerminalTypeDeviceNode = gPcAnsiTerminal;
+
+//
+// Platform specific keyboard device path
+//
+
+
+//
+// Debug Agent UART Device Path
+//
+VENDOR_UART_DEVICE_PATH gDebugAgentUartDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ EFI_DEBUG_AGENT_GUID,
+ },
+ {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_UART_DP,
+ {
+ (UINT8) (sizeof (UART_DEVICE_PATH)),
+ (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0, // Reserved
+ 0, // BaudRate - Default
+ 0, // DataBits - Default
+ 0, // Parity - Default
+ 0, // StopBits - Default
+ },
+ gPcAnsiTerminal,
+ gEndEntire
+};
+
+STATIC USB_KEYBOARD_DEVICE_PATH gUsbKeyboardDevicePath = {
+ {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_USB_CLASS_DP,
+ {
+ (UINT8)sizeof (USB_CLASS_DEVICE_PATH),
+ (UINT8)(sizeof (USB_CLASS_DEVICE_PATH) >> 8)
+ }
+ },
+ 0xFFFF, // VendorId: any
+ 0xFFFF, // ProductId: any
+ 3, // DeviceClass: HID
+ 1, // DeviceSubClass: boot
+ 1 // DeviceProtocol: keyboard
+ },
+ gEndEntire
+};
+
+STATIC VENDOR_RAMFB_DEVICE_PATH gQemuRamfbDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ QEMU_RAMFB_GUID,
+ },
+ {
+ {
+ ACPI_DEVICE_PATH,
+ ACPI_ADR_DP,
+ {
+ (UINT8) (sizeof (ACPI_ADR_DEVICE_PATH)),
+ (UINT8) ((sizeof (ACPI_ADR_DEVICE_PATH)) >> 8)
+ }
+ },
+ ACPI_DISPLAY_ADR (
+ 1, // DeviceIdScheme
+ 0, // HeadId
+ 0, // NonVgaOutput
+ 1, // BiosCanDetect
+ 0, // VendorInfo
+ ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL, // Type
+ 0, // Port
+ 0 // Index
+ ),
+ },
+ gEndEntire
+};
+
+//
+// Predefined platform default console device path
+//
+PLATFORM_CONSOLE_CONNECT_ENTRY gPlatformConsole[] = {
+ {
+ (EFI_DEVICE_PATH_PROTOCOL *) &gDebugAgentUartDevicePath,
+ (CONSOLE_OUT | CONSOLE_IN | STD_ERROR)
+ },
+ {
+ (EFI_DEVICE_PATH_PROTOCOL *)&gUsbKeyboardDevicePath,
+ CONSOLE_IN
+ },
+ {
+ (EFI_DEVICE_PATH_PROTOCOL *)&gQemuRamfbDevicePath,
+ CONSOLE_OUT
+ },
+ {
+ NULL,
+ 0
+ }
+};
+
+//
+// Predefined platform connect sequence
+//
+EFI_DEVICE_PATH_PROTOCOL *gPlatformConnectSequence[] = { NULL };
+
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortNocheck.c b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortNocheck.c new file mode 100644 index 000000000..0ef7920a8 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortNocheck.c @@ -0,0 +1,25 @@ +/** @file
+ Dectection code for hypervisor debug port.
+
+ Copyright (c) 2020, Citrix Systems, Inc.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "DebugLibDetect.h"
+
+/**
+ Always return TRUE without detection as the debug I/O port is always
+ present.
+
+ @retval TRUE The debug I/O port is always present.
+
+**/
+BOOLEAN
+EFIAPI
+PlatformDebugLibIoPortDetect (
+ VOID
+ )
+{
+ return TRUE;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortQemu.c b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortQemu.c new file mode 100644 index 000000000..bf9119807 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortQemu.c @@ -0,0 +1,34 @@ +/** @file
+ Detection code for QEMU debug port.
+
+ Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2012, Red Hat, Inc.<BR>
+ Copyright (c) 2020, Citrix Systems, Inc.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include "DebugLibDetect.h"
+
+//
+// The constant value that is read from the debug I/O port
+//
+#define BOCHS_DEBUG_PORT_MAGIC 0xE9
+
+/**
+ Return the result of detecting the debug I/O port device.
+
+ @retval TRUE if the debug I/O port device was detected.
+ @retval FALSE otherwise
+
+**/
+BOOLEAN
+EFIAPI
+PlatformDebugLibIoPortDetect (
+ VOID
+ )
+{
+ return IoRead8 (PcdGet16 (PcdDebugIoPort)) == BOCHS_DEBUG_PORT_MAGIC;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLib.c b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLib.c new file mode 100644 index 000000000..dffb20822 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLib.c @@ -0,0 +1,361 @@ +/** @file
+ Base Debug library instance for hypervisor debug port.
+ It uses PrintLib to send debug messages to a fixed I/O port.
+
+ Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2012, Red Hat, Inc.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/IoLib.h>
+#include <Library/PrintLib.h>
+#include <Library/PcdLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugPrintErrorLevelLib.h>
+#include "DebugLibDetect.h"
+
+//
+// Define the maximum debug and assert message length that this library supports
+//
+#define MAX_DEBUG_MESSAGE_LENGTH 0x100
+
+//
+// VA_LIST can not initialize to NULL for all compiler, so we use this to
+// indicate a null VA_LIST
+//
+VA_LIST mVaListNull;
+
+/**
+ Prints a debug message to the debug output device if the specified error level is enabled.
+
+ If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
+ GetDebugPrintErrorLevel (), then print the message specified by Format and the
+ associated variable argument list to the debug output device.
+
+ If Format is NULL, then ASSERT().
+
+ @param ErrorLevel The error level of the debug message.
+ @param Format Format string for the debug message to print.
+ @param ... Variable argument list whose contents are accessed
+ based on the format string specified by Format.
+
+**/
+VOID
+EFIAPI
+DebugPrint (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (ErrorLevel, Format, Marker);
+ VA_END (Marker);
+}
+
+
+/**
+ Prints a debug message to the debug output device if the specified
+ error level is enabled base on Null-terminated format string and a
+ VA_LIST argument list or a BASE_LIST argument list.
+
+ If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
+ GetDebugPrintErrorLevel (), then print the message specified by Format and
+ the associated variable argument list to the debug output device.
+
+ If Format is NULL, then ASSERT().
+
+ @param ErrorLevel The error level of the debug message.
+ @param Format Format string for the debug message to print.
+ @param VaListMarker VA_LIST marker for the variable argument list.
+ @param BaseListMarker BASE_LIST marker for the variable argument list.
+
+**/
+VOID
+DebugPrintMarker (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ IN VA_LIST VaListMarker,
+ IN BASE_LIST BaseListMarker
+ )
+{
+ CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];
+ UINTN Length;
+
+ //
+ // If Format is NULL, then ASSERT().
+ //
+ ASSERT (Format != NULL);
+
+ //
+ // Check if the global mask disables this message or the device is inactive
+ //
+ if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0 ||
+ !PlatformDebugLibIoPortFound ()) {
+ return;
+ }
+
+ //
+ // Convert the DEBUG() message to an ASCII String
+ //
+ if (BaseListMarker == NULL) {
+ Length = AsciiVSPrint (Buffer, sizeof (Buffer), Format, VaListMarker);
+ } else {
+ Length = AsciiBSPrint (Buffer, sizeof (Buffer), Format, BaseListMarker);
+ }
+
+ //
+ // Send the print string to the debug I/O port
+ //
+ IoWriteFifo8 (PcdGet16 (PcdDebugIoPort), Length, Buffer);
+}
+
+
+/**
+ Prints a debug message to the debug output device if the specified
+ error level is enabled.
+
+ If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
+ GetDebugPrintErrorLevel (), then print the message specified by Format and
+ the associated variable argument list to the debug output device.
+
+ If Format is NULL, then ASSERT().
+
+ @param ErrorLevel The error level of the debug message.
+ @param Format Format string for the debug message to print.
+ @param VaListMarker VA_LIST marker for the variable argument list.
+
+**/
+VOID
+EFIAPI
+DebugVPrint (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ IN VA_LIST VaListMarker
+ )
+{
+ DebugPrintMarker (ErrorLevel, Format, VaListMarker, NULL);
+}
+
+
+/**
+ Prints a debug message to the debug output device if the specified
+ error level is enabled.
+ This function use BASE_LIST which would provide a more compatible
+ service than VA_LIST.
+
+ If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
+ GetDebugPrintErrorLevel (), then print the message specified by Format and
+ the associated variable argument list to the debug output device.
+
+ If Format is NULL, then ASSERT().
+
+ @param ErrorLevel The error level of the debug message.
+ @param Format Format string for the debug message to print.
+ @param BaseListMarker BASE_LIST marker for the variable argument list.
+
+**/
+VOID
+EFIAPI
+DebugBPrint (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ IN BASE_LIST BaseListMarker
+ )
+{
+ DebugPrintMarker (ErrorLevel, Format, mVaListNull, BaseListMarker);
+}
+
+
+/**
+ Prints an assert message containing a filename, line number, and description.
+ This may be followed by a breakpoint or a dead loop.
+
+ Print a message of the form "ASSERT <FileName>(<LineNumber>): <Description>\n"
+ to the debug output device. If DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED bit of
+ PcdDebugProperyMask is set then CpuBreakpoint() is called. Otherwise, if
+ DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED bit of PcdDebugProperyMask is set then
+ CpuDeadLoop() is called. If neither of these bits are set, then this function
+ returns immediately after the message is printed to the debug output device.
+ DebugAssert() must actively prevent recursion. If DebugAssert() is called while
+ processing another DebugAssert(), then DebugAssert() must return immediately.
+
+ If FileName is NULL, then a <FileName> string of "(NULL) Filename" is printed.
+ If Description is NULL, then a <Description> string of "(NULL) Description" is printed.
+
+ @param FileName The pointer to the name of the source file that generated the assert condition.
+ @param LineNumber The line number in the source file that generated the assert condition
+ @param Description The pointer to the description of the assert condition.
+
+**/
+VOID
+EFIAPI
+DebugAssert (
+ IN CONST CHAR8 *FileName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *Description
+ )
+{
+ CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];
+ UINTN Length;
+
+ //
+ // Generate the ASSERT() message in Ascii format
+ //
+ Length = AsciiSPrint (Buffer, sizeof Buffer, "ASSERT %a(%Lu): %a\n",
+ FileName, (UINT64)LineNumber, Description);
+
+ //
+ // Send the print string to the debug I/O port, if present
+ //
+ if (PlatformDebugLibIoPortFound ()) {
+ IoWriteFifo8 (PcdGet16 (PcdDebugIoPort), Length, Buffer);
+ }
+
+ //
+ // Generate a Breakpoint, DeadLoop, or NOP based on PCD settings
+ //
+ if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED) != 0) {
+ CpuBreakpoint ();
+ } else if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED) != 0) {
+ CpuDeadLoop ();
+ }
+}
+
+
+/**
+ Fills a target buffer with PcdDebugClearMemoryValue, and returns the target buffer.
+
+ This function fills Length bytes of Buffer with the value specified by
+ PcdDebugClearMemoryValue, and returns Buffer.
+
+ If Buffer is NULL, then ASSERT().
+ If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT().
+
+ @param Buffer The pointer to the target buffer to be filled with PcdDebugClearMemoryValue.
+ @param Length The number of bytes in Buffer to fill with zeros PcdDebugClearMemoryValue.
+
+ @return Buffer The pointer to the target buffer filled with PcdDebugClearMemoryValue.
+
+**/
+VOID *
+EFIAPI
+DebugClearMemory (
+ OUT VOID *Buffer,
+ IN UINTN Length
+ )
+{
+ //
+ // If Buffer is NULL, then ASSERT().
+ //
+ ASSERT (Buffer != NULL);
+
+ //
+ // SetMem() checks for the the ASSERT() condition on Length and returns Buffer
+ //
+ return SetMem (Buffer, Length, PcdGet8(PcdDebugClearMemoryValue));
+}
+
+
+/**
+ Returns TRUE if ASSERT() macros are enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of
+ PcdDebugProperyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugProperyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugProperyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugAssertEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED) != 0);
+}
+
+
+/**
+ Returns TRUE if DEBUG() macros are enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of
+ PcdDebugProperyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugPrintEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0);
+}
+
+
+/**
+ Returns TRUE if DEBUG_CODE() macros are enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of
+ PcdDebugProperyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of PcdDebugProperyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of PcdDebugProperyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugCodeEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_CODE_ENABLED) != 0);
+}
+
+
+/**
+ Returns TRUE if DEBUG_CLEAR_MEMORY() macro is enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of
+ PcdDebugProperyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebugProperyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebugProperyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugClearMemoryEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED) != 0);
+}
+
+/**
+ Returns TRUE if any one of the bit is set both in ErrorLevel and PcdFixedDebugPrintErrorLevel.
+
+ This function compares the bit mask of ErrorLevel and PcdFixedDebugPrintErrorLevel.
+
+ @retval TRUE Current ErrorLevel is supported.
+ @retval FALSE Current ErrorLevel is not supported.
+
+**/
+BOOLEAN
+EFIAPI
+DebugPrintLevelEnabled (
+ IN CONST UINTN ErrorLevel
+ )
+{
+ return (BOOLEAN) ((ErrorLevel & PcdGet32(PcdFixedDebugPrintErrorLevel)) != 0);
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.c b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.c new file mode 100644 index 000000000..8c466e64e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.c @@ -0,0 +1,65 @@ +/** @file
+ Detection code for hypervisor debug port.
+ Non-SEC instance, caches the result of detection.
+
+ Copyright (c) 2017, Red Hat, Inc.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include "DebugLibDetect.h"
+
+//
+// Set to TRUE if the debug I/O port has been checked
+//
+STATIC BOOLEAN mDebugIoPortChecked = FALSE;
+
+//
+// Set to TRUE if the debug I/O port is enabled
+//
+STATIC BOOLEAN mDebugIoPortFound = FALSE;
+
+/**
+ This constructor function must not do anything.
+
+ Some modules consuming this library instance, such as the DXE Core, invoke
+ the DEBUG() macro before they explicitly call
+ ProcessLibraryConstructorList(). Therefore the auto-generated call from
+ ProcessLibraryConstructorList() to this constructor function may be preceded
+ by some calls to PlatformDebugLibIoPortFound() below. Hence
+ PlatformDebugLibIoPortFound() must not rely on anything this constructor
+ could set up.
+
+ @retval RETURN_SUCCESS The constructor always returns RETURN_SUCCESS.
+
+**/
+RETURN_STATUS
+EFIAPI
+PlatformDebugLibIoPortConstructor (
+ VOID
+ )
+{
+ return RETURN_SUCCESS;
+}
+
+/**
+ At the first call, check if the debug I/O port device is present, and cache
+ the result for later use. At subsequent calls, return the cached result.
+
+ @retval TRUE if the debug I/O port device was detected.
+ @retval FALSE otherwise
+
+**/
+BOOLEAN
+EFIAPI
+PlatformDebugLibIoPortFound (
+ VOID
+ )
+{
+ if (!mDebugIoPortChecked) {
+ mDebugIoPortFound = PlatformDebugLibIoPortDetect ();
+ mDebugIoPortChecked = TRUE;
+ }
+ return mDebugIoPortFound;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.h b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.h new file mode 100644 index 000000000..6d08909db --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.h @@ -0,0 +1,45 @@ +/** @file
+ Base Debug library instance for hypervisor debug port.
+ It uses PrintLib to send debug messages to a fixed I/O port.
+
+ Copyright (c) 2017, Red Hat, Inc.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __DEBUG_IO_PORT_DETECT_H__
+#define __DEBUG_IO_PORT_DETECT_H__
+
+#include <Base.h>
+
+/**
+ Helper function to return whether the virtual machine has a debug I/O port.
+ PlatformDebugLibIoPortFound can call this function directly or cache the
+ result.
+
+ @retval TRUE if the debug I/O port device was detected.
+ @retval FALSE otherwise
+
+**/
+BOOLEAN
+EFIAPI
+PlatformDebugLibIoPortDetect (
+ VOID
+ );
+
+/**
+ Return whether the virtual machine has a debug I/O port. DebugLib.c
+ calls this function instead of PlatformDebugLibIoPortDetect, to allow
+ caching if possible.
+
+ @retval TRUE if the debug I/O port device was detected.
+ @retval FALSE otherwise
+
+**/
+BOOLEAN
+EFIAPI
+PlatformDebugLibIoPortFound (
+ VOID
+ );
+
+#endif
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetectRom.c b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetectRom.c new file mode 100644 index 000000000..8cbdbd94f --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetectRom.c @@ -0,0 +1,42 @@ +/** @file
+ Detection code for hypervisor debug port.
+ SEC instance, cannot cache the result of detection.
+
+ Copyright (c) 2017, Red Hat, Inc.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include "DebugLibDetect.h"
+
+/**
+ This constructor function does not have anything to do.
+
+ @retval RETURN_SUCCESS The constructor always returns RETURN_SUCCESS.
+
+**/
+RETURN_STATUS
+EFIAPI
+PlatformRomDebugLibIoPortConstructor (
+ VOID
+ )
+{
+ return RETURN_SUCCESS;
+}
+
+/**
+ Return the result of detecting the debug I/O port device.
+
+ @retval TRUE if the debug I/O port device was detected.
+ @retval FALSE otherwise
+
+**/
+BOOLEAN
+EFIAPI
+PlatformDebugLibIoPortFound (
+ VOID
+ )
+{
+ return PlatformDebugLibIoPortDetect ();
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformDebugLibIoPort.inf b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformDebugLibIoPort.inf new file mode 100644 index 000000000..94ab91050 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformDebugLibIoPort.inf @@ -0,0 +1,49 @@ +## @file
+# Instance of Debug Library for the QEMU debug console port.
+# It uses Print Library to produce formatted output strings.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2012, Red Hat, Inc.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformDebugLibIoPort
+ FILE_GUID = DF934DA3-CD31-49FE-AF50-B3C87C79325F
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DebugLib|PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER SMM_CORE DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION
+ CONSTRUCTOR = PlatformDebugLibIoPortConstructor
+
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DebugIoPortQemu.c
+ DebugLib.c
+ DebugLibDetect.c
+ DebugLibDetect.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ IoLib
+ PcdLib
+ PrintLib
+ BaseLib
+ DebugPrintErrorLevelLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdDebugIoPort ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel ## CONSUMES
+
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPort.inf b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPort.inf new file mode 100644 index 000000000..8f721d249 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPort.inf @@ -0,0 +1,49 @@ +## @file
+# Instance of Debug Library for the QEMU debug console port.
+# It uses Print Library to produce formatted output strings.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017, Red Hat, Inc.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformRomDebugLibIoPort
+ FILE_GUID = CEB0D9D3-328F-4C24-8C02-28FA1986AE1B
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DebugLib|SEC
+ CONSTRUCTOR = PlatformRomDebugLibIoPortConstructor
+
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DebugIoPortQemu.c
+ DebugLib.c
+ DebugLibDetect.h
+ DebugLibDetectRom.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ IoLib
+ PcdLib
+ PrintLib
+ BaseLib
+ DebugPrintErrorLevelLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdDebugIoPort ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel ## CONSUMES
+
diff --git a/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPortNocheck.inf b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPortNocheck.inf new file mode 100644 index 000000000..6a85b333e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPortNocheck.inf @@ -0,0 +1,48 @@ +## @file
+# Instance of Debug Library for an hypervisor debug console port.
+# It uses Print Library to produce formatted output strings.
+#
+# Copyright (c) 2020, Citrix Systems, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformRomDebugLibIoPortNocheck
+ FILE_GUID = 92AEB68E-C2CF-466E-9AB2-3F5E713F7DE6
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DebugLib
+ CONSTRUCTOR = PlatformRomDebugLibIoPortConstructor
+
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DebugIoPortNocheck.c
+ DebugLib.c
+ DebugLibDetect.h
+ DebugLibDetectRom.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ IoLib
+ PcdLib
+ PrintLib
+ BaseLib
+ DebugPrintErrorLevelLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdDebugIoPort ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel ## CONSUMES
+
diff --git a/roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.c b/roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.c new file mode 100644 index 000000000..8ef7416d3 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.c @@ -0,0 +1,83 @@ +/** @file
+ NULL PlatformFvbLib library instance
+
+ Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PiDxe.h"
+#include <Library/PlatformFvbLib.h>
+
+
+/**
+ This function will be called following a call to the
+ EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL Read function.
+
+ @param[in] This The EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+ @param[in] Lba The starting logical block index
+ from which to read.
+ @param[in] Offset Offset into the block at which to begin reading.
+ @param[in] NumBytes The number of bytes read.
+ @param[in] Buffer Pointer to the buffer that was read, and will be
+ returned to the caller.
+
+**/
+VOID
+EFIAPI
+PlatformFvbDataRead (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+}
+
+
+/**
+ This function will be called following a call to the
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL Write function.
+
+ @param[in] This EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL instance.
+ @param[in] Lba The starting logical block index to written to.
+ @param[in] Offset Offset into the block at which to begin writing.
+ @param[in] NumBytes The number of bytes written.
+ @param[in] Buffer Pointer to the buffer that was written.
+
+**/
+VOID
+EFIAPI
+PlatformFvbDataWritten (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN UINTN NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+}
+
+
+/**
+ This function will be called following a call to the
+ EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL Erase function.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL
+ instance.
+ @param List The variable argument list as documented for
+ the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL Erase
+ function.
+
+**/
+VOID
+EFIAPI
+PlatformFvbBlocksErased (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN VA_LIST List
+ )
+{
+}
+
+
diff --git a/roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.inf b/roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.inf new file mode 100644 index 000000000..2966af31b --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.inf @@ -0,0 +1,34 @@ +## @file
+# NULL PlatformFvbLib library instance
+#
+# This library handles hooks for the EMU Variable FVB driver.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformFvbLibNull
+ FILE_GUID = 79263F9A-1701-4382-98C2-573F3558E6C8
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformFvbLib|DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ PlatformFvbLibNull.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
diff --git a/roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.c b/roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.c new file mode 100644 index 000000000..7c4ef769d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.c @@ -0,0 +1,27 @@ +/** @file
+ A hook-in library for MdeModulePkg/Bus/Pci/PciHostBridgeDxe.
+
+ Plugging this library instance into PciHostBridgeDxe makes
+ PciHostBridgeDxe depend on the platform's dynamic decision whether
+ to provide IOMMU implementation (usually through IoMmuDxe driver).
+
+ Copyright (C) 2017, Red Hat, Inc.
+ Copyright (C) 2017, AMD, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Base.h>
+
+RETURN_STATUS
+EFIAPI
+PlatformHasIoMmuInitialize (
+ VOID
+ )
+{
+ //
+ // Do nothing, just imbue PciHostBridgeDxe with a protocol dependency on
+ // gIoMmuAbsentProtocolGuid OR gEdkiiIoMmuProtocolGuid.
+ //
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.inf b/roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.inf new file mode 100644 index 000000000..61a159987 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.inf @@ -0,0 +1,32 @@ +## @file
+# A hook-in library for MdeModulePkg/Bus/Pci/PciHostBridgeDxe.
+#
+# Plugging this library instance into PciHostBridgeDxe makes
+# PciHostBridgeDxe depend on the platform's dynamic decision whether
+# to provide IOMMU implementation (usually through IoMmuDxe driver).
+#
+# Copyright (C) 2017, Red Hat, Inc.
+# Copyright (C) 2017, AMD, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = PlatformHasIoMmuLib
+ FILE_GUID = 49b4b018-0558-448b-1e1a-12226342877d
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformHasIoMmuLib|DXE_DRIVER
+ CONSTRUCTOR = PlatformHasIoMmuInitialize
+
+[Sources]
+ PlatformHasIoMmuLib.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[Depex]
+ gEdkiiIoMmuProtocolGuid OR gIoMmuAbsentProtocolGuid
diff --git a/roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.c b/roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.c new file mode 100644 index 000000000..734d30213 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.c @@ -0,0 +1,34 @@ +/** @file
+ Provides a platform-specific method to enable Secure Boot Custom Mode setup.
+
+ Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Library/PcdLib.h>
+
+/**
+
+ This function provides a platform-specific method to detect whether the platform
+ is operating by a physically present user.
+
+ Programmatic changing of platform security policy (such as disable Secure Boot,
+ or switch between Standard/Custom Secure Boot mode) MUST NOT be possible during
+ Boot Services or after exiting EFI Boot Services. Only a physically present user
+ is allowed to perform these operations.
+
+ NOTE THAT: This function cannot depend on any EFI Variable Service since they are
+ not available when this function is called in AuthenticateVariable driver.
+
+ @retval TRUE The platform is operated by a physically present user.
+ @retval FALSE The platform is NOT operated by a physically present user.
+
+**/
+BOOLEAN
+EFIAPI
+UserPhysicalPresent (
+ VOID
+ )
+{
+ return TRUE;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.inf b/roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.inf new file mode 100644 index 000000000..db340f60b --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.inf @@ -0,0 +1,28 @@ +## @file
+# Provides a platform-specific method to enable Secure Boot Custom Mode setup.
+#
+# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformSecureLib
+ FILE_GUID = 4204D78D-EDBF-4cee-BE80-3881457CF344
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformSecureLib|DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ PlatformSecureLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
diff --git a/roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcd.c b/roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcd.c new file mode 100644 index 000000000..7ce236326 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcd.c @@ -0,0 +1,39 @@ +/** @file
+ Configure some PCDs dynamically for
+ "NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf", from QEMU's fw_cfg.
+
+ Copyright (C) 2020, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/PcdLib.h>
+#include <Library/QemuFwCfgSimpleParserLib.h>
+
+RETURN_STATUS
+EFIAPI
+SetPxeBcPcds (
+ VOID
+ )
+{
+ BOOLEAN FwCfgBool;
+ RETURN_STATUS PcdStatus;
+
+ if (!RETURN_ERROR (QemuFwCfgParseBool ("opt/org.tianocore/IPv4PXESupport",
+ &FwCfgBool))) {
+ PcdStatus = PcdSet8S (PcdIPv4PXESupport, FwCfgBool);
+ if (RETURN_ERROR (PcdStatus)) {
+ return PcdStatus;
+ }
+ }
+
+ if (!RETURN_ERROR (QemuFwCfgParseBool ("opt/org.tianocore/IPv6PXESupport",
+ &FwCfgBool))) {
+ PcdStatus = PcdSet8S (PcdIPv6PXESupport, FwCfgBool);
+ if (RETURN_ERROR (PcdStatus)) {
+ return PcdStatus;
+ }
+ }
+
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcdProducerLib.inf b/roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcdProducerLib.inf new file mode 100644 index 000000000..948133228 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcdProducerLib.inf @@ -0,0 +1,33 @@ +## @file
+# Configure some PCDs dynamically for
+# "NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf", from QEMU's fw_cfg.
+#
+# Copyright (C) 2020, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = PxeBcPcdProducerLib
+ FILE_GUID = 1da2723f-52df-432a-8d03-6e8fa8acc107
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = NULL
+ CONSTRUCTOR = SetPxeBcPcds
+
+[Sources]
+ PxeBcPcd.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ NetworkPkg/NetworkPkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ PcdLib
+ QemuFwCfgSimpleParserLib
+
+[Pcd]
+ gEfiNetworkPkgTokenSpaceGuid.PcdIPv4PXESupport ## SOMETIMES_PRODUCES
+ gEfiNetworkPkgTokenSpaceGuid.PcdIPv6PXESupport ## SOMETIMES_PRODUCES
diff --git a/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c new file mode 100644 index 000000000..25d549404 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c @@ -0,0 +1,307 @@ +/** @file
+ Map positions of extra PCI root buses to bus numbers.
+
+ Copyright (C) 2015, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/OrderedCollectionLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PciRootBridgeIo.h>
+
+#include "ExtraRootBusMap.h"
+
+//
+// The BusNumbers field is an array with Count elements. The elements increase
+// strictry monotonically. Zero is not an element (because the zero bus number
+// belongs to the "main" root bus, never to an extra root bus). Offset N in the
+// array maps the extra root bus with position (N+1) to its bus number (because
+// the root bus with position 0 is always the main root bus, therefore we don't
+// store it).
+//
+// If there are no extra root buses in the system, then Count is 0, and
+// BusNumbers is NULL.
+//
+struct EXTRA_ROOT_BUS_MAP_STRUCT {
+ UINT32 *BusNumbers;
+ UINTN Count;
+};
+
+
+/**
+ An ORDERED_COLLECTION_USER_COMPARE function that compares root bridge
+ protocol device paths based on UID.
+
+ @param[in] UserStruct1 Pointer to the first ACPI_HID_DEVICE_PATH.
+
+ @param[in] UserStruct2 Pointer to the second ACPI_HID_DEVICE_PATH.
+
+ @retval <0 If UserStruct1 compares less than UserStruct2.
+
+ @retval 0 If UserStruct1 compares equal to UserStruct2.
+
+ @retval >0 If UserStruct1 compares greater than UserStruct2.
+**/
+STATIC
+INTN
+EFIAPI
+RootBridgePathCompare (
+ IN CONST VOID *UserStruct1,
+ IN CONST VOID *UserStruct2
+ )
+{
+ CONST ACPI_HID_DEVICE_PATH *Acpi1;
+ CONST ACPI_HID_DEVICE_PATH *Acpi2;
+
+ Acpi1 = UserStruct1;
+ Acpi2 = UserStruct2;
+
+ return Acpi1->UID < Acpi2->UID ? -1 :
+ Acpi1->UID > Acpi2->UID ? 1 :
+ 0;
+}
+
+
+/**
+ An ORDERED_COLLECTION_KEY_COMPARE function that compares a root bridge
+ protocol device path against a UID.
+
+ @param[in] StandaloneKey Pointer to the bare UINT32 UID.
+
+ @param[in] UserStruct Pointer to the ACPI_HID_DEVICE_PATH with the
+ embedded UINT32 UID.
+
+ @retval <0 If StandaloneKey compares less than UserStruct's key.
+
+ @retval 0 If StandaloneKey compares equal to UserStruct's key.
+
+ @retval >0 If StandaloneKey compares greater than UserStruct's key.
+**/
+STATIC
+INTN
+EFIAPI
+RootBridgePathKeyCompare (
+ IN CONST VOID *StandaloneKey,
+ IN CONST VOID *UserStruct
+ )
+{
+ CONST UINT32 *Uid;
+ CONST ACPI_HID_DEVICE_PATH *Acpi;
+
+ Uid = StandaloneKey;
+ Acpi = UserStruct;
+
+ return *Uid < Acpi->UID ? -1 :
+ *Uid > Acpi->UID ? 1 :
+ 0;
+}
+
+
+/**
+ Create a structure that maps the relative positions of PCI root buses to bus
+ numbers.
+
+ In the "bootorder" fw_cfg file, QEMU refers to extra PCI root buses by their
+ positions, in relative root bus number order, not by their actual PCI bus
+ numbers. The ACPI HID device path nodes however that are associated with
+ PciRootBridgeIo protocol instances in the system have their UID fields set to
+ the bus numbers. Create a map that gives, for each extra PCI root bus's
+ position (ie. "serial number") its actual PCI bus number.
+
+ @param[out] ExtraRootBusMap The data structure implementing the map.
+
+ @retval EFI_SUCCESS ExtraRootBusMap has been populated.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval EFI_ALREADY_STARTED A duplicate root bus number has been found in
+ the system. (This should never happen.)
+
+ @return Error codes returned by
+ gBS->LocateHandleBuffer() and
+ gBS->HandleProtocol().
+
+**/
+EFI_STATUS
+CreateExtraRootBusMap (
+ OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumHandles;
+ EFI_HANDLE *Handles;
+ ORDERED_COLLECTION *Collection;
+ EXTRA_ROOT_BUS_MAP *Map;
+ UINTN Idx;
+ ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
+
+ //
+ // Handles and Collection are temporary / helper variables, while in Map we
+ // build the return value.
+ //
+
+ Status = gBS->LocateHandleBuffer (ByProtocol,
+ &gEfiPciRootBridgeIoProtocolGuid, NULL /* SearchKey */,
+ &NumHandles, &Handles);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Collection = OrderedCollectionInit (RootBridgePathCompare,
+ RootBridgePathKeyCompare);
+ if (Collection == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeHandles;
+ }
+
+ Map = AllocateZeroPool (sizeof *Map);
+ if (Map == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeCollection;
+ }
+
+ //
+ // Collect the ACPI device path protocols of the root bridges.
+ //
+ for (Idx = 0; Idx < NumHandles; ++Idx) {
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ Status = gBS->HandleProtocol (Handles[Idx], &gEfiDevicePathProtocolGuid,
+ (VOID**)&DevicePath);
+ if (EFI_ERROR (Status)) {
+ goto FreeMap;
+ }
+
+ //
+ // Examine if the device path is an ACPI HID one, and if so, if UID is
+ // nonzero (ie. the root bridge that the bus number belongs to is "extra",
+ // not the main one). In that case, link the device path into Collection.
+ //
+ if (DevicePathType (DevicePath) == ACPI_DEVICE_PATH &&
+ DevicePathSubType (DevicePath) == ACPI_DP &&
+ ((ACPI_HID_DEVICE_PATH *)DevicePath)->HID == EISA_PNP_ID(0x0A03) &&
+ ((ACPI_HID_DEVICE_PATH *)DevicePath)->UID > 0) {
+ Status = OrderedCollectionInsert (Collection, NULL, DevicePath);
+ if (EFI_ERROR (Status)) {
+ goto FreeMap;
+ }
+ ++Map->Count;
+ }
+ }
+
+ if (Map->Count > 0) {
+ //
+ // At least one extra PCI root bus exists.
+ //
+ Map->BusNumbers = AllocatePool (Map->Count * sizeof *Map->BusNumbers);
+ if (Map->BusNumbers == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeMap;
+ }
+ }
+
+ //
+ // Now collect the bus numbers of the extra PCI root buses into Map.
+ //
+ Idx = 0;
+ Entry = OrderedCollectionMin (Collection);
+ while (Idx < Map->Count) {
+ ACPI_HID_DEVICE_PATH *Acpi;
+
+ ASSERT (Entry != NULL);
+ Acpi = OrderedCollectionUserStruct (Entry);
+ Map->BusNumbers[Idx] = Acpi->UID;
+ DEBUG ((DEBUG_VERBOSE,
+ "%a: extra bus position 0x%Lx maps to bus number (UID) 0x%x\n",
+ __FUNCTION__, (UINT64)(Idx + 1), Acpi->UID));
+ ++Idx;
+ Entry = OrderedCollectionNext (Entry);
+ }
+ ASSERT (Entry == NULL);
+
+ *ExtraRootBusMap = Map;
+ Status = EFI_SUCCESS;
+
+ //
+ // Fall through in order to release temporaries.
+ //
+
+FreeMap:
+ if (EFI_ERROR (Status)) {
+ if (Map->BusNumbers != NULL) {
+ FreePool (Map->BusNumbers);
+ }
+ FreePool (Map);
+ }
+
+FreeCollection:
+ for (Entry = OrderedCollectionMin (Collection); Entry != NULL;
+ Entry = Entry2) {
+ Entry2 = OrderedCollectionNext (Entry);
+ OrderedCollectionDelete (Collection, Entry, NULL);
+ }
+ OrderedCollectionUninit (Collection);
+
+FreeHandles:
+ FreePool (Handles);
+
+ return Status;
+}
+
+
+/**
+ Release a map created with CreateExtraRootBusMap().
+
+ @param[in] ExtraRootBusMap The map to release.
+*/
+VOID
+DestroyExtraRootBusMap (
+ IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap
+ )
+{
+ if (ExtraRootBusMap->BusNumbers != NULL) {
+ FreePool (ExtraRootBusMap->BusNumbers);
+ }
+ FreePool (ExtraRootBusMap);
+}
+
+/**
+ Map the position (serial number) of an extra PCI root bus to its bus number.
+
+ @param[in] ExtraRootBusMap The map created with CreateExtraRootBusMap();
+
+ @param[in] RootBusPos The extra PCI root bus position to map.
+
+ @param[out] RootBusNr The bus number belonging to the extra PCI root
+ bus identified by RootBusPos.
+
+ @retval EFI_INVALID_PARAMETER RootBusPos is zero. The zero position
+ identifies the main root bus, whose bus number
+ is always zero, and is therefore never
+ maintained in ExtraRootBusMap.
+
+ @retval EFI_NOT_FOUND RootBusPos is not found in ExtraRootBusMap.
+
+ @retval EFI_SUCCESS Mapping successful.
+**/
+EFI_STATUS
+MapRootBusPosToBusNr (
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraRootBusMap,
+ IN UINT64 RootBusPos,
+ OUT UINT32 *RootBusNr
+ )
+{
+ if (RootBusPos == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (RootBusPos > ExtraRootBusMap->Count) {
+ return EFI_NOT_FOUND;
+ }
+ *RootBusNr = ExtraRootBusMap->BusNumbers[(UINTN)RootBusPos - 1];
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.h b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.h new file mode 100644 index 000000000..3b79fbdc7 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.h @@ -0,0 +1,34 @@ +/** @file
+ Map positions of extra PCI root buses to bus numbers.
+
+ Copyright (C) 2015, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef __EXTRA_ROOT_BUS_MAP_H__
+#define __EXTRA_ROOT_BUS_MAP_H__
+
+/**
+ Incomplete ("opaque") data type implementing the map.
+**/
+typedef struct EXTRA_ROOT_BUS_MAP_STRUCT EXTRA_ROOT_BUS_MAP;
+
+EFI_STATUS
+CreateExtraRootBusMap (
+ OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap
+ );
+
+VOID
+DestroyExtraRootBusMap (
+ IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap
+ );
+
+EFI_STATUS
+MapRootBusPosToBusNr (
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraRootBusMap,
+ IN UINT64 RootBusPos,
+ OUT UINT32 *RootBusNr
+ );
+
+#endif
diff --git a/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c new file mode 100644 index 000000000..ceffb17fa --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c @@ -0,0 +1,2135 @@ +/** @file
+ Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
+
+ Copyright (C) 2012 - 2014, Red Hat, Inc.
+ Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/QemuFwCfgLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/QemuBootOrderLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/VirtioMmioTransport.h>
+
+#include "ExtraRootBusMap.h"
+
+/**
+ OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
+**/
+#define TRANSLATION_OUTPUT_SIZE 0x100
+
+/**
+ Output buffer size for OpenFirmware to UEFI device path fragment translation,
+ in CHAR16's, for a sequence of PCI bridges.
+**/
+#define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40
+
+/**
+ Numbers of nodes in OpenFirmware device paths that are required and examined.
+**/
+#define REQUIRED_PCI_OFW_NODES 2
+#define REQUIRED_MMIO_OFW_NODES 1
+#define EXAMINED_OFW_NODES 6
+
+
+/**
+ Simple character classification routines, corresponding to POSIX class names
+ and ASCII encoding.
+**/
+STATIC
+BOOLEAN
+IsAlnum (
+ IN CHAR8 Chr
+ )
+{
+ return (('0' <= Chr && Chr <= '9') ||
+ ('A' <= Chr && Chr <= 'Z') ||
+ ('a' <= Chr && Chr <= 'z')
+ );
+}
+
+
+STATIC
+BOOLEAN
+IsDriverNamePunct (
+ IN CHAR8 Chr
+ )
+{
+ return (Chr == ',' || Chr == '.' || Chr == '_' ||
+ Chr == '+' || Chr == '-'
+ );
+}
+
+
+STATIC
+BOOLEAN
+IsPrintNotDelim (
+ IN CHAR8 Chr
+ )
+{
+ return (32 <= Chr && Chr <= 126 &&
+ Chr != '/' && Chr != '@' && Chr != ':');
+}
+
+
+/**
+ Utility types and functions.
+**/
+typedef struct {
+ CONST CHAR8 *Ptr; // not necessarily NUL-terminated
+ UINTN Len; // number of non-NUL characters
+} SUBSTRING;
+
+
+/**
+
+ Check if Substring and String have identical contents.
+
+ The function relies on the restriction that a SUBSTRING cannot have embedded
+ NULs either.
+
+ @param[in] Substring The SUBSTRING input to the comparison.
+
+ @param[in] String The ASCII string input to the comparison.
+
+
+ @return Whether the inputs have identical contents.
+
+**/
+STATIC
+BOOLEAN
+SubstringEq (
+ IN SUBSTRING Substring,
+ IN CONST CHAR8 *String
+ )
+{
+ UINTN Pos;
+ CONST CHAR8 *Chr;
+
+ Pos = 0;
+ Chr = String;
+
+ while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {
+ ++Pos;
+ ++Chr;
+ }
+
+ return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');
+}
+
+
+/**
+
+ Parse a comma-separated list of hexadecimal integers into the elements of an
+ UINT64 array.
+
+ Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
+ or an empty string are not allowed; they are rejected.
+
+ The function relies on ASCII encoding.
+
+ @param[in] UnitAddress The substring to parse.
+
+ @param[out] Result The array, allocated by the caller, to receive
+ the parsed values. This parameter may be NULL if
+ NumResults is zero on input.
+
+ @param[in out] NumResults On input, the number of elements allocated for
+ Result. On output, the number of elements it has
+ taken (or would have taken) to parse the string
+ fully.
+
+
+ @retval RETURN_SUCCESS UnitAddress has been fully parsed.
+ NumResults is set to the number of parsed
+ values; the corresponding elements have
+ been set in Result. The rest of Result's
+ elements are unchanged.
+
+ @retval RETURN_BUFFER_TOO_SMALL UnitAddress has been fully parsed.
+ NumResults is set to the number of parsed
+ values, but elements have been stored only
+ up to the input value of NumResults, which
+ is less than what has been parsed.
+
+ @retval RETURN_INVALID_PARAMETER Parse error. The contents of Results is
+ indeterminate. NumResults has not been
+ changed.
+
+**/
+STATIC
+RETURN_STATUS
+ParseUnitAddressHexList (
+ IN SUBSTRING UnitAddress,
+ OUT UINT64 *Result,
+ IN OUT UINTN *NumResults
+ )
+{
+ UINTN Entry; // number of entry currently being parsed
+ UINT64 EntryVal; // value being constructed for current entry
+ CHAR8 PrevChr; // UnitAddress character previously checked
+ UINTN Pos; // current position within UnitAddress
+ RETURN_STATUS Status;
+
+ Entry = 0;
+ EntryVal = 0;
+ PrevChr = ',';
+
+ for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {
+ CHAR8 Chr;
+ INT8 Val;
+
+ Chr = UnitAddress.Ptr[Pos];
+ Val = ('a' <= Chr && Chr <= 'f') ? (Chr - 'a' + 10) :
+ ('A' <= Chr && Chr <= 'F') ? (Chr - 'A' + 10) :
+ ('0' <= Chr && Chr <= '9') ? (Chr - '0' ) :
+ -1;
+
+ if (Val >= 0) {
+ if (EntryVal > 0xFFFFFFFFFFFFFFFull) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ EntryVal = LShiftU64 (EntryVal, 4) | Val;
+ } else if (Chr == ',') {
+ if (PrevChr == ',') {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (Entry < *NumResults) {
+ Result[Entry] = EntryVal;
+ }
+ ++Entry;
+ EntryVal = 0;
+ } else {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ PrevChr = Chr;
+ }
+
+ if (PrevChr == ',') {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (Entry < *NumResults) {
+ Result[Entry] = EntryVal;
+ Status = RETURN_SUCCESS;
+ } else {
+ Status = RETURN_BUFFER_TOO_SMALL;
+ }
+ ++Entry;
+
+ *NumResults = Entry;
+ return Status;
+}
+
+
+/**
+ A simple array of Boot Option ID's.
+**/
+typedef struct {
+ UINT16 *Data;
+ UINTN Allocated;
+ UINTN Produced;
+} BOOT_ORDER;
+
+
+/**
+ Array element tracking an enumerated boot option that has the
+ LOAD_OPTION_ACTIVE attribute.
+**/
+typedef struct {
+ CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; // reference only, no
+ // ownership
+ BOOLEAN Appended; // has been added to a
+ // BOOT_ORDER?
+} ACTIVE_OPTION;
+
+
+/**
+
+ Append an active boot option to BootOrder, reallocating the latter if needed.
+
+ @param[in out] BootOrder The structure pointing to the array and holding
+ allocation and usage counters.
+
+ @param[in] ActiveOption The active boot option whose ID should be
+ appended to the array.
+
+
+ @retval RETURN_SUCCESS ID of ActiveOption appended.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.
+
+**/
+STATIC
+RETURN_STATUS
+BootOrderAppend (
+ IN OUT BOOT_ORDER *BootOrder,
+ IN OUT ACTIVE_OPTION *ActiveOption
+ )
+{
+ if (BootOrder->Produced == BootOrder->Allocated) {
+ UINTN AllocatedNew;
+ UINT16 *DataNew;
+
+ ASSERT (BootOrder->Allocated > 0);
+ AllocatedNew = BootOrder->Allocated * 2;
+ DataNew = ReallocatePool (
+ BootOrder->Allocated * sizeof (*BootOrder->Data),
+ AllocatedNew * sizeof (*DataNew),
+ BootOrder->Data
+ );
+ if (DataNew == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+ BootOrder->Allocated = AllocatedNew;
+ BootOrder->Data = DataNew;
+ }
+
+ BootOrder->Data[BootOrder->Produced++] =
+ (UINT16) ActiveOption->BootOption->OptionNumber;
+ ActiveOption->Appended = TRUE;
+ return RETURN_SUCCESS;
+}
+
+
+/**
+
+ Create an array of ACTIVE_OPTION elements for a boot option array.
+
+ @param[in] BootOptions A boot option array, created with
+ EfiBootManagerRefreshAllBootOption () and
+ EfiBootManagerGetLoadOptions ().
+
+ @param[in] BootOptionCount The number of elements in BootOptions.
+
+ @param[out] ActiveOption Pointer to the first element in the new array.
+ The caller is responsible for freeing the array
+ with FreePool() after use.
+
+ @param[out] Count Number of elements in the new array.
+
+
+ @retval RETURN_SUCCESS The ActiveOption array has been created.
+
+ @retval RETURN_NOT_FOUND No active entry has been found in
+ BootOptions.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+**/
+STATIC
+RETURN_STATUS
+CollectActiveOptions (
+ IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
+ IN UINTN BootOptionCount,
+ OUT ACTIVE_OPTION **ActiveOption,
+ OUT UINTN *Count
+ )
+{
+ UINTN Index;
+ UINTN ScanMode;
+
+ *ActiveOption = NULL;
+
+ //
+ // Scan the list twice:
+ // - count active entries,
+ // - store links to active entries.
+ //
+ for (ScanMode = 0; ScanMode < 2; ++ScanMode) {
+ *Count = 0;
+ for (Index = 0; Index < BootOptionCount; Index++) {
+ if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {
+ if (ScanMode == 1) {
+ (*ActiveOption)[*Count].BootOption = &BootOptions[Index];
+ (*ActiveOption)[*Count].Appended = FALSE;
+ }
+ ++*Count;
+ }
+ }
+
+ if (ScanMode == 0) {
+ if (*Count == 0) {
+ return RETURN_NOT_FOUND;
+ }
+ *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);
+ if (*ActiveOption == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+ }
+ }
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ OpenFirmware device path node
+**/
+typedef struct {
+ SUBSTRING DriverName;
+ SUBSTRING UnitAddress;
+ SUBSTRING DeviceArguments;
+} OFW_NODE;
+
+
+/**
+
+ Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
+ structure, and advance in the input string.
+
+ The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"
+ (a leading slash is expected and not returned):
+
+ /driver-name@unit-address[:device-arguments][<LF>]
+
+ A single trailing <LF> character is consumed but not returned. A trailing
+ <LF> or NUL character terminates the device path.
+
+ The function relies on ASCII encoding.
+
+ @param[in out] Ptr Address of the pointer pointing to the start of the
+ node string. After successful parsing *Ptr is set to
+ the byte immediately following the consumed
+ characters. On error it points to the byte that
+ caused the error. The input string is never modified.
+
+ @param[out] OfwNode The members of this structure point into the input
+ string, designating components of the node.
+ Separators are never included. If "device-arguments"
+ is missing, then DeviceArguments.Ptr is set to NULL.
+ All components that are present have nonzero length.
+
+ If the call doesn't succeed, the contents of this
+ structure is indeterminate.
+
+ @param[out] IsFinal In case of successful parsing, this parameter signals
+ whether the node just parsed is the final node in the
+ device path. The call after a final node will attempt
+ to start parsing the next path. If the call doesn't
+ succeed, then this parameter is not changed.
+
+
+ @retval RETURN_SUCCESS Parsing successful.
+
+ @retval RETURN_NOT_FOUND Parsing terminated. *Ptr was (and is)
+ pointing to an empty string.
+
+ @retval RETURN_INVALID_PARAMETER Parse error.
+
+**/
+STATIC
+RETURN_STATUS
+ParseOfwNode (
+ IN OUT CONST CHAR8 **Ptr,
+ OUT OFW_NODE *OfwNode,
+ OUT BOOLEAN *IsFinal
+ )
+{
+ //
+ // A leading slash is expected. End of string is tolerated.
+ //
+ switch (**Ptr) {
+ case '\0':
+ return RETURN_NOT_FOUND;
+
+ case '/':
+ ++*Ptr;
+ break;
+
+ default:
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // driver-name
+ //
+ OfwNode->DriverName.Ptr = *Ptr;
+ OfwNode->DriverName.Len = 0;
+ while (OfwNode->DriverName.Len < 32 &&
+ (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))
+ ) {
+ ++*Ptr;
+ ++OfwNode->DriverName.Len;
+ }
+
+ if (OfwNode->DriverName.Len == 0 || OfwNode->DriverName.Len == 32) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+
+ //
+ // unit-address
+ //
+ if (**Ptr != '@') {
+ return RETURN_INVALID_PARAMETER;
+ }
+ ++*Ptr;
+
+ OfwNode->UnitAddress.Ptr = *Ptr;
+ OfwNode->UnitAddress.Len = 0;
+ while (IsPrintNotDelim (**Ptr)) {
+ ++*Ptr;
+ ++OfwNode->UnitAddress.Len;
+ }
+
+ if (OfwNode->UnitAddress.Len == 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+
+ //
+ // device-arguments, may be omitted
+ //
+ OfwNode->DeviceArguments.Len = 0;
+ if (**Ptr == ':') {
+ ++*Ptr;
+ OfwNode->DeviceArguments.Ptr = *Ptr;
+
+ while (IsPrintNotDelim (**Ptr)) {
+ ++*Ptr;
+ ++OfwNode->DeviceArguments.Len;
+ }
+
+ if (OfwNode->DeviceArguments.Len == 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ }
+ else {
+ OfwNode->DeviceArguments.Ptr = NULL;
+ }
+
+ switch (**Ptr) {
+ case '\n':
+ ++*Ptr;
+ //
+ // fall through
+ //
+
+ case '\0':
+ *IsFinal = TRUE;
+ break;
+
+ case '/':
+ *IsFinal = FALSE;
+ break;
+
+ default:
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
+ __FUNCTION__,
+ OfwNode->DriverName.Len, OfwNode->DriverName.Ptr,
+ OfwNode->UnitAddress.Len, OfwNode->UnitAddress.Ptr,
+ OfwNode->DeviceArguments.Len,
+ OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr
+ ));
+ return RETURN_SUCCESS;
+}
+
+
+/**
+
+ Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
+ fragment.
+
+ @param[in] OfwNode Array of OpenFirmware device nodes to
+ translate, constituting the beginning of an
+ OpenFirmware device path.
+
+ @param[in] NumNodes Number of elements in OfwNode.
+
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
+ CreateExtraRootBusMap(), to be used for
+ translating positions of extra root buses to
+ bus numbers.
+
+ @param[out] Translated Destination array receiving the UEFI path
+ fragment, allocated by the caller. If the
+ return value differs from RETURN_SUCCESS, its
+ contents is indeterminate.
+
+ @param[in out] TranslatedSize On input, the number of CHAR16's in
+ Translated. On RETURN_SUCCESS this parameter
+ is assigned the number of non-NUL CHAR16's
+ written to Translated. In case of other return
+ values, TranslatedSize is indeterminate.
+
+
+ @retval RETURN_SUCCESS Translation successful.
+
+ @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
+ of bytes provided.
+
+ @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
+ be translated in the current implementation.
+
+ @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an
+ extra PCI root bus (by serial number) that
+ is invalid according to ExtraPciRoots.
+
+**/
+STATIC
+RETURN_STATUS
+TranslatePciOfwNodes (
+ IN CONST OFW_NODE *OfwNode,
+ IN UINTN NumNodes,
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
+ OUT CHAR16 *Translated,
+ IN OUT UINTN *TranslatedSize
+ )
+{
+ UINT32 PciRoot;
+ CHAR8 *Comma;
+ UINTN FirstNonBridge;
+ CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];
+ UINTN BridgesLen;
+ UINT64 PciDevFun[2];
+ UINTN NumEntries;
+ UINTN Written;
+
+ //
+ // Resolve the PCI root bus number.
+ //
+ // The initial OFW node for the main root bus (ie. bus number 0) is:
+ //
+ // /pci@i0cf8
+ //
+ // For extra root buses, the initial OFW node is
+ //
+ // /pci@i0cf8,4
+ // ^
+ // root bus serial number (not PCI bus number)
+ //
+ if (NumNodes < REQUIRED_PCI_OFW_NODES ||
+ !SubstringEq (OfwNode[0].DriverName, "pci")
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ PciRoot = 0;
+ Comma = ScanMem8 (OfwNode[0].UnitAddress.Ptr, OfwNode[0].UnitAddress.Len,
+ ',');
+ if (Comma != NULL) {
+ SUBSTRING PciRootSerialSubString;
+ UINT64 PciRootSerial;
+
+ //
+ // Parse the root bus serial number from the unit address after the comma.
+ //
+ PciRootSerialSubString.Ptr = Comma + 1;
+ PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -
+ (PciRootSerialSubString.Ptr -
+ OfwNode[0].UnitAddress.Ptr);
+ NumEntries = 1;
+ if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString,
+ &PciRootSerial, &NumEntries))) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // Map the extra root bus's serial number to its actual bus number.
+ //
+ if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots, PciRootSerial,
+ &PciRoot))) {
+ return RETURN_PROTOCOL_ERROR;
+ }
+ }
+
+ //
+ // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
+ //
+ // pci-bridge@1e[,0]
+ // ^ ^
+ // PCI slot & function on the parent, holding the bridge
+ //
+ // and the UEFI device path node is:
+ //
+ // Pci(0x1E,0x0)
+ //
+ FirstNonBridge = 1;
+ Bridges[0] = L'\0';
+ BridgesLen = 0;
+ do {
+ UINT64 BridgeDevFun[2];
+ UINTN BridgesFreeBytes;
+
+ if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {
+ break;
+ }
+
+ BridgeDevFun[1] = 0;
+ NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];
+ if (ParseUnitAddressHexList (OfwNode[FirstNonBridge].UnitAddress,
+ BridgeDevFun, &NumEntries) != RETURN_SUCCESS) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];
+ Written = UnicodeSPrintAsciiFormat (Bridges + BridgesLen, BridgesFreeBytes,
+ "/Pci(0x%Lx,0x%Lx)", BridgeDevFun[0], BridgeDevFun[1]);
+ BridgesLen += Written;
+
+ //
+ // There's no way to differentiate between "completely used up without
+ // truncation" and "truncated", so treat the former as the latter.
+ //
+ if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ ++FirstNonBridge;
+ } while (FirstNonBridge < NumNodes);
+
+ if (FirstNonBridge == NumNodes) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // Parse the OFW nodes starting with the first non-bridge node.
+ //
+ PciDevFun[1] = 0;
+ NumEntries = ARRAY_SIZE (PciDevFun);
+ if (ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge].UnitAddress,
+ PciDevFun,
+ &NumEntries
+ ) != RETURN_SUCCESS
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ if (NumNodes >= FirstNonBridge + 3 &&
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
+ ) {
+ //
+ // OpenFirmware device path (IDE disk, IDE CD-ROM):
+ //
+ // /pci@i0cf8/ide@1,1/drive@0/disk@0
+ // ^ ^ ^ ^ ^
+ // | | | | master or slave
+ // | | | primary or secondary
+ // | PCI slot & function holding IDE controller
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path:
+ //
+ // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
+ // ^
+ // fixed LUN
+ //
+ UINT64 Secondary;
+ UINT64 Slave;
+
+ NumEntries = 1;
+ if (ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge + 1].UnitAddress,
+ &Secondary,
+ &NumEntries
+ ) != RETURN_SUCCESS ||
+ Secondary > 1 ||
+ ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge + 2].UnitAddress,
+ &Slave,
+ &NumEntries // reuse after previous single-element call
+ ) != RETURN_SUCCESS ||
+ Slave > 1
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1],
+ Secondary ? "Secondary" : "Primary",
+ Slave ? "Slave" : "Master"
+ );
+ } else if (NumNodes >= FirstNonBridge + 3 &&
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
+ ) {
+ //
+ // OpenFirmware device path (Q35 SATA disk and CD-ROM):
+ //
+ // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
+ // ^ ^ ^ ^ ^
+ // | | | | device number (fixed 0)
+ // | | | channel (port) number
+ // | PCI slot & function holding SATA HBA
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path:
+ //
+ // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
+ // ^ ^ ^
+ // | | LUN (always 0 on Q35)
+ // | port multiplier port number,
+ // | always 0xFFFF on Q35
+ // channel (port) number
+ //
+ UINT64 Channel;
+
+ NumEntries = 1;
+ if (RETURN_ERROR (ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge + 1].UnitAddress, &Channel,
+ &NumEntries))) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1],
+ Channel
+ );
+ } else if (NumNodes >= FirstNonBridge + 3 &&
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")
+ ) {
+ //
+ // OpenFirmware device path (floppy disk):
+ //
+ // /pci@i0cf8/isa@1/fdc@03f0/floppy@0
+ // ^ ^ ^ ^
+ // | | | A: or B:
+ // | | ISA controller io-port (hex)
+ // | PCI slot holding ISA controller
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path:
+ //
+ // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
+ // ^
+ // ACPI UID
+ //
+ UINT64 AcpiUid;
+
+ NumEntries = 1;
+ if (ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge + 2].UnitAddress,
+ &AcpiUid,
+ &NumEntries
+ ) != RETURN_SUCCESS ||
+ AcpiUid > 1
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1],
+ AcpiUid
+ );
+ } else if (NumNodes >= FirstNonBridge + 2 &&
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")
+ ) {
+ //
+ // OpenFirmware device path (virtio-blk disk):
+ //
+ // /pci@i0cf8/scsi@6[,3]/disk@0,0
+ // ^ ^ ^ ^ ^
+ // | | | fixed
+ // | | PCI function corresponding to disk (optional)
+ // | PCI slot holding disk
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path prefix:
+ //
+ // PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
+ // PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
+ //
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1]
+ );
+ } else if (NumNodes >= FirstNonBridge + 3 &&
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
+ ) {
+ //
+ // OpenFirmware device path (virtio-scsi disk):
+ //
+ // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
+ // ^ ^ ^ ^ ^
+ // | | | | LUN
+ // | | | target
+ // | | channel (unused, fixed 0)
+ // | PCI slot[, function] holding SCSI controller
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path prefix:
+ //
+ // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
+ // -- if PCI function is 0 or absent
+ // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
+ // -- if PCI function is present and nonzero
+ //
+ UINT64 TargetLun[2];
+
+ TargetLun[1] = 0;
+ NumEntries = ARRAY_SIZE (TargetLun);
+ if (ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge + 2].UnitAddress,
+ TargetLun,
+ &NumEntries
+ ) != RETURN_SUCCESS
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1],
+ TargetLun[0],
+ TargetLun[1]
+ );
+ } else if (NumNodes >= FirstNonBridge + 2 &&
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")
+ ) {
+ //
+ // OpenFirmware device path (NVMe device):
+ //
+ // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
+ // ^ ^ ^ ^ ^
+ // | | | | Extended Unique Identifier
+ // | | | | (EUI-64), big endian interp.
+ // | | | namespace ID
+ // | PCI slot & function holding NVMe controller
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path:
+ //
+ // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
+ // ^ ^
+ // | octets of the EUI-64
+ // | in address order
+ // namespace ID
+ //
+ UINT64 Namespace[2];
+ UINTN RequiredEntries;
+ UINT8 *Eui64;
+
+ RequiredEntries = ARRAY_SIZE (Namespace);
+ NumEntries = RequiredEntries;
+ if (ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge + 1].UnitAddress,
+ Namespace,
+ &NumEntries
+ ) != RETURN_SUCCESS ||
+ NumEntries != RequiredEntries ||
+ Namespace[0] == 0 ||
+ Namespace[0] >= MAX_UINT32
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Eui64 = (UINT8 *)&Namespace[1];
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
+ "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1],
+ Namespace[0],
+ Eui64[7], Eui64[6], Eui64[5], Eui64[4],
+ Eui64[3], Eui64[2], Eui64[1], Eui64[0]
+ );
+ } else if (NumNodes >= FirstNonBridge + 2 &&
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "usb") &&
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "storage")) {
+ //
+ // OpenFirmware device path (usb-storage device in XHCI port):
+ //
+ // /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0
+ // ^ ^ ^ ^ ^ ^ ^
+ // | | | | fixed fixed
+ // | | | XHCI port number, 1-based
+ // | | PCI function corresponding to XHCI (optional)
+ // | PCI slot holding XHCI
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path prefix:
+ //
+ // PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)
+ // ^ ^
+ // | XHCI port number in 0-based notation
+ // 0x0 if PCI function is 0, or absent from OFW
+ //
+ RETURN_STATUS ParseStatus;
+ UINT64 OneBasedXhciPort;
+
+ NumEntries = 1;
+ ParseStatus = ParseUnitAddressHexList (
+ OfwNode[FirstNonBridge + 1].UnitAddress,
+ &OneBasedXhciPort,
+ &NumEntries
+ );
+ if (RETURN_ERROR (ParseStatus) || OneBasedXhciPort == 0) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1],
+ OneBasedXhciPort - 1
+ );
+ } else {
+ //
+ // Generic OpenFirmware device path for PCI devices:
+ //
+ // /pci@i0cf8/ethernet@3[,2]
+ // ^ ^
+ // | PCI slot[, function] holding Ethernet card
+ // PCI root at system bus port, PIO
+ //
+ // UEFI device path prefix (dependent on presence of nonzero PCI function):
+ //
+ // PciRoot(0x0)/Pci(0x3,0x0)
+ // PciRoot(0x0)/Pci(0x3,0x2)
+ //
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
+ PciRoot,
+ Bridges,
+ PciDevFun[0],
+ PciDevFun[1]
+ );
+ }
+
+ //
+ // There's no way to differentiate between "completely used up without
+ // truncation" and "truncated", so treat the former as the latter, and return
+ // success only for "some room left unused".
+ //
+ if (Written + 1 < *TranslatedSize) {
+ *TranslatedSize = Written;
+ return RETURN_SUCCESS;
+ }
+
+ return RETURN_BUFFER_TOO_SMALL;
+}
+
+
+//
+// A type providing easy raw access to the base address of a virtio-mmio
+// transport.
+//
+typedef union {
+ UINT64 Uint64;
+ UINT8 Raw[8];
+} VIRTIO_MMIO_BASE_ADDRESS;
+
+
+/**
+
+ Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
+ path fragment.
+
+ @param[in] OfwNode Array of OpenFirmware device nodes to
+ translate, constituting the beginning of an
+ OpenFirmware device path.
+
+ @param[in] NumNodes Number of elements in OfwNode.
+
+ @param[out] Translated Destination array receiving the UEFI path
+ fragment, allocated by the caller. If the
+ return value differs from RETURN_SUCCESS, its
+ contents is indeterminate.
+
+ @param[in out] TranslatedSize On input, the number of CHAR16's in
+ Translated. On RETURN_SUCCESS this parameter
+ is assigned the number of non-NUL CHAR16's
+ written to Translated. In case of other return
+ values, TranslatedSize is indeterminate.
+
+
+ @retval RETURN_SUCCESS Translation successful.
+
+ @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
+ of bytes provided.
+
+ @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
+ be translated in the current implementation.
+
+**/
+STATIC
+RETURN_STATUS
+TranslateMmioOfwNodes (
+ IN CONST OFW_NODE *OfwNode,
+ IN UINTN NumNodes,
+ OUT CHAR16 *Translated,
+ IN OUT UINTN *TranslatedSize
+ )
+{
+ VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;
+ CHAR16 VenHwString[60 + 1];
+ UINTN NumEntries;
+ UINTN Written;
+
+ //
+ // Get the base address of the virtio-mmio transport.
+ //
+ if (NumNodes < REQUIRED_MMIO_OFW_NODES ||
+ !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+ NumEntries = 1;
+ if (ParseUnitAddressHexList (
+ OfwNode[0].UnitAddress,
+ &VirtioMmioBase.Uint64,
+ &NumEntries
+ ) != RETURN_SUCCESS
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,
+ "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,
+ VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],
+ VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],
+ VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);
+
+ if (NumNodes >= 2 &&
+ SubstringEq (OfwNode[1].DriverName, "disk")) {
+ //
+ // OpenFirmware device path (virtio-blk disk):
+ //
+ // /virtio-mmio@000000000a003c00/disk@0,0
+ // ^ ^ ^
+ // | fixed
+ // base address of virtio-mmio register block
+ //
+ // UEFI device path prefix:
+ //
+ // <VenHwString>
+ //
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "%s",
+ VenHwString
+ );
+ } else if (NumNodes >= 3 &&
+ SubstringEq (OfwNode[1].DriverName, "channel") &&
+ SubstringEq (OfwNode[2].DriverName, "disk")) {
+ //
+ // OpenFirmware device path (virtio-scsi disk):
+ //
+ // /virtio-mmio@000000000a003a00/channel@0/disk@2,3
+ // ^ ^ ^ ^
+ // | | | LUN
+ // | | target
+ // | channel (unused, fixed 0)
+ // base address of virtio-mmio register block
+ //
+ // UEFI device path prefix:
+ //
+ // <VenHwString>/Scsi(0x2,0x3)
+ //
+ UINT64 TargetLun[2];
+
+ TargetLun[1] = 0;
+ NumEntries = ARRAY_SIZE (TargetLun);
+ if (ParseUnitAddressHexList (
+ OfwNode[2].UnitAddress,
+ TargetLun,
+ &NumEntries
+ ) != RETURN_SUCCESS
+ ) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "%s/Scsi(0x%Lx,0x%Lx)",
+ VenHwString,
+ TargetLun[0],
+ TargetLun[1]
+ );
+ } else if (NumNodes >= 2 &&
+ SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {
+ //
+ // OpenFirmware device path (virtio-net NIC):
+ //
+ // /virtio-mmio@000000000a003e00/ethernet-phy@0
+ // ^ ^
+ // | fixed
+ // base address of virtio-mmio register block
+ //
+ // UEFI device path prefix:
+ //
+ // <VenHwString>
+ //
+ Written = UnicodeSPrintAsciiFormat (
+ Translated,
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
+ "%s",
+ VenHwString
+ );
+ } else {
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // There's no way to differentiate between "completely used up without
+ // truncation" and "truncated", so treat the former as the latter, and return
+ // success only for "some room left unused".
+ //
+ if (Written + 1 < *TranslatedSize) {
+ *TranslatedSize = Written;
+ return RETURN_SUCCESS;
+ }
+
+ return RETURN_BUFFER_TOO_SMALL;
+}
+
+
+/**
+
+ Translate an array of OpenFirmware device nodes to a UEFI device path
+ fragment.
+
+ @param[in] OfwNode Array of OpenFirmware device nodes to
+ translate, constituting the beginning of an
+ OpenFirmware device path.
+
+ @param[in] NumNodes Number of elements in OfwNode.
+
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
+ CreateExtraRootBusMap(), to be used for
+ translating positions of extra root buses to
+ bus numbers.
+
+ @param[out] Translated Destination array receiving the UEFI path
+ fragment, allocated by the caller. If the
+ return value differs from RETURN_SUCCESS, its
+ contents is indeterminate.
+
+ @param[in out] TranslatedSize On input, the number of CHAR16's in
+ Translated. On RETURN_SUCCESS this parameter
+ is assigned the number of non-NUL CHAR16's
+ written to Translated. In case of other return
+ values, TranslatedSize is indeterminate.
+
+
+ @retval RETURN_SUCCESS Translation successful.
+
+ @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
+ of bytes provided.
+
+ @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
+ be translated in the current implementation.
+
+ @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has
+ been (partially) recognized, but it contains
+ a logic error / doesn't match system state.
+
+**/
+STATIC
+RETURN_STATUS
+TranslateOfwNodes (
+ IN CONST OFW_NODE *OfwNode,
+ IN UINTN NumNodes,
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
+ OUT CHAR16 *Translated,
+ IN OUT UINTN *TranslatedSize
+ )
+{
+ RETURN_STATUS Status;
+
+ Status = RETURN_UNSUPPORTED;
+
+ if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
+ Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,
+ Translated, TranslatedSize);
+ }
+ if (Status == RETURN_UNSUPPORTED &&
+ FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {
+ Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,
+ TranslatedSize);
+ }
+ return Status;
+}
+
+/**
+
+ Translate an OpenFirmware device path fragment to a UEFI device path
+ fragment, and advance in the input string.
+
+ @param[in out] Ptr Address of the pointer pointing to the start
+ of the path string. After successful
+ translation (RETURN_SUCCESS) or at least
+ successful parsing (RETURN_UNSUPPORTED,
+ RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
+ byte immediately following the consumed
+ characters. In other error cases, it points to
+ the byte that caused the error.
+
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
+ CreateExtraRootBusMap(), to be used for
+ translating positions of extra root buses to
+ bus numbers.
+
+ @param[out] Translated Destination array receiving the UEFI path
+ fragment, allocated by the caller. If the
+ return value differs from RETURN_SUCCESS, its
+ contents is indeterminate.
+
+ @param[in out] TranslatedSize On input, the number of CHAR16's in
+ Translated. On RETURN_SUCCESS this parameter
+ is assigned the number of non-NUL CHAR16's
+ written to Translated. In case of other return
+ values, TranslatedSize is indeterminate.
+
+
+ @retval RETURN_SUCCESS Translation successful.
+
+ @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed
+ successfully, but its translation did not
+ fit into the number of bytes provided.
+ Further calls to this function are
+ possible.
+
+ @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed
+ successfully, but it can't be translated in
+ the current implementation. Further calls
+ to this function are possible.
+
+ @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been
+ (partially) recognized, but it contains a
+ logic error / doesn't match system state.
+ Further calls to this function are
+ possible.
+
+ @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was
+ pointing to the empty string or "HALT". On
+ output, *Ptr points to the empty string
+ (ie. "HALT" is consumed transparently when
+ present).
+
+ @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.
+
+**/
+STATIC
+RETURN_STATUS
+TranslateOfwPath (
+ IN OUT CONST CHAR8 **Ptr,
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
+ OUT CHAR16 *Translated,
+ IN OUT UINTN *TranslatedSize
+ )
+{
+ UINTN NumNodes;
+ RETURN_STATUS Status;
+ OFW_NODE Node[EXAMINED_OFW_NODES];
+ BOOLEAN IsFinal;
+ OFW_NODE Skip;
+
+ IsFinal = FALSE;
+ NumNodes = 0;
+ if (AsciiStrCmp (*Ptr, "HALT") == 0) {
+ *Ptr += 4;
+ Status = RETURN_NOT_FOUND;
+ } else {
+ Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);
+ }
+
+ if (Status == RETURN_NOT_FOUND) {
+ DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));
+ return RETURN_NOT_FOUND;
+ }
+
+ while (Status == RETURN_SUCCESS && !IsFinal) {
+ ++NumNodes;
+ Status = ParseOfwNode (
+ Ptr,
+ (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,
+ &IsFinal
+ );
+ }
+
+ switch (Status) {
+ case RETURN_SUCCESS:
+ ++NumNodes;
+ break;
+
+ case RETURN_INVALID_PARAMETER:
+ DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));
+ return RETURN_INVALID_PARAMETER;
+
+ default:
+ ASSERT (0);
+ }
+
+ Status = TranslateOfwNodes (
+ Node,
+ NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,
+ ExtraPciRoots,
+ Translated,
+ TranslatedSize);
+ switch (Status) {
+ case RETURN_SUCCESS:
+ DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));
+ break;
+
+ case RETURN_BUFFER_TOO_SMALL:
+ DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));
+ break;
+
+ case RETURN_UNSUPPORTED:
+ DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));
+ break;
+
+ case RETURN_PROTOCOL_ERROR:
+ DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",
+ __FUNCTION__));
+ break;
+
+ default:
+ ASSERT (0);
+ }
+ return Status;
+}
+
+
+/**
+ Connect devices based on the boot order retrieved from QEMU.
+
+ Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
+ OpenFirmware device paths therein to UEFI device path fragments. Connect the
+ devices identified by the UEFI devpath prefixes as narrowly as possible, then
+ connect all their child devices, recursively.
+
+ If this function fails, then platform BDS should fall back to
+ EfiBootManagerConnectAll(), or some other method for connecting any expected
+ boot devices.
+
+ @retval RETURN_SUCCESS The "bootorder" fw_cfg file has been
+ parsed, and the referenced device-subtrees
+ have been connected.
+
+ @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
+
+ @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
+ file.
+
+ @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @return Error statuses propagated from underlying
+ functions.
+**/
+RETURN_STATUS
+EFIAPI
+ConnectDevicesFromQemu (
+ VOID
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ CHAR8 *FwCfg;
+ EFI_STATUS EfiStatus;
+ EXTRA_ROOT_BUS_MAP *ExtraPciRoots;
+ CONST CHAR8 *FwCfgPtr;
+ UINTN NumConnected;
+ UINTN TranslatedSize;
+ CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
+
+ Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+
+ if (FwCfgSize == 0) {
+ return RETURN_NOT_FOUND;
+ }
+
+ FwCfg = AllocatePool (FwCfgSize);
+ if (FwCfg == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, FwCfg);
+ if (FwCfg[FwCfgSize - 1] != '\0') {
+ Status = RETURN_INVALID_PARAMETER;
+ goto FreeFwCfg;
+ }
+ DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
+ DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
+ DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
+
+ if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
+ EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);
+ if (EFI_ERROR (EfiStatus)) {
+ Status = (RETURN_STATUS)EfiStatus;
+ goto FreeFwCfg;
+ }
+ } else {
+ ExtraPciRoots = NULL;
+ }
+
+ //
+ // Translate each OpenFirmware path to a UEFI devpath prefix.
+ //
+ FwCfgPtr = FwCfg;
+ NumConnected = 0;
+ TranslatedSize = ARRAY_SIZE (Translated);
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
+ &TranslatedSize);
+ while (!RETURN_ERROR (Status)) {
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE Controller;
+
+ //
+ // Convert the UEFI devpath prefix to binary representation.
+ //
+ ASSERT (Translated[TranslatedSize] == L'\0');
+ DevicePath = ConvertTextToDevicePath (Translated);
+ if (DevicePath == NULL) {
+ Status = RETURN_OUT_OF_RESOURCES;
+ goto FreeExtraPciRoots;
+ }
+ //
+ // Advance along DevicePath, connecting the nodes individually, and asking
+ // drivers not to produce sibling nodes. Retrieve the controller handle
+ // associated with the full DevicePath -- this is the device that QEMU's
+ // OFW devpath refers to.
+ //
+ EfiStatus = EfiBootManagerConnectDevicePath (DevicePath, &Controller);
+ FreePool (DevicePath);
+ if (EFI_ERROR (EfiStatus)) {
+ Status = (RETURN_STATUS)EfiStatus;
+ goto FreeExtraPciRoots;
+ }
+ //
+ // Because QEMU's OFW devpaths have lesser expressive power than UEFI
+ // devpaths (i.e., DevicePath is considered a prefix), connect the tree
+ // rooted at Controller, recursively. If no children are produced
+ // (EFI_NOT_FOUND), that's OK.
+ //
+ EfiStatus = gBS->ConnectController (Controller, NULL, NULL, TRUE);
+ if (EFI_ERROR (EfiStatus) && EfiStatus != EFI_NOT_FOUND) {
+ Status = (RETURN_STATUS)EfiStatus;
+ goto FreeExtraPciRoots;
+ }
+ ++NumConnected;
+ //
+ // Move to the next OFW devpath.
+ //
+ TranslatedSize = ARRAY_SIZE (Translated);
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
+ &TranslatedSize);
+ }
+
+ if (Status == RETURN_NOT_FOUND && NumConnected > 0) {
+ DEBUG ((DEBUG_INFO, "%a: %Lu OpenFirmware device path(s) connected\n",
+ __FUNCTION__, (UINT64)NumConnected));
+ Status = RETURN_SUCCESS;
+ }
+
+FreeExtraPciRoots:
+ if (ExtraPciRoots != NULL) {
+ DestroyExtraRootBusMap (ExtraPciRoots);
+ }
+
+FreeFwCfg:
+ FreePool (FwCfg);
+
+ return Status;
+}
+
+
+/**
+
+ Convert the UEFI DevicePath to full text representation with DevPathToText,
+ then match the UEFI device path fragment in Translated against it.
+
+ @param[in] Translated UEFI device path fragment, translated from
+ OpenFirmware format, to search for.
+
+ @param[in] TranslatedLength The length of Translated in CHAR16's.
+
+ @param[in] DevicePath Boot option device path whose textual rendering
+ to search in.
+
+ @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.
+
+
+ @retval TRUE If Translated was found at the beginning of DevicePath after
+ converting the latter to text.
+
+ @retval FALSE If DevicePath was NULL, or it could not be converted, or there
+ was no match.
+
+**/
+STATIC
+BOOLEAN
+Match (
+ IN CONST CHAR16 *Translated,
+ IN UINTN TranslatedLength,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ CHAR16 *Converted;
+ BOOLEAN Result;
+ VOID *FileBuffer;
+ UINTN FileSize;
+ EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;
+ CHAR16 *AbsConverted;
+ BOOLEAN Shortform;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ Converted = ConvertDevicePathToText (
+ DevicePath,
+ FALSE, // DisplayOnly
+ FALSE // AllowShortcuts
+ );
+ if (Converted == NULL) {
+ return FALSE;
+ }
+
+ Result = FALSE;
+ Shortform = FALSE;
+ //
+ // Expand the short-form device path to full device path
+ //
+ if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {
+ //
+ // Harddrive shortform device path
+ //
+ Shortform = TRUE;
+ } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {
+ //
+ // File-path shortform device path
+ //
+ Shortform = TRUE;
+ } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_URI_DP)) {
+ //
+ // URI shortform device path
+ //
+ Shortform = TRUE;
+ } else {
+ for ( Node = DevicePath
+ ; !IsDevicePathEnd (Node)
+ ; Node = NextDevicePathNode (Node)
+ ) {
+ if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
+ ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||
+ (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {
+ Shortform = TRUE;
+ break;
+ }
+ }
+ }
+
+ //
+ // Attempt to expand any relative UEFI device path to
+ // an absolute device path first.
+ //
+ if (Shortform) {
+ FileBuffer = EfiBootManagerGetLoadOptionBuffer (
+ DevicePath, &AbsDevicePath, &FileSize
+ );
+ if (FileBuffer == NULL) {
+ goto Exit;
+ }
+ FreePool (FileBuffer);
+ AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);
+ FreePool (AbsDevicePath);
+ if (AbsConverted == NULL) {
+ goto Exit;
+ }
+ DEBUG ((DEBUG_VERBOSE,
+ "%a: expanded relative device path \"%s\" for prefix matching\n",
+ __FUNCTION__, Converted));
+ FreePool (Converted);
+ Converted = AbsConverted;
+ }
+
+ //
+ // Is Translated a prefix of Converted?
+ //
+ Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: against \"%s\": %a\n",
+ __FUNCTION__,
+ Converted,
+ Result ? "match" : "no match"
+ ));
+Exit:
+ FreePool (Converted);
+ return Result;
+}
+
+
+/**
+ Append some of the unselected active boot options to the boot order.
+
+ This function should accommodate any further policy changes in "boot option
+ survival". Currently we're adding back everything that starts with neither
+ PciRoot() nor HD() nor a virtio-mmio VenHw() node.
+
+ @param[in,out] BootOrder The structure holding the boot order to
+ complete. The caller is responsible for
+ initializing (and potentially populating) it
+ before calling this function.
+
+ @param[in,out] ActiveOption The array of active boot options to scan.
+ Entries marked as Appended will be skipped.
+ Those of the rest that satisfy the survival
+ policy will be added to BootOrder with
+ BootOrderAppend().
+
+ @param[in] ActiveCount Number of elements in ActiveOption.
+
+
+ @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot
+ options.
+
+ @return Error codes returned by BootOrderAppend().
+**/
+STATIC
+RETURN_STATUS
+BootOrderComplete (
+ IN OUT BOOT_ORDER *BootOrder,
+ IN OUT ACTIVE_OPTION *ActiveOption,
+ IN UINTN ActiveCount
+ )
+{
+ RETURN_STATUS Status;
+ UINTN Idx;
+
+ Status = RETURN_SUCCESS;
+ Idx = 0;
+ while (!RETURN_ERROR (Status) && Idx < ActiveCount) {
+ if (!ActiveOption[Idx].Appended) {
+ CONST EFI_BOOT_MANAGER_LOAD_OPTION *Current;
+ CONST EFI_DEVICE_PATH_PROTOCOL *FirstNode;
+
+ Current = ActiveOption[Idx].BootOption;
+ FirstNode = Current->FilePath;
+ if (FirstNode != NULL) {
+ CHAR16 *Converted;
+ STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";
+ BOOLEAN Keep;
+
+ Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);
+ if (Converted == NULL) {
+ Converted = ConvFallBack;
+ }
+
+ Keep = TRUE;
+ if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {
+ //
+ // drop HD()
+ //
+ Keep = FALSE;
+ } else if (DevicePathType(FirstNode) == ACPI_DEVICE_PATH &&
+ DevicePathSubType(FirstNode) == ACPI_DP) {
+ ACPI_HID_DEVICE_PATH *Acpi;
+
+ Acpi = (ACPI_HID_DEVICE_PATH *) FirstNode;
+ if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST &&
+ EISA_ID_TO_NUM (Acpi->HID) == 0x0a03) {
+ //
+ // drop PciRoot() if we enabled the user to select PCI-like boot
+ // options, by providing translation for such OFW device path
+ // fragments
+ //
+ Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);
+ }
+ } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&
+ DevicePathSubType(FirstNode) == HW_VENDOR_DP) {
+ VENDOR_DEVICE_PATH *VenHw;
+
+ VenHw = (VENDOR_DEVICE_PATH *)FirstNode;
+ if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {
+ //
+ // drop virtio-mmio if we enabled the user to select boot options
+ // referencing such device paths
+ //
+ Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);
+ }
+ }
+
+ if (Keep) {
+ Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);
+ if (!RETURN_ERROR (Status)) {
+ DEBUG ((DEBUG_VERBOSE, "%a: keeping \"%s\"\n", __FUNCTION__,
+ Converted));
+ }
+ } else {
+ DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,
+ Converted));
+ }
+
+ if (Converted != ConvFallBack) {
+ FreePool (Converted);
+ }
+ }
+ }
+ ++Idx;
+ }
+ return Status;
+}
+
+
+/**
+ Delete Boot#### variables that stand for such active boot options that have
+ been dropped (ie. have not been selected by either matching or "survival
+ policy").
+
+ @param[in] ActiveOption The array of active boot options to scan. Each
+ entry not marked as appended will trigger the
+ deletion of the matching Boot#### variable.
+
+ @param[in] ActiveCount Number of elements in ActiveOption.
+**/
+STATIC
+VOID
+PruneBootVariables (
+ IN CONST ACTIVE_OPTION *ActiveOption,
+ IN UINTN ActiveCount
+ )
+{
+ UINTN Idx;
+
+ for (Idx = 0; Idx < ActiveCount; ++Idx) {
+ if (!ActiveOption[Idx].Appended) {
+ CHAR16 VariableName[9];
+
+ UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",
+ ActiveOption[Idx].BootOption->OptionNumber);
+
+ //
+ // "The space consumed by the deleted variable may not be available until
+ // the next power cycle", but that's good enough.
+ //
+ gRT->SetVariable (VariableName, &gEfiGlobalVariableGuid,
+ 0, // Attributes, 0 means deletion
+ 0, // DataSize, 0 means deletion
+ NULL // Data
+ );
+ }
+ }
+}
+
+
+/**
+
+ Set the boot order based on configuration retrieved from QEMU.
+
+ Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
+ OpenFirmware device paths therein to UEFI device path fragments. Match the
+ translated fragments against the current list of boot options, and rewrite
+ the BootOrder NvVar so that it corresponds to the order described in fw_cfg.
+
+ Platform BDS should call this function after connecting any expected boot
+ devices and calling EfiBootManagerRefreshAllBootOption ().
+
+ @retval RETURN_SUCCESS BootOrder NvVar rewritten.
+
+ @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
+
+ @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
+ file, or no match found between the
+ "bootorder" fw_cfg file and BootOptionList.
+
+ @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @return Values returned by gBS->LocateProtocol ()
+ or gRT->SetVariable ().
+
+**/
+RETURN_STATUS
+EFIAPI
+SetBootOrderFromQemu (
+ VOID
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ CHAR8 *FwCfg;
+ CONST CHAR8 *FwCfgPtr;
+
+ BOOT_ORDER BootOrder;
+ ACTIVE_OPTION *ActiveOption;
+ UINTN ActiveCount;
+
+ EXTRA_ROOT_BUS_MAP *ExtraPciRoots;
+
+ UINTN TranslatedSize;
+ CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+
+ Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
+ if (Status != RETURN_SUCCESS) {
+ return Status;
+ }
+
+ if (FwCfgSize == 0) {
+ return RETURN_NOT_FOUND;
+ }
+
+ FwCfg = AllocatePool (FwCfgSize);
+ if (FwCfg == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, FwCfg);
+ if (FwCfg[FwCfgSize - 1] != '\0') {
+ Status = RETURN_INVALID_PARAMETER;
+ goto ErrorFreeFwCfg;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
+ DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
+ DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
+ FwCfgPtr = FwCfg;
+
+ BootOrder.Produced = 0;
+ BootOrder.Allocated = 1;
+ BootOrder.Data = AllocatePool (
+ BootOrder.Allocated * sizeof (*BootOrder.Data)
+ );
+ if (BootOrder.Data == NULL) {
+ Status = RETURN_OUT_OF_RESOURCES;
+ goto ErrorFreeFwCfg;
+ }
+
+ BootOptions = EfiBootManagerGetLoadOptions (
+ &BootOptionCount, LoadOptionTypeBoot
+ );
+ if (BootOptions == NULL) {
+ Status = RETURN_NOT_FOUND;
+ goto ErrorFreeBootOrder;
+ }
+
+ Status = CollectActiveOptions (
+ BootOptions, BootOptionCount, &ActiveOption, &ActiveCount
+ );
+ if (RETURN_ERROR (Status)) {
+ goto ErrorFreeBootOptions;
+ }
+
+ if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
+ Status = CreateExtraRootBusMap (&ExtraPciRoots);
+ if (EFI_ERROR (Status)) {
+ goto ErrorFreeActiveOption;
+ }
+ } else {
+ ExtraPciRoots = NULL;
+ }
+
+ //
+ // translate each OpenFirmware path
+ //
+ TranslatedSize = ARRAY_SIZE (Translated);
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
+ &TranslatedSize);
+ while (Status == RETURN_SUCCESS ||
+ Status == RETURN_UNSUPPORTED ||
+ Status == RETURN_PROTOCOL_ERROR ||
+ Status == RETURN_BUFFER_TOO_SMALL) {
+ if (Status == RETURN_SUCCESS) {
+ UINTN Idx;
+
+ //
+ // match translated OpenFirmware path against all active boot options
+ //
+ for (Idx = 0; Idx < ActiveCount; ++Idx) {
+ if (!ActiveOption[Idx].Appended &&
+ Match (
+ Translated,
+ TranslatedSize, // contains length, not size, in CHAR16's here
+ ActiveOption[Idx].BootOption->FilePath
+ )
+ ) {
+ //
+ // match found, store ID and continue with next OpenFirmware path
+ //
+ Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
+ if (Status != RETURN_SUCCESS) {
+ goto ErrorFreeExtraPciRoots;
+ }
+ }
+ } // scanned all active boot options
+ } // translation successful
+
+ TranslatedSize = ARRAY_SIZE (Translated);
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
+ &TranslatedSize);
+ } // scanning of OpenFirmware paths done
+
+ if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {
+ //
+ // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
+ // Some of the active boot options that have not been selected over fw_cfg
+ // should be preserved at the end of the boot order.
+ //
+ Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
+ if (RETURN_ERROR (Status)) {
+ goto ErrorFreeExtraPciRoots;
+ }
+
+ //
+ // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
+ // attributes.
+ //
+ Status = gRT->SetVariable (
+ L"BootOrder",
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ BootOrder.Produced * sizeof (*BootOrder.Data),
+ BootOrder.Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__,
+ Status));
+ goto ErrorFreeExtraPciRoots;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));
+ PruneBootVariables (ActiveOption, ActiveCount);
+ }
+
+ErrorFreeExtraPciRoots:
+ if (ExtraPciRoots != NULL) {
+ DestroyExtraRootBusMap (ExtraPciRoots);
+ }
+
+ErrorFreeActiveOption:
+ FreePool (ActiveOption);
+
+ErrorFreeBootOptions:
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+
+ErrorFreeBootOrder:
+ FreePool (BootOrder.Data);
+
+ErrorFreeFwCfg:
+ FreePool (FwCfg);
+
+ return Status;
+}
+
+
+/**
+ Calculate the number of seconds we should be showing the FrontPage progress
+ bar for.
+
+ @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().
+**/
+UINT16
+EFIAPI
+GetFrontPageTimeoutFromQemu (
+ VOID
+ )
+{
+ FIRMWARE_CONFIG_ITEM BootMenuWaitItem;
+ UINTN BootMenuWaitSize;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);
+ if (QemuFwCfgRead16 () == 0) {
+ //
+ // The user specified "-boot menu=off", or didn't specify "-boot
+ // menu=(on|off)" at all. Return the platform default.
+ //
+ return PcdGet16 (PcdPlatformBootTimeOut);
+ }
+
+ if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,
+ &BootMenuWaitSize)) ||
+ BootMenuWaitSize != sizeof (UINT16)) {
+ //
+ // "-boot menu=on" was specified without "splash-time=N". In this case,
+ // return three seconds if the platform default would cause us to skip the
+ // front page, and return the platform default otherwise.
+ //
+ UINT16 Timeout;
+
+ Timeout = PcdGet16 (PcdPlatformBootTimeOut);
+ if (Timeout == 0) {
+ Timeout = 3;
+ }
+ return Timeout;
+ }
+
+ //
+ // "-boot menu=on,splash-time=N" was specified, where N is in units of
+ // milliseconds. The Intel BDS Front Page progress bar only supports whole
+ // seconds, round N up.
+ //
+ QemuFwCfgSelectItem (BootMenuWaitItem);
+ return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf new file mode 100644 index 000000000..7c02f04e7 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf @@ -0,0 +1,62 @@ +## @file
+# Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
+#
+# Copyright (C) 2012 - 2014, Red Hat, Inc.
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = QemuBootOrderLib
+ FILE_GUID = 1D677A58-C753-4AF1-B552-EFE142DF8F57
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuBootOrderLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC ARM AARCH64
+#
+
+[Sources]
+ ExtraRootBusMap.c
+ ExtraRootBusMap.h
+ QemuBootOrderLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ QemuFwCfgLib
+ DebugLib
+ MemoryAllocationLib
+ UefiBootManagerLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ BaseLib
+ PrintLib
+ DevicePathLib
+ BaseMemoryLib
+ OrderedCollectionLib
+
+[Guids]
+ gEfiGlobalVariableGuid
+ gVirtioMmioTransportGuid
+
+[FeaturePcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation
+ gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
+
+[Protocols]
+ gEfiDevicePathProtocolGuid ## CONSUMES
+ gEfiPciRootBridgeIoProtocolGuid ## CONSUMES
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c new file mode 100644 index 000000000..0182c9235 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c @@ -0,0 +1,453 @@ +/** @file
+
+ Stateful and implicitly initialized fw_cfg library implementation.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/IoMmu.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemEncryptSevLib.h>
+
+#include "QemuFwCfgLibInternal.h"
+
+STATIC BOOLEAN mQemuFwCfgSupported = FALSE;
+STATIC BOOLEAN mQemuFwCfgDmaSupported;
+
+STATIC EDKII_IOMMU_PROTOCOL *mIoMmuProtocol;
+
+/**
+ Returns a boolean indicating if the firmware configuration interface
+ is available or not.
+
+ This function may change fw_cfg state.
+
+ @retval TRUE The interface is available
+ @retval FALSE The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return InternalQemuFwCfgIsAvailable ();
+}
+
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgInitialize (
+ VOID
+ )
+{
+ UINT32 Signature;
+ UINT32 Revision;
+
+ //
+ // Enable the access routines while probing to see if it is supported.
+ // For probing we always use the IO Port (IoReadFifo8()) access method.
+ //
+ mQemuFwCfgSupported = TRUE;
+ mQemuFwCfgDmaSupported = FALSE;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemSignature);
+ Signature = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));
+ QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+ Revision = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));
+ if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
+ (Revision < 1)
+ ) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));
+ mQemuFwCfgSupported = FALSE;
+ return RETURN_SUCCESS;
+ }
+
+ if ((Revision & FW_CFG_F_DMA) == 0) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));
+ } else {
+ mQemuFwCfgDmaSupported = TRUE;
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
+ }
+
+ if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {
+ EFI_STATUS Status;
+
+ //
+ // IoMmuDxe driver must have installed the IOMMU protocol. If we are not
+ // able to locate the protocol then something must have gone wrong.
+ //
+ Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL,
+ (VOID **)&mIoMmuProtocol);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+ }
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Returns a boolean indicating if the firmware configuration interface is
+ available for library-internal purposes.
+
+ This function never changes fw_cfg state.
+
+ @retval TRUE The interface is available internally.
+ @retval FALSE The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return mQemuFwCfgSupported;
+}
+
+/**
+ Returns a boolean indicating whether QEMU provides the DMA-like access method
+ for fw_cfg.
+
+ @retval TRUE The DMA-like access method is available.
+ @retval FALSE The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+ VOID
+ )
+{
+ return mQemuFwCfgDmaSupported;
+}
+
+/**
+ Function is used for allocating a bi-directional FW_CFG_DMA_ACCESS used
+ between Host and device to exchange the information. The buffer must be free'd
+ using FreeFwCfgDmaAccessBuffer ().
+
+**/
+STATIC
+VOID
+AllocFwCfgDmaAccessBuffer (
+ OUT VOID **Access,
+ OUT VOID **MapInfo
+ )
+{
+ UINTN Size;
+ UINTN NumPages;
+ EFI_STATUS Status;
+ VOID *HostAddress;
+ EFI_PHYSICAL_ADDRESS DmaAddress;
+ VOID *Mapping;
+
+ Size = sizeof (FW_CFG_DMA_ACCESS);
+ NumPages = EFI_SIZE_TO_PAGES (Size);
+
+ //
+ // As per UEFI spec, in order to map a host address with
+ // BusMasterCommonBuffer64, the buffer must be allocated using the IOMMU
+ // AllocateBuffer()
+ //
+ Status = mIoMmuProtocol->AllocateBuffer (
+ mIoMmuProtocol,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ NumPages,
+ &HostAddress,
+ EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to allocate FW_CFG_DMA_ACCESS\n", gEfiCallerBaseName,
+ __FUNCTION__));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ //
+ // Avoid exposing stale data even temporarily: zero the area before mapping
+ // it.
+ //
+ ZeroMem (HostAddress, Size);
+
+ //
+ // Map the host buffer with BusMasterCommonBuffer64
+ //
+ Status = mIoMmuProtocol->Map (
+ mIoMmuProtocol,
+ EdkiiIoMmuOperationBusMasterCommonBuffer64,
+ HostAddress,
+ &Size,
+ &DmaAddress,
+ &Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to Map() FW_CFG_DMA_ACCESS\n", gEfiCallerBaseName,
+ __FUNCTION__));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ if (Size < sizeof (FW_CFG_DMA_ACCESS)) {
+ mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to Map() - requested 0x%Lx got 0x%Lx\n", gEfiCallerBaseName,
+ __FUNCTION__, (UINT64)sizeof (FW_CFG_DMA_ACCESS), (UINT64)Size));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ *Access = HostAddress;
+ *MapInfo = Mapping;
+}
+
+/**
+ Function is to used for freeing the Access buffer allocated using
+ AllocFwCfgDmaAccessBuffer()
+
+**/
+STATIC
+VOID
+FreeFwCfgDmaAccessBuffer (
+ IN VOID *Access,
+ IN VOID *Mapping
+ )
+{
+ UINTN NumPages;
+ EFI_STATUS Status;
+
+ NumPages = EFI_SIZE_TO_PAGES (sizeof (FW_CFG_DMA_ACCESS));
+
+ Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to UnMap() Mapping 0x%Lx\n", gEfiCallerBaseName,
+ __FUNCTION__, (UINT64)(UINTN)Mapping));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ Status = mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, Access);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to Free() 0x%Lx\n", gEfiCallerBaseName, __FUNCTION__,
+ (UINT64)(UINTN)Access));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+}
+
+/**
+ Function is used for mapping host address to device address. The buffer must
+ be unmapped with UnmapDmaDataBuffer ().
+
+**/
+STATIC
+VOID
+MapFwCfgDmaDataBuffer (
+ IN BOOLEAN IsWrite,
+ IN VOID *HostAddress,
+ IN UINT32 Size,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **MapInfo
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumberOfBytes;
+ VOID *Mapping;
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;
+
+ NumberOfBytes = Size;
+ Status = mIoMmuProtocol->Map (
+ mIoMmuProtocol,
+ (IsWrite ?
+ EdkiiIoMmuOperationBusMasterRead64 :
+ EdkiiIoMmuOperationBusMasterWrite64),
+ HostAddress,
+ &NumberOfBytes,
+ &PhysicalAddress,
+ &Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to Map() Address 0x%Lx Size 0x%Lx\n", gEfiCallerBaseName,
+ __FUNCTION__, (UINT64)(UINTN)HostAddress, (UINT64)Size));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ if (NumberOfBytes < Size) {
+ mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to Map() - requested 0x%x got 0x%Lx\n", gEfiCallerBaseName,
+ __FUNCTION__, Size, (UINT64)NumberOfBytes));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ *DeviceAddress = PhysicalAddress;
+ *MapInfo = Mapping;
+}
+
+STATIC
+VOID
+UnmapFwCfgDmaDataBuffer (
+ IN VOID *Mapping
+ )
+{
+ EFI_STATUS Status;
+
+ Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a:%a failed to UnMap() Mapping 0x%Lx\n", gEfiCallerBaseName,
+ __FUNCTION__, (UINT64)(UINTN)Mapping));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+}
+
+/**
+ Transfer an array of bytes, or skip a number of bytes, using the DMA
+ interface.
+
+ @param[in] Size Size in bytes to transfer or skip.
+
+ @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
+ and may be NULL, if Size is zero, or Control is
+ FW_CFG_DMA_CTL_SKIP.
+
+ @param[in] Control One of the following:
+ FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+ FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
+ FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+ IN UINT32 Size,
+ IN OUT VOID *Buffer OPTIONAL,
+ IN UINT32 Control
+ )
+{
+ volatile FW_CFG_DMA_ACCESS LocalAccess;
+ volatile FW_CFG_DMA_ACCESS *Access;
+ UINT32 AccessHigh, AccessLow;
+ UINT32 Status;
+ VOID *AccessMapping, *DataMapping;
+ VOID *DataBuffer;
+
+ ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
+ Control == FW_CFG_DMA_CTL_SKIP);
+
+ if (Size == 0) {
+ return;
+ }
+
+ Access = &LocalAccess;
+ AccessMapping = NULL;
+ DataMapping = NULL;
+ DataBuffer = Buffer;
+
+ //
+ // When SEV is enabled, map Buffer to DMA address before issuing the DMA
+ // request
+ //
+ if (MemEncryptSevIsEnabled ()) {
+ VOID *AccessBuffer;
+ EFI_PHYSICAL_ADDRESS DataBufferAddress;
+
+ //
+ // Allocate DMA Access buffer
+ //
+ AllocFwCfgDmaAccessBuffer (&AccessBuffer, &AccessMapping);
+
+ Access = AccessBuffer;
+
+ //
+ // Map actual data buffer
+ //
+ if (Control != FW_CFG_DMA_CTL_SKIP) {
+ MapFwCfgDmaDataBuffer (
+ Control == FW_CFG_DMA_CTL_WRITE,
+ Buffer,
+ Size,
+ &DataBufferAddress,
+ &DataMapping
+ );
+
+ DataBuffer = (VOID *) (UINTN) DataBufferAddress;
+ }
+ }
+
+ Access->Control = SwapBytes32 (Control);
+ Access->Length = SwapBytes32 (Size);
+ Access->Address = SwapBytes64 ((UINTN)DataBuffer);
+
+ //
+ // Delimit the transfer from (a) modifications to Access, (b) in case of a
+ // write, from writes to Buffer by the caller.
+ //
+ MemoryFence ();
+
+ //
+ // Start the transfer.
+ //
+ AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32);
+ AccessLow = (UINT32)(UINTN)Access;
+ IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh));
+ IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow));
+
+ //
+ // Don't look at Access.Control before starting the transfer.
+ //
+ MemoryFence ();
+
+ //
+ // Wait for the transfer to complete.
+ //
+ do {
+ Status = SwapBytes32 (Access->Control);
+ ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);
+ } while (Status != 0);
+
+ //
+ // After a read, the caller will want to use Buffer.
+ //
+ MemoryFence ();
+
+ //
+ // If Access buffer was dynamically allocated then free it.
+ //
+ if (AccessMapping != NULL) {
+ FreeFwCfgDmaAccessBuffer ((VOID *)Access, AccessMapping);
+ }
+
+ //
+ // If DataBuffer was mapped then unmap it.
+ //
+ if (DataMapping != NULL) {
+ UnmapFwCfgDmaDataBuffer (DataMapping);
+ }
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf new file mode 100644 index 000000000..48899ff12 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf @@ -0,0 +1,51 @@ +## @file
+#
+# Stateful, implicitly initialized fw_cfg library.
+#
+# Copyright (C) 2013, Red Hat, Inc.
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = QemuFwCfgDxeLib
+ FILE_GUID = 80474090-55e7-4c28-b25c-9f236ba41f28
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER
+
+ CONSTRUCTOR = QemuFwCfgInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ QemuFwCfgLibInternal.h
+ QemuFwCfgLib.c
+ QemuFwCfgDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ IoLib
+ MemoryAllocationLib
+ MemEncryptSevLib
+
+[Protocols]
+ gEdkiiIoMmuProtocolGuid ## SOMETIMES_CONSUMES
+
+[Depex]
+ gEdkiiIoMmuProtocolGuid OR gIoMmuAbsentProtocolGuid
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c new file mode 100644 index 000000000..4ef78b7a8 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c @@ -0,0 +1,292 @@ +/** @file
+
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Uefi.h"
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "QemuFwCfgLibInternal.h"
+
+
+/**
+ Selects a firmware configuration item for reading.
+
+ Following this call, any data read from this item will start from
+ the beginning of the configuration item's data.
+
+ @param[in] QemuFwCfgItem - Firmware Configuration item to read
+
+**/
+VOID
+EFIAPI
+QemuFwCfgSelectItem (
+ IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem
+ )
+{
+ DEBUG ((DEBUG_INFO, "Select Item: 0x%x\n", (UINT16)(UINTN) QemuFwCfgItem));
+ IoWrite16 (FW_CFG_IO_SELECTOR, (UINT16)(UINTN) QemuFwCfgItem);
+}
+
+/**
+ Reads firmware configuration bytes into a buffer
+
+ @param[in] Size - Size in bytes to read
+ @param[in] Buffer - Buffer to store data into (OPTIONAL if Size is 0)
+
+**/
+VOID
+EFIAPI
+InternalQemuFwCfgReadBytes (
+ IN UINTN Size,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
+ InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_READ);
+ return;
+ }
+ IoReadFifo8 (FW_CFG_IO_DATA, Size, Buffer);
+}
+
+
+/**
+ Reads firmware configuration bytes into a buffer
+
+ If called multiple times, then the data read will
+ continue at the offset of the firmware configuration
+ item where the previous read ended.
+
+ @param[in] Size - Size in bytes to read
+ @param[in] Buffer - Buffer to store data into
+
+**/
+VOID
+EFIAPI
+QemuFwCfgReadBytes (
+ IN UINTN Size,
+ IN VOID *Buffer
+ )
+{
+ if (InternalQemuFwCfgIsAvailable ()) {
+ InternalQemuFwCfgReadBytes (Size, Buffer);
+ } else {
+ ZeroMem (Buffer, Size);
+ }
+}
+
+/**
+ Write firmware configuration bytes from a buffer
+
+ If called multiple times, then the data written will
+ continue at the offset of the firmware configuration
+ item where the previous write ended.
+
+ @param[in] Size - Size in bytes to write
+ @param[in] Buffer - Buffer to read data from
+
+**/
+VOID
+EFIAPI
+QemuFwCfgWriteBytes (
+ IN UINTN Size,
+ IN VOID *Buffer
+ )
+{
+ if (InternalQemuFwCfgIsAvailable ()) {
+ if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
+ InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_WRITE);
+ return;
+ }
+ IoWriteFifo8 (FW_CFG_IO_DATA, Size, Buffer);
+ }
+}
+
+
+/**
+ Skip bytes in the firmware configuration item.
+
+ Increase the offset of the firmware configuration item without transferring
+ bytes between the item and a caller-provided buffer. Subsequent read, write
+ or skip operations will commence at the increased offset.
+
+ @param[in] Size Number of bytes to skip.
+**/
+VOID
+EFIAPI
+QemuFwCfgSkipBytes (
+ IN UINTN Size
+ )
+{
+ UINTN ChunkSize;
+ UINT8 SkipBuffer[256];
+
+ if (!InternalQemuFwCfgIsAvailable ()) {
+ return;
+ }
+
+ if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
+ InternalQemuFwCfgDmaBytes ((UINT32)Size, NULL, FW_CFG_DMA_CTL_SKIP);
+ return;
+ }
+
+ //
+ // Emulate the skip by reading data in chunks, and throwing it away. The
+ // implementation below is suitable even for phases where RAM or dynamic
+ // allocation is not available or appropriate. It also doesn't affect the
+ // static data footprint for client modules. Large skips are not expected,
+ // therefore this fallback is not performance critical. The size of
+ // SkipBuffer is thought not to exert a large pressure on the stack in any
+ // phase.
+ //
+ while (Size > 0) {
+ ChunkSize = MIN (Size, sizeof SkipBuffer);
+ IoReadFifo8 (FW_CFG_IO_DATA, ChunkSize, SkipBuffer);
+ Size -= ChunkSize;
+ }
+}
+
+
+/**
+ Reads a UINT8 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT8
+EFIAPI
+QemuFwCfgRead8 (
+ VOID
+ )
+{
+ UINT8 Result;
+
+ QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+ return Result;
+}
+
+
+/**
+ Reads a UINT16 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT16
+EFIAPI
+QemuFwCfgRead16 (
+ VOID
+ )
+{
+ UINT16 Result;
+
+ QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+ return Result;
+}
+
+
+/**
+ Reads a UINT32 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT32
+EFIAPI
+QemuFwCfgRead32 (
+ VOID
+ )
+{
+ UINT32 Result;
+
+ QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+ return Result;
+}
+
+
+/**
+ Reads a UINT64 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT64
+EFIAPI
+QemuFwCfgRead64 (
+ VOID
+ )
+{
+ UINT64 Result;
+
+ QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+ return Result;
+}
+
+
+/**
+ Find the configuration item corresponding to the firmware configuration file.
+
+ @param[in] Name - Name of file to look up.
+ @param[out] Item - Configuration item corresponding to the file, to be passed
+ to QemuFwCfgSelectItem ().
+ @param[out] Size - Number of bytes in the file.
+
+ @return RETURN_SUCCESS If file is found.
+ RETURN_NOT_FOUND If file is not found.
+ RETURN_UNSUPPORTED If firmware configuration is unavailable.
+
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgFindFile (
+ IN CONST CHAR8 *Name,
+ OUT FIRMWARE_CONFIG_ITEM *Item,
+ OUT UINTN *Size
+ )
+{
+ UINT32 Count;
+ UINT32 Idx;
+
+ if (!InternalQemuFwCfgIsAvailable ()) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ QemuFwCfgSelectItem (QemuFwCfgItemFileDir);
+ Count = SwapBytes32 (QemuFwCfgRead32 ());
+
+ for (Idx = 0; Idx < Count; ++Idx) {
+ UINT32 FileSize;
+ UINT16 FileSelect;
+ UINT16 FileReserved;
+ CHAR8 FName[QEMU_FW_CFG_FNAME_SIZE];
+
+ FileSize = QemuFwCfgRead32 ();
+ FileSelect = QemuFwCfgRead16 ();
+ FileReserved = QemuFwCfgRead16 ();
+ (VOID) FileReserved; /* Force a do-nothing reference. */
+ InternalQemuFwCfgReadBytes (sizeof (FName), FName);
+
+ if (AsciiStrCmp (Name, FName) == 0) {
+ *Item = SwapBytes16 (FileSelect);
+ *Size = SwapBytes32 (FileSize);
+ return RETURN_SUCCESS;
+ }
+ }
+
+ return RETURN_NOT_FOUND;
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h new file mode 100644 index 000000000..1fa80686e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h @@ -0,0 +1,63 @@ +/** @file
+ Internal interfaces specific to the QemuFwCfgLib instances in OvmfPkg.
+
+ Copyright (C) 2016, Red Hat, Inc.
+ Copyright (C) 2017, Advanced Micro Devices. All rights reserved
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __QEMU_FW_CFG_LIB_INTERNAL_H__
+#define __QEMU_FW_CFG_LIB_INTERNAL_H__
+
+/**
+ Returns a boolean indicating if the firmware configuration interface is
+ available for library-internal purposes.
+
+ This function never changes fw_cfg state.
+
+ @retval TRUE The interface is available internally.
+ @retval FALSE The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+ VOID
+ );
+
+
+/**
+ Returns a boolean indicating whether QEMU provides the DMA-like access method
+ for fw_cfg.
+
+ @retval TRUE The DMA-like access method is available.
+ @retval FALSE The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+ VOID
+ );
+
+/**
+ Transfer an array of bytes, or skip a number of bytes, using the DMA
+ interface.
+
+ @param[in] Size Size in bytes to transfer or skip.
+
+ @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
+ and may be NULL, if Size is zero, or Control is
+ FW_CFG_DMA_CTL_SKIP.
+
+ @param[in] Control One of the following:
+ FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+ FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
+ FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+ IN UINT32 Size,
+ IN OUT VOID *Buffer OPTIONAL,
+ IN UINT32 Control
+ );
+
+#endif
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibNull.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibNull.inf new file mode 100644 index 000000000..e5715a643 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibNull.inf @@ -0,0 +1,30 @@ +## @file
+#
+# Null implementation of the fw_cfg library.
+#
+# Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
+# Copyright (C) 2013, Red Hat, Inc.
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = QemuFwCfgLibNull
+ FILE_GUID = B9D1A1F2-01E2-4732-982D-C7F9ED51AC6B
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgLib
+
+[Sources]
+ QemuFwCfgNull.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ DebugLib
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgNull.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgNull.c new file mode 100644 index 000000000..edd97db49 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgNull.c @@ -0,0 +1,209 @@ +/** @file
+
+ Null implementation of the fw_cfg library.
+
+ Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+
+/**
+ Returns a boolean indicating if the firmware configuration interface
+ is available or not.
+
+ This function may change fw_cfg state.
+
+ @retval TRUE The interface is available
+ @retval FALSE The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+
+/**
+ Selects a firmware configuration item for reading.
+
+ Following this call, any data read from this item will start from
+ the beginning of the configuration item's data.
+
+ @param[in] QemuFwCfgItem - Firmware Configuration item to read
+
+**/
+VOID
+EFIAPI
+QemuFwCfgSelectItem (
+ IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem
+ )
+{
+ ASSERT (FALSE);
+}
+
+
+/**
+ Reads firmware configuration bytes into a buffer
+
+ If called multiple times, then the data read will
+ continue at the offset of the firmware configuration
+ item where the previous read ended.
+
+ @param[in] Size - Size in bytes to read
+ @param[in] Buffer - Buffer to store data into
+
+**/
+VOID
+EFIAPI
+QemuFwCfgReadBytes (
+ IN UINTN Size,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ ASSERT (FALSE);
+}
+
+
+/**
+ Writes firmware configuration bytes from a buffer
+
+ If called multiple times, then the data written will
+ continue at the offset of the firmware configuration
+ item where the previous write ended.
+
+ @param[in] Size - Size in bytes to write
+ @param[in] Buffer - Buffer to read data from
+
+**/
+VOID
+EFIAPI
+QemuFwCfgWriteBytes (
+ IN UINTN Size,
+ IN VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+}
+
+
+/**
+ Skip bytes in the firmware configuration item.
+
+ Increase the offset of the firmware configuration item without transferring
+ bytes between the item and a caller-provided buffer. Subsequent read, write
+ or skip operations will commence at the increased offset.
+
+ @param[in] Size Number of bytes to skip.
+**/
+VOID
+EFIAPI
+QemuFwCfgSkipBytes (
+ IN UINTN Size
+ )
+{
+ ASSERT (FALSE);
+}
+
+
+/**
+ Reads a UINT8 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT8
+EFIAPI
+QemuFwCfgRead8 (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+
+/**
+ Reads a UINT16 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT16
+EFIAPI
+QemuFwCfgRead16 (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+
+/**
+ Reads a UINT32 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT32
+EFIAPI
+QemuFwCfgRead32 (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+
+/**
+ Reads a UINT64 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT64
+EFIAPI
+QemuFwCfgRead64 (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+
+/**
+ Find the configuration item corresponding to the firmware configuration file.
+
+ @param[in] Name - Name of file to look up.
+ @param[out] Item - Configuration item corresponding to the file, to be passed
+ to QemuFwCfgSelectItem ().
+ @param[out] Size - Number of bytes in the file.
+
+ @return RETURN_SUCCESS If file is found.
+ RETURN_NOT_FOUND If file is not found.
+ RETURN_UNSUPPORTED If firmware configuration is unavailable.
+
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgFindFile (
+ IN CONST CHAR8 *Name,
+ OUT FIRMWARE_CONFIG_ITEM *Item,
+ OUT UINTN *Size
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c new file mode 100644 index 000000000..ecabd88fa --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c @@ -0,0 +1,201 @@ +/** @file
+
+ Stateful and implicitly initialized fw_cfg library implementation.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/MemEncryptSevLib.h>
+
+#include "QemuFwCfgLibInternal.h"
+
+STATIC BOOLEAN mQemuFwCfgSupported = FALSE;
+STATIC BOOLEAN mQemuFwCfgDmaSupported;
+
+
+/**
+ Returns a boolean indicating if the firmware configuration interface
+ is available or not.
+
+ This function may change fw_cfg state.
+
+ @retval TRUE The interface is available
+ @retval FALSE The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return InternalQemuFwCfgIsAvailable ();
+}
+
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgInitialize (
+ VOID
+ )
+{
+ UINT32 Signature;
+ UINT32 Revision;
+
+ //
+ // Enable the access routines while probing to see if it is supported.
+ // For probing we always use the IO Port (IoReadFifo8()) access method.
+ //
+ mQemuFwCfgSupported = TRUE;
+ mQemuFwCfgDmaSupported = FALSE;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemSignature);
+ Signature = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));
+ QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+ Revision = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));
+ if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
+ (Revision < 1)
+ ) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));
+ mQemuFwCfgSupported = FALSE;
+ return RETURN_SUCCESS;
+ }
+
+ if ((Revision & FW_CFG_F_DMA) == 0) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));
+ } else {
+ //
+ // If SEV is enabled then we do not support DMA operations in PEI phase.
+ // This is mainly because DMA in SEV guest requires using bounce buffer
+ // (which need to allocate dynamic memory and allocating a PAGE size'd
+ // buffer can be challenge in PEI phase)
+ //
+ if (MemEncryptSevIsEnabled ()) {
+ DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port interface.\n"));
+ } else {
+ mQemuFwCfgDmaSupported = TRUE;
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
+ }
+ }
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Returns a boolean indicating if the firmware configuration interface is
+ available for library-internal purposes.
+
+ This function never changes fw_cfg state.
+
+ @retval TRUE The interface is available internally.
+ @retval FALSE The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return mQemuFwCfgSupported;
+}
+
+/**
+ Returns a boolean indicating whether QEMU provides the DMA-like access method
+ for fw_cfg.
+
+ @retval TRUE The DMA-like access method is available.
+ @retval FALSE The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+ VOID
+ )
+{
+ return mQemuFwCfgDmaSupported;
+}
+
+/**
+ Transfer an array of bytes, or skip a number of bytes, using the DMA
+ interface.
+
+ @param[in] Size Size in bytes to transfer or skip.
+
+ @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
+ and may be NULL, if Size is zero, or Control is
+ FW_CFG_DMA_CTL_SKIP.
+
+ @param[in] Control One of the following:
+ FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+ FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
+ FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+ IN UINT32 Size,
+ IN OUT VOID *Buffer OPTIONAL,
+ IN UINT32 Control
+ )
+{
+ volatile FW_CFG_DMA_ACCESS Access;
+ UINT32 AccessHigh, AccessLow;
+ UINT32 Status;
+
+ ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
+ Control == FW_CFG_DMA_CTL_SKIP);
+
+ if (Size == 0) {
+ return;
+ }
+
+ //
+ // SEV does not support DMA operations in PEI stage, we should
+ // not have reached here.
+ //
+ ASSERT (!MemEncryptSevIsEnabled ());
+
+ Access.Control = SwapBytes32 (Control);
+ Access.Length = SwapBytes32 (Size);
+ Access.Address = SwapBytes64 ((UINTN)Buffer);
+
+ //
+ // Delimit the transfer from (a) modifications to Access, (b) in case of a
+ // write, from writes to Buffer by the caller.
+ //
+ MemoryFence ();
+
+ //
+ // Start the transfer.
+ //
+ AccessHigh = (UINT32)RShiftU64 ((UINTN)&Access, 32);
+ AccessLow = (UINT32)(UINTN)&Access;
+ IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh));
+ IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow));
+
+ //
+ // Don't look at Access.Control before starting the transfer.
+ //
+ MemoryFence ();
+
+ //
+ // Wait for the transfer to complete.
+ //
+ do {
+ Status = SwapBytes32 (Access.Control);
+ ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);
+ } while (Status != 0);
+
+ //
+ // After a read, the caller will want to use Buffer.
+ //
+ MemoryFence ();
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf new file mode 100644 index 000000000..9f9af7d03 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf @@ -0,0 +1,45 @@ +## @file
+#
+# Stateful, implicitly initialized fw_cfg library.
+#
+# Copyright (C) 2013, Red Hat, Inc.
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = QemuFwCfgPeiLib
+ FILE_GUID = ddd4f5f0-5304-42a8-9efa-d14bf11a3533
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgLib|PEIM
+
+ CONSTRUCTOR = QemuFwCfgInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ QemuFwCfgLibInternal.h
+ QemuFwCfgLib.c
+ QemuFwCfgPei.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ IoLib
+ MemoryAllocationLib
+ MemEncryptSevLib
+
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c new file mode 100644 index 000000000..94e8e9218 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c @@ -0,0 +1,121 @@ +/** @file
+
+ Stateless fw_cfg library implementation.
+
+ Clients must call QemuFwCfgIsAvailable() first.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+
+#include "QemuFwCfgLibInternal.h"
+
+/**
+ Returns a boolean indicating if the firmware configuration interface
+ is available or not.
+
+ This function may change fw_cfg state.
+
+ @retval TRUE The interface is available
+ @retval FALSE The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ UINT32 Signature;
+ UINT32 Revision;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemSignature);
+ Signature = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));
+ QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+ Revision = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));
+ if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
+ (Revision < 1)
+ ) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));
+ return FALSE;
+ }
+
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface is supported.\n"));
+ return TRUE;
+}
+
+
+/**
+ Returns a boolean indicating if the firmware configuration interface is
+ available for library-internal purposes.
+
+ This function never changes fw_cfg state.
+
+ @retval TRUE The interface is available internally.
+ @retval FALSE The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ //
+ // We always return TRUE, because the consumer of this library ought to have
+ // called QemuFwCfgIsAvailable before making other calls which would hit this
+ // path.
+ //
+ return TRUE;
+}
+
+/**
+ Returns a boolean indicating whether QEMU provides the DMA-like access method
+ for fw_cfg.
+
+ @retval TRUE The DMA-like access method is available.
+ @retval FALSE The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+/**
+ Transfer an array of bytes, or skip a number of bytes, using the DMA
+ interface.
+
+ @param[in] Size Size in bytes to transfer or skip.
+
+ @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
+ and may be NULL, if Size is zero, or Control is
+ FW_CFG_DMA_CTL_SKIP.
+
+ @param[in] Control One of the following:
+ FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+ FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
+ FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+ IN UINT32 Size,
+ IN OUT VOID *Buffer OPTIONAL,
+ IN UINT32 Control
+ )
+{
+ //
+ // We should never reach here
+ //
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSecLib.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSecLib.inf new file mode 100644 index 000000000..d34edd6e6 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSecLib.inf @@ -0,0 +1,42 @@ +## @file
+#
+# Stateless fw_cfg library that must be queried before use.
+#
+# Copyright (C) 2013, Red Hat, Inc.
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = QemuFwCfgSecLib
+ FILE_GUID = 60a910e5-7443-413d-9a30-97e57497cd1b
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgLib|SEC
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ QemuFwCfgLibInternal.h
+ QemuFwCfgLib.c
+ QemuFwCfgSec.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ IoLib
+ MemoryAllocationLib
+
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf new file mode 100644 index 000000000..c83b78d42 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf @@ -0,0 +1,38 @@ +## @file
+# Base Null library instance of the QemuFwCfgS3Lib class.
+#
+# This library instance returns constant FALSE from QemuFwCfgS3Enabled(), and
+# all other library functions trigger assertion failures. It is suitable for
+# QEMU targets and machine types that never enable S3.
+#
+# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (C) 2017, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = BaseQemuFwCfgS3LibNull
+ FILE_GUID = EA7D2B69-D221-4950-9C2C-C38A65BCC96E
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgS3Lib
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 EBC
+#
+
+[Sources]
+ QemuFwCfgS3Base.c
+ QemuFwCfgS3BasePei.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ DebugLib
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf new file mode 100644 index 000000000..10f41d64c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf @@ -0,0 +1,41 @@ +## @file
+# Full functionality QemuFwCfgS3Lib instance, for DXE phase modules.
+#
+# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (C) 2017, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = DxeQemuFwCfgS3LibFwCfg
+ FILE_GUID = C5DE76EB-E8DE-4057-A487-C5A09AB039AB
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgS3Lib|DXE_DRIVER DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 EBC
+#
+
+[Sources]
+ QemuFwCfgS3Dxe.c
+ QemuFwCfgS3PeiDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ MemoryAllocationLib
+ QemuFwCfgLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gEfiS3SaveStateProtocolGuid ## SOMETIMES_CONSUMES
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf new file mode 100644 index 000000000..8268828a7 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf @@ -0,0 +1,39 @@ +## @file
+# Limited functionality QemuFwCfgS3Lib instance, for PEI phase modules.
+#
+# QemuFwCfgS3Enabled() queries S3 enablement via fw_cfg. Other library APIs
+# will report lack of support.
+#
+# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (C) 2017, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = PeiQemuFwCfgS3LibFwCfg
+ FILE_GUID = DD8D28B4-C1DC-4CAF-BB93-074BE80DAE6D
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgS3Lib|PEIM
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 EBC
+#
+
+[Sources]
+ QemuFwCfgS3BasePei.c
+ QemuFwCfgS3Pei.c
+ QemuFwCfgS3PeiDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ QemuFwCfgLib
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Base.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Base.c new file mode 100644 index 000000000..c44d67580 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Base.c @@ -0,0 +1,105 @@ +/** @file
+ Base Null library instance of the QemuFwCfgS3Lib class.
+
+ This library instance returns constant FALSE from QemuFwCfgS3Enabled(), and
+ all other library functions trigger assertion failures. It is suitable for
+ QEMU targets and machine types that never enable S3.
+
+ Copyright (C) 2017, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+
+/**
+ Determine if S3 support is explicitly enabled.
+
+ @retval TRUE If S3 support is explicitly enabled. Other functions in this
+ library may be called (subject to their individual
+ restrictions).
+
+ FALSE Otherwise. This includes unavailability of the firmware
+ configuration interface. No other function in this library
+ must be called.
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgS3Enabled (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+
+/**
+ Install the client module's FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION callback for
+ when the production of ACPI S3 Boot Script opcodes becomes possible.
+
+ Take ownership of the client-provided Context, and pass it to the callback
+ function, when the latter is invoked.
+
+ Allocate scratch space for those ACPI S3 Boot Script opcodes to work upon
+ that the client will produce in the callback function.
+
+ @param[in] Callback FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION to invoke
+ when the production of ACPI S3 Boot Script
+ opcodes becomes possible. Callback() may be
+ called immediately from
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @param[in,out] Context Client-provided data structure for the
+ Callback() callback function to consume.
+
+ If Context points to dynamically allocated
+ memory, then Callback() must release it.
+
+ If Context points to dynamically allocated
+ memory, and
+ QemuFwCfgS3CallWhenBootScriptReady() returns
+ successfully, then the caller of
+ QemuFwCfgS3CallWhenBootScriptReady() must
+ neither dereference nor even evaluate Context
+ any longer, as ownership of the referenced area
+ has been transferred to Callback().
+
+ @param[in] ScratchBufferSize The size of the scratch buffer that will hold,
+ in reserved memory, all client data read,
+ written, and checked by the ACPI S3 Boot Script
+ opcodes produced by Callback().
+
+ @retval RETURN_UNSUPPORTED The library instance does not support this
+ function.
+
+ @retval RETURN_NOT_FOUND The fw_cfg DMA interface to QEMU is
+ unavailable.
+
+ @retval RETURN_BAD_BUFFER_SIZE ScratchBufferSize is too large.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval RETURN_SUCCESS Callback() has been installed, and the
+ ownership of Context has been transferred.
+ Reserved memory has been allocated for the
+ scratch buffer.
+
+ A successful invocation of
+ QemuFwCfgS3CallWhenBootScriptReady() cannot
+ be rolled back.
+
+ @return Error codes from underlying functions.
+**/
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3CallWhenBootScriptReady (
+ IN FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION *Callback,
+ IN OUT VOID *Context, OPTIONAL
+ IN UINTN ScratchBufferSize
+ )
+{
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3BasePei.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3BasePei.c new file mode 100644 index 000000000..3ec6209ba --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3BasePei.c @@ -0,0 +1,221 @@ +/** @file
+ Shared code for the Base Null and PEI fw_cfg instances of the QemuFwCfgS3Lib
+ class.
+
+ Copyright (C) 2017, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and transfer data to it.
+
+ The opcodes produced by QemuFwCfgS3ScriptWriteBytes() will first restore
+ NumberOfBytes bytes in ScratchBuffer in-place, in reserved memory, then write
+ them to fw_cfg using DMA.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to write, expressed as INT32. If
+ FirmwareConfigItem is -1, no selection is
+ made, the write will occur to the currently
+ selected item, at its currently selected
+ offset. Otherwise, the specified item will be
+ selected, and the write will occur at offset
+ 0.
+
+ @param[in] NumberOfBytes Size of the data to restore in ScratchBuffer,
+ and to write from ScratchBuffer, during S3
+ resume. NumberOfBytes must not exceed
+ ScratchBufferSize, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is larger than
+ ScratchBufferSize.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptWriteBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and transfer data from it.
+
+ The opcodes produced by QemuFwCfgS3ScriptReadBytes() will read NumberOfBytes
+ bytes from fw_cfg using DMA, storing the result in ScratchBuffer, in reserved
+ memory.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to read, expressed as INT32. If
+ FirmwareConfigItem is -1, no selection is
+ made, the read will occur from the currently
+ selected item, from its currently selected
+ offset. Otherwise, the specified item will be
+ selected, and the read will occur from offset
+ 0.
+
+ @param[in] NumberOfBytes Size of the data to read during S3 resume.
+ NumberOfBytes must not exceed
+ ScratchBufferSize, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is larger than
+ ScratchBufferSize.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptReadBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and increase its offset.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to advance the offset of, expressed as
+ INT32. If FirmwareConfigItem is -1, no
+ selection is made, and the offset for the
+ currently selected item is increased.
+ Otherwise, the specified item will be
+ selected, and the offset increment will occur
+ from offset 0.
+
+ @param[in] NumberOfBytes The number of bytes to skip in the subject
+ fw_cfg item.
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is too large.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptSkipBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that check a value in ScratchBuffer.
+
+ If the check fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] ScratchData Pointer to the UINT8, UINT16, UINT32 or UINT64 field
+ in ScratchBuffer that should be checked. The caller
+ is responsible for populating the field during S3
+ resume, by calling QemuFwCfgS3ScriptReadBytes() ahead
+ of QemuFwCfgS3ScriptCheckValue().
+
+ ScratchData must point into ScratchBuffer, which was
+ allocated, and passed to Callback(), by
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ ScratchData must be aligned at ValueSize bytes.
+
+ @param[in] ValueSize One of 1, 2, 4 or 8, specifying the size of the field
+ to check.
+
+ @param[in] ValueMask The value read from ScratchData is binarily AND-ed
+ with ValueMask, and the result is compared against
+ Value. If the masked data equals Value, the check
+ passes, and the boot script can proceed. Otherwise,
+ the check fails, and the boot script hangs.
+
+ @param[in] Value Refer to ValueMask.
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER ValueSize is invalid.
+
+ @retval RETURN_INVALID_PARAMETER ValueMask or Value cannot be represented in
+ ValueSize bytes.
+
+ @retval RETURN_INVALID_PARAMETER ScratchData is not aligned at ValueSize
+ bytes.
+
+ @retval RETURN_BAD_BUFFER_SIZE The ValueSize bytes at ScratchData aren't
+ wholly contained in the ScratchBufferSize
+ bytes at ScratchBuffer.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptCheckValue (
+ IN VOID *ScratchData,
+ IN UINT8 ValueSize,
+ IN UINT64 ValueMask,
+ IN UINT64 Value
+ )
+{
+ ASSERT (FALSE);
+ return RETURN_UNSUPPORTED;
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c new file mode 100644 index 000000000..3b3ecbc18 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c @@ -0,0 +1,786 @@ +/** @file
+ Full functionality QemuFwCfgS3Lib instance, for DXE phase modules.
+
+ Copyright (C) 2017, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/S3SaveState.h>
+
+
+//
+// Event to signal when the S3SaveState protocol interface is installed.
+//
+STATIC EFI_EVENT mS3SaveStateInstalledEvent;
+
+//
+// Reference to the S3SaveState protocol interface, after it is installed.
+//
+STATIC EFI_S3_SAVE_STATE_PROTOCOL *mS3SaveState;
+
+//
+// The control structure is allocated in reserved memory, aligned at 8 bytes.
+// The client-requested ScratchBuffer will be allocated adjacently, also
+// aligned at 8 bytes.
+//
+#define RESERVED_MEM_ALIGNMENT 8
+
+STATIC FW_CFG_DMA_ACCESS *mDmaAccess;
+STATIC VOID *mScratchBuffer;
+STATIC UINTN mScratchBufferSize;
+
+//
+// Callback provided by the client, for appending ACPI S3 Boot Script opcodes.
+// To be called from S3SaveStateInstalledNotify().
+//
+STATIC FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION *mCallback;
+
+
+/**
+ Event notification function for mS3SaveStateInstalledEvent.
+**/
+STATIC
+VOID
+EFIAPI
+S3SaveStateInstalledNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Event == mS3SaveStateInstalledEvent);
+
+ Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid,
+ NULL /* Registration */, (VOID **)&mS3SaveState);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ ASSERT (mCallback != NULL);
+
+ DEBUG ((DEBUG_INFO, "%a: %a: DmaAccess@0x%Lx ScratchBuffer@[0x%Lx+0x%Lx]\n",
+ gEfiCallerBaseName, __FUNCTION__, (UINT64)(UINTN)mDmaAccess,
+ (UINT64)(UINTN)mScratchBuffer, (UINT64)mScratchBufferSize));
+ mCallback (Context, mScratchBuffer);
+
+ gBS->CloseEvent (mS3SaveStateInstalledEvent);
+ mS3SaveStateInstalledEvent = NULL;
+}
+
+
+/**
+ Install the client module's FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION callback for
+ when the production of ACPI S3 Boot Script opcodes becomes possible.
+
+ Take ownership of the client-provided Context, and pass it to the callback
+ function, when the latter is invoked.
+
+ Allocate scratch space for those ACPI S3 Boot Script opcodes to work upon
+ that the client will produce in the callback function.
+
+ @param[in] Callback FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION to invoke
+ when the production of ACPI S3 Boot Script
+ opcodes becomes possible. Callback() may be
+ called immediately from
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @param[in,out] Context Client-provided data structure for the
+ Callback() callback function to consume.
+
+ If Context points to dynamically allocated
+ memory, then Callback() must release it.
+
+ If Context points to dynamically allocated
+ memory, and
+ QemuFwCfgS3CallWhenBootScriptReady() returns
+ successfully, then the caller of
+ QemuFwCfgS3CallWhenBootScriptReady() must
+ neither dereference nor even evaluate Context
+ any longer, as ownership of the referenced area
+ has been transferred to Callback().
+
+ @param[in] ScratchBufferSize The size of the scratch buffer that will hold,
+ in reserved memory, all client data read,
+ written, and checked by the ACPI S3 Boot Script
+ opcodes produced by Callback().
+
+ @retval RETURN_UNSUPPORTED The library instance does not support this
+ function.
+
+ @retval RETURN_NOT_FOUND The fw_cfg DMA interface to QEMU is
+ unavailable.
+
+ @retval RETURN_BAD_BUFFER_SIZE ScratchBufferSize is too large.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval RETURN_SUCCESS Callback() has been installed, and the
+ ownership of Context has been transferred.
+ Reserved memory has been allocated for the
+ scratch buffer.
+
+ A successful invocation of
+ QemuFwCfgS3CallWhenBootScriptReady() cannot
+ be rolled back.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3CallWhenBootScriptReady (
+ IN FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION *Callback,
+ IN OUT VOID *Context, OPTIONAL
+ IN UINTN ScratchBufferSize
+ )
+{
+ EFI_STATUS Status;
+ VOID *Registration;
+
+ //
+ // Basic fw_cfg is certainly available, as we can only be here after a
+ // successful call to QemuFwCfgS3Enabled(). Check fw_cfg DMA availability.
+ //
+ ASSERT (QemuFwCfgIsAvailable ());
+ QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+ if ((QemuFwCfgRead32 () & FW_CFG_F_DMA) == 0) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: fw_cfg DMA unavailable\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return RETURN_NOT_FOUND;
+ }
+
+ //
+ // Allocate a reserved buffer for the DMA access control structure and the
+ // client data together.
+ //
+ if (ScratchBufferSize >
+ MAX_UINT32 - (RESERVED_MEM_ALIGNMENT - 1) - sizeof *mDmaAccess) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: ScratchBufferSize too big: %Lu\n",
+ gEfiCallerBaseName, __FUNCTION__, (UINT64)ScratchBufferSize));
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+ mDmaAccess = AllocateReservedPool ((RESERVED_MEM_ALIGNMENT - 1) +
+ sizeof *mDmaAccess + ScratchBufferSize);
+ if (mDmaAccess == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: AllocateReservedPool(): out of resources\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return RETURN_OUT_OF_RESOURCES;
+ }
+ mDmaAccess = ALIGN_POINTER (mDmaAccess, RESERVED_MEM_ALIGNMENT);
+
+ //
+ // Set up a protocol notify for EFI_S3_SAVE_STATE_PROTOCOL. Forward the
+ // client's Context to the callback.
+ //
+ Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ S3SaveStateInstalledNotify, Context,
+ &mS3SaveStateInstalledEvent);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: CreateEvent(): %r\n", gEfiCallerBaseName,
+ __FUNCTION__, Status));
+ goto FreeDmaAccess;
+ }
+ Status = gBS->RegisterProtocolNotify (&gEfiS3SaveStateProtocolGuid,
+ mS3SaveStateInstalledEvent, &Registration);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: RegisterProtocolNotify(): %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ goto CloseEvent;
+ }
+
+ //
+ // Set the remaining global variables. For the alignment guarantee on
+ // mScratchBuffer, we rely on the fact that *mDmaAccess has a size that is an
+ // integral multiple of RESERVED_MEM_ALIGNMENT.
+ //
+ ASSERT (sizeof *mDmaAccess % RESERVED_MEM_ALIGNMENT == 0);
+ mScratchBuffer = mDmaAccess + 1;
+ mScratchBufferSize = ScratchBufferSize;
+ mCallback = Callback;
+
+ //
+ // Kick the event; EFI_S3_SAVE_STATE_PROTOCOL could be available already.
+ //
+ Status = gBS->SignalEvent (mS3SaveStateInstalledEvent);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: SignalEvent(): %r\n", gEfiCallerBaseName,
+ __FUNCTION__, Status));
+ goto NullGlobals;
+ }
+
+ return RETURN_SUCCESS;
+
+NullGlobals:
+ mScratchBuffer = NULL;
+ mScratchBufferSize = 0;
+ mCallback = NULL;
+
+CloseEvent:
+ gBS->CloseEvent (mS3SaveStateInstalledEvent);
+ mS3SaveStateInstalledEvent = NULL;
+
+FreeDmaAccess:
+ FreePool (mDmaAccess);
+ mDmaAccess = NULL;
+
+ return (RETURN_STATUS)Status;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and transfer data to it.
+
+ The opcodes produced by QemuFwCfgS3ScriptWriteBytes() will first restore
+ NumberOfBytes bytes in ScratchBuffer in-place, in reserved memory, then write
+ them to fw_cfg using DMA.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to write, expressed as INT32. If
+ FirmwareConfigItem is -1, no selection is
+ made, the write will occur to the currently
+ selected item, at its currently selected
+ offset. Otherwise, the specified item will be
+ selected, and the write will occur at offset
+ 0.
+
+ @param[in] NumberOfBytes Size of the data to restore in ScratchBuffer,
+ and to write from ScratchBuffer, during S3
+ resume. NumberOfBytes must not exceed
+ ScratchBufferSize, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is larger than
+ ScratchBufferSize.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptWriteBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ UINTN Count;
+ EFI_STATUS Status;
+ UINT64 AccessAddress;
+ UINT32 ControlPollData;
+ UINT32 ControlPollMask;
+
+ ASSERT (mDmaAccess != NULL);
+ ASSERT (mS3SaveState != NULL);
+
+ if (FirmwareConfigItem < -1 || FirmwareConfigItem > MAX_UINT16) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (NumberOfBytes > mScratchBufferSize) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Set up a write[+select] fw_cfg DMA command.
+ //
+ mDmaAccess->Control = FW_CFG_DMA_CTL_WRITE;
+ if (FirmwareConfigItem != -1) {
+ mDmaAccess->Control |= FW_CFG_DMA_CTL_SELECT;
+ mDmaAccess->Control |= (UINT32)FirmwareConfigItem << 16;
+ }
+ mDmaAccess->Control = SwapBytes32 (mDmaAccess->Control);
+
+ //
+ // We ensured the following constraint via mScratchBufferSize in
+ // QemuFwCfgS3CallWhenBootScriptReady().
+ //
+ ASSERT (NumberOfBytes <= MAX_UINT32);
+ mDmaAccess->Length = SwapBytes32 ((UINT32)NumberOfBytes);
+
+ mDmaAccess->Address = SwapBytes64 ((UINTN)mScratchBuffer);
+
+ //
+ // Copy mDmaAccess and NumberOfBytes bytes from mScratchBuffer into the boot
+ // script. When executed at S3 resume, this opcode will restore all of them
+ // in-place.
+ //
+ Count = (UINTN)mScratchBuffer + NumberOfBytes - (UINTN)mDmaAccess;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint8, // Width
+ (UINT64)(UINTN)mDmaAccess, // Address
+ Count, // Count
+ (VOID *)mDmaAccess // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // Append an opcode that will write the address of the fw_cfg DMA command to
+ // the fw_cfg DMA address register, which consists of two 32-bit IO ports.
+ // The second (highest address, least significant) write will start the
+ // transfer.
+ //
+ AccessAddress = SwapBytes64 ((UINTN)mDmaAccess);
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)FW_CFG_IO_DMA_ADDRESS, // Address
+ (UINTN)2, // Count
+ (VOID *)&AccessAddress // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // The following opcode will wait until the Control word reads as zero
+ // (transfer complete). As timeout we use MAX_UINT64 * 100ns, which is
+ // approximately 58494 years.
+ //
+ ControlPollData = 0;
+ ControlPollMask = MAX_UINT32;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)(UINTN)&mDmaAccess->Control, // Address
+ (VOID *)&ControlPollData, // Data
+ (VOID *)&ControlPollMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and transfer data from it.
+
+ The opcodes produced by QemuFwCfgS3ScriptReadBytes() will read NumberOfBytes
+ bytes from fw_cfg using DMA, storing the result in ScratchBuffer, in reserved
+ memory.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to read, expressed as INT32. If
+ FirmwareConfigItem is -1, no selection is
+ made, the read will occur from the currently
+ selected item, from its currently selected
+ offset. Otherwise, the specified item will be
+ selected, and the read will occur from offset
+ 0.
+
+ @param[in] NumberOfBytes Size of the data to read during S3 resume.
+ NumberOfBytes must not exceed
+ ScratchBufferSize, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is larger than
+ ScratchBufferSize.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptReadBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ EFI_STATUS Status;
+ UINT64 AccessAddress;
+ UINT32 ControlPollData;
+ UINT32 ControlPollMask;
+
+ ASSERT (mDmaAccess != NULL);
+ ASSERT (mS3SaveState != NULL);
+
+ if (FirmwareConfigItem < -1 || FirmwareConfigItem > MAX_UINT16) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (NumberOfBytes > mScratchBufferSize) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Set up a read[+select] fw_cfg DMA command.
+ //
+ mDmaAccess->Control = FW_CFG_DMA_CTL_READ;
+ if (FirmwareConfigItem != -1) {
+ mDmaAccess->Control |= FW_CFG_DMA_CTL_SELECT;
+ mDmaAccess->Control |= (UINT32)FirmwareConfigItem << 16;
+ }
+ mDmaAccess->Control = SwapBytes32 (mDmaAccess->Control);
+
+ //
+ // We ensured the following constraint via mScratchBufferSize in
+ // QemuFwCfgS3CallWhenBootScriptReady().
+ //
+ ASSERT (NumberOfBytes <= MAX_UINT32);
+ mDmaAccess->Length = SwapBytes32 ((UINT32)NumberOfBytes);
+
+ mDmaAccess->Address = SwapBytes64 ((UINTN)mScratchBuffer);
+
+ //
+ // Copy mDmaAccess into the boot script. When executed at S3 resume, this
+ // opcode will restore it in-place.
+ //
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint8, // Width
+ (UINT64)(UINTN)mDmaAccess, // Address
+ sizeof *mDmaAccess, // Count
+ (VOID *)mDmaAccess // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // Append an opcode that will write the address of the fw_cfg DMA command to
+ // the fw_cfg DMA address register, which consists of two 32-bit IO ports.
+ // The second (highest address, least significant) write will start the
+ // transfer.
+ //
+ AccessAddress = SwapBytes64 ((UINTN)mDmaAccess);
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)FW_CFG_IO_DMA_ADDRESS, // Address
+ (UINTN)2, // Count
+ (VOID *)&AccessAddress // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // The following opcode will wait until the Control word reads as zero
+ // (transfer complete). As timeout we use MAX_UINT64 * 100ns, which is
+ // approximately 58494 years.
+ //
+ ControlPollData = 0;
+ ControlPollMask = MAX_UINT32;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)(UINTN)&mDmaAccess->Control, // Address
+ (VOID *)&ControlPollData, // Data
+ (VOID *)&ControlPollMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and increase its offset.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to advance the offset of, expressed as
+ INT32. If FirmwareConfigItem is -1, no
+ selection is made, and the offset for the
+ currently selected item is increased.
+ Otherwise, the specified item will be
+ selected, and the offset increment will occur
+ from offset 0.
+
+ @param[in] NumberOfBytes The number of bytes to skip in the subject
+ fw_cfg item.
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is too large.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptSkipBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ EFI_STATUS Status;
+ UINT64 AccessAddress;
+ UINT32 ControlPollData;
+ UINT32 ControlPollMask;
+
+ ASSERT (mDmaAccess != NULL);
+ ASSERT (mS3SaveState != NULL);
+
+ if (FirmwareConfigItem < -1 || FirmwareConfigItem > MAX_UINT16) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (NumberOfBytes > MAX_UINT32) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Set up a skip[+select] fw_cfg DMA command.
+ //
+ mDmaAccess->Control = FW_CFG_DMA_CTL_SKIP;
+ if (FirmwareConfigItem != -1) {
+ mDmaAccess->Control |= FW_CFG_DMA_CTL_SELECT;
+ mDmaAccess->Control |= (UINT32)FirmwareConfigItem << 16;
+ }
+ mDmaAccess->Control = SwapBytes32 (mDmaAccess->Control);
+
+ mDmaAccess->Length = SwapBytes32 ((UINT32)NumberOfBytes);
+ mDmaAccess->Address = 0;
+
+ //
+ // Copy mDmaAccess into the boot script. When executed at S3 resume, this
+ // opcode will restore it in-place.
+ //
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint8, // Width
+ (UINT64)(UINTN)mDmaAccess, // Address
+ sizeof *mDmaAccess, // Count
+ (VOID *)mDmaAccess // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // Append an opcode that will write the address of the fw_cfg DMA command to
+ // the fw_cfg DMA address register, which consists of two 32-bit IO ports.
+ // The second (highest address, least significant) write will start the
+ // transfer.
+ //
+ AccessAddress = SwapBytes64 ((UINTN)mDmaAccess);
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)FW_CFG_IO_DMA_ADDRESS, // Address
+ (UINTN)2, // Count
+ (VOID *)&AccessAddress // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // The following opcode will wait until the Control word reads as zero
+ // (transfer complete). As timeout we use MAX_UINT64 * 100ns, which is
+ // approximately 58494 years.
+ //
+ ControlPollData = 0;
+ ControlPollMask = MAX_UINT32;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)(UINTN)&mDmaAccess->Control, // Address
+ (VOID *)&ControlPollData, // Data
+ (VOID *)&ControlPollMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that check a value in ScratchBuffer.
+
+ If the check fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] ScratchData Pointer to the UINT8, UINT16, UINT32 or UINT64 field
+ in ScratchBuffer that should be checked. The caller
+ is responsible for populating the field during S3
+ resume, by calling QemuFwCfgS3ScriptReadBytes() ahead
+ of QemuFwCfgS3ScriptCheckValue().
+
+ ScratchData must point into ScratchBuffer, which was
+ allocated, and passed to Callback(), by
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ ScratchData must be aligned at ValueSize bytes.
+
+ @param[in] ValueSize One of 1, 2, 4 or 8, specifying the size of the field
+ to check.
+
+ @param[in] ValueMask The value read from ScratchData is binarily AND-ed
+ with ValueMask, and the result is compared against
+ Value. If the masked data equals Value, the check
+ passes, and the boot script can proceed. Otherwise,
+ the check fails, and the boot script hangs.
+
+ @param[in] Value Refer to ValueMask.
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER ValueSize is invalid.
+
+ @retval RETURN_INVALID_PARAMETER ValueMask or Value cannot be represented in
+ ValueSize bytes.
+
+ @retval RETURN_INVALID_PARAMETER ScratchData is not aligned at ValueSize
+ bytes.
+
+ @retval RETURN_BAD_BUFFER_SIZE The ValueSize bytes at ScratchData aren't
+ wholly contained in the ScratchBufferSize
+ bytes at ScratchBuffer.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3ScriptCheckValue (
+ IN VOID *ScratchData,
+ IN UINT8 ValueSize,
+ IN UINT64 ValueMask,
+ IN UINT64 Value
+ )
+{
+ EFI_BOOT_SCRIPT_WIDTH Width;
+ EFI_STATUS Status;
+
+ ASSERT (mS3SaveState != NULL);
+
+ switch (ValueSize) {
+ case 1:
+ Width = EfiBootScriptWidthUint8;
+ break;
+
+ case 2:
+ Width = EfiBootScriptWidthUint16;
+ break;
+
+ case 4:
+ Width = EfiBootScriptWidthUint32;
+ break;
+
+ case 8:
+ Width = EfiBootScriptWidthUint64;
+ break;
+
+ default:
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (ValueSize < 8 &&
+ (RShiftU64 (ValueMask, ValueSize * 8) > 0 ||
+ RShiftU64 (Value, ValueSize * 8) > 0)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if ((UINTN)ScratchData % ValueSize > 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (((UINTN)ScratchData < (UINTN)mScratchBuffer) ||
+ ((UINTN)ScratchData > MAX_UINTN - ValueSize) ||
+ ((UINTN)ScratchData + ValueSize >
+ (UINTN)mScratchBuffer + mScratchBufferSize)) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // The following opcode will wait "until" (*ScratchData & ValueMask) reads as
+ // Value, considering the least significant ValueSize bytes. As timeout we
+ // use MAX_UINT64 * 100ns, which is approximately 58494 years.
+ //
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ Width, // Width
+ (UINT64)(UINTN)ScratchData, // Address
+ (VOID *)&Value, // Data
+ (VOID *)&ValueMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Pei.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Pei.c new file mode 100644 index 000000000..0d055cacb --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Pei.c @@ -0,0 +1,80 @@ +/** @file
+ Limited functionality QemuFwCfgS3Lib instance, for PEI phase modules.
+
+ QemuFwCfgS3Enabled() queries S3 enablement via fw_cfg. Other library APIs
+ will report lack of support.
+
+ Copyright (C) 2017, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/QemuFwCfgS3Lib.h>
+
+/**
+ Install the client module's FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION callback for
+ when the production of ACPI S3 Boot Script opcodes becomes possible.
+
+ Take ownership of the client-provided Context, and pass it to the callback
+ function, when the latter is invoked.
+
+ Allocate scratch space for those ACPI S3 Boot Script opcodes to work upon
+ that the client will produce in the callback function.
+
+ @param[in] Callback FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION to invoke
+ when the production of ACPI S3 Boot Script
+ opcodes becomes possible. Callback() may be
+ called immediately from
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @param[in,out] Context Client-provided data structure for the
+ Callback() callback function to consume.
+
+ If Context points to dynamically allocated
+ memory, then Callback() must release it.
+
+ If Context points to dynamically allocated
+ memory, and
+ QemuFwCfgS3CallWhenBootScriptReady() returns
+ successfully, then the caller of
+ QemuFwCfgS3CallWhenBootScriptReady() must
+ neither dereference nor even evaluate Context
+ any longer, as ownership of the referenced area
+ has been transferred to Callback().
+
+ @param[in] ScratchBufferSize The size of the scratch buffer that will hold,
+ in reserved memory, all client data read,
+ written, and checked by the ACPI S3 Boot Script
+ opcodes produced by Callback().
+
+ @retval RETURN_UNSUPPORTED The library instance does not support this
+ function.
+
+ @retval RETURN_NOT_FOUND The fw_cfg DMA interface to QEMU is
+ unavailable.
+
+ @retval RETURN_BAD_BUFFER_SIZE ScratchBufferSize is too large.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval RETURN_SUCCESS Callback() has been installed, and the
+ ownership of Context has been transferred.
+ Reserved memory has been allocated for the
+ scratch buffer.
+
+ A successful invocation of
+ QemuFwCfgS3CallWhenBootScriptReady() cannot
+ be rolled back.
+
+ @return Error codes from underlying functions.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgS3CallWhenBootScriptReady (
+ IN FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION *Callback,
+ IN OUT VOID *Context, OPTIONAL
+ IN UINTN ScratchBufferSize
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c new file mode 100644 index 000000000..5557c70aa --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c @@ -0,0 +1,42 @@ +/** @file
+ Shared code for the PEI fw_cfg and DXE fw_cfg instances of the QemuFwCfgS3Lib
+ class.
+
+ Copyright (C) 2017, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+
+/**
+ Determine if S3 support is explicitly enabled.
+
+ @retval TRUE If S3 support is explicitly enabled. Other functions in this
+ library may be called (subject to their individual
+ restrictions).
+
+ FALSE Otherwise. This includes unavailability of the firmware
+ configuration interface. No other function in this library
+ must be called.
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgS3Enabled (
+ VOID
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ UINT8 SystemStates[6];
+
+ Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);
+ if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) {
+ return FALSE;
+ }
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (sizeof SystemStates, SystemStates);
+ return (BOOLEAN) (SystemStates[3] & BIT7);
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParser.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParser.c new file mode 100644 index 000000000..5ca22fcf0 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParser.c @@ -0,0 +1,398 @@ +/** @file
+ Parse the contents of named fw_cfg files as simple (scalar) data types.
+
+ Copyright (C) 2020, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgSimpleParserLib.h>
+
+//
+// Size of the longest valid UINT64 string, including the terminating NUL.
+//
+#define UINT64_STRING_MAX_SIZE \
+ MAX (sizeof "18446744073709551615", sizeof "0xFFFFFFFFFFFFFFFF")
+
+//
+// Size of the longest valid BOOL string (see the "mTrueString" and
+// "mFalseString" arrays below), including the terminating NUL.
+//
+#define BOOL_STRING_MAX_SIZE (sizeof "disabled")
+
+//
+// Length of "\r\n", not including the terminating NUL.
+//
+#define CRLF_LENGTH (sizeof "\r\n" - 1)
+
+//
+// Words recognized as representing TRUE or FALSE.
+//
+STATIC CONST CHAR8 * CONST mTrueString[] = {
+ "true", "yes", "y", "enable", "enabled", "1"
+};
+STATIC CONST CHAR8 * CONST mFalseString[] = {
+ "false", "no", "n", "disable", "disabled", "0"
+};
+
+//
+// Helper functions.
+//
+
+/**
+ Look up FileName with QemuFwCfgFindFile() from QemuFwCfgLib. Read the fw_cfg
+ file into the caller-provided CHAR8 array. NUL-terminate the array.
+
+ @param[in] FileName The name of the fw_cfg file to look up and read.
+
+ @param[in,out] BufferSize On input, number of bytes available in Buffer.
+
+ On output, the number of bytes that have been
+ stored to Buffer.
+
+ On error, BufferSize is indeterminate.
+
+ @param[out] Buffer The buffer to read the fw_cfg file into. If the
+ fw_cfg file contents are not NUL-terminated, then
+ a NUL character is placed into Buffer after the
+ fw_cfg file contents.
+
+ On error, Buffer is indeterminate.
+
+ @retval RETURN_SUCCESS Buffer has been populated with the fw_cfg file
+ contents. Buffer is NUL-terminated regardless
+ of whether the fw_cfg file itself was
+ NUL-terminated.
+
+ @retval RETURN_UNSUPPORTED Firmware configuration is unavailable.
+
+ @retval RETURN_PROTOCOL_ERROR The fw_cfg file does not fit into Buffer.
+ (This is considered a QEMU configuration
+ error; BufferSize is considered authoritative
+ for the contents of the fw_cfg file identified
+ by FileName.)
+
+ @retval RETURN_PROTOCOL_ERROR The fw_cfg file contents are not themselves
+ NUL-terminated, and an extra NUL byte does not
+ fit into Buffer. (Again a QEMU configuration
+ error.)
+
+ @return Error codes propagated from
+ QemuFwCfgFindFile().
+**/
+STATIC
+RETURN_STATUS
+QemuFwCfgGetAsString (
+ IN CONST CHAR8 *FileName,
+ IN OUT UINTN *BufferSize,
+ OUT CHAR8 *Buffer
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+
+ if (!QemuFwCfgIsAvailable ()) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Status = QemuFwCfgFindFile (FileName, &FwCfgItem, &FwCfgSize);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+ if (FwCfgSize > *BufferSize) {
+ return RETURN_PROTOCOL_ERROR;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, Buffer);
+
+ //
+ // If Buffer is already NUL-terminated due to fw_cfg contents, we're done.
+ //
+ if (FwCfgSize > 0 && Buffer[FwCfgSize - 1] == '\0') {
+ *BufferSize = FwCfgSize;
+ return RETURN_SUCCESS;
+ }
+ //
+ // Otherwise, append a NUL byte to Buffer (if we have room for it).
+ //
+ if (FwCfgSize == *BufferSize) {
+ return RETURN_PROTOCOL_ERROR;
+ }
+ Buffer[FwCfgSize] = '\0';
+ *BufferSize = FwCfgSize + 1;
+ return RETURN_SUCCESS;
+}
+
+/**
+ Remove a trailing \r\n or \n sequence from a string.
+
+ @param[in,out] BufferSize On input, the number of bytes in Buffer, including
+ the terminating NUL.
+
+ On output, the adjusted string size (including the
+ terminating NUL), after stripping the \r\n or \n
+ suffix.
+
+ @param[in,out] Buffer The NUL-terminated string to trim.
+**/
+STATIC
+VOID
+StripNewline (
+ IN OUT UINTN *BufferSize,
+ IN OUT CHAR8 *Buffer
+ )
+{
+ UINTN InSize, OutSize;
+
+ InSize = *BufferSize;
+ OutSize = InSize;
+
+ if (InSize >= 3 &&
+ Buffer[InSize - 3] == '\r' && Buffer[InSize - 2] == '\n') {
+ OutSize = InSize - 2;
+ } else if (InSize >= 2 && Buffer[InSize - 2] == '\n') {
+ OutSize = InSize - 1;
+ }
+
+ if (OutSize < InSize) {
+ Buffer[OutSize - 1] = '\0';
+ *BufferSize = OutSize;
+ }
+}
+
+/**
+ Read the fw_cfg file identified by FileName as a string into a small array
+ with automatic storage duration, using QemuFwCfgGetAsString(). Parse the
+ string as a UINT64. Perform a range-check on the parsed value.
+
+ @param[in] FileName The name of the fw_cfg file to look up and parse.
+
+ @param[in] ParseAsHex If TRUE, call BaseLib's AsciiStrHexToUint64S() for
+ parsing the fw_cfg file.
+
+ If FALSE, call BaseLib's AsciiStrDecimalToUint64S()
+ for parsing the fw_cfg file.
+
+ @param[in] Limit The inclusive upper bound on the parsed UINT64 value.
+
+ @param[out] Value On success, Value has been parsed with the BaseLib
+ function determined by ParseAsHex, and has been
+ range-checked against [0, Limit].
+
+ On failure, Value is not changed.
+
+ @retval RETURN_SUCCESS Parsing successful. Value has been set.
+
+ @retval RETURN_UNSUPPORTED Firmware configuration is unavailable.
+
+ @retval RETURN_PROTOCOL_ERROR Parsing failed. Value has not been changed.
+
+ @retval RETURN_PROTOCOL_ERROR Parsing succeeded, but the result does not fit
+ in the [0, Limit] range. Value has not been
+ changed.
+
+ @return Error codes propagated from
+ QemuFwCfgFindFile() and from the BaseLib
+ function selected by ParseAsHex. Value has not
+ been changed.
+**/
+STATIC
+RETURN_STATUS
+QemuFwCfgParseUint64WithLimit (
+ IN CONST CHAR8 *FileName,
+ IN BOOLEAN ParseAsHex,
+ IN UINT64 Limit,
+ OUT UINT64 *Value
+ )
+{
+ UINTN Uint64StringSize;
+ CHAR8 Uint64String[UINT64_STRING_MAX_SIZE + CRLF_LENGTH];
+ RETURN_STATUS Status;
+ CHAR8 *EndPointer;
+ UINT64 Uint64;
+
+ Uint64StringSize = sizeof Uint64String;
+ Status = QemuFwCfgGetAsString (FileName, &Uint64StringSize, Uint64String);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+
+ StripNewline (&Uint64StringSize, Uint64String);
+
+ if (ParseAsHex) {
+ Status = AsciiStrHexToUint64S (Uint64String, &EndPointer, &Uint64);
+ } else {
+ Status = AsciiStrDecimalToUint64S (Uint64String, &EndPointer, &Uint64);
+ }
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Report a wire protocol error if the subject sequence is empty, or trailing
+ // garbage is present, or Limit is not honored.
+ //
+ if (EndPointer == Uint64String || *EndPointer != '\0' || Uint64 > Limit) {
+ return RETURN_PROTOCOL_ERROR;
+ }
+
+ *Value = Uint64;
+ return RETURN_SUCCESS;
+}
+
+//
+// Public functions.
+//
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgSimpleParserInit (
+ VOID
+ )
+{
+ //
+ // Do nothing, just participate in constructor dependency ordering.
+ //
+ return RETURN_SUCCESS;
+}
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgParseBool (
+ IN CONST CHAR8 *FileName,
+ OUT BOOLEAN *Value
+ )
+{
+ UINTN BoolStringSize;
+ CHAR8 BoolString[BOOL_STRING_MAX_SIZE + CRLF_LENGTH];
+ RETURN_STATUS Status;
+ UINTN Idx;
+
+ BoolStringSize = sizeof BoolString;
+ Status = QemuFwCfgGetAsString (FileName, &BoolStringSize, BoolString);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+
+ StripNewline (&BoolStringSize, BoolString);
+
+ for (Idx = 0; Idx < ARRAY_SIZE (mTrueString); ++Idx) {
+ if (AsciiStriCmp (BoolString, mTrueString[Idx]) == 0) {
+ *Value = TRUE;
+ return RETURN_SUCCESS;
+ }
+ }
+
+ for (Idx = 0; Idx < ARRAY_SIZE (mFalseString); ++Idx) {
+ if (AsciiStriCmp (BoolString, mFalseString[Idx]) == 0) {
+ *Value = FALSE;
+ return RETURN_SUCCESS;
+ }
+ }
+
+ return RETURN_PROTOCOL_ERROR;
+}
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgParseUint8 (
+ IN CONST CHAR8 *FileName,
+ IN BOOLEAN ParseAsHex,
+ OUT UINT8 *Value
+ )
+{
+ RETURN_STATUS Status;
+ UINT64 Uint64;
+
+ Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT8,
+ &Uint64);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+ *Value = (UINT8)Uint64;
+ return RETURN_SUCCESS;
+}
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgParseUint16 (
+ IN CONST CHAR8 *FileName,
+ IN BOOLEAN ParseAsHex,
+ OUT UINT16 *Value
+ )
+{
+ RETURN_STATUS Status;
+ UINT64 Uint64;
+
+ Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT16,
+ &Uint64);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+ *Value = (UINT16)Uint64;
+ return RETURN_SUCCESS;
+}
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgParseUint32 (
+ IN CONST CHAR8 *FileName,
+ IN BOOLEAN ParseAsHex,
+ OUT UINT32 *Value
+ )
+{
+ RETURN_STATUS Status;
+ UINT64 Uint64;
+
+ Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT32,
+ &Uint64);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+ *Value = (UINT32)Uint64;
+ return RETURN_SUCCESS;
+}
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgParseUint64 (
+ IN CONST CHAR8 *FileName,
+ IN BOOLEAN ParseAsHex,
+ OUT UINT64 *Value
+ )
+{
+ RETURN_STATUS Status;
+ UINT64 Uint64;
+
+ Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINT64,
+ &Uint64);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+ *Value = Uint64;
+ return RETURN_SUCCESS;
+}
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgParseUintn (
+ IN CONST CHAR8 *FileName,
+ IN BOOLEAN ParseAsHex,
+ OUT UINTN *Value
+ )
+{
+ RETURN_STATUS Status;
+ UINT64 Uint64;
+
+ Status = QemuFwCfgParseUint64WithLimit (FileName, ParseAsHex, MAX_UINTN,
+ &Uint64);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+ *Value = (UINTN)Uint64;
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf b/roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf new file mode 100644 index 000000000..c0e6ab3a1 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf @@ -0,0 +1,27 @@ +## @file
+# Parse the contents of named fw_cfg files as simple (scalar) data types.
+#
+# Copyright (C) 2020, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = QemuFwCfgSimpleParserLib
+ FILE_GUID = a9a1211d-061e-4b64-af30-5dd0cac9dc99
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuFwCfgSimpleParserLib
+ CONSTRUCTOR = QemuFwCfgSimpleParserInit
+
+[Sources]
+ QemuFwCfgSimpleParser.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ QemuFwCfgLib
diff --git a/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdown.c b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdown.c new file mode 100644 index 000000000..21c80e432 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdown.c @@ -0,0 +1,51 @@ +/** @file
+ Base Reset System Library Shutdown API implementation for OVMF.
+
+ Copyright (C) 2020, Red Hat, Inc.
+ Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Base.h> // BIT13
+
+#include <Library/BaseLib.h> // CpuDeadLoop()
+#include <Library/DebugLib.h> // ASSERT()
+#include <Library/IoLib.h> // IoOr16()
+#include <Library/PciLib.h> // PciRead16()
+#include <Library/ResetSystemLib.h> // ResetShutdown()
+#include <OvmfPlatforms.h> // OVMF_HOSTBRIDGE_DID
+
+/**
+ Calling this function causes the system to enter a power state equivalent
+ to the ACPI G2/S5 or G3 states.
+
+ System shutdown should not return, if it returns, it means the system does
+ not support shut down reset.
+**/
+VOID
+EFIAPI
+ResetShutdown (
+ VOID
+ )
+{
+ UINT16 AcpiPmBaseAddress;
+ UINT16 HostBridgeDevId;
+
+ AcpiPmBaseAddress = 0;
+ HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
+ switch (HostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ AcpiPmBaseAddress = PIIX4_PMBA_VALUE;
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ AcpiPmBaseAddress = ICH9_PMBASE_VALUE;
+ break;
+ default:
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ IoBitFieldWrite16 (AcpiPmBaseAddress + 4, 10, 13, 0);
+ IoOr16 (AcpiPmBaseAddress + 4, BIT13);
+ CpuDeadLoop ();
+}
diff --git a/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdownBhyve.c b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdownBhyve.c new file mode 100644 index 000000000..c2ac84787 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdownBhyve.c @@ -0,0 +1,34 @@ +/** @file
+ Base Reset System Library Shutdown API implementation for bhyve.
+
+ Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
+ Copyright (C) 2020, Red Hat, Inc.
+ Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Base.h> // BIT13
+
+#include <IndustryStandard/Bhyve.h> // BHYVE_PM_REG
+#include <Library/BaseLib.h> // CpuDeadLoop()
+#include <Library/IoLib.h> // IoOr16()
+#include <Library/ResetSystemLib.h> // ResetShutdown()
+
+/**
+ Calling this function causes the system to enter a power state equivalent
+ to the ACPI G2/S5 or G3 states.
+
+ System shutdown should not return, if it returns, it means the system does
+ not support shut down reset.
+**/
+VOID
+EFIAPI
+ResetShutdown (
+ VOID
+ )
+{
+ IoBitFieldWrite16 (BHYVE_PM_REG, 10, 13, 5);
+ IoOr16 (BHYVE_PM_REG, BIT13);
+ CpuDeadLoop ();
+}
diff --git a/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLib.inf b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLib.inf new file mode 100644 index 000000000..35d317f1e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLib.inf @@ -0,0 +1,38 @@ +## @file
+# Base library instance for ResetSystem library class for OVMF
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BaseResetSystemLib
+ FILE_GUID = 66564872-21d4-4d2a-a68b-1e844f980820
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ResetSystemLib|SEC PEI_CORE PEIM DXE_CORE
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ BaseResetShutdown.c
+ ResetSystemLib.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ IoLib
+ PciLib
+ TimerLib
diff --git a/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLibBhyve.inf b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLibBhyve.inf new file mode 100644 index 000000000..74124aed3 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLibBhyve.inf @@ -0,0 +1,40 @@ +## @file
+# Base library instance for ResetSystem library class for bhyve
+#
+# Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
+# Copyright (C) 2020, Red Hat, Inc.
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = BaseResetSystemLibBhyve
+ FILE_GUID = 5c71b08f-0ade-4607-8b9d-946c2757fee8
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ResetSystemLib
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ BaseResetShutdownBhyve.c
+ ResetSystemLib.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ IoLib
+ TimerLib
+
diff --git a/roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetShutdown.c b/roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetShutdown.c new file mode 100644 index 000000000..5a75c32df --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetShutdown.c @@ -0,0 +1,62 @@ +/** @file
+ DXE Reset System Library Shutdown API implementation for OVMF.
+
+ Copyright (C) 2020, Red Hat, Inc.
+ Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Base.h> // BIT13
+
+#include <Library/BaseLib.h> // CpuDeadLoop()
+#include <Library/DebugLib.h> // ASSERT()
+#include <Library/IoLib.h> // IoOr16()
+#include <Library/PcdLib.h> // PcdGet16()
+#include <Library/ResetSystemLib.h> // ResetShutdown()
+#include <OvmfPlatforms.h> // PIIX4_PMBA_VALUE
+
+STATIC UINT16 mAcpiPmBaseAddress;
+
+EFI_STATUS
+EFIAPI
+DxeResetInit (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ UINT16 HostBridgeDevId;
+
+ HostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
+ switch (HostBridgeDevId) {
+ case INTEL_82441_DEVICE_ID:
+ mAcpiPmBaseAddress = PIIX4_PMBA_VALUE;
+ break;
+ case INTEL_Q35_MCH_DEVICE_ID:
+ mAcpiPmBaseAddress = ICH9_PMBASE_VALUE;
+ break;
+ default:
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Calling this function causes the system to enter a power state equivalent
+ to the ACPI G2/S5 or G3 states.
+
+ System shutdown should not return, if it returns, it means the system does
+ not support shut down reset.
+**/
+VOID
+EFIAPI
+ResetShutdown (
+ VOID
+ )
+{
+ IoBitFieldWrite16 (mAcpiPmBaseAddress + 4, 10, 13, 0);
+ IoOr16 (mAcpiPmBaseAddress + 4, BIT13);
+ CpuDeadLoop ();
+}
diff --git a/roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetSystemLib.inf b/roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetSystemLib.inf new file mode 100644 index 000000000..a9b4ce900 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetSystemLib.inf @@ -0,0 +1,43 @@ +## @file
+# DXE library instance for ResetSystem library class for OVMF
+#
+# Copyright (C) 2020, Red Hat, Inc.
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = DxeResetSystemLib
+ FILE_GUID = bc7835ea-4094-41fe-b770-bad9e6c479b2
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ResetSystemLib|DXE_DRIVER DXE_RUNTIME_DRIVER SMM_CORE DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION
+ CONSTRUCTOR = DxeResetInit
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ DxeResetShutdown.c
+ ResetSystemLib.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ IoLib
+ PcdLib
+ TimerLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId ## CONSUMES
diff --git a/roms/edk2/OvmfPkg/Library/ResetSystemLib/ResetSystemLib.c b/roms/edk2/OvmfPkg/Library/ResetSystemLib/ResetSystemLib.c new file mode 100644 index 000000000..fe51f53d1 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/ResetSystemLib/ResetSystemLib.c @@ -0,0 +1,120 @@ +/** @file
+ Reset System Library functions for OVMF
+
+ Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h> // BIT1
+
+#include <Library/BaseLib.h> // CpuDeadLoop()
+#include <Library/IoLib.h> // IoWrite8()
+#include <Library/ResetSystemLib.h> // ResetCold()
+#include <Library/TimerLib.h> // MicroSecondDelay()
+
+/**
+ Calling this function causes a system-wide reset. This sets
+ all circuitry within the system to its initial state. This type of reset
+ is asynchronous to system operation and operates without regard to
+ cycle boundaries.
+
+ System reset should not return, if it returns, it means the system does
+ not support cold reset.
+**/
+VOID
+EFIAPI
+ResetCold (
+ VOID
+ )
+{
+ IoWrite8 (0xCF9, BIT2 | BIT1); // 1st choice: PIIX3 RCR, RCPU|SRST
+ MicroSecondDelay (50);
+
+ IoWrite8 (0x64, 0xfe); // 2nd choice: keyboard controller
+ CpuDeadLoop ();
+}
+
+/**
+ Calling this function causes a system-wide initialization. The processors
+ are set to their initial state, and pending cycles are not corrupted.
+
+ System reset should not return, if it returns, it means the system does
+ not support warm reset.
+**/
+VOID
+EFIAPI
+ResetWarm (
+ VOID
+ )
+{
+ IoWrite8 (0x64, 0xfe);
+ CpuDeadLoop ();
+}
+
+
+/**
+ This function causes a systemwide reset. The exact type of the reset is
+ defined by the EFI_GUID that follows the Null-terminated Unicode string
+ passed into ResetData. If the platform does not recognize the EFI_GUID in
+ ResetData the platform must pick a supported reset type to perform.The
+ platform may optionally log the parameters from any non-normal reset that
+ occurs.
+
+ @param[in] DataSize The size, in bytes, of ResetData.
+ @param[in] ResetData The data buffer starts with a Null-terminated string,
+ followed by the EFI_GUID.
+**/
+VOID
+EFIAPI
+ResetPlatformSpecific (
+ IN UINTN DataSize,
+ IN VOID *ResetData
+ )
+{
+ ResetCold ();
+}
+
+/**
+ The ResetSystem function resets the entire platform.
+
+ @param[in] ResetType The type of reset to perform.
+ @param[in] ResetStatus The status code for the reset.
+ @param[in] DataSize The size, in bytes, of ResetData.
+ @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or
+ EfiResetShutdown the data buffer starts with a
+ Null-terminated string, optionally followed by
+ additional binary data. The string is a description
+ that the caller may use to further indicate the
+ reason for the system reset.
+**/
+VOID
+EFIAPI
+ResetSystem (
+ IN EFI_RESET_TYPE ResetType,
+ IN EFI_STATUS ResetStatus,
+ IN UINTN DataSize,
+ IN VOID *ResetData OPTIONAL
+ )
+{
+ switch (ResetType) {
+ case EfiResetWarm:
+ ResetWarm ();
+ break;
+
+ case EfiResetCold:
+ ResetCold ();
+ break;
+
+ case EfiResetShutdown:
+ ResetShutdown ();
+ break;
+
+ case EfiResetPlatformSpecific:
+ ResetPlatformSpecific (DataSize, ResetData);
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.c b/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.c new file mode 100644 index 000000000..777b93f1a --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.c @@ -0,0 +1,869 @@ +/** @file
+ Serialize Variables Library implementation
+
+ Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "SerializeVariablesLib.h"
+
+/**
+ Serialization format:
+
+ The SerializeVariablesLib interface does not specify a format
+ for the serialization of the variable data. This library uses
+ a packed array of a non-uniformly sized data structure elements.
+
+ Each variable is stored (packed) as:
+ UINT32 VendorNameSize; // Name size in bytes
+ CHAR16 VendorName[?]; // The variable unicode name including the
+ // null terminating character.
+ EFI_GUID VendorGuid; // The variable GUID
+ UINT32 DataSize; // The size of variable data in bytes
+ UINT8 Data[?]; // The variable data
+
+**/
+
+
+/**
+ Unpacks the next variable from the buffer
+
+ @param[in] Buffer - Buffer pointing to the next variable instance
+ On subsequent calls, the pointer should be incremented
+ by the returned SizeUsed value.
+ @param[in] MaxSize - Max allowable size for the variable data
+ On subsequent calls, this should be decremented
+ by the returned SizeUsed value.
+ @param[out] Name - Variable name string (address in Buffer)
+ @param[out] NameSize - Size of Name in bytes
+ @param[out] Guid - GUID of variable (address in Buffer)
+ @param[out] Attributes - Attributes of variable
+ @param[out] Data - Buffer containing Data for variable (address in Buffer)
+ @param[out] DataSize - Size of Data in bytes
+ @param[out] SizeUsed - Total size used for this variable instance in Buffer
+
+ @return EFI_STATUS based on the success or failure of the operation
+
+**/
+STATIC
+EFI_STATUS
+UnpackVariableFromBuffer (
+ IN VOID *Buffer,
+ IN UINTN MaxSize,
+ OUT CHAR16 **Name,
+ OUT UINT32 *NameSize,
+ OUT EFI_GUID **Guid,
+ OUT UINT32 *Attributes,
+ OUT UINT32 *DataSize,
+ OUT VOID **Data,
+ OUT UINTN *SizeUsed
+ )
+{
+ UINT8 *BytePtr;
+ UINTN Offset;
+
+ BytePtr = (UINT8*)Buffer;
+ Offset = 0;
+
+ *NameSize = *(UINT32*) (BytePtr + Offset);
+ Offset = Offset + sizeof (UINT32);
+
+ if (Offset > MaxSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Name = (CHAR16*) (BytePtr + Offset);
+ Offset = Offset + *(UINT32*)BytePtr;
+ if (Offset > MaxSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Guid = (EFI_GUID*) (BytePtr + Offset);
+ Offset = Offset + sizeof (EFI_GUID);
+ if (Offset > MaxSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Attributes = *(UINT32*) (BytePtr + Offset);
+ Offset = Offset + sizeof (UINT32);
+ if (Offset > MaxSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *DataSize = *(UINT32*) (BytePtr + Offset);
+ Offset = Offset + sizeof (UINT32);
+ if (Offset > MaxSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Data = (VOID*) (BytePtr + Offset);
+ Offset = Offset + *DataSize;
+ if (Offset > MaxSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *SizeUsed = Offset;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Iterates through the variables in the buffer, and calls a callback
+ function for each variable found.
+
+ @param[in] CallbackFunction - Function called for each variable instance
+ @param[in] Context - Passed to each call of CallbackFunction
+ @param[in] Buffer - Buffer containing serialized variables
+ @param[in] MaxSize - Size of Buffer in bytes
+
+ @return EFI_STATUS based on the success or failure of the operation
+
+**/
+STATIC
+EFI_STATUS
+IterateVariablesInBuffer (
+ IN VARIABLE_SERIALIZATION_ITERATION_CALLBACK CallbackFunction,
+ IN VOID *CallbackContext,
+ IN VOID *Buffer,
+ IN UINTN MaxSize
+ )
+{
+ RETURN_STATUS Status;
+ UINTN TotalSizeUsed;
+ UINTN SizeUsed;
+
+ CHAR16 *Name;
+ UINT32 NameSize;
+ CHAR16 *AlignedName;
+ UINT32 AlignedNameMaxSize;
+ EFI_GUID *Guid;
+ UINT32 Attributes;
+ UINT32 DataSize;
+ VOID *Data;
+
+ SizeUsed = 0;
+ AlignedName = NULL;
+ AlignedNameMaxSize = 0;
+ Name = NULL;
+ Guid = NULL;
+ Attributes = 0;
+ DataSize = 0;
+ Data = NULL;
+
+ for (
+ Status = EFI_SUCCESS, TotalSizeUsed = 0;
+ !EFI_ERROR (Status) && (TotalSizeUsed < MaxSize);
+ ) {
+ Status = UnpackVariableFromBuffer (
+ (VOID*) ((UINT8*) Buffer + TotalSizeUsed),
+ (MaxSize - TotalSizeUsed),
+ &Name,
+ &NameSize,
+ &Guid,
+ &Attributes,
+ &DataSize,
+ &Data,
+ &SizeUsed
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // We copy the name to a separately allocated buffer,
+ // to be sure it is 16-bit aligned.
+ //
+ if (NameSize > AlignedNameMaxSize) {
+ if (AlignedName != NULL) {
+ FreePool (AlignedName);
+ }
+ AlignedName = AllocatePool (NameSize);
+ }
+ if (AlignedName == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (AlignedName, Name, NameSize);
+
+ TotalSizeUsed = TotalSizeUsed + SizeUsed;
+
+ //
+ // Run the callback function
+ //
+ Status = (*CallbackFunction) (
+ CallbackContext,
+ AlignedName,
+ Guid,
+ Attributes,
+ DataSize,
+ Data
+ );
+
+ }
+
+ if (AlignedName != NULL) {
+ FreePool (AlignedName);
+ }
+
+ //
+ // Make sure the entire buffer was used, or else return an error
+ //
+ if (TotalSizeUsed != MaxSize) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Deserialize variables error: TotalSizeUsed(%Lu) != MaxSize(%Lu)\n",
+ (UINT64)TotalSizeUsed,
+ (UINT64)MaxSize
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+STATIC
+RETURN_STATUS
+EFIAPI
+IterateVariablesCallbackNop (
+ IN VOID *Context,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ return RETURN_SUCCESS;
+}
+
+
+STATIC
+RETURN_STATUS
+EFIAPI
+IterateVariablesCallbackSetInInstance (
+ IN VOID *Context,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_HANDLE Instance;
+
+ Instance = (EFI_HANDLE) Context;
+
+ return SerializeVariablesAddVariable (
+ Instance,
+ VariableName,
+ VendorGuid,
+ Attributes,
+ DataSize,
+ Data
+ );
+}
+
+
+STATIC
+RETURN_STATUS
+EFIAPI
+IterateVariablesCallbackSetSystemVariable (
+ IN VOID *Context,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_STATUS Status;
+ STATIC CONST UINT32 AuthMask =
+ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS |
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+
+ Status = gRT->SetVariable (
+ VariableName,
+ VendorGuid,
+ Attributes,
+ DataSize,
+ Data
+ );
+
+ if (Status == EFI_SECURITY_VIOLATION && (Attributes & AuthMask) != 0) {
+ DEBUG ((DEBUG_WARN, "%a: setting authenticated variable \"%s\" "
+ "failed with EFI_SECURITY_VIOLATION, ignoring\n", __FUNCTION__,
+ VariableName));
+ Status = EFI_SUCCESS;
+ } else if (Status == EFI_WRITE_PROTECTED) {
+ DEBUG ((DEBUG_WARN, "%a: setting ReadOnly variable \"%s\" "
+ "failed with EFI_WRITE_PROTECTED, ignoring\n", __FUNCTION__,
+ VariableName));
+ Status = EFI_SUCCESS;
+ }
+ return Status;
+}
+
+
+STATIC
+RETURN_STATUS
+EnsureExtraBufferSpace (
+ IN SV_INSTANCE *Instance,
+ IN UINTN Size
+ )
+{
+ VOID *NewBuffer;
+ UINTN NewSize;
+
+ NewSize = Instance->DataSize + Size;
+ if (NewSize <= Instance->BufferSize) {
+ return RETURN_SUCCESS;
+ }
+
+ //
+ // Double the required size to lessen the need to re-allocate in the future
+ //
+ NewSize = 2 * NewSize;
+
+ NewBuffer = AllocatePool (NewSize);
+ if (NewBuffer == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ if (Instance->BufferPtr != NULL) {
+ CopyMem (NewBuffer, Instance->BufferPtr, Instance->DataSize);
+ FreePool (Instance->BufferPtr);
+ }
+
+ Instance->BufferPtr = NewBuffer;
+ Instance->BufferSize = NewSize;
+
+ return RETURN_SUCCESS;
+}
+
+
+STATIC
+VOID
+AppendToBuffer (
+ IN SV_INSTANCE *Instance,
+ IN VOID *Data,
+ IN UINTN Size
+ )
+{
+ UINTN NewSize;
+
+ ASSERT (Instance != NULL);
+ ASSERT (Data != NULL);
+
+ NewSize = Instance->DataSize + Size;
+ ASSERT ((Instance->DataSize + Size) <= Instance->BufferSize);
+
+ CopyMem (
+ (VOID*) (((UINT8*) (Instance->BufferPtr)) + Instance->DataSize),
+ Data,
+ Size
+ );
+
+ Instance->DataSize = NewSize;
+}
+
+
+/**
+ Creates a new variable serialization instance
+
+ @param[out] Handle - Handle for a variable serialization instance
+
+ @retval RETURN_SUCCESS - The variable serialization instance was
+ successfully created.
+ @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to
+ create the variable serialization instance.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesNewInstance (
+ OUT EFI_HANDLE *Handle
+ )
+{
+ SV_INSTANCE *New;
+
+ New = AllocateZeroPool (sizeof (*New));
+ if (New == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ New->Signature = SV_SIGNATURE;
+
+ *Handle = (EFI_HANDLE) New;
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Free memory associated with a variable serialization instance
+
+ @param[in] Handle - Handle for a variable serialization instance
+
+ @retval RETURN_SUCCESS - The variable serialization instance was
+ successfully freed.
+ @retval RETURN_INVALID_PARAMETER - Handle was not a valid
+ variable serialization instance.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesFreeInstance (
+ IN EFI_HANDLE Handle
+ )
+{
+ SV_INSTANCE *Instance;
+
+ Instance = SV_FROM_HANDLE (Handle);
+
+ if (Instance->Signature != SV_SIGNATURE) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ Instance->Signature = 0;
+
+ if (Instance->BufferPtr != NULL) {
+ FreePool (Instance->BufferPtr);
+ }
+
+ FreePool (Instance);
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Creates a new variable serialization instance using the given
+ binary representation of the variables to fill the new instance
+
+ @param[out] Handle - Handle for a variable serialization instance
+ @param[in] Buffer - A buffer with the serialized representation
+ of the variables. Must be the same format as produced
+ by SerializeVariablesToBuffer.
+ @param[in] Size - This is the size of the binary representation
+ of the variables.
+
+ @retval RETURN_SUCCESS - The binary representation was successfully
+ imported into a new variable serialization instance
+ @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to
+ create the new variable serialization instance
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesNewInstanceFromBuffer (
+ OUT EFI_HANDLE *Handle,
+ IN VOID *Buffer,
+ IN UINTN Size
+ )
+{
+ RETURN_STATUS Status;
+
+ Status = SerializeVariablesNewInstance (Handle);
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = IterateVariablesInBuffer (
+ IterateVariablesCallbackNop,
+ NULL,
+ Buffer,
+ Size
+ );
+ if (RETURN_ERROR (Status)) {
+ SerializeVariablesFreeInstance (*Handle);
+ return Status;
+ }
+
+ Status = IterateVariablesInBuffer (
+ IterateVariablesCallbackSetInInstance,
+ (VOID*) *Handle,
+ Buffer,
+ Size
+ );
+ if (RETURN_ERROR (Status)) {
+ SerializeVariablesFreeInstance (*Handle);
+ return Status;
+ }
+
+ return Status;
+}
+
+
+/**
+ Iterates all variables found with RuntimeServices GetNextVariableName
+
+ @param[in] CallbackFunction - Function called for each variable instance
+ @param[in] Context - Passed to each call of CallbackFunction
+
+ @retval RETURN_SUCCESS - All variables were iterated without the
+ CallbackFunction returning an error
+ @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to
+ iterate through the variables
+ @return Any of RETURN_ERROR indicates an error reading the variable
+ or an error was returned from CallbackFunction
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesIterateSystemVariables (
+ IN VARIABLE_SERIALIZATION_ITERATION_CALLBACK CallbackFunction,
+ IN VOID *Context
+ )
+{
+ RETURN_STATUS Status;
+ UINTN VariableNameBufferSize;
+ UINTN VariableNameSize;
+ CHAR16 *VariableName;
+ EFI_GUID VendorGuid;
+ UINTN VariableDataBufferSize;
+ UINTN VariableDataSize;
+ VOID *VariableData;
+ UINT32 VariableAttributes;
+ VOID *NewBuffer;
+
+ //
+ // Initialize the variable name and data buffer variables.
+ //
+ VariableNameBufferSize = sizeof (CHAR16);
+ VariableName = AllocateZeroPool (VariableNameBufferSize);
+
+ VariableDataBufferSize = 0;
+ VariableData = NULL;
+
+ for (;;) {
+ //
+ // Get the next variable name and guid
+ //
+ VariableNameSize = VariableNameBufferSize;
+ Status = gRT->GetNextVariableName (
+ &VariableNameSize,
+ VariableName,
+ &VendorGuid
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // The currently allocated VariableName buffer is too small,
+ // so we allocate a larger buffer, and copy the old buffer
+ // to it.
+ //
+ NewBuffer = AllocatePool (VariableNameSize);
+ if (NewBuffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ CopyMem (NewBuffer, VariableName, VariableNameBufferSize);
+ if (VariableName != NULL) {
+ FreePool (VariableName);
+ }
+ VariableName = NewBuffer;
+ VariableNameBufferSize = VariableNameSize;
+
+ //
+ // Try to get the next variable name again with the larger buffer.
+ //
+ Status = gRT->GetNextVariableName (
+ &VariableNameSize,
+ VariableName,
+ &VendorGuid
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ }
+ break;
+ }
+
+ //
+ // Get the variable data and attributes
+ //
+ VariableDataSize = VariableDataBufferSize;
+ Status = gRT->GetVariable (
+ VariableName,
+ &VendorGuid,
+ &VariableAttributes,
+ &VariableDataSize,
+ VariableData
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // The currently allocated VariableData buffer is too small,
+ // so we allocate a larger buffer.
+ //
+ if (VariableDataBufferSize != 0) {
+ FreePool (VariableData);
+ VariableData = NULL;
+ VariableDataBufferSize = 0;
+ }
+ VariableData = AllocatePool (VariableDataSize);
+ if (VariableData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ VariableDataBufferSize = VariableDataSize;
+
+ //
+ // Try to read the variable again with the larger buffer.
+ //
+ Status = gRT->GetVariable (
+ VariableName,
+ &VendorGuid,
+ &VariableAttributes,
+ &VariableDataSize,
+ VariableData
+ );
+ }
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // Run the callback function
+ //
+ Status = (*CallbackFunction) (
+ Context,
+ VariableName,
+ &VendorGuid,
+ VariableAttributes,
+ VariableDataSize,
+ VariableData
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ }
+
+ if (VariableName != NULL) {
+ FreePool (VariableName);
+ }
+
+ if (VariableData != NULL) {
+ FreePool (VariableData);
+ }
+
+ return Status;
+}
+
+
+/**
+ Iterates all variables found in the variable serialization instance
+
+ @param[in] Handle - Handle for a variable serialization instance
+ @param[in] CallbackFunction - Function called for each variable instance
+ @param[in] Context - Passed to each call of CallbackFunction
+
+ @retval RETURN_SUCCESS - All variables were iterated without the
+ CallbackFunction returning an error
+ @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to
+ iterate through the variables
+ @return Any of RETURN_ERROR indicates an error reading the variable
+ or an error was returned from CallbackFunction
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesIterateInstanceVariables (
+ IN EFI_HANDLE Handle,
+ IN VARIABLE_SERIALIZATION_ITERATION_CALLBACK CallbackFunction,
+ IN VOID *Context
+ )
+{
+ SV_INSTANCE *Instance;
+
+ Instance = SV_FROM_HANDLE (Handle);
+
+ if ((Instance->BufferPtr != NULL) && (Instance->DataSize != 0)) {
+ return IterateVariablesInBuffer (
+ CallbackFunction,
+ Context,
+ Instance->BufferPtr,
+ Instance->DataSize
+ );
+ } else {
+ return RETURN_SUCCESS;
+ }
+}
+
+
+/**
+ Sets all variables found in the variable serialization instance
+
+ @param[in] Handle - Handle for a variable serialization instance
+
+ @retval RETURN_SUCCESS - All variables were set successfully
+ @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to
+ set all the variables
+ @return Any of RETURN_ERROR indicates an error reading the variables
+ or in attempting to set a variable
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesSetSerializedVariables (
+ IN EFI_HANDLE Handle
+ )
+{
+ return SerializeVariablesIterateInstanceVariables (
+ Handle,
+ IterateVariablesCallbackSetSystemVariable,
+ NULL
+ );
+}
+
+
+/**
+ Adds a variable to the variable serialization instance
+
+ @param[in] Handle - Handle for a variable serialization instance
+ @param[in] VariableName - Refer to RuntimeServices GetVariable
+ @param[in] VendorGuid - Refer to RuntimeServices GetVariable
+ @param[in] Attributes - Refer to RuntimeServices GetVariable
+ @param[in] DataSize - Refer to RuntimeServices GetVariable
+ @param[in] Data - Refer to RuntimeServices GetVariable
+
+ @retval RETURN_SUCCESS - All variables were set successfully
+ @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to
+ add the variable
+ @retval RETURN_INVALID_PARAMETER - Handle was not a valid
+ variable serialization instance or
+ VariableName, VariableGuid or Data are NULL.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesAddVariable (
+ IN EFI_HANDLE Handle,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ RETURN_STATUS Status;
+ SV_INSTANCE *Instance;
+ UINT32 SerializedNameSize;
+ UINT32 SerializedDataSize;
+ UINTN SerializedSize;
+
+ Instance = SV_FROM_HANDLE (Handle);
+
+ if ((Instance->Signature != SV_SIGNATURE) ||
+ (VariableName == NULL) || (VendorGuid == NULL) || (Data == NULL)) {
+ }
+
+ SerializedNameSize = (UINT32) StrSize (VariableName);
+
+ SerializedSize =
+ sizeof (SerializedNameSize) +
+ SerializedNameSize +
+ sizeof (*VendorGuid) +
+ sizeof (Attributes) +
+ sizeof (SerializedDataSize) +
+ DataSize;
+
+ Status = EnsureExtraBufferSpace (
+ Instance,
+ SerializedSize
+ );
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Add name size (UINT32)
+ //
+ AppendToBuffer (Instance, (VOID*) &SerializedNameSize, sizeof (SerializedNameSize));
+
+ //
+ // Add variable unicode name string
+ //
+ AppendToBuffer (Instance, (VOID*) VariableName, SerializedNameSize);
+
+ //
+ // Add variable GUID
+ //
+ AppendToBuffer (Instance, (VOID*) VendorGuid, sizeof (*VendorGuid));
+
+ //
+ // Add variable attributes
+ //
+ AppendToBuffer (Instance, (VOID*) &Attributes, sizeof (Attributes));
+
+ //
+ // Add variable data size (UINT32)
+ //
+ SerializedDataSize = (UINT32) DataSize;
+ AppendToBuffer (Instance, (VOID*) &SerializedDataSize, sizeof (SerializedDataSize));
+
+ //
+ // Add variable data
+ //
+ AppendToBuffer (Instance, Data, DataSize);
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Serializes the variables known to this instance into the
+ provided buffer.
+
+ @param[in] Handle - Handle for a variable serialization instance
+ @param[out] Buffer - A buffer to store the binary representation
+ of the variables.
+ @param[in,out] Size - On input this is the size of the buffer.
+ On output this is the size of the binary representation
+ of the variables.
+
+ @retval RETURN_SUCCESS - The binary representation was successfully
+ completed and returned in the buffer.
+ @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to
+ save the variables to the buffer.
+ @retval RETURN_INVALID_PARAMETER - Handle was not a valid
+ variable serialization instance or
+ Size or Buffer were NULL.
+ @retval RETURN_BUFFER_TOO_SMALL - The Buffer size as indicated by
+ the Size parameter was too small for the serialized
+ variable data. Size is returned with the required size.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerializeVariablesToBuffer (
+ IN EFI_HANDLE Handle,
+ OUT VOID *Buffer,
+ IN OUT UINTN *Size
+ )
+{
+ SV_INSTANCE *Instance;
+
+ Instance = SV_FROM_HANDLE (Handle);
+
+ if (Size == NULL) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (*Size < Instance->DataSize) {
+ *Size = Instance->DataSize;
+ return RETURN_BUFFER_TOO_SMALL;
+ }
+
+ if (Buffer == NULL) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ *Size = Instance->DataSize;
+ CopyMem (Buffer, Instance->BufferPtr, Instance->DataSize);
+
+ return RETURN_SUCCESS;
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.h b/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.h new file mode 100644 index 000000000..966fab77b --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.h @@ -0,0 +1,33 @@ +/** @file
+ Serialize Variables Library implementation
+
+ Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __SERIALIZE_VARIABLES_LIB_INSTANCE__
+#define __SERIALIZE_VARIABLES_LIB_INSTANCE__
+
+#include <Uefi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SerializeVariablesLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#define SV_FROM_HANDLE(a) CR (a, SV_INSTANCE, Signature, SV_SIGNATURE)
+#define SV_SIGNATURE SIGNATURE_32 ('S', 'V', 'A', 'R')
+
+typedef struct {
+ UINT32 Signature;
+ VOID *BufferPtr;
+ UINTN BufferSize;
+ UINTN DataSize;
+} SV_INSTANCE;
+
+#endif
+
diff --git a/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf b/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf new file mode 100644 index 000000000..db45ba766 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf @@ -0,0 +1,37 @@ +## @file
+# Serialize Variables Library implementation
+#
+# This library serializes and deserializes UEFI variables
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeSerializeVariablesLib
+ FILE_GUID = 9515f92a-83ae-45fd-9d2e-e3dc15df52d0
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = SerializeVariablesLib|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER
+
+[Sources]
+ SerializeVariablesLib.c
+ SerializeVariablesLib.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+
diff --git a/roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.c b/roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.c new file mode 100644 index 000000000..4d2c23cfb --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.c @@ -0,0 +1,105 @@ +/** @file
+
+ A hook-in library for MdeModulePkg/Universal/SmbiosDxe, in order to set
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion (and possibly other PCDs)
+ just before SmbiosDxe consumes them.
+
+ Copyright (C) 2013, 2015, Red Hat, Inc.
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/SmBios.h>
+
+#include <Base.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/QemuFwCfgLib.h>
+
+typedef union {
+ SMBIOS_TABLE_ENTRY_POINT V2;
+ SMBIOS_TABLE_3_0_ENTRY_POINT V3;
+} QEMU_SMBIOS_ANCHOR;
+
+RETURN_STATUS
+EFIAPI
+DetectSmbiosVersion (
+ VOID
+ )
+{
+ FIRMWARE_CONFIG_ITEM Anchor, Tables;
+ UINTN AnchorSize, TablesSize;
+ QEMU_SMBIOS_ANCHOR QemuAnchor;
+ UINT16 SmbiosVersion;
+ RETURN_STATUS PcdStatus;
+
+ if (PcdGetBool (PcdQemuSmbiosValidated)) {
+ //
+ // Some other module, linked against this library, has already performed
+ // the task at hand. This should never happen, but it's easy to handle;
+ // just exit early.
+ //
+ return RETURN_SUCCESS;
+ }
+
+ if (RETURN_ERROR (QemuFwCfgFindFile (
+ "etc/smbios/smbios-anchor", &Anchor, &AnchorSize)) ||
+ RETURN_ERROR (QemuFwCfgFindFile (
+ "etc/smbios/smbios-tables", &Tables, &TablesSize)) ||
+ TablesSize == 0) {
+ return RETURN_SUCCESS;
+ }
+
+ QemuFwCfgSelectItem (Anchor);
+
+ switch (AnchorSize) {
+ case sizeof QemuAnchor.V2:
+ QemuFwCfgReadBytes (AnchorSize, &QemuAnchor);
+
+ if (QemuAnchor.V2.MajorVersion != 2 ||
+ QemuAnchor.V2.TableLength != TablesSize ||
+ CompareMem (QemuAnchor.V2.AnchorString, "_SM_", 4) != 0 ||
+ CompareMem (QemuAnchor.V2.IntermediateAnchorString, "_DMI_", 5) != 0) {
+ return RETURN_SUCCESS;
+ }
+ SmbiosVersion = (UINT16)(QemuAnchor.V2.MajorVersion << 8 |
+ QemuAnchor.V2.MinorVersion);
+ break;
+
+ case sizeof QemuAnchor.V3:
+ QemuFwCfgReadBytes (AnchorSize, &QemuAnchor);
+
+ if (QemuAnchor.V3.MajorVersion != 3 ||
+ QemuAnchor.V3.TableMaximumSize != TablesSize ||
+ CompareMem (QemuAnchor.V3.AnchorString, "_SM3_", 5) != 0) {
+ return RETURN_SUCCESS;
+ }
+ SmbiosVersion = (UINT16)(QemuAnchor.V3.MajorVersion << 8 |
+ QemuAnchor.V3.MinorVersion);
+
+ DEBUG ((DEBUG_INFO, "%a: SMBIOS 3.x DocRev from QEMU: 0x%02x\n",
+ __FUNCTION__, QemuAnchor.V3.DocRev));
+ PcdStatus = PcdSet8S (PcdSmbiosDocRev, QemuAnchor.V3.DocRev);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ break;
+
+ default:
+ return RETURN_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: SMBIOS version from QEMU: 0x%04x\n", __FUNCTION__,
+ SmbiosVersion));
+ PcdStatus = PcdSet16S (PcdSmbiosVersion, SmbiosVersion);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ //
+ // SMBIOS platform drivers can now fetch and install
+ // "etc/smbios/smbios-tables" from QEMU.
+ //
+ PcdStatus = PcdSetBoolS (PcdQemuSmbiosValidated, TRUE);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.inf b/roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.inf new file mode 100644 index 000000000..8417498d9 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.inf @@ -0,0 +1,47 @@ +## @file
+#
+# A hook-in library for MdeModulePkg/Universal/SmbiosDxe, in order to set
+# gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion (and possibly other PCDs)
+# just before SmbiosDxe consumes them.
+#
+# Copyright (C) 2013, 2015, Red Hat, Inc.
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DetectSmbiosVersionLib
+ FILE_GUID = 6c633bb2-ae33-49ae-9f89-b5aa999fe3ae
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = SmbiosVersionLib|DXE_DRIVER
+ CONSTRUCTOR = DetectSmbiosVersion
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64
+#
+
+[Sources]
+ DetectSmbiosVersionLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ PcdLib
+ QemuFwCfgLib
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosDocRev
+ gUefiOvmfPkgTokenSpaceGuid.PcdQemuSmbiosValidated
diff --git a/roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c b/roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c new file mode 100644 index 000000000..7ef7ed983 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c @@ -0,0 +1,1255 @@ +/** @file
+ The CPU specific programming for PiSmmCpuDxeSmm module.
+
+ Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <IndustryStandard/Q35MchIch9.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/PcdLib.h>
+#include <Library/SmmCpuFeaturesLib.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <PiSmm.h>
+#include <Register/Intel/SmramSaveStateMap.h>
+#include <Register/QemuSmramSaveStateMap.h>
+
+//
+// EFER register LMA bit
+//
+#define LMA BIT10
+
+/**
+ The constructor function
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+SmmCpuFeaturesLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ //
+ // No need to program SMRRs on our virtual platform.
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Called during the very first SMI into System Management Mode to initialize
+ CPU features, including SMBASE, for the currently executing CPU. Since this
+ is the first SMI, the SMRAM Save State Map is at the default address of
+ SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET. The currently executing
+ CPU is specified by CpuIndex and CpuIndex can be used to access information
+ about the currently executing CPU in the ProcessorInfo array and the
+ HotPlugCpuData data structure.
+
+ @param[in] CpuIndex The index of the CPU to initialize. The value
+ must be between 0 and the NumberOfCpus field in
+ the System Management System Table (SMST).
+ @param[in] IsMonarch TRUE if the CpuIndex is the index of the CPU that
+ was elected as monarch during System Management
+ Mode initialization.
+ FALSE if the CpuIndex is not the index of the CPU
+ that was elected as monarch during System
+ Management Mode initialization.
+ @param[in] ProcessorInfo Pointer to an array of EFI_PROCESSOR_INFORMATION
+ structures. ProcessorInfo[CpuIndex] contains the
+ information for the currently executing CPU.
+ @param[in] CpuHotPlugData Pointer to the CPU_HOT_PLUG_DATA structure that
+ contains the ApidId and SmBase arrays.
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesInitializeProcessor (
+ IN UINTN CpuIndex,
+ IN BOOLEAN IsMonarch,
+ IN EFI_PROCESSOR_INFORMATION *ProcessorInfo,
+ IN CPU_HOT_PLUG_DATA *CpuHotPlugData
+ )
+{
+ QEMU_SMRAM_SAVE_STATE_MAP *CpuState;
+
+ //
+ // Configure SMBASE.
+ //
+ CpuState = (QEMU_SMRAM_SAVE_STATE_MAP *)(UINTN)(
+ SMM_DEFAULT_SMBASE +
+ SMRAM_SAVE_STATE_MAP_OFFSET
+ );
+ if ((CpuState->x86.SMMRevId & 0xFFFF) == 0) {
+ CpuState->x86.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
+ } else {
+ CpuState->x64.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
+ }
+
+ //
+ // No need to program SMRRs on our virtual platform.
+ //
+}
+
+/**
+ This function updates the SMRAM save state on the currently executing CPU
+ to resume execution at a specific address after an RSM instruction. This
+ function must evaluate the SMRAM save state to determine the execution mode
+ the RSM instruction resumes and update the resume execution address with
+ either NewInstructionPointer32 or NewInstructionPoint. The auto HALT restart
+ flag in the SMRAM save state must always be cleared. This function returns
+ the value of the instruction pointer from the SMRAM save state that was
+ replaced. If this function returns 0, then the SMRAM save state was not
+ modified.
+
+ This function is called during the very first SMI on each CPU after
+ SmmCpuFeaturesInitializeProcessor() to set a flag in normal execution mode
+ to signal that the SMBASE of each CPU has been updated before the default
+ SMBASE address is used for the first SMI to the next CPU.
+
+ @param[in] CpuIndex The index of the CPU to hook. The value
+ must be between 0 and the NumberOfCpus
+ field in the System Management System
+ Table (SMST).
+ @param[in] CpuState Pointer to SMRAM Save State Map for the
+ currently executing CPU.
+ @param[in] NewInstructionPointer32 Instruction pointer to use if resuming to
+ 32-bit execution mode from 64-bit SMM.
+ @param[in] NewInstructionPointer Instruction pointer to use if resuming to
+ same execution mode as SMM.
+
+ @retval 0 This function did modify the SMRAM save state.
+ @retval > 0 The original instruction pointer value from the SMRAM save state
+ before it was replaced.
+**/
+UINT64
+EFIAPI
+SmmCpuFeaturesHookReturnFromSmm (
+ IN UINTN CpuIndex,
+ IN SMRAM_SAVE_STATE_MAP *CpuState,
+ IN UINT64 NewInstructionPointer32,
+ IN UINT64 NewInstructionPointer
+ )
+{
+ UINT64 OriginalInstructionPointer;
+ QEMU_SMRAM_SAVE_STATE_MAP *CpuSaveState;
+
+ CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)CpuState;
+ if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
+ OriginalInstructionPointer = (UINT64)CpuSaveState->x86._EIP;
+ CpuSaveState->x86._EIP = (UINT32)NewInstructionPointer;
+ //
+ // Clear the auto HALT restart flag so the RSM instruction returns
+ // program control to the instruction following the HLT instruction.
+ //
+ if ((CpuSaveState->x86.AutoHALTRestart & BIT0) != 0) {
+ CpuSaveState->x86.AutoHALTRestart &= ~BIT0;
+ }
+ } else {
+ OriginalInstructionPointer = CpuSaveState->x64._RIP;
+ if ((CpuSaveState->x64.IA32_EFER & LMA) == 0) {
+ CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer32;
+ } else {
+ CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer;
+ }
+ //
+ // Clear the auto HALT restart flag so the RSM instruction returns
+ // program control to the instruction following the HLT instruction.
+ //
+ if ((CpuSaveState->x64.AutoHALTRestart & BIT0) != 0) {
+ CpuSaveState->x64.AutoHALTRestart &= ~BIT0;
+ }
+ }
+ return OriginalInstructionPointer;
+}
+
+/**
+ Hook point in normal execution mode that allows the one CPU that was elected
+ as monarch during System Management Mode initialization to perform additional
+ initialization actions immediately after all of the CPUs have processed their
+ first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE
+ into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm().
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesSmmRelocationComplete (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN MapPagesBase;
+ UINTN MapPagesCount;
+
+ if (!MemEncryptSevIsEnabled ()) {
+ return;
+ }
+
+ //
+ // Now that SMBASE relocation is complete, re-encrypt the original SMRAM save
+ // state map's container pages, and release the pages to DXE. (The pages were
+ // allocated in PlatformPei.)
+ //
+ Status = MemEncryptSevLocateInitialSmramSaveStateMapPages (
+ &MapPagesBase,
+ &MapPagesCount
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = MemEncryptSevSetPageEncMask (
+ 0, // Cr3BaseAddress -- use current CR3
+ MapPagesBase, // BaseAddress
+ MapPagesCount, // NumPages
+ TRUE // Flush
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: MemEncryptSevSetPageEncMask(): %r\n",
+ __FUNCTION__, Status));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ ZeroMem ((VOID *)MapPagesBase, EFI_PAGES_TO_SIZE (MapPagesCount));
+
+ if (PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {
+ //
+ // The initial SMRAM Save State Map has been covered as part of a larger
+ // reserved memory allocation in PlatformPei's InitializeRamRegions(). That
+ // allocation is supposed to survive into OS runtime; we must not release
+ // any part of it. Only re-assert the containment here.
+ //
+ ASSERT (SMM_DEFAULT_SMBASE <= MapPagesBase);
+ ASSERT (
+ (MapPagesBase + EFI_PAGES_TO_SIZE (MapPagesCount) <=
+ SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE)
+ );
+ } else {
+ Status = gBS->FreePages (MapPagesBase, MapPagesCount);
+ ASSERT_EFI_ERROR (Status);
+ }
+}
+
+/**
+ Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is
+ returned, then a custom SMI handler is not provided by this library,
+ and the default SMI handler must be used.
+
+ @retval 0 Use the default SMI handler.
+ @retval > 0 Use the SMI handler installed by
+ SmmCpuFeaturesInstallSmiHandler(). The caller is required to
+ allocate enough SMRAM for each CPU to support the size of the
+ custom SMI handler.
+**/
+UINTN
+EFIAPI
+SmmCpuFeaturesGetSmiHandlerSize (
+ VOID
+ )
+{
+ return 0;
+}
+
+/**
+ Install a custom SMI handler for the CPU specified by CpuIndex. This
+ function is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size
+ is greater than zero and is called by the CPU that was elected as monarch
+ during System Management Mode initialization.
+
+ @param[in] CpuIndex The index of the CPU to install the custom SMI handler.
+ The value must be between 0 and the NumberOfCpus field
+ in the System Management System Table (SMST).
+ @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex.
+ @param[in] SmiStack The stack to use when an SMI is processed by the
+ the CPU specified by CpuIndex.
+ @param[in] StackSize The size, in bytes, if the stack used when an SMI is
+ processed by the CPU specified by CpuIndex.
+ @param[in] GdtBase The base address of the GDT to use when an SMI is
+ processed by the CPU specified by CpuIndex.
+ @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is
+ processed by the CPU specified by CpuIndex.
+ @param[in] IdtBase The base address of the IDT to use when an SMI is
+ processed by the CPU specified by CpuIndex.
+ @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is
+ processed by the CPU specified by CpuIndex.
+ @param[in] Cr3 The base address of the page tables to use when an SMI
+ is processed by the CPU specified by CpuIndex.
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesInstallSmiHandler (
+ IN UINTN CpuIndex,
+ IN UINT32 SmBase,
+ IN VOID *SmiStack,
+ IN UINTN StackSize,
+ IN UINTN GdtBase,
+ IN UINTN GdtSize,
+ IN UINTN IdtBase,
+ IN UINTN IdtSize,
+ IN UINT32 Cr3
+ )
+{
+}
+
+/**
+ Determines if MTRR registers must be configured to set SMRAM cache-ability
+ when executing in System Management Mode.
+
+ @retval TRUE MTRR registers must be configured to set SMRAM cache-ability.
+ @retval FALSE MTRR registers do not need to be configured to set SMRAM
+ cache-ability.
+**/
+BOOLEAN
+EFIAPI
+SmmCpuFeaturesNeedConfigureMtrrs (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+/**
+ Disable SMRR register if SMRR is supported and
+ SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE.
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesDisableSmrr (
+ VOID
+ )
+{
+ //
+ // No SMRR support, nothing to do
+ //
+}
+
+/**
+ Enable SMRR register if SMRR is supported and
+ SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE.
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesReenableSmrr (
+ VOID
+ )
+{
+ //
+ // No SMRR support, nothing to do
+ //
+}
+
+/**
+ Processor specific hook point each time a CPU enters System Management Mode.
+
+ @param[in] CpuIndex The index of the CPU that has entered SMM. The value
+ must be between 0 and the NumberOfCpus field in the
+ System Management System Table (SMST).
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesRendezvousEntry (
+ IN UINTN CpuIndex
+ )
+{
+ //
+ // No SMRR support, nothing to do
+ //
+}
+
+/**
+ Processor specific hook point each time a CPU exits System Management Mode.
+
+ @param[in] CpuIndex The index of the CPU that is exiting SMM. The value
+ must be between 0 and the NumberOfCpus field in the
+ System Management System Table (SMST).
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesRendezvousExit (
+ IN UINTN CpuIndex
+ )
+{
+}
+
+/**
+ Check to see if an SMM register is supported by a specified CPU.
+
+ @param[in] CpuIndex The index of the CPU to check for SMM register support.
+ The value must be between 0 and the NumberOfCpus field
+ in the System Management System Table (SMST).
+ @param[in] RegName Identifies the SMM register to check for support.
+
+ @retval TRUE The SMM register specified by RegName is supported by the CPU
+ specified by CpuIndex.
+ @retval FALSE The SMM register specified by RegName is not supported by the
+ CPU specified by CpuIndex.
+**/
+BOOLEAN
+EFIAPI
+SmmCpuFeaturesIsSmmRegisterSupported (
+ IN UINTN CpuIndex,
+ IN SMM_REG_NAME RegName
+ )
+{
+ ASSERT (RegName == SmmRegFeatureControl);
+ return FALSE;
+}
+
+/**
+ Returns the current value of the SMM register for the specified CPU.
+ If the SMM register is not supported, then 0 is returned.
+
+ @param[in] CpuIndex The index of the CPU to read the SMM register. The
+ value must be between 0 and the NumberOfCpus field in
+ the System Management System Table (SMST).
+ @param[in] RegName Identifies the SMM register to read.
+
+ @return The value of the SMM register specified by RegName from the CPU
+ specified by CpuIndex.
+**/
+UINT64
+EFIAPI
+SmmCpuFeaturesGetSmmRegister (
+ IN UINTN CpuIndex,
+ IN SMM_REG_NAME RegName
+ )
+{
+ //
+ // This is called for SmmRegSmmDelayed, SmmRegSmmBlocked, SmmRegSmmEnable.
+ // The last of these should actually be SmmRegSmmDisable, so we can just
+ // return FALSE.
+ //
+ return 0;
+}
+
+/**
+ Sets the value of an SMM register on a specified CPU.
+ If the SMM register is not supported, then no action is performed.
+
+ @param[in] CpuIndex The index of the CPU to write the SMM register. The
+ value must be between 0 and the NumberOfCpus field in
+ the System Management System Table (SMST).
+ @param[in] RegName Identifies the SMM register to write.
+ registers are read-only.
+ @param[in] Value The value to write to the SMM register.
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesSetSmmRegister (
+ IN UINTN CpuIndex,
+ IN SMM_REG_NAME RegName,
+ IN UINT64 Value
+ )
+{
+ ASSERT (FALSE);
+}
+
+///
+/// Macro used to simplify the lookup table entries of type
+/// CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
+///
+#define SMM_CPU_OFFSET(Field) OFFSET_OF (QEMU_SMRAM_SAVE_STATE_MAP, Field)
+
+///
+/// Macro used to simplify the lookup table entries of type
+/// CPU_SMM_SAVE_STATE_REGISTER_RANGE
+///
+#define SMM_REGISTER_RANGE(Start, End) { Start, End, End - Start + 1 }
+
+///
+/// Structure used to describe a range of registers
+///
+typedef struct {
+ EFI_SMM_SAVE_STATE_REGISTER Start;
+ EFI_SMM_SAVE_STATE_REGISTER End;
+ UINTN Length;
+} CPU_SMM_SAVE_STATE_REGISTER_RANGE;
+
+///
+/// Structure used to build a lookup table to retrieve the widths and offsets
+/// associated with each supported EFI_SMM_SAVE_STATE_REGISTER value
+///
+
+#define SMM_SAVE_STATE_REGISTER_FIRST_INDEX 1
+
+typedef struct {
+ UINT8 Width32;
+ UINT8 Width64;
+ UINT16 Offset32;
+ UINT16 Offset64Lo;
+ UINT16 Offset64Hi;
+ BOOLEAN Writeable;
+} CPU_SMM_SAVE_STATE_LOOKUP_ENTRY;
+
+///
+/// Table used by GetRegisterIndex() to convert an EFI_SMM_SAVE_STATE_REGISTER
+/// value to an index into a table of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
+///
+STATIC CONST CPU_SMM_SAVE_STATE_REGISTER_RANGE mSmmCpuRegisterRanges[] = {
+ SMM_REGISTER_RANGE (
+ EFI_SMM_SAVE_STATE_REGISTER_GDTBASE,
+ EFI_SMM_SAVE_STATE_REGISTER_LDTINFO
+ ),
+ SMM_REGISTER_RANGE (
+ EFI_SMM_SAVE_STATE_REGISTER_ES,
+ EFI_SMM_SAVE_STATE_REGISTER_RIP
+ ),
+ SMM_REGISTER_RANGE (
+ EFI_SMM_SAVE_STATE_REGISTER_RFLAGS,
+ EFI_SMM_SAVE_STATE_REGISTER_CR4
+ ),
+ { (EFI_SMM_SAVE_STATE_REGISTER)0, (EFI_SMM_SAVE_STATE_REGISTER)0, 0 }
+};
+
+///
+/// Lookup table used to retrieve the widths and offsets associated with each
+/// supported EFI_SMM_SAVE_STATE_REGISTER value
+///
+STATIC CONST CPU_SMM_SAVE_STATE_LOOKUP_ENTRY mSmmCpuWidthOffset[] = {
+ {
+ 0, // Width32
+ 0, // Width64
+ 0, // Offset32
+ 0, // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // Reserved
+
+ //
+ // CPU Save State registers defined in PI SMM CPU Protocol.
+ //
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._GDTRBase), // Offset64Lo
+ SMM_CPU_OFFSET (x64._GDTRBase) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_GDTBASE = 4
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._IDTRBase), // Offset64Lo
+ SMM_CPU_OFFSET (x64._IDTRBase) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_IDTBASE = 5
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._LDTRBase), // Offset64Lo
+ SMM_CPU_OFFSET (x64._LDTRBase) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_LDTBASE = 6
+
+ {
+ 0, // Width32
+ 0, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._GDTRLimit), // Offset64Lo
+ SMM_CPU_OFFSET (x64._GDTRLimit) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_GDTLIMIT = 7
+
+ {
+ 0, // Width32
+ 0, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._IDTRLimit), // Offset64Lo
+ SMM_CPU_OFFSET (x64._IDTRLimit) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_IDTLIMIT = 8
+
+ {
+ 0, // Width32
+ 0, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._LDTRLimit), // Offset64Lo
+ SMM_CPU_OFFSET (x64._LDTRLimit) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_LDTLIMIT = 9
+
+ {
+ 0, // Width32
+ 0, // Width64
+ 0, // Offset32
+ 0, // Offset64Lo
+ 0 + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_LDTINFO = 10
+
+ {
+ 4, // Width32
+ 4, // Width64
+ SMM_CPU_OFFSET (x86._ES), // Offset32
+ SMM_CPU_OFFSET (x64._ES), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_ES = 20
+
+ {
+ 4, // Width32
+ 4, // Width64
+ SMM_CPU_OFFSET (x86._CS), // Offset32
+ SMM_CPU_OFFSET (x64._CS), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_CS = 21
+
+ {
+ 4, // Width32
+ 4, // Width64
+ SMM_CPU_OFFSET (x86._SS), // Offset32
+ SMM_CPU_OFFSET (x64._SS), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_SS = 22
+
+ {
+ 4, // Width32
+ 4, // Width64
+ SMM_CPU_OFFSET (x86._DS), // Offset32
+ SMM_CPU_OFFSET (x64._DS), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_DS = 23
+
+ {
+ 4, // Width32
+ 4, // Width64
+ SMM_CPU_OFFSET (x86._FS), // Offset32
+ SMM_CPU_OFFSET (x64._FS), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_FS = 24
+
+ {
+ 4, // Width32
+ 4, // Width64
+ SMM_CPU_OFFSET (x86._GS), // Offset32
+ SMM_CPU_OFFSET (x64._GS), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_GS = 25
+
+ {
+ 0, // Width32
+ 4, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._LDTR), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_LDTR_SEL = 26
+
+ {
+ 4, // Width32
+ 4, // Width64
+ SMM_CPU_OFFSET (x86._TR), // Offset32
+ SMM_CPU_OFFSET (x64._TR), // Offset64Lo
+ 0, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_TR_SEL = 27
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._DR7), // Offset32
+ SMM_CPU_OFFSET (x64._DR7), // Offset64Lo
+ SMM_CPU_OFFSET (x64._DR7) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_DR7 = 28
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._DR6), // Offset32
+ SMM_CPU_OFFSET (x64._DR6), // Offset64Lo
+ SMM_CPU_OFFSET (x64._DR6) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_DR6 = 29
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R8), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R8) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R8 = 30
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R9), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R9) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R9 = 31
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R10), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R10) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R10 = 32
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R11), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R11) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R11 = 33
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R12), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R12) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R12 = 34
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R13), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R13) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R13 = 35
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R14), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R14) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R14 = 36
+
+ {
+ 0, // Width32
+ 8, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._R15), // Offset64Lo
+ SMM_CPU_OFFSET (x64._R15) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_R15 = 37
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._EAX), // Offset32
+ SMM_CPU_OFFSET (x64._RAX), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RAX) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RAX = 38
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._EBX), // Offset32
+ SMM_CPU_OFFSET (x64._RBX), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RBX) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RBX = 39
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._ECX), // Offset32
+ SMM_CPU_OFFSET (x64._RCX), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RCX) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RCX = 40
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._EDX), // Offset32
+ SMM_CPU_OFFSET (x64._RDX), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RDX) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RDX = 41
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._ESP), // Offset32
+ SMM_CPU_OFFSET (x64._RSP), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RSP) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RSP = 42
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._EBP), // Offset32
+ SMM_CPU_OFFSET (x64._RBP), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RBP) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RBP = 43
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._ESI), // Offset32
+ SMM_CPU_OFFSET (x64._RSI), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RSI) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RSI = 44
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._EDI), // Offset32
+ SMM_CPU_OFFSET (x64._RDI), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RDI) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RDI = 45
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._EIP), // Offset32
+ SMM_CPU_OFFSET (x64._RIP), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RIP) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RIP = 46
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._EFLAGS), // Offset32
+ SMM_CPU_OFFSET (x64._RFLAGS), // Offset64Lo
+ SMM_CPU_OFFSET (x64._RFLAGS) + 4, // Offset64Hi
+ TRUE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_RFLAGS = 51
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._CR0), // Offset32
+ SMM_CPU_OFFSET (x64._CR0), // Offset64Lo
+ SMM_CPU_OFFSET (x64._CR0) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_CR0 = 52
+
+ {
+ 4, // Width32
+ 8, // Width64
+ SMM_CPU_OFFSET (x86._CR3), // Offset32
+ SMM_CPU_OFFSET (x64._CR3), // Offset64Lo
+ SMM_CPU_OFFSET (x64._CR3) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_CR3 = 53
+
+ {
+ 0, // Width32
+ 4, // Width64
+ 0, // Offset32
+ SMM_CPU_OFFSET (x64._CR4), // Offset64Lo
+ SMM_CPU_OFFSET (x64._CR4) + 4, // Offset64Hi
+ FALSE // Writeable
+ }, // EFI_SMM_SAVE_STATE_REGISTER_CR4 = 54
+};
+
+//
+// No support for I/O restart
+//
+
+/**
+ Read information from the CPU save state.
+
+ @param Register Specifies the CPU register to read form the save state.
+
+ @retval 0 Register is not valid
+ @retval >0 Index into mSmmCpuWidthOffset[] associated with Register
+
+**/
+STATIC
+UINTN
+GetRegisterIndex (
+ IN EFI_SMM_SAVE_STATE_REGISTER Register
+ )
+{
+ UINTN Index;
+ UINTN Offset;
+
+ for (Index = 0, Offset = SMM_SAVE_STATE_REGISTER_FIRST_INDEX;
+ mSmmCpuRegisterRanges[Index].Length != 0;
+ Index++) {
+ if (Register >= mSmmCpuRegisterRanges[Index].Start &&
+ Register <= mSmmCpuRegisterRanges[Index].End) {
+ return Register - mSmmCpuRegisterRanges[Index].Start + Offset;
+ }
+ Offset += mSmmCpuRegisterRanges[Index].Length;
+ }
+ return 0;
+}
+
+/**
+ Read a CPU Save State register on the target processor.
+
+ This function abstracts the differences that whether the CPU Save State
+ register is in the IA32 CPU Save State Map or X64 CPU Save State Map.
+
+ This function supports reading a CPU Save State register in SMBase relocation
+ handler.
+
+ @param[in] CpuIndex Specifies the zero-based index of the CPU save
+ state.
+ @param[in] RegisterIndex Index into mSmmCpuWidthOffset[] look up table.
+ @param[in] Width The number of bytes to read from the CPU save
+ state.
+ @param[out] Buffer Upon return, this holds the CPU register value
+ read from the save state.
+
+ @retval EFI_SUCCESS The register was read from Save State.
+ @retval EFI_NOT_FOUND The register is not defined for the Save State
+ of Processor.
+ @retval EFI_INVALID_PARAMTER This or Buffer is NULL.
+
+**/
+STATIC
+EFI_STATUS
+ReadSaveStateRegisterByIndex (
+ IN UINTN CpuIndex,
+ IN UINTN RegisterIndex,
+ IN UINTN Width,
+ OUT VOID *Buffer
+ )
+{
+ QEMU_SMRAM_SAVE_STATE_MAP *CpuSaveState;
+
+ CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
+
+ if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
+ //
+ // If 32-bit mode width is zero, then the specified register can not be
+ // accessed
+ //
+ if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If Width is bigger than the 32-bit mode width, then the specified
+ // register can not be accessed
+ //
+ if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Write return buffer
+ //
+ ASSERT(CpuSaveState != NULL);
+ CopyMem (
+ Buffer,
+ (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32,
+ Width
+ );
+ } else {
+ //
+ // If 64-bit mode width is zero, then the specified register can not be
+ // accessed
+ //
+ if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If Width is bigger than the 64-bit mode width, then the specified
+ // register can not be accessed
+ //
+ if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Write lower 32-bits of return buffer
+ //
+ CopyMem (
+ Buffer,
+ (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo,
+ MIN (4, Width)
+ );
+ if (Width >= 4) {
+ //
+ // Write upper 32-bits of return buffer
+ //
+ CopyMem (
+ (UINT8 *)Buffer + 4,
+ (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi,
+ Width - 4
+ );
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Read an SMM Save State register on the target processor. If this function
+ returns EFI_UNSUPPORTED, then the caller is responsible for reading the
+ SMM Save Sate register.
+
+ @param[in] CpuIndex The index of the CPU to read the SMM Save State. The
+ value must be between 0 and the NumberOfCpus field in
+ the System Management System Table (SMST).
+ @param[in] Register The SMM Save State register to read.
+ @param[in] Width The number of bytes to read from the CPU save state.
+ @param[out] Buffer Upon return, this holds the CPU register value read
+ from the save state.
+
+ @retval EFI_SUCCESS The register was read from Save State.
+ @retval EFI_INVALID_PARAMTER Buffer is NULL.
+ @retval EFI_UNSUPPORTED This function does not support reading
+ Register.
+**/
+EFI_STATUS
+EFIAPI
+SmmCpuFeaturesReadSaveStateRegister (
+ IN UINTN CpuIndex,
+ IN EFI_SMM_SAVE_STATE_REGISTER Register,
+ IN UINTN Width,
+ OUT VOID *Buffer
+ )
+{
+ UINTN RegisterIndex;
+ QEMU_SMRAM_SAVE_STATE_MAP *CpuSaveState;
+
+ //
+ // Check for special EFI_SMM_SAVE_STATE_REGISTER_LMA
+ //
+ if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
+ //
+ // Only byte access is supported for this register
+ //
+ if (Width != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
+
+ //
+ // Check CPU mode
+ //
+ if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
+ *(UINT8 *)Buffer = 32;
+ } else {
+ *(UINT8 *)Buffer = 64;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Check for special EFI_SMM_SAVE_STATE_REGISTER_IO
+ //
+ if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Convert Register to a register lookup table index. Let
+ // PiSmmCpuDxeSmm implement other special registers (currently
+ // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
+ //
+ RegisterIndex = GetRegisterIndex (Register);
+ if (RegisterIndex == 0) {
+ return (Register < EFI_SMM_SAVE_STATE_REGISTER_IO ?
+ EFI_NOT_FOUND :
+ EFI_UNSUPPORTED);
+ }
+
+ return ReadSaveStateRegisterByIndex (CpuIndex, RegisterIndex, Width, Buffer);
+}
+
+/**
+ Writes an SMM Save State register on the target processor. If this function
+ returns EFI_UNSUPPORTED, then the caller is responsible for writing the
+ SMM Save Sate register.
+
+ @param[in] CpuIndex The index of the CPU to write the SMM Save State. The
+ value must be between 0 and the NumberOfCpus field in
+ the System Management System Table (SMST).
+ @param[in] Register The SMM Save State register to write.
+ @param[in] Width The number of bytes to write to the CPU save state.
+ @param[in] Buffer Upon entry, this holds the new CPU register value.
+
+ @retval EFI_SUCCESS The register was written to Save State.
+ @retval EFI_INVALID_PARAMTER Buffer is NULL.
+ @retval EFI_UNSUPPORTED This function does not support writing
+ Register.
+**/
+EFI_STATUS
+EFIAPI
+SmmCpuFeaturesWriteSaveStateRegister (
+ IN UINTN CpuIndex,
+ IN EFI_SMM_SAVE_STATE_REGISTER Register,
+ IN UINTN Width,
+ IN CONST VOID *Buffer
+ )
+{
+ UINTN RegisterIndex;
+ QEMU_SMRAM_SAVE_STATE_MAP *CpuSaveState;
+
+ //
+ // Writes to EFI_SMM_SAVE_STATE_REGISTER_LMA are ignored
+ //
+ if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Writes to EFI_SMM_SAVE_STATE_REGISTER_IO are not supported
+ //
+ if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Convert Register to a register lookup table index. Let
+ // PiSmmCpuDxeSmm implement other special registers (currently
+ // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
+ //
+ RegisterIndex = GetRegisterIndex (Register);
+ if (RegisterIndex == 0) {
+ return (Register < EFI_SMM_SAVE_STATE_REGISTER_IO ?
+ EFI_NOT_FOUND :
+ EFI_UNSUPPORTED);
+ }
+
+ CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
+
+ //
+ // Do not write non-writable SaveState, because it will cause exception.
+ //
+ if (!mSmmCpuWidthOffset[RegisterIndex].Writeable) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Check CPU mode
+ //
+ if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
+ //
+ // If 32-bit mode width is zero, then the specified register can not be
+ // accessed
+ //
+ if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If Width is bigger than the 32-bit mode width, then the specified
+ // register can not be accessed
+ //
+ if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Write SMM State register
+ //
+ ASSERT (CpuSaveState != NULL);
+ CopyMem (
+ (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32,
+ Buffer,
+ Width
+ );
+ } else {
+ //
+ // If 64-bit mode width is zero, then the specified register can not be
+ // accessed
+ //
+ if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If Width is bigger than the 64-bit mode width, then the specified
+ // register can not be accessed
+ //
+ if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Write lower 32-bits of SMM State register
+ //
+ CopyMem (
+ (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo,
+ Buffer,
+ MIN (4, Width)
+ );
+ if (Width >= 4) {
+ //
+ // Write upper 32-bits of SMM State register
+ //
+ CopyMem (
+ (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi,
+ (UINT8 *)Buffer + 4,
+ Width - 4
+ );
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is hook point called after the gEfiSmmReadyToLockProtocolGuid
+ notification is completely processed.
+**/
+VOID
+EFIAPI
+SmmCpuFeaturesCompleteSmmReadyToLock (
+ VOID
+ )
+{
+}
+
+/**
+ This API provides a method for a CPU to allocate a specific region for
+ storing page tables.
+
+ This API can be called more once to allocate memory for page tables.
+
+ Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns
+ a pointer to the allocated buffer. The buffer returned is aligned on a 4KB
+ boundary. If Pages is 0, then NULL is returned. If there is not enough
+ memory remaining to satisfy the request, then NULL is returned.
+
+ This function can also return NULL if there is no preference on where the
+ page tables are allocated in SMRAM.
+
+ @param Pages The number of 4 KB pages to allocate.
+
+ @return A pointer to the allocated buffer for page tables.
+ @retval NULL Fail to allocate a specific region for storing page tables,
+ Or there is no preference on where the page tables are
+ allocated in SMRAM.
+
+**/
+VOID *
+EFIAPI
+SmmCpuFeaturesAllocatePageTableMemory (
+ IN UINTN Pages
+ )
+{
+ return NULL;
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf b/roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf new file mode 100644 index 000000000..97a10afb6 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf @@ -0,0 +1,38 @@ +## @file
+# The CPU specific programming for PiSmmCpuDxeSmm module.
+#
+# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SmmCpuFeaturesLib
+ MODULE_UNI_FILE = SmmCpuFeaturesLib.uni
+ FILE_GUID = AC9991BE-D77A-464C-A8DE-A873DB8A4836
+ MODULE_TYPE = DXE_SMM_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = SmmCpuFeaturesLib
+ CONSTRUCTOR = SmmCpuFeaturesLibConstructor
+
+[Sources]
+ SmmCpuFeaturesLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemEncryptSevLib
+ PcdLib
+ SmmServicesTableLib
+ UefiBootServicesTableLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase
diff --git a/roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.c b/roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.c new file mode 100644 index 000000000..c88a95c6d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.c @@ -0,0 +1,115 @@ +/** @file
+SMM CPU Platform Hook library instance for QEMU.
+
+Copyright (c) 2020, Red Hat, Inc.
+Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Library/BaseLib.h> // AsmReadMsr64()
+#include <PiSmm.h>
+#include <Register/Intel/ArchitecturalMsr.h> // MSR_IA32_APIC_BASE_REGISTER
+
+#include <Library/SmmCpuPlatformHookLib.h>
+
+/**
+ Checks if platform produces a valid SMI.
+
+ This function checks if platform produces a valid SMI. This function is
+ called at SMM entry to detect if this is a spurious SMI. This function
+ must be implemented in an MP safe way because it is called by multiple CPU
+ threads.
+
+ @retval TRUE There is a valid SMI
+ @retval FALSE There is no valid SMI
+
+**/
+BOOLEAN
+EFIAPI
+PlatformValidSmi (
+ VOID
+ )
+{
+ return TRUE;
+}
+
+/**
+ Clears platform top level SMI status bit.
+
+ This function clears platform top level SMI status bit.
+
+ @retval TRUE The platform top level SMI status is cleared.
+ @retval FALSE The platform top level SMI status cannot be
+ cleared.
+
+**/
+BOOLEAN
+EFIAPI
+ClearTopLevelSmiStatus (
+ VOID
+ )
+{
+ return TRUE;
+}
+
+/**
+ Performs platform specific way of SMM BSP election.
+
+ This function performs platform specific way of SMM BSP election.
+
+ @param IsBsp Output parameter. TRUE: the CPU this function
+ executes on is elected to be the SMM BSP. FALSE:
+ the CPU this function executes on is to be SMM AP.
+
+ @retval EFI_SUCCESS The function executes successfully.
+ @retval EFI_NOT_READY The function does not determine whether this CPU
+ should be BSP or AP. This may occur if hardware
+ init sequence to enable the determination is yet to
+ be done, or the function chooses not to do BSP
+ election and will let SMM CPU driver to use its
+ default BSP election process.
+ @retval EFI_DEVICE_ERROR The function cannot determine whether this CPU
+ should be BSP or AP due to hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+PlatformSmmBspElection (
+ OUT BOOLEAN *IsBsp
+ )
+{
+ MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;
+
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
+ *IsBsp = (BOOLEAN)(ApicBaseMsr.Bits.BSP == 1);
+ return EFI_SUCCESS;
+}
+
+/**
+ Get platform page table attribute.
+
+ This function gets page table attribute of platform.
+
+ @param Address Input parameter. Obtain the page table entries
+ attribute on this address.
+ @param PageSize Output parameter. The size of the page.
+ @param NumOfPages Output parameter. Number of page.
+ @param PageAttribute Output parameter. Paging Attributes (WB, UC, etc).
+
+ @retval EFI_SUCCESS The platform page table attribute from the address
+ is determined.
+ @retval EFI_UNSUPPORTED The platform does not support getting page table
+ attribute for the address.
+
+**/
+EFI_STATUS
+EFIAPI
+GetPlatformPageTableAttribute (
+ IN UINT64 Address,
+ IN OUT SMM_PAGE_SIZE_TYPE *PageSize,
+ IN OUT UINTN *NumOfPages,
+ IN OUT UINTN *PageAttribute
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.inf b/roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.inf new file mode 100644 index 000000000..413c56fce --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.inf @@ -0,0 +1,32 @@ +## @file
+# SMM CPU Platform Hook library instance for QEMU.
+#
+# Copyright (c) 2020, Red Hat, Inc.
+# Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = SmmCpuPlatformHookLibQemu
+ FILE_GUID = 154D6D26-54B8-45BC-BA3A-CBAA20C02A6A
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = SmmCpuPlatformHookLib
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ SmmCpuPlatformHookLibQemu.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
diff --git a/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.c b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.c new file mode 100644 index 000000000..52455dd35 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.c @@ -0,0 +1,21 @@ +/** @file
+ NULL Tcg2PhysicalPresenceLib library instance
+
+ Copyright (c) 2018, Red Hat, Inc.
+ Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/Tcg2PhysicalPresenceLib.h>
+
+VOID
+EFIAPI
+Tcg2PhysicalPresenceLibProcessRequest (
+ IN TPM2B_AUTH *PlatformAuth OPTIONAL
+ )
+{
+ //
+ // do nothing
+ //
+}
diff --git a/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.inf b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.inf new file mode 100644 index 000000000..3057308d5 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.inf @@ -0,0 +1,28 @@ +## @file
+# NULL Tcg2PhysicalPresenceLib library instance
+#
+# Under SecurityPkg, the corresponding library instance will check and
+# execute TPM 2.0 request from OS or BIOS; the request may ask for user
+# confirmation before execution. This Null instance implements a no-op
+# Tcg2PhysicalPresenceLibProcessRequest(), without user interaction.
+#
+# Copyright (C) 2018, Red Hat, Inc.
+# Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeTcg2PhysicalPresenceLibNull
+ FILE_GUID = 2A6BA243-DC22-42D8-9C3D-AE3728DC7AFA
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = Tcg2PhysicalPresenceLib|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+[Sources]
+ DxeTcg2PhysicalPresenceLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ SecurityPkg/SecurityPkg.dec
diff --git a/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.c b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.c new file mode 100644 index 000000000..00d76ba2c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.c @@ -0,0 +1,917 @@ +/** @file
+ Execute pending TPM2 requests from OS or BIOS.
+
+ Caution: This module requires additional review when modified.
+ This driver will have external input - variable.
+ This external input must be validated carefully to avoid security issue.
+
+ Tcg2ExecutePendingTpmRequest() will receive untrusted input and do validation.
+
+Copyright (C) 2018, Red Hat, Inc.
+Copyright (c) 2018, IBM Corporation. All rights reserved.<BR>
+Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <Guid/Tcg2PhysicalPresenceData.h>
+#include <IndustryStandard/QemuTpm.h>
+#include <Protocol/Tcg2Protocol.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HiiLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/Tpm2CommandLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Library/Tcg2PhysicalPresenceLib.h>
+
+#define CONFIRM_BUFFER_SIZE 4096
+
+EFI_HII_HANDLE mTcg2PpStringPackHandle;
+
+#define TPM_PPI_FLAGS (QEMU_TPM_PPI_FUNC_ALLOWED_USR_REQ)
+
+STATIC volatile QEMU_TPM_PPI *mPpi;
+
+
+/**
+ Reads QEMU PPI config from fw_cfg.
+
+ @param[out] The Config structure to read to.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_PROTOCOL_ERROR Invalid fw_cfg entry size.
+**/
+STATIC
+EFI_STATUS
+QemuTpmReadConfig (
+ OUT QEMU_FWCFG_TPM_CONFIG *Config
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+
+ Status = QemuFwCfgFindFile ("etc/tpm/config", &FwCfgItem, &FwCfgSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (FwCfgSize != sizeof (*Config)) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (sizeof (*Config), Config);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Initializes QEMU PPI memory region.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_PROTOCOL_ERROR PPI address is invalid.
+**/
+STATIC
+EFI_STATUS
+QemuTpmInitPPI (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ QEMU_FWCFG_TPM_CONFIG Config;
+ EFI_PHYSICAL_ADDRESS PpiAddress64;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
+ UINTN Idx;
+
+ if (mPpi != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = QemuTpmReadConfig (&Config);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ mPpi = (QEMU_TPM_PPI *)(UINTN)Config.PpiAddress;
+ if (mPpi == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ DEBUG ((DEBUG_INFO, "[TPM2PP] mPpi=%p version=%d\n", mPpi, Config.TpmVersion));
+
+ PpiAddress64 = (UINTN)mPpi;
+ if ((PpiAddress64 & ~(UINT64)EFI_PAGE_MASK) !=
+ ((PpiAddress64 + sizeof *mPpi - 1) & ~(UINT64)EFI_PAGE_MASK)) {
+ DEBUG ((DEBUG_ERROR, "[TPM2PP] mPpi crosses a page boundary\n"));
+ goto InvalidPpiAddress;
+ }
+
+ Status = gDS->GetMemorySpaceDescriptor (PpiAddress64, &Descriptor);
+ if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
+ ASSERT_EFI_ERROR (Status);
+ goto InvalidPpiAddress;
+ }
+ if (!EFI_ERROR (Status) &&
+ (Descriptor.GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo &&
+ Descriptor.GcdMemoryType != EfiGcdMemoryTypeNonExistent)) {
+ DEBUG ((DEBUG_ERROR, "[TPM2PP] mPpi has an invalid memory type\n"));
+ goto InvalidPpiAddress;
+ }
+
+ for (Idx = 0; Idx < ARRAY_SIZE (mPpi->Func); Idx++) {
+ mPpi->Func[Idx] = 0;
+ }
+ if (Config.TpmVersion == QEMU_TPM_VERSION_2) {
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_NO_ACTION] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_CLEAR] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_2] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_3] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_SET_PCR_BANKS] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_CHANGE_EPS] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_LOG_ALL_DIGESTS] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_ENABLE_BLOCK_SID] = TPM_PPI_FLAGS;
+ mPpi->Func[TCG2_PHYSICAL_PRESENCE_DISABLE_BLOCK_SID] = TPM_PPI_FLAGS;
+ }
+
+ if (mPpi->In == 0) {
+ mPpi->In = 1;
+ mPpi->Request = TCG2_PHYSICAL_PRESENCE_NO_ACTION;
+ mPpi->LastRequest = TCG2_PHYSICAL_PRESENCE_NO_ACTION;
+ mPpi->NextStep = TCG2_PHYSICAL_PRESENCE_NO_ACTION;
+ }
+
+ return EFI_SUCCESS;
+
+InvalidPpiAddress:
+ mPpi = NULL;
+ return EFI_PROTOCOL_ERROR;
+}
+
+
+/**
+ Get string by string id from HII Interface.
+
+ @param[in] Id String ID.
+
+ @retval CHAR16 * String from ID.
+ @retval NULL If error occurs.
+
+**/
+STATIC
+CHAR16 *
+Tcg2PhysicalPresenceGetStringById (
+ IN EFI_STRING_ID Id
+ )
+{
+ return HiiGetString (mTcg2PpStringPackHandle, Id, NULL);
+}
+
+
+/**
+ Send ClearControl and Clear command to TPM.
+
+ @param[in] PlatformAuth platform auth value. NULL means no platform auth change.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_TIMEOUT The register can't run into the expected status in time.
+ @retval EFI_BUFFER_TOO_SMALL Response data buffer is too small.
+ @retval EFI_DEVICE_ERROR Unexpected device behavior.
+
+**/
+EFI_STATUS
+EFIAPI
+Tpm2CommandClear (
+ IN TPM2B_AUTH *PlatformAuth OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ TPMS_AUTH_COMMAND *AuthSession;
+ TPMS_AUTH_COMMAND LocalAuthSession;
+
+ if (PlatformAuth == NULL) {
+ AuthSession = NULL;
+ } else {
+ AuthSession = &LocalAuthSession;
+ ZeroMem (&LocalAuthSession, sizeof (LocalAuthSession));
+ LocalAuthSession.sessionHandle = TPM_RS_PW;
+ LocalAuthSession.hmac.size = PlatformAuth->size;
+ CopyMem (LocalAuthSession.hmac.buffer, PlatformAuth->buffer, PlatformAuth->size);
+ }
+
+ DEBUG ((DEBUG_INFO, "Tpm2ClearControl ... \n"));
+ Status = Tpm2ClearControl (TPM_RH_PLATFORM, AuthSession, NO);
+ DEBUG ((DEBUG_INFO, "Tpm2ClearControl - %r\n", Status));
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ DEBUG ((DEBUG_INFO, "Tpm2Clear ... \n"));
+ Status = Tpm2Clear (TPM_RH_PLATFORM, AuthSession);
+ DEBUG ((DEBUG_INFO, "Tpm2Clear - %r\n", Status));
+
+Done:
+ ZeroMem (&LocalAuthSession.hmac, sizeof (LocalAuthSession.hmac));
+ return Status;
+}
+
+
+/**
+ Change EPS.
+
+ @param[in] PlatformAuth platform auth value. NULL means no platform auth change.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+**/
+STATIC
+EFI_STATUS
+Tpm2CommandChangeEps (
+ IN TPM2B_AUTH *PlatformAuth OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ TPMS_AUTH_COMMAND *AuthSession;
+ TPMS_AUTH_COMMAND LocalAuthSession;
+
+ if (PlatformAuth == NULL) {
+ AuthSession = NULL;
+ } else {
+ AuthSession = &LocalAuthSession;
+ ZeroMem (&LocalAuthSession, sizeof (LocalAuthSession));
+ LocalAuthSession.sessionHandle = TPM_RS_PW;
+ LocalAuthSession.hmac.size = PlatformAuth->size;
+ CopyMem (LocalAuthSession.hmac.buffer, PlatformAuth->buffer, PlatformAuth->size);
+ }
+
+ Status = Tpm2ChangeEPS (TPM_RH_PLATFORM, AuthSession);
+ DEBUG ((DEBUG_INFO, "Tpm2ChangeEPS - %r\n", Status));
+
+ ZeroMem (&LocalAuthSession.hmac, sizeof(LocalAuthSession.hmac));
+ return Status;
+}
+
+
+/**
+ Execute physical presence operation requested by the OS.
+
+ @param[in] PlatformAuth platform auth value. NULL means no platform auth change.
+ @param[in] CommandCode Physical presence operation value.
+ @param[in] CommandParameter Physical presence operation parameter.
+
+ @retval TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE Unknown physical presence operation.
+ @retval TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE Error occurred during sending command to TPM or
+ receiving response from TPM.
+ @retval Others Return code from the TPM device after command execution.
+**/
+STATIC
+UINT32
+Tcg2ExecutePhysicalPresence (
+ IN TPM2B_AUTH *PlatformAuth, OPTIONAL
+ IN UINT32 CommandCode,
+ IN UINT32 CommandParameter
+ )
+{
+ EFI_STATUS Status;
+ EFI_TCG2_EVENT_ALGORITHM_BITMAP TpmHashAlgorithmBitmap;
+ UINT32 ActivePcrBanks;
+
+ switch (CommandCode) {
+ case TCG2_PHYSICAL_PRESENCE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_2:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_3:
+ Status = Tpm2CommandClear (PlatformAuth);
+ if (EFI_ERROR (Status)) {
+ return TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE;
+ } else {
+ return TCG_PP_OPERATION_RESPONSE_SUCCESS;
+ }
+
+ case TCG2_PHYSICAL_PRESENCE_SET_PCR_BANKS:
+ Status = Tpm2GetCapabilitySupportedAndActivePcrs (&TpmHashAlgorithmBitmap, &ActivePcrBanks);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // PP spec requirements:
+ // Firmware should check that all requested (set) hashing algorithms are supported with respective PCR banks.
+ // Firmware has to ensure that at least one PCR banks is active.
+ // If not, an error is returned and no action is taken.
+ //
+ if (CommandParameter == 0 || (CommandParameter & (~TpmHashAlgorithmBitmap)) != 0) {
+ DEBUG((DEBUG_ERROR, "PCR banks %x to allocate are not supported by TPM. Skip operation\n", CommandParameter));
+ return TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE;
+ }
+
+ Status = Tpm2PcrAllocateBanks (PlatformAuth, TpmHashAlgorithmBitmap, CommandParameter);
+ if (EFI_ERROR (Status)) {
+ return TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE;
+ } else {
+ return TCG_PP_OPERATION_RESPONSE_SUCCESS;
+ }
+
+ case TCG2_PHYSICAL_PRESENCE_CHANGE_EPS:
+ Status = Tpm2CommandChangeEps (PlatformAuth);
+ if (EFI_ERROR (Status)) {
+ return TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE;
+ } else {
+ return TCG_PP_OPERATION_RESPONSE_SUCCESS;
+ }
+
+ case TCG2_PHYSICAL_PRESENCE_LOG_ALL_DIGESTS:
+ Status = Tpm2GetCapabilitySupportedAndActivePcrs (&TpmHashAlgorithmBitmap, &ActivePcrBanks);
+ ASSERT_EFI_ERROR (Status);
+ Status = Tpm2PcrAllocateBanks (PlatformAuth, TpmHashAlgorithmBitmap, TpmHashAlgorithmBitmap);
+ if (EFI_ERROR (Status)) {
+ return TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE;
+ } else {
+ return TCG_PP_OPERATION_RESPONSE_SUCCESS;
+ }
+
+ default:
+ if (CommandCode <= TCG2_PHYSICAL_PRESENCE_NO_ACTION_MAX) {
+ return TCG_PP_OPERATION_RESPONSE_SUCCESS;
+ } else {
+ return TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE;
+ }
+ }
+}
+
+
+/**
+ Read the specified key for user confirmation.
+
+ @param[in] CautionKey If true, F12 is used as confirm key;
+ If false, F10 is used as confirm key.
+
+ @retval TRUE User confirmed the changes by input.
+ @retval FALSE User discarded the changes.
+**/
+STATIC
+BOOLEAN
+Tcg2ReadUserKey (
+ IN BOOLEAN CautionKey
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ UINT16 InputKey;
+
+ InputKey = 0;
+ do {
+ Status = gBS->CheckEvent (gST->ConIn->WaitForKey);
+ if (!EFI_ERROR (Status)) {
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+ if (Key.ScanCode == SCAN_ESC) {
+ InputKey = Key.ScanCode;
+ }
+ if ((Key.ScanCode == SCAN_F10) && !CautionKey) {
+ InputKey = Key.ScanCode;
+ }
+ if ((Key.ScanCode == SCAN_F12) && CautionKey) {
+ InputKey = Key.ScanCode;
+ }
+ }
+ } while (InputKey == 0);
+
+ if (InputKey != SCAN_ESC) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Fill Buffer With BootHashAlg.
+
+ @param[in] Buffer Buffer to be filled.
+ @param[in] BufferSize Size of buffer.
+ @param[in] BootHashAlg BootHashAlg.
+
+**/
+STATIC
+VOID
+Tcg2FillBufferWithBootHashAlg (
+ IN UINT16 *Buffer,
+ IN UINTN BufferSize,
+ IN UINT32 BootHashAlg
+ )
+{
+ Buffer[0] = 0;
+ if ((BootHashAlg & EFI_TCG2_BOOT_HASH_ALG_SHA1) != 0) {
+ if (Buffer[0] != 0) {
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L", ", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L"SHA1", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ if ((BootHashAlg & EFI_TCG2_BOOT_HASH_ALG_SHA256) != 0) {
+ if (Buffer[0] != 0) {
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L", ", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L"SHA256", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ if ((BootHashAlg & EFI_TCG2_BOOT_HASH_ALG_SHA384) != 0) {
+ if (Buffer[0] != 0) {
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L", ", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L"SHA384", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ if ((BootHashAlg & EFI_TCG2_BOOT_HASH_ALG_SHA512) != 0) {
+ if (Buffer[0] != 0) {
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L", ", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L"SHA512", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ if ((BootHashAlg & EFI_TCG2_BOOT_HASH_ALG_SM3_256) != 0) {
+ if (Buffer[0] != 0) {
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L", ", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+ StrnCatS (Buffer, BufferSize / sizeof (CHAR16), L"SM3_256", (BufferSize / sizeof (CHAR16)) - StrLen (Buffer) - 1);
+ }
+}
+
+
+/**
+ Display the confirm text and get user confirmation.
+
+ @param[in] TpmPpCommand The requested TPM physical presence command.
+ @param[in] TpmPpCommandParameter The requested TPM physical presence command parameter.
+
+ @retval TRUE The user has confirmed the changes.
+ @retval FALSE The user doesn't confirm the changes.
+**/
+STATIC
+BOOLEAN
+Tcg2UserConfirm (
+ IN UINT32 TpmPpCommand,
+ IN UINT32 TpmPpCommandParameter
+ )
+{
+ CHAR16 *ConfirmText;
+ CHAR16 *TmpStr1;
+ CHAR16 *TmpStr2;
+ UINTN BufSize;
+ BOOLEAN CautionKey;
+ BOOLEAN NoPpiInfo;
+ UINT16 Index;
+ CHAR16 DstStr[81];
+ CHAR16 TempBuffer[1024];
+ CHAR16 TempBuffer2[1024];
+ EFI_TCG2_PROTOCOL *Tcg2Protocol;
+ EFI_TCG2_BOOT_SERVICE_CAPABILITY ProtocolCapability;
+ UINT32 CurrentPCRBanks;
+ EFI_STATUS Status;
+
+ TmpStr2 = NULL;
+ CautionKey = FALSE;
+ NoPpiInfo = FALSE;
+ BufSize = CONFIRM_BUFFER_SIZE;
+ ConfirmText = AllocateZeroPool (BufSize);
+ ASSERT (ConfirmText != NULL);
+
+ mTcg2PpStringPackHandle = HiiAddPackages (&gEfiTcg2PhysicalPresenceGuid, gImageHandle, Tcg2PhysicalPresenceLibQemuStrings, NULL);
+ ASSERT (mTcg2PpStringPackHandle != NULL);
+
+ switch (TpmPpCommand) {
+
+ case TCG2_PHYSICAL_PRESENCE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_2:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_3:
+ CautionKey = TRUE;
+ TmpStr2 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_CLEAR));
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_HEAD_STR));
+ UnicodeSPrint (ConfirmText, BufSize, TmpStr1, TmpStr2);
+ FreePool (TmpStr1);
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_WARNING_CLEAR));
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), L" \n\n", (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ break;
+
+ case TCG2_PHYSICAL_PRESENCE_SET_PCR_BANKS:
+ Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **) &Tcg2Protocol);
+ ASSERT_EFI_ERROR (Status);
+
+ ProtocolCapability.Size = sizeof(ProtocolCapability);
+ Status = Tcg2Protocol->GetCapability (
+ Tcg2Protocol,
+ &ProtocolCapability
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = Tcg2Protocol->GetActivePcrBanks (
+ Tcg2Protocol,
+ &CurrentPCRBanks
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ CautionKey = TRUE;
+ TmpStr2 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_SET_PCR_BANKS));
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_HEAD_STR));
+ UnicodeSPrint (ConfirmText, BufSize, TmpStr1, TmpStr2);
+ FreePool (TmpStr1);
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_WARNING_SET_PCR_BANKS_1));
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_WARNING_SET_PCR_BANKS_2));
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ Tcg2FillBufferWithBootHashAlg (TempBuffer, sizeof(TempBuffer), TpmPpCommandParameter);
+ Tcg2FillBufferWithBootHashAlg (TempBuffer2, sizeof(TempBuffer2), CurrentPCRBanks);
+
+ TmpStr1 = AllocateZeroPool (BufSize);
+ ASSERT (TmpStr1 != NULL);
+ UnicodeSPrint (TmpStr1, BufSize, L"Current PCRBanks is 0x%x. (%s)\nNew PCRBanks is 0x%x. (%s)\n", CurrentPCRBanks, TempBuffer2, TpmPpCommandParameter, TempBuffer);
+
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), L" \n", (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ break;
+
+ case TCG2_PHYSICAL_PRESENCE_CHANGE_EPS:
+ CautionKey = TRUE;
+ TmpStr2 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_CHANGE_EPS));
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_HEAD_STR));
+ UnicodeSPrint (ConfirmText, BufSize, TmpStr1, TmpStr2);
+ FreePool (TmpStr1);
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_WARNING_CHANGE_EPS_1));
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_WARNING_CHANGE_EPS_2));
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ break;
+
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_BLOCK_SID:
+ TmpStr2 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_ENABLE_BLOCK_SID));
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_HEAD_STR));
+ UnicodeSPrint (ConfirmText, BufSize, TmpStr1, TmpStr2);
+ FreePool (TmpStr1);
+ break;
+
+ case TCG2_PHYSICAL_PRESENCE_DISABLE_BLOCK_SID:
+ TmpStr2 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_DISABLE_BLOCK_SID));
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_HEAD_STR));
+ UnicodeSPrint (ConfirmText, BufSize, TmpStr1, TmpStr2);
+ FreePool (TmpStr1);
+ break;
+
+ default:
+ ;
+ }
+
+ if (TmpStr2 == NULL) {
+ FreePool (ConfirmText);
+ return FALSE;
+ }
+
+ if (TpmPpCommand < TCG2_PHYSICAL_PRESENCE_STORAGE_MANAGEMENT_BEGIN) {
+ if (CautionKey) {
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_CAUTION_KEY));
+ } else {
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_ACCEPT_KEY));
+ }
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ if (NoPpiInfo) {
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_NO_PPI_INFO));
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+ }
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TPM_REJECT_KEY));
+ } else {
+ if (CautionKey) {
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_CAUTION_KEY));
+ } else {
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_ACCEPT_KEY));
+ }
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+
+ if (NoPpiInfo) {
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_NO_PPI_INFO));
+ StrnCatS (ConfirmText, BufSize / sizeof (CHAR16), TmpStr1, (BufSize / sizeof (CHAR16)) - StrLen (ConfirmText) - 1);
+ FreePool (TmpStr1);
+ }
+
+ TmpStr1 = Tcg2PhysicalPresenceGetStringById (STRING_TOKEN (TCG_STORAGE_REJECT_KEY));
+ }
+ BufSize -= StrSize (ConfirmText);
+ UnicodeSPrint (ConfirmText + StrLen (ConfirmText), BufSize, TmpStr1, TmpStr2);
+
+ DstStr[80] = L'\0';
+ for (Index = 0; Index < StrLen (ConfirmText); Index += 80) {
+ StrnCpyS (DstStr, sizeof (DstStr) / sizeof (CHAR16), ConfirmText + Index, sizeof (DstStr) / sizeof (CHAR16) - 1);
+ Print (DstStr);
+ }
+
+ FreePool (TmpStr1);
+ FreePool (TmpStr2);
+ FreePool (ConfirmText);
+ HiiRemovePackages (mTcg2PpStringPackHandle);
+
+ if (Tcg2ReadUserKey (CautionKey)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Check if there is a valid physical presence command request. Also updates parameter value
+ to whether the requested physical presence command already confirmed by user
+
+ @param[out] RequestConfirmed If the physical presence operation command required user confirm from UI.
+ True, it indicates the command doesn't require user confirm, or already confirmed
+ in last boot cycle by user.
+ False, it indicates the command need user confirm from UI.
+
+ @retval TRUE Physical Presence operation command is valid.
+ @retval FALSE Physical Presence operation command is invalid.
+
+**/
+STATIC
+BOOLEAN
+Tcg2HaveValidTpmRequest (
+ OUT BOOLEAN *RequestConfirmed
+ )
+{
+ EFI_TCG2_PROTOCOL *Tcg2Protocol;
+ EFI_STATUS Status;
+
+ *RequestConfirmed = FALSE;
+
+ if (mPpi->Request <= TCG2_PHYSICAL_PRESENCE_NO_ACTION_MAX) {
+ //
+ // Need TCG2 protocol.
+ //
+ Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **) &Tcg2Protocol);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ }
+
+ switch (mPpi->Request) {
+ case TCG2_PHYSICAL_PRESENCE_NO_ACTION:
+ case TCG2_PHYSICAL_PRESENCE_LOG_ALL_DIGESTS:
+ *RequestConfirmed = TRUE;
+ return TRUE;
+
+ case TCG2_PHYSICAL_PRESENCE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_2:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_3:
+ case TCG2_PHYSICAL_PRESENCE_SET_PCR_BANKS:
+ case TCG2_PHYSICAL_PRESENCE_CHANGE_EPS:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_BLOCK_SID:
+ case TCG2_PHYSICAL_PRESENCE_DISABLE_BLOCK_SID:
+ break;
+
+ default:
+ //
+ // Wrong Physical Presence command
+ //
+ return FALSE;
+ }
+
+ //
+ // Physical Presence command is correct
+ //
+ return TRUE;
+}
+
+
+/**
+ Check and execute the requested physical presence command.
+
+ @param[in] PlatformAuth platform auth value. NULL means no platform auth change.
+**/
+STATIC
+VOID
+Tcg2ExecutePendingTpmRequest (
+ IN TPM2B_AUTH *PlatformAuth OPTIONAL
+ )
+{
+ BOOLEAN RequestConfirmed;
+
+ if (mPpi->Request == TCG2_PHYSICAL_PRESENCE_NO_ACTION) {
+ //
+ // No operation request
+ //
+ return;
+ }
+
+ if (!Tcg2HaveValidTpmRequest (&RequestConfirmed)) {
+ //
+ // Invalid operation request.
+ //
+ if (mPpi->Request <= TCG2_PHYSICAL_PRESENCE_NO_ACTION_MAX) {
+ mPpi->Response = TCG_PP_OPERATION_RESPONSE_SUCCESS;
+ } else {
+ mPpi->Response = TCG_PP_OPERATION_RESPONSE_BIOS_FAILURE;
+ }
+ mPpi->LastRequest = mPpi->Request;
+ mPpi->Request = TCG2_PHYSICAL_PRESENCE_NO_ACTION;
+ mPpi->RequestParameter = 0;
+ return;
+ }
+
+ if (!RequestConfirmed) {
+ //
+ // Print confirm text and wait for approval.
+ //
+ RequestConfirmed = Tcg2UserConfirm (mPpi->Request, mPpi->RequestParameter);
+ }
+
+ //
+ // Execute requested physical presence command
+ //
+ mPpi->Response = TCG_PP_OPERATION_RESPONSE_USER_ABORT;
+ if (RequestConfirmed) {
+ mPpi->Response = Tcg2ExecutePhysicalPresence (
+ PlatformAuth,
+ mPpi->Request,
+ mPpi->RequestParameter
+ );
+ }
+
+ //
+ // Clear request
+ //
+ mPpi->LastRequest = mPpi->Request;
+ mPpi->Request = TCG2_PHYSICAL_PRESENCE_NO_ACTION;
+ mPpi->RequestParameter = 0;
+
+ if (mPpi->Response == TCG_PP_OPERATION_RESPONSE_USER_ABORT) {
+ return;
+ }
+
+ //
+ // Reset system to make new TPM settings in effect
+ //
+ switch (mPpi->LastRequest) {
+ case TCG2_PHYSICAL_PRESENCE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_2:
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_CLEAR_3:
+ case TCG2_PHYSICAL_PRESENCE_SET_PCR_BANKS:
+ case TCG2_PHYSICAL_PRESENCE_CHANGE_EPS:
+ case TCG2_PHYSICAL_PRESENCE_LOG_ALL_DIGESTS:
+ break;
+
+ case TCG2_PHYSICAL_PRESENCE_ENABLE_BLOCK_SID:
+ case TCG2_PHYSICAL_PRESENCE_DISABLE_BLOCK_SID:
+ break;
+
+ default:
+ if (mPpi->Request != TCG2_PHYSICAL_PRESENCE_NO_ACTION) {
+ break;
+ }
+ return;
+ }
+
+ Print (L"Rebooting system to make TPM2 settings in effect\n");
+ gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
+ ASSERT (FALSE);
+}
+
+
+/**
+ Check and execute the pending TPM request.
+
+ The TPM request may come from OS or BIOS. This API will display request information and wait
+ for user confirmation if TPM request exists. The TPM request will be sent to TPM device after
+ the TPM request is confirmed, and one or more reset may be required to make TPM request to
+ take effect.
+
+ This API should be invoked after console in and console out are all ready as they are required
+ to display request information and get user input to confirm the request.
+
+ @param[in] PlatformAuth platform auth value. NULL means no platform auth change.
+**/
+VOID
+EFIAPI
+Tcg2PhysicalPresenceLibProcessRequest (
+ IN TPM2B_AUTH *PlatformAuth OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = QemuTpmInitPPI ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "[TPM2PP] no PPI\n"));
+ return ;
+ }
+
+ //
+ // Check S4 resume
+ //
+ if (GetBootModeHob () == BOOT_ON_S4_RESUME) {
+ DEBUG ((DEBUG_INFO, "S4 Resume, Skip TPM PP process!\n"));
+ return ;
+ }
+
+ DEBUG ((DEBUG_INFO, "[TPM2PP] PPRequest=%x (PPRequestParameter=%x)\n", mPpi->Request, mPpi->RequestParameter));
+ Tcg2ExecutePendingTpmRequest (PlatformAuth);
+}
+
+
+/**
+ The handler for TPM physical presence function:
+ Return TPM Operation Response to OS Environment.
+
+ @param[out] MostRecentRequest Most recent operation request.
+ @param[out] Response Response to the most recent operation request.
+
+ @return Return Code for Return TPM Operation Response to OS Environment.
+**/
+UINT32
+EFIAPI
+Tcg2PhysicalPresenceLibReturnOperationResponseToOsFunction (
+ OUT UINT32 *MostRecentRequest,
+ OUT UINT32 *Response
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "[TPM2PP] ReturnOperationResponseToOsFunction\n"));
+
+ Status = QemuTpmInitPPI ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "[TPM2PP] no PPI\n"));
+ *MostRecentRequest = 0;
+ *Response = 0;
+ return TCG_PP_RETURN_TPM_OPERATION_RESPONSE_FAILURE;
+ }
+
+ *MostRecentRequest = mPpi->LastRequest;
+ *Response = mPpi->Response;
+
+ return TCG_PP_RETURN_TPM_OPERATION_RESPONSE_SUCCESS;
+}
+
+
+/**
+ The handler for TPM physical presence function:
+ Submit TPM Operation Request to Pre-OS Environment and
+ Submit TPM Operation Request to Pre-OS Environment 2.
+
+ Caution: This function may receive untrusted input.
+
+ @param[in] OperationRequest TPM physical presence operation request.
+ @param[in] RequestParameter TPM physical presence operation request parameter.
+
+ @return Return Code for Submit TPM Operation Request to Pre-OS Environment and
+ Submit TPM Operation Request to Pre-OS Environment 2.
+**/
+UINT32
+EFIAPI
+Tcg2PhysicalPresenceLibSubmitRequestToPreOSFunction (
+ IN UINT32 OperationRequest,
+ IN UINT32 RequestParameter
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "[TPM2PP] SubmitRequestToPreOSFunction, Request = %x, %x\n", OperationRequest, RequestParameter));
+
+ Status = QemuTpmInitPPI ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "[TPM2PP] no PPI\n"));
+ return TCG_PP_SUBMIT_REQUEST_TO_PREOS_GENERAL_FAILURE;
+ }
+
+ mPpi->Request = OperationRequest;
+ mPpi->RequestParameter = RequestParameter;
+
+ return TCG_PP_SUBMIT_REQUEST_TO_PREOS_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.inf b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.inf new file mode 100644 index 000000000..85ce0e2b2 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.inf @@ -0,0 +1,71 @@ +## @file
+# Executes TPM 2.0 requests from OS or BIOS
+#
+# This library will check and execute TPM 2.0 request from OS or
+# BIOS. The request may ask for user confirmation before
+# execution. It is a clone of
+# "SecurityPkg/Library/DxeTcg2PhysicalPresenceLib" with:
+#
+# - removed all the functions that are unreachable from
+# Tcg2PhysicalPresenceLibProcessRequest() [called from platform
+# BDS], or SubmitRequestToPreOSFunction() and
+# ReturnOperationResponseToOsFunction() [called from Tcg2Dxe].
+#
+# - replaced everything that's related to the
+# TCG2_PHYSICAL_PRESENCE*_VARIABLE variables, with direct access to
+# the QEMU structures.
+#
+# Caution: This module requires additional review when modified.
+# This driver will have external input - variable.
+# This external input must be validated carefully to avoid security issue.
+#
+# Copyright (C) 2018, Red Hat, Inc.
+# Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Tcg2PhysicalPresenceLibQemu
+ FILE_GUID = 41D3E698-9EEC-41FF-9CBB-5FE79A0CF326
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = Tcg2PhysicalPresenceLib|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeTcg2PhysicalPresenceLib.c
+ PhysicalPresenceStrings.uni
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ SecurityPkg/SecurityPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DxeServicesTableLib
+ HiiLib
+ HobLib
+ MemoryAllocationLib
+ PrintLib
+ QemuFwCfgLib
+ Tpm2CommandLib
+ UefiBootServicesTableLib
+ UefiLib
+ UefiRuntimeServicesTableLib
+
+[Protocols]
+ gEfiTcg2ProtocolGuid ## SOMETIMES_CONSUMES
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## HII
+ gEfiTcg2PhysicalPresenceGuid
diff --git a/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/PhysicalPresenceStrings.uni b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/PhysicalPresenceStrings.uni new file mode 100644 index 000000000..ea9ff41a1 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/PhysicalPresenceStrings.uni @@ -0,0 +1,38 @@ +/** @file
+ String definitions for TPM 2.0 physical presence confirm text.
+
+Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#langdef en-US "English"
+
+#string TPM_HEAD_STR #language en-US "A configuration change was requested to %s this computer's TPM (Trusted Platform Module)\n\n"
+
+#string TPM_ACCEPT_KEY #language en-US "Press F10 "
+#string TPM_CAUTION_KEY #language en-US "Press F12 "
+#string TPM_REJECT_KEY #language en-US "to %s the TPM \nPress ESC to reject this change request and continue\n"
+
+#string TPM_CLEAR #language en-US "clear"
+#string TPM_SET_PCR_BANKS #language en-US "change the boot measurements to use PCR bank(s) of"
+#string TPM_CHANGE_EPS #language en-US "clear and change identity of"
+
+#string TPM_NO_PPI_INFO #language en-US "to approve future Operating System requests "
+
+#string TPM_WARNING_CLEAR #language en-US "WARNING: Clearing erases information stored on the TPM. You will lose all created keys and access to data encrypted by these keys. "
+#string TPM_WARNING_SET_PCR_BANKS_1 #language en-US "WARNING: Changing the PCR bank(s) of the boot measurements may prevent the Operating System from properly processing the measurements. Please check if your Operating System supports the new PCR bank(s).\n\n"
+#string TPM_WARNING_SET_PCR_BANKS_2 #language en-US "WARNING: Secrets in the TPM that are bound to the boot state of your machine may become unusable.\n\n"
+#string TPM_WARNING_CHANGE_EPS_1 #language en-US "WARNING: Clearing erases information stored on the TPM. You will lose all created keys and access to data encrypted with these keys.\n\n"
+#string TPM_WARNING_CHANGE_EPS_2 #language en-US "WARNING: Changing the identity of the TPM may require additional steps to establish trust into the new identity.\n\n"
+
+#string TCG_STORAGE_HEAD_STR #language en-US "A configuration change was requested to %s on subsequent boots\n\n"
+
+#string TCG_STORAGE_ACCEPT_KEY #language en-US "Press F10 "
+#string TCG_STORAGE_CAUTION_KEY #language en-US "Press F12 "
+#string TCG_STORAGE_REJECT_KEY #language en-US "to %s\nPress ESC to reject this change request and continue\n"
+
+#string TCG_STORAGE_NO_PPI_INFO #language en-US "to approve future Operating System requests "
+
+#string TCG_STORAGE_ENABLE_BLOCK_SID #language en-US "issue a Block SID authentication command"
+#string TCG_STORAGE_DISABLE_BLOCK_SID #language en-US "disable issuing a Block SID authentication command"
diff --git a/roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.c b/roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.c new file mode 100644 index 000000000..14b6a2a54 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.c @@ -0,0 +1,225 @@ +/** @file
+
+ A hook-in library for NetworkPkg/TlsAuthConfigDxe, in order to set volatile
+ variables related to TLS configuration, before TlsAuthConfigDxe or HttpDxe
+ (which is a UEFI_DRIVER) consume them.
+
+ Copyright (C) 2013, 2015, 2018, Red Hat, Inc.
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+#include <Uefi/UefiSpec.h>
+
+#include <Guid/HttpTlsCipherList.h>
+#include <Guid/TlsAuthentication.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+/**
+ Read the list of trusted CA certificates from the fw_cfg file
+ "etc/edk2/https/cacerts", and store it to
+ gEfiTlsCaCertificateGuid:EFI_TLS_CA_CERTIFICATE_VARIABLE.
+
+ The contents are validated (for well-formedness) by NetworkPkg/HttpDxe.
+**/
+STATIC
+VOID
+SetCaCerts (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM HttpsCaCertsItem;
+ UINTN HttpsCaCertsSize;
+ VOID *HttpsCaCerts;
+
+ Status = QemuFwCfgFindFile ("etc/edk2/https/cacerts", &HttpsCaCertsItem,
+ &HttpsCaCertsSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: not touching CA cert list\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return;
+ }
+
+ //
+ // Delete the current EFI_TLS_CA_CERTIFICATE_VARIABLE if it exists. This
+ // serves two purposes:
+ //
+ // (a) If the variable exists with EFI_VARIABLE_NON_VOLATILE attribute, we
+ // cannot make it volatile without deleting it first.
+ //
+ // (b) If we fail to recreate the variable later, deleting the current one is
+ // still justified if the fw_cfg file exists. Emptying the set of trusted
+ // CA certificates will fail HTTPS boot, which is better than trusting
+ // any certificate that's possibly missing from the fw_cfg file.
+ //
+ Status = gRT->SetVariable (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE, // VariableName
+ &gEfiTlsCaCertificateGuid, // VendorGuid
+ 0, // Attributes
+ 0, // DataSize
+ NULL // Data
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ //
+ // This is fatal.
+ //
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to delete %g:\"%s\"\n",
+ gEfiCallerBaseName, __FUNCTION__, &gEfiTlsCaCertificateGuid,
+ EFI_TLS_CA_CERTIFICATE_VARIABLE));
+ ASSERT_EFI_ERROR (Status);
+ CpuDeadLoop ();
+ }
+
+ if (HttpsCaCertsSize == 0) {
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: applied empty CA cert list\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return;
+ }
+
+ HttpsCaCerts = AllocatePool (HttpsCaCertsSize);
+ if (HttpsCaCerts == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to allocate HttpsCaCerts\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return;
+ }
+
+ QemuFwCfgSelectItem (HttpsCaCertsItem);
+ QemuFwCfgReadBytes (HttpsCaCertsSize, HttpsCaCerts);
+
+ Status = gRT->SetVariable (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE, // VariableName
+ &gEfiTlsCaCertificateGuid, // VendorGuid
+ EFI_VARIABLE_BOOTSERVICE_ACCESS, // Attributes
+ HttpsCaCertsSize, // DataSize
+ HttpsCaCerts // Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to set %g:\"%s\": %r\n",
+ gEfiCallerBaseName, __FUNCTION__, &gEfiTlsCaCertificateGuid,
+ EFI_TLS_CA_CERTIFICATE_VARIABLE, Status));
+ goto FreeHttpsCaCerts;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: stored CA cert list (%Lu byte(s))\n",
+ gEfiCallerBaseName, __FUNCTION__, (UINT64)HttpsCaCertsSize));
+
+FreeHttpsCaCerts:
+ FreePool (HttpsCaCerts);
+}
+
+/**
+ Read the list of trusted cipher suites from the fw_cfg file
+ "etc/edk2/https/ciphers", and store it to
+ gEdkiiHttpTlsCipherListGuid:EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE.
+
+ The contents are propagated by NetworkPkg/HttpDxe to NetworkPkg/TlsDxe; the
+ list is processed by the latter.
+**/
+STATIC
+VOID
+SetCipherSuites (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM HttpsCiphersItem;
+ UINTN HttpsCiphersSize;
+ VOID *HttpsCiphers;
+
+ Status = QemuFwCfgFindFile ("etc/edk2/https/ciphers", &HttpsCiphersItem,
+ &HttpsCiphersSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: not touching cipher suites\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return;
+ }
+ //
+ // From this point on, any failure is fatal. An ordered cipher preference
+ // list is available from QEMU, thus we cannot let the firmware attempt HTTPS
+ // boot with either pre-existent or non-existent preferences. An empty set of
+ // cipher suites does not fail HTTPS boot automatically; the default cipher
+ // suite preferences would take effect, and we must prevent that.
+ //
+ // Delete the current EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE if it exists. If
+ // the variable exists with EFI_VARIABLE_NON_VOLATILE attribute, we cannot
+ // make it volatile without deleting it first.
+ //
+ Status = gRT->SetVariable (
+ EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE, // VariableName
+ &gEdkiiHttpTlsCipherListGuid, // VendorGuid
+ 0, // Attributes
+ 0, // DataSize
+ NULL // Data
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to delete %g:\"%s\"\n",
+ gEfiCallerBaseName, __FUNCTION__, &gEdkiiHttpTlsCipherListGuid,
+ EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE));
+ goto Done;
+ }
+
+ if (HttpsCiphersSize == 0) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: list of cipher suites must not be empty\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+
+ HttpsCiphers = AllocatePool (HttpsCiphersSize);
+ if (HttpsCiphers == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to allocate HttpsCiphers\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ QemuFwCfgSelectItem (HttpsCiphersItem);
+ QemuFwCfgReadBytes (HttpsCiphersSize, HttpsCiphers);
+
+ Status = gRT->SetVariable (
+ EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE, // VariableName
+ &gEdkiiHttpTlsCipherListGuid, // VendorGuid
+ EFI_VARIABLE_BOOTSERVICE_ACCESS, // Attributes
+ HttpsCiphersSize, // DataSize
+ HttpsCiphers // Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:%a: failed to set %g:\"%s\"\n",
+ gEfiCallerBaseName, __FUNCTION__, &gEdkiiHttpTlsCipherListGuid,
+ EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE));
+ goto FreeHttpsCiphers;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: stored list of cipher suites (%Lu byte(s))\n",
+ gEfiCallerBaseName, __FUNCTION__, (UINT64)HttpsCiphersSize));
+
+FreeHttpsCiphers:
+ FreePool (HttpsCiphers);
+
+Done:
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ CpuDeadLoop ();
+ }
+}
+
+RETURN_STATUS
+EFIAPI
+TlsAuthConfigInit (
+ VOID
+ )
+{
+ SetCaCerts ();
+ SetCipherSuites ();
+
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.inf b/roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.inf new file mode 100644 index 000000000..a2ec42a3a --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.inf @@ -0,0 +1,50 @@ +## @file
+#
+# A hook-in library for NetworkPkg/TlsAuthConfigDxe, in order to set volatile
+# variables related to TLS configuration, before TlsAuthConfigDxe or HttpDxe
+# (which is a UEFI_DRIVER) consume them.
+#
+# Copyright (C) 2013, 2015, 2018, Red Hat, Inc.
+# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.26
+ BASE_NAME = TlsAuthConfigLib
+ FILE_GUID = 660AB627-4C5F-4D42-A3B6-BD021E9028BD
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TlsAuthConfigLib|DXE_DRIVER
+ CONSTRUCTOR = TlsAuthConfigInit
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64
+#
+
+[Sources]
+ TlsAuthConfigLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ NetworkPkg/NetworkPkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ MemoryAllocationLib
+ QemuFwCfgLib
+ UefiRuntimeServicesTableLib
+
+[Guids]
+ gEdkiiHttpTlsCipherListGuid ## PRODUCES ## Variable:L"HttpTlsCipherList"
+ gEfiTlsCaCertificateGuid ## PRODUCES ## Variable:L"TlsCaCertificate"
+
+[Depex]
+ gEfiVariableWriteArchProtocolGuid
diff --git a/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.c b/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.c new file mode 100644 index 000000000..2b6835626 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.c @@ -0,0 +1,237 @@ +/** @file
+ Plug an EFI_PCI_IO_PROTOCOL backend into PciCapLib, for config space access.
+
+ Copyright (C) 2018, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/MemoryAllocationLib.h>
+
+#include "UefiPciCapPciIoLib.h"
+
+
+/**
+ Transfer bytes between the config space of a given PCI device and a memory
+ buffer.
+
+ ProtoDevTransferConfig() performs as few config space accesses as possible
+ (without attempting 64-bit wide accesses).
+
+ @param[in] PciIo The EFI_PCI_IO_PROTOCOL representation of the
+ PCI device.
+
+ @param[in] TransferFunction The EFI_PCI_IO_PROTOCOL_CONFIG function that
+ implements the transfer. The direction of the
+ transfer is inherent to TransferFunction.
+ TransferFunction() is required to return an
+ unspecified error if any sub-transfer within
+ Size bytes from ConfigOffset exceeds the config
+ space limit of the PCI device.
+
+ @param[in] ConfigOffset The offset in the config space of the PCI device
+ at which the transfer should commence.
+
+ @param[in,out] Buffer The memory buffer where the transfer should
+ occur.
+
+ @param[in] Size The number of bytes to transfer.
+
+ @retval EFI_SUCCESS Size bytes have been transferred between config space
+ and Buffer.
+
+ @return Error codes propagated from TransferFunction(). Fewer
+ than Size bytes may have been transferred.
+**/
+STATIC
+EFI_STATUS
+ProtoDevTransferConfig (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN EFI_PCI_IO_PROTOCOL_CONFIG TransferFunction,
+ IN UINT16 ConfigOffset,
+ IN OUT UINT8 *Buffer,
+ IN UINT16 Size
+ )
+{
+ while (Size > 0) {
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;
+ UINT16 Count;
+ EFI_STATUS Status;
+ UINT16 Progress;
+
+ //
+ // Pick the largest access size that is allowed by the remaining transfer
+ // Size and by the alignment of ConfigOffset.
+ //
+ // When the largest access size is available, transfer as many bytes as
+ // possible in one iteration of the loop. Otherwise, transfer only one
+ // unit, to improve the alignment.
+ //
+ if (Size >= 4 && (ConfigOffset & 3) == 0) {
+ Width = EfiPciIoWidthUint32;
+ Count = Size >> Width;
+ } else if (Size >= 2 && (ConfigOffset & 1) == 0) {
+ Width = EfiPciIoWidthUint16;
+ Count = 1;
+ } else {
+ Width = EfiPciIoWidthUint8;
+ Count = 1;
+ }
+ Status = TransferFunction (PciIo, Width, ConfigOffset, Count, Buffer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Progress = Count << Width;
+ ConfigOffset += Progress;
+ Buffer += Progress;
+ Size -= Progress;
+ }
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Read the config space of a given PCI device (both normal and extended).
+
+ ProtoDevReadConfig() performs as few config space accesses as possible
+ (without attempting 64-bit wide accesses).
+
+ ProtoDevReadConfig() returns an unspecified error if accessing Size bytes
+ from SourceOffset exceeds the config space limit of the PCI device. Fewer
+ than Size bytes may have been read in this case.
+
+ @param[in] PciDevice Implementation-specific unique representation
+ of the PCI device in the PCI hierarchy.
+
+ @param[in] SourceOffset Source offset in the config space of the PCI
+ device to start reading from.
+
+ @param[out] DestinationBuffer Buffer to store the read data to.
+
+ @param[in] Size The number of bytes to transfer.
+
+ @retval RETURN_SUCCESS Size bytes have been transferred from config space to
+ DestinationBuffer.
+
+ @return Error codes propagated from
+ EFI_PCI_IO_PROTOCOL.Pci.Read(). Fewer than Size bytes
+ may have been read.
+**/
+STATIC
+RETURN_STATUS
+EFIAPI
+ProtoDevReadConfig (
+ IN PCI_CAP_DEV *PciDevice,
+ IN UINT16 SourceOffset,
+ OUT VOID *DestinationBuffer,
+ IN UINT16 Size
+ )
+{
+ PROTO_DEV *ProtoDev;
+
+ ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);
+ return ProtoDevTransferConfig (ProtoDev->PciIo, ProtoDev->PciIo->Pci.Read,
+ SourceOffset, DestinationBuffer, Size);
+}
+
+
+/**
+ Write the config space of a given PCI device (both normal and extended).
+
+ ProtoDevWriteConfig() performs as few config space accesses as possible
+ (without attempting 64-bit wide accesses).
+
+ ProtoDevWriteConfig() returns an unspecified error if accessing Size bytes at
+ DestinationOffset exceeds the config space limit of the PCI device. Fewer
+ than Size bytes may have been written in this case.
+
+ @param[in] PciDevice Implementation-specific unique representation
+ of the PCI device in the PCI hierarchy.
+
+ @param[in] DestinationOffset Destination offset in the config space of the
+ PCI device to start writing at.
+
+ @param[in] SourceBuffer Buffer to read the data to be stored from.
+
+ @param[in] Size The number of bytes to transfer.
+
+ @retval RETURN_SUCCESS Size bytes have been transferred from SourceBuffer to
+ config space.
+
+ @return Error codes propagated from
+ EFI_PCI_IO_PROTOCOL.Pci.Write(). Fewer than Size
+ bytes may have been written.
+**/
+STATIC
+RETURN_STATUS
+EFIAPI
+ProtoDevWriteConfig (
+ IN PCI_CAP_DEV *PciDevice,
+ IN UINT16 DestinationOffset,
+ IN VOID *SourceBuffer,
+ IN UINT16 Size
+ )
+{
+ PROTO_DEV *ProtoDev;
+
+ ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);
+ return ProtoDevTransferConfig (ProtoDev->PciIo, ProtoDev->PciIo->Pci.Write,
+ DestinationOffset, SourceBuffer, Size);
+}
+
+
+/**
+ Create a PCI_CAP_DEV object from an EFI_PCI_IO_PROTOCOL instance. The config
+ space accessors are based upon EFI_PCI_IO_PROTOCOL.Pci.Read() and
+ EFI_PCI_IO_PROTOCOL.Pci.Write().
+
+ @param[in] PciIo EFI_PCI_IO_PROTOCOL representation of the PCI device.
+
+ @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.
+ PciDevice can be passed to the PciCapLib APIs.
+
+ @retval EFI_SUCCESS PciDevice has been constructed and output.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+PciCapPciIoDeviceInit (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ OUT PCI_CAP_DEV **PciDevice
+ )
+{
+ PROTO_DEV *ProtoDev;
+
+ ProtoDev = AllocatePool (sizeof *ProtoDev);
+ if (ProtoDev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ProtoDev->Signature = PROTO_DEV_SIG;
+ ProtoDev->PciIo = PciIo;
+ ProtoDev->BaseDevice.ReadConfig = ProtoDevReadConfig;
+ ProtoDev->BaseDevice.WriteConfig = ProtoDevWriteConfig;
+
+ *PciDevice = &ProtoDev->BaseDevice;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Free the resources used by PciDevice.
+
+ @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by
+ PciCapPciIoDeviceInit().
+**/
+VOID
+EFIAPI
+PciCapPciIoDeviceUninit (
+ IN PCI_CAP_DEV *PciDevice
+ )
+{
+ PROTO_DEV *ProtoDev;
+
+ ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);
+ FreePool (ProtoDev);
+}
diff --git a/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.h b/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.h new file mode 100644 index 000000000..cfe29d1a2 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.h @@ -0,0 +1,38 @@ +/** @file
+ Plug an EFI_PCI_IO_PROTOCOL backend into PciCapLib, for config space access
+ -- internal macro and type definitions.
+
+ Copyright (C) 2018, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef __UEFI_PCI_CAP_PCI_IO_LIB_H__
+#define __UEFI_PCI_CAP_PCI_IO_LIB_H__
+
+#include <Library/DebugLib.h>
+
+#include <Library/PciCapPciIoLib.h>
+
+#define PROTO_DEV_SIG SIGNATURE_64 ('P', 'C', 'P', 'I', 'O', 'P', 'R', 'T')
+
+typedef struct {
+ //
+ // Signature identifying the derived class.
+ //
+ UINT64 Signature;
+ //
+ // Members added by the derived class, specific to the use of
+ // EFI_PCI_IO_PROTOCOL.
+ //
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ //
+ // Base class.
+ //
+ PCI_CAP_DEV BaseDevice;
+} PROTO_DEV;
+
+#define PROTO_DEV_FROM_PCI_CAP_DEV(PciDevice) \
+ CR (PciDevice, PROTO_DEV, BaseDevice, PROTO_DEV_SIG)
+
+#endif // __UEFI_PCI_CAP_PCI_IO_LIB_H__
diff --git a/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf b/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf new file mode 100644 index 000000000..138081c9d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf @@ -0,0 +1,30 @@ +## @file
+# Plug an EFI_PCI_IO_PROTOCOL backend into PciCapLib, for config space access.
+#
+# Copyright (C) 2018, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = UefiPciCapPciIoLib
+ FILE_GUID = 4102F4FE-DA10-4F0F-AC18-4982ED506154
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PciCapPciIoLib
+
+[Sources]
+ UefiPciCapPciIoLib.h
+ UefiPciCapPciIoLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ MemoryAllocationLib
+
+[Protocols]
+ gEfiPciIoProtocolGuid ## CONSUMES
diff --git a/roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.c b/roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.c new file mode 100644 index 000000000..e09324813 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.c @@ -0,0 +1,552 @@ +/** @file
+
+ Utility functions used by virtio device drivers.
+
+ Copyright (C) 2012-2016, Red Hat, Inc.
+ Portion of Copyright (C) 2013, ARM Ltd.
+ Copyright (C) 2017, AMD Inc, All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Library/VirtioLib.h>
+
+
+/**
+
+ Configure a virtio ring.
+
+ This function sets up internal storage (the guest-host communication area)
+ and lays out several "navigation" (ie. no-ownership) pointers to parts of
+ that storage.
+
+ Relevant sections from the virtio-0.9.5 spec:
+ - 1.1 Virtqueues,
+ - 2.3 Virtqueue Configuration.
+
+ @param[in] VirtIo The virtio device which will use the ring.
+
+ @param[in] The number of descriptors to allocate for the
+ virtio ring, as requested by the host.
+
+ @param[out] Ring The virtio ring to set up.
+
+ @return Status codes propagated from
+ VirtIo->AllocateSharedPages().
+
+ @retval EFI_SUCCESS Allocation and setup successful. Ring->Base
+ (and nothing else) is responsible for
+ deallocation.
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioRingInit (
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,
+ IN UINT16 QueueSize,
+ OUT VRING *Ring
+ )
+{
+ EFI_STATUS Status;
+ UINTN RingSize;
+ volatile UINT8 *RingPagesPtr;
+
+ RingSize = ALIGN_VALUE (
+ sizeof *Ring->Desc * QueueSize +
+ sizeof *Ring->Avail.Flags +
+ sizeof *Ring->Avail.Idx +
+ sizeof *Ring->Avail.Ring * QueueSize +
+ sizeof *Ring->Avail.UsedEvent,
+ EFI_PAGE_SIZE);
+
+ RingSize += ALIGN_VALUE (
+ sizeof *Ring->Used.Flags +
+ sizeof *Ring->Used.Idx +
+ sizeof *Ring->Used.UsedElem * QueueSize +
+ sizeof *Ring->Used.AvailEvent,
+ EFI_PAGE_SIZE);
+
+ //
+ // Allocate a shared ring buffer
+ //
+ Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);
+ Status = VirtIo->AllocateSharedPages (
+ VirtIo,
+ Ring->NumPages,
+ &Ring->Base
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ SetMem (Ring->Base, RingSize, 0x00);
+ RingPagesPtr = Ring->Base;
+
+ Ring->Desc = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Desc * QueueSize;
+
+ Ring->Avail.Flags = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Avail.Flags;
+
+ Ring->Avail.Idx = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Avail.Idx;
+
+ Ring->Avail.Ring = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;
+
+ Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Avail.UsedEvent;
+
+ RingPagesPtr = (volatile UINT8 *) Ring->Base +
+ ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base,
+ EFI_PAGE_SIZE);
+
+ Ring->Used.Flags = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Used.Flags;
+
+ Ring->Used.Idx = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Used.Idx;
+
+ Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;
+
+ Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr;
+ RingPagesPtr += sizeof *Ring->Used.AvailEvent;
+
+ Ring->QueueSize = QueueSize;
+ return EFI_SUCCESS;
+}
+
+
+/**
+
+ Tear down the internal resources of a configured virtio ring.
+
+ The caller is responsible to stop the host from using this ring before
+ invoking this function: the VSTAT_DRIVER_OK bit must be clear in
+ VhdrDeviceStatus.
+
+ @param[in] VirtIo The virtio device which was using the ring.
+
+ @param[out] Ring The virtio ring to clean up.
+
+**/
+VOID
+EFIAPI
+VirtioRingUninit (
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,
+ IN OUT VRING *Ring
+ )
+{
+ VirtIo->FreeSharedPages (VirtIo, Ring->NumPages, Ring->Base);
+ SetMem (Ring, sizeof *Ring, 0x00);
+}
+
+
+/**
+
+ Turn off interrupt notifications from the host, and prepare for appending
+ multiple descriptors to the virtio ring.
+
+ The calling driver must be in VSTAT_DRIVER_OK state.
+
+ @param[in,out] Ring The virtio ring we intend to append descriptors to.
+
+ @param[out] Indices The DESC_INDICES structure to initialize.
+
+**/
+VOID
+EFIAPI
+VirtioPrepare (
+ IN OUT VRING *Ring,
+ OUT DESC_INDICES *Indices
+ )
+{
+ //
+ // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.
+ // We're going to poll the answer, the host should not send an interrupt.
+ //
+ *Ring->Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
+
+ //
+ // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.
+ //
+ // Since we support only one in-flight descriptor chain, we can always build
+ // that chain starting at entry #0 of the descriptor table.
+ //
+ Indices->HeadDescIdx = 0;
+ Indices->NextDescIdx = Indices->HeadDescIdx;
+}
+
+/**
+
+ Append a contiguous buffer for transmission / reception via the virtio ring.
+
+ This function implements the following section from virtio-0.9.5:
+ - 2.4.1.1 Placing Buffers into the Descriptor Table
+
+ Free space is taken as granted, since the individual drivers support only
+ synchronous requests and host side status is processed in lock-step with
+ request submission. It is the calling driver's responsibility to verify the
+ ring size in advance.
+
+ The caller is responsible for initializing *Indices with VirtioPrepare()
+ first.
+
+ @param[in,out] Ring The virtio ring to append the buffer to,
+ as a descriptor.
+
+ @param[in] BufferDeviceAddress (Bus master device) start address of the
+ transmit / receive buffer.
+
+ @param[in] BufferSize Number of bytes to transmit or receive.
+
+ @param[in] Flags A bitmask of VRING_DESC_F_* flags. The
+ caller computes this mask dependent on
+ further buffers to append and transfer
+ direction. VRING_DESC_F_INDIRECT is
+ unsupported. The VRING_DESC.Next field is
+ always set, but the host only interprets
+ it dependent on VRING_DESC_F_NEXT.
+
+ @param[in,out] Indices Indices->HeadDescIdx is not accessed.
+ On input, Indices->NextDescIdx identifies
+ the next descriptor to carry the buffer.
+ On output, Indices->NextDescIdx is
+ incremented by one, modulo 2^16.
+
+**/
+VOID
+EFIAPI
+VirtioAppendDesc (
+ IN OUT VRING *Ring,
+ IN UINT64 BufferDeviceAddress,
+ IN UINT32 BufferSize,
+ IN UINT16 Flags,
+ IN OUT DESC_INDICES *Indices
+ )
+{
+ volatile VRING_DESC *Desc;
+
+ Desc = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize];
+ Desc->Addr = BufferDeviceAddress;
+ Desc->Len = BufferSize;
+ Desc->Flags = Flags;
+ Desc->Next = Indices->NextDescIdx % Ring->QueueSize;
+}
+
+
+/**
+
+ Notify the host about the descriptor chain just built, and wait until the
+ host processes it.
+
+ @param[in] VirtIo The target virtio device to notify.
+
+ @param[in] VirtQueueId Identifies the queue for the target device.
+
+ @param[in,out] Ring The virtio ring with descriptors to submit.
+
+ @param[in] Indices Indices->NextDescIdx is not accessed.
+ Indices->HeadDescIdx identifies the head descriptor
+ of the descriptor chain.
+
+ @param[out] UsedLen On success, the total number of bytes, consecutively
+ across the buffers linked by the descriptor chain,
+ that the host wrote. May be NULL if the caller
+ doesn't care, or can compute the same information
+ from device-specific request structures linked by the
+ descriptor chain.
+
+ @return Error code from VirtIo->SetQueueNotify() if it fails.
+
+ @retval EFI_SUCCESS Otherwise, the host processed all descriptors.
+
+**/
+EFI_STATUS
+EFIAPI
+VirtioFlush (
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,
+ IN UINT16 VirtQueueId,
+ IN OUT VRING *Ring,
+ IN DESC_INDICES *Indices,
+ OUT UINT32 *UsedLen OPTIONAL
+ )
+{
+ UINT16 NextAvailIdx;
+ UINT16 LastUsedIdx;
+ EFI_STATUS Status;
+ UINTN PollPeriodUsecs;
+
+ //
+ // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
+ //
+ // It is not exactly clear from the wording of the virtio-0.9.5
+ // specification, but each entry in the Available Ring references only the
+ // head descriptor of any given descriptor chain.
+ //
+ NextAvailIdx = *Ring->Avail.Idx;
+ //
+ // (Due to our lock-step progress, this is where the host will produce the
+ // used element with the head descriptor's index in it.)
+ //
+ LastUsedIdx = NextAvailIdx;
+ Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] =
+ Indices->HeadDescIdx % Ring->QueueSize;
+
+ //
+ // virtio-0.9.5, 2.4.1.3 Updating the Index Field
+ //
+ MemoryFence();
+ *Ring->Avail.Idx = NextAvailIdx;
+
+ //
+ // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are
+ // OK.
+ //
+ MemoryFence();
+ Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
+ // Wait until the host processes and acknowledges our descriptor chain. The
+ // condition we use for polling is greatly simplified and relies on the
+ // synchronous, lock-step progress.
+ //
+ // Keep slowing down until we reach a poll period of slightly above 1 ms.
+ //
+ PollPeriodUsecs = 1;
+ MemoryFence();
+ while (*Ring->Used.Idx != NextAvailIdx) {
+ gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay
+
+ if (PollPeriodUsecs < 1024) {
+ PollPeriodUsecs *= 2;
+ }
+ MemoryFence();
+ }
+
+ MemoryFence();
+
+ if (UsedLen != NULL) {
+ volatile CONST VRING_USED_ELEM *UsedElem;
+
+ UsedElem = &Ring->Used.UsedElem[LastUsedIdx % Ring->QueueSize];
+ ASSERT (UsedElem->Id == Indices->HeadDescIdx);
+ *UsedLen = UsedElem->Len;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+
+ Report the feature bits to the VirtIo 1.0 device that the VirtIo 1.0 driver
+ understands.
+
+ In VirtIo 1.0, a device can reject a self-inconsistent feature bitmap through
+ the new VSTAT_FEATURES_OK status bit. (For example if the driver requests a
+ higher level feature but clears a prerequisite feature.) This function is a
+ small wrapper around VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures() that also
+ verifies if the VirtIo 1.0 device accepts the feature bitmap.
+
+ @param[in] VirtIo Report feature bits to this device.
+
+ @param[in] Features The set of feature bits that the driver wishes
+ to report. The caller is responsible to perform
+ any masking before calling this function; the
+ value is directly written with
+ VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures().
+
+ @param[in,out] DeviceStatus On input, the status byte most recently written
+ to the device's status register. On output (even
+ on error), DeviceStatus will be updated so that
+ it is suitable for further status bit
+ manipulation and writing to the device's status
+ register.
+
+ @retval EFI_SUCCESS The device accepted the configuration in Features.
+
+ @return EFI_UNSUPPORTED The device rejected the configuration in Features.
+
+ @retval EFI_UNSUPPORTED VirtIo->Revision is smaller than 1.0.0.
+
+ @return Error codes from the SetGuestFeatures(),
+ SetDeviceStatus(), GetDeviceStatus() member
+ functions.
+
+**/
+EFI_STATUS
+EFIAPI
+Virtio10WriteFeatures (
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,
+ IN UINT64 Features,
+ IN OUT UINT8 *DeviceStatus
+ )
+{
+ EFI_STATUS Status;
+
+ if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = VirtIo->SetGuestFeatures (VirtIo, Features);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *DeviceStatus |= VSTAT_FEATURES_OK;
+ Status = VirtIo->SetDeviceStatus (VirtIo, *DeviceStatus);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = VirtIo->GetDeviceStatus (VirtIo, DeviceStatus);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((*DeviceStatus & VSTAT_FEATURES_OK) == 0) {
+ Status = EFI_UNSUPPORTED;
+ }
+
+ return Status;
+}
+
+/**
+ Provides the virtio device address required to access system memory from a
+ DMA bus master.
+
+ The interface follows the same usage pattern as defined in UEFI spec 2.6
+ (Section 13.2 PCI Root Bridge I/O Protocol)
+
+ The VirtioMapAllBytesInSharedBuffer() is similar to VIRTIO_MAP_SHARED
+ with exception that NumberOfBytes is IN-only parameter. The function
+ maps all the bytes specified in NumberOfBytes param in one consecutive
+ range.
+
+ @param[in] VirtIo The virtio device for which the mapping is
+ requested.
+
+ @param[in] Operation Indicates if the bus master is going to
+ read or write to system memory.
+
+ @param[in] HostAddress The system memory address to map to shared
+ buffer address.
+
+ @param[in] NumberOfBytes Number of bytes to map.
+
+ @param[out] DeviceAddress The resulting shared map address for the
+ bus master to access the hosts HostAddress.
+
+ @param[out] Mapping A resulting token to pass to
+ VIRTIO_UNMAP_SHARED.
+
+
+ @retval EFI_SUCCESS The NumberOfBytes is successfully mapped.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a
+ common buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to
+ a lack of resources. This includes the case
+ when NumberOfBytes bytes cannot be mapped
+ in one consecutive range.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the
+ requested address.
+**/
+EFI_STATUS
+EFIAPI
+VirtioMapAllBytesInSharedBuffer (
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,
+ IN VIRTIO_MAP_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN UINTN NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ VOID *MapInfo;
+ UINTN Size;
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;
+
+ Size = NumberOfBytes;
+ Status = VirtIo->MapSharedBuffer (
+ VirtIo,
+ Operation,
+ HostAddress,
+ &Size,
+ &PhysicalAddress,
+ &MapInfo
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Size < NumberOfBytes) {
+ goto Failed;
+ }
+
+ *Mapping = MapInfo;
+ *DeviceAddress = PhysicalAddress;
+
+ return EFI_SUCCESS;
+
+Failed:
+ VirtIo->UnmapSharedBuffer (VirtIo, MapInfo);
+ return EFI_OUT_OF_RESOURCES;
+}
+
+/**
+
+ Map the ring buffer so that it can be accessed equally by both guest
+ and hypervisor.
+
+ @param[in] VirtIo The virtio device instance.
+
+ @param[in] Ring The virtio ring to map.
+
+ @param[out] RingBaseShift A resulting translation offset, to be
+ passed to VirtIo->SetQueueAddress().
+
+ @param[out] Mapping A resulting token to pass to
+ VirtIo->UnmapSharedBuffer().
+
+ @return Status code from VirtIo->MapSharedBuffer()
+**/
+EFI_STATUS
+EFIAPI
+VirtioRingMap (
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,
+ IN VRING *Ring,
+ OUT UINT64 *RingBaseShift,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+
+ Status = VirtioMapAllBytesInSharedBuffer (
+ VirtIo,
+ VirtioOperationBusMasterCommonBuffer,
+ Ring->Base,
+ EFI_PAGES_TO_SIZE (Ring->NumPages),
+ &DeviceAddress,
+ Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *RingBaseShift = DeviceAddress - (UINT64)(UINTN)Ring->Base;
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.inf b/roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.inf new file mode 100644 index 000000000..6791f64e6 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.inf @@ -0,0 +1,29 @@ +## @file
+# Library of virtio utility functions.
+#
+# Copyright (C) 2012, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VirtioLib
+ FILE_GUID = 90CED1D9-18F2-47CC-BF24-41EC29406637
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = VirtioLib
+
+[Sources]
+ VirtioLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ UefiBootServicesTableLib
diff --git a/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c new file mode 100644 index 000000000..2f20272c1 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c @@ -0,0 +1,221 @@ +/** @file
+
+ This driver produces Virtio Device Protocol instances for Virtio Mmio devices.
+
+ Copyright (C) 2013, ARM Ltd.
+ Copyright (C) 2017, AMD Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "VirtioMmioDevice.h"
+
+STATIC CONST VIRTIO_DEVICE_PROTOCOL mMmioDeviceProtocolTemplate = {
+ 0, // Revision
+ 0, // SubSystemDeviceId
+ VirtioMmioGetDeviceFeatures, // GetDeviceFeatures
+ VirtioMmioSetGuestFeatures, // SetGuestFeatures
+ VirtioMmioSetQueueAddress, // SetQueueAddress
+ VirtioMmioSetQueueSel, // SetQueueSel
+ VirtioMmioSetQueueNotify, // SetQueueNotify
+ VirtioMmioSetQueueAlignment, // SetQueueAlign
+ VirtioMmioSetPageSize, // SetPageSize
+ VirtioMmioGetQueueSize, // GetQueueNumMax
+ VirtioMmioSetQueueSize, // SetQueueNum
+ VirtioMmioGetDeviceStatus, // GetDeviceStatus
+ VirtioMmioSetDeviceStatus, // SetDeviceStatus
+ VirtioMmioDeviceWrite, // WriteDevice
+ VirtioMmioDeviceRead, // ReadDevice
+ VirtioMmioAllocateSharedPages, // AllocateSharedPages
+ VirtioMmioFreeSharedPages, // FreeSharedPages
+ VirtioMmioMapSharedBuffer, // MapSharedBuffer
+ VirtioMmioUnmapSharedBuffer // UnmapSharedBuffer
+};
+
+/**
+
+ Initialize the VirtIo MMIO Device
+
+ @param[in] BaseAddress Base Address of the VirtIo MMIO Device
+
+ @param[in, out] Device The driver instance to configure.
+
+ @retval EFI_SUCCESS Setup complete.
+
+ @retval EFI_UNSUPPORTED The driver is not a VirtIo MMIO device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioMmioInit (
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN OUT VIRTIO_MMIO_DEVICE *Device
+ )
+{
+ UINT32 MagicValue;
+ UINT32 VendorId;
+ UINT32 Version;
+
+ //
+ // Initialize VirtIo Mmio Device
+ //
+ CopyMem (&Device->VirtioDevice, &mMmioDeviceProtocolTemplate,
+ sizeof (VIRTIO_DEVICE_PROTOCOL));
+ Device->BaseAddress = BaseAddress;
+ Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5);
+ Device->VirtioDevice.SubSystemDeviceId =
+ MmioRead32 (BaseAddress + VIRTIO_MMIO_OFFSET_DEVICE_ID);
+
+ //
+ // Double-check MMIO-specific values
+ //
+ MagicValue = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_MAGIC);
+ if (MagicValue != VIRTIO_MMIO_MAGIC) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Version = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VERSION);
+ if (Version != 1) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Double-check MMIO-specific values
+ //
+ VendorId = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VENDOR_ID);
+ if (VendorId != VIRTIO_VENDOR_ID) {
+ //
+ // The ARM Base and Foundation Models do not report a valid VirtIo VendorId.
+ // They return a value of 0x0 for the VendorId.
+ //
+ DEBUG((DEBUG_WARN, "VirtioMmioInit: Warning: The VendorId (0x%X) does not "
+ "match the VirtIo VendorId (0x%X).\n",
+ VendorId, VIRTIO_VENDOR_ID));
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+
+ Uninitialize the internals of a virtio-mmio device that has been successfully
+ set up with VirtioMmioInit().
+
+ @param[in, out] Device The device to clean up.
+
+**/
+
+STATIC
+VOID
+EFIAPI
+VirtioMmioUninit (
+ IN VIRTIO_MMIO_DEVICE *Device
+ )
+{
+ //
+ // Note: This function mirrors VirtioMmioInit() that does not allocate any
+ // resources - there's nothing to free here.
+ //
+}
+
+EFI_STATUS
+VirtioMmioInstallDevice (
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ VIRTIO_MMIO_DEVICE *VirtIo;
+
+ if (!BaseAddress) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (Handle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate VIRTIO_MMIO_DEVICE
+ //
+ VirtIo = AllocateZeroPool (sizeof (VIRTIO_MMIO_DEVICE));
+ if (VirtIo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ VirtIo->Signature = VIRTIO_MMIO_DEVICE_SIGNATURE;
+
+ Status = VirtioMmioInit (BaseAddress, VirtIo);
+ if (EFI_ERROR (Status)) {
+ goto FreeVirtioMem;
+ }
+
+ //
+ // Install VIRTIO_DEVICE_PROTOCOL to Handle
+ //
+ Status = gBS->InstallProtocolInterface (&Handle,
+ &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,
+ &VirtIo->VirtioDevice);
+ if (EFI_ERROR (Status)) {
+ goto UninitVirtio;
+ }
+
+ return EFI_SUCCESS;
+
+UninitVirtio:
+ VirtioMmioUninit (VirtIo);
+
+FreeVirtioMem:
+ FreePool (VirtIo);
+ return Status;
+}
+
+EFI_STATUS
+VirtioMmioUninstallDevice (
+ IN EFI_HANDLE DeviceHandle
+ )
+{
+ VIRTIO_DEVICE_PROTOCOL *VirtioDevice;
+ VIRTIO_MMIO_DEVICE *MmioDevice;
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gVirtioDeviceProtocolGuid, // retrieve the VirtIo iface
+ (VOID **)&VirtioDevice, // target pointer
+ DeviceHandle, // requestor driver identity
+ DeviceHandle, // requesting lookup for dev.
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the MMIO device from the VirtIo Device instance
+ //
+ MmioDevice = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice);
+
+ //
+ // Uninstall the protocol interface
+ //
+ Status = gBS->UninstallProtocolInterface (DeviceHandle,
+ &gVirtioDeviceProtocolGuid, &MmioDevice->VirtioDevice
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Uninitialize the VirtIo Device
+ //
+ VirtioMmioUninit (MmioDevice);
+ FreePool (MmioDevice);
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h new file mode 100644 index 000000000..785876c75 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h @@ -0,0 +1,171 @@ +/** @file
+
+ Internal definitions for the VirtIo MMIO Device driver
+
+ Copyright (C) 2013, ARM Ltd
+ Copyright (C) 2017, AMD Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _VIRTIO_MMIO_DEVICE_INTERNAL_H_
+#define _VIRTIO_MMIO_DEVICE_INTERNAL_H_
+
+#include <Protocol/VirtioDevice.h>
+
+#include <IndustryStandard/Virtio.h>
+
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiLib.h>
+#include <Library/VirtioMmioDeviceLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define VIRTIO_MMIO_DEVICE_SIGNATURE SIGNATURE_32 ('V', 'M', 'I', 'O')
+
+typedef struct {
+ UINT32 Signature;
+ VIRTIO_DEVICE_PROTOCOL VirtioDevice;
+ PHYSICAL_ADDRESS BaseAddress;
+} VIRTIO_MMIO_DEVICE;
+
+#define VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE(Device) \
+ CR (Device, VIRTIO_MMIO_DEVICE, VirtioDevice, VIRTIO_MMIO_DEVICE_SIGNATURE)
+
+#define VIRTIO_CFG_WRITE(Device, Offset, Val) \
+ (MmioWrite32 (Device->BaseAddress + (Offset), Val))
+#define VIRTIO_CFG_READ(Device, Offset) \
+ (MmioRead32 (Device->BaseAddress + (Offset)))
+
+EFI_STATUS
+EFIAPI
+VirtioMmioDeviceRead (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN FieldOFfset,
+ IN UINTN FieldSize,
+ IN UINTN BufferSize,
+ OUT VOID* Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioDeviceWrite (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN UINT64 Value
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioGetDeviceFeatures (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT64 *DeviceFeatures
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioGetQueueSize (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT16 *QueueNumMax
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioGetDeviceStatus (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT8 *DeviceStatus
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueSize (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 QueueSize
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetDeviceStatus (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT8 DeviceStatus
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueNotify (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 QueueNotify
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueSel (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 Sel
+ );
+
+EFI_STATUS
+VirtioMmioSetQueueAddress (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VRING *Ring,
+ IN UINT64 RingBaseShift
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueAlignment (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT32 Alignment
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetPageSize (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT32 PageSize
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetGuestFeatures (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT64 Features
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioAllocateSharedPages (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN NumPages,
+ OUT VOID **HostAddress
+ );
+
+VOID
+EFIAPI
+VirtioMmioFreeSharedPages (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN NumPages,
+ IN VOID *HostAddress
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioMapSharedBuffer (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VIRTIO_MAP_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioMmioUnmapSharedBuffer (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VOID *Mapping
+ );
+
+#endif // _VIRTIO_MMIO_DEVICE_INTERNAL_H_
diff --git a/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c new file mode 100644 index 000000000..454f00882 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c @@ -0,0 +1,349 @@ +/** @file
+
+ This driver produces Virtio Device Protocol instances for Virtio MMIO devices.
+
+ Copyright (C) 2012, Red Hat, Inc.
+ Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>
+ Copyright (C) 2013, ARM Ltd.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "VirtioMmioDevice.h"
+
+EFI_STATUS
+EFIAPI
+VirtioMmioGetDeviceFeatures (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT64 *DeviceFeatures
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ if (DeviceFeatures == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ *DeviceFeatures = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_HOST_FEATURES);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioGetQueueSize (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT16 *QueueNumMax
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ if (QueueNumMax == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ *QueueNumMax = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM_MAX) & 0xFFFF;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioGetDeviceStatus (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ OUT UINT8 *DeviceStatus
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ if (DeviceStatus == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ *DeviceStatus = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_STATUS) & 0xFF;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueSize (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 QueueSize
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_NUM, QueueSize);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetDeviceStatus (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT8 DeviceStatus
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_STATUS, DeviceStatus);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueNotify (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 QueueNotify
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_NOTIFY, QueueNotify);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueAlignment (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT32 Alignment
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_ALIGN, Alignment);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetPageSize (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT32 PageSize
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ if (PageSize != EFI_PAGE_SIZE) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE, PageSize);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetQueueSel (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT16 Sel
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_SEL, Sel);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+VirtioMmioSetQueueAddress (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VRING *Ring,
+ IN UINT64 RingBaseShift
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ ASSERT (RingBaseShift == 0);
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_QUEUE_PFN,
+ (UINT32)((UINTN)Ring->Base >> EFI_PAGE_SHIFT));
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioSetGuestFeatures (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINT64 Features
+ )
+{
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ if (Features > MAX_UINT32) {
+ return EFI_UNSUPPORTED;
+ }
+ VIRTIO_CFG_WRITE (Device, VIRTIO_MMIO_OFFSET_GUEST_FEATURES,
+ (UINT32)Features);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioDeviceWrite (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN UINT64 Value
+ )
+{
+ UINTN DstBaseAddress;
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ //
+ // Double-check fieldsize
+ //
+ if ((FieldSize != 1) && (FieldSize != 2) &&
+ (FieldSize != 4) && (FieldSize != 8)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Compute base address
+ //
+ DstBaseAddress = Device->BaseAddress +
+ VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_MMIO + FieldOffset;
+
+ //
+ // The device-specific memory area of Virtio-MMIO can only be written in
+ // byte accesses. This is not currently in the Virtio spec.
+ //
+ MmioWriteBuffer8 (DstBaseAddress, FieldSize, (UINT8*)&Value);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioDeviceRead (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN FieldOffset,
+ IN UINTN FieldSize,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ UINTN SrcBaseAddress;
+ VIRTIO_MMIO_DEVICE *Device;
+
+ Device = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (This);
+
+ //
+ // Parameter validation
+ //
+ ASSERT (FieldSize == BufferSize);
+
+ //
+ // Double-check fieldsize
+ //
+ if ((FieldSize != 1) && (FieldSize != 2) &&
+ (FieldSize != 4) && (FieldSize != 8)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Compute base address
+ //
+ SrcBaseAddress = Device->BaseAddress +
+ VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_MMIO + FieldOffset;
+
+ //
+ // The device-specific memory area of Virtio-MMIO can only be read in
+ // byte reads. This is not currently in the Virtio spec.
+ //
+ MmioReadBuffer8 (SrcBaseAddress, BufferSize, Buffer);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioAllocateSharedPages (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN NumPages,
+ OUT VOID **HostAddress
+ )
+{
+ VOID *Buffer;
+
+ Buffer = AllocatePages (NumPages);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *HostAddress = Buffer;
+ return EFI_SUCCESS;
+}
+
+VOID
+EFIAPI
+VirtioMmioFreeSharedPages (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN UINTN NumPages,
+ IN VOID *HostAddress
+ )
+{
+ FreePages (HostAddress, NumPages);
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioMapSharedBuffer (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VIRTIO_MAP_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ *DeviceAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;
+ *Mapping = NULL;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioMmioUnmapSharedBuffer (
+ IN VIRTIO_DEVICE_PROTOCOL *This,
+ IN VOID *Mapping
+ )
+{
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf new file mode 100644 index 000000000..0736f89d6 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf @@ -0,0 +1,37 @@ +## @file
+# This driver produces the VirtIo Device Protocol instances for VirtIo Mmio
+# Device
+#
+# Copyright (C) 2013, ARM Ltd
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = VirtioMmioDeviceLib
+ FILE_GUID = 3b6ed966-b5d1-46a8-965b-867ff22d9c89
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = VirtioMmioDeviceLib
+
+[Sources]
+ VirtioMmioDevice.c
+ VirtioMmioDevice.h
+ VirtioMmioDeviceFunctions.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ IoLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiLib
+
+[Protocols]
+ gVirtioDeviceProtocolGuid ## PRODUCES
diff --git a/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.c b/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.c new file mode 100644 index 000000000..53040cc6f --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.c @@ -0,0 +1,159 @@ +/** @file
+ VMGEXIT Support Library.
+
+ Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/VmgExitLib.h>
+#include <Register/Amd/Msr.h>
+
+/**
+ Check for VMGEXIT error
+
+ Check if the hypervisor has returned an error after completion of the VMGEXIT
+ by examining the SwExitInfo1 field of the GHCB.
+
+ @param[in] Ghcb A pointer to the GHCB
+
+ @retval 0 VMGEXIT succeeded.
+ @return Exception number to be propagated, VMGEXIT processing
+ did not succeed.
+
+**/
+STATIC
+UINT64
+VmgExitErrorCheck (
+ IN GHCB *Ghcb
+ )
+{
+ GHCB_EVENT_INJECTION Event;
+ GHCB_EXIT_INFO ExitInfo;
+ UINT64 Status;
+
+ ExitInfo.Uint64 = Ghcb->SaveArea.SwExitInfo1;
+ ASSERT ((ExitInfo.Elements.Lower32Bits == 0) ||
+ (ExitInfo.Elements.Lower32Bits == 1));
+
+ Status = 0;
+ if (ExitInfo.Elements.Lower32Bits == 0) {
+ return Status;
+ }
+
+ if (ExitInfo.Elements.Lower32Bits == 1) {
+ ASSERT (Ghcb->SaveArea.SwExitInfo2 != 0);
+
+ //
+ // Check that the return event is valid
+ //
+ Event.Uint64 = Ghcb->SaveArea.SwExitInfo2;
+ if (Event.Elements.Valid &&
+ Event.Elements.Type == GHCB_EVENT_INJECTION_TYPE_EXCEPTION) {
+ switch (Event.Elements.Vector) {
+ case GP_EXCEPTION:
+ case UD_EXCEPTION:
+ //
+ // Use returned event as return code
+ //
+ Status = Event.Uint64;
+ }
+ }
+ }
+
+ if (Status == 0) {
+ GHCB_EVENT_INJECTION GpEvent;
+
+ GpEvent.Uint64 = 0;
+ GpEvent.Elements.Vector = GP_EXCEPTION;
+ GpEvent.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+ GpEvent.Elements.Valid = 1;
+
+ Status = GpEvent.Uint64;
+ }
+
+ return Status;
+}
+
+/**
+ Perform VMGEXIT.
+
+ Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and
+ then handles the return actions.
+
+ @param[in, out] Ghcb A pointer to the GHCB
+ @param[in] ExitCode VMGEXIT code to be assigned to the SwExitCode
+ field of the GHCB.
+ @param[in] ExitInfo1 VMGEXIT information to be assigned to the
+ SwExitInfo1 field of the GHCB.
+ @param[in] ExitInfo2 VMGEXIT information to be assigned to the
+ SwExitInfo2 field of the GHCB.
+
+ @retval 0 VMGEXIT succeeded.
+ @return Exception number to be propagated, VMGEXIT
+ processing did not succeed.
+
+**/
+UINT64
+EFIAPI
+VmgExit (
+ IN OUT GHCB *Ghcb,
+ IN UINT64 ExitCode,
+ IN UINT64 ExitInfo1,
+ IN UINT64 ExitInfo2
+ )
+{
+ Ghcb->SaveArea.SwExitCode = ExitCode;
+ Ghcb->SaveArea.SwExitInfo1 = ExitInfo1;
+ Ghcb->SaveArea.SwExitInfo2 = ExitInfo2;
+
+ //
+ // Guest memory is used for the guest-hypervisor communication, so fence
+ // the invocation of the VMGEXIT instruction to ensure GHCB accesses are
+ // synchronized properly.
+ //
+ MemoryFence ();
+ AsmVmgExit ();
+ MemoryFence ();
+
+ return VmgExitErrorCheck (Ghcb);
+}
+
+/**
+ Perform pre-VMGEXIT initialization/preparation.
+
+ Performs the necessary steps in preparation for invoking VMGEXIT. Must be
+ called before setting any fields within the GHCB.
+
+ @param[in, out] Ghcb A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgInit (
+ IN OUT GHCB *Ghcb
+ )
+{
+ SetMem (&Ghcb->SaveArea, sizeof (Ghcb->SaveArea), 0);
+}
+
+/**
+ Perform post-VMGEXIT cleanup.
+
+ Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
+ called after obtaining needed fields within the GHCB.
+
+ @param[in, out] Ghcb A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgDone (
+ IN OUT GHCB *Ghcb
+ )
+{
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf new file mode 100644 index 000000000..d003ac631 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf @@ -0,0 +1,36 @@ +## @file
+# VMGEXIT Support Library.
+#
+# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VmgExitLib
+ FILE_GUID = 0e923c25-13cd-430b-8714-ffe85652a97b
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = VmgExitLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = X64
+#
+
+[Sources.common]
+ VmgExitLib.c
+ VmgExitVcHandler.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+
diff --git a/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c b/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c new file mode 100644 index 000000000..8e42b305e --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c @@ -0,0 +1,1718 @@ +/** @file
+ X64 #VC Exception Handler functon.
+
+ Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/VmgExitLib.h>
+#include <Register/Amd/Msr.h>
+#include <Register/Intel/Cpuid.h>
+#include <IndustryStandard/InstructionParsing.h>
+
+//
+// Instruction execution mode definition
+//
+typedef enum {
+ LongMode64Bit = 0,
+ LongModeCompat32Bit,
+ LongModeCompat16Bit,
+} SEV_ES_INSTRUCTION_MODE;
+
+//
+// Instruction size definition (for operand and address)
+//
+typedef enum {
+ Size8Bits = 0,
+ Size16Bits,
+ Size32Bits,
+ Size64Bits,
+} SEV_ES_INSTRUCTION_SIZE;
+
+//
+// Intruction segment definition
+//
+typedef enum {
+ SegmentEs = 0,
+ SegmentCs,
+ SegmentSs,
+ SegmentDs,
+ SegmentFs,
+ SegmentGs,
+} SEV_ES_INSTRUCTION_SEGMENT;
+
+//
+// Instruction rep function definition
+//
+typedef enum {
+ RepNone = 0,
+ RepZ,
+ RepNZ,
+} SEV_ES_INSTRUCTION_REP;
+
+typedef struct {
+ UINT8 Rm;
+ UINT8 Reg;
+ UINT8 Mod;
+} SEV_ES_INSTRUCTION_MODRM_EXT;
+
+typedef struct {
+ UINT8 Base;
+ UINT8 Index;
+ UINT8 Scale;
+} SEV_ES_INSTRUCTION_SIB_EXT;
+
+//
+// Instruction opcode definition
+//
+typedef struct {
+ SEV_ES_INSTRUCTION_MODRM_EXT ModRm;
+
+ SEV_ES_INSTRUCTION_SIB_EXT Sib;
+
+ UINTN RegData;
+ UINTN RmData;
+} SEV_ES_INSTRUCTION_OPCODE_EXT;
+
+//
+// Instruction parsing context definition
+//
+typedef struct {
+ GHCB *Ghcb;
+
+ SEV_ES_INSTRUCTION_MODE Mode;
+ SEV_ES_INSTRUCTION_SIZE DataSize;
+ SEV_ES_INSTRUCTION_SIZE AddrSize;
+ BOOLEAN SegmentSpecified;
+ SEV_ES_INSTRUCTION_SEGMENT Segment;
+ SEV_ES_INSTRUCTION_REP RepMode;
+
+ UINT8 *Begin;
+ UINT8 *End;
+
+ UINT8 *Prefixes;
+ UINT8 *OpCodes;
+ UINT8 *Displacement;
+ UINT8 *Immediate;
+
+ INSTRUCTION_REX_PREFIX RexPrefix;
+
+ BOOLEAN ModRmPresent;
+ INSTRUCTION_MODRM ModRm;
+
+ BOOLEAN SibPresent;
+ INSTRUCTION_SIB Sib;
+
+ UINTN PrefixSize;
+ UINTN OpCodeSize;
+ UINTN DisplacementSize;
+ UINTN ImmediateSize;
+
+ SEV_ES_INSTRUCTION_OPCODE_EXT Ext;
+} SEV_ES_INSTRUCTION_DATA;
+
+//
+// Non-automatic Exit function prototype
+//
+typedef
+UINT64
+(*NAE_EXIT) (
+ GHCB *Ghcb,
+ EFI_SYSTEM_CONTEXT_X64 *Regs,
+ SEV_ES_INSTRUCTION_DATA *InstructionData
+ );
+
+//
+// Per-CPU data mapping structure
+//
+typedef struct {
+ BOOLEAN Dr7Cached;
+ UINT64 Dr7;
+} SEV_ES_PER_CPU_DATA;
+
+
+/**
+ Checks the GHCB to determine if the specified register has been marked valid.
+
+ The ValidBitmap area represents the areas of the GHCB that have been marked
+ valid. Return an indication of whether the area of the GHCB that holds the
+ specified register has been marked valid.
+
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
+ @param[in] Reg Offset in the GHCB of the register to check
+
+ @retval TRUE Register has been marked vald in the GHCB
+ @retval FALSE Register has not been marked valid in the GHCB
+
+**/
+STATIC
+BOOLEAN
+GhcbIsRegValid (
+ IN GHCB *Ghcb,
+ IN GHCB_REGISTER Reg
+ )
+{
+ UINT32 RegIndex;
+ UINT32 RegBit;
+
+ RegIndex = Reg / 8;
+ RegBit = Reg & 0x07;
+
+ return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);
+}
+
+/**
+ Marks a register as valid in the GHCB.
+
+ The ValidBitmap area represents the areas of the GHCB that have been marked
+ valid. Set the area of the GHCB that holds the specified register as valid.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block
+ @param[in] Reg Offset in the GHCB of the register to mark valid
+
+**/
+STATIC
+VOID
+GhcbSetRegValid (
+ IN OUT GHCB *Ghcb,
+ IN GHCB_REGISTER Reg
+ )
+{
+ UINT32 RegIndex;
+ UINT32 RegBit;
+
+ RegIndex = Reg / 8;
+ RegBit = Reg & 0x07;
+
+ Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
+}
+
+/**
+ Return a pointer to the contents of the specified register.
+
+ Based upon the input register, return a pointer to the registers contents
+ in the x86 processor context.
+
+ @param[in] Regs x64 processor context
+ @param[in] Register Register to obtain pointer for
+
+ @return Pointer to the contents of the requested register
+
+**/
+STATIC
+UINT64 *
+GetRegisterPointer (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN UINT8 Register
+ )
+{
+ UINT64 *Reg;
+
+ switch (Register) {
+ case 0:
+ Reg = &Regs->Rax;
+ break;
+ case 1:
+ Reg = &Regs->Rcx;
+ break;
+ case 2:
+ Reg = &Regs->Rdx;
+ break;
+ case 3:
+ Reg = &Regs->Rbx;
+ break;
+ case 4:
+ Reg = &Regs->Rsp;
+ break;
+ case 5:
+ Reg = &Regs->Rbp;
+ break;
+ case 6:
+ Reg = &Regs->Rsi;
+ break;
+ case 7:
+ Reg = &Regs->Rdi;
+ break;
+ case 8:
+ Reg = &Regs->R8;
+ break;
+ case 9:
+ Reg = &Regs->R9;
+ break;
+ case 10:
+ Reg = &Regs->R10;
+ break;
+ case 11:
+ Reg = &Regs->R11;
+ break;
+ case 12:
+ Reg = &Regs->R12;
+ break;
+ case 13:
+ Reg = &Regs->R13;
+ break;
+ case 14:
+ Reg = &Regs->R14;
+ break;
+ case 15:
+ Reg = &Regs->R15;
+ break;
+ default:
+ Reg = NULL;
+ }
+ ASSERT (Reg != NULL);
+
+ return Reg;
+}
+
+/**
+ Update the instruction parsing context for displacement bytes.
+
+ @param[in, out] InstructionData Instruction parsing context
+ @param[in] Size The instruction displacement size
+
+**/
+STATIC
+VOID
+UpdateForDisplacement (
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,
+ IN UINTN Size
+ )
+{
+ InstructionData->DisplacementSize = Size;
+ InstructionData->Immediate += Size;
+ InstructionData->End += Size;
+}
+
+/**
+ Determine if an instruction address if RIP relative.
+
+ Examine the instruction parsing context to determine if the address offset
+ is relative to the instruction pointer.
+
+ @param[in] InstructionData Instruction parsing context
+
+ @retval TRUE Instruction addressing is RIP relative
+ @retval FALSE Instruction addressing is not RIP relative
+
+**/
+STATIC
+BOOLEAN
+IsRipRelative (
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+
+ Ext = &InstructionData->Ext;
+
+ return ((InstructionData->Mode == LongMode64Bit) &&
+ (Ext->ModRm.Mod == 0) &&
+ (Ext->ModRm.Rm == 5) &&
+ (InstructionData->SibPresent == FALSE));
+}
+
+/**
+ Return the effective address of a memory operand.
+
+ Examine the instruction parsing context to obtain the effective memory
+ address of a memory operand.
+
+ @param[in] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @return The memory operand effective address
+
+**/
+STATIC
+UINT64
+GetEffectiveMemoryAddress (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ UINT64 EffectiveAddress;
+
+ Ext = &InstructionData->Ext;
+ EffectiveAddress = 0;
+
+ if (IsRipRelative (InstructionData)) {
+ //
+ // RIP-relative displacement is a 32-bit signed value
+ //
+ INT32 RipRelative;
+
+ RipRelative = *(INT32 *) InstructionData->Displacement;
+
+ UpdateForDisplacement (InstructionData, 4);
+
+ //
+ // Negative displacement is handled by standard UINT64 wrap-around.
+ //
+ return Regs->Rip + (UINT64) RipRelative;
+ }
+
+ switch (Ext->ModRm.Mod) {
+ case 1:
+ UpdateForDisplacement (InstructionData, 1);
+ EffectiveAddress += (UINT64) (*(INT8 *) (InstructionData->Displacement));
+ break;
+ case 2:
+ switch (InstructionData->AddrSize) {
+ case Size16Bits:
+ UpdateForDisplacement (InstructionData, 2);
+ EffectiveAddress += (UINT64) (*(INT16 *) (InstructionData->Displacement));
+ break;
+ default:
+ UpdateForDisplacement (InstructionData, 4);
+ EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));
+ break;
+ }
+ break;
+ }
+
+ if (InstructionData->SibPresent) {
+ INT64 Displacement;
+
+ if (Ext->Sib.Index != 4) {
+ CopyMem (
+ &Displacement,
+ GetRegisterPointer (Regs, Ext->Sib.Index),
+ sizeof (Displacement)
+ );
+ Displacement *= (INT64)(1 << Ext->Sib.Scale);
+
+ //
+ // Negative displacement is handled by standard UINT64 wrap-around.
+ //
+ EffectiveAddress += (UINT64) Displacement;
+ }
+
+ if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {
+ EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);
+ } else {
+ UpdateForDisplacement (InstructionData, 4);
+ EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));
+ }
+ } else {
+ EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);
+ }
+
+ return EffectiveAddress;
+}
+
+/**
+ Decode a ModRM byte.
+
+ Examine the instruction parsing context to decode a ModRM byte and the SIB
+ byte, if present.
+
+ @param[in] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+**/
+STATIC
+VOID
+DecodeModRm (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ INSTRUCTION_REX_PREFIX *RexPrefix;
+ INSTRUCTION_MODRM *ModRm;
+ INSTRUCTION_SIB *Sib;
+
+ RexPrefix = &InstructionData->RexPrefix;
+ Ext = &InstructionData->Ext;
+ ModRm = &InstructionData->ModRm;
+ Sib = &InstructionData->Sib;
+
+ InstructionData->ModRmPresent = TRUE;
+ ModRm->Uint8 = *(InstructionData->End);
+
+ InstructionData->Displacement++;
+ InstructionData->Immediate++;
+ InstructionData->End++;
+
+ Ext->ModRm.Mod = ModRm->Bits.Mod;
+ Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;
+ Ext->ModRm.Rm = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;
+
+ Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);
+
+ if (Ext->ModRm.Mod == 3) {
+ Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);
+ } else {
+ if (ModRm->Bits.Rm == 4) {
+ InstructionData->SibPresent = TRUE;
+ Sib->Uint8 = *(InstructionData->End);
+
+ InstructionData->Displacement++;
+ InstructionData->Immediate++;
+ InstructionData->End++;
+
+ Ext->Sib.Scale = Sib->Bits.Scale;
+ Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;
+ Ext->Sib.Base = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;
+ }
+
+ Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);
+ }
+}
+
+/**
+ Decode instruction prefixes.
+
+ Parse the instruction data to track the instruction prefixes that have
+ been used.
+
+ @param[in] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+**/
+STATIC
+VOID
+DecodePrefixes (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_MODE Mode;
+ SEV_ES_INSTRUCTION_SIZE ModeDataSize;
+ SEV_ES_INSTRUCTION_SIZE ModeAddrSize;
+ UINT8 *Byte;
+
+ //
+ // Always in 64-bit mode
+ //
+ Mode = LongMode64Bit;
+ ModeDataSize = Size32Bits;
+ ModeAddrSize = Size64Bits;
+
+ InstructionData->Mode = Mode;
+ InstructionData->DataSize = ModeDataSize;
+ InstructionData->AddrSize = ModeAddrSize;
+
+ InstructionData->Prefixes = InstructionData->Begin;
+
+ Byte = InstructionData->Prefixes;
+ for ( ; ; Byte++, InstructionData->PrefixSize++) {
+ //
+ // Check the 0x40 to 0x4F range using an if statement here since some
+ // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
+ // 16 case statements below.
+ //
+ if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {
+ InstructionData->RexPrefix.Uint8 = *Byte;
+ if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {
+ InstructionData->DataSize = Size64Bits;
+ }
+ continue;
+ }
+
+ switch (*Byte) {
+ case OVERRIDE_SEGMENT_CS:
+ case OVERRIDE_SEGMENT_DS:
+ case OVERRIDE_SEGMENT_ES:
+ case OVERRIDE_SEGMENT_SS:
+ if (Mode != LongMode64Bit) {
+ InstructionData->SegmentSpecified = TRUE;
+ InstructionData->Segment = (*Byte >> 3) & 3;
+ }
+ break;
+
+ case OVERRIDE_SEGMENT_FS:
+ case OVERRIDE_SEGMENT_GS:
+ InstructionData->SegmentSpecified = TRUE;
+ InstructionData->Segment = *Byte & 7;
+ break;
+
+ case OVERRIDE_OPERAND_SIZE:
+ if (InstructionData->RexPrefix.Uint8 == 0) {
+ InstructionData->DataSize =
+ (Mode == LongMode64Bit) ? Size16Bits :
+ (Mode == LongModeCompat32Bit) ? Size16Bits :
+ (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
+ }
+ break;
+
+ case OVERRIDE_ADDRESS_SIZE:
+ InstructionData->AddrSize =
+ (Mode == LongMode64Bit) ? Size32Bits :
+ (Mode == LongModeCompat32Bit) ? Size16Bits :
+ (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
+ break;
+
+ case LOCK_PREFIX:
+ break;
+
+ case REPZ_PREFIX:
+ InstructionData->RepMode = RepZ;
+ break;
+
+ case REPNZ_PREFIX:
+ InstructionData->RepMode = RepNZ;
+ break;
+
+ default:
+ InstructionData->OpCodes = Byte;
+ InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;
+
+ InstructionData->End = Byte + InstructionData->OpCodeSize;
+ InstructionData->Displacement = InstructionData->End;
+ InstructionData->Immediate = InstructionData->End;
+ return;
+ }
+ }
+}
+
+/**
+ Determine instruction length
+
+ Return the total length of the parsed instruction.
+
+ @param[in] InstructionData Instruction parsing context
+
+ @return Length of parsed instruction
+
+**/
+STATIC
+UINT64
+InstructionLength (
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ return (UINT64) (InstructionData->End - InstructionData->Begin);
+}
+
+/**
+ Initialize the instruction parsing context.
+
+ Initialize the instruction parsing context, which includes decoding the
+ instruction prefixes.
+
+ @param[in, out] InstructionData Instruction parsing context
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in] Regs x64 processor context
+
+**/
+STATIC
+VOID
+InitInstructionData (
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,
+ IN GHCB *Ghcb,
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs
+ )
+{
+ SetMem (InstructionData, sizeof (*InstructionData), 0);
+ InstructionData->Ghcb = Ghcb;
+ InstructionData->Begin = (UINT8 *) Regs->Rip;
+ InstructionData->End = (UINT8 *) Regs->Rip;
+
+ DecodePrefixes (Regs, InstructionData);
+}
+
+/**
+ Report an unsupported event to the hypervisor
+
+ Use the VMGEXIT support to report an unsupported event to the hypervisor.
+
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+UnsupportedExit (
+ IN GHCB *Ghcb,
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
+ if (Status == 0) {
+ GHCB_EVENT_INJECTION Event;
+
+ Event.Uint64 = 0;
+ Event.Elements.Vector = GP_EXCEPTION;
+ Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+ Event.Elements.Valid = 1;
+
+ Status = Event.Uint64;
+ }
+
+ return Status;
+}
+
+/**
+ Handle an MMIO event.
+
+ Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MmioExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo1, ExitInfo2, Status;
+ UINTN Bytes;
+ UINT64 *Register;
+ UINT8 OpCode, SignByte;
+
+ Bytes = 0;
+
+ OpCode = *(InstructionData->OpCodes);
+ if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {
+ OpCode = *(InstructionData->OpCodes + 1);
+ }
+
+ switch (OpCode) {
+ //
+ // MMIO write (MOV reg/memX, regX)
+ //
+ case 0x88:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0x89:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ (InstructionData->DataSize == Size64Bits) ? 8 :
+ 0);
+
+ if (InstructionData->Ext.ModRm.Mod == 3) {
+ //
+ // NPF on two register operands???
+ //
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+ CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
+
+ Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+ break;
+
+ //
+ // MMIO write (MOV reg/memX, immX)
+ //
+ case 0xC6:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xC7:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ 0);
+
+ InstructionData->ImmediateSize = Bytes;
+ InstructionData->End += Bytes;
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+ CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
+
+ Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+ break;
+
+ //
+ // MMIO read (MOV regX, reg/memX)
+ //
+ case 0x8A:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0x8B:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ (InstructionData->DataSize == Size64Bits) ? 8 :
+ 0);
+ if (InstructionData->Ext.ModRm.Mod == 3) {
+ //
+ // NPF on two register operands???
+ //
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+
+ Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+ if (Bytes == 4) {
+ //
+ // Zero-extend for 32-bit operation
+ //
+ *Register = 0;
+ }
+ CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+ break;
+
+ //
+ // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
+ //
+ case 0xB6:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xB7:
+ Bytes = (Bytes != 0) ? Bytes : 2;
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+
+ Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+ SetMem (Register, InstructionData->DataSize, 0);
+ CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+ break;
+
+ //
+ // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
+ //
+ case 0xBE:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xBF:
+ Bytes = (Bytes != 0) ? Bytes : 2;
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+
+ Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (Bytes == 1) {
+ UINT8 *Data;
+
+ Data = (UINT8 *) Ghcb->SharedBuffer;
+ SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00;
+ } else {
+ UINT16 *Data;
+
+ Data = (UINT16 *) Ghcb->SharedBuffer;
+ SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00;
+ }
+
+ Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+ SetMem (Register, InstructionData->DataSize, SignByte);
+ CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+ break;
+
+ default:
+ Status = GP_EXCEPTION;
+ ASSERT (FALSE);
+ }
+
+ return Status;
+}
+
+/**
+ Handle a MWAIT event.
+
+ Use the VMGEXIT instruction to handle a MWAIT event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MwaitExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ DecodeModRm (Regs, InstructionData);
+
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ GhcbSetRegValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ GhcbSetRegValid (Ghcb, GhcbRcx);
+
+ return VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);
+}
+
+/**
+ Handle a MONITOR event.
+
+ Use the VMGEXIT instruction to handle a MONITOR event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MonitorExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ DecodeModRm (Regs, InstructionData);
+
+ Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA
+ GhcbSetRegValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ GhcbSetRegValid (Ghcb, GhcbRcx);
+ Ghcb->SaveArea.Rdx = Regs->Rdx;
+ GhcbSetRegValid (Ghcb, GhcbRdx);
+
+ return VmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);
+}
+
+/**
+ Handle a WBINVD event.
+
+ Use the VMGEXIT instruction to handle a WBINVD event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+WbinvdExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
+}
+
+/**
+ Handle a RDTSCP event.
+
+ Use the VMGEXIT instruction to handle a RDTSCP event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdtscpExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ DecodeModRm (Regs, InstructionData);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+ !GhcbIsRegValid (Ghcb, GhcbRcx) ||
+ !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rcx = Ghcb->SaveArea.Rcx;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+ return 0;
+}
+
+/**
+ Handle a VMMCALL event.
+
+ Use the VMGEXIT instruction to handle a VMMCALL event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+VmmCallExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ DecodeModRm (Regs, InstructionData);
+
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ GhcbSetRegValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);
+ GhcbSetRegValid (Ghcb, GhcbCpl);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+ Regs->Rax = Ghcb->SaveArea.Rax;
+
+ return 0;
+}
+
+/**
+ Handle an MSR event.
+
+ Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MsrExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo1, Status;
+
+ ExitInfo1 = 0;
+
+ switch (*(InstructionData->OpCodes + 1)) {
+ case 0x30: // WRMSR
+ ExitInfo1 = 1;
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ GhcbSetRegValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rdx = Regs->Rdx;
+ GhcbSetRegValid (Ghcb, GhcbRdx);
+ //
+ // fall through
+ //
+ case 0x32: // RDMSR
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ GhcbSetRegValid (Ghcb, GhcbRcx);
+ break;
+ default:
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (ExitInfo1 == 0) {
+ if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+ !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+ }
+
+ return 0;
+}
+
+/**
+ Build the IOIO event information.
+
+ The IOIO event information identifies the type of IO operation to be performed
+ by the hypervisor. Build this information based on the instruction data.
+
+ @param[in] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+ @return IOIO event information value
+
+**/
+STATIC
+UINT64
+IoioExitInfo (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo;
+
+ ExitInfo = 0;
+
+ switch (*(InstructionData->OpCodes)) {
+ //
+ // INS opcodes
+ //
+ case 0x6C:
+ case 0x6D:
+ ExitInfo |= IOIO_TYPE_INS;
+ ExitInfo |= IOIO_SEG_ES;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ //
+ // OUTS opcodes
+ //
+ case 0x6E:
+ case 0x6F:
+ ExitInfo |= IOIO_TYPE_OUTS;
+ ExitInfo |= IOIO_SEG_DS;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ //
+ // IN immediate opcodes
+ //
+ case 0xE4:
+ case 0xE5:
+ InstructionData->ImmediateSize = 1;
+ InstructionData->End++;
+ ExitInfo |= IOIO_TYPE_IN;
+ ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
+ break;
+
+ //
+ // OUT immediate opcodes
+ //
+ case 0xE6:
+ case 0xE7:
+ InstructionData->ImmediateSize = 1;
+ InstructionData->End++;
+ ExitInfo |= IOIO_TYPE_OUT;
+ ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
+ break;
+
+ //
+ // IN register opcodes
+ //
+ case 0xEC:
+ case 0xED:
+ ExitInfo |= IOIO_TYPE_IN;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ //
+ // OUT register opcodes
+ //
+ case 0xEE:
+ case 0xEF:
+ ExitInfo |= IOIO_TYPE_OUT;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ default:
+ return 0;
+ }
+
+ switch (*(InstructionData->OpCodes)) {
+ //
+ // Single-byte opcodes
+ //
+ case 0x6C:
+ case 0x6E:
+ case 0xE4:
+ case 0xE6:
+ case 0xEC:
+ case 0xEE:
+ ExitInfo |= IOIO_DATA_8;
+ break;
+
+ //
+ // Length determined by instruction parsing
+ //
+ default:
+ ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
+ : IOIO_DATA_32;
+ }
+
+ switch (InstructionData->AddrSize) {
+ case Size16Bits:
+ ExitInfo |= IOIO_ADDR_16;
+ break;
+
+ case Size32Bits:
+ ExitInfo |= IOIO_ADDR_32;
+ break;
+
+ case Size64Bits:
+ ExitInfo |= IOIO_ADDR_64;
+ break;
+
+ default:
+ break;
+ }
+
+ if (InstructionData->RepMode != 0) {
+ ExitInfo |= IOIO_REP;
+ }
+
+ return ExitInfo;
+}
+
+/**
+ Handle an IOIO event.
+
+ Use the VMGEXIT instruction to handle an IOIO event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+IoioExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo1, ExitInfo2, Status;
+ BOOLEAN IsString;
+
+ ExitInfo1 = IoioExitInfo (Regs, InstructionData);
+ if (ExitInfo1 == 0) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;
+ if (IsString) {
+ UINTN IoBytes, VmgExitBytes;
+ UINTN GhcbCount, OpCount;
+
+ Status = 0;
+
+ IoBytes = IOIO_DATA_BYTES (ExitInfo1);
+ GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
+
+ OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;
+ while (OpCount != 0) {
+ ExitInfo2 = MIN (OpCount, GhcbCount);
+ VmgExitBytes = ExitInfo2 * IoBytes;
+
+ if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {
+ CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);
+ Regs->Rsi += VmgExitBytes;
+ }
+
+ Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
+ CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
+ Regs->Rdi += VmgExitBytes;
+ }
+
+ if ((ExitInfo1 & IOIO_REP) != 0) {
+ Regs->Rcx -= ExitInfo2;
+ }
+
+ OpCount -= ExitInfo2;
+ }
+ } else {
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
+ Ghcb->SaveArea.Rax = 0;
+ } else {
+ CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
+ }
+ GhcbSetRegValid (Ghcb, GhcbRax);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
+ if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+ CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
+ }
+ }
+
+ return 0;
+}
+
+/**
+ Handle a INVD event.
+
+ Use the VMGEXIT instruction to handle a INVD event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+InvdExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ return VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);
+}
+
+/**
+ Handle a CPUID event.
+
+ Use the VMGEXIT instruction to handle a CPUID event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+CpuidExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ GhcbSetRegValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ GhcbSetRegValid (Ghcb, GhcbRcx);
+ if (Regs->Rax == CPUID_EXTENDED_STATE) {
+ IA32_CR4 Cr4;
+
+ Cr4.UintN = AsmReadCr4 ();
+ Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;
+ GhcbSetRegValid (Ghcb, GhcbXCr0);
+ }
+
+ Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+ !GhcbIsRegValid (Ghcb, GhcbRbx) ||
+ !GhcbIsRegValid (Ghcb, GhcbRcx) ||
+ !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rbx = Ghcb->SaveArea.Rbx;
+ Regs->Rcx = Ghcb->SaveArea.Rcx;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+ return 0;
+}
+
+/**
+ Handle a RDPMC event.
+
+ Use the VMGEXIT instruction to handle a RDPMC event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdpmcExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ GhcbSetRegValid (Ghcb, GhcbRcx);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+ !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+ return 0;
+}
+
+/**
+ Handle a RDTSC event.
+
+ Use the VMGEXIT instruction to handle a RDTSC event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdtscExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ Status = VmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+ !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+ return 0;
+}
+
+/**
+ Handle a DR7 register write event.
+
+ Use the VMGEXIT instruction to handle a DR7 write event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+Dr7WriteExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ SEV_ES_PER_CPU_DATA *SevEsData;
+ UINT64 *Register;
+ UINT64 Status;
+
+ Ext = &InstructionData->Ext;
+ SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
+
+ DecodeModRm (Regs, InstructionData);
+
+ //
+ // MOV DRn always treats MOD == 3 no matter how encoded
+ //
+ Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+
+ //
+ // Using a value of 0 for ExitInfo1 means RAX holds the value
+ //
+ Ghcb->SaveArea.Rax = *Register;
+ GhcbSetRegValid (Ghcb, GhcbRax);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ SevEsData->Dr7 = *Register;
+ SevEsData->Dr7Cached = TRUE;
+
+ return 0;
+}
+
+/**
+ Handle a DR7 register read event.
+
+ Use the VMGEXIT instruction to handle a DR7 read event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+
+**/
+STATIC
+UINT64
+Dr7ReadExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ SEV_ES_PER_CPU_DATA *SevEsData;
+ UINT64 *Register;
+
+ Ext = &InstructionData->Ext;
+ SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
+
+ DecodeModRm (Regs, InstructionData);
+
+ //
+ // MOV DRn always treats MOD == 3 no matter how encoded
+ //
+ Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+
+ //
+ // If there is a cached valued for DR7, return that. Otherwise return the
+ // DR7 standard reset value of 0x400 (no debug breakpoints set).
+ //
+ *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : 0x400;
+
+ return 0;
+}
+
+/**
+ Handle a #VC exception.
+
+ Performs the necessary processing to handle a #VC exception.
+
+ @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
+ as value to use on error.
+ @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
+
+ @retval EFI_SUCCESS Exception handled
+ @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
+ propagate provided
+ @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
+ propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+VmgExitHandleVc (
+ IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ MSR_SEV_ES_GHCB_REGISTER Msr;
+ EFI_SYSTEM_CONTEXT_X64 *Regs;
+ GHCB *Ghcb;
+ NAE_EXIT NaeExit;
+ SEV_ES_INSTRUCTION_DATA InstructionData;
+ UINT64 ExitCode, Status;
+ EFI_STATUS VcRet;
+
+ VcRet = EFI_SUCCESS;
+
+ Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+ ASSERT (Msr.GhcbInfo.Function == 0);
+ ASSERT (Msr.Ghcb != 0);
+
+ Regs = SystemContext.SystemContextX64;
+ Ghcb = Msr.Ghcb;
+
+ VmgInit (Ghcb);
+
+ ExitCode = Regs->ExceptionData;
+ switch (ExitCode) {
+ case SVM_EXIT_DR7_READ:
+ NaeExit = Dr7ReadExit;
+ break;
+
+ case SVM_EXIT_DR7_WRITE:
+ NaeExit = Dr7WriteExit;
+ break;
+
+ case SVM_EXIT_RDTSC:
+ NaeExit = RdtscExit;
+ break;
+
+ case SVM_EXIT_RDPMC:
+ NaeExit = RdpmcExit;
+ break;
+
+ case SVM_EXIT_CPUID:
+ NaeExit = CpuidExit;
+ break;
+
+ case SVM_EXIT_INVD:
+ NaeExit = InvdExit;
+ break;
+
+ case SVM_EXIT_IOIO_PROT:
+ NaeExit = IoioExit;
+ break;
+
+ case SVM_EXIT_MSR:
+ NaeExit = MsrExit;
+ break;
+
+ case SVM_EXIT_VMMCALL:
+ NaeExit = VmmCallExit;
+ break;
+
+ case SVM_EXIT_RDTSCP:
+ NaeExit = RdtscpExit;
+ break;
+
+ case SVM_EXIT_WBINVD:
+ NaeExit = WbinvdExit;
+ break;
+
+ case SVM_EXIT_MONITOR:
+ NaeExit = MonitorExit;
+ break;
+
+ case SVM_EXIT_MWAIT:
+ NaeExit = MwaitExit;
+ break;
+
+ case SVM_EXIT_NPF:
+ NaeExit = MmioExit;
+ break;
+
+ default:
+ NaeExit = UnsupportedExit;
+ }
+
+ InitInstructionData (&InstructionData, Ghcb, Regs);
+
+ Status = NaeExit (Ghcb, Regs, &InstructionData);
+ if (Status == 0) {
+ Regs->Rip += InstructionLength (&InstructionData);
+ } else {
+ GHCB_EVENT_INJECTION Event;
+
+ Event.Uint64 = Status;
+ if (Event.Elements.ErrorCodeValid != 0) {
+ Regs->ExceptionData = Event.Elements.ErrorCode;
+ } else {
+ Regs->ExceptionData = 0;
+ }
+
+ *ExceptionType = Event.Elements.Vector;
+
+ VcRet = EFI_PROTOCOL_ERROR;
+ }
+
+ VmgDone (Ghcb);
+
+ return VcRet;
+}
diff --git a/roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c b/roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c new file mode 100644 index 000000000..931553c0c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c @@ -0,0 +1,579 @@ +/** @file
+ X86 specific implementation of QemuLoadImageLib library class interface
+ with support for loading mixed mode images and non-EFI stub images
+
+ Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Guid/QemuKernelLoaderFsMedia.h>
+#include <Library/DebugLib.h>
+#include <Library/LoadLinuxLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuLoadImageLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/OvmfLoadedX86LinuxKernel.h>
+
+#pragma pack (1)
+typedef struct {
+ EFI_DEVICE_PATH_PROTOCOL FilePathHeader;
+ CHAR16 FilePath[ARRAY_SIZE (L"kernel")];
+} KERNEL_FILE_DEVPATH;
+
+typedef struct {
+ VENDOR_DEVICE_PATH VenMediaNode;
+ KERNEL_FILE_DEVPATH FileNode;
+ EFI_DEVICE_PATH_PROTOCOL EndNode;
+} KERNEL_VENMEDIA_FILE_DEVPATH;
+#pragma pack ()
+
+STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
+ {
+ {
+ MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
+ { sizeof (VENDOR_DEVICE_PATH) }
+ },
+ QEMU_KERNEL_LOADER_FS_MEDIA_GUID
+ }, {
+ {
+ MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
+ { sizeof (KERNEL_FILE_DEVPATH) }
+ },
+ L"kernel",
+ }, {
+ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
+ }
+};
+
+STATIC
+VOID
+FreeLegacyImage (
+ IN OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage
+ )
+{
+ if (LoadedImage->SetupBuf != NULL) {
+ FreePages (LoadedImage->SetupBuf,
+ EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
+ }
+ if (LoadedImage->KernelBuf != NULL) {
+ FreePages (LoadedImage->KernelBuf,
+ EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize));
+ }
+ if (LoadedImage->CommandLine != NULL) {
+ FreePages (LoadedImage->CommandLine,
+ EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize));
+ }
+ if (LoadedImage->InitrdData != NULL) {
+ FreePages (LoadedImage->InitrdData,
+ EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
+ }
+}
+
+STATIC
+EFI_STATUS
+QemuLoadLegacyImage (
+ OUT EFI_HANDLE *ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ UINTN KernelSize;
+ UINTN SetupSize;
+ OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
+ KernelSize = (UINTN)QemuFwCfgRead32 ();
+
+ QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
+ SetupSize = (UINTN)QemuFwCfgRead32 ();
+
+ if (KernelSize == 0 || SetupSize == 0) {
+ DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ LoadedImage = AllocateZeroPool (sizeof (*LoadedImage));
+ if (LoadedImage == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ LoadedImage->SetupSize = SetupSize;
+ LoadedImage->SetupBuf = LoadLinuxAllocateKernelSetupPages (
+ EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
+ if (LoadedImage->SetupBuf == NULL) {
+ DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel setup!\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeImageDesc;
+ }
+
+ DEBUG ((DEBUG_INFO, "Setup size: 0x%x\n", (UINT32)LoadedImage->SetupSize));
+ DEBUG ((DEBUG_INFO, "Reading kernel setup image ..."));
+ QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
+ QemuFwCfgReadBytes (LoadedImage->SetupSize, LoadedImage->SetupBuf);
+ DEBUG ((DEBUG_INFO, " [done]\n"));
+
+ Status = LoadLinuxCheckKernelSetup (LoadedImage->SetupBuf,
+ LoadedImage->SetupSize);
+ if (EFI_ERROR (Status)) {
+ goto FreeImage;
+ }
+
+ Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf);
+ if (EFI_ERROR (Status)) {
+ goto FreeImage;
+ }
+
+ LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize (
+ LoadedImage->SetupBuf, KernelSize);
+ if (LoadedImage->KernelInitialSize == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto FreeImage;
+ }
+
+ LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages (
+ LoadedImage->SetupBuf,
+ EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)
+ );
+ if (LoadedImage->KernelBuf == NULL) {
+ DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeImage;
+ }
+
+ DEBUG ((DEBUG_INFO, "Kernel size: 0x%x\n", (UINT32)KernelSize));
+ DEBUG ((DEBUG_INFO, "Reading kernel image ..."));
+ QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
+ QemuFwCfgReadBytes (KernelSize, LoadedImage->KernelBuf);
+ DEBUG ((DEBUG_INFO, " [done]\n"));
+
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
+ LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead32 ();
+
+ if (LoadedImage->CommandLineSize > 0) {
+ LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages (
+ EFI_SIZE_TO_PAGES (
+ LoadedImage->CommandLineSize));
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
+ QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);
+ }
+
+ Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf,
+ LoadedImage->CommandLine);
+ if (EFI_ERROR (Status)) {
+ goto FreeImage;
+ }
+
+ QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
+ LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead32 ();
+
+ if (LoadedImage->InitrdSize > 0) {
+ LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages (
+ LoadedImage->SetupBuf,
+ EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
+ DEBUG ((DEBUG_INFO, "Initrd size: 0x%x\n",
+ (UINT32)LoadedImage->InitrdSize));
+ DEBUG ((DEBUG_INFO, "Reading initrd image ..."));
+ QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
+ QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData);
+ DEBUG ((DEBUG_INFO, " [done]\n"));
+ }
+
+ Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData,
+ LoadedImage->InitrdSize);
+ if (EFI_ERROR (Status)) {
+ goto FreeImage;
+ }
+
+ *ImageHandle = NULL;
+ Status = gBS->InstallProtocolInterface (ImageHandle,
+ &gOvmfLoadedX86LinuxKernelProtocolGuid, EFI_NATIVE_INTERFACE,
+ LoadedImage);
+ if (EFI_ERROR (Status)) {
+ goto FreeImage;
+ }
+ return EFI_SUCCESS;
+
+FreeImage:
+ FreeLegacyImage (LoadedImage);
+FreeImageDesc:
+ FreePool (LoadedImage);
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+QemuStartLegacyImage (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
+
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gOvmfLoadedX86LinuxKernelProtocolGuid,
+ (VOID **)&LoadedImage,
+ gImageHandle, // AgentHandle
+ NULL, // ControllerHandle
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return LoadLinux (LoadedImage->KernelBuf, LoadedImage->SetupBuf);
+}
+
+STATIC
+EFI_STATUS
+QemuUnloadLegacyImage (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
+
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gOvmfLoadedX86LinuxKernelProtocolGuid,
+ (VOID **)&LoadedImage,
+ gImageHandle, // AgentHandle
+ NULL, // ControllerHandle
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->UninstallProtocolInterface (ImageHandle,
+ &gOvmfLoadedX86LinuxKernelProtocolGuid, LoadedImage);
+ ASSERT_EFI_ERROR (Status);
+
+ FreeLegacyImage (LoadedImage);
+ FreePool (LoadedImage);
+ return EFI_SUCCESS;
+}
+
+/**
+ Download the kernel, the initial ramdisk, and the kernel command line from
+ QEMU's fw_cfg. The kernel will be instructed via its command line to load
+ the initrd from the same Simple FileSystem where the kernel was loaded from.
+
+ @param[out] ImageHandle The image handle that was allocated for
+ loading the image
+
+ @retval EFI_SUCCESS The image was loaded successfully.
+ @retval EFI_NOT_FOUND Kernel image was not found.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
+
+ @return Error codes from any of the underlying
+ functions.
+**/
+EFI_STATUS
+EFIAPI
+QemuLoadKernelImage (
+ OUT EFI_HANDLE *ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE KernelImageHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
+ UINTN CommandLineSize;
+ CHAR8 *CommandLine;
+ UINTN InitrdSize;
+
+ //
+ // Redundant assignment to work around GCC48/GCC49 limitations.
+ //
+ CommandLine = NULL;
+
+ //
+ // Load the image. This should call back into the QEMU EFI loader file system.
+ //
+ Status = gBS->LoadImage (
+ FALSE, // BootPolicy: exact match required
+ gImageHandle, // ParentImageHandle
+ (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
+ NULL, // SourceBuffer
+ 0, // SourceSize
+ &KernelImageHandle
+ );
+ switch (Status) {
+ case EFI_SUCCESS:
+ break;
+
+ case EFI_NOT_FOUND:
+ //
+ // The image does not exist - no -kernel image was supplied via the
+ // command line so no point in invoking the legacy fallback
+ //
+ return EFI_NOT_FOUND;
+
+ case EFI_SECURITY_VIOLATION:
+ //
+ // Since the image has been loaded, we need to unload it before proceeding
+ // to the EFI_ACCESS_DENIED case below.
+ //
+ gBS->UnloadImage (KernelImageHandle);
+ //
+ // Fall through
+ //
+ case EFI_ACCESS_DENIED:
+ //
+ // We are running with UEFI secure boot enabled, and the image failed to
+ // authenticate. For compatibility reasons, we fall back to the legacy
+ // loader in this case.
+ //
+ // Fall through
+ //
+ case EFI_UNSUPPORTED:
+ //
+ // The image is not natively supported or cross-type supported. Let's try
+ // loading it using the loader that parses the bzImage metadata directly.
+ //
+ Status = QemuLoadLegacyImage (&KernelImageHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__,
+ Status));
+ return Status;
+ }
+ *ImageHandle = KernelImageHandle;
+ return EFI_SUCCESS;
+
+ default:
+ DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+
+ //
+ // Construct the kernel command line.
+ //
+ Status = gBS->OpenProtocol (
+ KernelImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&KernelLoadedImage,
+ gImageHandle, // AgentHandle
+ NULL, // ControllerHandle
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
+ CommandLineSize = (UINTN)QemuFwCfgRead32 ();
+
+ if (CommandLineSize == 0) {
+ KernelLoadedImage->LoadOptionsSize = 0;
+ } else {
+ CommandLine = AllocatePool (CommandLineSize);
+ if (CommandLine == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UnloadImage;
+ }
+
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
+ QemuFwCfgReadBytes (CommandLineSize, CommandLine);
+
+ //
+ // Verify NUL-termination of the command line.
+ //
+ if (CommandLine[CommandLineSize - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
+ __FUNCTION__));
+ Status = EFI_PROTOCOL_ERROR;
+ goto FreeCommandLine;
+ }
+
+ //
+ // Drop the terminating NUL, convert to UTF-16.
+ //
+ KernelLoadedImage->LoadOptionsSize = (UINT32) ((CommandLineSize - 1) * 2);
+ }
+
+ QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
+ InitrdSize = (UINTN)QemuFwCfgRead32 ();
+
+ if (InitrdSize > 0) {
+ //
+ // Append ' initrd=initrd' in UTF-16.
+ //
+ KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
+ }
+
+ if (KernelLoadedImage->LoadOptionsSize == 0) {
+ KernelLoadedImage->LoadOptions = NULL;
+ } else {
+ //
+ // NUL-terminate in UTF-16.
+ //
+ KernelLoadedImage->LoadOptionsSize += 2;
+
+ KernelLoadedImage->LoadOptions = AllocatePool (
+ KernelLoadedImage->LoadOptionsSize);
+ if (KernelLoadedImage->LoadOptions == NULL) {
+ KernelLoadedImage->LoadOptionsSize = 0;
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeCommandLine;
+ }
+
+ UnicodeSPrintAsciiFormat (
+ KernelLoadedImage->LoadOptions,
+ KernelLoadedImage->LoadOptionsSize,
+ "%a%a",
+ (CommandLineSize == 0) ? "" : CommandLine,
+ (InitrdSize == 0) ? "" : " initrd=initrd"
+ );
+ DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
+ (CHAR16 *)KernelLoadedImage->LoadOptions));
+ }
+
+ *ImageHandle = KernelImageHandle;
+ return EFI_SUCCESS;
+
+FreeCommandLine:
+ if (CommandLineSize > 0) {
+ FreePool (CommandLine);
+ }
+UnloadImage:
+ gBS->UnloadImage (KernelImageHandle);
+
+ return Status;
+}
+
+/**
+ Transfer control to a kernel image loaded with QemuLoadKernelImage ()
+
+ @param[in,out] ImageHandle Handle of image to be started. May assume a
+ different value on return if the image was
+ reloaded.
+
+ @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
+ or the image has already been initialized with
+ StartImage
+ @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
+ image should not be started.
+
+ @return Error codes returned by the started image
+**/
+EFI_STATUS
+EFIAPI
+QemuStartKernelImage (
+ IN OUT EFI_HANDLE *ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
+
+ Status = gBS->OpenProtocol (
+ *ImageHandle,
+ &gOvmfLoadedX86LinuxKernelProtocolGuid,
+ (VOID **)&LoadedImage,
+ gImageHandle, // AgentHandle
+ NULL, // ControllerHandle
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return QemuStartLegacyImage (*ImageHandle);
+ }
+
+ Status = gBS->StartImage (
+ *ImageHandle,
+ NULL, // ExitDataSize
+ NULL // ExitData
+ );
+#ifdef MDE_CPU_IA32
+ if (Status == EFI_UNSUPPORTED) {
+ EFI_HANDLE KernelImageHandle;
+
+ //
+ // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while
+ // we are expecting a IA32 one, and the StartImage () boot service is unable
+ // to handle it, either because the image does not have the special .compat
+ // PE/COFF section that Linux specifies for mixed mode capable images, or
+ // because we are running without the support code for that. So load the
+ // image again, using the legacy loader, and unload the normally loaded
+ // image before starting the legacy one.
+ //
+ Status = QemuLoadLegacyImage (&KernelImageHandle);
+ if (EFI_ERROR (Status)) {
+ //
+ // Note: no change to (*ImageHandle), the caller will release it.
+ //
+ return Status;
+ }
+ //
+ // Swap in the legacy-loaded image.
+ //
+ QemuUnloadKernelImage (*ImageHandle);
+ *ImageHandle = KernelImageHandle;
+ return QemuStartLegacyImage (KernelImageHandle);
+ }
+#endif
+ return Status;
+}
+
+/**
+ Unloads an image loaded with QemuLoadKernelImage ().
+
+ @param ImageHandle Handle that identifies the image to be
+ unloaded.
+
+ @retval EFI_SUCCESS The image has been unloaded.
+ @retval EFI_UNSUPPORTED The image has been started, and does not
+ support unload.
+ @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
+
+ @return Exit code from the image's unload function.
+**/
+EFI_STATUS
+EFIAPI
+QemuUnloadKernelImage (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&KernelLoadedImage,
+ gImageHandle, // AgentHandle
+ NULL, // ControllerHandle
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (Status == EFI_UNSUPPORTED) {
+ //
+ // The handle exists but does not have an instance of the standard loaded
+ // image protocol installed on it. Attempt to unload it as a legacy image
+ // instead.
+ //
+ return QemuUnloadLegacyImage (ImageHandle);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // We are unloading a normal, non-legacy loaded image, either on behalf of
+ // an external caller, or called from QemuStartKernelImage() on IA32, while
+ // switching from the normal to the legacy method to load and start a X64
+ // image.
+ //
+ if (KernelLoadedImage->LoadOptions != NULL) {
+ FreePool (KernelLoadedImage->LoadOptions);
+ KernelLoadedImage->LoadOptions = NULL;
+ }
+ KernelLoadedImage->LoadOptionsSize = 0;
+
+ return gBS->UnloadImage (ImageHandle);
+}
diff --git a/roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf b/roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf new file mode 100644 index 000000000..e1615badd --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf @@ -0,0 +1,42 @@ +## @file
+# X86 specific implementation of QemuLoadImageLib library class interface
+# with support for loading mixed mode images and non-EFI stub images
+#
+# Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = X86QemuLoadImageLib
+ FILE_GUID = 2304df80-e21d-4170-9c3c-113c878f7ac0
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = QemuLoadImageLib|DXE_DRIVER
+
+[Sources]
+ X86QemuLoadImageLib.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ MemoryAllocationLib
+ LoadLinuxLib
+ PrintLib
+ QemuFwCfgLib
+ ReportStatusCodeLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gEfiDevicePathProtocolGuid
+ gEfiLoadedImageProtocolGuid
+ gOvmfLoadedX86LinuxKernelProtocolGuid
+
+[Guids]
+ gQemuKernelLoaderFsMediaGuid
diff --git a/roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.c b/roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.c new file mode 100644 index 000000000..895dfc02f --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.c @@ -0,0 +1,295 @@ +/** @file
+ Xen console SerialPortLib instance
+
+ Copyright (c) 2015, Linaro Ltd. All rights reserved.<BR>
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+
+#include <Library/BaseLib.h>
+#include <Library/SerialPortLib.h>
+#include <Library/XenHypercallLib.h>
+
+#include <IndustryStandard/Xen/io/console.h>
+#include <IndustryStandard/Xen/hvm/params.h>
+#include <IndustryStandard/Xen/event_channel.h>
+
+//
+// We can't use DebugLib due to a constructor dependency cycle between DebugLib
+// and ourselves.
+//
+#define ASSERT(Expression) \
+ do { \
+ if (!(Expression)) { \
+ CpuDeadLoop (); \
+ } \
+ } while (FALSE)
+
+//
+// The code below expects these global variables to be mutable, even in the case
+// that we have been incorporated into SEC or PEIM phase modules (which is
+// allowed by our INF description). While this is a dangerous assumption to make
+// in general, it is actually fine for the Xen domU (guest) environment that
+// this module is intended for, as UEFI always executes from DRAM in that case.
+//
+STATIC evtchn_send_t mXenConsoleEventChain;
+STATIC struct xencons_interface *mXenConsoleInterface;
+
+/**
+ Initialize the serial device hardware.
+
+ If no initialization is required, then return RETURN_SUCCESS.
+ If the serial device was successfully initialized, then return RETURN_SUCCESS.
+ If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.
+
+ @retval RETURN_SUCCESS The serial device was initialized.
+ @retval RETURN_DEVICE_ERROR The serial device could not be initialized.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortInitialize (
+ VOID
+ )
+{
+ if (! XenHypercallIsAvailable ()) {
+ return RETURN_DEVICE_ERROR;
+ }
+
+ if (!mXenConsoleInterface) {
+ mXenConsoleEventChain.port = (UINT32)XenHypercallHvmGetParam (HVM_PARAM_CONSOLE_EVTCHN);
+ mXenConsoleInterface = (struct xencons_interface *)(UINTN)
+ (XenHypercallHvmGetParam (HVM_PARAM_CONSOLE_PFN) << EFI_PAGE_SHIFT);
+
+ //
+ // No point in ASSERT'ing here as we won't be seeing the output
+ //
+ }
+ return RETURN_SUCCESS;
+}
+
+/**
+ Write data from buffer to serial device.
+
+ Writes NumberOfBytes data bytes from Buffer to the serial device.
+ The number of bytes actually written to the serial device is returned.
+ If the return value is less than NumberOfBytes, then the write operation failed.
+ If Buffer is NULL, then ASSERT().
+ If NumberOfBytes is zero, then return 0.
+
+ @param Buffer Pointer to the data buffer to be written.
+ @param NumberOfBytes Number of bytes to written to the serial device.
+
+ @retval 0 NumberOfBytes is 0.
+ @retval >0 The number of bytes written to the serial device.
+ If this value is less than NumberOfBytes, then the write operation failed.
+
+**/
+UINTN
+EFIAPI
+SerialPortWrite (
+ IN UINT8 *Buffer,
+ IN UINTN NumberOfBytes
+ )
+{
+ XENCONS_RING_IDX Consumer, Producer;
+ UINTN Sent;
+
+ ASSERT (Buffer != NULL);
+
+ if (NumberOfBytes == 0) {
+ return 0;
+ }
+
+ if (!mXenConsoleInterface) {
+ return 0;
+ }
+
+ Sent = 0;
+ do {
+ Consumer = mXenConsoleInterface->out_cons;
+ Producer = mXenConsoleInterface->out_prod;
+
+ MemoryFence ();
+
+ while (Sent < NumberOfBytes && ((Producer - Consumer) < sizeof (mXenConsoleInterface->out)))
+ mXenConsoleInterface->out[MASK_XENCONS_IDX(Producer++, mXenConsoleInterface->out)] = Buffer[Sent++];
+
+ MemoryFence ();
+
+ mXenConsoleInterface->out_prod = Producer;
+
+ XenHypercallEventChannelOp (EVTCHNOP_send, &mXenConsoleEventChain);
+
+ } while (Sent < NumberOfBytes);
+
+ return Sent;
+}
+
+/**
+ Read data from serial device and save the datas in buffer.
+
+ Reads NumberOfBytes data bytes from a serial device into the buffer
+ specified by Buffer. The number of bytes actually read is returned.
+ If Buffer is NULL, then ASSERT().
+ If NumberOfBytes is zero, then return 0.
+
+ @param Buffer Pointer to the data buffer to store the data read from the serial device.
+ @param NumberOfBytes Number of bytes which will be read.
+
+ @retval 0 Read data failed, no data is to be read.
+ @retval >0 Actual number of bytes read from serial device.
+
+**/
+UINTN
+EFIAPI
+SerialPortRead (
+ OUT UINT8 *Buffer,
+ IN UINTN NumberOfBytes
+)
+{
+ XENCONS_RING_IDX Consumer, Producer;
+ UINTN Received;
+
+ ASSERT (Buffer != NULL);
+
+ if (NumberOfBytes == 0) {
+ return 0;
+ }
+
+ if (!mXenConsoleInterface) {
+ return 0;
+ }
+
+ Consumer = mXenConsoleInterface->in_cons;
+ Producer = mXenConsoleInterface->in_prod;
+
+ MemoryFence ();
+
+ Received = 0;
+ while (Received < NumberOfBytes && Consumer < Producer)
+ Buffer[Received++] = mXenConsoleInterface->in[MASK_XENCONS_IDX(Consumer++, mXenConsoleInterface->in)];
+
+ MemoryFence ();
+
+ mXenConsoleInterface->in_cons = Consumer;
+
+ XenHypercallEventChannelOp (EVTCHNOP_send, &mXenConsoleEventChain);
+
+ return Received;
+}
+
+/**
+ Polls a serial device to see if there is any data waiting to be read.
+
+ @retval TRUE Data is waiting to be read from the serial device.
+ @retval FALSE There is no data waiting to be read from the serial device.
+
+**/
+BOOLEAN
+EFIAPI
+SerialPortPoll (
+ VOID
+ )
+{
+ return mXenConsoleInterface &&
+ mXenConsoleInterface->in_cons != mXenConsoleInterface->in_prod;
+}
+
+/**
+ Sets the control bits on a serial device.
+
+ @param Control Sets the bits of Control that are settable.
+
+ @retval RETURN_SUCCESS The new control bits were set on the serial device.
+ @retval RETURN_UNSUPPORTED The serial device does not support this operation.
+ @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetControl (
+ IN UINT32 Control
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
+
+/**
+ Retrieve the status of the control bits on a serial device.
+
+ @param Control A pointer to return the current control signals from the serial device.
+
+ @retval RETURN_SUCCESS The control bits were read from the serial device.
+ @retval RETURN_UNSUPPORTED The serial device does not support this operation.
+ @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortGetControl (
+ OUT UINT32 *Control
+ )
+{
+ if (!mXenConsoleInterface) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ *Control = 0;
+ if (!SerialPortPoll ()) {
+ *Control = EFI_SERIAL_INPUT_BUFFER_EMPTY;
+ }
+ return RETURN_SUCCESS;
+}
+
+/**
+ Sets the baud rate, receive FIFO depth, transmit/receive time out, parity,
+ data bits, and stop bits on a serial device.
+
+ @param BaudRate The requested baud rate. A BaudRate value of 0 will use the
+ device's default interface speed.
+ On output, the value actually set.
+ @param ReceiveFifoDepth The requested depth of the FIFO on the receive side of the
+ serial interface. A ReceiveFifoDepth value of 0 will use
+ the device's default FIFO depth.
+ On output, the value actually set.
+ @param Timeout The requested time out for a single character in microseconds.
+ This timeout applies to both the transmit and receive side of the
+ interface. A Timeout value of 0 will use the device's default time
+ out value.
+ On output, the value actually set.
+ @param Parity The type of parity to use on this serial device. A Parity value of
+ DefaultParity will use the device's default parity value.
+ On output, the value actually set.
+ @param DataBits The number of data bits to use on the serial device. A DataBits
+ value of 0 will use the device's default data bit setting.
+ On output, the value actually set.
+ @param StopBits The number of stop bits to use on this serial device. A StopBits
+ value of DefaultStopBits will use the device's default number of
+ stop bits.
+ On output, the value actually set.
+
+ @retval RETURN_SUCCESS The new attributes were set on the serial device.
+ @retval RETURN_UNSUPPORTED The serial device does not support this operation.
+ @retval RETURN_INVALID_PARAMETER One or more of the attributes has an unsupported value.
+ @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetAttributes (
+ IN OUT UINT64 *BaudRate,
+ IN OUT UINT32 *ReceiveFifoDepth,
+ IN OUT UINT32 *Timeout,
+ IN OUT EFI_PARITY_TYPE *Parity,
+ IN OUT UINT8 *DataBits,
+ IN OUT EFI_STOP_BITS_TYPE *StopBits
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
+
diff --git a/roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.inf b/roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.inf new file mode 100644 index 000000000..51c66266c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.inf @@ -0,0 +1,29 @@ +#/** @file
+#
+# Component description file for XenConsoleSerialPortLib module
+#
+# Copyright (c) 2015, Linaro Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = XenConsoleSerialPortLib
+ FILE_GUID = 401406DD-BCAC-4B91-9F4E-72A7FEBE4762
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = SerialPortLib
+ CONSTRUCTOR = SerialPortInitialize
+
+[Sources.common]
+ XenConsoleSerialPortLib.c
+
+[LibraryClasses]
+ BaseLib
+ XenHypercallLib
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/Aarch64/Hypercall.S b/roms/edk2/OvmfPkg/Library/XenHypercallLib/Aarch64/Hypercall.S new file mode 100644 index 000000000..1533d48d0 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/Aarch64/Hypercall.S @@ -0,0 +1,20 @@ +
+/** @file
+ AArch64 implementation of XenHypercall2
+
+ Copyright (C) 2014, Linaro Ltd.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/Xen/arch-arm/xen.h>
+
+ .text
+ .global ASM_PFX(XenHypercall2)
+ASM_PFX(XenHypercall2):
+ mov x16, x0
+ mov x0, x1
+ mov x1, x2
+ hvc #XEN_HYPERCALL_TAG
+ ret
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/Arm/Hypercall.S b/roms/edk2/OvmfPkg/Library/XenHypercallLib/Arm/Hypercall.S new file mode 100644 index 000000000..06cf1e68a --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/Arm/Hypercall.S @@ -0,0 +1,22 @@ +/** @file
+ ARM (AArch32) implementation of XenHypercall2
+
+ Copyright (C) 2014, Linaro Ltd.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/Xen/arch-arm/xen.h>
+
+ .text
+ .arch_extension virt
+
+GCC_ASM_EXPORT(XenHypercall2)
+
+ASM_PFX(XenHypercall2):
+ mov r12, r0
+ mov r0, r1
+ mov r1, r2
+ hvc #XEN_HYPERCALL_TAG
+ bx lr
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/ArmXenHypercall.c b/roms/edk2/OvmfPkg/Library/XenHypercallLib/ArmXenHypercall.c new file mode 100644 index 000000000..2cacc67ba --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/ArmXenHypercall.c @@ -0,0 +1,38 @@ +/** @file
+ Xen Hypercall Library implementation for ARM architecture
+
+ Copyright (C) 2015, Red Hat, Inc.
+ Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Base.h>
+
+/**
+ Check if the Xen Hypercall library is able to make calls to the Xen
+ hypervisor.
+
+ Client code should call further functions in this library only if, and after,
+ this function returns TRUE.
+
+ @retval TRUE Hypercalls are available.
+ @retval FALSE Hypercalls are not available.
+**/
+BOOLEAN
+EFIAPI
+XenHypercallIsAvailable (
+ VOID
+ )
+{
+ return TRUE;
+}
+
+RETURN_STATUS
+EFIAPI
+XenHypercallLibInit (
+ VOID
+ )
+{
+ return RETURN_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/Ia32/hypercall.nasm b/roms/edk2/OvmfPkg/Library/XenHypercallLib/Ia32/hypercall.nasm new file mode 100644 index 000000000..e0fa71bb5 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/Ia32/hypercall.nasm @@ -0,0 +1,25 @@ +SECTION .text
+
+; INTN
+; EFIAPI
+; __XenHypercall2 (
+; IN VOID *HypercallAddr,
+; IN OUT INTN Arg1,
+; IN OUT INTN Arg2
+; );
+global ASM_PFX(__XenHypercall2)
+ASM_PFX(__XenHypercall2):
+ ; Save only ebx, ecx is supposed to be a scratch register and needs to be
+ ; saved by the caller
+ push ebx
+ ; Copy HypercallAddr to eax
+ mov eax, [esp + 8]
+ ; Copy Arg1 to the register expected by Xen
+ mov ebx, [esp + 12]
+ ; Copy Arg2 to the register expected by Xen
+ mov ecx, [esp + 16]
+ ; Call HypercallAddr
+ call eax
+ pop ebx
+ ret
+
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/X64/hypercall.nasm b/roms/edk2/OvmfPkg/Library/XenHypercallLib/X64/hypercall.nasm new file mode 100644 index 000000000..5e6a0c05c --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/X64/hypercall.nasm @@ -0,0 +1,26 @@ +DEFAULT REL
+SECTION .text
+
+; INTN
+; EFIAPI
+; __XenHypercall2 (
+; IN VOID *HypercallAddr,
+; IN OUT INTN Arg1,
+; IN OUT INTN Arg2
+; );
+global ASM_PFX(__XenHypercall2)
+ASM_PFX(__XenHypercall2):
+ push rdi
+ push rsi
+ ; Copy HypercallAddr to rax
+ mov rax, rcx
+ ; Copy Arg1 to the register expected by Xen
+ mov rdi, rdx
+ ; Copy Arg2 to the register expected by Xen
+ mov rsi, r8
+ ; Call HypercallAddr
+ call rax
+ pop rsi
+ pop rdi
+ ret
+
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/X86XenHypercall.c b/roms/edk2/OvmfPkg/Library/XenHypercallLib/X86XenHypercall.c new file mode 100644 index 000000000..f779e4647 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/X86XenHypercall.c @@ -0,0 +1,90 @@ +/** @file
+ Xen Hypercall Library implementation for Intel architecture
+
+Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Library/HobLib.h>
+#include <Library/DebugLib.h>
+#include <Guid/XenInfo.h>
+
+STATIC VOID *HyperPage;
+
+/**
+ Check if the Xen Hypercall library is able to make calls to the Xen
+ hypervisor.
+
+ Client code should call further functions in this library only if, and after,
+ this function returns TRUE.
+
+ @retval TRUE Hypercalls are available.
+ @retval FALSE Hypercalls are not available.
+**/
+BOOLEAN
+EFIAPI
+XenHypercallIsAvailable (
+ VOID
+ )
+{
+ return HyperPage != NULL;
+}
+
+//
+// Interface exposed by the ASM implementation of the core hypercall
+//
+INTN
+EFIAPI
+__XenHypercall2 (
+ IN VOID *HypercallAddr,
+ IN OUT INTN Arg1,
+ IN OUT INTN Arg2
+ );
+
+/**
+ Library constructor: retrieves the Hyperpage address
+ from the gEfiXenInfoGuid HOB
+**/
+
+RETURN_STATUS
+EFIAPI
+XenHypercallLibInit (
+ VOID
+ )
+{
+ EFI_HOB_GUID_TYPE *GuidHob;
+ EFI_XEN_INFO *XenInfo;
+
+ GuidHob = GetFirstGuidHob (&gEfiXenInfoGuid);
+ if (GuidHob == NULL) {
+ return RETURN_NOT_FOUND;
+ }
+ XenInfo = (EFI_XEN_INFO *) GET_GUID_HOB_DATA (GuidHob);
+ HyperPage = XenInfo->HyperPages;
+ return RETURN_SUCCESS;
+}
+
+/**
+ This function will put the two arguments in the right place (registers) and
+ invoke the hypercall identified by HypercallID.
+
+ @param HypercallID The symbolic ID of the hypercall to be invoked
+ @param Arg1 First argument.
+ @param Arg2 Second argument.
+
+ @return Return 0 if success otherwise it return an errno.
+**/
+INTN
+EFIAPI
+XenHypercall2 (
+ IN UINTN HypercallID,
+ IN OUT INTN Arg1,
+ IN OUT INTN Arg2
+ )
+{
+ ASSERT (HyperPage != NULL);
+
+ return __XenHypercall2 ((UINT8*)HyperPage + HypercallID * 32, Arg1, Arg2);
+}
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercall.c b/roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercall.c new file mode 100644 index 000000000..265aa766d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercall.c @@ -0,0 +1,76 @@ +/** @file
+ Functions to make Xen hypercalls.
+
+ Copyright (C) 2014, Citrix Ltd.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <IndustryStandard/Xen/hvm/params.h>
+#include <IndustryStandard/Xen/memory.h>
+
+#include <Library/DebugLib.h>
+#include <Library/XenHypercallLib.h>
+
+RETURN_STATUS
+EFIAPI
+XenHypercallLibConstruct (
+ VOID
+ )
+{
+ XenHypercallLibInit ();
+ //
+ // We don't fail library construction, since that has catastrophic
+ // consequences for client modules (whereas those modules may easily be
+ // running on a non-Xen platform). Instead, XenHypercallIsAvailable()
+ // will return FALSE.
+ //
+ return RETURN_SUCCESS;
+}
+
+UINT64
+EFIAPI
+XenHypercallHvmGetParam (
+ IN UINT32 Index
+ )
+{
+ xen_hvm_param_t Parameter;
+ INTN Error;
+
+ Parameter.domid = DOMID_SELF;
+ Parameter.index = Index;
+ Error = XenHypercall2 (__HYPERVISOR_hvm_op,
+ HVMOP_get_param, (INTN) &Parameter);
+ if (Error != 0) {
+ DEBUG ((DEBUG_ERROR,
+ "XenHypercall: Error %Ld trying to get HVM parameter %d\n",
+ (INT64)Error, Index));
+ return 0;
+ }
+ return Parameter.value;
+}
+
+INTN
+EFIAPI
+XenHypercallMemoryOp (
+ IN UINTN Operation,
+ IN OUT VOID *Arguments
+ )
+{
+ return XenHypercall2 (__HYPERVISOR_memory_op,
+ Operation, (INTN) Arguments);
+}
+
+INTN
+EFIAPI
+XenHypercallEventChannelOp (
+ IN INTN Operation,
+ IN OUT VOID *Arguments
+ )
+{
+ return XenHypercall2 (__HYPERVISOR_event_channel_op,
+ Operation, (INTN) Arguments);
+}
diff --git a/roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf b/roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf new file mode 100644 index 000000000..21ce5b443 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf @@ -0,0 +1,63 @@ +## @file
+# Xen Hypercall abstraction lib for Intel and ARM architectures
+#
+# Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = XenHypercallLib
+ FILE_GUID = B5EE9A32-CA5A-49A8-82E3-ADA4CCB77C7C
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ CONSTRUCTOR = XenHypercallLibConstruct
+
+[Defines.IA32, Defines.X64]
+ LIBRARY_CLASS = XenHypercallLib|PEIM DXE_DRIVER UEFI_DRIVER
+
+[Defines.ARM, Defines.AARCH64]
+ LIBRARY_CLASS = XenHypercallLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64
+#
+
+[Sources.IA32, Sources.X64]
+ X86XenHypercall.c
+
+[Sources.IA32]
+ Ia32/hypercall.nasm
+
+[Sources.X64]
+ X64/hypercall.nasm
+
+[Sources.ARM, Sources.AARCH64]
+ ArmXenHypercall.c
+
+[Sources.ARM]
+ Arm/Hypercall.S
+
+[Sources.AARCH64]
+ Aarch64/Hypercall.S
+
+[Sources]
+ XenHypercall.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses.IA32, LibraryClasses.X64]
+ BaseLib
+ HobLib
+ DebugLib
+
+[Guids.IA32, Guids.X64]
+ gEfiXenInfoGuid
+
+[BuildOptions.ARM]
+ RVCT:*_*_ARM_PLATFORM_FLAGS == --cpu Cortex-A15
diff --git a/roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.c b/roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.c new file mode 100644 index 000000000..ae4ea0309 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.c @@ -0,0 +1,160 @@ +/** @file
+* Manage XenBus device path and I/O handles
+*
+* Copyright (c) 2015, Linaro Ltd. All rights reserved.<BR>
+*
+* SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/XenIoMmioLib.h>
+
+#include <Protocol/XenIo.h>
+#include <Guid/XenBusRootDevice.h>
+
+#pragma pack (1)
+typedef struct {
+ VENDOR_DEVICE_PATH Vendor;
+ EFI_PHYSICAL_ADDRESS GrantTableAddress;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} XENBUS_ROOT_DEVICE_PATH;
+#pragma pack ()
+
+STATIC CONST XENBUS_ROOT_DEVICE_PATH mXenBusRootDevicePathTemplate = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ { sizeof (VENDOR_DEVICE_PATH) + sizeof (EFI_PHYSICAL_ADDRESS), 0 }
+ },
+ XENBUS_ROOT_DEVICE_GUID,
+ },
+ 0,
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
+ }
+};
+
+/**
+
+ Install the XENBUS_ROOT_DEVICE_PATH and XENIO_PROTOCOL protocols on
+ the handle pointed to by @Handle, or on a new handle if it points to
+ NULL
+
+ @param Handle Pointer to the handle to install the protocols
+ on, may point to a NULL handle.
+
+ @param GrantTableAddress The address of the Xen grant table
+
+ @retval EFI_SUCCESS Protocols were installed successfully
+
+ @retval EFI_OUT_OF_RESOURCES The function failed to allocate memory required
+ by the XenIo MMIO and device path protocols
+
+ @return Status code returned by the boot service
+ InstallMultipleProtocolInterfaces ()
+
+**/
+EFI_STATUS
+XenIoMmioInstall (
+ IN OUT EFI_HANDLE *Handle,
+ IN EFI_PHYSICAL_ADDRESS GrantTableAddress
+ )
+{
+ EFI_STATUS Status;
+ XENIO_PROTOCOL *XenIo;
+ XENBUS_ROOT_DEVICE_PATH *XenBusDevicePath;
+ EFI_HANDLE OutHandle;
+
+ ASSERT (Handle != NULL);
+
+ OutHandle = *Handle;
+
+ XenIo = AllocateZeroPool (sizeof *XenIo);
+ if (!XenIo) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ XenIo->GrantTableAddress = GrantTableAddress;
+
+ XenBusDevicePath = AllocateCopyPool (sizeof *XenBusDevicePath,
+ &mXenBusRootDevicePathTemplate);
+ if (!XenBusDevicePath) {
+ DEBUG ((DEBUG_ERROR, "%a: Out of memory\n", __FUNCTION__));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeXenIo;
+ }
+ XenBusDevicePath->GrantTableAddress = GrantTableAddress;
+
+ Status = gBS->InstallMultipleProtocolInterfaces (&OutHandle,
+ &gEfiDevicePathProtocolGuid, XenBusDevicePath,
+ &gXenIoProtocolGuid, XenIo,
+ NULL);
+ if (!EFI_ERROR (Status)) {
+ *Handle = OutHandle;
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_ERROR, "%a: Failed to install the EFI_DEVICE_PATH and "
+ "XENIO_PROTOCOL protocols on handle %p (Status == %r)\n",
+ __FUNCTION__, OutHandle, Status));
+
+ FreePool (XenBusDevicePath);
+
+FreeXenIo:
+ FreePool (XenIo);
+ return Status;
+}
+
+/**
+
+ Uninstall the XENBUS_ROOT_DEVICE_PATH and XENIO_PROTOCOL protocols
+
+ @param Handle Handle onto which the protocols have been installed
+ earlier by XenIoMmioInstall ()
+
+ @retval EFI_SUCCESS Protocols were uninstalled successfully
+
+ @return Status code returned by the boot service
+ UninstallMultipleProtocolInterfaces ()
+
+**/
+EFI_STATUS
+XenIoMmioUninstall (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ VOID *XenIo;
+ VOID *XenBusDevicePath;
+
+ XenBusDevicePath = NULL;
+ gBS->OpenProtocol (Handle, &gEfiDevicePathProtocolGuid, &XenBusDevicePath,
+ NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ XenIo = NULL;
+ gBS->OpenProtocol (Handle, &gXenIoProtocolGuid, &XenIo,
+ NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (Handle,
+ &gEfiDevicePathProtocolGuid, XenBusDevicePath,
+ &gXenIoProtocolGuid, XenIo,
+ NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FreePool (XenBusDevicePath);
+ FreePool (XenIo);
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.inf b/roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.inf new file mode 100644 index 000000000..7a0395fac --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.inf @@ -0,0 +1,35 @@ +## @file
+# Manage XenBus device path and I/O handles
+#
+# Copyright (c) 2015, Linaro Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = XenIoMmioLib
+ FILE_GUID = de9bdc19-8434-47bb-be3c-7f28f2101fd0
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = XenIoMmioLib
+
+[Sources]
+ XenIoMmioLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ MemoryAllocationLib
+
+[Guids]
+ gXenBusRootDeviceGuid
+
+[Protocols]
+ gEfiDevicePathProtocolGuid
+ gXenIoProtocolGuid
diff --git a/roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.c b/roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.c new file mode 100644 index 000000000..8f20ae2d4 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.c @@ -0,0 +1,81 @@ +/** @file
+ Get information about Xen
+
+ This library simply allow to find out if OVMF is running under Xen and
+ allow to get more information when it is the case.
+
+ Copyright (c) 2019, Citrix Systems, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Library/HobLib.h>
+#include <Library/XenPlatformLib.h>
+
+/**
+ This function return a pointer to the XenInfo HOB.
+
+ @return XenInfo pointer or NULL if not available
+**/
+EFI_XEN_INFO *
+EFIAPI
+XenGetInfoHOB (
+ VOID
+ )
+{
+ EFI_HOB_GUID_TYPE *GuidHob;
+ STATIC BOOLEAN Cached = FALSE;
+ STATIC EFI_XEN_INFO *XenInfo;
+
+ //
+ // Return the cached result for the benefit of XenDetected that can be
+ // called many times.
+ //
+ if (Cached) {
+ return XenInfo;
+ }
+
+ GuidHob = GetFirstGuidHob (&gEfiXenInfoGuid);
+ if (GuidHob == NULL) {
+ XenInfo = NULL;
+ } else {
+ XenInfo = (EFI_XEN_INFO *) GET_GUID_HOB_DATA (GuidHob);
+ }
+ Cached = TRUE;
+ return XenInfo;
+}
+
+/**
+ This function detects if OVMF is running on Xen.
+
+ @retval TRUE OVMF is running on Xen
+ @retval FALSE Xen has not been detected
+**/
+BOOLEAN
+EFIAPI
+XenDetected (
+ VOID
+ )
+{
+ return (XenGetInfoHOB () != NULL);
+}
+
+/**
+ This function detect if OVMF have started via the PVH entry point.
+
+ @retval TRUE PVH entry point as been used
+ @retval FALSE OVMF have started via the HVM route
+**/
+BOOLEAN
+EFIAPI
+XenPvhDetected (
+ VOID
+ )
+{
+ EFI_XEN_INFO *XenInfo;
+
+ XenInfo = XenGetInfoHOB ();
+ return (XenInfo != NULL && XenInfo->RsdpPvh != NULL);
+}
diff --git a/roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.inf b/roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.inf new file mode 100644 index 000000000..32adb246d --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.inf @@ -0,0 +1,33 @@ +## @file
+# Get information about Xen
+#
+# This library simply allow to find out if OVMF is running under Xen and
+# allow to get more information when it is the case.
+#
+# Copyright (c) 2019, Citrix Systems, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = XenPlatformLib
+ FILE_GUID = DB54DBB7-8142-4EE5-9364-78C824B582EB
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = XenPlatformLib
+
+[Sources]
+ XenPlatformLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ HobLib
+
+[Guids]
+ gEfiXenInfoGuid
diff --git a/roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.c b/roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.c new file mode 100644 index 000000000..e113bc89b --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.c @@ -0,0 +1,190 @@ +/** @file
+ Implement EFI RealTimeClock runtime services via Xen shared info page
+
+ Copyright (c) 2015, Linaro Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <PiDxe.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+
+/**
+ Converts Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC) to EFI_TIME
+ **/
+STATIC
+VOID
+EpochToEfiTime (
+ IN UINTN EpochSeconds,
+ OUT EFI_TIME *Time
+ )
+{
+ UINTN a;
+ UINTN b;
+ UINTN c;
+ UINTN d;
+ UINTN g;
+ UINTN j;
+ UINTN m;
+ UINTN y;
+ UINTN da;
+ UINTN db;
+ UINTN dc;
+ UINTN dg;
+ UINTN hh;
+ UINTN mm;
+ UINTN ss;
+ UINTN J;
+
+ J = (EpochSeconds / 86400) + 2440588;
+ j = J + 32044;
+ g = j / 146097;
+ dg = j % 146097;
+ c = (((dg / 36524) + 1) * 3) / 4;
+ dc = dg - (c * 36524);
+ b = dc / 1461;
+ db = dc % 1461;
+ a = (((db / 365) + 1) * 3) / 4;
+ da = db - (a * 365);
+ y = (g * 400) + (c * 100) + (b * 4) + a;
+ m = (((da * 5) + 308) / 153) - 2;
+ d = da - (((m + 4) * 153) / 5) + 122;
+
+ Time->Year = y - 4800 + ((m + 2) / 12);
+ Time->Month = ((m + 2) % 12) + 1;
+ Time->Day = d + 1;
+
+ ss = EpochSeconds % 60;
+ a = (EpochSeconds - ss) / 60;
+ mm = a % 60;
+ b = (a - mm) / 60;
+ hh = b % 24;
+
+ Time->Hour = hh;
+ Time->Minute = mm;
+ Time->Second = ss;
+ Time->Nanosecond = 0;
+
+}
+
+/**
+ Returns the current time and date information, and the time-keeping capabilities
+ of the hardware platform.
+
+ @param Time A pointer to storage to receive a snapshot of the current time.
+ @param Capabilities An optional pointer to a buffer to receive the real time clock
+ device's capabilities.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER Time is NULL.
+ @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibGetTime (
+ OUT EFI_TIME *Time,
+ OUT EFI_TIME_CAPABILITIES *Capabilities
+ )
+{
+ ASSERT (Time != NULL);
+
+ //
+ // For now, there is nothing that we can do besides returning a bogus time,
+ // as Xen's timekeeping uses a shared info page which cannot be shared
+ // between UEFI and the OS
+ //
+ EpochToEfiTime(1421770011, Time);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Sets the current local time and date information.
+
+ @param Time A pointer to the current time.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER A time field is out of range.
+ @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibSetTime (
+ IN EFI_TIME *Time
+ )
+{
+ return EFI_DEVICE_ERROR;
+}
+
+
+/**
+ Returns the current wakeup alarm clock setting.
+
+ @param Enabled Indicates if the alarm is currently enabled or disabled.
+ @param Pending Indicates if the alarm signal is pending and requires acknowledgement.
+ @param Time The current alarm setting.
+
+ @retval EFI_SUCCESS The alarm settings were returned.
+ @retval EFI_INVALID_PARAMETER Any parameter is NULL.
+ @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
+ @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
+
+**/
+EFI_STATUS
+EFIAPI
+LibGetWakeupTime (
+ OUT BOOLEAN *Enabled,
+ OUT BOOLEAN *Pending,
+ OUT EFI_TIME *Time
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Sets the system wakeup alarm clock time.
+
+ @param Enabled Enable or disable the wakeup alarm.
+ @param Time If Enable is TRUE, the time to set the wakeup alarm for.
+
+ @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If
+ Enable is FALSE, then the wakeup alarm was disabled.
+ @retval EFI_INVALID_PARAMETER A time field is out of range.
+ @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
+ @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
+
+**/
+EFI_STATUS
+EFIAPI
+LibSetWakeupTime (
+ IN BOOLEAN Enabled,
+ OUT EFI_TIME *Time
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This can be the entry point to an application
+ written to this specification, an EFI boot service driver, or an EFI runtime driver.
+
+ @param ImageHandle Handle that identifies the loaded image.
+ @param SystemTable System Table for this image.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+LibRtcInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.inf b/roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.inf new file mode 100644 index 000000000..f4b269b21 --- /dev/null +++ b/roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.inf @@ -0,0 +1,33 @@ +#/** @file
+#
+# Copyright (c) 2015, Linaro Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = XenRealTimeClockLib
+ FILE_GUID = EC2557E8-7005-430B-9F6F-9BA109698248
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = RealTimeClockLib|DXE_CORE DXE_DRIVER UEFI_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION
+
+[Sources.common]
+ XenRealTimeClockLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+
+[LibraryClasses]
+ UefiLib
+ DebugLib
+ DxeServicesTableLib
+ UefiRuntimeLib
+
+[Guids]
+ gEfiEventVirtualAddressChangeGuid
|