From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.c | 210 ++ .../OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.h | 23 + .../Library/AcpiTimerLib/BaseAcpiTimerLib.c | 103 + .../Library/AcpiTimerLib/BaseAcpiTimerLib.inf | 32 + .../Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.c | 32 + .../Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.inf | 30 + .../Library/AcpiTimerLib/BaseRomAcpiTimerLib.c | 121 ++ .../Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf | 31 + .../OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.c | 83 + .../Library/AcpiTimerLib/DxeAcpiTimerLib.inf | 35 + .../BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf | 51 + .../BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c | 84 + .../MemEncryptSevLibInternal.c | 155 ++ .../BaseMemEncryptSevLib/X64/MemEncryptSevLib.c | 90 + .../BaseMemEncryptSevLib/X64/VirtualMemory.c | 897 ++++++++ .../BaseMemEncryptSevLib/X64/VirtualMemory.h | 237 +++ .../OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c | 1010 +++++++++ .../OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h | 54 + .../Library/BasePciCapLib/BasePciCapLib.inf | 32 + .../BasePciCapPciSegmentLib.c | 220 ++ .../BasePciCapPciSegmentLib.h | 41 + .../BasePciCapPciSegmentLib.inf | 29 + .../OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.c | 426 ++++ .../Library/BhyveFwCtlLib/BhyveFwCtlLib.inf | 41 + .../DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf | 40 + .../OvmfPkg/Library/DxePciLibI440FxQ35/PciLib.c | 1223 +++++++++++ .../Library/EmuVariableFvbLib/EmuVariableFvbLib.c | 97 + .../EmuVariableFvbLib/EmuVariableFvbLib.inf | 43 + .../GenericQemuLoadImageLib.c | 277 +++ .../GenericQemuLoadImageLib.inf | 38 + .../Library/LoadLinuxLib/Ia32/JumpToKernel.nasm | 43 + roms/edk2/OvmfPkg/Library/LoadLinuxLib/Linux.c | 664 ++++++ roms/edk2/OvmfPkg/Library/LoadLinuxLib/LinuxGdt.c | 175 ++ .../OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.h | 53 + .../OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf | 43 + .../Library/LoadLinuxLib/X64/JumpToKernel.nasm | 87 + roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBase.c | 36 + .../OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf | 41 + roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxe.c | 141 ++ .../OvmfPkg/Library/LockBoxLib/LockBoxDxeLib.inf | 47 + roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.c | 377 ++++ roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.h | 54 + roms/edk2/OvmfPkg/Library/NvVarsFileLib/FsAccess.c | 508 +++++ .../OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.c | 77 + .../OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.h | 55 + .../Library/NvVarsFileLib/NvVarsFileLib.inf | 56 + .../Library/PciHostBridgeLib/PciHostBridge.h | 69 + .../Library/PciHostBridgeLib/PciHostBridgeLib.c | 443 ++++ .../Library/PciHostBridgeLib/PciHostBridgeLib.inf | 53 + .../OvmfPkg/Library/PciHostBridgeLib/XenSupport.c | 470 +++++ .../PlatformBmPrintScLib/PlatformBmPrintScLib.inf | 60 + .../PlatformBmPrintScLib/StatusCodeHandler.c | 304 +++ .../Library/PlatformBootManagerLib/BdsPlatform.c | 1740 ++++++++++++++++ .../Library/PlatformBootManagerLib/BdsPlatform.h | 190 ++ .../PlatformBootManagerLib.inf | 85 + .../Library/PlatformBootManagerLib/PlatformData.c | 213 ++ .../Library/PlatformBootManagerLib/QemuKernel.c | 50 + .../PlatformBootManagerLibBhyve/BdsPlatform.c | 1660 +++++++++++++++ .../PlatformBootManagerLibBhyve/BdsPlatform.h | 190 ++ .../PlatformBootManagerLibBhyve.inf | 75 + .../PlatformBootManagerLibBhyve/PlatformData.c | 171 ++ .../PlatformDebugLibIoPort/DebugIoPortNocheck.c | 25 + .../PlatformDebugLibIoPort/DebugIoPortQemu.c | 34 + .../Library/PlatformDebugLibIoPort/DebugLib.c | 361 ++++ .../PlatformDebugLibIoPort/DebugLibDetect.c | 65 + .../PlatformDebugLibIoPort/DebugLibDetect.h | 45 + .../PlatformDebugLibIoPort/DebugLibDetectRom.c | 42 + .../PlatformDebugLibIoPort.inf | 49 + .../PlatformRomDebugLibIoPort.inf | 49 + .../PlatformRomDebugLibIoPortNocheck.inf | 48 + .../PlatformFvbLibNull/PlatformFvbLibNull.c | 83 + .../PlatformFvbLibNull/PlatformFvbLibNull.inf | 34 + .../PlatformHasIoMmuLib/PlatformHasIoMmuLib.c | 27 + .../PlatformHasIoMmuLib/PlatformHasIoMmuLib.inf | 32 + .../Library/PlatformSecureLib/PlatformSecureLib.c | 34 + .../PlatformSecureLib/PlatformSecureLib.inf | 28 + .../OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcd.c | 39 + .../PxeBcPcdProducerLib/PxeBcPcdProducerLib.inf | 33 + .../Library/QemuBootOrderLib/ExtraRootBusMap.c | 307 +++ .../Library/QemuBootOrderLib/ExtraRootBusMap.h | 34 + .../Library/QemuBootOrderLib/QemuBootOrderLib.c | 2135 ++++++++++++++++++++ .../Library/QemuBootOrderLib/QemuBootOrderLib.inf | 62 + .../OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c | 453 +++++ .../Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf | 51 + .../OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c | 292 +++ .../Library/QemuFwCfgLib/QemuFwCfgLibInternal.h | 63 + .../Library/QemuFwCfgLib/QemuFwCfgLibNull.inf | 30 + .../OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgNull.c | 209 ++ .../OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c | 201 ++ .../Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf | 45 + .../OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c | 121 ++ .../Library/QemuFwCfgLib/QemuFwCfgSecLib.inf | 42 + .../QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf | 38 + .../QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf | 41 + .../QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf | 39 + .../Library/QemuFwCfgS3Lib/QemuFwCfgS3Base.c | 105 + .../Library/QemuFwCfgS3Lib/QemuFwCfgS3BasePei.c | 221 ++ .../Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c | 786 +++++++ .../Library/QemuFwCfgS3Lib/QemuFwCfgS3Pei.c | 80 + .../Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c | 42 + .../QemuFwCfgSimpleParser.c | 398 ++++ .../QemuFwCfgSimpleParserLib.inf | 27 + .../Library/ResetSystemLib/BaseResetShutdown.c | 51 + .../ResetSystemLib/BaseResetShutdownBhyve.c | 34 + .../Library/ResetSystemLib/BaseResetSystemLib.inf | 38 + .../ResetSystemLib/BaseResetSystemLibBhyve.inf | 40 + .../Library/ResetSystemLib/DxeResetShutdown.c | 62 + .../Library/ResetSystemLib/DxeResetSystemLib.inf | 43 + .../Library/ResetSystemLib/ResetSystemLib.c | 120 ++ .../SerializeVariablesLib/SerializeVariablesLib.c | 869 ++++++++ .../SerializeVariablesLib/SerializeVariablesLib.h | 33 + .../SerializeVariablesLib.inf | 37 + .../SmbiosVersionLib/DetectSmbiosVersionLib.c | 105 + .../SmbiosVersionLib/DetectSmbiosVersionLib.inf | 47 + .../Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c | 1255 ++++++++++++ .../SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf | 38 + .../SmmCpuPlatformHookLibQemu.c | 115 ++ .../SmmCpuPlatformHookLibQemu.inf | 32 + .../DxeTcg2PhysicalPresenceLib.c | 21 + .../DxeTcg2PhysicalPresenceLib.inf | 28 + .../DxeTcg2PhysicalPresenceLib.c | 917 +++++++++ .../DxeTcg2PhysicalPresenceLib.inf | 71 + .../PhysicalPresenceStrings.uni | 38 + .../Library/TlsAuthConfigLib/TlsAuthConfigLib.c | 225 +++ .../Library/TlsAuthConfigLib/TlsAuthConfigLib.inf | 50 + .../UefiPciCapPciIoLib/UefiPciCapPciIoLib.c | 237 +++ .../UefiPciCapPciIoLib/UefiPciCapPciIoLib.h | 38 + .../UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf | 30 + roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.c | 552 +++++ roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.inf | 29 + .../Library/VirtioMmioDeviceLib/VirtioMmioDevice.c | 221 ++ .../Library/VirtioMmioDeviceLib/VirtioMmioDevice.h | 171 ++ .../VirtioMmioDeviceFunctions.c | 349 ++++ .../VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf | 37 + roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.c | 159 ++ .../edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf | 36 + .../OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c | 1718 ++++++++++++++++ .../X86QemuLoadImageLib/X86QemuLoadImageLib.c | 579 ++++++ .../X86QemuLoadImageLib/X86QemuLoadImageLib.inf | 42 + .../XenConsoleSerialPortLib.c | 295 +++ .../XenConsoleSerialPortLib.inf | 29 + .../Library/XenHypercallLib/Aarch64/Hypercall.S | 20 + .../Library/XenHypercallLib/Arm/Hypercall.S | 22 + .../Library/XenHypercallLib/ArmXenHypercall.c | 38 + .../Library/XenHypercallLib/Ia32/hypercall.nasm | 25 + .../Library/XenHypercallLib/X64/hypercall.nasm | 26 + .../Library/XenHypercallLib/X86XenHypercall.c | 90 + .../OvmfPkg/Library/XenHypercallLib/XenHypercall.c | 76 + .../Library/XenHypercallLib/XenHypercallLib.inf | 63 + .../OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.c | 160 ++ .../OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.inf | 35 + .../Library/XenPlatformLib/XenPlatformLib.c | 81 + .../Library/XenPlatformLib/XenPlatformLib.inf | 33 + .../XenRealTimeClockLib/XenRealTimeClockLib.c | 190 ++ .../XenRealTimeClockLib/XenRealTimeClockLib.inf | 33 + 155 files changed, 30548 insertions(+) create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.c create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/AcpiTimerLib.h create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.c create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.c create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLibBhyve.inf create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.c create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.c create mode 100644 roms/edk2/OvmfPkg/Library/AcpiTimerLib/DxeAcpiTimerLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/BaseMemEncryptSevLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c create mode 100644 roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c create mode 100644 roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c create mode 100644 roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c create mode 100644 roms/edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h create mode 100644 roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c create mode 100644 roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.h create mode 100644 roms/edk2/OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c create mode 100644 roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h create mode 100644 roms/edk2/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.c create mode 100644 roms/edk2/OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf create mode 100644 roms/edk2/OvmfPkg/Library/DxePciLibI440FxQ35/PciLib.c create mode 100644 roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.c create mode 100644 roms/edk2/OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c create mode 100644 roms/edk2/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/LoadLinuxLib/Ia32/JumpToKernel.nasm create mode 100644 roms/edk2/OvmfPkg/Library/LoadLinuxLib/Linux.c create mode 100644 roms/edk2/OvmfPkg/Library/LoadLinuxLib/LinuxGdt.c create mode 100644 roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.h create mode 100644 roms/edk2/OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/LoadLinuxLib/X64/JumpToKernel.nasm create mode 100644 roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBase.c create mode 100644 roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxe.c create mode 100644 roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxDxeLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.c create mode 100644 roms/edk2/OvmfPkg/Library/LockBoxLib/LockBoxLib.h create mode 100644 roms/edk2/OvmfPkg/Library/NvVarsFileLib/FsAccess.c create mode 100644 roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.c create mode 100644 roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.h create mode 100644 roms/edk2/OvmfPkg/Library/NvVarsFileLib/NvVarsFileLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridge.h create mode 100644 roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.c create mode 100644 roms/edk2/OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/PciHostBridgeLib/XenSupport.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformData.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.h create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformBootManagerLibBhyve.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformBootManagerLibBhyve/PlatformData.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortNocheck.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortQemu.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLib.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetect.h create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/DebugLibDetectRom.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformDebugLibIoPort.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPort.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPortNocheck.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformFvbLibNull/PlatformFvbLibNull.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformHasIoMmuLib/PlatformHasIoMmuLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.c create mode 100644 roms/edk2/OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcd.c create mode 100644 roms/edk2/OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcdProducerLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.h create mode 100644 roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibNull.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgNull.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSecLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Base.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3BasePei.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Pei.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParser.c create mode 100644 roms/edk2/OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdown.c create mode 100644 roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetShutdownBhyve.c create mode 100644 roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/ResetSystemLib/BaseResetSystemLibBhyve.inf create mode 100644 roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetShutdown.c create mode 100644 roms/edk2/OvmfPkg/Library/ResetSystemLib/DxeResetSystemLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/ResetSystemLib/ResetSystemLib.c create mode 100644 roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.c create mode 100644 roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.h create mode 100644 roms/edk2/OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.c create mode 100644 roms/edk2/OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c create mode 100644 roms/edk2/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.c create mode 100644 roms/edk2/OvmfPkg/Library/SmmCpuPlatformHookLibQemu/SmmCpuPlatformHookLibQemu.inf create mode 100644 roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.c create mode 100644 roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.c create mode 100644 roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/PhysicalPresenceStrings.uni create mode 100644 roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.c create mode 100644 roms/edk2/OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.c create mode 100644 roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.h create mode 100644 roms/edk2/OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.c create mode 100644 roms/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.c create mode 100644 roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDevice.h create mode 100644 roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceFunctions.c create mode 100644 roms/edk2/OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.c create mode 100644 roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c create mode 100644 roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c create mode 100644 roms/edk2/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.c create mode 100644 roms/edk2/OvmfPkg/Library/XenConsoleSerialPortLib/XenConsoleSerialPortLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/Aarch64/Hypercall.S create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/Arm/Hypercall.S create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/ArmXenHypercall.c create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/Ia32/hypercall.nasm create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/X64/hypercall.nasm create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/X86XenHypercall.c create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercall.c create mode 100644 roms/edk2/OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.c create mode 100644 roms/edk2/OvmfPkg/Library/XenIoMmioLib/XenIoMmioLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.c create mode 100644 roms/edk2/OvmfPkg/Library/XenPlatformLib/XenPlatformLib.inf create mode 100644 roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.c create mode 100644 roms/edk2/OvmfPkg/Library/XenRealTimeClockLib/XenRealTimeClockLib.inf (limited to 'roms/edk2/OvmfPkg/Library') 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.
+ Copyright (c) 2011, Andrei Warkentin + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include + +#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 + + 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 + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include + +// +// 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 +# 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 + Copyright (C) 2014, Gabriel L. Somlo + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +/** + 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 +# Copyright (C) 2014, Gabriel L. Somlo +# 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 + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include + +/** + 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 + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include + +// +// 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 +# 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.
+# +# 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +/** + 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#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.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c + +**/ + +#include +#include +#include + +#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.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h + +**/ + +#ifndef __VIRTUAL_MEMORY__ +#define __VIRTUAL_MEMORY__ + +#include +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include + +#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 + +#include + +// +// 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 + +#include +#include +#include + +#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 + +#include + +#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 + Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.
+ Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2015, Nahanni Systems. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uefi.h" +#include +#include +#include +#include +#include +#include +#include + +#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 +# Copyright (C) 2015 Nahanni Systems +# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
+# +# 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.
+# +# 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include + +#include + +#include +#include +#include +#include + +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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiDxe.h" +#include +#include +#include +#include +#include + + +/** + 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.
+# +# 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+# +# 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.
+; +; 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.
+ + 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.
+ 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LOAD_LINUX_LIB_INCLUDED_ +#define _LOAD_LINUX_LIB_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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.
+# +# 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.
+; +; 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include + +/** + 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.
+# +# 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + 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.
+# +# 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#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.
+ + 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "NvVarsFileLib.h" + +#include +#include +#include + + +/** + 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "NvVarsFileLib.h" +#include +#include + +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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __NV_VARS_FILE_LIB_INSTANCE__ +#define __NV_VARS_FILE_LIB_INSTANCE__ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +/** + 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.
+# +# 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.
+ + 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#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.
+# +# 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + + +// +// 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""; + } + + // + // 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "BdsPlatform.h" +#include +#include +#include +#include +#include + + +// +// 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"" : 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""; + 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.
+ 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +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.
+# 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "BdsPlatform.h" +#include +#include + +// +// 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include + + +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 + Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "BdsPlatform.h" +#include +#include +#include +#include + +#include + +// +// 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"" : 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""; + 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 + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +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 +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# +# 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 + Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "BdsPlatform.h" +#include + +// +// 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.
+ Copyright (c) 2012, Red Hat, Inc.
+ Copyright (c) 2020, Citrix Systems, Inc. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#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.
+ Copyright (c) 2012, Red Hat, Inc.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#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 (): \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 string of "(NULL) Filename" is printed. + If Description is NULL, then a 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __DEBUG_IO_PORT_DETECT_H__ +#define __DEBUG_IO_PORT_DETECT_H__ + +#include + +/** + 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#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.
+# Copyright (c) 2012, Red Hat, Inc.
+# +# 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.
+# Copyright (c) 2017, Red Hat, Inc.
+# +# 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiDxe.h" +#include + + +/** + 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.
+# +# 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 + +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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include + +/** + + 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.
+# +# 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 +#include + +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 +#include +#include +#include +#include +#include +#include + +#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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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][] + + A single trailing character is consumed but not returned. A trailing + 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: + // + // + // + 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: + // + // /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: + // + // + // + 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: \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""; + 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: \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.
+# +# 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.
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# 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.
+ Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uefi.h" +#include +#include +#include +#include +#include +#include +#include + +#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 +# Copyright (C) 2013, Red Hat, Inc. +# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# 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 + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +/** + 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.
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include + +#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.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# 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.
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include + +#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.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# 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.
+# 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.
+# 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.
+# 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 +#include + +/** + 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 +#include + +/** + 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 +#include +#include +#include +#include +#include +#include + + +// +// 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 + +/** + 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 +#include + +/** + 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 +#include +#include + +// +// 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include // BIT13 + +#include // CpuDeadLoop() +#include // ASSERT() +#include // IoOr16() +#include // PciRead16() +#include // ResetShutdown() +#include // 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 + Copyright (C) 2020, Red Hat, Inc. + Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include // BIT13 + +#include // BHYVE_PM_REG +#include // CpuDeadLoop() +#include // IoOr16() +#include // 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.
+# 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 +# Copyright (C) 2020, Red Hat, Inc. +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include // BIT13 + +#include // CpuDeadLoop() +#include // ASSERT() +#include // IoOr16() +#include // PcdGet16() +#include // ResetShutdown() +#include // 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.
+# 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include // BIT1 + +#include // CpuDeadLoop() +#include // IoWrite8() +#include // ResetCold() +#include // 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.
+ 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __SERIALIZE_VARIABLES_LIB_INSTANCE__ +#define __SERIALIZE_VARIABLES_LIB_INSTANCE__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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.
+# +# 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include + +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.
+# +# 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// 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.
+# +# 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.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include // AsmReadMsr64() +#include +#include // MSR_IA32_APIC_BASE_REGISTER + +#include + +/** + 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.
+# 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +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.
+# 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.
+Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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.
+# 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.
+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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/** + 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.
+# +# 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 + +#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 + +#include + +#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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include + + +/** + + 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _VIRTIO_MMIO_DEVICE_INTERNAL_H_ +#define _VIRTIO_MMIO_DEVICE_INTERNAL_H_ + +#include + +#include + +#include +#include +#include +#include +#include + +#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.
+ 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +/** + 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.
+# 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include + +// +// 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.
+ Copyright (c) 2020, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+# +# 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.
+ Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include + +#include +#include +#include + +// +// 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.
+# +# 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 + + .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 + + .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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +/** + 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.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +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 + +#include +#include + +#include +#include + +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.
+# 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.
+* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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.
+# +# 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 +#include +#include + +/** + 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +/** + 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.
+# +# 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 -- cgit 1.2.3-korg