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 --- .../Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.c | 257 + .../Acpi/AcpiPlatformDxe/AcpiPlatform.uni | 16 + .../Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf | 49 + .../Acpi/AcpiPlatformDxe/AcpiPlatformExtra.uni | 14 + .../Universal/Acpi/AcpiTableDxe/AcpiSdt.c | 1053 + .../Universal/Acpi/AcpiTableDxe/AcpiSdt.h | 580 + .../Universal/Acpi/AcpiTableDxe/AcpiTable.c | 84 + .../Universal/Acpi/AcpiTableDxe/AcpiTable.h | 234 + .../Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf | 78 + .../Universal/Acpi/AcpiTableDxe/AcpiTableDxe.uni | 14 + .../Acpi/AcpiTableDxe/AcpiTableDxeExtra.uni | 14 + .../Acpi/AcpiTableDxe/AcpiTableProtocol.c | 1850 ++ .../MdeModulePkg/Universal/Acpi/AcpiTableDxe/Aml.c | 296 + .../Universal/Acpi/AcpiTableDxe/AmlChild.c | 274 + .../Universal/Acpi/AcpiTableDxe/AmlNamespace.c | 608 + .../Universal/Acpi/AcpiTableDxe/AmlOption.c | 446 + .../Universal/Acpi/AcpiTableDxe/AmlString.c | 539 + .../BootGraphicsResourceTableDxe.c | 602 + .../BootGraphicsResourceTableDxe.inf | 60 + .../BootGraphicsResourceTableDxe.uni | 16 + .../BootGraphicsResourceTableDxeExtra.uni | 14 + .../BootScriptExecutorDxe.inf | 86 + .../BootScriptExecutorDxe.uni | 17 + .../BootScriptExecutorDxeExtra.uni | 14 + .../Acpi/BootScriptExecutorDxe/IA32/S3Asm.nasm | 62 + .../Acpi/BootScriptExecutorDxe/IA32/SetIdtEntry.c | 56 + .../Acpi/BootScriptExecutorDxe/ScriptExecute.c | 497 + .../Acpi/BootScriptExecutorDxe/ScriptExecute.h | 91 + .../Acpi/BootScriptExecutorDxe/X64/S3Asm.nasm | 130 + .../Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c | 261 + .../FirmwarePerformanceDxe.c | 677 + .../FirmwarePerformanceDxe.inf | 83 + .../FirmwarePerformanceDxe.uni | 18 + .../FirmwarePerformanceDxeExtra.uni | 14 + .../FirmwarePerformancePei.c | 225 + .../FirmwarePerformancePei.inf | 67 + .../FirmwarePerformancePei.uni | 19 + .../FirmwarePerformancePeiExtra.uni | 14 + .../FirmwarePerformanceSmm.c | 318 + .../FirmwarePerformanceSmm.inf | 67 + .../FirmwarePerformanceSmm.uni | 17 + .../FirmwarePerformanceSmmExtra.uni | 14 + .../Acpi/S3SaveStateDxe/AcpiS3ContextSave.c | 325 + .../Acpi/S3SaveStateDxe/InternalS3SaveState.h | 171 + .../Universal/Acpi/S3SaveStateDxe/S3SaveState.c | 928 + .../Acpi/S3SaveStateDxe/S3SaveStateDxe.inf | 69 + .../Acpi/S3SaveStateDxe/S3SaveStateDxe.uni | 16 + .../Acpi/S3SaveStateDxe/S3SaveStateDxeExtra.uni | 14 + .../Acpi/SmmS3SaveState/InternalSmmSaveState.h | 154 + .../Universal/Acpi/SmmS3SaveState/SmmS3SaveState.c | 913 + .../Acpi/SmmS3SaveState/SmmS3SaveState.inf | 56 + .../Acpi/SmmS3SaveState/SmmS3SaveState.uni | 16 + .../Acpi/SmmS3SaveState/SmmS3SaveStateExtra.uni | 14 + roms/edk2/MdeModulePkg/Universal/BdsDxe/Bds.h | 108 + roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.inf | 105 + roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.uni | 17 + .../MdeModulePkg/Universal/BdsDxe/BdsDxeExtra.uni | 14 + roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsEntry.c | 1170 + .../Universal/BdsDxe/HwErrRecSupport.c | 42 + .../Universal/BdsDxe/HwErrRecSupport.h | 26 + roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.c | 196 + roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.h | 24 + .../BootManagerPolicyDxe/BootManagerPolicyDxe.c | 281 + .../BootManagerPolicyDxe/BootManagerPolicyDxe.inf | 56 + .../BootManagerPolicyDxe/BootManagerPolicyDxe.uni | 13 + .../BootManagerPolicyDxeExtra.uni | 14 + .../CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c | 437 + .../CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.inf | 64 + .../CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.uni | 15 + .../CapsuleOnDiskLoadPeiExtra.uni | 14 + .../MdeModulePkg/Universal/CapsulePei/Capsule.h | 123 + .../Universal/CapsulePei/CapsulePei.inf | 94 + .../Universal/CapsulePei/CapsulePei.uni | 19 + .../Universal/CapsulePei/CapsulePeiExtra.uni | 14 + .../Universal/CapsulePei/CapsuleX64.inf | 52 + .../Universal/CapsulePei/CapsuleX64.uni | 23 + .../Universal/CapsulePei/CapsuleX64Extra.uni | 14 + .../Universal/CapsulePei/Common/CapsuleCoalesce.c | 1291 + .../Universal/CapsulePei/Common/CommonHeader.h | 115 + .../Universal/CapsulePei/UefiCapsule.c | 1332 + .../Universal/CapsulePei/X64/PageFaultHandler.nasm | 81 + .../Universal/CapsulePei/X64/X64Entry.c | 305 + .../Universal/CapsuleRuntimeDxe/Arm/CapsuleReset.c | 36 + .../Universal/CapsuleRuntimeDxe/CapsuleCache.c | 57 + .../Universal/CapsuleRuntimeDxe/CapsuleCacheNull.c | 32 + .../Universal/CapsuleRuntimeDxe/CapsuleReset.c | 29 + .../CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf | 108 + .../CapsuleRuntimeDxe/CapsuleRuntimeDxe.uni | 17 + .../CapsuleRuntimeDxe/CapsuleRuntimeDxeExtra.uni | 14 + .../Universal/CapsuleRuntimeDxe/CapsuleService.c | 397 + .../Universal/CapsuleRuntimeDxe/CapsuleService.h | 70 + .../CapsuleRuntimeDxe/SaveLongModeContext.c | 21 + .../CapsuleRuntimeDxe/X64/SaveLongModeContext.c | 209 + .../Console/ConPlatformDxe/ComponentName.c | 161 + .../Universal/Console/ConPlatformDxe/ConPlatform.c | 1271 + .../Universal/Console/ConPlatformDxe/ConPlatform.h | 419 + .../Console/ConPlatformDxe/ConPlatformDxe.inf | 95 + .../Console/ConPlatformDxe/ConPlatformDxe.uni | 17 + .../Console/ConPlatformDxe/ConPlatformDxeExtra.uni | 14 + .../Console/ConSplitterDxe/ComponentName.c | 770 + .../Universal/Console/ConSplitterDxe/ConSplitter.c | 5113 ++++ .../Universal/Console/ConSplitterDxe/ConSplitter.h | 2000 ++ .../Console/ConSplitterDxe/ConSplitterDxe.inf | 114 + .../Console/ConSplitterDxe/ConSplitterDxe.uni | 23 + .../Console/ConSplitterDxe/ConSplitterDxeExtra.uni | 14 + .../Console/ConSplitterDxe/ConSplitterGraphics.c | 622 + .../Console/GraphicsConsoleDxe/ComponentName.c | 176 + .../Console/GraphicsConsoleDxe/GraphicsConsole.c | 2136 ++ .../Console/GraphicsConsoleDxe/GraphicsConsole.h | 594 + .../GraphicsConsoleDxe/GraphicsConsoleDxe.inf | 72 + .../GraphicsConsoleDxe/GraphicsConsoleDxe.uni | 18 + .../GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni | 14 + .../Universal/Console/GraphicsConsoleDxe/LaffStd.c | 271 + .../Console/GraphicsOutputDxe/ComponentName.c | 184 + .../Console/GraphicsOutputDxe/GraphicsOutput.c | 729 + .../Console/GraphicsOutputDxe/GraphicsOutput.h | 53 + .../GraphicsOutputDxe/GraphicsOutputDxe.inf | 53 + .../Universal/Console/TerminalDxe/Ansi.c | 73 + .../Universal/Console/TerminalDxe/ComponentName.c | 231 + .../Universal/Console/TerminalDxe/Terminal.c | 1383 + .../Universal/Console/TerminalDxe/Terminal.h | 1458 + .../Universal/Console/TerminalDxe/TerminalConIn.c | 2093 ++ .../Universal/Console/TerminalDxe/TerminalConOut.c | 959 + .../Universal/Console/TerminalDxe/TerminalDxe.inf | 98 + .../Universal/Console/TerminalDxe/TerminalDxe.uni | 18 + .../Console/TerminalDxe/TerminalDxeExtra.uni | 14 + .../Universal/Console/TerminalDxe/Vtutf8.c | 322 + .../Universal/DebugPortDxe/ComponentName.c | 176 + .../Universal/DebugPortDxe/DebugPort.c | 739 + .../Universal/DebugPortDxe/DebugPort.h | 390 + .../Universal/DebugPortDxe/DebugPortDxe.inf | 66 + .../Universal/DebugPortDxe/DebugPortDxe.uni | 17 + .../Universal/DebugPortDxe/DebugPortDxeExtra.uni | 14 + .../Universal/DebugServicePei/DebugService.h | 50 + .../Universal/DebugServicePei/DebugServicePei.c | 94 + .../Universal/DebugServicePei/DebugServicePei.inf | 46 + .../Universal/DebugServicePei/DebugServicePei.uni | 14 + .../Universal/DebugSupportDxe/DebugSupport.c | 127 + .../Universal/DebugSupportDxe/DebugSupportDxe.inf | 72 + .../Universal/DebugSupportDxe/DebugSupportDxe.uni | 19 + .../DebugSupportDxe/DebugSupportDxeExtra.uni | 14 + .../Universal/DebugSupportDxe/Ia32/AsmFuncs.nasm | 493 + .../Universal/DebugSupportDxe/Ia32/DebugSupport.h | 292 + .../DebugSupportDxe/Ia32/PlDebugSupport.c | 367 + .../DebugSupportDxe/Ia32/PlDebugSupport.h | 16 + .../DebugSupportDxe/Ia32/PlDebugSupportIa32.c | 139 + .../Universal/DebugSupportDxe/X64/AsmFuncs.nasm | 581 + .../Universal/DebugSupportDxe/X64/PlDebugSupport.h | 16 + .../DebugSupportDxe/X64/PlDebugSupportX64.c | 140 + .../Universal/DevicePathDxe/DevicePath.c | 99 + .../Universal/DevicePathDxe/DevicePathDxe.inf | 54 + .../Universal/DevicePathDxe/DevicePathDxe.uni | 19 + .../Universal/DevicePathDxe/DevicePathDxeExtra.uni | 14 + .../Universal/Disk/CdExpressPei/CdExpressPei.inf | 68 + .../Universal/Disk/CdExpressPei/CdExpressPei.uni | 18 + .../Disk/CdExpressPei/CdExpressPeiExtra.uni | 14 + .../Universal/Disk/CdExpressPei/PeiCdExpress.c | 715 + .../Universal/Disk/CdExpressPei/PeiCdExpress.h | 292 + .../Universal/Disk/DiskIoDxe/ComponentName.c | 183 + .../MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.c | 1262 + .../MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.h | 467 + .../Universal/Disk/DiskIoDxe/DiskIoDxe.inf | 65 + .../Universal/Disk/DiskIoDxe/DiskIoDxe.uni | 21 + .../Universal/Disk/DiskIoDxe/DiskIoDxeExtra.uni | 14 + .../Universal/Disk/PartitionDxe/ComponentName.c | 182 + .../Universal/Disk/PartitionDxe/ElTorito.c | 275 + .../MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c | 883 + .../MdeModulePkg/Universal/Disk/PartitionDxe/Mbr.c | 353 + .../Universal/Disk/PartitionDxe/Partition.c | 1369 + .../Universal/Disk/PartitionDxe/Partition.h | 485 + .../Universal/Disk/PartitionDxe/PartitionDxe.inf | 85 + .../Universal/Disk/PartitionDxe/PartitionDxe.uni | 24 + .../Disk/PartitionDxe/PartitionDxeExtra.uni | 14 + .../MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c | 788 + .../Universal/Disk/RamDiskDxe/RamDisk.asl | 38 + .../Universal/Disk/RamDiskDxe/RamDiskBlockIo.c | 487 + .../Universal/Disk/RamDiskDxe/RamDiskDriver.c | 244 + .../Universal/Disk/RamDiskDxe/RamDiskDxe.inf | 84 + .../Universal/Disk/RamDiskDxe/RamDiskDxe.uni | 14 + .../Disk/RamDiskDxe/RamDiskFileExplorer.c | 107 + .../Universal/Disk/RamDiskDxe/RamDiskHii.vfr | 94 + .../Disk/RamDiskDxe/RamDiskHiiStrings.uni | 41 + .../Universal/Disk/RamDiskDxe/RamDiskImpl.c | 758 + .../Universal/Disk/RamDiskDxe/RamDiskImpl.h | 604 + .../Universal/Disk/RamDiskDxe/RamDiskNVData.h | 44 + .../Universal/Disk/RamDiskDxe/RamDiskProtocol.c | 857 + .../Universal/Disk/UdfDxe/ComponentName.c | 179 + .../edk2/MdeModulePkg/Universal/Disk/UdfDxe/File.c | 904 + .../MdeModulePkg/Universal/Disk/UdfDxe/FileName.c | 214 + .../Universal/Disk/UdfDxe/FileSystemOperations.c | 2924 ++ roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c | 331 + roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h | 1215 + .../MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf | 62 + .../UnicodeCollation/EnglishDxe/EnglishDxe.inf | 54 + .../UnicodeCollation/EnglishDxe/EnglishDxe.uni | 20 + .../EnglishDxe/EnglishDxeExtra.uni | 14 + .../EnglishDxe/UnicodeCollationEng.c | 467 + .../EnglishDxe/UnicodeCollationEng.h | 181 + .../Universal/DisplayEngineDxe/DisplayEngine.uni | 16 + .../DisplayEngineDxe/DisplayEngineDxe.inf | 62 + .../DisplayEngineDxe/DisplayEngineExtra.uni | 14 + .../Universal/DisplayEngineDxe/FormDisplay.c | 4259 +++ .../Universal/DisplayEngineDxe/FormDisplay.h | 741 + .../Universal/DisplayEngineDxe/FormDisplayStr.uni | 134 + .../Universal/DisplayEngineDxe/InputHandler.c | 1664 + .../Universal/DisplayEngineDxe/Popup.c | 724 + .../Universal/DisplayEngineDxe/ProcessOptions.c | 1593 + .../DriverHealthConfigureVfr.Vfr | 33 + .../DriverHealthManagerDxe.c | 987 + .../DriverHealthManagerDxe.h | 127 + .../DriverHealthManagerDxe.inf | 74 + .../DriverHealthManagerDxe.uni | 18 + .../DriverHealthManagerDxeExtra.uni | 19 + .../DriverHealthManagerStrings.uni | 34 + .../DriverHealthManagerVfr.Vfr | 32 + .../DriverHealthManagerVfr.h | 26 + .../Universal/DriverSampleDxe/DriverSample.c | 2239 ++ .../Universal/DriverSampleDxe/DriverSample.h | 122 + .../Universal/DriverSampleDxe/DriverSample.uni | 17 + .../Universal/DriverSampleDxe/DriverSampleDxe.inf | 96 + .../DriverSampleDxe/DriverSampleExtra.uni | 14 + .../Universal/DriverSampleDxe/Inventory.vfr | 111 + .../Universal/DriverSampleDxe/InventoryStrings.uni | 60 + .../Universal/DriverSampleDxe/NVDataStruc.h | 129 + .../MdeModulePkg/Universal/DriverSampleDxe/Vfr.vfr | 932 + .../Universal/DriverSampleDxe/VfrStrings.uni | 428 + .../Universal/EbcDxe/AArch64/EbcLowLevel.S | 156 + .../Universal/EbcDxe/AArch64/EbcSupport.c | 475 + .../MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf | 108 + .../MdeModulePkg/Universal/EbcDxe/EbcDebugger.uni | 13 + .../EbcDxe/EbcDebugger/EbcDebuggerConfig.c | 247 + .../Universal/EbcDxe/EbcDebugger/Edb.c | 585 + .../Universal/EbcDxe/EbcDebugger/Edb.h | 60 + .../Universal/EbcDxe/EbcDebugger/EdbCmdBranch.c | 306 + .../Universal/EbcDxe/EbcDebugger/EdbCmdBreak.c | 288 + .../EbcDxe/EbcDebugger/EdbCmdBreakpoint.c | 542 + .../Universal/EbcDxe/EbcDebugger/EdbCmdExtIo.c | 176 + .../Universal/EbcDxe/EbcDebugger/EdbCmdExtPci.c | 145 + .../Universal/EbcDxe/EbcDebugger/EdbCmdGo.c | 76 + .../Universal/EbcDxe/EbcDebugger/EdbCmdHelp.c | 68 + .../Universal/EbcDxe/EbcDebugger/EdbCmdMemory.c | 578 + .../Universal/EbcDxe/EbcDebugger/EdbCmdQuit.c | 38 + .../Universal/EbcDxe/EbcDebugger/EdbCmdRegister.c | 118 + .../Universal/EbcDxe/EbcDebugger/EdbCmdScope.c | 99 + .../Universal/EbcDxe/EbcDebugger/EdbCmdStep.c | 156 + .../Universal/EbcDxe/EbcDebugger/EdbCmdSymbol.c | 862 + .../Universal/EbcDxe/EbcDebugger/EdbCommand.c | 656 + .../Universal/EbcDxe/EbcDebugger/EdbCommand.h | 115 + .../Universal/EbcDxe/EbcDebugger/EdbCommon.h | 239 + .../Universal/EbcDxe/EbcDebugger/EdbDisasm.c | 1770 ++ .../Universal/EbcDxe/EbcDebugger/EdbDisasm.h | 30 + .../EbcDxe/EbcDebugger/EdbDisasmSupport.c | 1211 + .../EbcDxe/EbcDebugger/EdbDisasmSupport.h | 567 + .../Universal/EbcDxe/EbcDebugger/EdbHook.c | 833 + .../Universal/EbcDxe/EbcDebugger/EdbHook.h | 14 + .../Universal/EbcDxe/EbcDebugger/EdbSupport.h | 477 + .../Universal/EbcDxe/EbcDebugger/EdbSupportFile.c | 384 + .../EbcDxe/EbcDebugger/EdbSupportString.c | 1020 + .../Universal/EbcDxe/EbcDebugger/EdbSupportUI.c | 754 + .../Universal/EbcDxe/EbcDebugger/EdbSymbol.c | 2230 ++ .../Universal/EbcDxe/EbcDebugger/EdbSymbol.h | 244 + .../Universal/EbcDxe/EbcDebuggerConfig.inf | 57 + .../Universal/EbcDxe/EbcDebuggerConfig.uni | 13 + .../Universal/EbcDxe/EbcDebuggerConfigExtra.uni | 12 + .../Universal/EbcDxe/EbcDebuggerExtra.uni | 12 + .../Universal/EbcDxe/EbcDebuggerHook.c | 267 + .../Universal/EbcDxe/EbcDebuggerHook.h | 241 + roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf | 81 + roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.uni | 18 + .../MdeModulePkg/Universal/EbcDxe/EbcDxeExtra.uni | 14 + .../MdeModulePkg/Universal/EbcDxe/EbcExecute.c | 5383 ++++ .../MdeModulePkg/Universal/EbcDxe/EbcExecute.h | 135 + roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.c | 1542 + roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.h | 260 + .../Universal/EbcDxe/Ia32/EbcLowLevel.nasm | 191 + .../Universal/EbcDxe/Ia32/EbcSupport.c | 526 + .../Universal/EbcDxe/X64/EbcLowLevel.nasm | 236 + .../MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c | 570 + roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c | 661 + .../MdeModulePkg/Universal/EsrtDxe/EsrtDxe.inf | 66 + .../MdeModulePkg/Universal/EsrtDxe/EsrtDxe.uni | 17 + .../Universal/EsrtDxe/EsrtDxeExtra.uni | 14 + .../edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.c | 475 + .../edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.h | 238 + .../MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c | 558 + .../Universal/EsrtFmpDxe/EsrtFmpDebugPrint.c | 144 + .../Universal/EsrtFmpDxe/EsrtFmpDxe.inf | 57 + .../Universal/EsrtFmpDxe/EsrtFmpDxe.uni | 13 + .../Universal/EsrtFmpDxe/EsrtFmpDxeExtra.uni | 12 + .../FaultTolerantWriteDxe/FaultTolerantWrite.c | 887 + .../FaultTolerantWriteDxe/FaultTolerantWrite.h | 784 + .../FaultTolerantWriteDxe/FaultTolerantWriteDxe.c | 277 + .../FaultTolerantWriteDxe.inf | 86 + .../FaultTolerantWriteDxe.uni | 18 + .../FaultTolerantWriteDxeExtra.uni | 14 + .../FaultTolerantWriteDxe/FaultTolerantWriteSmm.c | 636 + .../FaultTolerantWriteSmm.inf | 95 + .../FaultTolerantWriteSmmCommon.h | 113 + .../FaultTolerantWriteSmmDxe.c | 559 + .../FaultTolerantWriteSmmDxe.h | 196 + .../FaultTolerantWriteSmmDxe.inf | 59 + .../FaultTolerantWriteSmmDxe.uni | 19 + .../FaultTolerantWriteSmmDxeExtra.uni | 14 + .../FaultTolerantWriteStandaloneMm.c | 90 + .../FaultTolerantWriteStandaloneMm.inf | 85 + .../FaultTolerantWriteTraditionalMm.c | 108 + .../Universal/FaultTolerantWriteDxe/FtwMisc.c | 1378 + .../SmmFaultTolerantWriteDxe.uni | 19 + .../SmmFaultTolerantWriteDxeExtra.uni | 14 + .../FaultTolerantWriteDxe/UpdateWorkingBlock.c | 607 + .../FaultTolerantWritePei/FaultTolerantWritePei.c | 315 + .../FaultTolerantWritePei.inf | 62 + .../FaultTolerantWritePei.uni | 16 + .../FaultTolerantWritePeiExtra.uni | 14 + .../Universal/FileExplorerDxe/FileExplorerDxe.c | 52 + .../Universal/FileExplorerDxe/FileExplorerDxe.inf | 47 + .../Universal/FileExplorerDxe/FileExplorerDxe.uni | 17 + .../FileExplorerDxe/FileExplorerDxeExtra.uni | 14 + .../FvSimpleFileSystemDxe/ComponentName.c | 181 + .../FvSimpleFileSystemDxe/FvSimpleFileSystem.c | 1030 + .../FvSimpleFileSystemDxe/FvSimpleFileSystem.uni | 16 + .../FvSimpleFileSystemDxe.inf | 68 + .../FvSimpleFileSystemEntryPoint.c | 673 + .../FvSimpleFileSystemExtra.uni | 14 + .../FvSimpleFileSystemInternal.h | 616 + .../HiiDatabaseDxe/ConfigKeywordHandler.c | 3329 ++ .../Universal/HiiDatabaseDxe/ConfigRouting.c | 6231 ++++ .../Universal/HiiDatabaseDxe/Database.c | 4632 +++ .../MdeModulePkg/Universal/HiiDatabaseDxe/Font.c | 2904 ++ .../Universal/HiiDatabaseDxe/HiiDatabase.h | 2352 ++ .../Universal/HiiDatabaseDxe/HiiDatabase.uni | 17 + .../Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf | 94 + .../Universal/HiiDatabaseDxe/HiiDatabaseEntry.c | 251 + .../Universal/HiiDatabaseDxe/HiiDatabaseExtra.uni | 14 + .../MdeModulePkg/Universal/HiiDatabaseDxe/Image.c | 1563 + .../Universal/HiiDatabaseDxe/ImageEx.c | 421 + .../MdeModulePkg/Universal/HiiDatabaseDxe/String.c | 2085 ++ .../HiiResourcesSampleDxe/HiiResourcesSample.c | 146 + .../HiiResourcesSampleDxe/HiiResourcesSample.uni | 18 + .../HiiResourcesSampleDxe.inf | 54 + .../HiiResourcesSampleExtra.uni | 14 + .../Universal/HiiResourcesSampleDxe/Sample.vfr | 39 + .../HiiResourcesSampleDxe/SampleStrings.uni | 38 + .../Universal/LegacyRegion2Dxe/LegacyRegion2.c | 251 + .../Universal/LegacyRegion2Dxe/LegacyRegion2.h | 169 + .../LegacyRegion2Dxe/LegacyRegion2Dxe.inf | 54 + .../LegacyRegion2Dxe/LegacyRegion2Dxe.uni | 24 + .../LegacyRegion2Dxe/LegacyRegion2DxeExtra.uni | 14 + .../Universal/LoadFileOnFv2/LoadFileOnFv2.c | 421 + .../Universal/LoadFileOnFv2/LoadFileOnFv2.inf | 62 + .../Universal/LoadFileOnFv2/LoadFileOnFv2.uni | 18 + .../Universal/LoadFileOnFv2/LoadFileOnFv2Extra.uni | 12 + .../Universal/LockBox/SmmLockBox/SmmLockBox.c | 434 + .../Universal/LockBox/SmmLockBox/SmmLockBox.inf | 59 + .../Universal/LockBox/SmmLockBox/SmmLockBox.uni | 19 + .../LockBox/SmmLockBox/SmmLockBoxExtra.uni | 14 + .../GenericMemoryTestDxe/GenericMemoryTestDxe.inf | 53 + .../GenericMemoryTestDxe/GenericMemoryTestDxe.uni | 16 + .../GenericMemoryTestDxeExtra.uni | 14 + .../GenericMemoryTestDxe/LightMemoryTest.c | 926 + .../GenericMemoryTestDxe/LightMemoryTest.h | 335 + .../MemoryTest/NullMemoryTestDxe/NullMemoryTest.c | 284 + .../MemoryTest/NullMemoryTestDxe/NullMemoryTest.h | 131 + .../NullMemoryTestDxe/NullMemoryTestDxe.inf | 46 + .../NullMemoryTestDxe/NullMemoryTestDxe.uni | 16 + .../NullMemoryTestDxe/NullMemoryTestDxeExtra.uni | 14 + .../MdeModulePkg/Universal/Metronome/Metronome.c | 119 + .../MdeModulePkg/Universal/Metronome/Metronome.h | 51 + .../MdeModulePkg/Universal/Metronome/Metronome.inf | 54 + .../MdeModulePkg/Universal/Metronome/Metronome.uni | 24 + .../Universal/Metronome/MetronomeExtra.uni | 14 + .../MonotonicCounterRuntimeDxe/MonotonicCounter.c | 269 + .../MonotonicCounterRuntimeDxe.inf | 55 + .../MonotonicCounterRuntimeDxe.uni | 16 + .../MonotonicCounterRuntimeDxeExtra.uni | 14 + roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Pcd.c | 1378 + roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Pcd.inf | 349 + .../edk2/MdeModulePkg/Universal/PCD/Dxe/PcdDxe.uni | 291 + .../MdeModulePkg/Universal/PCD/Dxe/PcdDxeExtra.uni | 14 + roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Service.c | 1901 ++ roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Service.h | 1195 + roms/edk2/MdeModulePkg/Universal/PCD/Pei/Pcd.c | 1672 + roms/edk2/MdeModulePkg/Universal/PCD/Pei/Pcd.inf | 351 + .../MdeModulePkg/Universal/PCD/Pei/PcdPeim.uni | 290 + .../Universal/PCD/Pei/PcdPeimExtra.uni | 14 + roms/edk2/MdeModulePkg/Universal/PCD/Pei/Service.c | 1081 + roms/edk2/MdeModulePkg/Universal/PCD/Pei/Service.h | 1082 + .../PcatSingleSegmentPciCfg2Pei.inf | 50 + .../PcatSingleSegmentPciCfg2Pei.uni | 17 + .../PcatSingleSegmentPciCfg2PeiExtra.uni | 14 + .../PcatSingleSegmentPciCfg2Pei/PciCfg2.c | 311 + .../InternalPlatDriOverrideDxe.h | 211 + .../PlatformDriOverrideDxe/PlatDriOverrideDxe.c | 1744 ++ .../PlatformDriOverrideDxe/PlatDriOverrideDxe.uni | 37 + .../PlatDriOverrideDxeExtra.uni | 14 + .../PlatformDriOverrideDxe/PlatDriOverrideLib.c | 1941 ++ .../PlatformDriOverrideDxe/PlatOverMngr.h | 61 + .../PlatformDriOverrideDxe.inf | 109 + .../Universal/PlatformDriOverrideDxe/Vfr.vfr | 100 + .../PlatformDriOverrideDxe/VfrStrings.uni | 59 + roms/edk2/MdeModulePkg/Universal/PrintDxe/Print.c | 163 + .../MdeModulePkg/Universal/PrintDxe/PrintDxe.inf | 47 + .../MdeModulePkg/Universal/PrintDxe/PrintDxe.uni | 16 + .../Universal/PrintDxe/PrintDxeExtra.uni | 14 + .../RegularExpressionDxe/OnigurumaUefiPort.c | 98 + .../RegularExpressionDxe/OnigurumaUefiPort.h | 104 + .../RegularExpressionDxe/RegularExpressionDxe.c | 381 + .../RegularExpressionDxe/RegularExpressionDxe.h | 125 + .../RegularExpressionDxe/RegularExpressionDxe.inf | 129 + .../Universal/RegularExpressionDxe/config.h | 9 + .../RegularExpressionDxe/oniguruma/.gitignore | 79 + .../RegularExpressionDxe/oniguruma/.travis.yml | 17 + .../RegularExpressionDxe/oniguruma/AUTHORS | 1 + .../RegularExpressionDxe/oniguruma/CMakeLists.txt | 180 + .../RegularExpressionDxe/oniguruma/COPYING | 26 + .../RegularExpressionDxe/oniguruma/ChangeLog | 0 .../RegularExpressionDxe/oniguruma/HISTORY | 2359 ++ .../RegularExpressionDxe/oniguruma/INSTALL | 368 + .../RegularExpressionDxe/oniguruma/Makefile.am | 81 + .../Universal/RegularExpressionDxe/oniguruma/NEWS | 0 .../RegularExpressionDxe/oniguruma/README | 195 + .../RegularExpressionDxe/oniguruma/README.md | 346 + .../RegularExpressionDxe/oniguruma/README_japanese | 183 + .../RegularExpressionDxe/oniguruma/autogen.sh | 9 + .../oniguruma/cmake/Config.cmake.in | 4 + .../RegularExpressionDxe/oniguruma/compile | 348 + .../RegularExpressionDxe/oniguruma/config.guess | 1476 + .../RegularExpressionDxe/oniguruma/config.sub | 1801 ++ .../RegularExpressionDxe/oniguruma/configure.ac | 68 + .../RegularExpressionDxe/oniguruma/depcomp | 791 + .../RegularExpressionDxe/oniguruma/doc/API | 982 + .../RegularExpressionDxe/oniguruma/doc/API.ja | 989 + .../oniguruma/doc/CALLOUTS.API | 385 + .../oniguruma/doc/CALLOUTS.API.ja | 382 + .../oniguruma/doc/CALLOUTS.BUILTIN | 95 + .../oniguruma/doc/CALLOUTS.BUILTIN.ja | 93 + .../RegularExpressionDxe/oniguruma/doc/FAQ | 12 + .../RegularExpressionDxe/oniguruma/doc/FAQ.ja | 22 + .../RegularExpressionDxe/oniguruma/doc/RE | 578 + .../RegularExpressionDxe/oniguruma/doc/RE.ja | 585 + .../RegularExpressionDxe/oniguruma/doc/SYNTAX.md | 1091 + .../oniguruma/doc/UNICODE_PROPERTIES | 788 + .../oniguruma/harnesses/ascii_compatible.dict | 113 + .../oniguruma/harnesses/base.c | 389 + .../oniguruma/harnesses/deluxe.c | 206 + .../oniguruma/harnesses/dict_conv.py | 72 + .../oniguruma/harnesses/fuzzer.options | 2 + .../oniguruma/harnesses/libfuzzer-onig.cpp | 45 + .../oniguruma/harnesses/makefile | 68 + .../oniguruma/harnesses/regset.c | 392 + .../RegularExpressionDxe/oniguruma/index.html | 192 + .../RegularExpressionDxe/oniguruma/index_ja.html | 195 + .../RegularExpressionDxe/oniguruma/install-sh | 518 + .../RegularExpressionDxe/oniguruma/m4/.whatever | 0 .../RegularExpressionDxe/oniguruma/make_win.bat | 5 + .../RegularExpressionDxe/oniguruma/make_win32.bat | 5 + .../RegularExpressionDxe/oniguruma/make_win64.bat | 5 + .../RegularExpressionDxe/oniguruma/missing | 215 + .../RegularExpressionDxe/oniguruma/onig-config.in | 78 + .../oniguruma/oniguruma.pc.cmake.in | 13 + .../RegularExpressionDxe/oniguruma/oniguruma.pc.in | 13 + .../oniguruma/sample/CMakeLists.txt | 35 + .../oniguruma/sample/Makefile.am | 49 + .../oniguruma/sample/bug_fix.c | 93 + .../oniguruma/sample/callout.c | 268 + .../RegularExpressionDxe/oniguruma/sample/count.c | 125 + .../RegularExpressionDxe/oniguruma/sample/crnl.c | 129 + .../RegularExpressionDxe/oniguruma/sample/echo.c | 136 + .../RegularExpressionDxe/oniguruma/sample/encode.c | 212 + .../oniguruma/sample/listcap.c | 118 + .../RegularExpressionDxe/oniguruma/sample/names.c | 79 + .../RegularExpressionDxe/oniguruma/sample/posix.c | 107 + .../RegularExpressionDxe/oniguruma/sample/regset.c | 95 + .../RegularExpressionDxe/oniguruma/sample/scan.c | 90 + .../RegularExpressionDxe/oniguruma/sample/simple.c | 63 + .../RegularExpressionDxe/oniguruma/sample/sql.c | 81 + .../RegularExpressionDxe/oniguruma/sample/syntax.c | 77 + .../oniguruma/sample/user_property.c | 92 + .../RegularExpressionDxe/oniguruma/src/Makefile.am | 69 + .../oniguruma/src/Makefile.windows | 189 + .../RegularExpressionDxe/oniguruma/src/ascii.c | 118 + .../RegularExpressionDxe/oniguruma/src/big5.c | 193 + .../oniguruma/src/config.h.cmake.in | 50 + .../oniguruma/src/config.h.win32 | 56 + .../oniguruma/src/config.h.win64 | 56 + .../oniguruma/src/config.h.windows.in | 62 + .../RegularExpressionDxe/oniguruma/src/cp1251.c | 205 + .../RegularExpressionDxe/oniguruma/src/euc_jp.c | 293 + .../oniguruma/src/euc_jp_prop.c | 151 + .../oniguruma/src/euc_jp_prop.gperf | 26 + .../RegularExpressionDxe/oniguruma/src/euc_kr.c | 193 + .../RegularExpressionDxe/oniguruma/src/euc_tw.c | 187 + .../RegularExpressionDxe/oniguruma/src/gb18030.c | 556 + .../oniguruma/src/gperf_fold_key_conv.py | 71 + .../oniguruma/src/gperf_unfold_key_conv.py | 57 + .../RegularExpressionDxe/oniguruma/src/iso8859_1.c | 268 + .../oniguruma/src/iso8859_10.c | 222 + .../oniguruma/src/iso8859_11.c | 101 + .../oniguruma/src/iso8859_13.c | 207 + .../oniguruma/src/iso8859_14.c | 223 + .../oniguruma/src/iso8859_15.c | 213 + .../oniguruma/src/iso8859_16.c | 220 + .../RegularExpressionDxe/oniguruma/src/iso8859_2.c | 218 + .../RegularExpressionDxe/oniguruma/src/iso8859_3.c | 214 + .../RegularExpressionDxe/oniguruma/src/iso8859_4.c | 217 + .../RegularExpressionDxe/oniguruma/src/iso8859_5.c | 218 + .../RegularExpressionDxe/oniguruma/src/iso8859_6.c | 101 + .../RegularExpressionDxe/oniguruma/src/iso8859_7.c | 206 + .../RegularExpressionDxe/oniguruma/src/iso8859_8.c | 101 + .../RegularExpressionDxe/oniguruma/src/iso8859_9.c | 207 + .../RegularExpressionDxe/oniguruma/src/koi8.c | 236 + .../RegularExpressionDxe/oniguruma/src/koi8_r.c | 204 + .../oniguruma/src/make_property.sh | 20 + .../oniguruma/src/make_unicode_egcb.sh | 7 + .../oniguruma/src/make_unicode_egcb_data.py | 267 + .../oniguruma/src/make_unicode_fold.sh | 36 + .../oniguruma/src/make_unicode_fold_data.py | 486 + .../oniguruma/src/make_unicode_property.sh | 26 + .../oniguruma/src/make_unicode_property_data.py | 605 + .../oniguruma/src/make_unicode_wb.sh | 7 + .../oniguruma/src/make_unicode_wb_data.py | 267 + .../RegularExpressionDxe/oniguruma/src/mktable.c | 1184 + .../RegularExpressionDxe/oniguruma/src/onig_init.c | 45 + .../RegularExpressionDxe/oniguruma/src/oniggnu.h | 87 + .../RegularExpressionDxe/oniguruma/src/onigposix.h | 178 + .../RegularExpressionDxe/oniguruma/src/oniguruma.h | 1053 + .../RegularExpressionDxe/oniguruma/src/regcomp.c | 7928 +++++ .../RegularExpressionDxe/oniguruma/src/regenc.c | 985 + .../RegularExpressionDxe/oniguruma/src/regenc.h | 282 + .../RegularExpressionDxe/oniguruma/src/regerror.c | 406 + .../RegularExpressionDxe/oniguruma/src/regexec.c | 6518 ++++ .../RegularExpressionDxe/oniguruma/src/regext.c | 202 + .../RegularExpressionDxe/oniguruma/src/reggnu.c | 143 + .../RegularExpressionDxe/oniguruma/src/regint.h | 1084 + .../RegularExpressionDxe/oniguruma/src/regparse.c | 8678 ++++++ .../RegularExpressionDxe/oniguruma/src/regparse.h | 475 + .../RegularExpressionDxe/oniguruma/src/regposerr.c | 123 + .../RegularExpressionDxe/oniguruma/src/regposix.c | 321 + .../RegularExpressionDxe/oniguruma/src/regsyntax.c | 338 + .../RegularExpressionDxe/oniguruma/src/regtrav.c | 76 + .../oniguruma/src/regversion.c | 59 + .../RegularExpressionDxe/oniguruma/src/sjis.c | 310 + .../RegularExpressionDxe/oniguruma/src/sjis_prop.c | 151 + .../oniguruma/src/sjis_prop.gperf | 26 + .../RegularExpressionDxe/oniguruma/src/st.c | 584 + .../RegularExpressionDxe/oniguruma/src/st.h | 67 + .../RegularExpressionDxe/oniguruma/src/unicode.c | 1215 + .../oniguruma/src/unicode_egcb_data.c | 1374 + .../oniguruma/src/unicode_fold1_key.c | 2998 ++ .../oniguruma/src/unicode_fold2_key.c | 226 + .../oniguruma/src/unicode_fold3_key.c | 136 + .../oniguruma/src/unicode_fold_data.c | 1549 + .../oniguruma/src/unicode_property_data.c | 30389 +++++++++++++++++++ .../oniguruma/src/unicode_property_data_posix.c | 5347 ++++ .../oniguruma/src/unicode_unfold_key.c | 3303 ++ .../oniguruma/src/unicode_wb_data.c | 1023 + .../RegularExpressionDxe/oniguruma/src/utf16_be.c | 276 + .../RegularExpressionDxe/oniguruma/src/utf16_le.c | 276 + .../RegularExpressionDxe/oniguruma/src/utf32_be.c | 163 + .../RegularExpressionDxe/oniguruma/src/utf32_le.c | 164 + .../RegularExpressionDxe/oniguruma/src/utf8.c | 290 + .../RegularExpressionDxe/oniguruma/test-driver | 148 + .../oniguruma/test/Makefile.am | 65 + .../oniguruma/test/test_regset.c | 456 + .../oniguruma/test/test_syntax.c | 246 + .../oniguruma/test/test_utf8.c | 1438 + .../RegularExpressionDxe/oniguruma/test/testc.c | 979 + .../RegularExpressionDxe/oniguruma/test/testu.c | 955 + .../RegularExpressionDxe/oniguruma/windows/testc.c | 865 + .../Universal/RegularExpressionDxe/stdarg.h | 9 + .../Universal/RegularExpressionDxe/stddef.h | 9 + .../Universal/RegularExpressionDxe/stdio.h | 9 + .../Universal/RegularExpressionDxe/stdlib.h | 9 + .../Universal/RegularExpressionDxe/string.h | 9 + .../Pei/ReportStatusCodeRouterPei.c | 315 + .../Pei/ReportStatusCodeRouterPei.h | 103 + .../Pei/ReportStatusCodeRouterPei.inf | 55 + .../Pei/ReportStatusCodeRouterPei.uni | 16 + .../Pei/ReportStatusCodeRouterPeiExtra.uni | 14 + .../RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.c | 409 + .../RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.h | 136 + .../ReportStatusCodeRouterRuntimeDxe.inf | 58 + .../ReportStatusCodeRouterRuntimeDxe.uni | 16 + .../ReportStatusCodeRouterRuntimeDxeExtra.uni | 14 + .../Smm/ReportStatusCodeRouterSmm.c | 233 + .../Smm/ReportStatusCodeRouterSmm.h | 103 + .../Smm/ReportStatusCodeRouterSmm.inf | 51 + .../Smm/ReportStatusCodeRouterSmm.uni | 16 + .../Smm/ReportStatusCodeRouterSmmExtra.uni | 14 + .../Universal/ResetSystemPei/ResetSystem.c | 362 + .../Universal/ResetSystemPei/ResetSystem.h | 121 + .../Universal/ResetSystemPei/ResetSystemPei.inf | 57 + .../Universal/ResetSystemPei/ResetSystemPei.uni | 14 + .../ResetSystemPei/ResetSystemPeiExtra.uni | 14 + .../Universal/ResetSystemRuntimeDxe/ResetSystem.c | 313 + .../Universal/ResetSystemRuntimeDxe/ResetSystem.h | 98 + .../ResetSystemRuntimeDxe.inf | 60 + .../ResetSystemRuntimeDxe.uni | 14 + .../ResetSystemRuntimeDxeExtra.uni | 14 + .../SectionExtractionDxe/SectionExtractionDxe.c | 354 + .../SectionExtractionDxe/SectionExtractionDxe.inf | 43 + .../SectionExtractionDxe/SectionExtractionDxe.uni | 16 + .../SectionExtractionDxeExtra.uni | 12 + .../SectionExtractionPei/SectionExtractionPei.c | 263 + .../SectionExtractionPei/SectionExtractionPei.inf | 44 + .../SectionExtractionPei/SectionExtractionPei.uni | 16 + .../SectionExtractionPeiExtra.uni | 12 + .../SecurityStubDxe/Defer3rdPartyImageLoad.c | 408 + .../SecurityStubDxe/Defer3rdPartyImageLoad.h | 89 + .../Universal/SecurityStubDxe/SecurityStub.c | 210 + .../Universal/SecurityStubDxe/SecurityStubDxe.inf | 54 + .../Universal/SecurityStubDxe/SecurityStubDxe.uni | 16 + .../SecurityStubDxe/SecurityStubDxeExtra.uni | 14 + .../MdeModulePkg/Universal/SerialDxe/SerialDxe.inf | 49 + .../MdeModulePkg/Universal/SerialDxe/SerialDxe.uni | 16 + .../Universal/SerialDxe/SerialDxeExtra.uni | 14 + .../MdeModulePkg/Universal/SerialDxe/SerialIo.c | 562 + .../Universal/SetupBrowserDxe/Expression.c | 3734 +++ .../Universal/SetupBrowserDxe/Expression.h | 259 + .../Universal/SetupBrowserDxe/IfrParse.c | 2694 ++ .../Universal/SetupBrowserDxe/Presentation.c | 2645 ++ .../MdeModulePkg/Universal/SetupBrowserDxe/Setup.c | 6675 ++++ .../MdeModulePkg/Universal/SetupBrowserDxe/Setup.h | 1875 ++ .../Universal/SetupBrowserDxe/SetupBrowser.uni | 16 + .../Universal/SetupBrowserDxe/SetupBrowserDxe.inf | 79 + .../SetupBrowserDxe/SetupBrowserExtra.uni | 14 + .../MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c | 1455 + .../MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.h | 124 + .../MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf | 61 + .../MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.uni | 16 + .../Universal/SmbiosDxe/SmbiosDxeExtra.uni | 14 + .../SmbiosMeasurementDxe/SmbiosMeasurementDxe.c | 671 + .../SmbiosMeasurementDxe/SmbiosMeasurementDxe.inf | 68 + .../SmbiosMeasurementDxe/SmbiosMeasurementDxe.uni | 16 + .../SmbiosMeasurementDxeExtra.uni | 14 + .../SmmCommunicationBufferDxe.c | 92 + .../SmmCommunicationBufferDxe.inf | 55 + .../SmmCommunicationBufferDxe.uni | 18 + .../SmmCommunicationBufferExtraDxe.uni | 12 + .../StatusCodeHandler/Pei/MemoryStausCodeWorker.c | 121 + .../StatusCodeHandler/Pei/SerialStatusCodeWorker.c | 161 + .../StatusCodeHandler/Pei/StatusCodeHandlerPei.c | 63 + .../StatusCodeHandler/Pei/StatusCodeHandlerPei.h | 119 + .../StatusCodeHandler/Pei/StatusCodeHandlerPei.inf | 65 + .../StatusCodeHandler/Pei/StatusCodeHandlerPei.uni | 16 + .../Pei/StatusCodeHandlerPeiExtra.uni | 14 + .../RuntimeDxe/MemoryStatusCodeWorker.c | 110 + .../RuntimeDxe/SerialStatusCodeWorker.c | 156 + .../RuntimeDxe/StatusCodeHandlerRuntimeDxe.c | 201 + .../RuntimeDxe/StatusCodeHandlerRuntimeDxe.h | 121 + .../RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf | 71 + .../RuntimeDxe/StatusCodeHandlerRuntimeDxe.uni | 16 + .../StatusCodeHandlerRuntimeDxeExtra.uni | 14 + .../StatusCodeHandler/Smm/MemoryStatusCodeWorker.c | 107 + .../StatusCodeHandler/Smm/SerialStatusCodeWorker.c | 156 + .../StatusCodeHandler/Smm/StatusCodeHandlerSmm.c | 84 + .../StatusCodeHandler/Smm/StatusCodeHandlerSmm.h | 117 + .../StatusCodeHandler/Smm/StatusCodeHandlerSmm.inf | 65 + .../StatusCodeHandler/Smm/StatusCodeHandlerSmm.uni | 16 + .../Smm/StatusCodeHandlerSmmExtra.uni | 14 + .../Universal/TimestampDxe/TimestampDxe.c | 160 + .../Universal/TimestampDxe/TimestampDxe.inf | 46 + .../Universal/TimestampDxe/TimestampDxe.uni | 16 + .../Universal/TimestampDxe/TimestampDxeExtra.uni | 14 + .../Universal/Variable/Pei/PeiVariable.uni | 16 + .../Universal/Variable/Pei/PeiVariableExtra.uni | 14 + .../MdeModulePkg/Universal/Variable/Pei/Variable.c | 1231 + .../MdeModulePkg/Universal/Variable/Pei/Variable.h | 144 + .../Universal/Variable/Pei/VariablePei.inf | 74 + .../Universal/Variable/RuntimeDxe/Measurement.c | 339 + .../Variable/RuntimeDxe/PrivilegePolymorphic.h | 157 + .../Universal/Variable/RuntimeDxe/Reclaim.c | 155 + .../Variable/RuntimeDxe/SpeculationBarrierDxe.c | 27 + .../Variable/RuntimeDxe/SpeculationBarrierSmm.c | 26 + .../Universal/Variable/RuntimeDxe/TcgMorLockDxe.c | 124 + .../Universal/Variable/RuntimeDxe/TcgMorLockSmm.c | 512 + .../Universal/Variable/RuntimeDxe/VarCheck.c | 149 + .../Universal/Variable/RuntimeDxe/Variable.c | 3753 +++ .../Universal/Variable/RuntimeDxe/Variable.h | 829 + .../Universal/Variable/RuntimeDxe/VariableDxe.c | 581 + .../Universal/Variable/RuntimeDxe/VariableExLib.c | 258 + .../Variable/RuntimeDxe/VariableNonVolatile.c | 334 + .../Variable/RuntimeDxe/VariableNonVolatile.h | 67 + .../Variable/RuntimeDxe/VariableParsing.c | 788 + .../Variable/RuntimeDxe/VariableParsing.h | 347 + .../Variable/RuntimeDxe/VariableRuntimeCache.c | 153 + .../Variable/RuntimeDxe/VariableRuntimeCache.h | 51 + .../Variable/RuntimeDxe/VariableRuntimeDxe.inf | 146 + .../Variable/RuntimeDxe/VariableRuntimeDxe.uni | 22 + .../RuntimeDxe/VariableRuntimeDxeExtra.uni | 14 + .../Universal/Variable/RuntimeDxe/VariableSmm.c | 1188 + .../Universal/Variable/RuntimeDxe/VariableSmm.inf | 149 + .../Universal/Variable/RuntimeDxe/VariableSmm.uni | 27 + .../Variable/RuntimeDxe/VariableSmmExtra.uni | 14 + .../Variable/RuntimeDxe/VariableSmmRuntimeDxe.c | 1801 ++ .../Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf | 106 + .../Variable/RuntimeDxe/VariableSmmRuntimeDxe.uni | 23 + .../RuntimeDxe/VariableSmmRuntimeDxeExtra.uni | 14 + .../Variable/RuntimeDxe/VariableStandaloneMm.c | 89 + .../Variable/RuntimeDxe/VariableStandaloneMm.inf | 139 + .../Variable/RuntimeDxe/VariableTraditionalMm.c | 130 + .../Universal/WatchdogTimerDxe/WatchdogTimer.c | 245 + .../Universal/WatchdogTimerDxe/WatchdogTimer.h | 102 + .../Universal/WatchdogTimerDxe/WatchdogTimer.inf | 51 + .../Universal/WatchdogTimerDxe/WatchdogTimer.uni | 16 + .../WatchdogTimerDxe/WatchdogTimerExtra.uni | 14 + 706 files changed, 311109 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableProtocol.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/Aml.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlChild.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlNamespace.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlOption.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlString.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/S3Asm.nasm create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/SetIdtEntry.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.nasm create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmmExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/InternalS3SaveState.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveState.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/InternalSmmSaveState.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveStateExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/Bds.h create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsEntry.c create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.c create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.h create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.c create mode 100644 roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.h create mode 100644 roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/Capsule.h create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/Arm/CapsuleReset.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCache.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCacheNull.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleReset.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.h create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterGraphics.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/LaffStd.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutputDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Ansi.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConIn.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConOut.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Vtutf8.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugPortDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugService.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.nasm create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.nasm create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePath.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ElTorito.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Mbr.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDisk.asl create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskBlockIo.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDriver.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskFileExplorer.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHii.vfr create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHiiStrings.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskNVData.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskProtocol.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/File.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngine.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplayStr.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/Popup.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthConfigureVfr.Vfr create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerStrings.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.Vfr create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.c create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Inventory.vfr create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/InventoryStrings.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/NVDataStruc.h create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Vfr.vfr create mode 100644 roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/VfrStrings.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcLowLevel.S create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcSupport.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EbcDebuggerConfig.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBranch.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreak.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreakpoint.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtIo.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtPci.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdGo.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdHelp.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdMemory.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdQuit.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdRegister.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdScope.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdStep.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdSymbol.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommon.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupport.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportFile.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportString.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportUI.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfigExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcLowLevel.nasm create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcSupport.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcLowLevel.nasm create mode 100644 roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.h create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDebugPrint.c create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteTraditionalMm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemEntryPoint.c create mode 100644 roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemInternal.h create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigKeywordHandler.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigRouting.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/Database.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/Font.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabase.h create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabase.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseEntry.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/Image.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ImageEx.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/String.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiResourcesSampleDxe/HiiResourcesSample.c create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiResourcesSampleDxe/HiiResourcesSample.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiResourcesSampleDxe/HiiResourcesSampleDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiResourcesSampleDxe/HiiResourcesSampleExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiResourcesSampleDxe/Sample.vfr create mode 100644 roms/edk2/MdeModulePkg/Universal/HiiResourcesSampleDxe/SampleStrings.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/LegacyRegion2Dxe/LegacyRegion2.c create mode 100644 roms/edk2/MdeModulePkg/Universal/LegacyRegion2Dxe/LegacyRegion2.h create mode 100644 roms/edk2/MdeModulePkg/Universal/LegacyRegion2Dxe/LegacyRegion2Dxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/LegacyRegion2Dxe/LegacyRegion2Dxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/LegacyRegion2Dxe/LegacyRegion2DxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.c create mode 100644 roms/edk2/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2Extra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.c create mode 100644 roms/edk2/MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBoxExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTest.c create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTest.h create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Metronome/Metronome.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Metronome/Metronome.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Metronome/Metronome.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Metronome/Metronome.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Metronome/MetronomeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounter.c create mode 100644 roms/edk2/MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Pcd.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Pcd.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Dxe/PcdDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Dxe/PcdDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Service.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Dxe/Service.h create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Pei/Pcd.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Pei/Pcd.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Pei/PcdPeim.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Pei/PcdPeimExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Pei/Service.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PCD/Pei/Service.h create mode 100644 roms/edk2/MdeModulePkg/Universal/PcatSingleSegmentPciCfg2Pei/PcatSingleSegmentPciCfg2Pei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/PcatSingleSegmentPciCfg2Pei/PcatSingleSegmentPciCfg2Pei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PcatSingleSegmentPciCfg2Pei/PcatSingleSegmentPciCfg2PeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PcatSingleSegmentPciCfg2Pei/PciCfg2.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/InternalPlatDriOverrideDxe.h create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatDriOverrideDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatDriOverrideDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatDriOverrideDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatDriOverrideLib.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatOverMngr.h create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatformDriOverrideDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/Vfr.vfr create mode 100644 roms/edk2/MdeModulePkg/Universal/PlatformDriOverrideDxe/VfrStrings.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PrintDxe/Print.c create mode 100644 roms/edk2/MdeModulePkg/Universal/PrintDxe/PrintDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/PrintDxe/PrintDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/PrintDxe/PrintDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/OnigurumaUefiPort.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/OnigurumaUefiPort.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/config.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/.gitignore create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/.travis.yml create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/AUTHORS create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/CMakeLists.txt create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/COPYING create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/ChangeLog create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/HISTORY create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/INSTALL create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/Makefile.am create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/NEWS create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/README create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/README.md create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/README_japanese create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/autogen.sh create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/cmake/Config.cmake.in create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/compile create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/config.guess create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/config.sub create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/configure.ac create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/depcomp create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/API create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/API.ja create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/CALLOUTS.API create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/CALLOUTS.API.ja create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/CALLOUTS.BUILTIN create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/CALLOUTS.BUILTIN.ja create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/FAQ create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/FAQ.ja create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/RE create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/RE.ja create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/SYNTAX.md create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/doc/UNICODE_PROPERTIES create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/ascii_compatible.dict create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/base.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/deluxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/dict_conv.py create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/fuzzer.options create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/libfuzzer-onig.cpp create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/makefile create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/harnesses/regset.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/index.html create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/index_ja.html create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/install-sh create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/m4/.whatever create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/make_win.bat create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/make_win32.bat create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/make_win64.bat create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/missing create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/onig-config.in create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/oniguruma.pc.cmake.in create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/oniguruma.pc.in create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/CMakeLists.txt create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/Makefile.am create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/bug_fix.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/callout.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/count.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/crnl.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/echo.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/encode.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/listcap.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/names.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/posix.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/regset.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/scan.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/simple.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/sql.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/syntax.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/sample/user_property.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/Makefile.am create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/Makefile.windows create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/ascii.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/big5.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/config.h.cmake.in create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/config.h.win32 create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/config.h.win64 create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/config.h.windows.in create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/cp1251.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/euc_jp.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/euc_jp_prop.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/euc_jp_prop.gperf create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/euc_kr.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/euc_tw.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/gb18030.c create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/gperf_fold_key_conv.py create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/gperf_unfold_key_conv.py create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_1.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_10.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_11.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_13.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_14.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_15.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_16.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_2.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_3.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_4.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_5.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_6.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_7.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_8.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/iso8859_9.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/koi8.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/koi8_r.c create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_property.sh create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_egcb.sh create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_egcb_data.py create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_fold.sh create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_fold_data.py create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_property.sh create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_property_data.py create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_wb.sh create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/make_unicode_wb_data.py create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/mktable.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/onig_init.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/oniggnu.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/onigposix.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/oniguruma.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regcomp.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regenc.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regenc.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regerror.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regexec.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regext.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/reggnu.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regint.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regparse.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regparse.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regposerr.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regposix.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regsyntax.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regtrav.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/regversion.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/sjis.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/sjis_prop.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/sjis_prop.gperf create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/st.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/st.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_egcb_data.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_fold1_key.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_fold2_key.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_fold3_key.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_fold_data.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_property_data.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_property_data_posix.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_unfold_key.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/unicode_wb_data.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/utf16_be.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/utf16_le.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/utf32_be.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/utf32_le.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/src/utf8.c create mode 100755 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/test-driver create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/test/Makefile.am create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/test/test_regset.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/test/test_syntax.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/test/test_utf8.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/test/testc.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/test/testu.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/oniguruma/windows/testc.c create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/stdarg.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/stddef.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/stdio.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/stdlib.h create mode 100644 roms/edk2/MdeModulePkg/Universal/RegularExpressionDxe/string.h create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.c create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.h create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.h create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Smm/ReportStatusCodeRouterSmm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Smm/ReportStatusCodeRouterSmm.h create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Smm/ReportStatusCodeRouterSmm.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Smm/ReportStatusCodeRouterSmm.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ReportStatusCodeRouter/Smm/ReportStatusCodeRouterSmmExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemPei/ResetSystem.c create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemPei/ResetSystem.h create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemPei/ResetSystemPei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemPei/ResetSystemPei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemPei/ResetSystemPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystem.c create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystem.h create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionDxe/SectionExtractionDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionDxe/SectionExtractionDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionDxe/SectionExtractionDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionDxe/SectionExtractionDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionPei/SectionExtractionPei.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionPei/SectionExtractionPei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionPei/SectionExtractionPei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SectionExtractionPei/SectionExtractionPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SecurityStubDxe/Defer3rdPartyImageLoad.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SecurityStubDxe/Defer3rdPartyImageLoad.h create mode 100644 roms/edk2/MdeModulePkg/Universal/SecurityStubDxe/SecurityStub.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SerialDxe/SerialDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SerialDxe/SerialDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SerialDxe/SerialDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SerialDxe/SerialIo.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Expression.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Expression.h create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/IfrParse.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Presentation.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Setup.h create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowser.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.h create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosMeasurementDxe/SmbiosMeasurementDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosMeasurementDxe/SmbiosMeasurementDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosMeasurementDxe/SmbiosMeasurementDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SmbiosMeasurementDxe/SmbiosMeasurementDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SmmCommunicationBufferDxe/SmmCommunicationBufferDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/SmmCommunicationBufferDxe/SmmCommunicationBufferDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/SmmCommunicationBufferDxe/SmmCommunicationBufferDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/SmmCommunicationBufferDxe/SmmCommunicationBufferExtraDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Pei/MemoryStausCodeWorker.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Pei/SerialStatusCodeWorker.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.h create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/MemoryStatusCodeWorker.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/SerialStatusCodeWorker.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.h create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Smm/MemoryStatusCodeWorker.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Smm/SerialStatusCodeWorker.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Smm/StatusCodeHandlerSmm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Smm/StatusCodeHandlerSmm.h create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Smm/StatusCodeHandlerSmm.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Smm/StatusCodeHandlerSmm.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/StatusCodeHandler/Smm/StatusCodeHandlerSmmExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/TimestampDxe/TimestampDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/TimestampDxe/TimestampDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/TimestampDxe/TimestampDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/TimestampDxe/TimestampDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/Pei/PeiVariable.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/Pei/PeiVariableExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/Pei/Variable.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/Pei/Variable.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/Pei/VariablePei.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/Measurement.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/PrivilegePolymorphic.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/SpeculationBarrierDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/SpeculationBarrierSmm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.h create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableTraditionalMm.c create mode 100644 roms/edk2/MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.c create mode 100644 roms/edk2/MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.h create mode 100644 roms/edk2/MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf create mode 100644 roms/edk2/MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.uni create mode 100644 roms/edk2/MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimerExtra.uni (limited to 'roms/edk2/MdeModulePkg/Universal') diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.c new file mode 100644 index 000000000..8d376af41 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.c @@ -0,0 +1,257 @@ +/** @file + Sample ACPI Platform Driver + + Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +/** + Locate the first instance of a protocol. If the protocol requested is an + FV protocol, then it will return the first FV that contains the ACPI table + storage file. + + @param Instance Return pointer to the first instance of the protocol + + @return EFI_SUCCESS The function completed successfully. + @return EFI_NOT_FOUND The protocol could not be located. + @return EFI_OUT_OF_RESOURCES There are not enough resources to find the protocol. + +**/ +EFI_STATUS +LocateFvInstanceWithTables ( + OUT EFI_FIRMWARE_VOLUME2_PROTOCOL **Instance + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN NumberOfHandles; + EFI_FV_FILETYPE FileType; + UINT32 FvStatus; + EFI_FV_FILE_ATTRIBUTES Attributes; + UINTN Size; + UINTN Index; + EFI_FIRMWARE_VOLUME2_PROTOCOL *FvInstance; + + FvStatus = 0; + + // + // Locate protocol. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + // + // Defined errors at this time are not found and out of resources. + // + return Status; + } + + + + // + // Looking for FV with ACPI storage file + // + + for (Index = 0; Index < NumberOfHandles; Index++) { + // + // Get the protocol on this handle + // This should not fail because of LocateHandleBuffer + // + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiFirmwareVolume2ProtocolGuid, + (VOID**) &FvInstance + ); + ASSERT_EFI_ERROR (Status); + + // + // See if it has the ACPI storage file + // + Status = FvInstance->ReadFile ( + FvInstance, + (EFI_GUID*)PcdGetPtr (PcdAcpiTableStorageFile), + NULL, + &Size, + &FileType, + &Attributes, + &FvStatus + ); + + // + // If we found it, then we are done + // + if (Status == EFI_SUCCESS) { + *Instance = FvInstance; + break; + } + } + + // + // Our exit status is determined by the success of the previous operations + // If the protocol was found, Instance already points to it. + // + + // + // Free any allocated buffers + // + gBS->FreePool (HandleBuffer); + + return Status; +} + + +/** + This function calculates and updates an UINT8 checksum. + + @param Buffer Pointer to buffer to checksum + @param Size Number of bytes to checksum + +**/ +VOID +AcpiPlatformChecksum ( + IN UINT8 *Buffer, + IN UINTN Size + ) +{ + UINTN ChecksumOffset; + + ChecksumOffset = OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, Checksum); + + // + // Set checksum to 0 first + // + Buffer[ChecksumOffset] = 0; + + // + // Update checksum value + // + Buffer[ChecksumOffset] = CalculateCheckSum8(Buffer, Size); +} + + +/** + Entrypoint of Acpi Platform driver. + + @param ImageHandle + @param SystemTable + + @return EFI_SUCCESS + @return EFI_LOAD_ERROR + @return EFI_OUT_OF_RESOURCES + +**/ +EFI_STATUS +EFIAPI +AcpiPlatformEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_ACPI_TABLE_PROTOCOL *AcpiTable; + EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol; + INTN Instance; + EFI_ACPI_COMMON_HEADER *CurrentTable; + UINTN TableHandle; + UINT32 FvStatus; + UINTN TableSize; + UINTN Size; + + Instance = 0; + CurrentTable = NULL; + TableHandle = 0; + + // + // Find the AcpiTable protocol + // + Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID**)&AcpiTable); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + // + // Locate the firmware volume protocol + // + Status = LocateFvInstanceWithTables (&FwVol); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Read tables from the storage file. + // + while (Status == EFI_SUCCESS) { + + Status = FwVol->ReadSection ( + FwVol, + (EFI_GUID*)PcdGetPtr (PcdAcpiTableStorageFile), + EFI_SECTION_RAW, + Instance, + (VOID**) &CurrentTable, + &Size, + &FvStatus + ); + if (!EFI_ERROR(Status)) { + // + // Add the table + // + TableHandle = 0; + + TableSize = ((EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable)->Length; + ASSERT (Size >= TableSize); + + // + // Checksum ACPI table + // + AcpiPlatformChecksum ((UINT8*)CurrentTable, TableSize); + + // + // Install ACPI table + // + Status = AcpiTable->InstallAcpiTable ( + AcpiTable, + CurrentTable, + TableSize, + &TableHandle + ); + + // + // Free memory allocated by ReadSection + // + gBS->FreePool (CurrentTable); + + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + + // + // Increment the instance + // + Instance++; + CurrentTable = NULL; + } + } + + // + // The driver does not require to be kept loaded. + // + return EFI_REQUEST_UNLOAD_IMAGE; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.uni new file mode 100644 index 000000000..4b8d33dbc --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.uni @@ -0,0 +1,16 @@ +// /** @file +// Sample ACPI Platform Driver +// +// Sample ACPI Platform Driver +// +// Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Sample ACPI Platform Driver" + +#string STR_MODULE_DESCRIPTION #language en-US "Sample ACPI Platform Driver" + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf new file mode 100644 index 000000000..ca29de430 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf @@ -0,0 +1,49 @@ +## @file +# Sample ACPI Platform Driver +# +# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = AcpiPlatform + MODULE_UNI_FILE = AcpiPlatform.uni + FILE_GUID = cb933912-df8f-4305-b1f9-7b44fa11395c + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = AcpiPlatformEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + AcpiPlatform.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiLib + PcdLib + DebugLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEfiAcpiTableProtocolGuid ## CONSUMES + gEfiFirmwareVolume2ProtocolGuid ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile ## CONSUMES + +[Depex] + gEfiAcpiTableProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + AcpiPlatformExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformExtra.uni new file mode 100644 index 000000000..7e7728ef9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// AcpiPlatform Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"ACPI Platform Sample DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.c new file mode 100644 index 000000000..b1cba20c8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.c @@ -0,0 +1,1053 @@ +/** @file + ACPI Sdt Protocol Driver + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// Includes +// +#include "AcpiTable.h" + +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_ACPI_SDT_PROTOCOL mAcpiSdtProtocolTemplate = { + EFI_ACPI_TABLE_VERSION_NONE, + GetAcpiTable2, + RegisterNotify, + Open, + OpenSdt, + Close, + GetChild, + GetOption, + SetOption, + FindPath +}; + +/** + This function returns ACPI Table instance. + + @return AcpiTableInstance +**/ +EFI_ACPI_TABLE_INSTANCE * +SdtGetAcpiTableInstance ( + VOID + ) +{ + return mPrivateData; +} + +/** + This function finds the table specified by the buffer. + + @param[in] Buffer Table buffer to find. + + @return ACPI table list. +**/ +EFI_ACPI_TABLE_LIST * +FindTableByBuffer ( + IN VOID *Buffer + ) +{ + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance; + LIST_ENTRY *CurrentLink; + EFI_ACPI_TABLE_LIST *CurrentTableList; + LIST_ENTRY *StartLink; + + // + // Get the instance of the ACPI Table + // + AcpiTableInstance = SdtGetAcpiTableInstance (); + + // + // Find the notify + // + StartLink = &AcpiTableInstance->TableList; + CurrentLink = StartLink->ForwardLink; + + while (CurrentLink != StartLink) { + CurrentTableList = EFI_ACPI_TABLE_LIST_FROM_LINK (CurrentLink); + if (((UINTN)CurrentTableList->PageAddress <= (UINTN)Buffer) && + ((UINTN)CurrentTableList->PageAddress + EFI_PAGES_TO_SIZE(CurrentTableList->NumberOfPages) > (UINTN)Buffer)) { + // + // Good! Found Table. + // + return CurrentTableList; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return NULL; +} + +/** + This function updates AML table checksum. + It will search the ACPI table installed by ACPI_TABLE protocol. + + @param[in] Buffer A piece of AML code buffer pointer. + + @retval EFI_SUCCESS The table holds the AML buffer is found, and checksum is updated. + @retval EFI_NOT_FOUND The table holds the AML buffer is not found. +**/ +EFI_STATUS +SdtUpdateAmlChecksum ( + IN VOID *Buffer + ) +{ + EFI_ACPI_TABLE_LIST *CurrentTableList; + + CurrentTableList = FindTableByBuffer (Buffer); + if (CurrentTableList == NULL) { + return EFI_NOT_FOUND; + } + + AcpiPlatformChecksum ( + (VOID *)CurrentTableList->Table, + CurrentTableList->Table->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, Checksum) + ); + return EFI_SUCCESS; +} + +/** + This function finds MAX AML buffer size. + It will search the ACPI table installed by ACPI_TABLE protocol. + + @param[in] Buffer A piece of AML code buffer pointer. + @param[out] MaxSize On return it holds the MAX size of buffer. + + @retval EFI_SUCCESS The table holds the AML buffer is found, and MAX size if returned. + @retval EFI_NOT_FOUND The table holds the AML buffer is not found. +**/ +EFI_STATUS +SdtGetMaxAmlBufferSize ( + IN VOID *Buffer, + OUT UINTN *MaxSize + ) +{ + EFI_ACPI_TABLE_LIST *CurrentTableList; + + CurrentTableList = FindTableByBuffer (Buffer); + if (CurrentTableList == NULL) { + return EFI_NOT_FOUND; + } + + *MaxSize = (UINTN)CurrentTableList->Table + CurrentTableList->Table->Length - (UINTN)Buffer; + return EFI_SUCCESS; +} + +/** + This function invokes ACPI notification. + + @param[in] AcpiTableInstance Instance to AcpiTable + @param[in] Version Version(s) to set. + @param[in] Handle Handle of the table. +**/ +VOID +SdtNotifyAcpiList ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN EFI_ACPI_TABLE_VERSION Version, + IN UINTN Handle + ) +{ + EFI_ACPI_NOTIFY_LIST *CurrentNotifyList; + LIST_ENTRY *CurrentLink; + LIST_ENTRY *StartLink; + EFI_ACPI_TABLE_LIST *Table; + EFI_STATUS Status; + + // + // We should not use Table buffer, because it is user input buffer. + // + Status = FindTableByHandle ( + Handle, + &AcpiTableInstance->TableList, + &Table + ); + ASSERT_EFI_ERROR (Status); + + // + // Find the notify + // + StartLink = &AcpiTableInstance->NotifyList; + CurrentLink = StartLink->ForwardLink; + + while (CurrentLink != StartLink) { + CurrentNotifyList = EFI_ACPI_NOTIFY_LIST_FROM_LINK (CurrentLink); + + // + // Inovke notification + // + CurrentNotifyList->Notification ((EFI_ACPI_SDT_HEADER *)Table->Table, Version, Handle); + + CurrentLink = CurrentLink->ForwardLink; + } + + return ; +} + +/** + Returns a requested ACPI table. + + The GetAcpiTable() function returns a pointer to a buffer containing the ACPI table associated + with the Index that was input. The following structures are not considered elements in the list of + ACPI tables: + - Root System Description Pointer (RSD_PTR) + - Root System Description Table (RSDT) + - Extended System Description Table (XSDT) + Version is updated with a bit map containing all the versions of ACPI of which the table is a + member. For tables installed via the EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable() interface, + the function returns the value of EFI_ACPI_STD_PROTOCOL.AcpiVersion. + + @param[in] Index The zero-based index of the table to retrieve. + @param[out] Table Pointer for returning the table buffer. + @param[out] Version On return, updated with the ACPI versions to which this table belongs. Type + EFI_ACPI_TABLE_VERSION is defined in "Related Definitions" in the + EFI_ACPI_SDT_PROTOCOL. + @param[out] TableKey On return, points to the table key for the specified ACPI system definition table. + This is identical to the table key used in the EFI_ACPI_TABLE_PROTOCOL. + The TableKey can be passed to EFI_ACPI_TABLE_PROTOCOL.UninstallAcpiTable() + to uninstall the table. + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The requested index is too large and a table was not found. +**/ +EFI_STATUS +EFIAPI +GetAcpiTable2 ( + IN UINTN Index, + OUT EFI_ACPI_SDT_HEADER **Table, + OUT EFI_ACPI_TABLE_VERSION *Version, + OUT UINTN *TableKey + ) +{ + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance; + UINTN TableIndex; + LIST_ENTRY *CurrentLink; + LIST_ENTRY *StartLink; + EFI_ACPI_TABLE_LIST *CurrentTable; + + ASSERT (Table != NULL); + ASSERT (Version != NULL); + ASSERT (TableKey != NULL); + + // + // Get the instance of the ACPI Table + // + AcpiTableInstance = SdtGetAcpiTableInstance (); + + // + // Find the table + // + StartLink = &AcpiTableInstance->TableList; + CurrentLink = StartLink->ForwardLink; + TableIndex = 0; + + while (CurrentLink != StartLink) { + if (TableIndex == Index) { + break; + } + // + // Next one + // + CurrentLink = CurrentLink->ForwardLink; + TableIndex ++; + } + + if ((TableIndex != Index) || (CurrentLink == StartLink)) { + return EFI_NOT_FOUND; + } + + // + // Get handle and version + // + CurrentTable = EFI_ACPI_TABLE_LIST_FROM_LINK (CurrentLink); + *TableKey = CurrentTable->Handle; + *Version = CurrentTable->Version; + *Table = (EFI_ACPI_SDT_HEADER *)CurrentTable->Table; + + return EFI_SUCCESS; +} + +/** + Register a callback when an ACPI table is installed. + + This function registers a function which will be called whenever a new ACPI table is installed. + + @param[in] Notification Points to the callback function to be registered +**/ +VOID +SdtRegisterNotify ( + IN EFI_ACPI_NOTIFICATION_FN Notification + ) +{ + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance; + EFI_ACPI_NOTIFY_LIST *CurrentNotifyList; + + // + // Get the instance of the ACPI Table + // + AcpiTableInstance = SdtGetAcpiTableInstance (); + + // + // Create a new list entry + // + CurrentNotifyList = AllocatePool (sizeof (EFI_ACPI_NOTIFY_LIST)); + ASSERT (CurrentNotifyList != NULL); + + // + // Initialize the table contents + // + CurrentNotifyList->Signature = EFI_ACPI_NOTIFY_LIST_SIGNATURE; + CurrentNotifyList->Notification = Notification; + + // + // Add the table to the current list of tables + // + InsertTailList (&AcpiTableInstance->NotifyList, &CurrentNotifyList->Link); + + return ; +} + +/** + Unregister a callback when an ACPI table is installed. + + This function unregisters a function which will be called whenever a new ACPI table is installed. + + @param[in] Notification Points to the callback function to be unregistered. + + @retval EFI_SUCCESS Callback successfully unregistered. + @retval EFI_INVALID_PARAMETER Notification does not match a known registration function. +**/ +EFI_STATUS +SdtUnregisterNotify ( + IN EFI_ACPI_NOTIFICATION_FN Notification + ) +{ + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance; + EFI_ACPI_NOTIFY_LIST *CurrentNotifyList; + LIST_ENTRY *CurrentLink; + LIST_ENTRY *StartLink; + + // + // Get the instance of the ACPI Table + // + AcpiTableInstance = SdtGetAcpiTableInstance (); + + // + // Find the notify + // + StartLink = &AcpiTableInstance->NotifyList; + CurrentLink = StartLink->ForwardLink; + + while (CurrentLink != StartLink) { + CurrentNotifyList = EFI_ACPI_NOTIFY_LIST_FROM_LINK (CurrentLink); + if (CurrentNotifyList->Notification == Notification) { + // + // Good! Found notification. + // + // Remove it from list and free the node. + // + RemoveEntryList (&(CurrentNotifyList->Link)); + FreePool (CurrentNotifyList); + return EFI_SUCCESS; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + // + // Not found! + // + return EFI_INVALID_PARAMETER; +} + +/** + Register or unregister a callback when an ACPI table is installed. + + This function registers or unregisters a function which will be called whenever a new ACPI table is + installed. + + @param[in] Register If TRUE, then the specified function will be registered. If FALSE, then the specified + function will be unregistered. + @param[in] Notification Points to the callback function to be registered or unregistered. + + @retval EFI_SUCCESS Callback successfully registered or unregistered. + @retval EFI_INVALID_PARAMETER Notification is NULL + @retval EFI_INVALID_PARAMETER Register is FALSE and Notification does not match a known registration function. +**/ +EFI_STATUS +EFIAPI +RegisterNotify ( + IN BOOLEAN Register, + IN EFI_ACPI_NOTIFICATION_FN Notification + ) +{ + // + // Check for invalid input parameters + // + if (Notification == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Register) { + // + // Register a new notify + // + SdtRegisterNotify (Notification); + return EFI_SUCCESS; + } else { + // + // Unregister an old notify + // + return SdtUnregisterNotify (Notification); + } +} + +/** + Create a handle for the first ACPI opcode in an ACPI system description table. + + @param[in] TableKey The table key for the ACPI table, as returned by GetTable(). + @param[out] Handle On return, points to the newly created ACPI handle. + + @retval EFI_SUCCESS Handle created successfully. + @retval EFI_NOT_FOUND TableKey does not refer to a valid ACPI table. +**/ +EFI_STATUS +SdtOpenSdtTable ( + IN UINTN TableKey, + OUT EFI_ACPI_HANDLE *Handle + ) +{ + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance; + EFI_STATUS Status; + EFI_ACPI_TABLE_LIST *Table; + EFI_AML_HANDLE *AmlHandle; + + // + // Get the instance of the ACPI Table + // + AcpiTableInstance = SdtGetAcpiTableInstance (); + + // + // Find the table + // + Status = FindTableByHandle ( + TableKey, + &AcpiTableInstance->TableList, + &Table + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + AmlHandle = AllocatePool (sizeof(*AmlHandle)); + ASSERT (AmlHandle != NULL); + AmlHandle->Signature = EFI_AML_ROOT_HANDLE_SIGNATURE; + AmlHandle->Buffer = (VOID *)((UINTN)Table->Table + sizeof(EFI_ACPI_SDT_HEADER)); + AmlHandle->Size = Table->Table->Length - sizeof(EFI_ACPI_SDT_HEADER); + AmlHandle->AmlByteEncoding = NULL; + AmlHandle->Modified = FALSE; + + // + // return the ACPI handle + // + *Handle = (EFI_ACPI_HANDLE)AmlHandle; + + return EFI_SUCCESS; +} + +/** + Create a handle for the first ACPI opcode in an ACPI system description table. + + @param[in] TableKey The table key for the ACPI table, as returned by GetTable(). + @param[out] Handle On return, points to the newly created ACPI handle. + + @retval EFI_SUCCESS Handle created successfully. + @retval EFI_NOT_FOUND TableKey does not refer to a valid ACPI table. +**/ +EFI_STATUS +EFIAPI +OpenSdt ( + IN UINTN TableKey, + OUT EFI_ACPI_HANDLE *Handle + ) +{ + if (Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + return SdtOpenSdtTable (TableKey, Handle); +} + +/** + Create a handle from an ACPI opcode + + @param[in] Buffer Points to the ACPI opcode. + @param[in] BufferSize Max buffer size. + @param[out] Handle Upon return, holds the handle. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Buffer is NULL or Handle is NULL or Buffer points to an + invalid opcode. + +**/ +EFI_STATUS +SdtOpenEx ( + IN VOID *Buffer, + IN UINTN BufferSize, + OUT EFI_ACPI_HANDLE *Handle + ) +{ + AML_BYTE_ENCODING *AmlByteEncoding; + EFI_AML_HANDLE *AmlHandle; + + AmlByteEncoding = AmlSearchByOpByte (Buffer); + if (AmlByteEncoding == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Do not open NameString as handle + // + if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Good, find it + // + AmlHandle = AllocatePool (sizeof(*AmlHandle)); + ASSERT (AmlHandle != NULL); + + AmlHandle->Signature = EFI_AML_HANDLE_SIGNATURE; + AmlHandle->Buffer = Buffer; + AmlHandle->AmlByteEncoding = AmlByteEncoding; + AmlHandle->Modified = FALSE; + + AmlHandle->Size = AmlGetObjectSize (AmlByteEncoding, Buffer, BufferSize); + if (AmlHandle->Size == 0) { + FreePool (AmlHandle); + return EFI_INVALID_PARAMETER; + } + + *Handle = (EFI_ACPI_HANDLE)AmlHandle; + + return EFI_SUCCESS; +} + +/** + Create a handle from an ACPI opcode + + @param[in] Buffer Points to the ACPI opcode. + @param[out] Handle Upon return, holds the handle. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Buffer is NULL or Handle is NULL or Buffer points to an + invalid opcode. + +**/ +EFI_STATUS +EFIAPI +Open ( + IN VOID *Buffer, + OUT EFI_ACPI_HANDLE *Handle + ) +{ + EFI_STATUS Status; + UINTN MaxSize; + + MaxSize = 0; + + // + // Check for invalid input parameters + // + if (Buffer == NULL || Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = SdtGetMaxAmlBufferSize (Buffer, &MaxSize); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + return SdtOpenEx (Buffer, MaxSize, Handle); +} + +/** + Close an ACPI handle. + + @param[in] Handle Returns the handle. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Handle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +Close ( + IN EFI_ACPI_HANDLE Handle + ) +{ + EFI_AML_HANDLE *AmlHandle; + EFI_STATUS Status; + + // + // Check for invalid input parameters + // + if (Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + AmlHandle = (EFI_AML_HANDLE *)Handle; + if ((AmlHandle->Signature != EFI_AML_ROOT_HANDLE_SIGNATURE) && + (AmlHandle->Signature != EFI_AML_HANDLE_SIGNATURE)) { + return EFI_INVALID_PARAMETER; + } + + // + // Update Checksum only if modified + // + if (AmlHandle->Modified) { + Status = SdtUpdateAmlChecksum (AmlHandle->Buffer); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + } + + FreePool (AmlHandle); + + return EFI_SUCCESS; +} + +/** + Retrieve information about an ACPI object. + + @param[in] Handle ACPI object handle. + @param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right + in the ACPI encoding, with index 0 always being the ACPI opcode. + @param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists + for the specified index. + @param[out] Data Upon return, points to the pointer to the data. + @param[out] DataSize Upon return, points to the size of Data. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Handle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +GetOption ( + IN EFI_ACPI_HANDLE Handle, + IN UINTN Index, + OUT EFI_ACPI_DATA_TYPE *DataType, + OUT CONST VOID **Data, + OUT UINTN *DataSize + ) +{ + EFI_AML_HANDLE *AmlHandle; + AML_BYTE_ENCODING *AmlByteEncoding; + EFI_STATUS Status; + + ASSERT (DataType != NULL); + ASSERT (Data != NULL); + ASSERT (DataSize != NULL); + + // + // Check for invalid input parameters + // + if (Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + AmlHandle = (EFI_AML_HANDLE *)Handle; + // + // Do not check EFI_AML_ROOT_HANDLE_SIGNATURE because there is no option for Root handle + // + if (AmlHandle->Signature != EFI_AML_HANDLE_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + AmlByteEncoding = AmlHandle->AmlByteEncoding; + if (Index > AmlByteEncoding->MaxIndex) { + *DataType = EFI_ACPI_DATA_TYPE_NONE; + return EFI_SUCCESS; + } + + // + // Parse option + // + Status = AmlParseOptionHandleCommon (AmlHandle, (AML_OP_PARSE_INDEX)Index, DataType, (VOID **)Data, DataSize); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Change information about an ACPI object. + + @param[in] Handle ACPI object handle. + @param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right + in the ACPI encoding, with index 0 always being the ACPI opcode. + @param[in] Data Points to the data. + @param[in] DataSize The size of the Data. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Handle is NULL or does not refer to a valid ACPI object. + @retval EFI_BAD_BUFFER_SIZE Data cannot be accommodated in the space occupied by + the option. + +**/ +EFI_STATUS +EFIAPI +SetOption ( + IN EFI_ACPI_HANDLE Handle, + IN UINTN Index, + IN CONST VOID *Data, + IN UINTN DataSize + ) +{ + EFI_AML_HANDLE *AmlHandle; + AML_BYTE_ENCODING *AmlByteEncoding; + EFI_STATUS Status; + EFI_ACPI_DATA_TYPE DataType; + VOID *OrgData; + UINTN OrgDataSize; + + ASSERT (Data != NULL); + + // + // Check for invalid input parameters + // + if (Handle == NULL) { + return EFI_INVALID_PARAMETER; + } + + AmlHandle = (EFI_AML_HANDLE *)Handle; + // + // Do not check EFI_AML_ROOT_HANDLE_SIGNATURE because there is no option for Root handle + // + if (AmlHandle->Signature != EFI_AML_HANDLE_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + AmlByteEncoding = AmlHandle->AmlByteEncoding; + + if (Index > AmlByteEncoding->MaxIndex) { + return EFI_INVALID_PARAMETER; + } + + // + // Parse option + // + Status = AmlParseOptionHandleCommon (AmlHandle, (AML_OP_PARSE_INDEX)Index, &DataType, &OrgData, &OrgDataSize); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + if (DataType == EFI_ACPI_DATA_TYPE_NONE) { + return EFI_INVALID_PARAMETER; + } + + if (DataSize > OrgDataSize) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Update + // + CopyMem (OrgData, Data, DataSize); + AmlHandle->Modified = TRUE; + + return EFI_SUCCESS; +} + +/** + Return the child ACPI objects. + + @param[in] ParentHandle Parent handle. + @param[in, out] Handle On entry, points to the previously returned handle or NULL to start with the first + handle. On return, points to the next returned ACPI handle or NULL if there are no + child objects. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER ParentHandle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +GetChild ( + IN EFI_ACPI_HANDLE ParentHandle, + IN OUT EFI_ACPI_HANDLE *Handle + ) +{ + EFI_AML_HANDLE *AmlParentHandle; + EFI_AML_HANDLE *AmlHandle; + VOID *Buffer; + EFI_STATUS Status; + + ASSERT (Handle != NULL); + + // + // Check for invalid input parameters + // + if (ParentHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + AmlHandle = *Handle; + if ((AmlHandle != NULL) && (AmlHandle->Signature != EFI_AML_HANDLE_SIGNATURE)) { + return EFI_INVALID_PARAMETER; + } + + AmlParentHandle = (EFI_AML_HANDLE *)ParentHandle; + if (AmlParentHandle->Signature == EFI_AML_ROOT_HANDLE_SIGNATURE) { + // + // Root handle + // + Status = AmlGetChildFromRoot (AmlParentHandle, AmlHandle, &Buffer); + } else if (AmlParentHandle->Signature == EFI_AML_HANDLE_SIGNATURE) { + // + // Non-root handle + // + Status = AmlGetChildFromNonRoot (AmlParentHandle, AmlHandle, &Buffer); + } else { + // + // Invalid + // + return EFI_INVALID_PARAMETER; + } + + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + if (Buffer == NULL) { + *Handle = NULL; + return EFI_SUCCESS; + } + return SdtOpenEx (Buffer, (UINTN)AmlParentHandle->Buffer + AmlParentHandle->Size - (UINTN)Buffer, Handle); +} + +/** + Returns the handle of the ACPI object representing the specified ACPI path + + @param[in] HandleIn Points to the handle of the object representing the starting point for the path search. + @param[in] AmlPath Points to the AML path. + @param[out] HandleOut On return, points to the ACPI object which represents AcpiPath, relative to + HandleIn. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER HandleIn is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +SdtFindPathFromNonRoot ( + IN EFI_ACPI_HANDLE HandleIn, + IN UINT8 *AmlPath, + OUT EFI_ACPI_HANDLE *HandleOut + ) +{ + EFI_AML_HANDLE *AmlHandle; + VOID *Buffer; + EFI_STATUS Status; + + Buffer = NULL; + AmlHandle = (EFI_AML_HANDLE *)HandleIn; + + // + // For non-root handle, we need search from THIS node instead of ROOT. + // + Status = AmlFindPath (AmlHandle, AmlPath, &Buffer, FALSE); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + if (Buffer == NULL) { + *HandleOut = NULL; + return EFI_SUCCESS; + } + return SdtOpenEx (Buffer, (UINTN)AmlHandle->Buffer + AmlHandle->Size - (UINTN)Buffer, HandleOut); +} + +/** + Duplicate AML handle. + + @param[in] AmlHandle Handle to be duplicated. + + @return Duplicated AML handle. +**/ +EFI_AML_HANDLE * +SdtDuplicateHandle ( + IN EFI_AML_HANDLE *AmlHandle + ) +{ + EFI_AML_HANDLE *DstAmlHandle; + + DstAmlHandle = AllocatePool (sizeof(*DstAmlHandle)); + ASSERT (DstAmlHandle != NULL); + CopyMem (DstAmlHandle, (VOID *)AmlHandle, sizeof(*DstAmlHandle)); + + return DstAmlHandle; +} + +/** + Returns the handle of the ACPI object representing the specified ACPI path + + @param[in] HandleIn Points to the handle of the object representing the starting point for the path search. + @param[in] AmlPath Points to the AML path. + @param[out] HandleOut On return, points to the ACPI object which represents AcpiPath, relative to + HandleIn. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER HandleIn is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +SdtFindPathFromRoot ( + IN EFI_ACPI_HANDLE HandleIn, + IN UINT8 *AmlPath, + OUT EFI_ACPI_HANDLE *HandleOut + ) +{ + EFI_ACPI_HANDLE ChildHandle; + EFI_AML_HANDLE *AmlHandle; + EFI_STATUS Status; + VOID *Buffer; + + Buffer = NULL; + AmlHandle = (EFI_AML_HANDLE *)HandleIn; + + // + // Handle case that AcpiPath is Root + // + if (AmlIsRootPath (AmlPath)) { + // + // Duplicate RootHandle + // + *HandleOut = (EFI_ACPI_HANDLE)SdtDuplicateHandle (AmlHandle); + return EFI_SUCCESS; + } + + // + // Let children find it. + // + ChildHandle = NULL; + while (TRUE) { + Status = GetChild (HandleIn, &ChildHandle); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + if (ChildHandle == NULL) { + // + // Not found + // + *HandleOut = NULL; + return EFI_SUCCESS; + } + + // + // More child + // + AmlHandle = (EFI_AML_HANDLE *)ChildHandle; + Status = AmlFindPath (AmlHandle, AmlPath, &Buffer, TRUE); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + if (Buffer != NULL) { + // + // Great! Find it, open + // + Status = SdtOpenEx (Buffer, (UINTN)AmlHandle->Buffer + AmlHandle->Size - (UINTN)Buffer, HandleOut); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + // + // Not success, try next one + // + } + } + + // + // Should not run here + // +} + +/** + Returns the handle of the ACPI object representing the specified ACPI path + + @param[in] HandleIn Points to the handle of the object representing the starting point for the path search. + @param[in] AcpiPath Points to the ACPI path, which conforms to the ACPI encoded path format. + @param[out] HandleOut On return, points to the ACPI object which represents AcpiPath, relative to + HandleIn. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER HandleIn is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +FindPath ( + IN EFI_ACPI_HANDLE HandleIn, + IN VOID *AcpiPath, + OUT EFI_ACPI_HANDLE *HandleOut + ) +{ + EFI_AML_HANDLE *AmlHandle; + EFI_STATUS Status; + UINT8 *AmlPath; + + // + // Check for invalid input parameters + // + if (HandleIn == NULL) { + return EFI_INVALID_PARAMETER; + } + + AmlHandle = (EFI_AML_HANDLE *)HandleIn; + + // + // Convert ASL path to AML path + // + AmlPath = AmlNameFromAslName (AcpiPath); + if (AmlPath == NULL) { + return EFI_INVALID_PARAMETER; + } + + DEBUG_CODE_BEGIN (); + DEBUG ((EFI_D_ERROR, "AcpiSdt: FindPath - ")); + AmlPrintNameString (AmlPath); + DEBUG ((EFI_D_ERROR, "\n")); + DEBUG_CODE_END (); + + if (AmlHandle->Signature == EFI_AML_ROOT_HANDLE_SIGNATURE) { + // + // Root Handle + // + Status = SdtFindPathFromRoot (HandleIn, AmlPath, HandleOut); + } else if (AmlHandle->Signature == EFI_AML_HANDLE_SIGNATURE) { + // + // Non-Root handle + // + Status = SdtFindPathFromNonRoot (HandleIn, AmlPath, HandleOut); + } else { + Status = EFI_INVALID_PARAMETER; + } + + FreePool (AmlPath); + + return Status; +} + +/** + This function initializes AcpiSdt protocol in ACPI table instance. + + @param[in] AcpiTableInstance Instance to construct +**/ +VOID +SdtAcpiTableAcpiSdtConstructor ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance + ) +{ + + InitializeListHead (&AcpiTableInstance->NotifyList); + CopyMem (&AcpiTableInstance->AcpiSdtProtocol, &mAcpiSdtProtocolTemplate, sizeof(mAcpiSdtProtocolTemplate)); + AcpiTableInstance->AcpiSdtProtocol.AcpiVersion = (EFI_ACPI_TABLE_VERSION)PcdGet32 (PcdAcpiExposedTableVersions); + + return ; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.h b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.h new file mode 100644 index 000000000..50d4c96ed --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiSdt.h @@ -0,0 +1,580 @@ +/** @file + ACPI Sdt Protocol Driver + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _ACPI_SDT_H_ +#define _ACPI_SDT_H_ + +// +// Privacy data structure +// + +// +// ACPI Notify Linked List Signature. +// +#define EFI_ACPI_NOTIFY_LIST_SIGNATURE SIGNATURE_32 ('E', 'A', 'N', 'L') + +// +// ACPI Notify List Entry definition. +// +// Signature must be set to EFI_ACPI_NOTIFY_LIST_SIGNATURE +// Link is the linked list data. +// Notification is the callback function. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + EFI_ACPI_NOTIFICATION_FN Notification; +} EFI_ACPI_NOTIFY_LIST; + +// +// Containment record for ACPI Notify linked list. +// +#define EFI_ACPI_NOTIFY_LIST_FROM_LINK(_link) CR (_link, EFI_ACPI_NOTIFY_LIST, Link, EFI_ACPI_NOTIFY_LIST_SIGNATURE) + +typedef struct _AML_BYTE_ENCODING AML_BYTE_ENCODING; +typedef struct _EFI_AML_NODE_LIST EFI_AML_NODE_LIST; + +// +// AML Node Linked List Signature. +// +#define EFI_AML_NODE_LIST_SIGNATURE SIGNATURE_32 ('E', 'A', 'M', 'L') + +// +// AML Node Linked List Entry definition. +// +// Signature must be set to EFI_AML_NODE_LIST_SIGNATURE +// Link is the linked list data. +// Name is the ACPI node name. +// This is listed for PATH finding. +// Buffer is the ACPI node buffer pointer, the first/second bytes are opcode. +// This buffer should not be freed. +// Size is the total size of this ACPI node buffer. +// Children is the children linked list of this node. +// +#define AML_NAME_SEG_SIZE 4 + +struct _EFI_AML_NODE_LIST { + UINT32 Signature; + UINT8 Name[AML_NAME_SEG_SIZE]; + UINT8 *Buffer; + UINTN Size; + LIST_ENTRY Link; + LIST_ENTRY Children; + EFI_AML_NODE_LIST *Parent; + AML_BYTE_ENCODING *AmlByteEncoding; +}; + +// +// Containment record for AML Node linked list. +// +#define EFI_AML_NODE_LIST_FROM_LINK(_link) CR (_link, EFI_AML_NODE_LIST, Link, EFI_AML_NODE_LIST_SIGNATURE) + +// +// AML Handle Signature. +// +#define EFI_AML_HANDLE_SIGNATURE SIGNATURE_32 ('E', 'A', 'H', 'S') +#define EFI_AML_ROOT_HANDLE_SIGNATURE SIGNATURE_32 ('E', 'A', 'R', 'H') + +// +// AML Handle Entry definition. +// +// Signature must be set to EFI_AML_HANDLE_SIGNATURE or EFI_AML_ROOT_HANDLE_SIGNATURE +// Buffer is the ACPI node buffer pointer, the first/second bytes are opcode. +// This buffer should not be freed. +// Size is the total size of this ACPI node buffer. +// +typedef struct { + UINT32 Signature; + UINT8 *Buffer; + UINTN Size; + AML_BYTE_ENCODING *AmlByteEncoding; + BOOLEAN Modified; +} EFI_AML_HANDLE; + +typedef UINT32 AML_OP_PARSE_INDEX; + +#define AML_OP_PARSE_INDEX_GET_OPCODE 0 +#define AML_OP_PARSE_INDEX_GET_TERM1 1 +#define AML_OP_PARSE_INDEX_GET_TERM2 2 +#define AML_OP_PARSE_INDEX_GET_TERM3 3 +#define AML_OP_PARSE_INDEX_GET_TERM4 4 +#define AML_OP_PARSE_INDEX_GET_TERM5 5 +#define AML_OP_PARSE_INDEX_GET_TERM6 6 +#define AML_OP_PARSE_INDEX_GET_SIZE (AML_OP_PARSE_INDEX)-1 + +typedef UINT32 AML_OP_PARSE_FORMAT; +#define AML_NONE 0 +#define AML_OPCODE 1 +#define AML_UINT8 2 +#define AML_UINT16 3 +#define AML_UINT32 4 +#define AML_UINT64 5 +#define AML_NAME 6 +#define AML_STRING 7 +#define AML_OBJECT 8 + +typedef UINT32 AML_OP_ATTRIBUTE; +#define AML_HAS_PKG_LENGTH 0x1 // It is ACPI attribute - if OpCode has PkgLength +#define AML_IS_NAME_CHAR 0x2 // It is ACPI attribute - if this is NameChar +#define AML_HAS_CHILD_OBJ 0x4 // it is ACPI attribute - if OpCode has Child Object. +#define AML_IN_NAMESPACE 0x10000 // It is UEFI SDT attribute - if OpCode will be in NameSpace + // NOTE; Not all OBJECT will be in NameSpace + // For example, BankField | CreateBitField | CreateByteField | CreateDWordField | + // CreateField | CreateQWordField | CreateWordField | Field | IndexField. + +struct _AML_BYTE_ENCODING { + UINT8 OpCode; + UINT8 SubOpCode; + AML_OP_PARSE_INDEX MaxIndex; + AML_OP_PARSE_FORMAT Format[6]; + AML_OP_ATTRIBUTE Attribute; +}; + +// +// AcpiSdt protocol declaration +// + +/** + Returns a requested ACPI table. + + The GetAcpiTable() function returns a pointer to a buffer containing the ACPI table associated + with the Index that was input. The following structures are not considered elements in the list of + ACPI tables: + - Root System Description Pointer (RSD_PTR) + - Root System Description Table (RSDT) + - Extended System Description Table (XSDT) + Version is updated with a bit map containing all the versions of ACPI of which the table is a + member. For tables installed via the EFI_ACPI_TABLE_PROTOCOL.InstallAcpiTable() interface, + the function returns the value of EFI_ACPI_STD_PROTOCOL.AcpiVersion. + + @param[in] Index The zero-based index of the table to retrieve. + @param[out] Table Pointer for returning the table buffer. + @param[out] Version On return, updated with the ACPI versions to which this table belongs. Type + EFI_ACPI_TABLE_VERSION is defined in "Related Definitions" in the + EFI_ACPI_SDT_PROTOCOL. + @param[out] TableKey On return, points to the table key for the specified ACPI system definition table. + This is identical to the table key used in the EFI_ACPI_TABLE_PROTOCOL. + The TableKey can be passed to EFI_ACPI_TABLE_PROTOCOL.UninstallAcpiTable() + to uninstall the table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The requested index is too large and a table was not found. +**/ +EFI_STATUS +EFIAPI +GetAcpiTable2 ( + IN UINTN Index, + OUT EFI_ACPI_SDT_HEADER **Table, + OUT EFI_ACPI_TABLE_VERSION *Version, + OUT UINTN *TableKey + ); + +/** + Register or unregister a callback when an ACPI table is installed. + + This function registers or unregisters a function which will be called whenever a new ACPI table is + installed. + + @param[in] Register If TRUE, then the specified function will be registered. If FALSE, then the specified + function will be unregistered. + @param[in] Notification Points to the callback function to be registered or unregistered. + + @retval EFI_SUCCESS Callback successfully registered or unregistered. + @retval EFI_INVALID_PARAMETER Notification is NULL + @retval EFI_INVALID_PARAMETER Register is FALSE and Notification does not match a known registration function. +**/ +EFI_STATUS +EFIAPI +RegisterNotify ( + IN BOOLEAN Register, + IN EFI_ACPI_NOTIFICATION_FN Notification + ); + +/** + Create a handle for the first ACPI opcode in an ACPI system description table. + + @param[in] TableKey The table key for the ACPI table, as returned by GetTable(). + @param[out] Handle On return, points to the newly created ACPI handle. + + @retval EFI_SUCCESS Handle created successfully. + @retval EFI_NOT_FOUND TableKey does not refer to a valid ACPI table. +**/ +EFI_STATUS +EFIAPI +OpenSdt ( + IN UINTN TableKey, + OUT EFI_ACPI_HANDLE *Handle + ); + +/** + Create a handle from an ACPI opcode + + @param[in] Buffer Points to the ACPI opcode. + @param[out] Handle Upon return, holds the handle. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Buffer is NULL or Handle is NULL or Buffer points to an + invalid opcode. + +**/ +EFI_STATUS +EFIAPI +Open ( + IN VOID *Buffer, + OUT EFI_ACPI_HANDLE *Handle + ); + +/** + Close an ACPI handle. + + @param[in] Handle Returns the handle. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Handle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +Close ( + IN EFI_ACPI_HANDLE Handle + ); + +/** + Retrieve information about an ACPI object. + + @param[in] Handle ACPI object handle. + @param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right + in the ACPI encoding, with index 0 always being the ACPI opcode. + @param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists + for the specified index. + @param[out] Data Upon return, points to the pointer to the data. + @param[out] DataSize Upon return, points to the size of Data. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Handle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +GetOption ( + IN EFI_ACPI_HANDLE Handle, + IN UINTN Index, + OUT EFI_ACPI_DATA_TYPE *DataType, + OUT CONST VOID **Data, + OUT UINTN *DataSize + ); + +/** + Change information about an ACPI object. + + @param[in] Handle ACPI object handle. + @param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right + in the ACPI encoding, with index 0 always being the ACPI opcode. + @param[in] Data Points to the data. + @param[in] DataSize The size of the Data. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Handle is NULL or does not refer to a valid ACPI object. + @retval EFI_BAD_BUFFER_SIZE Data cannot be accommodated in the space occupied by + the option. + +**/ +EFI_STATUS +EFIAPI +SetOption ( + IN EFI_ACPI_HANDLE Handle, + IN UINTN Index, + IN CONST VOID *Data, + IN UINTN DataSize + ); + +/** + Return the child ACPI objects. + + @param[in] ParentHandle Parent handle. + @param[in, out] Handle On entry, points to the previously returned handle or NULL to start with the first + handle. On return, points to the next returned ACPI handle or NULL if there are no + child objects. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER ParentHandle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +GetChild ( + IN EFI_ACPI_HANDLE ParentHandle, + IN OUT EFI_ACPI_HANDLE *Handle + ); + +/** + Returns the handle of the ACPI object representing the specified ACPI path + + @param[in] HandleIn Points to the handle of the object representing the starting point for the path search. + @param[in] AcpiPath Points to the ACPI path, which conforms to the ACPI encoded path format. + @param[out] HandleOut On return, points to the ACPI object which represents AcpiPath, relative to + HandleIn. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER HandleIn is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +EFIAPI +FindPath ( + IN EFI_ACPI_HANDLE HandleIn, + IN VOID *AcpiPath, + OUT EFI_ACPI_HANDLE *HandleOut + ); + +// +// ACPI SDT function +// + +/** + Create a handle from an ACPI opcode + + @param[in] Buffer Points to the ACPI opcode. + @param[in] BufferSize Max buffer size. + @param[out] Handle Upon return, holds the handle. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Buffer is NULL or Handle is NULL or Buffer points to an + invalid opcode. + +**/ +EFI_STATUS +SdtOpenEx ( + IN VOID *Buffer, + IN UINTN BufferSize, + OUT EFI_ACPI_HANDLE *Handle + ); + +// +// AML support function +// + +/** + Get AML NameString size. + + @param[in] Buffer AML NameString. + @param[out] BufferSize AML NameString size + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Buffer does not refer to a valid AML NameString. +**/ +EFI_STATUS +AmlGetNameStringSize ( + IN UINT8 *Buffer, + OUT UINTN *BufferSize + ); + +/** + This function retuns package length from the buffer. + + @param[in] Buffer AML buffer + @param[out] PkgLength The total length of package. + + @return The byte data count to present the package length. +**/ +UINTN +AmlGetPkgLength ( + IN UINT8 *Buffer, + OUT UINTN *PkgLength + ); + +/** + This function returns AcpiDataType according to AmlType. + + @param[in] AmlType AML Type. + + @return AcpiDataType +**/ +EFI_ACPI_DATA_TYPE +AmlTypeToAcpiType ( + IN AML_OP_PARSE_FORMAT AmlType + ); + +/** + This function returns AmlByteEncoding according to OpCode Byte. + + @param[in] OpByteBuffer OpCode byte buffer. + + @return AmlByteEncoding +**/ +AML_BYTE_ENCODING * +AmlSearchByOpByte ( + IN UINT8 *OpByteBuffer + ); + +/** + Return object size. + + @param[in] AmlByteEncoding AML Byte Encoding. + @param[in] Buffer AML object buffer. + @param[in] MaxBufferSize AML object buffer MAX size. The parser can not parse any data exceed this region. + + @return Size of the object. +**/ +UINTN +AmlGetObjectSize ( + IN AML_BYTE_ENCODING *AmlByteEncoding, + IN UINT8 *Buffer, + IN UINTN MaxBufferSize + ); + +/** + Return object name. + + @param[in] AmlHandle AML handle. + + @return Name of the object. +**/ +CHAR8 * +AmlGetObjectName ( + IN EFI_AML_HANDLE *AmlHandle + ); + +/** + Retrieve information according to AmlHandle + + @param[in] AmlHandle AML handle. + @param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right + in the ACPI encoding, with index 0 always being the ACPI opcode. + @param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists + for the specified index. + @param[out] Data Upon return, points to the pointer to the data. + @param[out] DataSize Upon return, points to the size of Data. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER AmlHandle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlParseOptionHandleCommon ( + IN EFI_AML_HANDLE *AmlHandle, + IN AML_OP_PARSE_INDEX Index, + OUT EFI_ACPI_DATA_TYPE *DataType, + OUT VOID **Data, + OUT UINTN *DataSize + ); + +/** + Return offset of last option. + + @param[in] AmlHandle AML Handle. + @param[out] Buffer Upon return, points to the offset after last option. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER AmlHandle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetOffsetAfterLastOption ( + IN EFI_AML_HANDLE *AmlHandle, + OUT UINT8 **Buffer + ); + +/** + Return the child ACPI objects from Root Handle. + + @param[in] AmlParentHandle Parent handle. It is Root Handle. + @param[in] AmlHandle The previously returned handle or NULL to start with the first handle. + @param[out] Buffer On return, points to the next returned ACPI handle or NULL if there are no + child objects. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER ParentHandle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetChildFromRoot ( + IN EFI_AML_HANDLE *AmlParentHandle, + IN EFI_AML_HANDLE *AmlHandle, + OUT VOID **Buffer + ); + +/** + Return the child ACPI objects from Non-Root Handle. + + @param[in] AmlParentHandle Parent handle. It is Non-Root Handle. + @param[in] AmlHandle The previously returned handle or NULL to start with the first handle. + @param[out] Buffer On return, points to the next returned ACPI handle or NULL if there are no + child objects. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER ParentHandle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetChildFromNonRoot ( + IN EFI_AML_HANDLE *AmlParentHandle, + IN EFI_AML_HANDLE *AmlHandle, + OUT VOID **Buffer + ); + +/** + Return AML name according to ASL name. + The caller need free the AmlName returned. + + @param[in] AslPath ASL name. + + @return AmlName +**/ +UINT8 * +AmlNameFromAslName ( + IN UINT8 *AslPath + ); + +/** + Returns the handle of the ACPI object representing the specified ACPI AML path + + @param[in] AmlHandle Points to the handle of the object representing the starting point for the path search. + @param[in] AmlPath Points to the ACPI AML path. + @param[out] Buffer On return, points to the ACPI object which represents AcpiPath, relative to + HandleIn. + @param[in] FromRoot TRUE means to find AML path from \ (Root) Node. + FALSE means to find AML path from this Node (The HandleIn). + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER HandleIn does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlFindPath ( + IN EFI_AML_HANDLE *AmlHandle, + IN UINT8 *AmlPath, + OUT VOID **Buffer, + IN BOOLEAN FromRoot + ); + +/** + Print AML NameString. + + @param[in] Buffer AML NameString. +**/ +VOID +AmlPrintNameString ( + IN UINT8 *Buffer + ); + +/** + Print AML NameSeg. + + @param[in] Buffer AML NameSeg. +**/ +VOID +AmlPrintNameSeg ( + IN UINT8 *Buffer + ); + +/** + Check if it is AML Root name + + @param[in] Buffer AML path. + + @retval TRUE AML path is root. + @retval FALSE AML path is not root. +**/ +BOOLEAN +AmlIsRootPath ( + IN UINT8 *Buffer + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.c new file mode 100644 index 000000000..1d91737cb --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.c @@ -0,0 +1,84 @@ +/** @file + ACPI Table Protocol Driver + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// Includes +// +#include "AcpiTable.h" + +// +// Handle to install ACPI Table Protocol +// +EFI_HANDLE mHandle = NULL; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_ACPI_TABLE_INSTANCE *mPrivateData = NULL; + +/** + Entry point of the ACPI table driver. + Creates and initializes an instance of the ACPI Table + Protocol and installs it on a new handle. + + @param ImageHandle A handle for the image that is initializing this driver. + @param SystemTable A pointer to the EFI system table. + + @return EFI_SUCCESS Driver initialized successfully. + @return EFI_LOAD_ERROR Failed to Initialize or has been loaded. + @return EFI_OUT_OF_RESOURCES Could not allocate needed resources. + +**/ +EFI_STATUS +EFIAPI +InitializeAcpiTableDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_ACPI_TABLE_INSTANCE *PrivateData; + + // + // Initialize our protocol + // + PrivateData = AllocateZeroPool (sizeof (EFI_ACPI_TABLE_INSTANCE)); + ASSERT (PrivateData); + PrivateData->Signature = EFI_ACPI_TABLE_SIGNATURE; + + // + // Call all constructors per produced protocols + // + Status = AcpiTableAcpiTableConstructor (PrivateData); + if (EFI_ERROR (Status)) { + gBS->FreePool (PrivateData); + return EFI_LOAD_ERROR; + } + + // + // Install ACPI Table protocol + // + if (FeaturePcdGet (PcdInstallAcpiSdtProtocol)) { + mPrivateData = PrivateData; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEfiAcpiTableProtocolGuid, + &PrivateData->AcpiTableProtocol, + &gEfiAcpiSdtProtocolGuid, + &mPrivateData->AcpiSdtProtocol, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEfiAcpiTableProtocolGuid, + &PrivateData->AcpiTableProtocol, + NULL + ); + } + ASSERT_EFI_ERROR (Status); + + return Status; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.h b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.h new file mode 100644 index 000000000..425a462fd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTable.h @@ -0,0 +1,234 @@ +/** @file + ACPI Table Protocol Driver + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _ACPI_TABLE_H_ +#define _ACPI_TABLE_H_ + + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Statements that include other files +// +#include + +#include "AcpiSdt.h" + +// +// Great than or equal to 2.0. +// +#define ACPI_TABLE_VERSION_GTE_2_0 (EFI_ACPI_TABLE_VERSION_2_0 | \ + EFI_ACPI_TABLE_VERSION_3_0 | \ + EFI_ACPI_TABLE_VERSION_4_0 | \ + EFI_ACPI_TABLE_VERSION_5_0) + +// +// Private Driver Data +// +// +// ACPI Table Linked List Signature. +// +#define EFI_ACPI_TABLE_LIST_SIGNATURE SIGNATURE_32 ('E', 'A', 'T', 'L') + +// +// ACPI Table Linked List Entry definition. +// +// Signature must be set to EFI_ACPI_TABLE_LIST_SIGNATURE +// Link is the linked list data. +// Version is the versions of the ACPI tables that this table belongs in. +// Table is a pointer to the table. +// PageAddress is the address of the pages allocated for the table. +// NumberOfPages is the number of pages allocated at PageAddress. +// Handle is used to identify a particular table. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + EFI_ACPI_TABLE_VERSION Version; + EFI_ACPI_COMMON_HEADER *Table; + EFI_PHYSICAL_ADDRESS PageAddress; + UINTN NumberOfPages; + UINTN Handle; +} EFI_ACPI_TABLE_LIST; + +// +// Containment record for ACPI Table linked list. +// +#define EFI_ACPI_TABLE_LIST_FROM_LINK(_link) CR (_link, EFI_ACPI_TABLE_LIST, Link, EFI_ACPI_TABLE_LIST_SIGNATURE) + +// +// The maximum number of tables this driver supports +// +#define EFI_ACPI_MAX_NUM_TABLES 20 + +// +// Protocol private structure definition +// +// +// ACPI support protocol instance signature definition. +// +#define EFI_ACPI_TABLE_SIGNATURE SIGNATURE_32 ('S', 'T', 'A', 'E') + +// +// ACPI support protocol instance data structure +// +typedef struct { + UINTN Signature; + EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp1; // Pointer to RSD_PTR structure + EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp3; // Pointer to RSD_PTR structure + EFI_ACPI_DESCRIPTION_HEADER *Rsdt1; // Pointer to RSDT table header + EFI_ACPI_DESCRIPTION_HEADER *Rsdt3; // Pointer to RSDT table header + EFI_ACPI_DESCRIPTION_HEADER *Xsdt; // Pointer to XSDT table header + EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt1; // Pointer to FADT table header + EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt3; // Pointer to FADT table header + EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs1; // Pointer to FACS table header + EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs3; // Pointer to FACS table header + EFI_ACPI_DESCRIPTION_HEADER *Dsdt1; // Pointer to DSDT table header + EFI_ACPI_DESCRIPTION_HEADER *Dsdt3; // Pointer to DSDT table header + LIST_ENTRY TableList; + UINTN NumberOfTableEntries1; // Number of ACPI 1.0 tables + UINTN NumberOfTableEntries3; // Number of ACPI 3.0 tables + UINTN CurrentHandle; + EFI_ACPI_TABLE_PROTOCOL AcpiTableProtocol; + EFI_ACPI_SDT_PROTOCOL AcpiSdtProtocol; + LIST_ENTRY NotifyList; +} EFI_ACPI_TABLE_INSTANCE; + +// +// ACPI table protocol instance containing record macro +// +#define EFI_ACPI_TABLE_INSTANCE_FROM_THIS(a) \ + CR (a, \ + EFI_ACPI_TABLE_INSTANCE, \ + AcpiTableProtocol, \ + EFI_ACPI_TABLE_SIGNATURE \ + ) + +// +// Protocol Constructor functions +// + +/** + Constructor for the ACPI support protocol. Initializes instance + data. + + @param AcpiTableInstance Instance to construct + + @return EFI_SUCCESS Instance initialized. + @return EFI_OUT_OF_RESOURCES Unable to allocate required resources. + +**/ +EFI_STATUS +AcpiTableAcpiTableConstructor ( + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance + ); + + +/** + Entry point of the ACPI table driver. + Creates and initializes an instance of the ACPI Table + Protocol and installs it on a new handle. + + @param ImageHandle A handle for the image that is initializing this driver + @param SystemTable A pointer to the EFI system table + + @return EFI_SUCCESS Driver initialized successfully + @return EFI_LOAD_ERROR Failed to Initialize or has been loaded + @return EFI_OUT_OF_RESOURCES Could not allocate needed resources + +**/ +EFI_STATUS +EFIAPI +InitializeAcpiTableDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + + This function finds the table specified by the handle and returns a pointer to it. + If the handle is not found, EFI_NOT_FOUND is returned and the contents of Table are + undefined. + + @param[in] Handle Table to find. + @param[in] TableList Table list to search + @param[out] Table Pointer to table found. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND No table found matching the handle specified. + +**/ +EFI_STATUS +FindTableByHandle ( + IN UINTN Handle, + IN LIST_ENTRY *TableList, + OUT EFI_ACPI_TABLE_LIST **Table + ); + +/** + + This function calculates and updates an UINT8 checksum. + + @param[in] Buffer Pointer to buffer to checksum + @param[in] Size Number of bytes to checksum + @param[in] ChecksumOffset Offset to place the checksum result in + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +AcpiPlatformChecksum ( + IN VOID *Buffer, + IN UINTN Size, + IN UINTN ChecksumOffset + ); + +/** + This function invokes ACPI notification. + + @param[in] AcpiTableInstance Instance to AcpiTable + @param[in] Version Version(s) to set. + @param[in] Handle Handle of the table. +**/ +VOID +SdtNotifyAcpiList ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN EFI_ACPI_TABLE_VERSION Version, + IN UINTN Handle + ); + +/** + This function initializes AcpiSdt protocol in ACPI table instance. + + @param[in] AcpiTableInstance Instance to construct +**/ +VOID +SdtAcpiTableAcpiSdtConstructor ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance + ); + +// +// export PrivateData symbol, because we need that in AcpiSdtProtol implementation +// +extern EFI_HANDLE mHandle; +extern EFI_ACPI_TABLE_INSTANCE *mPrivateData; + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf new file mode 100644 index 000000000..d341df439 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf @@ -0,0 +1,78 @@ +## @file +# ACPI Table Protocol Driver +# +# This driver initializes ACPI tables (Rsdp, Rsdt and Xsdt) and produces UEFI/PI +# services to install/uninstall/manage ACPI tables. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = AcpiTableDxe + MODULE_UNI_FILE = AcpiTableDxe.uni + FILE_GUID = 9622E42C-8E38-4a08-9E8F-54F784652F6B + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeAcpiTableDxe + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + AcpiTableProtocol.c + AcpiTable.h + AcpiTable.c + AcpiSdt.h + AcpiSdt.c + Aml.c + AmlString.c + AmlOption.c + AmlChild.c + AmlNamespace.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + UefiDriverEntryPoint + BaseMemoryLib + UefiLib + DebugLib + BaseLib + PcdLib + +[Guids] + gEfiAcpi10TableGuid ## PRODUCES ## SystemTable + gEfiAcpiTableGuid ## PRODUCES ## SystemTable + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdInstallAcpiSdtProtocol ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiExposedTableVersions ## CONSUMES + +[Protocols] + gEfiAcpiTableProtocolGuid ## PRODUCES + gEfiAcpiSdtProtocolGuid ## PRODUCES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + AcpiTableDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.uni new file mode 100644 index 000000000..7afe9bd4d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.uni @@ -0,0 +1,14 @@ +// /** @file +// AcpiTableDxe Module Localized Abstract and Description Content +// +// Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "ACPI Table Protocol Driver" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver initializes ACPI tables (Rsdp, Rsdt and Xsdt) and produces UEFI/PI services to install/uninstall/manage ACPI tables." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxeExtra.uni new file mode 100644 index 000000000..b64bc2b25 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// AcpiTableDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"ACPI Table DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableProtocol.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableProtocol.c new file mode 100644 index 000000000..ad7baf820 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableProtocol.c @@ -0,0 +1,1850 @@ +/** @file + ACPI Table Protocol Implementation + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// Includes +// +#include "AcpiTable.h" +// +// The maximum number of tables that pre-allocated. +// +UINTN mEfiAcpiMaxNumTables = EFI_ACPI_MAX_NUM_TABLES; + +// +// Allocation strategy to use for AllocatePages (). +// Runtime value depends on PcdExposedAcpiTableVersions. +// +STATIC EFI_ALLOCATE_TYPE mAcpiTableAllocType; + +/** + This function adds an ACPI table to the table list. It will detect FACS and + allocate the correct type of memory and properly align the table. + + @param AcpiTableInstance Instance of the protocol. + @param Table Table to add. + @param Checksum Does the table require checksumming. + @param Version The version of the list to add the table to. + @param Handle Pointer for returning the handle. + + @return EFI_SUCCESS The function completed successfully. + @return EFI_OUT_OF_RESOURCES Could not allocate a required resource. + @return EFI_ABORTED The table is a duplicate of a table that is required + to be unique. + +**/ +EFI_STATUS +AddTableToList ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN VOID *Table, + IN BOOLEAN Checksum, + IN EFI_ACPI_TABLE_VERSION Version, + OUT UINTN *Handle + ); + +/** + This function finds and removes the table specified by the handle. + + @param AcpiTableInstance Instance of the protocol. + @param Version Bitmask of which versions to remove. + @param Handle Table to remove. + + @return EFI_SUCCESS The function completed successfully. + @return EFI_ABORTED An error occurred. + @return EFI_NOT_FOUND Handle not found in table list. + +**/ +EFI_STATUS +RemoveTableFromList ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN EFI_ACPI_TABLE_VERSION Version, + IN UINTN Handle + ); + +/** + This function calculates and updates an UINT8 checksum. + + @param Buffer Pointer to buffer to checksum + @param Size Number of bytes to checksum + @param ChecksumOffset Offset to place the checksum result in + + @return EFI_SUCCESS The function completed successfully. +**/ +EFI_STATUS +AcpiPlatformChecksum ( + IN VOID *Buffer, + IN UINTN Size, + IN UINTN ChecksumOffset + ); + +/** + Checksum all versions of the common tables, RSDP, RSDT, XSDT. + + @param AcpiTableInstance Protocol instance private data. + + @return EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +ChecksumCommonTables ( + IN OUT EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance + ); + +// +// Protocol function implementations. +// + +/** + This function publishes the specified versions of the ACPI tables by + installing EFI configuration table entries for them. Any combination of + table versions can be published. + + @param AcpiTableInstance Instance of the protocol. + @param Version Version(s) to publish. + + @return EFI_SUCCESS The function completed successfully. + @return EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +EFIAPI +PublishTables ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN EFI_ACPI_TABLE_VERSION Version + ) +{ + EFI_STATUS Status; + UINT32 *CurrentRsdtEntry; + VOID *CurrentXsdtEntry; + UINT64 Buffer64; + + // + // Reorder tables as some operating systems don't seem to find the + // FADT correctly if it is not in the first few entries + // + + // + // Add FADT as the first entry + // + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + CurrentRsdtEntry = (UINT32 *) ((UINT8 *) AcpiTableInstance->Rsdt1 + sizeof (EFI_ACPI_DESCRIPTION_HEADER)); + *CurrentRsdtEntry = (UINT32) (UINTN) AcpiTableInstance->Fadt1; + + CurrentRsdtEntry = (UINT32 *) ((UINT8 *) AcpiTableInstance->Rsdt3 + sizeof (EFI_ACPI_DESCRIPTION_HEADER)); + *CurrentRsdtEntry = (UINT32) (UINTN) AcpiTableInstance->Fadt3; + } + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + CurrentXsdtEntry = (VOID *) ((UINT8 *) AcpiTableInstance->Xsdt + sizeof (EFI_ACPI_DESCRIPTION_HEADER)); + // + // Add entry to XSDT, XSDT expects 64 bit pointers, but + // the table pointers in XSDT are not aligned on 8 byte boundary. + // + Buffer64 = (UINT64) (UINTN) AcpiTableInstance->Fadt3; + CopyMem ( + CurrentXsdtEntry, + &Buffer64, + sizeof (UINT64) + ); + } + + // + // Do checksum again because Dsdt/Xsdt is updated. + // + ChecksumCommonTables (AcpiTableInstance); + + // + // Add the RSD_PTR to the system table and store that we have installed the + // tables. + // + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + Status = gBS->InstallConfigurationTable (&gEfiAcpi10TableGuid, AcpiTableInstance->Rsdp1); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + Status = gBS->InstallConfigurationTable (&gEfiAcpiTableGuid, AcpiTableInstance->Rsdp3); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + + return EFI_SUCCESS; +} + + +/** + Installs an ACPI table into the RSDT/XSDT. + Note that the ACPI table should be checksumed before installing it. + Otherwise it will assert. + + @param This Protocol instance pointer. + @param AcpiTableBuffer A pointer to a buffer containing the ACPI table to be installed. + @param AcpiTableBufferSize Specifies the size, in bytes, of the AcpiTableBuffer buffer. + @param TableKey Reurns a key to refer to the ACPI table. + + @return EFI_SUCCESS The table was successfully inserted. + @return EFI_INVALID_PARAMETER Either AcpiTableBuffer is NULL, TableKey is NULL, or AcpiTableBufferSize + and the size field embedded in the ACPI table pointed to by AcpiTableBuffer + are not in sync. + @return EFI_OUT_OF_RESOURCES Insufficient resources exist to complete the request. + @retval EFI_ACCESS_DENIED The table signature matches a table already + present in the system and platform policy + does not allow duplicate tables of this type. + +**/ +EFI_STATUS +EFIAPI +InstallAcpiTable ( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN VOID *AcpiTableBuffer, + IN UINTN AcpiTableBufferSize, + OUT UINTN *TableKey + ) +{ + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance; + EFI_STATUS Status; + VOID *AcpiTableBufferConst; + EFI_ACPI_TABLE_VERSION Version; + + // + // Check for invalid input parameters + // + if ((AcpiTableBuffer == NULL) || (TableKey == NULL) + || (((EFI_ACPI_DESCRIPTION_HEADER *) AcpiTableBuffer)->Length != AcpiTableBufferSize)) { + return EFI_INVALID_PARAMETER; + } + + Version = PcdGet32 (PcdAcpiExposedTableVersions); + + // + // Get the instance of the ACPI table protocol + // + AcpiTableInstance = EFI_ACPI_TABLE_INSTANCE_FROM_THIS (This); + + // + // Install the ACPI table + // + AcpiTableBufferConst = AllocateCopyPool (AcpiTableBufferSize,AcpiTableBuffer); + *TableKey = 0; + Status = AddTableToList ( + AcpiTableInstance, + AcpiTableBufferConst, + TRUE, + Version, + TableKey + ); + if (!EFI_ERROR (Status)) { + Status = PublishTables ( + AcpiTableInstance, + Version + ); + } + FreePool (AcpiTableBufferConst); + + // + // Add a new table successfully, notify registed callback + // + if (FeaturePcdGet (PcdInstallAcpiSdtProtocol)) { + if (!EFI_ERROR (Status)) { + SdtNotifyAcpiList ( + AcpiTableInstance, + Version, + *TableKey + ); + } + } + + return Status; +} + + +/** + Removes an ACPI table from the RSDT/XSDT. + + @param This Protocol instance pointer. + @param TableKey Specifies the table to uninstall. The key was returned from InstallAcpiTable(). + + @return EFI_SUCCESS The table was successfully uninstalled. + @return EFI_NOT_FOUND TableKey does not refer to a valid key for a table entry. + +**/ +EFI_STATUS +EFIAPI +UninstallAcpiTable ( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN UINTN TableKey + ) +{ + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance; + EFI_STATUS Status; + EFI_ACPI_TABLE_VERSION Version; + + // + // Get the instance of the ACPI table protocol + // + AcpiTableInstance = EFI_ACPI_TABLE_INSTANCE_FROM_THIS (This); + + Version = PcdGet32 (PcdAcpiExposedTableVersions); + + // + // Uninstall the ACPI table + // + Status = RemoveTableFromList ( + AcpiTableInstance, + Version, + TableKey + ); + if (!EFI_ERROR (Status)) { + Status = PublishTables ( + AcpiTableInstance, + Version + ); + } + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } else { + return EFI_SUCCESS; + } +} + +/** + If the number of APCI tables exceeds the preallocated max table number, enlarge the table buffer. + + @param AcpiTableInstance ACPI table protocol instance data structure. + + @return EFI_SUCCESS reallocate the table beffer successfully. + @return EFI_OUT_OF_RESOURCES Unable to allocate required resources. + +**/ +EFI_STATUS +ReallocateAcpiTableBuffer ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance + ) +{ + UINTN NewMaxTableNumber; + UINTN TotalSize; + UINT8 *Pointer; + EFI_PHYSICAL_ADDRESS PageAddress; + EFI_ACPI_TABLE_INSTANCE TempPrivateData; + EFI_STATUS Status; + UINT64 CurrentData; + + CopyMem (&TempPrivateData, AcpiTableInstance, sizeof (EFI_ACPI_TABLE_INSTANCE)); + // + // Enlarge the max table number from mEfiAcpiMaxNumTables to mEfiAcpiMaxNumTables + EFI_ACPI_MAX_NUM_TABLES + // + NewMaxTableNumber = mEfiAcpiMaxNumTables + EFI_ACPI_MAX_NUM_TABLES; + // + // Create RSDT, XSDT structures and allocate buffers. + // + TotalSize = sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 2.0/3.0 XSDT + NewMaxTableNumber * sizeof (UINT64); + + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + TotalSize += sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 1.0 RSDT + NewMaxTableNumber * sizeof (UINT32) + + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 2.0/3.0 RSDT + NewMaxTableNumber * sizeof (UINT32); + } + + // + // Allocate memory in the lower 32 bit of address range for + // compatibility with ACPI 1.0 OS. + // + // This is done because ACPI 1.0 pointers are 32 bit values. + // ACPI 2.0 OS and all 64 bit OS must use the 64 bit ACPI table addresses. + // There is no architectural reason these should be below 4GB, it is purely + // for convenience of implementation that we force memory below 4GB. + // + PageAddress = 0xFFFFFFFF; + Status = gBS->AllocatePages ( + mAcpiTableAllocType, + EfiACPIReclaimMemory, + EFI_SIZE_TO_PAGES (TotalSize), + &PageAddress + ); + + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + Pointer = (UINT8 *) (UINTN) PageAddress; + ZeroMem (Pointer, TotalSize); + + AcpiTableInstance->Rsdt1 = (EFI_ACPI_DESCRIPTION_HEADER *) Pointer; + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + Pointer += (sizeof (EFI_ACPI_DESCRIPTION_HEADER) + NewMaxTableNumber * sizeof (UINT32)); + AcpiTableInstance->Rsdt3 = (EFI_ACPI_DESCRIPTION_HEADER *) Pointer; + Pointer += (sizeof (EFI_ACPI_DESCRIPTION_HEADER) + NewMaxTableNumber * sizeof (UINT32)); + } + AcpiTableInstance->Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) Pointer; + + // + // Update RSDP to point to the new Rsdt and Xsdt address. + // + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + AcpiTableInstance->Rsdp1->RsdtAddress = (UINT32) (UINTN) AcpiTableInstance->Rsdt1; + AcpiTableInstance->Rsdp3->RsdtAddress = (UINT32) (UINTN) AcpiTableInstance->Rsdt3; + } + CurrentData = (UINT64) (UINTN) AcpiTableInstance->Xsdt; + CopyMem (&AcpiTableInstance->Rsdp3->XsdtAddress, &CurrentData, sizeof (UINT64)); + + // + // copy the original Rsdt1, Rsdt3 and Xsdt structure to new buffer + // + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + CopyMem (AcpiTableInstance->Rsdt1, TempPrivateData.Rsdt1, (sizeof (EFI_ACPI_DESCRIPTION_HEADER) + mEfiAcpiMaxNumTables * sizeof (UINT32))); + CopyMem (AcpiTableInstance->Rsdt3, TempPrivateData.Rsdt3, (sizeof (EFI_ACPI_DESCRIPTION_HEADER) + mEfiAcpiMaxNumTables * sizeof (UINT32))); + } + CopyMem (AcpiTableInstance->Xsdt, TempPrivateData.Xsdt, (sizeof (EFI_ACPI_DESCRIPTION_HEADER) + mEfiAcpiMaxNumTables * sizeof (UINT64))); + + // + // Calculate orignal ACPI table buffer size + // + TotalSize = sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 2.0/3.0 XSDT + mEfiAcpiMaxNumTables * sizeof (UINT64); + + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + TotalSize += sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 1.0 RSDT + mEfiAcpiMaxNumTables * sizeof (UINT32) + + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 2.0/3.0 RSDT + mEfiAcpiMaxNumTables * sizeof (UINT32); + } + + gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)TempPrivateData.Rsdt1, EFI_SIZE_TO_PAGES (TotalSize)); + + // + // Update the Max ACPI table number + // + mEfiAcpiMaxNumTables = NewMaxTableNumber; + return EFI_SUCCESS; +} + +/** + This function adds an ACPI table to the table list. It will detect FACS and + allocate the correct type of memory and properly align the table. + + @param AcpiTableInstance Instance of the protocol. + @param Table Table to add. + @param Checksum Does the table require checksumming. + @param Version The version of the list to add the table to. + @param Handle Pointer for returning the handle. + + @return EFI_SUCCESS The function completed successfully. + @return EFI_OUT_OF_RESOURCES Could not allocate a required resource. + @retval EFI_ACCESS_DENIED The table signature matches a table already + present in the system and platform policy + does not allow duplicate tables of this type. + +**/ +EFI_STATUS +AddTableToList ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN VOID *Table, + IN BOOLEAN Checksum, + IN EFI_ACPI_TABLE_VERSION Version, + OUT UINTN *Handle + ) +{ + EFI_STATUS Status; + EFI_ACPI_TABLE_LIST *CurrentTableList; + UINT32 CurrentTableSignature; + UINT32 CurrentTableSize; + UINT32 *CurrentRsdtEntry; + VOID *CurrentXsdtEntry; + UINT64 Buffer64; + BOOLEAN AddToRsdt; + + // + // Check for invalid input parameters + // + ASSERT (AcpiTableInstance); + ASSERT (Table); + ASSERT (Handle); + + // + // Init locals + // + AddToRsdt = TRUE; + + // + // Create a new list entry + // + CurrentTableList = AllocatePool (sizeof (EFI_ACPI_TABLE_LIST)); + ASSERT (CurrentTableList); + + // + // Determine table type and size + // + CurrentTableSignature = ((EFI_ACPI_COMMON_HEADER *) Table)->Signature; + CurrentTableSize = ((EFI_ACPI_COMMON_HEADER *) Table)->Length; + + // + // Allocate a buffer for the table. All tables are allocated in the lower 32 bits of address space + // for backwards compatibility with ACPI 1.0 OS. + // + // This is done because ACPI 1.0 pointers are 32 bit values. + // ACPI 2.0 OS and all 64 bit OS must use the 64 bit ACPI table addresses. + // There is no architectural reason these should be below 4GB, it is purely + // for convenience of implementation that we force memory below 4GB. + // + CurrentTableList->PageAddress = 0xFFFFFFFF; + CurrentTableList->NumberOfPages = EFI_SIZE_TO_PAGES (CurrentTableSize); + + // + // Allocation memory type depends on the type of the table + // + if ((CurrentTableSignature == EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) || + (CurrentTableSignature == EFI_ACPI_4_0_UEFI_ACPI_DATA_TABLE_SIGNATURE)) { + // + // Allocate memory for the FACS. This structure must be aligned + // on a 64 byte boundary and must be ACPI NVS memory. + // Using AllocatePages should ensure that it is always aligned. + // Do not change signature for new ACPI version because they are same. + // + // UEFI table also need to be in ACPI NVS memory, because some data field + // could be updated by OS present agent. For example, BufferPtrAddress in + // SMM communication ACPI table. + // + ASSERT ((EFI_PAGE_SIZE % 64) == 0); + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + CurrentTableList->NumberOfPages, + &CurrentTableList->PageAddress + ); + } else { + // + // All other tables are ACPI reclaim memory, no alignment requirements. + // + Status = gBS->AllocatePages ( + mAcpiTableAllocType, + EfiACPIReclaimMemory, + CurrentTableList->NumberOfPages, + &CurrentTableList->PageAddress + ); + } + // + // Check return value from memory alloc. + // + if (EFI_ERROR (Status)) { + gBS->FreePool (CurrentTableList); + return EFI_OUT_OF_RESOURCES; + } + // + // Update the table pointer with the allocated memory start + // + CurrentTableList->Table = (EFI_ACPI_COMMON_HEADER *) (UINTN) CurrentTableList->PageAddress; + + // + // Initialize the table contents + // + CurrentTableList->Signature = EFI_ACPI_TABLE_LIST_SIGNATURE; + CopyMem (CurrentTableList->Table, Table, CurrentTableSize); + CurrentTableList->Handle = AcpiTableInstance->CurrentHandle++; + *Handle = CurrentTableList->Handle; + CurrentTableList->Version = Version; + + // + // Update internal pointers if this is a required table. If it is a required + // table and a table of that type already exists, return an error. + // + // Calculate the checksum if the table is not FACS. + // + switch (CurrentTableSignature) { + + case EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE: + // + // We don't add the FADT in the standard way because some + // OS expect the FADT to be early in the table list. + // So we always add it as the first element in the list. + // + AddToRsdt = FALSE; + + // + // Check that the table has not been previously added. + // + if (((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0 && AcpiTableInstance->Fadt1 != NULL) || + ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0 && AcpiTableInstance->Fadt3 != NULL) + ) { + gBS->FreePages (CurrentTableList->PageAddress, CurrentTableList->NumberOfPages); + gBS->FreePool (CurrentTableList); + return EFI_ACCESS_DENIED; + } + // + // Add the table to the appropriate table version + // + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + // + // Save a pointer to the table + // + AcpiTableInstance->Fadt1 = (EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE *) CurrentTableList->Table; + + // + // Update pointers in FADT. If tables don't exist this will put NULL pointers there. + // + AcpiTableInstance->Fadt1->FirmwareCtrl = (UINT32) (UINTN) AcpiTableInstance->Facs1; + AcpiTableInstance->Fadt1->Dsdt = (UINT32) (UINTN) AcpiTableInstance->Dsdt1; + + // + // RSDP OEM information is updated to match the FADT OEM information + // + CopyMem ( + &AcpiTableInstance->Rsdp1->OemId, + &AcpiTableInstance->Fadt1->Header.OemId, + 6 + ); + + // + // RSDT OEM information is updated to match the FADT OEM information. + // + CopyMem ( + &AcpiTableInstance->Rsdt1->OemId, + &AcpiTableInstance->Fadt1->Header.OemId, + 6 + ); + + CopyMem ( + &AcpiTableInstance->Rsdt1->OemTableId, + &AcpiTableInstance->Fadt1->Header.OemTableId, + sizeof (UINT64) + ); + AcpiTableInstance->Rsdt1->OemRevision = AcpiTableInstance->Fadt1->Header.OemRevision; + } + + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + // + // Save a pointer to the table + // + AcpiTableInstance->Fadt3 = (EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE *) CurrentTableList->Table; + + // + // Update pointers in FADT. If tables don't exist this will put NULL pointers there. + // Note: If the FIRMWARE_CTRL is non-zero, then X_FIRMWARE_CTRL must be zero, and + // vice-versa. + // + if ((UINT64)(UINTN)AcpiTableInstance->Facs3 < BASE_4GB) { + AcpiTableInstance->Fadt3->FirmwareCtrl = (UINT32) (UINTN) AcpiTableInstance->Facs3; + ZeroMem (&AcpiTableInstance->Fadt3->XFirmwareCtrl, sizeof (UINT64)); + } else { + Buffer64 = (UINT64) (UINTN) AcpiTableInstance->Facs3; + CopyMem ( + &AcpiTableInstance->Fadt3->XFirmwareCtrl, + &Buffer64, + sizeof (UINT64) + ); + AcpiTableInstance->Fadt3->FirmwareCtrl = 0; + } + if ((UINT64)(UINTN)AcpiTableInstance->Dsdt3 < BASE_4GB) { + AcpiTableInstance->Fadt3->Dsdt = (UINT32) (UINTN) AcpiTableInstance->Dsdt3; + // + // Comment block "the caller installs the tables in "DSDT, FADT" order" + // The below comments are also in "the caller installs the tables in "FADT, DSDT" order" comment block. + // + // The ACPI specification, up to and including revision 5.1 Errata A, + // allows the DSDT and X_DSDT fields to be both set in the FADT. + // (Obviously, this only makes sense if the DSDT address is representable in 4 bytes.) + // Starting with 5.1 Errata B, specifically for Mantis 1393 , + // the spec requires at most one of DSDT and X_DSDT fields to be set to a nonzero value, + // but strangely an exception is 6.0 that has no this requirement. + // + // Here we do not make the DSDT and X_DSDT fields mutual exclusion conditionally + // by checking FADT revision, but always set both DSDT and X_DSDT fields in the FADT + // to have better compatibility as some OS may have assumption to only consume X_DSDT + // field even the DSDT address is < 4G. + // + Buffer64 = AcpiTableInstance->Fadt3->Dsdt; + } else { + AcpiTableInstance->Fadt3->Dsdt = 0; + Buffer64 = (UINT64) (UINTN) AcpiTableInstance->Dsdt3; + } + CopyMem (&AcpiTableInstance->Fadt3->XDsdt, &Buffer64, sizeof (UINT64)); + + // + // RSDP OEM information is updated to match the FADT OEM information + // + CopyMem ( + &AcpiTableInstance->Rsdp3->OemId, + &AcpiTableInstance->Fadt3->Header.OemId, + 6 + ); + + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + // + // RSDT OEM information is updated to match FADT OEM information. + // + CopyMem ( + &AcpiTableInstance->Rsdt3->OemId, + &AcpiTableInstance->Fadt3->Header.OemId, + 6 + ); + CopyMem ( + &AcpiTableInstance->Rsdt3->OemTableId, + &AcpiTableInstance->Fadt3->Header.OemTableId, + sizeof (UINT64) + ); + AcpiTableInstance->Rsdt3->OemRevision = AcpiTableInstance->Fadt3->Header.OemRevision; + } + + // + // XSDT OEM information is updated to match FADT OEM information. + // + CopyMem ( + &AcpiTableInstance->Xsdt->OemId, + &AcpiTableInstance->Fadt3->Header.OemId, + 6 + ); + CopyMem ( + &AcpiTableInstance->Xsdt->OemTableId, + &AcpiTableInstance->Fadt3->Header.OemTableId, + sizeof (UINT64) + ); + AcpiTableInstance->Xsdt->OemRevision = AcpiTableInstance->Fadt3->Header.OemRevision; + } + // + // Checksum the table + // + if (Checksum) { + AcpiPlatformChecksum ( + CurrentTableList->Table, + CurrentTableList->Table->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + break; + + case EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE: + // + // Check that the table has not been previously added. + // + if (((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0 && AcpiTableInstance->Facs1 != NULL) || + ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0 && AcpiTableInstance->Facs3 != NULL) + ) { + gBS->FreePages (CurrentTableList->PageAddress, CurrentTableList->NumberOfPages); + gBS->FreePool (CurrentTableList); + return EFI_ACCESS_DENIED; + } + // + // FACS is referenced by FADT and is not part of RSDT + // + AddToRsdt = FALSE; + + // + // Add the table to the appropriate table version + // + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + // + // Save a pointer to the table + // + AcpiTableInstance->Facs1 = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) CurrentTableList->Table; + + // + // If FADT already exists, update table pointers. + // + if (AcpiTableInstance->Fadt1 != NULL) { + AcpiTableInstance->Fadt1->FirmwareCtrl = (UINT32) (UINTN) AcpiTableInstance->Facs1; + + // + // Checksum FADT table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt1, + AcpiTableInstance->Fadt1->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + // + // Save a pointer to the table + // + AcpiTableInstance->Facs3 = (EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) CurrentTableList->Table; + + // + // If FADT already exists, update table pointers. + // + if (AcpiTableInstance->Fadt3 != NULL) { + // + // Note: If the FIRMWARE_CTRL is non-zero, then X_FIRMWARE_CTRL must be zero, and + // vice-versa. + // + if ((UINT64)(UINTN)AcpiTableInstance->Facs3 < BASE_4GB) { + AcpiTableInstance->Fadt3->FirmwareCtrl = (UINT32) (UINTN) AcpiTableInstance->Facs3; + ZeroMem (&AcpiTableInstance->Fadt3->XFirmwareCtrl, sizeof (UINT64)); + } else { + Buffer64 = (UINT64) (UINTN) AcpiTableInstance->Facs3; + CopyMem ( + &AcpiTableInstance->Fadt3->XFirmwareCtrl, + &Buffer64, + sizeof (UINT64) + ); + AcpiTableInstance->Fadt3->FirmwareCtrl = 0; + } + + // + // Checksum FADT table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt3, + AcpiTableInstance->Fadt3->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + + break; + + case EFI_ACPI_1_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE: + // + // Check that the table has not been previously added. + // + if (((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0 && AcpiTableInstance->Dsdt1 != NULL) || + ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0 && AcpiTableInstance->Dsdt3 != NULL) + ) { + gBS->FreePages (CurrentTableList->PageAddress, CurrentTableList->NumberOfPages); + gBS->FreePool (CurrentTableList); + return EFI_ACCESS_DENIED; + } + // + // DSDT is referenced by FADT and is not part of RSDT + // + AddToRsdt = FALSE; + + // + // Add the table to the appropriate table version + // + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + // + // Save a pointer to the table + // + AcpiTableInstance->Dsdt1 = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTableList->Table; + + // + // If FADT already exists, update table pointers. + // + if (AcpiTableInstance->Fadt1 != NULL) { + AcpiTableInstance->Fadt1->Dsdt = (UINT32) (UINTN) AcpiTableInstance->Dsdt1; + + // + // Checksum FADT table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt1, + AcpiTableInstance->Fadt1->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + // + // Save a pointer to the table + // + AcpiTableInstance->Dsdt3 = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTableList->Table; + + // + // If FADT already exists, update table pointers. + // + if (AcpiTableInstance->Fadt3 != NULL) { + if ((UINT64)(UINTN)AcpiTableInstance->Dsdt3 < BASE_4GB) { + AcpiTableInstance->Fadt3->Dsdt = (UINT32) (UINTN) AcpiTableInstance->Dsdt3; + // + // Comment block "the caller installs the tables in "FADT, DSDT" order" + // The below comments are also in "the caller installs the tables in "DSDT, FADT" order" comment block. + // + // The ACPI specification, up to and including revision 5.1 Errata A, + // allows the DSDT and X_DSDT fields to be both set in the FADT. + // (Obviously, this only makes sense if the DSDT address is representable in 4 bytes.) + // Starting with 5.1 Errata B, specifically for Mantis 1393 , + // the spec requires at most one of DSDT and X_DSDT fields to be set to a nonzero value, + // but strangely an exception is 6.0 that has no this requirement. + // + // Here we do not make the DSDT and X_DSDT fields mutual exclusion conditionally + // by checking FADT revision, but always set both DSDT and X_DSDT fields in the FADT + // to have better compatibility as some OS may have assumption to only consume X_DSDT + // field even the DSDT address is < 4G. + // + Buffer64 = AcpiTableInstance->Fadt3->Dsdt; + } else { + AcpiTableInstance->Fadt3->Dsdt = 0; + Buffer64 = (UINT64) (UINTN) AcpiTableInstance->Dsdt3; + } + CopyMem (&AcpiTableInstance->Fadt3->XDsdt, &Buffer64, sizeof (UINT64)); + + // + // Checksum FADT table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt3, + AcpiTableInstance->Fadt3->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + // + // Checksum the table + // + if (Checksum) { + AcpiPlatformChecksum ( + CurrentTableList->Table, + CurrentTableList->Table->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + break; + + default: + // + // Checksum the table + // + if (Checksum) { + AcpiPlatformChecksum ( + CurrentTableList->Table, + CurrentTableList->Table->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + break; + } + // + // Add the table to the current list of tables + // + InsertTailList (&AcpiTableInstance->TableList, &CurrentTableList->Link); + + // + // Add the table to RSDT and/or XSDT table entry lists. + // + // + // Add to ACPI 1.0b table tree + // + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + if (AddToRsdt) { + // + // If the table number exceed the gEfiAcpiMaxNumTables, enlarge the table buffer + // + if (AcpiTableInstance->NumberOfTableEntries1 >= mEfiAcpiMaxNumTables) { + Status = ReallocateAcpiTableBuffer (AcpiTableInstance); + ASSERT_EFI_ERROR (Status); + } + CurrentRsdtEntry = (UINT32 *) + ( + (UINT8 *) AcpiTableInstance->Rsdt1 + + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + + AcpiTableInstance->NumberOfTableEntries1 * + sizeof (UINT32) + ); + + // + // Add entry to the RSDT unless its the FACS or DSDT + // + *CurrentRsdtEntry = (UINT32) (UINTN) CurrentTableList->Table; + + // + // Update RSDT length + // + AcpiTableInstance->Rsdt1->Length = AcpiTableInstance->Rsdt1->Length + sizeof (UINT32); + + AcpiTableInstance->NumberOfTableEntries1++; + } + } + // + // Add to ACPI 2.0/3.0 table tree + // + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + if (AddToRsdt) { + // + // If the table number exceed the gEfiAcpiMaxNumTables, enlarge the table buffer + // + if (AcpiTableInstance->NumberOfTableEntries3 >= mEfiAcpiMaxNumTables) { + Status = ReallocateAcpiTableBuffer (AcpiTableInstance); + ASSERT_EFI_ERROR (Status); + } + + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + // + // At this time, it is assumed that RSDT and XSDT maintain parallel lists of tables. + // If it becomes necessary to maintain separate table lists, changes will be required. + // + CurrentRsdtEntry = (UINT32 *) + ( + (UINT8 *) AcpiTableInstance->Rsdt3 + + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + + AcpiTableInstance->NumberOfTableEntries3 * + sizeof (UINT32) + ); + + // + // Add entry to the RSDT + // + *CurrentRsdtEntry = (UINT32) (UINTN) CurrentTableList->Table; + + // + // Update RSDT length + // + AcpiTableInstance->Rsdt3->Length = AcpiTableInstance->Rsdt3->Length + sizeof (UINT32); + } + + // + // This pointer must not be directly dereferenced as the XSDT entries may not + // be 64 bit aligned resulting in a possible fault. Use CopyMem to update. + // + CurrentXsdtEntry = (VOID *) + ( + (UINT8 *) AcpiTableInstance->Xsdt + + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + + AcpiTableInstance->NumberOfTableEntries3 * + sizeof (UINT64) + ); + + // + // Add entry to XSDT, XSDT expects 64 bit pointers, but + // the table pointers in XSDT are not aligned on 8 byte boundary. + // + Buffer64 = (UINT64) (UINTN) CurrentTableList->Table; + CopyMem ( + CurrentXsdtEntry, + &Buffer64, + sizeof (UINT64) + ); + + // + // Update length + // + AcpiTableInstance->Xsdt->Length = AcpiTableInstance->Xsdt->Length + sizeof (UINT64); + + AcpiTableInstance->NumberOfTableEntries3++; + } + } + + ChecksumCommonTables (AcpiTableInstance); + return EFI_SUCCESS; +} + + +/** + This function finds the table specified by the handle and returns a pointer to it. + If the handle is not found, EFI_NOT_FOUND is returned and the contents of Table are + undefined. + + @param Handle Table to find. + @param TableList Table list to search + @param Table Pointer to table found. + + @return EFI_SUCCESS The function completed successfully. + @return EFI_NOT_FOUND No table found matching the handle specified. + +**/ +EFI_STATUS +FindTableByHandle ( + IN UINTN Handle, + IN LIST_ENTRY *TableList, + OUT EFI_ACPI_TABLE_LIST **Table + ) +{ + LIST_ENTRY *CurrentLink; + EFI_ACPI_TABLE_LIST *CurrentTable; + + // + // Check for invalid input parameters + // + ASSERT (Table); + + // + // Find the table + // + CurrentLink = TableList->ForwardLink; + + while (CurrentLink != TableList) { + CurrentTable = EFI_ACPI_TABLE_LIST_FROM_LINK (CurrentLink); + if (CurrentTable->Handle == Handle) { + // + // Found handle, so return this table. + // + *Table = CurrentTable; + return EFI_SUCCESS; + } + + CurrentLink = CurrentLink->ForwardLink; + } + // + // Table not found + // + return EFI_NOT_FOUND; +} + + +/** + This function removes a basic table from the RSDT and/or XSDT. + For Acpi 1.0 tables, pass in the Rsdt. + For Acpi 2.0 tables, pass in both Rsdt and Xsdt. + + @param Table Pointer to table found. + @param NumberOfTableEntries Current number of table entries in the RSDT/XSDT + @param Rsdt Pointer to the RSDT to remove from + @param Xsdt Pointer to the Xsdt to remove from + + @return EFI_SUCCESS The function completed successfully. + @return EFI_INVALID_PARAMETER The table was not found in both Rsdt and Xsdt. + +**/ +EFI_STATUS +RemoveTableFromRsdt ( + IN OUT EFI_ACPI_TABLE_LIST * Table, + IN OUT UINTN *NumberOfTableEntries, + IN OUT EFI_ACPI_DESCRIPTION_HEADER * Rsdt OPTIONAL, + IN OUT EFI_ACPI_DESCRIPTION_HEADER * Xsdt OPTIONAL + ) +{ + UINT32 *CurrentRsdtEntry; + VOID *CurrentXsdtEntry; + UINT64 CurrentTablePointer64; + UINTN Index; + + // + // Check for invalid input parameters + // + ASSERT (Table); + ASSERT (NumberOfTableEntries); + ASSERT (Rsdt || Xsdt); + + // + // Find the table entry in the RSDT and XSDT + // + for (Index = 0; Index < *NumberOfTableEntries; Index++) { + // + // At this time, it is assumed that RSDT and XSDT maintain parallel lists of tables. + // If it becomes necessary to maintain separate table lists, changes will be required. + // + if (Rsdt != NULL) { + CurrentRsdtEntry = (UINT32 *) ((UINT8 *) Rsdt + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + Index * sizeof (UINT32)); + } else { + CurrentRsdtEntry = NULL; + } + if (Xsdt != NULL) { + // + // This pointer must not be directly dereferenced as the XSDT entries may not + // be 64 bit aligned resulting in a possible fault. Use CopyMem to update. + // + CurrentXsdtEntry = (VOID *) ((UINT8 *) Xsdt + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + Index * sizeof (UINT64)); + + // + // Read the entry value out of the XSDT + // + CopyMem (&CurrentTablePointer64, CurrentXsdtEntry, sizeof (UINT64)); + } else { + // + // Initialize to NULL + // + CurrentXsdtEntry = 0; + CurrentTablePointer64 = 0; + } + // + // Check if we have found the corresponding entry in both RSDT and XSDT + // + if (((Rsdt == NULL) || *CurrentRsdtEntry == (UINT32) (UINTN) Table->Table) && + ((Xsdt == NULL) || CurrentTablePointer64 == (UINT64) (UINTN) Table->Table) + ) { + // + // Found entry, so copy all following entries and shrink table + // We actually copy all + 1 to copy the initialized value of memory over + // the last entry. + // + if (Rsdt != NULL) { + CopyMem (CurrentRsdtEntry, CurrentRsdtEntry + 1, (*NumberOfTableEntries - Index) * sizeof (UINT32)); + Rsdt->Length = Rsdt->Length - sizeof (UINT32); + } + if (Xsdt != NULL) { + CopyMem (CurrentXsdtEntry, ((UINT64 *) CurrentXsdtEntry) + 1, (*NumberOfTableEntries - Index) * sizeof (UINT64)); + Xsdt->Length = Xsdt->Length - sizeof (UINT64); + } + break; + } else if (Index + 1 == *NumberOfTableEntries) { + // + // At the last entry, and table not found + // + return EFI_INVALID_PARAMETER; + } + } + // + // Checksum the tables + // + if (Rsdt != NULL) { + AcpiPlatformChecksum ( + Rsdt, + Rsdt->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + + if (Xsdt != NULL) { + AcpiPlatformChecksum ( + Xsdt, + Xsdt->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + // + // Decrement the number of tables + // + (*NumberOfTableEntries)--; + + return EFI_SUCCESS; +} + + +/** + This function removes a table and frees any associated memory. + + @param AcpiTableInstance Instance of the protocol. + @param Version Version(s) to delete. + @param Table Pointer to table found. + + @return EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +DeleteTable ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN EFI_ACPI_TABLE_VERSION Version, + IN OUT EFI_ACPI_TABLE_LIST *Table + ) +{ + UINT32 CurrentTableSignature; + BOOLEAN RemoveFromRsdt; + + // + // Check for invalid input parameters + // + ASSERT (AcpiTableInstance); + ASSERT (Table); + + // + // Init locals + // + RemoveFromRsdt = TRUE; + // + // Check for Table->Table + // + ASSERT (Table->Table != NULL); + CurrentTableSignature = ((EFI_ACPI_COMMON_HEADER *) Table->Table)->Signature; + + // + // Basic tasks to accomplish delete are: + // Determine removal requirements (in RSDT/XSDT or not) + // Remove entry from RSDT/XSDT + // Remove any table references to the table + // If no one is using the table + // Free the table (removing pointers from private data and tables) + // Remove from list + // Free list structure + // + // + // Determine if this table is in the RSDT or XSDT + // + if ((CurrentTableSignature == EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) || + (CurrentTableSignature == EFI_ACPI_2_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) || + (CurrentTableSignature == EFI_ACPI_3_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) + ) { + RemoveFromRsdt = FALSE; + } + // + // We don't remove the FADT in the standard way because some + // OS expect the FADT to be early in the table list. + // So we always put it as the first element in the list. + // + if (CurrentTableSignature == EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) { + RemoveFromRsdt = FALSE; + } + + // + // Remove the table from RSDT and XSDT + // + if (Table->Table != NULL) { + // + // This is a basic table, remove it from any lists and the Rsdt and/or Xsdt + // + if (Version & EFI_ACPI_TABLE_VERSION_NONE & Table->Version) { + // + // Remove this version from the table + // + Table->Version = Table->Version &~EFI_ACPI_TABLE_VERSION_NONE; + } + + if (Version & EFI_ACPI_TABLE_VERSION_1_0B & Table->Version) { + // + // Remove this version from the table + // + Table->Version = Table->Version &~EFI_ACPI_TABLE_VERSION_1_0B; + + // + // Remove from Rsdt. We don't care about the return value because it is + // acceptable for the table to not exist in Rsdt. + // We didn't add some tables so we don't remove them. + // + if (RemoveFromRsdt) { + RemoveTableFromRsdt ( + Table, + &AcpiTableInstance->NumberOfTableEntries1, + AcpiTableInstance->Rsdt1, + NULL + ); + } + } + + if (Version & ACPI_TABLE_VERSION_GTE_2_0 & Table->Version) { + // + // Remove this version from the table + // + Table->Version = Table->Version &~(Version & ACPI_TABLE_VERSION_GTE_2_0); + + // + // Remove from Rsdt and Xsdt. We don't care about the return value + // because it is acceptable for the table to not exist in Rsdt/Xsdt. + // We didn't add some tables so we don't remove them. + // + if (RemoveFromRsdt) { + RemoveTableFromRsdt ( + Table, + &AcpiTableInstance->NumberOfTableEntries3, + AcpiTableInstance->Rsdt3, + AcpiTableInstance->Xsdt + ); + } + } + // + // Free the table, clean up any dependent tables and our private data pointers. + // + switch (Table->Table->Signature) { + + case EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE: + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + AcpiTableInstance->Fadt1 = NULL; + } + + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + AcpiTableInstance->Fadt3 = NULL; + } + break; + + case EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE: + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + AcpiTableInstance->Facs1 = NULL; + + // + // Update FADT table pointers + // + if (AcpiTableInstance->Fadt1 != NULL) { + AcpiTableInstance->Fadt1->FirmwareCtrl = 0; + + // + // Checksum table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt1, + AcpiTableInstance->Fadt1->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + AcpiTableInstance->Facs3 = NULL; + + // + // Update FADT table pointers + // + if (AcpiTableInstance->Fadt3 != NULL) { + AcpiTableInstance->Fadt3->FirmwareCtrl = 0; + ZeroMem (&AcpiTableInstance->Fadt3->XFirmwareCtrl, sizeof (UINT64)); + + // + // Checksum table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt3, + AcpiTableInstance->Fadt3->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + break; + + case EFI_ACPI_3_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE: + if ((Version & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + AcpiTableInstance->Dsdt1 = NULL; + + // + // Update FADT table pointers + // + if (AcpiTableInstance->Fadt1 != NULL) { + AcpiTableInstance->Fadt1->Dsdt = 0; + + // + // Checksum table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt1, + AcpiTableInstance->Fadt1->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + + + if ((Version & ACPI_TABLE_VERSION_GTE_2_0) != 0) { + AcpiTableInstance->Dsdt3 = NULL; + + // + // Update FADT table pointers + // + if (AcpiTableInstance->Fadt3 != NULL) { + AcpiTableInstance->Fadt3->Dsdt = 0; + ZeroMem (&AcpiTableInstance->Fadt3->XDsdt, sizeof (UINT64)); + + // + // Checksum table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Fadt3, + AcpiTableInstance->Fadt3->Header.Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + } + break; + + default: + // + // Do nothing + // + break; + } + } + // + // If no version is using this table anymore, remove and free list entry. + // + if (Table->Version == 0) { + // + // Free the Table + // + gBS->FreePages (Table->PageAddress, Table->NumberOfPages); + RemoveEntryList (&(Table->Link)); + gBS->FreePool (Table); + } + // + // Done + // + return EFI_SUCCESS; +} + + +/** + This function finds and removes the table specified by the handle. + + @param AcpiTableInstance Instance of the protocol. + @param Version Bitmask of which versions to remove. + @param Handle Table to remove. + + @return EFI_SUCCESS The function completed successfully. + @return EFI_ABORTED An error occurred. + @return EFI_NOT_FOUND Handle not found in table list. + +**/ +EFI_STATUS +RemoveTableFromList ( + IN EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance, + IN EFI_ACPI_TABLE_VERSION Version, + IN UINTN Handle + ) +{ + EFI_ACPI_TABLE_LIST *Table; + EFI_STATUS Status; + + Table = (EFI_ACPI_TABLE_LIST*) NULL; + + // + // Check for invalid input parameters + // + ASSERT (AcpiTableInstance); + + // + // Find the table + // + Status = FindTableByHandle ( + Handle, + &AcpiTableInstance->TableList, + &Table + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + // + // Remove the table + // + Status = DeleteTable (AcpiTableInstance, Version, Table); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Completed successfully + // + return EFI_SUCCESS; +} + + +/** + This function calculates and updates an UINT8 checksum. + + @param Buffer Pointer to buffer to checksum + @param Size Number of bytes to checksum + @param ChecksumOffset Offset to place the checksum result in + + @return EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +AcpiPlatformChecksum ( + IN VOID *Buffer, + IN UINTN Size, + IN UINTN ChecksumOffset + ) +{ + UINT8 Sum; + UINT8 *Ptr; + + Sum = 0; + // + // Initialize pointer + // + Ptr = Buffer; + + // + // set checksum to 0 first + // + Ptr[ChecksumOffset] = 0; + + // + // add all content of buffer + // + while ((Size--) != 0) { + Sum = (UINT8) (Sum + (*Ptr++)); + } + // + // set checksum + // + Ptr = Buffer; + Ptr[ChecksumOffset] = (UINT8) (0xff - Sum + 1); + + return EFI_SUCCESS; +} + + +/** + Checksum all versions of the common tables, RSDP, RSDT, XSDT. + + @param AcpiTableInstance Protocol instance private data. + + @return EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +ChecksumCommonTables ( + IN OUT EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance + ) +{ + // + // RSDP ACPI 1.0 checksum for 1.0 table. This is only the first 20 bytes of the structure + // + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + AcpiPlatformChecksum ( + AcpiTableInstance->Rsdp1, + sizeof (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER), + OFFSET_OF (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER, + Checksum) + ); + } + + // + // RSDP ACPI 1.0 checksum for 2.0/3.0 table. This is only the first 20 bytes of the structure + // + AcpiPlatformChecksum ( + AcpiTableInstance->Rsdp3, + sizeof (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER), + OFFSET_OF (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER, + Checksum) + ); + + // + // RSDP ACPI 2.0/3.0 checksum, this is the entire table + // + AcpiPlatformChecksum ( + AcpiTableInstance->Rsdp3, + sizeof (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER), + OFFSET_OF (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER, + ExtendedChecksum) + ); + + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + // + // RSDT checksums + // + AcpiPlatformChecksum ( + AcpiTableInstance->Rsdt1, + AcpiTableInstance->Rsdt1->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + + AcpiPlatformChecksum ( + AcpiTableInstance->Rsdt3, + AcpiTableInstance->Rsdt3->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + } + + // + // XSDT checksum + // + AcpiPlatformChecksum ( + AcpiTableInstance->Xsdt, + AcpiTableInstance->Xsdt->Length, + OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, + Checksum) + ); + + return EFI_SUCCESS; +} + + +/** + Constructor for the ACPI table protocol. Initializes instance + data. + + @param AcpiTableInstance Instance to construct + + @return EFI_SUCCESS Instance initialized. + @return EFI_OUT_OF_RESOURCES Unable to allocate required resources. + +**/ +EFI_STATUS +AcpiTableAcpiTableConstructor ( + EFI_ACPI_TABLE_INSTANCE *AcpiTableInstance + ) +{ + EFI_STATUS Status; + UINT64 CurrentData; + UINTN TotalSize; + UINTN RsdpTableSize; + UINT8 *Pointer; + EFI_PHYSICAL_ADDRESS PageAddress; + + // + // Check for invalid input parameters + // + ASSERT (AcpiTableInstance); + + // + // If ACPI v1.0b is among the ACPI versions we aim to support, we have to + // ensure that all memory allocations are below 4 GB. + // + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + mAcpiTableAllocType = AllocateMaxAddress; + } else { + mAcpiTableAllocType = AllocateAnyPages; + } + + InitializeListHead (&AcpiTableInstance->TableList); + AcpiTableInstance->CurrentHandle = 1; + + AcpiTableInstance->AcpiTableProtocol.InstallAcpiTable = InstallAcpiTable; + AcpiTableInstance->AcpiTableProtocol.UninstallAcpiTable = UninstallAcpiTable; + + if (FeaturePcdGet (PcdInstallAcpiSdtProtocol)) { + SdtAcpiTableAcpiSdtConstructor (AcpiTableInstance); + } + + // + // Create RSDP table + // + RsdpTableSize = sizeof (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER); + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + RsdpTableSize += sizeof (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER); + } + + PageAddress = 0xFFFFFFFF; + Status = gBS->AllocatePages ( + mAcpiTableAllocType, + EfiACPIReclaimMemory, + EFI_SIZE_TO_PAGES (RsdpTableSize), + &PageAddress + ); + + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + Pointer = (UINT8 *) (UINTN) PageAddress; + ZeroMem (Pointer, RsdpTableSize); + + AcpiTableInstance->Rsdp1 = (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER *) Pointer; + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + Pointer += sizeof (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER); + } + AcpiTableInstance->Rsdp3 = (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *) Pointer; + + // + // Create RSDT, XSDT structures + // + TotalSize = sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 2.0/3.0 XSDT + mEfiAcpiMaxNumTables * sizeof (UINT64); + + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + TotalSize += sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 1.0 RSDT + mEfiAcpiMaxNumTables * sizeof (UINT32) + + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + // for ACPI 2.0/3.0 RSDT + mEfiAcpiMaxNumTables * sizeof (UINT32); + } + + // + // Allocate memory in the lower 32 bit of address range for + // compatibility with ACPI 1.0 OS. + // + // This is done because ACPI 1.0 pointers are 32 bit values. + // ACPI 2.0 OS and all 64 bit OS must use the 64 bit ACPI table addresses. + // There is no architectural reason these should be below 4GB, it is purely + // for convenience of implementation that we force memory below 4GB. + // + PageAddress = 0xFFFFFFFF; + Status = gBS->AllocatePages ( + mAcpiTableAllocType, + EfiACPIReclaimMemory, + EFI_SIZE_TO_PAGES (TotalSize), + &PageAddress + ); + + if (EFI_ERROR (Status)) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)AcpiTableInstance->Rsdp1, EFI_SIZE_TO_PAGES (RsdpTableSize)); + return EFI_OUT_OF_RESOURCES; + } + + Pointer = (UINT8 *) (UINTN) PageAddress; + ZeroMem (Pointer, TotalSize); + + AcpiTableInstance->Rsdt1 = (EFI_ACPI_DESCRIPTION_HEADER *) Pointer; + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + Pointer += (sizeof (EFI_ACPI_DESCRIPTION_HEADER) + EFI_ACPI_MAX_NUM_TABLES * sizeof (UINT32)); + AcpiTableInstance->Rsdt3 = (EFI_ACPI_DESCRIPTION_HEADER *) Pointer; + Pointer += (sizeof (EFI_ACPI_DESCRIPTION_HEADER) + EFI_ACPI_MAX_NUM_TABLES * sizeof (UINT32)); + } + AcpiTableInstance->Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) Pointer; + + // + // Initialize RSDP + // + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + CurrentData = EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE; + CopyMem (&AcpiTableInstance->Rsdp1->Signature, &CurrentData, sizeof (UINT64)); + CopyMem (AcpiTableInstance->Rsdp1->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (AcpiTableInstance->Rsdp1->OemId)); + AcpiTableInstance->Rsdp1->Reserved = EFI_ACPI_RESERVED_BYTE; + AcpiTableInstance->Rsdp1->RsdtAddress = (UINT32) (UINTN) AcpiTableInstance->Rsdt1; + } + + CurrentData = EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE; + CopyMem (&AcpiTableInstance->Rsdp3->Signature, &CurrentData, sizeof (UINT64)); + CopyMem (AcpiTableInstance->Rsdp3->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (AcpiTableInstance->Rsdp3->OemId)); + AcpiTableInstance->Rsdp3->Revision = EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION; + AcpiTableInstance->Rsdp3->Length = sizeof (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER); + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + AcpiTableInstance->Rsdp3->RsdtAddress = (UINT32) (UINTN) AcpiTableInstance->Rsdt3; + } + CurrentData = (UINT64) (UINTN) AcpiTableInstance->Xsdt; + CopyMem (&AcpiTableInstance->Rsdp3->XsdtAddress, &CurrentData, sizeof (UINT64)); + SetMem (AcpiTableInstance->Rsdp3->Reserved, 3, EFI_ACPI_RESERVED_BYTE); + + if ((PcdGet32 (PcdAcpiExposedTableVersions) & EFI_ACPI_TABLE_VERSION_1_0B) != 0) { + // + // Initialize Rsdt + // + // Note that we "reserve" one entry for the FADT so it can always be + // at the beginning of the list of tables. Some OS don't seem + // to find it correctly if it is too far down the list. + // + AcpiTableInstance->Rsdt1->Signature = EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE; + AcpiTableInstance->Rsdt1->Length = sizeof (EFI_ACPI_DESCRIPTION_HEADER); + AcpiTableInstance->Rsdt1->Revision = EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_REVISION; + CopyMem (AcpiTableInstance->Rsdt1->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (AcpiTableInstance->Rsdt1->OemId)); + CurrentData = PcdGet64 (PcdAcpiDefaultOemTableId); + CopyMem (&AcpiTableInstance->Rsdt1->OemTableId, &CurrentData, sizeof (UINT64)); + AcpiTableInstance->Rsdt1->OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + AcpiTableInstance->Rsdt1->CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + AcpiTableInstance->Rsdt1->CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + // + // We always reserve first one for FADT + // + AcpiTableInstance->NumberOfTableEntries1 = 1; + AcpiTableInstance->Rsdt1->Length = AcpiTableInstance->Rsdt1->Length + sizeof(UINT32); + + AcpiTableInstance->Rsdt3->Signature = EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE; + AcpiTableInstance->Rsdt3->Length = sizeof (EFI_ACPI_DESCRIPTION_HEADER); + AcpiTableInstance->Rsdt3->Revision = EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_TABLE_REVISION; + CopyMem (AcpiTableInstance->Rsdt3->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (AcpiTableInstance->Rsdt3->OemId)); + CurrentData = PcdGet64 (PcdAcpiDefaultOemTableId); + CopyMem (&AcpiTableInstance->Rsdt3->OemTableId, &CurrentData, sizeof (UINT64)); + AcpiTableInstance->Rsdt3->OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + AcpiTableInstance->Rsdt3->CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + AcpiTableInstance->Rsdt3->CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + // + // We always reserve first one for FADT + // + AcpiTableInstance->Rsdt3->Length = AcpiTableInstance->Rsdt3->Length + sizeof(UINT32); + } + AcpiTableInstance->NumberOfTableEntries3 = 1; + + // + // Initialize Xsdt + // + AcpiTableInstance->Xsdt->Signature = EFI_ACPI_3_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE; + AcpiTableInstance->Xsdt->Length = sizeof (EFI_ACPI_DESCRIPTION_HEADER); + AcpiTableInstance->Xsdt->Revision = EFI_ACPI_3_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_REVISION; + CopyMem (AcpiTableInstance->Xsdt->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (AcpiTableInstance->Xsdt->OemId)); + CurrentData = PcdGet64 (PcdAcpiDefaultOemTableId); + CopyMem (&AcpiTableInstance->Xsdt->OemTableId, &CurrentData, sizeof (UINT64)); + AcpiTableInstance->Xsdt->OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + AcpiTableInstance->Xsdt->CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + AcpiTableInstance->Xsdt->CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + // + // We always reserve first one for FADT + // + AcpiTableInstance->Xsdt->Length = AcpiTableInstance->Xsdt->Length + sizeof(UINT64); + + ChecksumCommonTables (AcpiTableInstance); + + // + // Completed successfully + // + return EFI_SUCCESS; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/Aml.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/Aml.c new file mode 100644 index 000000000..368805674 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/Aml.c @@ -0,0 +1,296 @@ +/** @file + ACPI Sdt Protocol Driver + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "AcpiTable.h" + +GLOBAL_REMOVE_IF_UNREFERENCED +AML_BYTE_ENCODING mAmlByteEncoding[] = { + // OpCode SubOpCode Num 1 2 3 4 5 6 Attribute +/* ZeroOp - 0x00 */ {AML_ZERO_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* OneOp - 0x01 */ {AML_ONE_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* AliasOp - 0x06 */ {AML_ALIAS_OP, 0, 2, {AML_NAME, AML_NAME, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IN_NAMESPACE}, +/* NameOp - 0x08 */ {AML_NAME_OP, 0, 2, {AML_NAME, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IN_NAMESPACE}, +/* BytePrefix - 0x0A */ {AML_BYTE_PREFIX, 0, 1, {AML_UINT8, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* WordPrefix - 0x0B */ {AML_WORD_PREFIX, 0, 1, {AML_UINT16, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* DWordPrefix - 0x0C */ {AML_DWORD_PREFIX, 0, 1, {AML_UINT32, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* StringPrefix - 0x0D */ {AML_STRING_PREFIX, 0, 1, {AML_STRING, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* QWordPrefix - 0x0E */ {AML_QWORD_PREFIX, 0, 1, {AML_UINT64, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ScopeOp - 0x10 */ {AML_SCOPE_OP, 0, 1, {AML_NAME, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, +/* BufferOp - 0x11 */ {AML_BUFFER_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH}, +/* PackageOp - 0x12 */ {AML_PACKAGE_OP, 0, 1, {AML_UINT8, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, +/* VarPackageOp - 0x13 */ {AML_VAR_PACKAGE_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, +/* MethodOp - 0x14 */ {AML_METHOD_OP, 0, 2, {AML_NAME, AML_UINT8, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, +/* DualNamePrefix - 0x2F */ {AML_DUAL_NAME_PREFIX, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* MultiNamePrefix - 0x2F */ {AML_MULTI_NAME_PREFIX, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x41 */ {'A', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x42 */ {'B', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x43 */ {'C', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x44 */ {'D', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x45 */ {'E', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x46 */ {'F', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x47 */ {'G', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x48 */ {'H', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x49 */ {'I', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x4A */ {'J', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x4B */ {'K', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x4C */ {'L', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x4D */ {'M', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x4E */ {'N', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x4F */ {'O', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x50 */ {'P', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x51 */ {'Q', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x52 */ {'R', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x53 */ {'S', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x54 */ {'T', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x55 */ {'U', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x56 */ {'V', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x57 */ {'W', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x58 */ {'X', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x59 */ {'Y', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x5A */ {'Z', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* MutexOp - 0x5B 0x01 */ {AML_EXT_OP, AML_EXT_MUTEX_OP, 2, {AML_NAME, AML_UINT8, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IN_NAMESPACE}, +/* EventOp - 0x5B 0x02 */ {AML_EXT_OP, AML_EXT_EVENT_OP, 1, {AML_NAME, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IN_NAMESPACE}, +/* CondRefOfOp - 0x5B 0x12 */ {AML_EXT_OP, AML_EXT_COND_REF_OF_OP, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* CreateFieldOp - 0x5B 0x13 */ {AML_EXT_OP, AML_EXT_CREATE_FIELD_OP,4, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NAME, AML_NONE, AML_NONE}, 0}, +/* LoadTableOp - 0x5B 0x1F */ {AML_EXT_OP, AML_EXT_LOAD_TABLE_OP, 6, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_OBJECT}, 0}, +/* LoadOp - 0x5B 0x20 */ {AML_EXT_OP, AML_EXT_LOAD_OP, 2, {AML_NAME, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* StallOp - 0x5B 0x21 */ {AML_EXT_OP, AML_EXT_STALL_OP, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* SleepOp - 0x5B 0x22 */ {AML_EXT_OP, AML_EXT_SLEEP_OP, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* AcquireOp - 0x5B 0x23 */ {AML_EXT_OP, AML_EXT_ACQUIRE_OP, 2, {AML_OBJECT, AML_UINT16, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* SignalOp - 0x5B 0x24 */ {AML_EXT_OP, AML_EXT_SIGNAL_OP, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* WaitOp - 0x5B 0x25 */ {AML_EXT_OP, AML_EXT_WAIT_OP, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ResetOp - 0x5B 0x26 */ {AML_EXT_OP, AML_EXT_RESET_OP, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ReleaseOp - 0x5B 0x27 */ {AML_EXT_OP, AML_EXT_RELEASE_OP, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* FromBCDOp - 0x5B 0x28 */ {AML_EXT_OP, AML_EXT_FROM_BCD_OP, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ToBCDOp - 0x5B 0x29 */ {AML_EXT_OP, AML_EXT_TO_BCD_OP, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* UnloadOp - 0x5B 0x2A */ {AML_EXT_OP, AML_EXT_UNLOAD_OP, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* RevisionOp - 0x5B 0x30 */ {AML_EXT_OP, AML_EXT_REVISION_OP, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* DebugOp - 0x5B 0x31 */ {AML_EXT_OP, AML_EXT_DEBUG_OP, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* FatalOp - 0x5B 0x32 */ {AML_EXT_OP, AML_EXT_FATAL_OP, 3, {AML_UINT8, AML_UINT32, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* TimerOp - 0x5B 0x33 */ {AML_EXT_OP, AML_EXT_TIMER_OP, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* OpRegionOp - 0x5B 0x80 */ {AML_EXT_OP, AML_EXT_REGION_OP, 4, {AML_NAME, AML_UINT8, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE}, AML_IN_NAMESPACE}, +/* FieldOp - 0x5B 0x81 */ {AML_EXT_OP, AML_EXT_FIELD_OP, 2, {AML_NAME, AML_UINT8, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH}, +/* DeviceOp - 0x5B 0x82 */ {AML_EXT_OP, AML_EXT_DEVICE_OP, 1, {AML_NAME, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, +/* ProcessorOp - 0x5B 0x83 */ {AML_EXT_OP, AML_EXT_PROCESSOR_OP, 4, {AML_NAME, AML_UINT8, AML_UINT32, AML_UINT8, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, +/* PowerResOp - 0x5B 0x84 */ {AML_EXT_OP, AML_EXT_POWER_RES_OP, 3, {AML_NAME, AML_UINT8, AML_UINT16, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, +/* ThermalZoneOp - 0x5B 0x85 */ {AML_EXT_OP, AML_EXT_THERMAL_ZONE_OP,1, {AML_NAME, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ | AML_IN_NAMESPACE}, +/* IndexFieldOp - 0x5B 0x86 */ {AML_EXT_OP, AML_EXT_INDEX_FIELD_OP, 3, {AML_NAME, AML_NAME, AML_UINT8, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH}, +/* BankFieldOp - 0x5B 0x87 */ {AML_EXT_OP, AML_EXT_BANK_FIELD_OP, 4, {AML_NAME, AML_NAME, AML_OBJECT, AML_UINT8, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH}, +/* DataRegionOp - 0x5B 0x88 */ {AML_EXT_OP, AML_EXT_DATA_REGION_OP, 4, {AML_NAME, AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE}, AML_IN_NAMESPACE}, +/* RootChar - 0x5C */ {AML_ROOT_CHAR, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* ParentPrefixChar - 0x5E */ {AML_PARENT_PREFIX_CHAR, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* NameChar - 0x5F */ {'_', 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_IS_NAME_CHAR}, +/* Local0Op - 0x60 */ {AML_LOCAL0, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Local1Op - 0x61 */ {AML_LOCAL1, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Local2Op - 0x62 */ {AML_LOCAL2, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Local3Op - 0x63 */ {AML_LOCAL3, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Local4Op - 0x64 */ {AML_LOCAL4, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Local5Op - 0x65 */ {AML_LOCAL5, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Local6Op - 0x66 */ {AML_LOCAL6, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Local7Op - 0x67 */ {AML_LOCAL7, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Arg0Op - 0x68 */ {AML_ARG0, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Arg1Op - 0x69 */ {AML_ARG1, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Arg2Op - 0x6A */ {AML_ARG2, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Arg3Op - 0x6B */ {AML_ARG3, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Arg4Op - 0x6C */ {AML_ARG4, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Arg5Op - 0x6D */ {AML_ARG5, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* Arg6Op - 0x6E */ {AML_ARG6, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* StoreOp - 0x70 */ {AML_STORE_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* RefOfOp - 0x71 */ {AML_REF_OF_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* AddOp - 0x72 */ {AML_ADD_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ConcatOp - 0x73 */ {AML_CONCAT_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* SubtractOp - 0x74 */ {AML_SUBTRACT_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* IncrementOp - 0x75 */ {AML_INCREMENT_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* DecrementOp - 0x76 */ {AML_DECREMENT_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* MultiplyOp - 0x77 */ {AML_MULTIPLY_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* DivideOp - 0x78 */ {AML_DIVIDE_OP, 0, 4, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE}, 0}, +/* ShiftLeftOp - 0x79 */ {AML_SHIFT_LEFT_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ShiftRightOp - 0x7A */ {AML_SHIFT_RIGHT_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* AndOp - 0x7B */ {AML_AND_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* NAndOp - 0x7C */ {AML_NAND_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* OrOp - 0x7D */ {AML_OR_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* NorOp - 0x7E */ {AML_NOR_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* XOrOp - 0x7F */ {AML_XOR_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* NotOp - 0x80 */ {AML_NOT_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* FindSetLeftBitOp - 0x81 */ {AML_FIND_SET_LEFT_BIT_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* FindSetRightBitOp - 0x82 */ {AML_FIND_SET_RIGHT_BIT_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* DerefOfOp - 0x83 */ {AML_DEREF_OF_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ConcatResOp - 0x84 */ {AML_CONCAT_RES_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ModOp - 0x85 */ {AML_MOD_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* NotifyOp - 0x86 */ {AML_NOTIFY_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* SizeOfOp - 0x87 */ {AML_SIZE_OF_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* IndexOp - 0x88 */ {AML_INDEX_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* MatchOp - 0x89 */ {AML_MATCH_OP, 0, 6, {AML_OBJECT, AML_UINT8, AML_OBJECT, AML_UINT8, AML_OBJECT, AML_OBJECT}, 0}, +/* CreateDWordFieldOp - 0x8A */ {AML_CREATE_DWORD_FIELD_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_NAME, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* CreateWordFieldOp - 0x8B */ {AML_CREATE_WORD_FIELD_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_NAME, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* CreateByteFieldOp - 0x8C */ {AML_CREATE_BYTE_FIELD_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_NAME, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* CreateBitFieldOp - 0x8D */ {AML_CREATE_BIT_FIELD_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_NAME, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ObjectTypeOp - 0x8E */ {AML_OBJECT_TYPE_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* CreateQWordFieldOp - 0x8F */ {AML_CREATE_QWORD_FIELD_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_NAME, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* LAndOp - 0x90 */ {AML_LAND_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* LOrOp - 0x91 */ {AML_LOR_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* LNotOp - 0x92 */ {AML_LNOT_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* LEqualOp - 0x93 */ {AML_LEQUAL_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* LGreaterOp - 0x94 */ {AML_LGREATER_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* LLessOp - 0x95 */ {AML_LLESS_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ToBufferOp - 0x96 */ {AML_TO_BUFFER_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ToDecimalStringOp - 0x97 */ {AML_TO_DEC_STRING_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ToHexStringOp - 0x98 */ {AML_TO_HEX_STRING_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ToIntegerOp - 0x99 */ {AML_TO_INTEGER_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ToStringOp - 0x9C */ {AML_TO_STRING_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* CopyObjectOp - 0x9D */ {AML_COPY_OBJECT_OP, 0, 2, {AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* MidOp - 0x9E */ {AML_MID_OP, 0, 3, {AML_OBJECT, AML_OBJECT, AML_OBJECT, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ContinueOp - 0x9F */ {AML_CONTINUE_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* IfOp - 0xA0 */ {AML_IF_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, +/* ElseOp - 0xA1 */ {AML_ELSE_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, +/* WhileOp - 0xA2 */ {AML_WHILE_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, AML_HAS_PKG_LENGTH | AML_HAS_CHILD_OBJ}, +/* NoopOp - 0xA3 */ {AML_NOOP_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* ReturnOp - 0xA4 */ {AML_RETURN_OP, 0, 1, {AML_OBJECT, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* BreakOp - 0xA5 */ {AML_BREAK_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* BreakPointOp - 0xCC */ {AML_BREAK_POINT_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +/* OnesOp - 0xFF */ {AML_ONES_OP, 0, 0, {AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE, AML_NONE}, 0}, +}; + +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_ACPI_DATA_TYPE mAmlTypeToAcpiType[] = { + EFI_ACPI_DATA_TYPE_NONE, // AML_NONE + EFI_ACPI_DATA_TYPE_OPCODE, // AML_OPCODE + EFI_ACPI_DATA_TYPE_UINT, // AML_UINT8 + EFI_ACPI_DATA_TYPE_UINT, // AML_UINT16 + EFI_ACPI_DATA_TYPE_UINT, // AML_UINT32 + EFI_ACPI_DATA_TYPE_UINT, // AML_UINT64 + EFI_ACPI_DATA_TYPE_NAME_STRING, // AML_NAME + EFI_ACPI_DATA_TYPE_STRING, // AML_STRING + EFI_ACPI_DATA_TYPE_CHILD // AML_OBJECT +}; + +/** + This function returns AmlByteEncoding according to OpCode Byte. + + @param[in] OpByteBuffer OpCode byte buffer. + + @return AmlByteEncoding +**/ +AML_BYTE_ENCODING * +AmlSearchByOpByte ( + IN UINT8 *OpByteBuffer + ) +{ + UINT8 OpCode; + UINT8 SubOpCode; + UINTN Index; + + // + // Get OpCode and SubOpCode + // + OpCode = OpByteBuffer[0]; + if (OpCode == AML_EXT_OP) { + SubOpCode = OpByteBuffer[1]; + } else { + SubOpCode = 0; + } + + // + // Search the table + // + for (Index = 0; Index < sizeof(mAmlByteEncoding)/sizeof(mAmlByteEncoding[0]); Index++) { + if ((mAmlByteEncoding[Index].OpCode == OpCode) && (mAmlByteEncoding[Index].SubOpCode == SubOpCode)) { + return &mAmlByteEncoding[Index]; + } + } + + return NULL; +} + +/** + This function returns AcpiDataType according to AmlType. + + @param[in] AmlType AML Type. + + @return AcpiDataType +**/ +EFI_ACPI_DATA_TYPE +AmlTypeToAcpiType ( + IN AML_OP_PARSE_FORMAT AmlType + ) +{ + if (AmlType >= sizeof(mAmlTypeToAcpiType)/sizeof(mAmlTypeToAcpiType[0])) { + ASSERT(FALSE); + return EFI_ACPI_DATA_TYPE_NONE; + } + return mAmlTypeToAcpiType [AmlType]; +} + +/** + This function retuns package length from the buffer. + + @param[in] Buffer AML buffer + @param[out] PkgLength The total length of package. + + @return The byte data count to present the package length. +**/ +UINTN +AmlGetPkgLength ( + IN UINT8 *Buffer, + OUT UINTN *PkgLength + ) +{ + UINT8 LeadByte; + UINT8 ByteCount; + UINTN RealLength; + UINTN Offset; + + // + // + // + // + // + // Note: The high 2 bits of the first byte reveal how many follow bytes are in the + // If the PkgLength has only one byte, bit 0 through 5 are used to encode the + // package length (in other words, values 0-63). If the package length value is more than + // 63, more than one byte must be used for the encoding in which case bit 4 and 5 of the + // PkgLeadByte are reserved and must be zero. If the multiple bytes encoding is used, + // bits 0-3 of the PkgLeadByte become the least significant 4 bits of the resulting + // package length value. The next ByteData will become the next least significant 8 bits + // of the resulting value and so on, up to 3 ByteData bytes. Thus, the maximum package + // length is 2**28. + // + + LeadByte = *Buffer; + ByteCount = (UINT8)((LeadByte >> 6) & 0x03); + Offset = ByteCount + 1; + RealLength = 0; + + switch (ByteCount) { + case 0: + RealLength = (UINT32)LeadByte; + break; + case 1: + RealLength = *(Buffer + 1); + RealLength = (RealLength << 4) | (LeadByte & 0xF); + break; + case 2: + RealLength = *(Buffer + 1); + RealLength |= (UINTN)((*(Buffer + 2)) << 8); + RealLength = (RealLength << 4) | (LeadByte & 0xF); + break; + case 3: + RealLength = *(Buffer + 1); + RealLength |= (UINTN)((*(Buffer + 2)) << 8); + RealLength |= (UINTN)((*(Buffer + 3)) << 16); + RealLength = (RealLength << 4) | (LeadByte & 0xF); + break; + default: + ASSERT (0); + break; + } + + *PkgLength = RealLength; + return Offset; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlChild.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlChild.c new file mode 100644 index 000000000..562271807 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlChild.c @@ -0,0 +1,274 @@ +/** @file + ACPI Sdt Protocol Driver + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "AcpiTable.h" + +/** + Return the child objects buffer from AML Handle's buffer. + + @param[in] AmlParentHandle Parent handle. + @param[in] CurrentBuffer The current child buffer. + @param[out] Buffer On return, points to the next returned child buffer or NULL if there are no + child buffer. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER AmlParentHandle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetChildFromObjectBuffer ( + IN EFI_AML_HANDLE *AmlParentHandle, + IN UINT8 *CurrentBuffer, + OUT VOID **Buffer + ) +{ + AML_BYTE_ENCODING *AmlByteEncoding; + UINTN DataSize; + + // + // Root is considered as SCOPE, which has TermList. + // We need return only Object in TermList. + // + while ((UINTN)CurrentBuffer < (UINTN)(AmlParentHandle->Buffer + AmlParentHandle->Size)) { + AmlByteEncoding = AmlSearchByOpByte (CurrentBuffer); + if (AmlByteEncoding == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // NOTE: We need return everything, because user might need parse the returned object. + // + if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0) { + *Buffer = CurrentBuffer; + return EFI_SUCCESS; + } + + DataSize = AmlGetObjectSize ( + AmlByteEncoding, + CurrentBuffer, + (UINTN)AmlParentHandle->Buffer + AmlParentHandle->Size - (UINTN)CurrentBuffer + ); + if (DataSize == 0) { + return EFI_INVALID_PARAMETER; + } + CurrentBuffer += DataSize; + } + + // + // No more + // + *Buffer = NULL; + return EFI_SUCCESS; +} + +/** + Return the child ACPI objects from Root Handle. + + @param[in] AmlParentHandle Parent handle. It is Root Handle. + @param[in] AmlHandle The previously returned handle or NULL to start with the first handle. + @param[out] Buffer On return, points to the next returned ACPI handle or NULL if there are no + child objects. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER ParentHandle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetChildFromRoot ( + IN EFI_AML_HANDLE *AmlParentHandle, + IN EFI_AML_HANDLE *AmlHandle, + OUT VOID **Buffer + ) +{ + UINT8 *CurrentBuffer; + + if (AmlHandle == NULL) { + // + // First One + // + CurrentBuffer = (VOID *)AmlParentHandle->Buffer; + } else { + CurrentBuffer = (VOID *)(AmlHandle->Buffer + AmlHandle->Size); + } + + return AmlGetChildFromObjectBuffer (AmlParentHandle, CurrentBuffer, Buffer); +} + +/** + Return the child objects buffer from AML Handle's option list. + + @param[in] AmlParentHandle Parent handle. + @param[in] AmlHandle The current child handle. + @param[out] Buffer On return, points to the next returned child buffer or NULL if there are no + child buffer. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER AmlParentHandle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetChildFromOptionList ( + IN EFI_AML_HANDLE *AmlParentHandle, + IN EFI_AML_HANDLE *AmlHandle, + OUT VOID **Buffer + ) +{ + EFI_ACPI_DATA_TYPE DataType; + VOID *Data; + UINTN DataSize; + AML_OP_PARSE_INDEX Index; + EFI_STATUS Status; + AML_OP_PARSE_INDEX MaxTerm; + + Index = AML_OP_PARSE_INDEX_GET_TERM1; + MaxTerm = AmlParentHandle->AmlByteEncoding->MaxIndex; + while (Index <= MaxTerm) { + Status = AmlParseOptionHandleCommon ( + AmlParentHandle, + (AML_OP_PARSE_INDEX)Index, + &DataType, + (VOID **)&Data, + &DataSize + ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + if (DataType == EFI_ACPI_DATA_TYPE_NONE) { + // + // Not found + // + break; + } + + // + // Find it, and Check Data + // + if ((DataType == EFI_ACPI_DATA_TYPE_CHILD) && + ((UINTN)AmlHandle->Buffer < (UINTN)Data)) { + // + // Buffer < Data means current node is next one + // + *Buffer = Data; + return EFI_SUCCESS; + } + // + // Not Child + // + Index ++; + } + + *Buffer = NULL; + return EFI_SUCCESS; +} + +/** + Return the child objects buffer from AML Handle's object child list. + + @param[in] AmlParentHandle Parent handle. + @param[in] AmlHandle The current child handle. + @param[out] Buffer On return, points to the next returned child buffer or NULL if there are no + child buffer. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER AmlParentHandle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetChildFromObjectChildList ( + IN EFI_AML_HANDLE *AmlParentHandle, + IN EFI_AML_HANDLE *AmlHandle, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + UINT8 *CurrentBuffer; + + CurrentBuffer = NULL; + + if ((AmlParentHandle->AmlByteEncoding->Attribute & AML_HAS_CHILD_OBJ) == 0) { + // + // No ObjectList + // + *Buffer = NULL; + return EFI_SUCCESS; + } + + // + // Do we need add node within METHOD? + // Yes, just add Object is OK. But we need filter NameString for METHOD invoke. + // + + // + // Now, we get the last node. + // + Status = AmlGetOffsetAfterLastOption (AmlParentHandle, &CurrentBuffer); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + // + // Go through all the reset buffer. + // + if ((UINTN)AmlHandle->Buffer < (UINTN)CurrentBuffer) { + // + // Buffer < Data means next node is first object + // + } else if ((UINTN)AmlHandle->Buffer + AmlHandle->Size < (UINTN)AmlParentHandle->Buffer + AmlParentHandle->Size) { + // + // There is still more node + // + CurrentBuffer = AmlHandle->Buffer + AmlHandle->Size; + } else { + // + // No more data + // + *Buffer = NULL; + return EFI_SUCCESS; + } + + return AmlGetChildFromObjectBuffer (AmlParentHandle, CurrentBuffer, Buffer); +} + +/** + Return the child ACPI objects from Non-Root Handle. + + @param[in] AmlParentHandle Parent handle. It is Non-Root Handle. + @param[in] AmlHandle The previously returned handle or NULL to start with the first handle. + @param[out] Buffer On return, points to the next returned ACPI handle or NULL if there are no + child objects. + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER ParentHandle is NULL or does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetChildFromNonRoot ( + IN EFI_AML_HANDLE *AmlParentHandle, + IN EFI_AML_HANDLE *AmlHandle, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + + if (AmlHandle == NULL) { + // + // NULL means first one + // + AmlHandle = AmlParentHandle; + } + + // + // 1. Get Option + // + Status = AmlGetChildFromOptionList (AmlParentHandle, AmlHandle, Buffer); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + if (*Buffer != NULL) { + return EFI_SUCCESS; + } + + // + // 2. search ObjectList + // + return AmlGetChildFromObjectChildList (AmlParentHandle, AmlHandle, Buffer); +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlNamespace.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlNamespace.c new file mode 100644 index 000000000..8139f3ab4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlNamespace.c @@ -0,0 +1,608 @@ +/** @file + ACPI Sdt Protocol Driver + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "AcpiTable.h" + +/** + Construct node list according to the AML handle. + + @param[in] AmlHandle AML handle. + @param[in] AmlRootNodeList AML root node list. + @param[in] AmlParentNodeList AML parent node list. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlConstructNodeList ( + IN EFI_AML_HANDLE *AmlHandle, + IN EFI_AML_NODE_LIST *AmlRootNodeList, + IN EFI_AML_NODE_LIST *AmlParentNodeList + ); + +/** + Create AML Node. + + @param[in] NameSeg AML NameSeg. + @param[in] Parent AML parent node list. + @param[in] AmlByteEncoding AML Byte Encoding. + + @return AML Node. +**/ +EFI_AML_NODE_LIST * +AmlCreateNode ( + IN UINT8 *NameSeg, + IN EFI_AML_NODE_LIST *Parent, + IN AML_BYTE_ENCODING *AmlByteEncoding + ) +{ + EFI_AML_NODE_LIST *AmlNodeList; + + AmlNodeList = AllocatePool (sizeof(*AmlNodeList)); + ASSERT (AmlNodeList != NULL); + + AmlNodeList->Signature = EFI_AML_NODE_LIST_SIGNATURE; + CopyMem (AmlNodeList->Name, NameSeg, AML_NAME_SEG_SIZE); + AmlNodeList->Buffer = NULL; + AmlNodeList->Size = 0; + InitializeListHead (&AmlNodeList->Link); + InitializeListHead (&AmlNodeList->Children); + AmlNodeList->Parent = Parent; + AmlNodeList->AmlByteEncoding = AmlByteEncoding; + + return AmlNodeList; +} + +/** + Find the AML NameSeg in the children of AmlParentNodeList. + + @param[in] NameSeg AML NameSeg. + @param[in] AmlParentNodeList AML parent node list. + @param[in] Create TRUE means to create node if not found. + + @return AmlChildNode whoes name is same as NameSeg. +**/ +EFI_AML_NODE_LIST * +AmlFindNodeInThis ( + IN UINT8 *NameSeg, + IN EFI_AML_NODE_LIST *AmlParentNodeList, + IN BOOLEAN Create + ) +{ + EFI_AML_NODE_LIST *CurrentAmlNodeList; + LIST_ENTRY *CurrentLink; + LIST_ENTRY *StartLink; + EFI_AML_NODE_LIST *AmlNodeList; + + StartLink = &AmlParentNodeList->Children; + CurrentLink = StartLink->ForwardLink; + + while (CurrentLink != StartLink) { + CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink); + // + // AML name is same as the one stored + // + if (CompareMem (CurrentAmlNodeList->Name, NameSeg, AML_NAME_SEG_SIZE) == 0) { + // + // Good! Found it + // + return CurrentAmlNodeList; + } + CurrentLink = CurrentLink->ForwardLink; + } + + // + // Not found + // + if (!Create) { + return NULL; + } + + // + // Create new node with NULL buffer - it means namespace not be returned. + // + AmlNodeList = AmlCreateNode (NameSeg, AmlParentNodeList, NULL); + InsertTailList (&AmlParentNodeList->Children, &AmlNodeList->Link); + + return AmlNodeList; +} + +/** + Find the AML NameString in the children of AmlParentNodeList or AmlRootNodeList. + + @param[in] NameString AML NameString. + @param[in] AmlRootNodeList AML root node list. + @param[in] AmlParentNodeList AML parent node list. + @param[in] Create TRUE means to create node if not found. + + @return AmlChildNode whoes name is same as NameSeg. +**/ +EFI_AML_NODE_LIST * +AmlFindNodeInTheTree ( + IN UINT8 *NameString, + IN EFI_AML_NODE_LIST *AmlRootNodeList, + IN EFI_AML_NODE_LIST *AmlParentNodeList, + IN BOOLEAN Create + ) +{ + UINT8 *Buffer; + EFI_AML_NODE_LIST *AmlNodeList; + EFI_AML_NODE_LIST *AmlCurrentNodeList; + UINT8 Index; + UINT8 SegCount; + + Buffer = NameString; + + // + // Handle root or parent prefix + // + if (*Buffer == AML_ROOT_CHAR) { + AmlCurrentNodeList = AmlRootNodeList; + Buffer += 1; + } else if (*Buffer == AML_PARENT_PREFIX_CHAR) { + AmlCurrentNodeList = AmlParentNodeList; + do { + if (AmlCurrentNodeList->Parent != NULL) { + AmlCurrentNodeList = AmlCurrentNodeList->Parent; + } else { + // + // Only root has no parent + // + ASSERT (AmlCurrentNodeList == AmlRootNodeList); + } + Buffer += 1; + } while (*Buffer == AML_PARENT_PREFIX_CHAR); + } else { + AmlCurrentNodeList = AmlParentNodeList; + } + + // + // Handle name segment + // + if (*Buffer == AML_DUAL_NAME_PREFIX) { + Buffer += 1; + SegCount = 2; + } else if (*Buffer == AML_MULTI_NAME_PREFIX) { + Buffer += 1; + SegCount = *Buffer; + Buffer += 1; + } else if (*Buffer == 0) { + // + // NULL name, only for Root + // + ASSERT (AmlCurrentNodeList == AmlRootNodeList); + return AmlCurrentNodeList; + } else { + SegCount = 1; + } + + // + // Handle NamePath + // + Index = 0; + do { + AmlNodeList = AmlFindNodeInThis (Buffer, AmlCurrentNodeList, Create); + if (AmlNodeList == NULL) { + return NULL; + } + AmlCurrentNodeList = AmlNodeList; + Buffer += AML_NAME_SEG_SIZE; + Index ++; + } while (Index < SegCount); + + return AmlNodeList; +} + +/** + Insert the NameString to the AmlNodeList. + + @param[in] NameString AML NameString. + @param[in] Buffer Buffer for the Node. + @param[in] Size Size for the Node. + @param[in] AmlRootNodeList AML root node list. + @param[in] AmlParentNodeList AML parent node list. + + @return AmlChildNode whoes name is NameString. +**/ +EFI_AML_NODE_LIST * +AmlInsertNodeToTree ( + IN UINT8 *NameString, + IN VOID *Buffer, + IN UINTN Size, + IN EFI_AML_NODE_LIST *AmlRootNodeList, + IN EFI_AML_NODE_LIST *AmlParentNodeList + ) +{ + EFI_AML_NODE_LIST *AmlNodeList; + + AmlNodeList = AmlFindNodeInTheTree ( + NameString, + AmlRootNodeList, + AmlParentNodeList, + TRUE // Find and Create + ); + ASSERT (AmlNodeList != NULL); + if (AmlNodeList == NULL) { + return NULL; + } + + // + // Check buffer + // + if (AmlNodeList->Buffer == NULL) { + // + // NULL means new added one or SCOPE_OP + // + if (*(UINT8 *)Buffer != AML_SCOPE_OP) { + // + // We need check if new one is SCOPE_OP, because SCOPE_OP just means namespace, not a real device. + // We should not return SCOPE_OP. + // + AmlNodeList->Buffer = Buffer; + AmlNodeList->Size = Size; + AmlNodeList->AmlByteEncoding = AmlSearchByOpByte (Buffer); + } + return AmlNodeList; + } + + // + // Already added + // + if (*(UINT8 *)Buffer == AML_SCOPE_OP) { + // + // The new one is SCOPE_OP, OK just return; + // + return AmlNodeList; + } + + // + // Oops!!!, There must be something wrong. + // + DEBUG ((EFI_D_ERROR, "AML: Override Happen - %a!\n", NameString)); + DEBUG ((EFI_D_ERROR, "AML: Existing Node - %x\n", AmlNodeList->Buffer)); + DEBUG ((EFI_D_ERROR, "AML: New Buffer - %x\n", Buffer)); + + return NULL; +} + +/** + Construct child node list according to the AML handle. + + @param[in] AmlHandle AML handle. + @param[in] AmlRootNodeList AML root node list. + @param[in] AmlParentNodeList AML parent node list. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlConstructNodeListForChild ( + IN EFI_AML_HANDLE *AmlHandle, + IN EFI_AML_NODE_LIST *AmlRootNodeList, + IN EFI_AML_NODE_LIST *AmlParentNodeList + ) +{ + AML_BYTE_ENCODING *AmlByteEncoding; + UINT8 *Buffer; + UINTN BufferSize; + UINT8 *CurrentBuffer; + EFI_AML_HANDLE *AmlChildHandle; + EFI_STATUS Status; + + CurrentBuffer = NULL; + AmlChildHandle = NULL; + AmlByteEncoding = AmlHandle->AmlByteEncoding; + Buffer = AmlHandle->Buffer; + BufferSize = AmlHandle->Size; + + // + // Check if we need recursively add node + // + if ((AmlByteEncoding->Attribute & AML_HAS_CHILD_OBJ) == 0) { + // + // No more node need to be added + // + return EFI_SUCCESS; + } + + // + // Do we need add node within METHOD? + // Yes, just add Object is OK. But we need filter NameString for METHOD invoke. + // + + // + // Now, we get the last node. + // + Status = AmlGetOffsetAfterLastOption (AmlHandle, &CurrentBuffer); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + // + // Go through all the reset buffer. + // + while ((UINTN)CurrentBuffer < (UINTN)Buffer + BufferSize) { + // + // Find the child node. + // + Status = SdtOpenEx (CurrentBuffer, (UINTN)Buffer + BufferSize - (UINTN)CurrentBuffer, (EFI_ACPI_HANDLE *)&AmlChildHandle); + if (EFI_ERROR (Status)) { + // + // No child found, break now. + // + break; + } + + // + // Good, find the child. Construct node recursively + // + Status = AmlConstructNodeList ( + AmlChildHandle, + AmlRootNodeList, + AmlParentNodeList + ); + if (EFI_ERROR (Status)) { + break; + } + + // + // Parse next one + // + CurrentBuffer += AmlChildHandle->Size; + + Close ((EFI_ACPI_HANDLE)AmlChildHandle); + } + + return EFI_SUCCESS; +} + +/** + Construct node list according to the AML handle. + + @param[in] AmlHandle AML handle. + @param[in] AmlRootNodeList AML root node list. + @param[in] AmlParentNodeList AML parent node list. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlConstructNodeList ( + IN EFI_AML_HANDLE *AmlHandle, + IN EFI_AML_NODE_LIST *AmlRootNodeList, + IN EFI_AML_NODE_LIST *AmlParentNodeList + ) +{ + VOID *NameString; + EFI_AML_NODE_LIST *AmlNodeList; + + // + // 1. Check if there is need to construct node for this OpCode. + // + if ((AmlHandle->AmlByteEncoding->Attribute & AML_IN_NAMESPACE) == 0) { + // + // No need to construct node, so we just skip this OpCode. + // + return EFI_SUCCESS; + } + + // + // 2. Now, we need construct node for this OpCode. + // + NameString = AmlGetObjectName (AmlHandle); + if (NameString == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Now, we need to insert node to the node list. + // NOTE: The name here could be AML NameString. So the callee need parse it. + // + AmlNodeList = AmlInsertNodeToTree (NameString, AmlHandle->Buffer, AmlHandle->Size, AmlRootNodeList, AmlParentNodeList); + ASSERT (AmlNodeList != NULL); + + // + // 3. Ok, we need to parse the object list to see if there are more node to be added. + // + return AmlConstructNodeListForChild (AmlHandle, AmlRootNodeList, AmlNodeList); +} + +/** + Destruct node list + + @param[in] AmlParentNodeList AML parent node list. +**/ +VOID +AmlDestructNodeList ( + IN EFI_AML_NODE_LIST *AmlParentNodeList + ) +{ + EFI_AML_NODE_LIST *CurrentAmlNodeList; + LIST_ENTRY *CurrentLink; + LIST_ENTRY *StartLink; + + // + // Get the children link + // + StartLink = &AmlParentNodeList->Children; + CurrentLink = StartLink->ForwardLink; + + // + // Go through all the children + // + while (CurrentLink != StartLink) { + // + // Destruct the child's list recursively + // + CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink); + CurrentLink = CurrentLink->ForwardLink; + + // + // Remove this child from list and free the node + // + RemoveEntryList (&(CurrentAmlNodeList->Link)); + + AmlDestructNodeList (CurrentAmlNodeList); + } + + // + // Done. + // + FreePool (AmlParentNodeList); + return ; +} + +/** + Dump node list + + @param[in] AmlParentNodeList AML parent node list. + @param[in] Level Output debug level. +**/ +VOID +AmlDumpNodeInfo ( + IN EFI_AML_NODE_LIST *AmlParentNodeList, + IN UINTN Level + ) +{ + EFI_AML_NODE_LIST *CurrentAmlNodeList; + volatile LIST_ENTRY *CurrentLink; + UINTN Index; + + CurrentLink = AmlParentNodeList->Children.ForwardLink; + + if (Level == 0) { + DEBUG ((EFI_D_ERROR, "\\")); + } else { + for (Index = 0; Index < Level; Index++) { + DEBUG ((EFI_D_ERROR, " ")); + } + AmlPrintNameSeg (AmlParentNodeList->Name); + } + DEBUG ((EFI_D_ERROR, "\n")); + + while (CurrentLink != &AmlParentNodeList->Children) { + CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink); + AmlDumpNodeInfo (CurrentAmlNodeList, Level + 1); + CurrentLink = CurrentLink->ForwardLink; + } + + return ; +} + +/** + Returns the handle of the ACPI object representing the specified ACPI AML path + + @param[in] AmlHandle Points to the handle of the object representing the starting point for the path search. + @param[in] AmlPath Points to the ACPI AML path. + @param[out] Buffer On return, points to the ACPI object which represents AcpiPath, relative to + HandleIn. + @param[in] FromRoot TRUE means to find AML path from \ (Root) Node. + FALSE means to find AML path from this Node (The HandleIn). + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER HandleIn does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlFindPath ( + IN EFI_AML_HANDLE *AmlHandle, + IN UINT8 *AmlPath, + OUT VOID **Buffer, + IN BOOLEAN FromRoot + ) +{ + EFI_AML_NODE_LIST *AmlRootNodeList; + EFI_STATUS Status; + EFI_AML_NODE_LIST *AmlNodeList; + UINT8 RootNameSeg[AML_NAME_SEG_SIZE]; + EFI_AML_NODE_LIST *CurrentAmlNodeList; + LIST_ENTRY *CurrentLink; + + // + // 1. create tree + // + + // + // Create root handle + // + RootNameSeg[0] = AML_ROOT_CHAR; + RootNameSeg[1] = 0; + AmlRootNodeList = AmlCreateNode (RootNameSeg, NULL, AmlHandle->AmlByteEncoding); + + Status = AmlConstructNodeList ( + AmlHandle, + AmlRootNodeList, // Root + AmlRootNodeList // Parent + ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG_CODE_BEGIN (); + DEBUG ((EFI_D_ERROR, "AcpiSdt: NameSpace:\n")); + AmlDumpNodeInfo (AmlRootNodeList, 0); + DEBUG_CODE_END (); + + // + // 2. Search the node in the tree + // + if (FromRoot) { + // + // Search from Root + // + CurrentAmlNodeList = AmlRootNodeList; + } else { + // + // Search from this node, NOT ROOT. + // Since we insert node to ROOT one by one, we just get the first node and search from it. + // + CurrentLink = AmlRootNodeList->Children.ForwardLink; + if (CurrentLink != &AmlRootNodeList->Children) { + // + // First node + // + CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink); + } else { + // + // No child + // + CurrentAmlNodeList = NULL; + } + } + + // + // Search + // + if (CurrentAmlNodeList != NULL) { + DEBUG_CODE_BEGIN (); + DEBUG ((EFI_D_ERROR, "AcpiSdt: Search from: \\")); + AmlPrintNameSeg (CurrentAmlNodeList->Name); + DEBUG ((EFI_D_ERROR, "\n")); + DEBUG_CODE_END (); + AmlNodeList = AmlFindNodeInTheTree ( + AmlPath, + AmlRootNodeList, // Root + CurrentAmlNodeList, // Parent + FALSE + ); + } else { + AmlNodeList = NULL; + } + + *Buffer = NULL; + Status = EFI_SUCCESS; + if (AmlNodeList != NULL && AmlNodeList->Buffer != NULL) { + *Buffer = AmlNodeList->Buffer; + } + + // + // 3. free the tree + // + AmlDestructNodeList (AmlRootNodeList); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlOption.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlOption.c new file mode 100644 index 000000000..d0374c5de --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlOption.c @@ -0,0 +1,446 @@ +/** @file + ACPI Sdt Protocol Driver + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "AcpiTable.h" + +/** + Retrieve option term according to AmlByteEncoding and Buffer. + + @param[in] AmlByteEncoding AML Byte Encoding. + @param[in] Buffer AML buffer. + @param[in] MaxBufferSize AML buffer MAX size. The parser can not parse any data exceed this region. + @param[in] TermIndex Index of the data to retrieve from the object. + @param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists + for the specified index. + @param[out] Data Upon return, points to the pointer to the data. + @param[out] DataSize Upon return, points to the size of Data. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Buffer does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlParseOptionTerm ( + IN AML_BYTE_ENCODING *AmlByteEncoding, + IN UINT8 *Buffer, + IN UINTN MaxBufferSize, + IN AML_OP_PARSE_INDEX TermIndex, + OUT EFI_ACPI_DATA_TYPE *DataType, + OUT VOID **Data, + OUT UINTN *DataSize + ) +{ + AML_BYTE_ENCODING *ChildAmlByteEncoding; + EFI_STATUS Status; + + if (DataType != NULL) { + *DataType = AmlTypeToAcpiType (AmlByteEncoding->Format[TermIndex - 1]); + } + if (Data != NULL) { + *Data = Buffer; + } + // + // Parse term according to AML type + // + switch (AmlByteEncoding->Format[TermIndex - 1]) { + case AML_UINT8: + *DataSize = sizeof(UINT8); + break; + case AML_UINT16: + *DataSize = sizeof(UINT16); + break; + case AML_UINT32: + *DataSize = sizeof(UINT32); + break; + case AML_UINT64: + *DataSize = sizeof(UINT64); + break; + case AML_STRING: + *DataSize = AsciiStrSize((CHAR8 *)Buffer); + break; + case AML_NAME: + Status = AmlGetNameStringSize (Buffer, DataSize); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + break; + case AML_OBJECT: + ChildAmlByteEncoding = AmlSearchByOpByte (Buffer); + if (ChildAmlByteEncoding == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // NOTE: We need override DataType here, if there is a case the AML_OBJECT is AML_NAME. + // We need convert type from EFI_ACPI_DATA_TYPE_CHILD to EFI_ACPI_DATA_TYPE_NAME_STRING. + // We should not return CHILD because there is NO OpCode for NameString. + // + if ((ChildAmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) { + if (DataType != NULL) { + *DataType = AmlTypeToAcpiType (AML_NAME); + } + Status = AmlGetNameStringSize (Buffer, DataSize); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + break; + } + + // + // It is real AML_OBJECT + // + *DataSize = AmlGetObjectSize ( + ChildAmlByteEncoding, + Buffer, + MaxBufferSize + ); + if (*DataSize == 0) { + return EFI_INVALID_PARAMETER; + } + break; + case AML_NONE: + // + // No term + // + case AML_OPCODE: + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + if (*DataSize > MaxBufferSize) { + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +/** + Retrieve information according to AmlByteEncoding and Buffer. + + @param[in] AmlByteEncoding AML Byte Encoding. + @param[in] Buffer AML buffer. + @param[in] MaxBufferSize AML buffer MAX size. The parser can not parse any data exceed this region. + @param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right + in the ACPI encoding, with index 0 always being the ACPI opcode. + @param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists + for the specified index. + @param[out] Data Upon return, points to the pointer to the data. + @param[out] DataSize Upon return, points to the size of Data. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Buffer does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlParseOptionCommon ( + IN AML_BYTE_ENCODING *AmlByteEncoding, + IN UINT8 *Buffer, + IN UINTN MaxBufferSize, + IN AML_OP_PARSE_INDEX Index, + OUT EFI_ACPI_DATA_TYPE *DataType, + OUT VOID **Data, + OUT UINTN *DataSize + ) +{ + UINT8 *CurrentBuffer; + UINTN PkgLength; + UINTN OpLength; + UINTN PkgOffset; + AML_OP_PARSE_INDEX TermIndex; + EFI_STATUS Status; + + ASSERT ((Index <= AmlByteEncoding->MaxIndex) || (Index == AML_OP_PARSE_INDEX_GET_SIZE)); + + // + // 0. Check if this is NAME string. + // + if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) != 0) { + // + // Only allow GET_SIZE + // + if (Index != AML_OP_PARSE_INDEX_GET_SIZE) { + return EFI_INVALID_PARAMETER; + } + // + // return NameString size + // + Status = AmlGetNameStringSize (Buffer, DataSize); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + if (*DataSize > MaxBufferSize) { + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; + } + + // + // Not NAME string, start parsing + // + CurrentBuffer = Buffer; + + // + // 1. Get OpCode + // + if (Index != AML_OP_PARSE_INDEX_GET_SIZE) { + *DataType = EFI_ACPI_DATA_TYPE_OPCODE; + *Data = (VOID *)CurrentBuffer; + } + if (*CurrentBuffer == AML_EXT_OP) { + OpLength = 2; + } else { + OpLength = 1; + } + *DataSize = OpLength; + if (Index == AML_OP_PARSE_INDEX_GET_OPCODE) { + return EFI_SUCCESS; + } + if (OpLength > MaxBufferSize) { + return EFI_INVALID_PARAMETER; + } + CurrentBuffer += OpLength; + + // + // 2. Skip PkgLength field, if have + // + if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { + PkgOffset = AmlGetPkgLength(CurrentBuffer, &PkgLength); + // + // Override MaxBufferSize if it is valid PkgLength + // + if (OpLength + PkgLength > MaxBufferSize) { + return EFI_INVALID_PARAMETER; + } else { + MaxBufferSize = OpLength + PkgLength; + } + } else { + PkgOffset = 0; + PkgLength = 0; + } + CurrentBuffer += PkgOffset; + + // + // 3. Get Term one by one. + // + TermIndex = AML_OP_PARSE_INDEX_GET_TERM1; + while ((Index >= TermIndex) && (TermIndex <= AmlByteEncoding->MaxIndex) && ((UINTN)CurrentBuffer < (UINTN)Buffer + MaxBufferSize)) { + Status = AmlParseOptionTerm ( + AmlByteEncoding, + CurrentBuffer, + (UINTN)Buffer + MaxBufferSize - (UINTN)CurrentBuffer, + TermIndex, + DataType, + Data, + DataSize + ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + if (Index == TermIndex) { + // + // Done + // + return EFI_SUCCESS; + } + + // + // Parse next one + // + CurrentBuffer += *DataSize; + TermIndex ++; + } + + // + // Finish all options, but no option found. + // + if ((UINTN)CurrentBuffer > (UINTN)Buffer + MaxBufferSize) { + return EFI_INVALID_PARAMETER; + } + if ((UINTN)CurrentBuffer == (UINTN)Buffer + MaxBufferSize) { + if (Index != AML_OP_PARSE_INDEX_GET_SIZE) { + return EFI_INVALID_PARAMETER; + } + } + + // + // 4. Finish parsing all node, return size + // + ASSERT (Index == AML_OP_PARSE_INDEX_GET_SIZE); + if ((AmlByteEncoding->Attribute & AML_HAS_PKG_LENGTH) != 0) { + *DataSize = OpLength + PkgLength; + } else { + *DataSize = (UINTN)CurrentBuffer - (UINTN)Buffer; + } + + return EFI_SUCCESS; +} + +/** + Return object size. + + @param[in] AmlByteEncoding AML Byte Encoding. + @param[in] Buffer AML object buffer. + @param[in] MaxBufferSize AML object buffer MAX size. The parser can not parse any data exceed this region. + + @return Size of the object. +**/ +UINTN +AmlGetObjectSize ( + IN AML_BYTE_ENCODING *AmlByteEncoding, + IN UINT8 *Buffer, + IN UINTN MaxBufferSize + ) +{ + EFI_STATUS Status; + UINTN DataSize; + + Status = AmlParseOptionCommon ( + AmlByteEncoding, + Buffer, + MaxBufferSize, + AML_OP_PARSE_INDEX_GET_SIZE, + NULL, + NULL, + &DataSize + ); + if (EFI_ERROR (Status)) { + return 0; + } else { + return DataSize; + } +} + +/** + Return object name. + + @param[in] AmlHandle AML handle. + + @return Name of the object. +**/ +CHAR8 * +AmlGetObjectName ( + IN EFI_AML_HANDLE *AmlHandle + ) +{ + AML_BYTE_ENCODING *AmlByteEncoding; + VOID *NameString; + UINTN NameSize; + AML_OP_PARSE_INDEX TermIndex; + EFI_STATUS Status; + EFI_ACPI_DATA_TYPE DataType; + + AmlByteEncoding = AmlHandle->AmlByteEncoding; + + ASSERT ((AmlByteEncoding->Attribute & AML_IN_NAMESPACE) != 0); + + // + // Find out Last Name index, according to OpCode table. + // The last name will be the node name by design. + // + TermIndex = AmlByteEncoding->MaxIndex; + for (TermIndex = AmlByteEncoding->MaxIndex; TermIndex > 0; TermIndex--) { + if (AmlByteEncoding->Format[TermIndex - 1] == AML_NAME) { + break; + } + } + ASSERT (TermIndex != 0); + + // + // Get Name for this node. + // + Status = AmlParseOptionHandleCommon ( + AmlHandle, + TermIndex, + &DataType, + &NameString, + &NameSize + ); + if (EFI_ERROR (Status)) { + return NULL; + } + ASSERT (DataType == EFI_ACPI_DATA_TYPE_NAME_STRING); + + return NameString; +} + +/** + Return offset of last option. + + @param[in] AmlHandle AML Handle. + @param[out] Buffer Upon return, points to the offset after last option. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER AmlHandle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlGetOffsetAfterLastOption ( + IN EFI_AML_HANDLE *AmlHandle, + OUT UINT8 **Buffer + ) +{ + EFI_ACPI_DATA_TYPE DataType; + VOID *Data; + UINTN DataSize; + EFI_STATUS Status; + + Status = AmlParseOptionHandleCommon ( + AmlHandle, + AmlHandle->AmlByteEncoding->MaxIndex, + &DataType, + &Data, + &DataSize + ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + // + // We need to parse the rest buffer after last node. + // + *Buffer = (UINT8 *)((UINTN)Data + DataSize); + + // + // We need skip PkgLength if no Option + // + if (DataType == EFI_ACPI_DATA_TYPE_OPCODE) { + *Buffer += AmlGetPkgLength (*Buffer, &DataSize); + } + return EFI_SUCCESS; +} + +/** + Retrieve information according to AmlHandle + + @param[in] AmlHandle AML handle. + @param[in] Index Index of the data to retrieve from the object. In general, indexes read from left-to-right + in the ACPI encoding, with index 0 always being the ACPI opcode. + @param[out] DataType Points to the returned data type or EFI_ACPI_DATA_TYPE_NONE if no data exists + for the specified index. + @param[out] Data Upon return, points to the pointer to the data. + @param[out] DataSize Upon return, points to the size of Data. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER AmlHandle does not refer to a valid ACPI object. +**/ +EFI_STATUS +AmlParseOptionHandleCommon ( + IN EFI_AML_HANDLE *AmlHandle, + IN AML_OP_PARSE_INDEX Index, + OUT EFI_ACPI_DATA_TYPE *DataType, + OUT VOID **Data, + OUT UINTN *DataSize + ) +{ + return AmlParseOptionCommon ( + AmlHandle->AmlByteEncoding, + AmlHandle->Buffer, + AmlHandle->Size, + Index, + DataType, + Data, + DataSize + ); +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlString.c b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlString.c new file mode 100644 index 000000000..d0b8b0d0f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/AcpiTableDxe/AmlString.c @@ -0,0 +1,539 @@ +/** @file + ACPI Sdt Protocol Driver + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "AcpiTable.h" + +/** + Check if it is AML Root name + + @param[in] Buffer AML path. + + @retval TRUE AML path is root. + @retval FALSE AML path is not root. +**/ +BOOLEAN +AmlIsRootPath ( + IN UINT8 *Buffer + ) +{ + if ((Buffer[0] == AML_ROOT_CHAR) && (Buffer[1] == 0)) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Check if it is AML LeadName. + + @param[in] Ch Char. + + @retval TRUE Char is AML LeadName. + @retval FALSE Char is not AML LeadName. +**/ +BOOLEAN +AmlIsLeadName ( + IN CHAR8 Ch + ) +{ + if ((Ch == '_') || (Ch >= 'A' && Ch <= 'Z')) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Check if it is AML Name. + + @param[in] Ch Char. + + @retval TRUE Char is AML Name. + @retval FALSE Char is not AML Name. +**/ +BOOLEAN +AmlIsName ( + IN CHAR8 Ch + ) +{ + if (AmlIsLeadName (Ch) || (Ch >= '0' && Ch <= '9')) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Return is buffer is AML NameSeg. + + @param[in] Buffer AML NameSement. + + @retval TRUE It is AML NameSegment. + @retval FALSE It is not AML NameSegment. +**/ +BOOLEAN +AmlIsNameSeg ( + IN UINT8 *Buffer + ) +{ + UINTN Index; + if (!AmlIsLeadName (Buffer[0])) { + return FALSE; + } + for (Index = 1; Index < AML_NAME_SEG_SIZE; Index++) { + if (!AmlIsName (Buffer[Index])) { + return FALSE; + } + } + return TRUE; +} + +/** + Get AML NameString size. + + @param[in] Buffer AML NameString. + @param[out] BufferSize AML NameString size + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Buffer does not refer to a valid AML NameString. +**/ +EFI_STATUS +AmlGetNameStringSize ( + IN UINT8 *Buffer, + OUT UINTN *BufferSize + ) +{ + UINTN SegCount; + UINTN Length; + UINTN Index; + + Length = 0; + + // + // Parse root or parent prefix + // + if (*Buffer == AML_ROOT_CHAR) { + Buffer ++; + Length ++; + } else if (*Buffer == AML_PARENT_PREFIX_CHAR) { + do { + Buffer ++; + Length ++; + } while (*Buffer == AML_PARENT_PREFIX_CHAR); + } + + // + // Parse name segment + // + if (*Buffer == AML_DUAL_NAME_PREFIX) { + Buffer ++; + Length ++; + SegCount = 2; + } else if (*Buffer == AML_MULTI_NAME_PREFIX) { + Buffer ++; + Length ++; + SegCount = *Buffer; + Buffer ++; + Length ++; + } else if (*Buffer == 0) { + // + // NULL Name, only for Root + // + SegCount = 0; + Buffer --; + if ((Length == 1) && (*Buffer == AML_ROOT_CHAR)) { + *BufferSize = 2; + return EFI_SUCCESS; + } else { + return EFI_INVALID_PARAMETER; + } + } else { + // + // NameSeg + // + SegCount = 1; + } + + Index = 0; + do { + if (!AmlIsNameSeg (Buffer)) { + return EFI_INVALID_PARAMETER; + } + Buffer += AML_NAME_SEG_SIZE; + Length += AML_NAME_SEG_SIZE; + Index ++; + } while (Index < SegCount); + + *BufferSize = Length; + return EFI_SUCCESS; +} + +/** + Check if it is ASL LeadName. + + @param[in] Ch Char. + + @retval TRUE Char is ASL LeadName. + @retval FALSE Char is not ASL LeadName. +**/ +BOOLEAN +AmlIsAslLeadName ( + IN CHAR8 Ch + ) +{ + if (AmlIsLeadName (Ch) || (Ch >= 'a' && Ch <= 'z')) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Check if it is ASL Name. + + @param[in] Ch Char. + + @retval TRUE Char is ASL Name. + @retval FALSE Char is not ASL Name. +**/ +BOOLEAN +AmlIsAslName ( + IN CHAR8 Ch + ) +{ + if (AmlIsAslLeadName (Ch) || (Ch >= '0' && Ch <= '9')) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Get ASL NameString size. + + @param[in] Buffer ASL NameString. + + @return ASL NameString size. +**/ +UINTN +AmlGetAslNameSegLength ( + IN UINT8 *Buffer + ) +{ + UINTN Length; + UINTN Index; + + if (*Buffer == 0) { + return 0; + } + + Length = 0; + // + // 1st + // + if (AmlIsAslLeadName (*Buffer)) { + Length ++; + Buffer ++; + } + if ((*Buffer == 0) || (*Buffer == '.')) { + return Length; + } + // + // 2, 3, 4 name char + // + for (Index = 0; Index < 3; Index++) { + if (AmlIsAslName (*Buffer)) { + Length ++; + Buffer ++; + } + if ((*Buffer == 0) || (*Buffer == '.')) { + return Length; + } + } + + // + // Invalid ASL name + // + return 0; +} + +/** + Get ASL NameString size. + + @param[in] Buffer ASL NameString. + @param[out] Root On return, points to Root char number. + @param[out] Parent On return, points to Parent char number. + @param[out] SegCount On return, points to Segment count. + + @return ASL NameString size. +**/ +UINTN +AmlGetAslNameStringSize ( + IN UINT8 *Buffer, + OUT UINTN *Root, + OUT UINTN *Parent, + OUT UINTN *SegCount + ) +{ + UINTN NameLength; + UINTN TotalLength; + + *Root = 0; + *Parent = 0; + *SegCount = 0; + TotalLength = 0; + NameLength = 0; + if (*Buffer == AML_ROOT_CHAR) { + *Root = 1; + Buffer ++; + } else if (*Buffer == AML_PARENT_PREFIX_CHAR) { + do { + Buffer ++; + (*Parent) ++; + } while (*Buffer == AML_PARENT_PREFIX_CHAR); + } + + // + // Now parse name + // + while (*Buffer != 0) { + NameLength = AmlGetAslNameSegLength (Buffer); + if ((NameLength == 0) || (NameLength > AML_NAME_SEG_SIZE)) { + return 0; + } + (*SegCount) ++; + Buffer += NameLength; + if (*Buffer == 0) { + break; + } + Buffer ++; + } + + // + // Check SegCoount + // + if (*SegCount > 0xFF) { + return 0; + } + + // + // Calculate total length + // + TotalLength = *Root + *Parent + (*SegCount) * AML_NAME_SEG_SIZE; + if (*SegCount > 2) { + TotalLength += 2; + } else if (*SegCount == 2) { + TotalLength += 1; + } + + // + // Add NULL char + // + TotalLength ++; + + return TotalLength; +} + +/** + Copy mem, and cast all the char in dest to be upper case. + + @param[in] DstBuffer Destination buffer. + @param[in] SrcBuffer Source buffer. + @param[in] Length Buffer length. +**/ +VOID +AmlUpperCaseCopyMem ( + IN UINT8 *DstBuffer, + IN UINT8 *SrcBuffer, + IN UINTN Length + ) +{ + UINTN Index; + + for (Index = 0; Index < Length; Index++) { + if (SrcBuffer[Index] >= 'a' && SrcBuffer[Index] <= 'z') { + DstBuffer[Index] = (UINT8)(SrcBuffer[Index] - 'a' + 'A'); + } else { + DstBuffer[Index] = SrcBuffer[Index]; + } + } +} + +/** + Return AML name according to ASL name. + The caller need free the AmlName returned. + + @param[in] AslPath ASL name. + + @return AmlName +**/ +UINT8 * +AmlNameFromAslName ( + IN UINT8 *AslPath + ) +{ + UINTN Root; + UINTN Parent; + UINTN SegCount; + UINTN TotalLength; + UINTN NameLength; + UINT8 *Buffer; + UINT8 *AmlPath; + UINT8 *AmlBuffer; + + TotalLength = AmlGetAslNameStringSize (AslPath, &Root, &Parent, &SegCount); + if (TotalLength == 0) { + return NULL; + } + + AmlPath = AllocatePool (TotalLength); + ASSERT (AmlPath != NULL); + + AmlBuffer = AmlPath; + Buffer = AslPath; + + // + // Handle Root and Parent + // + if (Root == 1) { + *AmlBuffer = AML_ROOT_CHAR; + AmlBuffer ++; + Buffer ++; + } else if (Parent > 0) { + SetMem (AmlBuffer, Parent, AML_PARENT_PREFIX_CHAR); + AmlBuffer += Parent; + Buffer += Parent; + } + + // + // Handle SegCount + // + if (SegCount > 2) { + *AmlBuffer = AML_MULTI_NAME_PREFIX; + AmlBuffer ++; + *AmlBuffer = (UINT8)SegCount; + AmlBuffer ++; + } else if (SegCount == 2) { + *AmlBuffer = AML_DUAL_NAME_PREFIX; + AmlBuffer ++; + } + + // + // Now to name + // + while (*Buffer != 0) { + NameLength = AmlGetAslNameSegLength (Buffer); + ASSERT ((NameLength != 0) && (NameLength <= AML_NAME_SEG_SIZE)); + AmlUpperCaseCopyMem (AmlBuffer, Buffer, NameLength); + SetMem (AmlBuffer + NameLength, AML_NAME_SEG_SIZE - NameLength, AML_NAME_CHAR__); + Buffer += NameLength; + AmlBuffer += AML_NAME_SEG_SIZE; + if (*Buffer == 0) { + break; + } + Buffer ++; + } + + // + // Add NULL + // + AmlPath[TotalLength - 1] = 0; + + return AmlPath; +} + +/** + Print AML NameSeg. + + @param[in] Buffer AML NameSeg. +**/ +VOID +AmlPrintNameSeg ( + IN UINT8 *Buffer + ) +{ + DEBUG ((EFI_D_ERROR, "%c", Buffer[0])); + if ((Buffer[1] == '_') && (Buffer[2] == '_') && (Buffer[3] == '_')) { + return ; + } + DEBUG ((EFI_D_ERROR, "%c", Buffer[1])); + if ((Buffer[2] == '_') && (Buffer[3] == '_')) { + return ; + } + DEBUG ((EFI_D_ERROR, "%c", Buffer[2])); + if (Buffer[3] == '_') { + return ; + } + DEBUG ((EFI_D_ERROR, "%c", Buffer[3])); + return ; +} + +/** + Print AML NameString. + + @param[in] Buffer AML NameString. +**/ +VOID +AmlPrintNameString ( + IN UINT8 *Buffer + ) +{ + UINT8 SegCount; + UINT8 Index; + + if (*Buffer == AML_ROOT_CHAR) { + // + // RootChar + // + Buffer ++; + DEBUG ((EFI_D_ERROR, "\\")); + } else if (*Buffer == AML_PARENT_PREFIX_CHAR) { + // + // ParentPrefixChar + // + do { + Buffer ++; + DEBUG ((EFI_D_ERROR, "^")); + } while (*Buffer == AML_PARENT_PREFIX_CHAR); + } + + if (*Buffer == AML_DUAL_NAME_PREFIX) { + // + // DualName + // + Buffer ++; + SegCount = 2; + } else if (*Buffer == AML_MULTI_NAME_PREFIX) { + // + // MultiName + // + Buffer ++; + SegCount = *Buffer; + Buffer ++; + } else if (*Buffer == 0) { + // + // NULL Name + // + return ; + } else { + // + // NameSeg + // + SegCount = 1; + } + + AmlPrintNameSeg (Buffer); + Buffer += AML_NAME_SEG_SIZE; + for (Index = 0; Index < SegCount - 1; Index++) { + DEBUG ((EFI_D_ERROR, ".")); + AmlPrintNameSeg (Buffer); + Buffer += AML_NAME_SEG_SIZE; + } + + return ; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.c b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.c new file mode 100644 index 000000000..d16872e14 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.c @@ -0,0 +1,602 @@ +/** @file + This module install ACPI Boot Graphics Resource Table (BGRT). + + Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2016, Microsoft Corporation
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Update information of logo image drawn on screen. + + @param[in] This The pointer to the Boot Logo protocol 2 instance. + @param[in] BltBuffer The BLT buffer for logo drawn on screen. If BltBuffer + is set to NULL, it indicates that logo image is no + longer on the screen. + @param[in] DestinationX X coordinate of destination for the BltBuffer. + @param[in] DestinationY Y coordinate of destination for the BltBuffer. + @param[in] Width Width of rectangle in BltBuffer in pixels. + @param[in] Height Hight of rectangle in BltBuffer in pixels. + + @retval EFI_SUCCESS The boot logo information was updated. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_OUT_OF_RESOURCES The logo information was not updated due to + insufficient memory resources. +**/ +EFI_STATUS +EFIAPI +SetBootLogo ( + IN EFI_BOOT_LOGO_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ); + +/** + Update information of logo image drawn on screen. + + @param[in] This The pointer to the Boot Logo protocol 2 instance. + @param[in] BltBuffer The BLT buffer for logo drawn on screen. If BltBuffer + is set to NULL, it indicates that logo image is no + longer on the screen. + @param[in] DestinationX X coordinate of destination for the BltBuffer. + @param[in] DestinationY Y coordinate of destination for the BltBuffer. + @param[in] Width Width of rectangle in BltBuffer in pixels. + @param[in] Height Hight of rectangle in BltBuffer in pixels. + + @retval EFI_SUCCESS The boot logo information was updated. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_OUT_OF_RESOURCES The logo information was not updated due to + insufficient memory resources. +**/ +EFI_STATUS +EFIAPI +SetBootLogo2 ( + IN EDKII_BOOT_LOGO2_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ); + +/** + Get the location of the boot logo on the screen. + + @param[in] This The pointer to the Boot Logo Protocol 2 instance + @param[out] BltBuffer Returns pointer to the GOP BLT buffer that was + previously registered with SetBootLogo2(). The + buffer returned must not be modified or freed. + @param[out] DestinationX Returns the X start position of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + @param[out] DestinationY Returns the Y start position of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + @param[out] Width Returns the width of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + @param[out] Height Returns the height of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + + @retval EFI_SUCCESS The location of the boot logo was returned. + @retval EFI_NOT_READY The boot logo has not been set. + @retval EFI_INVALID_PARAMETER BltBuffer is NULL. + @retval EFI_INVALID_PARAMETER DestinationX is NULL. + @retval EFI_INVALID_PARAMETER DestinationY is NULL. + @retval EFI_INVALID_PARAMETER Width is NULL. + @retval EFI_INVALID_PARAMETER Height is NULL. +**/ +EFI_STATUS +EFIAPI +GetBootLogo2 ( + IN EDKII_BOOT_LOGO2_PROTOCOL *This, + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL **BltBuffer, + OUT UINTN *DestinationX, + OUT UINTN *DestinationY, + OUT UINTN *Width, + OUT UINTN *Height + ); + +// +// Boot Logo Protocol Handle +// +EFI_HANDLE mBootLogoHandle = NULL; + +// +// Boot Logo Protocol Instance +// +EFI_BOOT_LOGO_PROTOCOL mBootLogoProtocolTemplate = { + SetBootLogo +}; + +/// +/// Boot Logo 2 Protocol instance +/// +EDKII_BOOT_LOGO2_PROTOCOL mBootLogo2ProtocolTemplate = { + SetBootLogo2, + GetBootLogo2 +}; + +EFI_EVENT mBootGraphicsReadyToBootEvent; +UINTN mBootGraphicsResourceTableKey = 0; +BOOLEAN mIsLogoValid = FALSE; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mLogoBltBuffer = NULL; +UINTN mLogoDestX = 0; +UINTN mLogoDestY = 0; +UINTN mLogoWidth = 0; +UINTN mLogoHeight = 0; +BOOLEAN mAcpiBgrtInstalled = FALSE; +BOOLEAN mAcpiBgrtStatusChanged = FALSE; +BOOLEAN mAcpiBgrtBufferChanged = FALSE; + +// +// ACPI Boot Graphics Resource Table template +// +EFI_ACPI_5_0_BOOT_GRAPHICS_RESOURCE_TABLE mBootGraphicsResourceTableTemplate = { + { + EFI_ACPI_5_0_BOOT_GRAPHICS_RESOURCE_TABLE_SIGNATURE, + sizeof (EFI_ACPI_5_0_BOOT_GRAPHICS_RESOURCE_TABLE), + EFI_ACPI_5_0_BOOT_GRAPHICS_RESOURCE_TABLE_REVISION, // Revision + 0x00, // Checksum will be updated at runtime + // + // It is expected that these values will be updated at EntryPoint. + // + {0x00}, // OEM ID is a 6 bytes long field + 0x00, // OEM Table ID(8 bytes long) + 0x00, // OEM Revision + 0x00, // Creator ID + 0x00, // Creator Revision + }, + EFI_ACPI_5_0_BGRT_VERSION, // Version + EFI_ACPI_5_0_BGRT_STATUS_VALID, // Status + EFI_ACPI_5_0_BGRT_IMAGE_TYPE_BMP, // Image Type + 0, // Image Address + 0, // Image Offset X + 0 // Image Offset Y +}; + +/** + Update information of logo image drawn on screen. + + @param This The pointer to the Boot Logo protocol instance. + @param BltBuffer The BLT buffer for logo drawn on screen. If BltBuffer + is set to NULL, it indicates that logo image is no + longer on the screen. + @param DestinationX X coordinate of destination for the BltBuffer. + @param DestinationY Y coordinate of destination for the BltBuffer. + @param Width Width of rectangle in BltBuffer in pixels. + @param Height Hight of rectangle in BltBuffer in pixels. + + @retval EFI_SUCCESS The boot logo information was updated. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_OUT_OF_RESOURCES The logo information was not updated due to + insufficient memory resources. + +**/ +EFI_STATUS +EFIAPI +SetBootLogo ( + IN EFI_BOOT_LOGO_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + // + // Call same service in Boot Logo 2 Protocol + // + return SetBootLogo2 ( + &mBootLogo2ProtocolTemplate, + BltBuffer, + DestinationX, + DestinationY, + Width, + Height + ); +} + +/** + Update information of logo image drawn on screen. + + @param[in] This The pointer to the Boot Logo protocol 2 instance. + @param[in] BltBuffer The BLT buffer for logo drawn on screen. If BltBuffer + is set to NULL, it indicates that logo image is no + longer on the screen. + @param[in] DestinationX X coordinate of destination for the BltBuffer. + @param[in] DestinationY Y coordinate of destination for the BltBuffer. + @param[in] Width Width of rectangle in BltBuffer in pixels. + @param[in] Height Hight of rectangle in BltBuffer in pixels. + + @retval EFI_SUCCESS The boot logo information was updated. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_OUT_OF_RESOURCES The logo information was not updated due to + insufficient memory resources. +**/ +EFI_STATUS +EFIAPI +SetBootLogo2 ( + IN EDKII_BOOT_LOGO2_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + UINT32 Result32; + + if (BltBuffer == NULL) { + mIsLogoValid = FALSE; + mAcpiBgrtStatusChanged = TRUE; + return EFI_SUCCESS; + } + + // + // Width and height are not allowed to be zero. + // + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Verify destination, width, and height do not overflow 32-bit values. + // The Boot Graphics Resource Table only has 32-bit fields for these values. + // + Status = SafeUintnToUint32 (DestinationX, &Result32); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + Status = SafeUintnToUint32 (DestinationY, &Result32); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + Status = SafeUintnToUint32 (Width, &Result32); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + Status = SafeUintnToUint32 (Height, &Result32); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + // + // Ensure the Height * Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) does + // not overflow UINTN + // + Status = SafeUintnMult ( + Width, + Height, + &BufferSize + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + Status = SafeUintnMult ( + BufferSize, + sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), + &BufferSize + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Update state + // + mAcpiBgrtBufferChanged = TRUE; + + // + // Free old logo buffer + // + if (mLogoBltBuffer != NULL) { + FreePool (mLogoBltBuffer); + mLogoBltBuffer = NULL; + } + + // + // Allocate new logo buffer + // + mLogoBltBuffer = AllocateCopyPool (BufferSize, BltBuffer); + if (mLogoBltBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + mLogoDestX = DestinationX; + mLogoDestY = DestinationY; + mLogoWidth = Width; + mLogoHeight = Height; + mIsLogoValid = TRUE; + + return EFI_SUCCESS; +} + +/** + Get the location of the boot logo on the screen. + + @param[in] This The pointer to the Boot Logo Protocol 2 instance + @param[out] BltBuffer Returns pointer to the GOP BLT buffer that was + previously registered with SetBootLogo2(). The + buffer returned must not be modified or freed. + @param[out] DestinationX Returns the X start position of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + @param[out] DestinationY Returns the Y start position of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + @param[out] Width Returns the width of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + @param[out] Height Returns the height of the GOP BLT buffer + that was previously registered with SetBootLogo2(). + + @retval EFI_SUCCESS The location of the boot logo was returned. + @retval EFI_NOT_READY The boot logo has not been set. + @retval EFI_INVALID_PARAMETER BltBuffer is NULL. + @retval EFI_INVALID_PARAMETER DestinationX is NULL. + @retval EFI_INVALID_PARAMETER DestinationY is NULL. + @retval EFI_INVALID_PARAMETER Width is NULL. + @retval EFI_INVALID_PARAMETER Height is NULL. +**/ +EFI_STATUS +EFIAPI +GetBootLogo2 ( + IN EDKII_BOOT_LOGO2_PROTOCOL *This, + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL **BltBuffer, + OUT UINTN *DestinationX, + OUT UINTN *DestinationY, + OUT UINTN *Width, + OUT UINTN *Height + ) +{ + // + // If the boot logo has not been set with SetBootLogo() or SetBootLogo() was + // called with a NULL BltBuffer then the boot logo is not valid and + // EFI_NOT_READY is returned. + // + if (mLogoBltBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "Request to get boot logo location before boot logo has been set.\n")); + return EFI_NOT_READY; + } + + // + // Make sure none of the boot logo location parameters are NULL. + // + if (BltBuffer == NULL || DestinationX == NULL || DestinationY == NULL || + Width == NULL || Height == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Boot logo is valid. Return values from module globals. + // + *BltBuffer = mLogoBltBuffer; + *DestinationX = mLogoDestX; + *DestinationY = mLogoDestY; + *Width = mLogoWidth; + *Height = mLogoHeight; + + return EFI_SUCCESS; +} + +/** + Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to + install the Boot Graphics Resource Table. + + @param[in] Event The Event that is being processed. + @param[in] Context The Event Context. + +**/ +VOID +EFIAPI +BgrtReadyToBootEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol; + VOID *ImageBuffer; + UINT32 BmpSize; + + // + // Get ACPI Table protocol. + // + Status = gBS->LocateProtocol ( + &gEfiAcpiTableProtocolGuid, + NULL, + (VOID **) &AcpiTableProtocol + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // Check whether Boot Graphics Resource Table is already installed. + // + if (mAcpiBgrtInstalled) { + if (!mAcpiBgrtStatusChanged && !mAcpiBgrtBufferChanged) { + // + // Nothing has changed + // + return; + } else { + // + // If BGRT data change happens, then uninstall orignal AcpiTable first + // + Status = AcpiTableProtocol->UninstallAcpiTable ( + AcpiTableProtocol, + mBootGraphicsResourceTableKey + ); + if (EFI_ERROR (Status)) { + return; + } + } + } else { + // + // Check whether Logo exists + // + if (mLogoBltBuffer == NULL) { + return; + } + } + + if (mAcpiBgrtBufferChanged) { + // + // Free the old BMP image buffer + // + ImageBuffer = (UINT8 *)(UINTN)mBootGraphicsResourceTableTemplate.ImageAddress; + if (ImageBuffer != NULL) { + FreePool (ImageBuffer); + } + + // + // Convert GOP Blt buffer to BMP image. Pass in ImageBuffer set to NULL + // so the BMP image is allocated by TranslateGopBltToBmp(). + // + ImageBuffer = NULL; + Status = TranslateGopBltToBmp ( + mLogoBltBuffer, + (UINT32)mLogoHeight, + (UINT32)mLogoWidth, + &ImageBuffer, + &BmpSize + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // Free the logo buffer + // + FreePool (mLogoBltBuffer); + mLogoBltBuffer = NULL; + + // + // Update BMP image fields of the Boot Graphics Resource Table + // + mBootGraphicsResourceTableTemplate.ImageAddress = (UINT64)(UINTN)ImageBuffer; + mBootGraphicsResourceTableTemplate.ImageOffsetX = (UINT32)mLogoDestX; + mBootGraphicsResourceTableTemplate.ImageOffsetY = (UINT32)mLogoDestY; + } + + // + // Update Status field of Boot Graphics Resource Table + // + if (mIsLogoValid) { + mBootGraphicsResourceTableTemplate.Status = EFI_ACPI_5_0_BGRT_STATUS_VALID; + } else { + mBootGraphicsResourceTableTemplate.Status = EFI_ACPI_5_0_BGRT_STATUS_INVALID; + } + + // + // Update Checksum of Boot Graphics Resource Table + // + mBootGraphicsResourceTableTemplate.Header.Checksum = 0; + mBootGraphicsResourceTableTemplate.Header.Checksum = + CalculateCheckSum8 ( + (UINT8 *)&mBootGraphicsResourceTableTemplate, + sizeof (EFI_ACPI_5_0_BOOT_GRAPHICS_RESOURCE_TABLE) + ); + + // + // Publish Boot Graphics Resource Table. + // + Status = AcpiTableProtocol->InstallAcpiTable ( + AcpiTableProtocol, + &mBootGraphicsResourceTableTemplate, + sizeof (EFI_ACPI_5_0_BOOT_GRAPHICS_RESOURCE_TABLE), + &mBootGraphicsResourceTableKey + ); + if (EFI_ERROR (Status)) { + return; + } + + mAcpiBgrtInstalled = TRUE; + mAcpiBgrtStatusChanged = FALSE; + mAcpiBgrtBufferChanged = FALSE; +} + +/** + The module Entry Point of the Boot Graphics Resource Table DXE driver. + + @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 entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +BootGraphicsDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER *Header; + + // + // Update Header fields of Boot Graphics Resource Table from PCDs + // + Header = &mBootGraphicsResourceTableTemplate.Header; + ZeroMem (Header->OemId, sizeof (Header->OemId)); + CopyMem ( + Header->OemId, + PcdGetPtr (PcdAcpiDefaultOemId), + MIN (PcdGetSize (PcdAcpiDefaultOemId), sizeof (Header->OemId)) + ); + WriteUnaligned64 (&Header->OemTableId, PcdGet64 (PcdAcpiDefaultOemTableId)); + Header->OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + Header->CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + Header->CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + + // + // Install Boot Logo and Boot Logo 2 Protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mBootLogoHandle, + &gEfiBootLogoProtocolGuid, + &mBootLogoProtocolTemplate, + &gEdkiiBootLogo2ProtocolGuid, + &mBootLogo2ProtocolTemplate, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Register notify function to install BGRT on ReadyToBoot Event. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + BgrtReadyToBootEventNotify, + NULL, + &gEfiEventReadyToBootGuid, + &mBootGraphicsReadyToBootEvent + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf new file mode 100644 index 000000000..911092fc9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf @@ -0,0 +1,60 @@ +## @file +# This module install ACPI Boot Graphics Resource Table (BGRT). +# +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2016, Microsoft Corporation
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BootGraphicsResourceTableDxe + MODULE_UNI_FILE = BootGraphicsResourceTableDxe.uni + FILE_GUID = B8E62775-BB0A-43f0-A843-5BE8B14F8CCD + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = BootGraphicsDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + BootGraphicsResourceTableDxe.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiLib + UefiBootServicesTableLib + DebugLib + PcdLib + SafeIntLib + BmpSupportLib + +[Protocols] + gEfiAcpiTableProtocolGuid ## CONSUMES + gEfiBootLogoProtocolGuid ## PRODUCES + gEdkiiBootLogo2ProtocolGuid ## PRODUCES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision ## CONSUMES + +[Guids] + gEfiEventReadyToBootGuid ## CONSUMES ## Event + +[UserExtensions.TianoCore."ExtraFiles"] + BootGraphicsResourceTableDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.uni new file mode 100644 index 000000000..0a94edae4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.uni @@ -0,0 +1,16 @@ +// /** @file +// This module install ACPI Boot Graphics Resource Table (BGRT). +// +// This module installs the ACPI Boot Graphics Resource Table (BGRT). +// +// Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Installs ACPI Boot Graphics Resource Table (BGRT)" + +#string STR_MODULE_DESCRIPTION #language en-US "This module installs the ACPI Boot Graphics Resource Table (BGRT)." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxeExtra.uni new file mode 100644 index 000000000..e4956d4a0 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// BootGraphicsResourceTableDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"ACPI Boot Graphics Resource Table DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf new file mode 100644 index 000000000..fb149c2f0 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf @@ -0,0 +1,86 @@ +## @file +# Boot Script Executor Module +# +# This is a standalone Boot Script Executor. Standalone means it does not +# depends on any PEI or DXE service. +# +# Copyright (c) 2006 - 2018, 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 = BootScriptExecutorDxe + MODULE_UNI_FILE = BootScriptExecutorDxe.uni + FILE_GUID = FA20568B-548B-4b2b-81EF-1BA08D4A3CEC + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = BootScriptExecutorEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + ScriptExecute.h + ScriptExecute.c + +[Sources.X64] + X64/SetIdtEntry.c + X64/S3Asm.nasm + +[Sources.Ia32] + IA32/SetIdtEntry.c + IA32/S3Asm.nasm + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PcdLib + BaseMemoryLib + UefiDriverEntryPoint + BaseLib + S3BootScriptLib + PeCoffLib + DxeServicesLib + UefiBootServicesTableLib + CacheMaintenanceLib + UefiLib + DebugAgentLib + LockBoxLib + CpuExceptionHandlerLib + DevicePathLib + DxeServicesTableLib + +[Guids] + gEfiBootScriptExecutorVariableGuid ## PRODUCES ## UNDEFINED # SaveLockBox + gEfiBootScriptExecutorContextGuid ## PRODUCES ## UNDEFINED # SaveLockBox + gEdkiiMemoryProfileGuid ## SOMETIMES_CONSUMES ## GUID # Locate protocol + +[Protocols] + ## NOTIFY + ## CONSUMES + gEfiDxeSmmReadyToLockProtocolGuid + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMemoryProfilePropertyMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + +[Depex] + gEfiLockBoxProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + BootScriptExecutorDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.uni new file mode 100644 index 000000000..44aaf1e4c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// Boot Script Executor Module +// +// This is a standalone Boot Script Executor. Standalone means it does not +// depends on any PEI or DXE service. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Boot Script Executor Module" + +#string STR_MODULE_DESCRIPTION #language en-US "This is a standalone Boot Script Executor. Standalone means it does not depends on any PEI or DXE service." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxeExtra.uni new file mode 100644 index 000000000..05b472b44 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// BootScriptExecutorDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Boot Script Execution DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/S3Asm.nasm b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/S3Asm.nasm new file mode 100644 index 000000000..e121cc937 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/S3Asm.nasm @@ -0,0 +1,62 @@ +;; @file +; This is the assembly code for transferring to control to OS S3 waking vector +; for IA32 platform +; +; Copyright (c) 2006, Intel Corporation. All rights reserved.
+; +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;; + SECTION .text + +global ASM_PFX(AsmFixAddress16) +global ASM_PFX(AsmJmpAddr32) + +;----------------------------------------- +;VOID +;AsmTransferControl ( +; IN UINT32 S3WakingVector, +; IN UINT32 AcpiLowMemoryBase +; ); +;----------------------------------------- + +global ASM_PFX(AsmTransferControl) +ASM_PFX(AsmTransferControl): + ; S3WakingVector :DWORD + ; AcpiLowMemoryBase :DWORD + push ebp + mov ebp, esp + lea eax, [.0] + push 0x28 ; CS + push eax + mov ecx, [ebp + 8] + shrd ebx, ecx, 20 + and ecx, 0xf + mov bx, cx + mov [@jmp_addr + 1], ebx + retf + +BITS 16 +.0: + mov ax, 0x30 +o32 mov ds, eax +o32 mov es, eax +o32 mov fs, eax +o32 mov gs, eax +o32 mov ss, eax + mov eax, cr0 ; Get control register 0 + and eax, 0x0fffffffe ; Clear PE bit (bit #0) + mov cr0, eax ; Activate real mode +@jmp_addr: + jmp 0x0:0x0 + +global ASM_PFX(AsmTransferControl32) +ASM_PFX(AsmTransferControl32): + jmp ASM_PFX(AsmTransferControl) + +; dummy +global ASM_PFX(AsmTransferControl16) +ASM_PFX(AsmTransferControl16): +ASM_PFX(AsmFixAddress16): DD 0 +ASM_PFX(AsmJmpAddr32): DD 0 + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/SetIdtEntry.c b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/SetIdtEntry.c new file mode 100644 index 000000000..1e3c5b849 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/IA32/SetIdtEntry.c @@ -0,0 +1,56 @@ +/** @file + Set a IDT entry for debug purpose + + Set a IDT entry for interrupt vector 3 for debug purpose for IA32 platform + +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "ScriptExecute.h" + +/** + Set a IDT entry for interrupt vector 3 for debug purpose. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + +**/ +VOID +SetIdtEntry ( + IN ACPI_S3_CONTEXT *AcpiS3Context + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + IA32_DESCRIPTOR *IdtDescriptor; + UINTN S3DebugBuffer; + EFI_STATUS Status; + + // + // Restore IDT for debug + // + IdtDescriptor = (IA32_DESCRIPTOR *) (UINTN) (AcpiS3Context->IdtrProfile); + AsmWriteIdtr (IdtDescriptor); + + // + // Setup the default CPU exception handlers + // + Status = InitializeCpuExceptionHandlers (NULL); + ASSERT_EFI_ERROR (Status); + + DEBUG_CODE ( + // + // Update IDT entry INT3 if the instruction is valid in it + // + S3DebugBuffer = (UINTN) (AcpiS3Context->S3DebugBufferAddress); + if (*(UINTN *)S3DebugBuffer != (UINTN) -1) { + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (3 * sizeof (IA32_IDT_GATE_DESCRIPTOR))); + IdtEntry->Bits.OffsetLow = (UINT16)S3DebugBuffer; + IdtEntry->Bits.Selector = (UINT16)AsmReadCs (); + IdtEntry->Bits.Reserved_0 = 0; + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + IdtEntry->Bits.OffsetHigh = (UINT16)(S3DebugBuffer >> 16); + } + ); +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.c b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.c new file mode 100644 index 000000000..b2ae9ec3a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.c @@ -0,0 +1,497 @@ +/** @file + This is the code for Boot Script Executer module. + + This driver is dispatched by Dxe core and the driver will reload itself to ACPI reserved memory + in the entry point. The functionality is to interpret and restore the S3 boot script + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "ScriptExecute.h" + +EFI_GUID mBootScriptExecutorImageGuid = { + 0x9a8d3433, 0x9fe8, 0x42b6, { 0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b } +}; + +BOOLEAN mPage1GSupport = FALSE; +UINT64 mAddressEncMask = 0; + +/** + Entry function of Boot script exector. This function will be executed in + S3 boot path. + This function should not return, because it is invoked by switch stack. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE + + @retval EFI_INVALID_PARAMETER - OS waking vector not found + @retval EFI_UNSUPPORTED - something wrong when we resume to OS +**/ +EFI_STATUS +EFIAPI +S3BootScriptExecutorEntryFunction ( + IN ACPI_S3_CONTEXT *AcpiS3Context, + IN PEI_S3_RESUME_STATE *PeiS3ResumeState + ) +{ + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; + EFI_STATUS Status; + UINTN TempStackTop; + UINTN TempStack[0x10]; + UINTN AsmTransferControl16Address; + IA32_DESCRIPTOR IdtDescriptor; + + // + // Disable interrupt of Debug timer, since new IDT table cannot handle it. + // + SaveAndSetDebugTimerInterrupt (FALSE); + + AsmReadIdtr (&IdtDescriptor); + // + // Restore IDT for debug + // + SetIdtEntry (AcpiS3Context); + + // + // Initialize Debug Agent to support source level debug in S3 path, it will disable interrupt and Debug Timer. + // + InitializeDebugAgent (DEBUG_AGENT_INIT_S3, (VOID *)&IdtDescriptor, NULL); + + // + // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL + // for that parameter. + // + Status = S3BootScriptExecute (); + + // + // If invalid script table or opcode in S3 boot script table. + // + ASSERT_EFI_ERROR (Status); + + if (EFI_ERROR (Status)) { + CpuDeadLoop (); + return Status; + } + + AsmWbinvd (); + + // + // Get ACPI Table Address + // + Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable)); + + // + // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume. + // + if (PeiS3ResumeState != 0) { + // + // Need report status back to S3ResumePeim. + // If boot script execution is failed, S3ResumePeim wil report the error status code. + // + PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status; + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + // + // X64 S3 Resume + // + DEBUG ((DEBUG_INFO, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n")); + PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl32; + + if ((Facs != NULL) && + (Facs->Signature == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) && + (Facs->FirmwareWakingVector != 0) ) { + // + // more step needed - because relative address is handled differently between X64 and IA32. + // + AsmTransferControl16Address = (UINTN)AsmTransferControl16; + AsmFixAddress16 = (UINT32)AsmTransferControl16Address; + AsmJmpAddr32 = (UINT32)((Facs->FirmwareWakingVector & 0xF) | ((Facs->FirmwareWakingVector & 0xFFFF0) << 12)); + } + + AsmDisablePaging64 ( + PeiS3ResumeState->ReturnCs, + (UINT32)PeiS3ResumeState->ReturnEntryPoint, + (UINT32)(UINTN)AcpiS3Context, + (UINT32)(UINTN)PeiS3ResumeState, + (UINT32)PeiS3ResumeState->ReturnStackPointer + ); + } else { + // + // IA32 S3 Resume + // + DEBUG ((DEBUG_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n")); + PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl; + + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint, + (VOID *)(UINTN)AcpiS3Context, + (VOID *)(UINTN)PeiS3ResumeState, + (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer + ); + } + + // + // Never run to here + // + CpuDeadLoop(); + return EFI_UNSUPPORTED; + } + + // + // S3ResumePeim does not provide a way to jump back to itself, so resume to OS here directly + // + if (Facs->XFirmwareWakingVector != 0) { + // + // Switch to native waking vector + // + TempStackTop = (UINTN)&TempStack + sizeof(TempStack); + if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) && + ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) && + ((Facs->OspmFlags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) { + // + // X64 long mode waking vector + // + DEBUG ((DEBUG_INFO, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector)); + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector, + NULL, + NULL, + (VOID *)(UINTN)TempStackTop + ); + } else { + // Unsupported for 32bit DXE, 64bit OS vector + DEBUG (( EFI_D_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n")); + ASSERT (FALSE); + } + } else { + // + // IA32 protected mode waking vector (Page disabled) + // + DEBUG ((DEBUG_INFO, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector)); + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + AsmDisablePaging64 ( + 0x10, + (UINT32)Facs->XFirmwareWakingVector, + 0, + 0, + (UINT32)TempStackTop + ); + } else { + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector, + NULL, + NULL, + (VOID *)(UINTN)TempStackTop + ); + } + } + } else { + // + // 16bit Realmode waking vector + // + DEBUG ((DEBUG_INFO, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector)); + AsmTransferControl (Facs->FirmwareWakingVector, 0x0); + } + + // + // Never run to here + // + CpuDeadLoop(); + return EFI_UNSUPPORTED; +} + +/** + Register image to memory profile. + + @param FileName File name of the image. + @param ImageBase Image base address. + @param ImageSize Image size. + @param FileType File type of the image. + +**/ +VOID +RegisterMemoryProfileImage ( + IN EFI_GUID *FileName, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN EFI_FV_FILETYPE FileType + ) +{ + EFI_STATUS Status; + EDKII_MEMORY_PROFILE_PROTOCOL *ProfileProtocol; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; + UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)]; + + if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0) { + + FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer; + Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID **) &ProfileProtocol); + if (!EFI_ERROR (Status)) { + EfiInitializeFwVolDevicepathNode (FilePath, FileName); + SetDevicePathEndNode (FilePath + 1); + + Status = ProfileProtocol->RegisterImage ( + ProfileProtocol, + (EFI_DEVICE_PATH_PROTOCOL *) FilePath, + ImageBase, + ImageSize, + FileType + ); + } + } +} + +/** + This is the Event notification function to reload BootScriptExecutor image + to RESERVED mem and save it to LockBox. + + @param Event Pointer to this event + @param Context Event handler private data + **/ +VOID +EFIAPI +ReadyToLockEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *Interface; + UINT8 *Buffer; + UINTN BufferSize; + EFI_HANDLE NewImageHandle; + UINTN Pages; + EFI_PHYSICAL_ADDRESS FfsBuffer; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc; + + Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + return; + } + + // + // A workaround: Here we install a dummy handle + // + NewImageHandle = NULL; + Status = gBS->InstallProtocolInterface ( + &NewImageHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Reload BootScriptExecutor image itself to RESERVED mem + // + Status = GetSectionFromAnyFv ( + &gEfiCallerIdGuid, + EFI_SECTION_PE32, + 0, + (VOID **) &Buffer, + &BufferSize + ); + ASSERT_EFI_ERROR (Status); + ImageContext.Handle = Buffer; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + ASSERT_EFI_ERROR (Status); + if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { + Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment)); + } else { + Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize); + } + FfsBuffer = 0xFFFFFFFF; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + Pages, + &FfsBuffer + ); + ASSERT_EFI_ERROR (Status); + + // + // Make sure that the buffer can be used to store code. + // + Status = gDS->GetMemorySpaceDescriptor (FfsBuffer, &MemDesc); + if (!EFI_ERROR (Status) && (MemDesc.Attributes & EFI_MEMORY_XP) != 0) { + gDS->SetMemorySpaceAttributes ( + FfsBuffer, + EFI_PAGES_TO_SIZE (Pages), + MemDesc.Attributes & (~EFI_MEMORY_XP) + ); + } + + ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; + // + // Align buffer on section boundary + // + ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; + ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1); + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + ASSERT_EFI_ERROR (Status); + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + ASSERT_EFI_ERROR (Status); + + // + // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer + // + gBS->FreePool (Buffer); + + // + // Flush the instruction cache so the image data is written before we execute it + // + InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); + + RegisterMemoryProfileImage ( + &gEfiCallerIdGuid, + ImageContext.ImageAddress, + ImageContext.ImageSize, + EFI_FV_FILETYPE_DRIVER + ); + + Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST); + ASSERT_EFI_ERROR (Status); + + // + // Additional step for BootScript integrity + // Save BootScriptExecutor image + // + Status = SaveLockBox ( + &mBootScriptExecutorImageGuid, + (VOID *)(UINTN)ImageContext.ImageAddress, + (UINTN)ImageContext.ImageSize + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); + ASSERT_EFI_ERROR (Status); + + gBS->CloseEvent (Event); +} + +/** + Entrypoint of Boot script exector driver, this function will be executed in + normal boot phase and invoked by DXE dispatch. + + @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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. +**/ +EFI_STATUS +EFIAPI +BootScriptExecutorEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + UINTN BufferSize; + UINTN Pages; + BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable; + EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer; + EFI_STATUS Status; + VOID *DevicePath; + EFI_EVENT ReadyToLockEvent; + VOID *Registration; + UINT32 RegEax; + UINT32 RegEdx; + + if (!PcdGetBool (PcdAcpiS3Enable)) { + return EFI_UNSUPPORTED; + } + + // + // Make sure AddressEncMask is contained to smallest supported address field. + // + mAddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + // + // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry + // point is loaded by DXE code which is the first time loaded. or else, it is already + // be reloaded be itself.This is a work-around + // + Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath); + if (EFI_ERROR (Status)) { + // + // Create ReadyToLock event to reload BootScriptExecutor image + // to RESERVED mem and save it to LockBox. + // + ReadyToLockEvent = EfiCreateProtocolNotifyEvent ( + &gEfiDxeSmmReadyToLockProtocolGuid, + TPL_NOTIFY, + ReadyToLockEventNotify, + NULL, + &Registration + ); + ASSERT (ReadyToLockEvent != NULL); + } else { + // + // the entry point is invoked after reloading. following code only run in RESERVED mem + // + if (PcdGetBool(PcdUse1GPageTable)) { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + mPage1GSupport = TRUE; + } + } + } + + BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE); + + BootScriptExecutorBuffer = 0xFFFFFFFF; + Pages = EFI_SIZE_TO_PAGES(BufferSize); + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + Pages, + &BootScriptExecutorBuffer + ); + ASSERT_EFI_ERROR (Status); + + EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer; + EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ; + + Status = SaveLockBox ( + &gEfiBootScriptExecutorVariableGuid, + &BootScriptExecutorBuffer, + sizeof(BootScriptExecutorBuffer) + ); + ASSERT_EFI_ERROR (Status); + + // + // Additional step for BootScript integrity + // Save BootScriptExecutor context + // + Status = SaveLockBox ( + &gEfiBootScriptExecutorContextGuid, + EfiBootScriptExecutorVariable, + sizeof(*EfiBootScriptExecutorVariable) + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); + ASSERT_EFI_ERROR (Status); + } + + return EFI_SUCCESS; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.h b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.h new file mode 100644 index 000000000..14bb63f60 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/ScriptExecute.h @@ -0,0 +1,91 @@ +/** @file + The header file for Boot Script Executer module. + + This driver is dispatched by Dxe core and the driver will reload itself to ACPI reserved memory + in the entry point. The functionality is to interpret and restore the S3 boot script + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef _BOOT_SCRIPT_EXECUTOR_H_ +#define _BOOT_SCRIPT_EXECUTOR_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 + +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +/** + a ASM function to transfer control to OS. + + @param S3WakingVector The S3 waking up vector saved in ACPI Facs table + @param AcpiLowMemoryBase a buffer under 1M which could be used during the transfer +**/ +VOID +AsmTransferControl ( + IN UINT32 S3WakingVector, + IN UINT32 AcpiLowMemoryBase + ); +/** + a 32bit ASM function to transfer control to OS. + + @param S3WakingVector The S3 waking up vector saved in ACPI Facs table + @param AcpiLowMemoryBase a buffer under 1M which could be used during the transfer +**/ +VOID +AsmTransferControl32 ( + IN UINT32 S3WakingVector, + IN UINT32 AcpiLowMemoryBase + ); +/** + a 16bit ASM function to transfer control to OS. +**/ +VOID +AsmTransferControl16 ( + VOID + ); +/** + Set a IDT entry for interrupt vector 3 for debug purpose. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + +**/ +VOID +SetIdtEntry ( + IN ACPI_S3_CONTEXT *AcpiS3Context + ); + +extern UINT32 AsmFixAddress16; +extern UINT32 AsmJmpAddr32; +extern BOOLEAN mPage1GSupport; +extern UINT64 mAddressEncMask; + +#endif //_BOOT_SCRIPT_EXECUTOR_H_ diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.nasm b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.nasm new file mode 100644 index 000000000..713fd7b2e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.nasm @@ -0,0 +1,130 @@ +;; @file +; This is the assembly code for transferring to control to OS S3 waking vector +; for X64 platform +; +; Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
+; +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;; + +extern ASM_PFX(mOriginalHandler) +extern ASM_PFX(PageFaultHandler) + + DEFAULT REL + SECTION .text + +global ASM_PFX(AsmFixAddress16) +global ASM_PFX(AsmJmpAddr32) + +global ASM_PFX(AsmTransferControl) +ASM_PFX(AsmTransferControl): + ; rcx S3WakingVector :DWORD + ; rdx AcpiLowMemoryBase :DWORD + lea eax, [.0] + mov r8, 0x2800000000 + or rax, r8 + push rax + shrd ebx, ecx, 20 + and ecx, 0xf + mov bx, cx + mov [@jmp_addr + 1], ebx + retf +BITS 16 +.0: + mov ax, 0x30 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov eax, cr0 + mov ebx, cr4 + and eax, ((~ 0x80000001) & 0xffffffff) + and bl, ~ (1 << 5) + mov cr0, eax + mov ecx, 0xc0000080 + rdmsr + and ah, ~ 1 + wrmsr + mov cr4, ebx +@jmp_addr: + jmp 0x0:0x0 + +global ASM_PFX(AsmTransferControl32) +ASM_PFX(AsmTransferControl32): +BITS 32 + ; S3WakingVector :DWORD + ; AcpiLowMemoryBase :DWORD + push ebp + mov ebp, esp + DB 0x8d, 0x5 ; lea eax, AsmTransferControl16 +ASM_PFX(AsmFixAddress16): DD 0 + push 0x28 ; CS + push eax + retf + +global ASM_PFX(AsmTransferControl16) +ASM_PFX(AsmTransferControl16): +BITS 16 + mov ax, 0x30 +o32 mov ds, eax +o32 mov es, eax +o32 mov fs, eax +o32 mov gs, eax +o32 mov ss, eax + mov eax, cr0 ; Get control register 0 + and eax, 0fffffffeh ; Clear PE bit (bit #0) + mov cr0, eax ; Activate real mode + DB 0xea ; jmp far AsmJmpAddr32 +ASM_PFX(AsmJmpAddr32): DD 0 + +global ASM_PFX(PageFaultHandlerHook) +ASM_PFX(PageFaultHandlerHook): +BITS 64 + push rax ; save all volatile registers + push rcx + push rdx + push r8 + push r9 + push r10 + push r11 + ; save volatile fp registers + add rsp, -0x68 + stmxcsr [rsp + 0x60] + movdqa [rsp + 0x0], xmm0 + movdqa [rsp + 0x10], xmm1 + movdqa [rsp + 0x20], xmm2 + movdqa [rsp + 0x30], xmm3 + movdqa [rsp + 0x40], xmm4 + movdqa [rsp + 0x50], xmm5 + + add rsp, -0x20 + call ASM_PFX(PageFaultHandler) + add rsp, 0x20 + + ; load volatile fp registers + ldmxcsr [rsp + 0x60] + movdqa xmm0, [rsp + 0x0] + movdqa xmm1, [rsp + 0x10] + movdqa xmm2, [rsp + 0x20] + movdqa xmm3, [rsp + 0x30] + movdqa xmm4, [rsp + 0x40] + movdqa xmm5, [rsp + 0x50] + add rsp, 0x68 + + test al, al + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdx + pop rcx + pop rax ; restore all volatile registers + jnz .1 + jmp qword [ASM_PFX(mOriginalHandler)] +.1: + add rsp, 0x8 ; skip error code for PF + iretq + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c new file mode 100644 index 000000000..0d448cc60 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c @@ -0,0 +1,261 @@ +/** @file + Set a IDT entry for debug purpose + + Set a IDT entry for interrupt vector 3 for debug purpose for x64 platform + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "ScriptExecute.h" + +// +// 8 extra pages for PF handler. +// +#define EXTRA_PAGE_TABLE_PAGES 8 + +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_PS BIT7 + +UINT64 mPhyMask; +VOID *mOriginalHandler; +UINTN mPageFaultBuffer; +UINTN mPageFaultIndex = 0; +// +// Store the uplink information for each page being used. +// +UINT64 *mPageFaultUplink[EXTRA_PAGE_TABLE_PAGES]; + +/** + Page fault handler. + +**/ +VOID +EFIAPI +PageFaultHandlerHook ( + VOID + ); + +/** + Hook IDT with our page fault handler so that the on-demand paging works on page fault. + + @param IdtEntry a pointer to IDT entry + +**/ +VOID +HookPageFaultHandler ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry + ) +{ + UINT32 RegEax; + UINT8 PhysicalAddressBits; + UINTN PageFaultHandlerHookAddress; + + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + mPhyMask = LShiftU64 (1, PhysicalAddressBits) - 1; + mPhyMask &= (1ull << 48) - SIZE_4KB; + + // + // Set Page Fault entry to catch >4G access + // + PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook; + mOriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16)); + IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress; + IdtEntry->Bits.Selector = (UINT16)AsmReadCs (); + IdtEntry->Bits.Reserved_0 = 0; + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16); + IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32); + IdtEntry->Bits.Reserved_1 = 0; + + if (mPage1GSupport) { + mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(2); + }else { + mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(6); + } + ZeroMem (mPageFaultUplink, sizeof (mPageFaultUplink)); +} + +/** + The function will check if current waking vector is long mode. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + + @retval TRUE Current context need long mode waking vector. + @retval FALSE Current context need not long mode waking vector. +**/ +BOOLEAN +IsLongModeWakingVector ( + IN ACPI_S3_CONTEXT *AcpiS3Context + ) +{ + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; + + Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable)); + if ((Facs == NULL) || + (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) || + ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) { + // Something wrong with FACS + return FALSE; + } + if (Facs->XFirmwareWakingVector != 0) { + if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) && + ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) && + ((Facs->OspmFlags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) { + // Both BIOS and OS wants 64bit vector + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + return TRUE; + } + } + } + return FALSE; +} + +/** + Set a IDT entry for interrupt vector 3 for debug purpose. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + +**/ +VOID +SetIdtEntry ( + IN ACPI_S3_CONTEXT *AcpiS3Context + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + IA32_DESCRIPTOR *IdtDescriptor; + UINTN S3DebugBuffer; + EFI_STATUS Status; + + // + // Restore IDT for debug + // + IdtDescriptor = (IA32_DESCRIPTOR *) (UINTN) (AcpiS3Context->IdtrProfile); + AsmWriteIdtr (IdtDescriptor); + + // + // Setup the default CPU exception handlers + // + Status = InitializeCpuExceptionHandlers (NULL); + ASSERT_EFI_ERROR (Status); + + DEBUG_CODE ( + // + // Update IDT entry INT3 if the instruction is valid in it + // + S3DebugBuffer = (UINTN) (AcpiS3Context->S3DebugBufferAddress); + if (*(UINTN *)S3DebugBuffer != (UINTN) -1) { + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (3 * sizeof (IA32_IDT_GATE_DESCRIPTOR))); + IdtEntry->Bits.OffsetLow = (UINT16)S3DebugBuffer; + IdtEntry->Bits.Selector = (UINT16)AsmReadCs (); + IdtEntry->Bits.Reserved_0 = 0; + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + IdtEntry->Bits.OffsetHigh = (UINT16)(S3DebugBuffer >> 16); + IdtEntry->Bits.OffsetUpper = (UINT32)(S3DebugBuffer >> 32); + IdtEntry->Bits.Reserved_1 = 0; + } + ); + + // + // If both BIOS and OS wants long mode waking vector, + // S3ResumePei should have established 1:1 Virtual to Physical identity mapping page table, + // no need to hook page fault handler. + // + if (!IsLongModeWakingVector (AcpiS3Context)) { + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR))); + HookPageFaultHandler (IdtEntry); + } +} + +/** + Acquire page for page fault. + + @param[in, out] Uplink Pointer to up page table entry. + +**/ +VOID +AcquirePage ( + IN OUT UINT64 *Uplink + ) +{ + UINTN Address; + + Address = mPageFaultBuffer + EFI_PAGES_TO_SIZE (mPageFaultIndex); + ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1)); + + // + // Cut the previous uplink if it exists and wasn't overwritten. + // + if ((mPageFaultUplink[mPageFaultIndex] != NULL) && + ((*mPageFaultUplink[mPageFaultIndex] & ~mAddressEncMask & mPhyMask) == Address)) { + *mPageFaultUplink[mPageFaultIndex] = 0; + } + + // + // Link & Record the current uplink. + // + *Uplink = Address | mAddressEncMask | IA32_PG_P | IA32_PG_RW; + mPageFaultUplink[mPageFaultIndex] = Uplink; + + mPageFaultIndex = (mPageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES; +} + +/** + The page fault handler that on-demand read >4G memory/MMIO. + + @retval TRUE The page fault is correctly handled. + @retval FALSE The page fault is not handled and is passed through to original handler. + +**/ +BOOLEAN +EFIAPI +PageFaultHandler ( + VOID + ) +{ + UINT64 *PageTable; + UINT64 PFAddress; + UINTN PTIndex; + + PFAddress = AsmReadCr2 (); + DEBUG ((DEBUG_INFO, "BootScript - PageFaultHandler: Cr2 - %lx\n", PFAddress)); + + if (PFAddress >= mPhyMask + SIZE_4KB) { + return FALSE; + } + PFAddress &= mPhyMask; + + PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & mPhyMask); + + PTIndex = BitFieldRead64 (PFAddress, 39, 47); + // PML4E + if ((PageTable[PTIndex] & IA32_PG_P) == 0) { + AcquirePage (&PageTable[PTIndex]); + } + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask); + PTIndex = BitFieldRead64 (PFAddress, 30, 38); + // PDPTE + if (mPage1GSupport) { + PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS; + } else { + if ((PageTable[PTIndex] & IA32_PG_P) == 0) { + AcquirePage (&PageTable[PTIndex]); + } + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask); + PTIndex = BitFieldRead64 (PFAddress, 21, 29); + // PD + PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS; + } + + return TRUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.c b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.c new file mode 100644 index 000000000..61a7704b3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.c @@ -0,0 +1,677 @@ +/** @file + This module install ACPI Firmware Performance Data Table (FPDT). + + This module register report status code listener to collect performance data + for Firmware Basic Boot Performance Record and other boot performance records, + and install FPDT to ACPI table. + + Copyright (c) 2011 - 2019, 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 +#include +#include + +#define SMM_BOOT_RECORD_COMM_SIZE (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + sizeof(SMM_BOOT_RECORD_COMMUNICATE)) + +EFI_RSC_HANDLER_PROTOCOL *mRscHandlerProtocol = NULL; + +BOOLEAN mLockBoxReady = FALSE; +EFI_EVENT mReadyToBootEvent; +EFI_EVENT mLegacyBootEvent; +static EFI_EVENT mExitBootServicesEvent; +UINTN mFirmwarePerformanceTableTemplateKey = 0; +BOOLEAN mDxeCoreReportStatusCodeEnable = FALSE; + +BOOT_PERFORMANCE_TABLE *mAcpiBootPerformanceTable = NULL; +BOOT_PERFORMANCE_TABLE *mReceivedAcpiBootPerformanceTable = NULL; +S3_PERFORMANCE_TABLE *mAcpiS3PerformanceTable = NULL; + +FIRMWARE_PERFORMANCE_TABLE mFirmwarePerformanceTableTemplate = { + { + EFI_ACPI_5_0_FIRMWARE_PERFORMANCE_DATA_TABLE_SIGNATURE, + sizeof (FIRMWARE_PERFORMANCE_TABLE), + EFI_ACPI_5_0_FIRMWARE_PERFORMANCE_DATA_TABLE_REVISION, // Revision + 0x00, // Checksum will be updated at runtime + // + // It is expected that these values will be updated at EntryPoint. + // + {0x00}, // OEM ID is a 6 bytes long field + 0x00, // OEM Table ID(8 bytes long) + 0x00, // OEM Revision + 0x00, // Creator ID + 0x00, // Creator Revision + }, + // + // Firmware Basic Boot Performance Table Pointer Record. + // + { + { + EFI_ACPI_5_0_FPDT_RECORD_TYPE_FIRMWARE_BASIC_BOOT_POINTER , // Type + sizeof (EFI_ACPI_5_0_FPDT_BOOT_PERFORMANCE_TABLE_POINTER_RECORD), // Length + EFI_ACPI_5_0_FPDT_RECORD_REVISION_FIRMWARE_BASIC_BOOT_POINTER // Revision + }, + 0, // Reserved + 0 // BootPerformanceTablePointer will be updated at runtime. + }, + // + // S3 Performance Table Pointer Record. + // + { + { + EFI_ACPI_5_0_FPDT_RECORD_TYPE_S3_PERFORMANCE_TABLE_POINTER, // Type + sizeof (EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_POINTER_RECORD), // Length + EFI_ACPI_5_0_FPDT_RECORD_REVISION_S3_PERFORMANCE_TABLE_POINTER // Revision + }, + 0, // Reserved + 0 // S3PerformanceTablePointer will be updated at runtime. + } +}; + +BOOT_PERFORMANCE_TABLE mBootPerformanceTableTemplate = { + { + EFI_ACPI_5_0_FPDT_BOOT_PERFORMANCE_TABLE_SIGNATURE, + sizeof (BOOT_PERFORMANCE_TABLE) + }, + { + { + EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_TYPE_FIRMWARE_BASIC_BOOT, // Type + sizeof (EFI_ACPI_5_0_FPDT_FIRMWARE_BASIC_BOOT_RECORD), // Length + EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_REVISION_FIRMWARE_BASIC_BOOT // Revision + }, + 0, // Reserved + // + // These values will be updated at runtime. + // + 0, // ResetEnd + 0, // OsLoaderLoadImageStart + 0, // OsLoaderStartImageStart + 0, // ExitBootServicesEntry + 0 // ExitBootServicesExit + } +}; + +S3_PERFORMANCE_TABLE mS3PerformanceTableTemplate = { + { + EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_SIGNATURE, + sizeof (S3_PERFORMANCE_TABLE) + }, + { + { + EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_TYPE_S3_RESUME, // Type + sizeof (EFI_ACPI_5_0_FPDT_S3_RESUME_RECORD), // Length + EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_REVISION_S3_RESUME // Revision + }, + // + // These values will be updated by Firmware Performance PEIM. + // + 0, // ResumeCount + 0, // FullResume + 0 // AverageResume + }, + { + { + EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_TYPE_S3_SUSPEND, // Type + sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD), // Length + EFI_ACPI_5_0_FPDT_RUNTIME_RECORD_REVISION_S3_SUSPEND // Revision + }, + // + // These values will be updated bye Firmware Performance SMM driver. + // + 0, // SuspendStart + 0 // SuspendEnd + } +}; + +/** + This function calculates and updates an UINT8 checksum. + + @param[in] Buffer Pointer to buffer to checksum + @param[in] Size Number of bytes to checksum + +**/ +VOID +FpdtAcpiTableChecksum ( + IN UINT8 *Buffer, + IN UINTN Size + ) +{ + UINTN ChecksumOffset; + + ChecksumOffset = OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, Checksum); + + // + // Set checksum to 0 first. + // + Buffer[ChecksumOffset] = 0; + + // + // Update checksum value. + // + Buffer[ChecksumOffset] = CalculateCheckSum8 (Buffer, Size); +} + +/** + Callback function upon VariableArchProtocol and LockBoxProtocol + to allocate S3 performance table memory and save the pointer to LockBox. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +FpdtAllocateS3PerformanceTableMemory ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *Interface; + FIRMWARE_PERFORMANCE_VARIABLE PerformanceVariable; + UINTN Size; + EFI_PHYSICAL_ADDRESS S3PerformanceTablePointer; + + if (mLockBoxReady && (mAcpiS3PerformanceTable != NULL)) { + // + // The memory for S3 performance table should have been ready, + // and the pointer should have been saved to LockBox, just return. + // + return; + } + + if (!mLockBoxReady) { + Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface); + if (!EFI_ERROR (Status)) { + // + // LockBox services has been ready. + // + mLockBoxReady = TRUE; + } + } + + if (mAcpiS3PerformanceTable == NULL) { + Status = gBS->LocateProtocol (&gEfiVariableArchProtocolGuid, NULL, &Interface); + if (!EFI_ERROR (Status)) { + // + // Try to allocate the same runtime buffer as last time boot. + // + ZeroMem (&PerformanceVariable, sizeof (PerformanceVariable)); + Size = sizeof (PerformanceVariable); + Status = gRT->GetVariable ( + EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME, + &gEfiFirmwarePerformanceGuid, + NULL, + &Size, + &PerformanceVariable + ); + if (!EFI_ERROR (Status)) { + Status = gBS->AllocatePages ( + AllocateAddress, + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (sizeof (S3_PERFORMANCE_TABLE)), + &PerformanceVariable.S3PerformanceTablePointer + ); + if (!EFI_ERROR (Status)) { + mAcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) (UINTN) PerformanceVariable.S3PerformanceTablePointer; + } + } + if (mAcpiS3PerformanceTable == NULL) { + // + // Fail to allocate at specified address, continue to allocate at any address. + // + mAcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) AllocatePeiAccessiblePages ( + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (sizeof (S3_PERFORMANCE_TABLE)) + ); + } + DEBUG ((EFI_D_INFO, "FPDT: ACPI S3 Performance Table address = 0x%x\n", mAcpiS3PerformanceTable)); + if (mAcpiS3PerformanceTable != NULL) { + CopyMem (mAcpiS3PerformanceTable, &mS3PerformanceTableTemplate, sizeof (mS3PerformanceTableTemplate)); + } + } + } + + if (mLockBoxReady && (mAcpiS3PerformanceTable != NULL)) { + // + // If LockBox services has been ready and memory for FPDT S3 performance table has been allocated, + // save the pointer to LockBox for use in S3 resume. + // + S3PerformanceTablePointer = (EFI_PHYSICAL_ADDRESS) (UINTN) mAcpiS3PerformanceTable; + Status = SaveLockBox ( + &gFirmwarePerformanceS3PointerGuid, + &S3PerformanceTablePointer, + sizeof (EFI_PHYSICAL_ADDRESS) + ); + ASSERT_EFI_ERROR (Status); + } +} + +/** + Install ACPI Firmware Performance Data Table (FPDT). + + @return Status code. + +**/ +EFI_STATUS +InstallFirmwarePerformanceDataTable ( + VOID + ) +{ + EFI_STATUS Status; + EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol; + UINTN BootPerformanceDataSize; + FIRMWARE_PERFORMANCE_VARIABLE PerformanceVariable; + UINTN Size; + + // + // Get AcpiTable Protocol. + // + Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol); + if (EFI_ERROR (Status)) { + return Status; + } + + if (mReceivedAcpiBootPerformanceTable != NULL) { + mAcpiBootPerformanceTable = mReceivedAcpiBootPerformanceTable; + mAcpiBootPerformanceTable->BasicBoot.ResetEnd = mBootPerformanceTableTemplate.BasicBoot.ResetEnd; + } else { + // + // Try to allocate the same runtime buffer as last time boot. + // + BootPerformanceDataSize = sizeof (BOOT_PERFORMANCE_TABLE); + ZeroMem (&PerformanceVariable, sizeof (PerformanceVariable)); + Size = sizeof (PerformanceVariable); + Status = gRT->GetVariable ( + EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME, + &gEfiFirmwarePerformanceGuid, + NULL, + &Size, + &PerformanceVariable + ); + if (!EFI_ERROR (Status)) { + Status = gBS->AllocatePages ( + AllocateAddress, + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (BootPerformanceDataSize), + &PerformanceVariable.BootPerformanceTablePointer + ); + if (!EFI_ERROR (Status)) { + mAcpiBootPerformanceTable = (BOOT_PERFORMANCE_TABLE *) (UINTN) PerformanceVariable.BootPerformanceTablePointer; + } + } + if (mAcpiBootPerformanceTable == NULL) { + // + // Fail to allocate at specified address, continue to allocate at any address. + // + mAcpiBootPerformanceTable = (BOOT_PERFORMANCE_TABLE *) AllocatePeiAccessiblePages ( + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (BootPerformanceDataSize) + ); + } + DEBUG ((DEBUG_INFO, "FPDT: ACPI Boot Performance Table address = 0x%x\n", mAcpiBootPerformanceTable)); + if (mAcpiBootPerformanceTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fill Basic Boot record to Boot Performance Table. + // + CopyMem (mAcpiBootPerformanceTable, &mBootPerformanceTableTemplate, sizeof (mBootPerformanceTableTemplate)); + } + BootPerformanceDataSize = mAcpiBootPerformanceTable->Header.Length; + + // + // Save Boot Performance Table address to Variable for use in S4 resume. + // + PerformanceVariable.BootPerformanceTablePointer = (EFI_PHYSICAL_ADDRESS) (UINTN) mAcpiBootPerformanceTable; + // + // Update Boot Performance Table Pointer in template. + // + mFirmwarePerformanceTableTemplate.BootPointerRecord.BootPerformanceTablePointer = (UINT64) (UINTN) mAcpiBootPerformanceTable; + + // + // Save S3 Performance Table address to Variable for use in S4 resume. + // + PerformanceVariable.S3PerformanceTablePointer = (EFI_PHYSICAL_ADDRESS) (UINTN) mAcpiS3PerformanceTable; + // + // Update S3 Performance Table Pointer in template. + // + mFirmwarePerformanceTableTemplate.S3PointerRecord.S3PerformanceTablePointer = (UINT64) (UINTN) mAcpiS3PerformanceTable; + // + // Save Runtime Performance Table pointers to Variable. + // Don't check SetVariable return status. It doesn't impact FPDT table generation. + // + gRT->SetVariable ( + EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME, + &gEfiFirmwarePerformanceGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (PerformanceVariable), + &PerformanceVariable + ); + + // + // Publish Firmware Performance Data Table. + // + FpdtAcpiTableChecksum ((UINT8 *) &mFirmwarePerformanceTableTemplate, mFirmwarePerformanceTableTemplate.Header.Length); + Status = AcpiTableProtocol->InstallAcpiTable ( + AcpiTableProtocol, + &mFirmwarePerformanceTableTemplate, + mFirmwarePerformanceTableTemplate.Header.Length, + &mFirmwarePerformanceTableTemplateKey + ); + if (EFI_ERROR (Status)) { + if (mAcpiBootPerformanceTable != NULL) { + FreePages (mAcpiBootPerformanceTable, EFI_SIZE_TO_PAGES (BootPerformanceDataSize)); + } + if (mAcpiS3PerformanceTable != NULL) { + FreePages (mAcpiS3PerformanceTable, EFI_SIZE_TO_PAGES (sizeof (S3_PERFORMANCE_TABLE))); + } + mAcpiBootPerformanceTable = NULL; + mAcpiS3PerformanceTable = NULL; + return Status; + } + return EFI_SUCCESS; +} + +/** + Report status code listener of FPDT. This is used to collect performance data + for OsLoaderLoadImageStart and OsLoaderStartImageStart in FPDT. + + @param[in] CodeType Indicates the type of status code being reported. + @param[in] Value Describes the current status of a hardware or software entity. + This included information about the class and subclass that is used to + classify the entity as well as an operation. + @param[in] Instance The enumeration of a hardware or software entity within + the system. Valid instance numbers start with 1. + @param[in] CallerId This optional parameter may be used to identify the caller. + This parameter allows the status code driver to apply different rules to + different callers. + @param[in] Data This optional parameter may be used to pass additional data. + + @retval EFI_SUCCESS Status code is what we expected. + @retval EFI_UNSUPPORTED Status code not supported. + +**/ +EFI_STATUS +EFIAPI +FpdtStatusCodeListenerDxe ( + 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 + ) +{ + EFI_STATUS Status; + + // + // Check whether status code is what we are interested in. + // + if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) { + return EFI_UNSUPPORTED; + } + + if (Value == (EFI_SOFTWARE_DXE_CORE | EFI_SW_DXE_CORE_PC_HANDOFF_TO_NEXT)) { + // + // DxeCore ReportStatusCode Enable so that the capability can be supported. + // + mDxeCoreReportStatusCodeEnable = TRUE; + } + + Status = EFI_SUCCESS; + if (Value == PcdGet32 (PcdProgressCodeOsLoaderLoad)) { + // + // Progress code for OS Loader LoadImage. + // + if (mAcpiBootPerformanceTable == NULL) { + return Status; + } + + // + // Update OS Loader LoadImage Start for UEFI boot. + // + mAcpiBootPerformanceTable->BasicBoot.OsLoaderLoadImageStart = GetTimeInNanoSecond (GetPerformanceCounter ()); + } else if (Value == PcdGet32 (PcdProgressCodeOsLoaderStart)) { + // + // Progress code for OS Loader StartImage. + // + if (mAcpiBootPerformanceTable == NULL) { + return Status; + } + + // + // Update OS Loader StartImage Start for UEFI boot. + // + mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart = GetTimeInNanoSecond (GetPerformanceCounter ()); + } else if (Value == (EFI_SOFTWARE_EFI_BOOT_SERVICE | EFI_SW_BS_PC_EXIT_BOOT_SERVICES)) { + // + // Unregister boot time report status code listener. + // + mRscHandlerProtocol->Unregister (FpdtStatusCodeListenerDxe); + + // + // Progress code for ExitBootServices. + // + if (mAcpiBootPerformanceTable == NULL) { + return Status; + } + + // + // Update ExitBootServicesExit for UEFI boot. + // + mAcpiBootPerformanceTable->BasicBoot.ExitBootServicesExit = GetTimeInNanoSecond (GetPerformanceCounter ()); + } else if (Value == (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_LEGACY_BOOT_EVENT)) { + if (mAcpiBootPerformanceTable == NULL) { + // + // Firmware Performance Data Table not installed, do nothing. + // + return Status; + } + + // + // Update Firmware Basic Boot Performance Record for legacy boot. + // + mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart = GetTimeInNanoSecond (GetPerformanceCounter ()); + + // + // Dump FPDT Boot Performance record. + // + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ResetEnd = %ld\n", mAcpiBootPerformanceTable->BasicBoot.ResetEnd)); + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderLoadImageStart = 0\n")); + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderStartImageStart = %ld\n", mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart)); + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ExitBootServicesEntry = 0\n")); + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ExitBootServicesExit = 0\n")); + } else if (Value == (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)) { + if (mAcpiBootPerformanceTable == NULL) { + // + // ACPI Firmware Performance Data Table not installed yet, install it now. + // + InstallFirmwarePerformanceDataTable (); + } + } else if (Data != NULL && CompareGuid (&Data->Type, &gEdkiiFpdtExtendedFirmwarePerformanceGuid)) { + // + // Get the Boot performance table and then install it to ACPI table. + // + CopyMem (&mReceivedAcpiBootPerformanceTable, Data + 1, Data->Size); + } else if (Data != NULL && CompareGuid (&Data->Type, &gEfiFirmwarePerformanceGuid)) { + DEBUG ((DEBUG_ERROR, "FpdtStatusCodeListenerDxe: Performance data reported through gEfiFirmwarePerformanceGuid will not be collected by FirmwarePerformanceDataTableDxe\n")); + Status = EFI_UNSUPPORTED; + } else { + // + // Ignore else progress code. + // + Status = EFI_UNSUPPORTED; + } + + return Status; +} + + +/** + Notify function for event EVT_SIGNAL_EXIT_BOOT_SERVICES. This is used to record + performance data for ExitBootServicesEntry in FPDT. + + @param[in] Event The Event that is being processed. + @param[in] Context The Event Context. + +**/ +VOID +EFIAPI +FpdtExitBootServicesEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if (!mDxeCoreReportStatusCodeEnable) { + // + // When DxeCore Report Status Code is disabled, + // Unregister boot time report status code listener at ExitBootService Event. + // + mRscHandlerProtocol->Unregister (FpdtStatusCodeListenerDxe); + } + + if (mAcpiBootPerformanceTable == NULL) { + // + // Firmware Performance Data Table not installed, do nothing. + // + return ; + } + + // + // Update Firmware Basic Boot Performance Record for UEFI boot. + // + mAcpiBootPerformanceTable->BasicBoot.ExitBootServicesEntry = GetTimeInNanoSecond (GetPerformanceCounter ()); + + // + // Dump FPDT Boot Performance record. + // + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ResetEnd = %ld\n", mAcpiBootPerformanceTable->BasicBoot.ResetEnd)); + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderLoadImageStart = %ld\n", mAcpiBootPerformanceTable->BasicBoot.OsLoaderLoadImageStart)); + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - OsLoaderStartImageStart = %ld\n", mAcpiBootPerformanceTable->BasicBoot.OsLoaderStartImageStart)); + DEBUG ((EFI_D_INFO, "FPDT: Boot Performance - ExitBootServicesEntry = %ld\n", mAcpiBootPerformanceTable->BasicBoot.ExitBootServicesEntry)); + // + // ExitBootServicesExit will be updated later, so don't dump it here. + // +} + +/** + The module Entry Point of the Firmware Performance Data Table DXE driver. + + @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 entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +FirmwarePerformanceDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HOB_GUID_TYPE *GuidHob; + FIRMWARE_SEC_PERFORMANCE *Performance; + VOID *Registration; + UINT64 OemTableId; + + CopyMem ( + mFirmwarePerformanceTableTemplate.Header.OemId, + PcdGetPtr (PcdAcpiDefaultOemId), + sizeof (mFirmwarePerformanceTableTemplate.Header.OemId) + ); + OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId); + CopyMem (&mFirmwarePerformanceTableTemplate.Header.OemTableId, &OemTableId, sizeof (UINT64)); + mFirmwarePerformanceTableTemplate.Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + mFirmwarePerformanceTableTemplate.Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + mFirmwarePerformanceTableTemplate.Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + + // + // Get Report Status Code Handler Protocol. + // + Status = gBS->LocateProtocol (&gEfiRscHandlerProtocolGuid, NULL, (VOID **) &mRscHandlerProtocol); + ASSERT_EFI_ERROR (Status); + + // + // Register report status code listener for OS Loader load and start. + // + Status = mRscHandlerProtocol->Register (FpdtStatusCodeListenerDxe, TPL_HIGH_LEVEL); + ASSERT_EFI_ERROR (Status); + + // + // Register the notify function to update FPDT on ExitBootServices Event. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + FpdtExitBootServicesEventNotify, + NULL, + &gEfiEventExitBootServicesGuid, + &mExitBootServicesEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // Retrieve GUID HOB data that contains the ResetEnd. + // + GuidHob = GetFirstGuidHob (&gEfiFirmwarePerformanceGuid); + if (GuidHob != NULL) { + Performance = (FIRMWARE_SEC_PERFORMANCE *) GET_GUID_HOB_DATA (GuidHob); + mBootPerformanceTableTemplate.BasicBoot.ResetEnd = Performance->ResetEnd; + } else { + // + // SEC Performance Data Hob not found, ResetEnd in ACPI FPDT table will be 0. + // + DEBUG ((DEBUG_WARN, "FPDT: WARNING: SEC Performance Data Hob not found, ResetEnd will be set to 0!\n")); + } + + if (FeaturePcdGet (PcdFirmwarePerformanceDataTableS3Support)) { + // + // Register callback function upon VariableArchProtocol and LockBoxProtocol + // to allocate S3 performance table memory and save the pointer to LockBox. + // + EfiCreateProtocolNotifyEvent ( + &gEfiVariableArchProtocolGuid, + TPL_CALLBACK, + FpdtAllocateS3PerformanceTableMemory, + NULL, + &Registration + ); + EfiCreateProtocolNotifyEvent ( + &gEfiLockBoxProtocolGuid, + TPL_CALLBACK, + FpdtAllocateS3PerformanceTableMemory, + NULL, + &Registration + ); + } else { + // + // Exclude S3 Performance Table Pointer from FPDT table template. + // + mFirmwarePerformanceTableTemplate.Header.Length -= sizeof (EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_POINTER_RECORD); + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.inf new file mode 100644 index 000000000..1debb0193 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.inf @@ -0,0 +1,83 @@ +## @file +# This module installs ACPI Firmware Performance Data Table (FPDT). +# +# This module registers report status code listener to collect performance data +# for Firmware Basic Boot Performance Record and other boot performance records, +# and install FPDT to ACPI table. +# +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FirmwarePerformanceDxe + MODULE_UNI_FILE = FirmwarePerformanceDxe.uni + FILE_GUID = 00160F8D-2B35-4df2-BBE0-B272A8D631F0 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FirmwarePerformanceDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + FirmwarePerformanceDxe.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + BaseLib + DebugLib + DxeServicesLib + TimerLib + BaseMemoryLib + MemoryAllocationLib + PcdLib + HobLib + LockBoxLib + UefiLib + +[Protocols] + gEfiAcpiTableProtocolGuid ## CONSUMES + gEfiRscHandlerProtocolGuid ## CONSUMES + gEfiVariableArchProtocolGuid ## CONSUMES + gEfiLockBoxProtocolGuid ## CONSUMES + +[Guids] + gEfiEventExitBootServicesGuid ## CONSUMES ## Event + ## SOMETIMES_CONSUMES ## HOB + ## SOMETIMES_CONSUMES ## Variable:L"FirmwarePerformance" + ## PRODUCES ## Variable:L"FirmwarePerformance" + ## SOMETIMES_CONSUMES ## UNDEFINED # Used to do smm communication + ## SOMETIMES_CONSUMES ## UNDEFINED # StatusCode Data + gEfiFirmwarePerformanceGuid + gEdkiiFpdtExtendedFirmwarePerformanceGuid ## SOMETIMES_CONSUMES ## UNDEFINED # StatusCode Data + gFirmwarePerformanceS3PointerGuid ## PRODUCES ## UNDEFINED # SaveLockBox + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderLoad ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderStart ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision ## CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwarePerformanceDataTableS3Support ## CONSUMES + +[Depex] + gEfiRscHandlerProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + FirmwarePerformanceDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.uni new file mode 100644 index 000000000..a27b2cefa --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.uni @@ -0,0 +1,18 @@ +// /** @file +// This module installs ACPI Firmware Performance Data Table (FPDT). +// +// This module registers report status code listener to collect performance data +// for Firmware Basic Boot Performance Record and other boot performance records, +// and install FPDT to ACPI table. +// +// Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Installs ACPI Firmware Performance Data Table (FPDT)" + +#string STR_MODULE_DESCRIPTION #language en-US "This module registers report status code listener to collect performance data for Firmware Basic Boot Performance Record and other boot performance records, and installs FPDT to the ACPI table." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxeExtra.uni new file mode 100644 index 000000000..02e307e1f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FirmwarePerformanceDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"ACPI Firmware Performance DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.c b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.c new file mode 100644 index 000000000..493d09c78 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.c @@ -0,0 +1,225 @@ +/** @file + This module updates S3 Resume Performance Record in ACPI Firmware Performance + Data Table in S3 resume boot mode. + + This module register report status code listener to collect performance data + for S3 Resume Performance Record on S3 resume boot path. + + Copyright (c) 2011 - 2018, 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 + +/** + Report status code listener for PEI. This is used to record the performance + data for S3 FullResume in FPDT. + + @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. + @param[in] CodeType Indicates the type of status code being reported. + @param[in] Value Describes the current status of a hardware or software entity. + This included information about the class and subclass that is used to + classify the entity as well as an operation. + @param[in] Instance The enumeration of a hardware or software entity within + the system. Valid instance numbers start with 1. + @param[in] CallerId This optional parameter may be used to identify the caller. + This parameter allows the status code driver to apply different rules to + different callers. + @param[in] Data This optional parameter may be used to pass additional data. + + @retval EFI_SUCCESS Status code is what we expected. + @retval EFI_UNSUPPORTED Status code not supported. + +**/ +EFI_STATUS +EFIAPI +FpdtStatusCodeListenerPei ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_STATUS_CODE_TYPE CodeType, + IN EFI_STATUS_CODE_VALUE Value, + IN UINT32 Instance, + IN CONST EFI_GUID *CallerId, + IN CONST EFI_STATUS_CODE_DATA *Data + ) +{ + EFI_STATUS Status; + UINT64 CurrentTime; + UINTN VarSize; + EFI_PHYSICAL_ADDRESS S3PerformanceTablePointer; + S3_PERFORMANCE_TABLE *AcpiS3PerformanceTable; + EFI_ACPI_5_0_FPDT_S3_RESUME_RECORD *AcpiS3ResumeRecord; + UINT64 S3ResumeTotal; + EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD S3SuspendRecord; + EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD *AcpiS3SuspendRecord; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices; + UINT8 *BootPerformanceTable; + FIRMWARE_PERFORMANCE_VARIABLE PerformanceVariable; + EFI_HOB_GUID_TYPE *GuidHob; + FPDT_PEI_EXT_PERF_HEADER *PeiPerformanceLogHeader; + UINT8 *FirmwarePerformanceData; + UINT8 *FirmwarePerformanceTablePtr; + + // + // Check whether status code is what we are interested in. + // + if (((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) || + (Value != (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_PC_OS_WAKE))) { + return EFI_UNSUPPORTED; + } + + // + // Retrieve current time as early as possible. + // + CurrentTime = GetTimeInNanoSecond (GetPerformanceCounter ()); + + // + // Update S3 Resume Performance Record. + // + S3PerformanceTablePointer = 0; + VarSize = sizeof (EFI_PHYSICAL_ADDRESS); + Status = RestoreLockBox (&gFirmwarePerformanceS3PointerGuid, &S3PerformanceTablePointer, &VarSize); + ASSERT_EFI_ERROR (Status); + + AcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) (UINTN) S3PerformanceTablePointer; + ASSERT (AcpiS3PerformanceTable != NULL); + if (AcpiS3PerformanceTable->Header.Signature != EFI_ACPI_5_0_FPDT_S3_PERFORMANCE_TABLE_SIGNATURE) { + DEBUG ((EFI_D_ERROR, "FPDT S3 performance data in ACPI memory get corrupted\n")); + return EFI_ABORTED; + } + AcpiS3ResumeRecord = &AcpiS3PerformanceTable->S3Resume; + AcpiS3ResumeRecord->FullResume = CurrentTime; + // + // Calculate average S3 resume time. + // + S3ResumeTotal = MultU64x32 (AcpiS3ResumeRecord->AverageResume, AcpiS3ResumeRecord->ResumeCount); + AcpiS3ResumeRecord->ResumeCount++; + AcpiS3ResumeRecord->AverageResume = DivU64x32 (S3ResumeTotal + AcpiS3ResumeRecord->FullResume, AcpiS3ResumeRecord->ResumeCount); + + DEBUG ((EFI_D_INFO, "FPDT: S3 Resume Performance - ResumeCount = %d\n", AcpiS3ResumeRecord->ResumeCount)); + DEBUG ((EFI_D_INFO, "FPDT: S3 Resume Performance - FullResume = %ld\n", AcpiS3ResumeRecord->FullResume)); + DEBUG ((EFI_D_INFO, "FPDT: S3 Resume Performance - AverageResume = %ld\n", AcpiS3ResumeRecord->AverageResume)); + + // + // Update S3 Suspend Performance Record. + // + AcpiS3SuspendRecord = &AcpiS3PerformanceTable->S3Suspend; + VarSize = sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD); + ZeroMem (&S3SuspendRecord, sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD)); + Status = RestoreLockBox ( + &gEfiFirmwarePerformanceGuid, + &S3SuspendRecord, + &VarSize + ); + ASSERT_EFI_ERROR (Status); + + AcpiS3SuspendRecord->SuspendStart = S3SuspendRecord.SuspendStart; + AcpiS3SuspendRecord->SuspendEnd = S3SuspendRecord.SuspendEnd; + + DEBUG ((EFI_D_INFO, "FPDT: S3 Suspend Performance - SuspendStart = %ld\n", AcpiS3SuspendRecord->SuspendStart)); + DEBUG ((EFI_D_INFO, "FPDT: S3 Suspend Performance - SuspendEnd = %ld\n", AcpiS3SuspendRecord->SuspendEnd)); + + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **) &VariableServices + ); + ASSERT_EFI_ERROR (Status); + + // + // Update S3 boot records into the basic boot performance table. + // + VarSize = sizeof (PerformanceVariable); + Status = VariableServices->GetVariable ( + VariableServices, + EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME, + &gEfiFirmwarePerformanceGuid, + NULL, + &VarSize, + &PerformanceVariable + ); + if (EFI_ERROR (Status)) { + return Status; + } + BootPerformanceTable = (UINT8*) (UINTN) PerformanceVariable.BootPerformanceTablePointer; + + // + // Dump PEI boot records + // + FirmwarePerformanceTablePtr = (BootPerformanceTable + sizeof (BOOT_PERFORMANCE_TABLE)); + GuidHob = GetFirstGuidHob (&gEdkiiFpdtExtendedFirmwarePerformanceGuid); + while (GuidHob != NULL) { + FirmwarePerformanceData = GET_GUID_HOB_DATA (GuidHob); + PeiPerformanceLogHeader = (FPDT_PEI_EXT_PERF_HEADER *) FirmwarePerformanceData; + + CopyMem (FirmwarePerformanceTablePtr, FirmwarePerformanceData + sizeof (FPDT_PEI_EXT_PERF_HEADER), (UINTN)(PeiPerformanceLogHeader->SizeOfAllEntries)); + + GuidHob = GetNextGuidHob (&gEdkiiFpdtExtendedFirmwarePerformanceGuid, GET_NEXT_HOB (GuidHob)); + + FirmwarePerformanceTablePtr += (UINTN)(PeiPerformanceLogHeader->SizeOfAllEntries); + } + + // + // Update Table length. + // + ((BOOT_PERFORMANCE_TABLE *) BootPerformanceTable)->Header.Length = (UINT32)((UINTN)FirmwarePerformanceTablePtr - (UINTN)BootPerformanceTable); + + return EFI_SUCCESS; +} + +/** + Main entry for Firmware Performance Data Table PEIM. + + This routine is to register report status code listener for FPDT. + + @param[in] FileHandle Handle of the file being invoked. + @param[in] PeiServices Pointer to PEI Services table. + + @retval EFI_SUCCESS Report status code listener is registered successfully. + +**/ +EFI_STATUS +EFIAPI +FirmwarePerformancePeiEntryPoint ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_PEI_RSC_HANDLER_PPI *RscHandler; + + if (FeaturePcdGet (PcdFirmwarePerformanceDataTableS3Support)) { + // + // S3 resume - register status code listener for OS wake vector. + // + Status = PeiServicesLocatePpi ( + &gEfiPeiRscHandlerPpiGuid, + 0, + NULL, + (VOID **) &RscHandler + ); + ASSERT_EFI_ERROR (Status); + + Status = RscHandler->Register (FpdtStatusCodeListenerPei); + ASSERT_EFI_ERROR (Status); + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.inf new file mode 100644 index 000000000..e4d4f89d0 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.inf @@ -0,0 +1,67 @@ +## @file +# Firmware Performance Pei Module. +# +# In S3 resume boot mode, it updates S3 Resume Performance Record in ACPI Firmware Performance Data Table. +# +# This module register report status code listener to collect performance data +# for S3 Resume Performance Record on S3 resume boot path. +# +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FirmwarePerformancePei + MODULE_UNI_FILE = FirmwarePerformancePei.uni + FILE_GUID = ADF01BF6-47D6-495d-B95B-687777807214 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = FirmwarePerformancePeiEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + FirmwarePerformancePei.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PeimEntryPoint + PeiServicesLib + BaseLib + DebugLib + TimerLib + BaseMemoryLib + LockBoxLib + PcdLib + HobLib + +[Ppis] + gEfiPeiRscHandlerPpiGuid ## CONSUMES + gEfiPeiReadOnlyVariable2PpiGuid ## CONSUMES + +[Guids] + ## SOMETIMES_CONSUMES ## UNDEFINED # RestoreLockBox + gEfiFirmwarePerformanceGuid + gFirmwarePerformanceS3PointerGuid ## SOMETIMES_CONSUMES ## UNDEFINED # RestoreLockBox + gEdkiiFpdtExtendedFirmwarePerformanceGuid ## SOMETIMES_CONSUMES ## HOB + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwarePerformanceDataTableS3Support ## CONSUMES + +[Depex] + gEfiPeiMasterBootModePpiGuid AND gEfiPeiRscHandlerPpiGuid + +# [BootMode] +# S3_RESUME ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + FirmwarePerformancePeiExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.uni new file mode 100644 index 000000000..2e9e717b2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.uni @@ -0,0 +1,19 @@ +// /** @file +// Firmware Performance Pei Module. +// +// In S3 resume boot mode, it updates S3 Resume Performance Record in ACPI Firmware Performance Data Table. +// +// This module register report status code listener to collect performance data +// for S3 Resume Performance Record on S3 resume boot path. +// +// Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Firmware Performance Pei Module." + +#string STR_MODULE_DESCRIPTION #language en-US "In S3 resume boot mode, it updates S3 Resume Performance Record in ACPI Firmware Performance Data Table. This module register report status code listener to collect performance data for S3 Resume Performance Record on S3 resume boot path." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePeiExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePeiExtra.uni new file mode 100644 index 000000000..18d653156 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FirmwarePerformancePei Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Firmware Performance PEI Module" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.c b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.c new file mode 100644 index 000000000..d6c6e7693 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.c @@ -0,0 +1,318 @@ +/** @file + This module collects performance data for SMM driver boot records and S3 Suspend Performance Record. + + This module registers report status code listener to collect performance data + for SMM driver boot records and S3 Suspend Performance Record. + + Caution: This module requires additional review when modified. + This driver will have external input - communicate buffer in SMM mode. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + FpdtSmiHandler() will receive untrusted input and do basic validation. + + Copyright (c) 2011 - 2018, 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 + +SMM_BOOT_PERFORMANCE_TABLE *mSmmBootPerformanceTable = NULL; + +EFI_SMM_RSC_HANDLER_PROTOCOL *mRscHandlerProtocol = NULL; +UINT64 mSuspendStartTime = 0; +BOOLEAN mS3SuspendLockBoxSaved = FALSE; +UINT32 mBootRecordSize = 0; +UINT8 *mBootRecordBuffer = NULL; + +SPIN_LOCK mSmmFpdtLock; +BOOLEAN mSmramIsOutOfResource = FALSE; + +/** + Report status code listener for SMM. This is used to record the performance + data for S3 Suspend Start and S3 Suspend End in FPDT. + + @param[in] CodeType Indicates the type of status code being reported. + @param[in] Value Describes the current status of a hardware or software entity. + This included information about the class and subclass that is used to + classify the entity as well as an operation. + @param[in] Instance The enumeration of a hardware or software entity within + the system. Valid instance numbers start with 1. + @param[in] CallerId This optional parameter may be used to identify the caller. + This parameter allows the status code driver to apply different rules to + different callers. + @param[in] Data This optional parameter may be used to pass additional data. + + @retval EFI_SUCCESS Status code is what we expected. + @retval EFI_UNSUPPORTED Status code not supported. + +**/ +EFI_STATUS +EFIAPI +FpdtStatusCodeListenerSmm ( + 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 + ) +{ + EFI_STATUS Status; + UINT64 CurrentTime; + EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD S3SuspendRecord; + + // + // Check whether status code is what we are interested in. + // + if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) { + return EFI_UNSUPPORTED; + } + + // + // Collect one or more Boot records in boot time + // + if (Data != NULL && CompareGuid (&Data->Type, &gEdkiiFpdtExtendedFirmwarePerformanceGuid)) { + AcquireSpinLock (&mSmmFpdtLock); + // + // Get the boot performance data. + // + CopyMem (&mSmmBootPerformanceTable, Data + 1, Data->Size); + mBootRecordBuffer = ((UINT8 *) (mSmmBootPerformanceTable)) + sizeof (SMM_BOOT_PERFORMANCE_TABLE); + + ReleaseSpinLock (&mSmmFpdtLock); + return EFI_SUCCESS; + } + + if (Data != NULL && CompareGuid (&Data->Type, &gEfiFirmwarePerformanceGuid)) { + DEBUG ((DEBUG_ERROR, "FpdtStatusCodeListenerSmm: Performance data reported through gEfiFirmwarePerformanceGuid will not be collected by FirmwarePerformanceDataTableSmm\n")); + return EFI_UNSUPPORTED; + } + + if ((Value != PcdGet32 (PcdProgressCodeS3SuspendStart)) && + (Value != PcdGet32 (PcdProgressCodeS3SuspendEnd))) { + return EFI_UNSUPPORTED; + } + + // + // Retrieve current time. + // + CurrentTime = GetTimeInNanoSecond (GetPerformanceCounter ()); + + if (Value == PcdGet32 (PcdProgressCodeS3SuspendStart)) { + // + // S3 Suspend started, record the performance data and return. + // + mSuspendStartTime = CurrentTime; + return EFI_SUCCESS; + } + + // + // We are going to S3 sleep, record S3 Suspend End performance data. + // + S3SuspendRecord.SuspendStart = mSuspendStartTime; + S3SuspendRecord.SuspendEnd = CurrentTime; + + // + // Save S3 suspend performance data to lock box, it will be used by Firmware Performance PEIM. + // + if (!mS3SuspendLockBoxSaved) { + Status = SaveLockBox ( + &gEfiFirmwarePerformanceGuid, + &S3SuspendRecord, + sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD) + ); + ASSERT_EFI_ERROR (Status); + + mS3SuspendLockBoxSaved = TRUE; + } else { + Status = UpdateLockBox ( + &gEfiFirmwarePerformanceGuid, + 0, + &S3SuspendRecord, + sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD) + ); + ASSERT_EFI_ERROR (Status); + } + + return EFI_SUCCESS; +} + +/** + Communication service SMI Handler entry. + + This SMI handler provides services for report SMM boot records. + + Caution: This function may receive untrusted input. + Communicate buffer and buffer size are external input, so this function will do basic validation. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] RegisterContext Points to an optional handler context which was specified when the + handler was registered. + @param[in, out] CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param[in, out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. + +**/ +EFI_STATUS +EFIAPI +FpdtSmiHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + SMM_BOOT_RECORD_COMMUNICATE *SmmCommData; + UINTN BootRecordOffset; + UINTN BootRecordSize; + VOID *BootRecordData; + UINTN TempCommBufferSize; + + // + // If input is invalid, stop processing this SMI + // + if (CommBuffer == NULL || CommBufferSize == NULL) { + return EFI_SUCCESS; + } + + TempCommBufferSize = *CommBufferSize; + + if(TempCommBufferSize < sizeof (SMM_BOOT_RECORD_COMMUNICATE)) { + return EFI_SUCCESS; + } + + if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { + DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM communication data buffer in SMRAM or overflow!\n")); + return EFI_SUCCESS; + } + + SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE*)CommBuffer; + + Status = EFI_SUCCESS; + + switch (SmmCommData->Function) { + case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE : + if (mSmmBootPerformanceTable != NULL) { + mBootRecordSize = mSmmBootPerformanceTable->Header.Length - sizeof (SMM_BOOT_PERFORMANCE_TABLE); + } + SmmCommData->BootRecordSize = mBootRecordSize; + break; + + case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA : + Status = EFI_UNSUPPORTED; + break; + + case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA_BY_OFFSET : + BootRecordOffset = SmmCommData->BootRecordOffset; + BootRecordData = SmmCommData->BootRecordData; + BootRecordSize = SmmCommData->BootRecordSize; + if (BootRecordData == NULL || BootRecordOffset >= mBootRecordSize) { + Status = EFI_INVALID_PARAMETER; + break; + } + + // + // Sanity check + // + if (BootRecordSize > mBootRecordSize - BootRecordOffset) { + BootRecordSize = mBootRecordSize - BootRecordOffset; + } + SmmCommData->BootRecordSize = BootRecordSize; + if (!SmmIsBufferOutsideSmmValid ((UINTN)BootRecordData, BootRecordSize)) { + DEBUG ((EFI_D_ERROR, "FpdtSmiHandler: SMM Data buffer in SMRAM or overflow!\n")); + Status = EFI_ACCESS_DENIED; + break; + } + + CopyMem ( + (UINT8*)BootRecordData, + mBootRecordBuffer + BootRecordOffset, + BootRecordSize + ); + break; + + default: + Status = EFI_UNSUPPORTED; + } + + SmmCommData->ReturnStatus = Status; + + return EFI_SUCCESS; +} + +/** + The module Entry Point of the Firmware Performance Data Table SMM driver. + + @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 entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +FirmwarePerformanceSmmEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + // + // Initialize spin lock + // + InitializeSpinLock (&mSmmFpdtLock); + + // + // Get SMM Report Status Code Handler Protocol. + // + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmRscHandlerProtocolGuid, + NULL, + (VOID **) &mRscHandlerProtocol + ); + ASSERT_EFI_ERROR (Status); + + // + // Register report status code listener for BootRecords and S3 Suspend Start and End. + // + Status = mRscHandlerProtocol->Register (FpdtStatusCodeListenerSmm); + ASSERT_EFI_ERROR (Status); + + // + // Register SMI handler. + // + Handle = NULL; + Status = gSmst->SmiHandlerRegister (FpdtSmiHandler, &gEfiFirmwarePerformanceGuid, &Handle); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.inf new file mode 100644 index 000000000..618cbd56c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.inf @@ -0,0 +1,67 @@ +## @file +# This module collects performance data for SMM driver boot records and S3 Suspend Performance Record. +# +# This module registers report status code listener to collect performance data +# for SMM boot performance records and S3 Suspend Performance Record. +# +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FirmwarePerformanceSmm + MODULE_UNI_FILE = FirmwarePerformanceSmm.uni + FILE_GUID = 044310AB-77FD-402a-AF1A-87D4120E7329 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = FirmwarePerformanceSmmEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + FirmwarePerformanceSmm.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + SmmServicesTableLib + BaseLib + DebugLib + TimerLib + LockBoxLib + PcdLib + BaseMemoryLib + MemoryAllocationLib + UefiBootServicesTableLib + SynchronizationLib + SmmMemLib + +[Protocols] + gEfiSmmRscHandlerProtocolGuid ## CONSUMES + +[Guids] + ## SOMETIMES_PRODUCES ## UNDEFINED # SaveLockBox + ## PRODUCES ## UNDEFINED # SmiHandlerRegister + ## SOMETIMES_CONSUMES ## UNDEFINED # StatusCode Data + gEfiFirmwarePerformanceGuid + gEdkiiFpdtExtendedFirmwarePerformanceGuid ## SOMETIMES_PRODUCES ## UNDEFINED # StatusCode Data + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeS3SuspendStart ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeS3SuspendEnd ## CONSUMES + +[Depex] + gEfiSmmRscHandlerProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + FirmwarePerformanceSmmExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.uni new file mode 100644 index 000000000..dc940a78f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.uni @@ -0,0 +1,17 @@ +// /** @file +// This module collects performance data for SMM driver boot records and S3 Suspend Performance Record. +// +// This module registers report status code listener to collect performance data +// for SMM boot performance records and S3 Suspend Performance Record. +// +// Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Collects performance data for SMM driver boot records and S3 Suspend Performance Record" + +#string STR_MODULE_DESCRIPTION #language en-US "This module registers report status code listener to collect performance data for SMM boot performance records and S3 Suspend Performance Record." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmmExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmmExtra.uni new file mode 100644 index 000000000..edb57b528 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmmExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FirmwarePerformanceSmm Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SMM Firmware Performance Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c new file mode 100644 index 000000000..e7cd2a12a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/AcpiS3ContextSave.c @@ -0,0 +1,325 @@ +/** @file + This is the implementation to save ACPI S3 Context. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// 8 extra pages for PF handler. +// +#define EXTRA_PAGE_TABLE_PAGES 8 + +EFI_GUID mAcpiS3IdtrProfileGuid = { + 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d } +}; + +/** + 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. + +**/ +VOID* +AllocateMemoryBelow4G ( + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Size + ) +{ + UINTN Pages; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + VOID* Buffer; + + Pages = EFI_SIZE_TO_PAGES (Size); + Address = 0xffffffff; + + Status = gBS->AllocatePages ( + AllocateMaxAddress, + MemoryType, + Pages, + &Address + ); + ASSERT_EFI_ERROR (Status); + + Buffer = (VOID *) (UINTN) Address; + ZeroMem (Buffer, Size); + + return Buffer; +} + +/** + The function will check if long mode waking vector is supported. + + @param[in] Facs Pointer to FACS table. + + @retval TRUE Long mode waking vector is supported. + @retval FALSE Long mode waking vector is not supported. + +**/ +BOOLEAN +IsLongModeWakingVectorSupport ( + IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs + ) +{ + if ((Facs == NULL) || + (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ) { + // + // Something wrong with FACS. + // + return FALSE; + } + if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) && + ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0)) { + // + // BIOS supports 64bit waking vector. + // + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + return TRUE; + } + } + return FALSE; +} + +/** + Allocates page table buffer. + + @param[in] LongModeWakingVectorSupport Support long mode waking vector or not. + + If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1 + virtual to physical mapping page table when long mode waking vector is supported, otherwise + create 4G page table when long mode waking vector is not supported and let PF handler to + handle > 4G request. + If BootScriptExector driver will not run in 64-bit mode, this function will do nothing. + + @return Page table base address. + +**/ +EFI_PHYSICAL_ADDRESS +S3AllocatePageTablesBuffer ( + IN BOOLEAN LongModeWakingVectorSupport + ) +{ + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + UINTN ExtraPageTablePages; + UINT32 RegEax; + UINT32 RegEdx; + UINT8 PhysicalAddressBits; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress; + UINTN TotalPageTableSize; + VOID *Hob; + BOOLEAN Page1GSupport; + + Page1GSupport = FALSE; + if (PcdGetBool(PcdUse1GPageTable)) { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + Page1GSupport = TRUE; + } + } + } + + // + // Get physical address bits supported. + // + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + } else { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + } + + // + // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. + // + ASSERT (PhysicalAddressBits <= 52); + if (PhysicalAddressBits > 48) { + PhysicalAddressBits = 48; + } + + ExtraPageTablePages = 0; + if (!LongModeWakingVectorSupport) { + // + // Create 4G page table when BIOS does not support long mode waking vector, + // and let PF handler to handle > 4G request. + // + PhysicalAddressBits = 32; + ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES; + } + + // + // Calculate the table entries needed. + // + if (PhysicalAddressBits <= 39 ) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30)); + } else { + NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39)); + NumberOfPdpEntriesNeeded = 512; + } + + // + // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs. + // + if (!Page1GSupport) { + TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded; + } else { + TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded; + } + + TotalPageTableSize += ExtraPageTablePages; + DEBUG ((DEBUG_INFO, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize)); + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize)); + ASSERT (S3NvsPageTableAddress != 0); + return S3NvsPageTableAddress; + } else { + // + // If DXE is running 32-bit mode, no need to establish page table. + // + return (EFI_PHYSICAL_ADDRESS) 0; + } +} + +/** + Callback function executed when the EndOfDxe event group is signaled. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context The pointer to the notification function's context, which + is implementation-dependent. +**/ +VOID +EFIAPI +AcpiS3ContextSaveOnEndOfDxe ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer; + ACPI_S3_CONTEXT *AcpiS3Context; + IA32_DESCRIPTOR *Idtr; + IA32_IDT_GATE_DESCRIPTOR *IdtGate; + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; + VOID *Interface; + + DEBUG ((EFI_D_INFO, "AcpiS3ContextSave!\n")); + + Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO | EFI_D_WARN, "ACPI S3 context can't be saved without LockBox!\n")); + goto Done; + } + + AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context)); + ASSERT (AcpiS3Context != NULL); + AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context; + + // + // Get ACPI Table because we will save its position to variable + // + Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) EfiLocateFirstAcpiTable ( + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE + ); + AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS) (UINTN) Facs; + ASSERT (AcpiS3Context->AcpiFacsTable != 0); + + IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR)); + Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100); + Idtr->Base = (UINTN)IdtGate; + Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1); + AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr; + + Status = SaveLockBox ( + &mAcpiS3IdtrProfileGuid, + (VOID *)(UINTN)Idtr, + (UINTN)sizeof(IA32_DESCRIPTOR) + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); + ASSERT_EFI_ERROR (Status); + + // + // Allocate page table + // + AcpiS3Context->S3NvsPageTableAddress = S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs)); + + // + // Allocate stack + // + AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize); + AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize)); + ASSERT (AcpiS3Context->BootScriptStackBase != 0); + + // + // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it. + // + AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE); + SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff); + + DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable)); + DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile)); + DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress)); + DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress)); + DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context->BootScriptStackBase)); + DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context->BootScriptStackSize)); + + Status = SaveLockBox ( + &gEfiAcpiVariableGuid, + &AcpiS3ContextBuffer, + sizeof(AcpiS3ContextBuffer) + ); + ASSERT_EFI_ERROR (Status); + + Status = SaveLockBox ( + &gEfiAcpiS3ContextGuid, + (VOID *)(UINTN)AcpiS3Context, + (UINTN)sizeof(*AcpiS3Context) + ); + ASSERT_EFI_ERROR (Status); + + Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); + ASSERT_EFI_ERROR (Status); + +Done: + // + // Close the event, deregistering the callback and freeing resources. + // + Status = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (Status); +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/InternalS3SaveState.h b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/InternalS3SaveState.h new file mode 100644 index 000000000..a8dece428 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/InternalS3SaveState.h @@ -0,0 +1,171 @@ +/** @file + Internal header file for S3 Boot Script Saver state driver. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef _INTERNAL_S3_SAVE_STATE_H_ +#define _INTERNAL_S3_SAVE_STATE_H_ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Callback function executed when the EndOfDxe event group is signaled. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context The pointer to the notification function's context, which + is implementation-dependent. +**/ +VOID +EFIAPI +AcpiS3ContextSaveOnEndOfDxe ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Adds a record into S3 boot script table. + + This function is used to store a boot script record into a given boot + script table. If the table specified by TableName is nonexistent in the + system, a new table will automatically be created and then the script record + will be added into the new table. This function is responsible for allocating + necessary memory for the script. + + This function has a variable parameter list. The exact parameter list depends on + the OpCode that is passed into the function. If an unsupported OpCode or illegal + parameter list is passed in, this function returns EFI_INVALID_PARAMETER. + If there are not enough resources available for storing more scripts, this function returns + EFI_OUT_OF_RESOURCES. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The parameter is illegal or the given boot script is not supported. + If the opcode is unknow or not supported because of the PCD + Feature Flags. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptWrite ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN UINTN OpCode, + ... + ); +/** + Insert a record into a specified Framework boot script table. + + This function is used to store an OpCode to be replayed as part of the S3 resume boot path. It is + assumed this protocol has platform specific mechanism to store the OpCode set and replay them + during the S3 resume. + The opcode is inserted before or after the specified position in the boot script table. If Position is + NULL then that position is after the last opcode in the table (BeforeOrAfter is FALSE) or before + the first opcode in the table (BeforeOrAfter is TRUE). The position which is pointed to by + Position upon return can be used for subsequent insertions. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the opcode is stored before (TRUE) or after (FALSE) the position + in the boot script table specified by Position. If Position is NULL or points to + NULL then the new opcode is inserted at the beginning of the table (if TRUE) or end + of the table (if FALSE). + @param Position On entry, specifies the position in the boot script table where the opcode will be + inserted, either before or after, depending on BeforeOrAfter. On exit, specifies + the position of the inserted opcode in the boot script table. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The Opcode is an invalid opcode value or the Position is not a valid position in the boot script table.. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptInsert ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN UINTN OpCode, + ... + ); +/** + Find a label within the boot script table and, if not present, optionally create it. + + If the label Label is already exists in the boot script table, then no new label is created, the + position of the Label is returned in *Position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is TRUE, then it will be + created before or after the specified position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is FALSE, then + EFI_NOT_FOUND is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the label is stored before (TRUE) or after (FALSE) the position in + the boot script table specified by Position. If Position is NULL or points to + NULL then the new label is inserted at the beginning of the table (if TRUE) or end of + the table (if FALSE). + @param CreateIfNotFound Specifies whether the label will be created if the label does not exists (TRUE) or not + (FALSE). + @param Position On entry, specifies the position in the boot script table where the label will be inserted, + either before or after, depending on BeforeOrAfter. On exit, specifies the position + of the inserted label in the boot script table. + @param Label Points to the label which will be inserted in the boot script table. + + @retval EFI_SUCCESS The label already exists or was inserted. + @retval EFI_INVALID_PARAMETER The Opcode is an invalid opcode value or the Position is not a valid position in the boot script table.. + +**/ +EFI_STATUS +EFIAPI +BootScriptLabel ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN BOOLEAN CreateIfNotFound, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN CONST CHAR8 *Label + ); +/** + Compare two positions in the boot script table and return their relative position. + + This function compares two positions in the boot script table and returns their relative positions. If + Position1 is before Position2, then -1 is returned. If Position1 is equal to Position2, + then 0 is returned. If Position1 is after Position2, then 1 is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param Position1 The positions in the boot script table to compare + @param Position2 The positions in the boot script table to compare + @param RelativePosition On return, points to the result of the comparison + + @retval EFI_SUCCESS The operation succeeded. + @retval EFI_INVALID_PARAMETER The Position1 or Position2 is not a valid position in the boot script table. + +**/ +EFI_STATUS +EFIAPI +BootScriptCompare ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN EFI_S3_BOOT_SCRIPT_POSITION Position1, + IN EFI_S3_BOOT_SCRIPT_POSITION Position2, + OUT UINTN *RelativePosition + ); + +#endif //_INTERNAL_S3_SAVE_STATE_H_ diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveState.c b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveState.c new file mode 100644 index 000000000..e342f7348 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveState.c @@ -0,0 +1,928 @@ +/** @file + Implementation for S3 Boot Script Saver state driver. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "InternalS3SaveState.h" + +EFI_HANDLE mHandle = NULL; +EFI_S3_SAVE_STATE_PROTOCOL mS3SaveState = { + BootScriptWrite, + BootScriptInsert, + BootScriptLabel, + BootScriptCompare + }; +/** + Internal function to add IO write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteIoWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveIoWrite (Width, Address, Count, Buffer); +} +/** + Internal function to add IO read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteIoReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveIoReadWrite (Width, Address, Data, DataMask); +} + +/** + Internal function to add memory write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteMemWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveMemWrite (Width, Address, Count, Buffer); +} + +/** + Internal function to add memory read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteMemReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveMemReadWrite (Width, Address, Data, DataMask); +} + +/** + Internal function to add PciCfg write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfgWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfgWrite (Width, Address, Count, Buffer); +} + +/** + Internal function to PciCfg read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfgReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfgReadWrite (Width, Address, Data, DataMask); +} +/** + Internal function to add PciCfg2 write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfg2Write ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + UINT16 Segment; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Segment = VA_ARG (Marker, UINT16); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfg2Write (Width, Segment, Address, Count, Buffer); +} + +/** + Internal function to PciCfg2 read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfg2ReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT16 Segment; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Segment = VA_ARG (Marker, UINT16); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfg2ReadWrite (Width, Segment, Address, Data, DataMask); +} +/** + Internal function to add smbus execute opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteSmbusExecute ( + IN VA_LIST Marker + ) +{ + EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; + EFI_SMBUS_DEVICE_COMMAND Command; + EFI_SMBUS_OPERATION Operation; + BOOLEAN PecCheck; + VOID *Buffer; + UINTN *DataSize; + UINTN SmBusAddress; + + SlaveAddress.SmbusDeviceAddress = VA_ARG (Marker, UINTN); + Command = VA_ARG (Marker, EFI_SMBUS_DEVICE_COMMAND); + Operation = VA_ARG (Marker, EFI_SMBUS_OPERATION); + PecCheck = VA_ARG (Marker, BOOLEAN); + SmBusAddress = SMBUS_LIB_ADDRESS (SlaveAddress.SmbusDeviceAddress,Command,0,PecCheck); + DataSize = VA_ARG (Marker, UINTN *); + Buffer = VA_ARG (Marker, VOID *); + + return S3BootScriptSaveSmbusExecute (SmBusAddress, Operation, DataSize, Buffer); +} +/** + Internal function to add stall opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteStall ( + IN VA_LIST Marker + ) +{ + UINT32 Duration; + + Duration = VA_ARG (Marker, UINT32); + + return S3BootScriptSaveStall (Duration); +} + +/** + Internal function to add Save jmp address according to DISPATCH_OPCODE. + We ignore "Context" parameter + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteDispatch ( + IN VA_LIST Marker + ) +{ + VOID *EntryPoint; + + EntryPoint = (VOID*)(UINTN)VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + return S3BootScriptSaveDispatch (EntryPoint); +} + +/** + Internal function to add memory pool operation to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteMemPoll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + UINT64 LoopTimes; + UINT32 Remainder; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = VA_ARG (Marker, UINT64); + // + // According to the spec, the interval between 2 polls is 100ns, + // but the unit of Duration for S3BootScriptSaveMemPoll() is microsecond(1000ns). + // Duration * 1000ns * LoopTimes = Delay * 100ns + // Duration will be minimum 1(microsecond) to be minimum deviation, + // so LoopTimes = Delay / 10. + // + LoopTimes = DivU64x32Remainder ( + Delay, + 10, + &Remainder + ); + if (Remainder != 0) { + // + // If Remainder is not zero, LoopTimes will be rounded up by 1. + // + LoopTimes +=1; + } + return S3BootScriptSaveMemPoll (Width, Address, DataMask, Data, 1, LoopTimes); + +} + +/** + Internal function to add Save jmp address according to DISPATCH_OPCODE2. + The "Context" parameter is not ignored. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteDispatch2 ( + IN VA_LIST Marker + ) +{ + VOID *EntryPoint; + VOID *Context; + + EntryPoint = (VOID*)(UINTN)VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + Context = (VOID*)(UINTN)VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + + return S3BootScriptSaveDispatch2 (EntryPoint, Context); +} +/** + Internal function to add INFORAMTION opcode node to the table + list. + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWriteInformation ( + IN VA_LIST Marker + ) +{ + UINT32 InformationLength; + EFI_PHYSICAL_ADDRESS Information; + + InformationLength = VA_ARG (Marker, UINT32); + Information = VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + return S3BootScriptSaveInformation (InformationLength, (VOID*)(UINTN)Information); +} +/** + Internal function to add IO poll opcode node to the table + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWriteIoPoll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = (UINT64)VA_ARG (Marker, UINT64); + + return S3BootScriptSaveIoPoll (Width, Address, Data, DataMask, Delay); +} +/** + Internal function to add PCI config poll opcode node to the table + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWritePciConfigPoll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = (UINT64)VA_ARG (Marker, UINT64); + + return S3BootScriptSavePciPoll (Width, Address, Data, DataMask, Delay); +} +/** + Internal function to add PCI config 2 poll opcode node to the table + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWritePciConfig2Poll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT16 Segment; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Segment = VA_ARG (Marker, UINT16); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = (UINT64)VA_ARG (Marker, UINT64); + + return S3BootScriptSavePci2Poll (Width, Segment, Address, Data, DataMask, Delay); +} + + +/** + Adds a record into S3 boot script table. + + This function is used to store a boot script record into a given boot + script table. If the table specified by TableName is nonexistent in the + system, a new table will automatically be created and then the script record + will be added into the new table. This function is responsible for allocating + necessary memory for the script. + + This function has a variable parameter list. The exact parameter list depends on + the OpCode that is passed into the function. If an unsupported OpCode or illegal + parameter list is passed in, this function returns EFI_INVALID_PARAMETER. + If there are not enough resources available for storing more scripts, this function returns + EFI_OUT_OF_RESOURCES. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The parameter is illegal or the given boot script is not supported. + If the opcode is unknow or not supported because of the PCD + Feature Flags. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptWrite ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN UINTN OpCode, + ... + ) +{ + EFI_STATUS Status; + VA_LIST Marker; + // + // Build script according to opcode + // + switch (OpCode) { + + case EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_SMBUS_EXECUTE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteSmbusExecute (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_STALL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteStall (Marker); + VA_END (Marker); + + break; + + case EFI_BOOT_SCRIPT_DISPATCH_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_DISPATCH_2_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch2 (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_INFORMATION_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteInformation (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2Write (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2ReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfigPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfig2Poll (Marker); + VA_END (Marker); + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + return Status; +} +/** + Insert a record into a specified Framework boot script table. + + This function is used to store an OpCode to be replayed as part of the S3 resume boot path. It is + assumed this protocol has platform specific mechanism to store the OpCode set and replay them + during the S3 resume. + The opcode is inserted before or after the specified position in the boot script table. If Position is + NULL then that position is after the last opcode in the table (BeforeOrAfter is FALSE) or before + the first opcode in the table (BeforeOrAfter is TRUE). The position which is pointed to by + Position upon return can be used for subsequent insertions. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the opcode is stored before (TRUE) or after (FALSE) the position + in the boot script table specified by Position. If Position is NULL or points to + NULL then the new opcode is inserted at the beginning of the table (if TRUE) or end + of the table (if FALSE). + @param Position On entry, specifies the position in the boot script table where the opcode will be + inserted, either before or after, depending on BeforeOrAfter. On exit, specifies + the position of the inserted opcode in the boot script table. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The Opcode is an invalid opcode value or the Position is not a valid position in the boot script table.. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptInsert ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN UINTN OpCode, + ... + ) +{ + EFI_STATUS Status; + VA_LIST Marker; + // + // Build script according to opcode + // + switch (OpCode) { + + case EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_SMBUS_EXECUTE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteSmbusExecute (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_STALL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteStall (Marker); + VA_END (Marker); + + break; + + case EFI_BOOT_SCRIPT_DISPATCH_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_DISPATCH_2_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch2 (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_INFORMATION_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteInformation (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2Write (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2ReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfigPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfig2Poll (Marker); + VA_END (Marker); + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + if (!EFI_ERROR (Status)) { + Status = S3BootScriptMoveLastOpcode (BeforeOrAfter, (VOID **)Position); + } + return Status; +} +/** + Find a label within the boot script table and, if not present, optionally create it. + + If the label Label is already exists in the boot script table, then no new label is created, the + position of the Label is returned in *Position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is TRUE, then it will be + created before or after the specified position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is FALSE, then + EFI_NOT_FOUND is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the label is stored before (TRUE) or after (FALSE) the position in + the boot script table specified by Position. If Position is NULL or points to + NULL then the new label is inserted at the beginning of the table (if TRUE) or end of + the table (if FALSE). + @param CreateIfNotFound Specifies whether the label will be created if the label does not exists (TRUE) or not + (FALSE). + @param Position On entry, specifies the position in the boot script table where the label will be inserted, + either before or after, depending on BeforeOrAfter. On exit, specifies the position + of the inserted label in the boot script table. + @param Label Points to the label which will be inserted in the boot script table. + + @retval EFI_SUCCESS The label already exists or was inserted. + @retval EFI_INVALID_PARAMETER The Label is NULL or points to an empty string. + @retval EFI_INVALID_PARAMETER The Position is not a valid position in the boot script table. + +**/ +EFI_STATUS +EFIAPI +BootScriptLabel ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN BOOLEAN CreateIfNotFound, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN CONST CHAR8 *Label + ) +{ + return S3BootScriptLabel (BeforeOrAfter, CreateIfNotFound, (VOID **)Position, Label); +} +/** + Compare two positions in the boot script table and return their relative position. + + This function compares two positions in the boot script table and returns their relative positions. If + Position1 is before Position2, then -1 is returned. If Position1 is equal to Position2, + then 0 is returned. If Position1 is after Position2, then 1 is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param Position1 The positions in the boot script table to compare + @param Position2 The positions in the boot script table to compare + @param RelativePosition On return, points to the result of the comparison + + @retval EFI_SUCCESS The operation succeeded. + @retval EFI_INVALID_PARAMETER The Position1 or Position2 is not a valid position in the boot script table. + @retval EFI_INVALID_PARAMETER The RelativePosition is NULL. + +**/ +EFI_STATUS +EFIAPI +BootScriptCompare ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN EFI_S3_BOOT_SCRIPT_POSITION Position1, + IN EFI_S3_BOOT_SCRIPT_POSITION Position2, + OUT UINTN *RelativePosition + ) +{ + return S3BootScriptCompare (Position1, Position2, RelativePosition); +} +/** + This routine is entry point of ScriptSave driver. + + @param ImageHandle Handle for this drivers loaded image protocol. + @param SystemTable EFI system table. + + @retval EFI_OUT_OF_RESOURCES No enough resource + @retval EFI_SUCCESS Succesfully installed the ScriptSave driver. + @retval other Errors occurred. + +**/ +EFI_STATUS +EFIAPI +InitializeS3SaveState ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT EndOfDxeEvent; + + if (!PcdGetBool (PcdAcpiS3Enable)) { + return EFI_UNSUPPORTED; + } + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + AcpiS3ContextSaveOnEndOfDxe, + NULL, + &gEfiEndOfDxeEventGroupGuid, + &EndOfDxeEvent + ); + ASSERT_EFI_ERROR (Status); + + return gBS->InstallProtocolInterface ( + &mHandle, + &gEfiS3SaveStateProtocolGuid, + EFI_NATIVE_INTERFACE, + &mS3SaveState + ); + +} + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf new file mode 100644 index 000000000..2dc1ab822 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf @@ -0,0 +1,69 @@ +## @file +# S3 Boot Script Save State driver. +# +# It will install S3 Save State protocol to store or record various IO operations to be replayed during an S3 resume. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = S3SaveStateDxe + MODULE_UNI_FILE = S3SaveStateDxe.uni + FILE_GUID = BDCE85BB-FBAA-4f4e-9264-501A2C249581 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeS3SaveState + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + S3SaveState.c + InternalS3SaveState.h + AcpiS3ContextSave.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + BaseLib + S3BootScriptLib + PcdLib + HobLib + LockBoxLib + UefiLib + +[Guids] + gEfiAcpiVariableGuid ## PRODUCES ## UNDEFINED # LockBox Save Data. + gEfiAcpiS3ContextGuid ## PRODUCES ## UNDEFINED # LockBox Save Data. + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + +[Protocols] + gEfiS3SaveStateProtocolGuid ## PRODUCES + gEfiLockBoxProtocolGuid ## CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptStackSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + S3SaveStateDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.uni new file mode 100644 index 000000000..2e7299461 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.uni @@ -0,0 +1,16 @@ +// /** @file +// S3 Boot Script Save State driver. +// +// It will install S3 Save State protocol to store or record various IO operations to be replayed during an S3 resume. +// +// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "S3 Boot Script Save State driver" + +#string STR_MODULE_DESCRIPTION #language en-US "It will install S3 Save State protocol to store or record various IO operations to be replayed during an S3 resume." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxeExtra.uni new file mode 100644 index 000000000..d21c906c5 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// S3SaveStateDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"S3 Save State DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/InternalSmmSaveState.h b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/InternalSmmSaveState.h new file mode 100644 index 000000000..afc41ed3f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/InternalSmmSaveState.h @@ -0,0 +1,154 @@ +/** @file + Internal header file for SMM S3 Boot Script Saver state driver. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef _INTERNAL_SMM_S3_SAVE_STATE_H_ +#define _INTERNAL_SMM_S3_SAVE_STATE_H_ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +/** + Adds a record into S3 boot script table. + + This function is used to store a boot script record into a given boot + script table. If the table specified by TableName is nonexistent in the + system, a new table will automatically be created and then the script record + will be added into the new table. This function is responsible for allocating + necessary memory for the script. + + This function has a variable parameter list. The exact parameter list depends on + the OpCode that is passed into the function. If an unsupported OpCode or illegal + parameter list is passed in, this function returns EFI_INVALID_PARAMETER. + If there are not enough resources available for storing more scripts, this function returns + EFI_OUT_OF_RESOURCES. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The parameter is illegal or the given boot script is not supported. + If the opcode is unknow or not supported because of the PCD + Feature Flags. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptWrite ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN UINTN OpCode, + ... + ); +/** + Insert a record into a specified Framework boot script table. + + This function is used to store an OpCode to be replayed as part of the S3 resume boot path. It is + assumed this protocol has platform specific mechanism to store the OpCode set and replay them + during the S3 resume. + The opcode is inserted before or after the specified position in the boot script table. If Position is + NULL then that position is after the last opcode in the table (BeforeOrAfter is FALSE) or before + the first opcode in the table (BeforeOrAfter is TRUE). The position which is pointed to by + Position upon return can be used for subsequent insertions. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the opcode is stored before (TRUE) or after (FALSE) the position + in the boot script table specified by Position. If Position is NULL or points to + NULL then the new opcode is inserted at the beginning of the table (if TRUE) or end + of the table (if FALSE). + @param Position On entry, specifies the position in the boot script table where the opcode will be + inserted, either before or after, depending on BeforeOrAfter. On exit, specifies + the position of the inserted opcode in the boot script table. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The Opcode is an invalid opcode value or the Position is not a valid position in the boot script table.. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptInsert ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN UINTN OpCode, + ... + ); +/** + Find a label within the boot script table and, if not present, optionally create it. + + If the label Label is already exists in the boot script table, then no new label is created, the + position of the Label is returned in *Position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is TRUE, then it will be + created before or after the specified position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is FALSE, then + EFI_NOT_FOUND is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the label is stored before (TRUE) or after (FALSE) the position in + the boot script table specified by Position. If Position is NULL or points to + NULL then the new label is inserted at the beginning of the table (if TRUE) or end of + the table (if FALSE). + @param CreateIfNotFound Specifies whether the label will be created if the label does not exists (TRUE) or not + (FALSE). + @param Position On entry, specifies the position in the boot script table where the label will be inserted, + either before or after, depending on BeforeOrAfter. On exit, specifies the position + of the inserted label in the boot script table. + @param Label Points to the label which will be inserted in the boot script table. + + @retval EFI_SUCCESS The label already exists or was inserted. + @retval EFI_INVALID_PARAMETER The Opcode is an invalid opcode value or the Position is not a valid position in the boot script table.. + +**/ +EFI_STATUS +EFIAPI +BootScriptLabel ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN BOOLEAN CreateIfNotFound, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN CONST CHAR8 *Label + ); +/** + Compare two positions in the boot script table and return their relative position. + + This function compares two positions in the boot script table and returns their relative positions. If + Position1 is before Position2, then -1 is returned. If Position1 is equal to Position2, + then 0 is returned. If Position1 is after Position2, then 1 is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param Position1 The positions in the boot script table to compare + @param Position2 The positions in the boot script table to compare + @param RelativePosition On return, points to the result of the comparison + + @retval EFI_SUCCESS The operation succeeded. + @retval EFI_INVALID_PARAMETER The Position1 or Position2 is not a valid position in the boot script table. + +**/ +EFI_STATUS +EFIAPI +BootScriptCompare ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN EFI_S3_BOOT_SCRIPT_POSITION Position1, + IN EFI_S3_BOOT_SCRIPT_POSITION Position2, + OUT UINTN *RelativePosition + ); + +#endif //_INTERNAL_SMM_S3_SAVE_STATE_H_ diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.c b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.c new file mode 100644 index 000000000..601c8218d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.c @@ -0,0 +1,913 @@ +/** @file + Implementation for S3 SMM Boot Script Saver state driver. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "InternalSmmSaveState.h" + +EFI_S3_SMM_SAVE_STATE_PROTOCOL mS3SmmSaveState = { + BootScriptWrite, + BootScriptInsert, + BootScriptLabel, + BootScriptCompare + }; +/** + Internal function to add IO write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteIoWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveIoWrite (Width, Address, Count, Buffer); +} +/** + Internal function to add IO read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteIoReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveIoReadWrite (Width, Address, Data, DataMask); +} + +/** + Internal function to add memory write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteMemWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveMemWrite (Width, Address, Count, Buffer); +} + +/** + Internal function to add memory read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteMemReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSaveMemReadWrite (Width, Address, Data, DataMask); +} + +/** + Internal function to add PciCfg write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfgWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfgWrite (Width, Address, Count, Buffer); +} + +/** + Internal function to PciCfg read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfgReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfgReadWrite (Width, Address, Data, DataMask); +} +/** + Internal function to add PciCfg2 write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfg2Write ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + UINTN Count; + UINT8 *Buffer; + UINT16 Segment; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Segment = VA_ARG (Marker, UINT16); + Address = VA_ARG (Marker, UINT64); + Count = VA_ARG (Marker, UINTN); + Buffer = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfg2Write (Width, Segment, Address, Count, Buffer); +} + +/** + Internal function to PciCfg2 read/write opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWritePciCfg2ReadWrite ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT16 Segment; + UINT64 Address; + UINT8 *Data; + UINT8 *DataMask; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Segment = VA_ARG (Marker, UINT16); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, UINT8 *); + DataMask = VA_ARG (Marker, UINT8 *); + + return S3BootScriptSavePciCfg2ReadWrite (Width, Segment, Address, Data, DataMask); +} +/** + Internal function to add smbus execute opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteSmbusExecute ( + IN VA_LIST Marker + ) +{ + EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; + EFI_SMBUS_DEVICE_COMMAND Command; + EFI_SMBUS_OPERATION Operation; + BOOLEAN PecCheck; + VOID *Buffer; + UINTN *DataSize; + UINTN SmBusAddress; + + SlaveAddress.SmbusDeviceAddress = VA_ARG (Marker, UINTN); + Command = VA_ARG (Marker, EFI_SMBUS_DEVICE_COMMAND); + Operation = VA_ARG (Marker, EFI_SMBUS_OPERATION); + PecCheck = VA_ARG (Marker, BOOLEAN); + SmBusAddress = SMBUS_LIB_ADDRESS (SlaveAddress.SmbusDeviceAddress,Command,0,PecCheck); + DataSize = VA_ARG (Marker, UINTN *); + Buffer = VA_ARG (Marker, VOID *); + + return S3BootScriptSaveSmbusExecute (SmBusAddress, Operation, DataSize, Buffer); +} +/** + Internal function to add stall opcode to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteStall ( + IN VA_LIST Marker + ) +{ + UINT32 Duration; + + Duration = VA_ARG (Marker, UINT32); + + return S3BootScriptSaveStall (Duration); +} + +/** + Internal function to add Save jmp address according to DISPATCH_OPCODE. + We ignore "Context" parameter + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteDispatch ( + IN VA_LIST Marker + ) +{ + VOID *EntryPoint; + + EntryPoint = (VOID*)(UINTN)VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + return S3BootScriptSaveDispatch (EntryPoint); +} + +/** + Internal function to add memory pool operation to the table. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteMemPoll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + UINT64 LoopTimes; + UINT32 Remainder; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = VA_ARG (Marker, UINT64); + // + // According to the spec, the interval between 2 polls is 100ns, + // but the unit of Duration for S3BootScriptSaveMemPoll() is microsecond(1000ns). + // Duration * 1000ns * LoopTimes = Delay * 100ns + // Duration will be minimum 1(microsecond) to be minimum deviation, + // so LoopTimes = Delay / 10. + // + LoopTimes = DivU64x32Remainder ( + Delay, + 10, + &Remainder + ); + if (Remainder != 0) { + // + // If Remainder is not zero, LoopTimes will be rounded up by 1. + // + LoopTimes +=1; + } + return S3BootScriptSaveMemPoll (Width, Address, DataMask, Data, 1, LoopTimes); + +} + +/** + Internal function to add Save jmp address according to DISPATCH_OPCODE2. + The "Context" parameter is not ignored. + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enough resource to do operation. + @retval EFI_SUCCESS Opcode is added. + +**/ +EFI_STATUS +BootScriptWriteDispatch2 ( + IN VA_LIST Marker + ) +{ + VOID *EntryPoint; + VOID *Context; + + EntryPoint = (VOID*)(UINTN)VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + Context = (VOID*)(UINTN)VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + + return S3BootScriptSaveDispatch2 (EntryPoint, Context); +} +/** + Internal function to add INFORAMTION opcode node to the table + list. + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWriteInformation ( + IN VA_LIST Marker + ) +{ + UINT32 InformationLength; + EFI_PHYSICAL_ADDRESS Information; + + InformationLength = VA_ARG (Marker, UINT32); + Information = VA_ARG (Marker, EFI_PHYSICAL_ADDRESS); + return S3BootScriptSaveInformation (InformationLength, (VOID*)(UINTN)Information); +} +/** + Internal function to add IO poll opcode node to the table + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWriteIoPoll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = (UINT64)VA_ARG (Marker, UINT64); + + return S3BootScriptSaveIoPoll (Width, Address, Data, DataMask, Delay); +} +/** + Internal function to add PCI config poll opcode node to the table + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWritePciConfigPoll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = (UINT64)VA_ARG (Marker, UINT64); + + return S3BootScriptSavePciPoll (Width, Address, Data, DataMask, Delay); +} +/** + Internal function to add PCI config 2 poll opcode node to the table + + @param Marker The variable argument list to get the opcode + and associated attributes. + + @retval EFI_OUT_OF_RESOURCES Not enought resource to complete the operations. + @retval EFI_SUCCESS The opcode entry is added to the table + successfully. +**/ +EFI_STATUS +BootScriptWritePciConfig2Poll ( + IN VA_LIST Marker + ) +{ + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT16 Segment; + UINT64 Address; + VOID *Data; + VOID *DataMask; + UINT64 Delay; + + Width = VA_ARG (Marker, S3_BOOT_SCRIPT_LIB_WIDTH); + Segment = VA_ARG (Marker, UINT16); + Address = VA_ARG (Marker, UINT64); + Data = VA_ARG (Marker, VOID *); + DataMask = VA_ARG (Marker, VOID *); + Delay = (UINT64)VA_ARG (Marker, UINT64); + + return S3BootScriptSavePci2Poll (Width, Segment, Address, Data, DataMask, Delay); +} + +/** + Adds a record into S3 boot script table. + + This function is used to store a boot script record into a given boot + script table. If the table specified by TableName is nonexistent in the + system, a new table will automatically be created and then the script record + will be added into the new table. This function is responsible for allocating + necessary memory for the script. + + This function has a variable parameter list. The exact parameter list depends on + the OpCode that is passed into the function. If an unsupported OpCode or illegal + parameter list is passed in, this function returns EFI_INVALID_PARAMETER. + If there are not enough resources available for storing more scripts, this function returns + EFI_OUT_OF_RESOURCES. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The parameter is illegal or the given boot script is not supported. + If the opcode is unknow or not supported because of the PCD + Feature Flags. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptWrite ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN UINTN OpCode, + ... + ) +{ + EFI_STATUS Status; + VA_LIST Marker; + // + // Build script according to opcode + // + switch (OpCode) { + + case EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_SMBUS_EXECUTE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteSmbusExecute (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_STALL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteStall (Marker); + VA_END (Marker); + + break; + + case EFI_BOOT_SCRIPT_DISPATCH_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_DISPATCH_2_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch2 (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_INFORMATION_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteInformation (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2Write (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2ReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfigPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfig2Poll (Marker); + VA_END (Marker); + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + return Status; +} +/** + Insert a record into a specified Framework boot script table. + + This function is used to store an OpCode to be replayed as part of the S3 resume boot path. It is + assumed this protocol has platform specific mechanism to store the OpCode set and replay them + during the S3 resume. + The opcode is inserted before or after the specified position in the boot script table. If Position is + NULL then that position is after the last opcode in the table (BeforeOrAfter is FALSE) or before + the first opcode in the table (BeforeOrAfter is TRUE). The position which is pointed to by + Position upon return can be used for subsequent insertions. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the opcode is stored before (TRUE) or after (FALSE) the position + in the boot script table specified by Position. If Position is NULL or points to + NULL then the new opcode is inserted at the beginning of the table (if TRUE) or end + of the table (if FALSE). + @param Position On entry, specifies the position in the boot script table where the opcode will be + inserted, either before or after, depending on BeforeOrAfter. On exit, specifies + the position of the inserted opcode in the boot script table. + @param OpCode The operation code (opcode) number. + @param ... Argument list that is specific to each opcode. + + @retval EFI_SUCCESS The operation succeeded. A record was added into the + specified script table. + @retval EFI_INVALID_PARAMETER The Opcode is an invalid opcode value or the Position is not a valid position in the boot script table.. + @retval EFI_OUT_OF_RESOURCES There is insufficient memory to store the boot script. + +**/ +EFI_STATUS +EFIAPI +BootScriptInsert ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN UINTN OpCode, + ... + ) +{ + EFI_STATUS Status; + VA_LIST Marker; + // + // Build script according to opcode + // + switch (OpCode) { + + case EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfgReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_SMBUS_EXECUTE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteSmbusExecute (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_STALL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteStall (Marker); + VA_END (Marker); + + break; + + case EFI_BOOT_SCRIPT_DISPATCH_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_DISPATCH_2_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteDispatch2 (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_INFORMATION_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteInformation (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteMemPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2Write (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_READ_WRITE_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciCfg2ReadWrite (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_IO_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWriteIoPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfigPoll (Marker); + VA_END (Marker); + break; + + case EFI_BOOT_SCRIPT_PCI_CONFIG2_POLL_OPCODE: + VA_START (Marker, OpCode); + Status = BootScriptWritePciConfig2Poll (Marker); + VA_END (Marker); + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + if (!EFI_ERROR (Status)) { + Status = S3BootScriptMoveLastOpcode (BeforeOrAfter, (VOID **)Position); + } + return Status; +} +/** + Find a label within the boot script table and, if not present, optionally create it. + + If the label Label is already exists in the boot script table, then no new label is created, the + position of the Label is returned in *Position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is TRUE, then it will be + created before or after the specified position and EFI_SUCCESS is returned. + If the label Label does not already exist and CreateIfNotFound is FALSE, then + EFI_NOT_FOUND is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param BeforeOrAfter Specifies whether the label is stored before (TRUE) or after (FALSE) the position in + the boot script table specified by Position. If Position is NULL or points to + NULL then the new label is inserted at the beginning of the table (if TRUE) or end of + the table (if FALSE). + @param CreateIfNotFound Specifies whether the label will be created if the label does not exists (TRUE) or not + (FALSE). + @param Position On entry, specifies the position in the boot script table where the label will be inserted, + either before or after, depending on BeforeOrAfter. On exit, specifies the position + of the inserted label in the boot script table. + @param Label Points to the label which will be inserted in the boot script table. + + @retval EFI_SUCCESS The label already exists or was inserted. + @retval EFI_INVALID_PARAMETER The Label is NULL or points to an empty string. + @retval EFI_INVALID_PARAMETER The Position is not a valid position in the boot script table. + +**/ +EFI_STATUS +EFIAPI +BootScriptLabel ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN BOOLEAN BeforeOrAfter, + IN BOOLEAN CreateIfNotFound, + IN OUT EFI_S3_BOOT_SCRIPT_POSITION *Position OPTIONAL, + IN CONST CHAR8 *Label + ) +{ + return S3BootScriptLabel (BeforeOrAfter, CreateIfNotFound, (VOID **)Position, Label); +} +/** + Compare two positions in the boot script table and return their relative position. + + This function compares two positions in the boot script table and returns their relative positions. If + Position1 is before Position2, then -1 is returned. If Position1 is equal to Position2, + then 0 is returned. If Position1 is after Position2, then 1 is returned. + + @param This A pointer to the EFI_S3_SAVE_STATE_PROTOCOL instance. + @param Position1 The positions in the boot script table to compare + @param Position2 The positions in the boot script table to compare + @param RelativePosition On return, points to the result of the comparison + + @retval EFI_SUCCESS The operation succeeded. + @retval EFI_INVALID_PARAMETER The Position1 or Position2 is not a valid position in the boot script table. + @retval EFI_INVALID_PARAMETER The RelativePosition is NULL. + +**/ +EFI_STATUS +EFIAPI +BootScriptCompare ( + IN CONST EFI_S3_SAVE_STATE_PROTOCOL *This, + IN EFI_S3_BOOT_SCRIPT_POSITION Position1, + IN EFI_S3_BOOT_SCRIPT_POSITION Position2, + OUT UINTN *RelativePosition + ) +{ + return S3BootScriptCompare (Position1, Position2, RelativePosition); +} +/** + This routine is entry point of ScriptSave driver. + + @param ImageHandle Handle for this drivers loaded image protocol. + @param SystemTable EFI system table. + + @retval EFI_OUT_OF_RESOURCES No enough resource + @retval EFI_SUCCESS Succesfully installed the ScriptSave driver. + @retval other Errors occurred. + +**/ +EFI_STATUS +EFIAPI +InitializeSmmS3SaveState ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_HANDLE Handle; + + if (!PcdGetBool (PcdAcpiS3Enable)) { + return EFI_UNSUPPORTED; + } + + Handle = NULL; + return gSmst->SmmInstallProtocolInterface ( + &Handle, + &gEfiS3SmmSaveStateProtocolGuid, + EFI_NATIVE_INTERFACE, + &mS3SmmSaveState + ); +} diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.inf b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.inf new file mode 100644 index 000000000..91d99545e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.inf @@ -0,0 +1,56 @@ +## @file +# S3 SMM Boot Script Save State driver. +# +# It will install S3 SMM Save State protocol to store or record various IO operations to be replayed during an S3 resume. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmS3SaveState + MODULE_UNI_FILE = SmmS3SaveState.uni + FILE_GUID = 2D59F041-53A4-40d0-A6CD-844DC0DFEF17 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + + ENTRY_POINT = InitializeSmmS3SaveState + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + SmmS3SaveState.c + InternalSmmSaveState.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + SmmServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + BaseLib + S3BootScriptLib + PcdLib + +[Protocols] + gEfiS3SmmSaveStateProtocolGuid ## PRODUCES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + SmmS3SaveStateExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.uni new file mode 100644 index 000000000..7cdb7e551 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.uni @@ -0,0 +1,16 @@ +// /** @file +// S3 SMM Boot Script Save State driver. +// +// It will install S3 SMM Save State protocol to store or record various IO operations to be replayed during an S3 resume. +// +// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "S3 SMM Boot Script Save State driver" + +#string STR_MODULE_DESCRIPTION #language en-US "It will install the S3 SMM Save State protocol to store or record various IO operations to be replayed during an S3 resume." + diff --git a/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveStateExtra.uni b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveStateExtra.uni new file mode 100644 index 000000000..a63907fd0 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveStateExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// SmmS3SaveState Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SMM S3 Save State Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/Bds.h b/roms/edk2/MdeModulePkg/Universal/BdsDxe/Bds.h new file mode 100644 index 000000000..e7a9b5b4b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/Bds.h @@ -0,0 +1,108 @@ +/** @file + Head file for BDS Architectural Protocol implementation + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _BDS_MODULE_H_ +#define _BDS_MODULE_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 + +#if !defined (EFI_REMOVABLE_MEDIA_FILE_NAME) + #if defined (MDE_CPU_EBC) + // + // Uefi specification only defines the default boot file name for IA32, X64 + // and IPF processor, so need define boot file name for EBC architecture here. + // + #define EFI_REMOVABLE_MEDIA_FILE_NAME L"\\EFI\\BOOT\\BOOTEBC.EFI" + #else + #error "Can not determine the default boot file name for unknown processor type!" + #endif +#endif + +/** + + Service routine for BdsInstance->Entry(). Devices are connected, the + consoles are initialized, and the boot options are tried. + + @param This Protocol Instance structure. + +**/ +VOID +EFIAPI +BdsEntry ( + IN EFI_BDS_ARCH_PROTOCOL *This + ); + +/** + Set the variable and report the error through status code upon failure. + + @param VariableName A Null-terminated string that is the name of the vendor's variable. + Each VariableName is unique for each VendorGuid. VariableName must + contain 1 or more characters. If VariableName is an empty string, + then EFI_INVALID_PARAMETER is returned. + @param VendorGuid A unique identifier for the vendor. + @param Attributes Attributes bitmask to set for the variable. + @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, + or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero + causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is + set, then a SetVariable() call with a DataSize of zero will not cause any change to + the variable value (the timestamp associated with the variable may be updated however + even if no new data value is provided,see the description of the + EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not + be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). + @param Data The contents for the variable. + + @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as + defined by the Attributes. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the + DataSize exceeds the maximum allowed. + @retval EFI_INVALID_PARAMETER VariableName is an empty string. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. + @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS + being set, but the AuthInfo does NOT pass the validation check carried out by the firmware. + + @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. +**/ +EFI_STATUS +BdsDxeSetVariableAndReportStatusCodeOnError ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.inf b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.inf new file mode 100644 index 000000000..9310b4dcc --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.inf @@ -0,0 +1,105 @@ +## @file +# BdsDxe module is core driver for BDS phase. +# +# When DxeCore dispatching all DXE driver, this module will produce architecture protocol +# gEfiBdsArchProtocolGuid. After DxeCore finish dispatching, DxeCore will invoke Entry +# interface of protocol gEfiBdsArchProtocolGuid, then BDS phase is entered. +# +# Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BdsDxe + MODULE_UNI_FILE = BdsDxe.uni + FILE_GUID = 6D33944A-EC75-4855-A54D-809C75241F6C + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = BdsInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + Language.h + Bds.h + HwErrRecSupport.c + HwErrRecSupport.h + Language.c + BdsEntry.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + DevicePathLib + BaseLib + MemoryAllocationLib + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + ReportStatusCodeLib + UefiLib + BaseMemoryLib + DebugLib + UefiBootManagerLib + PlatformBootManagerLib + PcdLib + PrintLib + +[Guids] + gEfiGlobalVariableGuid ## SOMETIMES_PRODUCES ## Variable:L"BootNext" (The number of next boot option) + ## SOMETIMES_PRODUCES ## Variable:L"Boot####" (Boot option variable) + ## SOMETIMES_PRODUCES ## Variable:L"PlatformLang" (Platform supported languange in Rfc4646 format) + ## SOMETIMES_PRODUCES ## Variable:L"Lang" (Platform supported languange in Iso639 format) + ## SOMETIMES_PRODUCES ## Variable:L"Key####" (Hotkey option variable) + ## PRODUCES ## Variable:L"HwErrRecSupport" (The level of platform supported hardware Error Record Persistence) + ## SOMETIMES_PRODUCES ## Variable:L"BootOptionSupport" (The feature supported in boot option menu, value could be: EFI_BOOT_OPTION_SUPPORT_KEY, EFI_BOOT_OPTION_SUPPORT_APP + ## SOMETIMES_PRODUCES (not PcdUefiVariableDefaultLangDeprecate) ## Variable:L"LangCodes" (Value of PcdUefiVariableDefaultLangCodes) + ## PRODUCES ## Variable:L"PlatformLangCodes" (Value of PcdUefiVariableDefaultPlatformLangCodes) + ## PRODUCES ## Variable:L"Timeout" (The time out value in second of showing progress bar) + ## SOMETIMES_PRODUCES ## Variable:L"BootOrder" (The boot option array) + ## SOMETIMES_PRODUCES ## Variable:L"DriverOrder" (The driver order list) + ## SOMETIMES_CONSUMES ## Variable:L"ConIn" (The device path of console in device) + ## SOMETIMES_CONSUMES ## Variable:L"ConOut" (The device path of console out device) + ## SOMETIMES_CONSUMES ## Variable:L"ErrOut" (The device path of error out device) + gConnectConInEventGuid ## SOMETIMES_CONSUMES ## Event + gEdkiiStatusCodeDataTypeVariableGuid ## SOMETIMES_CONSUMES ## GUID + gEfiEventReadyToBootGuid ## CONSUMES ## Event + +[Protocols] + gEfiBdsArchProtocolGuid ## PRODUCES + gEfiSimpleTextInputExProtocolGuid ## CONSUMES + gEdkiiVariableLockProtocolGuid ## SOMETIMES_CONSUMES + gEfiDeferredImageLoadProtocolGuid ## CONSUMES + +[FeaturePcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangDeprecate ## CONSUMES + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangCodes ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang ## SOMETIMES_CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLangCodes ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdHardwareErrorRecordLevel ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVendor ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareRevision ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdConInConnectOnDemand ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdErrorCodeSetVariable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdTestKeyUsed ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleOnDiskSupport ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPlatformRecoverySupport ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + BdsDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.uni b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.uni new file mode 100644 index 000000000..8609352a6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// BDSDxe module is core driver for BDS phase. +// +// When DxeCore dispatching all DXE driver, this module will produce architecture protocol +// gEfiBdsArchProtocolGuid. After DxeCore finish dispatching, DxeCore will invoke Entry +// interface of protocol gEfiBdsArchProtocolGuid, then BDS phase is entered. +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "BdsDxe module is core driver for BDS phase" + +#string STR_MODULE_DESCRIPTION #language en-US "When DxeCore dispatching all DXE driver, this module will produce architecture protocol gEfiBdsArchProtocolGuid. After DxeCore finishes dispatching, DxeCore will invoke the Entry interface of protocol gEfiBdsArchProtocolGuid. Then BDS phase is entered." + diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxeExtra.uni new file mode 100644 index 000000000..cd561a723 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// BdsDxe Localized Strings and Content +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Boot Device Selection Core DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsEntry.c b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsEntry.c new file mode 100644 index 000000000..83b773a2f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/BdsEntry.c @@ -0,0 +1,1170 @@ +/** @file + This module produce main entry for BDS phase - BdsEntry. + When this module was dispatched by DxeCore, gEfiBdsArchProtocolGuid will be installed + which contains interface of BdsEntry. + After DxeCore finish DXE phase, gEfiBdsArchProtocolGuid->BdsEntry will be invoked + to enter BDS phase. + +Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.
+(C) Copyright 2016-2019 Hewlett Packard Enterprise Development LP
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Bds.h" +#include "Language.h" +#include "HwErrRecSupport.h" + +#define SET_BOOT_OPTION_SUPPORT_KEY_COUNT(a, c) { \ + (a) = ((a) & ~EFI_BOOT_OPTION_SUPPORT_COUNT) | (((c) << LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT)) & EFI_BOOT_OPTION_SUPPORT_COUNT); \ + } + +/// +/// BDS arch protocol instance initial value. +/// +EFI_BDS_ARCH_PROTOCOL gBds = { + BdsEntry +}; + +// +// gConnectConInEvent - Event which is signaled when ConIn connection is required +// +EFI_EVENT gConnectConInEvent = NULL; + +/// +/// The read-only variables defined in UEFI Spec. +/// +CHAR16 *mReadOnlyVariables[] = { + EFI_PLATFORM_LANG_CODES_VARIABLE_NAME, + EFI_LANG_CODES_VARIABLE_NAME, + EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, + EFI_HW_ERR_REC_SUPPORT_VARIABLE_NAME, + EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME + }; + +CHAR16 *mBdsLoadOptionName[] = { + L"Driver", + L"SysPrep", + L"Boot", + L"PlatformRecovery" +}; + +/** + Event to Connect ConIn. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +BdsDxeOnConnectConInCallBack ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // When Osloader call ReadKeyStroke to signal this event + // no driver dependency is assumed existing. So use a non-dispatch version + // + Status = EfiBootManagerConnectConsoleVariable (ConIn); + if (EFI_ERROR (Status)) { + // + // Should not enter this case, if enter, the keyboard will not work. + // May need platfrom policy to connect keyboard. + // + DEBUG ((EFI_D_WARN, "[Bds] Connect ConIn failed - %r!!!\n", Status)); + } +} +/** + Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to + check whether there is remaining deferred load images. + + @param[in] Event The Event that is being processed. + @param[in] Context The Event Context. + +**/ +VOID +EFIAPI +CheckDeferredLoadImageOnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_DEFERRED_IMAGE_LOAD_PROTOCOL *DeferredImage; + UINTN HandleCount; + EFI_HANDLE *Handles; + UINTN Index; + UINTN ImageIndex; + EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath; + VOID *Image; + UINTN ImageSize; + BOOLEAN BootOption; + CHAR16 *DevicePathStr; + + // + // Find all the deferred image load protocols. + // + HandleCount = 0; + Handles = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDeferredImageLoadProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + if (EFI_ERROR (Status)) { + return; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (Handles[Index], &gEfiDeferredImageLoadProtocolGuid, (VOID **) &DeferredImage); + if (EFI_ERROR (Status)) { + continue; + } + + for (ImageIndex = 0; ; ImageIndex++) { + // + // Load all the deferred images in this protocol instance. + // + Status = DeferredImage->GetImageInfo ( + DeferredImage, + ImageIndex, + &ImageDevicePath, + (VOID **) &Image, + &ImageSize, + &BootOption + ); + if (EFI_ERROR (Status)) { + break; + } + DevicePathStr = ConvertDevicePathToText (ImageDevicePath, FALSE, FALSE); + DEBUG ((DEBUG_LOAD, "[Bds] Image was deferred but not loaded: %s.\n", DevicePathStr)); + if (DevicePathStr != NULL) { + FreePool (DevicePathStr); + } + } + } + if (Handles != NULL) { + FreePool (Handles); + } +} + +/** + + Install Boot Device Selection Protocol + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCEESS BDS has finished initializing. + Return the dispatcher and recall BDS.Entry + @retval Other Return status from AllocatePool() or gBS->InstallProtocolInterface + +**/ +EFI_STATUS +EFIAPI +BdsInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + // + // Install protocol interface + // + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiBdsArchProtocolGuid, &gBds, + NULL + ); + ASSERT_EFI_ERROR (Status); + + DEBUG_CODE ( + EFI_EVENT Event; + // + // Register notify function to check deferred images on ReadyToBoot Event. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + CheckDeferredLoadImageOnReadyToBoot, + NULL, + &gEfiEventReadyToBootGuid, + &Event + ); + ASSERT_EFI_ERROR (Status); + ); + return Status; +} + +/** + Function waits for a given event to fire, or for an optional timeout to expire. + + @param Event The event to wait for + @param Timeout An optional timeout value in 100 ns units. + + @retval EFI_SUCCESS Event fired before Timeout expired. + @retval EFI_TIME_OUT Timout expired before Event fired.. + +**/ +EFI_STATUS +BdsWaitForSingleEvent ( + IN EFI_EVENT Event, + IN UINT64 Timeout OPTIONAL + ) +{ + UINTN Index; + EFI_STATUS Status; + EFI_EVENT TimerEvent; + EFI_EVENT WaitList[2]; + + if (Timeout != 0) { + // + // Create a timer event + // + Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent); + if (!EFI_ERROR (Status)) { + // + // Set the timer event + // + gBS->SetTimer ( + TimerEvent, + TimerRelative, + Timeout + ); + + // + // Wait for the original event or the timer + // + WaitList[0] = Event; + WaitList[1] = TimerEvent; + Status = gBS->WaitForEvent (2, WaitList, &Index); + ASSERT_EFI_ERROR (Status); + gBS->CloseEvent (TimerEvent); + + // + // If the timer expired, change the return to timed out + // + if (Index == 1) { + Status = EFI_TIMEOUT; + } + } + } else { + // + // No timeout... just wait on the event + // + Status = gBS->WaitForEvent (1, &Event, &Index); + ASSERT (!EFI_ERROR (Status)); + ASSERT (Index == 0); + } + + return Status; +} + +/** + The function reads user inputs. + +**/ +VOID +BdsReadKeys ( + VOID + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + + if (PcdGetBool (PcdConInConnectOnDemand)) { + return; + } + + while (gST->ConIn != NULL) { + + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + if (EFI_ERROR (Status)) { + // + // No more keys. + // + break; + } + } +} + +/** + The function waits for the boot manager timeout expires or hotkey is pressed. + + It calls PlatformBootManagerWaitCallback each second. + + @param HotkeyTriggered Input hotkey event. +**/ +VOID +BdsWait ( + IN EFI_EVENT HotkeyTriggered + ) +{ + EFI_STATUS Status; + UINT16 TimeoutRemain; + + DEBUG ((EFI_D_INFO, "[Bds]BdsWait ...Zzzzzzzzzzzz...\n")); + + TimeoutRemain = PcdGet16 (PcdPlatformBootTimeOut); + while (TimeoutRemain != 0) { + DEBUG ((EFI_D_INFO, "[Bds]BdsWait(%d)..Zzzz...\n", (UINTN) TimeoutRemain)); + PlatformBootManagerWaitCallback (TimeoutRemain); + + BdsReadKeys (); // BUGBUG: Only reading can signal HotkeyTriggered + // Can be removed after all keyboard drivers invoke callback in timer callback. + + if (HotkeyTriggered != NULL) { + Status = BdsWaitForSingleEvent (HotkeyTriggered, EFI_TIMER_PERIOD_SECONDS (1)); + if (!EFI_ERROR (Status)) { + break; + } + } else { + gBS->Stall (1000000); + } + + // + // 0xffff means waiting forever + // BDS with no hotkey provided and 0xffff as timeout will "hang" in the loop + // + if (TimeoutRemain != 0xffff) { + TimeoutRemain--; + } + } + + // + // If the platform configured a nonzero and finite time-out, and we have + // actually reached that, report 100% completion to the platform. + // + // Note that the (TimeoutRemain == 0) condition excludes + // PcdPlatformBootTimeOut=0xFFFF, and that's deliberate. + // + if (PcdGet16 (PcdPlatformBootTimeOut) != 0 && TimeoutRemain == 0) { + PlatformBootManagerWaitCallback (0); + } + DEBUG ((EFI_D_INFO, "[Bds]Exit the waiting!\n")); +} + +/** + Attempt to boot each boot option in the BootOptions array. + + @param BootOptions Input boot option array. + @param BootOptionCount Input boot option count. + @param BootManagerMenu Input boot manager menu. + + @retval TRUE Successfully boot one of the boot options. + @retval FALSE Failed boot any of the boot options. +**/ +BOOLEAN +BootBootOptions ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions, + IN UINTN BootOptionCount, + IN EFI_BOOT_MANAGER_LOAD_OPTION *BootManagerMenu OPTIONAL + ) +{ + UINTN Index; + + // + // Report Status Code to indicate BDS starts attempting booting from the UEFI BootOrder list. + // + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_ATTEMPT_BOOT_ORDER_EVENT)); + + // + // Attempt boot each boot option + // + for (Index = 0; Index < BootOptionCount; Index++) { + // + // According to EFI Specification, if a load option is not marked + // as LOAD_OPTION_ACTIVE, the boot manager will not automatically + // load the option. + // + if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) == 0) { + continue; + } + + // + // Boot#### load options with LOAD_OPTION_CATEGORY_APP are executables which are not + // part of the normal boot processing. Boot options with reserved category values will be + // ignored by the boot manager. + // + if ((BootOptions[Index].Attributes & LOAD_OPTION_CATEGORY) != LOAD_OPTION_CATEGORY_BOOT) { + continue; + } + + // + // All the driver options should have been processed since + // now boot will be performed. + // + EfiBootManagerBoot (&BootOptions[Index]); + + // + // If the boot via Boot#### returns with a status of EFI_SUCCESS, platform firmware + // supports boot manager menu, and if firmware is configured to boot in an + // interactive mode, the boot manager will stop processing the BootOrder variable and + // present a boot manager menu to the user. + // + if ((BootManagerMenu != NULL) && (BootOptions[Index].Status == EFI_SUCCESS)) { + EfiBootManagerBoot (BootManagerMenu); + break; + } + } + + return (BOOLEAN) (Index < BootOptionCount); +} + +/** + The function will load and start every Driver####, SysPrep#### or PlatformRecovery####. + + @param LoadOptions Load option array. + @param LoadOptionCount Load option count. +**/ +VOID +ProcessLoadOptions ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions, + IN UINTN LoadOptionCount + ) +{ + EFI_STATUS Status; + UINTN Index; + BOOLEAN ReconnectAll; + EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType; + + ReconnectAll = FALSE; + LoadOptionType = LoadOptionTypeMax; + + // + // Process the driver option + // + for (Index = 0; Index < LoadOptionCount; Index++) { + // + // All the load options in the array should be of the same type. + // + if (Index == 0) { + LoadOptionType = LoadOptions[Index].OptionType; + } + ASSERT (LoadOptionType == LoadOptions[Index].OptionType); + ASSERT (LoadOptionType != LoadOptionTypeBoot); + + Status = EfiBootManagerProcessLoadOption (&LoadOptions[Index]); + + // + // Status indicates whether the load option is loaded and executed + // LoadOptions[Index].Status is what the load option returns + // + if (!EFI_ERROR (Status)) { + // + // Stop processing if any PlatformRecovery#### returns success. + // + if ((LoadOptions[Index].Status == EFI_SUCCESS) && + (LoadOptionType == LoadOptionTypePlatformRecovery)) { + break; + } + + // + // Only set ReconnectAll flag when the load option executes successfully. + // + if (!EFI_ERROR (LoadOptions[Index].Status) && + (LoadOptions[Index].Attributes & LOAD_OPTION_FORCE_RECONNECT) != 0) { + ReconnectAll = TRUE; + } + } + } + + // + // If a driver load option is marked as LOAD_OPTION_FORCE_RECONNECT, + // then all of the EFI drivers in the system will be disconnected and + // reconnected after the last driver load option is processed. + // + if (ReconnectAll && LoadOptionType == LoadOptionTypeDriver) { + EfiBootManagerDisconnectAll (); + EfiBootManagerConnectAll (); + } +} + +/** + + Validate input console variable data. + + If found the device path is not a valid device path, remove the variable. + + @param VariableName Input console variable name. + +**/ +VOID +BdsFormalizeConsoleVariable ( + IN CHAR16 *VariableName + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN VariableSize; + EFI_STATUS Status; + + GetEfiGlobalVariable2 (VariableName, (VOID **) &DevicePath, &VariableSize); + if ((DevicePath != NULL) && !IsDevicePathValid (DevicePath, VariableSize)) { + Status = gRT->SetVariable ( + VariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + 0, + NULL + ); + // + // Deleting variable with current variable implementation shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + } + + if (DevicePath != NULL) { + FreePool (DevicePath); + } +} + +/** + Formalize OsIndication related variables. + + For OsIndicationsSupported, Create a BS/RT/UINT64 variable to report caps + Delete OsIndications variable if it is not NV/BS/RT UINT64. + + Item 3 is used to solve case when OS corrupts OsIndications. Here simply delete this NV variable. + + Create a boot option for BootManagerMenu if it hasn't been created yet + +**/ +VOID +BdsFormalizeOSIndicationVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINT64 OsIndicationSupport; + UINT64 OsIndication; + UINTN DataSize; + UINT32 Attributes; + EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu; + + // + // OS indicater support variable + // + Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu); + if (Status != EFI_NOT_FOUND) { + OsIndicationSupport = EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + EfiBootManagerFreeLoadOption (&BootManagerMenu); + } else { + OsIndicationSupport = 0; + } + + if (PcdGetBool (PcdPlatformRecoverySupport)) { + OsIndicationSupport |= EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY; + } + + if (PcdGetBool(PcdCapsuleOnDiskSupport)) { + OsIndicationSupport |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; + } + + Status = gRT->SetVariable ( + EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(UINT64), + &OsIndicationSupport + ); + // + // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + + // + // If OsIndications is invalid, remove it. + // Invalid case + // 1. Data size != UINT64 + // 2. OsIndication value inconsistence + // 3. OsIndication attribute inconsistence + // + OsIndication = 0; + Attributes = 0; + DataSize = sizeof(UINT64); + Status = gRT->GetVariable ( + EFI_OS_INDICATIONS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + &Attributes, + &DataSize, + &OsIndication + ); + if (Status == EFI_NOT_FOUND) { + return; + } + + if ((DataSize != sizeof (OsIndication)) || + ((OsIndication & ~OsIndicationSupport) != 0) || + (Attributes != (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE)) + ){ + + DEBUG ((EFI_D_ERROR, "[Bds] Unformalized OsIndications variable exists. Delete it\n")); + Status = gRT->SetVariable ( + EFI_OS_INDICATIONS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + 0, + 0, + NULL + ); + // + // Deleting variable with current variable implementation shouldn't fail. + // + ASSERT_EFI_ERROR(Status); + } +} + +/** + + Validate variables. + +**/ +VOID +BdsFormalizeEfiGlobalVariable ( + VOID + ) +{ + // + // Validate Console variable. + // + BdsFormalizeConsoleVariable (EFI_CON_IN_VARIABLE_NAME); + BdsFormalizeConsoleVariable (EFI_CON_OUT_VARIABLE_NAME); + BdsFormalizeConsoleVariable (EFI_ERR_OUT_VARIABLE_NAME); + + // + // Validate OSIndication related variable. + // + BdsFormalizeOSIndicationVariable (); +} + +/** + + Service routine for BdsInstance->Entry(). Devices are connected, the + consoles are initialized, and the boot options are tried. + + @param This Protocol Instance structure. + +**/ +VOID +EFIAPI +BdsEntry ( + IN EFI_BDS_ARCH_PROTOCOL *This + ) +{ + EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions; + UINTN LoadOptionCount; + CHAR16 *FirmwareVendor; + EFI_EVENT HotkeyTriggered; + UINT64 OsIndication; + UINTN DataSize; + EFI_STATUS Status; + UINT32 BootOptionSupport; + UINT16 BootTimeOut; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + UINTN Index; + EFI_BOOT_MANAGER_LOAD_OPTION LoadOption; + UINT16 *BootNext; + CHAR16 BootNextVariableName[sizeof ("Boot####")]; + EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu; + BOOLEAN BootFwUi; + BOOLEAN PlatformRecovery; + BOOLEAN BootSuccess; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_STATUS BootManagerMenuStatus; + EFI_BOOT_MANAGER_LOAD_OPTION PlatformDefaultBootOption; + + HotkeyTriggered = NULL; + Status = EFI_SUCCESS; + BootSuccess = FALSE; + + // + // Insert the performance probe + // + PERF_CROSSMODULE_END("DXE"); + PERF_CROSSMODULE_BEGIN("BDS"); + DEBUG ((EFI_D_INFO, "[Bds] Entry...\n")); + + // + // Fill in FirmwareVendor and FirmwareRevision from PCDs + // + FirmwareVendor = (CHAR16 *) PcdGetPtr (PcdFirmwareVendor); + gST->FirmwareVendor = AllocateRuntimeCopyPool (StrSize (FirmwareVendor), FirmwareVendor); + ASSERT (gST->FirmwareVendor != NULL); + gST->FirmwareRevision = PcdGet32 (PcdFirmwareRevision); + + // + // Fixup Tasble CRC after we updated Firmware Vendor and Revision + // + gST->Hdr.CRC32 = 0; + gBS->CalculateCrc32 ((VOID *) gST, sizeof (EFI_SYSTEM_TABLE), &gST->Hdr.CRC32); + + // + // Validate Variable. + // + BdsFormalizeEfiGlobalVariable (); + + // + // Mark the read-only variables if the Variable Lock protocol exists + // + Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock); + DEBUG ((EFI_D_INFO, "[BdsDxe] Locate Variable Lock protocol - %r\n", Status)); + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) { + Status = VariableLock->RequestToLock (VariableLock, mReadOnlyVariables[Index], &gEfiGlobalVariableGuid); + ASSERT_EFI_ERROR (Status); + } + } + + InitializeHwErrRecSupport (); + + // + // Initialize L"Timeout" EFI global variable. + // + BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut); + if (BootTimeOut != 0xFFFF) { + // + // If time out value equal 0xFFFF, no need set to 0xFFFF to variable area because UEFI specification + // define same behavior between no value or 0xFFFF value for L"Timeout". + // + BdsDxeSetVariableAndReportStatusCodeOnError ( + EFI_TIME_OUT_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (UINT16), + &BootTimeOut + ); + } + + // + // Initialize L"BootOptionSupport" EFI global variable. + // Lazy-ConIn implictly disables BDS hotkey. + // + BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP | EFI_BOOT_OPTION_SUPPORT_SYSPREP; + if (!PcdGetBool (PcdConInConnectOnDemand)) { + BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY; + SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3); + } + Status = gRT->SetVariable ( + EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (BootOptionSupport), + &BootOptionSupport + ); + // + // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + + // + // Cache the "BootNext" NV variable before calling any PlatformBootManagerLib APIs + // This could avoid the "BootNext" set by PlatformBootManagerLib be consumed in this boot. + // + GetEfiGlobalVariable2 (EFI_BOOT_NEXT_VARIABLE_NAME, (VOID **) &BootNext, &DataSize); + if (DataSize != sizeof (UINT16)) { + if (BootNext != NULL) { + FreePool (BootNext); + } + BootNext = NULL; + } + + // + // Initialize the platform language variables + // + InitializeLanguage (TRUE); + + FilePath = FileDevicePath (NULL, EFI_REMOVABLE_MEDIA_FILE_NAME); + if (FilePath == NULL) { + DEBUG ((DEBUG_ERROR, "Fail to allocate memory for default boot file path. Unable to boot.\n")); + CpuDeadLoop (); + } + Status = EfiBootManagerInitializeLoadOption ( + &PlatformDefaultBootOption, + LoadOptionNumberUnassigned, + LoadOptionTypePlatformRecovery, + LOAD_OPTION_ACTIVE, + L"Default PlatformRecovery", + FilePath, + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + + // + // System firmware must include a PlatformRecovery#### variable specifying + // a short-form File Path Media Device Path containing the platform default + // file path for removable media if the platform supports Platform Recovery. + // + if (PcdGetBool (PcdPlatformRecoverySupport)) { + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery); + if (EfiBootManagerFindLoadOption (&PlatformDefaultBootOption, LoadOptions, LoadOptionCount) == -1) { + for (Index = 0; Index < LoadOptionCount; Index++) { + // + // The PlatformRecovery#### options are sorted by OptionNumber. + // Find the the smallest unused number as the new OptionNumber. + // + if (LoadOptions[Index].OptionNumber != Index) { + break; + } + } + PlatformDefaultBootOption.OptionNumber = Index; + Status = EfiBootManagerLoadOptionToVariable (&PlatformDefaultBootOption); + ASSERT_EFI_ERROR (Status); + } + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); + } + FreePool (FilePath); + + // + // Report Status Code to indicate connecting drivers will happen + // + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS) + ); + + // + // Initialize ConnectConIn event before calling platform code. + // + if (PcdGetBool (PcdConInConnectOnDemand)) { + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + BdsDxeOnConnectConInCallBack, + NULL, + &gConnectConInEventGuid, + &gConnectConInEvent + ); + if (EFI_ERROR (Status)) { + gConnectConInEvent = NULL; + } + } + + // + // 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. + // + PERF_INMODULE_BEGIN("PlatformBootManagerBeforeConsole"); + PlatformBootManagerBeforeConsole (); + PERF_INMODULE_END("PlatformBootManagerBeforeConsole"); + + // + // Initialize hotkey service + // + EfiBootManagerStartHotkeyService (&HotkeyTriggered); + + // + // Execute Driver Options + // + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeDriver); + ProcessLoadOptions (LoadOptions, LoadOptionCount); + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); + + // + // Connect consoles + // + PERF_INMODULE_BEGIN("EfiBootManagerConnectAllDefaultConsoles"); + if (PcdGetBool (PcdConInConnectOnDemand)) { + EfiBootManagerConnectConsoleVariable (ConOut); + EfiBootManagerConnectConsoleVariable (ErrOut); + // + // Do not connect ConIn devices when lazy ConIn feature is ON. + // + } else { + EfiBootManagerConnectAllDefaultConsoles (); + } + PERF_INMODULE_END("EfiBootManagerConnectAllDefaultConsoles"); + + // + // 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 + // + PERF_INMODULE_BEGIN("PlatformBootManagerAfterConsole"); + PlatformBootManagerAfterConsole (); + PERF_INMODULE_END("PlatformBootManagerAfterConsole"); + + // + // If any component set PcdTestKeyUsed to TRUE because use of a test key + // was detected, then display a warning message on the debug log and the console + // + if (PcdGetBool (PcdTestKeyUsed)) { + DEBUG ((DEBUG_ERROR, "**********************************\n")); + DEBUG ((DEBUG_ERROR, "** WARNING: Test Key is used. **\n")); + DEBUG ((DEBUG_ERROR, "**********************************\n")); + Print (L"** WARNING: Test Key is used. **\n"); + } + + // + // Boot to Boot Manager Menu when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot + // + DataSize = sizeof (UINT64); + Status = gRT->GetVariable ( + EFI_OS_INDICATIONS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + NULL, + &DataSize, + &OsIndication + ); + if (EFI_ERROR (Status)) { + OsIndication = 0; + } + + DEBUG_CODE ( + EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType; + DEBUG ((EFI_D_INFO, "[Bds]OsIndication: %016x\n", OsIndication)); + DEBUG ((EFI_D_INFO, "[Bds]=============Begin Load Options Dumping ...=============\n")); + for (LoadOptionType = 0; LoadOptionType < LoadOptionTypeMax; LoadOptionType++) { + DEBUG (( + EFI_D_INFO, " %s Options:\n", + mBdsLoadOptionName[LoadOptionType] + )); + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionType); + for (Index = 0; Index < LoadOptionCount; Index++) { + DEBUG (( + EFI_D_INFO, " %s%04x: %s \t\t 0x%04x\n", + mBdsLoadOptionName[LoadOptionType], + LoadOptions[Index].OptionNumber, + LoadOptions[Index].Description, + LoadOptions[Index].Attributes + )); + } + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); + } + DEBUG ((EFI_D_INFO, "[Bds]=============End Load Options Dumping=============\n")); + ); + + // + // BootManagerMenu doesn't contain the correct information when return status is EFI_NOT_FOUND. + // + BootManagerMenuStatus = EfiBootManagerGetBootManagerMenu (&BootManagerMenu); + + BootFwUi = (BOOLEAN) ((OsIndication & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0); + PlatformRecovery = (BOOLEAN) ((OsIndication & EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY) != 0); + // + // Clear EFI_OS_INDICATIONS_BOOT_TO_FW_UI to acknowledge OS + // + if (BootFwUi || PlatformRecovery) { + OsIndication &= ~((UINT64) (EFI_OS_INDICATIONS_BOOT_TO_FW_UI | EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY)); + Status = gRT->SetVariable ( + EFI_OS_INDICATIONS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof(UINT64), + &OsIndication + ); + // + // Changing the content without increasing its size with current variable implementation shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + } + + // + // Launch Boot Manager Menu directly when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot + // + if (BootFwUi && (BootManagerMenuStatus != EFI_NOT_FOUND)) { + // + // Follow generic rule, Call BdsDxeOnConnectConInCallBack to connect ConIn before enter UI + // + if (PcdGetBool (PcdConInConnectOnDemand)) { + BdsDxeOnConnectConInCallBack (NULL, NULL); + } + + // + // Directly enter the setup page. + // + EfiBootManagerBoot (&BootManagerMenu); + } + + if (!PlatformRecovery) { + // + // Execute SysPrep#### + // + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeSysPrep); + ProcessLoadOptions (LoadOptions, LoadOptionCount); + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); + + // + // Execute Key#### + // + PERF_INMODULE_BEGIN ("BdsWait"); + BdsWait (HotkeyTriggered); + PERF_INMODULE_END ("BdsWait"); + // + // BdsReadKeys() can be removed after all keyboard drivers invoke callback in timer callback. + // + BdsReadKeys (); + + EfiBootManagerHotkeyBoot (); + + if (BootNext != NULL) { + // + // Delete "BootNext" NV variable before transferring control to it to prevent loops. + // + Status = gRT->SetVariable ( + EFI_BOOT_NEXT_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + 0, + 0, + NULL + ); + // + // Deleting NV variable shouldn't fail unless it doesn't exist. + // + ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND); + + // + // Boot to "BootNext" + // + UnicodeSPrint (BootNextVariableName, sizeof (BootNextVariableName), L"Boot%04x", *BootNext); + Status = EfiBootManagerVariableToLoadOption (BootNextVariableName, &LoadOption); + if (!EFI_ERROR (Status)) { + EfiBootManagerBoot (&LoadOption); + EfiBootManagerFreeLoadOption (&LoadOption); + if ((LoadOption.Status == EFI_SUCCESS) && + (BootManagerMenuStatus != EFI_NOT_FOUND) && + (LoadOption.OptionNumber != BootManagerMenu.OptionNumber)) { + // + // Boot to Boot Manager Menu upon EFI_SUCCESS + // Exception: Do not boot again when the BootNext points to Boot Manager Menu. + // + EfiBootManagerBoot (&BootManagerMenu); + } + } + } + + do { + // + // Retry to boot if any of the boot succeeds + // + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeBoot); + BootSuccess = BootBootOptions (LoadOptions, LoadOptionCount, (BootManagerMenuStatus != EFI_NOT_FOUND) ? &BootManagerMenu : NULL); + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); + } while (BootSuccess); + } + + if (BootManagerMenuStatus != EFI_NOT_FOUND) { + EfiBootManagerFreeLoadOption (&BootManagerMenu); + } + + if (!BootSuccess) { + if (PcdGetBool (PcdPlatformRecoverySupport)) { + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery); + ProcessLoadOptions (LoadOptions, LoadOptionCount); + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); + } else { + // + // When platform recovery is not enabled, still boot to platform default file path. + // + EfiBootManagerProcessLoadOption (&PlatformDefaultBootOption); + } + } + EfiBootManagerFreeLoadOption (&PlatformDefaultBootOption); + + DEBUG ((EFI_D_ERROR, "[Bds] Unable to boot!\n")); + PlatformBootManagerUnableToBoot (); + CpuDeadLoop (); +} + +/** + Set the variable and report the error through status code upon failure. + + @param VariableName A Null-terminated string that is the name of the vendor's variable. + Each VariableName is unique for each VendorGuid. VariableName must + contain 1 or more characters. If VariableName is an empty string, + then EFI_INVALID_PARAMETER is returned. + @param VendorGuid A unique identifier for the vendor. + @param Attributes Attributes bitmask to set for the variable. + @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, + or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero + causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is + set, then a SetVariable() call with a DataSize of zero will not cause any change to + the variable value (the timestamp associated with the variable may be updated however + even if no new data value is provided,see the description of the + EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not + be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). + @param Data The contents for the variable. + + @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as + defined by the Attributes. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the + DataSize exceeds the maximum allowed. + @retval EFI_INVALID_PARAMETER VariableName is an empty string. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. + @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS + being set, but the AuthInfo does NOT pass the validation check carried out by the firmware. + + @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. +**/ +EFI_STATUS +BdsDxeSetVariableAndReportStatusCodeOnError ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + EDKII_SET_VARIABLE_STATUS *SetVariableStatus; + UINTN NameSize; + + Status = gRT->SetVariable ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + if (EFI_ERROR (Status)) { + NameSize = StrSize (VariableName); + SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize); + if (SetVariableStatus != NULL) { + CopyGuid (&SetVariableStatus->Guid, VendorGuid); + SetVariableStatus->NameSize = NameSize; + SetVariableStatus->DataSize = DataSize; + SetVariableStatus->SetStatus = Status; + SetVariableStatus->Attributes = Attributes; + CopyMem (SetVariableStatus + 1, VariableName, NameSize); + CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data, DataSize); + + REPORT_STATUS_CODE_EX ( + EFI_ERROR_CODE, + PcdGet32 (PcdErrorCodeSetVariable), + 0, + NULL, + &gEdkiiStatusCodeDataTypeVariableGuid, + SetVariableStatus, + sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize + ); + + FreePool (SetVariableStatus); + } + } + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.c b/roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.c new file mode 100644 index 000000000..6e7411977 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.c @@ -0,0 +1,42 @@ +/** @file + Set the level of support for Hardware Error Record Persistence that is + implemented by the platform. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HwErrRecSupport.h" + +/** + Set the HwErrRecSupport variable contains a binary UINT16 that supplies the + level of support for Hardware Error Record Persistence that is implemented + by the platform. + +**/ +VOID +InitializeHwErrRecSupport ( + VOID + ) +{ + EFI_STATUS Status; + UINT16 HardwareErrorRecordLevel; + + HardwareErrorRecordLevel = PcdGet16 (PcdHardwareErrorRecordLevel); + + if (HardwareErrorRecordLevel != 0) { + // + // If level value equal 0, no need set to 0 to variable area because UEFI specification + // define same behavior between no value or 0 value for L"HwErrRecSupport". + // + Status = gRT->SetVariable ( + L"HwErrRecSupport", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (UINT16), + &HardwareErrorRecordLevel + ); + ASSERT_EFI_ERROR(Status); + } +} diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.h b/roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.h new file mode 100644 index 000000000..82c0fd38c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/HwErrRecSupport.h @@ -0,0 +1,26 @@ +/** @file + Set the level of support for Hardware Error Record Persistence that is + implemented by the platform. + +Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _HW_ERR_REC_SUPPORT_H_ +#define _HW_ERR_REC_SUPPORT_H_ + +#include "Bds.h" + +/** + Set the HwErrRecSupport variable contains a binary UINT16 that supplies the + level of support for Hardware Error Record Persistence that is implemented + by the platform. + +**/ +VOID +InitializeHwErrRecSupport ( + VOID + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.c b/roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.c new file mode 100644 index 000000000..03aa055bd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.c @@ -0,0 +1,196 @@ +/** @file + Language settings + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Bds.h" +#define ISO_639_2_ENTRY_SIZE 3 + +/** + Check if lang is in supported language codes according to language string. + + This code is used to check if lang is in in supported language codes. It can handle + RFC4646 and ISO639 language tags. + In ISO639 language tags, take 3-characters as a delimitation to find matched string. + In RFC4646 language tags, take semicolon as a delimitation to find matched string. + + For example: + SupportedLang = "engfraengfra" + Iso639Language = TRUE + Lang = "eng", the return value is "TRUE", or + Lang = "chs", the return value is "FALSE". + Another example: + SupportedLang = "en;fr;en-US;fr-FR" + Iso639Language = FALSE + Lang = "en", the return value is "TRUE", or + Lang = "zh", the return value is "FALSE". + + @param SupportedLang Platform supported language codes. + @param Lang Configured language. + @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646. + + @retval TRUE lang is in supported language codes. + @retval FALSE lang is not in supported language codes. + +**/ +BOOLEAN +IsLangInSupportedLangCodes( + IN CHAR8 *SupportedLang, + IN CHAR8 *Lang, + IN BOOLEAN Iso639Language + ) +{ + UINTN Index; + UINTN CompareLength; + UINTN LanguageLength; + + if (Iso639Language) { + CompareLength = ISO_639_2_ENTRY_SIZE; + for (Index = 0; Index < AsciiStrLen (SupportedLang); Index += CompareLength) { + if (AsciiStrnCmp (Lang, SupportedLang + Index, CompareLength) == 0) { + // + // Successfully find the Lang string in SupportedLang string. + // + return TRUE; + } + } + return FALSE; + } else { + // + // Compare RFC4646 language code + // + for (LanguageLength = 0; Lang[LanguageLength] != '\0'; LanguageLength++); + + for (; *SupportedLang != '\0'; SupportedLang += CompareLength) { + // + // Skip ';' characters in SupportedLang + // + for (; *SupportedLang != '\0' && *SupportedLang == ';'; SupportedLang++); + // + // Determine the length of the next language code in SupportedLang + // + for (CompareLength = 0; SupportedLang[CompareLength] != '\0' && SupportedLang[CompareLength] != ';'; CompareLength++); + + if ((CompareLength == LanguageLength) && + (AsciiStrnCmp (Lang, SupportedLang, CompareLength) == 0)) { + // + // Successfully find the Lang string in SupportedLang string. + // + return TRUE; + } + } + return FALSE; + } +} + +/** + Initialize Lang or PlatformLang variable, if Lang or PlatformLang variable is not found, + or it has been set to an unsupported value(not one of platform supported language codes), + set the default language code to it. + + @param LangName Language name, L"Lang" or L"PlatformLang". + @param SupportedLang Platform supported language codes. + @param DefaultLang Default language code. + @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646, + TRUE for L"Lang" LangName or FALSE for L"PlatformLang" LangName. + +**/ +VOID +InitializeLangVariable ( + IN CHAR16 *LangName, + IN CHAR8 *SupportedLang, + IN CHAR8 *DefaultLang, + IN BOOLEAN Iso639Language + ) +{ + CHAR8 *Lang; + + // + // Find current Lang or PlatformLang from EFI Variable. + // + GetEfiGlobalVariable2 (LangName, (VOID **) &Lang, NULL); + + // + // If Lang or PlatformLang variable is not found, + // or it has been set to an unsupported value(not one of the supported language codes), + // set the default language code to it. + // + if ((Lang == NULL) || !IsLangInSupportedLangCodes (SupportedLang, Lang, Iso639Language)) { + // + // The default language code should be one of the supported language codes. + // + ASSERT (IsLangInSupportedLangCodes (SupportedLang, DefaultLang, Iso639Language)); + BdsDxeSetVariableAndReportStatusCodeOnError ( + LangName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + AsciiStrSize (DefaultLang), + DefaultLang + ); + } + + if (Lang != NULL) { + FreePool (Lang); + } +} + +/** + Determine the current language that will be used + based on language related EFI Variables. + + @param LangCodesSettingRequired - If required to set LangCodes variable + +**/ +VOID +InitializeLanguage ( + BOOLEAN LangCodesSettingRequired + ) +{ + EFI_STATUS Status; + CHAR8 *LangCodes; + CHAR8 *PlatformLangCodes; + + LangCodes = (CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultLangCodes); + PlatformLangCodes = (CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultPlatformLangCodes); + if (LangCodesSettingRequired) { + if (!FeaturePcdGet (PcdUefiVariableDefaultLangDeprecate)) { + // + // UEFI 2.1 depricated this variable so we support turning it off + // + Status = gRT->SetVariable ( + L"LangCodes", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + AsciiStrSize (LangCodes), + LangCodes + ); + // + // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail. + // + ASSERT_EFI_ERROR(Status); + } + + Status = gRT->SetVariable ( + L"PlatformLangCodes", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + AsciiStrSize (PlatformLangCodes), + PlatformLangCodes + ); + // + // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail. + // + ASSERT_EFI_ERROR(Status); + } + + if (!FeaturePcdGet (PcdUefiVariableDefaultLangDeprecate)) { + // + // UEFI 2.1 depricated this variable so we support turning it off + // + InitializeLangVariable (L"Lang", LangCodes, (CHAR8 *) PcdGetPtr (PcdUefiVariableDefaultLang), TRUE); + } + InitializeLangVariable (L"PlatformLang", PlatformLangCodes, (CHAR8 *) PcdGetPtr (PcdUefiVariableDefaultPlatformLang), FALSE); +} diff --git a/roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.h b/roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.h new file mode 100644 index 000000000..647730ff3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BdsDxe/Language.h @@ -0,0 +1,24 @@ +/** @file + Language setting + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LANGUAGE_H_ +#define _LANGUAGE_H_ + +/** + Determine the current language that will be used + based on language related EFI Variables. + + @param LangCodesSettingRequired If required to set LangCode variable + +**/ +VOID +InitializeLanguage ( + BOOLEAN LangCodesSettingRequired + ); + +#endif // _LANGUAGE_H_ diff --git a/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.c b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.c new file mode 100644 index 000000000..ff45e4f8e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.c @@ -0,0 +1,281 @@ +/** @file + This module produces Boot Manager Policy protocol. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CHAR16 mNetworkDeviceList[] = L"_NDL"; + +/** + Connect all the system drivers to controllers and create the network device list in NV storage. + + @retval EFI_SUCCESS Network devices are connected. + @retval EFI_DEVICE_ERROR No network device is connected. + +**/ +EFI_STATUS +ConnectAllAndCreateNetworkDeviceList ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN HandleCount; + EFI_DEVICE_PATH_PROTOCOL *SingleDevice; + EFI_DEVICE_PATH_PROTOCOL *Devices; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + + EfiBootManagerConnectAll (); + + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiManagedNetworkServiceBindingProtocolGuid, NULL, &HandleCount, &Handles); + if (EFI_ERROR (Status)) { + Handles = NULL; + HandleCount = 0; + } + + Devices = NULL; + while (HandleCount-- != 0) { + Status = gBS->HandleProtocol (Handles[HandleCount], &gEfiDevicePathProtocolGuid, (VOID **) &SingleDevice); + if (EFI_ERROR (Status) || (SingleDevice == NULL)) { + continue; + } + TempDevicePath = Devices; + Devices = AppendDevicePathInstance (Devices, SingleDevice); + if (TempDevicePath != NULL) { + FreePool (TempDevicePath); + } + } + + if (Devices != NULL) { + Status = gRT->SetVariable ( + mNetworkDeviceList, + &gEfiCallerIdGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + GetDevicePathSize (Devices), + Devices + ); + // + // Fails to save the network device list to NV storage is not a fatal error. + // Only impact is performance. + // + FreePool (Devices); + } + + return (Devices == NULL) ? EFI_DEVICE_ERROR : EFI_SUCCESS; +} + +/** + Connect the network devices. + + @retval EFI_SUCCESS At least one network device was connected. + @retval EFI_DEVICE_ERROR Network devices were not connected due to an error. +**/ +EFI_STATUS +ConnectNetwork ( + VOID + ) +{ + EFI_STATUS Status; + BOOLEAN OneConnected; + EFI_DEVICE_PATH_PROTOCOL *Devices; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EFI_DEVICE_PATH_PROTOCOL *SingleDevice; + UINTN Size; + + OneConnected = FALSE; + GetVariable2 (mNetworkDeviceList, &gEfiCallerIdGuid, (VOID **) &Devices, NULL); + TempDevicePath = Devices; + while (TempDevicePath != NULL) { + SingleDevice = GetNextDevicePathInstance (&TempDevicePath, &Size); + Status = EfiBootManagerConnectDevicePath (SingleDevice, NULL); + if (!EFI_ERROR (Status)) { + OneConnected = TRUE; + } + FreePool (SingleDevice); + } + if (Devices != NULL) { + FreePool (Devices); + } + + if (OneConnected) { + return EFI_SUCCESS; + } else { + // + // Cached network devices list doesn't exist or is NOT valid. + // + return ConnectAllAndCreateNetworkDeviceList (); + } +} + +/** + Connect a device path following the platforms EFI Boot Manager policy. + + The ConnectDevicePath() function allows the caller to connect a DevicePath using the + same policy as the EFI Boot Manger. + + @param[in] This A pointer to the EFI_BOOT_MANAGER_POLICY_PROTOCOL instance. + @param[in] DevicePath Points to the start of the EFI device path to connect. + If DevicePath is NULL then all the controllers in the + system will be connected using the platforms EFI Boot + Manager policy. + @param[in] Recursive If TRUE, then ConnectController() is called recursively + until the entire tree of controllers below the + controller specified by DevicePath have been created. + If FALSE, then the tree of controllers is only expanded + one level. If DevicePath is NULL then Recursive is ignored. + + @retval EFI_SUCCESS The DevicePath was connected. + @retval EFI_NOT_FOUND The DevicePath was not found. + @retval EFI_NOT_FOUND No driver was connected to DevicePath. + @retval EFI_SECURITY_VIOLATION The user has no permission to start UEFI device + drivers on the DevicePath. + @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION. +**/ +EFI_STATUS +EFIAPI +BootManagerPolicyConnectDevicePath ( + IN EFI_BOOT_MANAGER_POLICY_PROTOCOL *This, + IN EFI_DEVICE_PATH *DevicePath, + IN BOOLEAN Recursive + ) +{ + EFI_STATUS Status; + EFI_HANDLE Controller; + + if (EfiGetCurrentTpl () != TPL_APPLICATION) { + return EFI_UNSUPPORTED; + } + + if (DevicePath == NULL) { + EfiBootManagerConnectAll (); + return EFI_SUCCESS; + } + + if (Recursive) { + Status = EfiBootManagerConnectDevicePath (DevicePath, NULL); + } else { + Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &DevicePath, &Controller); + if (!EFI_ERROR (Status)) { + Status = gBS->ConnectController (Controller, NULL, DevicePath, FALSE); + } + } + return Status; +} +/** + Connect a class of devices using the platform Boot Manager policy. + + The ConnectDeviceClass() function allows the caller to request that the Boot + Manager connect a class of devices. + + If Class is EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID then the Boot Manager will + use platform policy to connect consoles. Some platforms may restrict the + number of consoles connected as they attempt to fast boot, and calling + ConnectDeviceClass() with a Class value of EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID + must connect the set of consoles that follow the Boot Manager platform policy, + and the EFI_SIMPLE_TEXT_INPUT_PROTOCOL, EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL, and + the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL are produced on the connected handles. + The Boot Manager may restrict which consoles get connect due to platform policy, + for example a security policy may require that a given console is not connected. + + If Class is EFI_BOOT_MANAGER_POLICY_NETWORK_GUID then the Boot Manager will + connect the protocols the platforms supports for UEFI general purpose network + applications on one or more handles. If more than one network controller is + available a platform will connect, one, many, or all of the networks based + on platform policy. Connecting UEFI networking protocols, like EFI_DHCP4_PROTOCOL, + does not establish connections on the network. The UEFI general purpose network + application that called ConnectDeviceClass() may need to use the published + protocols to establish the network connection. The Boot Manager can optionally + have a policy to establish a network connection. + + If Class is EFI_BOOT_MANAGER_POLICY_CONNECT_ALL_GUID then the Boot Manager + will connect all UEFI drivers using the UEFI Boot Service + EFI_BOOT_SERVICES.ConnectController(). If the Boot Manager has policy + associated with connect all UEFI drivers this policy will be used. + + A platform can also define platform specific Class values as a properly generated + EFI_GUID would never conflict with this specification. + + @param[in] This A pointer to the EFI_BOOT_MANAGER_POLICY_PROTOCOL instance. + @param[in] Class A pointer to an EFI_GUID that represents a class of devices + that will be connected using the Boot Mangers platform policy. + + @retval EFI_SUCCESS At least one devices of the Class was connected. + @retval EFI_DEVICE_ERROR Devices were not connected due to an error. + @retval EFI_NOT_FOUND The Class is not supported by the platform. + @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION. +**/ +EFI_STATUS +EFIAPI +BootManagerPolicyConnectDeviceClass ( + IN EFI_BOOT_MANAGER_POLICY_PROTOCOL *This, + IN EFI_GUID *Class + ) +{ + if (EfiGetCurrentTpl () != TPL_APPLICATION) { + return EFI_UNSUPPORTED; + } + + if (CompareGuid (Class, &gEfiBootManagerPolicyConnectAllGuid)) { + ConnectAllAndCreateNetworkDeviceList (); + return EFI_SUCCESS; + } + + if (CompareGuid (Class, &gEfiBootManagerPolicyConsoleGuid)) { + return EfiBootManagerConnectAllDefaultConsoles (); + } + + if (CompareGuid (Class, &gEfiBootManagerPolicyNetworkGuid)) { + return ConnectNetwork (); + } + + return EFI_NOT_FOUND; +} + +EFI_BOOT_MANAGER_POLICY_PROTOCOL mBootManagerPolicy = { + EFI_BOOT_MANAGER_POLICY_PROTOCOL_REVISION, + BootManagerPolicyConnectDevicePath, + BootManagerPolicyConnectDeviceClass +}; + +/** + Install Boot Manager Policy Protocol. + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCEESS The Boot Manager Policy protocol is successfully installed. + @retval Other Return status from gBS->InstallMultipleProtocolInterfaces(). + +**/ +EFI_STATUS +EFIAPI +BootManagerPolicyInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_HANDLE Handle; + + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiBootManagerPolicyProtocolGuid); + + Handle = NULL; + return gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiBootManagerPolicyProtocolGuid, &mBootManagerPolicy, + NULL + ); +} diff --git a/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.inf b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.inf new file mode 100644 index 000000000..3e4f60764 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.inf @@ -0,0 +1,56 @@ +## @file +# This module produces Boot Manager Policy protocol. +# +# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BootManagerPolicyDxe + MODULE_UNI_FILE = BootManagerPolicyDxe.uni + FILE_GUID = E622443C-284E-4b47-A984-FD66B482DAC0 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = BootManagerPolicyInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + BootManagerPolicyDxe.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseMemoryLib + MemoryAllocationLib + UefiLib + DevicePathLib + DebugLib + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiBootManagerLib + +[Guids] + gEfiBootManagerPolicyConnectAllGuid ## CONSUMES ## GUID + gEfiBootManagerPolicyNetworkGuid ## CONSUMES ## GUID + gEfiBootManagerPolicyConsoleGuid ## CONSUMES ## GUID + +[Protocols] + gEfiManagedNetworkServiceBindingProtocolGuid ## CONSUMES + gEfiBootManagerPolicyProtocolGuid ## PRODUCES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + BootManagerPolicyDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.uni b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.uni new file mode 100644 index 000000000..8949cd349 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.uni @@ -0,0 +1,13 @@ +// /** @file +// This module produces Boot Manager Policy protocol. +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "This module produces Boot Manager Policy protocol" + +#string STR_MODULE_DESCRIPTION #language en-US "This module produces Boot Manager Policy protocol, which is used by EFI Applications to request the UEFI Boot Manager to connect devices using platform policy." + diff --git a/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxeExtra.uni new file mode 100644 index 000000000..fba87555f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// This module produces Boot Manager Policy protocol. +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Boot Manager Policy DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c new file mode 100644 index 000000000..e9b125008 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c @@ -0,0 +1,437 @@ +/** @file + Recovery module. + + Caution: This module requires additional review when modified. + This module will have external input - Capsule-on-Disk Temp Relocation image. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + RetrieveRelocatedCapsule() will receive untrusted input and do basic validation. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// The package level header files this module uses +// +#include +#include + +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +#include +#include +#include +#include + +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Loads a DXE capsule from some media into memory and updates the HOB table + with the DXE firmware volume information. + + @param[in] PeiServices General-purpose services that are available to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadCapsuleOnDisk ( + IN EFI_PEI_SERVICES **PeiServices, + IN EDKII_PEI_CAPSULE_ON_DISK_PPI *This + ); + +static EDKII_PEI_CAPSULE_ON_DISK_PPI mCapsuleOnDiskPpi = { + LoadCapsuleOnDisk +}; + +static EFI_PEI_PPI_DESCRIPTOR mCapsuleOnDiskPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiPeiCapsuleOnDiskPpiGuid, + &mCapsuleOnDiskPpi +}; + +/** + Determine if capsule comes from memory by checking Capsule PPI. + + @param[in] PeiServices General purpose services available to every PEIM. + + @retval TRUE Capsule comes from memory. + @retval FALSE No capsule comes from memory. + +**/ +static +BOOLEAN +CheckCapsuleFromRam ( + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + PEI_CAPSULE_PPI *Capsule; + + Status = PeiServicesLocatePpi ( + &gEfiPeiCapsulePpiGuid, + 0, + NULL, + (VOID **) &Capsule + ); + if (!EFI_ERROR(Status)) { + Status = Capsule->CheckCapsuleUpdate ((EFI_PEI_SERVICES **)PeiServices); + if (!EFI_ERROR(Status)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Determine if it is a Capsule On Disk mode. + + @retval TRUE Capsule On Disk mode. + @retval FALSE Not capsule On Disk mode. + +**/ +static +BOOLEAN +IsCapsuleOnDiskMode ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Size; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + BOOLEAN CodRelocInfo; + + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **) &PPIVariableServices + ); + ASSERT_EFI_ERROR (Status); + + Size = sizeof (BOOLEAN); + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + COD_RELOCATION_INFO_VAR_NAME, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + &CodRelocInfo + ); + + if (EFI_ERROR (Status) || Size != sizeof(BOOLEAN) || !CodRelocInfo) { + DEBUG (( DEBUG_ERROR, "Error Get CodRelocationInfo variable %r!\n", Status)); + return FALSE; + } + + return TRUE; +} + +/** + Gets capsule images from relocated capsule buffer. + Create Capsule hob for each Capsule. + + Caution: This function may receive untrusted input. + Capsule-on-Disk Temp Relocation image is external input, so this function + will validate Capsule-on-Disk Temp Relocation image to make sure the content + is read within the buffer. + + @param[in] RelocCapsuleBuf Buffer pointer to the relocated capsule. + @param[in] RelocCapsuleTotalSize Total size of the relocated capsule. + + @retval EFI_SUCCESS Succeed to get capsules and create hob. + @retval Others Fail to get capsules and create hob. + +**/ +static +EFI_STATUS +RetrieveRelocatedCapsule ( + IN UINT8 *RelocCapsuleBuf, + IN UINTN RelocCapsuleTotalSize + ) +{ + UINTN Index; + UINT8 *CapsuleDataBufEnd; + UINT8 *CapsulePtr; + UINT32 CapsuleSize; + UINT64 TotalImageSize; + UINTN CapsuleNum; + + // + // Temp file contains at least 2 capsule (including 1 capsule name capsule) & 1 UINT64 + // + if (RelocCapsuleTotalSize < sizeof(UINT64) + sizeof(EFI_CAPSULE_HEADER) * 2) { + return EFI_INVALID_PARAMETER; + } + + CopyMem(&TotalImageSize, RelocCapsuleBuf, sizeof(UINT64)); + + DEBUG ((DEBUG_INFO, "ProcessRelocatedCapsule CapsuleBuf %x TotalCapSize %lx\n", + RelocCapsuleBuf, TotalImageSize)); + + RelocCapsuleBuf += sizeof(UINT64); + + // + // TempCaspule file length check + // + if (MAX_ADDRESS - TotalImageSize <= sizeof(UINT64) || + (UINT64)RelocCapsuleTotalSize != TotalImageSize + sizeof(UINT64) || + (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)RelocCapsuleBuf) <= TotalImageSize) { + return EFI_INVALID_PARAMETER; + } + + CapsuleDataBufEnd = RelocCapsuleBuf + TotalImageSize; + + // + // TempCapsule file integrity check over Capsule Header to ensure no data corruption in NV Var & Relocation storage + // + CapsulePtr = RelocCapsuleBuf; + CapsuleNum = 0; + + while (CapsulePtr < CapsuleDataBufEnd) { + if ((CapsuleDataBufEnd - CapsulePtr) < sizeof(EFI_CAPSULE_HEADER) || + ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize < sizeof(EFI_CAPSULE_HEADER) || + (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)CapsulePtr) < ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize + ) { + break; + } + CapsulePtr += ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize; + CapsuleNum ++; + } + + if (CapsulePtr != CapsuleDataBufEnd) { + return EFI_INVALID_PARAMETER; + } + + // + // Capsule count must be less than PcdCapsuleMax, avoid building too many CvHobs to occupy all the free space in HobList. + // + if (CapsuleNum > PcdGet16 (PcdCapsuleMax)) { + return EFI_INVALID_PARAMETER; + } + + // + // Re-iterate the capsule buffer to create Capsule hob & Capsule Name Str Hob for each Capsule saved in relocated capsule file + // + CapsulePtr = RelocCapsuleBuf; + Index = 0; + while (CapsulePtr < CapsuleDataBufEnd) { + CapsuleSize = ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize; + BuildCvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)CapsulePtr, CapsuleSize); + + DEBUG((DEBUG_INFO, "Capsule saved in address %x size %x\n", CapsulePtr, CapsuleSize)); + + CapsulePtr += CapsuleSize; + Index++; + } + + return EFI_SUCCESS; +} + +/** + Recovery module entrypoint + + @param[in] FileHandle Handle of the file being invoked. + @param[in] PeiServices Describes the list of possible PEI Services. + + @return EFI_SUCCESS Recovery module is initialized. +**/ +EFI_STATUS +EFIAPI +InitializeCapsuleOnDiskLoad ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UINTN BootMode; + UINTN FileNameSize; + + BootMode = GetBootModeHob(); + ASSERT(BootMode == BOOT_ON_FLASH_UPDATE); + + // + // If there are capsules provisioned in memory, quit. + // Only one capsule resource is accept, CapsuleOnRam's priority is higher than CapsuleOnDisk. + // + if (CheckCapsuleFromRam(PeiServices)) { + DEBUG((DEBUG_ERROR, "Capsule On Memory Detected! Quit.\n")); + return EFI_ABORTED; + } + + DEBUG_CODE ( + VOID *CapsuleOnDiskModePpi; + + if (!IsCapsuleOnDiskMode()){ + return EFI_NOT_FOUND; + } + + // + // Check Capsule On Disk Relocation flag. If exists, load capsule & create Capsule Hob + // + Status = PeiServicesLocatePpi ( + &gEdkiiPeiBootInCapsuleOnDiskModePpiGuid, + 0, + NULL, + (VOID **)&CapsuleOnDiskModePpi + ); + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_ERROR, "Locate CapsuleOnDiskModePpi error %x\n", Status)); + return Status; + } + ); + + Status = PeiServicesInstallPpi (&mCapsuleOnDiskPpiList); + ASSERT_EFI_ERROR (Status); + + FileNameSize = PcdGetSize (PcdCoDRelocationFileName); + Status = PcdSetPtrS (PcdRecoveryFileName, &FileNameSize, (VOID *) PcdGetPtr(PcdCoDRelocationFileName)); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Loads a DXE capsule from some media into memory and updates the HOB table + with the DXE firmware volume information. + + @param[in] PeiServices General-purpose services that are available to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadCapsuleOnDisk ( + IN EFI_PEI_SERVICES **PeiServices, + IN EDKII_PEI_CAPSULE_ON_DISK_PPI *This + ) +{ + EFI_STATUS Status; + EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi; + UINTN NumberRecoveryCapsules; + UINTN Instance; + UINTN CapsuleInstance; + UINTN CapsuleSize; + EFI_GUID CapsuleType; + VOID *CapsuleBuffer; + + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Load Capsule On Disk Entry\n")); + + for (Instance = 0; ; Instance++) { + Status = PeiServicesLocatePpi ( + &gEfiPeiDeviceRecoveryModulePpiGuid, + Instance, + NULL, + (VOID **)&DeviceRecoveryPpi + ); + DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LocateRecoveryPpi (%d) - %r\n", Instance, Status)); + if (EFI_ERROR (Status)) { + if (Instance == 0) { + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_RECOVERY_PPI_NOT_FOUND) + ); + } + break; + } + NumberRecoveryCapsules = 0; + Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + &NumberRecoveryCapsules + ); + DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status)); + if (EFI_ERROR (Status)) { + continue; + } + + for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) { + CapsuleSize = 0; + Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + CapsuleInstance, + &CapsuleSize, + &CapsuleType + ); + DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status)); + if (EFI_ERROR (Status)) { + break; + } + + // + // Allocate the memory so that it gets preserved into DXE. + // Capsule is special because it may need to populate to system table + // + CapsuleBuffer = AllocateRuntimePages (EFI_SIZE_TO_PAGES (CapsuleSize)); + + if (CapsuleBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "LoadCapsuleOnDisk - AllocateRuntimePages fail\n")); + continue; + } + + Status = DeviceRecoveryPpi->LoadRecoveryCapsule ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + CapsuleInstance, + CapsuleBuffer + ); + DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status)); + if (EFI_ERROR (Status)) { + FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize)); + break; + } + + // + // Capsule Update Mode, Split relocated Capsule buffer into different capsule vehical hobs. + // + Status = RetrieveRelocatedCapsule(CapsuleBuffer, CapsuleSize); + + break; + } + + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_NO_RECOVERY_CAPSULE) + ); + } + + return Status; + } + + // + // Any attack against GPT, Relocation Info Variable or temp relocation file will result in no Capsule HOB and return EFI_NOT_FOUND. + // After flow to DXE phase. since no capsule hob is detected. Platform will clear Info flag and force restart. + // No volunerability will be exposed + // + + return EFI_NOT_FOUND; +} diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.inf b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.inf new file mode 100644 index 000000000..b1f562058 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.inf @@ -0,0 +1,64 @@ +## @file +# Load Capsule on Disk module. +# +# Load Capsule On Disk from Root Directory file system. Create CV hob +# based on temporary Capsule On Disk file. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CapsuleOnDiskLoadPei + MODULE_UNI_FILE = CapsuleOnDiskLoadPei.uni + FILE_GUID = 8ADEDF9E-2EC8-40fb-AE56-B76D90225D2D + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeCapsuleOnDiskLoad + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + CapsuleOnDiskLoadPei.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PeimEntryPoint + DebugLib + HobLib + BaseMemoryLib + MemoryAllocationLib + ReportStatusCodeLib + +[Ppis] + gEdkiiPeiCapsuleOnDiskPpiGuid ## PRODUCES + gEfiPeiReadOnlyVariable2PpiGuid ## CONSUMES + gEdkiiPeiBootInCapsuleOnDiskModePpiGuid ## SOMETIMES_CONSUMES + gEfiPeiDeviceRecoveryModulePpiGuid ## CONSUMES + gEfiPeiCapsulePpiGuid ## CONSUMES + +[Guids] + gEfiCapsuleVendorGuid ## SOMETIMES_CONSUMES ## Variable L"CodRelocationInfo" + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCoDRelocationFileName ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleMax ## CONSUMES + +[PcdEx] + gEfiMdeModulePkgTokenSpaceGuid.PcdRecoveryFileName ## PRODUCES + +[Depex] + gEdkiiPeiBootInCapsuleOnDiskModePpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + CapsuleOnDiskLoadPeiExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.uni b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.uni new file mode 100644 index 000000000..c3eae6a5c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.uni @@ -0,0 +1,15 @@ +// /** @file +// Caspule On Disk Load module. +// +// Load Capsule On Disk and build CV hob. +// +// Copyright (c) 2019, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Caspule On Disk Load module." + +#string STR_MODULE_DESCRIPTION #language en-US "Load Capsule On Disk and build CV hob." diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPeiExtra.uni b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPeiExtra.uni new file mode 100644 index 000000000..81034f629 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// CapsuleOnDiskLoadPei Localized Strings and Content +// +// Copyright (c) 2019, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"CapsuleOnDiskLoad PEI Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/Capsule.h b/roms/edk2/MdeModulePkg/Universal/CapsulePei/Capsule.h new file mode 100644 index 000000000..3d9cab02c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/Capsule.h @@ -0,0 +1,123 @@ +/** @file + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CAPSULE_PEIM_H_ +#define _CAPSULE_PEIM_H_ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Common/CommonHeader.h" + +#ifdef MDE_CPU_IA32 + +#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 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() + +typedef +EFI_STATUS +(*COALESCE_ENTRY) ( + SWITCH_32_TO_64_CONTEXT *EntrypointContext, + SWITCH_64_TO_32_CONTEXT *ReturnContext + ); + +#endif + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf new file mode 100644 index 000000000..e5078c798 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf @@ -0,0 +1,94 @@ +## @file +# Capsule update PEIM supports EFI and UEFI. +# +# Caution: This module requires additional review when modified. +# This driver will have external input - capsule image. +# This external input must be validated carefully to avoid security issue like +# buffer overflow, integer overflow. +# +# Copyright (c) 2006 - 2019, 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 = CapsulePei + MODULE_UNI_FILE = CapsulePei.uni + FILE_GUID = C779F6D8-7113-4AA1-9648-EB1633C7D53B + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = CapsuleMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + UefiCapsule.c + Capsule.h + Common/CapsuleCoalesce.c + Common/CommonHeader.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + HobLib + BaseMemoryLib + MemoryAllocationLib + PeiServicesLib + PeimEntryPoint + DebugLib + PeiServicesTablePointerLib + PrintLib + ReportStatusCodeLib + +[LibraryClasses.IA32] + PeCoffGetEntryPointLib + PcdLib + DebugAgentLib + +[Guids] + ## SOMETIMES_CONSUMES ## Variable:L"CapsuleUpdateData" + ## SOMETIMES_CONSUMES ## Variable:L"CapsuleLongModeBuffer" + gEfiCapsuleVendorGuid + +[Ppis] + gEfiPeiReadOnlyVariable2PpiGuid ## CONSUMES + gEfiPeiCapsulePpiGuid ## PRODUCES + +[Ppis.IA32] + gEfiPeiLoadFilePpiGuid ## SOMETIMES_CONSUMES + +[Pcd.IA32] + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleCoalesceFile ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + +[FeaturePcd.IA32] + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode ## CONSUMES + +[Depex] + gEfiPeiReadOnlyVariable2PpiGuid + +# [BootMode] +# FLASH_UPDATE ## SOMETIMES_CONSUMES + +# [Hob.IA32] +# UNDEFINED ## SOMETIMES_CONSUMES # CPU + +# [Hob] +# UNDEFINED ## SOMETIMES_PRODUCES # UEFI_CAPSULE + + +[UserExtensions.TianoCore."ExtraFiles"] + CapsulePeiExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni new file mode 100644 index 000000000..528946a95 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePei.uni @@ -0,0 +1,19 @@ +// /** @file +// Capsule update PEIM supports EFI and UEFI. +// +// Caution: This module requires additional review when modified. +// This driver will have external input - capsule image. +// This external input must be validated carefully to avoid security issue like +// buffer overflow, integer overflow. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Capsule update PEIM supporting EFI and UEFI" + +#string STR_MODULE_DESCRIPTION #language en-US "Capsule update PEIM supporting EFI and UEFI" + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni new file mode 100644 index 000000000..40539cfd5 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsulePeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// CapsulePei Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Firmware Update PEI Module" + + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf new file mode 100644 index 000000000..35d2535a5 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf @@ -0,0 +1,52 @@ +## @file +# CapsuleX64 module handles >4GB capsule blocks. +# +# The X64 entrypoint to process capsule in long mode. +# This module is built as X64. +# +# Caution: This module requires additional review when modified. +# This driver will have external input - capsule image. +# This external input must be validated carefully to avoid security issue like +# buffer overflow, integer overflow. +# +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CapsuleX64 + MODULE_UNI_FILE = CapsuleX64.uni + FILE_GUID = F7FDE4A6-294C-493c-B50F-9734553BB757 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + X64/X64Entry.c + X64/PageFaultHandler.nasm + Common/CapsuleCoalesce.c + Common/CommonHeader.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + CpuExceptionHandlerLib + DebugAgentLib + +[Depex] + FALSE + +[UserExtensions.TianoCore."ExtraFiles"] + CapsuleX64Extra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni new file mode 100644 index 000000000..8b1400880 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64.uni @@ -0,0 +1,23 @@ +// /** @file +// CapsuleX64 module handles >4GB capsule blocks. +// +// The X64 entrypoint to process capsule in long mode. +// This module is built as X64. +// +// Caution: This module requires additional review when modified. +// This driver will have external input - capsule image. +// This external input must be validated carefully to avoid security issue like +// buffer overflow, integer overflow. +// +// Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Handles >4GB capsule blocks" + +#string STR_MODULE_DESCRIPTION #language en-US "The X64 entry point to process capsule in long mode. This module is built as X64.

\n" + "This driver will have external input - capsule image. This external input must be validated carefully to avoid security issues like buffer overflow or integer overflow.
" + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni new file mode 100644 index 000000000..a6dffdd6a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/CapsuleX64Extra.uni @@ -0,0 +1,14 @@ +// /** @file +// CapsuleX64 Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Firmware Update PEI Module over 4GB" + + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c b/roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c new file mode 100644 index 000000000..468eea5d3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c @@ -0,0 +1,1291 @@ +/** @file + The logic to process capsule. + + Caution: This module requires additional review when modified. + This driver will have external input - capsule image. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + CapsuleDataCoalesce() will do basic validation before coalesce capsule data + into memory. + +(C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#include + +#include +#include +#include +#include + +#include "CommonHeader.h" + +#define MIN_COALESCE_ADDR (1024 * 1024) + +/** + Given a pointer to the capsule block list, info on the available system + memory, and the size of a buffer, find a free block of memory where a + buffer of the given size can be copied to safely. + + @param BlockList Pointer to head of capsule block descriptors + @param MemBase Pointer to the base of memory in which we want to find free space + @param MemSize The size of the block of memory pointed to by MemBase + @param DataSize How big a free block we want to find + + @return A pointer to a memory block of at least DataSize that lies somewhere + between MemBase and (MemBase + MemSize). The memory pointed to does not + contain any of the capsule block descriptors or capsule blocks pointed to + by the BlockList. + +**/ +UINT8 * +FindFreeMem ( + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + UINT8 *MemBase, + UINTN MemSize, + UINTN DataSize + ); + +/** + The capsule block descriptors may be fragmented and spread all over memory. + To simplify the coalescing of capsule blocks, first coalesce all the + capsule block descriptors low in memory. + + The descriptors passed in can be fragmented throughout memory. Here + they are relocated into memory to turn them into a contiguous (null + terminated) array. + + @param PeiServices pointer to PEI services table + @param BlockList pointer to the capsule block descriptors + @param NumDescriptors number of capsule data block descriptors, whose Length is non-zero. + @param MemBase base of system memory in which we can work + @param MemSize size of the system memory pointed to by MemBase + + @retval NULL could not relocate the descriptors + @retval Pointer to the base of the successfully-relocated block descriptors. + +**/ +EFI_CAPSULE_BLOCK_DESCRIPTOR * +RelocateBlockDescriptors ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + IN UINTN NumDescriptors, + IN UINT8 *MemBase, + IN UINTN MemSize + ); + +/** + Check every capsule header. + + @param CapsuleHeader The pointer to EFI_CAPSULE_HEADER + + @retval FALSE Capsule is OK + @retval TRUE Capsule is corrupted + +**/ +BOOLEAN +IsCapsuleCorrupted ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ); + +/** + Determine if two buffers overlap in memory. + + @param Buff1 pointer to first buffer + @param Size1 size of Buff1 + @param Buff2 pointer to second buffer + @param Size2 size of Buff2 + + @retval TRUE Buffers overlap in memory. + @retval FALSE Buffer doesn't overlap. + +**/ +BOOLEAN +IsOverlapped ( + UINT8 *Buff1, + UINTN Size1, + UINT8 *Buff2, + UINTN Size2 + ); + +/** + Given a pointer to a capsule block descriptor, traverse the list to figure + out how many legitimate descriptors there are, and how big the capsule it + refers to is. + + @param Desc Pointer to the capsule block descriptors + @param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero. + @param CapsuleSize Optional pointer to where to return the capsule image size + @param CapsuleNumber Optional pointer to where to return the number of capsule + + @retval EFI_NOT_FOUND No descriptors containing data in the list + @retval EFI_SUCCESS Return data is valid + +**/ +EFI_STATUS +GetCapsuleInfo ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc, + IN OUT UINTN *NumDescriptors OPTIONAL, + IN OUT UINTN *CapsuleSize OPTIONAL, + IN OUT UINTN *CapsuleNumber OPTIONAL + ); + +/** + Given a pointer to the capsule block list, info on the available system + memory, and the size of a buffer, find a free block of memory where a + buffer of the given size can be copied to safely. + + @param BlockList Pointer to head of capsule block descriptors + @param MemBase Pointer to the base of memory in which we want to find free space + @param MemSize The size of the block of memory pointed to by MemBase + @param DataSize How big a free block we want to find + + @return A pointer to a memory block of at least DataSize that lies somewhere + between MemBase and (MemBase + MemSize). The memory pointed to does not + contain any of the capsule block descriptors or capsule blocks pointed to + by the BlockList. + +**/ +UINT8 * +FindFreeMem ( + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + UINT8 *MemBase, + UINTN MemSize, + UINTN DataSize + ) +{ + UINTN Size; + EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrDesc; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempDesc; + UINT8 *MemEnd; + BOOLEAN Failed; + + // + // Need at least enough to copy the data to at the end of the buffer, so + // say the end is less the data size for easy comparisons here. + // + MemEnd = MemBase + MemSize - DataSize; + CurrDesc = BlockList; + // + // Go through all the descriptor blocks and see if any obstruct the range + // + while (CurrDesc != NULL) { + // + // Get the size of this block list and see if it's in the way + // + Failed = FALSE; + TempDesc = CurrDesc; + Size = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + while (TempDesc->Length != 0) { + Size += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + TempDesc++; + } + + if (IsOverlapped (MemBase, DataSize, (UINT8 *) CurrDesc, Size)) { + // + // Set our new base to the end of this block list and start all over + // + MemBase = (UINT8 *) CurrDesc + Size; + CurrDesc = BlockList; + if (MemBase > MemEnd) { + return NULL; + } + + Failed = TRUE; + } + // + // Now go through all the blocks and make sure none are in the way + // + while ((CurrDesc->Length != 0) && (!Failed)) { + if (IsOverlapped (MemBase, DataSize, (UINT8 *) (UINTN) CurrDesc->Union.DataBlock, (UINTN) CurrDesc->Length)) { + // + // Set our new base to the end of this block and start all over + // + Failed = TRUE; + MemBase = (UINT8 *) ((UINTN) CurrDesc->Union.DataBlock) + CurrDesc->Length; + CurrDesc = BlockList; + if (MemBase > MemEnd) { + return NULL; + } + } + CurrDesc++; + } + // + // Normal continuation -- jump to next block descriptor list + // + if (!Failed) { + CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) CurrDesc->Union.ContinuationPointer; + } + } + return MemBase; +} + +/** + Validate capsule by MemoryResource. + + @param MemoryResource Pointer to the buffer of memory resource descriptor. + @param Address Address to be validated. + @param Size Size to be validated. + + @retval TRUE No memory resource descriptor reported in HOB list before capsule Coalesce, + or it is valid in one MemoryResource. + FALSE It is not in any MemoryResource. + +**/ +BOOLEAN +ValidateCapsuleByMemoryResource ( + IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource, + IN EFI_PHYSICAL_ADDRESS Address, + IN UINT64 Size + ) +{ + UINTN Index; + + // + // Sanity Check + // + if (Size > MAX_ADDRESS) { + DEBUG ((DEBUG_ERROR, "ERROR: Size(0x%lx) > MAX_ADDRESS\n", Size)); + return FALSE; + } + + // + // Sanity Check + // + if (Address > (MAX_ADDRESS - Size)) { + DEBUG ((DEBUG_ERROR, "ERROR: Address(0x%lx) > (MAX_ADDRESS - Size(0x%lx))\n", Address, Size)); + return FALSE; + } + + if (MemoryResource == NULL) { + // + // No memory resource descriptor reported in HOB list before capsule Coalesce. + // + return TRUE; + } + + for (Index = 0; MemoryResource[Index].ResourceLength != 0; Index++) { + if ((Address >= MemoryResource[Index].PhysicalStart) && + ((Address + Size) <= (MemoryResource[Index].PhysicalStart + MemoryResource[Index].ResourceLength))) { + DEBUG ((DEBUG_INFO, "Address(0x%lx) Size(0x%lx) in MemoryResource[0x%x] - Start(0x%lx) Length(0x%lx)\n", + Address, Size, + Index, MemoryResource[Index].PhysicalStart, MemoryResource[Index].ResourceLength)); + return TRUE; + } + } + + DEBUG ((DEBUG_ERROR, "ERROR: Address(0x%lx) Size(0x%lx) not in any MemoryResource\n", Address, Size)); + return FALSE; +} + +/** + Check the integrity of the capsule descriptors. + + @param BlockList Pointer to the capsule descriptors + @param MemoryResource Pointer to the buffer of memory resource descriptor. + + @retval NULL BlockList is not valid. + @retval LastBlockDesc Last one Block in BlockList + +**/ +EFI_CAPSULE_BLOCK_DESCRIPTOR * +ValidateCapsuleIntegrity ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource + ) +{ + EFI_CAPSULE_HEADER *CapsuleHeader; + UINT64 CapsuleSize; + UINTN CapsuleCount; + EFI_CAPSULE_BLOCK_DESCRIPTOR *Ptr; + + DEBUG ((DEBUG_INFO, "ValidateCapsuleIntegrity\n")); + + // + // Go through the list to look for inconsistencies. Check for: + // * misaligned block descriptors. + // * The first capsule header guid + // * The first capsule header flag + // * The first capsule header HeaderSize + // * Below check will be done in ValidateCapsuleByMemoryResource() + // Length > MAX_ADDRESS + // Ptr + sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR) > MAX_ADDRESS + // DataBlock + Length > MAX_ADDRESS + // + CapsuleSize = 0; + CapsuleCount = 0; + Ptr = BlockList; + + if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS) (UINTN) Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) { + return NULL; + } + + DEBUG ((DEBUG_INFO, "Ptr - 0x%p\n", Ptr)); + DEBUG ((DEBUG_INFO, "Ptr->Length - 0x%lx\n", Ptr->Length)); + DEBUG ((DEBUG_INFO, "Ptr->Union - 0x%lx\n", Ptr->Union.ContinuationPointer)); + while ((Ptr->Length != 0) || (Ptr->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + // + // Make sure the descriptor is aligned at UINT64 in memory + // + if ((UINTN) Ptr & (sizeof(UINT64) - 1)) { + DEBUG ((DEBUG_ERROR, "ERROR: BlockList address failed alignment check\n")); + return NULL; + } + + if (Ptr->Length == 0) { + // + // Descriptor points to another list of block descriptors somewhere + // else. + // + Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Ptr->Union.ContinuationPointer; + if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS) (UINTN) Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) { + return NULL; + } + DEBUG ((DEBUG_INFO, "Ptr(C) - 0x%p\n", Ptr)); + DEBUG ((DEBUG_INFO, "Ptr->Length - 0x%lx\n", Ptr->Length)); + DEBUG ((DEBUG_INFO, "Ptr->Union - 0x%lx\n", Ptr->Union.ContinuationPointer)); + } else { + if (!ValidateCapsuleByMemoryResource (MemoryResource, Ptr->Union.DataBlock, Ptr->Length)) { + return NULL; + } + + // + //To enhance the reliability of check-up, the first capsule's header is checked here. + //More reliabilities check-up will do later. + // + if (CapsuleSize == 0) { + // + //Move to the first capsule to check its header. + // + CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Ptr->Union.DataBlock); + // + // Sanity check + // + if (Ptr->Length < sizeof(EFI_CAPSULE_HEADER)) { + DEBUG ((DEBUG_ERROR, "ERROR: Ptr->Length(0x%lx) < sizeof(EFI_CAPSULE_HEADER)\n", Ptr->Length)); + return NULL; + } + // + // Make sure HeaderSize field is valid + // + if (CapsuleHeader->HeaderSize > CapsuleHeader->CapsuleImageSize) { + DEBUG ((DEBUG_ERROR, "ERROR: CapsuleHeader->HeaderSize(0x%x) > CapsuleHeader->CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize)); + return NULL; + } + if (IsCapsuleCorrupted (CapsuleHeader)) { + return NULL; + } + CapsuleCount ++; + CapsuleSize = CapsuleHeader->CapsuleImageSize; + } + + if (CapsuleSize >= Ptr->Length) { + CapsuleSize = CapsuleSize - Ptr->Length; + } else { + DEBUG ((DEBUG_ERROR, "ERROR: CapsuleSize(0x%lx) < Ptr->Length(0x%lx)\n", CapsuleSize, Ptr->Length)); + // + // Sanity check + // + return NULL; + } + + // + // Move to next BLOCK descriptor + // + Ptr++; + if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS) (UINTN) Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) { + return NULL; + } + DEBUG ((DEBUG_INFO, "Ptr(B) - 0x%p\n", Ptr)); + DEBUG ((DEBUG_INFO, "Ptr->Length - 0x%lx\n", Ptr->Length)); + DEBUG ((DEBUG_INFO, "Ptr->Union - 0x%lx\n", Ptr->Union.ContinuationPointer)); + } + } + + if (CapsuleCount == 0) { + // + // No any capsule is found in BlockList + // + DEBUG ((DEBUG_ERROR, "ERROR: CapsuleCount(0x%x) == 0\n", CapsuleCount)); + return NULL; + } + + if (CapsuleSize != 0) { + // + // Capsule data is incomplete. + // + DEBUG ((DEBUG_ERROR, "ERROR: CapsuleSize(0x%lx) != 0\n", CapsuleSize)); + return NULL; + } + + return Ptr; +} + +/** + The capsule block descriptors may be fragmented and spread all over memory. + To simplify the coalescing of capsule blocks, first coalesce all the + capsule block descriptors low in memory. + + The descriptors passed in can be fragmented throughout memory. Here + they are relocated into memory to turn them into a contiguous (null + terminated) array. + + @param PeiServices pointer to PEI services table + @param BlockList pointer to the capsule block descriptors + @param NumDescriptors number of capsule data block descriptors, whose Length is non-zero. + @param MemBase base of system memory in which we can work + @param MemSize size of the system memory pointed to by MemBase + + @retval NULL could not relocate the descriptors + @retval Pointer to the base of the successfully-relocated block descriptors. + +**/ +EFI_CAPSULE_BLOCK_DESCRIPTOR * +RelocateBlockDescriptors ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + IN UINTN NumDescriptors, + IN UINT8 *MemBase, + IN UINTN MemSize + ) +{ + EFI_CAPSULE_BLOCK_DESCRIPTOR *NewBlockList; + EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrBlockDescHead; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc; + EFI_CAPSULE_BLOCK_DESCRIPTOR *PrevBlockDescTail; + UINTN BufferSize; + UINT8 *RelocBuffer; + UINTN BlockListSize; + + // + // Get the info on the blocks and descriptors. Since we're going to move + // the descriptors low in memory, adjust the base/size values accordingly here. + // NumDescriptors is the number of legit data descriptors, so add one for + // a terminator. (Already done by caller, no check is needed.) + // + + BufferSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + NewBlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) MemBase; + if (MemSize < BufferSize) { + return NULL; + } + + MemSize -= BufferSize; + MemBase += BufferSize; + // + // Go through all the blocks and make sure none are in the way + // + TempBlockDesc = BlockList; + while (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) { + if (TempBlockDesc->Length == 0) { + // + // Next block of descriptors + // + TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer; + } else { + // + // If the capsule data pointed to by this descriptor is in the way, + // move it. + // + if (IsOverlapped ( + (UINT8 *) NewBlockList, + BufferSize, + (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock, + (UINTN) TempBlockDesc->Length + )) { + // + // Relocate the block + // + RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, (UINTN) TempBlockDesc->Length); + if (RelocBuffer == NULL) { + return NULL; + } + + CopyMem ((VOID *) RelocBuffer, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length); + DEBUG ((DEBUG_INFO, "Capsule relocate descriptors from/to/size 0x%lX 0x%lX 0x%lX\n", TempBlockDesc->Union.DataBlock, (UINT64)(UINTN)RelocBuffer, TempBlockDesc->Length)); + TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer; + } + TempBlockDesc++; + } + } + // + // Now go through all the block descriptors to make sure that they're not + // in the memory region we want to copy them to. + // + CurrBlockDescHead = BlockList; + PrevBlockDescTail = NULL; + while ((CurrBlockDescHead != NULL) && (CurrBlockDescHead->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + // + // Get the size of this list then see if it overlaps our low region + // + TempBlockDesc = CurrBlockDescHead; + BlockListSize = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + while (TempBlockDesc->Length != 0) { + BlockListSize += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + TempBlockDesc++; + } + + if (IsOverlapped ( + (UINT8 *) NewBlockList, + BufferSize, + (UINT8 *) CurrBlockDescHead, + BlockListSize + )) { + // + // Overlaps, so move it out of the way + // + RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, BlockListSize); + if (RelocBuffer == NULL) { + return NULL; + } + CopyMem ((VOID *) RelocBuffer, (VOID *) CurrBlockDescHead, BlockListSize); + DEBUG ((DEBUG_INFO, "Capsule reloc descriptor block #2\n")); + // + // Point the previous block's next point to this copied version. If + // the tail pointer is null, then this is the first descriptor block. + // + if (PrevBlockDescTail == NULL) { + BlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) RelocBuffer; + } else { + PrevBlockDescTail->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer; + } + } + // + // Save our new tail and jump to the next block list + // + PrevBlockDescTail = TempBlockDesc; + CurrBlockDescHead = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer; + } + // + // Cleared out low memory. Now copy the descriptors down there. + // + TempBlockDesc = BlockList; + CurrBlockDescHead = NewBlockList; + while ((TempBlockDesc != NULL) && (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + if (TempBlockDesc->Length != 0) { + CurrBlockDescHead->Union.DataBlock = TempBlockDesc->Union.DataBlock; + CurrBlockDescHead->Length = TempBlockDesc->Length; + CurrBlockDescHead++; + TempBlockDesc++; + } else { + TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer; + } + } + // + // Null terminate + // + CurrBlockDescHead->Union.ContinuationPointer = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL; + CurrBlockDescHead->Length = 0; + return NewBlockList; +} + +/** + Determine if two buffers overlap in memory. + + @param Buff1 pointer to first buffer + @param Size1 size of Buff1 + @param Buff2 pointer to second buffer + @param Size2 size of Buff2 + + @retval TRUE Buffers overlap in memory. + @retval FALSE Buffer doesn't overlap. + +**/ +BOOLEAN +IsOverlapped ( + UINT8 *Buff1, + UINTN Size1, + UINT8 *Buff2, + UINTN Size2 + ) +{ + // + // If buff1's end is less than the start of buff2, then it's ok. + // Also, if buff1's start is beyond buff2's end, then it's ok. + // + if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) { + return FALSE; + } + + return TRUE; +} + +/** + Given a pointer to a capsule block descriptor, traverse the list to figure + out how many legitimate descriptors there are, and how big the capsule it + refers to is. + + @param Desc Pointer to the capsule block descriptors + @param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero. + @param CapsuleSize Optional pointer to where to return the capsule image size + @param CapsuleNumber Optional pointer to where to return the number of capsule + + @retval EFI_NOT_FOUND No descriptors containing data in the list + @retval EFI_SUCCESS Return data is valid + +**/ +EFI_STATUS +GetCapsuleInfo ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc, + IN OUT UINTN *NumDescriptors OPTIONAL, + IN OUT UINTN *CapsuleSize OPTIONAL, + IN OUT UINTN *CapsuleNumber OPTIONAL + ) +{ + UINTN Count; + UINTN Size; + UINTN Number; + UINTN ThisCapsuleImageSize; + EFI_CAPSULE_HEADER *CapsuleHeader; + + DEBUG ((DEBUG_INFO, "GetCapsuleInfo enter\n")); + + ASSERT (Desc != NULL); + + Count = 0; + Size = 0; + Number = 0; + ThisCapsuleImageSize = 0; + + while (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) { + if (Desc->Length == 0) { + // + // Descriptor points to another list of block descriptors somewhere + // + Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer; + } else { + // + // Sanity Check + // It is needed, because ValidateCapsuleIntegrity() only validate one individual capsule Size. + // While here we need check all capsules size. + // + if (Desc->Length >= (MAX_ADDRESS - Size)) { + DEBUG ((DEBUG_ERROR, "ERROR: Desc->Length(0x%lx) >= (MAX_ADDRESS - Size(0x%x))\n", Desc->Length, Size)); + return EFI_OUT_OF_RESOURCES; + } + Size += (UINTN) Desc->Length; + Count++; + + // + // See if this is first capsule's header + // + if (ThisCapsuleImageSize == 0) { + CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Desc->Union.DataBlock); + // + // This has been checked in ValidateCapsuleIntegrity() + // + Number ++; + ThisCapsuleImageSize = CapsuleHeader->CapsuleImageSize; + } + + // + // This has been checked in ValidateCapsuleIntegrity() + // + ASSERT (ThisCapsuleImageSize >= Desc->Length); + ThisCapsuleImageSize = (UINTN)(ThisCapsuleImageSize - Desc->Length); + + // + // Move to next + // + Desc++; + } + } + // + // If no descriptors, then fail + // + if (Count == 0) { + DEBUG ((DEBUG_ERROR, "ERROR: Count == 0\n")); + return EFI_NOT_FOUND; + } + + // + // checked in ValidateCapsuleIntegrity() + // + ASSERT (ThisCapsuleImageSize == 0); + + if (NumDescriptors != NULL) { + *NumDescriptors = Count; + } + + if (CapsuleSize != NULL) { + *CapsuleSize = Size; + } + + if (CapsuleNumber != NULL) { + *CapsuleNumber = Number; + } + + return EFI_SUCCESS; +} + +/** + Check every capsule header. + + @param CapsuleHeader The pointer to EFI_CAPSULE_HEADER + + @retval FALSE Capsule is OK + @retval TRUE Capsule is corrupted + +**/ +BOOLEAN +IsCapsuleCorrupted ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + // + //A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET. + // + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) { + return TRUE; + } + // + //Make sure the flags combination is supported by the platform. + // + if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) { + return TRUE; + } + if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) { + return TRUE; + } + + return FALSE; +} + +/** + Try to verify the integrity of a capsule test pattern before the + capsule gets coalesced. This can be useful in narrowing down + where capsule data corruption occurs. + + The test pattern mode fills in memory with a counting UINT32 value. + If the capsule is not divided up in a multiple of 4-byte blocks, then + things get messy doing the check. Therefore there are some cases + here where we just give up and skip the pre-coalesce check. + + @param PeiServices PEI services table + @param Desc Pointer to capsule descriptors +**/ +VOID +CapsuleTestPatternPreCoalesce ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc + ) +{ + UINT32 *TestPtr; + UINT32 TestCounter; + UINT32 TestSize; + + DEBUG ((DEBUG_INFO, "CapsuleTestPatternPreCoalesce\n")); + + // + // Find first data descriptor + // + while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer; + } + + if (Desc->Union.ContinuationPointer == 0) { + return ; + } + // + // First one better be long enough to at least hold the test signature + // + if (Desc->Length < sizeof (UINT32)) { + DEBUG ((DEBUG_INFO, "Capsule test pattern pre-coalesce punted #1\n")); + return ; + } + + TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock; + // + // 0x54534554 "TEST" + // + if (*TestPtr != 0x54534554) { + return ; + } + + TestCounter = 0; + TestSize = (UINT32) Desc->Length - 2 * sizeof (UINT32); + // + // Skip over the signature and the size fields in the pattern data header + // + TestPtr += 2; + while (1) { + if ((TestSize & 0x03) != 0) { + DEBUG ((DEBUG_INFO, "Capsule test pattern pre-coalesce punted #2\n")); + return ; + } + + while (TestSize > 0) { + if (*TestPtr != TestCounter) { + DEBUG ((DEBUG_INFO, "Capsule test pattern pre-coalesce failed data corruption check\n")); + return ; + } + + TestSize -= sizeof (UINT32); + TestCounter++; + TestPtr++; + } + Desc++; + while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer; + } + + if (Desc->Union.ContinuationPointer == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) { + return ; + } + TestSize = (UINT32) Desc->Length; + TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock; + } +} + +/** + Checks for the presence of capsule descriptors. + Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2... + + @param BlockListBuffer Pointer to the buffer of capsule descriptors variables + @param MemoryResource Pointer to the buffer of memory resource descriptor. + @param BlockDescriptorList Pointer to the capsule descriptors list + + @retval EFI_SUCCESS a valid capsule is present + @retval EFI_NOT_FOUND if a valid capsule is not present +**/ +EFI_STATUS +BuildCapsuleDescriptors ( + IN EFI_PHYSICAL_ADDRESS *BlockListBuffer, + IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource, + OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptorList + ) +{ + UINTN Index; + EFI_CAPSULE_BLOCK_DESCRIPTOR *LastBlock; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlock; + EFI_CAPSULE_BLOCK_DESCRIPTOR *HeadBlock; + + DEBUG ((DEBUG_INFO, "BuildCapsuleDescriptors enter\n")); + + LastBlock = NULL; + HeadBlock = NULL; + TempBlock = NULL; + Index = 0; + + while (BlockListBuffer[Index] != 0) { + // + // Test integrity of descriptors. + // + if (BlockListBuffer[Index] < MAX_ADDRESS) { + TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index], MemoryResource); + if (TempBlock != NULL) { + if (LastBlock == NULL) { + LastBlock = TempBlock; + + // + // Return the base of the block descriptors + // + HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]; + } else { + // + // Combine the different BlockList into single BlockList. + // + LastBlock->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)BlockListBuffer[Index]; + LastBlock->Length = 0; + LastBlock = TempBlock; + } + } + } else { + DEBUG ((DEBUG_ERROR, "ERROR: BlockListBuffer[Index](0x%lx) < MAX_ADDRESS\n", BlockListBuffer[Index])); + } + Index ++; + } + + if (HeadBlock != NULL) { + *BlockDescriptorList = HeadBlock; + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; +} + +/** + The function to coalesce a fragmented capsule in memory. + + Memory Map for coalesced capsule: + MemBase + ---->+---------------------------+<-----------+ + MemSize | ------------------------- | | + | | Capsule [Num-1] | | | + | ------------------------- | | + | | ................ | | | + | ------------------------- | | + | | Capsule [1] | | | + | ------------------------- | | + | | Capsule [0] | | | + | ------------------------- | | + | Capsule Image | | +CapsuleImageBase-->+---------------------------+ + | ------------------------- | | + | | CapsuleOffset[Num-1] | | | + | ------------------------- | | + | | ................ | | CapsuleSize + | ------------------------- | | + | | CapsuleOffset[1] | | | + | ------------------------- | | + | | CapsuleOffset[0] | | | + |---------------------------| | + | | CapsuleNumber | | | + | ------------------------- | | + | | CapsuleAllImageSize | | | + | ------------------------- | | + | PrivateData | | + DestPtr ---->+---------------------------+<-----------+ + | | | + | FreeMem | FreeMemSize + | | | + FreeMemBase --->+---------------------------+<-----------+ + | Terminator | + +---------------------------+ + | BlockDescriptor n | + +---------------------------+ + | ................. | + +---------------------------+ + | BlockDescriptor 1 | + +---------------------------+ + | BlockDescriptor 0 | + +---------------------------+ + | PrivateDataDesc 0 | + MemBase ---->+---------------------------+<----- BlockList + + Caution: This function may receive untrusted input. + The capsule data is external input, so this routine will do basic validation before + coalesce capsule data into memory. + + @param PeiServices General purpose services available to every PEIM. + @param BlockListBuffer Pointer to the buffer of Capsule Descriptor Variables. + @param MemoryResource Pointer to the buffer of memory resource descriptor. + @param MemoryBase Pointer to the base of a block of memory that we can walk + all over while trying to coalesce our buffers. + On output, this variable will hold the base address of + a coalesced capsule. + @param MemorySize Size of the memory region pointed to by MemoryBase. + On output, this variable will contain the size of the + coalesced capsule. + + @retval EFI_NOT_FOUND If we could not find the capsule descriptors. + + @retval EFI_BUFFER_TOO_SMALL + If we could not coalesce the capsule in the memory + region provided to us. + + @retval EFI_SUCCESS Processed the capsule successfully. +**/ +EFI_STATUS +EFIAPI +CapsuleDataCoalesce ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PHYSICAL_ADDRESS *BlockListBuffer, + IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ) +{ + VOID *NewCapsuleBase; + VOID *CapsuleImageBase; + UINTN CapsuleIndex; + UINT8 *FreeMemBase; + UINT8 *DestPtr; + UINTN DestLength; + UINT8 *RelocPtr; + UINTN CapsuleTimes; + UINT64 SizeLeft; + UINT64 CapsuleImageSize; + UINTN CapsuleSize; + UINTN CapsuleNumber; + UINTN DescriptorsSize; + UINTN FreeMemSize; + UINTN NumDescriptors; + BOOLEAN CapsuleBeginFlag; + EFI_STATUS Status; + EFI_CAPSULE_HEADER *CapsuleHeader; + EFI_CAPSULE_PEIM_PRIVATE_DATA PrivateData; + EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateDataPtr; + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList; + EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrentBlockDesc; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc; + EFI_CAPSULE_BLOCK_DESCRIPTOR PrivateDataDesc[2]; + + DEBUG ((DEBUG_INFO, "CapsuleDataCoalesce enter\n")); + + CapsuleIndex = 0; + SizeLeft = 0; + CapsuleTimes = 0; + CapsuleImageSize = 0; + PrivateDataPtr = NULL; + CapsuleHeader = NULL; + CapsuleBeginFlag = TRUE; + CapsuleSize = 0; + NumDescriptors = 0; + + // + // Build capsule descriptors list + // + Status = BuildCapsuleDescriptors (BlockListBuffer, MemoryResource, &BlockList); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG_CODE ( + CapsuleTestPatternPreCoalesce (PeiServices, BlockList); + ); + + // + // Get the size of our descriptors and the capsule size. GetCapsuleInfo() + // returns the number of descriptors that actually point to data, so add + // one for a terminator. Do that below. + // + Status = GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize, &CapsuleNumber); + if (EFI_ERROR (Status)) { + return Status; + } + DEBUG ((DEBUG_INFO, "CapsuleSize - 0x%x\n", CapsuleSize)); + DEBUG ((DEBUG_INFO, "CapsuleNumber - 0x%x\n", CapsuleNumber)); + DEBUG ((DEBUG_INFO, "NumDescriptors - 0x%x\n", NumDescriptors)); + if ((CapsuleSize == 0) || (NumDescriptors == 0) || (CapsuleNumber == 0)) { + return EFI_NOT_FOUND; + } + + if (CapsuleNumber - 1 >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(UINT64))) / sizeof(UINT64)) { + DEBUG ((DEBUG_ERROR, "ERROR: CapsuleNumber - 0x%x\n", CapsuleNumber)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Initialize our local copy of private data. When we're done, we'll create a + // descriptor for it as well so that it can be put into free memory without + // trashing anything. + // + PrivateData.Signature = EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE; + PrivateData.CapsuleAllImageSize = (UINT64) CapsuleSize; + PrivateData.CapsuleNumber = (UINT64) CapsuleNumber; + PrivateData.CapsuleOffset[0] = 0; + // + // NOTE: Only data in sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) is valid, CapsuleOffset field is uninitialized at this moment. + // The code sets partial length here for Descriptor.Length check, but later it will use full length to reserve those PrivateData region. + // + PrivateDataDesc[0].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) &PrivateData; + PrivateDataDesc[0].Length = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA); + PrivateDataDesc[1].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) BlockList; + PrivateDataDesc[1].Length = 0; + // + // Add PrivateDataDesc[0] in beginning, as it is new descriptor. PrivateDataDesc[1] is NOT needed. + // In addition, one NULL terminator is added in the end. See RelocateBlockDescriptors(). + // + NumDescriptors += 2; + // + // Sanity check + // + if (CapsuleSize >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64)))) { + DEBUG ((DEBUG_ERROR, "ERROR: CapsuleSize - 0x%x\n", CapsuleSize)); + return EFI_BUFFER_TOO_SMALL; + } + // + // Need add sizeof(UINT64) for PrivateData alignment + // + CapsuleSize += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64); + BlockList = PrivateDataDesc; + // + // Sanity check + // + if (NumDescriptors >= (MAX_ADDRESS / sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR))) { + DEBUG ((DEBUG_ERROR, "ERROR: NumDescriptors - 0x%x\n", NumDescriptors)); + return EFI_BUFFER_TOO_SMALL; + } + DescriptorsSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + // + // Sanity check + // + if (DescriptorsSize >= (MAX_ADDRESS - CapsuleSize)) { + DEBUG ((DEBUG_ERROR, "ERROR: DescriptorsSize - 0x%lx, CapsuleSize - 0x%lx\n", (UINT64)DescriptorsSize, (UINT64)CapsuleSize)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Don't go below some min address. If the base is below it, + // then move it up and adjust the size accordingly. + // + DEBUG ((DEBUG_INFO, "Capsule Memory range from 0x%8X to 0x%8X\n", (UINTN) *MemoryBase, (UINTN)*MemoryBase + *MemorySize)); + if ((UINTN)*MemoryBase < (UINTN) MIN_COALESCE_ADDR) { + if (((UINTN)*MemoryBase + *MemorySize) < (UINTN) MIN_COALESCE_ADDR) { + DEBUG ((DEBUG_ERROR, "ERROR: *MemoryBase + *MemorySize - 0x%x\n", (UINTN)*MemoryBase + *MemorySize)); + return EFI_BUFFER_TOO_SMALL; + } else { + *MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase); + *MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR; + } + } + + if (*MemorySize <= (CapsuleSize + DescriptorsSize)) { + DEBUG ((DEBUG_ERROR, "ERROR: CapsuleSize + DescriptorsSize - 0x%x\n", CapsuleSize + DescriptorsSize)); + return EFI_BUFFER_TOO_SMALL; + } + + FreeMemBase = *MemoryBase; + FreeMemSize = *MemorySize; + DEBUG ((DEBUG_INFO, "Capsule Free Memory from 0x%8X to 0x%8X\n", (UINTN) FreeMemBase, (UINTN) FreeMemBase + FreeMemSize)); + + // + // Relocate all the block descriptors to low memory to make further + // processing easier. + // + BlockList = RelocateBlockDescriptors (PeiServices, BlockList, NumDescriptors, FreeMemBase, FreeMemSize); + if (BlockList == NULL) { + // + // Not enough room to relocate the descriptors + // + return EFI_BUFFER_TOO_SMALL; + } + + // + // Take the top of memory for the capsule. UINT64 align up. + // + DestPtr = FreeMemBase + FreeMemSize - CapsuleSize; + DestPtr = (UINT8 *) (((UINTN)DestPtr + sizeof (UINT64) - 1) & ~(sizeof (UINT64) - 1)); + FreeMemBase = (UINT8 *) BlockList + DescriptorsSize; + FreeMemSize = (UINTN) DestPtr - (UINTN) FreeMemBase; + NewCapsuleBase = (VOID *) DestPtr; + CapsuleImageBase = (UINT8 *)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64); + + PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase; + + // + // Move all the blocks to the top (high) of memory. + // Relocate all the obstructing blocks. Note that the block descriptors + // were coalesced when they were relocated, so we can just ++ the pointer. + // + CurrentBlockDesc = BlockList; + while ((CurrentBlockDesc->Length != 0) || (CurrentBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + if (CapsuleTimes == 0) { + // + // The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA. + // CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use. + // + ASSERT (CurrentBlockDesc->Union.DataBlock == (UINT64)(UINTN)&PrivateData); + DestLength = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64); + } else { + DestLength = (UINTN)CurrentBlockDesc->Length; + } + // + // See if any of the remaining capsule blocks are in the way + // + TempBlockDesc = CurrentBlockDesc; + while (TempBlockDesc->Length != 0) { + // + // Is this block in the way of where we want to copy the current descriptor to? + // + if (IsOverlapped ( + (UINT8 *) DestPtr, + (UINTN) DestLength, + (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock, + (UINTN) TempBlockDesc->Length + )) { + // + // Relocate the block + // + RelocPtr = FindFreeMem (BlockList, FreeMemBase, FreeMemSize, (UINTN) TempBlockDesc->Length); + if (RelocPtr == NULL) { + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem ((VOID *) RelocPtr, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length); + DEBUG ((DEBUG_INFO, "Capsule reloc data block from 0x%8X to 0x%8X with size 0x%8X\n", + (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) RelocPtr, (UINTN) TempBlockDesc->Length)); + + TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocPtr; + } + // + // Next descriptor + // + TempBlockDesc++; + } + // + // Ok, we made it through. Copy the block. + // we just support greping one capsule from the lists of block descs list. + // + CapsuleTimes ++; + // + //Skip the first block descriptor that filled with EFI_CAPSULE_PEIM_PRIVATE_DATA + // + if (CapsuleTimes > 1) { + // + //For every capsule entry point, check its header to determine whether to relocate it. + //If it is invalid, skip it and move on to the next capsule. If it is valid, relocate it. + // + if (CapsuleBeginFlag) { + CapsuleBeginFlag = FALSE; + CapsuleHeader = (EFI_CAPSULE_HEADER*)(UINTN)CurrentBlockDesc->Union.DataBlock; + SizeLeft = CapsuleHeader->CapsuleImageSize; + + // + // No more check here is needed, because IsCapsuleCorrupted() already in ValidateCapsuleIntegrity() + // + ASSERT (CapsuleIndex < CapsuleNumber); + + // + // Relocate this capsule + // + CapsuleImageSize += SizeLeft; + // + // Cache the begin offset of this capsule + // + ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE); + ASSERT ((UINTN)DestPtr >= (UINTN)CapsuleImageBase); + PrivateDataPtr->CapsuleOffset[CapsuleIndex++] = (UINTN)DestPtr - (UINTN)CapsuleImageBase; + } + + // + // Below ASSERT is checked in ValidateCapsuleIntegrity() + // + ASSERT (CurrentBlockDesc->Length <= SizeLeft); + + CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length); + DEBUG ((DEBUG_INFO, "Capsule coalesce block no.0x%lX from 0x%lX to 0x%lX with size 0x%lX\n",(UINT64)CapsuleTimes, + CurrentBlockDesc->Union.DataBlock, (UINT64)(UINTN)DestPtr, CurrentBlockDesc->Length)); + DestPtr += CurrentBlockDesc->Length; + SizeLeft -= CurrentBlockDesc->Length; + + if (SizeLeft == 0) { + // + //Here is the end of the current capsule image. + // + CapsuleBeginFlag = TRUE; + } + } else { + // + // The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA. + // CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use. + // + ASSERT (CurrentBlockDesc->Length == sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA)); + ASSERT ((UINTN)DestPtr == (UINTN)NewCapsuleBase); + CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length); + DestPtr += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64); + } + // + //Walk through the block descriptor list. + // + CurrentBlockDesc++; + } + // + // We return the base of memory we want reserved, and the size. + // The memory peim should handle it appropriately from there. + // + *MemorySize = (UINTN) CapsuleSize; + *MemoryBase = (VOID *) NewCapsuleBase; + + ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE); + ASSERT (PrivateDataPtr->CapsuleAllImageSize == CapsuleImageSize); + ASSERT (PrivateDataPtr->CapsuleNumber == CapsuleIndex); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h b/roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h new file mode 100644 index 000000000..695520aa2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h @@ -0,0 +1,115 @@ +/** @file + Common header file. + +Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CAPSULE_COMMON_HEADER_ +#define _CAPSULE_COMMON_HEADER_ + +// +// 8 extra pages for PF handler. +// +#define EXTRA_PAGE_TABLE_PAGES 8 + +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +// +// This capsule PEIM puts its private data at the start of the +// coalesced capsule. Here's the structure definition. +// +#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'P') + +#pragma pack(1) +typedef struct { + UINT64 Signature; + UINT64 CapsuleAllImageSize; + UINT64 CapsuleNumber; + UINT64 CapsuleOffset[1]; +} EFI_CAPSULE_PEIM_PRIVATE_DATA; +#pragma pack() + +typedef struct { + /// + /// The physical start address of the resource region. + /// + EFI_PHYSICAL_ADDRESS PhysicalStart; + /// + /// The number of bytes of the resource region. + /// + UINT64 ResourceLength; +} MEMORY_RESOURCE_DESCRIPTOR; + +#define CAPSULE_TEST_SIGNATURE SIGNATURE_32('T', 'E', 'S', 'T') + +#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) +#pragma pack(1) +typedef struct { + EFI_PHYSICAL_ADDRESS EntryPoint; + EFI_PHYSICAL_ADDRESS StackBufferBase; + UINT64 StackBufferLength; + EFI_PHYSICAL_ADDRESS JumpBuffer; + EFI_PHYSICAL_ADDRESS BlockListAddr; + EFI_PHYSICAL_ADDRESS MemoryResource; + EFI_PHYSICAL_ADDRESS MemoryBase64Ptr; + EFI_PHYSICAL_ADDRESS MemorySize64Ptr; + BOOLEAN Page1GSupport; + UINT64 AddressEncMask; +} SWITCH_32_TO_64_CONTEXT; + +typedef struct { + UINT16 ReturnCs; + EFI_PHYSICAL_ADDRESS ReturnEntryPoint; + UINT64 ReturnStatus; + // + // NOTICE: + // Be careful about the Base field of IA32_DESCRIPTOR + // that is UINTN type. + // To extend new field for this structure, add it to + // right before this Gdtr field. + // + IA32_DESCRIPTOR Gdtr; +} SWITCH_64_TO_32_CONTEXT; +#pragma pack() +#endif + +/** + The function to coalesce a fragmented capsule in memory. + + @param PeiServices General purpose services available to every PEIM. + @param BlockListBuffer Point to the buffer of Capsule Descriptor Variables. + @param MemoryResource Pointer to the buffer of memory resource descriptor. + @param MemoryBase Pointer to the base of a block of memory that we can walk + all over while trying to coalesce our buffers. + On output, this variable will hold the base address of + a coalesced capsule. + @param MemorySize Size of the memory region pointed to by MemoryBase. + On output, this variable will contain the size of the + coalesced capsule. + + @retval EFI_NOT_FOUND if we can't determine the boot mode + if the boot mode is not flash-update + if we could not find the capsule descriptors + + @retval EFI_BUFFER_TOO_SMALL + if we could not coalesce the capsule in the memory + region provided to us + + @retval EFI_SUCCESS if there's no capsule, or if we processed the + capsule successfully. +**/ +EFI_STATUS +EFIAPI +CapsuleDataCoalesce ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PHYSICAL_ADDRESS *BlockListBuffer, + IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c b/roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c new file mode 100644 index 000000000..51afab7b0 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c @@ -0,0 +1,1332 @@ +/** @file + Capsule update PEIM for UEFI2.0 + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Capsule.h" + +#define DEFAULT_SG_LIST_HEADS (20) + +#ifdef MDE_CPU_IA32 +// +// Global Descriptor Table (GDT) +// +GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries[] = { +/* selector { Global Segment Descriptor } */ +/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor +/* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor +/* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor +/* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor +/* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor +/* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor +/* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor +/* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor +/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor +}; + +// +// IA32 Gdt register +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = { + sizeof (mGdtEntries) - 1, + (UINTN) mGdtEntries + }; + + +/** + The function will check if 1G page is supported. + + @retval TRUE 1G page is supported. + @retval FALSE 1G page is not supported. + +**/ +BOOLEAN +IsPage1GSupport ( + VOID + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + BOOLEAN Page1GSupport; + + Page1GSupport = FALSE; + if (PcdGetBool(PcdUse1GPageTable)) { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + Page1GSupport = TRUE; + } + } + } + + return Page1GSupport; +} + +/** + Calculate the total size of page table. + + @param[in] Page1GSupport 1G page support or not. + + @return The size of page table. + +**/ +UINTN +CalculatePageTableSize ( + IN BOOLEAN Page1GSupport + ) +{ + UINTN ExtraPageTablePages; + UINTN TotalPagesNum; + UINT8 PhysicalAddressBits; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + + // + // Create 4G page table by default, + // and let PF handler to handle > 4G request. + // + PhysicalAddressBits = 32; + ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES; + + // + // Calculate the table entries needed. + // + if (PhysicalAddressBits <= 39 ) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30)); + } else { + NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39)); + NumberOfPdpEntriesNeeded = 512; + } + + if (!Page1GSupport) { + TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1; + } else { + TotalPagesNum = NumberOfPml4EntriesNeeded + 1; + } + TotalPagesNum += ExtraPageTablePages; + + return EFI_PAGES_TO_SIZE (TotalPagesNum); +} + +/** + Allocates and fills in the Page Directory and Page Table Entries to + establish a 4G page table. + + @param[in] PageTablesAddress The base address of page table. + @param[in] Page1GSupport 1G page support or not. + +**/ +VOID +Create4GPageTables ( + IN EFI_PHYSICAL_ADDRESS PageTablesAddress, + IN BOOLEAN Page1GSupport + ) +{ + UINT8 PhysicalAddressBits; + EFI_PHYSICAL_ADDRESS PageAddress; + UINTN IndexOfPml4Entries; + UINTN IndexOfPdpEntries; + UINTN IndexOfPageDirectoryEntries; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMap; + PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + UINTN BigPageAddress; + PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry; + UINT64 AddressEncMask; + + // + // Make sure AddressEncMask is contained to smallest supported address field. + // + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + // + // Create 4G page table by default, + // and let PF handler to handle > 4G request. + // + PhysicalAddressBits = 32; + + // + // Calculate the table entries needed. + // + if (PhysicalAddressBits <= 39 ) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30)); + } else { + NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39)); + NumberOfPdpEntriesNeeded = 512; + } + + // + // Pre-allocate big pages to avoid later allocations. + // + BigPageAddress = (UINTN) PageTablesAddress; + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + PageMap = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + PageMapLevel4Entry = PageMap; + PageAddress = 0; + for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) { + // + // Each PML4 entry points to a page of Page Directory Pointer entires. + // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop. + // + PageDirectoryPointerEntry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + // + // Make a PML4 Entry + // + PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask; + PageMapLevel4Entry->Bits.ReadWrite = 1; + PageMapLevel4Entry->Bits.Present = 1; + + if (Page1GSupport) { + PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) { + // + // Fill in the Page Directory entries + // + PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectory1GEntry->Bits.ReadWrite = 1; + PageDirectory1GEntry->Bits.Present = 1; + PageDirectory1GEntry->Bits.MustBe1 = 1; + } + } else { + for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { + // + // Each Directory Pointer entries points to a page of Page Directory entires. + // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop. + // + PageDirectoryEntry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + // + // Fill in a Page Directory Pointer Entries + // + PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask; + PageDirectoryPointerEntry->Bits.ReadWrite = 1; + PageDirectoryPointerEntry->Bits.Present = 1; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) { + // + // Fill in the Page Directory entries + // + PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectoryEntry->Bits.ReadWrite = 1; + PageDirectoryEntry->Bits.Present = 1; + PageDirectoryEntry->Bits.MustBe1 = 1; + } + } + + for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { + ZeroMem ( + PageDirectoryPointerEntry, + sizeof(PAGE_MAP_AND_DIRECTORY_POINTER) + ); + } + } + } + + // + // For the PML4 entries we are not using fill in a null entry. + // + for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) { + ZeroMem ( + PageMapLevel4Entry, + sizeof (PAGE_MAP_AND_DIRECTORY_POINTER) + ); + } +} + +/** + Return function from long mode to 32-bit mode. + + @param EntrypointContext Context for mode switching + @param ReturnContext Context for mode switching + +**/ +VOID +ReturnFunction ( + SWITCH_32_TO_64_CONTEXT *EntrypointContext, + SWITCH_64_TO_32_CONTEXT *ReturnContext + ) +{ + // + // Restore original GDT + // + AsmWriteGdtr (&ReturnContext->Gdtr); + + // + // return to original caller + // + LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)EntrypointContext->JumpBuffer, 1); + + // + // never be here + // + ASSERT (FALSE); +} + +/** + Thunk function from 32-bit protection mode to long mode. + + @param PageTableAddress Page table base address + @param Context Context for mode switching + @param ReturnContext Context for mode switching + + @retval EFI_SUCCESS Function successfully executed. + +**/ +EFI_STATUS +Thunk32To64 ( + EFI_PHYSICAL_ADDRESS PageTableAddress, + SWITCH_32_TO_64_CONTEXT *Context, + SWITCH_64_TO_32_CONTEXT *ReturnContext + ) +{ + UINTN SetJumpFlag; + EFI_STATUS Status; + + // + // Save return address, LongJump will return here then + // + SetJumpFlag = SetJump ((BASE_LIBRARY_JUMP_BUFFER *) (UINTN) Context->JumpBuffer); + + if (SetJumpFlag == 0) { + + // + // Build 4G Page Tables. + // + Create4GPageTables (PageTableAddress, Context->Page1GSupport); + + // + // Create 64-bit GDT + // + AsmWriteGdtr (&mGdt); + + // + // Write CR3 + // + AsmWriteCr3 ((UINTN) PageTableAddress); + + DEBUG (( + DEBUG_INFO, + "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n", + __FUNCTION__, + Context->StackBufferBase, + Context->StackBufferLength + )); + + // + // Disable interrupt of Debug timer, since the IDT table cannot work in long mode + // + SaveAndSetDebugTimerInterrupt (FALSE); + // + // Transfer to long mode + // + AsmEnablePaging64 ( + 0x38, + (UINT64) Context->EntryPoint, + (UINT64)(UINTN) Context, + (UINT64)(UINTN) ReturnContext, + Context->StackBufferBase + Context->StackBufferLength + ); + } + + // + // Convert to 32-bit Status and return + // + Status = EFI_SUCCESS; + if ((UINTN) ReturnContext->ReturnStatus != 0) { + Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus); + } + + return Status; +} + +/** + If in 32 bit protection mode, and coalesce image is of X64, switch to long mode. + + @param LongModeBuffer The context of long mode. + @param CoalesceEntry Entry of coalesce image. + @param BlockListAddr Address of block list. + @param MemoryResource Pointer to the buffer of memory resource descriptor. + @param MemoryBase Base of memory range. + @param MemorySize Size of memory range. + + @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce. + @retval Others Failed to execute coalesce in long mode. + +**/ +EFI_STATUS +ModeSwitch ( + IN EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer, + IN COALESCE_ENTRY CoalesceEntry, + IN EFI_PHYSICAL_ADDRESS BlockListAddr, + IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS MemoryBase64; + UINT64 MemorySize64; + EFI_PHYSICAL_ADDRESS MemoryEnd64; + SWITCH_32_TO_64_CONTEXT Context; + SWITCH_64_TO_32_CONTEXT ReturnContext; + BASE_LIBRARY_JUMP_BUFFER JumpBuffer; + EFI_PHYSICAL_ADDRESS ReservedRangeBase; + EFI_PHYSICAL_ADDRESS ReservedRangeEnd; + BOOLEAN Page1GSupport; + + ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT)); + ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT)); + + MemoryBase64 = (UINT64) (UINTN) *MemoryBase; + MemorySize64 = (UINT64) (UINTN) *MemorySize; + MemoryEnd64 = MemoryBase64 + MemorySize64; + + Page1GSupport = IsPage1GSupport (); + + // + // Merge memory range reserved for stack and page table + // + if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) { + ReservedRangeBase = LongModeBuffer->StackBaseAddress; + ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize (Page1GSupport); + } else { + ReservedRangeBase = LongModeBuffer->PageTableAddress; + ReservedRangeEnd = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize; + } + + // + // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize. + // If they are overlapped, get a larger range to process capsule data. + // + if (ReservedRangeBase <= MemoryBase64) { + if (ReservedRangeEnd < MemoryEnd64) { + MemoryBase64 = ReservedRangeEnd; + } else { + DEBUG ((DEBUG_ERROR, "Memory is not enough to process capsule!\n")); + return EFI_OUT_OF_RESOURCES; + } + } else if (ReservedRangeBase < MemoryEnd64) { + if (ReservedRangeEnd < MemoryEnd64 && + ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) { + MemoryBase64 = ReservedRangeEnd; + } else { + MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64); + } + } + + // + // Initialize context jumping to 64-bit enviroment + // + Context.JumpBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer; + Context.StackBufferBase = LongModeBuffer->StackBaseAddress; + Context.StackBufferLength = LongModeBuffer->StackSize; + Context.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry; + Context.BlockListAddr = BlockListAddr; + Context.MemoryResource = (EFI_PHYSICAL_ADDRESS)(UINTN)MemoryResource; + Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64; + Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64; + Context.Page1GSupport = Page1GSupport; + Context.AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + // + // Prepare data for return back + // + ReturnContext.ReturnCs = 0x10; + ReturnContext.ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction; + // + // Will save the return status of processing capsule + // + ReturnContext.ReturnStatus = 0; + + // + // Save original GDT + // + AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr); + + Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext); + + if (!EFI_ERROR (Status)) { + *MemoryBase = (VOID *) (UINTN) MemoryBase64; + *MemorySize = (UINTN) MemorySize64; + } + + return Status; + +} + +/** + Locates the coalesce image entry point, and detects its machine type. + + @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output. + @param CoalesceImageMachineType Pointer to machine type of coalesce image. + + @retval EFI_SUCCESS Coalesce image successfully located. + @retval Others Failed to locate the coalesce image. + +**/ +EFI_STATUS +FindCapsuleCoalesceImage ( + OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint, + OUT UINT16 *CoalesceImageMachineType + ) +{ + EFI_STATUS Status; + UINTN Instance; + EFI_PEI_LOAD_FILE_PPI *LoadFile; + EFI_PEI_FV_HANDLE VolumeHandle; + EFI_PEI_FILE_HANDLE FileHandle; + EFI_PHYSICAL_ADDRESS CoalesceImageAddress; + UINT64 CoalesceImageSize; + UINT32 AuthenticationState; + + Instance = 0; + + while (TRUE) { + Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle); + if (EFI_ERROR (Status)) { + return Status; + } + Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle); + if (!EFI_ERROR (Status)) { + Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile); + ASSERT_EFI_ERROR (Status); + + Status = LoadFile->LoadFile ( + LoadFile, + FileHandle, + &CoalesceImageAddress, + &CoalesceImageSize, + CoalesceImageEntryPoint, + &AuthenticationState + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status)); + return Status; + } + *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress); + break; + } else { + continue; + } + } + + return Status; +} + +/** + Gets the reserved long mode buffer. + + @param LongModeBuffer Pointer to the long mode buffer for output. + + @retval EFI_SUCCESS Long mode buffer successfully retrieved. + @retval Others Variable storing long mode buffer not found. + +**/ +EFI_STATUS +GetLongModeContext ( + OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer + ) +{ + EFI_STATUS Status; + UINTN Size; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **) &PPIVariableServices + ); + ASSERT_EFI_ERROR (Status); + + Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER); + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + EFI_CAPSULE_LONG_MODE_BUFFER_NAME, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + LongModeBuffer + ); + if (EFI_ERROR (Status)) { + DEBUG (( DEBUG_ERROR, "Error Get LongModeBuffer variable %r!\n", Status)); + } + return Status; +} +#endif + +#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) +/** + Get physical address bits. + + @return Physical address bits. + +**/ +UINT8 +GetPhysicalAddressBits ( + VOID + ) +{ + UINT32 RegEax; + UINT8 PhysicalAddressBits; + VOID *Hob; + + // + // Get physical address bits supported. + // + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + } else { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + } + + // + // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. + // + ASSERT (PhysicalAddressBits <= 52); + if (PhysicalAddressBits > 48) { + PhysicalAddressBits = 48; + } + + return PhysicalAddressBits; +} +#endif + +/** + Sort memory resource entries based upon PhysicalStart, from low to high. + + @param[in, out] MemoryResource A pointer to the memory resource entry buffer. + +**/ +VOID +SortMemoryResourceDescriptor ( + IN OUT MEMORY_RESOURCE_DESCRIPTOR *MemoryResource + ) +{ + MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEntry; + MEMORY_RESOURCE_DESCRIPTOR *NextMemoryResourceEntry; + MEMORY_RESOURCE_DESCRIPTOR TempMemoryResource; + + MemoryResourceEntry = MemoryResource; + NextMemoryResourceEntry = MemoryResource + 1; + while (MemoryResourceEntry->ResourceLength != 0) { + while (NextMemoryResourceEntry->ResourceLength != 0) { + if (MemoryResourceEntry->PhysicalStart > NextMemoryResourceEntry->PhysicalStart) { + CopyMem (&TempMemoryResource, MemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR)); + CopyMem (MemoryResourceEntry, NextMemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR)); + CopyMem (NextMemoryResourceEntry, &TempMemoryResource, sizeof (MEMORY_RESOURCE_DESCRIPTOR)); + } + + NextMemoryResourceEntry = NextMemoryResourceEntry + 1; + } + + MemoryResourceEntry = MemoryResourceEntry + 1; + NextMemoryResourceEntry = MemoryResourceEntry + 1; + } +} + +/** + Merge continous memory resource entries. + + @param[in, out] MemoryResource A pointer to the memory resource entry buffer. + +**/ +VOID +MergeMemoryResourceDescriptor ( + IN OUT MEMORY_RESOURCE_DESCRIPTOR *MemoryResource + ) +{ + MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEntry; + MEMORY_RESOURCE_DESCRIPTOR *NewMemoryResourceEntry; + MEMORY_RESOURCE_DESCRIPTOR *NextMemoryResourceEntry; + MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEnd; + + MemoryResourceEntry = MemoryResource; + NewMemoryResourceEntry = MemoryResource; + while (MemoryResourceEntry->ResourceLength != 0) { + CopyMem (NewMemoryResourceEntry, MemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR)); + NextMemoryResourceEntry = MemoryResourceEntry + 1; + + while ((NextMemoryResourceEntry->ResourceLength != 0) && + (NextMemoryResourceEntry->PhysicalStart == (MemoryResourceEntry->PhysicalStart + MemoryResourceEntry->ResourceLength))) { + MemoryResourceEntry->ResourceLength += NextMemoryResourceEntry->ResourceLength; + if (NewMemoryResourceEntry != MemoryResourceEntry) { + NewMemoryResourceEntry->ResourceLength += NextMemoryResourceEntry->ResourceLength; + } + + NextMemoryResourceEntry = NextMemoryResourceEntry + 1; + } + + MemoryResourceEntry = NextMemoryResourceEntry; + NewMemoryResourceEntry = NewMemoryResourceEntry + 1; + } + + // + // Set NULL terminate memory resource descriptor after merging. + // + MemoryResourceEnd = NewMemoryResourceEntry; + ZeroMem (MemoryResourceEnd, sizeof (MEMORY_RESOURCE_DESCRIPTOR)); +} + +/** + Build memory resource descriptor from resource descriptor in HOB list. + + @return Pointer to the buffer of memory resource descriptor. + NULL if no memory resource descriptor reported in HOB list + before capsule Coalesce. + +**/ +MEMORY_RESOURCE_DESCRIPTOR * +BuildMemoryResourceDescriptor ( + VOID + ) +{ + EFI_PEI_HOB_POINTERS Hob; + UINTN Index; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor; + MEMORY_RESOURCE_DESCRIPTOR *MemoryResource; + EFI_STATUS Status; + + // + // Get the count of memory resource descriptor. + // + Index = 0; + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); + while (Hob.Raw != NULL) { + ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw; + if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) { + Index++; + } + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw); + } + + if (Index == 0) { + DEBUG ((DEBUG_INFO | DEBUG_WARN, "No memory resource descriptor reported in HOB list before capsule Coalesce\n")); +#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) + // + // Allocate memory to hold memory resource descriptor, + // include extra one NULL terminate memory resource descriptor. + // + Status = PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource); + ASSERT_EFI_ERROR (Status); + ZeroMem (MemoryResource, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR)); + + MemoryResource[0].PhysicalStart = 0; + MemoryResource[0].ResourceLength = LShiftU64 (1, GetPhysicalAddressBits ()); + DEBUG ((DEBUG_INFO, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n", + MemoryResource[0x0].PhysicalStart, MemoryResource[0x0].ResourceLength)); + return MemoryResource; +#else + return NULL; +#endif + } + + // + // Allocate memory to hold memory resource descriptor, + // include extra one NULL terminate memory resource descriptor. + // + Status = PeiServicesAllocatePool ((Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource); + ASSERT_EFI_ERROR (Status); + ZeroMem (MemoryResource, (Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR)); + + // + // Get the content of memory resource descriptor. + // + Index = 0; + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); + while (Hob.Raw != NULL) { + ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw; + if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) { + DEBUG ((DEBUG_INFO, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n", + Index, ResourceDescriptor->PhysicalStart, ResourceDescriptor->ResourceLength)); + MemoryResource[Index].PhysicalStart = ResourceDescriptor->PhysicalStart; + MemoryResource[Index].ResourceLength = ResourceDescriptor->ResourceLength; + Index++; + } + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw); + } + + SortMemoryResourceDescriptor (MemoryResource); + MergeMemoryResourceDescriptor (MemoryResource); + + DEBUG ((DEBUG_INFO, "Dump MemoryResource[] after sorted and merged\n")); + for (Index = 0; MemoryResource[Index].ResourceLength != 0; Index++) { + DEBUG (( + DEBUG_INFO, + " MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n", + Index, + MemoryResource[Index].PhysicalStart, + MemoryResource[Index].ResourceLength + )); + } + + return MemoryResource; +} + +/** + Check if the capsules are staged. + + @retval TRUE The capsules are staged. + @retval FALSE The capsules are not staged. + +**/ +BOOLEAN +AreCapsulesStaged ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Size; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + EFI_PHYSICAL_ADDRESS CapsuleDataPtr64; + + CapsuleDataPtr64 = 0; + + Status = PeiServicesLocatePpi( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **)&PPIVariableServices + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to find ReadOnlyVariable2PPI\n")); + return FALSE; + } + + // + // Check for Update capsule + // + Size = sizeof (CapsuleDataPtr64); + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + EFI_CAPSULE_VARIABLE_NAME, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + (VOID *)&CapsuleDataPtr64 + ); + + if (!EFI_ERROR (Status)) { + return TRUE; + } + + return FALSE; +} + +/** + Check all the variables for SG list heads and get the count and addresses. + + @param ListLength A pointer would return the SG list length. + @param HeadList A ponter to the capsule SG list. + + @retval EFI_SUCCESS a valid capsule is present + @retval EFI_NOT_FOUND if a valid capsule is not present + @retval EFI_INVALID_PARAMETER the input parameter is invalid + @retval EFI_OUT_OF_RESOURCES fail to allocate memory + +**/ +EFI_STATUS +GetScatterGatherHeadEntries ( + OUT UINTN *ListLength, + OUT EFI_PHYSICAL_ADDRESS **HeadList + ) +{ + EFI_STATUS Status; + UINTN Size; + UINTN Index; + UINTN TempIndex; + UINTN ValidIndex; + BOOLEAN Flag; + CHAR16 CapsuleVarName[30]; + CHAR16 *TempVarName; + EFI_PHYSICAL_ADDRESS CapsuleDataPtr64; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + EFI_PHYSICAL_ADDRESS *TempList; + EFI_PHYSICAL_ADDRESS *EnlargedTempList; + UINTN TempListLength; + + Index = 0; + TempVarName = NULL; + CapsuleVarName[0] = 0; + ValidIndex = 0; + CapsuleDataPtr64 = 0; + + if ((ListLength == NULL) || (HeadList == NULL)) { + DEBUG ((DEBUG_ERROR, "%a Invalid parameters. Inputs can't be NULL\n", __FUNCTION__)); + ASSERT (ListLength != NULL); + ASSERT (HeadList != NULL); + return EFI_INVALID_PARAMETER; + } + + *ListLength = 0; + *HeadList = NULL; + + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **)&PPIVariableServices + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to find ReadOnlyVariable2PPI\n")); + return Status; + } + + // + // Allocate memory for sg list head + // + TempListLength = DEFAULT_SG_LIST_HEADS * sizeof (EFI_PHYSICAL_ADDRESS); + TempList = AllocateZeroPool (TempListLength); + if (TempList == NULL) { + DEBUG((DEBUG_ERROR, "Failed to allocate memory\n")); + return EFI_OUT_OF_RESOURCES; + } + + // + // setup var name buffer for update capsules + // + StrCpyS (CapsuleVarName, sizeof (CapsuleVarName) / sizeof (CHAR16), EFI_CAPSULE_VARIABLE_NAME); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + while (TRUE) { + if (Index != 0) { + UnicodeValueToStringS ( + TempVarName, + (sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName)), + 0, + Index, + 0 + ); + } + Size = sizeof (CapsuleDataPtr64); + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + CapsuleVarName, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + (VOID *)&CapsuleDataPtr64 + ); + + if (EFI_ERROR (Status)) { + if (Status != EFI_NOT_FOUND) { + DEBUG ((DEBUG_ERROR, "Unexpected error getting Capsule Update variable. Status = %r\n")); + } + break; + } + + // + // If this BlockList has been linked before, skip this variable + // + Flag = FALSE; + for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) { + if (TempList[TempIndex] == CapsuleDataPtr64) { + Flag = TRUE; + break; + } + } + if (Flag) { + Index++; + continue; + } + + // + // The TempList is full, enlarge it + // + if ((ValidIndex + 1) >= TempListLength) { + EnlargedTempList = AllocateZeroPool (TempListLength * 2); + if (EnlargedTempList == NULL) { + DEBUG ((DEBUG_ERROR, "Fail to allocate memory!\n")); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (EnlargedTempList, TempList, TempListLength); + FreePool (TempList); + TempList = EnlargedTempList; + TempListLength *= 2; + } + + // + // add it to the cached list + // + TempList[ValidIndex++] = CapsuleDataPtr64; + Index++; + } + + if (ValidIndex == 0) { + DEBUG ((DEBUG_ERROR, "%a didn't find any SG lists in variables\n", __FUNCTION__)); + return EFI_NOT_FOUND; + } + + *HeadList = AllocateZeroPool ((ValidIndex + 1) * sizeof (EFI_PHYSICAL_ADDRESS)); + if (*HeadList == NULL) { + DEBUG ((DEBUG_ERROR, "Failed to allocate memory\n")); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (*HeadList, TempList, (ValidIndex) * sizeof (EFI_PHYSICAL_ADDRESS)); + *ListLength = ValidIndex; + + return EFI_SUCCESS; +} + +/** + Capsule PPI service to coalesce a fragmented capsule in memory. + + @param PeiServices General purpose services available to every PEIM. + @param MemoryBase Pointer to the base of a block of memory that we can walk + all over while trying to coalesce our buffers. + On output, this variable will hold the base address of + a coalesced capsule. + @param MemorySize Size of the memory region pointed to by MemoryBase. + On output, this variable will contain the size of the + coalesced capsule. + + @retval EFI_NOT_FOUND if we can't determine the boot mode + if the boot mode is not flash-update + if we could not find the capsule descriptors + + @retval EFI_BUFFER_TOO_SMALL + if we could not coalesce the capsule in the memory + region provided to us + + @retval EFI_SUCCESS if there's no capsule, or if we processed the + capsule successfully. +**/ +EFI_STATUS +EFIAPI +CapsuleCoalesce ( + IN EFI_PEI_SERVICES **PeiServices, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + UINTN ListLength; + EFI_PHYSICAL_ADDRESS *VariableArrayAddress; + MEMORY_RESOURCE_DESCRIPTOR *MemoryResource; +#ifdef MDE_CPU_IA32 + UINT16 CoalesceImageMachineType; + EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint; + COALESCE_ENTRY CoalesceEntry; + EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer; +#endif + + ListLength = 0; + VariableArrayAddress = NULL; + + // + // Someone should have already ascertained the boot mode. If it's not + // capsule update, then return normally. + // + Status = PeiServicesGetBootMode (&BootMode); + if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) { + DEBUG ((DEBUG_ERROR, "Boot mode is not correct for capsule update path.\n")); + Status = EFI_NOT_FOUND; + goto Done; + } + + // + // Get SG list entries + // + Status = GetScatterGatherHeadEntries (&ListLength, &VariableArrayAddress); + if (EFI_ERROR (Status) || VariableArrayAddress == NULL) { + DEBUG ((DEBUG_ERROR, "%a failed to get Scatter Gather List Head Entries. Status = %r\n", __FUNCTION__, Status)); + goto Done; + } + + MemoryResource = BuildMemoryResourceDescriptor (); + +#ifdef MDE_CPU_IA32 + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + // + // Switch to 64-bit mode to process capsule data when: + // 1. When DXE phase is 64-bit + // 2. When the buffer for 64-bit transition exists + // 3. When Capsule X64 image is built in BIOS image + // In 64-bit mode, we can process capsule data above 4GB. + // + CoalesceImageEntryPoint = 0; + Status = GetLongModeContext (&LongModeBuffer); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Fail to find the variable for long mode context!\n")); + Status = EFI_NOT_FOUND; + goto Done; + } + + Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType); + if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) { + DEBUG ((DEBUG_ERROR, "Fail to find CapsuleX64 module in FV!\n")); + Status = EFI_NOT_FOUND; + goto Done; + } + ASSERT (CoalesceImageEntryPoint != 0); + CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint; + Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize); + } else { + // + // Capsule is processed in IA32 mode. + // + Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize); + } +#else + // + // Process capsule directly. + // + Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize); +#endif + + DEBUG ((DEBUG_INFO, "Capsule Coalesce Status = %r!\n", Status)); + + if (Status == EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_ERROR, "There is not enough memory to process capsule!\n")); + } + + if (Status == EFI_NOT_FOUND) { + DEBUG ((DEBUG_ERROR, "Fail to parse capsule descriptor in memory!\n")); + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR) + ); + } + +Done: + return Status; +} + +/** + Determine if we're in capsule update boot mode. + + @param PeiServices PEI services table + + @retval EFI_SUCCESS if we have a capsule available + @retval EFI_NOT_FOUND no capsule detected + +**/ +EFI_STATUS +EFIAPI +CheckCapsuleUpdate ( + IN EFI_PEI_SERVICES **PeiServices + ) +{ + if (AreCapsulesStaged ()) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} +/** + This function will look at a capsule and determine if it's a test pattern. + If it is, then it will verify it and emit an error message if corruption is detected. + + @param PeiServices Standard pei services pointer + @param CapsuleBase Base address of coalesced capsule, which is preceeded + by private data. Very implementation specific. + + @retval TRUE Capsule image is the test image + @retval FALSE Capsule image is not the test image. + +**/ +BOOLEAN +CapsuleTestPattern ( + IN EFI_PEI_SERVICES **PeiServices, + IN VOID *CapsuleBase + ) +{ + UINT32 *TestPtr; + UINT32 TestCounter; + UINT32 TestSize; + BOOLEAN RetValue; + + RetValue = FALSE; + + // + // Look at the capsule data and determine if it's a test pattern. If it + // is, then test it now. + // + TestPtr = (UINT32 *) CapsuleBase; + // + // 0x54534554 "TEST" + // + if (*TestPtr == 0x54534554) { + RetValue = TRUE; + DEBUG ((DEBUG_INFO, "Capsule test pattern mode activated...\n")); + TestSize = TestPtr[1] / sizeof (UINT32); + // + // Skip over the signature and the size fields in the pattern data header + // + TestPtr += 2; + TestCounter = 0; + while (TestSize > 0) { + if (*TestPtr != TestCounter) { + DEBUG ((DEBUG_INFO, "Capsule test pattern mode FAILED: BaseAddr/FailAddr 0x%X 0x%X\n", (UINT32)(UINTN)(EFI_CAPSULE_PEIM_PRIVATE_DATA *)CapsuleBase, (UINT32)(UINTN)TestPtr)); + return TRUE; + } + + TestPtr++; + TestCounter++; + TestSize--; + } + + DEBUG ((DEBUG_INFO, "Capsule test pattern mode SUCCESS\n")); + } + + return RetValue; +} + +/** + Capsule PPI service that gets called after memory is available. The + capsule coalesce function, which must be called first, returns a base + address and size, which can be anything actually. Once the memory init + PEIM has discovered memory, then it should call this function and pass in + the base address and size returned by the coalesce function. Then this + function can create a capsule HOB and return. + + @param PeiServices standard pei services pointer + @param CapsuleBase address returned by the capsule coalesce function. Most + likely this will actually be a pointer to private data. + @param CapsuleSize value returned by the capsule coalesce function. + + @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a + coalesced capsule + @retval EFI_SUCCESS if all goes well. +**/ +EFI_STATUS +EFIAPI +CreateState ( + IN EFI_PEI_SERVICES **PeiServices, + IN VOID *CapsuleBase, + IN UINTN CapsuleSize + ) +{ + EFI_STATUS Status; + EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateData; + UINTN Size; + EFI_PHYSICAL_ADDRESS NewBuffer; + UINTN CapsuleNumber; + UINT32 Index; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT64 Length; + + PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase; + if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) { + return EFI_VOLUME_CORRUPTED; + } + if (PrivateData->CapsuleAllImageSize >= MAX_ADDRESS) { + DEBUG ((DEBUG_ERROR, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData->CapsuleAllImageSize)); + return EFI_OUT_OF_RESOURCES; + } + if (PrivateData->CapsuleNumber >= MAX_ADDRESS) { + DEBUG ((DEBUG_ERROR, "CapsuleNumber too big - 0x%lx\n", PrivateData->CapsuleNumber)); + return EFI_OUT_OF_RESOURCES; + } + // + // Capsule Number and Capsule Offset is in the tail of Capsule data. + // + Size = (UINTN)PrivateData->CapsuleAllImageSize; + CapsuleNumber = (UINTN)PrivateData->CapsuleNumber; + // + // Allocate the memory so that it gets preserved into DXE + // + Status = PeiServicesAllocatePages ( + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES (Size), + &NewBuffer + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_ERROR, "AllocatePages Failed!\n")); + return Status; + } + // + // Copy to our new buffer for DXE + // + DEBUG ((DEBUG_INFO, "Capsule copy from 0x%8X to 0x%8X with size 0x%8X\n", (UINTN)((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), (UINTN) NewBuffer, Size)); + CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) ((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), Size); + // + // Check for test data pattern. If it is the test pattern, then we'll + // test it and still create the HOB so that it can be used to verify + // that capsules don't get corrupted all the way into BDS. BDS will + // still try to turn it into a firmware volume, but will think it's + // corrupted so nothing will happen. + // + DEBUG_CODE ( + CapsuleTestPattern (PeiServices, (VOID *) (UINTN) NewBuffer); + ); + + // + // Build the UEFI Capsule Hob for each capsule image. + // + for (Index = 0; Index < CapsuleNumber; Index ++) { + BaseAddress = NewBuffer + PrivateData->CapsuleOffset[Index]; + Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize; + + BuildCvHob (BaseAddress, Length); + } + + return EFI_SUCCESS; +} + +CONST EFI_PEI_CAPSULE_PPI mCapsulePpi = { + CapsuleCoalesce, + CheckCapsuleUpdate, + CreateState +}; + +CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiCapsulePpiGuid, + (EFI_PEI_CAPSULE_PPI *) &mCapsulePpi +}; + +/** + Entry point function for the PEIM + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @return EFI_SUCCESS If we installed our PPI + +**/ +EFI_STATUS +EFIAPI +CapsuleMain ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + // + // Just produce our PPI + // + return PeiServicesInstallPpi (&mUefiPpiListCapsule); +} diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm b/roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm new file mode 100644 index 000000000..6adcc4c37 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.nasm @@ -0,0 +1,81 @@ +;; @file +; This is the assembly code for page fault handler hook. +; +; Copyright (c) 2015, Intel Corporation. All rights reserved.
+; +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;; + +extern ASM_PFX(PageFaultHandler) + + DEFAULT REL + SECTION .text + +global ASM_PFX(PageFaultHandlerHook) +ASM_PFX(PageFaultHandlerHook): + add rsp, -0x10 + ; save rax + mov [rsp + 0x8], rax + + ;push rax ; save all volatile registers + push rcx + push rdx + push r8 + push r9 + push r10 + push r11 + ; save volatile fp registers + ; 68h + 08h(for alignment) + add rsp, -0x70 + stmxcsr [rsp + 0x60] + movdqa [rsp + 0x0], xmm0 + movdqa [rsp + 0x10], xmm1 + movdqa [rsp + 0x20], xmm2 + movdqa [rsp + 0x30], xmm3 + movdqa [rsp + 0x40], xmm4 + movdqa [rsp + 0x50], xmm5 + + add rsp, -0x20 + call ASM_PFX(PageFaultHandler) + add rsp, 0x20 + + ; load volatile fp registers + ldmxcsr [rsp + 0x60] + movdqa xmm0, [rsp + 0x0] + movdqa xmm1, [rsp + 0x10] + movdqa xmm2, [rsp + 0x20] + movdqa xmm3, [rsp + 0x30] + movdqa xmm4, [rsp + 0x40] + movdqa xmm5, [rsp + 0x50] + add rsp, 0x70 + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdx + pop rcx + ;pop rax ; restore all volatile registers + + add rsp, 0x10 + + ; rax returned from PageFaultHandler is NULL or OriginalHandler address + ; NULL if the page fault is handled by PageFaultHandler + ; OriginalHandler address if the page fault is not handled by PageFaultHandler + test rax, rax + + ; save OriginalHandler address + mov [rsp - 0x10], rax + ; restore rax + mov rax, [rsp - 0x8] + + jz .0 + + ; jump to OriginalHandler + jmp qword [rsp - 0x10] + +.0: + add rsp, 0x8 ; skip error code for PF + iretq + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c b/roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c new file mode 100644 index 000000000..4a0567fa7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c @@ -0,0 +1,305 @@ +/** @file + The X64 entrypoint is used to process capsule in long mode. + +Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include "CommonHeader.h" + +#define EXCEPTION_VECTOR_NUMBER 0x22 + +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_PS BIT7 + +typedef struct _PAGE_FAULT_CONTEXT { + BOOLEAN Page1GSupport; + UINT64 PhyMask; + UINTN PageFaultBuffer; + UINTN PageFaultIndex; + UINT64 AddressEncMask; + // + // Store the uplink information for each page being used. + // + UINT64 *PageFaultUplink[EXTRA_PAGE_TABLE_PAGES]; + VOID *OriginalHandler; +} PAGE_FAULT_CONTEXT; + +typedef struct _PAGE_FAULT_IDT_TABLE { + PAGE_FAULT_CONTEXT PageFaultContext; + IA32_IDT_GATE_DESCRIPTOR IdtEntryTable[EXCEPTION_VECTOR_NUMBER]; +} PAGE_FAULT_IDT_TABLE; + +/** + Page fault handler. + +**/ +VOID +EFIAPI +PageFaultHandlerHook ( + VOID + ); + +/** + Hook IDT with our page fault handler so that the on-demand paging works on page fault. + + @param[in, out] IdtEntry Pointer to IDT entry. + @param[in, out] PageFaultContext Pointer to page fault context. + +**/ +VOID +HookPageFaultHandler ( + IN OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry, + IN OUT PAGE_FAULT_CONTEXT *PageFaultContext + ) +{ + UINT32 RegEax; + UINT8 PhysicalAddressBits; + UINTN PageFaultHandlerHookAddress; + + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + PageFaultContext->PhyMask = LShiftU64 (1, PhysicalAddressBits) - 1; + PageFaultContext->PhyMask &= (1ull << 48) - SIZE_4KB; + + // + // Set Page Fault entry to catch >4G access + // + PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook; + PageFaultContext->OriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16)); + IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress; + IdtEntry->Bits.Selector = (UINT16)AsmReadCs (); + IdtEntry->Bits.Reserved_0 = 0; + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16); + IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32); + IdtEntry->Bits.Reserved_1 = 0; + + if (PageFaultContext->Page1GSupport) { + PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(2); + }else { + PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(6); + } + PageFaultContext->PageFaultIndex = 0; + ZeroMem (PageFaultContext->PageFaultUplink, sizeof (PageFaultContext->PageFaultUplink)); +} + +/** + Acquire page for page fault. + + @param[in, out] PageFaultContext Pointer to page fault context. + @param[in, out] Uplink Pointer to up page table entry. + +**/ +VOID +AcquirePage ( + IN OUT PAGE_FAULT_CONTEXT *PageFaultContext, + IN OUT UINT64 *Uplink + ) +{ + UINTN Address; + UINT64 AddressEncMask; + + Address = PageFaultContext->PageFaultBuffer + EFI_PAGES_TO_SIZE (PageFaultContext->PageFaultIndex); + ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1)); + + AddressEncMask = PageFaultContext->AddressEncMask; + + // + // Cut the previous uplink if it exists and wasn't overwritten. + // + if ((PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] != NULL) && + ((*PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] & ~AddressEncMask & PageFaultContext->PhyMask) == Address)) { + *PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = 0; + } + + // + // Link & Record the current uplink. + // + *Uplink = Address | AddressEncMask | IA32_PG_P | IA32_PG_RW; + PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = Uplink; + + PageFaultContext->PageFaultIndex = (PageFaultContext->PageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES; +} + +/** + The page fault handler that on-demand read >4G memory/MMIO. + + @retval NULL The page fault is correctly handled. + @retval OriginalHandler The page fault is not handled and is passed through to original handler. + +**/ +VOID * +EFIAPI +PageFaultHandler ( + VOID + ) +{ + IA32_DESCRIPTOR Idtr; + PAGE_FAULT_CONTEXT *PageFaultContext; + UINT64 PhyMask; + UINT64 *PageTable; + UINT64 PFAddress; + UINTN PTIndex; + UINT64 AddressEncMask; + + // + // Get the IDT Descriptor. + // + AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr); + // + // Then get page fault context by IDT Descriptor. + // + PageFaultContext = (PAGE_FAULT_CONTEXT *) (UINTN) (Idtr.Base - sizeof (PAGE_FAULT_CONTEXT)); + PhyMask = PageFaultContext->PhyMask; + AddressEncMask = PageFaultContext->AddressEncMask; + + PFAddress = AsmReadCr2 (); + DEBUG ((EFI_D_ERROR, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress)); + + if (PFAddress >= PhyMask + SIZE_4KB) { + return PageFaultContext->OriginalHandler; + } + PFAddress &= PhyMask; + + PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & PhyMask); + + PTIndex = BitFieldRead64 (PFAddress, 39, 47); + // PML4E + if ((PageTable[PTIndex] & IA32_PG_P) == 0) { + AcquirePage (PageFaultContext, &PageTable[PTIndex]); + } + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask); + PTIndex = BitFieldRead64 (PFAddress, 30, 38); + // PDPTE + if (PageFaultContext->Page1GSupport) { + PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS; + } else { + if ((PageTable[PTIndex] & IA32_PG_P) == 0) { + AcquirePage (PageFaultContext, &PageTable[PTIndex]); + } + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask); + PTIndex = BitFieldRead64 (PFAddress, 21, 29); + // PD + PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS; + } + + return NULL; +} + + +/** + The X64 entrypoint is used to process capsule in long mode then + return to 32-bit protected mode. + + @param EntrypointContext Pointer to the context of long mode. + @param ReturnContext Pointer to the context of 32-bit protected mode. + + @retval This function should never return actually. + +**/ +EFI_STATUS +EFIAPI +_ModuleEntryPoint ( + SWITCH_32_TO_64_CONTEXT *EntrypointContext, + SWITCH_64_TO_32_CONTEXT *ReturnContext +) +{ + EFI_STATUS Status; + IA32_DESCRIPTOR Ia32Idtr; + IA32_DESCRIPTOR X64Idtr; + PAGE_FAULT_IDT_TABLE PageFaultIdtTable; + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + + // + // Save the IA32 IDT Descriptor + // + AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); + + // + // Setup X64 IDT table + // + ZeroMem (PageFaultIdtTable.IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER); + X64Idtr.Base = (UINTN) PageFaultIdtTable.IdtEntryTable; + X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER - 1); + AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr); + + // + // Setup the default CPU exception handlers + // + Status = InitializeCpuExceptionHandlers (NULL); + ASSERT_EFI_ERROR (Status); + + // + // Hook page fault handler to handle >4G request. + // + PageFaultIdtTable.PageFaultContext.Page1GSupport = EntrypointContext->Page1GSupport; + PageFaultIdtTable.PageFaultContext.AddressEncMask = EntrypointContext->AddressEncMask; + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) (X64Idtr.Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR))); + HookPageFaultHandler (IdtEntry, &(PageFaultIdtTable.PageFaultContext)); + + // + // Initialize Debug Agent to support source level debug + // + InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *) &Ia32Idtr, NULL); + + // + // Call CapsuleDataCoalesce to process capsule. + // + Status = CapsuleDataCoalesce ( + NULL, + (EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr, + (MEMORY_RESOURCE_DESCRIPTOR *) (UINTN) EntrypointContext->MemoryResource, + (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr, + (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr + ); + + ReturnContext->ReturnStatus = Status; + + DEBUG (( + DEBUG_INFO, + "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n", + __FUNCTION__, + EntrypointContext->StackBufferBase, + EntrypointContext->StackBufferLength + )); + + // + // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode + // + SaveAndSetDebugTimerInterrupt (FALSE); + // + // Restore IA32 IDT table + // + AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); + + // + // Finish to coalesce capsule, and return to 32-bit mode. + // + AsmDisablePaging64 ( + ReturnContext->ReturnCs, + (UINT32) ReturnContext->ReturnEntryPoint, + (UINT32) (UINTN) EntrypointContext, + (UINT32) (UINTN) ReturnContext, + (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength) + ); + + // + // Should never be here. + // + ASSERT (FALSE); + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/Arm/CapsuleReset.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/Arm/CapsuleReset.c new file mode 100644 index 000000000..e05f14de6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/Arm/CapsuleReset.c @@ -0,0 +1,36 @@ +/** @file + ARM implementation of architecture specific routines related to + PersistAcrossReset capsules + + Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+ Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleService.h" + +/** + Whether the platform supports capsules that persist across reset. Note that + some platforms only support such capsules at boot time. + + @return TRUE if a PersistAcrossReset capsule may be passed to UpdateCapsule() + at this time + FALSE otherwise +**/ +BOOLEAN +IsPersistAcrossResetCapsuleSupported ( + VOID + ) +{ + // + // ARM requires the capsule payload to be cleaned to the point of coherency + // (PoC), but only permits doing so using cache maintenance instructions that + // operate on virtual addresses. Since at runtime, we don't know the virtual + // addresses of the data structures that make up the scatter/gather list, we + // cannot perform the maintenance, and all we can do is give up. + // + return FeaturePcdGet (PcdSupportUpdateCapsuleReset) && !EfiAtRuntime (); +} + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCache.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCache.c new file mode 100644 index 000000000..3c96851e9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCache.c @@ -0,0 +1,57 @@ +/** @file + Flush the cache is required for most architectures while do capsule + update. It is not support at Runtime. + + Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+ Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleService.h" + +#include + +/** + Writes Back a range of data cache lines covering a set of capsules in memory. + + Writes Back the data cache lines specified by ScatterGatherList. + + @param ScatterGatherList Physical address of the data structure that + describes a set of capsules in memory + +**/ +VOID +CapsuleCacheWriteBack ( + IN EFI_PHYSICAL_ADDRESS ScatterGatherList + ) +{ + EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc; + + if (!EfiAtRuntime ()) { + Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)ScatterGatherList; + do { + WriteBackDataCacheRange ( + (VOID *)(UINTN)Desc, + (UINTN)sizeof (*Desc) + ); + + if (Desc->Length > 0) { + WriteBackDataCacheRange ( + (VOID *)(UINTN)Desc->Union.DataBlock, + (UINTN)Desc->Length + ); + Desc++; + } else if (Desc->Union.ContinuationPointer > 0) { + Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)Desc->Union.ContinuationPointer; + } + } while (Desc->Length > 0 || Desc->Union.ContinuationPointer > 0); + + WriteBackDataCacheRange ( + (VOID *)(UINTN)Desc, + (UINTN)sizeof (*Desc) + ); + } +} + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCacheNull.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCacheNull.c new file mode 100644 index 000000000..edff7573b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleCacheNull.c @@ -0,0 +1,32 @@ +/** @file + Null function version of cache function. + + Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+ Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleService.h" + +#include + +/** + Writes Back a range of data cache lines covering a set of capsules in memory. + + Writes Back the data cache lines specified by ScatterGatherList. + + Null version, do nothing. + + @param ScatterGatherList Physical address of the data structure that + describes a set of capsules in memory + +**/ +VOID +CapsuleCacheWriteBack ( + IN EFI_PHYSICAL_ADDRESS ScatterGatherList + ) +{ +} + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleReset.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleReset.c new file mode 100644 index 000000000..732a3c9ab --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleReset.c @@ -0,0 +1,29 @@ +/** @file + Default implementation of architecture specific routines related to + PersistAcrossReset capsules + + Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+ Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleService.h" + +/** + Whether the platform supports capsules that persist across reset. Note that + some platforms only support such capsules at boot time. + + @return TRUE if a PersistAcrossReset capsule may be passed to UpdateCapsule() + at this time + FALSE otherwise +**/ +BOOLEAN +IsPersistAcrossResetCapsuleSupported ( + VOID + ) +{ + return FeaturePcdGet (PcdSupportUpdateCapsuleReset); +} + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf new file mode 100644 index 000000000..8bf5035a6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf @@ -0,0 +1,108 @@ +## @file +# Capsule Runtime Driver produces two UEFI capsule runtime services: (UpdateCapsule, QueryCapsuleCapabilities). +# +# It installs the Capsule Architectural Protocol defined in PI1.0a to signify +# the capsule runtime services are ready. +# +# Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+# Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CapsuleRuntimeDxe + MODULE_UNI_FILE = CapsuleRuntimeDxe.uni + FILE_GUID = 42857F0A-13F2-4B21-8A23-53D3F714B840 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = CapsuleServiceInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC ARM AARCH64 RISCV64 +# + +[Sources] + CapsuleService.c + CapsuleService.h + +[Sources.Ia32, Sources.EBC, Sources.ARM, Sources.AARCH64, Sources.RISCV64] + SaveLongModeContext.c + +[Sources.Ia32, Sources.X64, Sources.ARM, Sources.AARCH64, Sources.RISCV64] + CapsuleCache.c + +[Sources.Ia32, Sources.X64, Sources.EBC, Sources.RISCV64] + CapsuleReset.c + +[Sources.ARM, Sources.AARCH64] + Arm/CapsuleReset.c + +[Sources.EBC] + CapsuleCacheNull.c + +[Sources.X64] + X64/SaveLongModeContext.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + PcdLib + DebugLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + CapsuleLib + UefiRuntimeLib + BaseLib + PrintLib + BaseMemoryLib + CacheMaintenanceLib + +[LibraryClasses.X64] + UefiLib + BaseMemoryLib + +[Guids] + ## SOMETIMES_PRODUCES ## Variable:L"CapsuleUpdateData" # (Process across reset capsule image) for capsule updated data + ## SOMETIMES_PRODUCES ## Variable:L"CapsuleLongModeBuffer" # The long mode buffer used by IA32 Capsule PEIM to call X64 CapsuleCoalesce code to handle >4GB capsule blocks + gEfiCapsuleVendorGuid + gEfiFmpCapsuleGuid ## SOMETIMES_CONSUMES ## GUID # FMP capsule GUID + +[Protocols] + gEfiCapsuleArchProtocolGuid ## PRODUCES + +[Protocols.X64] + ## UNDEFINED ## NOTIFY + ## SOMETIMES_CONSUMES + gEdkiiVariableLockProtocolGuid + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSupportProcessCapsuleAtRuntime ## CONSUMES + +[FeaturePcd.X64] + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizeNonPopulateCapsule ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizePopulateCapsule ## SOMETIMES_CONSUMES # Populate Image requires reset support. + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleInRamSupport ## CONSUMES + +[Pcd.X64] + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsulePeiLongModeStackSize ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES + +[Depex] + gEfiVariableWriteArchProtocolGuid # Depends on variable write functionality to produce capsule data variable + +# [Hob.X64] +# UNDEFINED ## SOMETIMES_CONSUMES # CPU + +[UserExtensions.TianoCore."ExtraFiles"] + CapsuleRuntimeDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.uni b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.uni new file mode 100644 index 000000000..b161af45c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// Capsule Runtime Driver produces two UEFI capsule runtime services: (UpdateCapsule, QueryCapsuleCapabilities). +// +// It installs the Capsule Architectural Protocol defined in PI1.0a to signify +// the capsule runtime services are ready. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Produces two UEFI capsule runtime services: (UpdateCapsule, QueryCapsuleCapabilities)" + +#string STR_MODULE_DESCRIPTION #language en-US "It installs the Capsule Architectural Protocol defined in PI1.0a to signify the capsule runtime services are ready." + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxeExtra.uni new file mode 100644 index 000000000..f97bfd64a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// CapsuleRuntimeDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Runtime Firmware Update DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c new file mode 100644 index 000000000..2fba22dec --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c @@ -0,0 +1,397 @@ +/** @file + Capsule Runtime Driver produces two UEFI capsule runtime services. + (UpdateCapsule, QueryCapsuleCapabilities) + It installs the Capsule Architectural Protocol defined in PI1.0a to signify + the capsule runtime services are ready. + +Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleService.h" + +// +// Handle for the installation of Capsule Architecture Protocol. +// +EFI_HANDLE mNewHandle = NULL; + +// +// The times of calling UpdateCapsule () +// +UINTN mTimes = 0; + +UINT32 mMaxSizePopulateCapsule = 0; +UINT32 mMaxSizeNonPopulateCapsule = 0; + +/** + Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended + consumption, the firmware may process the capsule immediately. If the payload should persist + across a system reset, the reset value returned from EFI_QueryCapsuleCapabilities must + be passed into ResetSystem() and will cause the capsule to be processed by the firmware as + part of the reset process. + + @param CapsuleHeaderArray Virtual pointer to an array of virtual pointers to the capsules + being passed into update capsule. + @param CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in + CaspuleHeaderArray. + @param ScatterGatherList Physical pointer to a set of + EFI_CAPSULE_BLOCK_DESCRIPTOR that describes the + location in physical memory of a set of capsules. + + @retval EFI_SUCCESS Valid capsule was passed. If + CAPSULE_FLAGS_PERSIT_ACROSS_RESET is not set, the + capsule has been successfully processed by the firmware. + @retval EFI_DEVICE_ERROR The capsule update was started, but failed due to a device error. + @retval EFI_INVALID_PARAMETER CapsuleSize is NULL, or an incompatible set of flags were + set in the capsule header. + @retval EFI_INVALID_PARAMETER CapsuleCount is Zero. + @retval EFI_INVALID_PARAMETER For across reset capsule image, ScatterGatherList is NULL. + @retval EFI_UNSUPPORTED CapsuleImage is not recognized by the firmware. + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has been previously called this error indicates the capsule + is compatible with this platform but is not capable of being submitted or processed + in runtime. The caller may resubmit the capsule prior to ExitBootServices(). + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has not been previously called then this error indicates + the capsule is compatible with this platform but there are insufficient resources to process. + +**/ +EFI_STATUS +EFIAPI +UpdateCapsule ( + IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, + IN UINTN CapsuleCount, + IN EFI_PHYSICAL_ADDRESS ScatterGatherList OPTIONAL + ) +{ + UINTN ArrayNumber; + EFI_STATUS Status; + EFI_CAPSULE_HEADER *CapsuleHeader; + BOOLEAN NeedReset; + BOOLEAN InitiateReset; + CHAR16 CapsuleVarName[30]; + CHAR16 *TempVarName; + + // + // Check if platform support Capsule In RAM or not. + // Platform could choose to drop CapsulePei/CapsuleX64 and do not support Capsule In RAM. + // + if (!PcdGetBool(PcdCapsuleInRamSupport)) { + return EFI_UNSUPPORTED; + } + + // + // Capsule Count can't be less than one. + // + if (CapsuleCount < 1) { + return EFI_INVALID_PARAMETER; + } + + NeedReset = FALSE; + InitiateReset = FALSE; + CapsuleHeader = NULL; + CapsuleVarName[0] = 0; + + for (ArrayNumber = 0; ArrayNumber < CapsuleCount; ArrayNumber++) { + // + // A capsule which has the CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE flag must have + // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well. + // + CapsuleHeader = CapsuleHeaderArray[ArrayNumber]; + if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) { + return EFI_INVALID_PARAMETER; + } + // + // A capsule which has the CAPSULE_FLAGS_INITIATE_RESET flag must have + // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well. + // + if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) { + return EFI_INVALID_PARAMETER; + } + + // + // Check FMP capsule flag + // + if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid) + && (CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 ) { + return EFI_INVALID_PARAMETER; + } + + // + // Check Capsule image without populate flag by firmware support capsule function + // + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) { + Status = SupportCapsuleImage (CapsuleHeader); + if (EFI_ERROR(Status)) { + return Status; + } + } + } + + // + // Walk through all capsules, record whether there is a capsule needs reset + // or initiate reset. And then process capsules which has no reset flag directly. + // + for (ArrayNumber = 0; ArrayNumber < CapsuleCount ; ArrayNumber++) { + CapsuleHeader = CapsuleHeaderArray[ArrayNumber]; + // + // Here should be in the boot-time for non-reset capsule image + // Platform specific update for the non-reset capsule image. + // + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) { + if (EfiAtRuntime () && !FeaturePcdGet (PcdSupportProcessCapsuleAtRuntime)) { + Status = EFI_OUT_OF_RESOURCES; + } else { + Status = ProcessCapsuleImage(CapsuleHeader); + } + if (EFI_ERROR(Status)) { + return Status; + } + } else { + NeedReset = TRUE; + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_INITIATE_RESET) != 0) { + InitiateReset = TRUE; + } + } + } + + // + // After launching all capsules who has no reset flag, if no more capsules claims + // for a system reset just return. + // + if (!NeedReset) { + return EFI_SUCCESS; + } + + // + // ScatterGatherList is only referenced if the capsules are defined to persist across + // system reset. + // + if (ScatterGatherList == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check if the platform supports update capsule across a system reset + // + if (!IsPersistAcrossResetCapsuleSupported ()) { + return EFI_UNSUPPORTED; + } + + CapsuleCacheWriteBack (ScatterGatherList); + + // + // Construct variable name CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2... + // if user calls UpdateCapsule multiple times. + // + StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + if (mTimes > 0) { + UnicodeValueToStringS ( + TempVarName, + sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName), + 0, + mTimes, + 0 + ); + } + + // + // ScatterGatherList is only referenced if the capsules are defined to persist across + // system reset. Set its value into NV storage to let pre-boot driver to pick it up + // after coming through a system reset. + // + Status = EfiSetVariable ( + CapsuleVarName, + &gEfiCapsuleVendorGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (UINTN), + (VOID *) &ScatterGatherList + ); + if (!EFI_ERROR (Status)) { + // + // Variable has been set successfully, increase variable index. + // + mTimes++; + if(InitiateReset) { + // + // Firmware that encounters a capsule which has the CAPSULE_FLAGS_INITIATE_RESET Flag set in its header + // will initiate a reset of the platform which is compatible with the passed-in capsule request and will + // not return back to the caller. + // + EfiResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + } + return Status; +} + +/** + Returns if the capsule can be supported via UpdateCapsule(). + Notice: When PcdCapsuleInRamSupport is unsupported, even this routine returns a valid answer, + the capsule still is unsupported via UpdateCapsule(). + + @param CapsuleHeaderArray Virtual pointer to an array of virtual pointers to the capsules + being passed into update capsule. + @param CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in + CaspuleHeaderArray. + @param MaxiumCapsuleSize On output the maximum size that UpdateCapsule() can + support as an argument to UpdateCapsule() via + CapsuleHeaderArray and ScatterGatherList. + @param ResetType Returns the type of reset required for the capsule update. + + @retval EFI_SUCCESS Valid answer returned. + @retval EFI_UNSUPPORTED The capsule image is not supported on this platform, and + MaximumCapsuleSize and ResetType are undefined. + @retval EFI_INVALID_PARAMETER MaximumCapsuleSize is NULL, or ResetTyep is NULL, + Or CapsuleCount is Zero, or CapsuleImage is not valid. + +**/ +EFI_STATUS +EFIAPI +QueryCapsuleCapabilities ( + IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, + IN UINTN CapsuleCount, + OUT UINT64 *MaxiumCapsuleSize, + OUT EFI_RESET_TYPE *ResetType + ) +{ + EFI_STATUS Status; + UINTN ArrayNumber; + EFI_CAPSULE_HEADER *CapsuleHeader; + BOOLEAN NeedReset; + + // + // Capsule Count can't be less than one. + // + if (CapsuleCount < 1) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether input parameter is valid + // + if ((MaxiumCapsuleSize == NULL) ||(ResetType == NULL)) { + return EFI_INVALID_PARAMETER; + } + + CapsuleHeader = NULL; + NeedReset = FALSE; + + for (ArrayNumber = 0; ArrayNumber < CapsuleCount; ArrayNumber++) { + CapsuleHeader = CapsuleHeaderArray[ArrayNumber]; + // + // A capsule which has the CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE flag must have + // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well. + // + if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) { + return EFI_INVALID_PARAMETER; + } + // + // A capsule which has the CAPSULE_FLAGS_INITIATE_RESET flag must have + // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well. + // + if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) { + return EFI_INVALID_PARAMETER; + } + + // + // Check FMP capsule flag + // + if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid) + && (CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 ) { + return EFI_INVALID_PARAMETER; + } + + // + // Check Capsule image without populate flag is supported by firmware + // + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) { + Status = SupportCapsuleImage (CapsuleHeader); + if (EFI_ERROR(Status)) { + return Status; + } + } + } + + // + // Find out whether there is any capsule defined to persist across system reset. + // + for (ArrayNumber = 0; ArrayNumber < CapsuleCount ; ArrayNumber++) { + CapsuleHeader = CapsuleHeaderArray[ArrayNumber]; + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) { + NeedReset = TRUE; + break; + } + } + + if (NeedReset) { + // + //Check if the platform supports update capsule across a system reset + // + if (!IsPersistAcrossResetCapsuleSupported ()) { + return EFI_UNSUPPORTED; + } + *ResetType = EfiResetWarm; + *MaxiumCapsuleSize = (UINT64) mMaxSizePopulateCapsule; + } else { + // + // For non-reset capsule image. + // + *ResetType = EfiResetCold; + *MaxiumCapsuleSize = (UINT64) mMaxSizeNonPopulateCapsule; + } + + return EFI_SUCCESS; +} + + +/** + + This code installs UEFI capsule runtime service. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS UEFI Capsule Runtime Services are installed successfully. + +**/ +EFI_STATUS +EFIAPI +CapsuleServiceInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + mMaxSizePopulateCapsule = PcdGet32(PcdMaxSizePopulateCapsule); + mMaxSizeNonPopulateCapsule = PcdGet32(PcdMaxSizeNonPopulateCapsule); + + // + // When PEI phase is IA32, DXE phase is X64, it is possible that capsule data are + // put above 4GB, so capsule PEI will transfer to long mode to get capsule data. + // The page table and stack is used to transfer processor mode from IA32 to long mode. + // Create the base address of page table and stack, and save them into variable. + // This is not needed when capsule with reset type is not supported. + // + SaveLongModeContext (); + + // + // Install capsule runtime services into UEFI runtime service tables. + // + gRT->UpdateCapsule = UpdateCapsule; + gRT->QueryCapsuleCapabilities = QueryCapsuleCapabilities; + + // + // Install the Capsule Architectural Protocol on a new handle + // to signify the capsule runtime services are ready. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mNewHandle, + &gEfiCapsuleArchProtocolGuid, + NULL, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.h b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.h new file mode 100644 index 000000000..069df3c75 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.h @@ -0,0 +1,70 @@ +/** @file + Capsule Runtime Driver produces two UEFI capsule runtime services. + (UpdateCapsule, QueryCapsuleCapabilities) + It installs the Capsule Architectural Protocol defined in PI1.0a to signify + the capsule runtime services are ready. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CAPSULE_SERVICE_H_ +#define _CAPSULE_SERVICE_H_ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Create the variable to save the base address of page table and stack + for transferring into long mode in IA32 PEI. +**/ +VOID +SaveLongModeContext ( + VOID + ); + +/** + Whether the platform supports capsules that persist across reset. Note that + some platforms only support such capsules at boot time. + + @return TRUE if a PersistAcrossReset capsule may be passed to UpdateCapsule() + at this time + FALSE otherwise +**/ +BOOLEAN +IsPersistAcrossResetCapsuleSupported ( + VOID + ); + +/** + Writes Back a range of data cache lines covering a set of capsules in memory. + + Writes Back the data cache lines specified by ScatterGatherList. + + @param ScatterGatherList Physical address of the data structure that + describes a set of capsules in memory + +**/ +VOID +CapsuleCacheWriteBack ( + IN EFI_PHYSICAL_ADDRESS ScatterGatherList + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c new file mode 100644 index 000000000..37b388902 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c @@ -0,0 +1,21 @@ +/** @file + Create the NULL function to pass build in IA32/IPF/ARM/EBC. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +/** + Only when PEI is IA32 and DXE is X64, we need transfer to long mode in PEI + in order to process capsule data above 4GB. So create a NULL function here for + other cases. +**/ +VOID +SaveLongModeContext ( + VOID + ) +{ +} diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c new file mode 100644 index 000000000..d80d4ed3a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c @@ -0,0 +1,209 @@ +/** @file + Create the variable to save the base address of page table and stack + for transferring into long mode in IA32 capsule PEI. + +Copyright (c) 2011 - 2018, 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 + +// +// 8 extra pages for PF handler. +// +#define EXTRA_PAGE_TABLE_PAGES 8 + +/** + Allocate EfiReservedMemoryType below 4G memory address. + + This function allocates EfiReservedMemoryType below 4G memory address. + + @param Size Size of memory to allocate. + + @return Allocated Address for output. + +**/ +VOID* +AllocateReservedMemoryBelow4G ( + IN UINTN Size + ) +{ + UINTN Pages; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + VOID* Buffer; + + Pages = EFI_SIZE_TO_PAGES (Size); + Address = 0xffffffff; + + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + Pages, + &Address + ); + ASSERT_EFI_ERROR (Status); + + Buffer = (VOID *) (UINTN) Address; + ZeroMem (Buffer, Size); + + return Buffer; +} + +/** + Register callback function upon VariableLockProtocol + to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +VariableLockCapsuleLongModeBufferVariable ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + // + // Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists + // + Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock); + if (!EFI_ERROR (Status)) { + Status = VariableLock->RequestToLock (VariableLock, EFI_CAPSULE_LONG_MODE_BUFFER_NAME, &gEfiCapsuleVendorGuid); + ASSERT_EFI_ERROR (Status); + } +} + +/** + 1. Allocate Reserved memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping. + 2. Allocate Reserved memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode. + +**/ +VOID +EFIAPI +PrepareContextForCapsulePei ( + VOID + ) +{ + UINTN ExtraPageTablePages; + UINT32 RegEax; + UINT32 RegEdx; + UINTN TotalPagesNum; + UINT8 PhysicalAddressBits; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + BOOLEAN Page1GSupport; + EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer; + EFI_STATUS Status; + VOID *Registration; + + // + // Calculate the size of page table, allocate the memory. + // + Page1GSupport = FALSE; + if (PcdGetBool(PcdUse1GPageTable)) { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + Page1GSupport = TRUE; + } + } + } + + // + // Create 4G page table by default, + // and let PF handler to handle > 4G request. + // + PhysicalAddressBits = 32; + ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES; + + // + // Calculate the table entries needed. + // + if (PhysicalAddressBits <= 39 ) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30)); + } else { + NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39)); + NumberOfPdpEntriesNeeded = 512; + } + + if (!Page1GSupport) { + TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1; + } else { + TotalPagesNum = NumberOfPml4EntriesNeeded + 1; + } + TotalPagesNum += ExtraPageTablePages; + DEBUG ((DEBUG_INFO, "CapsuleRuntimeDxe X64 TotalPagesNum - 0x%x pages\n", TotalPagesNum)); + + LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum)); + ASSERT (LongModeBuffer.PageTableAddress != 0); + + // + // Allocate stack + // + LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize); + LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize)); + ASSERT (LongModeBuffer.StackBaseAddress != 0); + + Status = gRT->SetVariable ( + EFI_CAPSULE_LONG_MODE_BUFFER_NAME, + &gEfiCapsuleVendorGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (EFI_CAPSULE_LONG_MODE_BUFFER), + &LongModeBuffer + ); + if (!EFI_ERROR (Status)) { + // + // Register callback function upon VariableLockProtocol + // to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it. + // + EfiCreateProtocolNotifyEvent ( + &gEdkiiVariableLockProtocolGuid, + TPL_CALLBACK, + VariableLockCapsuleLongModeBufferVariable, + NULL, + &Registration + ); + } else { + DEBUG ((EFI_D_ERROR, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status)); + gBS->FreePages (LongModeBuffer.StackBaseAddress, EFI_SIZE_TO_PAGES (LongModeBuffer.StackSize)); + } +} + +/** + Create the variable to save the base address of page table and stack + for transferring into long mode in IA32 capsule PEI. +**/ +VOID +SaveLongModeContext ( + VOID + ) +{ + if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) { + // + // Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB. + // + PrepareContextForCapsulePei (); + } +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ComponentName.c new file mode 100644 index 000000000..6930fae65 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ComponentName.c @@ -0,0 +1,161 @@ +/** @file + UEFI Component Name(2) protocol implementation for ConPlatform driver. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "ConPlatform.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gConPlatformComponentName = { + ConPlatformComponentNameGetDriverName, + ConPlatformComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gConPlatformComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ConPlatformComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ConPlatformComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mConPlatformDriverNameTable[] = { + { + "eng;en", + L"Platform Console Management Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConPlatformComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mConPlatformDriverNameTable, + DriverName, + (BOOLEAN)(This == &gConPlatformComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConPlatformComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c new file mode 100644 index 000000000..46e7688f7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c @@ -0,0 +1,1271 @@ +/** @file + Console Platform DXE Driver, install Console Device Guids and update Console + Environment Variables. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "ConPlatform.h" + + +EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextInDriverBinding = { + ConPlatformTextInDriverBindingSupported, + ConPlatformTextInDriverBindingStart, + ConPlatformTextInDriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextOutDriverBinding = { + ConPlatformTextOutDriverBindingSupported, + ConPlatformTextOutDriverBindingStart, + ConPlatformTextOutDriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + Entrypoint of this module. + + This function is the entrypoint of this module. It installs Driver Binding + Protocols together with Component Name Protocols. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + +**/ +EFI_STATUS +EFIAPI +InitializeConPlatform( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gConPlatformTextInDriverBinding, + ImageHandle, + &gConPlatformComponentName, + &gConPlatformComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gConPlatformTextOutDriverBinding, + NULL, + &gConPlatformComponentName, + &gConPlatformComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + + +/** + Test to see if EFI_SIMPLE_TEXT_INPUT_PROTOCOL is supported on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextInDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return ConPlatformDriverBindingSupported ( + This, + ControllerHandle, + &gEfiSimpleTextInProtocolGuid + ); +} + + +/** + Test to see if EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL is supported on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextOutDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return ConPlatformDriverBindingSupported ( + This, + ControllerHandle, + &gEfiSimpleTextOutProtocolGuid + ); +} + + +/** + Test to see if the specified protocol is supported on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param ProtocolGuid The specfic protocol. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +ConPlatformDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_GUID *ProtocolGuid + ) +{ + EFI_STATUS Status; + VOID *Interface; + + // + // Test to see if this is a physical device by checking if + // it has a Device Path Protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Test to see if this device supports the specified Protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + ProtocolGuid, + (VOID **) &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + ControllerHandle, + ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return EFI_SUCCESS; +} + +/** + Start this driver on the device for console input. + + Start this driver on ControllerHandle by opening Simple Text Input Protocol, + reading Device Path, and installing Console In Devcice GUID on ControllerHandle. + + Append its device path into the console environment variables ConInDev. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextInDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn; + BOOLEAN IsInConInVariable; + + // + // Get the Device Path Protocol so the environment variables can be updated + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Open the Simple Text Input Protocol BY_DRIVER + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleTextInProtocolGuid, + (VOID **) &TextIn, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check if the device path is in ConIn Variable + // + IsInConInVariable = FALSE; + Status = ConPlatformUpdateDeviceVariable ( + L"ConIn", + DevicePath, + Check + ); + if (!EFI_ERROR (Status)) { + IsInConInVariable = TRUE; + } + + // + // Append the device path to the ConInDev environment variable + // + ConPlatformUpdateDeviceVariable ( + L"ConInDev", + DevicePath, + Append + ); + + // + // If the device path is an instance in the ConIn environment variable, + // then install EfiConsoleInDeviceGuid onto ControllerHandle + // + if (IsInConInVariable) { + gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiConsoleInDeviceGuid, + NULL, + NULL + ); + } else { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSimpleTextInProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + + return EFI_SUCCESS; +} + +/** + Start this driver on the device for console output and standard error output. + + Start this driver on ControllerHandle by opening Simple Text Output Protocol, + reading Device Path, and installing Console Out Devcic GUID, Standard Error + Device GUID on ControllerHandle. + + Append its device path into the console environment variables ConOutDev, ErrOutDev. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextOutDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + BOOLEAN NeedClose; + BOOLEAN IsInConOutVariable; + BOOLEAN IsInErrOutVariable; + + NeedClose = TRUE; + + // + // Get the Device Path Protocol so the environment variables can be updated + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Open the Simple Text Output Protocol BY_DRIVER + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &TextOut, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check if the device path is in ConOut & ErrOut Variable + // + IsInConOutVariable = FALSE; + Status = ConPlatformUpdateDeviceVariable ( + L"ConOut", + DevicePath, + Check + ); + if (!EFI_ERROR (Status)) { + IsInConOutVariable = TRUE; + } + + IsInErrOutVariable = FALSE; + Status = ConPlatformUpdateDeviceVariable ( + L"ErrOut", + DevicePath, + Check + ); + if (!EFI_ERROR (Status)) { + IsInErrOutVariable = TRUE; + } + + // + // Append the device path to the ConOutDev and ErrOutDev environment variable. + // For GOP device path, append the sibling device path as well. + // + if (!ConPlatformUpdateGopCandidate (DevicePath)) { + ConPlatformUpdateDeviceVariable ( + L"ConOutDev", + DevicePath, + Append + ); + // + // Then append the device path to the ErrOutDev environment variable + // + ConPlatformUpdateDeviceVariable ( + L"ErrOutDev", + DevicePath, + Append + ); + } + + // + // If the device path is an instance in the ConOut environment variable, + // then install EfiConsoleOutDeviceGuid onto ControllerHandle + // + if (IsInConOutVariable) { + NeedClose = FALSE; + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiConsoleOutDeviceGuid, + NULL, + NULL + ); + } + // + // If the device path is an instance in the ErrOut environment variable, + // then install EfiStandardErrorDeviceGuid onto ControllerHandle + // + if (IsInErrOutVariable) { + NeedClose = FALSE; + gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiStandardErrorDeviceGuid, + NULL, + NULL + ); + } + + if (NeedClose) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSimpleTextOutProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + + return EFI_SUCCESS; +} + +/** + Stop this driver on ControllerHandle by removing Console In Devcice GUID + and closing the Simple Text Input protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextInDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + // + // Get the Device Path Protocol firstly + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + // + // If there is device path on ControllerHandle + // + if (!EFI_ERROR (Status)) { + // + // Remove DevicePath from ConInDev if exists. + // + ConPlatformUpdateDeviceVariable ( + L"ConInDev", + DevicePath, + Delete + ); + } + + // + // Uninstall the Console Device GUIDs from Controller Handle + // + ConPlatformUnInstallProtocol ( + This, + ControllerHandle, + &gEfiConsoleInDeviceGuid + ); + + // + // Close the Simple Text Input Protocol + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSimpleTextInProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return EFI_SUCCESS; +} + + +/** + Stop this driver on ControllerHandle by removing Console Out Devcice GUID + and closing the Simple Text Output protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextOutDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + // + // Get the Device Path Protocol firstly + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Remove DevicePath from ConOutDev and ErrOutDev if exists. + // + ConPlatformUpdateDeviceVariable ( + L"ConOutDev", + DevicePath, + Delete + ); + ConPlatformUpdateDeviceVariable ( + L"ErrOutDev", + DevicePath, + Delete + ); + } + + // + // Uninstall the Console Device GUIDs from Controller Handle + // + ConPlatformUnInstallProtocol ( + This, + ControllerHandle, + &gEfiConsoleOutDeviceGuid + ); + + ConPlatformUnInstallProtocol ( + This, + ControllerHandle, + &gEfiStandardErrorDeviceGuid + ); + + // + // Close the Simple Text Output Protocol + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSimpleTextOutProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return EFI_SUCCESS; +} + + +/** + Uninstall the specified protocol. + + @param This Protocol instance pointer. + @param Handle Handle of device to uninstall protocol on. + @param ProtocolGuid The specified protocol need to be uninstalled. + +**/ +VOID +ConPlatformUnInstallProtocol ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_GUID *ProtocolGuid + ) +{ + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + Handle, + ProtocolGuid, + NULL, + This->DriverBindingHandle, + Handle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Handle, + ProtocolGuid, + NULL, + NULL + ); + } + + return ; +} + +/** + Get the necessary size of buffer and read the variable. + + First get the necessary size of buffer. Then read the + EFI variable (Name) and return a dynamically allocated + buffer. On failure return NULL. + + @param Name String part of EFI variable name + + @return Dynamically allocated memory that contains a copy of the EFI variable. + Caller is repsoncible freeing the buffer. Return NULL means Variable + was not read. + +**/ +VOID * +ConPlatformGetVariable ( + IN CHAR16 *Name + ) +{ + EFI_STATUS Status; + VOID *Buffer; + UINTN BufferSize; + + BufferSize = 0; + Buffer = NULL; + + // + // Test to see if the variable exists. If it doesn't, return NULL. + // + Status = gRT->GetVariable ( + Name, + &gEfiGlobalVariableGuid, + NULL, + &BufferSize, + Buffer + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate the buffer to return + // + Buffer = AllocatePool (BufferSize); + if (Buffer == NULL) { + return NULL; + } + // + // Read variable into the allocated buffer. + // + Status = gRT->GetVariable ( + Name, + &gEfiGlobalVariableGuid, + NULL, + &BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + // + // To make sure Buffer is NULL if any error occurs. + // + Buffer = NULL; + } + } + + return Buffer; +} + +/** + Function returns TRUE when the two input device paths point to the two + GOP child handles that have the same parent. + + @param Left A pointer to a device path data structure. + @param Right A pointer to a device path data structure. + + @retval TRUE Left and Right share the same parent. + @retval FALSE Left and Right don't share the same parent or either of them is not + a GOP device path. +**/ +BOOLEAN +IsGopSibling ( + IN EFI_DEVICE_PATH_PROTOCOL *Left, + IN EFI_DEVICE_PATH_PROTOCOL *Right + ) +{ + EFI_DEVICE_PATH_PROTOCOL *NodeLeft; + EFI_DEVICE_PATH_PROTOCOL *NodeRight; + + for (NodeLeft = Left; !IsDevicePathEndType (NodeLeft); NodeLeft = NextDevicePathNode (NodeLeft)) { + if ((DevicePathType (NodeLeft) == ACPI_DEVICE_PATH && DevicePathSubType (NodeLeft) == ACPI_ADR_DP) || + (DevicePathType (NodeLeft) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeLeft) == HW_CONTROLLER_DP && + DevicePathType (NextDevicePathNode (NodeLeft)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeLeft)) == ACPI_ADR_DP)) { + break; + } + } + + if (IsDevicePathEndType (NodeLeft)) { + return FALSE; + } + + for (NodeRight = Right; !IsDevicePathEndType (NodeRight); NodeRight = NextDevicePathNode (NodeRight)) { + if ((DevicePathType (NodeRight) == ACPI_DEVICE_PATH && DevicePathSubType (NodeRight) == ACPI_ADR_DP) || + (DevicePathType (NodeRight) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeRight) == HW_CONTROLLER_DP && + DevicePathType (NextDevicePathNode (NodeRight)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeRight)) == ACPI_ADR_DP)) { + break; + } + } + + if (IsDevicePathEndType (NodeRight)) { + return FALSE; + } + + if (((UINTN) NodeLeft - (UINTN) Left) != ((UINTN) NodeRight - (UINTN) Right)) { + return FALSE; + } + + return (BOOLEAN) (CompareMem (Left, Right, (UINTN) NodeLeft - (UINTN) Left) == 0); +} + +/** + Check whether a USB device match the specified USB Class device path. This + function follows "Load Option Processing" behavior in UEFI specification. + + @param UsbIo USB I/O protocol associated with the USB device. + @param UsbClass The USB Class device path to match. + + @retval TRUE The USB device match the USB Class device path. + @retval FALSE The USB device does not match the USB Class device path. + +**/ +BOOLEAN +MatchUsbClass ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN USB_CLASS_DEVICE_PATH *UsbClass + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_INTERFACE_DESCRIPTOR IfDesc; + UINT8 DeviceClass; + UINT8 DeviceSubClass; + UINT8 DeviceProtocol; + + if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){ + return FALSE; + } + + // + // Check Vendor Id and Product Id. + // + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if ((UsbClass->VendorId != 0xffff) && + (UsbClass->VendorId != DevDesc.IdVendor)) { + return FALSE; + } + + if ((UsbClass->ProductId != 0xffff) && + (UsbClass->ProductId != DevDesc.IdProduct)) { + return FALSE; + } + + DeviceClass = DevDesc.DeviceClass; + DeviceSubClass = DevDesc.DeviceSubClass; + DeviceProtocol = DevDesc.DeviceProtocol; + if (DeviceClass == 0) { + // + // If Class in Device Descriptor is set to 0, use the Class, SubClass and + // Protocol in Interface Descriptor instead. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + DeviceClass = IfDesc.InterfaceClass; + DeviceSubClass = IfDesc.InterfaceSubClass; + DeviceProtocol = IfDesc.InterfaceProtocol; + } + + // + // Check Class, SubClass and Protocol. + // + if ((UsbClass->DeviceClass != 0xff) && + (UsbClass->DeviceClass != DeviceClass)) { + return FALSE; + } + + if ((UsbClass->DeviceSubClass != 0xff) && + (UsbClass->DeviceSubClass != DeviceSubClass)) { + return FALSE; + } + + if ((UsbClass->DeviceProtocol != 0xff) && + (UsbClass->DeviceProtocol != DeviceProtocol)) { + return FALSE; + } + + return TRUE; +} + +/** + Check whether a USB device match the specified USB WWID device path. This + function follows "Load Option Processing" behavior in UEFI specification. + + @param UsbIo USB I/O protocol associated with the USB device. + @param UsbWwid The USB WWID device path to match. + + @retval TRUE The USB device match the USB WWID device path. + @retval FALSE The USB device does not match the USB WWID device path. + +**/ +BOOLEAN +MatchUsbWwid ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN USB_WWID_DEVICE_PATH *UsbWwid + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_INTERFACE_DESCRIPTOR IfDesc; + UINT16 *LangIdTable; + UINT16 TableSize; + UINT16 Index; + CHAR16 *CompareStr; + UINTN CompareLen; + CHAR16 *SerialNumberStr; + UINTN Length; + + if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) { + return FALSE; + } + + // + // Check Vendor Id and Product Id. + // + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + if ((DevDesc.IdVendor != UsbWwid->VendorId) || + (DevDesc.IdProduct != UsbWwid->ProductId)) { + return FALSE; + } + + // + // Check Interface Number. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) { + return FALSE; + } + + // + // Check Serial Number. + // + if (DevDesc.StrSerialNumber == 0) { + return FALSE; + } + + // + // Get all supported languages. + // + TableSize = 0; + LangIdTable = NULL; + Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize); + if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) { + return FALSE; + } + + // + // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters. + // + CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1); + CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16); + if (CompareStr[CompareLen - 1] == L'\0') { + CompareLen--; + } + + // + // Compare serial number in each supported language. + // + for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) { + SerialNumberStr = NULL; + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + LangIdTable[Index], + DevDesc.StrSerialNumber, + &SerialNumberStr + ); + if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) { + continue; + } + + Length = StrLen (SerialNumberStr); + if ((Length >= CompareLen) && + (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) { + FreePool (SerialNumberStr); + return TRUE; + } + + FreePool (SerialNumberStr); + } + + return FALSE; +} + +/** + Compare whether a full console device path matches a USB shortform device path. + + @param[in] FullPath Full console device path. + @param[in] ShortformPath Short-form device path. Short-form device node may in the beginning or in the middle. + + @retval TRUE The full console device path matches the short-form device path. + @retval FALSE The full console device path doesn't match the short-form device path. +**/ +BOOLEAN +MatchUsbShortformDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FullPath, + IN EFI_DEVICE_PATH_PROTOCOL *ShortformPath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ShortformNode; + UINTN ParentDevicePathSize; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_HANDLE Handle; + + for ( ShortformNode = ShortformPath + ; !IsDevicePathEnd (ShortformNode) + ; ShortformNode = NextDevicePathNode (ShortformNode) + ) { + if ((DevicePathType (ShortformNode) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (ShortformNode) == MSG_USB_CLASS_DP) || + (DevicePathSubType (ShortformNode) == MSG_USB_WWID_DP)) + ) { + break; + } + } + + // + // Skip further compare when it's not a shortform device path. + // + if (IsDevicePathEnd (ShortformNode)) { + return FALSE; + } + + // + // Compare the parent device path when the ShortformPath doesn't start with short-form node. + // + ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) ShortformPath; + RemainingDevicePath = FullPath; + Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &RemainingDevicePath, &Handle); + if (EFI_ERROR (Status)) { + return FALSE; + } + if (ParentDevicePathSize != 0) { + if ((ParentDevicePathSize > (UINTN) RemainingDevicePath - (UINTN) FullPath) || + (CompareMem (FullPath, ShortformPath, ParentDevicePathSize) != 0)) { + return FALSE; + } + } + + // + // Compar the USB layer. + // + Status = gBS->HandleProtocol( + Handle, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo + ); + ASSERT_EFI_ERROR (Status); + + return MatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *)ShortformNode) || + MatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *)ShortformNode); +} + +/** + Function compares a device path data structure to that of all the nodes of a + second device path instance. + + @param Multi A pointer to a multi-instance device path data structure. + @param Single A pointer to a single-instance device path data structure. + @param NewDevicePath If Delete is TRUE, this parameter must not be null, and it + points to the remaining device path data structure. + (remaining device path = Multi - Single.) + @param Delete If TRUE, means removing Single from Multi. + If FALSE, the routine just check whether Single matches + with any instance in Multi. + + @retval EFI_SUCCESS If the Single is contained within Multi. + @retval EFI_NOT_FOUND If the Single is not contained within Multi. + @retval EFI_INVALID_PARAMETER Multi is NULL. + @retval EFI_INVALID_PARAMETER Single is NULL. + @retval EFI_INVALID_PARAMETER NewDevicePath is NULL when Delete is TRUE. + +**/ +EFI_STATUS +ConPlatformMatchDevicePaths ( + IN EFI_DEVICE_PATH_PROTOCOL *Multi, + IN EFI_DEVICE_PATH_PROTOCOL *Single, + OUT EFI_DEVICE_PATH_PROTOCOL **NewDevicePath OPTIONAL, + IN BOOLEAN Delete + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath1; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath2; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInst; + UINTN Size; + + // + // The passed in DevicePath should not be NULL + // + if ((Multi == NULL) || (Single == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // If performing Delete operation, the NewDevicePath must not be NULL. + // + if (Delete) { + if (NewDevicePath == NULL) { + return EFI_INVALID_PARAMETER; + } + } + + TempDevicePath1 = NULL; + + DevicePath = Multi; + DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size); + + // + // Search for the match of 'Single' in 'Multi' + // + while (DevicePathInst != NULL) { + if ((CompareMem (Single, DevicePathInst, Size) == 0) || + IsGopSibling (Single, DevicePathInst) || MatchUsbShortformDevicePath (Single, DevicePathInst)) { + if (!Delete) { + // + // If Delete is FALSE, return EFI_SUCCESS if Single is found in Multi. + // + FreePool (DevicePathInst); + return EFI_SUCCESS; + } + } else { + if (Delete) { + // + // If the node of Multi does not match Single, then added it back to the result. + // That is, the node matching Single will be dropped and deleted from result. + // + TempDevicePath2 = AppendDevicePathInstance ( + TempDevicePath1, + DevicePathInst + ); + if (TempDevicePath1 != NULL) { + FreePool (TempDevicePath1); + } + TempDevicePath1 = TempDevicePath2; + } + } + + FreePool (DevicePathInst); + DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size); + } + + if (Delete) { + // + // Return the new device path data structure with specified node deleted. + // + *NewDevicePath = TempDevicePath1; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + Update console environment variables. + + @param VariableName Console environment variables, ConOutDev, ConInDev + ErrOutDev, ConIn ,ConOut or ErrOut. + @param DevicePath Console devcie's device path. + @param Operation Variable operations, including APPEND, CHECK and DELETE. + + @retval EFI_SUCCESS Variable operates successfully. + @retval EFI_OUT_OF_RESOURCES If variable cannot be appended. + @retval other Variable updating failed. + +**/ +EFI_STATUS +ConPlatformUpdateDeviceVariable ( + IN CHAR16 *VariableName, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CONPLATFORM_VAR_OPERATION Operation + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *VariableDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewVariableDevicePath; + + VariableDevicePath = NULL; + NewVariableDevicePath = NULL; + + // + // Get Variable according to variable name. + // The memory for Variable is allocated within ConPlatformGetVarible(), + // it is the caller's responsibility to free the memory before return. + // + VariableDevicePath = ConPlatformGetVariable (VariableName); + + if (Operation != Delete) { + // + // Match specified DevicePath in Console Variable. + // + Status = ConPlatformMatchDevicePaths ( + VariableDevicePath, + DevicePath, + NULL, + FALSE + ); + + if ((Operation == Check) || (!EFI_ERROR (Status))) { + // + // Branch here includes 2 cases: + // 1. Operation is CHECK, simply return Status. + // 2. Operation is APPEND, and device path already exists in variable, also return. + // + if (VariableDevicePath != NULL) { + FreePool (VariableDevicePath); + } + + return Status; + } + // + // We reach here to append a device path that does not exist in variable. + // + Status = EFI_SUCCESS; + NewVariableDevicePath = AppendDevicePathInstance ( + VariableDevicePath, + DevicePath + ); + if (NewVariableDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + + } else { + // + // We reach here to remove DevicePath from the environment variable that + // is a multi-instance device path. + // + Status = ConPlatformMatchDevicePaths ( + VariableDevicePath, + DevicePath, + &NewVariableDevicePath, + TRUE + ); + } + + if (VariableDevicePath != NULL) { + FreePool (VariableDevicePath); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (NewVariableDevicePath != NULL) { + // + // Update Console Environment Variable. + // + Status = gRT->SetVariable ( + VariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + GetDevicePathSize (NewVariableDevicePath), + NewVariableDevicePath + ); + + FreePool (NewVariableDevicePath); + } + + return Status; +} + +/** + Update ConOutDev and ErrOutDev variables to add the device path of + GOP controller itself and the sibling controllers. + + @param DevicePath Pointer to device's device path. + + @retval TRUE The devcie is a GOP device. + @retval FALSE The devcie is not a GOP device. + +**/ +BOOLEAN +ConPlatformUpdateGopCandidate ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + EFI_HANDLE PciHandle; + EFI_HANDLE GopHandle; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + + // + // Check whether it's a GOP device. + // + TempDevicePath = DevicePath; + Status = gBS->LocateDevicePath (&gEfiGraphicsOutputProtocolGuid, &TempDevicePath, &GopHandle); + if (EFI_ERROR (Status)) { + return FALSE; + } + // + // Get the parent PciIo handle in order to find all the children + // + Status = gBS->LocateDevicePath (&gEfiPciIoProtocolGuid, &DevicePath, &PciHandle); + if (EFI_ERROR (Status)) { + return FALSE; + } + TempDevicePath = EfiBootManagerGetGopDevicePath (PciHandle); + if (TempDevicePath != NULL) { + ConPlatformUpdateDeviceVariable (L"ConOutDev", TempDevicePath, Append); + ConPlatformUpdateDeviceVariable (L"ErrOutDev", TempDevicePath, Append); + } + return TRUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.h b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.h new file mode 100644 index 000000000..f11af2192 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.h @@ -0,0 +1,419 @@ +/** @file + Header file for Console Platfrom DXE Driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CON_PLATFORM_H_ +#define _CON_PLATFORM_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Driver Binding Externs +// +extern EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextInDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gConPlatformComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gConPlatformComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gConPlatformTextOutDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gConPlatformComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gConPlatformComponentName2; + + +typedef enum { + Check, + Append, + Delete +} CONPLATFORM_VAR_OPERATION; + +/** + Test to see if specific protocol could be supported on the ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param ProtocolGuid The specfic protocol. + + @retval EFI_SUCCESS This driver supports this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +ConPlatformDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_GUID *ProtocolGuid + ); + +/** + Test to see if EFI_SIMPLE_TEXT_INPUT_PROTOCOL is supported on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextInDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Test to see if EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL is supported on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextOutDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on the device for console input. + + Start this driver on ControllerHandle by opening Simple Text Input Protocol, + reading Device Path, and installing Console In Devcice GUID on ControllerHandle. + + Append its device path into the console environment variables ConInDev. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextInDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on the device for console output and standard error output. + + Start this driver on ControllerHandle by opening Simple Text Output Protocol, + reading Device Path, and installing Console Out Devcic GUID, Standard Error + Device GUID on ControllerHandle. + + Append its device path into the console environment variables ConOutDev, ErrOutDev. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextOutDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle by removing Console In Devcice GUID + and closing the Simple Text Input protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextInDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Stop this driver on ControllerHandle by removing Console Out Devcice GUID + and closing the Simple Text Output protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConPlatformTextOutDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Uninstall the specified protocol. + + @param This Protocol instance pointer. + @param Handle Handle of device to uninstall protocol on. + @param ProtocolGuid The specified protocol need to be uninstalled. + +**/ +VOID +ConPlatformUnInstallProtocol ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_GUID *ProtocolGuid + ); + +/** + Read the EFI variable (Name) and return a dynamically allocated + buffer, and the size of the buffer. On failure return NULL. + + @param Name String part of EFI variable name + + @return Dynamically allocated memory that contains a copy of the EFI variable. + Caller is repsoncible freeing the buffer. Return NULL means Variable + was not read. + +**/ +VOID * +ConPlatformGetVariable ( + IN CHAR16 *Name + ); + +/** + Function compares a device path data structure to that of all the nodes of a + second device path instance. + + + @param Multi A pointer to a multi-instance device path data structure. + @param Single A pointer to a single-instance device path data structure. + @param NewDevicePath If Delete is TRUE, this parameter must not be null, and it + points to the remaining device path data structure. + (remaining device path = Multi - Single.) + @param Delete If TRUE, means removing Single from Multi. + If FALSE, the routine just check whether Single matches + with any instance in Multi. + + @retval EFI_SUCCESS If the Single is contained within Multi. + @retval EFI_NOT_FOUND If the Single is not contained within Multi. + @retval EFI_INVALID_PARAMETER Multi is NULL. + @retval EFI_INVALID_PARAMETER Single is NULL. + @retval EFI_INVALID_PARAMETER NewDevicePath is NULL when Delete is TRUE. + +**/ +EFI_STATUS +ConPlatformMatchDevicePaths ( + IN EFI_DEVICE_PATH_PROTOCOL *Multi, + IN EFI_DEVICE_PATH_PROTOCOL *Single, + OUT EFI_DEVICE_PATH_PROTOCOL **NewDevicePath OPTIONAL, + IN BOOLEAN Delete + ); + +/** + Update console environment variables. + + @param VariableName Console environment variables, ConOutDev, ConInDev + StdErrDev, ConIn or ConOut. + @param DevicePath Console devcie's device path. + @param Operation Variable operations, including APPEND, CHECK and DELETE. + + @retval EFI_SUCCESS Variable operates successfully. + @retval EFI_OUT_OF_RESOURCES If variable cannot be appended. + @retval other Variable updating failed. + +**/ +EFI_STATUS +ConPlatformUpdateDeviceVariable ( + IN CHAR16 *VariableName, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CONPLATFORM_VAR_OPERATION Operation + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConPlatformComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConPlatformComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Update ConOutDev and ErrOutDev variables to add the device path of + GOP controller itself and the sibling controllers. + + @param DevicePath Pointer to device's device path. + + @retval TRUE The devcie is a GOP device. + @retval FALSE The devcie is not a GOP device. + +**/ +BOOLEAN +ConPlatformUpdateGopCandidate ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf new file mode 100644 index 000000000..ad11c09c7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf @@ -0,0 +1,95 @@ +## @file +# Platform console driver manages console devices. +# +# Console Platfrom DXE Driver that specifies whether device can be used as console +# input/output device or error output device and update global variables accordingly. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ConPlatformDxe + MODULE_UNI_FILE = ConPlatformDxe.uni + FILE_GUID = 51ccf399-4fdf-4e55-a45b-e123f84d456a + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeConPlatform + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gConPlatformTextInDriverBinding +# COMPONENT_NAME = gConPlatformComponentName +# COMPONENT_NAME2 = gConPlatformComponentName2 +# DRIVER_BINDING = gConPlatformTextOutDriverBinding +# COMPONENT_NAME = gConPlatformComponentName +# COMPONENT_NAME2 = gConPlatformComponentName2 +# + +[Sources] + ComponentName.c + ConPlatform.h + ConPlatform.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MemoryAllocationLib + DevicePathLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + BaseMemoryLib + UefiLib + UefiDriverEntryPoint + DebugLib + UefiBootManagerLib + +[Guids] + # + # This is the VendorGuid of all architecturally defined variables in UEFI spec. + # + ## SOMETIMES_CONSUMES ## Variable:L"ConIn" + ## SOMETIMES_CONSUMES ## Variable:L"ConOut" + ## SOMETIMES_CONSUMES ## Variable:L"ErrOut" + ## SOMETIMES_PRODUCES ## Variable:L"ConInDev" + ## SOMETIMES_PRODUCES ## Variable:L"ConOutDev" + ## SOMETIMES_PRODUCES ## Variable:L"ErrOutDev" + gEfiGlobalVariableGuid + # + # This GUID is used to specify the device is the standard error device. + # If the device is a standard error device, this GUID as the protocol GUID will be installed + # onto this device handle. + # + gEfiStandardErrorDeviceGuid ## SOMETIMES_PRODUCES ## UNDEFINED # protocol GUID installed on device handle + # + # This GUID is used to specify the device is the console output device. + # If the device is a console output device, this GUID as the protocol GUID will be installed + # onto this device handle. + # + gEfiConsoleOutDeviceGuid ## SOMETIMES_PRODUCES ## UNDEFINED # protocol GUID installed on device handle + # + # This GUID is used to specify the device is the console input device. + # If the device is a console input device, this GUID as the protocol GUID will be installed + # onto this device handle. + # + gEfiConsoleInDeviceGuid ## SOMETIMES_PRODUCES ## UNDEFINED # protocol GUID installed on device handle + +[Protocols] + gEfiDevicePathProtocolGuid ## TO_START + gEfiSimpleTextInProtocolGuid ## TO_START + gEfiSimpleTextOutProtocolGuid ## TO_START + gEfiPciIoProtocolGuid ## SOMETIMES_CONSUMES + gEfiGraphicsOutputProtocolGuid ## SOMETIMES_CONSUMES + gEfiUsbIoProtocolGuid ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + ConPlatformDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.uni b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.uni new file mode 100644 index 000000000..40c54790f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// Platform console driver manages console devices. +// +// Console Platfrom DXE Driver that specifies whether device can be used as console +// input/output device or error output device and update global variables accordingly. +// +// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Manages console devices" + +#string STR_MODULE_DESCRIPTION #language en-US "Console Platform DXE Driver that specifies whether a device can be used as console input/output device or error output device and updates global variables accordingly." + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxeExtra.uni new file mode 100644 index 000000000..797a143be --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// ConPlatformDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Console Platform DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ComponentName.c new file mode 100644 index 000000000..72c01ece5 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ComponentName.c @@ -0,0 +1,770 @@ +/** @file + UEFI Component Name(2) protocol implementation for ConSplitter driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "ConSplitter.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gConSplitterConInComponentName = { + ConSplitterComponentNameGetDriverName, + ConSplitterConInComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gConSplitterConInComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ConSplitterComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ConSplitterConInComponentNameGetControllerName, + "en" +}; + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gConSplitterSimplePointerComponentName = { + ConSplitterComponentNameGetDriverName, + ConSplitterSimplePointerComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gConSplitterSimplePointerComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ConSplitterComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ConSplitterSimplePointerComponentNameGetControllerName, + "en" +}; + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gConSplitterAbsolutePointerComponentName = { + ConSplitterComponentNameGetDriverName, + ConSplitterAbsolutePointerComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gConSplitterAbsolutePointerComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ConSplitterComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ConSplitterAbsolutePointerComponentNameGetControllerName, + "en" +}; + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gConSplitterConOutComponentName = { + ConSplitterComponentNameGetDriverName, + ConSplitterConOutComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gConSplitterConOutComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ConSplitterComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ConSplitterConOutComponentNameGetControllerName, + "en" +}; + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gConSplitterStdErrComponentName = { + ConSplitterComponentNameGetDriverName, + ConSplitterStdErrComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gConSplitterStdErrComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ConSplitterComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ConSplitterStdErrComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mConSplitterDriverNameTable[] = { + { + "eng;en", + (CHAR16 *) L"Console Splitter Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mConSplitterConInControllerNameTable[] = { + { + "eng;en", + (CHAR16 *) L"Primary Console Input Device" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mConSplitterSimplePointerControllerNameTable[] = { + { + "eng;en", + (CHAR16 *) L"Primary Simple Pointer Device" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mConSplitterAbsolutePointerControllerNameTable[] = { + { + "eng;en", + (CHAR16 *)L"Primary Absolute Pointer Device" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mConSplitterConOutControllerNameTable[] = { + { + "eng;en", + (CHAR16 *) L"Primary Console Output Device" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mConSplitterStdErrControllerNameTable[] = { + { + "eng;en", + (CHAR16 *) L"Primary Standard Error Device" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mConSplitterDriverNameTable, + DriverName, + (BOOLEAN)((This == &gConSplitterConInComponentName) || + (This == &gConSplitterSimplePointerComponentName) || + (This == &gConSplitterAbsolutePointerComponentName) || + (This == &gConSplitterConOutComponentName) || + (This == &gConSplitterStdErrComponentName)) + ); +} + +/** + Tests whether a controller handle is being managed by a specific driver and + the child handle is a child device of the controller. + + @param ControllerHandle A handle for a controller to test. + @param DriverBindingHandle Specifies the driver binding handle for the + driver. + @param ProtocolGuid Specifies the protocol that the driver specified + by DriverBindingHandle opens in its Start() + function. + @param ChildHandle A child handle to test. + @param ConsumsedGuid Supplies the protocol that the child controller + opens on its parent controller. + + @retval EFI_SUCCESS ControllerHandle is managed by the driver + specifed by DriverBindingHandle and ChildHandle + is a child of the ControllerHandle. + @retval EFI_UNSUPPORTED ControllerHandle is not managed by the driver + specifed by DriverBindingHandle. + @retval EFI_UNSUPPORTED ChildHandle is not a child of the + ControllerHandle. + +**/ +EFI_STATUS +ConSplitterTestControllerHandles ( + IN CONST EFI_HANDLE ControllerHandle, + IN CONST EFI_HANDLE DriverBindingHandle, + IN CONST EFI_GUID *ProtocolGuid, + IN EFI_HANDLE ChildHandle, + IN CONST EFI_GUID *ConsumsedGuid + ) +{ + EFI_STATUS Status; + + // + // here ChildHandle is not an Optional parameter. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Tests whether a controller handle is being managed by a specific driver. + // + Status = EfiTestManagedDevice ( + ControllerHandle, + DriverBindingHandle, + ProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Tests whether a child handle is a child device of the controller. + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + ConsumsedGuid + ); + + return Status; +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + Status = ConSplitterTestControllerHandles ( + ControllerHandle, + gConSplitterConInDriverBinding.DriverBindingHandle, + &gEfiConsoleInDeviceGuid, + ChildHandle, + &gEfiConsoleInDeviceGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mConSplitterConInControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gConSplitterConInComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + Status = ConSplitterTestControllerHandles ( + ControllerHandle, + gConSplitterSimplePointerDriverBinding.DriverBindingHandle, + &gEfiSimplePointerProtocolGuid, + ChildHandle, + &gEfiSimplePointerProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mConSplitterSimplePointerControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gConSplitterSimplePointerComponentName) + ); +} + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL + instance. + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param ChildHandle The handle of the child controller to retrieve the + name of. This is an optional parameter that may + be NULL. It will be NULL for device drivers. It + will also be NULL for a bus drivers that wish to + retrieve the name of the bus controller. It will + not be NULL for a bus driver that wishes to + retrieve the name of a child controller. + @param Language A pointer to RFC4646 language identifier. This is + the language of the controller name that that the + caller is requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up to + the driver writer. + @param ControllerName A pointer to the Unicode string to return. This + Unicode string is the name of the controller + specified by ControllerHandle and ChildHandle in + the language specified by Language from the point + of view of the driver specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + Status = ConSplitterTestControllerHandles ( + ControllerHandle, + gConSplitterAbsolutePointerDriverBinding.DriverBindingHandle, + &gEfiAbsolutePointerProtocolGuid, + ChildHandle, + &gEfiAbsolutePointerProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mConSplitterAbsolutePointerControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gConSplitterAbsolutePointerComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + Status = ConSplitterTestControllerHandles ( + ControllerHandle, + gConSplitterConOutDriverBinding.DriverBindingHandle, + &gEfiConsoleOutDeviceGuid, + ChildHandle, + &gEfiConsoleOutDeviceGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mConSplitterConOutControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gConSplitterConOutComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + Status = ConSplitterTestControllerHandles ( + ControllerHandle, + gConSplitterStdErrDriverBinding.DriverBindingHandle, + &gEfiStandardErrorDeviceGuid, + ChildHandle, + &gEfiStandardErrorDeviceGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mConSplitterStdErrControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gConSplitterStdErrComponentName) + ); +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.c b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.c new file mode 100644 index 000000000..9c38271b6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.c @@ -0,0 +1,5113 @@ +/** @file + Console Splitter Driver. Any Handle that attatched console I/O protocols + (Console In device, Console Out device, Console Error device, Simple Pointer + protocol, Absolute Pointer protocol) can be bound by this driver. + + So far it works like any other driver by opening a SimpleTextIn and/or + SimpleTextOut protocol with EFI_OPEN_PROTOCOL_BY_DRIVER attributes. The big + difference is this driver does not layer a protocol on the passed in + handle, or construct a child handle like a standard device or bus driver. + This driver produces three virtual handles as children, one for console input + splitter, one for console output splitter and one for error output splitter. + These 3 virtual handles would be installed on gST. + + Each virtual handle, that supports the Console I/O protocol, will be produced + in the driver entry point. The virtual handle are added on driver entry and + never removed. Such design ensures sytem function well during none console + device situation. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "ConSplitter.h" + +// +// Identify if ConIn is connected in PcdConInConnectOnDemand enabled mode. +// default not connect +// +BOOLEAN mConInIsConnect = FALSE; + +// +// Text In Splitter Private Data template +// +GLOBAL_REMOVE_IF_UNREFERENCED TEXT_IN_SPLITTER_PRIVATE_DATA mConIn = { + TEXT_IN_SPLITTER_PRIVATE_DATA_SIGNATURE, + (EFI_HANDLE) NULL, + + { + ConSplitterTextInReset, + ConSplitterTextInReadKeyStroke, + (EFI_EVENT) NULL + }, + 0, + (EFI_SIMPLE_TEXT_INPUT_PROTOCOL **) NULL, + 0, + + { + ConSplitterTextInResetEx, + ConSplitterTextInReadKeyStrokeEx, + (EFI_EVENT) NULL, + ConSplitterTextInSetState, + ConSplitterTextInRegisterKeyNotify, + ConSplitterTextInUnregisterKeyNotify + }, + 0, + (EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL **) NULL, + 0, + { + (LIST_ENTRY *) NULL, + (LIST_ENTRY *) NULL + }, + (EFI_KEY_DATA *) NULL, + 0, + 0, + FALSE, + + { + ConSplitterSimplePointerReset, + ConSplitterSimplePointerGetState, + (EFI_EVENT) NULL, + (EFI_SIMPLE_POINTER_MODE *) NULL + }, + { + 0x10000, + 0x10000, + 0x10000, + TRUE, + TRUE + }, + 0, + (EFI_SIMPLE_POINTER_PROTOCOL **) NULL, + 0, + + { + ConSplitterAbsolutePointerReset, + ConSplitterAbsolutePointerGetState, + (EFI_EVENT) NULL, + (EFI_ABSOLUTE_POINTER_MODE *) NULL + }, + { + 0, // AbsoluteMinX + 0, // AbsoluteMinY + 0, // AbsoluteMinZ + 0x10000, // AbsoluteMaxX + 0x10000, // AbsoluteMaxY + 0x10000, // AbsoluteMaxZ + 0 // Attributes + }, + 0, + (EFI_ABSOLUTE_POINTER_PROTOCOL **) NULL, + 0, + FALSE, + + FALSE, + FALSE +}; + + +// +// Uga Draw Protocol Private Data template +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UGA_DRAW_PROTOCOL mUgaDrawProtocolTemplate = { + ConSplitterUgaDrawGetMode, + ConSplitterUgaDrawSetMode, + ConSplitterUgaDrawBlt +}; + +// +// Graphics Output Protocol Private Data template +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GRAPHICS_OUTPUT_PROTOCOL mGraphicsOutputProtocolTemplate = { + ConSplitterGraphicsOutputQueryMode, + ConSplitterGraphicsOutputSetMode, + ConSplitterGraphicsOutputBlt, + NULL +}; + + +// +// Text Out Splitter Private Data template +// +GLOBAL_REMOVE_IF_UNREFERENCED TEXT_OUT_SPLITTER_PRIVATE_DATA mConOut = { + TEXT_OUT_SPLITTER_PRIVATE_DATA_SIGNATURE, + (EFI_HANDLE) NULL, + { + ConSplitterTextOutReset, + ConSplitterTextOutOutputString, + ConSplitterTextOutTestString, + ConSplitterTextOutQueryMode, + ConSplitterTextOutSetMode, + ConSplitterTextOutSetAttribute, + ConSplitterTextOutClearScreen, + ConSplitterTextOutSetCursorPosition, + ConSplitterTextOutEnableCursor, + (EFI_SIMPLE_TEXT_OUTPUT_MODE *) NULL + }, + { + 1, + 0, + 0, + 0, + 0, + FALSE, + }, + + { + NULL, + NULL, + NULL + }, + 0, + 0, + 0, + 0, + + { + NULL, + NULL, + NULL, + NULL + }, + (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *) NULL, + 0, + 0, + + 0, + (TEXT_OUT_AND_GOP_DATA *) NULL, + 0, + (TEXT_OUT_SPLITTER_QUERY_DATA *) NULL, + 0, + (INT32 *) NULL, + FALSE +}; + +// +// Standard Error Text Out Splitter Data Template +// +GLOBAL_REMOVE_IF_UNREFERENCED TEXT_OUT_SPLITTER_PRIVATE_DATA mStdErr = { + TEXT_OUT_SPLITTER_PRIVATE_DATA_SIGNATURE, + (EFI_HANDLE) NULL, + { + ConSplitterTextOutReset, + ConSplitterTextOutOutputString, + ConSplitterTextOutTestString, + ConSplitterTextOutQueryMode, + ConSplitterTextOutSetMode, + ConSplitterTextOutSetAttribute, + ConSplitterTextOutClearScreen, + ConSplitterTextOutSetCursorPosition, + ConSplitterTextOutEnableCursor, + (EFI_SIMPLE_TEXT_OUTPUT_MODE *) NULL + }, + { + 1, + 0, + 0, + 0, + 0, + FALSE, + }, + + { + NULL, + NULL, + NULL + }, + 0, + 0, + 0, + 0, + + { + NULL, + NULL, + NULL, + NULL + }, + (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *) NULL, + 0, + 0, + + 0, + (TEXT_OUT_AND_GOP_DATA *) NULL, + 0, + (TEXT_OUT_SPLITTER_QUERY_DATA *) NULL, + 0, + (INT32 *) NULL, + FALSE +}; + +// +// Driver binding instance for Console Input Device +// +EFI_DRIVER_BINDING_PROTOCOL gConSplitterConInDriverBinding = { + ConSplitterConInDriverBindingSupported, + ConSplitterConInDriverBindingStart, + ConSplitterConInDriverBindingStop, + 0xa, + NULL, + NULL +}; + +// +// Driver binding instance for Console Out device +// +EFI_DRIVER_BINDING_PROTOCOL gConSplitterConOutDriverBinding = { + ConSplitterConOutDriverBindingSupported, + ConSplitterConOutDriverBindingStart, + ConSplitterConOutDriverBindingStop, + 0xa, + NULL, + NULL +}; + +// +// Driver binding instance for Standard Error device +// +EFI_DRIVER_BINDING_PROTOCOL gConSplitterStdErrDriverBinding = { + ConSplitterStdErrDriverBindingSupported, + ConSplitterStdErrDriverBindingStart, + ConSplitterStdErrDriverBindingStop, + 0xa, + NULL, + NULL +}; + +// +// Driver binding instance for Simple Pointer protocol +// +EFI_DRIVER_BINDING_PROTOCOL gConSplitterSimplePointerDriverBinding = { + ConSplitterSimplePointerDriverBindingSupported, + ConSplitterSimplePointerDriverBindingStart, + ConSplitterSimplePointerDriverBindingStop, + 0xa, + NULL, + NULL +}; + +// +// Driver binding instance for Absolute Pointer protocol +// +EFI_DRIVER_BINDING_PROTOCOL gConSplitterAbsolutePointerDriverBinding = { + ConSplitterAbsolutePointerDriverBindingSupported, + ConSplitterAbsolutePointerDriverBindingStart, + ConSplitterAbsolutePointerDriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + Key notify for toggle state sync. + + @param KeyData A pointer to a buffer that is filled in with + the keystroke information for the key that was + pressed. + + @retval EFI_SUCCESS Toggle state sync successfully. + +**/ +EFI_STATUS +EFIAPI +ToggleStateSyncKeyNotify ( + IN EFI_KEY_DATA *KeyData + ) +{ + UINTN Index; + + if (((KeyData->KeyState.KeyToggleState & KEY_STATE_VALID_EXPOSED) == KEY_STATE_VALID_EXPOSED) && + (KeyData->KeyState.KeyToggleState != mConIn.PhysicalKeyToggleState)) { + // + // There is toggle state change, sync to other console input devices. + // + for (Index = 0; Index < mConIn.CurrentNumberOfExConsoles; Index++) { + mConIn.TextInExList[Index]->SetState ( + mConIn.TextInExList[Index], + &KeyData->KeyState.KeyToggleState + ); + } + mConIn.PhysicalKeyToggleState = KeyData->KeyState.KeyToggleState; + DEBUG ((EFI_D_INFO, "Current toggle state is 0x%02x\n", mConIn.PhysicalKeyToggleState)); + } + + return EFI_SUCCESS; +} + +/** + Initialization for toggle state sync. + + @param Private Text In Splitter pointer. + +**/ +VOID +ToggleStateSyncInitialization ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private + ) +{ + EFI_KEY_DATA KeyData; + VOID *NotifyHandle; + + // + // Initialize PhysicalKeyToggleState that will be synced to new console + // input device to turn on physical TextInEx partial key report for + // toggle state sync. + // + Private->PhysicalKeyToggleState = KEY_STATE_VALID_EXPOSED; + + // + // Initialize VirtualKeyStateExported to let the virtual TextInEx not report + // the partial key even though the physical TextInEx turns on the partial + // key report. The virtual TextInEx will report the partial key after it is + // required by calling SetState(X | KEY_STATE_VALID_EXPOSED) explicitly. + // + Private->VirtualKeyStateExported = FALSE; + + // + // Register key notify for toggle state sync. + // + KeyData.Key.ScanCode = SCAN_NULL; + KeyData.Key.UnicodeChar = CHAR_NULL; + KeyData.KeyState.KeyShiftState = 0; + KeyData.KeyState.KeyToggleState = 0; + Private->TextInEx.RegisterKeyNotify ( + &Private->TextInEx, + &KeyData, + ToggleStateSyncKeyNotify, + &NotifyHandle + ); +} + +/** + Reinitialization for toggle state sync. + + @param Private Text In Splitter pointer. + +**/ +VOID +ToggleStateSyncReInitialization ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private + ) +{ + UINTN Index; + + // + // Reinitialize PhysicalKeyToggleState that will be synced to new console + // input device to turn on physical TextInEx partial key report for + // toggle state sync. + // + Private->PhysicalKeyToggleState = KEY_STATE_VALID_EXPOSED; + + // + // Reinitialize VirtualKeyStateExported to let the virtual TextInEx not report + // the partial key even though the physical TextInEx turns on the partial + // key report. The virtual TextInEx will report the partial key after it is + // required by calling SetState(X | KEY_STATE_VALID_EXPOSED) explicitly. + // + Private->VirtualKeyStateExported = FALSE; + + for (Index = 0; Index < Private->CurrentNumberOfExConsoles; Index++) { + Private->TextInExList[Index]->SetState ( + Private->TextInExList[Index], + &Private->PhysicalKeyToggleState + ); + } +} + +/** + The Entry Point for module ConSplitter. The user code starts with this function. + + Installs driver module protocols and. Creates virtual device handles for ConIn, + ConOut, and StdErr. Installs Simple Text In protocol, Simple Text In Ex protocol, + Simple Pointer protocol, Absolute Pointer protocol on those virtual handlers. + Installs Graphics Output protocol and/or UGA Draw protocol if needed. + + @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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +ConSplitterDriverEntry( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gConSplitterConInDriverBinding, + ImageHandle, + &gConSplitterConInComponentName, + &gConSplitterConInComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gConSplitterSimplePointerDriverBinding, + NULL, + &gConSplitterSimplePointerComponentName, + &gConSplitterSimplePointerComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gConSplitterAbsolutePointerDriverBinding, + NULL, + &gConSplitterAbsolutePointerComponentName, + &gConSplitterAbsolutePointerComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gConSplitterConOutDriverBinding, + NULL, + &gConSplitterConOutComponentName, + &gConSplitterConOutComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gConSplitterStdErrDriverBinding, + NULL, + &gConSplitterStdErrComponentName, + &gConSplitterStdErrComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Either Graphics Output protocol or UGA Draw protocol must be supported. + // + ASSERT (FeaturePcdGet (PcdConOutGopSupport) || + FeaturePcdGet (PcdConOutUgaSupport)); + + // + // The driver creates virtual handles for ConIn, ConOut, StdErr. + // The virtual handles will always exist even if no console exist in the + // system. This is need to support hotplug devices like USB. + // + // + // Create virtual device handle for ConIn Splitter + // + Status = ConSplitterTextInConstructor (&mConIn); + if (!EFI_ERROR (Status)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mConIn.VirtualHandle, + &gEfiSimpleTextInProtocolGuid, + &mConIn.TextIn, + &gEfiSimpleTextInputExProtocolGuid, + &mConIn.TextInEx, + &gEfiSimplePointerProtocolGuid, + &mConIn.SimplePointer, + &gEfiAbsolutePointerProtocolGuid, + &mConIn.AbsolutePointer, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Update the EFI System Table with new virtual console + // and update the pointer to Simple Text Input protocol. + // + gST->ConsoleInHandle = mConIn.VirtualHandle; + gST->ConIn = &mConIn.TextIn; + } + } + // + // Create virtual device handle for ConOut Splitter + // + Status = ConSplitterTextOutConstructor (&mConOut); + if (!EFI_ERROR (Status)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mConOut.VirtualHandle, + &gEfiSimpleTextOutProtocolGuid, + &mConOut.TextOut, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Update the EFI System Table with new virtual console + // and Update the pointer to Text Output protocol. + // + gST->ConsoleOutHandle = mConOut.VirtualHandle; + gST->ConOut = &mConOut.TextOut; + } + + } + + // + // Create virtual device handle for StdErr Splitter + // + Status = ConSplitterTextOutConstructor (&mStdErr); + if (!EFI_ERROR (Status)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mStdErr.VirtualHandle, + &gEfiSimpleTextOutProtocolGuid, + &mStdErr.TextOut, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Update the EFI System Table with new virtual console + // and update the pointer to Text Output protocol. + // + gST->StandardErrorHandle = mStdErr.VirtualHandle; + gST->StdErr = &mStdErr.TextOut; + } + } + + // + // Update the CRC32 in the EFI System Table header + // + gST->Hdr.CRC32 = 0; + gBS->CalculateCrc32 ( + (UINT8 *) &gST->Hdr, + gST->Hdr.HeaderSize, + &gST->Hdr.CRC32 + ); + + return EFI_SUCCESS; + +} + +/** + Construct console input devices' private data. + + @param ConInPrivate A pointer to the TEXT_IN_SPLITTER_PRIVATE_DATA + structure. + + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_SUCCESS Text Input Devcie's private data has been constructed. + @retval other Failed to construct private data. + +**/ +EFI_STATUS +ConSplitterTextInConstructor ( + TEXT_IN_SPLITTER_PRIVATE_DATA *ConInPrivate + ) +{ + EFI_STATUS Status; + UINTN TextInExListCount; + + // + // Allocate buffer for Simple Text Input device + // + Status = ConSplitterGrowBuffer ( + sizeof (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *), + &ConInPrivate->TextInListCount, + (VOID **) &ConInPrivate->TextInList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create Event to wait for a key + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + ConSplitterTextInWaitForKey, + ConInPrivate, + &ConInPrivate->TextIn.WaitForKey + ); + ASSERT_EFI_ERROR (Status); + + // + // Allocate buffer for KeyQueue + // + TextInExListCount = ConInPrivate->TextInExListCount; + Status = ConSplitterGrowBuffer ( + sizeof (EFI_KEY_DATA), + &TextInExListCount, + (VOID **) &ConInPrivate->KeyQueue + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Allocate buffer for Simple Text Input Ex device + // + Status = ConSplitterGrowBuffer ( + sizeof (EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *), + &ConInPrivate->TextInExListCount, + (VOID **) &ConInPrivate->TextInExList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // Create Event to wait for a key Ex + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + ConSplitterTextInWaitForKey, + ConInPrivate, + &ConInPrivate->TextInEx.WaitForKeyEx + ); + ASSERT_EFI_ERROR (Status); + + InitializeListHead (&ConInPrivate->NotifyList); + + ToggleStateSyncInitialization (ConInPrivate); + + ConInPrivate->AbsolutePointer.Mode = &ConInPrivate->AbsolutePointerMode; + // + // Allocate buffer for Absolute Pointer device + // + Status = ConSplitterGrowBuffer ( + sizeof (EFI_ABSOLUTE_POINTER_PROTOCOL *), + &ConInPrivate->AbsolutePointerListCount, + (VOID **) &ConInPrivate->AbsolutePointerList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // Create Event to wait for device input for Absolute pointer device + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + ConSplitterAbsolutePointerWaitForInput, + ConInPrivate, + &ConInPrivate->AbsolutePointer.WaitForInput + ); + ASSERT_EFI_ERROR (Status); + + ConInPrivate->SimplePointer.Mode = &ConInPrivate->SimplePointerMode; + // + // Allocate buffer for Simple Pointer device + // + Status = ConSplitterGrowBuffer ( + sizeof (EFI_SIMPLE_POINTER_PROTOCOL *), + &ConInPrivate->PointerListCount, + (VOID **) &ConInPrivate->PointerList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // Create Event to wait for device input for Simple pointer device + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + ConSplitterSimplePointerWaitForInput, + ConInPrivate, + &ConInPrivate->SimplePointer.WaitForInput + ); + ASSERT_EFI_ERROR (Status); + // + // Create Event to signal ConIn connection request + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + EfiEventEmptyFunction, + NULL, + &gConnectConInEventGuid, + &ConInPrivate->ConnectConInEvent + ); + + return Status; +} + +/** + Construct console output devices' private data. + + @param ConOutPrivate A pointer to the TEXT_OUT_SPLITTER_PRIVATE_DATA + structure. + + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_SUCCESS Text Input Devcie's private data has been constructed. + +**/ +EFI_STATUS +ConSplitterTextOutConstructor ( + TEXT_OUT_SPLITTER_PRIVATE_DATA *ConOutPrivate + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + + // + // Copy protocols template + // + if (FeaturePcdGet (PcdConOutUgaSupport)) { + CopyMem (&ConOutPrivate->UgaDraw, &mUgaDrawProtocolTemplate, sizeof (EFI_UGA_DRAW_PROTOCOL)); + } + if (FeaturePcdGet (PcdConOutGopSupport)) { + CopyMem (&ConOutPrivate->GraphicsOutput, &mGraphicsOutputProtocolTemplate, sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL)); + } + + // + // Initilize console output splitter's private data. + // + ConOutPrivate->TextOut.Mode = &ConOutPrivate->TextOutMode; + + // + // When new console device is added, the new mode will be set later, + // so put current mode back to init state. + // + ConOutPrivate->TextOutMode.Mode = 0xFF; + // + // Allocate buffer for Console Out device + // + Status = ConSplitterGrowBuffer ( + sizeof (TEXT_OUT_AND_GOP_DATA), + &ConOutPrivate->TextOutListCount, + (VOID **) &ConOutPrivate->TextOutList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // Allocate buffer for Text Out query data + // + Status = ConSplitterGrowBuffer ( + sizeof (TEXT_OUT_SPLITTER_QUERY_DATA), + &ConOutPrivate->TextOutQueryDataCount, + (VOID **) &ConOutPrivate->TextOutQueryData + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Setup the default console to 80 x 25 and mode to 0 + // + ConOutPrivate->TextOutQueryData[0].Columns = 80; + ConOutPrivate->TextOutQueryData[0].Rows = 25; + TextOutSetMode (ConOutPrivate, 0); + + + if (FeaturePcdGet (PcdConOutUgaSupport)) { + // + // Setup the UgaDraw to 800 x 600 x 32 bits per pixel, 60Hz. + // + ConSplitterUgaDrawSetMode (&ConOutPrivate->UgaDraw, 800, 600, 32, 60); + } + if (FeaturePcdGet (PcdConOutGopSupport)) { + // + // Setup resource for mode information in Graphics Output Protocol interface + // + if ((ConOutPrivate->GraphicsOutput.Mode = AllocateZeroPool (sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE))) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + if ((ConOutPrivate->GraphicsOutput.Mode->Info = AllocateZeroPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION))) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Setup the DevNullGraphicsOutput to 800 x 600 x 32 bits per pixel + // DevNull will be updated to user-defined mode after driver has started. + // + if ((ConOutPrivate->GraphicsOutputModeBuffer = AllocateZeroPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION))) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Info = &ConOutPrivate->GraphicsOutputModeBuffer[0]; + Info->Version = 0; + Info->HorizontalResolution = 800; + Info->VerticalResolution = 600; + Info->PixelFormat = PixelBltOnly; + Info->PixelsPerScanLine = 800; + CopyMem (ConOutPrivate->GraphicsOutput.Mode->Info, Info, sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + ConOutPrivate->GraphicsOutput.Mode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + // + // Initialize the following items, theset items remain unchanged in GraphicsOutput->SetMode() + // GraphicsOutputMode->FrameBufferBase, GraphicsOutputMode->FrameBufferSize + // + ConOutPrivate->GraphicsOutput.Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL; + ConOutPrivate->GraphicsOutput.Mode->FrameBufferSize = 0; + + ConOutPrivate->GraphicsOutput.Mode->MaxMode = 1; + // + // Initial current mode to unknown state, and then set to mode 0 + // + ConOutPrivate->GraphicsOutput.Mode->Mode = 0xffff; + ConOutPrivate->GraphicsOutput.SetMode (&ConOutPrivate->GraphicsOutput, 0); + } + + return EFI_SUCCESS; +} + + +/** + Test to see if the specified protocol could be supported on the specified device. + + @param This Driver Binding protocol pointer. + @param ControllerHandle Handle of device to test. + @param Guid The specified protocol. + + @retval EFI_SUCCESS The specified protocol is supported on this device. + @retval EFI_UNSUPPORTED The specified protocol attempts to be installed on virtul handle. + @retval other Failed to open specified protocol on this device. + +**/ +EFI_STATUS +ConSplitterSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_GUID *Guid + ) +{ + EFI_STATUS Status; + VOID *Instance; + + // + // Make sure the Console Splitter does not attempt to attach to itself + // + if (ControllerHandle == mConIn.VirtualHandle || + ControllerHandle == mConOut.VirtualHandle || + ControllerHandle == mStdErr.VirtualHandle + ) { + return EFI_UNSUPPORTED; + } + + // + // Check to see whether the specific protocol could be opened BY_DRIVER + // + Status = gBS->OpenProtocol ( + ControllerHandle, + Guid, + &Instance, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + ControllerHandle, + Guid, + This->DriverBindingHandle, + ControllerHandle + ); + + return EFI_SUCCESS; +} + +/** + Test to see if Console In Device could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return ConSplitterSupported ( + This, + ControllerHandle, + &gEfiConsoleInDeviceGuid + ); +} + +/** + Test to see if Simple Pointer protocol could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return ConSplitterSupported ( + This, + ControllerHandle, + &gEfiSimplePointerProtocolGuid + ); +} + +/** + Test to see if Absolute Pointer protocol could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return ConSplitterSupported ( + This, + ControllerHandle, + &gEfiAbsolutePointerProtocolGuid + ); +} + + +/** + Test to see if Console Out Device could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return ConSplitterSupported ( + This, + ControllerHandle, + &gEfiConsoleOutDeviceGuid + ); +} + +/** + Test to see if Standard Error Device could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return ConSplitterSupported ( + This, + ControllerHandle, + &gEfiStandardErrorDeviceGuid + ); +} + + +/** + Start ConSplitter on devcie handle by opening Console Device Guid on device handle + and the console virtual handle. And Get the console interface on controller handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device. + @param ConSplitterVirtualHandle Console virtual Handle. + @param DeviceGuid The specified Console Device, such as ConInDev, + ConOutDev. + @param InterfaceGuid The specified protocol to be opened. + @param Interface Protocol interface returned. + + @retval EFI_SUCCESS This driver supports this device. + @retval other Failed to open the specified Console Device Guid + or specified protocol. + +**/ +EFI_STATUS +ConSplitterStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ConSplitterVirtualHandle, + IN EFI_GUID *DeviceGuid, + IN EFI_GUID *InterfaceGuid, + OUT VOID **Interface + ) +{ + EFI_STATUS Status; + VOID *Instance; + + // + // Check to see whether the ControllerHandle has the DeviceGuid on it. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + DeviceGuid, + &Instance, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the Parent Handle for the child. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + DeviceGuid, + &Instance, + This->DriverBindingHandle, + ConSplitterVirtualHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto Err; + } + + // + // Open InterfaceGuid on the virtul handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + InterfaceGuid, + Interface, + This->DriverBindingHandle, + ConSplitterVirtualHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + // + // close the DeviceGuid on ConSplitter VirtualHandle. + // + gBS->CloseProtocol ( + ControllerHandle, + DeviceGuid, + This->DriverBindingHandle, + ConSplitterVirtualHandle + ); + +Err: + // + // close the DeviceGuid on ControllerHandle. + // + gBS->CloseProtocol ( + ControllerHandle, + DeviceGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return Status; +} + + +/** + Start Console In Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Console In Consplitter is added to ControllerHandle. + @retval other Console In Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx; + + // + // Start ConSplitter on ControllerHandle, and create the virtual + // agrogated console device on first call Start for a SimpleTextIn handle. + // + Status = ConSplitterStart ( + This, + ControllerHandle, + mConIn.VirtualHandle, + &gEfiConsoleInDeviceGuid, + &gEfiSimpleTextInProtocolGuid, + (VOID **) &TextIn + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Add this device into Text In devices list. + // + Status = ConSplitterTextInAddDevice (&mConIn, TextIn); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleTextInputExProtocolGuid, + (VOID **) &TextInEx, + This->DriverBindingHandle, + mConIn.VirtualHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // If Simple Text Input Ex protocol exists, + // add this device into Text In Ex devices list. + // + Status = ConSplitterTextInExAddDevice (&mConIn, TextInEx); + } + + return Status; +} + + +/** + Start Simple Pointer Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Simple Pointer Consplitter is added to ControllerHandle. + @retval other Simple Pointer Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer; + + // + // Start ConSplitter on ControllerHandle, and create the virtual + // agrogated console device on first call Start for a SimplePointer handle. + // + Status = ConSplitterStart ( + This, + ControllerHandle, + mConIn.VirtualHandle, + &gEfiSimplePointerProtocolGuid, + &gEfiSimplePointerProtocolGuid, + (VOID **) &SimplePointer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Add this devcie into Simple Pointer devices list. + // + return ConSplitterSimplePointerAddDevice (&mConIn, SimplePointer); +} + + +/** + Start Absolute Pointer Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Absolute Pointer Consplitter is added to ControllerHandle. + @retval other Absolute Pointer Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointer; + + // + // Start ConSplitter on ControllerHandle, and create the virtual + // agrogated console device on first call Start for a AbsolutePointer handle. + // + Status = ConSplitterStart ( + This, + ControllerHandle, + mConIn.VirtualHandle, + &gEfiAbsolutePointerProtocolGuid, + &gEfiAbsolutePointerProtocolGuid, + (VOID **) &AbsolutePointer + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Add this devcie into Absolute Pointer devices list. + // + return ConSplitterAbsolutePointerAddDevice (&mConIn, AbsolutePointer); +} + + +/** + Start Console Out Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Console Out Consplitter is added to ControllerHandle. + @retval other Console Out Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + UINTN SizeOfInfo; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + + // + // Start ConSplitter on ControllerHandle, and create the virtual + // agrogated console device on first call Start for a ConsoleOut handle. + // + Status = ConSplitterStart ( + This, + ControllerHandle, + mConOut.VirtualHandle, + &gEfiConsoleOutDeviceGuid, + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &TextOut + ); + if (EFI_ERROR (Status)) { + return Status; + } + + GraphicsOutput = NULL; + UgaDraw = NULL; + // + // Try to Open Graphics Output protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &GraphicsOutput, + This->DriverBindingHandle, + mConOut.VirtualHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status) && FeaturePcdGet (PcdUgaConsumeSupport)) { + // + // Open UGA DRAW protocol + // + gBS->OpenProtocol ( + ControllerHandle, + &gEfiUgaDrawProtocolGuid, + (VOID **) &UgaDraw, + This->DriverBindingHandle, + mConOut.VirtualHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + } + + // + // When new console device is added, the new mode will be set later, + // so put current mode back to init state. + // + mConOut.TextOutMode.Mode = 0xFF; + + // + // If both ConOut and StdErr incorporate the same Text Out device, + // their MaxMode and QueryData should be the intersection of both. + // + Status = ConSplitterTextOutAddDevice (&mConOut, TextOut, GraphicsOutput, UgaDraw); + ConSplitterTextOutSetAttribute (&mConOut.TextOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + + if (FeaturePcdGet (PcdConOutUgaSupport)) { + // + // Get the UGA mode data of ConOut from the current mode + // + if (GraphicsOutput != NULL) { + Status = GraphicsOutput->QueryMode (GraphicsOutput, GraphicsOutput->Mode->Mode, &SizeOfInfo, &Info); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT ( SizeOfInfo <= sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + + mConOut.UgaHorizontalResolution = Info->HorizontalResolution; + mConOut.UgaVerticalResolution = Info->VerticalResolution; + mConOut.UgaColorDepth = 32; + mConOut.UgaRefreshRate = 60; + + FreePool (Info); + + } else if (UgaDraw != NULL) { + Status = UgaDraw->GetMode ( + UgaDraw, + &mConOut.UgaHorizontalResolution, + &mConOut.UgaVerticalResolution, + &mConOut.UgaColorDepth, + &mConOut.UgaRefreshRate + ); + } + } + + return Status; +} + + +/** + Start Standard Error Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Standard Error Consplitter is added to ControllerHandle. + @retval other Standard Error Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + + // + // Start ConSplitter on ControllerHandle, and create the virtual + // agrogated console device on first call Start for a StandardError handle. + // + Status = ConSplitterStart ( + This, + ControllerHandle, + mStdErr.VirtualHandle, + &gEfiStandardErrorDeviceGuid, + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &TextOut + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // When new console device is added, the new mode will be set later, + // so put current mode back to init state. + // + mStdErr.TextOutMode.Mode = 0xFF; + + // + // If both ConOut and StdErr incorporate the same Text Out device, + // their MaxMode and QueryData should be the intersection of both. + // + Status = ConSplitterTextOutAddDevice (&mStdErr, TextOut, NULL, NULL); + ConSplitterTextOutSetAttribute (&mStdErr.TextOut, EFI_TEXT_ATTR (EFI_MAGENTA, EFI_BLACK)); + + return Status; +} + + +/** + Stop ConSplitter on device handle by closing Console Device Guid on device handle + and the console virtual handle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device. + @param ConSplitterVirtualHandle Console virtual Handle. + @param DeviceGuid The specified Console Device, such as ConInDev, + ConOutDev. + @param InterfaceGuid The specified protocol to be opened. + @param Interface Protocol interface returned. + + @retval EFI_SUCCESS Stop ConSplitter on ControllerHandle successfully. + @retval other Failed to Stop ConSplitter on ControllerHandle. + +**/ +EFI_STATUS +ConSplitterStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ConSplitterVirtualHandle, + IN EFI_GUID *DeviceGuid, + IN EFI_GUID *InterfaceGuid, + IN VOID **Interface + ) +{ + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + ControllerHandle, + InterfaceGuid, + Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // close the protocol refered. + // + gBS->CloseProtocol ( + ControllerHandle, + DeviceGuid, + This->DriverBindingHandle, + ConSplitterVirtualHandle + ); + + gBS->CloseProtocol ( + ControllerHandle, + DeviceGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return EFI_SUCCESS; +} + + +/** + Stop Console In ConSplitter on ControllerHandle by closing Console In Devcice GUID. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx; + + if (NumberOfChildren == 0) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleTextInputExProtocolGuid, + (VOID **) &TextInEx, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // If Simple Text Input Ex protocol exists, + // remove device from Text Input Ex devices list. + // + Status = ConSplitterTextInExDeleteDevice (&mConIn, TextInEx); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Close Simple Text In protocol on controller handle and virtual handle. + // + Status = ConSplitterStop ( + This, + ControllerHandle, + mConIn.VirtualHandle, + &gEfiConsoleInDeviceGuid, + &gEfiSimpleTextInProtocolGuid, + (VOID **) &TextIn + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Remove device from Text Input devices list. + // + return ConSplitterTextInDeleteDevice (&mConIn, TextIn); +} + + +/** + Stop Simple Pointer protocol ConSplitter on ControllerHandle by closing + Simple Pointer protocol. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer; + + if (NumberOfChildren == 0) { + return EFI_SUCCESS; + } + + // + // Close Simple Pointer protocol on controller handle and virtual handle. + // + Status = ConSplitterStop ( + This, + ControllerHandle, + mConIn.VirtualHandle, + &gEfiSimplePointerProtocolGuid, + &gEfiSimplePointerProtocolGuid, + (VOID **) &SimplePointer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Remove this device from Simple Pointer device list. + // + return ConSplitterSimplePointerDeleteDevice (&mConIn, SimplePointer); +} + + +/** + Stop Absolute Pointer protocol ConSplitter on ControllerHandle by closing + Absolute Pointer protocol. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointer; + + if (NumberOfChildren == 0) { + return EFI_SUCCESS; + } + + // + // Close Absolute Pointer protocol on controller handle and virtual handle. + // + Status = ConSplitterStop ( + This, + ControllerHandle, + mConIn.VirtualHandle, + &gEfiAbsolutePointerProtocolGuid, + &gEfiAbsolutePointerProtocolGuid, + (VOID **) &AbsolutePointer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Remove this device from Absolute Pointer device list. + // + return ConSplitterAbsolutePointerDeleteDevice (&mConIn, AbsolutePointer); +} + + +/** + Stop Console Out ConSplitter on device handle by closing Console Out Devcice GUID. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + + if (NumberOfChildren == 0) { + return EFI_SUCCESS; + } + + // + // Close Absolute Pointer protocol on controller handle and virtual handle. + // + Status = ConSplitterStop ( + This, + ControllerHandle, + mConOut.VirtualHandle, + &gEfiConsoleOutDeviceGuid, + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &TextOut + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Remove this device from Text Out device list. + // + return ConSplitterTextOutDeleteDevice (&mConOut, TextOut); +} + + +/** + Stop Standard Error ConSplitter on ControllerHandle by closing Standard Error GUID. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + + if (NumberOfChildren == 0) { + return EFI_SUCCESS; + } + + // + // Close Standard Error Device on controller handle and virtual handle. + // + Status = ConSplitterStop ( + This, + ControllerHandle, + mStdErr.VirtualHandle, + &gEfiStandardErrorDeviceGuid, + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &TextOut + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Delete this console error out device's data structures. + // + return ConSplitterTextOutDeleteDevice (&mStdErr, TextOut); +} + + +/** + Take the passed in Buffer of size ElementSize and grow the buffer + by CONSOLE_SPLITTER_ALLOC_UNIT * ElementSize bytes. + Copy the current data in Buffer to the new version of Buffer and + free the old version of buffer. + + @param ElementSize Size of element in array. + @param Count Current number of elements in array. + @param Buffer Bigger version of passed in Buffer with all the + data. + + @retval EFI_SUCCESS Buffer size has grown. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterGrowBuffer ( + IN UINTN ElementSize, + IN OUT UINTN *Count, + IN OUT VOID **Buffer + ) +{ + VOID *Ptr; + + // + // grow the buffer to new buffer size, + // copy the old buffer's content to the new-size buffer, + // then free the old buffer. + // + Ptr = ReallocatePool ( + ElementSize * (*Count), + ElementSize * ((*Count) + CONSOLE_SPLITTER_ALLOC_UNIT), + *Buffer + ); + if (Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + *Count += CONSOLE_SPLITTER_ALLOC_UNIT; + *Buffer = Ptr; + return EFI_SUCCESS; +} + + +/** + Add Text Input Device in Consplitter Text Input list. + + @param Private Text In Splitter pointer. + @param TextIn Simple Text Input protocol pointer. + + @retval EFI_SUCCESS Text Input Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterTextInAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn + ) +{ + EFI_STATUS Status; + + // + // If the Text In List is full, enlarge it by calling ConSplitterGrowBuffer(). + // + if (Private->CurrentNumberOfConsoles >= Private->TextInListCount) { + Status = ConSplitterGrowBuffer ( + sizeof (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *), + &Private->TextInListCount, + (VOID **) &Private->TextInList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + // + // Add the new text-in device data structure into the Text In List. + // + Private->TextInList[Private->CurrentNumberOfConsoles] = TextIn; + Private->CurrentNumberOfConsoles++; + + // + // Extra CheckEvent added to reduce the double CheckEvent(). + // + gBS->CheckEvent (TextIn->WaitForKey); + + return EFI_SUCCESS; +} + + +/** + Remove Text Input Device from Consplitter Text Input list. + + @param Private Text In Splitter pointer. + @param TextIn Simple Text protocol pointer. + + @retval EFI_SUCCESS Simple Text Device removed successfully. + @retval EFI_NOT_FOUND No Simple Text Device found. + +**/ +EFI_STATUS +ConSplitterTextInDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn + ) +{ + UINTN Index; + // + // Remove the specified text-in device data structure from the Text In List, + // and rearrange the remaining data structures in the Text In List. + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) { + if (Private->TextInList[Index] == TextIn) { + for (; Index < Private->CurrentNumberOfConsoles - 1; Index++) { + Private->TextInList[Index] = Private->TextInList[Index + 1]; + } + + Private->CurrentNumberOfConsoles--; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Add Text Input Ex Device in Consplitter Text Input Ex list. + + @param Private Text In Splitter pointer. + @param TextInEx Simple Text Input Ex Input protocol pointer. + + @retval EFI_SUCCESS Text Input Ex Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterTextInExAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + TEXT_IN_EX_SPLITTER_NOTIFY *CurrentNotify; + UINTN TextInExListCount; + + // + // Enlarge the NotifyHandleList and the TextInExList + // + if (Private->CurrentNumberOfExConsoles >= Private->TextInExListCount) { + for (Link = Private->NotifyList.ForwardLink; Link != &Private->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = TEXT_IN_EX_SPLITTER_NOTIFY_FROM_THIS (Link); + TextInExListCount = Private->TextInExListCount; + + Status = ConSplitterGrowBuffer ( + sizeof (EFI_HANDLE), + &TextInExListCount, + (VOID **) &CurrentNotify->NotifyHandleList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + + TextInExListCount = Private->TextInExListCount; + Status = ConSplitterGrowBuffer ( + sizeof (EFI_KEY_DATA), + &TextInExListCount, + (VOID **) &Private->KeyQueue + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + Status = ConSplitterGrowBuffer ( + sizeof (EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *), + &Private->TextInExListCount, + (VOID **) &Private->TextInExList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Register the key notify in the new text-in device + // + for (Link = Private->NotifyList.ForwardLink; Link != &Private->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = TEXT_IN_EX_SPLITTER_NOTIFY_FROM_THIS (Link); + Status = TextInEx->RegisterKeyNotify ( + TextInEx, + &CurrentNotify->KeyData, + CurrentNotify->KeyNotificationFn, + &CurrentNotify->NotifyHandleList[Private->CurrentNumberOfExConsoles] + ); + if (EFI_ERROR (Status)) { + for (Link = Link->BackLink; Link != &Private->NotifyList; Link = Link->BackLink) { + CurrentNotify = TEXT_IN_EX_SPLITTER_NOTIFY_FROM_THIS (Link); + TextInEx->UnregisterKeyNotify ( + TextInEx, + CurrentNotify->NotifyHandleList[Private->CurrentNumberOfExConsoles] + ); + } + return Status; + } + } + + // + // Add the new text-in device data structure into the Text Input Ex List. + // + Private->TextInExList[Private->CurrentNumberOfExConsoles] = TextInEx; + Private->CurrentNumberOfExConsoles++; + + // + // Sync current toggle state to this new console input device. + // + TextInEx->SetState (TextInEx, &Private->PhysicalKeyToggleState); + + // + // Extra CheckEvent added to reduce the double CheckEvent(). + // + gBS->CheckEvent (TextInEx->WaitForKeyEx); + + return EFI_SUCCESS; +} + +/** + Remove Text Ex Device from Consplitter Text Input Ex list. + + @param Private Text In Splitter pointer. + @param TextInEx Simple Text Ex protocol pointer. + + @retval EFI_SUCCESS Simple Text Input Ex Device removed successfully. + @retval EFI_NOT_FOUND No Simple Text Input Ex Device found. + +**/ +EFI_STATUS +ConSplitterTextInExDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx + ) +{ + UINTN Index; + // + // Remove the specified text-in device data structure from the Text Input Ex List, + // and rearrange the remaining data structures in the Text In List. + // + for (Index = 0; Index < Private->CurrentNumberOfExConsoles; Index++) { + if (Private->TextInExList[Index] == TextInEx) { + for (; Index < Private->CurrentNumberOfExConsoles - 1; Index++) { + Private->TextInExList[Index] = Private->TextInExList[Index + 1]; + } + + Private->CurrentNumberOfExConsoles--; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Add Simple Pointer Device in Consplitter Simple Pointer list. + + @param Private Text In Splitter pointer. + @param SimplePointer Simple Pointer protocol pointer. + + @retval EFI_SUCCESS Simple Pointer Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterSimplePointerAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer + ) +{ + EFI_STATUS Status; + + // + // If the Simple Pointer List is full, enlarge it by calling ConSplitterGrowBuffer(). + // + if (Private->CurrentNumberOfPointers >= Private->PointerListCount) { + Status = ConSplitterGrowBuffer ( + sizeof (EFI_SIMPLE_POINTER_PROTOCOL *), + &Private->PointerListCount, + (VOID **) &Private->PointerList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + // + // Add the new text-in device data structure into the Simple Pointer List. + // + Private->PointerList[Private->CurrentNumberOfPointers] = SimplePointer; + Private->CurrentNumberOfPointers++; + + return EFI_SUCCESS; +} + + +/** + Remove Simple Pointer Device from Consplitter Simple Pointer list. + + @param Private Text In Splitter pointer. + @param SimplePointer Simple Pointer protocol pointer. + + @retval EFI_SUCCESS Simple Pointer Device removed successfully. + @retval EFI_NOT_FOUND No Simple Pointer Device found. + +**/ +EFI_STATUS +ConSplitterSimplePointerDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer + ) +{ + UINTN Index; + // + // Remove the specified text-in device data structure from the Simple Pointer List, + // and rearrange the remaining data structures in the Text In List. + // + for (Index = 0; Index < Private->CurrentNumberOfPointers; Index++) { + if (Private->PointerList[Index] == SimplePointer) { + for (; Index < Private->CurrentNumberOfPointers - 1; Index++) { + Private->PointerList[Index] = Private->PointerList[Index + 1]; + } + + Private->CurrentNumberOfPointers--; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Add Absolute Pointer Device in Consplitter Absolute Pointer list. + + @param Private Text In Splitter pointer. + @param AbsolutePointer Absolute Pointer protocol pointer. + + @retval EFI_SUCCESS Absolute Pointer Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterAbsolutePointerAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointer + ) +{ + EFI_STATUS Status; + + // + // If the Absolute Pointer List is full, enlarge it by calling ConSplitterGrowBuffer(). + // + if (Private->CurrentNumberOfAbsolutePointers >= Private->AbsolutePointerListCount) { + Status = ConSplitterGrowBuffer ( + sizeof (EFI_ABSOLUTE_POINTER_PROTOCOL *), + &Private->AbsolutePointerListCount, + (VOID **) &Private->AbsolutePointerList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + // + // Add the new text-in device data structure into the Absolute Pointer List. + // + Private->AbsolutePointerList[Private->CurrentNumberOfAbsolutePointers] = AbsolutePointer; + Private->CurrentNumberOfAbsolutePointers++; + + return EFI_SUCCESS; +} + + +/** + Remove Absolute Pointer Device from Consplitter Absolute Pointer list. + + @param Private Text In Splitter pointer. + @param AbsolutePointer Absolute Pointer protocol pointer. + + @retval EFI_SUCCESS Absolute Pointer Device removed successfully. + @retval EFI_NOT_FOUND No Absolute Pointer Device found. + +**/ +EFI_STATUS +ConSplitterAbsolutePointerDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointer + ) +{ + UINTN Index; + // + // Remove the specified text-in device data structure from the Absolute Pointer List, + // and rearrange the remaining data structures from the Absolute Pointer List. + // + for (Index = 0; Index < Private->CurrentNumberOfAbsolutePointers; Index++) { + if (Private->AbsolutePointerList[Index] == AbsolutePointer) { + for (; Index < Private->CurrentNumberOfAbsolutePointers - 1; Index++) { + Private->AbsolutePointerList[Index] = Private->AbsolutePointerList[Index + 1]; + } + + Private->CurrentNumberOfAbsolutePointers--; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Reallocate Text Out mode map. + + Allocate new buffer and copy original buffer into the new buffer. + + @param Private Consplitter Text Out pointer. + + @retval EFI_SUCCESS Buffer size has grown + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterGrowMapTable ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private + ) +{ + UINTN Size; + UINTN NewSize; + UINTN TotalSize; + INT32 *TextOutModeMap; + INT32 *OldTextOutModeMap; + INT32 *SrcAddress; + INT32 Index; + UINTN OldStepSize; + UINTN NewStepSize; + + NewSize = Private->TextOutListCount * sizeof (INT32); + OldTextOutModeMap = Private->TextOutModeMap; + TotalSize = NewSize * (Private->TextOutQueryDataCount); + + // + // Allocate new buffer for Text Out List. + // + TextOutModeMap = AllocatePool (TotalSize); + if (TextOutModeMap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SetMem (TextOutModeMap, TotalSize, 0xFF); + Private->TextOutModeMap = TextOutModeMap; + + // + // If TextOutList has been enlarged, need to realloc the mode map table + // The mode map table is regarded as a two dimension array. + // + // Old New + // 0 ---------> TextOutListCount ----> TextOutListCount + // | ------------------------------------------- + // | | | | + // | | | | + // | | | | + // | | | | + // | | | | + // \/ | | | + // ------------------------------------------- + // QueryDataCount + // + if (OldTextOutModeMap != NULL) { + + Size = Private->CurrentNumberOfConsoles * sizeof (INT32); + Index = 0; + SrcAddress = OldTextOutModeMap; + NewStepSize = NewSize / sizeof(INT32); + // If Private->CurrentNumberOfConsoles is not zero and OldTextOutModeMap + // is not NULL, it indicates that the original TextOutModeMap is not enough + // for the new console devices and has been enlarged by CONSOLE_SPLITTER_ALLOC_UNIT columns. + // + OldStepSize = NewStepSize - CONSOLE_SPLITTER_ALLOC_UNIT; + + // + // Copy the old data to the new one + // + while (Index < Private->TextOutMode.MaxMode) { + CopyMem (TextOutModeMap, SrcAddress, Size); + // + // Go to next row of new TextOutModeMap. + // + TextOutModeMap += NewStepSize; + // + // Go to next row of old TextOutModeMap. + // + SrcAddress += OldStepSize; + Index++; + } + // + // Free the old buffer + // + FreePool (OldTextOutModeMap); + } + + return EFI_SUCCESS; +} + + +/** + Add new device's output mode to console splitter's mode list. + + @param Private Text Out Splitter pointer + @param TextOut Simple Text Output protocol pointer. + + @retval EFI_SUCCESS Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterAddOutputMode ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut + ) +{ + EFI_STATUS Status; + INT32 MaxMode; + INT32 Mode; + UINTN Index; + + MaxMode = TextOut->Mode->MaxMode; + Private->TextOutMode.MaxMode = MaxMode; + + // + // Grow the buffer if query data buffer is not large enough to + // hold all the mode supported by the first console. + // + while (MaxMode > (INT32) Private->TextOutQueryDataCount) { + Status = ConSplitterGrowBuffer ( + sizeof (TEXT_OUT_SPLITTER_QUERY_DATA), + &Private->TextOutQueryDataCount, + (VOID **) &Private->TextOutQueryData + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + // + // Allocate buffer for the output mode map + // + Status = ConSplitterGrowMapTable (Private); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // As the first textout device, directly add the mode in to QueryData + // and at the same time record the mapping between QueryData and TextOut. + // + Mode = 0; + Index = 0; + while (Mode < MaxMode) { + Status = TextOut->QueryMode ( + TextOut, + Mode, + &Private->TextOutQueryData[Mode].Columns, + &Private->TextOutQueryData[Mode].Rows + ); + // + // If mode 1 (80x50) is not supported, make sure mode 1 in TextOutQueryData + // is clear to 0x0. + // + if ((EFI_ERROR(Status)) && (Mode == 1)) { + Private->TextOutQueryData[Mode].Columns = 0; + Private->TextOutQueryData[Mode].Rows = 0; + } + Private->TextOutModeMap[Index] = Mode; + Mode++; + Index += Private->TextOutListCount; + } + + return EFI_SUCCESS; +} + +/** + Reconstruct TextOutModeMap to get intersection of modes. + + This routine reconstruct TextOutModeMap to get the intersection + of modes for all console out devices. Because EFI/UEFI spec require + mode 0 is 80x25, mode 1 is 80x50, this routine will not check the + intersection for mode 0 and mode 1. + + @param TextOutModeMap Current text out mode map, begin with the mode 80x25 + @param NewlyAddedMap New text out mode map, begin with the mode 80x25 + @param MapStepSize Mode step size for one console device + @param NewMapStepSize New Mode step size for one console device + @param MaxMode IN: Current max text mode, OUT: Updated max text mode. + @param CurrentMode IN: Current text mode, OUT: Updated current text mode. + +**/ +VOID +ConSplitterGetIntersection ( + IN INT32 *TextOutModeMap, + IN INT32 *NewlyAddedMap, + IN UINTN MapStepSize, + IN UINTN NewMapStepSize, + IN OUT INT32 *MaxMode, + IN OUT INT32 *CurrentMode + ) +{ + INT32 Index; + INT32 *CurrentMapEntry; + INT32 *NextMapEntry; + INT32 *NewMapEntry; + INT32 CurrentMaxMode; + INT32 Mode; + + // + // According to EFI/UEFI spec, mode 0 and mode 1 have been reserved + // for 80x25 and 80x50 in Simple Text Out protocol, so don't make intersection + // for mode 0 and mode 1, mode number starts from 2. + // + Index = 2; + CurrentMapEntry = &TextOutModeMap[MapStepSize * 2]; + NextMapEntry = CurrentMapEntry; + NewMapEntry = &NewlyAddedMap[NewMapStepSize * 2]; + + CurrentMaxMode = *MaxMode; + Mode = *CurrentMode; + + while (Index < CurrentMaxMode) { + if (*NewMapEntry == -1) { + // + // This mode is not supported any more. Remove it. Special care + // must be taken as this remove will also affect current mode; + // + if (Index == *CurrentMode) { + Mode = -1; + } else if (Index < *CurrentMode) { + Mode--; + } + (*MaxMode)--; + } else { + if (CurrentMapEntry != NextMapEntry) { + CopyMem (NextMapEntry, CurrentMapEntry, MapStepSize * sizeof (INT32)); + } + + NextMapEntry += MapStepSize; + } + + CurrentMapEntry += MapStepSize; + NewMapEntry += NewMapStepSize; + Index++; + } + + *CurrentMode = Mode; + + return ; +} + +/** + Sync the device's output mode to console splitter's mode list. + + @param Private Text Out Splitter pointer. + @param TextOut Simple Text Output protocol pointer. + +**/ +VOID +ConSplitterSyncOutputMode ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut + ) +{ + INT32 CurrentMaxMode; + INT32 Mode; + INT32 Index; + INT32 *TextOutModeMap; + INT32 *MapTable; + INT32 QueryMode; + TEXT_OUT_SPLITTER_QUERY_DATA *TextOutQueryData; + UINTN Rows; + UINTN Columns; + UINTN StepSize; + EFI_STATUS Status; + + // + // Must make sure that current mode won't change even if mode number changes + // + CurrentMaxMode = Private->TextOutMode.MaxMode; + TextOutModeMap = Private->TextOutModeMap; + StepSize = Private->TextOutListCount; + TextOutQueryData = Private->TextOutQueryData; + + // + // Query all the mode that the newly added TextOut supports + // + Mode = 0; + MapTable = TextOutModeMap + Private->CurrentNumberOfConsoles; + while (Mode < TextOut->Mode->MaxMode) { + Status = TextOut->QueryMode (TextOut, Mode, &Columns, &Rows); + + if (EFI_ERROR(Status)) { + if (Mode == 1) { + // + // If mode 1 (80x50) is not supported, make sure mode 1 in TextOutQueryData + // is clear to 0x0. + // + MapTable[StepSize] = Mode; + TextOutQueryData[Mode].Columns = 0; + TextOutQueryData[Mode].Rows = 0; + } + Mode++; + continue; + } + // + // Search the intersection map and QueryData database to see if they intersects + // + Index = 0; + while (Index < CurrentMaxMode) { + QueryMode = *(TextOutModeMap + Index * StepSize); + if ((TextOutQueryData[QueryMode].Rows == Rows) && (TextOutQueryData[QueryMode].Columns == Columns)) { + MapTable[Index * StepSize] = Mode; + break; + } + Index++; + } + Mode++; + } + // + // Now search the TextOutModeMap table to find the intersection of supported + // mode between ConSplitter and the newly added device. + // + ConSplitterGetIntersection ( + TextOutModeMap, + MapTable, + StepSize, + StepSize, + &Private->TextOutMode.MaxMode, + &Private->TextOutMode.Mode + ); + + return ; +} + + +/** + Sync output device between ConOut and StdErr output. + + @retval EFI_SUCCESS Sync implemented successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterGetIntersectionBetweenConOutAndStrErr ( + VOID + ) +{ + UINTN ConOutNumOfConsoles; + UINTN StdErrNumOfConsoles; + TEXT_OUT_AND_GOP_DATA *ConOutTextOutList; + TEXT_OUT_AND_GOP_DATA *StdErrTextOutList; + UINTN Indexi; + UINTN Indexj; + UINTN ConOutRows; + UINTN ConOutColumns; + UINTN StdErrRows; + UINTN StdErrColumns; + INT32 ConOutMaxMode; + INT32 StdErrMaxMode; + INT32 ConOutMode; + INT32 StdErrMode; + INT32 Mode; + INT32 Index; + INT32 *ConOutModeMap; + INT32 *StdErrModeMap; + INT32 *ConOutMapTable; + INT32 *StdErrMapTable; + TEXT_OUT_SPLITTER_QUERY_DATA *ConOutQueryData; + TEXT_OUT_SPLITTER_QUERY_DATA *StdErrQueryData; + UINTN ConOutStepSize; + UINTN StdErrStepSize; + BOOLEAN FoundTheSameTextOut; + UINTN ConOutMapTableSize; + UINTN StdErrMapTableSize; + + ConOutNumOfConsoles = mConOut.CurrentNumberOfConsoles; + StdErrNumOfConsoles = mStdErr.CurrentNumberOfConsoles; + ConOutTextOutList = mConOut.TextOutList; + StdErrTextOutList = mStdErr.TextOutList; + + Indexi = 0; + FoundTheSameTextOut = FALSE; + while ((Indexi < ConOutNumOfConsoles) && (!FoundTheSameTextOut)) { + Indexj = 0; + while (Indexj < StdErrNumOfConsoles) { + if (ConOutTextOutList->TextOut == StdErrTextOutList->TextOut) { + FoundTheSameTextOut = TRUE; + break; + } + + Indexj++; + StdErrTextOutList++; + } + + Indexi++; + ConOutTextOutList++; + } + + if (!FoundTheSameTextOut) { + return EFI_SUCCESS; + } + // + // Must make sure that current mode won't change even if mode number changes + // + ConOutMaxMode = mConOut.TextOutMode.MaxMode; + ConOutModeMap = mConOut.TextOutModeMap; + ConOutStepSize = mConOut.TextOutListCount; + ConOutQueryData = mConOut.TextOutQueryData; + + StdErrMaxMode = mStdErr.TextOutMode.MaxMode; + StdErrModeMap = mStdErr.TextOutModeMap; + StdErrStepSize = mStdErr.TextOutListCount; + StdErrQueryData = mStdErr.TextOutQueryData; + + // + // Allocate the map table and set the map table's index to -1. + // + ConOutMapTableSize = ConOutMaxMode * sizeof (INT32); + ConOutMapTable = AllocateZeroPool (ConOutMapTableSize); + if (ConOutMapTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SetMem (ConOutMapTable, ConOutMapTableSize, 0xFF); + + StdErrMapTableSize = StdErrMaxMode * sizeof (INT32); + StdErrMapTable = AllocateZeroPool (StdErrMapTableSize); + if (StdErrMapTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SetMem (StdErrMapTable, StdErrMapTableSize, 0xFF); + + // + // Find the intersection of the two set of modes. If they actually intersect, the + // corresponding entry in the map table is set to 1. + // + Mode = 0; + while (Mode < ConOutMaxMode) { + // + // Search the intersection map and QueryData database to see if they intersect + // + Index = 0; + ConOutMode = *(ConOutModeMap + Mode * ConOutStepSize); + ConOutRows = ConOutQueryData[ConOutMode].Rows; + ConOutColumns = ConOutQueryData[ConOutMode].Columns; + while (Index < StdErrMaxMode) { + StdErrMode = *(StdErrModeMap + Index * StdErrStepSize); + StdErrRows = StdErrQueryData[StdErrMode].Rows; + StdErrColumns = StdErrQueryData[StdErrMode].Columns; + if ((StdErrRows == ConOutRows) && (StdErrColumns == ConOutColumns)) { + ConOutMapTable[Mode] = 1; + StdErrMapTable[Index] = 1; + break; + } + + Index++; + } + + Mode++; + } + // + // Now search the TextOutModeMap table to find the intersection of supported + // mode between ConSplitter and the newly added device. + // + ConSplitterGetIntersection ( + ConOutModeMap, + ConOutMapTable, + mConOut.TextOutListCount, + 1, + &(mConOut.TextOutMode.MaxMode), + &(mConOut.TextOutMode.Mode) + ); + + if (mConOut.TextOutMode.Mode < 0) { + mConOut.TextOut.SetMode (&(mConOut.TextOut), 0); + } + + ConSplitterGetIntersection ( + StdErrModeMap, + StdErrMapTable, + mStdErr.TextOutListCount, + 1, + &(mStdErr.TextOutMode.MaxMode), + &(mStdErr.TextOutMode.Mode) + ); + + if (mStdErr.TextOutMode.Mode < 0) { + mStdErr.TextOut.SetMode (&(mStdErr.TextOut), 0); + } + + FreePool (ConOutMapTable); + FreePool (StdErrMapTable); + + return EFI_SUCCESS; +} + + +/** + Add Grahpics Output modes into Consplitter Text Out list. + + @param Private Text Out Splitter pointer. + @param GraphicsOutput Graphics Output protocol pointer. + @param UgaDraw UGA Draw protocol pointer. + + @retval EFI_SUCCESS Output mode added successfully. + @retval other Failed to add output mode. + +**/ +EFI_STATUS +ConSplitterAddGraphicsOutputMode ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput, + IN EFI_UGA_DRAW_PROTOCOL *UgaDraw + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN CurrentIndex; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Mode; + UINTN SizeOfInfo; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *CurrentGraphicsOutputMode; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeBuffer; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *MatchedMode; + UINTN NumberIndex; + BOOLEAN Match; + BOOLEAN AlreadyExist; + UINT32 UgaHorizontalResolution; + UINT32 UgaVerticalResolution; + UINT32 UgaColorDepth; + UINT32 UgaRefreshRate; + + ASSERT (GraphicsOutput != NULL || UgaDraw != NULL); + + CurrentGraphicsOutputMode = Private->GraphicsOutput.Mode; + + Index = 0; + CurrentIndex = 0; + Status = EFI_SUCCESS; + + if (Private->CurrentNumberOfUgaDraw != 0) { + // + // If any UGA device has already been added, then there is no need to + // calculate intersection of display mode of different GOP/UGA device, + // since only one display mode will be exported (i.e. user-defined mode) + // + goto Done; + } + + if (GraphicsOutput != NULL) { + if (Private->CurrentNumberOfGraphicsOutput == 0) { + // + // This is the first Graphics Output device added + // + CurrentGraphicsOutputMode->MaxMode = GraphicsOutput->Mode->MaxMode; + CurrentGraphicsOutputMode->Mode = GraphicsOutput->Mode->Mode; + CopyMem (CurrentGraphicsOutputMode->Info, GraphicsOutput->Mode->Info, GraphicsOutput->Mode->SizeOfInfo); + CurrentGraphicsOutputMode->SizeOfInfo = GraphicsOutput->Mode->SizeOfInfo; + CurrentGraphicsOutputMode->FrameBufferBase = GraphicsOutput->Mode->FrameBufferBase; + CurrentGraphicsOutputMode->FrameBufferSize = GraphicsOutput->Mode->FrameBufferSize; + + // + // Allocate resource for the private mode buffer + // + ModeBuffer = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION) * GraphicsOutput->Mode->MaxMode); + if (ModeBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + FreePool (Private->GraphicsOutputModeBuffer); + Private->GraphicsOutputModeBuffer = ModeBuffer; + + // + // Store all supported display modes to the private mode buffer + // + Mode = ModeBuffer; + for (Index = 0; Index < GraphicsOutput->Mode->MaxMode; Index++) { + // + // The Info buffer would be allocated by callee + // + Status = GraphicsOutput->QueryMode (GraphicsOutput, (UINT32) Index, &SizeOfInfo, &Info); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT ( SizeOfInfo <= sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + CopyMem (Mode, Info, SizeOfInfo); + Mode++; + FreePool (Info); + } + } else { + // + // Check intersection of display mode + // + ModeBuffer = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION) * CurrentGraphicsOutputMode->MaxMode); + if (ModeBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MatchedMode = ModeBuffer; + Mode = &Private->GraphicsOutputModeBuffer[0]; + for (Index = 0; Index < CurrentGraphicsOutputMode->MaxMode; Index++) { + Match = FALSE; + + for (NumberIndex = 0; NumberIndex < GraphicsOutput->Mode->MaxMode; NumberIndex++) { + // + // The Info buffer would be allocated by callee + // + Status = GraphicsOutput->QueryMode (GraphicsOutput, (UINT32) NumberIndex, &SizeOfInfo, &Info); + if (EFI_ERROR (Status)) { + return Status; + } + if ((Info->HorizontalResolution == Mode->HorizontalResolution) && + (Info->VerticalResolution == Mode->VerticalResolution)) { + // + // If GOP device supports one mode in current mode buffer, + // it will be added into matched mode buffer + // + Match = TRUE; + FreePool (Info); + break; + } + FreePool (Info); + } + + if (Match) { + AlreadyExist = FALSE; + + // + // Check if GOP mode has been in the mode buffer, ModeBuffer = MatchedMode at begin. + // + for (Info = ModeBuffer; Info < MatchedMode; Info++) { + if ((Info->HorizontalResolution == Mode->HorizontalResolution) && + (Info->VerticalResolution == Mode->VerticalResolution)) { + AlreadyExist = TRUE; + break; + } + } + + if (!AlreadyExist) { + CopyMem (MatchedMode, Mode, sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + + // + // Physical frame buffer is no longer available, change PixelFormat to PixelBltOnly + // + MatchedMode->Version = 0; + MatchedMode->PixelFormat = PixelBltOnly; + ZeroMem (&MatchedMode->PixelInformation, sizeof (EFI_PIXEL_BITMASK)); + + MatchedMode++; + } + } + + Mode++; + } + + // + // Drop the old mode buffer, assign it to a new one + // + FreePool (Private->GraphicsOutputModeBuffer); + Private->GraphicsOutputModeBuffer = ModeBuffer; + + // + // Physical frame buffer is no longer available when there are more than one physical GOP devices + // + CurrentGraphicsOutputMode->MaxMode = (UINT32) (((UINTN) MatchedMode - (UINTN) ModeBuffer) / sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + CurrentGraphicsOutputMode->Info->PixelFormat = PixelBltOnly; + ZeroMem (&CurrentGraphicsOutputMode->Info->PixelInformation, sizeof (EFI_PIXEL_BITMASK)); + CurrentGraphicsOutputMode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + CurrentGraphicsOutputMode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL; + CurrentGraphicsOutputMode->FrameBufferSize = 0; + } + + // + // Graphics console driver can ensure the same mode for all GOP devices + // + for (Index = 0; Index < CurrentGraphicsOutputMode->MaxMode; Index++) { + Mode = &Private->GraphicsOutputModeBuffer[Index]; + if ((Mode->HorizontalResolution == GraphicsOutput->Mode->Info->HorizontalResolution) && + (Mode->VerticalResolution == GraphicsOutput->Mode->Info->VerticalResolution)) { + CurrentIndex = Index; + break; + } + } + if (Index >= CurrentGraphicsOutputMode->MaxMode) { + // + // if user defined mode is not found, set to default mode 800x600 + // + for (Index = 0; Index < CurrentGraphicsOutputMode->MaxMode; Index++) { + Mode = &Private->GraphicsOutputModeBuffer[Index]; + if ((Mode->HorizontalResolution == 800) && (Mode->VerticalResolution == 600)) { + CurrentIndex = Index; + break; + } + } + } + } else if (UgaDraw != NULL) { + // + // Graphics console driver can ensure the same mode for all GOP devices + // so we can get the current mode from this video device + // + UgaDraw->GetMode ( + UgaDraw, + &UgaHorizontalResolution, + &UgaVerticalResolution, + &UgaColorDepth, + &UgaRefreshRate + ); + + CurrentGraphicsOutputMode->MaxMode = 1; + Info = CurrentGraphicsOutputMode->Info; + Info->Version = 0; + Info->HorizontalResolution = UgaHorizontalResolution; + Info->VerticalResolution = UgaVerticalResolution; + Info->PixelFormat = PixelBltOnly; + Info->PixelsPerScanLine = UgaHorizontalResolution; + CurrentGraphicsOutputMode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + CurrentGraphicsOutputMode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL; + CurrentGraphicsOutputMode->FrameBufferSize = 0; + + // + // Update the private mode buffer + // + CopyMem (&Private->GraphicsOutputModeBuffer[0], Info, sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + + // + // Only mode 0 is available to be set + // + CurrentIndex = 0; + } + +Done: + + if (GraphicsOutput != NULL) { + Private->CurrentNumberOfGraphicsOutput++; + } + if (UgaDraw != NULL) { + Private->CurrentNumberOfUgaDraw++; + } + + // + // Force GraphicsOutput mode to be set, + // + + Mode = &Private->GraphicsOutputModeBuffer[CurrentIndex]; + if ((GraphicsOutput != NULL) && + (Mode->HorizontalResolution == CurrentGraphicsOutputMode->Info->HorizontalResolution) && + (Mode->VerticalResolution == CurrentGraphicsOutputMode->Info->VerticalResolution)) { + CurrentGraphicsOutputMode->Mode = (UINT32) CurrentIndex; + if ((Mode->HorizontalResolution != GraphicsOutput->Mode->Info->HorizontalResolution) || + (Mode->VerticalResolution != GraphicsOutput->Mode->Info->VerticalResolution)) { + // + // If all existing video device has been set to common mode, only set new GOP device to + // the common mode + // + for (NumberIndex = 0; NumberIndex < GraphicsOutput->Mode->MaxMode; NumberIndex ++) { + Status = GraphicsOutput->QueryMode (GraphicsOutput, (UINT32) NumberIndex, &SizeOfInfo, &Info); + if (EFI_ERROR (Status)) { + return Status; + } + if ((Info->HorizontalResolution == Mode->HorizontalResolution) && (Info->VerticalResolution == Mode->VerticalResolution)) { + FreePool (Info); + break; + } + FreePool (Info); + } + Status = GraphicsOutput->SetMode (GraphicsOutput, (UINT32) NumberIndex); + } + } else { + // + // Current mode number may need update now, so set it to an invalid mode number + // + CurrentGraphicsOutputMode->Mode = 0xffff; + // + // Graphics console can ensure all GOP devices have the same mode which can be taken as current mode. + // + Status = Private->GraphicsOutput.SetMode (&Private->GraphicsOutput, (UINT32) CurrentIndex); + if (EFI_ERROR(Status)) { + // + // If user defined mode is not valid for display device, set to the default mode 800x600. + // + (Private->GraphicsOutputModeBuffer[0]).HorizontalResolution = 800; + (Private->GraphicsOutputModeBuffer[0]).VerticalResolution = 600; + Status = Private->GraphicsOutput.SetMode (&Private->GraphicsOutput, 0); + } + } + + return Status; +} + +/** + Set the current console out mode. + + This routine will get the current console mode information (column, row) + from ConsoleOutMode variable and set it; if the variable does not exist, + set to user defined console mode. + + @param Private Consplitter Text Out pointer. + +**/ +VOID +ConsplitterSetConsoleOutMode ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private + ) +{ + UINTN Col; + UINTN Row; + UINTN Mode; + UINTN PreferMode; + UINTN BaseMode; + UINTN MaxMode; + EFI_STATUS Status; + CONSOLE_OUT_MODE ModeInfo; + CONSOLE_OUT_MODE MaxModeInfo; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + + PreferMode = 0xFF; + BaseMode = 0xFF; + TextOut = &Private->TextOut; + MaxMode = (UINTN) (TextOut->Mode->MaxMode); + + MaxModeInfo.Column = 0; + MaxModeInfo.Row = 0; + ModeInfo.Column = PcdGet32 (PcdConOutColumn); + ModeInfo.Row = PcdGet32 (PcdConOutRow); + + // + // To find the prefer mode and basic mode from Text Out mode list + // + for (Mode = 0; Mode < MaxMode; Mode++) { + Status = TextOut->QueryMode (TextOut, Mode, &Col, &Row); + if (!EFI_ERROR(Status)) { + if ((ModeInfo.Column != 0) && (ModeInfo.Row != 0)) { + // + // Use user defined column and row + // + if (Col == ModeInfo.Column && Row == ModeInfo.Row) { + PreferMode = Mode; + } + } else { + // + // If user sets PcdConOutColumn or PcdConOutRow to 0, + // find and set the highest text mode. + // + if ((Col >= MaxModeInfo.Column) && (Row >= MaxModeInfo.Row)) { + MaxModeInfo.Column = Col; + MaxModeInfo.Row = Row; + PreferMode = Mode; + } + } + if (Col == 80 && Row == 25) { + BaseMode = Mode; + } + } + } + + // + // Set prefer mode to Text Out devices. + // + Status = TextOut->SetMode (TextOut, PreferMode); + if (EFI_ERROR(Status)) { + // + // if current mode setting is failed, default 80x25 mode will be set. + // + Status = TextOut->SetMode (TextOut, BaseMode); + ASSERT(!EFI_ERROR(Status)); + + Status = PcdSet32S (PcdConOutColumn, 80); + ASSERT(!EFI_ERROR(Status)); + Status = PcdSet32S (PcdConOutRow, 25); + ASSERT(!EFI_ERROR(Status)); + } + + return ; +} + + +/** + Add Text Output Device in Consplitter Text Output list. + + @param Private Text Out Splitter pointer. + @param TextOut Simple Text Output protocol pointer. + @param GraphicsOutput Graphics Output protocol pointer. + @param UgaDraw UGA Draw protocol pointer. + + @retval EFI_SUCCESS Text Output Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterTextOutAddDevice ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut, + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput, + IN EFI_UGA_DRAW_PROTOCOL *UgaDraw + ) +{ + EFI_STATUS Status; + UINTN CurrentNumOfConsoles; + INT32 MaxMode; + UINT32 UgaHorizontalResolution; + UINT32 UgaVerticalResolution; + UINT32 UgaColorDepth; + UINT32 UgaRefreshRate; + TEXT_OUT_AND_GOP_DATA *TextAndGop; + UINTN SizeOfInfo; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_STATUS DeviceStatus; + + Status = EFI_SUCCESS; + CurrentNumOfConsoles = Private->CurrentNumberOfConsoles; + Private->AddingConOutDevice = TRUE; + + // + // If the Text Out List is full, enlarge it by calling ConSplitterGrowBuffer(). + // + while (CurrentNumOfConsoles >= Private->TextOutListCount) { + Status = ConSplitterGrowBuffer ( + sizeof (TEXT_OUT_AND_GOP_DATA), + &Private->TextOutListCount, + (VOID **) &Private->TextOutList + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // Also need to reallocate the TextOutModeMap table + // + Status = ConSplitterGrowMapTable (Private); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + + TextAndGop = &Private->TextOutList[CurrentNumOfConsoles]; + + TextAndGop->TextOut = TextOut; + TextAndGop->GraphicsOutput = GraphicsOutput; + TextAndGop->UgaDraw = UgaDraw; + + if (CurrentNumOfConsoles == 0) { + // + // Add the first device's output mode to console splitter's mode list + // + Status = ConSplitterAddOutputMode (Private, TextOut); + } else { + ConSplitterSyncOutputMode (Private, TextOut); + } + + Private->CurrentNumberOfConsoles++; + + // + // Scan both TextOutList, for the intersection TextOut device + // maybe both ConOut and StdErr incorporate the same Text Out + // device in them, thus the output of both should be synced. + // + ConSplitterGetIntersectionBetweenConOutAndStrErr (); + + MaxMode = Private->TextOutMode.MaxMode; + ASSERT (MaxMode >= 1); + + DeviceStatus = EFI_DEVICE_ERROR; + Status = EFI_DEVICE_ERROR; + + // + // This device display mode will be added into Graphics Ouput modes. + // + if ((GraphicsOutput != NULL) || (UgaDraw != NULL)) { + DeviceStatus = ConSplitterAddGraphicsOutputMode (Private, GraphicsOutput, UgaDraw); + } + + if (FeaturePcdGet (PcdConOutUgaSupport)) { + // + // If UGA is produced by Consplitter + // + if (GraphicsOutput != NULL) { + Status = GraphicsOutput->QueryMode (GraphicsOutput, GraphicsOutput->Mode->Mode, &SizeOfInfo, &Info); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT ( SizeOfInfo <= sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + + UgaHorizontalResolution = Info->HorizontalResolution; + UgaVerticalResolution = Info->VerticalResolution; + + FreePool (Info); + + } else if (UgaDraw != NULL) { + Status = UgaDraw->GetMode ( + UgaDraw, + &UgaHorizontalResolution, + &UgaVerticalResolution, + &UgaColorDepth, + &UgaRefreshRate + ); + if (!EFI_ERROR (Status) && EFI_ERROR (DeviceStatus)) { + // + // if GetMode is successfully and UGA device hasn't been set, set it + // + Status = ConSplitterUgaDrawSetMode ( + &Private->UgaDraw, + UgaHorizontalResolution, + UgaVerticalResolution, + UgaColorDepth, + UgaRefreshRate + ); + } + // + // If GetMode/SetMode is failed, set to 800x600 mode + // + if(EFI_ERROR (Status)) { + Status = ConSplitterUgaDrawSetMode ( + &Private->UgaDraw, + 800, + 600, + 32, + 60 + ); + } + } + } + + if (((!EFI_ERROR (DeviceStatus)) || (!EFI_ERROR (Status))) && + ((Private->CurrentNumberOfGraphicsOutput + Private->CurrentNumberOfUgaDraw) == 1)) { + if (!FeaturePcdGet (PcdConOutGopSupport)) { + // + // If Graphics Outpurt protocol not supported, UGA Draw protocol is installed + // on the virtual handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mConOut.VirtualHandle, + &gEfiUgaDrawProtocolGuid, + &mConOut.UgaDraw, + NULL + ); + } else if (!FeaturePcdGet (PcdConOutUgaSupport)) { + // + // If UGA Draw protocol not supported, Graphics Output Protocol is installed + // on virtual handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mConOut.VirtualHandle, + &gEfiGraphicsOutputProtocolGuid, + &mConOut.GraphicsOutput, + NULL + ); + } else { + // + // Boot Graphics Output protocol and UGA Draw protocol are supported, + // both they will be installed on virtual handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mConOut.VirtualHandle, + &gEfiGraphicsOutputProtocolGuid, + &mConOut.GraphicsOutput, + &gEfiUgaDrawProtocolGuid, + &mConOut.UgaDraw, + NULL + ); + } + } + + // + // After adding new console device, all existing console devices should be + // synced to the current shared mode. + // + ConsplitterSetConsoleOutMode (Private); + + Private->AddingConOutDevice = FALSE; + + return Status; +} + + +/** + Remove Text Out Device in Consplitter Text Out list. + + @param Private Text Out Splitter pointer. + @param TextOut Simple Text Output Pointer protocol pointer. + + @retval EFI_SUCCESS Text Out Device removed successfully. + @retval EFI_NOT_FOUND No Text Out Device found. + +**/ +EFI_STATUS +ConSplitterTextOutDeleteDevice ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut + ) +{ + INT32 Index; + UINTN CurrentNumOfConsoles; + TEXT_OUT_AND_GOP_DATA *TextOutList; + EFI_STATUS Status; + + // + // Remove the specified text-out device data structure from the Text out List, + // and rearrange the remaining data structures in the Text out List. + // + CurrentNumOfConsoles = Private->CurrentNumberOfConsoles; + Index = (INT32) CurrentNumOfConsoles - 1; + TextOutList = Private->TextOutList; + while (Index >= 0) { + if (TextOutList->TextOut == TextOut) { + if (TextOutList->UgaDraw != NULL) { + Private->CurrentNumberOfUgaDraw--; + } + if (TextOutList->GraphicsOutput != NULL) { + Private->CurrentNumberOfGraphicsOutput--; + } + CopyMem (TextOutList, TextOutList + 1, sizeof (TEXT_OUT_AND_GOP_DATA) * Index); + CurrentNumOfConsoles--; + break; + } + + Index--; + TextOutList++; + } + // + // The specified TextOut is not managed by the ConSplitter driver + // + if (Index < 0) { + return EFI_NOT_FOUND; + } + + if ((Private->CurrentNumberOfGraphicsOutput == 0) && (Private->CurrentNumberOfUgaDraw == 0)) { + // + // If there is not any physical GOP and UGA device in system, + // Consplitter GOP or UGA protocol will be uninstalled + // + if (!FeaturePcdGet (PcdConOutGopSupport)) { + Status = gBS->UninstallProtocolInterface ( + Private->VirtualHandle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw + ); + } else if (!FeaturePcdGet (PcdConOutUgaSupport)) { + Status = gBS->UninstallProtocolInterface ( + Private->VirtualHandle, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput + ); + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->VirtualHandle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + NULL + ); + } + } + + if (CurrentNumOfConsoles == 0) { + // + // If the number of consoles is zero, reset all parameters + // + Private->CurrentNumberOfConsoles = 0; + Private->TextOutMode.MaxMode = 1; + Private->TextOutQueryData[0].Columns = 80; + Private->TextOutQueryData[0].Rows = 25; + TextOutSetMode (Private, 0); + + return EFI_SUCCESS; + } + // + // Max Mode is realy an intersection of the QueryMode command to all + // devices. So we must copy the QueryMode of the first device to + // QueryData. + // + ZeroMem ( + Private->TextOutQueryData, + Private->TextOutQueryDataCount * sizeof (TEXT_OUT_SPLITTER_QUERY_DATA) + ); + + FreePool (Private->TextOutModeMap); + Private->TextOutModeMap = NULL; + TextOutList = Private->TextOutList; + + // + // Add the first TextOut to the QueryData array and ModeMap table + // + Status = ConSplitterAddOutputMode (Private, TextOutList->TextOut); + + // + // Now add one by one + // + Index = 1; + Private->CurrentNumberOfConsoles = 1; + TextOutList++; + while ((UINTN) Index < CurrentNumOfConsoles) { + ConSplitterSyncOutputMode (Private, TextOutList->TextOut); + Index++; + Private->CurrentNumberOfConsoles++; + TextOutList++; + } + + ConSplitterGetIntersectionBetweenConOutAndStrErr (); + + return Status; +} + + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + + Private = TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + Private->KeyEventSignalState = FALSE; + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextInList[Index]->Reset ( + Private->TextInList[Index], + ExtendedVerification + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + if (!EFI_ERROR (ReturnStatus)) { + ToggleStateSyncReInitialization (Private); + // + // Empty the key queue. + // + Private->CurrentNumberOfKeys = 0; + } + + return ReturnStatus; +} + +/** + Dequeue the saved key from internal key queue. + + @param Private Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + @retval EFI_NOT_FOUND Queue is empty. + @retval EFI_SUCCESS First key is dequeued and returned. +**/ +EFI_STATUS +ConSplitterTextInExDequeueKey ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + OUT EFI_KEY_DATA *KeyData + ) +{ + if (Private->CurrentNumberOfKeys == 0) { + return EFI_NOT_FOUND; + } + // + // Return the first saved key. + // + CopyMem (KeyData, &Private->KeyQueue[0], sizeof (EFI_KEY_DATA)); + Private->CurrentNumberOfKeys--; + CopyMem ( + &Private->KeyQueue[0], + &Private->KeyQueue[1], + Private->CurrentNumberOfKeys * sizeof (EFI_KEY_DATA) + ); + return EFI_SUCCESS; +} + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param Private Protocol instance pointer. + @param Key Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due + to hardware errors. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInPrivateReadKeyStroke ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + OUT EFI_INPUT_KEY *Key + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_KEY_DATA KeyData; + + // + // Return the first saved non-NULL key. + // + while (TRUE) { + Status = ConSplitterTextInExDequeueKey (Private, &KeyData); + if (EFI_ERROR (Status)) { + break; + } + if ((KeyData.Key.ScanCode != CHAR_NULL) || (KeyData.Key.UnicodeChar != SCAN_NULL)) { + CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); + return Status; + } + } + + Key->UnicodeChar = 0; + Key->ScanCode = SCAN_NULL; + + // + // if no physical console input device exists, return EFI_NOT_READY; + // if any physical console input device has key input, + // return the key and EFI_SUCCESS. + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles;) { + Status = Private->TextInList[Index]->ReadKeyStroke ( + Private->TextInList[Index], + &KeyData.Key + ); + if (!EFI_ERROR (Status)) { + // + // If it is not partial keystorke, return the key. Otherwise, continue + // to read key from THIS physical console input device. + // + if ((KeyData.Key.ScanCode != CHAR_NULL) || (KeyData.Key.UnicodeChar != SCAN_NULL)) { + CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); + return Status; + } + } else { + // + // Continue to read key from NEXT physical console input device. + // + Index++; + } + } + + return EFI_NOT_READY; +} + + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param Key Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due + to hardware errors. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ) +{ + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + + Private = TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + Private->KeyEventSignalState = FALSE; + + // + // Signal ConnectConIn event on first call in Lazy ConIn mode + // + if (!mConInIsConnect && PcdGetBool (PcdConInConnectOnDemand)) { + DEBUG ((EFI_D_INFO, "Connect ConIn in first ReadKeyStoke in Lazy ConIn mode.\n")); + gBS->SignalEvent (Private->ConnectConInEvent); + mConInIsConnect = TRUE; + } + + return ConSplitterTextInPrivateReadKeyStroke (Private, Key); +} + + +/** + This event aggregates all the events of the ConIn devices in the spliter. + + If any events of physical ConIn devices are signaled, signal the ConIn + spliter event. This will cause the calling code to call + ConSplitterTextInReadKeyStroke (). + + @param Event The Event assoicated with callback. + @param Context Context registered when Event was created. + +**/ +VOID +EFIAPI +ConSplitterTextInWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + + Private = (TEXT_IN_SPLITTER_PRIVATE_DATA *) Context; + + if (Private->KeyEventSignalState) { + // + // If KeyEventSignalState is flagged before, and not cleared by Reset() or ReadKeyStroke() + // + gBS->SignalEvent (Event); + return ; + } + + // + // If any physical console input device has key input, signal the event. + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = gBS->CheckEvent (Private->TextInList[Index]->WaitForKey); + if (!EFI_ERROR (Status)) { + gBS->SignalEvent (Event); + Private->KeyEventSignalState = TRUE; + } + } +} + + + +/** + Test if the key has been registered on input device. + + @param RegsiteredData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + registered. + @param InputData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval TRUE Key be pressed matches a registered key. + @retval FLASE Match failed. + +**/ +BOOLEAN +IsKeyRegistered ( + IN EFI_KEY_DATA *RegsiteredData, + IN EFI_KEY_DATA *InputData + ) +{ + ASSERT (RegsiteredData != NULL && InputData != NULL); + + if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) || + (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) { + return FALSE; + } + + // + // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored. + // + if (RegsiteredData->KeyState.KeyShiftState != 0 && + RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState) { + return FALSE; + } + if (RegsiteredData->KeyState.KeyToggleState != 0 && + RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState) { + return FALSE; + } + + return TRUE; + +} + + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + + Private = TEXT_IN_EX_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + Private->KeyEventSignalState = FALSE; + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfExConsoles; Index++) { + Status = Private->TextInExList[Index]->Reset ( + Private->TextInExList[Index], + ExtendedVerification + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + if (!EFI_ERROR (ReturnStatus)) { + ToggleStateSyncReInitialization (Private); + // + // Empty the key queue. + // + Private->CurrentNumberOfKeys = 0; + } + + return ReturnStatus; + +} + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due + to hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ) +{ + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + EFI_STATUS Status; + UINTN Index; + EFI_KEY_STATE KeyState; + EFI_KEY_DATA CurrentKeyData; + + + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = TEXT_IN_EX_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + Private->KeyEventSignalState = FALSE; + + // + // Signal ConnectConIn event on first call in Lazy ConIn mode + // + if (!mConInIsConnect && PcdGetBool (PcdConInConnectOnDemand)) { + DEBUG ((EFI_D_INFO, "Connect ConIn in first ReadKeyStoke in Lazy ConIn mode.\n")); + gBS->SignalEvent (Private->ConnectConInEvent); + mConInIsConnect = TRUE; + } + + // + // Return the first saved key. + // + Status = ConSplitterTextInExDequeueKey (Private, KeyData); + if (!EFI_ERROR (Status)) { + return Status; + } + ASSERT (Private->CurrentNumberOfKeys == 0); + + ZeroMem (&KeyState, sizeof (KeyState)); + + // + // Iterate through all physical consoles to get key state. + // Some physical consoles may return valid key. + // Queue the valid keys. + // + for (Index = 0; Index < Private->CurrentNumberOfExConsoles; Index++) { + ZeroMem (&CurrentKeyData, sizeof (EFI_KEY_DATA)); + Status = Private->TextInExList[Index]->ReadKeyStrokeEx ( + Private->TextInExList[Index], + &CurrentKeyData + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) { + continue; + } + + // + // Consolidate the key state from all physical consoles. + // + if ((CurrentKeyData.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) { + KeyState.KeyShiftState |= CurrentKeyData.KeyState.KeyShiftState; + } + if ((CurrentKeyData.KeyState.KeyToggleState & EFI_TOGGLE_STATE_VALID) != 0) { + KeyState.KeyToggleState |= CurrentKeyData.KeyState.KeyToggleState; + } + + if (!EFI_ERROR (Status)) { + // + // If virtual KeyState has been required to be exposed, or it is not + // partial keystorke, queue the key. + // It's possible that user presses at multiple keyboards at the same moment, + // Private->KeyQueue[] are the storage to save all the keys. + // + if ((Private->VirtualKeyStateExported) || + (CurrentKeyData.Key.ScanCode != CHAR_NULL) || + (CurrentKeyData.Key.UnicodeChar != SCAN_NULL)) { + CopyMem ( + &Private->KeyQueue[Private->CurrentNumberOfKeys], + &CurrentKeyData, + sizeof (EFI_KEY_DATA) + ); + Private->CurrentNumberOfKeys++; + } + } + } + + // + // Consolidate the key state for all keys in Private->KeyQueue[] + // + for (Index = 0; Index < Private->CurrentNumberOfKeys; Index++) { + CopyMem (&Private->KeyQueue[Index].KeyState, &KeyState, sizeof (EFI_KEY_STATE)); + } + + // + // Return the first saved key. + // + Status = ConSplitterTextInExDequeueKey (Private, KeyData); + if (!EFI_ERROR (Status)) { + return Status; + } + + // + // Always return the key state even there is no key pressed. + // + ZeroMem (&KeyData->Key, sizeof (KeyData->Key)); + CopyMem (&KeyData->KeyState, &KeyState, sizeof (KeyData->KeyState)); + return EFI_NOT_READY; +} + + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + could not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set its + state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ) +{ + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + EFI_STATUS Status; + UINTN Index; + EFI_KEY_TOGGLE_STATE PhysicalKeyToggleState; + + if (KeyToggleState == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = TEXT_IN_EX_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // Always turn on physical TextInEx partial key report for + // toggle state sync. + // + PhysicalKeyToggleState = *KeyToggleState | EFI_KEY_STATE_EXPOSED; + + // + // if no physical console input device exists, return EFI_SUCCESS; + // otherwise return the status of setting state of physical console input device + // + for (Index = 0; Index < Private->CurrentNumberOfExConsoles; Index++) { + Status = Private->TextInExList[Index]->SetState ( + Private->TextInExList[Index], + &PhysicalKeyToggleState + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Record the physical KeyToggleState. + // + Private->PhysicalKeyToggleState = PhysicalKeyToggleState; + // + // Get if virtual KeyState has been required to be exposed. + // + Private->VirtualKeyStateExported = (((*KeyToggleState) & EFI_KEY_STATE_EXPOSED) != 0); + + return EFI_SUCCESS; + +} + + +/** + Register a notification function for a particular keystroke for the input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with + the keystroke information for the key that was + pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState + and KeyData.KeyState.KeyShiftState are 0, then any incomplete + keystroke will trigger a notification of the KeyNotificationFunction. + @param KeyNotificationFunction Points to the function to be called when the key + sequence is typed specified by KeyData. This notification function + should be called at <=TPL_CALLBACK. + @param NotifyHandle Points to the unique handle assigned to the + registered notification. + + @retval EFI_SUCCESS The notification function was registered + successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data + structures. + @retval EFI_INVALID_PARAMETER KeyData or KeyNotificationFunction or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ) +{ + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + EFI_STATUS Status; + UINTN Index; + TEXT_IN_EX_SPLITTER_NOTIFY *NewNotify; + LIST_ENTRY *Link; + TEXT_IN_EX_SPLITTER_NOTIFY *CurrentNotify; + + + if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = TEXT_IN_EX_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. + // + for (Link = Private->NotifyList.ForwardLink; Link != &Private->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = TEXT_IN_EX_SPLITTER_NOTIFY_FROM_THIS (Link); + if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { + if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { + *NotifyHandle = CurrentNotify; + return EFI_SUCCESS; + } + } + } + + // + // Allocate resource to save the notification function + // + NewNotify = (TEXT_IN_EX_SPLITTER_NOTIFY *) AllocateZeroPool (sizeof (TEXT_IN_EX_SPLITTER_NOTIFY)); + if (NewNotify == NULL) { + return EFI_OUT_OF_RESOURCES; + } + NewNotify->NotifyHandleList = (VOID **) AllocateZeroPool (sizeof (VOID *) * Private->TextInExListCount); + if (NewNotify->NotifyHandleList == NULL) { + gBS->FreePool (NewNotify); + return EFI_OUT_OF_RESOURCES; + } + NewNotify->Signature = TEXT_IN_EX_SPLITTER_NOTIFY_SIGNATURE; + NewNotify->KeyNotificationFn = KeyNotificationFunction; + CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); + + // + // Return the wrong status of registering key notify of + // physical console input device if meet problems + // + for (Index = 0; Index < Private->CurrentNumberOfExConsoles; Index++) { + Status = Private->TextInExList[Index]->RegisterKeyNotify ( + Private->TextInExList[Index], + KeyData, + KeyNotificationFunction, + &NewNotify->NotifyHandleList[Index] + ); + if (EFI_ERROR (Status)) { + // + // Un-register the key notify on all physical console input devices + // + while (Index-- != 0) { + Private->TextInExList[Index]->UnregisterKeyNotify ( + Private->TextInExList[Index], + NewNotify->NotifyHandleList[Index] + ); + } + gBS->FreePool (NewNotify->NotifyHandleList); + gBS->FreePool (NewNotify); + return Status; + } + } + + InsertTailList (&Private->NotifyList, &NewNotify->NotifyEntry); + + *NotifyHandle = NewNotify; + + return EFI_SUCCESS; + +} + + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function being + unregistered. + + @retval EFI_SUCCESS The notification function was unregistered + successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ) +{ + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + TEXT_IN_EX_SPLITTER_NOTIFY *CurrentNotify; + LIST_ENTRY *Link; + + if (NotificationHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = TEXT_IN_EX_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + for (Link = Private->NotifyList.ForwardLink; Link != &Private->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = TEXT_IN_EX_SPLITTER_NOTIFY_FROM_THIS (Link); + if (CurrentNotify == NotificationHandle) { + for (Index = 0; Index < Private->CurrentNumberOfExConsoles; Index++) { + Private->TextInExList[Index]->UnregisterKeyNotify ( + Private->TextInExList[Index], + CurrentNotify->NotifyHandleList[Index] + ); + } + RemoveEntryList (&CurrentNotify->NotifyEntry); + + gBS->FreePool (CurrentNotify->NotifyHandleList); + gBS->FreePool (CurrentNotify); + return EFI_SUCCESS; + } + } + + // + // NotificationHandle is not found in database + // + return EFI_INVALID_PARAMETER; +} + + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerReset ( + IN EFI_SIMPLE_POINTER_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + + Private = TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_SIMPLE_POINTER_THIS (This); + + Private->InputEventSignalState = FALSE; + + if (Private->CurrentNumberOfPointers == 0) { + return EFI_SUCCESS; + } + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfPointers; Index++) { + Status = Private->PointerList[Index]->Reset ( + Private->PointerList[Index], + ExtendedVerification + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + return ReturnStatus; +} + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param Private Protocol instance pointer. + @param State The state information of simple pointer device. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due + to hardware errors. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerPrivateGetState ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN OUT EFI_SIMPLE_POINTER_STATE *State + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINTN Index; + EFI_SIMPLE_POINTER_STATE CurrentState; + + State->RelativeMovementX = 0; + State->RelativeMovementY = 0; + State->RelativeMovementZ = 0; + State->LeftButton = FALSE; + State->RightButton = FALSE; + + // + // if no physical console input device exists, return EFI_NOT_READY; + // if any physical console input device has key input, + // return the key and EFI_SUCCESS. + // + ReturnStatus = EFI_NOT_READY; + for (Index = 0; Index < Private->CurrentNumberOfPointers; Index++) { + + Status = Private->PointerList[Index]->GetState ( + Private->PointerList[Index], + &CurrentState + ); + if (!EFI_ERROR (Status)) { + if (ReturnStatus == EFI_NOT_READY) { + ReturnStatus = EFI_SUCCESS; + } + + if (CurrentState.LeftButton) { + State->LeftButton = TRUE; + } + + if (CurrentState.RightButton) { + State->RightButton = TRUE; + } + + if (CurrentState.RelativeMovementX != 0 && Private->PointerList[Index]->Mode->ResolutionX != 0) { + State->RelativeMovementX += (CurrentState.RelativeMovementX * (INT32) Private->SimplePointerMode.ResolutionX) / (INT32) Private->PointerList[Index]->Mode->ResolutionX; + } + + if (CurrentState.RelativeMovementY != 0 && Private->PointerList[Index]->Mode->ResolutionY != 0) { + State->RelativeMovementY += (CurrentState.RelativeMovementY * (INT32) Private->SimplePointerMode.ResolutionY) / (INT32) Private->PointerList[Index]->Mode->ResolutionY; + } + + if (CurrentState.RelativeMovementZ != 0 && Private->PointerList[Index]->Mode->ResolutionZ != 0) { + State->RelativeMovementZ += (CurrentState.RelativeMovementZ * (INT32) Private->SimplePointerMode.ResolutionZ) / (INT32) Private->PointerList[Index]->Mode->ResolutionZ; + } + } else if (Status == EFI_DEVICE_ERROR) { + ReturnStatus = EFI_DEVICE_ERROR; + } + } + + return ReturnStatus; +} + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This A pointer to protocol instance. + @param State A pointer to state information on the pointer device + + @retval EFI_SUCCESS The keystroke information was returned in State. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due + to hardware errors. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerGetState ( + IN EFI_SIMPLE_POINTER_PROTOCOL *This, + IN OUT EFI_SIMPLE_POINTER_STATE *State + ) +{ + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + + Private = TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_SIMPLE_POINTER_THIS (This); + + Private->InputEventSignalState = FALSE; + + return ConSplitterSimplePointerPrivateGetState (Private, State); +} + + +/** + This event agregates all the events of the ConIn devices in the spliter. + If any events of physical ConIn devices are signaled, signal the ConIn + spliter event. This will cause the calling code to call + ConSplitterTextInReadKeyStroke (). + + @param Event The Event assoicated with callback. + @param Context Context registered when Event was created. + +**/ +VOID +EFIAPI +ConSplitterSimplePointerWaitForInput ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + + Private = (TEXT_IN_SPLITTER_PRIVATE_DATA *) Context; + + // + // if InputEventSignalState is flagged before, and not cleared by Reset() or ReadKeyStroke() + // + if (Private->InputEventSignalState) { + gBS->SignalEvent (Event); + return ; + } + // + // if any physical console input device has key input, signal the event. + // + for (Index = 0; Index < Private->CurrentNumberOfPointers; Index++) { + Status = gBS->CheckEvent (Private->PointerList[Index]->WaitForInput); + if (!EFI_ERROR (Status)) { + gBS->SignalEvent (Event); + Private->InputEventSignalState = TRUE; + } + } +} + +/** + Resets the pointer device hardware. + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + could not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerReset ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + + Private = TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_ABSOLUTE_POINTER_THIS (This); + + Private->AbsoluteInputEventSignalState = FALSE; + + if (Private->CurrentNumberOfAbsolutePointers == 0) { + return EFI_SUCCESS; + } + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfAbsolutePointers; Index++) { + Status = Private->AbsolutePointerList[Index]->Reset ( + Private->AbsolutePointerList[Index], + ExtendedVerification + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + return ReturnStatus; +} + + +/** + Retrieves the current state of a pointer device. + + @param This Protocol instance pointer. + @param State A pointer to the state information on the + pointer device. + + @retval EFI_SUCCESS The state of the pointer device was returned in + State.. + @retval EFI_NOT_READY The state of the pointer device has not changed + since the last call to GetState(). + @retval EFI_DEVICE_ERROR A device error occurred while attempting to + retrieve the pointer device's current state. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerGetState ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + IN OUT EFI_ABSOLUTE_POINTER_STATE *State + ) +{ + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINTN Index; + EFI_ABSOLUTE_POINTER_STATE CurrentState; + UINT64 MinX; + UINT64 MinY; + UINT64 MinZ; + UINT64 MaxX; + UINT64 MaxY; + UINT64 MaxZ; + UINT64 VirtualMinX; + UINT64 VirtualMinY; + UINT64 VirtualMinZ; + UINT64 VirtualMaxX; + UINT64 VirtualMaxY; + UINT64 VirtualMaxZ; + + Private = TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_ABSOLUTE_POINTER_THIS (This); + + Private->AbsoluteInputEventSignalState = FALSE; + + State->CurrentX = 0; + State->CurrentY = 0; + State->CurrentZ = 0; + State->ActiveButtons = 0; + + VirtualMinX = Private->AbsolutePointerMode.AbsoluteMinX; + VirtualMinY = Private->AbsolutePointerMode.AbsoluteMinY; + VirtualMinZ = Private->AbsolutePointerMode.AbsoluteMinZ; + VirtualMaxX = Private->AbsolutePointerMode.AbsoluteMaxX; + VirtualMaxY = Private->AbsolutePointerMode.AbsoluteMaxY; + VirtualMaxZ = Private->AbsolutePointerMode.AbsoluteMaxZ; + + // + // if no physical pointer device exists, return EFI_NOT_READY; + // if any physical pointer device has changed state, + // return the state and EFI_SUCCESS. + // + ReturnStatus = EFI_NOT_READY; + for (Index = 0; Index < Private->CurrentNumberOfAbsolutePointers; Index++) { + + Status = Private->AbsolutePointerList[Index]->GetState ( + Private->AbsolutePointerList[Index], + &CurrentState + ); + if (!EFI_ERROR (Status)) { + if (ReturnStatus == EFI_NOT_READY) { + ReturnStatus = EFI_SUCCESS; + } + + MinX = Private->AbsolutePointerList[Index]->Mode->AbsoluteMinX; + MinY = Private->AbsolutePointerList[Index]->Mode->AbsoluteMinY; + MinZ = Private->AbsolutePointerList[Index]->Mode->AbsoluteMinZ; + MaxX = Private->AbsolutePointerList[Index]->Mode->AbsoluteMaxX; + MaxY = Private->AbsolutePointerList[Index]->Mode->AbsoluteMaxY; + MaxZ = Private->AbsolutePointerList[Index]->Mode->AbsoluteMaxZ; + + State->ActiveButtons = CurrentState.ActiveButtons; + + // + // Rescale to Con Splitter virtual Absolute Pointer's resolution. + // + if (!(MinX == 0 && MaxX == 0)) { + State->CurrentX = VirtualMinX + DivU64x64Remainder ( + MultU64x64 ( + CurrentState.CurrentX, + VirtualMaxX - VirtualMinX + ), + MaxX - MinX, + NULL + ); + } + if (!(MinY == 0 && MaxY == 0)) { + State->CurrentY = VirtualMinY + DivU64x64Remainder ( + MultU64x64 ( + CurrentState.CurrentY, + VirtualMaxY - VirtualMinY + ), + MaxY - MinY, + NULL + ); + } + if (!(MinZ == 0 && MaxZ == 0)) { + State->CurrentZ = VirtualMinZ + DivU64x64Remainder ( + MultU64x64 ( + CurrentState.CurrentZ, + VirtualMaxZ - VirtualMinZ + ), + MaxZ - MinZ, + NULL + ); + } + + } else if (Status == EFI_DEVICE_ERROR) { + ReturnStatus = EFI_DEVICE_ERROR; + } + } + + return ReturnStatus; +} + + +/** + This event agregates all the events of the pointer devices in the splitter. + If any events of physical pointer devices are signaled, signal the pointer + splitter event. This will cause the calling code to call + ConSplitterAbsolutePointerGetState (). + + @param Event The Event assoicated with callback. + @param Context Context registered when Event was created. + +**/ +VOID +EFIAPI +ConSplitterAbsolutePointerWaitForInput ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TEXT_IN_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + + Private = (TEXT_IN_SPLITTER_PRIVATE_DATA *) Context; + + // + // if AbsoluteInputEventSignalState is flagged before, + // and not cleared by Reset() or GetState(), signal it + // + if (Private->AbsoluteInputEventSignalState) { + gBS->SignalEvent (Event); + return ; + } + // + // if any physical console input device has key input, signal the event. + // + for (Index = 0; Index < Private->CurrentNumberOfAbsolutePointers; Index++) { + Status = gBS->CheckEvent (Private->AbsolutePointerList[Index]->WaitForInput); + if (!EFI_ERROR (Status)) { + gBS->SignalEvent (Event); + Private->AbsoluteInputEventSignalState = TRUE; + } + } +} + + +/** + Reset the text output device hardware and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform more exhaustive verfication + operation of the device during reset. + + @retval EFI_SUCCESS The text output device was reset. + @retval EFI_DEVICE_ERROR The text output device is not functioning + correctly and could not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextOutList[Index].TextOut->Reset ( + Private->TextOutList[Index].TextOut, + ExtendedVerification + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + This->SetAttribute (This, EFI_TEXT_ATTR (This->Mode->Attribute & 0x0F, EFI_BLACK)); + + // + // reset all mode parameters + // + TextOutSetMode (Private, 0); + + return ReturnStatus; +} + + +/** + Write a Unicode string to the output device. + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be + displayed on the output device(s). All output + devices must also support the Unicode drawing + defined in this file. + + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to + output the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + UINTN MaxColumn; + UINTN MaxRow; + + This->SetAttribute (This, This->Mode->Attribute); + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextOutList[Index].TextOut->OutputString ( + Private->TextOutList[Index].TextOut, + WString + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + if (Private->CurrentNumberOfConsoles > 0) { + Private->TextOutMode.CursorColumn = Private->TextOutList[0].TextOut->Mode->CursorColumn; + Private->TextOutMode.CursorRow = Private->TextOutList[0].TextOut->Mode->CursorRow; + } else { + // + // When there is no real console devices in system, + // update cursor position for the virtual device in consplitter. + // + Private->TextOut.QueryMode ( + &Private->TextOut, + Private->TextOutMode.Mode, + &MaxColumn, + &MaxRow + ); + for (; *WString != CHAR_NULL; WString++) { + switch (*WString) { + case CHAR_BACKSPACE: + if (Private->TextOutMode.CursorColumn == 0 && Private->TextOutMode.CursorRow > 0) { + Private->TextOutMode.CursorRow--; + Private->TextOutMode.CursorColumn = (INT32) (MaxColumn - 1); + } else if (Private->TextOutMode.CursorColumn > 0) { + Private->TextOutMode.CursorColumn--; + } + break; + + case CHAR_LINEFEED: + if (Private->TextOutMode.CursorRow < (INT32) (MaxRow - 1)) { + Private->TextOutMode.CursorRow++; + } + break; + + case CHAR_CARRIAGE_RETURN: + Private->TextOutMode.CursorColumn = 0; + break; + + default: + if (Private->TextOutMode.CursorColumn < (INT32) (MaxColumn - 1)) { + Private->TextOutMode.CursorColumn++; + } else { + Private->TextOutMode.CursorColumn = 0; + if (Private->TextOutMode.CursorRow < (INT32) (MaxRow - 1)) { + Private->TextOutMode.CursorRow++; + } + } + break; + } + } + } + + return ReturnStatus; +} + + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be + examined for the output device(s). + + @retval EFI_SUCCESS The device(s) are capable of rendering the + output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string + cannot be rendered by one or more of the output + devices mapped by the EFI handle. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextOutList[Index].TextOut->TestString ( + Private->TextOutList[Index].TextOut, + WString + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + // + // There is no DevNullTextOutTestString () since a Unicode buffer would + // always return EFI_SUCCESS. + // ReturnStatus will be EFI_SUCCESS if no consoles are present + // + return ReturnStatus; +} + + +/** + Returns information for an available text mode that the output device(s) + supports. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to return information on. + @param Columns Returns the columns of the text output device + for the requested ModeNumber. + @param Rows Returns the rows of the text output device + for the requested ModeNumber. + + @retval EFI_SUCCESS The requested mode information was returned. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The mode number was not valid. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ) +{ + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN CurrentMode; + INT32 *TextOutModeMap; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // Check whether param ModeNumber is valid. + // ModeNumber should be within range 0 ~ MaxMode - 1. + // + if ( (ModeNumber > (UINTN)(((UINT32)-1)>>1)) ) { + return EFI_UNSUPPORTED; + } + + if ((INT32) ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + // + // We get the available mode from mode intersection map if it's available + // + if (Private->TextOutModeMap != NULL) { + TextOutModeMap = Private->TextOutModeMap + Private->TextOutListCount * ModeNumber; + CurrentMode = (UINTN)(*TextOutModeMap); + *Columns = Private->TextOutQueryData[CurrentMode].Columns; + *Rows = Private->TextOutQueryData[CurrentMode].Rows; + } else { + *Columns = Private->TextOutQueryData[ModeNumber].Columns; + *Rows = Private->TextOutQueryData[ModeNumber].Rows; + } + + if (*Columns <= 0 && *Rows <= 0) { + return EFI_UNSUPPORTED; + + } + + return EFI_SUCCESS; +} + + +/** + Sets the output device(s) to a specified mode. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to set. + + @retval EFI_SUCCESS The requested text mode was set. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The mode number was not valid. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + INT32 *TextOutModeMap; + EFI_STATUS ReturnStatus; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // Check whether param ModeNumber is valid. + // ModeNumber should be within range 0 ~ MaxMode - 1. + // + if ( (ModeNumber > (UINTN)(((UINT32)-1)>>1)) ) { + return EFI_UNSUPPORTED; + } + + if ((INT32) ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + // + // If the mode is being set to the curent mode, then just clear the screen and return. + // + if (Private->TextOutMode.Mode == (INT32) ModeNumber) { + return ConSplitterTextOutClearScreen (This); + } + // + // return the worst status met + // + TextOutModeMap = Private->TextOutModeMap + Private->TextOutListCount * ModeNumber; + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + // + // While adding a console out device do not set same mode again for the same device. + // + if ((!Private->AddingConOutDevice) || + (TextOutModeMap[Index] != Private->TextOutList[Index].TextOut->Mode->Mode)) { + Status = Private->TextOutList[Index].TextOut->SetMode ( + Private->TextOutList[Index].TextOut, + TextOutModeMap[Index] + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + } + + // + // Set mode parameter to specified mode number + // + TextOutSetMode (Private, ModeNumber); + + return ReturnStatus; +} + + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + @param This Protocol instance pointer. + @param Attribute The attribute to set. Bits 0..3 are the + foreground color, and bits 4..6 are the + background color. All other bits are undefined + and must be zero. The valid Attributes are + defined in this file. + + @retval EFI_SUCCESS The attribute was set. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The attribute requested is not defined. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // Check whether param Attribute is valid. + // + if ((Attribute | 0x7F) != 0x7F) { + return EFI_UNSUPPORTED; + } + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextOutList[Index].TextOut->SetAttribute ( + Private->TextOutList[Index].TextOut, + Attribute + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + Private->TextOutMode.Attribute = (INT32) Attribute; + + return ReturnStatus; +} + + +/** + Clears the output device(s) display to the currently selected background + color. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextOutList[Index].TextOut->ClearScreen (Private->TextOutList[Index].TextOut); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + // + // No need to do extra check here as whether (Column, Row) is valid has + // been checked in ConSplitterTextOutSetCursorPosition. And (0, 0) should + // always be supported. + // + Private->TextOutMode.CursorColumn = 0; + Private->TextOutMode.CursorRow = 0; + Private->TextOutMode.CursorVisible = TRUE; + + return ReturnStatus; +} + + +/** + Sets the current coordinates of the cursor position + + @param This Protocol instance pointer. + @param Column The column position to set the cursor to. Must be + greater than or equal to zero and less than the + number of columns by QueryMode (). + @param Row The row position to set the cursor to. Must be + greater than or equal to zero and less than the + number of rows by QueryMode (). + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode, + or the cursor position is invalid for the + current mode. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + UINTN MaxColumn; + UINTN MaxRow; + INT32 *TextOutModeMap; + INT32 ModeNumber; + INT32 CurrentMode; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + TextOutModeMap = NULL; + ModeNumber = Private->TextOutMode.Mode; + + // + // Get current MaxColumn and MaxRow from intersection map + // + if (Private->TextOutModeMap != NULL) { + TextOutModeMap = Private->TextOutModeMap + Private->TextOutListCount * ModeNumber; + CurrentMode = *TextOutModeMap; + } else { + CurrentMode = ModeNumber; + } + + MaxColumn = Private->TextOutQueryData[CurrentMode].Columns; + MaxRow = Private->TextOutQueryData[CurrentMode].Rows; + + if (Column >= MaxColumn || Row >= MaxRow) { + return EFI_UNSUPPORTED; + } + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextOutList[Index].TextOut->SetCursorPosition ( + Private->TextOutList[Index].TextOut, + Column, + Row + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + // + // No need to do extra check here as whether (Column, Row) is valid has + // been checked in ConSplitterTextOutSetCursorPosition. And (0, 0) should + // always be supported. + // + Private->TextOutMode.CursorColumn = (INT32) Column; + Private->TextOutMode.CursorRow = (INT32) Row; + + return ReturnStatus; +} + + +/** + Makes the cursor visible or invisible + + @param This Protocol instance pointer. + @param Visible If TRUE, the cursor is set to be visible. If + FALSE, the cursor is set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request, or the device does not support + changing the cursor mode. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + + Private = TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + // + // return the worst status met + // + for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) { + Status = Private->TextOutList[Index].TextOut->EnableCursor ( + Private->TextOutList[Index].TextOut, + Visible + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + + Private->TextOutMode.CursorVisible = Visible; + + return ReturnStatus; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.h b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.h new file mode 100644 index 000000000..f26d2adb8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.h @@ -0,0 +1,2000 @@ +/** @file + Private data structures for the Console Splitter driver + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CON_SPLITTER_H_ +#define _CON_SPLITTER_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 + +// +// Driver Binding Externs +// +extern EFI_DRIVER_BINDING_PROTOCOL gConSplitterConInDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gConSplitterConInComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gConSplitterConInComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gConSplitterSimplePointerDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gConSplitterSimplePointerComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gConSplitterSimplePointerComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gConSplitterAbsolutePointerDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gConSplitterAbsolutePointerComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gConSplitterAbsolutePointerComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gConSplitterConOutDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gConSplitterConOutComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gConSplitterConOutComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gConSplitterStdErrDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gConSplitterStdErrComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gConSplitterStdErrComponentName2; + + +// +// These definitions were in the old Hii protocol, but are not in the new UEFI +// version. So they are defined locally. +// +#define UNICODE_NARROW_CHAR 0xFFF0 +#define UNICODE_WIDE_CHAR 0xFFF1 + + +// +// Private Data Structures +// +#define CONSOLE_SPLITTER_ALLOC_UNIT 32 + + +typedef struct { + UINTN Column; + UINTN Row; +} CONSOLE_OUT_MODE; + +typedef struct { + UINTN Columns; + UINTN Rows; +} TEXT_OUT_SPLITTER_QUERY_DATA; + +#define KEY_STATE_VALID_EXPOSED (EFI_TOGGLE_STATE_VALID | EFI_KEY_STATE_EXPOSED) + +#define TEXT_IN_EX_SPLITTER_NOTIFY_SIGNATURE SIGNATURE_32 ('T', 'i', 'S', 'n') + +// +// Private data for Text In Ex Splitter Notify +// +typedef struct _TEXT_IN_EX_SPLITTER_NOTIFY { + UINTN Signature; + VOID **NotifyHandleList; + EFI_KEY_DATA KeyData; + EFI_KEY_NOTIFY_FUNCTION KeyNotificationFn; + LIST_ENTRY NotifyEntry; +} TEXT_IN_EX_SPLITTER_NOTIFY; + +#define TEXT_IN_EX_SPLITTER_NOTIFY_FROM_THIS(a) \ + CR ((a), \ + TEXT_IN_EX_SPLITTER_NOTIFY, \ + NotifyEntry, \ + TEXT_IN_EX_SPLITTER_NOTIFY_SIGNATURE \ + ) + +#define TEXT_IN_SPLITTER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('T', 'i', 'S', 'p') + +// +// Private data for the Console In splitter +// +typedef struct { + UINT64 Signature; + EFI_HANDLE VirtualHandle; + + EFI_SIMPLE_TEXT_INPUT_PROTOCOL TextIn; + UINTN CurrentNumberOfConsoles; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL **TextInList; + UINTN TextInListCount; + + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL TextInEx; + UINTN CurrentNumberOfExConsoles; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL **TextInExList; + UINTN TextInExListCount; + LIST_ENTRY NotifyList; + EFI_KEY_DATA *KeyQueue; + UINTN CurrentNumberOfKeys; + // + // It will be initialized and synced between console input devices + // for toggle state sync. + // + EFI_KEY_TOGGLE_STATE PhysicalKeyToggleState; + // + // It will be initialized and used to record if virtual KeyState + // has been required to be exposed. + // + BOOLEAN VirtualKeyStateExported; + + + EFI_SIMPLE_POINTER_PROTOCOL SimplePointer; + EFI_SIMPLE_POINTER_MODE SimplePointerMode; + UINTN CurrentNumberOfPointers; + EFI_SIMPLE_POINTER_PROTOCOL **PointerList; + UINTN PointerListCount; + + EFI_ABSOLUTE_POINTER_PROTOCOL AbsolutePointer; + EFI_ABSOLUTE_POINTER_MODE AbsolutePointerMode; + UINTN CurrentNumberOfAbsolutePointers; + EFI_ABSOLUTE_POINTER_PROTOCOL **AbsolutePointerList; + UINTN AbsolutePointerListCount; + BOOLEAN AbsoluteInputEventSignalState; + + BOOLEAN KeyEventSignalState; + BOOLEAN InputEventSignalState; + EFI_EVENT ConnectConInEvent; +} TEXT_IN_SPLITTER_PRIVATE_DATA; + +#define TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_THIS(a) \ + CR ((a), \ + TEXT_IN_SPLITTER_PRIVATE_DATA, \ + TextIn, \ + TEXT_IN_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) + +#define TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_SIMPLE_POINTER_THIS(a) \ + CR ((a), \ + TEXT_IN_SPLITTER_PRIVATE_DATA, \ + SimplePointer, \ + TEXT_IN_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) +#define TEXT_IN_EX_SPLITTER_PRIVATE_DATA_FROM_THIS(a) \ + CR (a, \ + TEXT_IN_SPLITTER_PRIVATE_DATA, \ + TextInEx, \ + TEXT_IN_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) + +#define TEXT_IN_SPLITTER_PRIVATE_DATA_FROM_ABSOLUTE_POINTER_THIS(a) \ + CR (a, \ + TEXT_IN_SPLITTER_PRIVATE_DATA, \ + AbsolutePointer, \ + TEXT_IN_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) + + +#define TEXT_OUT_SPLITTER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('T', 'o', 'S', 'p') + +typedef struct { + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; +} TEXT_OUT_AND_GOP_DATA; + +// +// Private data for the Console Out splitter +// +typedef struct { + UINT64 Signature; + EFI_HANDLE VirtualHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL TextOut; + EFI_SIMPLE_TEXT_OUTPUT_MODE TextOutMode; + + EFI_UGA_DRAW_PROTOCOL UgaDraw; + UINT32 UgaHorizontalResolution; + UINT32 UgaVerticalResolution; + UINT32 UgaColorDepth; + UINT32 UgaRefreshRate; + + EFI_GRAPHICS_OUTPUT_PROTOCOL GraphicsOutput; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GraphicsOutputModeBuffer; + UINTN CurrentNumberOfGraphicsOutput; + UINTN CurrentNumberOfUgaDraw; + + UINTN CurrentNumberOfConsoles; + TEXT_OUT_AND_GOP_DATA *TextOutList; + UINTN TextOutListCount; + TEXT_OUT_SPLITTER_QUERY_DATA *TextOutQueryData; + UINTN TextOutQueryDataCount; + INT32 *TextOutModeMap; + + BOOLEAN AddingConOutDevice; + +} TEXT_OUT_SPLITTER_PRIVATE_DATA; + +#define TEXT_OUT_SPLITTER_PRIVATE_DATA_FROM_THIS(a) \ + CR ((a), \ + TEXT_OUT_SPLITTER_PRIVATE_DATA, \ + TextOut, \ + TEXT_OUT_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) + +#define GRAPHICS_OUTPUT_SPLITTER_PRIVATE_DATA_FROM_THIS(a) \ + CR ((a), \ + TEXT_OUT_SPLITTER_PRIVATE_DATA, \ + GraphicsOutput, \ + TEXT_OUT_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) + +#define UGA_DRAW_SPLITTER_PRIVATE_DATA_FROM_THIS(a) \ + CR ((a), \ + TEXT_OUT_SPLITTER_PRIVATE_DATA, \ + UgaDraw, \ + TEXT_OUT_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) + +#define CONSOLE_CONTROL_SPLITTER_PRIVATE_DATA_FROM_THIS(a) \ + CR ((a), \ + TEXT_OUT_SPLITTER_PRIVATE_DATA, \ + ConsoleControl, \ + TEXT_OUT_SPLITTER_PRIVATE_DATA_SIGNATURE \ + ) + +// +// Function Prototypes +// + +/** + The user Entry Point for module ConSplitter. The user code starts with this function. + + Installs driver module protocols and. Creates virtual device handles for ConIn, + ConOut, and StdErr. Installs Simple Text In protocol, Simple Text In Ex protocol, + Simple Pointer protocol, Absolute Pointer protocol on those virtual handlers. + Installs Graphics Output protocol and/or UGA Draw protocol if needed. + + @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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +ConSplitterDriverEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + Construct console input devices' private data. + + @param ConInPrivate A pointer to the TEXT_IN_SPLITTER_PRIVATE_DATA + structure. + + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_SUCCESS Text Input Devcie's private data has been constructed. + @retval other Failed to construct private data. + +**/ +EFI_STATUS +ConSplitterTextInConstructor ( + TEXT_IN_SPLITTER_PRIVATE_DATA *ConInPrivate + ); + +/** + Construct console output devices' private data. + + @param ConOutPrivate A pointer to the TEXT_OUT_SPLITTER_PRIVATE_DATA + structure. + + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_SUCCESS Text Input Devcie's private data has been constructed. + +**/ +EFI_STATUS +ConSplitterTextOutConstructor ( + TEXT_OUT_SPLITTER_PRIVATE_DATA *ConOutPrivate + ); + + +/** + Test to see if Console In Device could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Test to see if Simple Pointer protocol could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Test to see if Console Out Device could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Test to see if Standard Error Device could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start Console In Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Console In Consplitter is added to ControllerHandle. + @retval other Console In Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start Simple Pointer Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Simple Pointer Consplitter is added to ControllerHandle. + @retval other Simple Pointer Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start Console Out Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Console Out Consplitter is added to ControllerHandle. + @retval other Console Out Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start Standard Error Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Standard Error Consplitter is added to ControllerHandle. + @retval other Standard Error Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop Console In ConSplitter on ControllerHandle by closing Console In Devcice GUID. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Stop Simple Pointer protocol ConSplitter on ControllerHandle by closing + Simple Pointer protocol. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Stop Console Out ConSplitter on device handle by closing Console Out Devcice GUID. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Stop Standard Error ConSplitter on ControllerHandle by closing Standard Error GUID. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + + +/** + Test to see if Absolute Pointer protocol could be supported on the Controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start Absolute Pointer Consplitter on device handle. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS Absolute Pointer Consplitter is added to ControllerHandle. + @retval other Absolute Pointer Consplitter does not support this device. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop Absolute Pointer protocol ConSplitter on ControllerHandle by closing + Absolute Pointer protocol. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Add Absolute Pointer Device in Consplitter Absolute Pointer list. + + @param Private Text In Splitter pointer. + @param AbsolutePointer Absolute Pointer protocol pointer. + + @retval EFI_SUCCESS Absolute Pointer Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterAbsolutePointerAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointer + ); + +/** + Remove Absolute Pointer Device from Consplitter Absolute Pointer list. + + @param Private Text In Splitter pointer. + @param AbsolutePointer Absolute Pointer protocol pointer. + + @retval EFI_SUCCESS Absolute Pointer Device removed successfully. + @retval EFI_NOT_FOUND No Absolute Pointer Device found. + +**/ +EFI_STATUS +ConSplitterAbsolutePointerDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointer + ); + +// +// Absolute Pointer protocol interfaces +// + + +/** + Resets the pointer device hardware. + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + could not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerReset ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + + +/** + Retrieves the current state of a pointer device. + + @param This Protocol instance pointer. + @param State A pointer to the state information on the + pointer device. + + @retval EFI_SUCCESS The state of the pointer device was returned in + State.. + @retval EFI_NOT_READY The state of the pointer device has not changed + since the last call to GetState(). + @retval EFI_DEVICE_ERROR A device error occurred while attempting to + retrieve the pointer device's current state. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerGetState ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + IN OUT EFI_ABSOLUTE_POINTER_STATE *State + ); + +/** + This event agregates all the events of the pointer devices in the splitter. + + If any events of physical pointer devices are signaled, signal the pointer + splitter event. This will cause the calling code to call + ConSplitterAbsolutePointerGetState (). + + @param Event The Event assoicated with callback. + @param Context Context registered when Event was created. + +**/ +VOID +EFIAPI +ConSplitterAbsolutePointerWaitForInput ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConInComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL + instance. + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param ChildHandle The handle of the child controller to retrieve the + name of. This is an optional parameter that may + be NULL. It will be NULL for device drivers. It + will also be NULL for a bus drivers that wish to + retrieve the name of the bus controller. It will + not be NULL for a bus driver that wishes to + retrieve the name of a child controller. + @param Language A pointer to RFC4646 language identifier. This is + the language of the controller name that that the + caller is requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up to + the driver writer. + @param ControllerName A pointer to the Unicode string to return. This + Unicode string is the name of the controller + specified by ControllerHandle and ChildHandle in + the language specified by Language from the point + of view of the driver specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterAbsolutePointerComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterConOutComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ConSplitterStdErrComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// TextIn Constructor/Destructor functions +// + +/** + Add Text Input Device in Consplitter Text Input list. + + @param Private Text In Splitter pointer. + @param TextIn Simple Text Input protocol pointer. + + @retval EFI_SUCCESS Text Input Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterTextInAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn + ); + +/** + Remove Text Input Device from Consplitter Text Input list. + + @param Private Text In Splitter pointer. + @param TextIn Simple Text protocol pointer. + + @retval EFI_SUCCESS Simple Text Device removed successfully. + @retval EFI_NOT_FOUND No Simple Text Device found. + +**/ +EFI_STATUS +ConSplitterTextInDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn + ); + +// +// SimplePointer Constuctor/Destructor functions +// + +/** + Add Simple Pointer Device in Consplitter Simple Pointer list. + + @param Private Text In Splitter pointer. + @param SimplePointer Simple Pointer protocol pointer. + + @retval EFI_SUCCESS Simple Pointer Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterSimplePointerAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer + ); + +/** + Remove Simple Pointer Device from Consplitter Simple Pointer list. + + @param Private Text In Splitter pointer. + @param SimplePointer Simple Pointer protocol pointer. + + @retval EFI_SUCCESS Simple Pointer Device removed successfully. + @retval EFI_NOT_FOUND No Simple Pointer Device found. + +**/ +EFI_STATUS +ConSplitterSimplePointerDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer + ); + +// +// TextOut Constuctor/Destructor functions +// + +/** + Add Text Output Device in Consplitter Text Output list. + + @param Private Text Out Splitter pointer. + @param TextOut Simple Text Output protocol pointer. + @param GraphicsOutput Graphics Output protocol pointer. + @param UgaDraw UGA Draw protocol pointer. + + @retval EFI_SUCCESS Text Output Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterTextOutAddDevice ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut, + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput, + IN EFI_UGA_DRAW_PROTOCOL *UgaDraw + ); + +/** + Remove Text Out Device in Consplitter Text Out list. + + @param Private Text Out Splitter pointer. + @param TextOut Simple Text Output Pointer protocol pointer. + + @retval EFI_SUCCESS Text Out Device removed successfully. + @retval EFI_NOT_FOUND No Text Out Device found. + +**/ +EFI_STATUS +ConSplitterTextOutDeleteDevice ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut + ); + +// +// TextIn I/O Functions +// + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param Key Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due + to hardware errors. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ); + +/** + Add Text Input Ex Device in Consplitter Text Input Ex list. + + @param Private Text In Splitter pointer. + @param TextInEx Simple Text Input Ex Input protocol pointer. + + @retval EFI_SUCCESS Text Input Ex Device added successfully. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterTextInExAddDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx + ); + +/** + Remove Text Ex Device from Consplitter Text Input Ex list. + + @param Private Text In Splitter pointer. + @param TextInEx Simple Text Ex protocol pointer. + + @retval EFI_SUCCESS Simple Text Input Ex Device removed successfully. + @retval EFI_NOT_FOUND No Simple Text Input Ex Device found. + +**/ +EFI_STATUS +ConSplitterTextInExDeleteDevice ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx + ); + +// +// Simple Text Input Ex protocol function prototypes +// + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due + to hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ); + + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + could not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set its + state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ); + + +/** + Register a notification function for a particular keystroke for the input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with + the keystroke information for the key that was + pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState + and KeyData.KeyState.KeyShiftState are 0, then any incomplete + keystroke will trigger a notification of the KeyNotificationFunction. + @param KeyNotificationFunction Points to the function to be called when the key + sequence is typed specified by KeyData. This notification function + should be called at <=TPL_CALLBACK. + @param NotifyHandle Points to the unique handle assigned to the + registered notification. + + @retval EFI_SUCCESS The notification function was registered + successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data + structures. + @retval EFI_INVALID_PARAMETER KeyData or KeyNotificationFunction or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ); + + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function being + unregistered. + + @retval EFI_SUCCESS The notification function was unregistered + successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + @retval EFI_NOT_FOUND Can not find the matching entry in database. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ); + +/** + This event aggregates all the events of the ConIn devices in the spliter. + + If any events of physical ConIn devices are signaled, signal the ConIn + spliter event. This will cause the calling code to call + ConSplitterTextInReadKeyStroke (). + + @param Event The Event assoicated with callback. + @param Context Context registered when Event was created. + +**/ +VOID +EFIAPI +ConSplitterTextInWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param Private Protocol instance pointer. + @param Key Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due + to hardware errors. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextInPrivateReadKeyStroke ( + IN TEXT_IN_SPLITTER_PRIVATE_DATA *Private, + OUT EFI_INPUT_KEY *Key + ); + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerReset ( + IN EFI_SIMPLE_POINTER_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This A pointer to protocol instance. + @param State A pointer to state information on the pointer device + + @retval EFI_SUCCESS The keystroke information was returned in State. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due + to hardware errors. + +**/ +EFI_STATUS +EFIAPI +ConSplitterSimplePointerGetState ( + IN EFI_SIMPLE_POINTER_PROTOCOL *This, + IN OUT EFI_SIMPLE_POINTER_STATE *State + ); + +/** + This event agregates all the events of the ConIn devices in the spliter. + If any events of physical ConIn devices are signaled, signal the ConIn + spliter event. This will cause the calling code to call + ConSplitterTextInReadKeyStroke (). + + @param Event The Event assoicated with callback. + @param Context Context registered when Event was created. + +**/ +VOID +EFIAPI +ConSplitterSimplePointerWaitForInput ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// TextOut I/O Functions +// + +/** + Reset the text output device hardware and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform more exhaustive verfication + operation of the device during reset. + + @retval EFI_SUCCESS The text output device was reset. + @retval EFI_DEVICE_ERROR The text output device is not functioning + correctly and could not be reset. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Write a Unicode string to the output device. + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be + displayed on the output device(s). All output + devices must also support the Unicode drawing + defined in this file. + + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to + output the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be + examined for the output device(s). + + @retval EFI_SUCCESS The device(s) are capable of rendering the + output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string + cannot be rendered by one or more of the output + devices mapped by the EFI handle. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Returns information for an available text mode that the output device(s) + supports. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to return information on. + @param Columns Returns the columns of the text output device + for the requested ModeNumber. + @param Rows Returns the rows of the text output device + for the requested ModeNumber. + + @retval EFI_SUCCESS The requested mode information was returned. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The mode number was not valid. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ); + +/** + Sets the output device(s) to a specified mode. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to set. + + @retval EFI_SUCCESS The requested text mode was set. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The mode number was not valid. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ); + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + @param This Protocol instance pointer. + @param Attribute The attribute to set. Bits 0..3 are the + foreground color, and bits 4..6 are the + background color. All other bits are undefined + and must be zero. The valid Attributes are + defined in this file. + + @retval EFI_SUCCESS The attribute was set. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The attribute requested is not defined. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ); + +/** + Clears the output device(s) display to the currently selected background + color. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ); + +/** + Sets the current coordinates of the cursor position + + @param This Protocol instance pointer. + @param Column The column position to set the cursor to. Must be + greater than or equal to zero and less than the + number of columns by QueryMode (). + @param Row The row position to set the cursor to. Must be + greater than or equal to zero and less than the + number of rows by QueryMode (). + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode, + or the cursor position is invalid for the + current mode. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ); + + +/** + Makes the cursor visible or invisible + + @param This Protocol instance pointer. + @param Visible If TRUE, the cursor is set to be visible. If + FALSE, the cursor is set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete + the request, or the device does not support + changing the cursor mode. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +EFI_STATUS +EFIAPI +ConSplitterTextOutEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ); + +/** + Take the passed in Buffer of size ElementSize and grow the buffer + by CONSOLE_SPLITTER_ALLOC_UNIT * ElementSize bytes. + Copy the current data in Buffer to the new version of Buffer and + free the old version of buffer. + + @param ElementSize Size of element in array. + @param Count Current number of elements in array. + @param Buffer Bigger version of passed in Buffer with all the + data. + + @retval EFI_SUCCESS Buffer size has grown. + @retval EFI_OUT_OF_RESOURCES Could not grow the buffer size. + +**/ +EFI_STATUS +ConSplitterGrowBuffer ( + IN UINTN ElementSize, + IN OUT UINTN *Count, + IN OUT VOID **Buffer + ); + +/** + Returns information for an available graphics mode that the graphics device + and the set of active video output devices supports. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber The mode number to return information on. + @param SizeOfInfo A pointer to the size, in bytes, of the Info buffer. + @param Info A pointer to callee allocated buffer that returns information about ModeNumber. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_BUFFER_TOO_SMALL The Info buffer was too small. + @retval EFI_DEVICE_ERROR A hardware error occurred trying to retrieve the video mode. + @retval EFI_INVALID_PARAMETER One of the input args was NULL. + @retval EFI_OUT_OF_RESOURCES No resource available. + +**/ +EFI_STATUS +EFIAPI +ConSplitterGraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ); + +/** + Set the video device into the specified mode and clears the visible portions of + the output display to black. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber Abstraction that defines the current video mode. + + @retval EFI_SUCCESS The graphics mode specified by ModeNumber was selected. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED ModeNumber is not supported by this device. + @retval EFI_OUT_OF_RESOURCES No resource available. + +**/ +EFI_STATUS +EFIAPI +ConSplitterGraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This, + IN UINT32 ModeNumber + ); + +/** + The following table defines actions for BltOperations. + + EfiBltVideoFill - Write data from the BltBuffer pixel (SourceX, SourceY) + directly to every pixel of the video display rectangle + (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + Only one pixel will be used from the BltBuffer. Delta is NOT used. + EfiBltVideoToBltBuffer - Read data from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) and place it in + the BltBuffer rectangle (DestinationX, DestinationY ) + (DestinationX + Width, DestinationY + Height). If DestinationX or + DestinationY is not zero then Delta must be set to the length in bytes + of a row in the BltBuffer. + EfiBltBufferToVideo - Write data from the BltBuffer rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) directly to the + video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). If SourceX or SourceY is + not zero then Delta must be set to the length in bytes of a row in the + BltBuffer. + EfiBltVideoToVideo - Copy from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) . + to the video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + The BltBuffer and Delta are not used in this mode. + + @param This Protocol instance pointer. + @param BltBuffer Buffer containing data to blit into video buffer. + This buffer has a size of + Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + @param BltOperation Operation to perform on BlitBuffer and video + memory + @param SourceX X coordinate of source for the BltBuffer. + @param SourceY Y coordinate of source for the BltBuffer. + @param DestinationX X coordinate of destination for the BltBuffer. + @param DestinationY Y coordinate of destination for the BltBuffer. + @param Width Width of rectangle in BltBuffer in pixels. + @param Height Hight of rectangle in BltBuffer in pixels. + @param Delta OPTIONAL. + + @retval EFI_SUCCESS The Blt operation completed. + @retval EFI_INVALID_PARAMETER BltOperation is not valid. + @retval EFI_DEVICE_ERROR A hardware error occurred writting to the video + buffer. + +**/ +EFI_STATUS +EFIAPI +ConSplitterGraphicsOutputBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ); + + +/** + Return the current video mode information. + + @param This The EFI_UGA_DRAW_PROTOCOL instance. + @param HorizontalResolution The size of video screen in pixels in the X dimension. + @param VerticalResolution The size of video screen in pixels in the Y dimension. + @param ColorDepth Number of bits per pixel, currently defined to be 32. + @param RefreshRate The refresh rate of the monitor in Hertz. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode () + @retval EFI_INVALID_PARAMETER One of the input args was NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterUgaDrawGetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + OUT UINT32 *HorizontalResolution, + OUT UINT32 *VerticalResolution, + OUT UINT32 *ColorDepth, + OUT UINT32 *RefreshRate + ); + +/** + Set the current video mode information. + + @param This The EFI_UGA_DRAW_PROTOCOL instance. + @param HorizontalResolution The size of video screen in pixels in the X dimension. + @param VerticalResolution The size of video screen in pixels in the Y dimension. + @param ColorDepth Number of bits per pixel, currently defined to be 32. + @param RefreshRate The refresh rate of the monitor in Hertz. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode () + @retval EFI_OUT_OF_RESOURCES Out of resources. + +**/ +EFI_STATUS +EFIAPI +ConSplitterUgaDrawSetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + IN UINT32 ColorDepth, + IN UINT32 RefreshRate + ); + +/** + Blt a rectangle of pixels on the graphics screen. + + The following table defines actions for BltOperations. + + EfiUgaVideoFill: + Write data from the BltBuffer pixel (SourceX, SourceY) + directly to every pixel of the video display rectangle + (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + Only one pixel will be used from the BltBuffer. Delta is NOT used. + EfiUgaVideoToBltBuffer: + Read data from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) and place it in + the BltBuffer rectangle (DestinationX, DestinationY ) + (DestinationX + Width, DestinationY + Height). If DestinationX or + DestinationY is not zero then Delta must be set to the length in bytes + of a row in the BltBuffer. + EfiUgaBltBufferToVideo: + Write data from the BltBuffer rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) directly to the + video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). If SourceX or SourceY is + not zero then Delta must be set to the length in bytes of a row in the + BltBuffer. + EfiUgaVideoToVideo: + Copy from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) . + to the video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + The BltBuffer and Delta are not used in this mode. + + @param This Protocol instance pointer. + @param BltBuffer Buffer containing data to blit into video buffer. This + buffer has a size of Width*Height*sizeof(EFI_UGA_PIXEL) + @param BltOperation Operation to perform on BlitBuffer and video memory + @param SourceX X coordinate of source for the BltBuffer. + @param SourceY Y coordinate of source for the BltBuffer. + @param DestinationX X coordinate of destination for the BltBuffer. + @param DestinationY Y coordinate of destination for the BltBuffer. + @param Width Width of rectangle in BltBuffer in pixels. + @param Height Hight of rectangle in BltBuffer in pixels. + @param Delta OPTIONAL + + @retval EFI_SUCCESS The Blt operation completed. + @retval EFI_INVALID_PARAMETER BltOperation is not valid. + @retval EFI_DEVICE_ERROR A hardware error occurred writting to the video buffer. + +**/ +EFI_STATUS +EFIAPI +ConSplitterUgaDrawBlt ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN EFI_UGA_PIXEL *BltBuffer, OPTIONAL + IN EFI_UGA_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ); + +/** + Sets the output device(s) to a specified mode. + + @param Private Text Out Splitter pointer. + @param ModeNumber The mode number to set. + +**/ +VOID +TextOutSetMode ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN UINTN ModeNumber + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf new file mode 100644 index 000000000..9aa1dade7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf @@ -0,0 +1,114 @@ +## @file +# This driver provides multi console supports. +# +# This driver acts as a virtual console, takes over the console I/O control from selected +# standard console devices, and transmits console I/O to related console device drivers. +# Consplitter could install Graphics Output protocol and/or UGA Draw protocol in system +# table according PCD settings(PcdConOutGopSupport, and PcdConOutUgaSupport). It always +# consumes Graphics Output protocol which is produced by display device, and consumes UGA Draw +# protocol which is produced by display device according to PcdUgaConsumeSupport value. +# Note: If only UGA Draw protocol is installed in system, PcdUgaConsumeSupport should be +# set to TRUE. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ConSplitterDxe + MODULE_UNI_FILE = ConSplitterDxe.uni + FILE_GUID = 408edcec-cf6d-477c-a5a8-b4844e3de281 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = ConSplitterDriverEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gConSplitterConInDriverBinding +# COMPONENT_NAME = gConSplitterConInComponentName +# COMPONENT_NAME2 = gConSplitterConInComponentName2 +# DRIVER_BINDING = gConSplitterSimplePointerDriverBinding +# COMPONENT_NAME = gConSplitterSimplePointerComponentName +# COMPONENT_NAME2 = gConSplitterSimplePointerComponentName2 +# DRIVER_BINDING = gConSplitterConOutDriverBinding +# COMPONENT_NAME = gConSplitterConOutComponentName +# COMPONENT_NAME2 = gConSplitterConOutComponentName2 +# DRIVER_BINDING = gConSplitterStdErrDriverBinding +# COMPONENT_NAME = gConSplitterStdErrComponentName +# COMPONENT_NAME2 = gConSplitterStdErrComponentName2 +# + +[Sources] + ConSplitterGraphics.c + ComponentName.c + ConSplitter.h + ConSplitter.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + +[Guids] + gEfiConsoleInDeviceGuid ## SOMETIMES_CONSUMES ## UNDEFINED # protocol GUID installed on device handle + gEfiStandardErrorDeviceGuid ## SOMETIMES_CONSUMES ## UNDEFINED # protocol GUID installed on device handle + gEfiConsoleOutDeviceGuid ## SOMETIMES_CONSUMES ## UNDEFINED # protocol GUID installed on device handle + ## SOMETIMES_PRODUCES ## Event + ## SOMETIMES_CONSUMES ## Event + gConnectConInEventGuid + +[Protocols] + ## PRODUCES + ## TO_START + gEfiSimplePointerProtocolGuid + ## PRODUCES + ## TO_START + gEfiAbsolutePointerProtocolGuid + ## PRODUCES + ## TO_START + gEfiSimpleTextInProtocolGuid + ## PRODUCES + ## TO_START + gEfiSimpleTextInputExProtocolGuid + ## PRODUCES + ## TO_START + gEfiSimpleTextOutProtocolGuid + ## SOMETIMES_PRODUCES + ## SOMETIMES_CONSUMES + gEfiGraphicsOutputProtocolGuid + ## SOMETIMES_PRODUCES + ## SOMETIMES_CONSUMES + gEfiUgaDrawProtocolGuid + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutGopSupport ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutUgaSupport ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport ## CONSUMES + +[Pcd] + ## SOMETIMES_PRODUCES + ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutRow + ## SOMETIMES_PRODUCES + ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutColumn + gEfiMdeModulePkgTokenSpaceGuid.PcdConInConnectOnDemand ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + ConSplitterDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.uni b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.uni new file mode 100644 index 000000000..13c25b2a4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.uni @@ -0,0 +1,23 @@ +// /** @file +// This driver provides multi console supports. +// +// This driver acts as a virtual console, takes over the console I/O control from selected +// standard console devices, and transmits console I/O to related console device drivers. +// Consplitter could install Graphics Output protocol and/or UGA Draw protocol in system +// table according PCD settings(PcdConOutGopSupport, and PcdConOutUgaSupport). It always +// consumes Graphics Output protocol which is produced by display device, and consumes UGA Draw +// protocol which is produced by display device according to PcdUgaConsumeSupport value. +// Note: If only UGA Draw protocol is installed in system, PcdUgaConsumeSupport should be +// set to TRUE. +// +// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Provides multi console support" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver acts as a virtual console, takes over the console I/O control from selected standard console devices, and transmits console I/O to related console device drivers. Consplitter could install Graphics Output protocol and/or UGA Draw protocol in system table according PCD settings(PcdConOutGopSupport, and PcdConOutUgaSupport). It always consumes Graphics Output protocol, which is produced by display device, and consumes UGA Draw protocol, which is produced by display device according to PcdUgaConsumeSupport value. Note: If only UGA Draw protocol is installed in system, PcdUgaConsumeSupport should be set to TRUE." + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxeExtra.uni new file mode 100644 index 000000000..1ec405734 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// ConSplitterDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Console Splitter DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterGraphics.c b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterGraphics.c new file mode 100644 index 000000000..9323f31b6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterGraphics.c @@ -0,0 +1,622 @@ +/** @file + Support for Graphics output spliter. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "ConSplitter.h" + + +CHAR16 mCrLfString[3] = { CHAR_CARRIAGE_RETURN, CHAR_LINEFEED, CHAR_NULL }; + +/** + Returns information for an available graphics mode that the graphics device + and the set of active video output devices supports. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber The mode number to return information on. + @param SizeOfInfo A pointer to the size, in bytes, of the Info buffer. + @param Info A pointer to callee allocated buffer that returns information about ModeNumber. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_BUFFER_TOO_SMALL The Info buffer was too small. + @retval EFI_DEVICE_ERROR A hardware error occurred trying to retrieve the video mode. + @retval EFI_INVALID_PARAMETER One of the input args was NULL. + @retval EFI_OUT_OF_RESOURCES No resource available. + +**/ +EFI_STATUS +EFIAPI +ConSplitterGraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ) +{ + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_STATUS Status; + UINTN Index; + + if (This == NULL || Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) { + return EFI_INVALID_PARAMETER; + } + + // + // retrieve private data + // + Private = GRAPHICS_OUTPUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + GraphicsOutput = NULL; + + if (Private->CurrentNumberOfGraphicsOutput == 1) { + // + // Find the only one GraphicsOutput. + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) { + GraphicsOutput = Private->TextOutList[Index].GraphicsOutput; + if (GraphicsOutput != NULL) { + break; + } + } + } + + if (GraphicsOutput != NULL) { + // + // If only one physical GOP device exist, return its information. + // + Status = GraphicsOutput->QueryMode (GraphicsOutput, (UINT32) ModeNumber, SizeOfInfo, Info); + return Status; + } else { + // + // If 2 more phyiscal GOP device exist or GOP protocol does not exist, + // return GOP information (PixelFormat is PixelBltOnly) created in ConSplitterAddGraphicsOutputMode (). + // + *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + if (*Info == NULL) { + return EFI_OUT_OF_RESOURCES; + } + *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + CopyMem (*Info, &Private->GraphicsOutputModeBuffer[ModeNumber], *SizeOfInfo); + } + + return EFI_SUCCESS; +} + + +/** + Set the video device into the specified mode and clears the visible portions of + the output display to black. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber Abstraction that defines the current video mode. + + @retval EFI_SUCCESS The graphics mode specified by ModeNumber was selected. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED ModeNumber is not supported by this device. + @retval EFI_OUT_OF_RESOURCES No resource available. + +**/ +EFI_STATUS +EFIAPI +ConSplitterGraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This, + IN UINT32 ModeNumber + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Mode; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_GRAPHICS_OUTPUT_PROTOCOL *PhysicalGraphicsOutput; + UINTN NumberIndex; + UINTN SizeOfInfo; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + + if (ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + Private = GRAPHICS_OUTPUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + Mode = &Private->GraphicsOutputModeBuffer[ModeNumber]; + + ReturnStatus = EFI_SUCCESS; + GraphicsOutput = NULL; + PhysicalGraphicsOutput = NULL; + // + // return the worst status met + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) { + GraphicsOutput = Private->TextOutList[Index].GraphicsOutput; + if (GraphicsOutput != NULL) { + PhysicalGraphicsOutput = GraphicsOutput; + // + // Find corresponding ModeNumber of this GraphicsOutput instance + // + for (NumberIndex = 0; NumberIndex < GraphicsOutput->Mode->MaxMode; NumberIndex ++) { + Status = GraphicsOutput->QueryMode (GraphicsOutput, (UINT32) NumberIndex, &SizeOfInfo, &Info); + if (EFI_ERROR (Status)) { + return Status; + } + if ((Info->HorizontalResolution == Mode->HorizontalResolution) && (Info->VerticalResolution == Mode->VerticalResolution)) { + FreePool (Info); + break; + } + FreePool (Info); + } + + Status = GraphicsOutput->SetMode (GraphicsOutput, (UINT32) NumberIndex); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + UgaDraw = Private->TextOutList[Index].UgaDraw; + if (UgaDraw != NULL) { + Status = UgaDraw->SetMode ( + UgaDraw, + Mode->HorizontalResolution, + Mode->VerticalResolution, + 32, + 60 + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + } + } + + This->Mode->Mode = ModeNumber; + + if ((Private->CurrentNumberOfGraphicsOutput == 1) && (PhysicalGraphicsOutput != NULL)) { + // + // If only one physical GOP device exist, copy physical information to consplitter. + // + CopyMem (This->Mode->Info, PhysicalGraphicsOutput->Mode->Info, PhysicalGraphicsOutput->Mode->SizeOfInfo); + This->Mode->SizeOfInfo = PhysicalGraphicsOutput->Mode->SizeOfInfo; + This->Mode->FrameBufferBase = PhysicalGraphicsOutput->Mode->FrameBufferBase; + This->Mode->FrameBufferSize = PhysicalGraphicsOutput->Mode->FrameBufferSize; + } else { + // + // If 2 more phyiscal GOP device exist or GOP protocol does not exist, + // return GOP information (PixelFormat is PixelBltOnly) created in ConSplitterAddGraphicsOutputMode (). + // + CopyMem (This->Mode->Info, &Private->GraphicsOutputModeBuffer[ModeNumber], This->Mode->SizeOfInfo); + } + + return ReturnStatus; +} + + + +/** + The following table defines actions for BltOperations. + + EfiBltVideoFill - Write data from the BltBuffer pixel (SourceX, SourceY) + directly to every pixel of the video display rectangle + (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + Only one pixel will be used from the BltBuffer. Delta is NOT used. + EfiBltVideoToBltBuffer - Read data from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) and place it in + the BltBuffer rectangle (DestinationX, DestinationY ) + (DestinationX + Width, DestinationY + Height). If DestinationX or + DestinationY is not zero then Delta must be set to the length in bytes + of a row in the BltBuffer. + EfiBltBufferToVideo - Write data from the BltBuffer rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) directly to the + video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). If SourceX or SourceY is + not zero then Delta must be set to the length in bytes of a row in the + BltBuffer. + EfiBltVideoToVideo - Copy from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) . + to the video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + The BltBuffer and Delta are not used in this mode. + + @param This Protocol instance pointer. + @param BltBuffer Buffer containing data to blit into video buffer. + This buffer has a size of + Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + @param BltOperation Operation to perform on BlitBuffer and video + memory + @param SourceX X coordinate of source for the BltBuffer. + @param SourceY Y coordinate of source for the BltBuffer. + @param DestinationX X coordinate of destination for the BltBuffer. + @param DestinationY Y coordinate of destination for the BltBuffer. + @param Width Width of rectangle in BltBuffer in pixels. + @param Height Hight of rectangle in BltBuffer in pixels. + @param Delta OPTIONAL. + + @retval EFI_SUCCESS The Blt operation completed. + @retval EFI_INVALID_PARAMETER BltOperation is not valid. + @retval EFI_DEVICE_ERROR A hardware error occurred writting to the video + buffer. + +**/ +EFI_STATUS +EFIAPI +ConSplitterGraphicsOutputBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + + if (This == NULL || ((UINTN) BltOperation) >= EfiGraphicsOutputBltOperationMax) { + return EFI_INVALID_PARAMETER; + } + + Private = GRAPHICS_OUTPUT_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + ReturnStatus = EFI_SUCCESS; + + // + // return the worst status met + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) { + GraphicsOutput = Private->TextOutList[Index].GraphicsOutput; + if (GraphicsOutput != NULL) { + Status = GraphicsOutput->Blt ( + GraphicsOutput, + BltBuffer, + BltOperation, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } else if (BltOperation == EfiBltVideoToBltBuffer) { + // + // Only need to read the data into buffer one time + // + return EFI_SUCCESS; + } + } + + UgaDraw = Private->TextOutList[Index].UgaDraw; + if (UgaDraw != NULL && FeaturePcdGet (PcdUgaConsumeSupport)) { + Status = UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) BltBuffer, + (EFI_UGA_BLT_OPERATION) BltOperation, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } else if (BltOperation == EfiBltVideoToBltBuffer) { + // + // Only need to read the data into buffer one time + // + return EFI_SUCCESS; + } + } + } + + return ReturnStatus; +} + +/** + Return the current video mode information. + + @param This The EFI_UGA_DRAW_PROTOCOL instance. + @param HorizontalResolution The size of video screen in pixels in the X dimension. + @param VerticalResolution The size of video screen in pixels in the Y dimension. + @param ColorDepth Number of bits per pixel, currently defined to be 32. + @param RefreshRate The refresh rate of the monitor in Hertz. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode () + @retval EFI_INVALID_PARAMETER One of the input args was NULL. + +**/ +EFI_STATUS +EFIAPI +ConSplitterUgaDrawGetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + OUT UINT32 *HorizontalResolution, + OUT UINT32 *VerticalResolution, + OUT UINT32 *ColorDepth, + OUT UINT32 *RefreshRate + ) +{ + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + + if ((HorizontalResolution == NULL) || + (VerticalResolution == NULL) || + (RefreshRate == NULL) || + (ColorDepth == NULL)) { + return EFI_INVALID_PARAMETER; + } + // + // retrieve private data + // + Private = UGA_DRAW_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + *HorizontalResolution = Private->UgaHorizontalResolution; + *VerticalResolution = Private->UgaVerticalResolution; + *ColorDepth = Private->UgaColorDepth; + *RefreshRate = Private->UgaRefreshRate; + + return EFI_SUCCESS; +} + + +/** + Set the current video mode information. + + @param This The EFI_UGA_DRAW_PROTOCOL instance. + @param HorizontalResolution The size of video screen in pixels in the X dimension. + @param VerticalResolution The size of video screen in pixels in the Y dimension. + @param ColorDepth Number of bits per pixel, currently defined to be 32. + @param RefreshRate The refresh rate of the monitor in Hertz. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode () + @retval EFI_OUT_OF_RESOURCES Out of resources. + +**/ +EFI_STATUS +EFIAPI +ConSplitterUgaDrawSetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + IN UINT32 ColorDepth, + IN UINT32 RefreshRate + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + UINTN NumberIndex; + UINTN SizeOfInfo; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + + Private = UGA_DRAW_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + ReturnStatus = EFI_SUCCESS; + + // + // Update the Mode data + // + Private->UgaHorizontalResolution = HorizontalResolution; + Private->UgaVerticalResolution = VerticalResolution; + Private->UgaColorDepth = ColorDepth; + Private->UgaRefreshRate = RefreshRate; + + // + // return the worst status met + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) { + + GraphicsOutput = Private->TextOutList[Index].GraphicsOutput; + if (GraphicsOutput != NULL) { + // + // Find corresponding ModeNumber of this GraphicsOutput instance + // + for (NumberIndex = 0; NumberIndex < GraphicsOutput->Mode->MaxMode; NumberIndex ++) { + Status = GraphicsOutput->QueryMode (GraphicsOutput, (UINT32) NumberIndex, &SizeOfInfo, &Info); + if (EFI_ERROR (Status)) { + return Status; + } + if ((Info->HorizontalResolution == HorizontalResolution) && (Info->VerticalResolution == VerticalResolution)) { + FreePool (Info); + break; + } + FreePool (Info); + } + + Status = GraphicsOutput->SetMode (GraphicsOutput, (UINT32) NumberIndex); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } else if (FeaturePcdGet (PcdUgaConsumeSupport)){ + UgaDraw = Private->TextOutList[Index].UgaDraw; + if (UgaDraw != NULL) { + Status = UgaDraw->SetMode ( + UgaDraw, + HorizontalResolution, + VerticalResolution, + ColorDepth, + RefreshRate + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + } + } + } + + return ReturnStatus; +} + + +/** + Blt a rectangle of pixels on the graphics screen. + + The following table defines actions for BltOperations. + + EfiUgaVideoFill: + Write data from the BltBuffer pixel (SourceX, SourceY) + directly to every pixel of the video display rectangle + (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + Only one pixel will be used from the BltBuffer. Delta is NOT used. + EfiUgaVideoToBltBuffer: + Read data from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) and place it in + the BltBuffer rectangle (DestinationX, DestinationY ) + (DestinationX + Width, DestinationY + Height). If DestinationX or + DestinationY is not zero then Delta must be set to the length in bytes + of a row in the BltBuffer. + EfiUgaBltBufferToVideo: + Write data from the BltBuffer rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) directly to the + video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). If SourceX or SourceY is + not zero then Delta must be set to the length in bytes of a row in the + BltBuffer. + EfiUgaVideoToVideo: + Copy from the video display rectangle + (SourceX, SourceY) (SourceX + Width, SourceY + Height) . + to the video display rectangle (DestinationX, DestinationY) + (DestinationX + Width, DestinationY + Height). + The BltBuffer and Delta are not used in this mode. + + @param This Protocol instance pointer. + @param BltBuffer Buffer containing data to blit into video buffer. This + buffer has a size of Width*Height*sizeof(EFI_UGA_PIXEL) + @param BltOperation Operation to perform on BlitBuffer and video memory + @param SourceX X coordinate of source for the BltBuffer. + @param SourceY Y coordinate of source for the BltBuffer. + @param DestinationX X coordinate of destination for the BltBuffer. + @param DestinationY Y coordinate of destination for the BltBuffer. + @param Width Width of rectangle in BltBuffer in pixels. + @param Height Hight of rectangle in BltBuffer in pixels. + @param Delta OPTIONAL + + @retval EFI_SUCCESS The Blt operation completed. + @retval EFI_INVALID_PARAMETER BltOperation is not valid. + @retval EFI_DEVICE_ERROR A hardware error occurred writting to the video buffer. + +**/ +EFI_STATUS +EFIAPI +ConSplitterUgaDrawBlt ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN EFI_UGA_PIXEL *BltBuffer, OPTIONAL + IN EFI_UGA_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ) +{ + EFI_STATUS Status; + TEXT_OUT_SPLITTER_PRIVATE_DATA *Private; + UINTN Index; + EFI_STATUS ReturnStatus; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + + Private = UGA_DRAW_SPLITTER_PRIVATE_DATA_FROM_THIS (This); + + ReturnStatus = EFI_SUCCESS; + // + // return the worst status met + // + for (Index = 0; Index < Private->CurrentNumberOfConsoles; Index++) { + GraphicsOutput = Private->TextOutList[Index].GraphicsOutput; + if (GraphicsOutput != NULL) { + Status = GraphicsOutput->Blt ( + GraphicsOutput, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) BltBuffer, + (EFI_GRAPHICS_OUTPUT_BLT_OPERATION) BltOperation, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } else if (BltOperation == EfiUgaVideoToBltBuffer) { + // + // Only need to read the data into buffer one time + // + return EFI_SUCCESS; + } + } + + if (Private->TextOutList[Index].UgaDraw != NULL && FeaturePcdGet (PcdUgaConsumeSupport)) { + Status = Private->TextOutList[Index].UgaDraw->Blt ( + Private->TextOutList[Index].UgaDraw, + BltBuffer, + BltOperation, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } else if (BltOperation == EfiUgaVideoToBltBuffer) { + // + // Only need to read the data into buffer one time + // + return EFI_SUCCESS; + } + } + } + + return ReturnStatus; +} + +/** + Sets the output device(s) to a specified mode. + + @param Private Text Out Splitter pointer. + @param ModeNumber The mode number to set. + +**/ +VOID +TextOutSetMode ( + IN TEXT_OUT_SPLITTER_PRIVATE_DATA *Private, + IN UINTN ModeNumber + ) +{ + // + // No need to do extra check here as whether (Column, Row) is valid has + // been checked in ConSplitterTextOutSetCursorPosition. And (0, 0) should + // always be supported. + // + Private->TextOutMode.Mode = (INT32) ModeNumber; + Private->TextOutMode.CursorColumn = 0; + Private->TextOutMode.CursorRow = 0; + Private->TextOutMode.CursorVisible = TRUE; + + return; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/ComponentName.c new file mode 100644 index 000000000..0e6304087 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/ComponentName.c @@ -0,0 +1,176 @@ +/** @file + UEFI Component Name(2) protocol implementation for GraphicsConsole driver. + +Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "GraphicsConsole.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gGraphicsConsoleComponentName = { + GraphicsConsoleComponentNameGetDriverName, + GraphicsConsoleComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gGraphicsConsoleComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GraphicsConsoleComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GraphicsConsoleComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mGraphicsConsoleDriverNameTable[] = { + { + "eng;en", + (CHAR16 *)L"Graphics Console Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mGraphicsConsoleDriverNameTable, + DriverName, + (BOOLEAN)(This == &gGraphicsConsoleComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.c b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.c new file mode 100644 index 000000000..c042451a9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.c @@ -0,0 +1,2136 @@ +/** @file + This is the main routine for initializing the Graphics Console support routines. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "GraphicsConsole.h" + +// +// Graphics Console Device Private Data template +// +GRAPHICS_CONSOLE_DEV mGraphicsConsoleDevTemplate = { + GRAPHICS_CONSOLE_DEV_SIGNATURE, + (EFI_GRAPHICS_OUTPUT_PROTOCOL *) NULL, + (EFI_UGA_DRAW_PROTOCOL *) NULL, + { + GraphicsConsoleConOutReset, + GraphicsConsoleConOutOutputString, + GraphicsConsoleConOutTestString, + GraphicsConsoleConOutQueryMode, + GraphicsConsoleConOutSetMode, + GraphicsConsoleConOutSetAttribute, + GraphicsConsoleConOutClearScreen, + GraphicsConsoleConOutSetCursorPosition, + GraphicsConsoleConOutEnableCursor, + (EFI_SIMPLE_TEXT_OUTPUT_MODE *) NULL + }, + { + 0, + -1, + EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK), + 0, + 0, + TRUE + }, + (GRAPHICS_CONSOLE_MODE_DATA *) NULL, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) NULL +}; + +GRAPHICS_CONSOLE_MODE_DATA mGraphicsConsoleModeData[] = { + {100, 31}, + // + // New modes can be added here. + // The last entry is specific for full screen mode. + // + {0, 0} +}; + +EFI_HII_DATABASE_PROTOCOL *mHiiDatabase; +EFI_HII_FONT_PROTOCOL *mHiiFont; +EFI_HII_HANDLE mHiiHandle; +VOID *mHiiRegistration; + +EFI_GUID mFontPackageListGuid = {0xf5f219d3, 0x7006, 0x4648, {0xac, 0x8d, 0xd6, 0x1d, 0xfb, 0x7b, 0xc6, 0xad}}; + +CHAR16 mCrLfString[3] = { CHAR_CARRIAGE_RETURN, CHAR_LINEFEED, CHAR_NULL }; + +EFI_GRAPHICS_OUTPUT_BLT_PIXEL mGraphicsEfiColors[16] = { + // + // B G R reserved + // + {0x00, 0x00, 0x00, 0x00}, // BLACK + {0x98, 0x00, 0x00, 0x00}, // LIGHTBLUE + {0x00, 0x98, 0x00, 0x00}, // LIGHGREEN + {0x98, 0x98, 0x00, 0x00}, // LIGHCYAN + {0x00, 0x00, 0x98, 0x00}, // LIGHRED + {0x98, 0x00, 0x98, 0x00}, // MAGENTA + {0x00, 0x98, 0x98, 0x00}, // BROWN + {0x98, 0x98, 0x98, 0x00}, // LIGHTGRAY + {0x30, 0x30, 0x30, 0x00}, // DARKGRAY - BRIGHT BLACK + {0xff, 0x00, 0x00, 0x00}, // BLUE + {0x00, 0xff, 0x00, 0x00}, // LIME + {0xff, 0xff, 0x00, 0x00}, // CYAN + {0x00, 0x00, 0xff, 0x00}, // RED + {0xff, 0x00, 0xff, 0x00}, // FUCHSIA + {0x00, 0xff, 0xff, 0x00}, // YELLOW + {0xff, 0xff, 0xff, 0x00} // WHITE +}; + +EFI_NARROW_GLYPH mCursorGlyph = { + 0x0000, + 0x00, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF } +}; + +CHAR16 SpaceStr[] = { NARROW_CHAR, ' ', 0 }; + +EFI_DRIVER_BINDING_PROTOCOL gGraphicsConsoleDriverBinding = { + GraphicsConsoleControllerDriverSupported, + GraphicsConsoleControllerDriverStart, + GraphicsConsoleControllerDriverStop, + 0xa, + NULL, + NULL +}; + +/** + Test to see if Graphics Console could be supported on the Controller. + + Graphics Console could be supported if Graphics Output Protocol or UGA Draw + Protocol exists on the Controller. (UGA Draw Protocol could be skipped + if PcdUgaConsumeSupport is set to FALSE.) + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + GraphicsOutput = NULL; + UgaDraw = NULL; + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &GraphicsOutput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status) && FeaturePcdGet (PcdUgaConsumeSupport)) { + // + // Open Graphics Output Protocol failed, try to open UGA Draw Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + (VOID **) &UgaDraw, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // We need to ensure that we do not layer on top of a virtual handle. + // We need to ensure that the handles produced by the conspliter do not + // get used. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } else { + goto Error; + } + + // + // Does Hii Exist? If not, we aren't ready to run + // + Status = EfiLocateHiiProtocol (); + + // + // Close the I/O Abstraction(s) used to perform the supported test + // +Error: + if (GraphicsOutput != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + gBS->CloseProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + return Status; +} + +/** + Initialize all the text modes which the graphics console supports. + + It returns information for available text modes that the graphics can support. + + @param[in] HorizontalResolution The size of video screen in pixels in the X dimension. + @param[in] VerticalResolution The size of video screen in pixels in the Y dimension. + @param[in] GopModeNumber The graphics mode number which graphis console is based on. + @param[out] TextModeCount The total number of text modes that graphics console supports. + @param[out] TextModeData The buffer to the text modes column and row information. + Caller is responsible to free it when it's non-NULL. + + @retval EFI_SUCCESS The supporting mode information is returned. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +InitializeGraphicsConsoleTextMode ( + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + IN UINT32 GopModeNumber, + OUT UINTN *TextModeCount, + OUT GRAPHICS_CONSOLE_MODE_DATA **TextModeData + ) +{ + UINTN Index; + UINTN Count; + GRAPHICS_CONSOLE_MODE_DATA *ModeBuffer; + GRAPHICS_CONSOLE_MODE_DATA *NewModeBuffer; + UINTN ValidCount; + UINTN ValidIndex; + UINTN MaxColumns; + UINTN MaxRows; + + if ((TextModeCount == NULL) || (TextModeData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Count = sizeof (mGraphicsConsoleModeData) / sizeof (GRAPHICS_CONSOLE_MODE_DATA); + + // + // Compute the maximum number of text Rows and Columns that this current graphics mode can support. + // To make graphics console work well, MaxColumns and MaxRows should not be zero. + // + MaxColumns = HorizontalResolution / EFI_GLYPH_WIDTH; + MaxRows = VerticalResolution / EFI_GLYPH_HEIGHT; + + // + // According to UEFI spec, all output devices support at least 80x25 text mode. + // + ASSERT ((MaxColumns >= 80) && (MaxRows >= 25)); + + // + // Add full screen mode to the last entry. + // + mGraphicsConsoleModeData[Count - 1].Columns = MaxColumns; + mGraphicsConsoleModeData[Count - 1].Rows = MaxRows; + + // + // Get defined mode buffer pointer. + // + ModeBuffer = mGraphicsConsoleModeData; + + // + // Here we make sure that the final mode exposed does not include the duplicated modes, + // and does not include the invalid modes which exceed the max column and row. + // Reserve 2 modes for 80x25, 80x50 of graphics console. + // + NewModeBuffer = AllocateZeroPool (sizeof (GRAPHICS_CONSOLE_MODE_DATA) * (Count + 2)); + ASSERT (NewModeBuffer != NULL); + + // + // Mode 0 and mode 1 is for 80x25, 80x50 according to UEFI spec. + // + ValidCount = 0; + + NewModeBuffer[ValidCount].Columns = 80; + NewModeBuffer[ValidCount].Rows = 25; + NewModeBuffer[ValidCount].GopWidth = HorizontalResolution; + NewModeBuffer[ValidCount].GopHeight = VerticalResolution; + NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber; + NewModeBuffer[ValidCount].DeltaX = (HorizontalResolution - (NewModeBuffer[ValidCount].Columns * EFI_GLYPH_WIDTH)) >> 1; + NewModeBuffer[ValidCount].DeltaY = (VerticalResolution - (NewModeBuffer[ValidCount].Rows * EFI_GLYPH_HEIGHT)) >> 1; + ValidCount++; + + if ((MaxColumns >= 80) && (MaxRows >= 50)) { + NewModeBuffer[ValidCount].Columns = 80; + NewModeBuffer[ValidCount].Rows = 50; + NewModeBuffer[ValidCount].DeltaX = (HorizontalResolution - (80 * EFI_GLYPH_WIDTH)) >> 1; + NewModeBuffer[ValidCount].DeltaY = (VerticalResolution - (50 * EFI_GLYPH_HEIGHT)) >> 1; + } + NewModeBuffer[ValidCount].GopWidth = HorizontalResolution; + NewModeBuffer[ValidCount].GopHeight = VerticalResolution; + NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber; + ValidCount++; + + // + // Start from mode 2 to put the valid mode other than 80x25 and 80x50 in the output mode buffer. + // + for (Index = 0; Index < Count; Index++) { + if ((ModeBuffer[Index].Columns == 0) || (ModeBuffer[Index].Rows == 0) || + (ModeBuffer[Index].Columns > MaxColumns) || (ModeBuffer[Index].Rows > MaxRows)) { + // + // Skip the pre-defined mode which is invalid or exceeds the max column and row. + // + continue; + } + for (ValidIndex = 0; ValidIndex < ValidCount; ValidIndex++) { + if ((ModeBuffer[Index].Columns == NewModeBuffer[ValidIndex].Columns) && + (ModeBuffer[Index].Rows == NewModeBuffer[ValidIndex].Rows)) { + // + // Skip the duplicated mode. + // + break; + } + } + if (ValidIndex == ValidCount) { + NewModeBuffer[ValidCount].Columns = ModeBuffer[Index].Columns; + NewModeBuffer[ValidCount].Rows = ModeBuffer[Index].Rows; + NewModeBuffer[ValidCount].GopWidth = HorizontalResolution; + NewModeBuffer[ValidCount].GopHeight = VerticalResolution; + NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber; + NewModeBuffer[ValidCount].DeltaX = (HorizontalResolution - (NewModeBuffer[ValidCount].Columns * EFI_GLYPH_WIDTH)) >> 1; + NewModeBuffer[ValidCount].DeltaY = (VerticalResolution - (NewModeBuffer[ValidCount].Rows * EFI_GLYPH_HEIGHT)) >> 1; + ValidCount++; + } + } + + DEBUG_CODE ( + for (Index = 0; Index < ValidCount; Index++) { + DEBUG ((EFI_D_INFO, "Graphics - Mode %d, Column = %d, Row = %d\n", + Index, NewModeBuffer[Index].Columns, NewModeBuffer[Index].Rows)); + } + ); + + // + // Return valid mode count and mode information buffer. + // + *TextModeCount = ValidCount; + *TextModeData = NewModeBuffer; + return EFI_SUCCESS; +} + +/** + Start this driver on Controller by opening Graphics Output protocol or + UGA Draw protocol, and installing Simple Text Out protocol on Controller. + (UGA Draw protocol could be skipped if PcdUgaConsumeSupport is set to FALSE.) + + @param This Protocol instance pointer. + @param Controller Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + GRAPHICS_CONSOLE_DEV *Private; + UINT32 HorizontalResolution; + UINT32 VerticalResolution; + UINT32 ColorDepth; + UINT32 RefreshRate; + UINT32 ModeIndex; + UINTN MaxMode; + UINT32 ModeNumber; + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode; + UINTN SizeOfInfo; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + INT32 PreferMode; + INT32 Index; + UINTN Column; + UINTN Row; + UINTN DefaultColumn; + UINTN DefaultRow; + + ModeNumber = 0; + + // + // Initialize the Graphics Console device instance + // + Private = AllocateCopyPool ( + sizeof (GRAPHICS_CONSOLE_DEV), + &mGraphicsConsoleDevTemplate + ); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->SimpleTextOutput.Mode = &(Private->SimpleTextOutputMode); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &Private->GraphicsOutput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR(Status) && FeaturePcdGet (PcdUgaConsumeSupport)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + (VOID **) &Private->UgaDraw, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + } + + if (EFI_ERROR (Status)) { + goto Error; + } + + HorizontalResolution = PcdGet32 (PcdVideoHorizontalResolution); + VerticalResolution = PcdGet32 (PcdVideoVerticalResolution); + + if (Private->GraphicsOutput != NULL) { + // + // The console is build on top of Graphics Output Protocol, find the mode number + // for the user-defined mode; if there are multiple video devices, + // graphic console driver will set all the video devices to the same mode. + // + if ((HorizontalResolution == 0x0) || (VerticalResolution == 0x0)) { + // + // Find the highest resolution which GOP supports. + // + MaxMode = Private->GraphicsOutput->Mode->MaxMode; + + for (ModeIndex = 0; ModeIndex < MaxMode; ModeIndex++) { + Status = Private->GraphicsOutput->QueryMode ( + Private->GraphicsOutput, + ModeIndex, + &SizeOfInfo, + &Info + ); + if (!EFI_ERROR (Status)) { + if ((Info->HorizontalResolution > HorizontalResolution) || + ((Info->HorizontalResolution == HorizontalResolution) && (Info->VerticalResolution > VerticalResolution))) { + HorizontalResolution = Info->HorizontalResolution; + VerticalResolution = Info->VerticalResolution; + ModeNumber = ModeIndex; + } + FreePool (Info); + } + } + if ((HorizontalResolution == 0x0) || (VerticalResolution == 0x0)) { + Status = EFI_UNSUPPORTED; + goto Error; + } + } else { + // + // Use user-defined resolution + // + Status = CheckModeSupported ( + Private->GraphicsOutput, + HorizontalResolution, + VerticalResolution, + &ModeNumber + ); + if (EFI_ERROR (Status)) { + // + // if not supporting current mode, try 800x600 which is required by UEFI/EFI spec + // + HorizontalResolution = 800; + VerticalResolution = 600; + Status = CheckModeSupported ( + Private->GraphicsOutput, + HorizontalResolution, + VerticalResolution, + &ModeNumber + ); + Mode = Private->GraphicsOutput->Mode; + if (EFI_ERROR (Status) && Mode->MaxMode != 0) { + // + // Set default mode failed or device don't support default mode, then get the current mode information + // + HorizontalResolution = Mode->Info->HorizontalResolution; + VerticalResolution = Mode->Info->VerticalResolution; + ModeNumber = Mode->Mode; + } + } + } + if (ModeNumber != Private->GraphicsOutput->Mode->Mode) { + // + // Current graphics mode is not set or is not set to the mode which we has found, + // set the new graphic mode. + // + Status = Private->GraphicsOutput->SetMode (Private->GraphicsOutput, ModeNumber); + if (EFI_ERROR (Status)) { + // + // The mode set operation failed + // + goto Error; + } + } + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + // + // At first try to set user-defined resolution + // + ColorDepth = 32; + RefreshRate = 60; + Status = Private->UgaDraw->SetMode ( + Private->UgaDraw, + HorizontalResolution, + VerticalResolution, + ColorDepth, + RefreshRate + ); + if (EFI_ERROR (Status)) { + // + // Try to set 800*600 which is required by UEFI/EFI spec + // + Status = Private->UgaDraw->SetMode ( + Private->UgaDraw, + 800, + 600, + ColorDepth, + RefreshRate + ); + if (EFI_ERROR (Status)) { + Status = Private->UgaDraw->GetMode ( + Private->UgaDraw, + &HorizontalResolution, + &VerticalResolution, + &ColorDepth, + &RefreshRate + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } + } + } + + DEBUG ((EFI_D_INFO, "GraphicsConsole video resolution %d x %d\n", HorizontalResolution, VerticalResolution)); + + // + // Initialize the mode which GraphicsConsole supports. + // + Status = InitializeGraphicsConsoleTextMode ( + HorizontalResolution, + VerticalResolution, + ModeNumber, + &MaxMode, + &Private->ModeData + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Update the maximum number of modes + // + Private->SimpleTextOutputMode.MaxMode = (INT32) MaxMode; + + // + // Initialize the Mode of graphics console devices + // + PreferMode = -1; + DefaultColumn = PcdGet32 (PcdConOutColumn); + DefaultRow = PcdGet32 (PcdConOutRow); + Column = 0; + Row = 0; + for (Index = 0; Index < (INT32)MaxMode; Index++) { + if (DefaultColumn != 0 && DefaultRow != 0) { + if ((Private->ModeData[Index].Columns == DefaultColumn) && + (Private->ModeData[Index].Rows == DefaultRow)) { + PreferMode = Index; + break; + } + } else { + if ((Private->ModeData[Index].Columns > Column) && + (Private->ModeData[Index].Rows > Row)) { + Column = Private->ModeData[Index].Columns; + Row = Private->ModeData[Index].Rows; + PreferMode = Index; + } + } + } + Private->SimpleTextOutput.Mode->Mode = (INT32)PreferMode; + DEBUG ((DEBUG_INFO, "Graphics Console Started, Mode: %d\n", PreferMode)); + + // + // Install protocol interfaces for the Graphics Console device. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiSimpleTextOutProtocolGuid, + &Private->SimpleTextOutput, + NULL + ); + +Error: + if (EFI_ERROR (Status)) { + // + // Close the GOP and UGA Draw Protocol + // + if (Private->GraphicsOutput != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + gBS->CloseProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + if (Private->LineBuffer != NULL) { + FreePool (Private->LineBuffer); + } + + if (Private->ModeData != NULL) { + FreePool (Private->ModeData); + } + + // + // Free private data + // + FreePool (Private); + } + + return Status; +} + +/** + Stop this driver on Controller by removing Simple Text Out protocol + and closing the Graphics Output Protocol or UGA Draw protocol on Controller. + (UGA Draw protocol could be skipped if PcdUgaConsumeSupport is set to FALSE.) + + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_NOT_STARTED Simple Text Out protocol could not be found the + Controller. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput; + GRAPHICS_CONSOLE_DEV *Private; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &SimpleTextOutput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_STARTED; + } + + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (SimpleTextOutput); + + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiSimpleTextOutProtocolGuid, + &Private->SimpleTextOutput + ); + + if (!EFI_ERROR (Status)) { + // + // Close the GOP or UGA IO Protocol + // + if (Private->GraphicsOutput != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + gBS->CloseProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + if (Private->LineBuffer != NULL) { + FreePool (Private->LineBuffer); + } + + if (Private->ModeData != NULL) { + FreePool (Private->ModeData); + } + + // + // Free our instance data + // + FreePool (Private); + } + + return Status; +} + +/** + Check if the current specific mode supported the user defined resolution + for the Graphics Console device based on Graphics Output Protocol. + + If yes, set the graphic devcice's current mode to this specific mode. + + @param GraphicsOutput Graphics Output Protocol instance pointer. + @param HorizontalResolution User defined horizontal resolution + @param VerticalResolution User defined vertical resolution. + @param CurrentModeNumber Current specific mode to be check. + + @retval EFI_SUCCESS The mode is supported. + @retval EFI_UNSUPPORTED The specific mode is out of range of graphics + device supported. + @retval other The specific mode does not support user defined + resolution or failed to set the current mode to the + specific mode on graphics device. + +**/ +EFI_STATUS +CheckModeSupported ( + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput, + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + OUT UINT32 *CurrentModeNumber + ) +{ + UINT32 ModeNumber; + EFI_STATUS Status; + UINTN SizeOfInfo; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + UINT32 MaxMode; + + Status = EFI_SUCCESS; + MaxMode = GraphicsOutput->Mode->MaxMode; + + for (ModeNumber = 0; ModeNumber < MaxMode; ModeNumber++) { + Status = GraphicsOutput->QueryMode ( + GraphicsOutput, + ModeNumber, + &SizeOfInfo, + &Info + ); + if (!EFI_ERROR (Status)) { + if ((Info->HorizontalResolution == HorizontalResolution) && + (Info->VerticalResolution == VerticalResolution)) { + if ((GraphicsOutput->Mode->Info->HorizontalResolution == HorizontalResolution) && + (GraphicsOutput->Mode->Info->VerticalResolution == VerticalResolution)) { + // + // If video device has been set to this mode, we do not need to SetMode again + // + FreePool (Info); + break; + } else { + Status = GraphicsOutput->SetMode (GraphicsOutput, ModeNumber); + if (!EFI_ERROR (Status)) { + FreePool (Info); + break; + } + } + } + FreePool (Info); + } + } + + if (ModeNumber == GraphicsOutput->Mode->MaxMode) { + Status = EFI_UNSUPPORTED; + } + + *CurrentModeNumber = ModeNumber; + return Status; +} + + +/** + Locate HII Database protocol and HII Font protocol. + + @retval EFI_SUCCESS HII Database protocol and HII Font protocol + are located successfully. + @return other Failed to locate HII Database protocol or + HII Font protocol. + +**/ +EFI_STATUS +EfiLocateHiiProtocol ( + VOID + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **) &mHiiDatabase); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->LocateProtocol (&gEfiHiiFontProtocolGuid, NULL, (VOID **) &mHiiFont); + return Status; +} + +// +// Body of the STO functions +// + +/** + Reset the text output device hardware and optionally run diagnostics. + + Implements SIMPLE_TEXT_OUTPUT.Reset(). + If ExtendeVerification is TRUE, then perform dependent Graphics Console + device reset, and set display mode to mode 0. + If ExtendedVerification is FALSE, only set display mode to mode 0. + + @param This Protocol instance pointer. + @param ExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS The text output device was reset. + @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and + could not be reset. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + Status = This->SetMode (This, 0); + if (EFI_ERROR (Status)) { + return Status; + } + Status = This->SetAttribute (This, EFI_TEXT_ATTR (This->Mode->Attribute & 0x0F, EFI_BACKGROUND_BLACK)); + return Status; +} + + +/** + Write a Unicode string to the output device. + + Implements SIMPLE_TEXT_OUTPUT.OutputString(). + The Unicode string will be converted to Glyphs and will be + sent to the Graphics Console. + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be displayed + on the output device(s). All output devices must + also support the Unicode drawing defined in this file. + + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + GRAPHICS_CONSOLE_DEV *Private; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + INTN Mode; + UINTN MaxColumn; + UINTN MaxRow; + UINTN Width; + UINTN Height; + UINTN Delta; + EFI_STATUS Status; + BOOLEAN Warning; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Foreground; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background; + UINTN DeltaX; + UINTN DeltaY; + UINTN Count; + UINTN Index; + INT32 OriginAttribute; + EFI_TPL OldTpl; + + if (This->Mode->Mode == -1) { + // + // If current mode is not valid, return error. + // + return EFI_UNSUPPORTED; + } + + Status = EFI_SUCCESS; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + // + // Current mode + // + Mode = This->Mode->Mode; + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This); + GraphicsOutput = Private->GraphicsOutput; + UgaDraw = Private->UgaDraw; + + MaxColumn = Private->ModeData[Mode].Columns; + MaxRow = Private->ModeData[Mode].Rows; + DeltaX = (UINTN) Private->ModeData[Mode].DeltaX; + DeltaY = (UINTN) Private->ModeData[Mode].DeltaY; + Width = MaxColumn * EFI_GLYPH_WIDTH; + Height = (MaxRow - 1) * EFI_GLYPH_HEIGHT; + Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + + // + // The Attributes won't change when during the time OutputString is called + // + GetTextColors (This, &Foreground, &Background); + + FlushCursor (This); + + Warning = FALSE; + + // + // Backup attribute + // + OriginAttribute = This->Mode->Attribute; + + while (*WString != L'\0') { + + if (*WString == CHAR_BACKSPACE) { + // + // If the cursor is at the left edge of the display, then move the cursor + // one row up. + // + if (This->Mode->CursorColumn == 0 && This->Mode->CursorRow > 0) { + This->Mode->CursorRow--; + This->Mode->CursorColumn = (INT32) (MaxColumn - 1); + This->OutputString (This, SpaceStr); + FlushCursor (This); + This->Mode->CursorRow--; + This->Mode->CursorColumn = (INT32) (MaxColumn - 1); + } else if (This->Mode->CursorColumn > 0) { + // + // If the cursor is not at the left edge of the display, then move the cursor + // left one column. + // + This->Mode->CursorColumn--; + This->OutputString (This, SpaceStr); + FlushCursor (This); + This->Mode->CursorColumn--; + } + + WString++; + + } else if (*WString == CHAR_LINEFEED) { + // + // If the cursor is at the bottom of the display, then scroll the display one + // row, and do not update the cursor position. Otherwise, move the cursor + // down one row. + // + if (This->Mode->CursorRow == (INT32) (MaxRow - 1)) { + if (GraphicsOutput != NULL) { + // + // Scroll Screen Up One Row + // + GraphicsOutput->Blt ( + GraphicsOutput, + NULL, + EfiBltVideoToVideo, + DeltaX, + DeltaY + EFI_GLYPH_HEIGHT, + DeltaX, + DeltaY, + Width, + Height, + Delta + ); + + // + // Print Blank Line at last line + // + GraphicsOutput->Blt ( + GraphicsOutput, + &Background, + EfiBltVideoFill, + 0, + 0, + DeltaX, + DeltaY + Height, + Width, + EFI_GLYPH_HEIGHT, + Delta + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + // + // Scroll Screen Up One Row + // + UgaDraw->Blt ( + UgaDraw, + NULL, + EfiUgaVideoToVideo, + DeltaX, + DeltaY + EFI_GLYPH_HEIGHT, + DeltaX, + DeltaY, + Width, + Height, + Delta + ); + + // + // Print Blank Line at last line + // + UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) (UINTN) &Background, + EfiUgaVideoFill, + 0, + 0, + DeltaX, + DeltaY + Height, + Width, + EFI_GLYPH_HEIGHT, + Delta + ); + } + } else { + This->Mode->CursorRow++; + } + + WString++; + + } else if (*WString == CHAR_CARRIAGE_RETURN) { + // + // Move the cursor to the beginning of the current row. + // + This->Mode->CursorColumn = 0; + WString++; + + } else if (*WString == WIDE_CHAR) { + + This->Mode->Attribute |= EFI_WIDE_ATTRIBUTE; + WString++; + + } else if (*WString == NARROW_CHAR) { + + This->Mode->Attribute &= (~ (UINT32) EFI_WIDE_ATTRIBUTE); + WString++; + + } else { + // + // Print the character at the current cursor position and move the cursor + // right one column. If this moves the cursor past the right edge of the + // display, then the line should wrap to the beginning of the next line. This + // is equivalent to inserting a CR and an LF. Note that if the cursor is at the + // bottom of the display, and the line wraps, then the display will be scrolled + // one line. + // If wide char is going to be displayed, need to display one character at a time + // Or, need to know the display length of a certain string. + // + // Index is used to determine how many character width units (wide = 2, narrow = 1) + // Count is used to determine how many characters are used regardless of their attributes + // + for (Count = 0, Index = 0; (This->Mode->CursorColumn + Index) < MaxColumn; Count++, Index++) { + if (WString[Count] == CHAR_NULL || + WString[Count] == CHAR_BACKSPACE || + WString[Count] == CHAR_LINEFEED || + WString[Count] == CHAR_CARRIAGE_RETURN || + WString[Count] == WIDE_CHAR || + WString[Count] == NARROW_CHAR) { + break; + } + // + // Is the wide attribute on? + // + if ((This->Mode->Attribute & EFI_WIDE_ATTRIBUTE) != 0) { + // + // If wide, add one more width unit than normal since we are going to increment at the end of the for loop + // + Index++; + // + // This is the end-case where if we are at column 79 and about to print a wide character + // We should prevent this from happening because we will wrap inappropriately. We should + // not print this character until the next line. + // + if ((This->Mode->CursorColumn + Index + 1) > MaxColumn) { + Index++; + break; + } + } + } + + Status = DrawUnicodeWeightAtCursorN (This, WString, Count); + if (EFI_ERROR (Status)) { + Warning = TRUE; + } + // + // At the end of line, output carriage return and line feed + // + WString += Count; + This->Mode->CursorColumn += (INT32) Index; + if (This->Mode->CursorColumn > (INT32) MaxColumn) { + This->Mode->CursorColumn -= 2; + This->OutputString (This, SpaceStr); + } + + if (This->Mode->CursorColumn >= (INT32) MaxColumn) { + FlushCursor (This); + This->OutputString (This, mCrLfString); + FlushCursor (This); + } + } + } + + This->Mode->Attribute = OriginAttribute; + + FlushCursor (This); + + if (Warning) { + Status = EFI_WARN_UNKNOWN_GLYPH; + } + + gBS->RestoreTPL (OldTpl); + return Status; + +} + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + Implements SIMPLE_TEXT_OUTPUT.TestString(). + If one of the characters in the *Wstring is neither valid valid Unicode + drawing characters, not ASCII code, then this function will return + EFI_UNSUPPORTED + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be examined for the output + device(s). + + @retval EFI_SUCCESS The device(s) are capable of rendering the output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be + rendered by one or more of the output devices mapped + by the EFI handle. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + EFI_STATUS Status; + UINT16 Count; + + EFI_IMAGE_OUTPUT *Blt; + + Blt = NULL; + Count = 0; + + while (WString[Count] != 0) { + Status = mHiiFont->GetGlyph ( + mHiiFont, + WString[Count], + NULL, + &Blt, + NULL + ); + if (Blt != NULL) { + FreePool (Blt); + Blt = NULL; + } + Count++; + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + + +/** + Returns information for an available text mode that the output device(s) + supports + + Implements SIMPLE_TEXT_OUTPUT.QueryMode(). + It returnes information for an available text mode that the Graphics Console supports. + In this driver,we only support text mode 80x25, which is defined as mode 0. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to return information on. + @param Columns The returned columns of the requested mode. + @param Rows The returned rows of the requested mode. + + @retval EFI_SUCCESS The requested mode information is returned. + @retval EFI_UNSUPPORTED The mode number is not valid. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ) +{ + GRAPHICS_CONSOLE_DEV *Private; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (ModeNumber >= (UINTN) This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + Status = EFI_SUCCESS; + + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This); + + *Columns = Private->ModeData[ModeNumber].Columns; + *Rows = Private->ModeData[ModeNumber].Rows; + + if (*Columns <= 0 || *Rows <= 0) { + Status = EFI_UNSUPPORTED; + goto Done; + + } + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Sets the output device(s) to a specified mode. + + Implements SIMPLE_TEXT_OUTPUT.SetMode(). + Set the Graphics Console to a specified mode. In this driver, we only support mode 0. + + @param This Protocol instance pointer. + @param ModeNumber The text mode to set. + + @retval EFI_SUCCESS The requested text mode is set. + @retval EFI_DEVICE_ERROR The requested text mode cannot be set because of + Graphics Console device error. + @retval EFI_UNSUPPORTED The text mode number is not valid. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ) +{ + EFI_STATUS Status; + GRAPHICS_CONSOLE_DEV *Private; + GRAPHICS_CONSOLE_MODE_DATA *ModeData; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *NewLineBuffer; + UINT32 HorizontalResolution; + UINT32 VerticalResolution; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + UINT32 ColorDepth; + UINT32 RefreshRate; + EFI_TPL OldTpl; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This); + GraphicsOutput = Private->GraphicsOutput; + UgaDraw = Private->UgaDraw; + + // + // Make sure the requested mode number is supported + // + if (ModeNumber >= (UINTN) This->Mode->MaxMode) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + ModeData = &(Private->ModeData[ModeNumber]); + + if (ModeData->Columns <= 0 && ModeData->Rows <= 0) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + // + // If the mode has been set at least one other time, then LineBuffer will not be NULL + // + if (Private->LineBuffer != NULL) { + // + // If the new mode is the same as the old mode, then just return EFI_SUCCESS + // + if ((INT32) ModeNumber == This->Mode->Mode) { + // + // Clear the current text window on the current graphics console + // + This->ClearScreen (This); + Status = EFI_SUCCESS; + goto Done; + } + // + // Otherwise, the size of the text console and/or the GOP/UGA mode will be changed, + // so erase the cursor, and free the LineBuffer for the current mode + // + FlushCursor (This); + + FreePool (Private->LineBuffer); + } + + // + // Attempt to allocate a line buffer for the requested mode number + // + NewLineBuffer = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * ModeData->Columns * EFI_GLYPH_WIDTH * EFI_GLYPH_HEIGHT); + + if (NewLineBuffer == NULL) { + // + // The new line buffer could not be allocated, so return an error. + // No changes to the state of the current console have been made, so the current console is still valid + // + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Assign the current line buffer to the newly allocated line buffer + // + Private->LineBuffer = NewLineBuffer; + + if (GraphicsOutput != NULL) { + if (ModeData->GopModeNumber != GraphicsOutput->Mode->Mode) { + // + // Either no graphics mode is currently set, or it is set to the wrong resolution, so set the new graphics mode + // + Status = GraphicsOutput->SetMode (GraphicsOutput, ModeData->GopModeNumber); + if (EFI_ERROR (Status)) { + // + // The mode set operation failed + // + goto Done; + } + } else { + // + // The current graphics mode is correct, so simply clear the entire display + // + Status = GraphicsOutput->Blt ( + GraphicsOutput, + &mGraphicsEfiColors[0], + EfiBltVideoFill, + 0, + 0, + 0, + 0, + ModeData->GopWidth, + ModeData->GopHeight, + 0 + ); + } + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + // + // Get the current UGA Draw mode information + // + Status = UgaDraw->GetMode ( + UgaDraw, + &HorizontalResolution, + &VerticalResolution, + &ColorDepth, + &RefreshRate + ); + if (EFI_ERROR (Status) || HorizontalResolution != ModeData->GopWidth || VerticalResolution != ModeData->GopHeight) { + // + // Either no graphics mode is currently set, or it is set to the wrong resolution, so set the new graphics mode + // + Status = UgaDraw->SetMode ( + UgaDraw, + ModeData->GopWidth, + ModeData->GopHeight, + 32, + 60 + ); + if (EFI_ERROR (Status)) { + // + // The mode set operation failed + // + goto Done; + } + } else { + // + // The current graphics mode is correct, so simply clear the entire display + // + Status = UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) (UINTN) &mGraphicsEfiColors[0], + EfiUgaVideoFill, + 0, + 0, + 0, + 0, + ModeData->GopWidth, + ModeData->GopHeight, + 0 + ); + } + } + + // + // The new mode is valid, so commit the mode change + // + This->Mode->Mode = (INT32) ModeNumber; + + // + // Move the text cursor to the upper left hand corner of the display and flush it + // + This->Mode->CursorColumn = 0; + This->Mode->CursorRow = 0; + + FlushCursor (This); + + Status = EFI_SUCCESS; + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + Implements SIMPLE_TEXT_OUTPUT.SetAttribute(). + + @param This Protocol instance pointer. + @param Attribute The attribute to set. Bits 0..3 are the foreground + color, and bits 4..6 are the background color. + All other bits are undefined and must be zero. + + @retval EFI_SUCCESS The requested attribute is set. + @retval EFI_DEVICE_ERROR The requested attribute cannot be set due to Graphics Console port error. + @retval EFI_UNSUPPORTED The attribute requested is not defined. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ) +{ + EFI_TPL OldTpl; + + if ((Attribute | 0x7F) != 0x7F) { + return EFI_UNSUPPORTED; + } + + if ((INT32) Attribute == This->Mode->Attribute) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + FlushCursor (This); + + This->Mode->Attribute = (INT32) Attribute; + + FlushCursor (This); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Clears the output device(s) display to the currently selected background + color. + + Implements SIMPLE_TEXT_OUTPUT.ClearScreen(). + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ) +{ + EFI_STATUS Status; + GRAPHICS_CONSOLE_DEV *Private; + GRAPHICS_CONSOLE_MODE_DATA *ModeData; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Foreground; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background; + EFI_TPL OldTpl; + + if (This->Mode->Mode == -1) { + // + // If current mode is not valid, return error. + // + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This); + GraphicsOutput = Private->GraphicsOutput; + UgaDraw = Private->UgaDraw; + ModeData = &(Private->ModeData[This->Mode->Mode]); + + GetTextColors (This, &Foreground, &Background); + if (GraphicsOutput != NULL) { + Status = GraphicsOutput->Blt ( + GraphicsOutput, + &Background, + EfiBltVideoFill, + 0, + 0, + 0, + 0, + ModeData->GopWidth, + ModeData->GopHeight, + 0 + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + Status = UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) (UINTN) &Background, + EfiUgaVideoFill, + 0, + 0, + 0, + 0, + ModeData->GopWidth, + ModeData->GopHeight, + 0 + ); + } else { + Status = EFI_UNSUPPORTED; + } + + This->Mode->CursorColumn = 0; + This->Mode->CursorRow = 0; + + FlushCursor (This); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Sets the current coordinates of the cursor position. + + Implements SIMPLE_TEXT_OUTPUT.SetCursorPosition(). + + @param This Protocol instance pointer. + @param Column The position to set the cursor to. Must be greater than or + equal to zero and less than the number of columns and rows + by QueryMode (). + @param Row The position to set the cursor to. Must be greater than or + equal to zero and less than the number of columns and rows + by QueryMode (). + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the + cursor position is invalid for the current mode. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ) +{ + GRAPHICS_CONSOLE_DEV *Private; + GRAPHICS_CONSOLE_MODE_DATA *ModeData; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This->Mode->Mode == -1) { + // + // If current mode is not valid, return error. + // + return EFI_UNSUPPORTED; + } + + Status = EFI_SUCCESS; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This); + ModeData = &(Private->ModeData[This->Mode->Mode]); + + if ((Column >= ModeData->Columns) || (Row >= ModeData->Rows)) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + if ((This->Mode->CursorColumn == (INT32) Column) && (This->Mode->CursorRow == (INT32) Row)) { + Status = EFI_SUCCESS; + goto Done; + } + + FlushCursor (This); + + This->Mode->CursorColumn = (INT32) Column; + This->Mode->CursorRow = (INT32) Row; + + FlushCursor (This); + +Done: + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Makes the cursor visible or invisible. + + Implements SIMPLE_TEXT_OUTPUT.EnableCursor(). + + @param This Protocol instance pointer. + @param Visible If TRUE, the cursor is set to be visible, If FALSE, + the cursor is set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ) +{ + EFI_TPL OldTpl; + + if (This->Mode->Mode == -1) { + // + // If current mode is not valid, return error. + // + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + FlushCursor (This); + + This->Mode->CursorVisible = Visible; + + FlushCursor (This); + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; +} + +/** + Gets Graphics Console devcie's foreground color and background color. + + @param This Protocol instance pointer. + @param Foreground Returned text foreground color. + @param Background Returned text background color. + + @retval EFI_SUCCESS It returned always. + +**/ +EFI_STATUS +GetTextColors ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Foreground, + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Background + ) +{ + INTN Attribute; + + Attribute = This->Mode->Attribute & 0x7F; + + *Foreground = mGraphicsEfiColors[Attribute & 0x0f]; + *Background = mGraphicsEfiColors[Attribute >> 4]; + + return EFI_SUCCESS; +} + +/** + Draw Unicode string on the Graphics Console device's screen. + + @param This Protocol instance pointer. + @param UnicodeWeight One Unicode string to be displayed. + @param Count The count of Unicode string. + + @retval EFI_OUT_OF_RESOURCES If no memory resource to use. + @retval EFI_UNSUPPORTED If no Graphics Output protocol and UGA Draw + protocol exist. + @retval EFI_SUCCESS Drawing Unicode string implemented successfully. + +**/ +EFI_STATUS +DrawUnicodeWeightAtCursorN ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *UnicodeWeight, + IN UINTN Count + ) +{ + EFI_STATUS Status; + GRAPHICS_CONSOLE_DEV *Private; + EFI_IMAGE_OUTPUT *Blt; + EFI_STRING String; + EFI_FONT_DISPLAY_INFO *FontInfo; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_HII_ROW_INFO *RowInfoArray; + UINTN RowInfoArraySize; + + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This); + Blt = (EFI_IMAGE_OUTPUT *) AllocateZeroPool (sizeof (EFI_IMAGE_OUTPUT)); + if (Blt == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Blt->Width = (UINT16) (Private->ModeData[This->Mode->Mode].GopWidth); + Blt->Height = (UINT16) (Private->ModeData[This->Mode->Mode].GopHeight); + + String = AllocateCopyPool ((Count + 1) * sizeof (CHAR16), UnicodeWeight); + if (String == NULL) { + FreePool (Blt); + return EFI_OUT_OF_RESOURCES; + } + // + // Set the end character + // + *(String + Count) = L'\0'; + + FontInfo = (EFI_FONT_DISPLAY_INFO *) AllocateZeroPool (sizeof (EFI_FONT_DISPLAY_INFO)); + if (FontInfo == NULL) { + FreePool (Blt); + FreePool (String); + return EFI_OUT_OF_RESOURCES; + } + // + // Get current foreground and background colors. + // + GetTextColors (This, &FontInfo->ForegroundColor, &FontInfo->BackgroundColor); + + if (Private->GraphicsOutput != NULL) { + // + // If Graphics Output protocol exists, using HII Font protocol to draw. + // + Blt->Image.Screen = Private->GraphicsOutput; + + Status = mHiiFont->StringToImage ( + mHiiFont, + EFI_HII_IGNORE_IF_NO_GLYPH | EFI_HII_DIRECT_TO_SCREEN | EFI_HII_IGNORE_LINE_BREAK, + String, + FontInfo, + &Blt, + This->Mode->CursorColumn * EFI_GLYPH_WIDTH + Private->ModeData[This->Mode->Mode].DeltaX, + This->Mode->CursorRow * EFI_GLYPH_HEIGHT + Private->ModeData[This->Mode->Mode].DeltaY, + NULL, + NULL, + NULL + ); + + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + // + // If Graphics Output protocol cannot be found and PcdUgaConsumeSupport enabled, + // using UGA Draw protocol to draw. + // + ASSERT (Private->UgaDraw!= NULL); + + UgaDraw = Private->UgaDraw; + + Blt->Image.Bitmap = AllocateZeroPool (Blt->Width * Blt->Height * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + if (Blt->Image.Bitmap == NULL) { + FreePool (Blt); + FreePool (String); + return EFI_OUT_OF_RESOURCES; + } + + RowInfoArray = NULL; + // + // StringToImage only support blt'ing image to device using GOP protocol. If GOP is not supported in this platform, + // we ask StringToImage to print the string to blt buffer, then blt to device using UgaDraw. + // + Status = mHiiFont->StringToImage ( + mHiiFont, + EFI_HII_IGNORE_IF_NO_GLYPH | EFI_HII_IGNORE_LINE_BREAK, + String, + FontInfo, + &Blt, + This->Mode->CursorColumn * EFI_GLYPH_WIDTH + Private->ModeData[This->Mode->Mode].DeltaX, + This->Mode->CursorRow * EFI_GLYPH_HEIGHT + Private->ModeData[This->Mode->Mode].DeltaY, + &RowInfoArray, + &RowInfoArraySize, + NULL + ); + + if (!EFI_ERROR (Status)) { + // + // Line breaks are handled by caller of DrawUnicodeWeightAtCursorN, so the updated parameter RowInfoArraySize by StringToImage will + // always be 1 or 0 (if there is no valid Unicode Char can be printed). ASSERT here to make sure. + // + ASSERT (RowInfoArraySize <= 1); + + Status = UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) Blt->Image.Bitmap, + EfiUgaBltBufferToVideo, + This->Mode->CursorColumn * EFI_GLYPH_WIDTH + Private->ModeData[This->Mode->Mode].DeltaX, + (This->Mode->CursorRow) * EFI_GLYPH_HEIGHT + Private->ModeData[This->Mode->Mode].DeltaY, + This->Mode->CursorColumn * EFI_GLYPH_WIDTH + Private->ModeData[This->Mode->Mode].DeltaX, + (This->Mode->CursorRow) * EFI_GLYPH_HEIGHT + Private->ModeData[This->Mode->Mode].DeltaY, + RowInfoArray[0].LineWidth, + RowInfoArray[0].LineHeight, + Blt->Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + ); + } + + FreePool (RowInfoArray); + FreePool (Blt->Image.Bitmap); + } else { + Status = EFI_UNSUPPORTED; + } + + if (Blt != NULL) { + FreePool (Blt); + } + if (String != NULL) { + FreePool (String); + } + if (FontInfo != NULL) { + FreePool (FontInfo); + } + return Status; +} + +/** + Flush the cursor on the screen. + + If CursorVisible is FALSE, nothing to do and return directly. + If CursorVisible is TRUE, + i) If the cursor shows on screen, it will be erased. + ii) If the cursor does not show on screen, it will be shown. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The cursor is erased successfully. + +**/ +EFI_STATUS +FlushCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ) +{ + GRAPHICS_CONSOLE_DEV *Private; + EFI_SIMPLE_TEXT_OUTPUT_MODE *CurrentMode; + INTN GlyphX; + INTN GlyphY; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Foreground; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Background; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION BltChar[EFI_GLYPH_HEIGHT][EFI_GLYPH_WIDTH]; + UINTN PosX; + UINTN PosY; + + CurrentMode = This->Mode; + + if (!CurrentMode->CursorVisible) { + return EFI_SUCCESS; + } + + Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This); + GraphicsOutput = Private->GraphicsOutput; + UgaDraw = Private->UgaDraw; + + // + // In this driver, only narrow character was supported. + // + // + // Blt a character to the screen + // + GlyphX = (CurrentMode->CursorColumn * EFI_GLYPH_WIDTH) + Private->ModeData[CurrentMode->Mode].DeltaX; + GlyphY = (CurrentMode->CursorRow * EFI_GLYPH_HEIGHT) + Private->ModeData[CurrentMode->Mode].DeltaY; + if (GraphicsOutput != NULL) { + GraphicsOutput->Blt ( + GraphicsOutput, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) BltChar, + EfiBltVideoToBltBuffer, + GlyphX, + GlyphY, + 0, + 0, + EFI_GLYPH_WIDTH, + EFI_GLYPH_HEIGHT, + EFI_GLYPH_WIDTH * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) (UINTN) BltChar, + EfiUgaVideoToBltBuffer, + GlyphX, + GlyphY, + 0, + 0, + EFI_GLYPH_WIDTH, + EFI_GLYPH_HEIGHT, + EFI_GLYPH_WIDTH * sizeof (EFI_UGA_PIXEL) + ); + } + + GetTextColors (This, &Foreground.Pixel, &Background.Pixel); + + // + // Convert Monochrome bitmap of the Glyph to BltBuffer structure + // + for (PosY = 0; PosY < EFI_GLYPH_HEIGHT; PosY++) { + for (PosX = 0; PosX < EFI_GLYPH_WIDTH; PosX++) { + if ((mCursorGlyph.GlyphCol1[PosY] & (BIT0 << PosX)) != 0) { + BltChar[PosY][EFI_GLYPH_WIDTH - PosX - 1].Raw ^= Foreground.Raw; + } + } + } + + if (GraphicsOutput != NULL) { + GraphicsOutput->Blt ( + GraphicsOutput, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) BltChar, + EfiBltBufferToVideo, + 0, + 0, + GlyphX, + GlyphY, + EFI_GLYPH_WIDTH, + EFI_GLYPH_HEIGHT, + EFI_GLYPH_WIDTH * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) (UINTN) BltChar, + EfiUgaBltBufferToVideo, + 0, + 0, + GlyphX, + GlyphY, + EFI_GLYPH_WIDTH, + EFI_GLYPH_HEIGHT, + EFI_GLYPH_WIDTH * sizeof (EFI_UGA_PIXEL) + ); + } + + return EFI_SUCCESS; +} + +/** + HII Database Protocol notification event handler. + + Register font package when HII Database Protocol has been installed. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +RegisterFontPackage ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_HII_SIMPLE_FONT_PACKAGE_HDR *SimplifiedFont; + UINT32 PackageLength; + UINT8 *Package; + UINT8 *Location; + EFI_HII_DATABASE_PROTOCOL *HiiDatabase; + + // + // Locate HII Database Protocol + // + Status = gBS->LocateProtocol ( + &gEfiHiiDatabaseProtocolGuid, + NULL, + (VOID **) &HiiDatabase + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // Add 4 bytes to the header for entire length for HiiAddPackages use only. + // + // +--------------------------------+ <-- Package + // | | + // | PackageLength(4 bytes) | + // | | + // |--------------------------------| <-- SimplifiedFont + // | | + // |EFI_HII_SIMPLE_FONT_PACKAGE_HDR | + // | | + // |--------------------------------| <-- Location + // | | + // | gUsStdNarrowGlyphData | + // | | + // +--------------------------------+ + + PackageLength = sizeof (EFI_HII_SIMPLE_FONT_PACKAGE_HDR) + mNarrowFontSize + 4; + Package = AllocateZeroPool (PackageLength); + ASSERT (Package != NULL); + + WriteUnaligned32((UINT32 *) Package,PackageLength); + SimplifiedFont = (EFI_HII_SIMPLE_FONT_PACKAGE_HDR *) (Package + 4); + SimplifiedFont->Header.Length = (UINT32) (PackageLength - 4); + SimplifiedFont->Header.Type = EFI_HII_PACKAGE_SIMPLE_FONTS; + SimplifiedFont->NumberOfNarrowGlyphs = (UINT16) (mNarrowFontSize / sizeof (EFI_NARROW_GLYPH)); + + Location = (UINT8 *) (&SimplifiedFont->NumberOfWideGlyphs + 1); + CopyMem (Location, gUsStdNarrowGlyphData, mNarrowFontSize); + + // + // Add this simplified font package to a package list then install it. + // + mHiiHandle = HiiAddPackages ( + &mFontPackageListGuid, + NULL, + Package, + NULL + ); + ASSERT (mHiiHandle != NULL); + FreePool (Package); +} + +/** + The user Entry Point for module GraphicsConsole. The user code starts with this 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 entry point is executed successfully. + @return other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeGraphicsConsole ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Register notify function on HII Database Protocol to add font package. + // + EfiCreateProtocolNotifyEvent ( + &gEfiHiiDatabaseProtocolGuid, + TPL_CALLBACK, + RegisterFontPackage, + NULL, + &mHiiRegistration + ); + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gGraphicsConsoleDriverBinding, + ImageHandle, + &gGraphicsConsoleComponentName, + &gGraphicsConsoleComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.h b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.h new file mode 100644 index 000000000..28d47ac7c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.h @@ -0,0 +1,594 @@ +/** @file + Header file for GraphicsConsole driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _GRAPHICS_CONSOLE_H_ +#define _GRAPHICS_CONSOLE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +extern EFI_COMPONENT_NAME_PROTOCOL gGraphicsConsoleComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gGraphicsConsoleComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gGraphicsConsoleDriverBinding; +extern EFI_NARROW_GLYPH gUsStdNarrowGlyphData[]; + +extern UINT32 mNarrowFontSize; + +typedef union { + EFI_NARROW_GLYPH NarrowGlyph; + EFI_WIDE_GLYPH WideGlyph; +} GLYPH_UNION; + +// +// Device Structure +// +#define GRAPHICS_CONSOLE_DEV_SIGNATURE SIGNATURE_32 ('g', 's', 't', 'o') + +typedef struct { + UINTN Columns; + UINTN Rows; + INTN DeltaX; + INTN DeltaY; + UINT32 GopWidth; + UINT32 GopHeight; + UINT32 GopModeNumber; +} GRAPHICS_CONSOLE_MODE_DATA; + +typedef struct { + UINTN Signature; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SimpleTextOutput; + EFI_SIMPLE_TEXT_OUTPUT_MODE SimpleTextOutputMode; + GRAPHICS_CONSOLE_MODE_DATA *ModeData; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *LineBuffer; +} GRAPHICS_CONSOLE_DEV; + +#define GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS(a) \ + CR (a, GRAPHICS_CONSOLE_DEV, SimpleTextOutput, GRAPHICS_CONSOLE_DEV_SIGNATURE) + + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +/** + Reset the text output device hardware and optionally run diagnostics. + + Implements SIMPLE_TEXT_OUTPUT.Reset(). + If ExtendeVerification is TRUE, then perform dependent Graphics Console + device reset, and set display mode to mode 0. + If ExtendedVerification is FALSE, only set display mode to mode 0. + + @param This Protocol instance pointer. + @param ExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS The text output device was reset. + @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and + could not be reset. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Write a Unicode string to the output device. + + Implements SIMPLE_TEXT_OUTPUT.OutputString(). + The Unicode string will be converted to Glyphs and will be + sent to the Graphics Console. + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be displayed + on the output device(s). All output devices must + also support the Unicode drawing defined in this file. + + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + Implements SIMPLE_TEXT_OUTPUT.TestString(). + If one of the characters in the *Wstring is neither valid valid Unicode + drawing characters, not ASCII code, then this function will return + EFI_UNSUPPORTED + + @param This Protocol instance pointer. + @param WString The NULL-terminated Unicode string to be examined for the output + device(s). + + @retval EFI_SUCCESS The device(s) are capable of rendering the output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be + rendered by one or more of the output devices mapped + by the EFI handle. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Returns information for an available text mode that the output device(s) + supports + + Implements SIMPLE_TEXT_OUTPUT.QueryMode(). + It returnes information for an available text mode that the Graphics Console supports. + In this driver,we only support text mode 80x25, which is defined as mode 0. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to return information on. + @param Columns The returned columns of the requested mode. + @param Rows The returned rows of the requested mode. + + @retval EFI_SUCCESS The requested mode information is returned. + @retval EFI_UNSUPPORTED The mode number is not valid. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ); + + +/** + Sets the output device(s) to a specified mode. + + Implements SIMPLE_TEXT_OUTPUT.SetMode(). + Set the Graphics Console to a specified mode. In this driver, we only support mode 0. + + @param This Protocol instance pointer. + @param ModeNumber The text mode to set. + + @retval EFI_SUCCESS The requested text mode is set. + @retval EFI_DEVICE_ERROR The requested text mode cannot be set because of + Graphics Console device error. + @retval EFI_UNSUPPORTED The text mode number is not valid. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ); + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + Implements SIMPLE_TEXT_OUTPUT.SetAttribute(). + + @param This Protocol instance pointer. + @param Attribute The attribute to set. Bits 0..3 are the foreground + color, and bits 4..6 are the background color. + All other bits are undefined and must be zero. + + @retval EFI_SUCCESS The requested attribute is set. + @retval EFI_DEVICE_ERROR The requested attribute cannot be set due to Graphics Console port error. + @retval EFI_UNSUPPORTED The attribute requested is not defined. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ); + +/** + Clears the output device(s) display to the currently selected background + color. + + Implements SIMPLE_TEXT_OUTPUT.ClearScreen(). + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ); + +/** + Sets the current coordinates of the cursor position. + + Implements SIMPLE_TEXT_OUTPUT.SetCursorPosition(). + + @param This Protocol instance pointer. + @param Column The position to set the cursor to. Must be greater than or + equal to zero and less than the number of columns and rows + by QueryMode (). + @param Row The position to set the cursor to. Must be greater than or + equal to zero and less than the number of columns and rows + by QueryMode (). + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the + cursor position is invalid for the current mode. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ); + + +/** + Makes the cursor visible or invisible. + + Implements SIMPLE_TEXT_OUTPUT.EnableCursor(). + + @param This Protocol instance pointer. + @param Visible If TRUE, the cursor is set to be visible, If FALSE, + the cursor is set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleConOutEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ); + +/** + Test to see if Graphics Console could be supported on the Controller. + + Graphics Console could be supported if Graphics Output Protocol or UGA Draw + Protocol exists on the Controller. (UGA Draw Protocol could be skipped + if PcdUgaConsumeSupport is set to FALSE.) + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + + +/** + Start this driver on Controller by opening Graphics Output protocol or + UGA Draw protocol, and installing Simple Text Out protocol on Controller. + (UGA Draw protocol could be skipped if PcdUgaConsumeSupport is set to FALSE.) + + @param This Protocol instance pointer. + @param Controller Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on Controller by removing Simple Text Out protocol + and closing the Graphics Output Protocol or UGA Draw protocol on Controller. + (UGA Draw protocol could be skipped if PcdUgaConsumeSupport is set to FALSE.) + + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_NOT_STARTED Simple Text Out protocol could not be found the + Controller. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +GraphicsConsoleControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + + +/** + Locate HII Database protocol and HII Font protocol. + + @retval EFI_SUCCESS HII Database protocol and HII Font protocol + are located successfully. + @return other Failed to locate HII Database protocol or + HII Font protocol. + +**/ +EFI_STATUS +EfiLocateHiiProtocol ( + VOID + ); + + +/** + Gets Graphics Console devcie's foreground color and background color. + + @param This Protocol instance pointer. + @param Foreground Returned text foreground color. + @param Background Returned text background color. + + @retval EFI_SUCCESS It returned always. + +**/ +EFI_STATUS +GetTextColors ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Foreground, + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Background + ); + +/** + Draw Unicode string on the Graphics Console device's screen. + + @param This Protocol instance pointer. + @param UnicodeWeight One Unicode string to be displayed. + @param Count The count of Unicode string. + + @retval EFI_OUT_OF_RESOURCES If no memory resource to use. + @retval EFI_UNSUPPORTED If no Graphics Output protocol and UGA Draw + protocol exist. + @retval EFI_SUCCESS Drawing Unicode string implemented successfully. + +**/ +EFI_STATUS +DrawUnicodeWeightAtCursorN ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *UnicodeWeight, + IN UINTN Count + ); + +/** + Flush the cursor on the screen. + + If CursorVisible is FALSE, nothing to do and return directly. + If CursorVisible is TRUE, + i) If the cursor shows on screen, it will be erased. + ii) If the cursor does not show on screen, it will be shown. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The cursor is erased successfully. + +**/ +EFI_STATUS +FlushCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ); + +/** + Check if the current specific mode supported the user defined resolution + for the Graphics Console device based on Graphics Output Protocol. + + If yes, set the graphic device's current mode to this specific mode. + + @param GraphicsOutput Graphics Output Protocol instance pointer. + @param HorizontalResolution User defined horizontal resolution + @param VerticalResolution User defined vertical resolution. + @param CurrentModeNumber Current specific mode to be check. + + @retval EFI_SUCCESS The mode is supported. + @retval EFI_UNSUPPORTED The specific mode is out of range of graphics + device supported. + @retval other The specific mode does not support user defined + resolution or failed to set the current mode to the + specific mode on graphics device. + +**/ +EFI_STATUS +CheckModeSupported ( + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput, + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + OUT UINT32 *CurrentModeNumber + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf new file mode 100644 index 000000000..bcfd306ee --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf @@ -0,0 +1,72 @@ +## @file +# Console support on graphic devices. +# +# This driver will install Simple Text Output protocol by consuming Graphices Output +# protocol or UGA Draw protocol on graphic devices. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = GraphicsConsoleDxe + MODULE_UNI_FILE = GraphicsConsoleDxe.uni + FILE_GUID = CCCB0C28-4B24-11d5-9A5A-0090273FC14D + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeGraphicsConsole + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gGraphicsConsoleDriverBinding +# COMPONENT_NAME = gGraphicsConsoleComponentName +# COMPONENT_NAME2 = gGraphicsConsoleComponentName2 +# + +[Sources] + ComponentName.c + LaffStd.c + GraphicsConsole.c + GraphicsConsole.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + UefiDriverEntryPoint + DebugLib + HiiLib + PcdLib + +[Protocols] + gEfiDevicePathProtocolGuid ## TO_START + gEfiSimpleTextOutProtocolGuid ## BY_START + gEfiGraphicsOutputProtocolGuid ## TO_START + gEfiUgaDrawProtocolGuid ## TO_START + gEfiHiiFontProtocolGuid ## TO_START + ## TO_START + ## NOTIFY + gEfiHiiDatabaseProtocolGuid + +[FeaturePcd] + gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutRow ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutColumn ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + GraphicsConsoleDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.uni b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.uni new file mode 100644 index 000000000..aec5d57f9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.uni @@ -0,0 +1,18 @@ +// /** @file +// Console support on graphic devices. +// +// This driver will install Simple Text Output protocol by consuming Graphices Output +// protocol or UGA Draw protocol on graphic devices. +// +// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Console support on graphic devices" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver will install SimpleTextOutputProtocol by consuming GraphicesOutput\n" + "Protocol or UgaDrawProtocol on graphics devices." + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni new file mode 100644 index 000000000..4b97b6938 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// GraphicsConsoleDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Graphics Console DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/LaffStd.c b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/LaffStd.c new file mode 100644 index 000000000..70715527a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsConsoleDxe/LaffStd.c @@ -0,0 +1,271 @@ +/** @file + Narrow font Data definition for GraphicsConsole driver. + +Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "GraphicsConsole.h" + + + +EFI_NARROW_GLYPH gUsStdNarrowGlyphData[] = { + // + // Unicode glyphs from 0x20 to 0x7e are the same as ASCII characters 0x20 to 0x7e + // + { 0x0020, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x0021, 0x00, {0x00,0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00}}, + { 0x0022, 0x00, {0x00,0x00,0x00,0x6C,0x6C,0x6C,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x0023, 0x00, {0x00,0x00,0x00,0x00,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00}}, + { 0x0024, 0x00, {0x00,0x00,0x18,0x18,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x18,0x18,0x00,0x00,0x00}}, + { 0x0025, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0x0C,0x0C,0x18,0x18,0x30,0x30,0x60,0x60,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x0026, 0x00, {0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x78,0x76,0xDC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x0027, 0x00, {0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x0028, 0x00, {0x00,0x00,0x00,0x06,0x0C,0x0C,0x18,0x18,0x18,0x18,0x18,0x18,0x0C,0x0C,0x06,0x00,0x00,0x00,0x00}}, + { 0x0029, 0x00, {0x00,0x00,0x00,0xC0,0x60,0x60,0x30,0x30,0x30,0x30,0x30,0x30,0x60,0x60,0xC0,0x00,0x00,0x00,0x00}}, + { 0x002a, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6C,0x38,0xFE,0x38,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x002b, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x002c, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00,0x00}}, + { 0x002d, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x002e, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00}}, + { 0x002f, 0x00, {0x00,0x00,0x00,0x06,0x06,0x0C,0x0C,0x18,0x18,0x30,0x30,0x60,0x60,0xC0,0xC0,0x00,0x00,0x00,0x00}}, + { 0x0030, 0x00, {0x00,0x00,0x00,0x38,0x6C,0xC6,0xC6,0xC6,0xD6,0xD6,0xC6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00}}, + { 0x0031, 0x00, {0x00,0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00}}, + { 0x0032, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0x06,0x06,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC2,0xFE,0x00,0x00,0x00,0x00}}, + { 0x0033, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0x06,0x06,0x06,0x3C,0x06,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0034, 0x00, {0x00,0x00,0x00,0x1C,0x1C,0x3C,0x3C,0x6C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00}}, + { 0x0035, 0x00, {0x00,0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0036, 0x00, {0x00,0x00,0x00,0x3C,0x60,0xC0,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0037, 0x00, {0x00,0x00,0x00,0xFE,0xC6,0x06,0x06,0x06,0x0C,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00}}, + { 0x0038, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0039, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00}}, + { 0x003a, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00}}, + { 0x003b, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00}}, + { 0x003c, 0x00, {0x00,0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00}}, + { 0x003d, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x003e, 0x00, {0x00,0x00,0x00,0x00,0xC0,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0xC0,0x00,0x00,0x00,0x00}}, + { 0x003f, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00}}, + { 0x0040, 0x00, {0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0xC0,0x7E,0x00,0x00,0x00,0x00}}, + + { 0x0041, 0x00, {0x00,0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + + { 0x0042, 0x00, {0x00,0x00,0x00,0xFC,0x66,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00}}, + { 0x0043, 0x00, {0x00,0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00}}, + { 0x0044, 0x00, {0x00,0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00}}, + { 0x0045, 0x00, {0x00,0x00,0x00,0xFE,0x66,0x62,0x60,0x68,0x78,0x68,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00}}, + { 0x0046, 0x00, {0x00,0x00,0x00,0xFE,0x66,0x62,0x60,0x64,0x7C,0x64,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00}}, + { 0x0047, 0x00, {0x00,0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xDE,0xC6,0xC6,0xC6,0x66,0x3C,0x00,0x00,0x00,0x00}}, + { 0x0048, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x0049, 0x00, {0x00,0x00,0x00,0xFC,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xFC,0x00,0x00,0x00,0x00}}, + { 0x004a, 0x00, {0x00,0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00}}, + { 0x004b, 0x00, {0x00,0x00,0x00,0xE6,0x66,0x6C,0x6C,0x78,0x70,0x78,0x6C,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00}}, + { 0x004c, 0x00, {0x00,0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00}}, + { 0x004d, 0x00, {0x00,0x00,0x00,0xC6,0xEE,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x004e, 0x00, {0x00,0x00,0x00,0xC6,0xE6,0xF6,0xF6,0xF6,0xDE,0xCE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x004f, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0050, 0x00, {0x00,0x00,0x00,0xFC,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00}}, + { 0x0051, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0x7C,0x1C,0x0E,0x00,0x00}}, + { 0x0052, 0x00, {0x00,0x00,0x00,0xFC,0x66,0x66,0x66,0x66,0x7C,0x78,0x6C,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00}}, + { 0x0053, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x60,0x38,0x0C,0x06,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0054, 0x00, {0x00,0x00,0x00,0xFC,0xFC,0xB4,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}}, + { 0x0055, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0056, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00}}, + { 0x0057, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00}}, + { 0x0058, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0xC6,0x6C,0x6C,0x38,0x6C,0x6C,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x0059, 0x00, {0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}}, + { 0x005a, 0x00, {0x00,0x00,0x00,0xFE,0xC6,0x86,0x0C,0x0C,0x18,0x30,0x60,0xC0,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00}}, + { 0x005b, 0x00, {0x00,0x00,0x00,0x1E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1E,0x00,0x00,0x00,0x00}}, + { 0x005c, 0x00, {0x00,0x00,0x00,0xC0,0xC0,0x60,0x60,0x30,0x30,0x18,0x18,0x0C,0x0C,0x06,0x06,0x00,0x00,0x00,0x00}}, + { 0x005d, 0x00, {0x00,0x00,0x00,0xF0,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xF0,0x00,0x00,0x00,0x00}}, + { 0x005e, 0x00, {0x00,0x00,0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x005f, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00}}, + { 0x0060, 0x00, {0x00,0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x0061, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x0062, 0x00, {0x00,0x00,0x00,0xE0,0x60,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0063, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0064, 0x00, {0x00,0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x3C,0x6C,0xCC,0xCC,0xCC,0xCC,0xCC,0x7E,0x00,0x00,0x00,0x00}}, + { 0x0065, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0066, 0x00, {0x00,0x00,0x00,0x1E,0x33,0x30,0x30,0x30,0x78,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}}, + { 0x0067, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,0x00}}, + { 0x0068, 0x00, {0x00,0x00,0x00,0xE0,0x60,0x60,0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00}}, + { 0x0069, 0x00, {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x006a, 0x00, {0x00,0x00,0x00,0x0C,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}}, + { 0x006b, 0x00, {0x00,0x00,0x00,0xE0,0x60,0x60,0x66,0x6C,0x78,0x70,0x78,0x6C,0x6C,0x66,0xE6,0x00,0x00,0x00,0x00}}, + { 0x006c, 0x00, {0x00,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x006d, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEC,0xEE,0xFE,0xD6,0xD6,0xD6,0xD6,0xD6,0x00,0x00,0x00,0x00}}, + { 0x006e, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00}}, + { 0x006f, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0070, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00}}, + { 0x0071, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x1E,0x00}}, + { 0x0072, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x60,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00}}, + { 0x0073, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0x7C,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x0074, 0x00, {0x00,0x00,0x00,0x10,0x30,0x30,0x30,0xFC,0x30,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00}}, + { 0x0075, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x0076, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x00,0x00,0x00,0x00}}, + { 0x0077, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00}}, + { 0x0078, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x6C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x0079, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00}}, + { 0x007a, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x86,0x0C,0x18,0x30,0x60,0xC0,0xFE,0x00,0x00,0x00,0x00}}, + { 0x007b, 0x00, {0x00,0x00,0x00,0x0E,0x18,0x18,0x18,0x18,0x30,0x18,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00}}, + { 0x007c, 0x00, {0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00}}, + { 0x007d, 0x00, {0x00,0x00,0x00,0xE0,0x30,0x30,0x30,0x30,0x18,0x30,0x30,0x30,0x30,0x30,0xE0,0x00,0x00,0x00,0x00}}, + { 0x007e, 0x00, {0x00,0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + + { 0x00a0, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00a1, 0x00, {0x00,0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x3C,0x3C,0x3C,0x18,0x00,0x00,0x00,0x00}}, + { 0x00a2, 0x00, {0x00,0x00,0x00,0x00,0x18,0x18,0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x18,0x18,0x00,0x00,0x00,0x00}}, + { 0x00a3, 0x00, {0x00,0x00,0x00,0x38,0x6C,0x64,0x60,0x60,0xF0,0x60,0x60,0x60,0x60,0xE6,0xFC,0x00,0x00,0x00,0x00}}, + { 0x00a4, 0x00, {0x00,0x00,0x18,0x00,0x00,0x00,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0xC6,0x00,0x00,0x00,0x00,0x00}}, + { 0x00a5, 0x00, {0x00,0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x7E,0x18,0x7E,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00}}, + { 0x00a6, 0x00, {0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00}}, + { 0x00a7, 0x00, {0x00,0x00,0x18,0x7C,0xC6,0x60,0x38,0x6C,0xC6,0xC6,0x6C,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00a8, 0x00, {0x00,0x00,0x00,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00a9, 0x00, {0x00,0x00,0x00,0x00,0x7C,0x82,0x9A,0xA2,0xA2,0xA2,0x9A,0x82,0x7C,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00aa, 0x00, {0x00,0x00,0x00,0x00,0x3C,0x6C,0x6C,0x6C,0x3E,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00ab, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x6C,0xD8,0x6C,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00ac, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00ad, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00ae, 0x00, {0x00,0x00,0x00,0x00,0x7C,0x82,0xB2,0xAA,0xAA,0xB2,0xAA,0xAA,0x82,0x7C,0x00,0x00,0x00,0x00,0x00}}, + { 0x00af, 0x00, {0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00b0, 0x00, {0x00,0x00,0x00,0x38,0x6C,0x6C,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00b1, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00}}, + { 0x00b2, 0x00, {0x00,0x00,0x00,0x3C,0x66,0x0C,0x18,0x32,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00b3, 0x00, {0x00,0x00,0x00,0x7C,0x06,0x3C,0x06,0x06,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00b4, 0x00, {0x00,0x00,0x00,0x0C,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00b5, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xC0,0x00}}, + { 0x00b6, 0x00, {0x00,0x00,0x00,0x7F,0xDB,0xDB,0xDB,0xDB,0x7B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x00,0x00,0x00,0x00}}, + { 0x00b7, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00b8, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x0C,0x78,0x00,0x00,0x00}}, + { 0x00b9, 0x00, {0x00,0x00,0x00,0x18,0x38,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00ba, 0x00, {0x00,0x00,0x00,0x00,0x38,0x6C,0x6C,0x6C,0x38,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00bb, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xD8,0x6C,0x36,0x6C,0xD8,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00bc, 0x00, {0x00,0x00,0x00,0x60,0xE0,0x62,0x66,0x6C,0x18,0x30,0x66,0xCE,0x9A,0x3F,0x06,0x06,0x00,0x00,0x00}}, + { 0x00bd, 0x00, {0x00,0x00,0x00,0x60,0xE0,0x62,0x66,0x6C,0x18,0x30,0x60,0xDC,0x86,0x0C,0x18,0x3E,0x00,0x00,0x00}}, + { 0x00be, 0x00, {0x00,0x00,0x00,0xE0,0x30,0x62,0x36,0xEC,0x18,0x30,0x66,0xCE,0x9A,0x3F,0x06,0x06,0x00,0x00,0x00}}, + { 0x00bf, 0x00, {0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,0x60,0xC0,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00c0, 0x00, {0x60,0x30,0x18,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x00c1, 0x00, {0x18,0x30,0x60,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x00c2, 0x00, {0x10,0x38,0x6C,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x00c3, 0x00, {0x76,0xDC,0x00,0x00,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x00c4, 0x00, {0xCC,0xCC,0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x00c5, 0x00, {0x38,0x6C,0x38,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x00c6, 0x00, {0x00,0x00,0x00,0x00,0x3E,0x6C,0xCC,0xCC,0xCC,0xFE,0xCC,0xCC,0xCC,0xCC,0xCE,0x00,0x00,0x00,0x00}}, + { 0x00c7, 0x00, {0x00,0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x18,0x70,0x00,0x00}}, + { 0x00c8, 0x00, {0x60,0x30,0x18,0x00,0xFE,0x66,0x62,0x60,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00}}, + { 0x00c9, 0x00, {0x18,0x30,0x60,0x00,0xFE,0x66,0x62,0x60,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00}}, + { 0x00ca, 0x00, {0x10,0x38,0x6C,0x00,0xFE,0x66,0x62,0x60,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00}}, + { 0x00cb, 0x00, {0xCC,0xCC,0x00,0x00,0xFE,0x66,0x62,0x60,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00}}, + { 0x00cc, 0x00, {0x60,0x30,0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00cd, 0x00, {0x18,0x30,0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00ce, 0x00, {0x10,0x38,0x6C,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00cf, 0x00, {0xCC,0xCC,0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00d0, 0x00, {0x00,0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0xF6,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00}}, + { 0x00d1, 0x00, {0x76,0xDC,0x00,0x00,0xC6,0xE6,0xE6,0xF6,0xF6,0xDE,0xDE,0xCE,0xCE,0xC6,0xC6,0x00,0x00,0x00,0x00}}, + { 0x00d2, 0x00, {0x60,0x30,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00d3, 0x00, {0x18,0x30,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00d4, 0x00, {0x10,0x38,0x6C,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00d5, 0x00, {0x76,0xDC,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00d6, 0x00, {0xCC,0xCC,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00d7, 0x00, {0x10,0x28,0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x6C,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00}}, + { 0x00d8, 0x00, {0x00,0x00,0x00,0x7C,0xCE,0xCE,0xDE,0xD6,0xD6,0xD6,0xD6,0xF6,0xE6,0xE6,0x7C,0x40,0x00,0x00,0x00}}, + { 0x00d9, 0x00, {0x60,0x30,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00da, 0x00, {0x18,0x30,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00db, 0x00, {0x10,0x38,0x6C,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00dc, 0x00, {0xCC,0xCC,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00dd, 0x00, {0x18,0x30,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00de, 0x00, {0x00,0x00,0x10,0x00,0xF0,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x60,0xF0,0x00,0x00,0x00,0x00}}, + { 0x00df, 0x00, {0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xD8,0xCC,0xC6,0xC6,0xC6,0xC6,0xCC,0x00,0x00,0x00,0x00}}, + { 0x00e0, 0x00, {0x00,0x30,0x30,0x60,0x30,0x18,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00e1, 0x00, {0x00,0x00,0x00,0x18,0x30,0x60,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00e2, 0x00, {0x00,0x00,0x00,0x10,0x38,0x6C,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00e3, 0x00, {0x00,0x00,0x00,0x00,0x76,0xDC,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00e4, 0x00, {0x00,0x00,0x00,0xCC,0xCC,0x00,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00e5, 0x00, {0x00,0x00,0x00,0x38,0x6C,0x38,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00e6, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEC,0x36,0x36,0x7E,0xD8,0xD8,0xD8,0x6E,0x00,0x00,0x00,0x00}}, + { 0x00e7, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC0,0xC6,0x7C,0x18,0x70,0x00,0x00}}, + { 0x00e8, 0x00, {0x00,0x00,0x00,0x60,0x30,0x18,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00e9, 0x00, {0x00,0x00,0x00,0x0C,0x18,0x30,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00ea, 0x00, {0x00,0x00,0x00,0x10,0x38,0x6C,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00eb, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0x00,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00ec, 0x00, {0x00,0x00,0x00,0x60,0x30,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00ed, 0x00, {0x00,0x00,0x00,0x0C,0x18,0x30,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00ee, 0x00, {0x00,0x00,0x00,0x18,0x3C,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00ef, 0x00, {0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00f0, 0x00, {0x00,0x00,0x00,0x34,0x18,0x2C,0x0C,0x06,0x3E,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00}}, + { 0x00f1, 0x00, {0x00,0x00,0x00,0x00,0x76,0xDC,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00}}, + { 0x00f2, 0x00, {0x00,0x00,0x00,0x60,0x30,0x18,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00f3, 0x00, {0x00,0x00,0x00,0x18,0x30,0x60,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00f4, 0x00, {0x00,0x00,0x00,0x10,0x38,0x6C,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00f5, 0x00, {0x00,0x00,0x00,0x00,0x76,0xDC,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00f6, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00f7, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x7E,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { 0x00f8, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xCE,0xDE,0xD6,0xF6,0xE6,0xC6,0x7C,0x00,0x00,0x00,0x00}}, + { 0x00f9, 0x00, {0x00,0x00,0x00,0x60,0x30,0x18,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00fa, 0x00, {0x00,0x00,0x00,0x18,0x30,0x60,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00fb, 0x00, {0x00,0x00,0x00,0x30,0x78,0xCC,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00fc, 0x00, {0x00,0x00,0x00,0xCC,0xCC,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}}, + { 0x00fd, 0x00, {0x00,0x00,0x00,0x0C,0x18,0x30,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00}}, + { 0x00fe, 0x00, {0x00,0x00,0x00,0xE0,0x60,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00}}, + { 0x00ff, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00}}, + + { (CHAR16)BOXDRAW_HORIZONTAL, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_VERTICAL, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_DOWN_RIGHT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_DOWN_LEFT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_UP_RIGHT, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_UP_LEFT, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_VERTICAL_RIGHT, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_VERTICAL_LEFT, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_DOWN_HORIZONTAL, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_UP_HORIZONTAL, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_VERTICAL_HORIZONTAL, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_DOUBLE_HORIZONTAL, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_DOUBLE_VERTICAL, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOWN_RIGHT_DOUBLE, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_DOWN_DOUBLE_RIGHT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOUBLE_DOWN_RIGHT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOWN_LEFT_DOUBLE, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_DOWN_DOUBLE_LEFT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOUBLE_DOWN_LEFT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x06,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_UP_RIGHT_DOUBLE, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_UP_DOUBLE_RIGHT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_DOUBLE_UP_RIGHT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_UP_LEFT_DOUBLE, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_UP_DOUBLE_LEFT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_DOUBLE_UP_LEFT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF6,0x06,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_VERTICAL_RIGHT_DOUBLE, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_VERTICAL_DOUBLE_RIGHT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOUBLE_VERTICAL_RIGHT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_VERTICAL_LEFT_DOUBLE, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_VERTICAL_DOUBLE_LEFT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOUBLE_VERTICAL_LEFT, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF6,0x06,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOWN_HORIZONTAL_DOUBLE, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_DOWN_DOUBLE_HORIZONTAL, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOUBLE_DOWN_HORIZONTAL, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xF7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_UP_HORIZONTAL_DOUBLE, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_UP_DOUBLE_HORIZONTAL, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_DOUBLE_UP_HORIZONTAL, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF7,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x18,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}}, + { (CHAR16)BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFF,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + { (CHAR16)BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF7,0x00,0xF7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}}, + + { (CHAR16)BLOCKELEMENT_FULL_BLOCK, 0x00, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}, + { (CHAR16)BLOCKELEMENT_LIGHT_SHADE, 0x00, {0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,0x22}}, + + { (CHAR16)GEOMETRICSHAPE_RIGHT_TRIANGLE, 0x00, {0x00,0x00,0x00,0x00,0x00,0xC0,0xE0,0xF0,0xF8,0xFE,0xF8,0xF0,0xE0,0xC0,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)GEOMETRICSHAPE_LEFT_TRIANGLE, 0x00, {0x00,0x00,0x00,0x00,0x00,0x06,0x0E,0x1E,0x3E,0xFE,0x3E,0x1E,0x0E,0x06,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)GEOMETRICSHAPE_UP_TRIANGLE, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7C,0x7C,0xFE,0xFE,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)GEOMETRICSHAPE_DOWN_TRIANGLE, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFE,0x7C,0x7C,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00}}, + + { (CHAR16)ARROW_UP, 0x00, {0x00,0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)ARROW_DOWN, 0x00, {0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x3C,0x18,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)ARROW_LEFT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x20,0x60,0x60,0xFE,0xFE,0x60,0x60,0x20,0x00,0x00,0x00,0x00,0x00,0x00}}, + { (CHAR16)ARROW_RIGHT, 0x00, {0x00,0x00,0x00,0x00,0x00,0x08,0x0C,0x0C,0xFE,0xFE,0x0C,0x0C,0x08,0x00,0x00,0x00,0x00,0x00,0x00}}, + + { 0x0000, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} //EOL +}; + +// Get available Unicode glyphs narrow fonts(8*19 pixels) size. +UINT32 mNarrowFontSize = sizeof (gUsStdNarrowGlyphData); + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/ComponentName.c new file mode 100644 index 000000000..7b7f5683a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/ComponentName.c @@ -0,0 +1,184 @@ +/** @file + UEFI Component Name(2) protocol implementation for the generic GOP driver. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include +#include + +extern EFI_COMPONENT_NAME_PROTOCOL mGraphicsOutputComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL mGraphicsOutputComponentName2; + +// +// Driver name table for GraphicsOutput module. +// It is shared by the implementation of ComponentName & ComponentName2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mGraphicsOutputDriverNameTable[] = { + { + "eng;en", + L"Generic Graphics Output Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GraphicsOutputComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mGraphicsOutputDriverNameTable, + DriverName, + (BOOLEAN) (This == &mGraphicsOutputComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GraphicsOutputComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL mGraphicsOutputComponentName = { + GraphicsOutputComponentNameGetDriverName, + GraphicsOutputComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL mGraphicsOutputComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GraphicsOutputComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GraphicsOutputComponentNameGetControllerName, + "en" +}; diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.c b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.c new file mode 100644 index 000000000..99a76aa55 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.c @@ -0,0 +1,729 @@ +/** @file + Implementation for a generic GOP driver. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "GraphicsOutput.h" +CONST ACPI_ADR_DEVICE_PATH mGraphicsOutputAdrNode = { + { + ACPI_DEVICE_PATH, + ACPI_ADR_DP, + { sizeof (ACPI_ADR_DEVICE_PATH), 0 }, + }, + ACPI_DISPLAY_ADR (1, 0, 0, 1, 0, ACPI_ADR_DISPLAY_TYPE_VGA, 0, 0) +}; + +EFI_PEI_GRAPHICS_DEVICE_INFO_HOB mDefaultGraphicsDeviceInfo = { + MAX_UINT16, MAX_UINT16, MAX_UINT16, MAX_UINT16, MAX_UINT8, MAX_UINT8 +}; + +// +// The driver should only start on one graphics controller. +// So a global flag is used to remember that the driver is already started. +// +BOOLEAN mDriverStarted = FALSE; + +/** + Returns information for an available graphics mode that the graphics device + and the set of active video output devices supports. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber The mode number to return information on. + @param SizeOfInfo A pointer to the size, in bytes, of the Info buffer. + @param Info A pointer to callee allocated buffer that returns information about ModeNumber. + + @retval EFI_SUCCESS Valid mode information was returned. + @retval EFI_DEVICE_ERROR A hardware error occurred trying to retrieve the video mode. + @retval EFI_INVALID_PARAMETER ModeNumber is not valid. + +**/ +EFI_STATUS +EFIAPI +GraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ) +{ + if (This == NULL || Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) { + return EFI_INVALID_PARAMETER; + } + + *SizeOfInfo = This->Mode->SizeOfInfo; + *Info = AllocateCopyPool (*SizeOfInfo, This->Mode->Info); + return EFI_SUCCESS; +} + +/** + Set the video device into the specified mode and clears the visible portions of + the output display to black. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber Abstraction that defines the current video mode. + + @retval EFI_SUCCESS The graphics mode specified by ModeNumber was selected. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED ModeNumber is not supported by this device. + +**/ +EFI_STATUS +EFIAPI +GraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber +) +{ + RETURN_STATUS Status; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black; + GRAPHICS_OUTPUT_PRIVATE_DATA *Private; + + if (ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + Private = GRAPHICS_OUTPUT_PRIVATE_FROM_THIS (This); + + Black.Blue = 0; + Black.Green = 0; + Black.Red = 0; + Black.Reserved = 0; + + Status = FrameBufferBlt ( + Private->FrameBufferBltLibConfigure, + &Black, + EfiBltVideoFill, + 0, 0, + 0, 0, + This->Mode->Info->HorizontalResolution, + This->Mode->Info->VerticalResolution, + 0 + ); + return RETURN_ERROR (Status) ? EFI_DEVICE_ERROR : EFI_SUCCESS; +} + +/** + Blt a rectangle of pixels on the graphics screen. Blt stands for BLock Transfer. + + @param This Protocol instance pointer. + @param BltBuffer The data to transfer to the graphics screen. + Size is at least Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL). + @param BltOperation The operation to perform when copying BltBuffer on to the graphics screen. + @param SourceX The X coordinate of source for the BltOperation. + @param SourceY The Y coordinate of source for the BltOperation. + @param DestinationX The X coordinate of destination for the BltOperation. + @param DestinationY The Y coordinate of destination for the BltOperation. + @param Width The width of a rectangle in the blt rectangle in pixels. + @param Height The height of a rectangle in the blt rectangle in pixels. + @param Delta Not used for EfiBltVideoFill or the EfiBltVideoToVideo operation. + If a Delta of zero is used, the entire BltBuffer is being operated on. + If a subrectangle of the BltBuffer is being used then Delta + represents the number of bytes in a row of the BltBuffer. + + @retval EFI_SUCCESS BltBuffer was drawn to the graphics screen. + @retval EFI_INVALID_PARAMETER BltOperation is not valid. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + +**/ +EFI_STATUS +EFIAPI +GraphicsOutputBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ) +{ + RETURN_STATUS Status; + EFI_TPL Tpl; + GRAPHICS_OUTPUT_PRIVATE_DATA *Private; + + Private = GRAPHICS_OUTPUT_PRIVATE_FROM_THIS (This); + // + // We have to raise to TPL_NOTIFY, so we make an atomic write to the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + Status = FrameBufferBlt ( + Private->FrameBufferBltLibConfigure, + BltBuffer, + BltOperation, + SourceX, SourceY, + DestinationX, DestinationY, Width, Height, + Delta + ); + gBS->RestoreTPL (Tpl); + + return RETURN_ERROR (Status) ? EFI_INVALID_PARAMETER : EFI_SUCCESS; +} + +CONST GRAPHICS_OUTPUT_PRIVATE_DATA mGraphicsOutputInstanceTemplate = { + GRAPHICS_OUTPUT_PRIVATE_DATA_SIGNATURE, // Signature + NULL, // GraphicsOutputHandle + { + GraphicsOutputQueryMode, + GraphicsOutputSetMode, + GraphicsOutputBlt, + NULL // Mode + }, + { + 1, // MaxMode + 0, // Mode + NULL, // Info + sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION), // SizeOfInfo + 0, // FrameBufferBase + 0 // FrameBufferSize + }, + NULL, // DevicePath + NULL, // PciIo + 0, // PciAttributes + NULL, // FrameBufferBltLibConfigure + 0 // FrameBufferBltLibConfigureSize +}; + +/** + Test whether the Controller can be managed by the driver. + + @param This Driver Binding protocol instance pointer. + @param Controller The PCI controller. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver can manage the video device. + @retval other The driver cannot manage the video device. +**/ +EFI_STATUS +EFIAPI +GraphicsOutputDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + // + // Since there is only one GraphicsInfo HOB, the driver only manages one video device. + // + if (mDriverStarted) { + return EFI_ALREADY_STARTED; + } + + // + // Test the PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Test the DevicePath protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if ((RemainingDevicePath == NULL) || + IsDevicePathEnd (RemainingDevicePath) || + CompareMem (RemainingDevicePath, &mGraphicsOutputAdrNode, sizeof (mGraphicsOutputAdrNode)) == 0) { + return EFI_SUCCESS; + } else { + return EFI_INVALID_PARAMETER; + } +} + +/** + Start the video controller. + + @param This Driver Binding protocol instance pointer. + @param ControllerHandle The PCI controller. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver starts to manage the video device. + @retval other The driver cannot manage the video device. +**/ +EFI_STATUS +EFIAPI +GraphicsOutputDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + RETURN_STATUS ReturnStatus; + GRAPHICS_OUTPUT_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH *PciDevicePath; + PCI_TYPE00 Pci; + UINT8 Index; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Resources; + VOID *HobStart; + EFI_PEI_GRAPHICS_INFO_HOB *GraphicsInfo; + EFI_PEI_GRAPHICS_DEVICE_INFO_HOB *DeviceInfo; + EFI_PHYSICAL_ADDRESS FrameBufferBase; + + FrameBufferBase = 0; + + HobStart = GetFirstGuidHob (&gEfiGraphicsInfoHobGuid); + ASSERT ((HobStart != NULL) && (GET_GUID_HOB_DATA_SIZE (HobStart) == sizeof (EFI_PEI_GRAPHICS_INFO_HOB))); + GraphicsInfo = (EFI_PEI_GRAPHICS_INFO_HOB *) (GET_GUID_HOB_DATA (HobStart)); + + HobStart = GetFirstGuidHob (&gEfiGraphicsDeviceInfoHobGuid); + if ((HobStart == NULL) || (GET_GUID_HOB_DATA_SIZE (HobStart) < sizeof (*DeviceInfo))) { + // + // Use default device infomation when the device info HOB doesn't exist + // + DeviceInfo = &mDefaultGraphicsDeviceInfo; + DEBUG ((EFI_D_INFO, "[%a]: GraphicsDeviceInfo HOB doesn't exist!\n", gEfiCallerBaseName)); + } else { + DeviceInfo = (EFI_PEI_GRAPHICS_DEVICE_INFO_HOB *) (GET_GUID_HOB_DATA (HobStart)); + DEBUG ((EFI_D_INFO, "[%a]: GraphicsDeviceInfo HOB:\n" + " VendorId = %04x, DeviceId = %04x,\n" + " RevisionId = %02x, BarIndex = %x,\n" + " SubsystemVendorId = %04x, SubsystemId = %04x\n", + gEfiCallerBaseName, + DeviceInfo->VendorId, DeviceInfo->DeviceId, + DeviceInfo->RevisionId, DeviceInfo->BarIndex, + DeviceInfo->SubsystemVendorId, DeviceInfo->SubsystemId)); + } + + // + // Open the PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + ASSERT_EFI_ERROR (Status); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &PciDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + ASSERT_EFI_ERROR (Status); + + // + // Read the PCI Class Code from the PCI Device + // + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci); + if (!EFI_ERROR (Status)) { + if (!IS_PCI_DISPLAY (&Pci) || ( + ((DeviceInfo->VendorId != MAX_UINT16) && (DeviceInfo->VendorId != Pci.Hdr.VendorId)) || + ((DeviceInfo->DeviceId != MAX_UINT16) && (DeviceInfo->DeviceId != Pci.Hdr.DeviceId)) || + ((DeviceInfo->RevisionId != MAX_UINT8) && (DeviceInfo->RevisionId != Pci.Hdr.RevisionID)) || + ((DeviceInfo->SubsystemVendorId != MAX_UINT16) && (DeviceInfo->SubsystemVendorId != Pci.Device.SubsystemVendorID)) || + ((DeviceInfo->SubsystemId != MAX_UINT16) && (DeviceInfo->SubsystemId != Pci.Device.SubsystemID)) + ) + ) { + // + // It's not a video device, or device infomation doesn't match. + // + Status = EFI_UNSUPPORTED; + } else { + // + // If it's a video device and device information matches, use the BarIndex + // from device information, or any BAR if BarIndex is not specified + // whose size >= the frame buffer size from GraphicsInfo HOB. + // Store the new frame buffer base. + // + for (Index = 0; Index < MAX_PCI_BAR; Index++) { + if ((DeviceInfo->BarIndex != MAX_UINT8) && (DeviceInfo->BarIndex != Index)) { + continue; + } + Status = PciIo->GetBarAttributes (PciIo, Index, NULL, (VOID**) &Resources); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "[%a]: BAR[%d]: Base = %lx, Length = %lx\n", + gEfiCallerBaseName, Index, Resources->AddrRangeMin, Resources->AddrLen)); + if ((Resources->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) && + (Resources->Len == (UINT16) (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3)) && + (Resources->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) && + (Resources->AddrLen >= GraphicsInfo->FrameBufferSize) + ) { + FrameBufferBase = Resources->AddrRangeMin; + DEBUG ((EFI_D_INFO, "[%a]: ... matched!\n", gEfiCallerBaseName)); + break; + } + } + } + if (Index == MAX_PCI_BAR) { + Status = EFI_UNSUPPORTED; + } + } + } + + if (EFI_ERROR (Status)) { + goto CloseProtocols; + } + + if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + + Private = AllocateCopyPool (sizeof (mGraphicsOutputInstanceTemplate), &mGraphicsOutputInstanceTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CloseProtocols; + } + + Private->GraphicsOutputMode.FrameBufferBase = FrameBufferBase; + Private->GraphicsOutputMode.FrameBufferSize = GraphicsInfo->FrameBufferSize; + Private->GraphicsOutputMode.Info = &GraphicsInfo->GraphicsMode; + + // + // Fix up Mode pointer in GraphicsOutput + // + Private->GraphicsOutput.Mode = &Private->GraphicsOutputMode; + + // + // Set attributes + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Private->PciAttributes + ); + if (!EFI_ERROR (Status)) { + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + } + + if (EFI_ERROR (Status)) { + goto FreeMemory; + } + + // + // Create the FrameBufferBltLib configuration. + // + ReturnStatus = FrameBufferBltConfigure ( + (VOID *) (UINTN) Private->GraphicsOutput.Mode->FrameBufferBase, + Private->GraphicsOutput.Mode->Info, + Private->FrameBufferBltLibConfigure, + &Private->FrameBufferBltLibConfigureSize + ); + if (ReturnStatus == RETURN_BUFFER_TOO_SMALL) { + Private->FrameBufferBltLibConfigure = AllocatePool (Private->FrameBufferBltLibConfigureSize); + if (Private->FrameBufferBltLibConfigure != NULL) { + ReturnStatus = FrameBufferBltConfigure ( + (VOID *) (UINTN) Private->GraphicsOutput.Mode->FrameBufferBase, + Private->GraphicsOutput.Mode->Info, + Private->FrameBufferBltLibConfigure, + &Private->FrameBufferBltLibConfigureSize + ); + } + } + if (RETURN_ERROR (ReturnStatus)) { + Status = EFI_OUT_OF_RESOURCES; + goto RestorePciAttributes; + } + + Private->DevicePath = AppendDevicePathNode (PciDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &mGraphicsOutputAdrNode); + if (Private->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto RestorePciAttributes; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->GraphicsOutputHandle, + &gEfiGraphicsOutputProtocolGuid, &Private->GraphicsOutput, + &gEfiDevicePathProtocolGuid, Private->DevicePath, + NULL + ); + + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &Private->PciIo, + This->DriverBindingHandle, + Private->GraphicsOutputHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (!EFI_ERROR (Status)) { + mDriverStarted = TRUE; + } else { + gBS->UninstallMultipleProtocolInterfaces ( + Private->GraphicsOutputHandle, + &gEfiGraphicsOutputProtocolGuid, &Private->GraphicsOutput, + &gEfiDevicePathProtocolGuid, Private->DevicePath, + NULL + ); + } + } + +RestorePciAttributes: + if (EFI_ERROR (Status)) { + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + } + +FreeMemory: + if (EFI_ERROR (Status)) { + if (Private != NULL) { + if (Private->DevicePath != NULL) { + FreePool (Private->DevicePath); + } + if (Private->FrameBufferBltLibConfigure != NULL) { + FreePool (Private->FrameBufferBltLibConfigure); + } + FreePool (Private); + } + } + +CloseProtocols: + if (EFI_ERROR (Status)) { + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + return Status; +} + +/** + Stop the video controller. + + @param This Driver Binding protocol instance pointer. + @param Controller The PCI controller. + @param NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. +**/ +EFI_STATUS +EFIAPI +GraphicsOutputDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop; + GRAPHICS_OUTPUT_PRIVATE_DATA *Private; + + if (NumberOfChildren == 0) { + + // + // Close the PCI I/O Protocol + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + ASSERT_EFI_ERROR (Status); + return EFI_SUCCESS; + } + + ASSERT (NumberOfChildren == 1); + Status = gBS->OpenProtocol ( + ChildHandleBuffer[0], + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &Gop, + This->DriverBindingHandle, + ChildHandleBuffer[0], + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private = GRAPHICS_OUTPUT_PRIVATE_FROM_THIS (Gop); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Private->GraphicsOutputHandle + ); + ASSERT_EFI_ERROR (Status); + // + // Remove the GOP protocol interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->GraphicsOutputHandle, + &gEfiGraphicsOutputProtocolGuid, &Private->GraphicsOutput, + &gEfiDevicePathProtocolGuid, Private->DevicePath, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Restore original PCI attributes + // + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + + FreePool (Private->DevicePath); + FreePool (Private->FrameBufferBltLibConfigure); + mDriverStarted = FALSE; + } else { + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &Private->PciIo, + This->DriverBindingHandle, + Private->GraphicsOutputHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + ASSERT_EFI_ERROR (Status); + } + return Status; +} + +EFI_DRIVER_BINDING_PROTOCOL mGraphicsOutputDriverBinding = { + GraphicsOutputDriverBindingSupported, + GraphicsOutputDriverBindingStart, + GraphicsOutputDriverBindingStop, + 0x10, + NULL, + NULL +}; + +/** + The Entry Point for GraphicsOutput driver. + + It installs DriverBinding, ComponentName and ComponentName2 protocol if there is + GraphicsInfo HOB passed from Graphics PEIM. + + @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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeGraphicsOutput ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *HobStart; + + HobStart = GetFirstGuidHob (&gEfiGraphicsInfoHobGuid); + + if ((HobStart == NULL) || (GET_GUID_HOB_DATA_SIZE (HobStart) < sizeof (EFI_PEI_GRAPHICS_INFO_HOB))) { + return EFI_NOT_FOUND; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &mGraphicsOutputDriverBinding, + ImageHandle, + &mGraphicsOutputComponentName, + &mGraphicsOutputComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.h b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.h new file mode 100644 index 000000000..a40768b1e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutput.h @@ -0,0 +1,53 @@ +/** @file + Header file for a generic GOP driver. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ +#ifndef _GRAPHICS_OUTPUT_DXE_H_ +#define _GRAPHICS_OUTPUT_DXE_H_ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PCI_BAR 6 + +typedef struct { + UINT32 Signature; + EFI_HANDLE GraphicsOutputHandle; + EFI_GRAPHICS_OUTPUT_PROTOCOL GraphicsOutput; + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE GraphicsOutputMode; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 PciAttributes; + FRAME_BUFFER_CONFIGURE *FrameBufferBltLibConfigure; + UINTN FrameBufferBltLibConfigureSize; +} GRAPHICS_OUTPUT_PRIVATE_DATA; + +#define GRAPHICS_OUTPUT_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('g', 'g', 'o', 'p') +#define GRAPHICS_OUTPUT_PRIVATE_FROM_THIS(a) \ + CR(a, GRAPHICS_OUTPUT_PRIVATE_DATA, GraphicsOutput, GRAPHICS_OUTPUT_PRIVATE_DATA_SIGNATURE) + +extern EFI_COMPONENT_NAME_PROTOCOL mGraphicsOutputComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL mGraphicsOutputComponentName2; +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutputDxe.inf b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutputDxe.inf new file mode 100644 index 000000000..abc3de594 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutputDxe.inf @@ -0,0 +1,53 @@ +## @file +# This driver produces GraphicsOutput protocol based on the GraphicsInfo HOB information. +# +# Copyright (c) 2016, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = GraphicsOutputDxe + FILE_GUID = 20830080-CC28-4169-9836-7F42B8D0C8C9 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeGraphicsOutput + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + GraphicsOutput.h + GraphicsOutput.c + ComponentName.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + DxeServicesTableLib + DebugLib + MemoryAllocationLib + BaseMemoryLib + DevicePathLib + FrameBufferBltLib + UefiLib + HobLib + +[Guids] + gEfiGraphicsInfoHobGuid ## CONSUMES ## HOB + gEfiGraphicsDeviceInfoHobGuid ## CONSUMES ## HOB + +[Protocols] + gEfiGraphicsOutputProtocolGuid ## BY_START + gEfiDevicePathProtocolGuid ## BY_START + gEfiPciIoProtocolGuid ## TO_START diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Ansi.c b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Ansi.c new file mode 100644 index 000000000..f117d90b9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Ansi.c @@ -0,0 +1,73 @@ +/** @file + Implementation of translation upon PC ANSI. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Terminal.h" + +/** + Translate all raw data in the Raw FIFO into unicode, and insert + them into Unicode FIFO. + + @param TerminalDevice The terminal device. + +**/ +VOID +AnsiRawDataToUnicode ( + IN TERMINAL_DEV *TerminalDevice + ) +{ + UINT8 RawData; + + // + // pop the raw data out from the raw fifo, + // and translate it into unicode, then push + // the unicode into unicode fifo, until the raw fifo is empty. + // + while (!IsRawFiFoEmpty (TerminalDevice) && !IsUnicodeFiFoFull (TerminalDevice)) { + + RawFiFoRemoveOneKey (TerminalDevice, &RawData); + + UnicodeFiFoInsertOneKey (TerminalDevice, (UINT16) RawData); + } +} + +/** + Check if input string is valid Ascii string, valid EFI control characters + or valid text graphics. + + @param TerminalDevice The terminal device. + @param WString The input string. + + @retval EFI_UNSUPPORTED If not all input characters are valid. + @retval EFI_SUCCESS If all input characters are valid. + +**/ +EFI_STATUS +AnsiTestString ( + IN TERMINAL_DEV *TerminalDevice, + IN CHAR16 *WString + ) +{ + CHAR8 GraphicChar; + + // + // support three kind of character: + // valid ascii, valid efi control char, valid text graphics. + // + for (; *WString != CHAR_NULL; WString++) { + + if ( !(TerminalIsValidAscii (*WString) || + TerminalIsValidEfiCntlChar (*WString) || + TerminalIsValidTextGraphics (*WString, &GraphicChar, NULL) )) { + + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/ComponentName.c new file mode 100644 index 000000000..c06d21bfd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/ComponentName.c @@ -0,0 +1,231 @@ +/** @file + UEFI Component Name(2) protocol implementation for Terminal driver. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Terminal.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gTerminalComponentName = { + TerminalComponentNameGetDriverName, + TerminalComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gTerminalComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TerminalComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TerminalComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mTerminalDriverNameTable[] = { + { + "eng;en", + (CHAR16 *) L"Serial Terminal Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TerminalComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mTerminalDriverNameTable, + DriverName, + (BOOLEAN)(This == &gTerminalComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TerminalComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput; + TERMINAL_DEV *TerminalDevice; + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gTerminalDriverBinding.DriverBindingHandle, + &gEfiSerialIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // This is a bus driver, so ChildHandle can not be NULL. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiSerialIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get our context back + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &SimpleTextOutput, + gTerminalDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput); + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + TerminalDevice->ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gTerminalComponentName) + ); +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c new file mode 100644 index 000000000..a98b690c8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c @@ -0,0 +1,1383 @@ +/** @file + Produces Simple Text Input Protocol, Simple Text Input Extended Protocol and + Simple Text Output Protocol upon Serial IO Protocol. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Terminal.h" + +// +// Globals +// +EFI_DRIVER_BINDING_PROTOCOL gTerminalDriverBinding = { + TerminalDriverBindingSupported, + TerminalDriverBindingStart, + TerminalDriverBindingStop, + 0xa, + NULL, + NULL +}; + + +EFI_GUID *mTerminalType[] = { + &gEfiPcAnsiGuid, + &gEfiVT100Guid, + &gEfiVT100PlusGuid, + &gEfiVTUTF8Guid, + &gEfiTtyTermGuid, + &gEdkiiLinuxTermGuid, + &gEdkiiXtermR6Guid, + &gEdkiiVT400Guid, + &gEdkiiSCOTermGuid +}; + + +CHAR16 *mSerialConsoleNames[] = { + L"PC-ANSI Serial Console", + L"VT-100 Serial Console", + L"VT-100+ Serial Console", + L"VT-UTF8 Serial Console", + L"Tty Terminal Serial Console", + L"Linux Terminal Serial Console", + L"Xterm R6 Serial Console", + L"VT-400 Serial Console", + L"SCO Terminal Serial Console" +}; + +TERMINAL_DEV mTerminalDevTemplate = { + TERMINAL_DEV_SIGNATURE, + NULL, + 0, + NULL, + NULL, + { // SimpleTextInput + TerminalConInReset, + TerminalConInReadKeyStroke, + NULL + }, + { // SimpleTextOutput + TerminalConOutReset, + TerminalConOutOutputString, + TerminalConOutTestString, + TerminalConOutQueryMode, + TerminalConOutSetMode, + TerminalConOutSetAttribute, + TerminalConOutClearScreen, + TerminalConOutSetCursorPosition, + TerminalConOutEnableCursor, + NULL + }, + { // SimpleTextOutputMode + 1, // MaxMode + 0, // Mode + EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK), // Attribute + 0, // CursorColumn + 0, // CursorRow + TRUE // CursorVisible + }, + NULL, // TerminalConsoleModeData + 0, // SerialInTimeOut + + NULL, // RawFifo + NULL, // UnicodeFiFo + NULL, // EfiKeyFiFo + NULL, // EfiKeyFiFoForNotify + + NULL, // ControllerNameTable + NULL, // TimerEvent + NULL, // TwoSecondTimeOut + INPUT_STATE_DEFAULT, + RESET_STATE_DEFAULT, + { + 0, + 0, + 0 + }, + 0, + FALSE, + { // SimpleTextInputEx + TerminalConInResetEx, + TerminalConInReadKeyStrokeEx, + NULL, + TerminalConInSetState, + TerminalConInRegisterKeyNotify, + TerminalConInUnregisterKeyNotify, + }, + { // NotifyList + NULL, + NULL, + }, + NULL // KeyNotifyProcessEvent +}; + +TERMINAL_CONSOLE_MODE_DATA mTerminalConsoleModeData[] = { + {80, 25}, + {80, 50}, + {100, 31}, + // + // New modes can be added here. + // +}; + +/** + Convert the GUID representation of terminal type to enum type. + + @param Guid The GUID representation of terminal type. + + @return The terminal type in enum type. +**/ +TERMINAL_TYPE +TerminalTypeFromGuid ( + IN EFI_GUID *Guid +) +{ + TERMINAL_TYPE Type; + + for (Type = 0; Type < ARRAY_SIZE (mTerminalType); Type++) { + if (CompareGuid (Guid, mTerminalType[Type])) { + break; + } + } + return Type; +} + +/** + Test to see if this driver supports Controller. + + @param This Protocol instance pointer. + @param Controller Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +TerminalDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + VENDOR_DEVICE_PATH *Node; + + // + // If remaining device path is not NULL, then make sure it is a + // device path that describes a terminal communications protocol. + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, go on checking other conditions + // + if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + Node = (VENDOR_DEVICE_PATH *) RemainingDevicePath; + + if (Node->Header.Type != MESSAGING_DEVICE_PATH || + Node->Header.SubType != MSG_VENDOR_DP || + DevicePathNodeLength(&Node->Header) != sizeof(VENDOR_DEVICE_PATH)) { + + return EFI_UNSUPPORTED; + + } + // + // only supports PC ANSI, VT100, VT100+, VT-UTF8, TtyTerm + // Linux, XtermR6, VT400 and SCO terminal types + // + if (TerminalTypeFromGuid (&Node->Guid) == ARRAY_SIZE (mTerminalType)) { + return EFI_UNSUPPORTED; + } + } + } + // + // Open the IO Abstraction(s) needed to perform the supported test + // The Controller must support the Serial I/O Protocol. + // This driver is a bus driver with at most 1 child device, so it is + // ok for it to be already started. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Open the EFI Device Path protocol needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close protocol, don't use device path protocol in the Support() function + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Free notify functions list. + + @param ListHead The list head + + @retval EFI_SUCCESS Free the notify list successfully. + @retval EFI_INVALID_PARAMETER ListHead is NULL. + +**/ +EFI_STATUS +TerminalFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ) +{ + TERMINAL_CONSOLE_IN_EX_NOTIFY *NotifyNode; + + if (ListHead == NULL) { + return EFI_INVALID_PARAMETER; + } + while (!IsListEmpty (ListHead)) { + NotifyNode = CR ( + ListHead->ForwardLink, + TERMINAL_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + RemoveEntryList (ListHead->ForwardLink); + FreePool (NotifyNode); + } + + return EFI_SUCCESS; +} + +/** + Initialize all the text modes which the terminal console supports. + + It returns information for available text modes that the terminal can support. + + @param[out] TextModeCount The total number of text modes that terminal console supports. + + @return The buffer to the text modes column and row information. + Caller is responsible to free it when it's non-NULL. + +**/ +TERMINAL_CONSOLE_MODE_DATA * +InitializeTerminalConsoleTextMode ( + OUT INT32 *TextModeCount +) +{ + TERMINAL_CONSOLE_MODE_DATA *TextModeData; + + ASSERT (TextModeCount != NULL); + + TextModeData = AllocateCopyPool (sizeof (mTerminalConsoleModeData), mTerminalConsoleModeData); + if (TextModeData == NULL) { + return NULL; + } + *TextModeCount = ARRAY_SIZE (mTerminalConsoleModeData); + + DEBUG_CODE ( + INT32 Index; + for (Index = 0; Index < *TextModeCount; Index++) { + DEBUG ((DEBUG_INFO, "Terminal - Mode %d, Column = %d, Row = %d\n", + Index, TextModeData[Index].Columns, TextModeData[Index].Rows)); + } + ); + return TextModeData; +} + +/** + Stop the terminal state machine. + + @param TerminalDevice The terminal device. +**/ +VOID +StopTerminalStateMachine ( + TERMINAL_DEV *TerminalDevice + ) +{ + EFI_TPL OriginalTpl; + + OriginalTpl = gBS->RaiseTPL (TPL_NOTIFY); + + gBS->CloseEvent (TerminalDevice->TimerEvent); + gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut); + + gBS->RestoreTPL (OriginalTpl); +} + +/** + Start the terminal state machine. + + @param TerminalDevice The terminal device. +**/ +VOID +StartTerminalStateMachine ( + TERMINAL_DEV *TerminalDevice + ) +{ + EFI_STATUS Status; + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + TerminalConInTimerHandler, + TerminalDevice, + &TerminalDevice->TimerEvent + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->SetTimer ( + TerminalDevice->TimerEvent, + TimerPeriodic, + KEYBOARD_TIMER_INTERVAL + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TerminalDevice->TwoSecondTimeOut + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Initialize the controller name table. + + @param TerminalType The terminal type. + @param ControllerNameTable The controller name table. + + @retval EFI_SUCCESS The controller name table is initialized successfully. + @retval others Return status of AddUnicodeString2 (). +**/ +EFI_STATUS +InitializeControllerNameTable ( + TERMINAL_TYPE TerminalType, + EFI_UNICODE_STRING_TABLE **ControllerNameTable +) +{ + EFI_STATUS Status; + EFI_UNICODE_STRING_TABLE *Table; + + ASSERT (TerminalType < ARRAY_SIZE (mTerminalType)); + Table = NULL; + Status = AddUnicodeString2 ( + "eng", + gTerminalComponentName.SupportedLanguages, + &Table, + mSerialConsoleNames[TerminalType], + TRUE + ); + if (!EFI_ERROR (Status)) { + Status = AddUnicodeString2 ( + "en", + gTerminalComponentName2.SupportedLanguages, + &Table, + mSerialConsoleNames[TerminalType], + FALSE + ); + if (EFI_ERROR (Status)) { + FreeUnicodeStringTable (Table); + } + } + if (!EFI_ERROR (Status)) { + *ControllerNameTable = Table; + } + return Status; +} + +/** + Start this driver on Controller by opening a Serial IO protocol, + reading Device Path, and creating a child handle with a Simple Text In, + Simple Text In Ex and Simple Text Out protocol, and device path protocol. + And store Console Device Environment Variables. + + @param This Protocol instance pointer. + @param Controller Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval EFI_ALREADY_STARTED This driver is already running on Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +TerminalDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Vendor; + EFI_HANDLE SerialIoHandle; + EFI_SERIAL_IO_MODE *Mode; + UINTN SerialInTimeOut; + TERMINAL_DEV *TerminalDevice; + UINT8 TerminalType; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + UINTN Index; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextInput; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + + // + // Get the Device Path Protocol to build the device path of the child device + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_ALREADY_STARTED)); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + // + // Open the Serial I/O Protocol BY_DRIVER. It might already be started. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_ALREADY_STARTED)); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + if (!IsHotPlugDevice (ParentDevicePath)) { + // + // if the serial device is a hot plug device, do not update the + // ConInDev, ConOutDev, and StdErrDev variables. + // + TerminalUpdateConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalUpdateConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalUpdateConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + } + + // + // Do not create any child for END remaining device path. + // + if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + + if (Status == EFI_ALREADY_STARTED) { + + if (RemainingDevicePath == NULL) { + // + // If RemainingDevicePath is NULL or is the End of Device Path Node + // + return EFI_SUCCESS; + } + + // + // This driver can only produce one child per serial port. + // Change its terminal type as remaining device path requests. + // + Status = gBS->OpenProtocolInformation ( + Controller, + &gEfiSerialIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (!EFI_ERROR (Status)) { + Status = EFI_NOT_FOUND; + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiSimpleTextInProtocolGuid, + (VOID **) &SimpleTextInput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (SimpleTextInput); + TerminalType = TerminalTypeFromGuid (&((VENDOR_DEVICE_PATH *) RemainingDevicePath)->Guid); + ASSERT (TerminalType < ARRAY_SIZE (mTerminalType)); + if (TerminalDevice->TerminalType != TerminalType) { + Status = InitializeControllerNameTable (TerminalType, &ControllerNameTable); + if (!EFI_ERROR (Status)) { + StopTerminalStateMachine (TerminalDevice); + // + // Update the device path + // + Vendor = TerminalDevice->DevicePath; + Status = gBS->LocateDevicePath (&gEfiSerialIoProtocolGuid, &Vendor, &SerialIoHandle); + ASSERT_EFI_ERROR (Status); + CopyGuid (&((VENDOR_DEVICE_PATH *) Vendor)->Guid, mTerminalType[TerminalType]); + Status = gBS->ReinstallProtocolInterface ( + TerminalDevice->Handle, + &gEfiDevicePathProtocolGuid, + TerminalDevice->DevicePath, + TerminalDevice->DevicePath + ); + if (!EFI_ERROR (Status)) { + TerminalDevice->TerminalType = TerminalType; + StartTerminalStateMachine (TerminalDevice); + FreeUnicodeStringTable (TerminalDevice->ControllerNameTable); + TerminalDevice->ControllerNameTable = ControllerNameTable; + } else { + // + // Restore the device path on failure + // + CopyGuid (&((VENDOR_DEVICE_PATH *) Vendor)->Guid, mTerminalType[TerminalDevice->TerminalType]); + FreeUnicodeStringTable (ControllerNameTable); + } + } + } + } + break; + } + } + FreePool (OpenInfoBuffer); + } + return Status; + } + + // + // Initialize the Terminal Dev + // + TerminalDevice = AllocateCopyPool (sizeof (TERMINAL_DEV), &mTerminalDevTemplate); + if (TerminalDevice == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CloseProtocols; + } + + if (RemainingDevicePath == NULL) { + // + // If RemainingDevicePath is NULL, use default terminal type + // + TerminalDevice->TerminalType = PcdGet8 (PcdDefaultTerminalType); + } else { + // + // End of Device Path Node is handled in above. + // + ASSERT (!IsDevicePathEnd (RemainingDevicePath)); + // + // If RemainingDevicePath isn't the End of Device Path Node, + // Use the RemainingDevicePath to determine the terminal type + // + TerminalDevice->TerminalType = TerminalTypeFromGuid (&((VENDOR_DEVICE_PATH *) RemainingDevicePath)->Guid); + } + ASSERT (TerminalDevice->TerminalType < ARRAY_SIZE (mTerminalType)); + TerminalDevice->SerialIo = SerialIo; + + // + // Build the component name for the child device + // + Status = InitializeControllerNameTable (TerminalDevice->TerminalType, &TerminalDevice->ControllerNameTable); + if (EFI_ERROR (Status)) { + goto FreeResources; + } + + // + // Build the device path for the child device + // + Status = SetTerminalDevicePath (TerminalDevice->TerminalType, ParentDevicePath, &TerminalDevice->DevicePath); + if (EFI_ERROR (Status)) { + goto FreeResources; + } + + InitializeListHead (&TerminalDevice->NotifyList); + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + TerminalConInWaitForKeyEx, + TerminalDevice, + &TerminalDevice->SimpleInputEx.WaitForKeyEx + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + TerminalConInWaitForKey, + TerminalDevice, + &TerminalDevice->SimpleInput.WaitForKey + ); + ASSERT_EFI_ERROR (Status); + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + KeyNotifyProcessHandler, + TerminalDevice, + &TerminalDevice->KeyNotifyProcessEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // Allocates and initializes the FIFO buffer to be zero, used for accommodating + // the pre-read pending characters. + // + TerminalDevice->RawFiFo = AllocateZeroPool (sizeof (RAW_DATA_FIFO)); + if (TerminalDevice->RawFiFo == NULL) { + goto FreeResources; + } + TerminalDevice->UnicodeFiFo = AllocateZeroPool (sizeof (UNICODE_FIFO)); + if (TerminalDevice->UnicodeFiFo == NULL) { + goto FreeResources; + } + TerminalDevice->EfiKeyFiFo = AllocateZeroPool (sizeof (EFI_KEY_FIFO)); + if (TerminalDevice->EfiKeyFiFo == NULL) { + goto FreeResources; + } + TerminalDevice->EfiKeyFiFoForNotify = AllocateZeroPool (sizeof (EFI_KEY_FIFO)); + if (TerminalDevice->EfiKeyFiFoForNotify == NULL) { + goto FreeResources; + } + + // + // Set the timeout value of serial buffer for keystroke response performance issue + // + Mode = TerminalDevice->SerialIo->Mode; + + SerialInTimeOut = 0; + if (Mode->BaudRate != 0) { + SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate; + } + + Status = TerminalDevice->SerialIo->SetAttributes ( + TerminalDevice->SerialIo, + Mode->BaudRate, + Mode->ReceiveFifoDepth, + (UINT32) SerialInTimeOut, + (EFI_PARITY_TYPE) (Mode->Parity), + (UINT8) Mode->DataBits, + (EFI_STOP_BITS_TYPE) (Mode->StopBits) + ); + if (EFI_ERROR (Status)) { + // + // if set attributes operation fails, invalidate + // the value of SerialInTimeOut,thus make it + // inconsistent with the default timeout value + // of serial buffer. This will invoke the recalculation + // in the readkeystroke routine. + // + TerminalDevice->SerialInTimeOut = 0; + } else { + TerminalDevice->SerialInTimeOut = SerialInTimeOut; + } + + SimpleTextOutput = &TerminalDevice->SimpleTextOutput; + SimpleTextInput = &TerminalDevice->SimpleInput; + + // + // Initialize SimpleTextOut instance + // + SimpleTextOutput->Mode = &TerminalDevice->SimpleTextOutputMode; + TerminalDevice->TerminalConsoleModeData = InitializeTerminalConsoleTextMode ( + &SimpleTextOutput->Mode->MaxMode + ); + if (TerminalDevice->TerminalConsoleModeData == NULL) { + goto FreeResources; + } + // + // For terminal devices, cursor is always visible + // + SimpleTextOutput->Mode->CursorVisible = TRUE; + Status = SimpleTextOutput->SetAttribute (SimpleTextOutput, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + if (!EFI_ERROR (Status)) { + Status = SimpleTextOutput->Reset (SimpleTextOutput, FALSE); + } + if (EFI_ERROR (Status)) { + goto ReportError; + } + + // + // Initialize SimpleTextInput instance + // + Status = SimpleTextInput->Reset (SimpleTextInput, FALSE); + if (EFI_ERROR (Status)) { + goto ReportError; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &TerminalDevice->Handle, + &gEfiSimpleTextInProtocolGuid, &TerminalDevice->SimpleInput, + &gEfiSimpleTextInputExProtocolGuid, &TerminalDevice->SimpleInputEx, + &gEfiSimpleTextOutProtocolGuid, &TerminalDevice->SimpleTextOutput, + &gEfiDevicePathProtocolGuid, TerminalDevice->DevicePath, + NULL + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + (VOID **) &TerminalDevice->SerialIo, + This->DriverBindingHandle, + TerminalDevice->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + ASSERT_EFI_ERROR (Status); + StartTerminalStateMachine (TerminalDevice); + return Status; + } + +ReportError: + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR), + ParentDevicePath + ); + +FreeResources: + ASSERT (TerminalDevice != NULL); + + if (TerminalDevice->SimpleInput.WaitForKey != NULL) { + gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey); + } + if (TerminalDevice->SimpleInputEx.WaitForKeyEx != NULL) { + gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx); + } + if (TerminalDevice->KeyNotifyProcessEvent != NULL) { + gBS->CloseEvent (TerminalDevice->KeyNotifyProcessEvent); + } + + if (TerminalDevice->RawFiFo != NULL) { + FreePool (TerminalDevice->RawFiFo); + } + if (TerminalDevice->UnicodeFiFo != NULL) { + FreePool (TerminalDevice->UnicodeFiFo); + } + if (TerminalDevice->EfiKeyFiFo != NULL) { + FreePool (TerminalDevice->EfiKeyFiFo); + } + if (TerminalDevice->EfiKeyFiFoForNotify != NULL) { + FreePool (TerminalDevice->EfiKeyFiFoForNotify); + } + + if (TerminalDevice->ControllerNameTable != NULL) { + FreeUnicodeStringTable (TerminalDevice->ControllerNameTable); + } + + if (TerminalDevice->DevicePath != NULL) { + FreePool (TerminalDevice->DevicePath); + } + + if (TerminalDevice->TerminalConsoleModeData != NULL) { + FreePool (TerminalDevice->TerminalConsoleModeData); + } + + FreePool (TerminalDevice); + +CloseProtocols: + + // + // Remove Parent Device Path from + // the Console Device Environment Variables + // + TerminalRemoveConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + Stop this driver on Controller by closing Simple Text In, Simple Text + In Ex, Simple Text Out protocol, and removing parent device path from + Console Device Environment Variables. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval other This driver could not be removed from this device. + +**/ +EFI_STATUS +EFIAPI +TerminalDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + UINTN Index; + BOOLEAN AllChildrenStopped; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput; + TERMINAL_DEV *TerminalDevice; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + + // + // Complete all outstanding transactions to Controller. + // Don't allow any new transaction to Controller to be started. + // + if (NumberOfChildren == 0) { + // + // Close the bus driver + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + + // + // Remove Parent Device Path from + // the Console Device Environment Variables + // + TerminalRemoveConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + + gBS->CloseProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiSimpleTextOutProtocolGuid, + (VOID **) &SimpleTextOutput, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput); + + gBS->CloseProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiSimpleTextInProtocolGuid, + &TerminalDevice->SimpleInput, + &gEfiSimpleTextInputExProtocolGuid, + &TerminalDevice->SimpleInputEx, + &gEfiSimpleTextOutProtocolGuid, + &TerminalDevice->SimpleTextOutput, + &gEfiDevicePathProtocolGuid, + TerminalDevice->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + + FreeUnicodeStringTable (TerminalDevice->ControllerNameTable); + StopTerminalStateMachine (TerminalDevice); + gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey); + gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx); + gBS->CloseEvent (TerminalDevice->KeyNotifyProcessEvent); + TerminalFreeNotifyList (&TerminalDevice->NotifyList); + FreePool (TerminalDevice->DevicePath); + FreePool (TerminalDevice->TerminalConsoleModeData); + FreePool (TerminalDevice); + } + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Compare a device path data structure to that of all the nodes of a + second device path instance. + + @param Multi A pointer to a multi-instance device path data structure. + @param Single A pointer to a single-instance device path data structure. + + @retval TRUE If the Single is contained within Multi. + @retval FALSE The Single is not match within Multi. + +**/ +BOOLEAN +MatchDevicePaths ( + IN EFI_DEVICE_PATH_PROTOCOL *Multi, + IN EFI_DEVICE_PATH_PROTOCOL *Single + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInst; + UINTN Size; + + DevicePath = Multi; + DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size); + // + // Search for the match of 'Single' in 'Multi' + // + while (DevicePathInst != NULL) { + // + // If the single device path is found in multiple device paths, + // return success + // + if (CompareMem (Single, DevicePathInst, Size) == 0) { + FreePool (DevicePathInst); + return TRUE; + } + + FreePool (DevicePathInst); + DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size); + } + + return FALSE; +} + +/** + Update terminal device path in Console Device Environment Variables. + + @param VariableName The Console Device Environment Variable. + @param ParentDevicePath The terminal device path to be updated. + +**/ +VOID +TerminalUpdateConsoleDevVariable ( + IN CHAR16 *VariableName, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath + ) +{ + EFI_STATUS Status; + UINTN NameSize; + UINTN VariableSize; + TERMINAL_TYPE TerminalType; + EFI_DEVICE_PATH_PROTOCOL *Variable; + EFI_DEVICE_PATH_PROTOCOL *NewVariable; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EDKII_SET_VARIABLE_STATUS *SetVariableStatus; + + // + // Get global variable and its size according to the name given. + // + Status = GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL); + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + Variable = NULL; + } + if (EFI_ERROR (Status)) { + return; + } + + // + // Append terminal device path onto the variable. + // + for (TerminalType = 0; TerminalType < ARRAY_SIZE (mTerminalType); TerminalType++) { + SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath); + + if (TempDevicePath != NULL) { + if (!MatchDevicePaths (Variable, TempDevicePath)) { + NewVariable = AppendDevicePathInstance (Variable, TempDevicePath); + if (NewVariable != NULL) { + if (Variable != NULL) { + FreePool (Variable); + } + Variable = NewVariable; + } + } + + FreePool (TempDevicePath); + } + + } + + VariableSize = GetDevicePathSize (Variable); + + Status = gRT->SetVariable ( + VariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + VariableSize, + Variable + ); + + if (EFI_ERROR (Status)) { + NameSize = StrSize (VariableName); + SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize); + if (SetVariableStatus != NULL) { + CopyGuid (&SetVariableStatus->Guid, &gEfiGlobalVariableGuid); + SetVariableStatus->NameSize = NameSize; + SetVariableStatus->DataSize = VariableSize; + SetVariableStatus->SetStatus = Status; + SetVariableStatus->Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; + CopyMem (SetVariableStatus + 1, VariableName, NameSize); + CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Variable, VariableSize); + + REPORT_STATUS_CODE_EX ( + EFI_ERROR_CODE, + PcdGet32 (PcdErrorCodeSetVariable), + 0, + NULL, + &gEdkiiStatusCodeDataTypeVariableGuid, + SetVariableStatus, + sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize + ); + + FreePool (SetVariableStatus); + } + } + + FreePool (Variable); + + return ; +} + + +/** + Remove terminal device path from Console Device Environment Variables. + + @param VariableName Console Device Environment Variables. + @param ParentDevicePath The terminal device path to be updated. + +**/ +VOID +TerminalRemoveConsoleDevVariable ( + IN CHAR16 *VariableName, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath + ) +{ + EFI_STATUS Status; + BOOLEAN FoundOne; + BOOLEAN Match; + UINTN VariableSize; + UINTN InstanceSize; + TERMINAL_TYPE TerminalType; + EFI_DEVICE_PATH_PROTOCOL *Instance; + EFI_DEVICE_PATH_PROTOCOL *Variable; + EFI_DEVICE_PATH_PROTOCOL *OriginalVariable; + EFI_DEVICE_PATH_PROTOCOL *NewVariable; + EFI_DEVICE_PATH_PROTOCOL *SavedNewVariable; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + + Instance = NULL; + + // + // Get global variable and its size according to the name given. + // + GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL); + if (Variable == NULL) { + return ; + } + + FoundOne = FALSE; + OriginalVariable = Variable; + NewVariable = NULL; + + // + // Get first device path instance from Variable + // + Instance = GetNextDevicePathInstance (&Variable, &InstanceSize); + if (Instance == NULL) { + FreePool (OriginalVariable); + return ; + } + // + // Loop through all the device path instances of Variable + // + do { + // + // Loop through all the terminal types that this driver supports + // + Match = FALSE; + for (TerminalType = 0; TerminalType < ARRAY_SIZE (mTerminalType); TerminalType++) { + + SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath); + + // + // Compare the generated device path to the current device path instance + // + if (TempDevicePath != NULL) { + if (CompareMem (Instance, TempDevicePath, InstanceSize) == 0) { + Match = TRUE; + FoundOne = TRUE; + } + + FreePool (TempDevicePath); + } + } + // + // If a match was not found, then keep the current device path instance + // + if (!Match) { + SavedNewVariable = NewVariable; + NewVariable = AppendDevicePathInstance (NewVariable, Instance); + if (SavedNewVariable != NULL) { + FreePool (SavedNewVariable); + } + } + // + // Get next device path instance from Variable + // + FreePool (Instance); + Instance = GetNextDevicePathInstance (&Variable, &InstanceSize); + } while (Instance != NULL); + + FreePool (OriginalVariable); + + if (FoundOne) { + VariableSize = GetDevicePathSize (NewVariable); + + Status = gRT->SetVariable ( + VariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + VariableSize, + NewVariable + ); + // + // Shrinking variable with existing variable driver implementation shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + } + + if (NewVariable != NULL) { + FreePool (NewVariable); + } + + return ; +} + +/** + Build terminal device path according to terminal type. + + @param TerminalType The terminal type is PC ANSI, VT100, VT100+ or VT-UTF8. + @param ParentDevicePath Parent device path. + @param TerminalDevicePath Returned terminal device path, if building successfully. + + @retval EFI_UNSUPPORTED Terminal does not belong to the supported type. + @retval EFI_OUT_OF_RESOURCES Generate terminal device path failed. + @retval EFI_SUCCESS Build terminal device path successfully. + +**/ +EFI_STATUS +SetTerminalDevicePath ( + IN TERMINAL_TYPE TerminalType, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath + ) +{ + VENDOR_DEVICE_PATH Node; + + ASSERT (TerminalType < ARRAY_SIZE (mTerminalType)); + Node.Header.Type = MESSAGING_DEVICE_PATH; + Node.Header.SubType = MSG_VENDOR_DP; + SetDevicePathNodeLength (&Node.Header, sizeof (VENDOR_DEVICE_PATH)); + CopyGuid (&Node.Guid, mTerminalType[TerminalType]); + + // + // Append the terminal node onto parent device path + // to generate a complete terminal device path. + // + *TerminalDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &Node + ); + if (*TerminalDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + The user Entry Point for module Terminal. The user code starts with this function. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeTerminal( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gTerminalDriverBinding, + ImageHandle, + &gTerminalComponentName, + &gTerminalComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Check if the device supports hot-plug through its device path. + + This function could be updated to check more types of Hot Plug devices. + Currently, it checks USB and PCCard device. + + @param DevicePath Pointer to device's device path. + + @retval TRUE The devcie is a hot-plug device + @retval FALSE The devcie is not a hot-plug device. + +**/ +BOOLEAN +IsHotPlugDevice ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *CheckDevicePath; + + CheckDevicePath = DevicePath; + while (!IsDevicePathEnd (CheckDevicePath)) { + // + // Check device whether is hot plug device or not throught Device Path + // + if ((DevicePathType (CheckDevicePath) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (CheckDevicePath) == MSG_USB_DP || + DevicePathSubType (CheckDevicePath) == MSG_USB_CLASS_DP || + DevicePathSubType (CheckDevicePath) == MSG_USB_WWID_DP)) { + // + // If Device is USB device + // + return TRUE; + } + if ((DevicePathType (CheckDevicePath) == HARDWARE_DEVICE_PATH) && + (DevicePathSubType (CheckDevicePath) == HW_PCCARD_DP)) { + // + // If Device is PCCard + // + return TRUE; + } + + CheckDevicePath = NextDevicePathNode (CheckDevicePath); + } + + return FALSE; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.h b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.h new file mode 100644 index 000000000..378ace13c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.h @@ -0,0 +1,1458 @@ +/** @file + Header file for Terminal driver. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (C) 2016 Silicon Graphics, Inc. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _TERMINAL_H_ +#define _TERMINAL_H_ + + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define RAW_FIFO_MAX_NUMBER 256 +#define FIFO_MAX_NUMBER 128 + +typedef struct { + UINT8 Head; + UINT8 Tail; + UINT8 Data[RAW_FIFO_MAX_NUMBER + 1]; +} RAW_DATA_FIFO; + +typedef struct { + UINT8 Head; + UINT8 Tail; + UINT16 Data[FIFO_MAX_NUMBER + 1]; +} UNICODE_FIFO; + +typedef struct { + UINT8 Head; + UINT8 Tail; + EFI_INPUT_KEY Data[FIFO_MAX_NUMBER + 1]; +} EFI_KEY_FIFO; + +typedef struct { + UINTN Columns; + UINTN Rows; +} TERMINAL_CONSOLE_MODE_DATA; + +#define KEYBOARD_TIMER_INTERVAL 200000 // 0.02s + +#define TERMINAL_DEV_SIGNATURE SIGNATURE_32 ('t', 'm', 'n', 'l') + +#define TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE SIGNATURE_32 ('t', 'm', 'e', 'n') + +typedef struct _TERMINAL_CONSOLE_IN_EX_NOTIFY { + UINTN Signature; + EFI_KEY_DATA KeyData; + EFI_KEY_NOTIFY_FUNCTION KeyNotificationFn; + LIST_ENTRY NotifyEntry; +} TERMINAL_CONSOLE_IN_EX_NOTIFY; + +typedef enum { + TerminalTypePcAnsi, + TerminalTypeVt100, + TerminalTypeVt100Plus, + TerminalTypeVtUtf8, + TerminalTypeTtyTerm, + TerminalTypeLinux, + TerminalTypeXtermR6, + TerminalTypeVt400, + TerminalTypeSCO +} TERMINAL_TYPE; + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + TERMINAL_TYPE TerminalType; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL SimpleInput; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SimpleTextOutput; + EFI_SIMPLE_TEXT_OUTPUT_MODE SimpleTextOutputMode; + TERMINAL_CONSOLE_MODE_DATA *TerminalConsoleModeData; + UINTN SerialInTimeOut; + RAW_DATA_FIFO *RawFiFo; + UNICODE_FIFO *UnicodeFiFo; + EFI_KEY_FIFO *EfiKeyFiFo; + EFI_KEY_FIFO *EfiKeyFiFoForNotify; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + EFI_EVENT TimerEvent; + EFI_EVENT TwoSecondTimeOut; + UINT32 InputState; + UINT32 ResetState; + UINT16 TtyEscapeStr[3]; + INTN TtyEscapeIndex; + + // + // Esc could not be output to the screen by user, + // but the terminal driver need to output it to + // the terminal emulation software to send control sequence. + // This boolean is used by the terminal driver only + // to indicate whether the Esc could be sent or not. + // + BOOLEAN OutputEscChar; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL SimpleInputEx; + LIST_ENTRY NotifyList; + EFI_EVENT KeyNotifyProcessEvent; +} TERMINAL_DEV; + +#define INPUT_STATE_DEFAULT 0x00 +#define INPUT_STATE_ESC 0x01 +#define INPUT_STATE_CSI 0x02 +#define INPUT_STATE_LEFTOPENBRACKET 0x04 +#define INPUT_STATE_O 0x08 +#define INPUT_STATE_2 0x10 +#define INPUT_STATE_LEFTOPENBRACKET_TTY 0x20 +#define INPUT_STATE_1 0x40 +#define INPUT_STATE_LEFTOPENBRACKET_2ND 0x80 + +#define RESET_STATE_DEFAULT 0x00 +#define RESET_STATE_ESC_R 0x01 +#define RESET_STATE_ESC_R_ESC_R 0x02 + +#define TERMINAL_CON_IN_DEV_FROM_THIS(a) CR (a, TERMINAL_DEV, SimpleInput, TERMINAL_DEV_SIGNATURE) +#define TERMINAL_CON_OUT_DEV_FROM_THIS(a) CR (a, TERMINAL_DEV, SimpleTextOutput, TERMINAL_DEV_SIGNATURE) +#define TERMINAL_CON_IN_EX_DEV_FROM_THIS(a) CR (a, TERMINAL_DEV, SimpleInputEx, TERMINAL_DEV_SIGNATURE) + +typedef union { + UINT8 Utf8_1; + UINT8 Utf8_2[2]; + UINT8 Utf8_3[3]; +} UTF8_CHAR; + +#define LEFTOPENBRACKET 0x5b // '[' +#define ACAP 0x41 +#define BCAP 0x42 +#define CCAP 0x43 +#define DCAP 0x44 + +#define BACKSPACE 8 +#define ESC 27 +#define CSI 0x9B +#define DEL 127 +#define BRIGHT_CONTROL_OFFSET 2 +#define FOREGROUND_CONTROL_OFFSET 6 +#define BACKGROUND_CONTROL_OFFSET 11 +#define ROW_OFFSET 2 +#define COLUMN_OFFSET 5 +#define FW_BACK_OFFSET 2 + +typedef struct { + UINT16 Unicode; + CHAR8 PcAnsi; + CHAR8 Ascii; +} UNICODE_TO_CHAR; + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gTerminalDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gTerminalComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gTerminalComponentName2; + +/** + The user Entry Point for module Terminal. The user code starts with this 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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeTerminal ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.Reset(). + This driver only perform dependent serial device reset regardless of + the value of ExtendeVerification + + @param This Indicates the calling context. + @param ExtendedVerification Skip by this driver. + + @retval EFI_SUCCESS The reset operation succeeds. + @retval EFI_DEVICE_ERROR The dependent serial port reset fails. + +**/ +EFI_STATUS +EFIAPI +TerminalConInReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + + +/** + Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke(). + + @param This Indicates the calling context. + @param Key A pointer to a buffer that is filled in with the + keystroke information for the key that was sent + from terminal. + + @retval EFI_SUCCESS The keystroke information is returned successfully. + @retval EFI_NOT_READY There is no keystroke data available. + @retval EFI_DEVICE_ERROR The dependent serial device encounters error. + +**/ +EFI_STATUS +EFIAPI +TerminalConInReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ); + +/** + Check if the key already has been registered. + + @param RegsiteredData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + registered. + @param InputData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval TRUE Key be pressed matches a registered key. + @retval FALSE Match failed. + +**/ +BOOLEAN +IsKeyRegistered ( + IN EFI_KEY_DATA *RegsiteredData, + IN EFI_KEY_DATA *InputData + ); + +/** + Event notification function for EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event + Signal the event if there is key available + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. + +**/ +VOID +EFIAPI +TerminalConInWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// Simple Text Input Ex protocol prototypes +// + +/** + Reset the input device and optionally run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +TerminalConInResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existence of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data available. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due + to hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +TerminalConInReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ); + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + could not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set its + state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +TerminalConInSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ); + +/** + Register a notification function for a particular keystroke for the input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the + keystroke information data for the key that was + pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState + and KeyData.KeyState.KeyShiftState are 0, then any incomplete + keystroke will trigger a notification of the KeyNotificationFunction. + @param KeyNotificationFunction Points to the function to be called when the key + sequence is typed specified by KeyData. This notification function + should be called at <=TPL_CALLBACK. + @param NotifyHandle Points to the unique handle assigned to the + registered notification. + + @retval EFI_SUCCESS The notification function was registered + successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data + structures. + @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +TerminalConInRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ); + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function being + unregistered. + + @retval EFI_SUCCESS The notification function was unregistered + successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + @retval EFI_NOT_FOUND Can not find the matching entry in database. + +**/ +EFI_STATUS +EFIAPI +TerminalConInUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ); + +/** + Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event + Signal the event if there is key available + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. + +**/ +VOID +EFIAPI +TerminalConInWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.Reset(). + If ExtendeVerification is TRUE, then perform dependent serial device reset, + and set display mode to mode 0. + If ExtendedVerification is FALSE, only set display mode to mode 0. + + @param This Indicates the calling context. + @param ExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS The reset operation succeeds. + @retval EFI_DEVICE_ERROR The terminal is not functioning correctly or the serial port reset fails. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString(). + The Unicode string will be converted to terminal expressible data stream + and send to terminal via serial port. + + @param This Indicates the calling context. + @param WString The Null-terminated Unicode string to be displayed + on the terminal screen. + + @retval EFI_SUCCESS The string is output successfully. + @retval EFI_DEVICE_ERROR The serial port fails to send the string out. + @retval EFI_WARN_UNKNOWN_GLYPH Indicates that some of the characters in the Unicode string could not + be rendered and are skipped. + @retval EFI_UNSUPPORTED If current display mode is out of range. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.TestString(). + If one of the characters in the *Wstring is + neither valid Unicode drawing characters, + not ASCII code, then this function will return + EFI_UNSUPPORTED. + + @param This Indicates the calling context. + @param WString The Null-terminated Unicode string to be tested. + + @retval EFI_SUCCESS The terminal is capable of rendering the output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be rendered. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.QueryMode(). + It returns information for an available text mode + that the terminal supports. + In this driver, we support text mode 80x25 (mode 0), + 80x50 (mode 1), 100x31 (mode 2). + + @param This Indicates the calling context. + @param ModeNumber The mode number to return information on. + @param Columns The returned columns of the requested mode. + @param Rows The returned rows of the requested mode. + + @retval EFI_SUCCESS The requested mode information is returned. + @retval EFI_UNSUPPORTED The mode number is not valid. + @retval EFI_DEVICE_ERROR + +**/ +EFI_STATUS +EFIAPI +TerminalConOutQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ); + +/** + Implements EFI_SIMPLE_TEXT_OUT.SetMode(). + Set the terminal to a specified display mode. + In this driver, we only support mode 0. + + @param This Indicates the calling context. + @param ModeNumber The text mode to set. + + @retval EFI_SUCCESS The requested text mode is set. + @retval EFI_DEVICE_ERROR The requested text mode cannot be set + because of serial device error. + @retval EFI_UNSUPPORTED The text mode number is not valid. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ); + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). + + @param This Indicates the calling context. + @param Attribute The attribute to set. Only bit0..6 are valid, all other bits + are undefined and must be zero. + + @retval EFI_SUCCESS The requested attribute is set. + @retval EFI_DEVICE_ERROR The requested attribute cannot be set due to serial port error. + @retval EFI_UNSUPPORTED The attribute requested is not defined by EFI spec. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ); + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.ClearScreen(). + It clears the ANSI terminal's display to the + currently selected background color. + + @param This Indicates the calling context. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The terminal screen cannot be cleared due to serial port error. + @retval EFI_UNSUPPORTED The terminal is not in a valid display mode. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ); + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetCursorPosition(). + + @param This Indicates the calling context. + @param Column The row to set cursor to. + @param Row The column to set cursor to. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The request fails due to serial port error. + @retval EFI_UNSUPPORTED The terminal is not in a valid text mode, or the cursor position + is invalid for current mode. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ); + +/** + Implements SIMPLE_TEXT_OUTPUT.EnableCursor(). + In this driver, the cursor cannot be hidden. + + @param This Indicates the calling context. + @param Visible If TRUE, the cursor is set to be visible, + If FALSE, the cursor is set to be invisible. + + @retval EFI_SUCCESS The request is valid. + @retval EFI_UNSUPPORTED The terminal does not support cursor hidden. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ); + +/** + Test to see if this driver supports Controller. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +TerminalDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on Controller by opening a Serial IO protocol, + reading Device Path, and creating a child handle with a Simple Text In, + Simple Text In Ex and Simple Text Out protocol, and device path protocol. + And store Console Device Environment Variables. + + @param This Protocol instance pointer. + @param Controller Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval EFI_ALREADY_STARTED This driver is already running on Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +TerminalDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + + +/** + Stop this driver on Controller by closing Simple Text In, Simple Text + In Ex, Simple Text Out protocol, and removing parent device path from + Console Device Environment Variables. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval other This driver could not be removed from this device. + +**/ +EFI_STATUS +EFIAPI +TerminalDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Free notify functions list. + + @param ListHead The list head + + @retval EFI_SUCCESS Free the notify list successfully. + @retval EFI_INVALID_PARAMETER ListHead is NULL. + +**/ +EFI_STATUS +TerminalFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TerminalComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TerminalComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// internal functions +// + +/** + Check for a pending key in the Efi Key FIFO or Serial device buffer. + + @param This Indicates the calling context. + + @retval EFI_SUCCESS There is key pending. + @retval EFI_NOT_READY There is no key pending. + @retval EFI_DEVICE_ERROR If Serial IO is not attached to serial device. + +**/ +EFI_STATUS +TerminalConInCheckForKey ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This + ); + +/** + Update terminal device path in Console Device Environment Variables. + + @param VariableName The Console Device Environment Variable. + @param ParentDevicePath The terminal device path to be updated. + + @return None. + +**/ +VOID +TerminalUpdateConsoleDevVariable ( + IN CHAR16 *VariableName, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath + ); + +/** + Remove console device variable. + + @param VariableName A pointer to the variable name. + @param ParentDevicePath A pointer to the parent device path. + +**/ +VOID +TerminalRemoveConsoleDevVariable ( + IN CHAR16 *VariableName, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath + ); + +/** + Build termial device path according to terminal type. + + @param TerminalType The terminal type is PC ANSI, VT100, VT100+, VT-UTF8, TTY-Term, + Linux, XtermR6, VT400 and SCO. + @param ParentDevicePath Parent device path. + @param TerminalDevicePath Returned terminal device path, if building successfully. + + @retval EFI_UNSUPPORTED Terminal does not belong to the supported type. + @retval EFI_OUT_OF_RESOURCES Generate terminal device path failed. + @retval EFI_SUCCESS Build terminal device path successfully. + +**/ +EFI_STATUS +SetTerminalDevicePath ( + IN TERMINAL_TYPE TerminalType, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath + ); + +/** + Get one key out of serial buffer. + + @param SerialIo Serial I/O protocl attached to the serial device. + @param Input The fetched key. + + @retval EFI_NOT_READY If serial buffer is empty. + @retval EFI_DEVICE_ERROR If reading serial buffer encounter error. + @retval EFI_SUCCESS If reading serial buffer successfully, put + the fetched key to the parameter output. + +**/ +EFI_STATUS +GetOneKeyFromSerial ( + EFI_SERIAL_IO_PROTOCOL *SerialIo, + UINT8 *Input + ); + +/** + Insert one byte raw data into the Raw Data FIFO. + + @param TerminalDevice Terminal driver private structure. + @param Input The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If Raw Data buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +RawFiFoInsertOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT8 Input + ); + +/** + Remove one pre-fetched key out of the Raw Data FIFO. + + @param TerminalDevice Terminal driver private structure. + @param Output The key will be removed. + + @retval TRUE If insert successfully. + @retval FALSE If Raw Data FIFO buffer is empty before remove operation. + +**/ +BOOLEAN +RawFiFoRemoveOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT8 *Output + ); + +/** + Clarify whether Raw Data FIFO buffer is empty. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Raw Data FIFO buffer is empty. + @retval FALSE If Raw Data FIFO buffer is not empty. + +**/ +BOOLEAN +IsRawFiFoEmpty ( + TERMINAL_DEV *TerminalDevice + ); + +/** + Clarify whether Raw Data FIFO buffer is full. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Raw Data FIFO buffer is full. + @retval FALSE If Raw Data FIFO buffer is not full. + +**/ +BOOLEAN +IsRawFiFoFull ( + TERMINAL_DEV *TerminalDevice + ); + +/** + Insert one pre-fetched key into the FIFO buffer. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + @param Input The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If FIFO buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +EfiKeyFiFoForNotifyInsertOneKey ( + EFI_KEY_FIFO *EfiKeyFiFo, + EFI_INPUT_KEY *Input + ); + +/** + Remove one pre-fetched key out of the FIFO buffer. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + @param Output The key will be removed. + + @retval TRUE If insert successfully. + @retval FALSE If FIFO buffer is empty before remove operation. + +**/ +BOOLEAN +EfiKeyFiFoForNotifyRemoveOneKey ( + EFI_KEY_FIFO *EfiKeyFiFo, + EFI_INPUT_KEY *Output + ); + +/** + Clarify whether FIFO buffer is empty. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + + @retval TRUE If FIFO buffer is empty. + @retval FALSE If FIFO buffer is not empty. + +**/ +BOOLEAN +IsEfiKeyFiFoForNotifyEmpty ( + IN EFI_KEY_FIFO *EfiKeyFiFo + ); + +/** + Clarify whether FIFO buffer is full. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + + @retval TRUE If FIFO buffer is full. + @retval FALSE If FIFO buffer is not full. + +**/ +BOOLEAN +IsEfiKeyFiFoForNotifyFull ( + EFI_KEY_FIFO *EfiKeyFiFo + ); + +/** + Insert one pre-fetched key into the FIFO buffer. + + @param TerminalDevice Terminal driver private structure. + @param Key The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If FIFO buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +EfiKeyFiFoInsertOneKey ( + TERMINAL_DEV *TerminalDevice, + EFI_INPUT_KEY *Key + ); + +/** + Remove one pre-fetched key out of the FIFO buffer. + + @param TerminalDevice Terminal driver private structure. + @param Output The key will be removed. + + @retval TRUE If insert successfully. + @retval FALSE If FIFO buffer is empty before remove operation. + +**/ +BOOLEAN +EfiKeyFiFoRemoveOneKey ( + TERMINAL_DEV *TerminalDevice, + EFI_INPUT_KEY *Output + ); + +/** + Clarify whether FIFO buffer is empty. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If FIFO buffer is empty. + @retval FALSE If FIFO buffer is not empty. + +**/ +BOOLEAN +IsEfiKeyFiFoEmpty ( + TERMINAL_DEV *TerminalDevice + ); + +/** + Clarify whether FIFO buffer is full. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If FIFO buffer is full. + @retval FALSE If FIFO buffer is not full. + +**/ +BOOLEAN +IsEfiKeyFiFoFull ( + TERMINAL_DEV *TerminalDevice + ); + +/** + Insert one pre-fetched key into the Unicode FIFO buffer. + + @param TerminalDevice Terminal driver private structure. + @param Input The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If Unicode FIFO buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +UnicodeFiFoInsertOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT16 Input + ); + +/** + Remove one pre-fetched key out of the Unicode FIFO buffer. + The caller should guarantee that Unicode FIFO buffer is not empty + by IsUnicodeFiFoEmpty (). + + @param TerminalDevice Terminal driver private structure. + @param Output The key will be removed. + +**/ +VOID +UnicodeFiFoRemoveOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT16 *Output + ); + +/** + Clarify whether Unicode FIFO buffer is empty. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Unicode FIFO buffer is empty. + @retval FALSE If Unicode FIFO buffer is not empty. + +**/ +BOOLEAN +IsUnicodeFiFoEmpty ( + TERMINAL_DEV *TerminalDevice + ); + +/** + Clarify whether Unicode FIFO buffer is full. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Unicode FIFO buffer is full. + @retval FALSE If Unicode FIFO buffer is not full. + +**/ +BOOLEAN +IsUnicodeFiFoFull ( + TERMINAL_DEV *TerminalDevice + ); + + +/** + Translate raw data into Unicode (according to different encode), and + translate Unicode into key information. (according to different standard). + + @param TerminalDevice Terminal driver private structure. + +**/ +VOID +TranslateRawDataToEfiKey ( + IN TERMINAL_DEV *TerminalDevice + ); + +// +// internal functions for PC ANSI +// + +/** + Translate all raw data in the Raw FIFI into unicode, and insert + them into Unicode FIFO. + + @param TerminalDevice The terminal device. + +**/ +VOID +AnsiRawDataToUnicode ( + IN TERMINAL_DEV *TerminalDevice + ); + +/** + Converts a stream of Unicode characters from a terminal input device into EFI Keys that + can be read through the Simple Input Protocol. + + The table below shows the keyboard input mappings that this function supports. + If the ESC sequence listed in one of the columns is presented, then it is translated + into the coorespoding EFI Scan Code. If a matching sequence is not found, then the raw + key strokes are converted into EFI Keys. + + 2 seconds are allowed for an ESC sequence to be completed. If the ESC sequence is not + completed in 2 seconds, then the raw key strokes of the partial ESC sequence are + converted into EFI Keys. + There is one special input sequence that will force the system to reset. + This is ESC R ESC r ESC R. + + Symbols used in table below + =========================== + ESC = 0x1B + CSI = 0x9B + DEL = 0x7f + ^ = CTRL + +=========+======+===========+==========+==========+ + | | EFI | UEFI 2.0 | | | + | | Scan | | VT100+ | | + | KEY | Code | PC ANSI | VTUTF8 | VT100 | + +=========+======+===========+==========+==========+ + | NULL | 0x00 | | | | + | UP | 0x01 | ESC [ A | ESC [ A | ESC [ A | + | DOWN | 0x02 | ESC [ B | ESC [ B | ESC [ B | + | RIGHT | 0x03 | ESC [ C | ESC [ C | ESC [ C | + | LEFT | 0x04 | ESC [ D | ESC [ D | ESC [ D | + | HOME | 0x05 | ESC [ H | ESC h | ESC [ H | + | END | 0x06 | ESC [ F | ESC k | ESC [ K | + | INSERT | 0x07 | ESC [ @ | ESC + | ESC [ @ | + | | | ESC [ L | | ESC [ L | + | DELETE | 0x08 | ESC [ X | ESC - | ESC [ P | + | PG UP | 0x09 | ESC [ I | ESC ? | ESC [ V | + | | | | | ESC [ ? | + | PG DOWN | 0x0A | ESC [ G | ESC / | ESC [ U | + | | | | | ESC [ / | + | F1 | 0x0B | ESC [ M | ESC 1 | ESC O P | + | F2 | 0x0C | ESC [ N | ESC 2 | ESC O Q | + | F3 | 0x0D | ESC [ O | ESC 3 | ESC O w | + | F4 | 0x0E | ESC [ P | ESC 4 | ESC O x | + | F5 | 0x0F | ESC [ Q | ESC 5 | ESC O t | + | F6 | 0x10 | ESC [ R | ESC 6 | ESC O u | + | F7 | 0x11 | ESC [ S | ESC 7 | ESC O q | + | F8 | 0x12 | ESC [ T | ESC 8 | ESC O r | + | F9 | 0x13 | ESC [ U | ESC 9 | ESC O p | + | F10 | 0x14 | ESC [ V | ESC 0 | ESC O M | + | Escape | 0x17 | ESC | ESC | ESC | + | F11 | 0x15 | | ESC ! | | + | F12 | 0x16 | | ESC @ | | + +=========+======+===========+==========+==========+ + +Putty function key map: + +=========+======+===========+=============+=============+=============+=========+ + | | EFI | | | | | | + | | Scan | | | Normal | | | + | KEY | Code | VT100+ | Xterm R6 | VT400 | Linux | SCO | + +=========+======+===========+=============+=============+=============+=========+ + | F1 | 0x0B | ESC O P | ESC O P | ESC [ 1 1 ~ | ESC [ [ A | ESC [ M | + | F2 | 0x0C | ESC O Q | ESC O Q | ESC [ 1 2 ~ | ESC [ [ B | ESC [ N | + | F3 | 0x0D | ESC O R | ESC O R | ESC [ 1 3 ~ | ESC [ [ C | ESC [ O | + | F4 | 0x0E | ESC O S | ESC O S | ESC [ 1 4 ~ | ESC [ [ D | ESC [ P | + | F5 | 0x0F | ESC O T | ESC [ 1 5 ~ | ESC [ 1 5 ~ | ESC [ [ E | ESC [ Q | + | F6 | 0x10 | ESC O U | ESC [ 1 7 ~ | ESC [ 1 7 ~ | ESC [ 1 7 ~ | ESC [ R | + | F7 | 0x11 | ESC O V | ESC [ 1 8 ~ | ESC [ 1 8 ~ | ESC [ 1 8 ~ | ESC [ S | + | F8 | 0x12 | ESC O W | ESC [ 1 9 ~ | ESC [ 1 9 ~ | ESC [ 1 9 ~ | ESC [ T | + | F9 | 0x13 | ESC O X | ESC [ 2 0 ~ | ESC [ 2 0 ~ | ESC [ 2 0 ~ | ESC [ U | + | F10 | 0x14 | ESC O Y | ESC [ 2 1 ~ | ESC [ 2 1 ~ | ESC [ 2 1 ~ | ESC [ V | + | Escape | 0x17 | ESC | ESC | ESC | ESC | ESC | + | F11 | 0x15 | ESC O Z | ESC [ 2 3 ~ | ESC [ 2 3 ~ | ESC [ 2 3 ~ | ESC [ W | + | F12 | 0x16 | ESC O [ | ESC [ 2 4 ~ | ESC [ 2 4 ~ | ESC [ 2 4 ~ | ESC [ X | + +=========+======+===========+=============+=============+=============+=========+ + + + Special Mappings + ================ + ESC R ESC r ESC R = Reset System + + + @param TerminalDevice The terminal device to use to translate raw input into EFI Keys + +**/ +VOID +UnicodeToEfiKey ( + IN TERMINAL_DEV *TerminalDevice + ); + +/** + Check if input string is valid Ascii string, valid EFI control characters + or valid text graphics. + + @param TerminalDevice The terminal device. + @param WString The input string. + + @retval EFI_UNSUPPORTED If not all input characters are valid. + @retval EFI_SUCCESS If all input characters are valid. + +**/ +EFI_STATUS +AnsiTestString ( + IN TERMINAL_DEV *TerminalDevice, + IN CHAR16 *WString + ); + +// +// internal functions for VTUTF8 +// + +/** + Translate all VT-UTF8 characters in the Raw FIFI into unicode characters, + and insert them into Unicode FIFO. + + @param VtUtf8Device The terminal device. + +**/ +VOID +VTUTF8RawDataToUnicode ( + IN TERMINAL_DEV *VtUtf8Device + ); + +/** + Check if input string is valid VT-UTF8 string. + + @param TerminalDevice The terminal device. + @param WString The input string. + + @retval EFI_SUCCESS If all input characters are valid. + +**/ +EFI_STATUS +VTUTF8TestString ( + IN TERMINAL_DEV *TerminalDevice, + IN CHAR16 *WString + ); + +/** + Translate one Unicode character into VT-UTF8 characters. + + UTF8 Encoding Table + Bits per Character | Unicode Character Range | Unicode Binary Encoding | UTF8 Binary Encoding + 0-7 | 0x0000 - 0x007F | 00000000 0xxxxxxx | 0xxxxxxx + 8-11 | 0x0080 - 0x07FF | 00000xxx xxxxxxxx | 110xxxxx 10xxxxxx + 12-16 | 0x0800 - 0xFFFF | xxxxxxxx xxxxxxxx | 1110xxxx 10xxxxxx 10xxxxxx + + + @param Unicode Unicode character need translating. + @param Utf8Char Return VT-UTF8 character set. + @param ValidBytes The count of valid VT-UTF8 characters. If + ValidBytes is zero, no valid VT-UTF8 returned. + +**/ +VOID +UnicodeToUtf8 ( + IN CHAR16 Unicode, + OUT UTF8_CHAR *Utf8Char, + OUT UINT8 *ValidBytes + ); + +/** + Get one valid VT-UTF8 characters set from Raw Data FIFO. + + @param Utf8Device The terminal device. + @param Utf8Char Returned valid VT-UTF8 characters set. + @param ValidBytes The count of returned VT-VTF8 characters. + If ValidBytes is zero, no valid VT-UTF8 returned. + +**/ +VOID +GetOneValidUtf8Char ( + IN TERMINAL_DEV *Utf8Device, + OUT UTF8_CHAR *Utf8Char, + OUT UINT8 *ValidBytes + ); + +/** + Translate VT-UTF8 characters into one Unicode character. + + UTF8 Encoding Table + Bits per Character | Unicode Character Range | Unicode Binary Encoding | UTF8 Binary Encoding + 0-7 | 0x0000 - 0x007F | 00000000 0xxxxxxx | 0xxxxxxx + 8-11 | 0x0080 - 0x07FF | 00000xxx xxxxxxxx | 110xxxxx 10xxxxxx + 12-16 | 0x0800 - 0xFFFF | xxxxxxxx xxxxxxxx | 1110xxxx 10xxxxxx 10xxxxxx + + + @param Utf8Char VT-UTF8 character set needs translating. + @param ValidBytes The count of valid VT-UTF8 characters. + @param UnicodeChar Returned unicode character. + +**/ +VOID +Utf8ToUnicode ( + IN UTF8_CHAR Utf8Char, + IN UINT8 ValidBytes, + OUT CHAR16 *UnicodeChar + ); + +// +// functions for boxdraw unicode +// + +/** + Detects if a Unicode char is for Box Drawing text graphics. + + @param Graphic Unicode char to test. + @param PcAnsi Optional pointer to return PCANSI equivalent of + Graphic. + @param Ascii Optional pointer to return ASCII equivalent of + Graphic. + + @retval TRUE If Graphic is a supported Unicode Box Drawing character. + +**/ +BOOLEAN +TerminalIsValidTextGraphics ( + IN CHAR16 Graphic, + OUT CHAR8 *PcAnsi, OPTIONAL + OUT CHAR8 *Ascii OPTIONAL + ); + +/** + Detects if a valid ASCII char. + + @param Ascii An ASCII character. + + @retval TRUE If it is a valid ASCII character. + @retval FALSE If it is not a valid ASCII character. + +**/ +BOOLEAN +TerminalIsValidAscii ( + IN CHAR16 Ascii + ); + +/** + Detects if a valid EFI control character. + + @param CharC An input EFI Control character. + + @retval TRUE If it is a valid EFI control character. + @retval FALSE If it is not a valid EFI control character. + +**/ +BOOLEAN +TerminalIsValidEfiCntlChar ( + IN CHAR16 CharC + ); + +/** + Check if the device supports hot-plug through its device path. + + This function could be updated to check more types of Hot Plug devices. + Currently, it checks USB and PCCard device. + + @param DevicePath Pointer to device's device path. + + @retval TRUE The devcie is a hot-plug device + @retval FALSE The devcie is not a hot-plug device. + +**/ +BOOLEAN +IsHotPlugDevice ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +/** + Timer handler to poll the key from serial. + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. +**/ +VOID +EFIAPI +TerminalConInTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + Process key notify. + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. +**/ +VOID +EFIAPI +KeyNotifyProcessHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConIn.c b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConIn.c new file mode 100644 index 000000000..f8c71f95c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConIn.c @@ -0,0 +1,2093 @@ +/** @file + Implementation for EFI_SIMPLE_TEXT_INPUT_PROTOCOL protocol. + +(C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (C) 2016 Silicon Graphics, Inc. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Terminal.h" + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existence of a keystroke via WaitForEvent () call. + + @param TerminalDevice Terminal driver private structure + @param KeyData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data available. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +ReadKeyStrokeWorker ( + IN TERMINAL_DEV *TerminalDevice, + OUT EFI_KEY_DATA *KeyData + ) +{ + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + KeyData->KeyState.KeyShiftState = 0; + KeyData->KeyState.KeyToggleState = 0; + + if (!EfiKeyFiFoRemoveOneKey (TerminalDevice, &KeyData->Key)) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; + +} + +/** + Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.Reset(). + This driver only perform dependent serial device reset regardless of + the value of ExtendeVerification + + @param This Indicates the calling context. + @param ExtendedVerification Skip by this driver. + + @retval EFI_SUCCESS The reset operation succeeds. + @retval EFI_DEVICE_ERROR The dependent serial port reset fails. + +**/ +EFI_STATUS +EFIAPI +TerminalConInReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + + TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (This); + + // + // Report progress code here + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_PC_RESET), + TerminalDevice->DevicePath + ); + + Status = TerminalDevice->SerialIo->Reset (TerminalDevice->SerialIo); + + // + // Make all the internal buffer empty for keys + // + TerminalDevice->RawFiFo->Head = TerminalDevice->RawFiFo->Tail; + TerminalDevice->UnicodeFiFo->Head = TerminalDevice->UnicodeFiFo->Tail; + TerminalDevice->EfiKeyFiFo->Head = TerminalDevice->EfiKeyFiFo->Tail; + TerminalDevice->EfiKeyFiFoForNotify->Head = TerminalDevice->EfiKeyFiFoForNotify->Tail; + + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR), + TerminalDevice->DevicePath + ); + } + + return Status; +} + +/** + Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke(). + + @param This Indicates the calling context. + @param Key A pointer to a buffer that is filled in with the + keystroke information for the key that was sent + from terminal. + + @retval EFI_SUCCESS The keystroke information is returned successfully. + @retval EFI_NOT_READY There is no keystroke data available. + @retval EFI_DEVICE_ERROR The dependent serial device encounters error. + +**/ +EFI_STATUS +EFIAPI +TerminalConInReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ) +{ + TERMINAL_DEV *TerminalDevice; + EFI_STATUS Status; + EFI_KEY_DATA KeyData; + + // + // get TERMINAL_DEV from "This" parameter. + // + TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (This); + + Status = ReadKeyStrokeWorker (TerminalDevice, &KeyData); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); + + return EFI_SUCCESS; + +} + +/** + Check if the key already has been registered. + + If both RegsiteredData and InputData is NULL, then ASSERT(). + + @param RegsiteredData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + registered. + @param InputData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval TRUE Key be pressed matches a registered key. + @retval FALSE Match failed. + +**/ +BOOLEAN +IsKeyRegistered ( + IN EFI_KEY_DATA *RegsiteredData, + IN EFI_KEY_DATA *InputData + ) +{ + ASSERT (RegsiteredData != NULL && InputData != NULL); + + if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) || + (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) { + return FALSE; + } + + return TRUE; +} + + + +/** + Event notification function for EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event + Signal the event if there is key available + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. + +**/ +VOID +EFIAPI +TerminalConInWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + TerminalConInWaitForKey (Event, Context); +} + +// +// Simple Text Input Ex protocol functions +// + +/** + Reset the input device and optionally run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +TerminalConInResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + + TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); + + Status = TerminalDevice->SimpleInput.Reset (&TerminalDevice->SimpleInput, ExtendedVerification); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; + +} + + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existence of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the + keystroke state data for the key that was + pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data available. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due + to hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +TerminalConInReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ) +{ + TERMINAL_DEV *TerminalDevice; + + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); + + return ReadKeyStrokeWorker (TerminalDevice, KeyData); + +} + + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + could not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set its + state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +TerminalConInSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ) +{ + if (KeyToggleState == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + + +/** + Register a notification function for a particular keystroke for the input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with + the keystroke information for the key that was + pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState + and KeyData.KeyState.KeyShiftState are 0, then any incomplete + keystroke will trigger a notification of the KeyNotificationFunction. + @param KeyNotificationFunction Points to the function to be called when the key + sequence is typed specified by KeyData. This notification function + should be called at <=TPL_CALLBACK. + @param NotifyHandle Points to the unique handle assigned to the + registered notification. + + @retval EFI_SUCCESS The notification function was registered + successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necessary data + structures. + @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +TerminalConInRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ) +{ + TERMINAL_DEV *TerminalDevice; + TERMINAL_CONSOLE_IN_EX_NOTIFY *NewNotify; + LIST_ENTRY *Link; + LIST_ENTRY *NotifyList; + TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + + if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + + TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); + + // + // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. + // + NotifyList = &TerminalDevice->NotifyList; + for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) { + CurrentNotify = CR ( + Link, + TERMINAL_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { + if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { + *NotifyHandle = CurrentNotify; + return EFI_SUCCESS; + } + } + } + + // + // Allocate resource to save the notification function + // + NewNotify = (TERMINAL_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (TERMINAL_CONSOLE_IN_EX_NOTIFY)); + if (NewNotify == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NewNotify->Signature = TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE; + NewNotify->KeyNotificationFn = KeyNotificationFunction; + CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); + InsertTailList (&TerminalDevice->NotifyList, &NewNotify->NotifyEntry); + + *NotifyHandle = NewNotify; + + return EFI_SUCCESS; +} + + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function being + unregistered. + + @retval EFI_SUCCESS The notification function was unregistered + successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + +**/ +EFI_STATUS +EFIAPI +TerminalConInUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ) +{ + TERMINAL_DEV *TerminalDevice; + LIST_ENTRY *Link; + TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + LIST_ENTRY *NotifyList; + + if (NotificationHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); + + NotifyList = &TerminalDevice->NotifyList; + for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) { + CurrentNotify = CR ( + Link, + TERMINAL_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (CurrentNotify == NotificationHandle) { + // + // Remove the notification function from NotifyList and free resources + // + RemoveEntryList (&CurrentNotify->NotifyEntry); + + gBS->FreePool (CurrentNotify); + return EFI_SUCCESS; + } + } + + // + // Can not find the matching entry in database. + // + return EFI_INVALID_PARAMETER; +} + +/** + Translate raw data into Unicode (according to different encode), and + translate Unicode into key information. (according to different standard). + + @param TerminalDevice Terminal driver private structure. + +**/ +VOID +TranslateRawDataToEfiKey ( + IN TERMINAL_DEV *TerminalDevice + ) +{ + switch (TerminalDevice->TerminalType) { + + case TerminalTypePcAnsi: + case TerminalTypeVt100: + case TerminalTypeVt100Plus: + case TerminalTypeTtyTerm: + case TerminalTypeLinux: + case TerminalTypeXtermR6: + case TerminalTypeVt400: + case TerminalTypeSCO: + AnsiRawDataToUnicode (TerminalDevice); + UnicodeToEfiKey (TerminalDevice); + break; + + case TerminalTypeVtUtf8: + // + // Process all the raw data in the RawFIFO, + // put the processed key into UnicodeFIFO. + // + VTUTF8RawDataToUnicode (TerminalDevice); + + // + // Translate all the Unicode data in the UnicodeFIFO to Efi key, + // then put into EfiKeyFIFO. + // + UnicodeToEfiKey (TerminalDevice); + + break; + } +} + +/** + Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event + Signal the event if there is key available + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. + +**/ +VOID +EFIAPI +TerminalConInWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Someone is waiting on the keystroke event, if there's + // a key pending, signal the event + // + if (!IsEfiKeyFiFoEmpty ((TERMINAL_DEV *) Context)) { + + gBS->SignalEvent (Event); + } +} + +/** + Timer handler to poll the key from serial. + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. +**/ +VOID +EFIAPI +TerminalConInTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + UINT32 Control; + UINT8 Input; + EFI_SERIAL_IO_MODE *Mode; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + UINTN SerialInTimeOut; + + TerminalDevice = (TERMINAL_DEV *) Context; + + SerialIo = TerminalDevice->SerialIo; + if (SerialIo == NULL) { + return ; + } + // + // if current timeout value for serial device is not identical with + // the value saved in TERMINAL_DEV structure, then recalculate the + // timeout value again and set serial attribute according to this value. + // + Mode = SerialIo->Mode; + if (Mode->Timeout != TerminalDevice->SerialInTimeOut) { + + SerialInTimeOut = 0; + if (Mode->BaudRate != 0) { + // + // According to BAUD rate to calculate the timeout value. + // + SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate; + } + + Status = SerialIo->SetAttributes ( + SerialIo, + Mode->BaudRate, + Mode->ReceiveFifoDepth, + (UINT32) SerialInTimeOut, + (EFI_PARITY_TYPE) (Mode->Parity), + (UINT8) Mode->DataBits, + (EFI_STOP_BITS_TYPE) (Mode->StopBits) + ); + + if (EFI_ERROR (Status)) { + TerminalDevice->SerialInTimeOut = 0; + } else { + TerminalDevice->SerialInTimeOut = SerialInTimeOut; + } + } + // + // Check whether serial buffer is empty. + // Skip the key transfer loop only if the SerialIo protocol instance + // successfully reports EFI_SERIAL_INPUT_BUFFER_EMPTY. + // + Status = SerialIo->GetControl (SerialIo, &Control); + if (EFI_ERROR (Status) || ((Control & EFI_SERIAL_INPUT_BUFFER_EMPTY) == 0)) { + // + // Fetch all the keys in the serial buffer, + // and insert the byte stream into RawFIFO. + // + while (!IsRawFiFoFull (TerminalDevice)) { + + Status = GetOneKeyFromSerial (TerminalDevice->SerialIo, &Input); + + if (EFI_ERROR (Status)) { + if (Status == EFI_DEVICE_ERROR) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_INPUT_ERROR), + TerminalDevice->DevicePath + ); + } + break; + } + + RawFiFoInsertOneKey (TerminalDevice, Input); + } + } + + // + // Translate all the raw data in RawFIFO into EFI Key, + // according to different terminal type supported. + // + TranslateRawDataToEfiKey (TerminalDevice); +} + +/** + Process key notify. + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. +**/ +VOID +EFIAPI +KeyNotifyProcessHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + BOOLEAN HasKey; + TERMINAL_DEV *TerminalDevice; + EFI_INPUT_KEY Key; + EFI_KEY_DATA KeyData; + LIST_ENTRY *Link; + LIST_ENTRY *NotifyList; + TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + EFI_TPL OldTpl; + + TerminalDevice = (TERMINAL_DEV *) Context; + + // + // Invoke notification functions. + // + NotifyList = &TerminalDevice->NotifyList; + while (TRUE) { + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + HasKey = EfiKeyFiFoForNotifyRemoveOneKey (TerminalDevice->EfiKeyFiFoForNotify, &Key); + CopyMem (&KeyData.Key, &Key, sizeof (EFI_INPUT_KEY)); + KeyData.KeyState.KeyShiftState = 0; + KeyData.KeyState.KeyToggleState = 0; + // + // Leave critical section + // + gBS->RestoreTPL (OldTpl); + if (!HasKey) { + break; + } + for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) { + CurrentNotify = CR (Link, TERMINAL_CONSOLE_IN_EX_NOTIFY, NotifyEntry, TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE); + if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { + CurrentNotify->KeyNotificationFn (&KeyData); + } + } + } +} + +/** + Get one key out of serial buffer. + + @param SerialIo Serial I/O protocol attached to the serial device. + @param Output The fetched key. + + @retval EFI_NOT_READY If serial buffer is empty. + @retval EFI_DEVICE_ERROR If reading serial buffer encounter error. + @retval EFI_SUCCESS If reading serial buffer successfully, put + the fetched key to the parameter output. + +**/ +EFI_STATUS +GetOneKeyFromSerial ( + EFI_SERIAL_IO_PROTOCOL *SerialIo, + UINT8 *Output + ) +{ + EFI_STATUS Status; + UINTN Size; + + Size = 1; + *Output = 0; + + // + // Read one key from serial I/O device. + // + Status = SerialIo->Read (SerialIo, &Size, Output); + + if (EFI_ERROR (Status)) { + + if (Status == EFI_TIMEOUT) { + return EFI_NOT_READY; + } + + return EFI_DEVICE_ERROR; + + } + + if (*Output == 0) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Insert one byte raw data into the Raw Data FIFO. + + @param TerminalDevice Terminal driver private structure. + @param Input The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If Raw Data buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +RawFiFoInsertOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT8 Input + ) +{ + UINT8 Tail; + + Tail = TerminalDevice->RawFiFo->Tail; + + if (IsRawFiFoFull (TerminalDevice)) { + // + // Raw FIFO is full + // + return FALSE; + } + + TerminalDevice->RawFiFo->Data[Tail] = Input; + + TerminalDevice->RawFiFo->Tail = (UINT8) ((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1)); + + return TRUE; +} + +/** + Remove one pre-fetched key out of the Raw Data FIFO. + + @param TerminalDevice Terminal driver private structure. + @param Output The key will be removed. + + @retval TRUE If insert successfully. + @retval FALSE If Raw Data FIFO buffer is empty before remove operation. + +**/ +BOOLEAN +RawFiFoRemoveOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT8 *Output + ) +{ + UINT8 Head; + + Head = TerminalDevice->RawFiFo->Head; + + if (IsRawFiFoEmpty (TerminalDevice)) { + // + // FIFO is empty + // + *Output = 0; + return FALSE; + } + + *Output = TerminalDevice->RawFiFo->Data[Head]; + + TerminalDevice->RawFiFo->Head = (UINT8) ((Head + 1) % (RAW_FIFO_MAX_NUMBER + 1)); + + return TRUE; +} + +/** + Clarify whether Raw Data FIFO buffer is empty. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Raw Data FIFO buffer is empty. + @retval FALSE If Raw Data FIFO buffer is not empty. + +**/ +BOOLEAN +IsRawFiFoEmpty ( + TERMINAL_DEV *TerminalDevice + ) +{ + if (TerminalDevice->RawFiFo->Head == TerminalDevice->RawFiFo->Tail) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Clarify whether Raw Data FIFO buffer is full. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Raw Data FIFO buffer is full. + @retval FALSE If Raw Data FIFO buffer is not full. + +**/ +BOOLEAN +IsRawFiFoFull ( + TERMINAL_DEV *TerminalDevice + ) +{ + UINT8 Tail; + UINT8 Head; + + Tail = TerminalDevice->RawFiFo->Tail; + Head = TerminalDevice->RawFiFo->Head; + + if (((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1)) == Head) { + + return TRUE; + } + + return FALSE; +} + +/** + Insert one pre-fetched key into the FIFO buffer. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + @param Input The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If FIFO buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +EfiKeyFiFoForNotifyInsertOneKey ( + EFI_KEY_FIFO *EfiKeyFiFo, + EFI_INPUT_KEY *Input + ) +{ + UINT8 Tail; + + Tail = EfiKeyFiFo->Tail; + + if (IsEfiKeyFiFoForNotifyFull (EfiKeyFiFo)) { + // + // FIFO is full + // + return FALSE; + } + + CopyMem (&EfiKeyFiFo->Data[Tail], Input, sizeof (EFI_INPUT_KEY)); + + EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1)); + + return TRUE; +} + +/** + Remove one pre-fetched key out of the FIFO buffer. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + @param Output The key will be removed. + + @retval TRUE If remove successfully. + @retval FALSE If FIFO buffer is empty before remove operation. + +**/ +BOOLEAN +EfiKeyFiFoForNotifyRemoveOneKey ( + EFI_KEY_FIFO *EfiKeyFiFo, + EFI_INPUT_KEY *Output + ) +{ + UINT8 Head; + + Head = EfiKeyFiFo->Head; + ASSERT (Head < FIFO_MAX_NUMBER + 1); + + if (IsEfiKeyFiFoForNotifyEmpty (EfiKeyFiFo)) { + // + // FIFO is empty + // + Output->ScanCode = SCAN_NULL; + Output->UnicodeChar = 0; + return FALSE; + } + + CopyMem (Output, &EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY)); + + EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1)); + + return TRUE; +} + +/** + Clarify whether FIFO buffer is empty. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + + @retval TRUE If FIFO buffer is empty. + @retval FALSE If FIFO buffer is not empty. + +**/ +BOOLEAN +IsEfiKeyFiFoForNotifyEmpty ( + EFI_KEY_FIFO *EfiKeyFiFo + ) +{ + if (EfiKeyFiFo->Head == EfiKeyFiFo->Tail) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Clarify whether FIFO buffer is full. + + @param EfiKeyFiFo Pointer to instance of EFI_KEY_FIFO. + + @retval TRUE If FIFO buffer is full. + @retval FALSE If FIFO buffer is not full. + +**/ +BOOLEAN +IsEfiKeyFiFoForNotifyFull ( + EFI_KEY_FIFO *EfiKeyFiFo + ) +{ + UINT8 Tail; + UINT8 Head; + + Tail = EfiKeyFiFo->Tail; + Head = EfiKeyFiFo->Head; + + if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) { + return TRUE; + } + + return FALSE; +} + +/** + Insert one pre-fetched key into the FIFO buffer. + + @param TerminalDevice Terminal driver private structure. + @param Key The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If FIFO buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +EfiKeyFiFoInsertOneKey ( + TERMINAL_DEV *TerminalDevice, + EFI_INPUT_KEY *Key + ) +{ + UINT8 Tail; + LIST_ENTRY *Link; + LIST_ENTRY *NotifyList; + TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + EFI_KEY_DATA KeyData; + + Tail = TerminalDevice->EfiKeyFiFo->Tail; + + CopyMem (&KeyData.Key, Key, sizeof (EFI_INPUT_KEY)); + KeyData.KeyState.KeyShiftState = 0; + KeyData.KeyState.KeyToggleState = 0; + + // + // Signal KeyNotify process event if this key pressed matches any key registered. + // + NotifyList = &TerminalDevice->NotifyList; + for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) { + CurrentNotify = CR ( + Link, + TERMINAL_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { + // + // The key notification function needs to run at TPL_CALLBACK + // while current TPL is TPL_NOTIFY. It will be invoked in + // KeyNotifyProcessHandler() which runs at TPL_CALLBACK. + // + EfiKeyFiFoForNotifyInsertOneKey (TerminalDevice->EfiKeyFiFoForNotify, Key); + gBS->SignalEvent (TerminalDevice->KeyNotifyProcessEvent); + break; + } + } + if (IsEfiKeyFiFoFull (TerminalDevice)) { + // + // Efi Key FIFO is full + // + return FALSE; + } + + CopyMem (&TerminalDevice->EfiKeyFiFo->Data[Tail], Key, sizeof (EFI_INPUT_KEY)); + + TerminalDevice->EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1)); + + return TRUE; +} + +/** + Remove one pre-fetched key out of the FIFO buffer. + + @param TerminalDevice Terminal driver private structure. + @param Output The key will be removed. + + @retval TRUE If insert successfully. + @retval FALSE If FIFO buffer is empty before remove operation. + +**/ +BOOLEAN +EfiKeyFiFoRemoveOneKey ( + TERMINAL_DEV *TerminalDevice, + EFI_INPUT_KEY *Output + ) +{ + UINT8 Head; + + Head = TerminalDevice->EfiKeyFiFo->Head; + ASSERT (Head < FIFO_MAX_NUMBER + 1); + + if (IsEfiKeyFiFoEmpty (TerminalDevice)) { + // + // FIFO is empty + // + Output->ScanCode = SCAN_NULL; + Output->UnicodeChar = 0; + return FALSE; + } + + CopyMem (Output, &TerminalDevice->EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY)); + + TerminalDevice->EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1)); + + return TRUE; +} + +/** + Clarify whether FIFO buffer is empty. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If FIFO buffer is empty. + @retval FALSE If FIFO buffer is not empty. + +**/ +BOOLEAN +IsEfiKeyFiFoEmpty ( + TERMINAL_DEV *TerminalDevice + ) +{ + if (TerminalDevice->EfiKeyFiFo->Head == TerminalDevice->EfiKeyFiFo->Tail) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Clarify whether FIFO buffer is full. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If FIFO buffer is full. + @retval FALSE If FIFO buffer is not full. + +**/ +BOOLEAN +IsEfiKeyFiFoFull ( + TERMINAL_DEV *TerminalDevice + ) +{ + UINT8 Tail; + UINT8 Head; + + Tail = TerminalDevice->EfiKeyFiFo->Tail; + Head = TerminalDevice->EfiKeyFiFo->Head; + + if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) { + + return TRUE; + } + + return FALSE; +} + +/** + Insert one pre-fetched key into the Unicode FIFO buffer. + + @param TerminalDevice Terminal driver private structure. + @param Input The key will be input. + + @retval TRUE If insert successfully. + @retval FALSE If Unicode FIFO buffer is full before key insertion, + and the key is lost. + +**/ +BOOLEAN +UnicodeFiFoInsertOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT16 Input + ) +{ + UINT8 Tail; + + Tail = TerminalDevice->UnicodeFiFo->Tail; + ASSERT (Tail < FIFO_MAX_NUMBER + 1); + + + if (IsUnicodeFiFoFull (TerminalDevice)) { + // + // Unicode FIFO is full + // + return FALSE; + } + + TerminalDevice->UnicodeFiFo->Data[Tail] = Input; + + TerminalDevice->UnicodeFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1)); + + return TRUE; +} + +/** + Remove one pre-fetched key out of the Unicode FIFO buffer. + The caller should guarantee that Unicode FIFO buffer is not empty + by IsUnicodeFiFoEmpty (). + + @param TerminalDevice Terminal driver private structure. + @param Output The key will be removed. + +**/ +VOID +UnicodeFiFoRemoveOneKey ( + TERMINAL_DEV *TerminalDevice, + UINT16 *Output + ) +{ + UINT8 Head; + + Head = TerminalDevice->UnicodeFiFo->Head; + ASSERT (Head < FIFO_MAX_NUMBER + 1); + + *Output = TerminalDevice->UnicodeFiFo->Data[Head]; + + TerminalDevice->UnicodeFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1)); +} + +/** + Clarify whether Unicode FIFO buffer is empty. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Unicode FIFO buffer is empty. + @retval FALSE If Unicode FIFO buffer is not empty. + +**/ +BOOLEAN +IsUnicodeFiFoEmpty ( + TERMINAL_DEV *TerminalDevice + ) +{ + if (TerminalDevice->UnicodeFiFo->Head == TerminalDevice->UnicodeFiFo->Tail) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Clarify whether Unicode FIFO buffer is full. + + @param TerminalDevice Terminal driver private structure + + @retval TRUE If Unicode FIFO buffer is full. + @retval FALSE If Unicode FIFO buffer is not full. + +**/ +BOOLEAN +IsUnicodeFiFoFull ( + TERMINAL_DEV *TerminalDevice + ) +{ + UINT8 Tail; + UINT8 Head; + + Tail = TerminalDevice->UnicodeFiFo->Tail; + Head = TerminalDevice->UnicodeFiFo->Head; + + if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) { + + return TRUE; + } + + return FALSE; +} + + +/** + Update the Unicode characters from a terminal input device into EFI Keys FIFO. + + @param TerminalDevice The terminal device to use to translate raw input into EFI Keys + +**/ +VOID +UnicodeToEfiKeyFlushState ( + IN TERMINAL_DEV *TerminalDevice + ) +{ + EFI_INPUT_KEY Key; + UINT32 InputState; + + InputState = TerminalDevice->InputState; + + if (IsEfiKeyFiFoFull (TerminalDevice)) { + return; + } + + if ((InputState & INPUT_STATE_ESC) != 0) { + Key.ScanCode = SCAN_ESC; + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + } + + if ((InputState & INPUT_STATE_CSI) != 0) { + Key.ScanCode = SCAN_NULL; + Key.UnicodeChar = CSI; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + } + + if ((InputState & INPUT_STATE_LEFTOPENBRACKET) != 0) { + Key.ScanCode = SCAN_NULL; + Key.UnicodeChar = LEFTOPENBRACKET; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + } + + if ((InputState & INPUT_STATE_O) != 0) { + Key.ScanCode = SCAN_NULL; + Key.UnicodeChar = 'O'; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + } + + if ((InputState & INPUT_STATE_2) != 0) { + Key.ScanCode = SCAN_NULL; + Key.UnicodeChar = '2'; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + } + + // + // Cancel the timer. + // + gBS->SetTimer ( + TerminalDevice->TwoSecondTimeOut, + TimerCancel, + 0 + ); + + TerminalDevice->InputState = INPUT_STATE_DEFAULT; +} + + +/** + Converts a stream of Unicode characters from a terminal input device into EFI Keys that + can be read through the Simple Input Protocol. + + The table below shows the keyboard input mappings that this function supports. + If the ESC sequence listed in one of the columns is presented, then it is translated + into the corresponding EFI Scan Code. If a matching sequence is not found, then the raw + key strokes are converted into EFI Keys. + + 2 seconds are allowed for an ESC sequence to be completed. If the ESC sequence is not + completed in 2 seconds, then the raw key strokes of the partial ESC sequence are + converted into EFI Keys. + There is one special input sequence that will force the system to reset. + This is ESC R ESC r ESC R. + + Note: current implementation support terminal types include: PC ANSI, VT100+/VTUTF8, VT100. + The table below is not same with UEFI Spec 2.3 Appendix B Table 201(not support ANSI X3.64 / + DEC VT200-500 and extra support PC ANSI, VT100)since UEFI Table 201 is just an example. + + Symbols used in table below + =========================== + ESC = 0x1B + CSI = 0x9B + DEL = 0x7f + ^ = CTRL + + +=========+======+===========+==========+==========+ + | | EFI | UEFI 2.0 | | | + | | Scan | | VT100+ | | + | KEY | Code | PC ANSI | VTUTF8 | VT100 | + +=========+======+===========+==========+==========+ + | NULL | 0x00 | | | | + | UP | 0x01 | ESC [ A | ESC [ A | ESC [ A | + | DOWN | 0x02 | ESC [ B | ESC [ B | ESC [ B | + | RIGHT | 0x03 | ESC [ C | ESC [ C | ESC [ C | + | LEFT | 0x04 | ESC [ D | ESC [ D | ESC [ D | + | HOME | 0x05 | ESC [ H | ESC h | ESC [ H | + | END | 0x06 | ESC [ F | ESC k | ESC [ K | + | INSERT | 0x07 | ESC [ @ | ESC + | ESC [ @ | + | | | ESC [ L | | ESC [ L | + | DELETE | 0x08 | ESC [ X | ESC - | ESC [ P | + | PG UP | 0x09 | ESC [ I | ESC ? | ESC [ V | + | | | | | ESC [ ? | + | PG DOWN | 0x0A | ESC [ G | ESC / | ESC [ U | + | | | | | ESC [ / | + | F1 | 0x0B | ESC [ M | ESC 1 | ESC O P | + | F2 | 0x0C | ESC [ N | ESC 2 | ESC O Q | + | F3 | 0x0D | ESC [ O | ESC 3 | ESC O w | + | F4 | 0x0E | ESC [ P | ESC 4 | ESC O x | + | F5 | 0x0F | ESC [ Q | ESC 5 | ESC O t | + | F6 | 0x10 | ESC [ R | ESC 6 | ESC O u | + | F7 | 0x11 | ESC [ S | ESC 7 | ESC O q | + | F8 | 0x12 | ESC [ T | ESC 8 | ESC O r | + | F9 | 0x13 | ESC [ U | ESC 9 | ESC O p | + | F10 | 0x14 | ESC [ V | ESC 0 | ESC O M | + | Escape | 0x17 | ESC | ESC | ESC | + | F11 | 0x15 | | ESC ! | | + | F12 | 0x16 | | ESC @ | | + +=========+======+===========+==========+==========+ + +Putty function key map: + +=========+======+===========+=============+=============+=============+=========+ + | | EFI | | | | | | + | | Scan | | | Normal | | | + | KEY | Code | VT100+ | Xterm R6 | VT400 | Linux | SCO | + +=========+======+===========+=============+=============+=============+=========+ + | F1 | 0x0B | ESC O P | ESC O P | ESC [ 1 1 ~ | ESC [ [ A | ESC [ M | + | F2 | 0x0C | ESC O Q | ESC O Q | ESC [ 1 2 ~ | ESC [ [ B | ESC [ N | + | F3 | 0x0D | ESC O R | ESC O R | ESC [ 1 3 ~ | ESC [ [ C | ESC [ O | + | F4 | 0x0E | ESC O S | ESC O S | ESC [ 1 4 ~ | ESC [ [ D | ESC [ P | + | F5 | 0x0F | ESC O T | ESC [ 1 5 ~ | ESC [ 1 5 ~ | ESC [ [ E | ESC [ Q | + | F6 | 0x10 | ESC O U | ESC [ 1 7 ~ | ESC [ 1 7 ~ | ESC [ 1 7 ~ | ESC [ R | + | F7 | 0x11 | ESC O V | ESC [ 1 8 ~ | ESC [ 1 8 ~ | ESC [ 1 8 ~ | ESC [ S | + | F8 | 0x12 | ESC O W | ESC [ 1 9 ~ | ESC [ 1 9 ~ | ESC [ 1 9 ~ | ESC [ T | + | F9 | 0x13 | ESC O X | ESC [ 2 0 ~ | ESC [ 2 0 ~ | ESC [ 2 0 ~ | ESC [ U | + | F10 | 0x14 | ESC O Y | ESC [ 2 1 ~ | ESC [ 2 1 ~ | ESC [ 2 1 ~ | ESC [ V | + | Escape | 0x17 | ESC | ESC | ESC | ESC | ESC | + | F11 | 0x15 | ESC O Z | ESC [ 2 3 ~ | ESC [ 2 3 ~ | ESC [ 2 3 ~ | ESC [ W | + | F12 | 0x16 | ESC O [ | ESC [ 2 4 ~ | ESC [ 2 4 ~ | ESC [ 2 4 ~ | ESC [ X | + +=========+======+===========+=============+=============+=============+=========+ + + Special Mappings + ================ + ESC R ESC r ESC R = Reset System + + @param TerminalDevice The terminal device to use to translate raw input into EFI Keys + +**/ +VOID +UnicodeToEfiKey ( + IN TERMINAL_DEV *TerminalDevice + ) +{ + EFI_STATUS Status; + EFI_STATUS TimerStatus; + UINT16 UnicodeChar; + EFI_INPUT_KEY Key; + BOOLEAN SetDefaultResetState; + + TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut); + + if (!EFI_ERROR (TimerStatus)) { + UnicodeToEfiKeyFlushState (TerminalDevice); + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + } + + while (!IsUnicodeFiFoEmpty (TerminalDevice) && !IsEfiKeyFiFoFull (TerminalDevice)) { + + if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) { + // + // Check to see if the 2 seconds timer has expired + // + TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut); + if (!EFI_ERROR (TimerStatus)) { + UnicodeToEfiKeyFlushState (TerminalDevice); + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + } + } + + // + // Fetch one Unicode character from the Unicode FIFO + // + UnicodeFiFoRemoveOneKey (TerminalDevice, &UnicodeChar); + + SetDefaultResetState = TRUE; + + switch (TerminalDevice->InputState) { + case INPUT_STATE_DEFAULT: + + break; + + case INPUT_STATE_ESC: + + if (UnicodeChar == LEFTOPENBRACKET) { + TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET; + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + continue; + } + + if (UnicodeChar == 'O' && (TerminalDevice->TerminalType == TerminalTypeVt100 || + TerminalDevice->TerminalType == TerminalTypeTtyTerm || + TerminalDevice->TerminalType == TerminalTypeXtermR6 || + TerminalDevice->TerminalType == TerminalTypeVt100Plus)) { + TerminalDevice->InputState |= INPUT_STATE_O; + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + continue; + } + + Key.ScanCode = SCAN_NULL; + + if (TerminalDevice->TerminalType == TerminalTypeVt100Plus || + TerminalDevice->TerminalType == TerminalTypeVtUtf8) { + switch (UnicodeChar) { + case '1': + Key.ScanCode = SCAN_F1; + break; + case '2': + Key.ScanCode = SCAN_F2; + break; + case '3': + Key.ScanCode = SCAN_F3; + break; + case '4': + Key.ScanCode = SCAN_F4; + break; + case '5': + Key.ScanCode = SCAN_F5; + break; + case '6': + Key.ScanCode = SCAN_F6; + break; + case '7': + Key.ScanCode = SCAN_F7; + break; + case '8': + Key.ScanCode = SCAN_F8; + break; + case '9': + Key.ScanCode = SCAN_F9; + break; + case '0': + Key.ScanCode = SCAN_F10; + break; + case '!': + Key.ScanCode = SCAN_F11; + break; + case '@': + Key.ScanCode = SCAN_F12; + break; + case 'h': + Key.ScanCode = SCAN_HOME; + break; + case 'k': + Key.ScanCode = SCAN_END; + break; + case '+': + Key.ScanCode = SCAN_INSERT; + break; + case '-': + Key.ScanCode = SCAN_DELETE; + break; + case '/': + Key.ScanCode = SCAN_PAGE_DOWN; + break; + case '?': + Key.ScanCode = SCAN_PAGE_UP; + break; + default : + break; + } + } + + switch (UnicodeChar) { + case 'R': + if (TerminalDevice->ResetState == RESET_STATE_DEFAULT) { + TerminalDevice->ResetState = RESET_STATE_ESC_R; + SetDefaultResetState = FALSE; + } else if (TerminalDevice->ResetState == RESET_STATE_ESC_R_ESC_R) { + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + Key.ScanCode = SCAN_NULL; + break; + case 'r': + if (TerminalDevice->ResetState == RESET_STATE_ESC_R) { + TerminalDevice->ResetState = RESET_STATE_ESC_R_ESC_R; + SetDefaultResetState = FALSE; + } + Key.ScanCode = SCAN_NULL; + break; + default : + break; + } + + if (SetDefaultResetState) { + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + } + + if (Key.ScanCode != SCAN_NULL) { + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + UnicodeToEfiKeyFlushState (TerminalDevice); + continue; + } + + UnicodeToEfiKeyFlushState (TerminalDevice); + + break; + + case INPUT_STATE_ESC | INPUT_STATE_O: + + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + + Key.ScanCode = SCAN_NULL; + + if (TerminalDevice->TerminalType == TerminalTypeVt100) { + switch (UnicodeChar) { + case 'P': + Key.ScanCode = SCAN_F1; + break; + case 'Q': + Key.ScanCode = SCAN_F2; + break; + case 'w': + Key.ScanCode = SCAN_F3; + break; + case 'x': + Key.ScanCode = SCAN_F4; + break; + case 't': + Key.ScanCode = SCAN_F5; + break; + case 'u': + Key.ScanCode = SCAN_F6; + break; + case 'q': + Key.ScanCode = SCAN_F7; + break; + case 'r': + Key.ScanCode = SCAN_F8; + break; + case 'p': + Key.ScanCode = SCAN_F9; + break; + case 'M': + Key.ScanCode = SCAN_F10; + break; + default : + break; + } + } else if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) { + /* Also accept VT100 escape codes for F1-F4, HOME and END for TTY term */ + switch (UnicodeChar) { + case 'P': + Key.ScanCode = SCAN_F1; + break; + case 'Q': + Key.ScanCode = SCAN_F2; + break; + case 'R': + Key.ScanCode = SCAN_F3; + break; + case 'S': + Key.ScanCode = SCAN_F4; + break; + case 'H': + Key.ScanCode = SCAN_HOME; + break; + case 'F': + Key.ScanCode = SCAN_END; + break; + } + } else if (TerminalDevice->TerminalType == TerminalTypeVt100Plus) { + switch (UnicodeChar) { + case 'P': + Key.ScanCode = SCAN_F1; + break; + case 'Q': + Key.ScanCode = SCAN_F2; + break; + case 'R': + Key.ScanCode = SCAN_F3; + break; + case 'S': + Key.ScanCode = SCAN_F4; + break; + case 'T': + Key.ScanCode = SCAN_F5; + break; + case 'U': + Key.ScanCode = SCAN_F6; + break; + case 'V': + Key.ScanCode = SCAN_F7; + break; + case 'W': + Key.ScanCode = SCAN_F8; + break; + case 'X': + Key.ScanCode = SCAN_F9; + break; + case 'Y': + Key.ScanCode = SCAN_F10; + break; + case 'Z': + Key.ScanCode = SCAN_F11; + break; + case '[': + Key.ScanCode = SCAN_F12; + break; + } + } else if (TerminalDevice->TerminalType == TerminalTypeXtermR6) { + switch (UnicodeChar) { + case 'P': + Key.ScanCode = SCAN_F1; + break; + case 'Q': + Key.ScanCode = SCAN_F2; + break; + case 'R': + Key.ScanCode = SCAN_F3; + break; + case 'S': + Key.ScanCode = SCAN_F4; + break; + } + } + + if (Key.ScanCode != SCAN_NULL) { + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + UnicodeToEfiKeyFlushState (TerminalDevice); + continue; + } + + UnicodeToEfiKeyFlushState (TerminalDevice); + + break; + + case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET: + + if (UnicodeChar == '1' && (TerminalDevice->TerminalType == TerminalTypeXtermR6 || + TerminalDevice->TerminalType == TerminalTypeVt400 || + TerminalDevice->TerminalType == TerminalTypeLinux)) { + TerminalDevice->InputState |= INPUT_STATE_1; + continue; + } + + if (UnicodeChar == '2' && (TerminalDevice->TerminalType == TerminalTypeXtermR6 || + TerminalDevice->TerminalType == TerminalTypeVt400 || + TerminalDevice->TerminalType == TerminalTypeLinux)) { + TerminalDevice->InputState |= INPUT_STATE_2; + continue; + } + + if (UnicodeChar == LEFTOPENBRACKET && TerminalDevice->TerminalType == TerminalTypeLinux) { + TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET_2ND; + continue; + } + + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + + Key.ScanCode = SCAN_NULL; + + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeVt100 || + TerminalDevice->TerminalType == TerminalTypeVt100Plus || + TerminalDevice->TerminalType == TerminalTypeVtUtf8 || + TerminalDevice->TerminalType == TerminalTypeTtyTerm || + TerminalDevice->TerminalType == TerminalTypeLinux || + TerminalDevice->TerminalType == TerminalTypeXtermR6 || + TerminalDevice->TerminalType == TerminalTypeVt400 || + TerminalDevice->TerminalType == TerminalTypeSCO) { + switch (UnicodeChar) { + case 'A': + Key.ScanCode = SCAN_UP; + break; + case 'B': + Key.ScanCode = SCAN_DOWN; + break; + case 'C': + Key.ScanCode = SCAN_RIGHT; + break; + case 'D': + Key.ScanCode = SCAN_LEFT; + break; + case 'H': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeVt100 || + TerminalDevice->TerminalType == TerminalTypeTtyTerm) { + Key.ScanCode = SCAN_HOME; + } + break; + case 'F': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeTtyTerm) { + Key.ScanCode = SCAN_END; + } + break; + case 'K': + if (TerminalDevice->TerminalType == TerminalTypeVt100) { + Key.ScanCode = SCAN_END; + } + break; + case 'L': + case '@': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeVt100) { + Key.ScanCode = SCAN_INSERT; + } + break; + case 'X': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi) { + Key.ScanCode = SCAN_DELETE; + } else if (TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F12; + } + break; + case 'P': + if (TerminalDevice->TerminalType == TerminalTypeVt100) { + Key.ScanCode = SCAN_DELETE; + } else if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F4; + } + break; + case 'I': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi) { + Key.ScanCode = SCAN_PAGE_UP; + } + break; + case 'V': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F10; + } + break; + case '?': + if (TerminalDevice->TerminalType == TerminalTypeVt100) { + Key.ScanCode = SCAN_PAGE_UP; + } + break; + case 'G': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi) { + Key.ScanCode = SCAN_PAGE_DOWN; + } + break; + case 'U': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F9; + } + break; + case '/': + if (TerminalDevice->TerminalType == TerminalTypeVt100) { + Key.ScanCode = SCAN_PAGE_DOWN; + } + break; + case 'M': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F1; + } + break; + case 'N': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F2; + } + break; + case 'O': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F3; + } + break; + case 'Q': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F5; + } + break; + case 'R': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F6; + } + break; + case 'S': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F7; + } + break; + case 'T': + if (TerminalDevice->TerminalType == TerminalTypePcAnsi || + TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F8; + } + break; + case 'W': + if (TerminalDevice->TerminalType == TerminalTypeSCO) { + Key.ScanCode = SCAN_F11; + } + break; + default : + break; + } + } + + /* + * The VT220 escape codes that the TTY terminal accepts all have + * numeric codes, and there are no ambiguous prefixes shared with + * other terminal types. + */ + if (TerminalDevice->TerminalType == TerminalTypeTtyTerm && + Key.ScanCode == SCAN_NULL && + UnicodeChar >= '0' && + UnicodeChar <= '9') { + TerminalDevice->TtyEscapeStr[0] = UnicodeChar; + TerminalDevice->TtyEscapeIndex = 1; + TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET_TTY; + continue; + } + + if (Key.ScanCode != SCAN_NULL) { + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + UnicodeToEfiKeyFlushState (TerminalDevice); + continue; + } + + UnicodeToEfiKeyFlushState (TerminalDevice); + + break; + + case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_1: + + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + + Key.ScanCode = SCAN_NULL; + + if (TerminalDevice->TerminalType == TerminalTypeXtermR6 || + TerminalDevice->TerminalType == TerminalTypeVt400 || + TerminalDevice->TerminalType == TerminalTypeLinux) { + switch (UnicodeChar) { + case '1': + if (TerminalDevice->TerminalType == TerminalTypeVt400) { + Key.ScanCode = SCAN_F1; + } + break; + case '2': + if (TerminalDevice->TerminalType == TerminalTypeVt400) { + Key.ScanCode = SCAN_F2; + } + break; + case '3': + if (TerminalDevice->TerminalType == TerminalTypeVt400) { + Key.ScanCode = SCAN_F3; + } + break; + case '4': + if (TerminalDevice->TerminalType == TerminalTypeVt400) { + Key.ScanCode = SCAN_F4; + } + break; + case '5': + if (TerminalDevice->TerminalType == TerminalTypeXtermR6 || + TerminalDevice->TerminalType == TerminalTypeVt400) { + Key.ScanCode = SCAN_F5; + } + break; + case '7': + Key.ScanCode = SCAN_F6; + break; + case '8': + Key.ScanCode = SCAN_F7; + break; + case '9': + Key.ScanCode = SCAN_F8; + break; + } + } + + if (Key.ScanCode != SCAN_NULL) { + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + UnicodeToEfiKeyFlushState (TerminalDevice); + continue; + } + + UnicodeToEfiKeyFlushState (TerminalDevice); + + break; + + case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_2: + + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + Key.ScanCode = SCAN_NULL; + if (TerminalDevice->TerminalType == TerminalTypeXtermR6 || + TerminalDevice->TerminalType == TerminalTypeVt400 || + TerminalDevice->TerminalType == TerminalTypeLinux) { + switch (UnicodeChar) { + case '0': + Key.ScanCode = SCAN_F9; + break; + case '1': + Key.ScanCode = SCAN_F10; + break; + case '3': + Key.ScanCode = SCAN_F11; + break; + case '4': + Key.ScanCode = SCAN_F12; + break; + } + } + + if (Key.ScanCode != SCAN_NULL) { + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + UnicodeToEfiKeyFlushState (TerminalDevice); + continue; + } + + UnicodeToEfiKeyFlushState (TerminalDevice); + + break; + + case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_LEFTOPENBRACKET_2ND: + + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + Key.ScanCode = SCAN_NULL; + + if (TerminalDevice->TerminalType == TerminalTypeLinux) { + switch (UnicodeChar) { + case 'A': + Key.ScanCode = SCAN_F1; + break; + case 'B': + Key.ScanCode = SCAN_F2; + break; + case 'C': + Key.ScanCode = SCAN_F3; + break; + case 'D': + Key.ScanCode = SCAN_F4; + break; + case 'E': + Key.ScanCode = SCAN_F5; + break; + } + } + + if (Key.ScanCode != SCAN_NULL) { + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + UnicodeToEfiKeyFlushState (TerminalDevice); + continue; + } + + UnicodeToEfiKeyFlushState (TerminalDevice); + + break; + + case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET | INPUT_STATE_LEFTOPENBRACKET_TTY: + /* + * Here we handle the VT220 escape codes that we accept. This + * state is only used by the TTY terminal type. + */ + Key.ScanCode = SCAN_NULL; + if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) { + + if (UnicodeChar == '~' && TerminalDevice->TtyEscapeIndex <= 2) { + UINT16 EscCode; + TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex] = 0; /* Terminate string */ + EscCode = (UINT16) StrDecimalToUintn(TerminalDevice->TtyEscapeStr); + switch (EscCode) { + case 2: + Key.ScanCode = SCAN_INSERT; + break; + case 3: + Key.ScanCode = SCAN_DELETE; + break; + case 5: + Key.ScanCode = SCAN_PAGE_UP; + break; + case 6: + Key.ScanCode = SCAN_PAGE_DOWN; + break; + case 11: + case 12: + case 13: + case 14: + case 15: + Key.ScanCode = SCAN_F1 + EscCode - 11; + break; + case 17: + case 18: + case 19: + case 20: + case 21: + Key.ScanCode = SCAN_F6 + EscCode - 17; + break; + case 23: + case 24: + Key.ScanCode = SCAN_F11 + EscCode - 23; + break; + default: + break; + } + } else if (TerminalDevice->TtyEscapeIndex == 1){ + /* 2 character escape code */ + TerminalDevice->TtyEscapeStr[TerminalDevice->TtyEscapeIndex++] = UnicodeChar; + continue; + } + else { + DEBUG ((EFI_D_ERROR, "Unexpected state in escape2\n")); + } + } + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + + if (Key.ScanCode != SCAN_NULL) { + Key.UnicodeChar = 0; + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + TerminalDevice->InputState = INPUT_STATE_DEFAULT; + UnicodeToEfiKeyFlushState (TerminalDevice); + continue; + } + + UnicodeToEfiKeyFlushState (TerminalDevice); + break; + + default: + // + // Invalid state. This should never happen. + // + ASSERT (FALSE); + + UnicodeToEfiKeyFlushState (TerminalDevice); + + break; + } + + if (UnicodeChar == ESC) { + TerminalDevice->InputState = INPUT_STATE_ESC; + } + + if (UnicodeChar == CSI) { + TerminalDevice->InputState = INPUT_STATE_CSI; + } + + if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) { + Status = gBS->SetTimer( + TerminalDevice->TwoSecondTimeOut, + TimerRelative, + (UINT64)20000000 + ); + ASSERT_EFI_ERROR (Status); + continue; + } + + if (SetDefaultResetState) { + TerminalDevice->ResetState = RESET_STATE_DEFAULT; + } + + if (UnicodeChar == DEL) { + if (TerminalDevice->TerminalType == TerminalTypeTtyTerm) { + Key.ScanCode = SCAN_NULL; + Key.UnicodeChar = CHAR_BACKSPACE; + } + else { + Key.ScanCode = SCAN_DELETE; + Key.UnicodeChar = 0; + } + } else { + Key.ScanCode = SCAN_NULL; + Key.UnicodeChar = UnicodeChar; + } + + EfiKeyFiFoInsertOneKey (TerminalDevice, &Key); + } +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConOut.c b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConOut.c new file mode 100644 index 000000000..aae470e95 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalConOut.c @@ -0,0 +1,959 @@ +/** @file + Implementation for EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL protocol. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+Copyright (C) 2016 Silicon Graphics, Inc. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Terminal.h" + +// +// This list is used to define the valid extend chars. +// It also provides a mapping from Unicode to PCANSI or +// ASCII. The ASCII mapping we just made up. +// +// +UNICODE_TO_CHAR UnicodeToPcAnsiOrAscii[] = { + { BOXDRAW_HORIZONTAL, 0xc4, L'-' }, + { BOXDRAW_VERTICAL, 0xb3, L'|' }, + { BOXDRAW_DOWN_RIGHT, 0xda, L'/' }, + { BOXDRAW_DOWN_LEFT, 0xbf, L'\\' }, + { BOXDRAW_UP_RIGHT, 0xc0, L'\\' }, + { BOXDRAW_UP_LEFT, 0xd9, L'/' }, + { BOXDRAW_VERTICAL_RIGHT, 0xc3, L'|' }, + { BOXDRAW_VERTICAL_LEFT, 0xb4, L'|' }, + { BOXDRAW_DOWN_HORIZONTAL, 0xc2, L'+' }, + { BOXDRAW_UP_HORIZONTAL, 0xc1, L'+' }, + { BOXDRAW_VERTICAL_HORIZONTAL, 0xc5, L'+' }, + { BOXDRAW_DOUBLE_HORIZONTAL, 0xcd, L'-' }, + { BOXDRAW_DOUBLE_VERTICAL, 0xba, L'|' }, + { BOXDRAW_DOWN_RIGHT_DOUBLE, 0xd5, L'/' }, + { BOXDRAW_DOWN_DOUBLE_RIGHT, 0xd6, L'/' }, + { BOXDRAW_DOUBLE_DOWN_RIGHT, 0xc9, L'/' }, + { BOXDRAW_DOWN_LEFT_DOUBLE, 0xb8, L'\\' }, + { BOXDRAW_DOWN_DOUBLE_LEFT, 0xb7, L'\\' }, + { BOXDRAW_DOUBLE_DOWN_LEFT, 0xbb, L'\\' }, + { BOXDRAW_UP_RIGHT_DOUBLE, 0xd4, L'\\' }, + { BOXDRAW_UP_DOUBLE_RIGHT, 0xd3, L'\\' }, + { BOXDRAW_DOUBLE_UP_RIGHT, 0xc8, L'\\' }, + { BOXDRAW_UP_LEFT_DOUBLE, 0xbe, L'/' }, + { BOXDRAW_UP_DOUBLE_LEFT, 0xbd, L'/' }, + { BOXDRAW_DOUBLE_UP_LEFT, 0xbc, L'/' }, + { BOXDRAW_VERTICAL_RIGHT_DOUBLE, 0xc6, L'|' }, + { BOXDRAW_VERTICAL_DOUBLE_RIGHT, 0xc7, L'|' }, + { BOXDRAW_DOUBLE_VERTICAL_RIGHT, 0xcc, L'|' }, + { BOXDRAW_VERTICAL_LEFT_DOUBLE, 0xb5, L'|' }, + { BOXDRAW_VERTICAL_DOUBLE_LEFT, 0xb6, L'|' }, + { BOXDRAW_DOUBLE_VERTICAL_LEFT, 0xb9, L'|' }, + { BOXDRAW_DOWN_HORIZONTAL_DOUBLE, 0xd1, L'+' }, + { BOXDRAW_DOWN_DOUBLE_HORIZONTAL, 0xd2, L'+' }, + { BOXDRAW_DOUBLE_DOWN_HORIZONTAL, 0xcb, L'+' }, + { BOXDRAW_UP_HORIZONTAL_DOUBLE, 0xcf, L'+' }, + { BOXDRAW_UP_DOUBLE_HORIZONTAL, 0xd0, L'+' }, + { BOXDRAW_DOUBLE_UP_HORIZONTAL, 0xca, L'+' }, + { BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE, 0xd8, L'+' }, + { BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL, 0xd7, L'+' }, + { BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL, 0xce, L'+' }, + + { BLOCKELEMENT_FULL_BLOCK, 0xdb, L'*' }, + { BLOCKELEMENT_LIGHT_SHADE, 0xb0, L'+' }, + + { GEOMETRICSHAPE_UP_TRIANGLE, '^', L'^' }, + { GEOMETRICSHAPE_RIGHT_TRIANGLE, '>', L'>' }, + { GEOMETRICSHAPE_DOWN_TRIANGLE, 'v', L'v' }, + { GEOMETRICSHAPE_LEFT_TRIANGLE, '<', L'<' }, + + { ARROW_LEFT, '<', L'<' }, + { ARROW_UP, '^', L'^' }, + { ARROW_RIGHT, '>', L'>' }, + { ARROW_DOWN, 'v', L'v' }, + + { 0x0000, 0x00, L'\0' } +}; + +CHAR16 mSetModeString[] = { ESC, '[', '=', '3', 'h', 0 }; +CHAR16 mSetAttributeString[] = { ESC, '[', '0', 'm', ESC, '[', '4', '0', 'm', ESC, '[', '4', '0', 'm', 0 }; +CHAR16 mClearScreenString[] = { ESC, '[', '2', 'J', 0 }; +CHAR16 mSetCursorPositionString[] = { ESC, '[', '0', '0', ';', '0', '0', 'H', 0 }; +CHAR16 mCursorForwardString[] = { ESC, '[', '0', '0', 'C', 0 }; +CHAR16 mCursorBackwardString[] = { ESC, '[', '0', '0', 'D', 0 }; + +// +// Body of the ConOut functions +// + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.Reset(). + + If ExtendeVerification is TRUE, then perform dependent serial device reset, + and set display mode to mode 0. + If ExtendedVerification is FALSE, only set display mode to mode 0. + + @param This Indicates the calling context. + @param ExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS The reset operation succeeds. + @retval EFI_DEVICE_ERROR The terminal is not functioning correctly or the serial port reset fails. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + + // + // Perform a more exhaustive reset by resetting the serial port. + // + if (ExtendedVerification) { + // + // Report progress code here + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_PC_RESET), + TerminalDevice->DevicePath + ); + + Status = TerminalDevice->SerialIo->Reset (TerminalDevice->SerialIo); + if (EFI_ERROR (Status)) { + // + // Report error code here + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR), + TerminalDevice->DevicePath + ); + + return Status; + } + } + + This->SetAttribute (This, EFI_TEXT_ATTR (This->Mode->Attribute & 0x0F, EFI_BLACK)); + + Status = This->SetMode (This, 0); + + return Status; +} + + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString(). + + The Unicode string will be converted to terminal expressible data stream + and send to terminal via serial port. + + @param This Indicates the calling context. + @param WString The Null-terminated Unicode string to be displayed + on the terminal screen. + + @retval EFI_SUCCESS The string is output successfully. + @retval EFI_DEVICE_ERROR The serial port fails to send the string out. + @retval EFI_WARN_UNKNOWN_GLYPH Indicates that some of the characters in the Unicode string could not + be rendered and are skipped. + @retval EFI_UNSUPPORTED If current display mode is out of range. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + TERMINAL_DEV *TerminalDevice; + EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode; + UINTN MaxColumn; + UINTN MaxRow; + UINTN Length; + UTF8_CHAR Utf8Char; + CHAR8 GraphicChar; + CHAR8 AsciiChar; + EFI_STATUS Status; + UINT8 ValidBytes; + CHAR8 CrLfStr[2]; + // + // flag used to indicate whether condition happens which will cause + // return EFI_WARN_UNKNOWN_GLYPH + // + BOOLEAN Warning; + + ValidBytes = 0; + Warning = FALSE; + AsciiChar = 0; + + // + // get Terminal device data structure pointer. + // + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + + // + // Get current display mode + // + Mode = This->Mode; + + if (Mode->Mode >= Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + This->QueryMode ( + This, + Mode->Mode, + &MaxColumn, + &MaxRow + ); + + for (; *WString != CHAR_NULL; WString++) { + + switch (TerminalDevice->TerminalType) { + + case TerminalTypePcAnsi: + case TerminalTypeVt100: + case TerminalTypeVt100Plus: + case TerminalTypeTtyTerm: + case TerminalTypeLinux: + case TerminalTypeXtermR6: + case TerminalTypeVt400: + case TerminalTypeSCO: + + if (!TerminalIsValidTextGraphics (*WString, &GraphicChar, &AsciiChar)) { + // + // If it's not a graphic character convert Unicode to ASCII. + // + GraphicChar = (CHAR8) *WString; + + if (!(TerminalIsValidAscii (GraphicChar) || TerminalIsValidEfiCntlChar (GraphicChar))) { + // + // when this driver use the OutputString to output control string, + // TerminalDevice->OutputEscChar is set to let the Esc char + // to be output to the terminal emulation software. + // + if ((GraphicChar == 27) && TerminalDevice->OutputEscChar) { + GraphicChar = 27; + } else { + GraphicChar = '?'; + Warning = TRUE; + } + } + + AsciiChar = GraphicChar; + + } + + if (TerminalDevice->TerminalType != TerminalTypePcAnsi) { + GraphicChar = AsciiChar; + } + + Length = 1; + + Status = TerminalDevice->SerialIo->Write ( + TerminalDevice->SerialIo, + &Length, + &GraphicChar + ); + + if (EFI_ERROR (Status)) { + goto OutputError; + } + + break; + + case TerminalTypeVtUtf8: + UnicodeToUtf8 (*WString, &Utf8Char, &ValidBytes); + Length = ValidBytes; + Status = TerminalDevice->SerialIo->Write ( + TerminalDevice->SerialIo, + &Length, + (UINT8 *) &Utf8Char + ); + if (EFI_ERROR (Status)) { + goto OutputError; + } + break; + } + // + // Update cursor position. + // + switch (*WString) { + + case CHAR_BACKSPACE: + if (Mode->CursorColumn > 0) { + Mode->CursorColumn--; + } + break; + + case CHAR_LINEFEED: + if (Mode->CursorRow < (INT32) (MaxRow - 1)) { + Mode->CursorRow++; + } + break; + + case CHAR_CARRIAGE_RETURN: + Mode->CursorColumn = 0; + break; + + default: + if (Mode->CursorColumn < (INT32) (MaxColumn - 1)) { + + Mode->CursorColumn++; + + } else { + + Mode->CursorColumn = 0; + if (Mode->CursorRow < (INT32) (MaxRow - 1)) { + Mode->CursorRow++; + } + + if (TerminalDevice->TerminalType == TerminalTypeTtyTerm && + !TerminalDevice->OutputEscChar) { + // + // We've written the last character on the line. The + // terminal doesn't actually wrap its cursor until we print + // the next character, but the driver thinks it has wrapped + // already. Print CR LF to synchronize the terminal with + // the driver, but only if we're not in the middle of + // printing an escape sequence. + // + CrLfStr[0] = '\r'; + CrLfStr[1] = '\n'; + + Length = sizeof(CrLfStr); + + Status = TerminalDevice->SerialIo->Write ( + TerminalDevice->SerialIo, + &Length, + CrLfStr + ); + + if (EFI_ERROR (Status)) { + goto OutputError; + } + } + } + break; + + }; + + } + + if (Warning) { + return EFI_WARN_UNKNOWN_GLYPH; + } + + return EFI_SUCCESS; + +OutputError: + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_OUTPUT_ERROR), + TerminalDevice->DevicePath + ); + + return EFI_DEVICE_ERROR; +} + + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.TestString(). + + If one of the characters in the *Wstring is + neither valid Unicode drawing characters, + not ASCII code, then this function will return + EFI_UNSUPPORTED. + + @param This Indicates the calling context. + @param WString The Null-terminated Unicode string to be tested. + + @retval EFI_SUCCESS The terminal is capable of rendering the output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be rendered. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + TERMINAL_DEV *TerminalDevice; + EFI_STATUS Status; + + // + // get Terminal device data structure pointer. + // + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + + switch (TerminalDevice->TerminalType) { + + case TerminalTypePcAnsi: + case TerminalTypeVt100: + case TerminalTypeVt100Plus: + case TerminalTypeTtyTerm: + Status = AnsiTestString (TerminalDevice, WString); + break; + + case TerminalTypeVtUtf8: + Status = VTUTF8TestString (TerminalDevice, WString); + break; + + default: + Status = EFI_UNSUPPORTED; + break; + } + + return Status; +} + + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.QueryMode(). + + It returns information for an available text mode + that the terminal supports. + + @param This Indicates the calling context. + @param ModeNumber The mode number to return information on. + @param Columns The returned columns of the requested mode. + @param Rows The returned rows of the requested mode. + + @retval EFI_SUCCESS The requested mode information is returned. + @retval EFI_UNSUPPORTED The mode number is not valid. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ) +{ + TERMINAL_DEV *TerminalDevice; + + if (ModeNumber >= (UINTN) This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + // + // Get Terminal device data structure pointer. + // + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + *Columns = TerminalDevice->TerminalConsoleModeData[ModeNumber].Columns; + *Rows = TerminalDevice->TerminalConsoleModeData[ModeNumber].Rows; + + return EFI_SUCCESS; +} + + +/** + Implements EFI_SIMPLE_TEXT_OUT.SetMode(). + + Set the terminal to a specified display mode. + In this driver, we only support mode 0. + + @param This Indicates the calling context. + @param ModeNumber The text mode to set. + + @retval EFI_SUCCESS The requested text mode is set. + @retval EFI_DEVICE_ERROR The requested text mode cannot be set + because of serial device error. + @retval EFI_UNSUPPORTED The text mode number is not valid. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ) +{ + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + + // + // get Terminal device data structure pointer. + // + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + + if (ModeNumber >= (UINTN) This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + // + // Set the current mode + // + This->Mode->Mode = (INT32) ModeNumber; + + This->ClearScreen (This); + + TerminalDevice->OutputEscChar = TRUE; + Status = This->OutputString (This, mSetModeString); + TerminalDevice->OutputEscChar = FALSE; + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + This->Mode->Mode = (INT32) ModeNumber; + + Status = This->ClearScreen (This); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; + +} + + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). + + @param This Indicates the calling context. + @param Attribute The attribute to set. Only bit0..6 are valid, all other bits + are undefined and must be zero. + + @retval EFI_SUCCESS The requested attribute is set. + @retval EFI_DEVICE_ERROR The requested attribute cannot be set due to serial port error. + @retval EFI_UNSUPPORTED The attribute requested is not defined by EFI spec. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ) +{ + UINT8 ForegroundControl; + UINT8 BackgroundControl; + UINT8 BrightControl; + INT32 SavedColumn; + INT32 SavedRow; + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + + SavedColumn = 0; + SavedRow = 0; + + // + // get Terminal device data structure pointer. + // + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + + // + // only the bit0..6 of the Attribute is valid + // + if ((Attribute | 0x7f) != 0x7f) { + return EFI_UNSUPPORTED; + } + + // + // Skip outputting the command string for the same attribute + // It improves the terminal performance significantly + // + if (This->Mode->Attribute == (INT32) Attribute) { + return EFI_SUCCESS; + } + + // + // convert Attribute value to terminal emulator + // understandable foreground color + // + switch (Attribute & 0x07) { + + case EFI_BLACK: + ForegroundControl = 30; + break; + + case EFI_BLUE: + ForegroundControl = 34; + break; + + case EFI_GREEN: + ForegroundControl = 32; + break; + + case EFI_CYAN: + ForegroundControl = 36; + break; + + case EFI_RED: + ForegroundControl = 31; + break; + + case EFI_MAGENTA: + ForegroundControl = 35; + break; + + case EFI_BROWN: + ForegroundControl = 33; + break; + + default: + + case EFI_LIGHTGRAY: + ForegroundControl = 37; + break; + + } + // + // bit4 of the Attribute indicates bright control + // of terminal emulator. + // + BrightControl = (UINT8) ((Attribute >> 3) & 1); + + // + // convert Attribute value to terminal emulator + // understandable background color. + // + switch ((Attribute >> 4) & 0x07) { + + case EFI_BLACK: + BackgroundControl = 40; + break; + + case EFI_BLUE: + BackgroundControl = 44; + break; + + case EFI_GREEN: + BackgroundControl = 42; + break; + + case EFI_CYAN: + BackgroundControl = 46; + break; + + case EFI_RED: + BackgroundControl = 41; + break; + + case EFI_MAGENTA: + BackgroundControl = 45; + break; + + case EFI_BROWN: + BackgroundControl = 43; + break; + + default: + + case EFI_LIGHTGRAY: + BackgroundControl = 47; + break; + } + // + // terminal emulator's control sequence to set attributes + // + mSetAttributeString[BRIGHT_CONTROL_OFFSET] = (CHAR16) ('0' + BrightControl); + mSetAttributeString[FOREGROUND_CONTROL_OFFSET + 0] = (CHAR16) ('0' + (ForegroundControl / 10)); + mSetAttributeString[FOREGROUND_CONTROL_OFFSET + 1] = (CHAR16) ('0' + (ForegroundControl % 10)); + mSetAttributeString[BACKGROUND_CONTROL_OFFSET + 0] = (CHAR16) ('0' + (BackgroundControl / 10)); + mSetAttributeString[BACKGROUND_CONTROL_OFFSET + 1] = (CHAR16) ('0' + (BackgroundControl % 10)); + + // + // save current column and row + // for future scrolling back use. + // + SavedColumn = This->Mode->CursorColumn; + SavedRow = This->Mode->CursorRow; + + TerminalDevice->OutputEscChar = TRUE; + Status = This->OutputString (This, mSetAttributeString); + TerminalDevice->OutputEscChar = FALSE; + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // scroll back to saved cursor position. + // + This->Mode->CursorColumn = SavedColumn; + This->Mode->CursorRow = SavedRow; + + This->Mode->Attribute = (INT32) Attribute; + + return EFI_SUCCESS; + +} + + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.ClearScreen(). + It clears the ANSI terminal's display to the + currently selected background color. + + @param This Indicates the calling context. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The terminal screen cannot be cleared due to serial port error. + @retval EFI_UNSUPPORTED The terminal is not in a valid display mode. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ) +{ + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + + // + // control sequence for clear screen request + // + TerminalDevice->OutputEscChar = TRUE; + Status = This->OutputString (This, mClearScreenString); + TerminalDevice->OutputEscChar = FALSE; + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Status = This->SetCursorPosition (This, 0, 0); + + return Status; +} + + +/** + Implements EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetCursorPosition(). + + @param This Indicates the calling context. + @param Column The row to set cursor to. + @param Row The column to set cursor to. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The request fails due to serial port error. + @retval EFI_UNSUPPORTED The terminal is not in a valid text mode, or the cursor position + is invalid for current mode. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ) +{ + EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode; + UINTN MaxColumn; + UINTN MaxRow; + EFI_STATUS Status; + TERMINAL_DEV *TerminalDevice; + CHAR16 *String; + + TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (This); + + // + // get current mode + // + Mode = This->Mode; + + // + // get geometry of current mode + // + Status = This->QueryMode ( + This, + Mode->Mode, + &MaxColumn, + &MaxRow + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + if (Column >= MaxColumn || Row >= MaxRow) { + return EFI_UNSUPPORTED; + } + // + // control sequence to move the cursor + // + // Optimize cursor motion control sequences for TtyTerm. Move + // within the current line if possible, and don't output anyting if + // it isn't necessary. + // + if (TerminalDevice->TerminalType == TerminalTypeTtyTerm && + (UINTN)Mode->CursorRow == Row) { + if ((UINTN)Mode->CursorColumn > Column) { + mCursorBackwardString[FW_BACK_OFFSET + 0] = (CHAR16) ('0' + ((Mode->CursorColumn - Column) / 10)); + mCursorBackwardString[FW_BACK_OFFSET + 1] = (CHAR16) ('0' + ((Mode->CursorColumn - Column) % 10)); + String = mCursorBackwardString; + } + else if (Column > (UINTN)Mode->CursorColumn) { + mCursorForwardString[FW_BACK_OFFSET + 0] = (CHAR16) ('0' + ((Column - Mode->CursorColumn) / 10)); + mCursorForwardString[FW_BACK_OFFSET + 1] = (CHAR16) ('0' + ((Column - Mode->CursorColumn) % 10)); + String = mCursorForwardString; + } + else { + String = L""; // No cursor motion necessary + } + } + else { + mSetCursorPositionString[ROW_OFFSET + 0] = (CHAR16) ('0' + ((Row + 1) / 10)); + mSetCursorPositionString[ROW_OFFSET + 1] = (CHAR16) ('0' + ((Row + 1) % 10)); + mSetCursorPositionString[COLUMN_OFFSET + 0] = (CHAR16) ('0' + ((Column + 1) / 10)); + mSetCursorPositionString[COLUMN_OFFSET + 1] = (CHAR16) ('0' + ((Column + 1) % 10)); + String = mSetCursorPositionString; + } + + TerminalDevice->OutputEscChar = TRUE; + Status = This->OutputString (This, String); + TerminalDevice->OutputEscChar = FALSE; + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // update current cursor position + // in the Mode data structure. + // + Mode->CursorColumn = (INT32) Column; + Mode->CursorRow = (INT32) Row; + + return EFI_SUCCESS; +} + + +/** + Implements SIMPLE_TEXT_OUTPUT.EnableCursor(). + + In this driver, the cursor cannot be hidden. + + @param This Indicates the calling context. + @param Visible If TRUE, the cursor is set to be visible, + If FALSE, the cursor is set to be invisible. + + @retval EFI_SUCCESS The request is valid. + @retval EFI_UNSUPPORTED The terminal does not support cursor hidden. + +**/ +EFI_STATUS +EFIAPI +TerminalConOutEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ) +{ + if (!Visible) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + + +/** + Detects if a Unicode char is for Box Drawing text graphics. + + @param Graphic Unicode char to test. + @param PcAnsi Optional pointer to return PCANSI equivalent of + Graphic. + @param Ascii Optional pointer to return ASCII equivalent of + Graphic. + + @retval TRUE If Graphic is a supported Unicode Box Drawing character. + +**/ +BOOLEAN +TerminalIsValidTextGraphics ( + IN CHAR16 Graphic, + OUT CHAR8 *PcAnsi, OPTIONAL + OUT CHAR8 *Ascii OPTIONAL + ) +{ + UNICODE_TO_CHAR *Table; + + if ((((Graphic & 0xff00) != 0x2500) && ((Graphic & 0xff00) != 0x2100))) { + // + // Unicode drawing code charts are all in the 0x25xx range, + // arrows are 0x21xx + // + return FALSE; + } + + for (Table = UnicodeToPcAnsiOrAscii; Table->Unicode != 0x0000; Table++) { + if (Graphic == Table->Unicode) { + if (PcAnsi != NULL) { + *PcAnsi = Table->PcAnsi; + } + + if (Ascii != NULL) { + *Ascii = Table->Ascii; + } + + return TRUE; + } + } + + return FALSE; +} + +/** + Detects if a valid ASCII char. + + @param Ascii An ASCII character. + + @retval TRUE If it is a valid ASCII character. + @retval FALSE If it is not a valid ASCII character. + +**/ +BOOLEAN +TerminalIsValidAscii ( + IN CHAR16 Ascii + ) +{ + // + // valid ascii code lies in the extent of 0x20 ~ 0x7f + // + if ((Ascii >= 0x20) && (Ascii <= 0x7f)) { + return TRUE; + } + + return FALSE; +} + +/** + Detects if a valid EFI control character. + + @param CharC An input EFI Control character. + + @retval TRUE If it is a valid EFI control character. + @retval FALSE If it is not a valid EFI control character. + +**/ +BOOLEAN +TerminalIsValidEfiCntlChar ( + IN CHAR16 CharC + ) +{ + // + // only support four control characters. + // + if (CharC == CHAR_NULL || + CharC == CHAR_BACKSPACE || + CharC == CHAR_LINEFEED || + CharC == CHAR_CARRIAGE_RETURN || + CharC == CHAR_TAB + ) { + return TRUE; + } + + return FALSE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf new file mode 100644 index 000000000..b2a8aeba8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf @@ -0,0 +1,98 @@ +## @file +# Terminal module installs Simple Text Input(ex)/Out protocols for serial devices. +# +# This module will install Simple Text Input (Ex) protocol and Simple Test Output +# protocols based on Serial I/O protocol for serial devices including hotplug serial +# devices. +# +# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TerminalDxe + MODULE_UNI_FILE = TerminalDxe.uni + FILE_GUID = 9E863906-A40F-4875-977F-5B93FF237FC6 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeTerminal + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gTerminalDriverBinding +# COMPONENT_NAME = gTerminalComponentName +# COMPONENT_NAME2 = gTerminalComponentName2 +# + +[Sources] + ComponentName.c + Vtutf8.c + Ansi.c + TerminalConOut.c + TerminalConIn.c + Terminal.c + Terminal.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + DevicePathLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + ReportStatusCodeLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + BaseLib + +[Guids] + ## SOMETIMES_PRODUCES ## Variable:L"ConInDev" + ## SOMETIMES_CONSUMES ## Variable:L"ConInDev" + ## SOMETIMES_PRODUCES ## Variable:L"ConOutDev" + ## SOMETIMES_CONSUMES ## Variable:L"ConOutDev" + ## SOMETIMES_PRODUCES ## Variable:L"ErrOutDev" + ## SOMETIMES_CONSUMES ## Variable:L"ErrOutDev" + gEfiGlobalVariableGuid + gEfiVTUTF8Guid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEfiVT100Guid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEfiVT100PlusGuid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEfiPcAnsiGuid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEfiTtyTermGuid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEdkiiLinuxTermGuid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEdkiiXtermR6Guid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEdkiiVT400Guid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEdkiiSCOTermGuid ## SOMETIMES_CONSUMES ## GUID # used with a Vendor-Defined Messaging Device Path + gEdkiiStatusCodeDataTypeVariableGuid ## SOMETIMES_CONSUMES ## GUID + +[Protocols] + gEfiSerialIoProtocolGuid ## TO_START + ## BY_START + ## TO_START + gEfiDevicePathProtocolGuid + gEfiSimpleTextInProtocolGuid ## BY_START + gEfiSimpleTextInputExProtocolGuid ## BY_START + gEfiSimpleTextOutProtocolGuid ## BY_START + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdErrorCodeSetVariable ## CONSUMES + +# [Event] +# # Relative timer event set by UnicodeToEfiKey(), used to be one 2 seconds input timeout. +# EVENT_TYPE_RELATIVE_TIMER ## CONSUMES +# # Period timer event to invoke TerminalConInTimerHandler(), period value is KEYBOARD_TIMER_INTERVAL and used to poll the key from serial +# EVENT_TYPE_PERIODIC_TIMER ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + TerminalDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.uni b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.uni new file mode 100644 index 000000000..0048229a1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.uni @@ -0,0 +1,18 @@ +// /** @file +// Terminal module installs Simple Text Input(ex)/Out protocols for serial devices. +// +// This module will install Simple Text Input (Ex) protocol and Simple Test Output +// protocols based on Serial I/O protocol for serial devices including hotplug serial +// devices. +// +// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Terminal module installs Simple Text Input(ex)/Out protocols for serial devices" + +#string STR_MODULE_DESCRIPTION #language en-US "This module will install Simple Text Input (Ex) protocol and Simple Test Output protocols based on Serial I/O protocol for serial devices including hotplug serial devices." + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxeExtra.uni new file mode 100644 index 000000000..018a9c817 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// TerminalDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Terminal DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Vtutf8.c b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Vtutf8.c new file mode 100644 index 000000000..9cf52d90b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Vtutf8.c @@ -0,0 +1,322 @@ +/** @file + Implementation of translation upon VT-UTF8. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Terminal.h" + +/** + Translate all VT-UTF8 characters in the Raw FIFI into unicode characters, + and insert them into Unicode FIFO. + + @param TerminalDevice The terminal device. + +**/ +VOID +VTUTF8RawDataToUnicode ( + IN TERMINAL_DEV *TerminalDevice + ) +{ + UTF8_CHAR Utf8Char; + UINT8 ValidBytes; + UINT16 UnicodeChar; + + ValidBytes = 0; + // + // pop the raw data out from the raw fifo, + // and translate it into unicode, then push + // the unicode into unicode fifo, until the raw fifo is empty. + // + while (!IsRawFiFoEmpty (TerminalDevice) && !IsUnicodeFiFoFull (TerminalDevice)) { + + GetOneValidUtf8Char (TerminalDevice, &Utf8Char, &ValidBytes); + + if (ValidBytes < 1 || ValidBytes > 3) { + continue; + } + + Utf8ToUnicode (Utf8Char, ValidBytes, (CHAR16 *) &UnicodeChar); + + UnicodeFiFoInsertOneKey (TerminalDevice, UnicodeChar); + } +} + +/** + Get one valid VT-UTF8 characters set from Raw Data FIFO. + + @param Utf8Device The terminal device. + @param Utf8Char Returned valid VT-UTF8 characters set. + @param ValidBytes The count of returned VT-VTF8 characters. + If ValidBytes is zero, no valid VT-UTF8 returned. + +**/ +VOID +GetOneValidUtf8Char ( + IN TERMINAL_DEV *Utf8Device, + OUT UTF8_CHAR *Utf8Char, + OUT UINT8 *ValidBytes + ) +{ + UINT8 Temp; + UINT8 Index; + BOOLEAN FetchFlag; + + Temp = 0; + Index = 0; + FetchFlag = TRUE; + + // + // if no valid Utf8 char is found in the RawFiFo, + // then *ValidBytes will be zero. + // + *ValidBytes = 0; + + while (!IsRawFiFoEmpty (Utf8Device)) { + + RawFiFoRemoveOneKey (Utf8Device, &Temp); + + switch (*ValidBytes) { + + case 0: + if ((Temp & 0x80) == 0) { + // + // one-byte utf8 char + // + *ValidBytes = 1; + + Utf8Char->Utf8_1 = Temp; + + FetchFlag = FALSE; + + } else if ((Temp & 0xe0) == 0xc0) { + // + // two-byte utf8 char + // + *ValidBytes = 2; + + Utf8Char->Utf8_2[1] = Temp; + + } else if ((Temp & 0xf0) == 0xe0) { + // + // three-byte utf8 char + // + *ValidBytes = 3; + + Utf8Char->Utf8_3[2] = Temp; + + Index++; + + } else { + // + // reset *ValidBytes to zero, let valid utf8 char search restart + // + *ValidBytes = 0; + } + + break; + + case 2: + // + // two-byte utf8 char go on + // + if ((Temp & 0xc0) == 0x80) { + + Utf8Char->Utf8_2[0] = Temp; + + FetchFlag = FALSE; + + } else { + + *ValidBytes = 0; + } + break; + + case 3: + // + // three-byte utf8 char go on + // + if ((Temp & 0xc0) == 0x80) { + if (Index == 1) { + Utf8Char->Utf8_3[1] = Temp; + Index++; + } else { + Utf8Char->Utf8_3[0] = Temp; + FetchFlag = FALSE; + } + } else { + // + // reset *ValidBytes and Index to zero, let valid utf8 char search restart + // + *ValidBytes = 0; + Index = 0; + } + break; + + default: + break; + } + + if (!FetchFlag) { + break; + } + } + + return ; +} + +/** + Translate VT-UTF8 characters into one Unicode character. + + UTF8 Encoding Table + Bits per Character | Unicode Character Range | Unicode Binary Encoding | UTF8 Binary Encoding + 0-7 | 0x0000 - 0x007F | 00000000 0xxxxxxx | 0xxxxxxx + 8-11 | 0x0080 - 0x07FF | 00000xxx xxxxxxxx | 110xxxxx 10xxxxxx + 12-16 | 0x0800 - 0xFFFF | xxxxxxxx xxxxxxxx | 1110xxxx 10xxxxxx 10xxxxxx + + + @param Utf8Char VT-UTF8 character set needs translating. + @param ValidBytes The count of valid VT-UTF8 characters. + @param UnicodeChar Returned unicode character. + +**/ +VOID +Utf8ToUnicode ( + IN UTF8_CHAR Utf8Char, + IN UINT8 ValidBytes, + OUT CHAR16 *UnicodeChar + ) +{ + UINT8 UnicodeByte0; + UINT8 UnicodeByte1; + UINT8 Byte0; + UINT8 Byte1; + UINT8 Byte2; + + *UnicodeChar = 0; + + // + // translate utf8 code to unicode, in terminal standard, + // up to 3 bytes utf8 code is supported. + // + switch (ValidBytes) { + case 1: + // + // one-byte utf8 code + // + *UnicodeChar = (UINT16) Utf8Char.Utf8_1; + break; + + case 2: + // + // two-byte utf8 code + // + Byte0 = Utf8Char.Utf8_2[0]; + Byte1 = Utf8Char.Utf8_2[1]; + + UnicodeByte0 = (UINT8) ((Byte1 << 6) | (Byte0 & 0x3f)); + UnicodeByte1 = (UINT8) ((Byte1 >> 2) & 0x07); + *UnicodeChar = (UINT16) (UnicodeByte0 | (UnicodeByte1 << 8)); + break; + + case 3: + // + // three-byte utf8 code + // + Byte0 = Utf8Char.Utf8_3[0]; + Byte1 = Utf8Char.Utf8_3[1]; + Byte2 = Utf8Char.Utf8_3[2]; + + UnicodeByte0 = (UINT8) ((Byte1 << 6) | (Byte0 & 0x3f)); + UnicodeByte1 = (UINT8) ((Byte2 << 4) | ((Byte1 >> 2) & 0x0f)); + *UnicodeChar = (UINT16) (UnicodeByte0 | (UnicodeByte1 << 8)); + + default: + break; + } + + return ; +} + +/** + Translate one Unicode character into VT-UTF8 characters. + + UTF8 Encoding Table + Bits per Character | Unicode Character Range | Unicode Binary Encoding | UTF8 Binary Encoding + 0-7 | 0x0000 - 0x007F | 00000000 0xxxxxxx | 0xxxxxxx + 8-11 | 0x0080 - 0x07FF | 00000xxx xxxxxxxx | 110xxxxx 10xxxxxx + 12-16 | 0x0800 - 0xFFFF | xxxxxxxx xxxxxxxx | 1110xxxx 10xxxxxx 10xxxxxx + + + @param Unicode Unicode character need translating. + @param Utf8Char Return VT-UTF8 character set. + @param ValidBytes The count of valid VT-UTF8 characters. If + ValidBytes is zero, no valid VT-UTF8 returned. + +**/ +VOID +UnicodeToUtf8 ( + IN CHAR16 Unicode, + OUT UTF8_CHAR *Utf8Char, + OUT UINT8 *ValidBytes + ) +{ + UINT8 UnicodeByte0; + UINT8 UnicodeByte1; + // + // translate unicode to utf8 code + // + UnicodeByte0 = (UINT8) Unicode; + UnicodeByte1 = (UINT8) (Unicode >> 8); + + if (Unicode < 0x0080) { + + Utf8Char->Utf8_1 = (UINT8) (UnicodeByte0 & 0x7f); + *ValidBytes = 1; + + } else if (Unicode < 0x0800) { + // + // byte sequence: high -> low + // Utf8_2[0], Utf8_2[1] + // + Utf8Char->Utf8_2[1] = (UINT8) ((UnicodeByte0 & 0x3f) + 0x80); + Utf8Char->Utf8_2[0] = (UINT8) ((((UnicodeByte1 << 2) + (UnicodeByte0 >> 6)) & 0x1f) + 0xc0); + + *ValidBytes = 2; + + } else { + // + // byte sequence: high -> low + // Utf8_3[0], Utf8_3[1], Utf8_3[2] + // + Utf8Char->Utf8_3[2] = (UINT8) ((UnicodeByte0 & 0x3f) + 0x80); + Utf8Char->Utf8_3[1] = (UINT8) ((((UnicodeByte1 << 2) + (UnicodeByte0 >> 6)) & 0x3f) + 0x80); + Utf8Char->Utf8_3[0] = (UINT8) (((UnicodeByte1 >> 4) & 0x0f) + 0xe0); + + *ValidBytes = 3; + } +} + + +/** + Check if input string is valid VT-UTF8 string. + + @param TerminalDevice The terminal device. + @param WString The input string. + + @retval EFI_SUCCESS If all input characters are valid. + +**/ +EFI_STATUS +VTUTF8TestString ( + IN TERMINAL_DEV *TerminalDevice, + IN CHAR16 *WString + ) +{ + // + // to utf8, all kind of characters are supported. + // + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/ComponentName.c new file mode 100644 index 000000000..69915b842 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/ComponentName.c @@ -0,0 +1,176 @@ +/** @file + UEFI Component Name(2) protocol implementation for DebugPort driver. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DebugPort.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDebugPortComponentName = { + DebugPortComponentNameGetDriverName, + DebugPortComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDebugPortComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DebugPortComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DebugPortComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDebugPortDriverNameTable[] = { + { + "eng;en", + (CHAR16 *) L"DebugPort Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DebugPortComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDebugPortDriverNameTable, + DriverName, + (BOOLEAN)(This == &gDebugPortComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DebugPortComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.c b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.c new file mode 100644 index 000000000..172c1cbca --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.c @@ -0,0 +1,739 @@ +/** @file + Top level C file for debugport driver. Contains initialization function. + This driver layers on top of SerialIo. + ALL CODE IN THE SERIALIO STACK MUST BE RE-ENTRANT AND CALLABLE FROM + INTERRUPT CONTEXT + +Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DebugPort.h" + +// +// Globals +// +EFI_DRIVER_BINDING_PROTOCOL gDebugPortDriverBinding = { + DebugPortSupported, + DebugPortStart, + DebugPortStop, + DEBUGPORT_DRIVER_VERSION, + NULL, + NULL +}; + +DEBUGPORT_DEVICE mDebugPortDevice = { + DEBUGPORT_DEVICE_SIGNATURE, + (EFI_HANDLE) 0, + (EFI_HANDLE) 0, + (EFI_DEVICE_PATH_PROTOCOL *) NULL, + { + DebugPortReset, + DebugPortWrite, + DebugPortRead, + DebugPortPoll + }, + (EFI_HANDLE) 0, + (EFI_SERIAL_IO_PROTOCOL *) NULL, + DEBUGPORT_UART_DEFAULT_BAUDRATE, + DEBUGPORT_UART_DEFAULT_FIFO_DEPTH, + DEBUGPORT_UART_DEFAULT_TIMEOUT, + (EFI_PARITY_TYPE) DEBUGPORT_UART_DEFAULT_PARITY, + DEBUGPORT_UART_DEFAULT_DATA_BITS, + (EFI_STOP_BITS_TYPE) DEBUGPORT_UART_DEFAULT_STOP_BITS +}; + +/** + Local worker function to obtain device path information from DebugPort variable. + + Records requested settings in DebugPort device structure. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +GetDebugPortVariable ( + VOID + ) +{ + UINTN DataSize; + EFI_DEVICE_PATH_PROTOCOL *DebugPortVariable; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + GetVariable2 (EFI_DEBUGPORT_VARIABLE_NAME, &gEfiDebugPortVariableGuid, (VOID **) &DebugPortVariable, &DataSize); + if (DebugPortVariable == NULL) { + return NULL; + } + + DevicePath = DebugPortVariable; + while (!IsDevicePathEnd (DevicePath) && !IS_UART_DEVICEPATH (DevicePath)) { + DevicePath = NextDevicePathNode (DevicePath); + } + + if (IsDevicePathEnd (DevicePath)) { + FreePool (DebugPortVariable); + return NULL; + } else { + CopyMem ( + &mDebugPortDevice.BaudRate, + &((UART_DEVICE_PATH *) DevicePath)->BaudRate, + sizeof (((UART_DEVICE_PATH *) DevicePath)->BaudRate) + ); + mDebugPortDevice.ReceiveFifoDepth = DEBUGPORT_UART_DEFAULT_FIFO_DEPTH; + mDebugPortDevice.Timeout = DEBUGPORT_UART_DEFAULT_TIMEOUT; + CopyMem ( + &mDebugPortDevice.Parity, + &((UART_DEVICE_PATH *) DevicePath)->Parity, + sizeof (((UART_DEVICE_PATH *) DevicePath)->Parity) + ); + CopyMem ( + &mDebugPortDevice.DataBits, + &((UART_DEVICE_PATH *) DevicePath)->DataBits, + sizeof (((UART_DEVICE_PATH *) DevicePath)->DataBits) + ); + CopyMem ( + &mDebugPortDevice.StopBits, + &((UART_DEVICE_PATH *) DevicePath)->StopBits, + sizeof (((UART_DEVICE_PATH *) DevicePath)->StopBits) + ); + return DebugPortVariable; + } +} + +/** + Debug Port Driver entry point. + + Reads DebugPort variable to determine what device and settings to use as the + debug port. Binds exclusively to SerialIo. Reverts to defaults if no variable + is found. + + @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 entry point is executed successfully. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory for device. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeDebugPortDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDebugPortDriverBinding, + ImageHandle, + &gDebugPortComponentName, + &gDebugPortComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Checks to see if there's not already a DebugPort interface somewhere. + + If there's a DEBUGPORT variable, the device path must match exactly. If there's + no DEBUGPORT variable, then device path is not checked and does not matter. + Checks to see that there's a serial io interface on the controller handle + that can be bound BY_DRIVER | EXCLUSIVE. + If all these tests succeed, then we return EFI_SUCCESS, else, EFI_UNSUPPORTED + or other error returned by OpenProtocol. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED Debug Port device is not supported. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory for device. + @retval others Some error occurs. + +**/ +EFI_STATUS +EFIAPI +DebugPortSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *DebugPortVariable; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + EFI_DEBUGPORT_PROTOCOL *DebugPortInterface; + EFI_HANDLE TempHandle; + + // + // Check to see that there's not a debugport protocol already published, + // since only one standard UART serial port could be supported by this driver. + // + if (gBS->LocateProtocol (&gEfiDebugPortProtocolGuid, NULL, (VOID **) &DebugPortInterface) != EFI_NOT_FOUND) { + return EFI_UNSUPPORTED; + } + // + // Read DebugPort variable to determine debug port selection and parameters + // + DebugPortVariable = GetDebugPortVariable (); + + if (DebugPortVariable != NULL) { + // + // There's a DEBUGPORT variable, so do LocateDevicePath and check to see if + // the closest matching handle matches the controller handle, and if it does, + // check to see that the remaining device path has the DebugPort GUIDed messaging + // device path only. Otherwise, it's a mismatch and EFI_UNSUPPORTED is returned. + // + DevicePath = DebugPortVariable; + Status = gBS->LocateDevicePath ( + &gEfiSerialIoProtocolGuid, + &DevicePath, + &TempHandle + ); + + if (Status == EFI_SUCCESS && TempHandle != ControllerHandle) { + Status = EFI_UNSUPPORTED; + } + + if (Status == EFI_SUCCESS && + (DevicePath->Type != MESSAGING_DEVICE_PATH || + DevicePath->SubType != MSG_VENDOR_DP || + *((UINT16 *) DevicePath->Length) != sizeof (DEBUGPORT_DEVICE_PATH))) { + + Status = EFI_UNSUPPORTED; + } + + if (Status == EFI_SUCCESS && !CompareGuid (&gEfiDebugPortDevicePathGuid, (GUID *) (DevicePath + 1))) { + Status = EFI_UNSUPPORTED; + } + + FreePool (DebugPortVariable); + if (EFI_ERROR (Status)) { + return Status; + } + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return Status; +} + +/** + Binds exclusively to serial io on the controller handle, Produces DebugPort + protocol and DevicePath on new handle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory for device. + @retval others Some error occurs. + +**/ +EFI_STATUS +EFIAPI +DebugPortStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + DEBUGPORT_DEVICE_PATH DebugPortDP; + EFI_DEVICE_PATH_PROTOCOL EndDP; + EFI_DEVICE_PATH_PROTOCOL *Dp1; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &mDebugPortDevice.SerialIoBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + mDebugPortDevice.SerialIoDeviceHandle = ControllerHandle; + + // + // Initialize the Serial Io interface... + // + Status = mDebugPortDevice.SerialIoBinding->SetAttributes ( + mDebugPortDevice.SerialIoBinding, + mDebugPortDevice.BaudRate, + mDebugPortDevice.ReceiveFifoDepth, + mDebugPortDevice.Timeout, + mDebugPortDevice.Parity, + mDebugPortDevice.DataBits, + mDebugPortDevice.StopBits + ); + if (EFI_ERROR (Status)) { + mDebugPortDevice.BaudRate = 0; + mDebugPortDevice.Parity = DefaultParity; + mDebugPortDevice.DataBits = 0; + mDebugPortDevice.StopBits = DefaultStopBits; + mDebugPortDevice.ReceiveFifoDepth = 0; + Status = mDebugPortDevice.SerialIoBinding->SetAttributes ( + mDebugPortDevice.SerialIoBinding, + mDebugPortDevice.BaudRate, + mDebugPortDevice.ReceiveFifoDepth, + mDebugPortDevice.Timeout, + mDebugPortDevice.Parity, + mDebugPortDevice.DataBits, + mDebugPortDevice.StopBits + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; + } + } + + mDebugPortDevice.SerialIoBinding->Reset (mDebugPortDevice.SerialIoBinding); + + // + // Create device path instance for DebugPort + // + DebugPortDP.Header.Type = MESSAGING_DEVICE_PATH; + DebugPortDP.Header.SubType = MSG_VENDOR_DP; + SetDevicePathNodeLength (&(DebugPortDP.Header), sizeof (DebugPortDP)); + CopyGuid (&DebugPortDP.Guid, &gEfiDebugPortDevicePathGuid); + + Dp1 = DevicePathFromHandle (ControllerHandle); + if (Dp1 == NULL) { + Dp1 = &EndDP; + SetDevicePathEndNode (Dp1); + } + + mDebugPortDevice.DebugPortDevicePath = AppendDevicePathNode (Dp1, (EFI_DEVICE_PATH_PROTOCOL *) &DebugPortDP); + if (mDebugPortDevice.DebugPortDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Publish DebugPort and Device Path protocols + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mDebugPortDevice.DebugPortDeviceHandle, + &gEfiDevicePathProtocolGuid, + mDebugPortDevice.DebugPortDevicePath, + &gEfiDebugPortProtocolGuid, + &mDebugPortDevice.DebugPortInterface, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; + } + // + // Connect debugport child to serial io + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &mDebugPortDevice.SerialIoBinding, + This->DriverBindingHandle, + mDebugPortDevice.DebugPortDeviceHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; + } + + return EFI_SUCCESS; +} + +/** + Stop this driver on ControllerHandle by removing Serial IO protocol on + the ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +DebugPortStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + + if (NumberOfChildren == 0) { + // + // Close the bus driver + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + mDebugPortDevice.SerialIoBinding = NULL; + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + FreePool (mDebugPortDevice.DebugPortDevicePath); + + return EFI_SUCCESS; + } else { + // + // Disconnect SerialIo child handle + // + Status = gBS->CloseProtocol ( + mDebugPortDevice.SerialIoDeviceHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + mDebugPortDevice.DebugPortDeviceHandle + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Unpublish our protocols (DevicePath, DebugPort) + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + mDebugPortDevice.DebugPortDeviceHandle, + &gEfiDevicePathProtocolGuid, + mDebugPortDevice.DebugPortDevicePath, + &gEfiDebugPortProtocolGuid, + &mDebugPortDevice.DebugPortInterface, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &mDebugPortDevice.SerialIoBinding, + This->DriverBindingHandle, + mDebugPortDevice.DebugPortDeviceHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + mDebugPortDevice.DebugPortDeviceHandle = NULL; + } + } + + return Status; +} + +/** + DebugPort protocol member function. Calls SerialIo:GetControl to flush buffer. + We cannot call SerialIo:SetAttributes because it uses pool services, which use + locks, which affect TPL, so it's not interrupt context safe or re-entrant. + SerialIo:Reset() calls SetAttributes, so it can't be used either. + + The port itself should be fine since it was set up during initialization. + + @param This Protocol instance pointer. + + @return EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +DebugPortReset ( + IN EFI_DEBUGPORT_PROTOCOL *This + ) +{ + UINTN BufferSize; + UINTN BitBucket; + + while (This->Poll (This) == EFI_SUCCESS) { + BufferSize = 1; + This->Read (This, 0, &BufferSize, &BitBucket); + } + + return EFI_SUCCESS; +} + +/** + DebugPort protocol member function. Calls SerialIo:Read() after setting + if it's different than the last SerialIo access. + + @param This Pointer to DebugPort protocol. + @param Timeout Timeout value. + @param BufferSize On input, the size of Buffer. + On output, the amount of data actually written. + @param Buffer Pointer to buffer to read. + + @retval EFI_SUCCESS + @retval others + +**/ +EFI_STATUS +EFIAPI +DebugPortRead ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + DEBUGPORT_DEVICE *DebugPortDevice; + UINTN LocalBufferSize; + EFI_STATUS Status; + UINT8 *BufferPtr; + + DebugPortDevice = DEBUGPORT_DEVICE_FROM_THIS (This); + BufferPtr = Buffer; + LocalBufferSize = *BufferSize; + + do { + Status = DebugPortDevice->SerialIoBinding->Read ( + DebugPortDevice->SerialIoBinding, + &LocalBufferSize, + BufferPtr + ); + if (Status == EFI_TIMEOUT) { + if (Timeout > DEBUGPORT_UART_DEFAULT_TIMEOUT) { + Timeout -= DEBUGPORT_UART_DEFAULT_TIMEOUT; + } else { + Timeout = 0; + } + } else if (EFI_ERROR (Status)) { + break; + } + + BufferPtr += LocalBufferSize; + LocalBufferSize = *BufferSize - (BufferPtr - (UINT8 *) Buffer); + } while (LocalBufferSize != 0 && Timeout > 0); + + *BufferSize = (UINTN) BufferPtr - (UINTN) Buffer; + + return Status; +} + +/** + DebugPort protocol member function. Calls SerialIo:Write() Writes 8 bytes at + a time and does a GetControl between 8 byte writes to help insure reads are + interspersed This is poor-man's flow control. + + @param This Pointer to DebugPort protocol. + @param Timeout Timeout value. + @param BufferSize On input, the size of Buffer. + On output, the amount of data actually written. + @param Buffer Pointer to buffer to read. + + @retval EFI_SUCCESS The data was written. + @retval others Fails when writting datas to debug port device. + +**/ +EFI_STATUS +EFIAPI +DebugPortWrite ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + DEBUGPORT_DEVICE *DebugPortDevice; + UINTN Position; + UINTN WriteSize; + EFI_STATUS Status; + UINT32 SerialControl; + + Status = EFI_SUCCESS; + DebugPortDevice = DEBUGPORT_DEVICE_FROM_THIS (This); + + WriteSize = 8; + for (Position = 0; Position < *BufferSize && !EFI_ERROR (Status); Position += WriteSize) { + DebugPortDevice->SerialIoBinding->GetControl ( + DebugPortDevice->SerialIoBinding, + &SerialControl + ); + if (*BufferSize - Position < 8) { + WriteSize = *BufferSize - Position; + } + + Status = DebugPortDevice->SerialIoBinding->Write ( + DebugPortDevice->SerialIoBinding, + &WriteSize, + &((UINT8 *) Buffer)[Position] + ); + } + + *BufferSize = Position; + return Status; +} + +/** + DebugPort protocol member function. Calls SerialIo:Write() after setting + if it's different than the last SerialIo access. + + @param This Pointer to DebugPort protocol. + + @retval EFI_SUCCESS At least 1 character is ready to be read from + the DebugPort interface. + @retval EFI_NOT_READY There are no characters ready to read from the + DebugPort interface + @retval EFI_DEVICE_ERROR A hardware failure occurred... (from SerialIo) + +**/ +EFI_STATUS +EFIAPI +DebugPortPoll ( + IN EFI_DEBUGPORT_PROTOCOL *This + ) +{ + EFI_STATUS Status; + UINT32 SerialControl; + DEBUGPORT_DEVICE *DebugPortDevice; + + DebugPortDevice = DEBUGPORT_DEVICE_FROM_THIS (This); + + Status = DebugPortDevice->SerialIoBinding->GetControl ( + DebugPortDevice->SerialIoBinding, + &SerialControl + ); + + if (!EFI_ERROR (Status)) { + if ((SerialControl & EFI_SERIAL_INPUT_BUFFER_EMPTY) != 0) { + Status = EFI_NOT_READY; + } else { + Status = EFI_SUCCESS; + } + } + + return Status; +} + +/** + Unload function that is registered in the LoadImage protocol. It un-installs + protocols produced and deallocates pool used by the driver. Called by the core + when unloading the driver. + + @param ImageHandle + + @retval EFI_SUCCESS Unload Debug Port driver successfully. + @retval EFI_ABORTED Serial IO is still binding. + +**/ +EFI_STATUS +EFIAPI +ImageUnloadHandler ( + EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + VOID *ComponentName; + VOID *ComponentName2; + + if (mDebugPortDevice.SerialIoBinding != NULL) { + return EFI_ABORTED; + } + + // + // Driver is stopped already. + // + Status = gBS->HandleProtocol (ImageHandle, &gEfiComponentNameProtocolGuid, &ComponentName); + if (EFI_ERROR (Status)) { + ComponentName = NULL; + } + + Status = gBS->HandleProtocol (ImageHandle, &gEfiComponentName2ProtocolGuid, &ComponentName2); + if (EFI_ERROR (Status)) { + ComponentName2 = NULL; + } + + if (ComponentName == NULL) { + if (ComponentName2 == NULL) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gDebugPortDriverBinding, + NULL + ); + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gDebugPortDriverBinding, + &gEfiComponentName2ProtocolGuid, ComponentName2, + NULL + ); + } + } else { + if (ComponentName2 == NULL) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gDebugPortDriverBinding, + &gEfiComponentNameProtocolGuid, ComponentName, + NULL + ); + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, &gDebugPortDriverBinding, + &gEfiComponentNameProtocolGuid, ComponentName, + &gEfiComponentName2ProtocolGuid, ComponentName2, + NULL + ); + } + } + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.h b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.h new file mode 100644 index 000000000..12fca8e27 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPort.h @@ -0,0 +1,390 @@ +/** @file + Definitions and prototypes for DebugPort driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __DEBUGPORT_H__ +#define __DEBUGPORT_H__ + + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Driver Binding Externs +// +extern EFI_DRIVER_BINDING_PROTOCOL gDebugPortDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gDebugPortComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gDebugPortComponentName2; + +// +// local type definitions +// +#define DEBUGPORT_DEVICE_SIGNATURE SIGNATURE_32 ('D', 'B', 'G', 'P') + +// +// Device structure used by driver +// +typedef struct { + UINT32 Signature; + EFI_HANDLE DriverBindingHandle; + EFI_HANDLE DebugPortDeviceHandle; + + EFI_DEVICE_PATH_PROTOCOL *DebugPortDevicePath; + EFI_DEBUGPORT_PROTOCOL DebugPortInterface; + + EFI_HANDLE SerialIoDeviceHandle; + EFI_SERIAL_IO_PROTOCOL *SerialIoBinding; + UINT64 BaudRate; + UINT32 ReceiveFifoDepth; + UINT32 Timeout; + EFI_PARITY_TYPE Parity; + UINT8 DataBits; + EFI_STOP_BITS_TYPE StopBits; +} DEBUGPORT_DEVICE; + +#define DEBUGPORT_DEVICE_FROM_THIS(a) CR (a, DEBUGPORT_DEVICE, DebugPortInterface, DEBUGPORT_DEVICE_SIGNATURE) + +#define EFI_ACPI_PC_COMPORT_HID EISA_PNP_ID (0x0500) +#define EFI_ACPI_16550UART_HID EISA_PNP_ID (0x0501) + +#define DEBUGPORT_UART_DEFAULT_BAUDRATE 115200 +#define DEBUGPORT_UART_DEFAULT_PARITY 0 +#define DEBUGPORT_UART_DEFAULT_FIFO_DEPTH 16 +#define DEBUGPORT_UART_DEFAULT_TIMEOUT 50000 ///< 5 ms +#define DEBUGPORT_UART_DEFAULT_DATA_BITS 8 +#define DEBUGPORT_UART_DEFAULT_STOP_BITS 1 + +#define DEBUGPORT_DRIVER_VERSION 1 + +#define IS_UART_DEVICEPATH(dp) (DevicePathType (dp) == MESSAGING_DEVICE_PATH && DevicePathSubType (dp) == MSG_UART_DP) + +/** + Debug Port Driver entry point. + + Reads DebugPort variable to determine what device and settings to use as the + debug port. Binds exclusively to SerialIo. Reverts to defaults if no variable + is found. + + @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 entry point is executed successfully. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory for device. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeDebugPortDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + Checks to see if there's not already a DebugPort interface somewhere. + + If there's a DEBUGPORT variable, the device path must match exactly. If there's + no DEBUGPORT variable, then device path is not checked and does not matter. + Checks to see that there's a serial io interface on the controller handle + that can be bound BY_DRIVER | EXCLUSIVE. + If all these tests succeed, then we return EFI_SUCCESS, else, EFI_UNSUPPORTED + or other error returned by OpenProtocol. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED Debug Port device is not supported. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory for device. + @retval others Some error occurs. + +**/ +EFI_STATUS +EFIAPI +DebugPortSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Binds exclusively to serial io on the controller handle, Produces DebugPort + protocol and DevicePath on new handle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory for device. + @retval others Some error occurs. + +**/ +EFI_STATUS +EFIAPI +DebugPortStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle by removing Serial IO protocol on + the ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +DebugPortStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DebugPortComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DebugPortComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +/** + DebugPort protocol member function. Calls SerialIo:GetControl to flush buffer. + We cannot call SerialIo:SetAttributes because it uses pool services, which use + locks, which affect TPL, so it's not interrupt context safe or re-entrant. + SerialIo:Reset() calls SetAttributes, so it can't be used either. + + The port itself should be fine since it was set up during initialization. + + @param This Protocol instance pointer. + + @return EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +DebugPortReset ( + IN EFI_DEBUGPORT_PROTOCOL *This + ); + +/** + DebugPort protocol member function. Calls SerialIo:Read() after setting + if it's different than the last SerialIo access. + + @param This Pointer to DebugPort protocol. + @param Timeout Timeout value. + @param BufferSize On input, the size of Buffer. + On output, the amount of data actually written. + @param Buffer Pointer to buffer to read. + + @retval EFI_SUCCESS + @retval others + +**/ +EFI_STATUS +EFIAPI +DebugPortRead ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +/** + DebugPort protocol member function. Calls SerialIo:Write() Writes 8 bytes at + a time and does a GetControl between 8 byte writes to help insure reads are + interspersed This is poor-man's flow control. + + @param This Pointer to DebugPort protocol. + @param Timeout Timeout value. + @param BufferSize On input, the size of Buffer. + On output, the amount of data actually written. + @param Buffer Pointer to buffer to read. + + @retval EFI_SUCCESS The data was written. + @retval others Fails when writting datas to debug port device. + +**/ +EFI_STATUS +EFIAPI +DebugPortWrite ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + DebugPort protocol member function. Calls SerialIo:Write() after setting + if it's different than the last SerialIo access. + + @param This Pointer to DebugPort protocol. + + @retval EFI_SUCCESS At least 1 character is ready to be read from + the DebugPort interface. + @retval EFI_NOT_READY There are no characters ready to read from the + DebugPort interface + @retval EFI_DEVICE_ERROR A hardware failure occurred... (from SerialIo) + +**/ +EFI_STATUS +EFIAPI +DebugPortPoll ( + IN EFI_DEBUGPORT_PROTOCOL *This + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.inf b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.inf new file mode 100644 index 000000000..66da0dea9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.inf @@ -0,0 +1,66 @@ +## @file +# This driver produces Debug Port protocol to be used by debug agent to communicate with the remote debug host. +# +# This driver binds exclusively to a standard UART serial port on the controller handle, +# and initializes serial Io interface, publishs Debug Port and Device Path Protocol. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugPortDxe + MODULE_UNI_FILE = DebugPortDxe.uni + FILE_GUID = 73E9457A-CEA1-4917-9A9C-9F1F0F0FD322 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeDebugPortDriver + UNLOAD_IMAGE = ImageUnloadHandler + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gDebugPortDriverBinding +# COMPONENT_NAME = gDebugPortComponentName +# COMPONENT_NAME2 = gDebugPortComponentName2 +# Variable Guid C Name: gEfiDebugPortProtocolGuid Variable Name: L"DEBUGPORT" +# +# + +[Sources] + ComponentName.c + DebugPort.c + DebugPort.h + + +[Packages] + MdePkg/MdePkg.dec + + +[LibraryClasses] + DevicePathLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + UefiDriverEntryPoint + DebugLib + +[Guids] + gEfiDebugPortVariableGuid ## SOMETIMES_CONSUMES ## Variable:L"DEBUGPORT" + gEfiDebugPortDevicePathGuid ## SOMETIMES_CONSUMES ## UNDEFINED # Device path + +[Protocols] + gEfiSerialIoProtocolGuid ## TO_START + gEfiDevicePathProtocolGuid ## BY_START + gEfiDebugPortProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + DebugPortDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.uni b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.uni new file mode 100644 index 000000000..2f631eada --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// This driver produces Debug Port protocol to be used by debug agent to communicate with the remote debug host. +// +// This driver binds exclusively to a standard UART serial port on the controller handle, +// and initializes serial Io interface, publishs Debug Port and Device Path Protocol. +// +// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Produces Debug Port protocol to be used by debug agent to communicate with the remote debug host and initializes serial Io interface, publishes Debug Port and Device Path Protocol." + +#string STR_MODULE_DESCRIPTION #language en-US "This driver binds exclusively to a standard UART serial port on the controller handle, and initializes serial Io interface, publishes Debug Port and Device Path Protocol." + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxeExtra.uni new file mode 100644 index 000000000..3450ab45b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugPortDxe/DebugPortDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// DebugPortDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Debug Port DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugService.h b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugService.h new file mode 100644 index 000000000..e81b9cdb6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugService.h @@ -0,0 +1,50 @@ +/** @file + Header file of Debug services instances. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef __DEBUG_SERVICE_H__ +#define __DEBUG_SERVICE_H__ + +#include + +/** + Print a debug message to debug output device if the specified error level + is enabled. + + @param[in] ErrorLevel The error level of the debug message. + @param[in] Format Format string for the debug message to print. + @param[in] Marker BASE_LIST marker for the variable argument list. + +**/ +VOID +EFIAPI +PeiDebugBPrint( + IN UINTN ErrorLevel, + IN CONST CHAR8 *Format, + IN BASE_LIST Marker + ); + +/** + Prints an assert message containing a filename, line number, and description. + This may be followed by a breakpoint or a dead loop. + + @param[in] FileName The pointer to the name of the source file that + generated the assert condition. + @param[in] LineNumber The line number in the source file that generated + the assert condition + @param[in] Description The pointer to the description of the assert condition. + +**/ +VOID +EFIAPI +PeiDebugAssert( + IN CONST CHAR8 *FileName, + IN UINTN LineNumber, + IN CONST CHAR8 *Description + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.c b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.c new file mode 100644 index 000000000..6d08f5158 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.c @@ -0,0 +1,94 @@ +/** @file + This driver installs gEdkiiDebugPpiGuid PPI to provide + debug services for PEIMs. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include + +#include "DebugService.h" + +EDKII_DEBUG_PPI mDebugPpi = { + PeiDebugBPrint, + PeiDebugAssert +}; + +EFI_PEI_PPI_DESCRIPTOR mDebugServicePpi = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiDebugPpiGuid, + (VOID *)&mDebugPpi +}; + +/** + Print a debug message to debug output device if the specified error level + is enabled. + + @param[in] ErrorLevel The error level of the debug message. + @param[in] Format Format string for the debug message to print. + @param[in] Marker BASE_LIST marker for the variable argument list. + +**/ +VOID +EFIAPI +PeiDebugBPrint( + IN UINTN ErrorLevel, + IN CONST CHAR8 *Format, + IN BASE_LIST Marker + ) +{ + DebugBPrint(ErrorLevel, Format, Marker); +} + +/** + Print an assert message containing a filename, line number, and description. + This may be followed by a breakpoint or a dead loop. + + @param[in] FileName The pointer to the name of the source file that + generated the assert condition. + @param[in] LineNumber The line number in the source file that generated + the assert condition + @param[in] Description The pointer to the description of the assert condition. + +**/ +VOID +EFIAPI +PeiDebugAssert( + IN CONST CHAR8 *FileName, + IN UINTN LineNumber, + IN CONST CHAR8 *Description + ) +{ + DebugAssert(FileName, LineNumber, Description); +} + +/** + Entry point of Debug Service PEIM + + This funciton installs EDKII DEBUG PPI + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCESS The entry point of Debug Service PEIM executes successfully. + @retval Others Some error occurs during the execution of this function. + +**/ +EFI_STATUS +EFIAPI +DebugSerivceInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + return PeiServicesInstallPpi (&mDebugServicePpi); +} + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.inf b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.inf new file mode 100644 index 000000000..eb76af034 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.inf @@ -0,0 +1,46 @@ +## @file +# Debug services for PEI phase +# +# This module installs gEdkiiDebugPpiGuid PPI to provide +# debug services for PEIMs. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugServicePei + MODULE_UNI_FILE = DebugServicePei.uni + FILE_GUID = B73F81B9-1DFC-487C-824C-0509EE2B0128 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = DebugSerivceInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + DebugServicePei.c + DebugService.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PeimEntryPoint + PeiServicesLib + DebugLib + +[Ppis] + gEdkiiDebugPpiGuid ## PRODUCE + +[Depex] + TRUE + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.uni b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.uni new file mode 100644 index 000000000..8fbd4069e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugServicePei/DebugServicePei.uni @@ -0,0 +1,14 @@ +///** @file +// This driver installs gEdkiiDebugPpiGuid PPI to provide +// debug services for PEIMs. +// +// Copyright (c) 2019, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +//**/ + +#string STR_MODULE_ABSTRACT #language en-US "Provide debug services at PEI phase." + +#string STR_MODULE_DESCRIPTION #language en-US "It produces gEdkiiDebugPpiGuid to print message to debug output device" + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c new file mode 100644 index 000000000..1a03ba465 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c @@ -0,0 +1,127 @@ +/** @file + Top level C file for debug support driver. Contains initialization function. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PlDebugSupport.h" + +EFI_DEBUG_SUPPORT_PROTOCOL mDebugSupportProtocolInterface = { + EFI_ISA, + GetMaximumProcessorIndex, + RegisterPeriodicCallback, + RegisterExceptionCallback, + InvalidateInstructionCache +}; + + +/** + Debug Support Driver entry point. + + Checks to see if there's not already a Debug Support protocol installed for + the selected processor before installing it. + + @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 entry point is executed successfully. + @retval EFI_ALREADY_STARTED Debug Support protocol is installed already. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeDebugSupportDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_LOADED_IMAGE_PROTOCOL *LoadedImageProtocolPtr; + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_HANDLE *HandlePtr; + UINTN NumHandles; + EFI_DEBUG_SUPPORT_PROTOCOL *DebugSupportProtocolPtr; + + // + // First check to see that the debug support protocol for this processor + // type is not already installed + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDebugSupportProtocolGuid, + NULL, + &NumHandles, + &HandlePtr + ); + + if (Status != EFI_NOT_FOUND) { + do { + NumHandles--; + Status = gBS->OpenProtocol ( + HandlePtr[NumHandles], + &gEfiDebugSupportProtocolGuid, + (VOID **) &DebugSupportProtocolPtr, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ((Status == EFI_SUCCESS) && (DebugSupportProtocolPtr->Isa == EFI_ISA)) { + // + // a Debug Support protocol has been installed for this processor + // + FreePool (HandlePtr); + Status = EFI_ALREADY_STARTED; + goto ErrExit; + } + } while (NumHandles > 0); + FreePool (HandlePtr); + } + + // + // Get our image information and install platform specific unload handler + // + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &LoadedImageProtocolPtr, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + + LoadedImageProtocolPtr->Unload = PlUnloadDebugSupportDriver; + + // + // Call hook for processor specific initialization + // + Status = PlInitializeDebugSupportDriver (); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + + // + // Install Debug Support protocol to new handle + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEfiDebugSupportProtocolGuid, + EFI_NATIVE_INTERFACE, + &mDebugSupportProtocolInterface + ); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + +ErrExit: + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf new file mode 100644 index 000000000..1bb8635bf --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf @@ -0,0 +1,72 @@ +## @file +# This driver installs Debug Support protocol for the selected processor. +# +# This driver provides the capabilities for debug-agent to gain control of the machine +# when certain types of events occur, i.e. breakpoint, processor execptions, etc. It also +# provides debug-agent to periodically gain control during operation of the machine to +# check for asynchronous commands form the host. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugSupportDxe + MODULE_UNI_FILE = DebugSupportDxe.uni + FILE_GUID = 911D584C-35F7-4955-BEF9-B452769DDC3A + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeDebugSupportDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + DebugSupport.c + +[Sources.Ia32] + Ia32/DebugSupport.h + Ia32/PlDebugSupport.c + Ia32/PlDebugSupport.h + Ia32/PlDebugSupportIa32.c + Ia32/AsmFuncs.nasm + +[Sources.X64] + Ia32/DebugSupport.h + Ia32/PlDebugSupport.c + X64/PlDebugSupport.h + X64/PlDebugSupportX64.c + X64/AsmFuncs.nasm + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiDriverEntryPoint + DebugLib + +[LibraryClasses.IA32, LibraryClasses.X64] + BaseLib + +[Protocols] + gEfiLoadedImageProtocolGuid ## CONSUMES + gEfiDebugSupportProtocolGuid ## PRODUCES + + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + DebugSupportDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni new file mode 100644 index 000000000..1403d41ef --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.uni @@ -0,0 +1,19 @@ +// /** @file +// This driver installs Debug Support protocol for the selected processor. +// +// This driver provides the capabilities for debug-agent to gain control of the machine +// when certain types of events occur, i.e. breakpoint, processor execptions, etc. It also +// provides debug-agent to periodically gain control during operation of the machine to +// check for asynchronous commands form the host. +// +// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Installs Debug Support protocol for the selected processor" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver provides the capabilities for debug-agent to gain control of the machine when certain types of events occur, i.e. breakpoint, processor exceptions, etc. It also provides debug-agent to periodically gain control during operation of the machine to check for asynchronous commands from the host." + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni new file mode 100644 index 000000000..7ceb95d3f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// DebugSupportDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Debug Support DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.nasm b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.nasm new file mode 100644 index 000000000..cfb418748 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.nasm @@ -0,0 +1,493 @@ +;/** @file +; Low leve IA32 specific debug support functions. +; +; Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;**/ + +%define EXCPT32_DIVIDE_ERROR 0 +%define EXCPT32_DEBUG 1 +%define EXCPT32_NMI 2 +%define EXCPT32_BREAKPOINT 3 +%define EXCPT32_OVERFLOW 4 +%define EXCPT32_BOUND 5 +%define EXCPT32_INVALID_OPCODE 6 +%define EXCPT32_DOUBLE_FAULT 8 +%define EXCPT32_INVALID_TSS 10 +%define EXCPT32_SEG_NOT_PRESENT 11 +%define EXCPT32_STACK_FAULT 12 +%define EXCPT32_GP_FAULT 13 +%define EXCPT32_PAGE_FAULT 14 +%define EXCPT32_FP_ERROR 16 +%define EXCPT32_ALIGNMENT_CHECK 17 +%define EXCPT32_MACHINE_CHECK 18 +%define EXCPT32_SIMD 19 + +%define FXSTOR_FLAG 0x1000000 ; bit cpuid 24 of feature flags + +;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87, +;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver +;; MUST check the CPUID feature flags to see that these instructions are available +;; and fail to init if they are not. + +;; fxstor [edi] +%macro FXSTOR_EDI 0 + db 0xf, 0xae, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [edi] +%endmacro + +;; fxrstor [esi] +%macro FXRSTOR_ESI 0 + db 0xf, 0xae, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [esi] +%endmacro +SECTION .data + +global ASM_PFX(OrigVector) +global ASM_PFX(InterruptEntryStub) +global ASM_PFX(StubSize) +global ASM_PFX(CommonIdtEntry) +global ASM_PFX(FxStorSupport) +extern ASM_PFX(InterruptDistrubutionHub) + +ASM_PFX(StubSize): dd InterruptEntryStubEnd - ASM_PFX(InterruptEntryStub) +AppEsp: dd 0x11111111 ; ? +DebugEsp: dd 0x22222222 ; ? +ExtraPush: dd 0x33333333 ; ? +ExceptData: dd 0x44444444 ; ? +Eflags: dd 0x55555555 ; ? +ASM_PFX(OrigVector): dd 0x66666666 ; ? + +;; The declarations below define the memory region that will be used for the debug stack. +;; The context record will be built by pushing register values onto this stack. +;; It is imparitive that alignment be carefully managed, since the FXSTOR and +;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned. +;; +;; The stub will switch stacks from the application stack to the debuger stack +;; and pushes the exception number. +;; +;; Then we building the context record on the stack. Since the stack grows down, +;; we push the fields of the context record from the back to the front. There +;; are 132 bytes of stack used prior allocating the 512 bytes of stack to be +;; used as the memory buffer for the fxstor instruction. Therefore address of +;; the buffer used for the FXSTOR instruction is &Eax - 132 - 512, which +;; must be 16 byte aligned. +;; +;; We carefully locate the stack to make this happen. +;; +;; For reference, the context structure looks like this: +;; struct { +;; UINT32 ExceptionData; +;; FX_SAVE_STATE_IA32 FxSaveState; // 512 bytes, must be 16 byte aligned +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; +;; UINT32 EFlags; +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; UINT32 Eip; +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; +;; } SYSTEM_CONTEXT_IA32; // 32 bit system context record + +align 16 +DebugStackEnd: db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment + times 0x1ffc dd 0x0 ;; 32K should be enough stack + ;; This allocation is coocked to insure + ;; that the the buffer for the FXSTORE instruction + ;; will be 16 byte aligned also. + ;; +ExceptionNumber: dd 0 ;; first entry will be the vector number pushed by the stub + +DebugStackBegin: db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub + +SECTION .text + +;------------------------------------------------------------------------------ +; BOOLEAN +; FxStorSupport ( +; void +; ) +; +; Abstract: Returns TRUE if FxStor instructions are supported +; +global ASM_PFX(FxStorSupport) +ASM_PFX(FxStorSupport): + +; +; cpuid corrupts ebx which must be preserved per the C calling convention +; + push ebx + mov eax, 1 + cpuid + mov eax, edx + and eax, FXSTOR_FLAG + shr eax, 24 + pop ebx + ret + +;------------------------------------------------------------------------------ +; void +; Vect2Desc ( +; DESCRIPTOR * DestDesc, +; void (*Vector) (void) +; ) +; +; Abstract: Encodes an IDT descriptor with the given physical address +; +global ASM_PFX(Vect2Desc) +ASM_PFX(Vect2Desc): + push ebp + mov ebp, esp + mov eax, [ebp + 0xC] + mov ecx, [ebp + 0x8] + mov word [ecx], ax ; write bits 15..0 of offset + mov dx, cs + mov word [ecx+2], dx ; SYS_CODE_SEL from GDT + mov word [ecx+4], 0xe00 | 0x8000 ; type = 386 interrupt gate, present + shr eax, 16 + mov word [ecx+6], ax ; write bits 31..16 of offset + leave + ret + +;------------------------------------------------------------------------------ +; InterruptEntryStub +; +; Abstract: This code is not a function, but is a small piece of code that is +; copied and fixed up once for each IDT entry that is hooked. +; +ASM_PFX(InterruptEntryStub): + mov [AppEsp], esp ; save stack top + mov esp, DebugStackBegin ; switch to debugger stack + push 0 ; push vector number - will be modified before installed + db 0xe9 ; jump rel32 + dd 0 ; fixed up to relative address of CommonIdtEntry +InterruptEntryStubEnd: + +;------------------------------------------------------------------------------ +; CommonIdtEntry +; +; Abstract: This code is not a function, but is the common part for all IDT +; vectors. +; +ASM_PFX(CommonIdtEntry): +;; +;; At this point, the stub has saved the current application stack esp into AppEsp +;; and switched stacks to the debug stack, where it pushed the vector number +;; +;; The application stack looks like this: +;; +;; ... +;; (last application stack entry) +;; eflags from interrupted task +;; CS from interrupted task +;; EIP from interrupted task +;; Error code <-------------------- Only present for some exeption types +;; +;; + +;; The stub switched us to the debug stack and pushed the interrupt number. +;; +;; Next, construct the context record. It will be build on the debug stack by +;; pushing the registers in the correct order so as to create the context structure +;; on the debug stack. The context record must be built from the end back to the +;; beginning because the stack grows down... +; +;; For reference, the context record looks like this: +;; +;; typedef +;; struct { +;; UINT32 ExceptionData; +;; FX_SAVE_STATE_IA32 FxSaveState; +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT32 Cr0, Cr2, Cr3, Cr4; +;; UINT32 EFlags; +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; UINT32 Eip; +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; +;; } SYSTEM_CONTEXT_IA32; // 32 bit system context record + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + pushad + +;; Save interrupt state eflags register... + pushfd + pop eax + mov [Eflags], eax + +;; We need to determine if any extra data was pushed by the exception, and if so, save it +;; To do this, we check the exception number pushed by the stub, and cache the +;; result in a variable since we'll need this again. + cmp dword [ExceptionNumber], EXCPT32_DOUBLE_FAULT + jz ExtraPushOne + cmp dword [ExceptionNumber], EXCPT32_INVALID_TSS + jz ExtraPushOne + cmp dword [ExceptionNumber], EXCPT32_SEG_NOT_PRESENT + jz ExtraPushOne + cmp dword [ExceptionNumber], EXCPT32_STACK_FAULT + jz ExtraPushOne + cmp dword [ExceptionNumber], EXCPT32_GP_FAULT + jz ExtraPushOne + cmp dword [ExceptionNumber], EXCPT32_PAGE_FAULT + jz ExtraPushOne + cmp dword [ExceptionNumber], EXCPT32_ALIGNMENT_CHECK + jz ExtraPushOne + mov dword [ExtraPush], 0 + mov dword [ExceptData], 0 + jmp ExtraPushDone + +ExtraPushOne: + mov dword [ExtraPush], 1 + +;; If there's some extra data, save it also, and modify the saved AppEsp to effectively +;; pop this value off the application's stack. + mov eax, [AppEsp] + mov ebx, [eax] + mov [ExceptData], ebx + add eax, 4 + mov [AppEsp], eax + +ExtraPushDone: + +;; The "pushad" above pushed the debug stack esp. Since what we're actually doing +;; is building the context record on the debug stack, we need to save the pushed +;; debug ESP, and replace it with the application's last stack entry... + mov eax, [esp + 12] + mov [DebugEsp], eax + mov eax, [AppEsp] + add eax, 12 + ; application stack has eflags, cs, & eip, so + ; last actual application stack entry is + ; 12 bytes into the application stack. + mov [esp + 12], eax + +;; continue building context record +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov eax, ss + push eax + + ; CS from application is one entry back in application stack + mov eax, [AppEsp] + movzx eax, word [eax + 4] + push eax + + mov eax, ds + push eax + mov eax, es + push eax + mov eax, fs + push eax + mov eax, gs + push eax + +;; UINT32 Eip; + ; Eip from application is on top of application stack + mov eax, [AppEsp] + push dword [eax] + +;; UINT32 Gdtr[2], Idtr[2]; + push 0 + push 0 + sidt [esp] + push 0 + push 0 + sgdt [esp] + +;; UINT32 Ldtr, Tr; + xor eax, eax + str ax + push eax + sldt ax + push eax + +;; UINT32 EFlags; +;; Eflags from application is two entries back in application stack + mov eax, [AppEsp] + push dword [eax + 8] + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; +;; insure FXSAVE/FXRSTOR is enabled in CR4... +;; ... while we're at it, make sure DE is also enabled... + mov eax, cr4 + or eax, 0x208 + mov cr4, eax + push eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + push 0 + mov eax, cr0 + push eax + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax +;; clear Dr7 while executing debugger itself + xor eax, eax + mov dr7, eax + + mov eax, dr6 + push eax +;; insure all status bits in dr6 are clear... + xor eax, eax + mov dr6, eax + + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + +;; FX_SAVE_STATE_IA32 FxSaveState; + sub esp, 512 + mov edi, esp + ; IMPORTANT!! The debug stack has been carefully constructed to + ; insure that esp and edi are 16 byte aligned when we get here. + ; They MUST be. If they are not, a GP fault will occur. + FXSTOR_EDI + +;; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + mov eax, [ExceptData] + push eax + +; call to C code which will in turn call registered handler +; pass in the vector number + mov eax, esp + push eax + mov eax, [ExceptionNumber] + push eax + call ASM_PFX(InterruptDistrubutionHub) + add esp, 8 + +; restore context... +;; UINT32 ExceptionData; + add esp, 4 + +;; FX_SAVE_STATE_IA32 FxSaveState; + mov esi, esp + FXRSTOR_ESI + add esp, 512 + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop eax + mov dr0, eax + pop eax + mov dr1, eax + pop eax + mov dr2, eax + pop eax + mov dr3, eax +;; skip restore of dr6. We cleared dr6 during the context save. + add esp, 4 + pop eax + mov dr7, eax + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 + pop eax + mov cr2, eax + pop eax + mov cr3, eax + pop eax + mov cr4, eax + +;; UINT32 EFlags; + mov eax, [AppEsp] + pop dword [eax + 8] + +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add esp, 24 + +;; UINT32 Eip; + pop dword [eax] + +;; UINT32 SegGs, SegFs, SegEs, SegDs, SegCs, SegSs; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + + pop gs + pop fs + pop es + pop ds + pop dword [eax + 4] + pop ss + +;; The next stuff to restore is the general purpose registers that were pushed +;; using the "pushad" instruction. +;; +;; The value of ESP as stored in the context record is the application ESP +;; including the 3 entries on the application stack caused by the exception +;; itself. It may have been modified by the debug agent, so we need to +;; determine if we need to relocate the application stack. + + mov ebx, [esp + 12] ; move the potentially modified AppEsp into ebx + mov eax, [AppEsp] + add eax, 12 + cmp ebx, eax + je NoAppStackMove + + mov eax, [AppEsp] + mov ecx, [eax] ; EIP + mov [ebx], ecx + + mov ecx, [eax + 4] ; CS + mov [ebx + 4], ecx + + mov ecx, [eax + 8] ; EFLAGS + mov [ebx + 8], ecx + + mov eax, ebx ; modify the saved AppEsp to the new AppEsp + mov [AppEsp], eax +NoAppStackMove: + mov eax, [DebugEsp] ; restore the DebugEsp on the debug stack + ; so our "popad" will not cause a stack switch + mov [esp + 12], eax + + cmp dword [ExceptionNumber], 0x68 + jne NoChain + +Chain: + +;; Restore eflags so when we chain, the flags will be exactly as if we were never here. +;; We gin up the stack to do an iretd so we can get ALL the flags. + mov eax, [AppEsp] + mov ebx, [eax + 8] + and ebx, ~ 0x300 ; special handling for IF and TF + push ebx + push cs + push PhonyIretd + iretd +PhonyIretd: + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + popad + +;; Switch back to application stack + mov esp, [AppEsp] + +;; Jump to original handler + jmp [ASM_PFX(OrigVector)] + +NoChain: +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + popad + +;; Switch back to application stack + mov esp, [AppEsp] + +;; We're outa here... + iretd + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h new file mode 100644 index 000000000..9f88fab71 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/DebugSupport.h @@ -0,0 +1,292 @@ +/** @file + Generic debug support macros, typedefs and prototypes for IA32/x64. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DEBUG_SUPPORT_H_ +#define _DEBUG_SUPPORT_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define NUM_IDT_ENTRIES 0x78 +#define SYSTEM_TIMER_VECTOR 0x68 + +typedef +VOID +(*DEBUG_PROC) ( + VOID + ); + +typedef +VOID +(EFIAPI *CALLBACK_FUNC) ( + ); + +typedef struct { + IA32_IDT_GATE_DESCRIPTOR OrigDesc; + DEBUG_PROC OrigVector; + IA32_IDT_GATE_DESCRIPTOR NewDesc; + DEBUG_PROC StubEntry; + CALLBACK_FUNC RegisteredCallback; +} IDT_ENTRY; + +extern UINT8 InterruptEntryStub[]; +extern UINT32 StubSize; +extern VOID (*OrigVector) (VOID); +extern IDT_ENTRY *IdtEntryTable; +extern IA32_IDT_GATE_DESCRIPTOR NullDesc; + +/** + Generic IDT entry. + +**/ +VOID +CommonIdtEntry ( + VOID + ); + +/** + Check whether FXSTOR is supported + + @retval TRUE FXSTOR is supported. + @retval FALSE FXSTOR is not supported. + +**/ +BOOLEAN +FxStorSupport ( + VOID + ); + +/** + Encodes an IDT descriptor with the given physical address. + + @param DestDesc The IDT descriptor address. + @param Vecotr The interrupt vector entry. + +**/ +VOID +Vect2Desc ( + IA32_IDT_GATE_DESCRIPTOR * DestDesc, + VOID (*Vector) (VOID) + ); + +/** + Initializes driver's handler registration database. + + This code executes in boot services context + Must be public because it's referenced from DebugSupport.c + + @retval EFI_UNSUPPORTED If IA32 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processor's are not supported. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory. + @retval EFI_SUCCESS Initializes successfully. + +**/ +EFI_STATUS +PlInitializeDebugSupportDriver ( + VOID + ); + +/** + This is the callback that is written to the LoadedImage protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +PlUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ); + +/** + Returns the maximum value that may be used for the ProcessorIndex parameter in + RegisterPeriodicCallback() and RegisterExceptionCallback(). + + Hard coded to support only 1 processor for now. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported + processor index is returned. Always 0 returned. + + @retval EFI_SUCCESS Always returned with **MaxProcessorIndex set to 0. + +**/ +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ); + +/** + Registers a function to be called back periodically in interrupt context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main + periodic entry point of the debug agent. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ); + +/** + Registers a function to be called when a given processor exception occurs. + + This code executes in boot services context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param ExceptionCallback A pointer to a function of type EXCEPTION_CALLBACK that is called + when the processor exception specified by ExceptionType occurs. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Invalidates processor instruction cache for a memory range. Subsequent execution in this range + causes a fresh memory fetch to retrieve code to be executed. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. + @param Start Specifies the physical base of the memory range to be invalidated. + @param Length Specifies the minimum number of bytes in the processor's instruction + cache to invalidate. + + @retval EFI_SUCCESS Always returned. + +**/ +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ); + +/** + Allocate pool for a new IDT entry stub. + + Copy the generic stub into the new buffer and fixup the vector number + and jump target address. + + @param ExceptionType This is the exception type that the new stub will be created + for. + @param Stub On successful exit, *Stub contains the newly allocated entry stub. + +**/ +VOID +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ); + +/** + Get Interrupt Handle from IDT Gate Descriptor. + + @param IdtGateDecriptor IDT Gate Descriptor. + + @return Interrupt Handle stored in IDT Gate Descriptor. + +**/ +UINTN +GetInterruptHandleFromIdt ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtGateDecriptor + ); + +/** + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + + @param NewCallback If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + @param ExceptionType Indicates which entry to manage. + + @retval EFI_SUCCESS Process is ok. + @retval EFI_INVALID_PARAMETER Requested uninstalling a handler from a vector that has + no handler registered for it + @retval EFI_ALREADY_STARTED Requested install to a vector that already has a handler registered. + @retval others Possible return values are passed through from UnHookEntry and HookEntry. + +**/ +EFI_STATUS +ManageIdtEntryTable ( + CALLBACK_FUNC NewCallback, + EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Creates a nes entry stub. Then saves the current IDT entry and replaces it + with an interrupt gate for the new entry point. The IdtEntryTable is updated + with the new registered function. + + This code executes in boot services context. The stub entry executes in interrupt + context. + + @param ExceptionType Specifies which vector to hook. + @param NewCallback A pointer to the new function to be registered. + +**/ +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN CALLBACK_FUNC NewCallback + ); + +/** + Undoes HookEntry. This code executes in boot services context. + + @param ExceptionType Specifies which entry to unhook + +**/ +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c new file mode 100644 index 000000000..afea3a218 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.c @@ -0,0 +1,367 @@ +/** @file + IA32/x64 generic functions to support Debug Support protocol. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DebugSupport.h" + +// +// This the global main table to keep track of the interrupts +// +IDT_ENTRY *IdtEntryTable = NULL; + +/** + Read IDT Gate Descriptor from IDT Table. + + @param Vector Specifies vector number. + @param IdtGateDescriptor Pointer to IDT Gate Descriptor read from IDT Table. + +**/ +VOID +ReadIdtGateDescriptor ( + IN EFI_EXCEPTION_TYPE Vector, + OUT IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor + ) +{ + IA32_DESCRIPTOR IdtrValue; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + + AsmReadIdtr (&IdtrValue); + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtrValue.Base; + + CopyMem ((VOID *) IdtGateDescriptor, (VOID *) &(IdtTable)[Vector], sizeof (IA32_IDT_GATE_DESCRIPTOR)); +} + +/** + Write IDT Gate Descriptor into IDT Table. + + @param Vector Specifies vector number. + @param IdtGateDescriptor Pointer to IDT Gate Descriptor written into IDT Table. + +**/ +VOID +WriteIdtGateDescriptor ( + EFI_EXCEPTION_TYPE Vector, + IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor + ) +{ + IA32_DESCRIPTOR IdtrValue; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + + AsmReadIdtr (&IdtrValue); + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtrValue.Base; + + CopyMem ((VOID *) &(IdtTable)[Vector], (VOID *) IdtGateDescriptor, sizeof (IA32_IDT_GATE_DESCRIPTOR)); +} + +/** + Creates a nes entry stub. Then saves the current IDT entry and replaces it + with an interrupt gate for the new entry point. The IdtEntryTable is updated + with the new registered function. + + This code executes in boot services context. The stub entry executes in interrupt + context. + + @param ExceptionType Specifies which vector to hook. + @param NewCallback A pointer to the new function to be registered. + +**/ +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN CALLBACK_FUNC NewCallback + ) +{ + BOOLEAN OldIntFlagState; + + CreateEntryStub (ExceptionType, (VOID **) &IdtEntryTable[ExceptionType].StubEntry); + + // + // Disables CPU interrupts and returns the previous interrupt state + // + OldIntFlagState = SaveAndDisableInterrupts (); + + // + // gets IDT Gate descriptor by index + // + ReadIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + // + // stores orignal interrupt handle + // + IdtEntryTable[ExceptionType].OrigVector = (DEBUG_PROC) GetInterruptHandleFromIdt (&(IdtEntryTable[ExceptionType].OrigDesc)); + + // + // encodes new IDT Gate descriptor by stub entry + // + Vect2Desc (&IdtEntryTable[ExceptionType].NewDesc, IdtEntryTable[ExceptionType].StubEntry); + // + // stores NewCallback + // + IdtEntryTable[ExceptionType].RegisteredCallback = NewCallback; + + // + // writes back new IDT Gate descriptor + // + WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].NewDesc)); + + // + // restore interrupt state + // + SetInterruptState (OldIntFlagState); + + return ; +} + +/** + Undoes HookEntry. This code executes in boot services context. + + @param ExceptionType Specifies which entry to unhook + +**/ +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + BOOLEAN OldIntFlagState; + + // + // Disables CPU interrupts and returns the previous interrupt state + // + OldIntFlagState = SaveAndDisableInterrupts (); + + // + // restore the default IDT Date Descriptor + // + WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + + // + // restore interrupt state + // + SetInterruptState (OldIntFlagState); + + return ; +} + +/** + Returns the maximum value that may be used for the ProcessorIndex parameter in + RegisterPeriodicCallback() and RegisterExceptionCallback(). + + Hard coded to support only 1 processor for now. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported + processor index is returned. Always 0 returned. + + @retval EFI_SUCCESS Always returned with **MaxProcessorIndex set to 0. + +**/ +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +{ + *MaxProcessorIndex = 0; + return EFI_SUCCESS; +} + +/** + Registers a function to be called back periodically in interrupt context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main + periodic entry point of the debug agent. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +{ + return ManageIdtEntryTable (PeriodicCallback, SYSTEM_TIMER_VECTOR); +} + +/** + Registers a function to be called when a given processor exception occurs. + + This code executes in boot services context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param ExceptionCallback A pointer to a function of type EXCEPTION_CALLBACK that is called + when the processor exception specified by ExceptionType occurs. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. +**/ +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + return ManageIdtEntryTable (ExceptionCallback, ExceptionType); +} + + +/** + Invalidates processor instruction cache for a memory range. Subsequent execution in this range + causes a fresh memory fetch to retrieve code to be executed. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. + @param Start Specifies the physical base of the memory range to be invalidated. + @param Length Specifies the minimum number of bytes in the processor's instruction + cache to invalidate. + + @retval EFI_SUCCESS Always returned. + +**/ +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ) +{ + AsmWbinvd (); + return EFI_SUCCESS; +} + +/** + Common piece of code that invokes the registered handlers. + + This code executes in exception context so no efi calls are allowed. + This code is called from assembly file. + + @param ExceptionType Exception type + @param ContextRecord System context + +**/ +VOID +InterruptDistrubutionHub ( + EFI_EXCEPTION_TYPE ExceptionType, + EFI_SYSTEM_CONTEXT_IA32 *ContextRecord + ) +{ + if (IdtEntryTable[ExceptionType].RegisteredCallback != NULL) { + if (ExceptionType != SYSTEM_TIMER_VECTOR) { + IdtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, ContextRecord); + } else { + OrigVector = IdtEntryTable[ExceptionType].OrigVector; + IdtEntryTable[ExceptionType].RegisteredCallback (ContextRecord); + } + } +} + +/** + This is the callback that is written to the Loaded Image protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS Always. + +**/ +EFI_STATUS +EFIAPI +PlUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_EXCEPTION_TYPE ExceptionType; + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + ManageIdtEntryTable (NULL, ExceptionType); + // + // Free space for each Interrupt Stub precedure. + // + if (IdtEntryTable[ExceptionType].StubEntry != NULL) { + FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); + } + } + + FreePool (IdtEntryTable); + + return EFI_SUCCESS; +} + +/** + Initializes driver's handler registration database. + + This code executes in boot services context. + Must be public because it's referenced from DebugSupport.c + + @retval EFI_UNSUPPORTED If IA32/x64 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processors are not supported. + @retval EFI_OUT_OF_RESOURCES Fails to allocate memory. + @retval EFI_SUCCESS Initializes successfully. + +**/ +EFI_STATUS +PlInitializeDebugSupportDriver ( + VOID + ) +{ + EFI_EXCEPTION_TYPE ExceptionType; + + // + // Check whether FxStor instructions are supported. + // + if (!FxStorSupport ()) { + return EFI_UNSUPPORTED; + } + + IdtEntryTable = AllocateZeroPool (sizeof (IDT_ENTRY) * NUM_IDT_ENTRIES); + if (IdtEntryTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType ++) { + IdtEntryTable[ExceptionType].StubEntry = (DEBUG_PROC) (UINTN) AllocatePool (StubSize); + if (IdtEntryTable[ExceptionType].StubEntry == NULL) { + goto ErrorCleanup; + } + + // + // Copy Interrupt stub code. + // + CopyMem ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry, InterruptEntryStub, StubSize); + } + return EFI_SUCCESS; + +ErrorCleanup: + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + if (IdtEntryTable[ExceptionType].StubEntry != NULL) { + FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); + } + } + FreePool (IdtEntryTable); + + return EFI_OUT_OF_RESOURCES; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h new file mode 100644 index 000000000..ed7d98ce2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupport.h @@ -0,0 +1,16 @@ +/** @file + IA32 specific debug support macros, typedefs and prototypes. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PLDEBUG_SUPPORT_H_ +#define _PLDEBUG_SUPPORT_H_ + +#include "Ia32/DebugSupport.h" + +#define EFI_ISA IsaIa32 + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c new file mode 100644 index 000000000..37c330626 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/Ia32/PlDebugSupportIa32.c @@ -0,0 +1,139 @@ +/** @file + IA32 specific functions to support Debug Support protocol. + +Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PlDebugSupport.h" + +IA32_IDT_GATE_DESCRIPTOR NullDesc = {{0}}; + +/** + Get Interrupt Handle from IDT Gate Descriptor. + + @param IdtGateDescriptor IDT Gate Descriptor. + + @return Interrupt Handle stored in IDT Gate Descriptor. + +**/ +UINTN +GetInterruptHandleFromIdt ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor + ) +{ + UINTN InterruptHandle; + + // + // InterruptHandle 0-15 : OffsetLow + // InterruptHandle 16-31 : OffsetHigh + // + ((UINT16 *) &InterruptHandle)[0] = (UINT16) IdtGateDescriptor->Bits.OffsetLow; + ((UINT16 *) &InterruptHandle)[1] = (UINT16) IdtGateDescriptor->Bits.OffsetHigh; + + return InterruptHandle; +} + +/** + Allocate pool for a new IDT entry stub. + + Copy the generic stub into the new buffer and fixup the vector number + and jump target address. + + @param ExceptionType This is the exception type that the new stub will be created + for. + @param Stub On successful exit, *Stub contains the newly allocated entry stub. + +**/ +VOID +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ) +{ + UINT8 *StubCopy; + + StubCopy = *Stub; + + // + // Fixup the stub code for this vector + // + + // The stub code looks like this: + // + // 00000000 89 25 00000004 R mov AppEsp, esp ; save stack top + // 00000006 BC 00008014 R mov esp, offset DbgStkBot ; switch to debugger stack + // 0000000B 6A 00 push 0 ; push vector number - will be modified before installed + // 0000000D E9 db 0e9h ; jump rel32 + // 0000000E 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry + // + + // + // poke in the exception type so the second push pushes the exception type + // + StubCopy[0x0c] = (UINT8) ExceptionType; + + // + // fixup the jump target to point to the common entry + // + *(UINT32 *) &StubCopy[0x0e] = (UINT32) CommonIdtEntry - (UINT32) &StubCopy[StubSize]; + + return ; +} + +/** + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + + @param NewCallback If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + @param ExceptionType Indicates which entry to manage. + + @retval EFI_SUCCESS Installing or Uninstalling operation is ok. + @retval EFI_INVALID_PARAMETER Requested uninstalling a handler from a vector that has + no handler registered for it + @retval EFI_ALREADY_STARTED Requested install to a vector that already has a handler registered. + +**/ +EFI_STATUS +ManageIdtEntryTable ( + CALLBACK_FUNC NewCallback, + EFI_EXCEPTION_TYPE ExceptionType + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (CompareMem (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc, sizeof (IA32_IDT_GATE_DESCRIPTOR)) != 0) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + Status = EFI_ALREADY_STARTED; + } else { + UnhookEntry (ExceptionType); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback == NULL) { + // + // if the input handler is null, error + // + Status = EFI_INVALID_PARAMETER; + } else { + HookEntry (ExceptionType, NewCallback); + } + } + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.nasm b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.nasm new file mode 100644 index 000000000..9cc38a312 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/AsmFuncs.nasm @@ -0,0 +1,581 @@ +;/** @file +; Low level x64 routines used by the debug support driver. +; +; Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;**/ + +%define EXCPT64_DIVIDE_ERROR 0 +%define EXCPT64_DEBUG 1 +%define EXCPT64_NMI 2 +%define EXCPT64_BREAKPOINT 3 +%define EXCPT64_OVERFLOW 4 +%define EXCPT64_BOUND 5 +%define EXCPT64_INVALID_OPCODE 6 +%define EXCPT64_DOUBLE_FAULT 8 +%define EXCPT64_INVALID_TSS 10 +%define EXCPT64_SEG_NOT_PRESENT 11 +%define EXCPT64_STACK_FAULT 12 +%define EXCPT64_GP_FAULT 13 +%define EXCPT64_PAGE_FAULT 14 +%define EXCPT64_FP_ERROR 16 +%define EXCPT64_ALIGNMENT_CHECK 17 +%define EXCPT64_MACHINE_CHECK 18 +%define EXCPT64_SIMD 19 + +%define FXSTOR_FLAG 0x1000000 ; bit cpuid 24 of feature flags + +;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87, +;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver +;; MUST check the CPUID feature flags to see that these instructions are available +;; and fail to init if they are not. + +;; fxstor [rdi] +%macro FXSTOR_RDI 0 + db 0xf, 0xae, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [rdi] +%endmacro + +;; fxrstor [rsi] +%macro FXRSTOR_RSI 0 + db 0xf, 0xae, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [rsi] +%endmacro + +SECTION .data + +global ASM_PFX(OrigVector) +global ASM_PFX(InterruptEntryStub) +global ASM_PFX(StubSize) +global ASM_PFX(CommonIdtEntry) +global ASM_PFX(FxStorSupport) +extern ASM_PFX(InterruptDistrubutionHub) + +ASM_PFX(StubSize): dd InterruptEntryStubEnd - ASM_PFX(InterruptEntryStub) +AppRsp: dq 0x1111111111111111 ; ? +DebugRsp: dq 0x2222222222222222 ; ? +ExtraPush: dq 0x3333333333333333 ; ? +ExceptData: dq 0x4444444444444444 ; ? +Rflags: dq 0x5555555555555555 ; ? +ASM_PFX(OrigVector): dq 0x6666666666666666 ; ? + +;; The declarations below define the memory region that will be used for the debug stack. +;; The context record will be built by pushing register values onto this stack. +;; It is imparitive that alignment be carefully managed, since the FXSTOR and +;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned. +;; +;; The stub will switch stacks from the application stack to the debuger stack +;; and pushes the exception number. +;; +;; Then we building the context record on the stack. Since the stack grows down, +;; we push the fields of the context record from the back to the front. There +;; are 336 bytes of stack used prior allocating the 512 bytes of stack to be +;; used as the memory buffer for the fxstor instruction. Therefore address of +;; the buffer used for the FXSTOR instruction is &Eax - 336 - 512, which +;; must be 16 byte aligned. +;; +;; We carefully locate the stack to make this happen. +;; +;; For reference, the context structure looks like this: +;; struct { +;; UINT64 ExceptionData; +;; FX_SAVE_STATE_X64 FxSaveState; // 512 bytes, must be 16 byte aligned +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +;; UINT64 RFlags; +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; UINT64 Rip; +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +;; } SYSTEM_CONTEXT_X64; // 64 bit system context record + +align 16 +DebugStackEnd: db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment + times 0x1ffc dd 0x0 ;; 32K should be enough stack + ;; This allocation is coocked to insure + ;; that the the buffer for the FXSTORE instruction + ;; will be 16 byte aligned also. + ;; +ExceptionNumber: dq 0 ;; first entry will be the vector number pushed by the stub + +DebugStackBegin: db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub + +DEFAULT REL +SECTION .text + +;------------------------------------------------------------------------------ +; BOOLEAN +; FxStorSupport ( +; void +; ) +; +; Abstract: Returns TRUE if FxStor instructions are supported +; +global ASM_PFX(FxStorSupport) +ASM_PFX(FxStorSupport): + +; +; cpuid corrupts rbx which must be preserved per the C calling convention +; + push rbx + mov rax, dword 1 + cpuid + mov eax, edx + and rax, FXSTOR_FLAG + shr rax, 24 + pop rbx + ret + +;------------------------------------------------------------------------------ +; void +; Vect2Desc ( +; IA32_IDT_GATE_DESCRIPTOR * DestDesc, // rcx +; void (*Vector) (void) // rdx +; ) +; +; Abstract: Encodes an IDT descriptor with the given physical address +; +global ASM_PFX(Vect2Desc) +ASM_PFX(Vect2Desc): + + mov rax, rdx + mov word [rcx], ax ; write bits 15..0 of offset + mov dx, cs + mov word [rcx+2], dx ; SYS_CODE_SEL from GDT + mov word [rcx+4], 0xe00 | 0x8000 ; type = 386 interrupt gate, present + shr rax, 16 + mov word [rcx+6], ax ; write bits 31..16 of offset + shr rax, 16 + mov dword [rcx+8], eax ; write bits 63..32 of offset + + ret + +;------------------------------------------------------------------------------ +; InterruptEntryStub +; +; Abstract: This code is not a function, but is a small piece of code that is +; copied and fixed up once for each IDT entry that is hooked. +; +ASM_PFX(InterruptEntryStub): + push 0 ; push vector number - will be modified before installed + db 0xe9 ; jump rel32 + dd 0 ; fixed up to relative address of CommonIdtEntry +InterruptEntryStubEnd: + +;------------------------------------------------------------------------------ +; CommonIdtEntry +; +; Abstract: This code is not a function, but is the common part for all IDT +; vectors. +; +ASM_PFX(CommonIdtEntry): +;; +;; At this point, the stub has saved the current application stack esp into AppRsp +;; and switched stacks to the debug stack, where it pushed the vector number +;; +;; The application stack looks like this: +;; +;; ... +;; (last application stack entry) +;; [16 bytes alignment, do not care it] +;; SS from interrupted task +;; RSP from interrupted task +;; rflags from interrupted task +;; CS from interrupted task +;; RIP from interrupted task +;; Error code <-------------------- Only present for some exeption types +;; +;; Vector Number <----------------- pushed in our IDT Entry +;; + +;; The stub switched us to the debug stack and pushed the interrupt number. +;; +;; Next, construct the context record. It will be build on the debug stack by +;; pushing the registers in the correct order so as to create the context structure +;; on the debug stack. The context record must be built from the end back to the +;; beginning because the stack grows down... +; +;; For reference, the context record looks like this: +;; +;; typedef +;; struct { +;; UINT64 ExceptionData; +;; FX_SAVE_STATE_X64 FxSaveState; +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT64 Cr0, Cr2, Cr3, Cr4, Cr8; +;; UINT64 RFlags; +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; UINT64 Rip; +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +;; } SYSTEM_CONTEXT_X64; // 64 bit system context record + +;; NOTE: we save rsp here to prevent compiler put rip reference cause error AppRsp + push rax + mov rax, qword [rsp+8] ; save vector number + mov [ExceptionNumber], rax ; save vector number + pop rax + add rsp, 8 ; pop vector number + mov [AppRsp], rsp ; save stack top + lea rsp, [DebugStackBegin] ; switch to debugger stack + sub rsp, 8 ; leave space for vector number + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi + +;; Save interrupt state rflags register... + pushfq + pop rax + mov [Rflags], rax + +;; We need to determine if any extra data was pushed by the exception, and if so, save it +;; To do this, we check the exception number pushed by the stub, and cache the +;; result in a variable since we'll need this again. + cmp qword [ExceptionNumber], EXCPT64_DOUBLE_FAULT + jz ExtraPushOne + cmp qword [ExceptionNumber], EXCPT64_INVALID_TSS + jz ExtraPushOne + cmp qword [ExceptionNumber], EXCPT64_SEG_NOT_PRESENT + jz ExtraPushOne + cmp qword [ExceptionNumber], EXCPT64_STACK_FAULT + jz ExtraPushOne + cmp qword [ExceptionNumber], EXCPT64_GP_FAULT + jz ExtraPushOne + cmp qword [ExceptionNumber], EXCPT64_PAGE_FAULT + jz ExtraPushOne + cmp qword [ExceptionNumber], EXCPT64_ALIGNMENT_CHECK + jz ExtraPushOne + mov qword [ExtraPush], 0 + mov qword [ExceptData], 0 + jmp ExtraPushDone +ExtraPushOne: + mov qword [ExtraPush], 1 + +;; If there's some extra data, save it also, and modify the saved AppRsp to effectively +;; pop this value off the application's stack. + mov rax, [AppRsp] + mov rbx, [rax] + mov qword [ExceptData], rbx + add rax, 8 + mov [AppRsp], rax + +ExtraPushDone: + +;; The "push" above pushed the debug stack rsp. Since what we're actually doing +;; is building the context record on the debug stack, we need to save the pushed +;; debug RSP, and replace it with the application's last stack entry... + mov rax, [rsp + 24] + mov [DebugRsp], rax + mov rax, [AppRsp] + mov rax, QWORD [rax + 24] + ; application stack has ss, rsp, rflags, cs, & rip, so + ; last actual application stack entry is saved at offset + ; 24 bytes from stack top. + mov [rsp + 24], rax + +;; continue building context record +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov rax, ss + push rax + + ; CS from application is one entry back in application stack + mov rax, [AppRsp] + movzx rax, word [rax + 8] + push rax + + mov rax, ds + push rax + mov rax, es + push rax + mov rax, fs + push rax + mov rax, gs + push rax + +;; UINT64 Rip; + ; Rip from application is on top of application stack + mov rax, [AppRsp] + push qword [rax] + +;; UINT64 Gdtr[2], Idtr[2]; + push 0 + push 0 + sidt [rsp] + push 0 + push 0 + sgdt [rsp] + +;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + +;; UINT64 RFlags; +;; Rflags from application is two entries back in application stack + mov rax, [AppRsp] + push qword [rax + 16] + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +;; insure FXSAVE/FXRSTOR is enabled in CR4... +;; ... while we're at it, make sure DE is also enabled... + mov rax, cr8 + push rax + mov rax, cr4 + or rax, 0x208 + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + push 0 + mov rax, cr0 + push rax + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov rax, dr7 + push rax +;; clear Dr7 while executing debugger itself + xor rax, rax + mov dr7, rax + + mov rax, dr6 + push rax +;; insure all status bits in dr6 are clear... + xor rax, rax + mov dr6, rax + + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + +;; FX_SAVE_STATE_X64 FxSaveState; + sub rsp, 512 + mov rdi, rsp + ; IMPORTANT!! The debug stack has been carefully constructed to + ; insure that rsp and rdi are 16 byte aligned when we get here. + ; They MUST be. If they are not, a GP fault will occur. + FXSTOR_RDI + +;; UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear + cld + +;; UINT64 ExceptionData; + mov rax, [ExceptData] + push rax + +; call to C code which will in turn call registered handler +; pass in the vector number + mov rdx, rsp + mov rcx, [ExceptionNumber] + sub rsp, 40 + call ASM_PFX(InterruptDistrubutionHub) + add rsp, 40 + +; restore context... +;; UINT64 ExceptionData; + add rsp, 8 + +;; FX_SAVE_STATE_X64 FxSaveState; + mov rsi, rsp + FXRSTOR_RSI + add rsp, 512 + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop rax + mov dr0, rax + pop rax + mov dr1, rax + pop rax + mov dr2, rax + pop rax + mov dr3, rax +;; skip restore of dr6. We cleared dr6 during the context save. + add rsp, 8 + pop rax + mov dr7, rax + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + pop rax + mov cr0, rax + add rsp, 8 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + pop rax + mov cr8, rax + +;; UINT64 RFlags; + mov rax, [AppRsp] + pop qword [rax + 16] + +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add rsp, 48 + +;; UINT64 Rip; + pop qword [rax] + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + + pop rax + ; mov gs, rax + pop rax + ; mov fs, rax + pop rax + mov es, rax + pop rax + mov ds, rax + mov rax, [AppRsp] + pop qword [rax + 8] + pop rax + mov ss, rax + +;; The next stuff to restore is the general purpose registers that were pushed +;; using the "push" instruction. +;; +;; The value of RSP as stored in the context record is the application RSP +;; including the 5 entries on the application stack caused by the exception +;; itself. It may have been modified by the debug agent, so we need to +;; determine if we need to relocate the application stack. + + mov rbx, [rsp + 24] ; move the potentially modified AppRsp into rbx + mov rax, [AppRsp] + mov rax, QWORD [rax + 24] + cmp rbx, rax + je NoAppStackMove + + mov rax, [AppRsp] + mov rcx, [rax] ; RIP + mov [rbx], rcx + + mov rcx, [rax + 8] ; CS + mov [rbx + 8], rcx + + mov rcx, [rax + 16] ; RFLAGS + mov [rbx + 16], rcx + + mov rcx, [rax + 24] ; RSP + mov [rbx + 24], rcx + + mov rcx, [rax + 32] ; SS + mov [rbx + 32], rcx + + mov rax, rbx ; modify the saved AppRsp to the new AppRsp + mov [AppRsp], rax +NoAppStackMove: + mov rax, [DebugRsp] ; restore the DebugRsp on the debug stack + ; so our "pop" will not cause a stack switch + mov [rsp + 24], rax + + cmp qword [ExceptionNumber], 0x68 + jne NoChain + +Chain: + +;; Restore rflags so when we chain, the flags will be exactly as if we were never here. +;; We gin up the stack to do an iretq so we can get ALL the flags. + mov rax, [AppRsp] + mov rbx, [rax + 40] + push rbx + mov rax, ss + push rax + mov rax, rsp + add rax, 16 + push rax + mov rax, [AppRsp] + mov rbx, [rax + 16] + and rbx, ~ 0x300 ; special handling for IF and TF + push rbx + mov rax, cs + push rax + lea rax, [PhonyIretq] + push rax + iretq +PhonyIretq: + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, [AppRsp] + +;; Jump to original handler + jmp [ASM_PFX(OrigVector)] + +NoChain: +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, [AppRsp] + +;; We're outa here... + iretq + diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h new file mode 100644 index 000000000..b3743fcdb --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupport.h @@ -0,0 +1,16 @@ +/** @file + X64 specific debug support macros. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PLDEBUG_SUPPORT_H_ +#define _PLDEBUG_SUPPORT_H_ + +#include "Ia32/DebugSupport.h" + +#define EFI_ISA IsaX64 + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c new file mode 100644 index 000000000..1e40873bc --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DebugSupportDxe/X64/PlDebugSupportX64.c @@ -0,0 +1,140 @@ +/** @file + X64 specific functions to support Debug Support protocol. + +Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PlDebugSupport.h" + +IA32_IDT_GATE_DESCRIPTOR NullDesc = {{0,0}}; + +/** + Get Interrupt Handle from IDT Gate Descriptor. + + @param IdtGateDecriptor IDT Gate Descriptor. + + @return Interrupt Handle stored in IDT Gate Descriptor. + +**/ +UINTN +GetInterruptHandleFromIdt ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtGateDecriptor + ) +{ + UINTN InterruptHandle; + + // + // InterruptHandle 0-15 : OffsetLow + // InterruptHandle 16-31 : OffsetHigh + // InterruptHandle 32-63 : OffsetUpper + // + InterruptHandle = ((UINTN) IdtGateDecriptor->Bits.OffsetLow) | + (((UINTN) IdtGateDecriptor->Bits.OffsetHigh) << 16) | + (((UINTN) IdtGateDecriptor->Bits.OffsetUpper) << 32) ; + + return InterruptHandle; +} + +/** + Allocate pool for a new IDT entry stub. + + Copy the generic stub into the new buffer and fixup the vector number + and jump target address. + + @param ExceptionType This is the exception type that the new stub will be created + for. + @param Stub On successful exit, *Stub contains the newly allocated entry stub. + +**/ +VOID +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ) +{ + UINT8 *StubCopy; + + StubCopy = *Stub; + + // + // Fixup the stub code for this vector + // + + // The stub code looks like this: + // + // 00000000 6A 00 push 0 ; push vector number - will be modified before installed + // 00000002 E9 db 0e9h ; jump rel32 + // 00000003 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry + // + + // + // poke in the exception type so the second push pushes the exception type + // + StubCopy[0x1] = (UINT8) ExceptionType; + + // + // fixup the jump target to point to the common entry + // + *(UINT32 *) &StubCopy[0x3] = (UINT32)((UINTN) CommonIdtEntry - (UINTN) &StubCopy[StubSize]); + + return; +} + +/** + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + + @param NewCallback If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + @param ExceptionType Indicates which entry to manage. + + @retval EFI_SUCCESS Process is ok. + @retval EFI_INVALID_PARAMETER Requested uninstalling a handler from a vector that has + no handler registered for it + @retval EFI_ALREADY_STARTED Requested install to a vector that already has a handler registered. + @retval others Possible return values are passed through from UnHookEntry and HookEntry. + +**/ +EFI_STATUS +ManageIdtEntryTable ( + CALLBACK_FUNC NewCallback, + EFI_EXCEPTION_TYPE ExceptionType + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (CompareMem (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc, sizeof (IA32_IDT_GATE_DESCRIPTOR)) != 0) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + Status = EFI_ALREADY_STARTED; + } else { + UnhookEntry (ExceptionType); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback == NULL) { + // + // if the input handler is null, error + // + Status = EFI_INVALID_PARAMETER; + } else { + HookEntry (ExceptionType, NewCallback); + } + } + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePath.c b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePath.c new file mode 100644 index 000000000..cba214987 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePath.c @@ -0,0 +1,99 @@ +/** @file + Device Path Driver to produce DevPathUtilities Protocol, DevPathFromText Protocol + and DevPathToText Protocol. + +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +GLOBAL_REMOVE_IF_UNREFERENCED CONST EFI_DEVICE_PATH_UTILITIES_PROTOCOL mDevicePathUtilities = { + GetDevicePathSize, + DuplicateDevicePath, + AppendDevicePath, + AppendDevicePathNode, + AppendDevicePathInstance, + GetNextDevicePathInstance, + IsDevicePathMultiInstance, + CreateDeviceNode +}; + +GLOBAL_REMOVE_IF_UNREFERENCED CONST EFI_DEVICE_PATH_TO_TEXT_PROTOCOL mDevicePathToText = { + ConvertDeviceNodeToText, + ConvertDevicePathToText +}; + +GLOBAL_REMOVE_IF_UNREFERENCED CONST EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL mDevicePathFromText = { + ConvertTextToDeviceNode, + ConvertTextToDevicePath +}; + +/** + The user Entry Point for DevicePath module. + + This is the entry point for DevicePath module. It installs the UEFI Device Path Utility Protocol and + optionally the Device Path to Text and Device Path from Text protocols based on feature flags. + + @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 entry point is executed successfully. + @retval Others Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +DevicePathEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + Handle = NULL; + Status = EFI_UNSUPPORTED; + if (FeaturePcdGet (PcdDevicePathSupportDevicePathToText)) { + if (FeaturePcdGet (PcdDevicePathSupportDevicePathFromText)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiDevicePathUtilitiesProtocolGuid, &mDevicePathUtilities, + &gEfiDevicePathToTextProtocolGuid, &mDevicePathToText, + &gEfiDevicePathFromTextProtocolGuid, &mDevicePathFromText, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiDevicePathUtilitiesProtocolGuid, &mDevicePathUtilities, + &gEfiDevicePathToTextProtocolGuid, &mDevicePathToText, + NULL + ); + } + } else { + if (FeaturePcdGet (PcdDevicePathSupportDevicePathFromText)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiDevicePathUtilitiesProtocolGuid, &mDevicePathUtilities, + &gEfiDevicePathFromTextProtocolGuid, &mDevicePathFromText, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiDevicePathUtilitiesProtocolGuid, &mDevicePathUtilities, + NULL + ); + } + } + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf new file mode 100644 index 000000000..0b8ea7267 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf @@ -0,0 +1,54 @@ +## @file +# Device path driver that produces three UEFI device path protocols. +# +# This driver produces Device Path Utilities protocol and optionally +# DevicePahtToText and DevicePathFromText protocols based on feature flags +# PcdDevicePathSupportDevicePathToText & PcdDevicePathSupportDevicePathFromText +# respectively. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DevicePathDxe + MODULE_UNI_FILE = DevicePathDxe.uni + FILE_GUID = 9B680FCE-AD6B-4F3A-B60B-F59899003443 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DevicePathEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + DevicePath.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEfiDevicePathToTextProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdDevicePathSupportDevicePathFromText ## SOMETIMES_PRODUCES + gEfiDevicePathFromTextProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdDevicePathSupportDevicePathToText ## SOMETIMES_PRODUCES + gEfiDevicePathUtilitiesProtocolGuid ## PRODUCES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdDevicePathSupportDevicePathFromText ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDevicePathSupportDevicePathToText ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + DevicePathDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.uni b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.uni new file mode 100644 index 000000000..05337a7b2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.uni @@ -0,0 +1,19 @@ +// /** @file +// Device path driver that produces three UEFI device path protocols. +// +// This driver produces Device Path Utilities protocol and optionally +// DevicePahtToText and DevicePathFromText protocols based on feature flags +// PcdDevicePathSupportDevicePathToText & PcdDevicePathSupportDevicePathFromText +// respectively. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Produces three UEFI device path protocols" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver produces Device Path Utilities protocol and optionally DevicePahtToText and DevicePathFromText protocols based on feature flags PcdDevicePathSupportDevicePathToText & PcdDevicePathSupportDevicePathFromText respectively." + diff --git a/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxeExtra.uni new file mode 100644 index 000000000..d4beebcd6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DevicePathDxe/DevicePathDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// DevicePathDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"UEFI Device Path DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.inf b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.inf new file mode 100644 index 000000000..691203816 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.inf @@ -0,0 +1,68 @@ +## @file +# PeiCdExpress recovery module. +# +# This module reads data from CDROM device by all installed block IO ppi and +# finds whether there is Recovery data in the device. If it finds recovery +# data, it will install Device Recovery Module PPI. +# +# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CdExpressPei + MODULE_UNI_FILE = CdExpressPei.uni + FILE_GUID = 31e147a6-d39a-4147-9da3-befd4d523243 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = CdExpressPeimEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + PeiCdExpress.c + PeiCdExpress.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseMemoryLib + PeimEntryPoint + DebugLib + PeiServicesTablePointerLib + PeiServicesLib + MemoryAllocationLib + PcdLib + +[Guids] + gRecoveryOnDataCdGuid ## CONSUMES ## UNDEFINED # Indicate the recovery device type + + +[Ppis] + ## NOTIFY + ## CONSUMES + gEfiPeiVirtualBlockIoPpiGuid + ## NOTIFY + ## CONSUMES + gEfiPeiVirtualBlockIo2PpiGuid + gEfiPeiDeviceRecoveryModulePpiGuid ## PRODUCES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdRecoveryFileName ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + CdExpressPeiExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.uni b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.uni new file mode 100644 index 000000000..89f77e2a9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.uni @@ -0,0 +1,18 @@ +// /** @file +// PeiCdExpress recovery module. +// +// This module reads data from CDROM device by all installed block IO ppi and +// finds whether there is Recovery data in the device. If it finds recovery +// data, it will install Device Recovery Module PPI. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "PeiCdExpress recovery module" + +#string STR_MODULE_DESCRIPTION #language en-US "This module reads data from CDROM device by all installed block IO ppi and finds whether there is Recovery data in the device. If it finds recovery data, it will install Device Recovery Module PPI." + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPeiExtra.uni b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPeiExtra.uni new file mode 100644 index 000000000..d05fff83e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// CdExpressPei Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"CD PEI Module for Recovery" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.c b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.c new file mode 100644 index 000000000..fd34f07ee --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.c @@ -0,0 +1,715 @@ +/** @file + Source file for CD recovery PEIM + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PeiCdExpress.h" + +PEI_CD_EXPRESS_PRIVATE_DATA *mPrivateData = NULL; +CHAR8 *mRecoveryFileName; +UINTN mRecoveryFileNameSize; + +/** + Installs the Device Recovery Module PPI, Initialize BlockIo Ppi + installation notification + + @param FileHandle The file handle of the image. + @param PeiServices General purpose services available to every PEIM. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory. + +**/ +EFI_STATUS +EFIAPI +CdExpressPeimEntry ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData; + + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + PrivateData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*PrivateData))); + if (PrivateData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + mRecoveryFileNameSize = PcdGetSize(PcdRecoveryFileName) / sizeof(CHAR16); + mRecoveryFileName = AllocatePool(mRecoveryFileNameSize); + if (mRecoveryFileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = UnicodeStrToAsciiStrS(PcdGetPtr(PcdRecoveryFileName), mRecoveryFileName, mRecoveryFileNameSize); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Initialize Private Data (to zero, as is required by subsequent operations) + // + ZeroMem (PrivateData, sizeof (*PrivateData)); + PrivateData->Signature = PEI_CD_EXPRESS_PRIVATE_DATA_SIGNATURE; + + PrivateData->BlockBuffer = AllocatePages (EFI_SIZE_TO_PAGES (PEI_CD_BLOCK_SIZE)); + if (PrivateData->BlockBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PrivateData->CapsuleCount = 0; + Status = UpdateBlocksAndVolumes (PrivateData, TRUE); + Status = UpdateBlocksAndVolumes (PrivateData, FALSE); + + // + // Installs Ppi + // + PrivateData->DeviceRecoveryPpi.GetNumberRecoveryCapsules = GetNumberRecoveryCapsules; + PrivateData->DeviceRecoveryPpi.GetRecoveryCapsuleInfo = GetRecoveryCapsuleInfo; + PrivateData->DeviceRecoveryPpi.LoadRecoveryCapsule = LoadRecoveryCapsule; + + PrivateData->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + PrivateData->PpiDescriptor.Guid = &gEfiPeiDeviceRecoveryModulePpiGuid; + PrivateData->PpiDescriptor.Ppi = &PrivateData->DeviceRecoveryPpi; + + Status = PeiServicesInstallPpi (&PrivateData->PpiDescriptor); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + // + // PrivateData is allocated now, set it to the module variable + // + mPrivateData = PrivateData; + + // + // Installs Block Io Ppi notification function + // + PrivateData->NotifyDescriptor.Flags = + ( + EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK + ); + PrivateData->NotifyDescriptor.Guid = &gEfiPeiVirtualBlockIoPpiGuid; + PrivateData->NotifyDescriptor.Notify = BlockIoNotifyEntry; + + PrivateData->NotifyDescriptor2.Flags = + ( + EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | + EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST + ); + PrivateData->NotifyDescriptor2.Guid = &gEfiPeiVirtualBlockIo2PpiGuid; + PrivateData->NotifyDescriptor2.Notify = BlockIoNotifyEntry; + + return PeiServicesNotifyPpi (&PrivateData->NotifyDescriptor); + +} + +/** + BlockIo installation notification function. + + This function finds out all the current Block IO PPIs in the system and add them + into private data. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDescriptor Address of the notification descriptor data structure. + @param Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS The function completes successfully. + +**/ +EFI_STATUS +EFIAPI +BlockIoNotifyEntry ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + if (CompareGuid (NotifyDescriptor->Guid, &gEfiPeiVirtualBlockIo2PpiGuid)) { + UpdateBlocksAndVolumes (mPrivateData, TRUE); + } else { + UpdateBlocksAndVolumes (mPrivateData, FALSE); + } + + return EFI_SUCCESS; +} + +/** + Finds out all the current Block IO PPIs in the system and add them into private data. + + @param PrivateData The private data structure that contains recovery module information. + @param BlockIo2 Boolean to show whether using BlockIo2 or BlockIo. + + @retval EFI_SUCCESS The blocks and volumes are updated successfully. + +**/ +EFI_STATUS +UpdateBlocksAndVolumes ( + IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData, + IN BOOLEAN BlockIo2 + ) +{ + EFI_STATUS Status; + EFI_PEI_PPI_DESCRIPTOR *TempPpiDescriptor; + UINTN BlockIoPpiInstance; + EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2Ppi; + UINTN NumberBlockDevices; + UINTN IndexBlockDevice; + EFI_PEI_BLOCK_IO_MEDIA Media; + EFI_PEI_BLOCK_IO2_MEDIA Media2; + EFI_PEI_SERVICES **PeiServices; + + IndexBlockDevice = 0; + BlockIo2Ppi = NULL; + BlockIoPpi = NULL; + // + // Find out all Block Io Ppi instances within the system + // Assuming all device Block Io Peims are dispatched already + // + for (BlockIoPpiInstance = 0; BlockIoPpiInstance < PEI_CD_EXPRESS_MAX_BLOCK_IO_PPI; BlockIoPpiInstance++) { + if (BlockIo2) { + Status = PeiServicesLocatePpi ( + &gEfiPeiVirtualBlockIo2PpiGuid, + BlockIoPpiInstance, + &TempPpiDescriptor, + (VOID **) &BlockIo2Ppi + ); + } else { + Status = PeiServicesLocatePpi ( + &gEfiPeiVirtualBlockIoPpiGuid, + BlockIoPpiInstance, + &TempPpiDescriptor, + (VOID **) &BlockIoPpi + ); + } + if (EFI_ERROR (Status)) { + // + // Done with all Block Io Ppis + // + break; + } + + PeiServices = (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (); + if (BlockIo2) { + Status = BlockIo2Ppi->GetNumberOfBlockDevices ( + PeiServices, + BlockIo2Ppi, + &NumberBlockDevices + ); + } else { + Status = BlockIoPpi->GetNumberOfBlockDevices ( + PeiServices, + BlockIoPpi, + &NumberBlockDevices + ); + } + if (EFI_ERROR (Status) || (NumberBlockDevices == 0)) { + continue; + } + // + // Just retrieve the first block, should emulate all blocks. + // + for (IndexBlockDevice = 1; IndexBlockDevice <= NumberBlockDevices && PrivateData->CapsuleCount < PEI_CD_EXPRESS_MAX_CAPSULE_NUMBER; IndexBlockDevice ++) { + if (BlockIo2) { + Status = BlockIo2Ppi->GetBlockDeviceMediaInfo ( + PeiServices, + BlockIo2Ppi, + IndexBlockDevice, + &Media2 + ); + if (EFI_ERROR (Status) || + !Media2.MediaPresent || + ((Media2.InterfaceType != MSG_ATAPI_DP) && (Media2.InterfaceType != MSG_USB_DP)) || + (Media2.BlockSize != PEI_CD_BLOCK_SIZE) + ) { + continue; + } + DEBUG ((EFI_D_INFO, "PeiCdExpress InterfaceType is %d\n", Media2.InterfaceType)); + DEBUG ((EFI_D_INFO, "PeiCdExpress MediaPresent is %d\n", Media2.MediaPresent)); + DEBUG ((EFI_D_INFO, "PeiCdExpress BlockSize is 0x%x\n", Media2.BlockSize)); + } else { + Status = BlockIoPpi->GetBlockDeviceMediaInfo ( + PeiServices, + BlockIoPpi, + IndexBlockDevice, + &Media + ); + if (EFI_ERROR (Status) || + !Media.MediaPresent || + ((Media.DeviceType != IdeCDROM) && (Media.DeviceType != UsbMassStorage)) || + (Media.BlockSize != PEI_CD_BLOCK_SIZE) + ) { + continue; + } + DEBUG ((EFI_D_INFO, "PeiCdExpress DeviceType is %d\n", Media.DeviceType)); + DEBUG ((EFI_D_INFO, "PeiCdExpress MediaPresent is %d\n", Media.MediaPresent)); + DEBUG ((EFI_D_INFO, "PeiCdExpress BlockSize is 0x%x\n", Media.BlockSize)); + } + + DEBUG ((EFI_D_INFO, "PeiCdExpress Status is %d\n", Status)); + + DEBUG ((EFI_D_INFO, "IndexBlockDevice is %d\n", IndexBlockDevice)); + PrivateData->CapsuleData[PrivateData->CapsuleCount].IndexBlock = IndexBlockDevice; + if (BlockIo2) { + PrivateData->CapsuleData[PrivateData->CapsuleCount].BlockIo2 = BlockIo2Ppi; + } else { + PrivateData->CapsuleData[PrivateData->CapsuleCount].BlockIo = BlockIoPpi; + } + Status = FindRecoveryCapsules (PrivateData); + DEBUG ((EFI_D_INFO, "Status is %d\n", Status)); + + if (EFI_ERROR (Status)) { + continue; + } + + PrivateData->CapsuleCount++; + } + + } + + return EFI_SUCCESS; +} + +/** + Finds out the recovery capsule in the current volume. + + @param PrivateData The private data structure that contains recovery module information. + + @retval EFI_SUCCESS The recovery capsule is successfully found in the volume. + @retval EFI_NOT_FOUND The recovery capsule is not found in the volume. + +**/ +EFI_STATUS +EFIAPI +FindRecoveryCapsules ( + IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData + ) +{ + EFI_STATUS Status; + UINTN Lba; + EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2Ppi; + UINTN BufferSize; + UINT8 *Buffer; + UINT8 Type; + UINT8 *StandardID; + UINT32 RootDirLBA; + PEI_CD_EXPRESS_DIR_FILE_RECORD *RoorDirRecord; + UINTN VolumeSpaceSize; + BOOLEAN StartOfVolume; + UINTN OriginalLBA; + UINTN IndexBlockDevice; + + Buffer = PrivateData->BlockBuffer; + BufferSize = PEI_CD_BLOCK_SIZE; + + Lba = 16; + // + // The volume descriptor starts on Lba 16 + // + IndexBlockDevice = PrivateData->CapsuleData[PrivateData->CapsuleCount].IndexBlock; + BlockIoPpi = PrivateData->CapsuleData[PrivateData->CapsuleCount].BlockIo; + BlockIo2Ppi = PrivateData->CapsuleData[PrivateData->CapsuleCount].BlockIo2; + + VolumeSpaceSize = 0; + StartOfVolume = TRUE; + OriginalLBA = 16; + + while (TRUE) { + SetMem (Buffer, BufferSize, 0); + if (BlockIo2Ppi != NULL) { + Status = BlockIo2Ppi->ReadBlocks ( + (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (), + BlockIo2Ppi, + IndexBlockDevice, + Lba, + BufferSize, + Buffer + ); + } else { + Status = BlockIoPpi->ReadBlocks ( + (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (), + BlockIoPpi, + IndexBlockDevice, + Lba, + BufferSize, + Buffer + ); + } + if (EFI_ERROR (Status)) { + return Status; + } + + StandardID = (UINT8 *) (Buffer + PEI_CD_EXPRESS_STANDARD_ID_OFFSET); + if (!StringCmp (StandardID, (UINT8 *) PEI_CD_STANDARD_ID, PEI_CD_EXPRESS_STANDARD_ID_SIZE, TRUE)) { + break; + } + + if (StartOfVolume) { + OriginalLBA = Lba; + StartOfVolume = FALSE; + } + + Type = *(UINT8 *) (Buffer + PEI_CD_EXPRESS_VOLUME_TYPE_OFFSET); + if (Type == PEI_CD_EXPRESS_VOLUME_TYPE_TERMINATOR) { + if (VolumeSpaceSize == 0) { + break; + } else { + Lba = (OriginalLBA + VolumeSpaceSize); + VolumeSpaceSize = 0; + StartOfVolume = TRUE; + continue; + } + } + + if (Type != PEI_CD_EXPRESS_VOLUME_TYPE_PRIMARY) { + Lba++; + continue; + } + + VolumeSpaceSize = *(UINT32 *) (Buffer + PEI_CD_EXPRESS_VOLUME_SPACE_OFFSET); + + RoorDirRecord = (PEI_CD_EXPRESS_DIR_FILE_RECORD *) (Buffer + PEI_CD_EXPRESS_ROOT_DIR_RECORD_OFFSET); + RootDirLBA = RoorDirRecord->LocationOfExtent[0]; + + Status = RetrieveCapsuleFileFromRoot (PrivateData, BlockIoPpi, BlockIo2Ppi, IndexBlockDevice, RootDirLBA); + if (!EFI_ERROR (Status)) { + // + // Just look for the first primary descriptor + // + return EFI_SUCCESS; + } + + Lba++; + } + + return EFI_NOT_FOUND; +} + +/** + Retrieves the recovery capsule in root directory of the current volume. + + @param PrivateData The private data structure that contains recovery module information. + @param BlockIoPpi The Block IO PPI used to access the volume. + @param BlockIo2Ppi The Block IO 2 PPI used to access the volume. + @param IndexBlockDevice The index of current block device. + @param Lba The starting logic block address to retrieve capsule. + + @retval EFI_SUCCESS The recovery capsule is successfully found in the volume. + @retval EFI_NOT_FOUND The recovery capsule is not found in the volume. + @retval Others + +**/ +EFI_STATUS +EFIAPI +RetrieveCapsuleFileFromRoot ( + IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2Ppi, + IN UINTN IndexBlockDevice, + IN UINT32 Lba + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + UINT8 *Buffer; + PEI_CD_EXPRESS_DIR_FILE_RECORD *FileRecord; + UINTN Index; + + Buffer = PrivateData->BlockBuffer; + BufferSize = PEI_CD_BLOCK_SIZE; + + SetMem (Buffer, BufferSize, 0); + + if (BlockIo2Ppi != NULL) { + Status = BlockIo2Ppi->ReadBlocks ( + (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (), + BlockIo2Ppi, + IndexBlockDevice, + Lba, + BufferSize, + Buffer + ); + } else { + Status = BlockIoPpi->ReadBlocks ( + (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (), + BlockIoPpi, + IndexBlockDevice, + Lba, + BufferSize, + Buffer + ); + } + if (EFI_ERROR (Status)) { + return Status; + } + + while (1) { + FileRecord = (PEI_CD_EXPRESS_DIR_FILE_RECORD *) Buffer; + + if (FileRecord->Length == 0) { + break; + } + // + // Not intend to check other flag now + // + if ((FileRecord->Flag & PEI_CD_EXPRESS_DIR_FILE_REC_FLAG_ISDIR) != 0) { + Buffer += FileRecord->Length; + continue; + } + + for (Index = 0; Index < FileRecord->FileIDLength; Index++) { + if (FileRecord->FileID[Index] == ';') { + break; + } + } + + if (Index != mRecoveryFileNameSize - 1) { + Buffer += FileRecord->Length; + continue; + } + + if (!StringCmp (FileRecord->FileID, (UINT8 *)mRecoveryFileName, mRecoveryFileNameSize - 1, FALSE)) { + Buffer += FileRecord->Length; + continue; + } + + PrivateData->CapsuleData[PrivateData->CapsuleCount].CapsuleStartLBA = FileRecord->LocationOfExtent[0]; + PrivateData->CapsuleData[PrivateData->CapsuleCount].CapsuleBlockAlignedSize = + ( + FileRecord->DataLength[0] / + PEI_CD_BLOCK_SIZE + + 1 + ) * + PEI_CD_BLOCK_SIZE; + PrivateData->CapsuleData[PrivateData->CapsuleCount].CapsuleSize = FileRecord->DataLength[0]; + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + Returns the number of DXE capsules residing on the device. + + This function searches for DXE capsules from the associated device and returns + the number and maximum size in bytes of the capsules discovered. Entry 1 is + assumed to be the highest load priority and entry N is assumed to be the lowest + priority. + + @param[in] PeiServices General-purpose services that are available + to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[out] NumberRecoveryCapsules Pointer to a caller-allocated UINTN. On + output, *NumberRecoveryCapsules contains + the number of recovery capsule images + available for retrieval from this PEIM + instance. + + @retval EFI_SUCCESS One or more capsules were discovered. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +GetNumberRecoveryCapsules ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + OUT UINTN *NumberRecoveryCapsules + ) +{ + PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData; + + PrivateData = PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This); + UpdateBlocksAndVolumes (PrivateData, TRUE); + UpdateBlocksAndVolumes (PrivateData, FALSE); + *NumberRecoveryCapsules = PrivateData->CapsuleCount; + + if (*NumberRecoveryCapsules == 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + Returns the size and type of the requested recovery capsule. + + This function gets the size and type of the capsule specified by CapsuleInstance. + + @param[in] PeiServices General-purpose services that are available to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[in] CapsuleInstance Specifies for which capsule instance to retrieve + the information. This parameter must be between + one and the value returned by GetNumberRecoveryCapsules() + in NumberRecoveryCapsules. + @param[out] Size A pointer to a caller-allocated UINTN in which + the size of the requested recovery module is + returned. + @param[out] CapsuleType A pointer to a caller-allocated EFI_GUID in which + the type of the requested recovery capsule is + returned. The semantic meaning of the value + returned is defined by the implementation. + + @retval EFI_SUCCESS One or more capsules were discovered. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +GetRecoveryCapsuleInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT UINTN *Size, + OUT EFI_GUID *CapsuleType + ) +{ + PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData; + UINTN NumberRecoveryCapsules; + EFI_STATUS Status; + + Status = GetNumberRecoveryCapsules (PeiServices, This, &NumberRecoveryCapsules); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((CapsuleInstance == 0) || (CapsuleInstance > NumberRecoveryCapsules)) { + return EFI_NOT_FOUND; + } + + PrivateData = PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This); + + *Size = PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleSize; + CopyMem ( + CapsuleType, + &gRecoveryOnDataCdGuid, + sizeof (EFI_GUID) + ); + + return EFI_SUCCESS; +} + +/** + Loads a DXE capsule from some media into memory. + + This function, by whatever mechanism, retrieves a DXE capsule from some device + and loads it into memory. Note that the published interface is device neutral. + + @param[in] PeiServices General-purpose services that are available + to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[in] CapsuleInstance Specifies which capsule instance to retrieve. + @param[out] Buffer Specifies a caller-allocated buffer in which + the requested recovery capsule will be returned. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A requested recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadRecoveryCapsule ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData; + EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2Ppi; + UINTN NumberRecoveryCapsules; + + Status = GetNumberRecoveryCapsules (PeiServices, This, &NumberRecoveryCapsules); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((CapsuleInstance == 0) || (CapsuleInstance > NumberRecoveryCapsules)) { + return EFI_NOT_FOUND; + } + + PrivateData = PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This); + BlockIoPpi = PrivateData->CapsuleData[CapsuleInstance - 1].BlockIo; + BlockIo2Ppi = PrivateData->CapsuleData[CapsuleInstance - 1].BlockIo2; + + if (BlockIo2Ppi != NULL) { + Status = BlockIo2Ppi->ReadBlocks ( + PeiServices, + BlockIo2Ppi, + PrivateData->CapsuleData[CapsuleInstance - 1].IndexBlock, + PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleStartLBA, + PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleBlockAlignedSize, + Buffer + ); + } else { + Status = BlockIoPpi->ReadBlocks ( + PeiServices, + BlockIoPpi, + PrivateData->CapsuleData[CapsuleInstance - 1].IndexBlock, + PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleStartLBA, + PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleBlockAlignedSize, + Buffer + ); + } + return Status; +} + +/** + This function compares two ASCII strings in case sensitive/insensitive way. + + @param Source1 The first string. + @param Source2 The second string. + @param Size The maximum comparison length. + @param CaseSensitive Flag to indicate whether the comparison is case sensitive. + + @retval TRUE The two strings are the same. + @retval FALSE The two string are not the same. + +**/ +BOOLEAN +StringCmp ( + IN UINT8 *Source1, + IN UINT8 *Source2, + IN UINTN Size, + IN BOOLEAN CaseSensitive + ) +{ + UINTN Index; + UINT8 Dif; + + for (Index = 0; Index < Size; Index++) { + if (Source1[Index] == Source2[Index]) { + continue; + } + + if (!CaseSensitive) { + Dif = (UINT8) ((Source1[Index] > Source2[Index]) ? (Source1[Index] - Source2[Index]) : (Source2[Index] - Source1[Index])); + if (Dif == ('a' - 'A')) { + continue; + } + } + + return FALSE; + } + + return TRUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.h b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.h new file mode 100644 index 000000000..513f0709b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/CdExpressPei/PeiCdExpress.h @@ -0,0 +1,292 @@ +/** @file + Header file for CD recovery PEIM + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PEI_CD_EXPRESS_H_ +#define _PEI_CD_EXPRESS_H_ + + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#pragma pack(1) + +#define PEI_CD_EXPRESS_MAX_BLOCK_IO_PPI 8 +#define PEI_CD_EXPRESS_MAX_CAPSULE_NUMBER 16 + +#define PEI_CD_BLOCK_SIZE 0x800 +#define PEI_MEMMORY_PAGE_SIZE 0x1000 + +// +// Following are defined according to ISO-9660 specification +// +#define PEI_CD_STANDARD_ID "CD001" +#define PEI_CD_EXPRESS_STANDARD_ID_SIZE 5 + +#define PEI_CD_EXPRESS_VOLUME_TYPE_OFFSET 0 +#define PEI_CD_EXPRESS_STANDARD_ID_OFFSET 1 +#define PEI_CD_EXPRESS_VOLUME_SPACE_OFFSET 80 +#define PEI_CD_EXPRESS_ROOT_DIR_RECORD_OFFSET 156 + +#define PEI_CD_EXPRESS_VOLUME_TYPE_PRIMARY 1 +#define PEI_CD_EXPRESS_VOLUME_TYPE_TERMINATOR 255 + +#define PEI_CD_EXPRESS_DIR_FILE_REC_FLAG_ISDIR 0x02 + +typedef struct { + UINTN CapsuleStartLBA; + UINTN CapsuleSize; + UINTN CapsuleBlockAlignedSize; + UINTN IndexBlock; + EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIo; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2; +} PEI_CD_EXPRESS_CAPSULE_DATA; + +#define PEI_CD_EXPRESS_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('p', 'c', 'd', 'e') + +typedef struct { + + UINTN Signature; + EFI_PEI_DEVICE_RECOVERY_MODULE_PPI DeviceRecoveryPpi; + EFI_PEI_PPI_DESCRIPTOR PpiDescriptor; + EFI_PEI_NOTIFY_DESCRIPTOR NotifyDescriptor; + EFI_PEI_NOTIFY_DESCRIPTOR NotifyDescriptor2; + + UINT8 *BlockBuffer; + UINTN CapsuleCount; + PEI_CD_EXPRESS_CAPSULE_DATA CapsuleData[PEI_CD_EXPRESS_MAX_CAPSULE_NUMBER]; + +} PEI_CD_EXPRESS_PRIVATE_DATA; + +#define PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS(a) \ + CR (a, \ + PEI_CD_EXPRESS_PRIVATE_DATA, \ + DeviceRecoveryPpi, \ + PEI_CD_EXPRESS_PRIVATE_DATA_SIGNATURE \ + ) + +typedef struct { + UINT8 Length; + UINT8 ExtendedAttributeRecordLength; + UINT32 LocationOfExtent[2]; + UINT32 DataLength[2]; + UINT8 DateTime[7]; + UINT8 Flag; + UINT8 FileUnitSize; + UINT8 InterleaveGapSize; + UINT32 VolumeSequenceNumber; + UINT8 FileIDLength; + UINT8 FileID[1]; +} PEI_CD_EXPRESS_DIR_FILE_RECORD; + +/** + BlockIo installation notification function. + + This function finds out all the current Block IO PPIs in the system and add them + into private data. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDescriptor Address of the notification descriptor data structure. + @param Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS The function completes successfully. + +**/ +EFI_STATUS +EFIAPI +BlockIoNotifyEntry ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +/** + Finds out all the current Block IO PPIs in the system and add them into private data. + + @param PrivateData The private data structure that contains recovery module information. + @param BlockIo2 Boolean to show whether using BlockIo2 or BlockIo. + + @retval EFI_SUCCESS The blocks and volumes are updated successfully. + +**/ +EFI_STATUS +UpdateBlocksAndVolumes ( + IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData, + IN BOOLEAN BlockIo2 + ); + +/** + Returns the number of DXE capsules residing on the device. + + This function searches for DXE capsules from the associated device and returns + the number and maximum size in bytes of the capsules discovered. Entry 1 is + assumed to be the highest load priority and entry N is assumed to be the lowest + priority. + + @param[in] PeiServices General-purpose services that are available + to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[out] NumberRecoveryCapsules Pointer to a caller-allocated UINTN. On + output, *NumberRecoveryCapsules contains + the number of recovery capsule images + available for retrieval from this PEIM + instance. + + @retval EFI_SUCCESS One or more capsules were discovered. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +GetNumberRecoveryCapsules ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + OUT UINTN *NumberRecoveryCapsules + ); + +/** + Returns the size and type of the requested recovery capsule. + + This function gets the size and type of the capsule specified by CapsuleInstance. + + @param[in] PeiServices General-purpose services that are available to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[in] CapsuleInstance Specifies for which capsule instance to retrieve + the information. This parameter must be between + one and the value returned by GetNumberRecoveryCapsules() + in NumberRecoveryCapsules. + @param[out] Size A pointer to a caller-allocated UINTN in which + the size of the requested recovery module is + returned. + @param[out] CapsuleType A pointer to a caller-allocated EFI_GUID in which + the type of the requested recovery capsule is + returned. The semantic meaning of the value + returned is defined by the implementation. + + @retval EFI_SUCCESS One or more capsules were discovered. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +GetRecoveryCapsuleInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT UINTN *Size, + OUT EFI_GUID *CapsuleType + ); + +/** + Loads a DXE capsule from some media into memory. + + This function, by whatever mechanism, retrieves a DXE capsule from some device + and loads it into memory. Note that the published interface is device neutral. + + @param[in] PeiServices General-purpose services that are available + to every PEIM + @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI + instance. + @param[in] CapsuleInstance Specifies which capsule instance to retrieve. + @param[out] Buffer Specifies a caller-allocated buffer in which + the requested recovery capsule will be returned. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A requested recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadRecoveryCapsule ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This, + IN UINTN CapsuleInstance, + OUT VOID *Buffer + ); + +/** + Finds out the recovery capsule in the current volume. + + @param PrivateData The private data structure that contains recovery module information. + + @retval EFI_SUCCESS The recovery capsule is successfully found in the volume. + @retval EFI_NOT_FOUND The recovery capsule is not found in the volume. + +**/ +EFI_STATUS +EFIAPI +FindRecoveryCapsules ( + IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData + ); + +/** + Retrieves the recovery capsule in root directory of the current volume. + + @param PrivateData The private data structure that contains recovery module information. + @param BlockIoPpi The Block IO PPI used to access the volume. + @param BlockIo2Ppi The Block IO 2 PPI used to access the volume. + @param IndexBlockDevice The index of current block device. + @param Lba The starting logic block address to retrieve capsule. + + @retval EFI_SUCCESS The recovery capsule is successfully found in the volume. + @retval EFI_NOT_FOUND The recovery capsule is not found in the volume. + @retval Others + +**/ +EFI_STATUS +EFIAPI +RetrieveCapsuleFileFromRoot ( + IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *BlockIo2Ppi, + IN UINTN IndexBlockDevice, + IN UINT32 Lba + ); + + +/** + This function compares two ASCII strings in case sensitive/insensitive way. + + @param Source1 The first string. + @param Source2 The second string. + @param Size The maximum comparison length. + @param CaseSensitive Flag to indicate whether the comparison is case sensitive. + + @retval TRUE The two strings are the same. + @retval FALSE The two string are not the same. + +**/ +BOOLEAN +StringCmp ( + IN UINT8 *Source1, + IN UINT8 *Source2, + IN UINTN Size, + IN BOOLEAN CaseSensitive + ); + +#pragma pack() + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/ComponentName.c new file mode 100644 index 000000000..ba6e427cd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/ComponentName.c @@ -0,0 +1,183 @@ +/** @file + UEFI Component Name(2) protocol implementation for DiskIo driver. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DiskIo.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDiskIoComponentName = { + DiskIoComponentNameGetDriverName, + DiskIoComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDiskIoComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DiskIoComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DiskIoComponentNameGetControllerName, + "en" +}; + +// +// Driver name table for DiskIo module. +// It is shared by the implementation of ComponentName & ComponentName2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDiskIoDriverNameTable[] = { + { + "eng;en", + (CHAR16 *)L"Generic Disk I/O Driver" + }, + { + NULL, + NULL + } +}; + + + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DiskIoComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDiskIoDriverNameTable, + DriverName, + (BOOLEAN)(This == &gDiskIoComponentName) + ); +} + + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DiskIoComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.c b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.c new file mode 100644 index 000000000..e19466bd2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.c @@ -0,0 +1,1262 @@ +/** @file + DiskIo driver that lays on every BlockIo protocol in the system. + DiskIo converts a block oriented device to a byte oriented device. + + Disk access may have to handle unaligned request about sector boundaries. + There are three cases: + UnderRun - The first byte is not on a sector boundary or the read request is + less than a sector in length. + Aligned - A read of N contiguous sectors. + OverRun - The last byte is not on a sector boundary. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DiskIo.h" + +// +// Driver binding protocol implementation for DiskIo driver. +// +EFI_DRIVER_BINDING_PROTOCOL gDiskIoDriverBinding = { + DiskIoDriverBindingSupported, + DiskIoDriverBindingStart, + DiskIoDriverBindingStop, + 0xa, + NULL, + NULL +}; + +// +// Template for DiskIo private data structure. +// The pointer to BlockIo protocol interface is assigned dynamically. +// +DISK_IO_PRIVATE_DATA gDiskIoPrivateDataTemplate = { + DISK_IO_PRIVATE_DATA_SIGNATURE, + { + EFI_DISK_IO_PROTOCOL_REVISION, + DiskIoReadDisk, + DiskIoWriteDisk + }, + { + EFI_DISK_IO2_PROTOCOL_REVISION, + DiskIo2Cancel, + DiskIo2ReadDiskEx, + DiskIo2WriteDiskEx, + DiskIo2FlushDiskEx + } +}; + +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +DiskIoDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + // + // Open the IO Abstraction(s) needed to perform the supported test. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close the I/O Abstraction(s) used to perform the supported test. + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return EFI_SUCCESS; +} + + +/** + Start this driver on ControllerHandle by opening a Block IO protocol and + installing a Disk IO protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +DiskIoDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + DISK_IO_PRIVATE_DATA *Instance; + EFI_TPL OldTpl; + + Instance = NULL; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Connect to the Block IO and Block IO2 interface on ControllerHandle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &gDiskIoPrivateDataTemplate.BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ErrorExit1; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIo2ProtocolGuid, + (VOID **) &gDiskIoPrivateDataTemplate.BlockIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + gDiskIoPrivateDataTemplate.BlockIo2 = NULL; + } + + // + // Initialize the Disk IO device instance. + // + Instance = AllocateCopyPool (sizeof (DISK_IO_PRIVATE_DATA), &gDiskIoPrivateDataTemplate); + if (Instance == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + // + // The BlockSize and IoAlign of BlockIo and BlockIo2 should equal. + // + ASSERT ((Instance->BlockIo2 == NULL) || + ((Instance->BlockIo->Media->IoAlign == Instance->BlockIo2->Media->IoAlign) && + (Instance->BlockIo->Media->BlockSize == Instance->BlockIo2->Media->BlockSize) + )); + + InitializeListHead (&Instance->TaskQueue); + EfiInitializeLock (&Instance->TaskQueueLock, TPL_NOTIFY); + Instance->SharedWorkingBuffer = AllocateAlignedPages ( + EFI_SIZE_TO_PAGES (PcdGet32 (PcdDiskIoDataBufferBlockNum) * Instance->BlockIo->Media->BlockSize), + Instance->BlockIo->Media->IoAlign + ); + if (Instance->SharedWorkingBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + // + // Install protocol interfaces for the Disk IO device. + // + if (Instance->BlockIo2 != NULL) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiDiskIoProtocolGuid, &Instance->DiskIo, + &gEfiDiskIo2ProtocolGuid, &Instance->DiskIo2, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiDiskIoProtocolGuid, &Instance->DiskIo, + NULL + ); + } + +ErrorExit: + if (EFI_ERROR (Status)) { + if (Instance != NULL && Instance->SharedWorkingBuffer != NULL) { + FreeAlignedPages ( + Instance->SharedWorkingBuffer, + EFI_SIZE_TO_PAGES (PcdGet32 (PcdDiskIoDataBufferBlockNum) * Instance->BlockIo->Media->BlockSize) + ); + } + + if (Instance != NULL) { + FreePool (Instance); + } + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + +ErrorExit1: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Stop this driver on ControllerHandle by removing Disk IO protocol and closing + the Block IO protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +DiskIoDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + DISK_IO_PRIVATE_DATA *Instance; + BOOLEAN AllTaskDone; + + // + // Get our context back. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + (VOID **) &DiskIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + DiskIo2 = NULL; + } + + Instance = DISK_IO_PRIVATE_DATA_FROM_DISK_IO (DiskIo); + + if (DiskIo2 != NULL) { + // + // Call BlockIo2::Reset() to terminate any in-flight non-blocking I/O requests + // + ASSERT (Instance->BlockIo2 != NULL); + Status = Instance->BlockIo2->Reset (Instance->BlockIo2, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gBS->UninstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, &Instance->DiskIo, + &gEfiDiskIo2ProtocolGuid, &Instance->DiskIo2, + NULL + ); + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, &Instance->DiskIo, + NULL + ); + } + if (!EFI_ERROR (Status)) { + + do { + EfiAcquireLock (&Instance->TaskQueueLock); + AllTaskDone = IsListEmpty (&Instance->TaskQueue); + EfiReleaseLock (&Instance->TaskQueueLock); + } while (!AllTaskDone); + + FreeAlignedPages ( + Instance->SharedWorkingBuffer, + EFI_SIZE_TO_PAGES (PcdGet32 (PcdDiskIoDataBufferBlockNum) * Instance->BlockIo->Media->BlockSize) + ); + + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + ASSERT_EFI_ERROR (Status); + if (DiskIo2 != NULL) { + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + ASSERT_EFI_ERROR (Status); + } + + FreePool (Instance); + } + + return Status; +} + + +/** + Destroy the sub task. + + @param Instance Pointer to the DISK_IO_PRIVATE_DATA. + @param Subtask Subtask. + + @return LIST_ENTRY * Pointer to the next link of subtask. +**/ +LIST_ENTRY * +DiskIoDestroySubtask ( + IN DISK_IO_PRIVATE_DATA *Instance, + IN DISK_IO_SUBTASK *Subtask + ) +{ + LIST_ENTRY *Link; + + if (Subtask->Task != NULL) { + EfiAcquireLock (&Subtask->Task->SubtasksLock); + } + Link = RemoveEntryList (&Subtask->Link); + if (Subtask->Task != NULL) { + EfiReleaseLock (&Subtask->Task->SubtasksLock); + } + + if (!Subtask->Blocking) { + if (Subtask->WorkingBuffer != NULL) { + FreeAlignedPages ( + Subtask->WorkingBuffer, + Subtask->Length < Instance->BlockIo->Media->BlockSize + ? EFI_SIZE_TO_PAGES (Instance->BlockIo->Media->BlockSize) + : EFI_SIZE_TO_PAGES (Subtask->Length) + ); + } + if (Subtask->BlockIo2Token.Event != NULL) { + gBS->CloseEvent (Subtask->BlockIo2Token.Event); + } + } + FreePool (Subtask); + + return Link; +} + +/** + The callback for the BlockIo2 ReadBlocksEx/WriteBlocksEx. + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which points to the DISK_IO_SUBTASK instance. +**/ +VOID +EFIAPI +DiskIo2OnReadWriteComplete ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + DISK_IO_SUBTASK *Subtask; + DISK_IO2_TASK *Task; + EFI_STATUS TransactionStatus; + DISK_IO_PRIVATE_DATA *Instance; + + Subtask = (DISK_IO_SUBTASK *) Context; + TransactionStatus = Subtask->BlockIo2Token.TransactionStatus; + Task = Subtask->Task; + Instance = Task->Instance; + + ASSERT (Subtask->Signature == DISK_IO_SUBTASK_SIGNATURE); + ASSERT (Instance->Signature == DISK_IO_PRIVATE_DATA_SIGNATURE); + ASSERT (Task->Signature == DISK_IO2_TASK_SIGNATURE); + + if ((Subtask->WorkingBuffer != NULL) && !EFI_ERROR (TransactionStatus) && + (Task->Token != NULL) && !Subtask->Write + ) { + CopyMem (Subtask->Buffer, Subtask->WorkingBuffer + Subtask->Offset, Subtask->Length); + } + + DiskIoDestroySubtask (Instance, Subtask); + + if (EFI_ERROR (TransactionStatus) || IsListEmpty (&Task->Subtasks)) { + if (Task->Token != NULL) { + // + // Signal error status once the subtask is failed. + // Or signal the last status once the last subtask is finished. + // + Task->Token->TransactionStatus = TransactionStatus; + gBS->SignalEvent (Task->Token->Event); + + // + // Mark token to NULL indicating the Task is a dead task. + // + Task->Token = NULL; + } + } +} + +/** + Create the subtask. + + @param Write TRUE: Write request; FALSE: Read request. + @param Lba The starting logical block address to read from on the device. + @param Offset The starting byte offset to read from the LBA. + @param Length The number of bytes to read from the device. + @param WorkingBuffer The aligned buffer to hold the data for reading or writing. + @param Buffer The buffer to hold the data for reading or writing. + @param Blocking TRUE: Blocking request; FALSE: Non-blocking request. + + @return A pointer to the created subtask. +**/ +DISK_IO_SUBTASK * +DiskIoCreateSubtask ( + IN BOOLEAN Write, + IN UINT64 Lba, + IN UINT32 Offset, + IN UINTN Length, + IN VOID *WorkingBuffer, OPTIONAL + IN VOID *Buffer, + IN BOOLEAN Blocking + ) +{ + DISK_IO_SUBTASK *Subtask; + EFI_STATUS Status; + + Subtask = AllocateZeroPool (sizeof (DISK_IO_SUBTASK)); + if (Subtask == NULL) { + return NULL; + } + Subtask->Signature = DISK_IO_SUBTASK_SIGNATURE; + Subtask->Write = Write; + Subtask->Lba = Lba; + Subtask->Offset = Offset; + Subtask->Length = Length; + Subtask->WorkingBuffer = WorkingBuffer; + Subtask->Buffer = Buffer; + Subtask->Blocking = Blocking; + if (!Blocking) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + DiskIo2OnReadWriteComplete, + Subtask, + &Subtask->BlockIo2Token.Event + ); + if (EFI_ERROR (Status)) { + FreePool (Subtask); + return NULL; + } + } + DEBUG (( + EFI_D_BLKIO, + " %c:Lba/Offset/Length/WorkingBuffer/Buffer = %016lx/%08x/%08x/%08x/%08x\n", + Write ? 'W': 'R', Lba, Offset, Length, WorkingBuffer, Buffer + )); + + return Subtask; +} + +/** + Create the subtask list. + + @param Instance Pointer to the DISK_IO_PRIVATE_DATA. + @param Write TRUE: Write request; FALSE: Read request. + @param Offset The starting byte offset to read from the device. + @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device. + @param Buffer A pointer to the buffer for the data. + @param Blocking TRUE: Blocking request; FALSE: Non-blocking request. + @param SharedWorkingBuffer The aligned buffer to hold the data for reading or writing. + @param Subtasks The subtask list header. + + @retval TRUE The subtask list is created successfully. + @retval FALSE The subtask list is not created. +**/ +BOOLEAN +DiskIoCreateSubtaskList ( + IN DISK_IO_PRIVATE_DATA *Instance, + IN BOOLEAN Write, + IN UINT64 Offset, + IN UINTN BufferSize, + IN VOID *Buffer, + IN BOOLEAN Blocking, + IN VOID *SharedWorkingBuffer, + IN OUT LIST_ENTRY *Subtasks + ) +{ + UINT32 BlockSize; + UINT32 IoAlign; + UINT64 Lba; + UINT64 OverRunLba; + UINT32 UnderRun; + UINT32 OverRun; + UINT8 *BufferPtr; + UINTN Length; + UINTN DataBufferSize; + DISK_IO_SUBTASK *Subtask; + VOID *WorkingBuffer; + LIST_ENTRY *Link; + + DEBUG ((EFI_D_BLKIO, "DiskIo: Create subtasks for task: Offset/BufferSize/Buffer = %016lx/%08x/%08x\n", Offset, BufferSize, Buffer)); + + BlockSize = Instance->BlockIo->Media->BlockSize; + IoAlign = Instance->BlockIo->Media->IoAlign; + if (IoAlign == 0) { + IoAlign = 1; + } + + Lba = DivU64x32Remainder (Offset, BlockSize, &UnderRun); + BufferPtr = (UINT8 *) Buffer; + + // + // Special handling for zero BufferSize + // + if (BufferSize == 0) { + Subtask = DiskIoCreateSubtask (Write, Lba, UnderRun, 0, NULL, BufferPtr, Blocking); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + return TRUE; + } + + if (UnderRun != 0) { + Length = MIN (BlockSize - UnderRun, BufferSize); + if (Blocking) { + WorkingBuffer = SharedWorkingBuffer; + } else { + WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BlockSize), IoAlign); + if (WorkingBuffer == NULL) { + goto Done; + } + } + if (Write) { + // + // A half write operation can be splitted to a blocking block-read and half write operation + // This can simplify the sub task processing logic + // + Subtask = DiskIoCreateSubtask (FALSE, Lba, 0, BlockSize, NULL, WorkingBuffer, TRUE); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + } + + Subtask = DiskIoCreateSubtask (Write, Lba, UnderRun, Length, WorkingBuffer, BufferPtr, Blocking); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + + BufferPtr += Length; + Offset += Length; + BufferSize -= Length; + Lba ++; + } + + OverRunLba = Lba + DivU64x32Remainder (BufferSize, BlockSize, &OverRun); + BufferSize -= OverRun; + + if (OverRun != 0) { + if (Blocking) { + WorkingBuffer = SharedWorkingBuffer; + } else { + WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BlockSize), IoAlign); + if (WorkingBuffer == NULL) { + goto Done; + } + } + if (Write) { + // + // A half write operation can be splitted to a blocking block-read and half write operation + // This can simplify the sub task processing logic + // + Subtask = DiskIoCreateSubtask (FALSE, OverRunLba, 0, BlockSize, NULL, WorkingBuffer, TRUE); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + } + + Subtask = DiskIoCreateSubtask (Write, OverRunLba, 0, OverRun, WorkingBuffer, BufferPtr + BufferSize, Blocking); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + } + + if (OverRunLba > Lba) { + // + // If the DiskIo maps directly to a BlockIo device do the read. + // + if (ALIGN_POINTER (BufferPtr, IoAlign) == BufferPtr) { + Subtask = DiskIoCreateSubtask (Write, Lba, 0, BufferSize, NULL, BufferPtr, Blocking); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + + BufferPtr += BufferSize; + Offset += BufferSize; + BufferSize -= BufferSize; + + } else { + if (Blocking) { + // + // Use the allocated buffer instead of the original buffer + // to avoid alignment issue. + // + for (; Lba < OverRunLba; Lba += PcdGet32 (PcdDiskIoDataBufferBlockNum)) { + DataBufferSize = MIN (BufferSize, PcdGet32 (PcdDiskIoDataBufferBlockNum) * BlockSize); + + Subtask = DiskIoCreateSubtask (Write, Lba, 0, DataBufferSize, SharedWorkingBuffer, BufferPtr, Blocking); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + + BufferPtr += DataBufferSize; + Offset += DataBufferSize; + BufferSize -= DataBufferSize; + } + } else { + WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), IoAlign); + if (WorkingBuffer == NULL) { + // + // If there is not enough memory, downgrade to blocking access + // + DEBUG ((EFI_D_VERBOSE, "DiskIo: No enough memory so downgrade to blocking access\n")); + if (!DiskIoCreateSubtaskList (Instance, Write, Offset, BufferSize, BufferPtr, TRUE, SharedWorkingBuffer, Subtasks)) { + goto Done; + } + } else { + Subtask = DiskIoCreateSubtask (Write, Lba, 0, BufferSize, WorkingBuffer, BufferPtr, Blocking); + if (Subtask == NULL) { + goto Done; + } + InsertTailList (Subtasks, &Subtask->Link); + } + + BufferPtr += BufferSize; + Offset += BufferSize; + BufferSize -= BufferSize; + } + } + } + + ASSERT (BufferSize == 0); + + return TRUE; + +Done: + // + // Remove all the subtasks. + // + for (Link = GetFirstNode (Subtasks); !IsNull (Subtasks, Link); ) { + Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE); + Link = DiskIoDestroySubtask (Instance, Subtask); + } + return FALSE; +} + +/** + Terminate outstanding asynchronous requests to a device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding requests were successfully terminated. + @retval EFI_DEVICE_ERROR The device reported an error while performing the cancel + operation. +**/ +EFI_STATUS +EFIAPI +DiskIo2Cancel ( + IN EFI_DISK_IO2_PROTOCOL *This + ) +{ + DISK_IO_PRIVATE_DATA *Instance; + DISK_IO2_TASK *Task; + LIST_ENTRY *Link; + + Instance = DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This); + + EfiAcquireLock (&Instance->TaskQueueLock); + + for (Link = GetFirstNode (&Instance->TaskQueue) + ; !IsNull (&Instance->TaskQueue, Link) + ; Link = GetNextNode (&Instance->TaskQueue, Link) + ) { + Task = CR (Link, DISK_IO2_TASK, Link, DISK_IO2_TASK_SIGNATURE); + + if (Task->Token != NULL) { + Task->Token->TransactionStatus = EFI_ABORTED; + gBS->SignalEvent (Task->Token->Event); + // + // Set Token to NULL so that the further BlockIo2 responses will be ignored + // + Task->Token = NULL; + } + } + + EfiReleaseLock (&Instance->TaskQueueLock); + + return EFI_SUCCESS; +} + +/** + Remove the completed tasks from Instance->TaskQueue. Completed tasks are those who don't have any subtasks. + + @param Instance Pointer to the DISK_IO_PRIVATE_DATA. + + @retval TRUE The Instance->TaskQueue is empty after the completed tasks are removed. + @retval FALSE The Instance->TaskQueue is not empty after the completed tasks are removed. +**/ +BOOLEAN +DiskIo2RemoveCompletedTask ( + IN DISK_IO_PRIVATE_DATA *Instance + ) +{ + BOOLEAN QueueEmpty; + LIST_ENTRY *Link; + DISK_IO2_TASK *Task; + + QueueEmpty = TRUE; + + EfiAcquireLock (&Instance->TaskQueueLock); + for (Link = GetFirstNode (&Instance->TaskQueue); !IsNull (&Instance->TaskQueue, Link); ) { + Task = CR (Link, DISK_IO2_TASK, Link, DISK_IO2_TASK_SIGNATURE); + if (IsListEmpty (&Task->Subtasks)) { + Link = RemoveEntryList (&Task->Link); + ASSERT (Task->Token == NULL); + FreePool (Task); + } else { + Link = GetNextNode (&Instance->TaskQueue, Link); + QueueEmpty = FALSE; + } + } + EfiReleaseLock (&Instance->TaskQueueLock); + + return QueueEmpty; +} + +/** + Common routine to access the disk. + + @param Instance Pointer to the DISK_IO_PRIVATE_DATA. + @param Write TRUE: Write operation; FALSE: Read operation. + @param MediaId ID of the medium to access. + @param Offset The starting byte offset on the logical block I/O device to access. + @param Token A pointer to the token associated with the transaction. + If this field is NULL, synchronous/blocking IO is performed. + @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device. + @param Buffer A pointer to the destination buffer for the data. + The caller is responsible either having implicit or explicit ownership of the buffer. +**/ +EFI_STATUS +DiskIo2ReadWriteDisk ( + IN DISK_IO_PRIVATE_DATA *Instance, + IN BOOLEAN Write, + IN UINT32 MediaId, + IN UINT64 Offset, + IN EFI_DISK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN UINT8 *Buffer + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + EFI_BLOCK_IO_MEDIA *Media; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + LIST_ENTRY Subtasks; + DISK_IO_SUBTASK *Subtask; + DISK_IO2_TASK *Task; + EFI_TPL OldTpl; + BOOLEAN Blocking; + BOOLEAN SubtaskBlocking; + LIST_ENTRY *SubtasksPtr; + + Task = NULL; + BlockIo = Instance->BlockIo; + BlockIo2 = Instance->BlockIo2; + Media = BlockIo->Media; + Status = EFI_SUCCESS; + Blocking = (BOOLEAN) ((Token == NULL) || (Token->Event == NULL)); + + if (Blocking) { + // + // Wait till pending async task is completed. + // + while (!DiskIo2RemoveCompletedTask (Instance)); + + SubtasksPtr = &Subtasks; + } else { + DiskIo2RemoveCompletedTask (Instance); + Task = AllocatePool (sizeof (DISK_IO2_TASK)); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + EfiAcquireLock (&Instance->TaskQueueLock); + InsertTailList (&Instance->TaskQueue, &Task->Link); + EfiReleaseLock (&Instance->TaskQueueLock); + + Task->Signature = DISK_IO2_TASK_SIGNATURE; + Task->Instance = Instance; + Task->Token = Token; + EfiInitializeLock (&Task->SubtasksLock, TPL_NOTIFY); + + SubtasksPtr = &Task->Subtasks; + } + + InitializeListHead (SubtasksPtr); + if (!DiskIoCreateSubtaskList (Instance, Write, Offset, BufferSize, Buffer, Blocking, Instance->SharedWorkingBuffer, SubtasksPtr)) { + if (Task != NULL) { + FreePool (Task); + } + return EFI_OUT_OF_RESOURCES; + } + ASSERT (!IsListEmpty (SubtasksPtr)); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + for ( Link = GetFirstNode (SubtasksPtr), NextLink = GetNextNode (SubtasksPtr, Link) + ; !IsNull (SubtasksPtr, Link) + ; Link = NextLink, NextLink = GetNextNode (SubtasksPtr, NextLink) + ) { + Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE); + Subtask->Task = Task; + SubtaskBlocking = Subtask->Blocking; + + ASSERT ((Subtask->Length % Media->BlockSize == 0) || (Subtask->Length < Media->BlockSize)); + + if (Subtask->Write) { + // + // Write + // + if (Subtask->WorkingBuffer != NULL) { + // + // A sub task before this one should be a block read operation, causing the WorkingBuffer filled with the entire one block data. + // + CopyMem (Subtask->WorkingBuffer + Subtask->Offset, Subtask->Buffer, Subtask->Length); + } + + if (SubtaskBlocking) { + Status = BlockIo->WriteBlocks ( + BlockIo, + MediaId, + Subtask->Lba, + (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, + (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer + ); + } else { + Status = BlockIo2->WriteBlocksEx ( + BlockIo2, + MediaId, + Subtask->Lba, + &Subtask->BlockIo2Token, + (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, + (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer + ); + } + + } else { + // + // Read + // + if (SubtaskBlocking) { + Status = BlockIo->ReadBlocks ( + BlockIo, + MediaId, + Subtask->Lba, + (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, + (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer + ); + if (!EFI_ERROR (Status) && (Subtask->WorkingBuffer != NULL)) { + CopyMem (Subtask->Buffer, Subtask->WorkingBuffer + Subtask->Offset, Subtask->Length); + } + } else { + Status = BlockIo2->ReadBlocksEx ( + BlockIo2, + MediaId, + Subtask->Lba, + &Subtask->BlockIo2Token, + (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, + (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer + ); + } + } + + if (SubtaskBlocking || EFI_ERROR (Status)) { + // + // Make sure the subtask list only contains non-blocking subtasks. + // Remove failed non-blocking subtasks as well because the callback won't be called. + // + DiskIoDestroySubtask (Instance, Subtask); + } + + if (EFI_ERROR (Status)) { + break; + } + } + + gBS->RaiseTPL (TPL_NOTIFY); + + // + // Remove all the remaining subtasks when failure. + // We shouldn't remove all the tasks because the non-blocking requests have been submitted and cannot be canceled. + // + if (EFI_ERROR (Status)) { + while (!IsNull (SubtasksPtr, NextLink)) { + Subtask = CR (NextLink, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE); + NextLink = DiskIoDestroySubtask (Instance, Subtask); + } + } + + // + // It's possible that the non-blocking subtasks finish before raising TPL to NOTIFY, + // so the subtasks list might be empty at this point. + // + if (!Blocking && IsListEmpty (SubtasksPtr)) { + EfiAcquireLock (&Instance->TaskQueueLock); + RemoveEntryList (&Task->Link); + EfiReleaseLock (&Instance->TaskQueueLock); + + if (!EFI_ERROR (Status) && (Task->Token != NULL)) { + // + // Task->Token should be set to NULL by the DiskIo2OnReadWriteComplete + // It it's not, that means the non-blocking request was downgraded to blocking request. + // + DEBUG ((EFI_D_VERBOSE, "DiskIo: Non-blocking request was downgraded to blocking request, signal event directly.\n")); + Task->Token->TransactionStatus = Status; + gBS->SignalEvent (Task->Token->Event); + } + + FreePool (Task); + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Reads a specified number of bytes from a device. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to be read. + @param Offset The starting byte offset on the logical block I/O device to read from. + @param Token A pointer to the token associated with the transaction. + If this field is NULL, synchronous/blocking IO is performed. + @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device. + @param Buffer A pointer to the destination buffer for the data. + The caller is responsible either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read correctly from the device. + If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. + Event will be signaled upon completion. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no medium in the device. + @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not valid for the device. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +DiskIo2ReadDiskEx ( + IN EFI_DISK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN OUT EFI_DISK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + return DiskIo2ReadWriteDisk ( + DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This), + FALSE, MediaId, Offset, Token, BufferSize, (UINT8 *) Buffer + ); +} + +/** + Writes a specified number of bytes to a device. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to be written. + @param Offset The starting byte offset on the logical block I/O device to write to. + @param Token A pointer to the token associated with the transaction. + If this field is NULL, synchronous/blocking IO is performed. + @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. + @param Buffer A pointer to the buffer containing the data to be written. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was written correctly to the device. + If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. + Event will be signaled upon completion. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation. + @retval EFI_NO_MEDIA There is no medium in the device. + @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium. + @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not valid for the device. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +DiskIo2WriteDiskEx ( + IN EFI_DISK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN OUT EFI_DISK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return DiskIo2ReadWriteDisk ( + DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This), + TRUE, MediaId, Offset, Token, BufferSize, (UINT8 *) Buffer + ); +} + +/** + The callback for the BlockIo2 FlushBlocksEx. + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which points to the DISK_IO2_FLUSH_TASK instance. +**/ +VOID +EFIAPI +DiskIo2OnFlushComplete ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + DISK_IO2_FLUSH_TASK *Task; + + gBS->CloseEvent (Event); + + Task = (DISK_IO2_FLUSH_TASK *) Context; + ASSERT (Task->Signature == DISK_IO2_FLUSH_TASK_SIGNATURE); + Task->Token->TransactionStatus = Task->BlockIo2Token.TransactionStatus; + gBS->SignalEvent (Task->Token->Event); + + FreePool (Task); +} + +/** + Flushes all modified data to the physical device. + + @param This Indicates a pointer to the calling context. + @param Token A pointer to the token associated with the transaction. + If this field is NULL, synchronous/blocking IO is performed. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was flushed successfully to the device. + If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. + Event will be signaled upon completion. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation. + @retval EFI_NO_MEDIA There is no medium in the device. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. +**/ +EFI_STATUS +EFIAPI +DiskIo2FlushDiskEx ( + IN EFI_DISK_IO2_PROTOCOL *This, + IN OUT EFI_DISK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + DISK_IO2_FLUSH_TASK *Task; + DISK_IO_PRIVATE_DATA *Private; + + Private = DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This); + + if ((Token != NULL) && (Token->Event != NULL)) { + Task = AllocatePool (sizeof (DISK_IO2_FLUSH_TASK)); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + DiskIo2OnFlushComplete, + Task, + &Task->BlockIo2Token.Event + ); + if (EFI_ERROR (Status)) { + FreePool (Task); + return Status; + } + Task->Signature = DISK_IO2_FLUSH_TASK_SIGNATURE; + Task->Token = Token; + Status = Private->BlockIo2->FlushBlocksEx (Private->BlockIo2, &Task->BlockIo2Token); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Task->BlockIo2Token.Event); + FreePool (Task); + } + } else { + Status = Private->BlockIo2->FlushBlocksEx (Private->BlockIo2, NULL); + } + + return Status; +} + +/** + Read BufferSize bytes from Offset into Buffer. + Reads may support reads that are not aligned on + sector boundaries. There are three cases: + UnderRun - The first byte is not on a sector boundary or the read request is + less than a sector in length. + Aligned - A read of N contiguous sectors. + OverRun - The last byte is not on a sector boundary. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Offset The starting byte offset to read from + @param BufferSize Size of Buffer + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +DiskIoReadDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + return DiskIo2ReadWriteDisk ( + DISK_IO_PRIVATE_DATA_FROM_DISK_IO (This), + FALSE, MediaId, Offset, NULL, BufferSize, (UINT8 *) Buffer + ); +} + + +/** + Writes BufferSize bytes from Buffer into Offset. + Writes may require a read modify write to support writes that are not + aligned on sector boundaries. There are three cases: + UnderRun - The first byte is not on a sector boundary or the write request + is less than a sector in length. Read modify write is required. + Aligned - A write of N contiguous sectors. + OverRun - The last byte is not on a sector boundary. Read modified write + required. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Offset The starting byte offset to read from + @param BufferSize Size of Buffer + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +DiskIoWriteDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return DiskIo2ReadWriteDisk ( + DISK_IO_PRIVATE_DATA_FROM_DISK_IO (This), + TRUE, MediaId, Offset, NULL, BufferSize, (UINT8 *) Buffer + ); +} + +/** + The user Entry Point for module DiskIo. The user code starts with this 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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeDiskIo ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDiskIoDriverBinding, + ImageHandle, + &gDiskIoComponentName, + &gDiskIoComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.h b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.h new file mode 100644 index 000000000..3207b9fa2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.h @@ -0,0 +1,467 @@ +/** @file + Master header file for DiskIo driver. It includes the module private defininitions. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DISK_IO_H_ +#define _DISK_IO_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DISK_IO_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('d', 's', 'k', 'I') +typedef struct { + UINT32 Signature; + + EFI_DISK_IO_PROTOCOL DiskIo; + EFI_DISK_IO2_PROTOCOL DiskIo2; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + + UINT8 *SharedWorkingBuffer; + + EFI_LOCK TaskQueueLock; + LIST_ENTRY TaskQueue; +} DISK_IO_PRIVATE_DATA; +#define DISK_IO_PRIVATE_DATA_FROM_DISK_IO(a) CR (a, DISK_IO_PRIVATE_DATA, DiskIo, DISK_IO_PRIVATE_DATA_SIGNATURE) +#define DISK_IO_PRIVATE_DATA_FROM_DISK_IO2(a) CR (a, DISK_IO_PRIVATE_DATA, DiskIo2, DISK_IO_PRIVATE_DATA_SIGNATURE) + +#define DISK_IO2_TASK_SIGNATURE SIGNATURE_32 ('d', 'i', 'a', 't') +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; /// < link to other task + EFI_LOCK SubtasksLock; + LIST_ENTRY Subtasks; /// < header of subtasks + EFI_DISK_IO2_TOKEN *Token; + DISK_IO_PRIVATE_DATA *Instance; +} DISK_IO2_TASK; + +#define DISK_IO2_FLUSH_TASK_SIGNATURE SIGNATURE_32 ('d', 'i', 'f', 't') +typedef struct { + UINT32 Signature; + EFI_BLOCK_IO2_TOKEN BlockIo2Token; + EFI_DISK_IO2_TOKEN *Token; +} DISK_IO2_FLUSH_TASK; + +#define DISK_IO_SUBTASK_SIGNATURE SIGNATURE_32 ('d', 'i', 's', 't') +typedef struct { + // + // UnderRun: Offset != 0, Length < BlockSize + // OverRun: Offset == 0, Length < BlockSize + // Middle: Offset is block aligned, Length is multiple of block size + // + UINT32 Signature; + LIST_ENTRY Link; + BOOLEAN Write; + UINT64 Lba; + UINT32 Offset; + UINTN Length; + UINT8 *WorkingBuffer; /// < NULL indicates using "Buffer" directly + UINT8 *Buffer; + BOOLEAN Blocking; + + // + // Following fields are for DiskIo2 + // + DISK_IO2_TASK *Task; + EFI_BLOCK_IO2_TOKEN BlockIo2Token; +} DISK_IO_SUBTASK; + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gDiskIoDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gDiskIoComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gDiskIoComponentName2; + +// +// Prototypes +// Driver model protocol interface +// +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +DiskIoDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle by opening a Block IO protocol and + installing a Disk IO protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +DiskIoDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle by removing Disk IO protocol and closing + the Block IO protocol on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +DiskIoDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Disk I/O Protocol Interface +// +/** + Read BufferSize bytes from Offset into Buffer. + Reads may support reads that are not aligned on + sector boundaries. There are three cases: + UnderRun - The first byte is not on a sector boundary or the read request is + less than a sector in length. + Aligned - A read of N contiguous sectors. + OverRun - The last byte is not on a sector boundary. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Offset The starting byte offset to read from + @param BufferSize Size of Buffer + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +DiskIoReadDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Writes BufferSize bytes from Buffer into Offset. + Writes may require a read modify write to support writes that are not + aligned on sector boundaries. There are three cases: + UnderRun - The first byte is not on a sector boundary or the write request + is less than a sector in length. Read modify write is required. + Aligned - A write of N contiguous sectors. + OverRun - The last byte is not on a sector boundary. Read modified write + required. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Offset The starting byte offset to read from + @param BufferSize Size of Buffer + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +DiskIoWriteDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN UINTN BufferSize, + IN VOID *Buffer + ); + + +/** + Terminate outstanding asynchronous requests to a device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding requests were successfully terminated. + @retval EFI_DEVICE_ERROR The device reported an error while performing the cancel + operation. +**/ +EFI_STATUS +EFIAPI +DiskIo2Cancel ( + IN EFI_DISK_IO2_PROTOCOL *This + ); + +/** + Reads a specified number of bytes from a device. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to be read. + @param Offset The starting byte offset on the logical block I/O device to read from. + @param Token A pointer to the token associated with the transaction. + If this field is NULL, synchronous/blocking IO is performed. + @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device. + @param Buffer A pointer to the destination buffer for the data. + The caller is responsible either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read correctly from the device. + If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. + Event will be signaled upon completion. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no medium in the device. + @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not valid for the device. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +DiskIo2ReadDiskEx ( + IN EFI_DISK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN OUT EFI_DISK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Writes a specified number of bytes to a device. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to be written. + @param Offset The starting byte offset on the logical block I/O device to write to. + @param Token A pointer to the token associated with the transaction. + If this field is NULL, synchronous/blocking IO is performed. + @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. + @param Buffer A pointer to the buffer containing the data to be written. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was written correctly to the device. + If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. + Event will be signaled upon completion. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation. + @retval EFI_NO_MEDIA There is no medium in the device. + @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium. + @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not valid for the device. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +DiskIo2WriteDiskEx ( + IN EFI_DISK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN EFI_DISK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flushes all modified data to the physical device. + + @param This Indicates a pointer to the calling context. + @param Token A pointer to the token associated with the transaction. + If this field is NULL, synchronous/blocking IO is performed. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was flushed successfully to the device. + If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. + Event will be signaled upon completion. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation. + @retval EFI_NO_MEDIA There is no medium in the device. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. +**/ +EFI_STATUS +EFIAPI +DiskIo2FlushDiskEx ( + IN EFI_DISK_IO2_PROTOCOL *This, + IN OUT EFI_DISK_IO2_TOKEN *Token + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DiskIoComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DiskIoComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf new file mode 100644 index 000000000..9ea371b3b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf @@ -0,0 +1,65 @@ +## @file +# Module that lays Disk I/O protocol on every Block I/O protocol. +# +# This module produces Disk I/O protocol to abstract the block accesses +# of the Block I/O protocol to a more general offset-length protocol +# to provide byte-oriented access to block media. It adds this protocol +# to any Block I/O interface that appears in the system that does not +# already have a Disk I/O protocol. File systems and other disk access +# code utilize the Disk I/O protocol. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DiskIoDxe + MODULE_UNI_FILE = DiskIoDxe.uni + FILE_GUID = 6B38F7B4-AD98-40e9-9093-ACA2B5A253C4 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeDiskIo + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gDiskIoDriverBinding +# COMPONENT_NAME = gDiskIoComponentName +# COMPONENT_NAME2 = gDiskIoComponentName2 +# + +[Sources] + ComponentName.c + DiskIo.h + DiskIo.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + +[Protocols] + gEfiDiskIoProtocolGuid ## BY_START + gEfiDiskIo2ProtocolGuid ## BY_START + gEfiBlockIoProtocolGuid ## TO_START + gEfiBlockIo2ProtocolGuid ## TO_START + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdDiskIoDataBufferBlockNum ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + DiskIoDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.uni b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.uni new file mode 100644 index 000000000..3bdf78d44 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.uni @@ -0,0 +1,21 @@ +// /** @file +// Module that lays Disk I/O protocol on every Block I/O protocol. +// +// This module produces Disk I/O protocol to abstract the block accesses +// of the Block I/O protocol to a more general offset-length protocol +// to provide byte-oriented access to block media. It adds this protocol +// to any Block I/O interface that appears in the system that does not +// already have a Disk I/O protocol. File systems and other disk access +// code utilize the Disk I/O protocol. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Lays Disk I/O protocol on every Block I/O protocol" + +#string STR_MODULE_DESCRIPTION #language en-US "This module produces Disk I/O protocol to abstract the block accesses of the Block I/O protocol to a more general offset-length protocol to provide byte-oriented access to block media. It adds this protocol to any Block I/O interface that appears in the system that does not already have a Disk I/O protocol. File systems and other disk access code utilize the Disk I/O protocol." + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxeExtra.uni new file mode 100644 index 000000000..a63822cc6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// DiskIoDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Disk I/O DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ComponentName.c new file mode 100644 index 000000000..7d1031f35 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ComponentName.c @@ -0,0 +1,182 @@ +/** @file + UEFI Component Name protocol for Partition driver. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Partition.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPartitionComponentName = { + PartitionComponentNameGetDriverName, + PartitionComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPartitionComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PartitionComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PartitionComponentNameGetControllerName, + "en" +}; + +// +// Driver name table for Partition module. +// It is shared by the implementation of ComponentName & ComponentName2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPartitionDriverNameTable[] = { + { + "eng;en", + L"Partition Driver(MBR/GPT/El Torito)" + }, + { + NULL, + NULL + } +}; + + + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PartitionComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mPartitionDriverNameTable, + DriverName, + (BOOLEAN)(This == &gPartitionComponentName) + ); +} + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PartitionComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ElTorito.c b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ElTorito.c new file mode 100644 index 000000000..3d2ff3bc2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/ElTorito.c @@ -0,0 +1,275 @@ +/** @file + Decode an El Torito formatted CD-ROM + +Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Partition.h" + + +/** + Install child handles if the Handle supports El Torito format. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path + + + @retval EFI_SUCCESS Child handle(s) was added. + @retval EFI_MEDIA_CHANGED Media changed Detected. + @retval other no child handle was added. + +**/ +EFI_STATUS +PartitionInstallElToritoChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + UINT64 VolDescriptorOffset; + UINT32 Lba2KB; + EFI_BLOCK_IO_MEDIA *Media; + CDROM_VOLUME_DESCRIPTOR *VolDescriptor; + ELTORITO_CATALOG *Catalog; + UINTN Check; + UINTN Index; + UINTN BootEntry; + UINTN MaxIndex; + UINT16 *CheckBuffer; + CDROM_DEVICE_PATH CdDev; + UINT32 SubBlockSize; + UINT32 SectorCount; + EFI_STATUS Found; + UINT32 VolSpaceSize; + EFI_PARTITION_INFO_PROTOCOL PartitionInfo; + + Found = EFI_NOT_FOUND; + Media = BlockIo->Media; + + VolSpaceSize = 0; + + // + // CD_ROM has the fixed block size as 2048 bytes (SIZE_2KB) + // + + // If the ISO image has been copied onto a different storage media + // then the block size might be different (eg: USB). + // Ensure 2048 (SIZE_2KB) is a multiple of block size + if (((SIZE_2KB % Media->BlockSize) != 0) || (Media->BlockSize > SIZE_2KB)) { + return EFI_NOT_FOUND; + } + + VolDescriptor = AllocatePool ((UINTN)SIZE_2KB); + + if (VolDescriptor == NULL) { + return EFI_NOT_FOUND; + } + + Catalog = (ELTORITO_CATALOG *) VolDescriptor; + + // + // Loop: handle one volume descriptor per time + // The ISO-9660 volume descriptor starts at 32k on the media + // + for (VolDescriptorOffset = SIZE_32KB; + VolDescriptorOffset <= MultU64x32 (Media->LastBlock, Media->BlockSize); + VolDescriptorOffset += SIZE_2KB) { + Status = DiskIo->ReadDisk ( + DiskIo, + Media->MediaId, + VolDescriptorOffset, + SIZE_2KB, + VolDescriptor + ); + if (EFI_ERROR (Status)) { + Found = Status; + break; + } + // + // Check for valid volume descriptor signature + // + if (VolDescriptor->Unknown.Type == CDVOL_TYPE_END || + CompareMem (VolDescriptor->Unknown.Id, CDVOL_ID, sizeof (VolDescriptor->Unknown.Id)) != 0 + ) { + // + // end of Volume descriptor list + // + break; + } + // + // Read the Volume Space Size from Primary Volume Descriptor 81-88 byte, + // the 32-bit numerical values is stored in Both-byte orders + // + if (VolDescriptor->PrimaryVolume.Type == CDVOL_TYPE_CODED) { + VolSpaceSize = VolDescriptor->PrimaryVolume.VolSpaceSize[0]; + } + // + // Is it an El Torito volume descriptor? + // + if (CompareMem (VolDescriptor->BootRecordVolume.SystemId, CDVOL_ELTORITO_ID, sizeof (CDVOL_ELTORITO_ID) - 1) != 0) { + continue; + } + // + // Read in the boot El Torito boot catalog + // The LBA unit used by El Torito boot catalog is 2KB unit + // + Lba2KB = UNPACK_INT32 (VolDescriptor->BootRecordVolume.EltCatalog); + // Ensure the LBA (in 2KB unit) fits into our media + if (Lba2KB * (SIZE_2KB / Media->BlockSize) > Media->LastBlock) { + continue; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + Media->MediaId, + MultU64x32 (Lba2KB, SIZE_2KB), + SIZE_2KB, + Catalog + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EltCheckDevice: error reading catalog %r\n", Status)); + continue; + } + // + // We don't care too much about the Catalog header's contents, but we do want + // to make sure it looks like a Catalog header + // + if (Catalog->Catalog.Indicator != ELTORITO_ID_CATALOG || Catalog->Catalog.Id55AA != 0xAA55) { + DEBUG ((EFI_D_ERROR, "EltCheckBootCatalog: El Torito boot catalog header IDs not correct\n")); + continue; + } + + Check = 0; + CheckBuffer = (UINT16 *) Catalog; + for (Index = 0; Index < sizeof (ELTORITO_CATALOG) / sizeof (UINT16); Index += 1) { + Check += CheckBuffer[Index]; + } + + if ((Check & 0xFFFF) != 0) { + DEBUG ((EFI_D_ERROR, "EltCheckBootCatalog: El Torito boot catalog header checksum failed\n")); + continue; + } + + MaxIndex = Media->BlockSize / sizeof (ELTORITO_CATALOG); + for (Index = 1, BootEntry = 1; Index < MaxIndex; Index += 1) { + // + // Next entry + // + Catalog += 1; + + // + // Check this entry + // + if (Catalog->Boot.Indicator != ELTORITO_ID_SECTION_BOOTABLE || Catalog->Boot.Lba == 0) { + continue; + } + + SubBlockSize = 512; + SectorCount = Catalog->Boot.SectorCount; + + switch (Catalog->Boot.MediaType) { + + case ELTORITO_NO_EMULATION: + SubBlockSize = Media->BlockSize; + break; + + case ELTORITO_HARD_DISK: + break; + + case ELTORITO_12_DISKETTE: + SectorCount = 0x50 * 0x02 * 0x0F; + break; + + case ELTORITO_14_DISKETTE: + SectorCount = 0x50 * 0x02 * 0x12; + break; + + case ELTORITO_28_DISKETTE: + SectorCount = 0x50 * 0x02 * 0x24; + break; + + default: + DEBUG ((EFI_D_INIT, "EltCheckDevice: unsupported El Torito boot media type %x\n", Catalog->Boot.MediaType)); + SectorCount = 0; + SubBlockSize = Media->BlockSize; + break; + } + // + // Create child device handle + // + CdDev.Header.Type = MEDIA_DEVICE_PATH; + CdDev.Header.SubType = MEDIA_CDROM_DP; + SetDevicePathNodeLength (&CdDev.Header, sizeof (CdDev)); + + if (Index == 1) { + // + // This is the initial/default entry + // + BootEntry = 0; + } + + CdDev.BootEntry = (UINT32) BootEntry; + BootEntry++; + CdDev.PartitionStart = Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize); + if (SectorCount < 2) { + // + // When the SectorCount < 2, set the Partition as the whole CD. + // + if (VolSpaceSize * (SIZE_2KB / Media->BlockSize) > (Media->LastBlock + 1)) { + CdDev.PartitionSize = (UINT32)(Media->LastBlock - Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize) + 1); + } else { + CdDev.PartitionSize = (UINT32)(VolSpaceSize - Catalog->Boot.Lba) * (SIZE_2KB / Media->BlockSize); + } + } else { + CdDev.PartitionSize = DivU64x32 ( + MultU64x32 ( + SectorCount * (SIZE_2KB / Media->BlockSize), + SubBlockSize + ) + Media->BlockSize - 1, + Media->BlockSize + ); + } + + ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; + PartitionInfo.Type = PARTITION_TYPE_OTHER; + + Status = PartitionInstallChildHandle ( + This, + Handle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &CdDev, + &PartitionInfo, + Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize), + Catalog->Boot.Lba * (SIZE_2KB / Media->BlockSize) + CdDev.PartitionSize - 1, + SubBlockSize, + NULL + ); + if (!EFI_ERROR (Status)) { + Found = EFI_SUCCESS; + } + } + } + + FreePool (VolDescriptor); + + return Found; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c new file mode 100644 index 000000000..aefb2d6ec --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Gpt.c @@ -0,0 +1,883 @@ +/** @file + Decode a hard disk partitioned with the GPT scheme in the UEFI 2.0 + specification. + + Caution: This file requires additional review when modified. + This driver will have external input - disk partition. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + PartitionInstallGptChildHandles() routine will read disk partition content and + do basic validation before PartitionInstallChildHandle(). + + PartitionValidGptTable(), PartitionCheckGptEntry() routine will accept disk + partition content and validate the GPT table and GPT entry. + +Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Partition.h" + +/** + Install child handles if the Handle supports GPT partition structure. + + Caution: This function may receive untrusted input. + The GPT partition table header is external input, so this routine + will do basic validation for GPT partition table header before return. + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io protocol. + @param[in] Lba The starting Lba of the Partition Table + @param[out] PartHeader Stores the partition table that is read + + @retval TRUE The partition table is valid + @retval FALSE The partition table is not valid + +**/ +BOOLEAN +PartitionValidGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_LBA Lba, + OUT EFI_PARTITION_TABLE_HEADER *PartHeader + ); + +/** + Check if the CRC field in the Partition table header is valid + for Partition entry array. + + @param[in] BlockIo Parent BlockIo interface + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +BOOLEAN +PartitionCheckGptEntryArrayCRC ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ); + + +/** + Restore Partition Table to its alternate place + (Primary -> Backup or Backup -> Primary). + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure. + + @retval TRUE Restoring succeeds + @retval FALSE Restoring failed + +**/ +BOOLEAN +PartitionRestoreGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ); + + +/** + This routine will check GPT partition entry and return entry status. + + Caution: This function may receive untrusted input. + The GPT partition entry is external input, so this routine + will do basic validation for GPT partition entry and report status. + + @param[in] PartHeader Partition table header structure + @param[in] PartEntry The partition entry array + @param[out] PEntryStatus the partition entry status array + recording the status of each partition + +**/ +VOID +PartitionCheckGptEntry ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader, + IN EFI_PARTITION_ENTRY *PartEntry, + OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus + ); + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Size The size of the table + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +BOOLEAN +PartitionCheckCrcAltSize ( + IN UINTN MaxSize, + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ); + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +BOOLEAN +PartitionCheckCrc ( + IN UINTN MaxSize, + IN OUT EFI_TABLE_HEADER *Hdr + ); + + +/** + Updates the CRC32 value in the table header. + + @param Size The size of the table + @param Hdr Table to update + +**/ +VOID +PartitionSetCrcAltSize ( + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ); + + +/** + Updates the CRC32 value in the table header. + + @param Hdr Table to update + +**/ +VOID +PartitionSetCrc ( + IN OUT EFI_TABLE_HEADER *Hdr + ); + +/** + Install child handles if the Handle supports GPT partition structure. + + Caution: This function may receive untrusted input. + The GPT partition table is external input, so this routine + will do basic validation for GPT partition table before install + child handle for each GPT partition. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path. + + @retval EFI_SUCCESS Valid GPT disk. + @retval EFI_MEDIA_CHANGED Media changed Detected. + @retval other Not a valid GPT disk. + +**/ +EFI_STATUS +PartitionInstallGptChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + EFI_LBA LastBlock; + MASTER_BOOT_RECORD *ProtectiveMbr; + EFI_PARTITION_TABLE_HEADER *PrimaryHeader; + EFI_PARTITION_TABLE_HEADER *BackupHeader; + EFI_PARTITION_ENTRY *PartEntry; + EFI_PARTITION_ENTRY *Entry; + EFI_PARTITION_ENTRY_STATUS *PEntryStatus; + UINTN Index; + EFI_STATUS GptValidStatus; + HARDDRIVE_DEVICE_PATH HdDev; + UINT32 MediaId; + EFI_PARTITION_INFO_PROTOCOL PartitionInfo; + + ProtectiveMbr = NULL; + PrimaryHeader = NULL; + BackupHeader = NULL; + PartEntry = NULL; + PEntryStatus = NULL; + + BlockSize = BlockIo->Media->BlockSize; + LastBlock = BlockIo->Media->LastBlock; + MediaId = BlockIo->Media->MediaId; + + DEBUG ((EFI_D_INFO, " BlockSize : %d \n", BlockSize)); + DEBUG ((EFI_D_INFO, " LastBlock : %lx \n", LastBlock)); + + GptValidStatus = EFI_NOT_FOUND; + + // + // Ensure the block size can hold the MBR + // + if (BlockSize < sizeof (MASTER_BOOT_RECORD)) { + return EFI_NOT_FOUND; + } + + // + // Allocate a buffer for the Protective MBR + // + ProtectiveMbr = AllocatePool (BlockSize); + if (ProtectiveMbr == NULL) { + return EFI_NOT_FOUND; + } + + // + // Read the Protective MBR from LBA #0 + // + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + 0, + BlockSize, + ProtectiveMbr + ); + if (EFI_ERROR (Status)) { + GptValidStatus = Status; + goto Done; + } + + // + // Verify that the Protective MBR is valid + // + for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) { + if (ProtectiveMbr->Partition[Index].BootIndicator == 0x00 && + ProtectiveMbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION && + UNPACK_UINT32 (ProtectiveMbr->Partition[Index].StartingLBA) == 1 + ) { + break; + } + } + if (Index == MAX_MBR_PARTITIONS) { + goto Done; + } + + // + // Allocate the GPT structures + // + PrimaryHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER)); + if (PrimaryHeader == NULL) { + goto Done; + } + + BackupHeader = AllocateZeroPool (sizeof (EFI_PARTITION_TABLE_HEADER)); + if (BackupHeader == NULL) { + goto Done; + } + + // + // Check primary and backup partition tables + // + if (!PartitionValidGptTable (BlockIo, DiskIo, PRIMARY_PART_HEADER_LBA, PrimaryHeader)) { + DEBUG ((EFI_D_INFO, " Not Valid primary partition table\n")); + + if (!PartitionValidGptTable (BlockIo, DiskIo, LastBlock, BackupHeader)) { + DEBUG ((EFI_D_INFO, " Not Valid backup partition table\n")); + goto Done; + } else { + DEBUG ((EFI_D_INFO, " Valid backup partition table\n")); + DEBUG ((EFI_D_INFO, " Restore primary partition table by the backup\n")); + if (!PartitionRestoreGptTable (BlockIo, DiskIo, BackupHeader)) { + DEBUG ((EFI_D_INFO, " Restore primary partition table error\n")); + } + + if (PartitionValidGptTable (BlockIo, DiskIo, BackupHeader->AlternateLBA, PrimaryHeader)) { + DEBUG ((EFI_D_INFO, " Restore backup partition table success\n")); + } + } + } else if (!PartitionValidGptTable (BlockIo, DiskIo, PrimaryHeader->AlternateLBA, BackupHeader)) { + DEBUG ((EFI_D_INFO, " Valid primary and !Valid backup partition table\n")); + DEBUG ((EFI_D_INFO, " Restore backup partition table by the primary\n")); + if (!PartitionRestoreGptTable (BlockIo, DiskIo, PrimaryHeader)) { + DEBUG ((EFI_D_INFO, " Restore backup partition table error\n")); + } + + if (PartitionValidGptTable (BlockIo, DiskIo, PrimaryHeader->AlternateLBA, BackupHeader)) { + DEBUG ((EFI_D_INFO, " Restore backup partition table success\n")); + } + + } + + DEBUG ((EFI_D_INFO, " Valid primary and Valid backup partition table\n")); + + // + // Read the EFI Partition Entries + // + PartEntry = AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry); + if (PartEntry == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + goto Done; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32(PrimaryHeader->PartitionEntryLBA, BlockSize), + PrimaryHeader->NumberOfPartitionEntries * (PrimaryHeader->SizeOfPartitionEntry), + PartEntry + ); + if (EFI_ERROR (Status)) { + GptValidStatus = Status; + DEBUG ((EFI_D_ERROR, " Partition Entry ReadDisk error\n")); + goto Done; + } + + DEBUG ((EFI_D_INFO, " Partition entries read block success\n")); + + DEBUG ((EFI_D_INFO, " Number of partition entries: %d\n", PrimaryHeader->NumberOfPartitionEntries)); + + PEntryStatus = AllocateZeroPool (PrimaryHeader->NumberOfPartitionEntries * sizeof (EFI_PARTITION_ENTRY_STATUS)); + if (PEntryStatus == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + goto Done; + } + + // + // Check the integrity of partition entries + // + PartitionCheckGptEntry (PrimaryHeader, PartEntry, PEntryStatus); + + // + // If we got this far the GPT layout of the disk is valid and we should return true + // + GptValidStatus = EFI_SUCCESS; + + // + // Create child device handles + // + for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) { + Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartEntry + Index * PrimaryHeader->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid) || + PEntryStatus[Index].OutOfRange || + PEntryStatus[Index].Overlap || + PEntryStatus[Index].OsSpecific + ) { + // + // Don't use null EFI Partition Entries, Invalid Partition Entries or OS specific + // partition Entries + // + continue; + } + + ZeroMem (&HdDev, sizeof (HdDev)); + HdDev.Header.Type = MEDIA_DEVICE_PATH; + HdDev.Header.SubType = MEDIA_HARDDRIVE_DP; + SetDevicePathNodeLength (&HdDev.Header, sizeof (HdDev)); + + HdDev.PartitionNumber = (UINT32) Index + 1; + HdDev.MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + HdDev.SignatureType = SIGNATURE_TYPE_GUID; + HdDev.PartitionStart = Entry->StartingLBA; + HdDev.PartitionSize = Entry->EndingLBA - Entry->StartingLBA + 1; + CopyMem (HdDev.Signature, &Entry->UniquePartitionGUID, sizeof (EFI_GUID)); + + ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; + PartitionInfo.Type = PARTITION_TYPE_GPT; + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeSystemPartGuid)) { + PartitionInfo.System = 1; + } + CopyMem (&PartitionInfo.Info.Gpt, Entry, sizeof (EFI_PARTITION_ENTRY)); + + DEBUG ((EFI_D_INFO, " Index : %d\n", (UINT32) Index)); + DEBUG ((EFI_D_INFO, " Start LBA : %lx\n", (UINT64) HdDev.PartitionStart)); + DEBUG ((EFI_D_INFO, " End LBA : %lx\n", (UINT64) Entry->EndingLBA)); + DEBUG ((EFI_D_INFO, " Partition size: %lx\n", (UINT64) HdDev.PartitionSize)); + DEBUG ((EFI_D_INFO, " Start : %lx", MultU64x32 (Entry->StartingLBA, BlockSize))); + DEBUG ((EFI_D_INFO, " End : %lx\n", MultU64x32 (Entry->EndingLBA, BlockSize))); + + Status = PartitionInstallChildHandle ( + This, + Handle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &HdDev, + &PartitionInfo, + Entry->StartingLBA, + Entry->EndingLBA, + BlockSize, + &Entry->PartitionTypeGUID + ); + } + + DEBUG ((EFI_D_INFO, "Prepare to Free Pool\n")); + +Done: + if (ProtectiveMbr != NULL) { + FreePool (ProtectiveMbr); + } + if (PrimaryHeader != NULL) { + FreePool (PrimaryHeader); + } + if (BackupHeader != NULL) { + FreePool (BackupHeader); + } + if (PartEntry != NULL) { + FreePool (PartEntry); + } + if (PEntryStatus != NULL) { + FreePool (PEntryStatus); + } + + return GptValidStatus; +} + +/** + This routine will read GPT partition table header and return it. + + Caution: This function may receive untrusted input. + The GPT partition table header is external input, so this routine + will do basic validation for GPT partition table header before return. + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io protocol. + @param[in] Lba The starting Lba of the Partition Table + @param[out] PartHeader Stores the partition table that is read + + @retval TRUE The partition table is valid + @retval FALSE The partition table is not valid + +**/ +BOOLEAN +PartitionValidGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_LBA Lba, + OUT EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + EFI_PARTITION_TABLE_HEADER *PartHdr; + UINT32 MediaId; + + BlockSize = BlockIo->Media->BlockSize; + MediaId = BlockIo->Media->MediaId; + PartHdr = AllocateZeroPool (BlockSize); + + if (PartHdr == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + return FALSE; + } + // + // Read the EFI Partition Table Header + // + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32 (Lba, BlockSize), + BlockSize, + PartHdr + ); + if (EFI_ERROR (Status)) { + FreePool (PartHdr); + return FALSE; + } + + if ((PartHdr->Header.Signature != EFI_PTAB_HEADER_ID) || + !PartitionCheckCrc (BlockSize, &PartHdr->Header) || + PartHdr->MyLBA != Lba || + (PartHdr->SizeOfPartitionEntry < sizeof (EFI_PARTITION_ENTRY)) + ) { + DEBUG ((EFI_D_INFO, "Invalid efi partition table header\n")); + FreePool (PartHdr); + return FALSE; + } + + // + // Ensure the NumberOfPartitionEntries * SizeOfPartitionEntry doesn't overflow. + // + if (PartHdr->NumberOfPartitionEntries > DivU64x32 (MAX_UINTN, PartHdr->SizeOfPartitionEntry)) { + FreePool (PartHdr); + return FALSE; + } + + CopyMem (PartHeader, PartHdr, sizeof (EFI_PARTITION_TABLE_HEADER)); + if (!PartitionCheckGptEntryArrayCRC (BlockIo, DiskIo, PartHeader)) { + FreePool (PartHdr); + return FALSE; + } + + DEBUG ((EFI_D_INFO, " Valid efi partition table header\n")); + FreePool (PartHdr); + return TRUE; +} + +/** + Check if the CRC field in the Partition table header is valid + for Partition entry array. + + @param[in] BlockIo Parent BlockIo interface + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure + + @retval TRUE the CRC is valid + @retval FALSE the CRC is invalid + +**/ +BOOLEAN +PartitionCheckGptEntryArrayCRC ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINT32 Crc; + UINTN Size; + + // + // Read the EFI Partition Entries + // + Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); + if (Ptr == NULL) { + DEBUG ((EFI_D_ERROR, " Allocate pool error\n")); + return FALSE; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32(PartHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (Ptr); + return FALSE; + } + + Size = PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry; + + Status = gBS->CalculateCrc32 (Ptr, Size, &Crc); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "CheckPEntryArrayCRC: Crc calculation failed\n")); + FreePool (Ptr); + return FALSE; + } + + FreePool (Ptr); + + return (BOOLEAN) (PartHeader->PartitionEntryArrayCRC32 == Crc); +} + + +/** + Restore Partition Table to its alternate place + (Primary -> Backup or Backup -> Primary). + + @param[in] BlockIo Parent BlockIo interface. + @param[in] DiskIo Disk Io Protocol. + @param[in] PartHeader Partition table header structure. + + @retval TRUE Restoring succeeds + @retval FALSE Restoring failed + +**/ +BOOLEAN +PartitionRestoreGptTable ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_PARTITION_TABLE_HEADER *PartHeader + ) +{ + EFI_STATUS Status; + UINTN BlockSize; + EFI_PARTITION_TABLE_HEADER *PartHdr; + EFI_LBA PEntryLBA; + UINT8 *Ptr; + UINT32 MediaId; + + PartHdr = NULL; + Ptr = NULL; + + BlockSize = BlockIo->Media->BlockSize; + MediaId = BlockIo->Media->MediaId; + + PartHdr = AllocateZeroPool (BlockSize); + + if (PartHdr == NULL) { + DEBUG ((EFI_D_ERROR, "Allocate pool error\n")); + return FALSE; + } + + PEntryLBA = (PartHeader->MyLBA == PRIMARY_PART_HEADER_LBA) ? \ + (PartHeader->LastUsableLBA + 1) : \ + (PRIMARY_PART_HEADER_LBA + 1); + + CopyMem (PartHdr, PartHeader, sizeof (EFI_PARTITION_TABLE_HEADER)); + + PartHdr->MyLBA = PartHeader->AlternateLBA; + PartHdr->AlternateLBA = PartHeader->MyLBA; + PartHdr->PartitionEntryLBA = PEntryLBA; + PartitionSetCrc ((EFI_TABLE_HEADER *) PartHdr); + + Status = DiskIo->WriteDisk ( + DiskIo, + MediaId, + MultU64x32 (PartHdr->MyLBA, (UINT32) BlockSize), + BlockSize, + PartHdr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Ptr = AllocatePool (PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry); + if (Ptr == NULL) { + DEBUG ((EFI_D_ERROR, " Allocate pool error\n")); + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32(PartHeader->PartitionEntryLBA, (UINT32) BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = DiskIo->WriteDisk ( + DiskIo, + MediaId, + MultU64x32(PEntryLBA, (UINT32) BlockSize), + PartHeader->NumberOfPartitionEntries * PartHeader->SizeOfPartitionEntry, + Ptr + ); + +Done: + FreePool (PartHdr); + + if (Ptr != NULL) { + FreePool (Ptr); + } + + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + This routine will check GPT partition entry and return entry status. + + Caution: This function may receive untrusted input. + The GPT partition entry is external input, so this routine + will do basic validation for GPT partition entry and report status. + + @param[in] PartHeader Partition table header structure + @param[in] PartEntry The partition entry array + @param[out] PEntryStatus the partition entry status array + recording the status of each partition + +**/ +VOID +PartitionCheckGptEntry ( + IN EFI_PARTITION_TABLE_HEADER *PartHeader, + IN EFI_PARTITION_ENTRY *PartEntry, + OUT EFI_PARTITION_ENTRY_STATUS *PEntryStatus + ) +{ + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + EFI_PARTITION_ENTRY *Entry; + UINTN Index1; + UINTN Index2; + + DEBUG ((EFI_D_INFO, " start check partition entries\n")); + for (Index1 = 0; Index1 < PartHeader->NumberOfPartitionEntries; Index1++) { + Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartEntry + Index1 * PartHeader->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + StartingLBA = Entry->StartingLBA; + EndingLBA = Entry->EndingLBA; + if (StartingLBA > EndingLBA || + StartingLBA < PartHeader->FirstUsableLBA || + StartingLBA > PartHeader->LastUsableLBA || + EndingLBA < PartHeader->FirstUsableLBA || + EndingLBA > PartHeader->LastUsableLBA + ) { + PEntryStatus[Index1].OutOfRange = TRUE; + continue; + } + + if ((Entry->Attributes & BIT1) != 0) { + // + // If Bit 1 is set, this indicate that this is an OS specific GUID partition. + // + PEntryStatus[Index1].OsSpecific = TRUE; + } + + for (Index2 = Index1 + 1; Index2 < PartHeader->NumberOfPartitionEntries; Index2++) { + Entry = (EFI_PARTITION_ENTRY *) ((UINT8 *) PartEntry + Index2 * PartHeader->SizeOfPartitionEntry); + if (CompareGuid (&Entry->PartitionTypeGUID, &gEfiPartTypeUnusedGuid)) { + continue; + } + + if (Entry->EndingLBA >= StartingLBA && Entry->StartingLBA <= EndingLBA) { + // + // This region overlaps with the Index1'th region + // + PEntryStatus[Index1].Overlap = TRUE; + PEntryStatus[Index2].Overlap = TRUE; + continue; + } + } + } + + DEBUG ((EFI_D_INFO, " End check partition entries\n")); +} + + +/** + Updates the CRC32 value in the table header. + + @param Hdr Table to update + +**/ +VOID +PartitionSetCrc ( + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + PartitionSetCrcAltSize (Hdr->HeaderSize, Hdr); +} + + +/** + Updates the CRC32 value in the table header. + + @param Size The size of the table + @param Hdr Table to update + +**/ +VOID +PartitionSetCrcAltSize ( + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + UINT32 Crc; + + Hdr->CRC32 = 0; + gBS->CalculateCrc32 ((UINT8 *) Hdr, Size, &Crc); + Hdr->CRC32 = Crc; +} + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +BOOLEAN +PartitionCheckCrc ( + IN UINTN MaxSize, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + return PartitionCheckCrcAltSize (MaxSize, Hdr->HeaderSize, Hdr); +} + + +/** + Checks the CRC32 value in the table header. + + @param MaxSize Max Size limit + @param Size The size of the table + @param Hdr Table to check + + @return TRUE CRC Valid + @return FALSE CRC Invalid + +**/ +BOOLEAN +PartitionCheckCrcAltSize ( + IN UINTN MaxSize, + IN UINTN Size, + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + UINT32 Crc; + UINT32 OrgCrc; + EFI_STATUS Status; + + Crc = 0; + + if (Size == 0) { + // + // If header size is 0 CRC will pass so return FALSE here + // + return FALSE; + } + + if ((MaxSize != 0) && (Size > MaxSize)) { + DEBUG ((EFI_D_ERROR, "CheckCrc32: Size > MaxSize\n")); + return FALSE; + } + // + // clear old crc from header + // + OrgCrc = Hdr->CRC32; + Hdr->CRC32 = 0; + + Status = gBS->CalculateCrc32 ((UINT8 *) Hdr, Size, &Crc); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "CheckCrc32: Crc calculation failed\n")); + return FALSE; + } + // + // set results + // + Hdr->CRC32 = Crc; + + // + // return status + // + DEBUG_CODE_BEGIN (); + if (OrgCrc != Crc) { + DEBUG ((EFI_D_ERROR, "CheckCrc32: Crc check failed\n")); + } + DEBUG_CODE_END (); + + return (BOOLEAN) (OrgCrc == Crc); +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Mbr.c b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Mbr.c new file mode 100644 index 000000000..f0c92aa09 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Mbr.c @@ -0,0 +1,353 @@ +/** @file + Decode a hard disk partitioned with the legacy MBR found on most PC's + + MBR - Master Boot Record is in the first sector of a partitioned hard disk. + The MBR supports four partitions per disk. The MBR also contains legacy + code that is not run on an EFI system. The legacy code reads the + first sector of the active partition into memory and + + BPB - BIOS Parameter Block is in the first sector of a FAT file system. + The BPB contains information about the FAT file system. The BPB is + always on the first sector of a media. The first sector also contains + the legacy boot strap code. + +Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2014, Hewlett-Packard Development Company, L.P.
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Partition.h" + +/** + Test to see if the Mbr buffer is a valid MBR. + + @param Mbr Parent Handle. + @param LastLba Last Lba address on the device. + + @retval TRUE Mbr is a Valid MBR. + @retval FALSE Mbr is not a Valid MBR. + +**/ +BOOLEAN +PartitionValidMbr ( + IN MASTER_BOOT_RECORD *Mbr, + IN EFI_LBA LastLba + ) +{ + UINT32 StartingLBA; + UINT32 EndingLBA; + UINT32 NewEndingLBA; + INTN Index1; + INTN Index2; + BOOLEAN MbrValid; + + if (Mbr->Signature != MBR_SIGNATURE) { + return FALSE; + } + // + // The BPB also has this signature, so it can not be used alone. + // + MbrValid = FALSE; + for (Index1 = 0; Index1 < MAX_MBR_PARTITIONS; Index1++) { + if (Mbr->Partition[Index1].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) == 0) { + continue; + } + + MbrValid = TRUE; + StartingLBA = UNPACK_UINT32 (Mbr->Partition[Index1].StartingLBA); + EndingLBA = StartingLBA + UNPACK_UINT32 (Mbr->Partition[Index1].SizeInLBA) - 1; + if (EndingLBA > LastLba) { + // + // Compatibility Errata: + // Some systems try to hide drive space with their INT 13h driver + // This does not hide space from the OS driver. This means the MBR + // that gets created from DOS is smaller than the MBR created from + // a real OS (NT & Win98). This leads to BlockIo->LastBlock being + // wrong on some systems FDISKed by the OS. + // + // return FALSE since no block devices on a system are implemented + // with INT 13h + // + + DEBUG((EFI_D_INFO, "PartitionValidMbr: Bad MBR partition size EndingLBA(%1x) > LastLBA(%1x)\n", EndingLBA, LastLba)); + + return FALSE; + } + + for (Index2 = Index1 + 1; Index2 < MAX_MBR_PARTITIONS; Index2++) { + if (Mbr->Partition[Index2].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) == 0) { + continue; + } + + NewEndingLBA = UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) + UNPACK_UINT32 (Mbr->Partition[Index2].SizeInLBA) - 1; + if (NewEndingLBA >= StartingLBA && UNPACK_UINT32 (Mbr->Partition[Index2].StartingLBA) <= EndingLBA) { + // + // This region overlaps with the Index1'th region + // + return FALSE; + } + } + } + // + // None of the regions overlapped so MBR is O.K. + // + return MbrValid; +} + + +/** + Install child handles if the Handle supports MBR format. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path. + + @retval EFI_SUCCESS A child handle was added. + @retval EFI_MEDIA_CHANGED Media change was detected. + @retval Others MBR partition was not found. + +**/ +EFI_STATUS +PartitionInstallMbrChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + MASTER_BOOT_RECORD *Mbr; + UINT32 ExtMbrStartingLba; + UINT32 Index; + HARDDRIVE_DEVICE_PATH HdDev; + HARDDRIVE_DEVICE_PATH ParentHdDev; + EFI_STATUS Found; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + EFI_DEVICE_PATH_PROTOCOL *LastDevicePathNode; + UINT32 BlockSize; + UINT32 MediaId; + EFI_LBA LastSector; + EFI_PARTITION_INFO_PROTOCOL PartitionInfo; + + Found = EFI_NOT_FOUND; + + BlockSize = BlockIo->Media->BlockSize; + MediaId = BlockIo->Media->MediaId; + LastSector = DivU64x32 ( + MultU64x32 (BlockIo->Media->LastBlock + 1, BlockSize), + MBR_SIZE + ) - 1; + + // + // Ensure the block size can hold the MBR + // + if (BlockSize < sizeof (MASTER_BOOT_RECORD)) { + return EFI_NOT_FOUND; + } + + Mbr = AllocatePool (BlockSize); + if (Mbr == NULL) { + return Found; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + 0, + BlockSize, + Mbr + ); + if (EFI_ERROR (Status)) { + Found = Status; + goto Done; + } + if (!PartitionValidMbr (Mbr, LastSector)) { + goto Done; + } + // + // We have a valid mbr - add each partition + // + // + // Get starting and ending LBA of the parent block device. + // + LastDevicePathNode = NULL; + ZeroMem (&ParentHdDev, sizeof (ParentHdDev)); + DevicePathNode = DevicePath; + while (!IsDevicePathEnd (DevicePathNode)) { + LastDevicePathNode = DevicePathNode; + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + + if (LastDevicePathNode != NULL) { + if (DevicePathType (LastDevicePathNode) == MEDIA_DEVICE_PATH && + DevicePathSubType (LastDevicePathNode) == MEDIA_HARDDRIVE_DP + ) { + CopyMem (&ParentHdDev, LastDevicePathNode, sizeof (ParentHdDev)); + } else { + LastDevicePathNode = NULL; + } + } + + ZeroMem (&HdDev, sizeof (HdDev)); + HdDev.Header.Type = MEDIA_DEVICE_PATH; + HdDev.Header.SubType = MEDIA_HARDDRIVE_DP; + SetDevicePathNodeLength (&HdDev.Header, sizeof (HdDev)); + HdDev.MBRType = MBR_TYPE_PCAT; + HdDev.SignatureType = SIGNATURE_TYPE_MBR; + + if (LastDevicePathNode == NULL) { + // + // This is a MBR, add each partition + // + for (Index = 0; Index < MAX_MBR_PARTITIONS; Index++) { + if (Mbr->Partition[Index].OSIndicator == 0x00 || UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA) == 0) { + // + // Don't use null MBR entries + // + continue; + } + + if (Mbr->Partition[Index].OSIndicator == PMBR_GPT_PARTITION) { + // + // This is the guard MBR for the GPT. If you ever see a GPT disk with zero partitions you can get here. + // We can not produce an MBR BlockIo for this device as the MBR spans the GPT headers. So formating + // this BlockIo would corrupt the GPT structures and require a recovery that would corrupt the format + // that corrupted the GPT partition. + // + continue; + } + + HdDev.PartitionNumber = Index + 1; + HdDev.PartitionStart = UNPACK_UINT32 (Mbr->Partition[Index].StartingLBA); + HdDev.PartitionSize = UNPACK_UINT32 (Mbr->Partition[Index].SizeInLBA); + CopyMem (HdDev.Signature, &(Mbr->UniqueMbrSignature[0]), sizeof (Mbr->UniqueMbrSignature)); + + ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; + PartitionInfo.Type = PARTITION_TYPE_MBR; + if (Mbr->Partition[Index].OSIndicator == EFI_PARTITION) { + PartitionInfo.System = 1; + } + CopyMem (&PartitionInfo.Info.Mbr, &Mbr->Partition[Index], sizeof (MBR_PARTITION_RECORD)); + + Status = PartitionInstallChildHandle ( + This, + Handle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &HdDev, + &PartitionInfo, + HdDev.PartitionStart, + HdDev.PartitionStart + HdDev.PartitionSize - 1, + MBR_SIZE, + ((Mbr->Partition[Index].OSIndicator == EFI_PARTITION) ? &gEfiPartTypeSystemPartGuid: NULL) + ); + + if (!EFI_ERROR (Status)) { + Found = EFI_SUCCESS; + } + } + } else { + // + // It's an extended partition. Follow the extended partition + // chain to get all the logical drives + // + Index = 0; + ExtMbrStartingLba = 0; + + do { + + Status = DiskIo->ReadDisk ( + DiskIo, + MediaId, + MultU64x32 (ExtMbrStartingLba, BlockSize), + BlockSize, + Mbr + ); + if (EFI_ERROR (Status)) { + Found = Status; + goto Done; + } + + if (UNPACK_UINT32 (Mbr->Partition[0].SizeInLBA) == 0) { + break; + } + + if ((Mbr->Partition[0].OSIndicator == EXTENDED_DOS_PARTITION) || + (Mbr->Partition[0].OSIndicator == EXTENDED_WINDOWS_PARTITION)) { + ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA); + continue; + } + HdDev.PartitionNumber = ++Index; + HdDev.PartitionStart = UNPACK_UINT32 (Mbr->Partition[0].StartingLBA) + ExtMbrStartingLba + ParentHdDev.PartitionStart; + HdDev.PartitionSize = UNPACK_UINT32 (Mbr->Partition[0].SizeInLBA); + if ((HdDev.PartitionStart + HdDev.PartitionSize - 1 >= ParentHdDev.PartitionStart + ParentHdDev.PartitionSize) || + (HdDev.PartitionStart <= ParentHdDev.PartitionStart)) { + break; + } + + // + // The signature in EBR(Extended Boot Record) should always be 0. + // + *((UINT32 *) &HdDev.Signature[0]) = 0; + + ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; + PartitionInfo.Type = PARTITION_TYPE_MBR; + if (Mbr->Partition[0].OSIndicator == EFI_PARTITION) { + PartitionInfo.System = 1; + } + CopyMem (&PartitionInfo.Info.Mbr, &Mbr->Partition[0], sizeof (MBR_PARTITION_RECORD)); + + Status = PartitionInstallChildHandle ( + This, + Handle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &HdDev, + &PartitionInfo, + HdDev.PartitionStart - ParentHdDev.PartitionStart, + HdDev.PartitionStart - ParentHdDev.PartitionStart + HdDev.PartitionSize - 1, + MBR_SIZE, + ((Mbr->Partition[0].OSIndicator == EFI_PARTITION) ? &gEfiPartTypeSystemPartGuid: NULL) + ); + if (!EFI_ERROR (Status)) { + Found = EFI_SUCCESS; + } + + if ((Mbr->Partition[1].OSIndicator != EXTENDED_DOS_PARTITION) && + (Mbr->Partition[1].OSIndicator != EXTENDED_WINDOWS_PARTITION) + ) { + break; + } + + ExtMbrStartingLba = UNPACK_UINT32 (Mbr->Partition[1].StartingLBA); + // + // Don't allow partition to be self referencing + // + if (ExtMbrStartingLba == 0) { + break; + } + } while (ExtMbrStartingLba < ParentHdDev.PartitionSize); + } + +Done: + FreePool (Mbr); + + return Found; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c new file mode 100644 index 000000000..f10ce7c65 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c @@ -0,0 +1,1369 @@ +/** @file + Partition driver that produces logical BlockIo devices from a physical + BlockIo device. The logical BlockIo devices are based on the format + of the raw block devices media. Currently "El Torito CD-ROM", UDF, Legacy + MBR, and GPT partition schemes are supported. + +Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Partition.h" + +// +// Partition Driver Global Variables. +// +EFI_DRIVER_BINDING_PROTOCOL gPartitionDriverBinding = { + PartitionDriverBindingSupported, + PartitionDriverBindingStart, + PartitionDriverBindingStop, + // + // Grub4Dos copies the BPB of the first partition to the MBR. If the + // DriverBindingStart() of the Fat driver gets run before that of Partition + // driver only the first partition can be recognized. + // Let the driver binding version of Partition driver be higher than that of + // Fat driver to make sure the DriverBindingStart() of the Partition driver + // gets run before that of Fat driver so that all the partitions can be recognized. + // + 0xb, + NULL, + NULL +}; + +// +// Prioritized function list to detect partition table. +// Refer to UEFI Spec 13.3.2 Partition Discovery, the block device +// should be scanned in below order: +// 1. GPT +// 2. ISO 9660 (El Torito) (or UDF) +// 3. MBR +// 4. no partiton found +// Note: UDF is using a same method as booting from CD-ROM, so put it along +// with CD-ROM check. +// +PARTITION_DETECT_ROUTINE mPartitionDetectRoutineTable[] = { + PartitionInstallGptChildHandles, + PartitionInstallUdfChildHandles, + PartitionInstallMbrChildHandles, + NULL +}; + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a BlockIo and DiskIo protocol or a BlockIo2 protocol can be + supported. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DEV_PATH *Node; + + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, go on checking other conditions + // + if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + Node = (EFI_DEV_PATH *) RemainingDevicePath; + if (Node->DevPath.Type != MEDIA_DEVICE_PATH || + Node->DevPath.SubType != MEDIA_HARDDRIVE_DP || + DevicePathNodeLength (&Node->DevPath) != sizeof (HARDDRIVE_DEVICE_PATH)) { + return EFI_UNSUPPORTED; + } + } + } + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + // + // Open the EFI Device Path protocol needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close protocol, don't use device path protocol in the Support() function + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Start this driver on ControllerHandle by opening a Block IO or a Block IO2 + or both, and Disk IO protocol, reading Device Path, and creating a child + handle with a Disk IO and device path protocol. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_STATUS OpenStatus; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + PARTITION_DETECT_ROUTINE *Routine; + BOOLEAN MediaPresent; + EFI_TPL OldTpl; + + BlockIo2 = NULL; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (IsDevicePathEnd (RemainingDevicePath)) { + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Try to open BlockIO and BlockIO2. If BlockIO would be opened, continue, + // otherwise, return error. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIo2ProtocolGuid, + (VOID **) &BlockIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + BlockIo2 = NULL; + } + + // + // Get the Device Path Protocol on ControllerHandle's handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + goto Exit; + } + + // + // Get the DiskIo and DiskIo2. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + goto Exit; + } + + OpenStatus = Status; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + (VOID **) &DiskIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + DiskIo2 = NULL; + } + + // + // Try to read blocks when there's media or it is removable physical partition. + // + Status = EFI_UNSUPPORTED; + MediaPresent = BlockIo->Media->MediaPresent; + if (BlockIo->Media->MediaPresent || + (BlockIo->Media->RemovableMedia && !BlockIo->Media->LogicalPartition)) { + // + // Try for GPT, then legacy MBR partition types, and then UDF and El Torito. + // If the media supports a given partition type install child handles to + // represent the partitions described by the media. + // + Routine = &mPartitionDetectRoutineTable[0]; + while (*Routine != NULL) { + Status = (*Routine) ( + This, + ControllerHandle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + ParentDevicePath + ); + if (!EFI_ERROR (Status) || Status == EFI_MEDIA_CHANGED || Status == EFI_NO_MEDIA) { + break; + } + Routine++; + } + } + // + // In the case that the driver is already started (OpenStatus == EFI_ALREADY_STARTED), + // the DevicePathProtocol and the DiskIoProtocol are not actually opened by the + // driver. So don't try to close them. Otherwise, we will break the dependency + // between the controller and the driver set up before. + // + // In the case that when the media changes on a device it will Reinstall the + // BlockIo interaface. This will cause a call to our Stop(), and a subsequent + // reentrant call to our Start() successfully. We should leave the device open + // when this happen. The "media change" case includes either the status is + // EFI_MEDIA_CHANGED or it is a "media" to "no media" change. + // + if (EFI_ERROR (Status) && + !EFI_ERROR (OpenStatus) && + Status != EFI_MEDIA_CHANGED && + !(MediaPresent && Status == EFI_NO_MEDIA)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + // + // Close Parent DiskIo2 if has. + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + BOOLEAN AllChildrenStopped; + PARTITION_PRIVATE_DATA *Private; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_GUID *TypeGuid; + + BlockIo = NULL; + BlockIo2 = NULL; + Private = NULL; + + if (NumberOfChildren == 0) { + // + // In the case of re-entry of the PartitionDriverBindingStop, the + // NumberOfChildren may not reflect the actual number of children on the + // bus driver. Hence, additional check is needed here. + // + if (HasChildren (ControllerHandle)) { + DEBUG((EFI_D_ERROR, "PartitionDriverBindingStop: Still has child.\n")); + return EFI_DEVICE_ERROR; + } + + // + // Close the bus driver + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + // + // Close Parent BlockIO2 if has. + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return EFI_SUCCESS; + } + + AllChildrenStopped = TRUE; + for (Index = 0; Index < NumberOfChildren; Index++) { + gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + // + // Try to locate BlockIo2. + // + gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIo2ProtocolGuid, + (VOID **) &BlockIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (BlockIo); + if (Private->InStop) { + // + // If the child handle is going to be stopped again during the re-entry + // of DriverBindingStop, just do nothing. + // + break; + } + Private->InStop = TRUE; + + BlockIo->FlushBlocks (BlockIo); + + if (BlockIo2 != NULL) { + Status = BlockIo2->FlushBlocksEx (BlockIo2, NULL); + DEBUG((EFI_D_ERROR, "PartitionDriverBindingStop: FlushBlocksEx returned with %r\n", Status)); + } else { + Status = EFI_SUCCESS; + } + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + if (IsZeroGuid (&Private->TypeGuid)) { + TypeGuid = NULL; + } else { + TypeGuid = &Private->TypeGuid; + } + + // + // All Software protocols have be freed from the handle so remove it. + // Remove the BlockIo Protocol if has. + // Remove the BlockIo2 Protocol if has. + // + if (BlockIo2 != NULL) { + // + // Some device drivers might re-install the BlockIO(2) protocols for a + // media change condition. Therefore, if the FlushBlocksEx returned with + // EFI_MEDIA_CHANGED, just let the BindingStop fail to avoid potential + // reference of already stopped child handle. + // + if (Status != EFI_MEDIA_CHANGED) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Private->BlockIo2, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } + + if (EFI_ERROR (Status)) { + Private->InStop = FALSE; + gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + FreePool (Private->DevicePath); + FreePool (Private); + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + if (Status == EFI_MEDIA_CHANGED) { + break; + } + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Reset the Block Device. + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +PartitionReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + PARTITION_PRIVATE_DATA *Private; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + return Private->ParentBlockIo->Reset ( + Private->ParentBlockIo, + ExtendedVerification + ); +} + +/** + Probe the media status and return EFI_NO_MEDIA or EFI_MEDIA_CHANGED + for no media or media change case. Otherwise DefaultStatus is returned. + + @param DiskIo Pointer to the DiskIo instance. + @param MediaId Id of the media, changes every time the media is replaced. + @param DefaultStatus The default status to return when it's not the no media + or media change case. + + @retval EFI_NO_MEDIA There is no media. + @retval EFI_MEDIA_CHANGED The media was changed. + @retval others The default status to return. +**/ +EFI_STATUS +ProbeMediaStatus ( + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UINT32 MediaId, + IN EFI_STATUS DefaultStatus + ) +{ + EFI_STATUS Status; + UINT8 Buffer[1]; + + // + // Read 1 byte from offset 0 to check if the MediaId is still valid. + // The reading operation is synchronious thus it is not worth it to + // allocate a buffer from the pool. The destination buffer for the + // data is in the stack. + // + Status = DiskIo->ReadDisk (DiskIo, MediaId, 0, 1, (VOID*)Buffer); + if ((Status == EFI_NO_MEDIA) || (Status == EFI_MEDIA_CHANGED)) { + return Status; + } + return DefaultStatus; +} + +/** + Read by using the Disk IO protocol on the parent device. Lba addresses + must be converted to byte offsets. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +PartitionReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_INVALID_PARAMETER); + } + // + // Because some kinds of partition have different block size from their parent + // device, we call the Disk IO protocol on the parent device, not the Block IO + // protocol + // + return Private->DiskIo->ReadDisk (Private->DiskIo, MediaId, Offset, BufferSize, Buffer); +} + +/** + Write by using the Disk IO protocol on the parent device. Lba addresses + must be converted to byte offsets. + + @param[in] This Protocol instance pointer. + @param[in] MediaId Id of the media, changes every time the media is replaced. + @param[in] Lba The starting Logical Block Address to read from + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer Buffer containing data to be written to device. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains a LBA that is not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +PartitionWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_INVALID_PARAMETER); + } + // + // Because some kinds of partition have different block size from their parent + // device, we call the Disk IO protocol on the parent device, not the Block IO + // protocol + // + return Private->DiskIo->WriteDisk (Private->DiskIo, MediaId, Offset, BufferSize, Buffer); +} + + +/** + Flush the parent Block Device. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writting back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +PartitionFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + PARTITION_PRIVATE_DATA *Private; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + return Private->ParentBlockIo->FlushBlocks (Private->ParentBlockIo); +} + +/** + Probe the media status and return EFI_NO_MEDIA or EFI_MEDIA_CHANGED + for no media or media change case. Otherwise DefaultStatus is returned. + + @param DiskIo2 Pointer to the DiskIo2 instance. + @param MediaId Id of the media, changes every time the media is replaced. + @param DefaultStatus The default status to return when it's not the no media + or media change case. + + @retval EFI_NO_MEDIA There is no media. + @retval EFI_MEDIA_CHANGED The media was changed. + @retval others The default status to return. +**/ +EFI_STATUS +ProbeMediaStatusEx ( + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN UINT32 MediaId, + IN EFI_STATUS DefaultStatus + ) +{ + EFI_STATUS Status; + UINT8 Buffer[1]; + + // + // Read 1 byte from offset 0 to check if the MediaId is still valid. + // The reading operation is synchronious thus it is not worth it to + // allocate a buffer from the pool. The destination buffer for the + // data is in the stack. + // + Status = DiskIo2->ReadDiskEx (DiskIo2, MediaId, 0, NULL, 1, (VOID*)Buffer); + if ((Status == EFI_NO_MEDIA) || (Status == EFI_MEDIA_CHANGED)) { + return Status; + } + return DefaultStatus; +} + +/** + Reset the Block Device throught Block I/O2 protocol. + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +PartitionResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + PARTITION_PRIVATE_DATA *Private; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + return Private->ParentBlockIo2->Reset ( + Private->ParentBlockIo2, + ExtendedVerification + ); +} + +/** + The general callback for the DiskIo2 interfaces. + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which points to the PARTITION_ACCESS_TASK instance. +**/ +VOID +EFIAPI +PartitionOnAccessComplete ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PARTITION_ACCESS_TASK *Task; + + Task = (PARTITION_ACCESS_TASK *) Context; + + gBS->CloseEvent (Event); + + Task->BlockIo2Token->TransactionStatus = Task->DiskIo2Token.TransactionStatus; + gBS->SignalEvent (Task->BlockIo2Token->Event); + + FreePool (Task); +} + +/** + Create a new PARTITION_ACCESS_TASK instance. + + @param Token Pointer to the EFI_BLOCK_IO2_TOKEN. + + @return Pointer to the created PARTITION_ACCESS_TASK instance or NULL upon failure. +**/ +PARTITION_ACCESS_TASK * +PartitionCreateAccessTask ( + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + PARTITION_ACCESS_TASK *Task; + + Task = AllocatePool (sizeof (*Task)); + if (Task == NULL) { + return NULL; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PartitionOnAccessComplete, + Task, + &Task->DiskIo2Token.Event + ); + if (EFI_ERROR (Status)) { + FreePool (Task); + return NULL; + } + + Task->BlockIo2Token = Token; + + return Task; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + This function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and + non-blocking I/O is being used, the Event associated with this request will + not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is + replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The + caller is responsible for either having implicit or + explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Token->Event is + not NULL.The data was read correctly from the + device if the Token->Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. +**/ +EFI_STATUS +EFIAPI +PartitionReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + PARTITION_ACCESS_TASK *Task; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_INVALID_PARAMETER); + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Task = PartitionCreateAccessTask (Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->DiskIo2->ReadDiskEx (Private->DiskIo2, MediaId, Offset, &Task->DiskIo2Token, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Task->DiskIo2Token.Event); + FreePool (Task); + } + } else { + Status = Private->DiskIo2->ReadDiskEx (Private->DiskIo2, MediaId, Offset, NULL, BufferSize, Buffer); + } + + return Status; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + This function writes the requested number of blocks to the device. All blocks + are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA, + EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is + being used, the Event associated with this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The write request was queued if Event is not NULL. + The data was written correctly to the device if + the Event is NULL. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +PartitionWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + PARTITION_ACCESS_TASK *Task; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_INVALID_PARAMETER); + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Task = PartitionCreateAccessTask (Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->DiskIo2->WriteDiskEx (Private->DiskIo2, MediaId, Offset, &Task->DiskIo2Token, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Task->DiskIo2Token.Event); + FreePool (Task); + } + } else { + Status = Private->DiskIo2->WriteDiskEx (Private->DiskIo2, MediaId, Offset, NULL, BufferSize, Buffer); + } + return Status; +} + +/** + Flush the Block Device. + + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED + is returned and non-blocking I/O is being used, the Event associated with + this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the transaction + + @retval EFI_SUCCESS The flush request was queued if Event is not NULL. + All outstanding data was written correctly to the + device if the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while writting back + the data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +PartitionFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + PARTITION_ACCESS_TASK *Task; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + if ((Token != NULL) && (Token->Event != NULL)) { + Task = PartitionCreateAccessTask (Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->DiskIo2->FlushDiskEx (Private->DiskIo2, &Task->DiskIo2Token); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Task->DiskIo2Token.Event); + FreePool (Task); + } + } else { + Status = Private->DiskIo2->FlushDiskEx (Private->DiskIo2, NULL); + } + return Status; +} + + +/** + Create a child handle for a logical block device that represents the + bytes Start to End of the Parent Block IO device. + + @param[in] This Protocol instance pointer. + @param[in] ParentHandle Parent Handle for new child. + @param[in] ParentDiskIo Parent DiskIo interface. + @param[in] ParentDiskIo2 Parent DiskIo2 interface. + @param[in] ParentBlockIo Parent BlockIo interface. + @param[in] ParentBlockIo2 Parent BlockIo2 interface. + @param[in] ParentDevicePath Parent Device Path. + @param[in] DevicePathNode Child Device Path node. + @param[in] PartitionInfo Child Partition Information interface. + @param[in] Start Start Block. + @param[in] End End Block. + @param[in] BlockSize Child block size. + @param[in] TypeGuid Partition GUID Type. + + @retval EFI_SUCCESS A child handle was added. + @retval other A child handle was not added. + +**/ +EFI_STATUS +PartitionInstallChildHandle ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ParentHandle, + IN EFI_DISK_IO_PROTOCOL *ParentDiskIo, + IN EFI_DISK_IO2_PROTOCOL *ParentDiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePathNode, + IN EFI_PARTITION_INFO_PROTOCOL *PartitionInfo, + IN EFI_LBA Start, + IN EFI_LBA End, + IN UINT32 BlockSize, + IN EFI_GUID *TypeGuid + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Private = AllocateZeroPool (sizeof (PARTITION_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Signature = PARTITION_PRIVATE_DATA_SIGNATURE; + + Private->Start = MultU64x32 (Start, BlockSize); + Private->End = MultU64x32 (End + 1, BlockSize); + + Private->BlockSize = BlockSize; + Private->ParentBlockIo = ParentBlockIo; + Private->ParentBlockIo2 = ParentBlockIo2; + Private->DiskIo = ParentDiskIo; + Private->DiskIo2 = ParentDiskIo2; + + // + // Set the BlockIO into Private Data. + // + Private->BlockIo.Revision = ParentBlockIo->Revision; + + Private->BlockIo.Media = &Private->Media; + CopyMem (Private->BlockIo.Media, ParentBlockIo->Media, sizeof (EFI_BLOCK_IO_MEDIA)); + + Private->BlockIo.Reset = PartitionReset; + Private->BlockIo.ReadBlocks = PartitionReadBlocks; + Private->BlockIo.WriteBlocks = PartitionWriteBlocks; + Private->BlockIo.FlushBlocks = PartitionFlushBlocks; + + // + // Set the BlockIO2 into Private Data. + // + if (Private->DiskIo2 != NULL) { + ASSERT (Private->ParentBlockIo2 != NULL); + Private->BlockIo2.Media = &Private->Media2; + CopyMem (Private->BlockIo2.Media, ParentBlockIo2->Media, sizeof (EFI_BLOCK_IO_MEDIA)); + + Private->BlockIo2.Reset = PartitionResetEx; + Private->BlockIo2.ReadBlocksEx = PartitionReadBlocksEx; + Private->BlockIo2.WriteBlocksEx = PartitionWriteBlocksEx; + Private->BlockIo2.FlushBlocksEx = PartitionFlushBlocksEx; + } + + Private->Media.IoAlign = 0; + Private->Media.LogicalPartition = TRUE; + Private->Media.LastBlock = End - Start; + + Private->Media.BlockSize = (UINT32) BlockSize; + + Private->Media2.IoAlign = 0; + Private->Media2.LogicalPartition = TRUE; + Private->Media2.LastBlock = Private->Media.LastBlock; + Private->Media2.BlockSize = (UINT32) BlockSize; + + // + // Per UEFI Spec, LowestAlignedLba, LogicalBlocksPerPhysicalBlock and OptimalTransferLengthGranularity must be 0 + // for logical partitions. + // + if (Private->BlockIo.Revision >= EFI_BLOCK_IO_PROTOCOL_REVISION2) { + Private->Media.LowestAlignedLba = 0; + Private->Media.LogicalBlocksPerPhysicalBlock = 0; + Private->Media2.LowestAlignedLba = 0; + Private->Media2.LogicalBlocksPerPhysicalBlock = 0; + if (Private->BlockIo.Revision >= EFI_BLOCK_IO_PROTOCOL_REVISION3) { + Private->Media.OptimalTransferLengthGranularity = 0; + Private->Media2.OptimalTransferLengthGranularity = 0; + } + } + + Private->DevicePath = AppendDevicePathNode (ParentDevicePath, DevicePathNode); + + if (Private->DevicePath == NULL) { + FreePool (Private); + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the PartitionInfo into Private Data. + // + CopyMem (&Private->PartitionInfo, PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + + if (TypeGuid != NULL) { + CopyGuid(&(Private->TypeGuid), TypeGuid); + } else { + ZeroMem ((VOID *)&(Private->TypeGuid), sizeof (EFI_GUID)); + } + + // + // Create the new handle. + // + Private->Handle = NULL; + if (Private->DiskIo2 != NULL) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Private->BlockIo2, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } + + if (!EFI_ERROR (Status)) { + // + // Open the Parent Handle for the child + // + Status = gBS->OpenProtocol ( + ParentHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &ParentDiskIo, + This->DriverBindingHandle, + Private->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + FreePool (Private->DevicePath); + FreePool (Private); + + // + // if the Status == EFI_ALREADY_STARTED, it means the child handles + // are already installed. So return EFI_SUCCESS to avoid do the next + // partition type check. + // + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + The user Entry Point for module Partition. The user code starts with this 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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializePartition ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPartitionDriverBinding, + ImageHandle, + &gPartitionComponentName, + &gPartitionComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + + return Status; +} + + +/** + Test to see if there is any child on ControllerHandle. + + @param[in] ControllerHandle Handle of device to test. + + @retval TRUE There are children on the ControllerHandle. + @retval FALSE No child is on the ControllerHandle. + +**/ +BOOLEAN +HasChildren ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + EFI_STATUS Status; + UINTN Index; + + Status = gBS->OpenProtocolInformation ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + ASSERT_EFI_ERROR (Status); + + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + break; + } + } + FreePool (OpenInfoBuffer); + + return (BOOLEAN) (Index < EntryCount); +} + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h new file mode 100644 index 000000000..a633950be --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.h @@ -0,0 +1,485 @@ +/** @file + Partition driver that produces logical BlockIo devices from a physical + BlockIo device. The logical BlockIo devices are based on the format + of the raw block devices media. Currently "El Torito CD-ROM", UDF, Legacy + MBR, and GPT partition schemes are supported. + +Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PARTITION_H_ +#define _PARTITION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// +// Partition private data +// +#define PARTITION_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'a', 'r', 't') +typedef struct { + UINT64 Signature; + + EFI_HANDLE Handle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_BLOCK_IO_PROTOCOL BlockIo; + EFI_BLOCK_IO2_PROTOCOL BlockIo2; + EFI_BLOCK_IO_MEDIA Media; + EFI_BLOCK_IO_MEDIA Media2;//For BlockIO2 + EFI_PARTITION_INFO_PROTOCOL PartitionInfo; + + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + EFI_BLOCK_IO_PROTOCOL *ParentBlockIo; + EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2; + UINT64 Start; + UINT64 End; + UINT32 BlockSize; + BOOLEAN InStop; + + EFI_GUID TypeGuid; + +} PARTITION_PRIVATE_DATA; + +typedef struct { + EFI_DISK_IO2_TOKEN DiskIo2Token; + EFI_BLOCK_IO2_TOKEN *BlockIo2Token; +} PARTITION_ACCESS_TASK; + +#define PARTITION_DEVICE_FROM_BLOCK_IO_THIS(a) CR (a, PARTITION_PRIVATE_DATA, BlockIo, PARTITION_PRIVATE_DATA_SIGNATURE) +#define PARTITION_DEVICE_FROM_BLOCK_IO2_THIS(a) CR (a, PARTITION_PRIVATE_DATA, BlockIo2, PARTITION_PRIVATE_DATA_SIGNATURE) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gPartitionDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gPartitionComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPartitionComponentName2; + +// +// Extract INT32 from char array +// +#define UNPACK_INT32(a) (INT32)( (((UINT8 *) a)[0] << 0) | \ + (((UINT8 *) a)[1] << 8) | \ + (((UINT8 *) a)[2] << 16) | \ + (((UINT8 *) a)[3] << 24) ) + +// +// Extract UINT32 from char array +// +#define UNPACK_UINT32(a) (UINT32)( (((UINT8 *) a)[0] << 0) | \ + (((UINT8 *) a)[1] << 8) | \ + (((UINT8 *) a)[2] << 16) | \ + (((UINT8 *) a)[3] << 24) ) + + +// +// GPT Partition Entry Status +// +typedef struct { + BOOLEAN OutOfRange; + BOOLEAN Overlap; + BOOLEAN OsSpecific; +} EFI_PARTITION_ENTRY_STATUS; + +// +// Function Prototypes +// +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a BlockIo and DiskIo protocol can be supported. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on ControllerHandle by opening a Block IO and Disk IO + protocol, reading Device Path, and creating a child handle with a + Disk IO and device path protocol. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PartitionComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PartitionComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +/** + Create a child handle for a logical block device that represents the + bytes Start to End of the Parent Block IO device. + + @param[in] This Protocol instance pointer. + @param[in] ParentHandle Parent Handle for new child. + @param[in] ParentDiskIo Parent DiskIo interface. + @param[in] ParentDiskIo2 Parent DiskIo2 interface. + @param[in] ParentBlockIo Parent BlockIo interface. + @param[in] ParentBlockIo2 Parent BlockIo2 interface. + @param[in] ParentDevicePath Parent Device Path. + @param[in] DevicePathNode Child Device Path node. + @param[in] PartitionInfo Child Partition Information interface. + @param[in] Start Start Block. + @param[in] End End Block. + @param[in] BlockSize Child block size. + @param[in] TypeGuid Parition Type Guid. + + @retval EFI_SUCCESS A child handle was added. + @retval other A child handle was not added. + +**/ +EFI_STATUS +PartitionInstallChildHandle ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ParentHandle, + IN EFI_DISK_IO_PROTOCOL *ParentDiskIo, + IN EFI_DISK_IO2_PROTOCOL *ParentDiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePathNode, + IN EFI_PARTITION_INFO_PROTOCOL *PartitionInfo, + IN EFI_LBA Start, + IN EFI_LBA End, + IN UINT32 BlockSize, + IN EFI_GUID *TypeGuid + ); + +/** + Test to see if there is any child on ControllerHandle. + + @param[in] ControllerHandle Handle of device to test. + + @retval TRUE There are children on the ControllerHandle. + @retval FALSE No child is on the ControllerHandle. + +**/ +BOOLEAN +HasChildren ( + IN EFI_HANDLE ControllerHandle + ); + +/** + Install child handles if the Handle supports GPT partition structure. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path. + + @retval EFI_SUCCESS Valid GPT disk. + @retval EFI_MEDIA_CHANGED Media changed Detected. + @retval EFI_INVALID_PARAMETER If both BlockIo and BlockIo2 are NULL; + @retval other Not a valid GPT disk. + +**/ +EFI_STATUS +PartitionInstallGptChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +/** + Install child handles if the Handle supports El Torito format. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path + + + @retval EFI_SUCCESS Child handle(s) was added. + @retval EFI_MEDIA_CHANGED Media changed Detected. + @retval other no child handle was added. + +**/ +EFI_STATUS +PartitionInstallElToritoChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +/** + Install child handles if the Handle supports MBR format. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path. + + @retval EFI_SUCCESS A child handle was added. + @retval EFI_MEDIA_CHANGED Media change was detected. + @retval Others MBR partition was not found. + +**/ +EFI_STATUS +PartitionInstallMbrChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +/** + Install child handles if the Handle supports UDF/ECMA-167 volume format. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path + + + @retval EFI_SUCCESS Child handle(s) was added. + @retval EFI_MEDIA_CHANGED Media changed Detected. + @retval other no child handle was added. + +**/ +EFI_STATUS +PartitionInstallUdfChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +typedef +EFI_STATUS +(*PARTITION_DETECT_ROUTINE) ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf new file mode 100644 index 000000000..14ab6ae19 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf @@ -0,0 +1,85 @@ +## @file +# Modules that produces the logic Block I/O protocol for every partition via the physical Block I/O. +# +# This module produces the logical Block I/O device that represents +# the bytes from Start to End of the Parent Block I/O device. +# The partition of physical BlockIo device supported is one of legacy MBR, GPT, +# UDF and "El Torito" partitions. +# +# Caution: This module requires additional review when modified. +# This driver will have external input - disk partition. +# This external input must be validated carefully to avoid security issue like +# buffer overflow, integer overflow. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PartitionDxe + MODULE_UNI_FILE = PartitionDxe.uni + FILE_GUID = 1FA1F39E-FEFF-4aae-BD7B-38A070A3B609 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializePartition + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gPartitionDriverBinding +# COMPONENT_NAME = gPartitionComponentName +# COMPONENT_NAME2 = gPartitionComponentName2 +# + +[Sources] + ComponentName.c + Mbr.c + Gpt.c + ElTorito.c + Udf.c + Partition.c + Partition.h + + +[Packages] + MdePkg/MdePkg.dec + + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + + +[Guids] + gEfiPartTypeUnusedGuid ## SOMETIMES_CONSUMES ## GUID + ## SOMETIMES_CONSUMES ## GUID + ## SOMETIMES_CONSUMES ## GUID # Install protocol + gEfiPartTypeSystemPartGuid + + +[Protocols] + ## BY_START + ## TO_START + gEfiBlockIoProtocolGuid + ## BY_START + ## TO_START + gEfiBlockIo2ProtocolGuid + ## BY_START + ## TO_START + gEfiDevicePathProtocolGuid + gEfiPartitionInfoProtocolGuid ## BY_START + gEfiDiskIoProtocolGuid ## TO_START + gEfiDiskIo2ProtocolGuid ## TO_START + +[UserExtensions.TianoCore."ExtraFiles"] + PartitionDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.uni b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.uni new file mode 100644 index 000000000..e22c47bbc --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.uni @@ -0,0 +1,24 @@ +// /** @file +// Modules that produces the logic Block I/O protocol for every partition via the physical Block I/O. +// +// This module produces the logical Block I/O device that represents +// the bytes from Start to End of the Parent Block I/O device. +// The partition of physical BlockIo device supported is one of legacy MBR, GPT, +// and "El Torito" partitions. +// +// Caution: This module requires additional review when modified. +// This driver will have external input - disk partition. +// This external input must be validated carefully to avoid security issue like +// buffer overflow, integer overflow. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Produces the logic Block I/O protocol for every partition via the physical Block I/O." + +#string STR_MODULE_DESCRIPTION #language en-US "This module produces the logical Block I/O device that represents the bytes from Start to End of the Parent Block I/O device. The partition of physical BlockIO device supported is one of legacy MBR, GPT, and \"El Torito\" partitions.

" + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxeExtra.uni new file mode 100644 index 000000000..f92d2e53c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// PartitionDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Partition DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c new file mode 100644 index 000000000..3bf89a187 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c @@ -0,0 +1,788 @@ +/** @file + Scan for an UDF file system on a formatted media. + + Caution: This file requires additional review when modified. + This driver will have external input - CD/DVD media. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + FindUdfFileSystem() routine will consume the media properties and do basic + validation. + + Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. + Copyright (C) 2014-2017 Paulo Alcantara + Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Partition.h" + +#define MAX_CORRECTION_BLOCKS_NUM 512u + +// +// C5BD4D42-1A76-4996-8956-73CDA326CD0A +// +#define EFI_UDF_DEVICE_PATH_GUID \ + { 0xC5BD4D42, 0x1A76, 0x4996, \ + { 0x89, 0x56, 0x73, 0xCD, 0xA3, 0x26, 0xCD, 0x0A } \ + } + +typedef struct { + VENDOR_DEVICE_PATH DevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} UDF_DEVICE_PATH; + +// +// Vendor-Defined Device Path GUID for UDF file system +// +EFI_GUID gUdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID; + +// +// Vendor-Defined Media Device Path for UDF file system +// +UDF_DEVICE_PATH gUdfDevicePath = { + { { MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, + { sizeof (VENDOR_DEVICE_PATH), 0 } }, + EFI_UDF_DEVICE_PATH_GUID + }, + { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } + } +}; + +/** + Find the anchor volume descriptor pointer. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[out] AnchorPoint Anchor volume descriptor pointer. + @param[out] LastRecordedBlock Last recorded block. + + @retval EFI_SUCCESS Anchor volume descriptor pointer found. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval other Anchor volume descriptor pointer not found. + +**/ +EFI_STATUS +FindAnchorVolumeDescriptorPointer ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + OUT UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint, + OUT EFI_LBA *LastRecordedBlock + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + EFI_LBA EndLBA; + UDF_DESCRIPTOR_TAG *DescriptorTag; + UINTN AvdpsCount; + UINTN Size; + UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoints; + INTN Index; + UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPointPtr; + EFI_LBA LastAvdpBlockNum; + + // + // UDF 2.60, 2.2.3 Anchor Volume Descriptor Pointer + // + // An Anchor Volume Descriptor Pointer structure shall be recorded in at + // least 2 of the following 3 locations on the media: Logical Sector 256, + // N - 256 or N, where N is the last *addressable* sector of a volume. + // + // To figure out what logical sector N is, the SCSI commands READ CAPACITY and + // READ TRACK INFORMATION are used, however many drives or medias report their + // "last recorded block" wrongly. Although, READ CAPACITY returns the last + // readable data block but there might be unwritten blocks, which are located + // outside any track and therefore AVDP will not be found at block N. + // + // That said, we define a magic number of 512 blocks to be used as correction + // when attempting to find AVDP and define last block number. + // + BlockSize = BlockIo->Media->BlockSize; + EndLBA = BlockIo->Media->LastBlock; + *LastRecordedBlock = EndLBA; + AvdpsCount = 0; + + // + // Check if the block size of the underlying media can hold the data of an + // Anchor Volume Descriptor Pointer + // + if (BlockSize < sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER)) { + DEBUG (( + DEBUG_ERROR, + "%a: Media block size 0x%x unable to hold an AVDP.\n", + __FUNCTION__, + BlockSize + )); + return EFI_UNSUPPORTED; + } + + // + // Find AVDP at block 256 + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (256, BlockSize), + sizeof (*AnchorPoint), + AnchorPoint + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DescriptorTag = &AnchorPoint->DescriptorTag; + + // + // Check if read block is a valid AVDP descriptor + // + if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) { + DEBUG ((DEBUG_INFO, "%a: found AVDP at block %d\n", __FUNCTION__, 256)); + AvdpsCount++; + } + + // + // Find AVDP at block N - 256 + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 ((UINT64)EndLBA - 256, BlockSize), + sizeof (*AnchorPoint), + AnchorPoint + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check if read block is a valid AVDP descriptor + // + if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer && + ++AvdpsCount == 2) { + DEBUG ((DEBUG_INFO, "%a: found AVDP at block %Ld\n", __FUNCTION__, + EndLBA - 256)); + return EFI_SUCCESS; + } + + // + // Check if at least one AVDP was found in previous locations + // + if (AvdpsCount == 0) { + return EFI_VOLUME_CORRUPTED; + } + + // + // Find AVDP at block N + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 ((UINT64)EndLBA, BlockSize), + sizeof (*AnchorPoint), + AnchorPoint + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check if read block is a valid AVDP descriptor + // + if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) { + return EFI_SUCCESS; + } + + // + // No AVDP found at block N. Possibly drive/media returned bad last recorded + // block, or it is part of unwritten data blocks and outside any track. + // + // Search backwards for an AVDP from block N-1 through + // N-MAX_CORRECTION_BLOCKS_NUM. If any AVDP is found, then correct last block + // number for the new UDF partition child handle. + // + Size = MAX_CORRECTION_BLOCKS_NUM * BlockSize; + + AnchorPoints = AllocateZeroPool (Size); + if (AnchorPoints == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Read consecutive MAX_CORRECTION_BLOCKS_NUM disk blocks + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 ((UINT64)EndLBA - MAX_CORRECTION_BLOCKS_NUM, BlockSize), + Size, + AnchorPoints + ); + if (EFI_ERROR (Status)) { + goto Out_Free; + } + + Status = EFI_VOLUME_CORRUPTED; + + // + // Search for AVDP from blocks N-1 through N-MAX_CORRECTION_BLOCKS_NUM + // + for (Index = MAX_CORRECTION_BLOCKS_NUM - 2; Index >= 0; Index--) { + AnchorPointPtr = (VOID *)((UINTN)AnchorPoints + Index * BlockSize); + + DescriptorTag = &AnchorPointPtr->DescriptorTag; + + // + // Check if read block is a valid AVDP descriptor + // + if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) { + // + // Calculate last recorded block number + // + LastAvdpBlockNum = EndLBA - (MAX_CORRECTION_BLOCKS_NUM - Index); + DEBUG ((DEBUG_WARN, "%a: found AVDP at block %Ld\n", __FUNCTION__, + LastAvdpBlockNum)); + DEBUG ((DEBUG_WARN, "%a: correcting last block from %Ld to %Ld\n", + __FUNCTION__, EndLBA, LastAvdpBlockNum)); + // + // Save read AVDP from last block + // + CopyMem (AnchorPoint, AnchorPointPtr, sizeof (*AnchorPointPtr)); + // + // Set last recorded block number + // + *LastRecordedBlock = LastAvdpBlockNum; + Status = EFI_SUCCESS; + break; + } + } + +Out_Free: + FreePool (AnchorPoints); + return Status; +} + +/** + Find UDF volume identifiers in a Volume Recognition Sequence. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + + @retval EFI_SUCCESS UDF volume identifiers were found. + @retval EFI_NOT_FOUND UDF volume identifiers were not found. + @retval other Failed to perform disk I/O. + +**/ +EFI_STATUS +FindUdfVolumeIdentifiers ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo + ) +{ + EFI_STATUS Status; + UINT64 Offset; + UINT64 EndDiskOffset; + CDROM_VOLUME_DESCRIPTOR VolDescriptor; + CDROM_VOLUME_DESCRIPTOR TerminatingVolDescriptor; + + ZeroMem ((VOID *)&TerminatingVolDescriptor, sizeof (CDROM_VOLUME_DESCRIPTOR)); + + // + // Start Volume Recognition Sequence + // + EndDiskOffset = MultU64x32 (BlockIo->Media->LastBlock, + BlockIo->Media->BlockSize); + + for (Offset = UDF_VRS_START_OFFSET; Offset < EndDiskOffset; + Offset += UDF_LOGICAL_SECTOR_SIZE) { + // + // Check if block device has a Volume Structure Descriptor and an Extended + // Area. + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + Offset, + sizeof (CDROM_VOLUME_DESCRIPTOR), + (VOID *)&VolDescriptor + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (CompareMem ((VOID *)VolDescriptor.Unknown.Id, + (VOID *)UDF_BEA_IDENTIFIER, + sizeof (VolDescriptor.Unknown.Id)) == 0) { + break; + } + + if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id, + (VOID *)CDVOL_ID, + sizeof (VolDescriptor.Unknown.Id)) != 0) || + (CompareMem ((VOID *)&VolDescriptor, + (VOID *)&TerminatingVolDescriptor, + sizeof (CDROM_VOLUME_DESCRIPTOR)) == 0)) { + return EFI_NOT_FOUND; + } + } + + // + // Look for "NSR0{2,3}" identifiers in the Extended Area. + // + Offset += UDF_LOGICAL_SECTOR_SIZE; + if (Offset >= EndDiskOffset) { + return EFI_NOT_FOUND; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + Offset, + sizeof (CDROM_VOLUME_DESCRIPTOR), + (VOID *)&VolDescriptor + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id, + (VOID *)UDF_NSR2_IDENTIFIER, + sizeof (VolDescriptor.Unknown.Id)) != 0) && + (CompareMem ((VOID *)VolDescriptor.Unknown.Id, + (VOID *)UDF_NSR3_IDENTIFIER, + sizeof (VolDescriptor.Unknown.Id)) != 0)) { + return EFI_NOT_FOUND; + } + + // + // Look for "TEA01" identifier in the Extended Area + // + Offset += UDF_LOGICAL_SECTOR_SIZE; + if (Offset >= EndDiskOffset) { + return EFI_NOT_FOUND; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + Offset, + sizeof (CDROM_VOLUME_DESCRIPTOR), + (VOID *)&VolDescriptor + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (CompareMem ((VOID *)VolDescriptor.Unknown.Id, + (VOID *)UDF_TEA_IDENTIFIER, + sizeof (VolDescriptor.Unknown.Id)) != 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + Check if Logical Volume Descriptor is supported by current EDK2 UDF file + system implementation. + + @param[in] LogicalVolDesc Logical Volume Descriptor pointer. + + @retval TRUE Logical Volume Descriptor is supported. + @retval FALSE Logical Volume Descriptor is not supported. + +**/ +BOOLEAN +IsLogicalVolumeDescriptorSupported ( + UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc + ) +{ + // + // Check for a valid UDF revision range + // + switch (LogicalVolDesc->DomainIdentifier.Suffix.Domain.UdfRevision) { + case 0x0102: + case 0x0150: + case 0x0200: + case 0x0201: + case 0x0250: + case 0x0260: + break; + default: + return FALSE; + } + + // + // Check for a single Partition Map + // + if (LogicalVolDesc->NumberOfPartitionMaps > 1) { + return FALSE; + } + // + // UDF 1.02 revision supports only Type 1 (Physical) partitions, but + // let's check it any way. + // + // PartitionMap[0] -> type + // PartitionMap[1] -> length (in bytes) + // + if (LogicalVolDesc->PartitionMaps[0] != 1 || + LogicalVolDesc->PartitionMaps[1] != 6) { + return FALSE; + } + + return TRUE; +} + +/** + Find UDF logical volume location and whether it is supported by current EDK2 + UDF file system implementation. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] AnchorPoint Anchor volume descriptor pointer. + @param[in] LastRecordedBlock Last recorded block in media. + @param[out] MainVdsStartBlock Main VDS starting block number. + @param[out] MainVdsEndBlock Main VDS ending block number. + + @retval EFI_SUCCESS UDF logical volume was found. + @retval EFI_VOLUME_CORRUPTED UDF file system structures are corrupted. + @retval EFI_UNSUPPORTED UDF logical volume is not supported. + @retval other Failed to perform disk I/O. + +**/ +EFI_STATUS +FindLogicalVolumeLocation ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint, + IN EFI_LBA LastRecordedBlock, + OUT UINT64 *MainVdsStartBlock, + OUT UINT64 *MainVdsEndBlock + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + UDF_EXTENT_AD *ExtentAd; + UINT64 SeqBlocksNum; + UINT64 SeqStartBlock; + UINT64 GuardMainVdsStartBlock; + VOID *Buffer; + UINT64 SeqEndBlock; + BOOLEAN StopSequence; + UINTN LvdsCount; + UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc; + UDF_DESCRIPTOR_TAG *DescriptorTag; + + BlockSize = BlockIo->Media->BlockSize; + ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent; + + // + // UDF 2.60, 2.2.3.1 struct MainVolumeDescriptorSequenceExtent + // + // The Main Volume Descriptor Sequence Extent shall have a minimum length of + // 16 logical sectors. + // + // Also make sure it does not exceed maximum number of blocks in the disk. + // + SeqBlocksNum = DivU64x32 ((UINT64)ExtentAd->ExtentLength, BlockSize); + if (SeqBlocksNum < 16 || (EFI_LBA)SeqBlocksNum > LastRecordedBlock + 1) { + return EFI_VOLUME_CORRUPTED; + } + + // + // Check for valid Volume Descriptor Sequence starting block number + // + SeqStartBlock = (UINT64)ExtentAd->ExtentLocation; + if (SeqStartBlock > LastRecordedBlock || + SeqStartBlock + SeqBlocksNum - 1 > LastRecordedBlock) { + return EFI_VOLUME_CORRUPTED; + } + + GuardMainVdsStartBlock = SeqStartBlock; + + // + // Allocate buffer for reading disk blocks + // + Buffer = AllocateZeroPool ((UINTN)BlockSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SeqEndBlock = SeqStartBlock + SeqBlocksNum; + StopSequence = FALSE; + LvdsCount = 0; + Status = EFI_VOLUME_CORRUPTED; + // + // Start Main Volume Descriptor Sequence + // + for (; SeqStartBlock < SeqEndBlock && !StopSequence; SeqStartBlock++) { + // + // Read disk block + // + Status = BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + SeqStartBlock, + BlockSize, + Buffer + ); + if (EFI_ERROR (Status)) { + goto Out_Free; + } + + DescriptorTag = Buffer; + + // + // ECMA 167, 8.4.1 Contents of a Volume Descriptor Sequence + // + // - A Volume Descriptor Sequence shall contain one or more Primary Volume + // Descriptors. + // - A Volume Descriptor Sequence shall contain zero or more Implementation + // Use Volume Descriptors. + // - A Volume Descriptor Sequence shall contain zero or more Partition + // Descriptors. + // - A Volume Descriptor Sequence shall contain zero or more Logical Volume + // Descriptors. + // - A Volume Descriptor Sequence shall contain zero or more Unallocated + // Space Descriptors. + // + switch (DescriptorTag->TagIdentifier) { + case UdfPrimaryVolumeDescriptor: + case UdfImplemenationUseVolumeDescriptor: + case UdfPartitionDescriptor: + case UdfUnallocatedSpaceDescriptor: + break; + + case UdfLogicalVolumeDescriptor: + LogicalVolDesc = Buffer; + + // + // Check for existence of a single LVD and whether it is supported by + // current EDK2 UDF file system implementation. + // + if (++LvdsCount > 1 || + !IsLogicalVolumeDescriptorSupported (LogicalVolDesc)) { + Status = EFI_UNSUPPORTED; + StopSequence = TRUE; + } + + break; + + case UdfTerminatingDescriptor: + // + // Stop the sequence when we find a Terminating Descriptor + // (aka Unallocated Sector), se we don't have to walk all the unallocated + // area unnecessarily. + // + StopSequence = TRUE; + break; + + default: + // + // An invalid Volume Descriptor has been found in the sequece. Volume is + // corrupted. + // + Status = EFI_VOLUME_CORRUPTED; + goto Out_Free; + } + } + + // + // Check if LVD was found + // + if (!EFI_ERROR (Status) && LvdsCount == 1) { + *MainVdsStartBlock = GuardMainVdsStartBlock; + // + // We do not need to read either LVD or PD descriptors to know the last + // valid block in the found UDF file system. It's already + // LastRecordedBlock. + // + *MainVdsEndBlock = LastRecordedBlock; + + Status = EFI_SUCCESS; + } + +Out_Free: + // + // Free block read buffer + // + FreePool (Buffer); + + return Status; +} + +/** + Find a supported UDF file system in block device. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from Partition. + + The CD/DVD media is the external input, so this routine will do basic + validation for the media. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[out] StartingLBA UDF file system starting LBA. + @param[out] EndingLBA UDF file system starting LBA. + + @retval EFI_SUCCESS UDF file system was found. + @retval other UDF file system was not found. + +**/ +EFI_STATUS +FindUdfFileSystem ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + OUT EFI_LBA *StartingLBA, + OUT EFI_LBA *EndingLBA + ) +{ + EFI_STATUS Status; + UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER AnchorPoint; + EFI_LBA LastRecordedBlock; + + // + // Find UDF volume identifiers + // + Status = FindUdfVolumeIdentifiers (BlockIo, DiskIo); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find Anchor Volume Descriptor Pointer + // + Status = FindAnchorVolumeDescriptorPointer ( + BlockIo, + DiskIo, + &AnchorPoint, + &LastRecordedBlock + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find Logical Volume location + // + Status = FindLogicalVolumeLocation ( + BlockIo, + DiskIo, + &AnchorPoint, + LastRecordedBlock, + (UINT64 *)StartingLBA, + (UINT64 *)EndingLBA + ); + + return Status; +} + +/** + Install child handles if the Handle supports UDF/ECMA-167 volume format. + + @param[in] This Calling context. + @param[in] Handle Parent Handle. + @param[in] DiskIo Parent DiskIo interface. + @param[in] DiskIo2 Parent DiskIo2 interface. + @param[in] BlockIo Parent BlockIo interface. + @param[in] BlockIo2 Parent BlockIo2 interface. + @param[in] DevicePath Parent Device Path + + + @retval EFI_SUCCESS Child handle(s) was added. + @retval EFI_MEDIA_CHANGED Media changed Detected. + @retval other no child handle was added. + +**/ +EFI_STATUS +PartitionInstallUdfChildHandles ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Handle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + UINT32 RemainderByMediaBlockSize; + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + EFI_PARTITION_INFO_PROTOCOL PartitionInfo; + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + BOOLEAN ChildCreated; + + Media = BlockIo->Media; + ChildCreated = FALSE; + + // + // Check if UDF logical block size is multiple of underlying device block size + // + DivU64x32Remainder ( + UDF_LOGICAL_SECTOR_SIZE, // Dividend + Media->BlockSize, // Divisor + &RemainderByMediaBlockSize // Remainder + ); + if (RemainderByMediaBlockSize != 0) { + return EFI_NOT_FOUND; + } + + // + // Detect El Torito feature first. + // And always continue to search for UDF. + // + Status = PartitionInstallElToritoChildHandles ( + This, + Handle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + DevicePath + ); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "PartitionDxe: El Torito standard found on handle 0x%p.\n", Handle)); + ChildCreated = TRUE; + } + + // + // Search for an UDF file system on block device + // + Status = FindUdfFileSystem (BlockIo, DiskIo, &StartingLBA, &EndingLBA); + if (EFI_ERROR (Status)) { + return (ChildCreated ? EFI_SUCCESS : EFI_NOT_FOUND); + } + + // + // Create Partition Info protocol for UDF file system + // + ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; + PartitionInfo.Type = PARTITION_TYPE_OTHER; + + // + // Install partition child handle for UDF file system + // + Status = PartitionInstallChildHandle ( + This, + Handle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *)&gUdfDevicePath, + &PartitionInfo, + StartingLBA, + EndingLBA, + Media->BlockSize, + NULL + ); + if (EFI_ERROR (Status)) { + return (ChildCreated ? EFI_SUCCESS : Status); + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDisk.asl b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDisk.asl new file mode 100644 index 000000000..9615aaa66 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDisk.asl @@ -0,0 +1,38 @@ +/** @file + The definition block in ACPI table for NVDIMM root device. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +DefinitionBlock ( + "RamDisk.aml", + "SSDT", + 2, + "INTEL ", + "RamDisk ", + 0x1000 + ) +{ + Scope (\_SB) + { + Device (NVDR) + { + // + // Define _HID, "ACPI0012" NVDIMM Root Device + // + Name (_HID, "ACPI0012") + + // + // Readable name of this device + // + Name (_STR, Unicode ("NVDIMM Root Device")) + + Method (_STA, 0) + { + Return (0x0f) + } + } + } +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskBlockIo.c b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskBlockIo.c new file mode 100644 index 000000000..13f81bae1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskBlockIo.c @@ -0,0 +1,487 @@ +/** @file + Produce EFI_BLOCK_IO_PROTOCOL on a RAM disk device. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RamDiskImpl.h" + +// +// The EFI_BLOCK_IO_PROTOCOL instances that is installed onto the handle +// for newly registered RAM disks +// +EFI_BLOCK_IO_PROTOCOL mRamDiskBlockIoTemplate = { + EFI_BLOCK_IO_PROTOCOL_REVISION, + (EFI_BLOCK_IO_MEDIA *) 0, + RamDiskBlkIoReset, + RamDiskBlkIoReadBlocks, + RamDiskBlkIoWriteBlocks, + RamDiskBlkIoFlushBlocks +}; + +// +// The EFI_BLOCK_IO_PROTOCOL2 instances that is installed onto the handle +// for newly registered RAM disks +// +EFI_BLOCK_IO2_PROTOCOL mRamDiskBlockIo2Template = { + (EFI_BLOCK_IO_MEDIA *) 0, + RamDiskBlkIo2Reset, + RamDiskBlkIo2ReadBlocksEx, + RamDiskBlkIo2WriteBlocksEx, + RamDiskBlkIo2FlushBlocksEx +}; + + +/** + Initialize the BlockIO & BlockIO2 protocol of a RAM disk device. + + @param[in] PrivateData Points to RAM disk private data. + +**/ +VOID +RamDiskInitBlockIo ( + IN RAM_DISK_PRIVATE_DATA *PrivateData + ) +{ + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + EFI_BLOCK_IO_MEDIA *Media; + UINT32 Remainder; + + BlockIo = &PrivateData->BlockIo; + BlockIo2 = &PrivateData->BlockIo2; + Media = &PrivateData->Media; + + CopyMem (BlockIo, &mRamDiskBlockIoTemplate, sizeof (EFI_BLOCK_IO_PROTOCOL)); + CopyMem (BlockIo2, &mRamDiskBlockIo2Template, sizeof (EFI_BLOCK_IO2_PROTOCOL)); + + BlockIo->Media = Media; + BlockIo2->Media = Media; + Media->RemovableMedia = FALSE; + Media->MediaPresent = TRUE; + Media->LogicalPartition = FALSE; + Media->ReadOnly = FALSE; + Media->WriteCaching = FALSE; + + for (Media->BlockSize = RAM_DISK_DEFAULT_BLOCK_SIZE; + Media->BlockSize >= 1; + Media->BlockSize = Media->BlockSize >> 1) { + Media->LastBlock = DivU64x32Remainder (PrivateData->Size, Media->BlockSize, &Remainder) - 1; + if (Remainder == 0) { + break; + } + } + ASSERT (Media->BlockSize != 0); + + return; +} + + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + return EFI_SUCCESS; +} + + +/** + Read BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is + replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in] BufferSize Size of Buffer, must be a multiple of device block + size. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for either having + implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current + device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block + size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + RAM_DISK_PRIVATE_DATA *PrivateData; + UINTN NumberOfBlocks; + + PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO (This); + + if (MediaId != PrivateData->Media.MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + if ((BufferSize % PrivateData->Media.BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Lba > PrivateData->Media.LastBlock) { + return EFI_INVALID_PARAMETER; + } + + NumberOfBlocks = BufferSize / PrivateData->Media.BlockSize; + if ((Lba + NumberOfBlocks - 1) > PrivateData->Media.LastBlock) { + return EFI_INVALID_PARAMETER; + } + + CopyMem ( + Buffer, + (VOID *)(UINTN)(PrivateData->StartingAddr + MultU64x32 (Lba, PrivateData->Media.BlockSize)), + BufferSize + ); + + return EFI_SUCCESS; +} + + +/** + Write BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. + The caller is responsible for writing to only + legitimate locations. + @param[in] BufferSize Size of Buffer, must be a multiple of device block + size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current + device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block + size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not + valid, or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + RAM_DISK_PRIVATE_DATA *PrivateData; + UINTN NumberOfBlocks; + + PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO (This); + + if (MediaId != PrivateData->Media.MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (TRUE == PrivateData->Media.ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + if ((BufferSize % PrivateData->Media.BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Lba > PrivateData->Media.LastBlock) { + return EFI_INVALID_PARAMETER; + } + + NumberOfBlocks = BufferSize / PrivateData->Media.BlockSize; + if ((Lba + NumberOfBlocks - 1) > PrivateData->Media.LastBlock) { + return EFI_INVALID_PARAMETER; + } + + CopyMem ( + (VOID *)(UINTN)(PrivateData->StartingAddr + MultU64x32 (Lba, PrivateData->Media.BlockSize)), + Buffer, + BufferSize + ); + + return EFI_SUCCESS; +} + + +/** + Flush the Block Device. + + @param[in] This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device. + @retval EFI_DEVICE_ERROR The device reported an error while writting + back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + return EFI_SUCCESS; +} + + +/** + Resets the block device hardware. + + @param[in] This The pointer of EFI_BLOCK_IO2_PROTOCOL. + @param[in] ExtendedVerification The flag about if extend verificate. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The block device is not functioning correctly + and could not be reset. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2Reset ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + return EFI_SUCCESS; +} + + +/** + Reads the requested number of blocks from the device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the read request is for. + @param[in] Lba The starting logical block address to read + from on the device. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] BufferSize The size of the Buffer in bytes. This must be + a multiple of the intrinsic block size of the + device. + @param[out] Buffer A pointer to the destination buffer for the + data. The caller is responsible for either + having implicit or explicit ownership of the + buffer. + + @retval EFI_SUCCESS The read request was queued if Token->Event + is not NULL. The data was read correctly from + the device if the Token->Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2ReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + RAM_DISK_PRIVATE_DATA *PrivateData; + EFI_STATUS Status; + + PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO2 (This); + + Status = RamDiskBlkIoReadBlocks ( + &PrivateData->BlockIo, + MediaId, + Lba, + BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If caller's event is given, signal it after the memory read completes. + // + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + + +/** + Writes a specified number of blocks to the device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be + written. The caller is responsible for + writing to only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] BufferSize The size in bytes of Buffer. This must be a + multiple of the intrinsic block size of the + device. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The write request was queued if Event is not + NULL. The data was written correctly to the + device if the Event is NULL. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the write operation. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2WriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + RAM_DISK_PRIVATE_DATA *PrivateData; + EFI_STATUS Status; + + PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO2 (This); + + Status = RamDiskBlkIoWriteBlocks ( + &PrivateData->BlockIo, + MediaId, + Lba, + BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If caller's event is given, signal it after the memory write completes. + // + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + + +/** + Flushes all modified data to a physical block device. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the + transaction. + + @retval EFI_SUCCESS The flush request was queued if Event is not + NULL. All outstanding data was written + correctly to the device if the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to write data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2FlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + RAM_DISK_PRIVATE_DATA *PrivateData; + + PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO2 (This); + + if (TRUE == PrivateData->Media.ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + // + // If caller's event is given, signal it directly. + // + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDriver.c b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDriver.c new file mode 100644 index 000000000..fcbf4f117 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDriver.c @@ -0,0 +1,244 @@ +/** @file + The driver entry point for RamDiskDxe driver. + + Copyright (c) 2016, Intel Corporation. All rights reserved.
+ Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RamDiskImpl.h" + +// +// Handle for the EFI_RAM_DISK_PROTOCOL instance +// +EFI_HANDLE mRamDiskHandle = NULL; + +// +// The EFI_RAM_DISK_PROTOCOL instances that is installed onto the driver +// handle +// +EFI_RAM_DISK_PROTOCOL mRamDiskProtocol = { + RamDiskRegister, + RamDiskUnregister +}; + +// +// RamDiskDxe driver maintains a list of registered RAM disks. +// +LIST_ENTRY RegisteredRamDisks; + +// +// Pointers to the EFI_ACPI_TABLE_PROTOCOL and EFI_ACPI_SDT_PROTOCOL. +// +EFI_ACPI_TABLE_PROTOCOL *mAcpiTableProtocol = NULL; +EFI_ACPI_SDT_PROTOCOL *mAcpiSdtProtocol = NULL; + + +/** + Check whether EFI_ACPI_TABLE_PROTOCOL and EFI_ACPI_SDT_PROTOCOL are produced. + If both protocols are produced, publish all the reserved memory type RAM + disks to the NVDIMM Firmware Interface Table (NFIT). + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +RamDiskAcpiCheck ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + RAM_DISK_PRIVATE_DATA *PrivateData; + + gBS->CloseEvent (Event); + + // + // Locate the EFI_ACPI_TABLE_PROTOCOL. + // + Status = gBS->LocateProtocol ( + &gEfiAcpiTableProtocolGuid, + NULL, + (VOID **)&mAcpiTableProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG (( + EFI_D_INFO, + "RamDiskAcpiCheck: Cannot locate the EFI ACPI Table Protocol, " + "unable to publish RAM disks to NFIT.\n" + )); + return; + } + + // + // Locate the EFI_ACPI_SDT_PROTOCOL. + // + Status = gBS->LocateProtocol ( + &gEfiAcpiSdtProtocolGuid, + NULL, + (VOID **)&mAcpiSdtProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG (( + EFI_D_INFO, + "RamDiskAcpiCheck: Cannot locate the EFI ACPI Sdt Protocol, " + "unable to publish RAM disks to NFIT.\n" + )); + mAcpiTableProtocol = NULL; + return; + } + + BASE_LIST_FOR_EACH (Entry, &RegisteredRamDisks) { + PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); + RamDiskPublishNfit (PrivateData); + } +} + + +/** + The entry point for RamDiskDxe driver. + + @param[in] ImageHandle The image handle of the driver. + @param[in] SystemTable The system table. + + @retval EFI_ALREADY_STARTED The driver already exists in system. + @retval EFI_OUT_OF_RESOURCES Fail to execute entry point due to lack of + resources. + @retval EFI_SUCCES All the related protocols are installed on + the driver. + +**/ +EFI_STATUS +EFIAPI +RamDiskDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivate; + VOID *DummyInterface; + EFI_EVENT Event; + + // + // If already started, return. + // + Status = gBS->LocateProtocol ( + &gEfiRamDiskProtocolGuid, + NULL, + &DummyInterface + ); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Driver already started!\n")); + return EFI_ALREADY_STARTED; + } + + // + // Create a private data structure. + // + ConfigPrivate = AllocateCopyPool (sizeof (RAM_DISK_CONFIG_PRIVATE_DATA), &mRamDiskConfigPrivateDataTemplate); + if (ConfigPrivate == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Install RAM disk configuration form + // + Status = InstallRamDiskConfigForm (ConfigPrivate); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + // + // Install the EFI_RAM_DISK_PROTOCOL and RAM disk private data onto a + // new handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mRamDiskHandle, + &gEfiRamDiskProtocolGuid, + &mRamDiskProtocol, + &gEfiCallerIdGuid, + ConfigPrivate, + NULL + ); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + // + // Initialize the list of registered RAM disks maintained by the driver + // + InitializeListHead (&RegisteredRamDisks); + + Status = EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + RamDiskAcpiCheck, + NULL, + &Event + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; + +ErrorExit: + if (ConfigPrivate != NULL) { + UninstallRamDiskConfigForm (ConfigPrivate); + } + + return Status; +} + + +/** + Unload the RamDiskDxe driver and its configuration form. + + @param[in] ImageHandle The driver's image handle. + + @retval EFI_SUCCESS The RamDiskDxe driver and its configuration + form is unloaded. + @retval Others Failed to unload the form. + +**/ +EFI_STATUS +EFIAPI +RamDiskDxeUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivate; + + Status = gBS->HandleProtocol ( + mRamDiskHandle, + &gEfiCallerIdGuid, + (VOID **) &ConfigPrivate + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (ConfigPrivate->Signature == RAM_DISK_CONFIG_PRIVATE_DATA_SIGNATURE); + + // + // Unregister all registered RAM disks + // + UnregisterAllRamDisks (); + + gBS->UninstallMultipleProtocolInterfaces ( + mRamDiskHandle, + &gEfiRamDiskProtocolGuid, + &mRamDiskProtocol, + &gEfiCallerIdGuid, + ConfigPrivate, + NULL + ); + + UninstallRamDiskConfigForm (ConfigPrivate); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf new file mode 100644 index 000000000..bc2e642cf --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf @@ -0,0 +1,84 @@ +## @file +# Produces EFI_RAM_DISK_PROTOCOL and provides the capability to +# create/remove RAM disks in a setup browser. +# +# Copyright (c) 2016, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = RamDiskDxe + MODULE_UNI_FILE = RamDiskDxe.uni + FILE_GUID = 28A03FF4-12B3-4305-A417-BB1A4F94081E + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = RamDiskDxeEntryPoint + UNLOAD_IMAGE = RamDiskDxeUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 +# + +[Sources] + RamDiskDriver.c + RamDiskImpl.c + RamDiskBlockIo.c + RamDiskProtocol.c + RamDiskFileExplorer.c + RamDiskImpl.h + RamDiskHii.vfr + RamDiskHiiStrings.uni + RamDiskNVData.h + RamDisk.asl + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + UefiLib + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiHiiServicesLib + MemoryAllocationLib + HiiLib + FileExplorerLib + DevicePathLib + PrintLib + PcdLib + DxeServicesLib + +[Guids] + gEfiIfrTianoGuid ## PRODUCES ## GUID # HII opcode + ## PRODUCES ## HII + ## CONSUMES ## HII + gRamDiskFormSetGuid + gEfiVirtualDiskGuid ## SOMETIMES_CONSUMES ## GUID + gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## GUID # Indicate the information type + +[Protocols] + gEfiRamDiskProtocolGuid ## PRODUCES + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiDevicePathProtocolGuid ## PRODUCES + gEfiBlockIoProtocolGuid ## PRODUCES + gEfiBlockIo2ProtocolGuid ## PRODUCES + gEfiAcpiTableProtocolGuid ## SOMETIMES_CONSUMES + gEfiAcpiSdtProtocolGuid ## SOMETIMES_CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision ## SOMETIMES_CONSUMES + +[Depex] + gEfiHiiConfigRoutingProtocolGuid AND + gEfiHiiDatabaseProtocolGuid diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.uni b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.uni new file mode 100644 index 000000000..edf35ea18 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.uni @@ -0,0 +1,14 @@ +// /** @file +// Produces EFI_RAM_DISK_PROTOCOL and provides the capability to +// create/remove RAM disks in a setup browser. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Produces EFI_RAM_DISK_PROTOCOL and provides the capability to create/remove RAM disks in a setup browser." + +#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI_RAM_DISK_PROTOCOL and provides the capability to create/remove RAM disks in a setup browser." + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskFileExplorer.c b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskFileExplorer.c new file mode 100644 index 000000000..3a000389f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskFileExplorer.c @@ -0,0 +1,107 @@ +/** @file + Internal file explorer helper functions for RamDiskDxe driver. + + Copyright (c) 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RamDiskImpl.h" + + +/** + Helper function called as part of the code needed to allocate the proper + sized buffer for various EFI interfaces. + + @param[in, out] Status Current status. + @param[in, out] Buffer Current allocated buffer, or NULL. + @param[in] BufferSize Current buffer size needed. + + @retval TRUE If the buffer was reallocated and the caller should + try the API again. + @retval FALSE The caller should not call this function again. + +**/ +BOOLEAN +GrowBuffer ( + IN OUT EFI_STATUS *Status, + IN OUT VOID **Buffer, + IN UINTN BufferSize + ) +{ + BOOLEAN TryAgain; + + // + // If this is an initial request, buffer will be null with a new buffer size + // + if ((*Buffer == NULL) && (BufferSize != 0)) { + *Status = EFI_BUFFER_TOO_SMALL; + } + // + // If the status code is "buffer too small", resize the buffer + // + TryAgain = FALSE; + if (*Status == EFI_BUFFER_TOO_SMALL) { + + if (*Buffer != NULL) { + FreePool (*Buffer); + } + + *Buffer = AllocateZeroPool (BufferSize); + + if (*Buffer != NULL) { + TryAgain = TRUE; + } else { + *Status = EFI_OUT_OF_RESOURCES; + } + } + // + // If there's an error, free the buffer + // + if (!TryAgain && EFI_ERROR (*Status) && (*Buffer != NULL)) { + FreePool (*Buffer); + *Buffer = NULL; + } + + return TryAgain; +} + + +/** + This function gets the file information from an open file descriptor, + and stores it in a buffer allocated from pool. + + @param[in] FHand File Handle. + + @return A pointer to a buffer with file information or NULL is returned. + +**/ +EFI_FILE_INFO * +FileInfo ( + IN EFI_FILE_HANDLE FHand + ) +{ + EFI_STATUS Status; + EFI_FILE_INFO *Buffer; + UINTN BufferSize; + + // + // Initialize for GrowBuffer loop + // + Buffer = NULL; + BufferSize = SIZE_OF_EFI_FILE_INFO + 200; + + // + // Call the real function + // + while (GrowBuffer (&Status, (VOID **) &Buffer, BufferSize)) { + Status = FHand->GetInfo ( + FHand, + &gEfiFileInfoGuid, + &BufferSize, + Buffer + ); + } + + return Buffer; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHii.vfr b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHii.vfr new file mode 100644 index 000000000..3964d2f5b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHii.vfr @@ -0,0 +1,94 @@ +///** @file +// VFR file used by the RamDiskDxe driver. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +//**/ + +#include "RamDiskNVData.h" + +formset + guid = RAM_DISK_FORM_SET_GUID, + title = STRING_TOKEN(STR_FORM_SET_TITLE), + help = STRING_TOKEN(STR_FORM_SET_TITLE_HELP), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + // + // Form #1 "Main Form - Add/Remove/Show RAM Disks" + // + form formid = MAIN_FORM_ID, + title = STRING_TOKEN(STR_MAIN_FORM_TITLE); + + oneof + questionid = CREATE_RAW_MEMORY_TYPE_QUESTION_ID, + prompt = STRING_TOKEN(STR_MEMORY_TYPE_PROMPT), + help = STRING_TOKEN(STR_MEMORY_TYPE_HELP), + flags = NUMERIC_SIZE_1 | INTERACTIVE, + option text = STRING_TOKEN(STR_RAM_DISK_BOOT_SERVICE_DATA_MEMORY), value = RAM_DISK_BOOT_SERVICE_DATA_MEMORY, flags = DEFAULT; + option text = STRING_TOKEN(STR_RAM_DISK_RESERVED_MEMORY), value = RAM_DISK_RESERVED_MEMORY, flags = 0; + endoneof; + + subtitle text = STRING_TOKEN(STR_RAM_DISK_NULL_STRING); + + goto CREATE_RAW_RAM_DISK_FORM_ID, + prompt = STRING_TOKEN(STR_GOTO_ADD_RAW_FORM), + help = STRING_TOKEN(STR_GOTO_ADD_RAW_FORM_HELP); + + goto MAIN_FORM_ID, + prompt = STRING_TOKEN(STR_GOTO_ADD_FROM_FILE_FORM), + help = STRING_TOKEN(STR_GOTO_ADD_FROM_FILE_FORM_HELP), + flags = INTERACTIVE, + key = MAIN_GOTO_FILE_EXPLORER_ID; + + subtitle text = STRING_TOKEN(STR_RAM_DISK_NULL_STRING); + subtitle text = STRING_TOKEN(STR_RAM_DISK_LIST_TEXT); + + label MAIN_LABEL_LIST_START; + label MAIN_LABEL_LIST_END; + + subtitle text = STRING_TOKEN(STR_RAM_DISK_NULL_STRING); + + text + help = STRING_TOKEN(STR_REMOVE_SEL_HELP), + text = STRING_TOKEN(STR_REMOVE_SEL_TEXT), + flags = INTERACTIVE, + key = MAIN_REMOVE_RD_QUESTION_ID; + + endform; + + // + // Form #2 "Add New Raw RAM Disk" + // + form formid = CREATE_RAW_RAM_DISK_FORM_ID, + title = STRING_TOKEN(STR_ADD_RAW_FORM_TITLE); + + subtitle text = STRING_TOKEN(STR_RAM_DISK_NULL_STRING); + + numeric + questionid = CREATE_RAW_SIZE_QUESTION_ID, + prompt = STRING_TOKEN(STR_SIZE_PROMPT), + help = STRING_TOKEN(STR_SIZE_HELP), + flags = NUMERIC_SIZE_8 | DISPLAY_UINT_HEX | INTERACTIVE, + minimum = 1, + maximum = 0xFFFFFFFFFFFFFFFF, + endnumeric; + + subtitle text = STRING_TOKEN(STR_RAM_DISK_NULL_STRING); + + text + help = STRING_TOKEN(STR_CREATE_AND_EXIT_HELP), + text = STRING_TOKEN(STR_CREATE_AND_EXIT_PROMPT), + flags = INTERACTIVE, + key = CREATE_RAW_SUBMIT_QUESTION_ID; + + text + help = STRING_TOKEN(STR_DISCARD_AND_EXIT_HELP), + text = STRING_TOKEN(STR_DISCARD_AND_EXIT_PROMPT), + flags = INTERACTIVE, + key = CREATE_RAW_DISCARD_QUESTION_ID; + + endform; + +endformset; diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHiiStrings.uni b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHiiStrings.uni new file mode 100644 index 000000000..5ee1172c4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskHiiStrings.uni @@ -0,0 +1,41 @@ +// /** @file +// String definitions for RamDiskDxe driver form. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#langdef en-US "English" + +#string STR_FORM_SET_TITLE #language en-US "RAM Disk Configuration" +#string STR_FORM_SET_TITLE_HELP #language en-US "Press to add/remove RAM disks." + +#string STR_MAIN_FORM_TITLE #language en-US "RAM Disk HII Main Screen" +#string STR_RAM_DISK_NULL_STRING #language en-US "" + +#string STR_RAM_DISK_LIST_TEXT #language en-US "Created RAM disk list:" +#string STR_RAM_DISK_LIST_HELP #language en-US "Select for remove" +#string STR_GOTO_ADD_RAW_FORM #language en-US "Create raw" +#string STR_GOTO_ADD_RAW_FORM_HELP #language en-US "Create a raw RAM disk." +#string STR_GOTO_ADD_FROM_FILE_FORM #language en-US "Create from file" +#string STR_GOTO_ADD_FROM_FILE_FORM_HELP #language en-US "Create a RAM disk from a given file." +#string STR_REMOVE_SEL_HELP #language en-US "Remove selected RAM disk(s)" +#string STR_REMOVE_SEL_TEXT #language en-US "Remove selected RAM disk(s)." + +#string STR_ADD_RAW_FORM_TITLE #language en-US "Add A Raw RAM Disk" +#string STR_ADD_RAW_FORM_SUBTITLE_TEXT #language en-US " " + +#string STR_SIZE_PROMPT #language en-US "Size (Hex):" +#string STR_SIZE_HELP #language en-US "The valid RAM disk size should be multiples of the RAM disk block size." + +#string STR_MEMORY_TYPE_PROMPT #language en-US "Disk Memory Type:" +#string STR_MEMORY_TYPE_HELP #language en-US "Specifies type of memory to use from available memory pool in system to create a disk." +#string STR_RAM_DISK_BOOT_SERVICE_DATA_MEMORY #language en-US "Boot Service Data" +#string STR_RAM_DISK_RESERVED_MEMORY #language en-US "Reserved" + +#string STR_CREATE_AND_EXIT_HELP #language en-US "Create a new RAM disk with the given starting and ending address." +#string STR_CREATE_AND_EXIT_PROMPT #language en-US "Create & Exit" +#string STR_DISCARD_AND_EXIT_HELP #language en-US "Discard and exit." +#string STR_DISCARD_AND_EXIT_PROMPT #language en-US "Discard & Exit" diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.c b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.c new file mode 100644 index 000000000..e35b8fa22 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.c @@ -0,0 +1,758 @@ +/** @file + HII Config Access protocol implementation of RamDiskDxe driver. + + Copyright (c) 2016, Intel Corporation. All rights reserved.
+ (C) Copyright 2016-2018 Hewlett Packard Enterprise Development LP
+ Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RamDiskImpl.h" + +CHAR16 mRamDiskStorageName[] = L"RAM_DISK_CONFIGURATION"; + +RAM_DISK_CONFIG_PRIVATE_DATA mRamDiskConfigPrivateDataTemplate = { + RAM_DISK_CONFIG_PRIVATE_DATA_SIGNATURE, + { + EFI_PAGE_SIZE, + RAM_DISK_BOOT_SERVICE_DATA_MEMORY + }, + { + RamDiskExtractConfig, + RamDiskRouteConfig, + RamDiskCallback + } +}; + +HII_VENDOR_DEVICE_PATH mRamDiskHiiVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + RAM_DISK_FORM_SET_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + + +/** + This function publish the RAM disk configuration Form. + + @param[in, out] ConfigPrivateData + Points to RAM disk configuration private data. + + @retval EFI_SUCCESS HII Form is installed successfully. + @retval EFI_OUT_OF_RESOURCES Not enough resource for HII Form installation. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +InstallRamDiskConfigForm ( + IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivateData + ) +{ + EFI_STATUS Status; + EFI_HII_HANDLE HiiHandle; + EFI_HANDLE DriverHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; + + DriverHandle = NULL; + ConfigAccess = &ConfigPrivateData->ConfigAccess; + Status = gBS->InstallMultipleProtocolInterfaces ( + &DriverHandle, + &gEfiDevicePathProtocolGuid, + &mRamDiskHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + ConfigAccess, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ConfigPrivateData->DriverHandle = DriverHandle; + + // + // Publish the HII package list + // + HiiHandle = HiiAddPackages ( + &gRamDiskFormSetGuid, + DriverHandle, + RamDiskDxeStrings, + RamDiskHiiBin, + NULL + ); + if (HiiHandle == NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + DriverHandle, + &gEfiDevicePathProtocolGuid, + &mRamDiskHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + ConfigAccess, + NULL + ); + return EFI_OUT_OF_RESOURCES; + } + + ConfigPrivateData->HiiHandle = HiiHandle; + + return EFI_SUCCESS; +} + + +/** + This function removes RAM disk configuration Form. + + @param[in, out] ConfigPrivateData + Points to RAM disk configuration private data. + +**/ +VOID +UninstallRamDiskConfigForm ( + IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivateData + ) +{ + // + // Uninstall HII package list + // + if (ConfigPrivateData->HiiHandle != NULL) { + HiiRemovePackages (ConfigPrivateData->HiiHandle); + ConfigPrivateData->HiiHandle = NULL; + } + + // + // Uninstall HII Config Access Protocol + // + if (ConfigPrivateData->DriverHandle != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + ConfigPrivateData->DriverHandle, + &gEfiDevicePathProtocolGuid, + &mRamDiskHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &ConfigPrivateData->ConfigAccess, + NULL + ); + ConfigPrivateData->DriverHandle = NULL; + } + + FreePool (ConfigPrivateData); +} + + +/** + Unregister all registered RAM disks. + +**/ +VOID +UnregisterAllRamDisks ( + VOID + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + RAM_DISK_PRIVATE_DATA *PrivateData; + + if (!IsListEmpty(&RegisteredRamDisks)) { + BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &RegisteredRamDisks) { + PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); + + gBS->UninstallMultipleProtocolInterfaces ( + PrivateData->Handle, + &gEfiBlockIoProtocolGuid, + &PrivateData->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &PrivateData->BlockIo2, + &gEfiDevicePathProtocolGuid, + (EFI_DEVICE_PATH_PROTOCOL *) PrivateData->DevicePath, + NULL + ); + + RemoveEntryList (&PrivateData->ThisInstance); + + if (RamDiskCreateHii == PrivateData->CreateMethod) { + // + // If a RAM disk is created within HII, then the RamDiskDxe driver + // driver is responsible for freeing the allocated memory for the + // RAM disk. + // + FreePool ((VOID *)(UINTN) PrivateData->StartingAddr); + } + + FreePool (PrivateData->DevicePath); + FreePool (PrivateData); + } + } +} + + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Request A null-terminated Unicode string in + format. + @param[out] Progress On return, points to a character in the Request + string. Points to the string's null terminator if + request was successful. Points to the most recent + '&' before the first failing name/value pair (or + the beginning of the string if the failure is in + the first name/value pair) if the request was not + successful. + @param[out] Results A null-terminated Unicode string in + format which has all values filled + in for the names in the Request string. String to + be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested + values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in + this driver. + +**/ +EFI_STATUS +EFIAPI +RamDiskExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + *Progress = Request; + return EFI_NOT_FOUND; +} + + +/** + This function processes the results of changes in configuration. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Configuration A null-terminated Unicode string in + format. + @param[out] Progress A pointer to a string filled in with the offset of + the most recent '&' before the first failing + name/value pair (or the beginning of the string if + the failure is in the first name/value pair) or + the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in + this driver. + +**/ +EFI_STATUS +EFIAPI +RamDiskRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Configuration; + + return EFI_NOT_FOUND; +} + + +/** + Allocate memory and register the RAM disk created within RamDiskDxe + driver HII. + + @param[in] Size If creating raw, size of the RAM disk to create. + If creating from file, zero. + @param[in] FileHandle If creating raw, NULL. If creating from file, the + file handle. + @param[in] MemoryType Type of memory to be used to create RAM Disk. + + @retval EFI_SUCCESS RAM disk is created and registered. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to match the + size required. + +**/ +EFI_STATUS +HiiCreateRamDisk ( + IN UINT64 Size, + IN EFI_FILE_HANDLE FileHandle, + IN UINT8 MemoryType + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + UINT64 *StartingAddr; + EFI_INPUT_KEY Key; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + RAM_DISK_PRIVATE_DATA *PrivateData; + EFI_FILE_INFO *FileInformation; + + FileInformation = NULL; + StartingAddr = NULL; + + if (FileHandle != NULL) { + // + // Create from file. + // + FileInformation = FileInfo (FileHandle); + if (NULL == FileInformation) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Not enough memory to get the file information!", + L"Press ENTER to continue ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Update the size of RAM disk according to the file size. + // + Size = FileInformation->FileSize; + } + + if (Size > (UINTN) -1) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"The given RAM disk size is too large!", + L"Press ENTER to continue ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + return EFI_OUT_OF_RESOURCES; + } + + if (MemoryType == RAM_DISK_BOOT_SERVICE_DATA_MEMORY) { + Status = gBS->AllocatePool ( + EfiBootServicesData, + (UINTN)Size, + (VOID**)&StartingAddr + ); + } else if (MemoryType == RAM_DISK_RESERVED_MEMORY) { + Status = gBS->AllocatePool ( + EfiReservedMemoryType, + (UINTN)Size, + (VOID**)&StartingAddr + ); + } else { + Status = EFI_INVALID_PARAMETER; + } + + if ((StartingAddr == NULL) || EFI_ERROR(Status)) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Not enough memory to create the RAM disk!", + L"Press ENTER to continue ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + return EFI_OUT_OF_RESOURCES; + } + + if (FileHandle != NULL) { + // + // Copy the file content to the RAM disk. + // + BufferSize = (UINTN) Size; + FileHandle->Read ( + FileHandle, + &BufferSize, + (VOID *)(UINTN) StartingAddr + ); + if (BufferSize != FileInformation->FileSize) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"File content read error!", + L"Press ENTER to continue ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + return EFI_DEVICE_ERROR; + } + } + + // + // Register the newly created RAM disk. + // + Status = RamDiskRegister ( + ((UINT64)(UINTN) StartingAddr), + Size, + &gEfiVirtualDiskGuid, + NULL, + &DevicePath + ); + if (EFI_ERROR (Status)) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Fail to register the newly created RAM disk!", + L"Press ENTER to continue ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + return Status; + } + + // + // If RAM disk is created within HII, memory should be freed when the + // RAM disk is unregisterd. + // + PrivateData = RAM_DISK_PRIVATE_FROM_THIS (RegisteredRamDisks.BackLink); + PrivateData->CreateMethod = RamDiskCreateHii; + + return EFI_SUCCESS; +} + + +/** + This function updates the registered RAM disks list on the main form. + + @param[in, out] ConfigPrivate + Private data for configurating hii data for RAM + disks. + +**/ +VOID +UpdateMainForm ( + IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivate + ) +{ + VOID *StartOpCodeHandle; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + EFI_IFR_GUID_LABEL *EndLabel; + LIST_ENTRY *Entry; + UINTN Index; + RAM_DISK_PRIVATE_DATA *PrivateData; + CHAR16 *String; + CHAR16 RamDiskStr[128]; + EFI_STRING_ID StringId; + + // + // Init OpCode Handle + // + StartOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (StartOpCodeHandle != NULL); + + EndOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (EndOpCodeHandle != NULL); + + // + // Create Hii Extend Label OpCode as the start opcode + // + StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + StartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + StartLabel->Number = MAIN_LABEL_LIST_START; + + // + // Create Hii Extend Label OpCode as the end opcode + // + EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + EndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + EndLabel->Number = MAIN_LABEL_LIST_END; + + Index = 0; + BASE_LIST_FOR_EACH (Entry, &RegisteredRamDisks) { + PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); + PrivateData->CheckBoxId = (EFI_QUESTION_ID) + (MAIN_CHECKBOX_QUESTION_ID_START + Index); + // + // CheckBox is unchecked by default. + // + PrivateData->CheckBoxChecked = FALSE; + String = RamDiskStr; + + UnicodeSPrint ( + String, + sizeof (RamDiskStr), + L" RAM Disk %d: [0x%lx, 0x%lx]\n", + Index, + PrivateData->StartingAddr, + PrivateData->StartingAddr + PrivateData->Size - 1 + ); + + StringId = HiiSetString (ConfigPrivate->HiiHandle, 0, RamDiskStr, NULL); + ASSERT (StringId != 0); + + HiiCreateCheckBoxOpCode ( + StartOpCodeHandle, + PrivateData->CheckBoxId, + 0, + 0, + StringId, + STRING_TOKEN (STR_RAM_DISK_LIST_HELP), + EFI_IFR_FLAG_CALLBACK, + 0, + NULL + ); + + Index++; + } + + HiiUpdateForm ( + ConfigPrivate->HiiHandle, + &gRamDiskFormSetGuid, + MAIN_FORM_ID, + StartOpCodeHandle, + EndOpCodeHandle + ); + + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); +} + + +/** + This function processes the results of changes in configuration. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Action Specifies the type of action taken by the browser. + @param[in] QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. + @param[in] Type The type of value for the question. + @param[in] Value A pointer to the data being sent to the original + exporting driver. + @param[out] ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. + +**/ +EFI_STATUS +EFIAPI +RamDiskCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + EFI_STATUS Status; + RAM_DISK_PRIVATE_DATA *PrivateData; + RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivate; + EFI_DEVICE_PATH_PROTOCOL *FileDevPath; + EFI_FILE_HANDLE FileHandle; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + if ((This == NULL) || (Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ConfigPrivate = RAM_DISK_CONFIG_PRIVATE_FROM_THIS (This); + + if (Action == EFI_BROWSER_ACTION_RETRIEVE) { + Status = EFI_UNSUPPORTED; + if (QuestionId == CREATE_RAW_SIZE_QUESTION_ID) { + Value->u64 = EFI_PAGE_SIZE; + ConfigPrivate->ConfigStore.Size = EFI_PAGE_SIZE; + Status = EFI_SUCCESS; + } else if (QuestionId == CREATE_RAW_MEMORY_TYPE_QUESTION_ID) { + Value->u8 = RAM_DISK_BOOT_SERVICE_DATA_MEMORY; + ConfigPrivate->ConfigStore.MemType = RAM_DISK_BOOT_SERVICE_DATA_MEMORY; + Status = EFI_SUCCESS; + } + return Status; + } + + if ((Action != EFI_BROWSER_ACTION_CHANGED) && + (Action != EFI_BROWSER_ACTION_CHANGING) && + (Action != EFI_BROWSER_ACTION_FORM_OPEN)) { + return EFI_UNSUPPORTED; + } + + // + // Update the RAM disk list show at the main form first. + // + if (Action == EFI_BROWSER_ACTION_FORM_OPEN) { + Status = EFI_UNSUPPORTED; + if (QuestionId == MAIN_GOTO_FILE_EXPLORER_ID) { + UpdateMainForm (ConfigPrivate); + Status = EFI_SUCCESS; + } + return Status; + } + + Status = EFI_SUCCESS; + + if (Action == EFI_BROWSER_ACTION_CHANGING) { + switch (QuestionId) { + case MAIN_GOTO_FILE_EXPLORER_ID: + Status = ChooseFile (NULL, NULL, NULL, &FileDevPath); + if (EFI_ERROR (Status)) { + break; + } + + if (FileDevPath != NULL) { + // + // Open the file. + // + Status = EfiOpenFileByDevicePath ( + &FileDevPath, + &FileHandle, + EFI_FILE_MODE_READ, + 0 + ); + if (EFI_ERROR (Status)) { + break; + } + + // + // Create from file, RAM disk size is zero. It will be updated + // according to the file size. + // + Status = HiiCreateRamDisk ( + 0, + FileHandle, + ConfigPrivate->ConfigStore.MemType + ); + if (EFI_ERROR (Status)) { + break; + } + + // + // Refresh the registered RAM disks list. + // + UpdateMainForm (ConfigPrivate); + } + break; + + default: + break; + } + } else if (Action == EFI_BROWSER_ACTION_CHANGED) { + switch (QuestionId) { + case MAIN_REMOVE_RD_QUESTION_ID: + // + // Remove the selected RAM disks + // + BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &RegisteredRamDisks) { + PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); + if (PrivateData->CheckBoxChecked) { + RamDiskUnregister ( + (EFI_DEVICE_PATH_PROTOCOL *) PrivateData->DevicePath + ); + } + } + + UpdateMainForm (ConfigPrivate); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + break; + + case CREATE_RAW_SIZE_QUESTION_ID: + ConfigPrivate->ConfigStore.Size = Value->u64; + break; + + case CREATE_RAW_MEMORY_TYPE_QUESTION_ID: + ConfigPrivate->ConfigStore.MemType = Value->u8; + break; + + case CREATE_RAW_SUBMIT_QUESTION_ID: + // + // Create raw, FileHandle is NULL. + // + Status = HiiCreateRamDisk ( + ConfigPrivate->ConfigStore.Size, + NULL, + ConfigPrivate->ConfigStore.MemType + ); + if (EFI_ERROR (Status)) { + break; + } + + // + // Refresh the registered RAM disks list. + // + UpdateMainForm (ConfigPrivate); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; + break; + + case CREATE_RAW_DISCARD_QUESTION_ID: + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; + break; + + default: + // + // QuestionIds for checkboxes + // + if ((QuestionId >= MAIN_CHECKBOX_QUESTION_ID_START) && + (QuestionId < CREATE_RAW_RAM_DISK_FORM_ID)) { + BASE_LIST_FOR_EACH (Entry, &RegisteredRamDisks) { + PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); + if (PrivateData->CheckBoxId == QuestionId) { + PrivateData->CheckBoxChecked = (BOOLEAN) (Value->u8 != 0); + } + } + } + break; + } + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.h b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.h new file mode 100644 index 000000000..ed80b47cc --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskImpl.h @@ -0,0 +1,604 @@ +/** @file + The header file of RamDiskDxe driver. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _RAM_DISK_IMPL_H_ +#define _RAM_DISK_IMPL_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 "RamDiskNVData.h" + +/// +/// RAM disk general definitions and declarations +/// + +// +// Default block size for RAM disk +// +#define RAM_DISK_DEFAULT_BLOCK_SIZE 512 + +// +// RamDiskDxe driver maintains a list of registered RAM disks. +// +extern LIST_ENTRY RegisteredRamDisks; + +// +// Pointers to the EFI_ACPI_TABLE_PROTOCOL and EFI_ACPI_SDT_PROTOCOL. +// +extern EFI_ACPI_TABLE_PROTOCOL *mAcpiTableProtocol; +extern EFI_ACPI_SDT_PROTOCOL *mAcpiSdtProtocol; + +// +// RAM Disk create method. +// +typedef enum _RAM_DISK_CREATE_METHOD { + RamDiskCreateOthers = 0, + RamDiskCreateHii +} RAM_DISK_CREATE_METHOD; + +// +// RamDiskDxe driver maintains a list of registered RAM disks. +// The struct contains the list entry and the information of each RAM +// disk +// +typedef struct { + UINTN Signature; + + EFI_HANDLE Handle; + + EFI_BLOCK_IO_PROTOCOL BlockIo; + EFI_BLOCK_IO2_PROTOCOL BlockIo2; + EFI_BLOCK_IO_MEDIA Media; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + UINT64 StartingAddr; + UINT64 Size; + EFI_GUID TypeGuid; + UINT16 InstanceNumber; + RAM_DISK_CREATE_METHOD CreateMethod; + BOOLEAN InNfit; + EFI_QUESTION_ID CheckBoxId; + BOOLEAN CheckBoxChecked; + + LIST_ENTRY ThisInstance; +} RAM_DISK_PRIVATE_DATA; + +#define RAM_DISK_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('R', 'D', 'S', 'K') +#define RAM_DISK_PRIVATE_FROM_BLKIO(a) CR (a, RAM_DISK_PRIVATE_DATA, BlockIo, RAM_DISK_PRIVATE_DATA_SIGNATURE) +#define RAM_DISK_PRIVATE_FROM_BLKIO2(a) CR (a, RAM_DISK_PRIVATE_DATA, BlockIo2, RAM_DISK_PRIVATE_DATA_SIGNATURE) +#define RAM_DISK_PRIVATE_FROM_THIS(a) CR (a, RAM_DISK_PRIVATE_DATA, ThisInstance, RAM_DISK_PRIVATE_DATA_SIGNATURE) + +/// +/// RAM disk HII-related definitions and declarations +/// + +// +// Tool generated IFR binary data and String package data +// +extern UINT8 RamDiskHiiBin[]; +extern UINT8 RamDiskDxeStrings[]; + +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +typedef struct { + UINTN Signature; + + RAM_DISK_CONFIGURATION ConfigStore; + + EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess; + EFI_HANDLE DriverHandle; + EFI_HII_HANDLE HiiHandle; +} RAM_DISK_CONFIG_PRIVATE_DATA; + +extern RAM_DISK_CONFIG_PRIVATE_DATA mRamDiskConfigPrivateDataTemplate; + +#define RAM_DISK_CONFIG_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('R', 'C', 'F', 'G') +#define RAM_DISK_CONFIG_PRIVATE_FROM_THIS(a) CR (a, RAM_DISK_CONFIG_PRIVATE_DATA, ConfigAccess, RAM_DISK_CONFIG_PRIVATE_DATA_SIGNATURE) + +/** + Register a RAM disk with specified address, size and type. + + @param[in] RamDiskBase The base address of registered RAM disk. + @param[in] RamDiskSize The size of registered RAM disk. + @param[in] RamDiskType The type of registered RAM disk. The GUID can be + any of the values defined in section 9.3.6.9, or a + vendor defined GUID. + @param[in] ParentDevicePath + Pointer to the parent device path. If there is no + parent device path then ParentDevicePath is NULL. + @param[out] DevicePath On return, points to a pointer to the device path + of the RAM disk device. + If ParentDevicePath is not NULL, the returned + DevicePath is created by appending a RAM disk node + to the parent device path. If ParentDevicePath is + NULL, the returned DevicePath is a RAM disk device + path without appending. This function is + responsible for allocating the buffer DevicePath + with the boot service AllocatePool(). + + @retval EFI_SUCCESS The RAM disk is registered successfully. + @retval EFI_INVALID_PARAMETER DevicePath or RamDiskType is NULL. + RamDiskSize is 0. + @retval EFI_ALREADY_STARTED A Device Path Protocol instance to be created + is already present in the handle database. + @retval EFI_OUT_OF_RESOURCES The RAM disk register operation fails due to + resource limitation. + +**/ +EFI_STATUS +EFIAPI +RamDiskRegister ( + IN UINT64 RamDiskBase, + IN UINT64 RamDiskSize, + IN EFI_GUID *RamDiskType, + IN EFI_DEVICE_PATH *ParentDevicePath OPTIONAL, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + Unregister a RAM disk specified by DevicePath. + + @param[in] DevicePath A pointer to the device path that describes a RAM + Disk device. + + @retval EFI_SUCCESS The RAM disk is unregistered successfully. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_UNSUPPORTED The device specified by DevicePath is not a + valid ramdisk device path and not supported + by the driver. + @retval EFI_NOT_FOUND The RAM disk pointed by DevicePath doesn't + exist. + +**/ +EFI_STATUS +EFIAPI +RamDiskUnregister ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +/** + Initialize the BlockIO protocol of a RAM disk device. + + @param[in] PrivateData Points to RAM disk private data. + +**/ +VOID +RamDiskInitBlockIo ( + IN RAM_DISK_PRIVATE_DATA *PrivateData + ); + +/** + Reset the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification + Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and + could not be reset. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is + replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in] BufferSize Size of Buffer, must be a multiple of device block + size. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for either having + implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current + device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block + size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. + The caller is responsible for writing to only + legitimate locations. + @param[in] BufferSize Size of Buffer, must be a multiple of device block + size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current + device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block + size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not + valid, or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + @param[in] This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device. + @retval EFI_DEVICE_ERROR The device reported an error while writting + back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +/** + Resets the block device hardware. + + @param[in] This The pointer of EFI_BLOCK_IO2_PROTOCOL. + @param[in] ExtendedVerification The flag about if extend verificate. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The block device is not functioning correctly + and could not be reset. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2Reset ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the requested number of blocks from the device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the read request is for. + @param[in] Lba The starting logical block address to read + from on the device. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] BufferSize The size of the Buffer in bytes. This must be + a multiple of the intrinsic block size of the + device. + @param[out] Buffer A pointer to the destination buffer for the + data. The caller is responsible for either + having implicit or explicit ownership of the + buffer. + + @retval EFI_SUCCESS The read request was queued if Token->Event + is not NULL. The data was read correctly from + the device if the Token->Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2ReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Writes a specified number of blocks to the device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be + written. The caller is responsible for + writing to only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] BufferSize The size in bytes of Buffer. This must be a + multiple of the intrinsic block size of the + device. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The write request was queued if Event is not + NULL. The data was written correctly to the + device if the Event is NULL. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the write operation. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2WriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flushes all modified data to a physical block device. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the + transaction. + + @retval EFI_SUCCESS The flush request was queued if Event is not + NULL. All outstanding data was written + correctly to the device if the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to write data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +RamDiskBlkIo2FlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + This function publish the RAM disk configuration Form. + + @param[in, out] ConfigPrivateData + Points to RAM disk configuration private data. + + @retval EFI_SUCCESS HII Form is installed successfully. + @retval EFI_OUT_OF_RESOURCES Not enough resource for HII Form installation. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +InstallRamDiskConfigForm ( + IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivateData + ); + +/** + This function removes RAM disk configuration Form. + + @param[in, out] ConfigPrivateData + Points to RAM disk configuration private data. + +**/ +VOID +UninstallRamDiskConfigForm ( + IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivateData + ); + +/** + Unregister all registered RAM disks. + +**/ +VOID +UnregisterAllRamDisks ( + VOID + ); + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Request A null-terminated Unicode string in + format. + @param[out] Progress On return, points to a character in the Request + string. Points to the string's null terminator if + request was successful. Points to the most recent + '&' before the first failing name/value pair (or + the beginning of the string if the failure is in + the first name/value pair) if the request was not + successful. + @param[out] Results A null-terminated Unicode string in + format which has all values filled + in for the names in the Request string. String to + be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested + values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in + this driver. + +**/ +EFI_STATUS +EFIAPI +RamDiskExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ); + +/** + This function processes the results of changes in configuration. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Configuration A null-terminated Unicode string in + format. + @param[out] Progress A pointer to a string filled in with the offset of + the most recent '&' before the first failing + name/value pair (or the beginning of the string if + the failure is in the first name/value pair) or + the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in + this driver. + +**/ +EFI_STATUS +EFIAPI +RamDiskRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ); + +/** + This function processes the results of changes in configuration. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Action Specifies the type of action taken by the browser. + @param[in] QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. + @param[in] Type The type of value for the question. + @param[in] Value A pointer to the data being sent to the original + exporting driver. + @param[out] ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. + +**/ +EFI_STATUS +EFIAPI +RamDiskCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ); + + +/** + This function gets the file information from an open file descriptor, + and stores it in a buffer allocated from pool. + + @param[in] FHand File Handle. + + @return A pointer to a buffer with file information or NULL is returned. + +**/ +EFI_FILE_INFO * +FileInfo ( + IN EFI_FILE_HANDLE FHand + ); + + +/** + Publish the RAM disk NVDIMM Firmware Interface Table (NFIT) to the ACPI + table. + + @param[in] PrivateData Points to RAM disk private data. + + @retval EFI_SUCCESS The RAM disk NFIT has been published. + @retval others The RAM disk NFIT has not been published. + +**/ +EFI_STATUS +RamDiskPublishNfit ( + IN RAM_DISK_PRIVATE_DATA *PrivateData + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskNVData.h b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskNVData.h new file mode 100644 index 000000000..3293ec1d5 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskNVData.h @@ -0,0 +1,44 @@ +/** @file + Header file for NV data structure definition. + + Copyright (c) 2016, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _RAM_DISK_NVDATA_H_ +#define _RAM_DISK_NVDATA_H_ + +#include +#include + +#define MAIN_FORM_ID 0x1000 +#define MAIN_GOTO_FILE_EXPLORER_ID 0x1001 +#define MAIN_REMOVE_RD_QUESTION_ID 0x1002 +#define MAIN_LABEL_LIST_START 0x1003 +#define MAIN_LABEL_LIST_END 0x1004 +#define MAIN_CHECKBOX_QUESTION_ID_START 0x1100 + +#define CREATE_RAW_RAM_DISK_FORM_ID 0x2000 +#define CREATE_RAW_SIZE_QUESTION_ID 0x2001 +#define CREATE_RAW_SUBMIT_QUESTION_ID 0x2002 +#define CREATE_RAW_DISCARD_QUESTION_ID 0x2003 +#define CREATE_RAW_MEMORY_TYPE_QUESTION_ID 0x2004 + +#define RAM_DISK_BOOT_SERVICE_DATA_MEMORY 0x00 +#define RAM_DISK_RESERVED_MEMORY 0x01 +#define RAM_DISK_MEMORY_TYPE_MAX 0x02 + +typedef struct { + // + // The size of the RAM disk to be created. + // + UINT64 Size; + // + // Selected RAM Disk Memory Type + // + UINT8 MemType; +} RAM_DISK_CONFIGURATION; + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskProtocol.c b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskProtocol.c new file mode 100644 index 000000000..4333e0005 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskProtocol.c @@ -0,0 +1,857 @@ +/** @file + The realization of EFI_RAM_DISK_PROTOCOL. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RamDiskImpl.h" + +RAM_DISK_PRIVATE_DATA mRamDiskPrivateDataTemplate = { + RAM_DISK_PRIVATE_DATA_SIGNATURE, + NULL +}; + +MEDIA_RAM_DISK_DEVICE_PATH mRamDiskDeviceNodeTemplate = { + { + MEDIA_DEVICE_PATH, + MEDIA_RAM_DISK_DP, + { + (UINT8) (sizeof (MEDIA_RAM_DISK_DEVICE_PATH)), + (UINT8) ((sizeof (MEDIA_RAM_DISK_DEVICE_PATH)) >> 8) + } + } +}; + +BOOLEAN mRamDiskSsdtTableKeyValid = FALSE; +UINTN mRamDiskSsdtTableKey; + + +/** + Initialize the RAM disk device node. + + @param[in] PrivateData Points to RAM disk private data. + @param[in, out] RamDiskDevNode Points to the RAM disk device node. + +**/ +VOID +RamDiskInitDeviceNode ( + IN RAM_DISK_PRIVATE_DATA *PrivateData, + IN OUT MEDIA_RAM_DISK_DEVICE_PATH *RamDiskDevNode + ) +{ + WriteUnaligned64 ( + (UINT64 *) &(RamDiskDevNode->StartingAddr[0]), + (UINT64) PrivateData->StartingAddr + ); + WriteUnaligned64 ( + (UINT64 *) &(RamDiskDevNode->EndingAddr[0]), + (UINT64) PrivateData->StartingAddr + PrivateData->Size - 1 + ); + CopyGuid (&RamDiskDevNode->TypeGuid, &PrivateData->TypeGuid); + RamDiskDevNode->Instance = PrivateData->InstanceNumber; +} + + +/** + Initialize and publish NVDIMM root device SSDT in ACPI table. + + @retval EFI_SUCCESS The NVDIMM root device SSDT is published. + @retval Others The NVDIMM root device SSDT is not published. + +**/ +EFI_STATUS +RamDiskPublishSsdt ( + VOID + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER *Table; + UINTN SectionInstance; + UINTN TableSize; + + Status = EFI_SUCCESS; + SectionInstance = 0; + + // + // Scan all the EFI raw section instances in FV to find the NVDIMM root + // device SSDT. + // + while (TRUE) { + Status = GetSectionFromFv ( + &gEfiCallerIdGuid, + EFI_SECTION_RAW, + SectionInstance, + (VOID **) &Table, + &TableSize + ); + if (EFI_ERROR (Status)) { + break; + } + + if (Table->OemTableId == SIGNATURE_64 ('R', 'a', 'm', 'D', 'i', 's', 'k', ' ')) { + Status = mAcpiTableProtocol->InstallAcpiTable ( + mAcpiTableProtocol, + Table, + TableSize, + &mRamDiskSsdtTableKey + ); + ASSERT_EFI_ERROR (Status); + + if (!EFI_ERROR (Status)) { + mRamDiskSsdtTableKeyValid = TRUE; + } + + FreePool (Table); + return Status; + } else { + FreePool (Table); + SectionInstance++; + } + } + + return Status; +} + + +/** + Publish the RAM disk NVDIMM Firmware Interface Table (NFIT) to the ACPI + table. + + @param[in] PrivateData Points to RAM disk private data. + + @retval EFI_SUCCESS The RAM disk NFIT has been published. + @retval others The RAM disk NFIT has not been published. + +**/ +EFI_STATUS +RamDiskPublishNfit ( + IN RAM_DISK_PRIVATE_DATA *PrivateData + ) +{ + EFI_STATUS Status; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + UINTN TableIndex; + VOID *TableHeader; + EFI_ACPI_TABLE_VERSION TableVersion; + UINTN TableKey; + EFI_ACPI_DESCRIPTION_HEADER *NfitHeader; + EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE + *SpaRange; + VOID *Nfit; + UINT32 NfitLen; + UINTN MemoryMapSize; + UINTN MapKey; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + UINT64 CurrentData; + UINT8 Checksum; + BOOLEAN MemoryFound; + + // + // Get the EFI memory map. + // + MemoryMapSize = 0; + MemoryMap = NULL; + MemoryFound = FALSE; + + Status = gBS->GetMemoryMap ( + &MemoryMapSize, + MemoryMap, + &MapKey, + &DescriptorSize, + &DescriptorVersion + ); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + do { + MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize); + ASSERT (MemoryMap != NULL); + Status = gBS->GetMemoryMap ( + &MemoryMapSize, + MemoryMap, + &MapKey, + &DescriptorSize, + &DescriptorVersion + ); + if (EFI_ERROR (Status)) { + FreePool (MemoryMap); + } + } while (Status == EFI_BUFFER_TOO_SMALL); + ASSERT_EFI_ERROR (Status); + + MemoryMapEntry = MemoryMap; + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize); + while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) { + if ((MemoryMapEntry->Type == EfiReservedMemoryType) && + (MemoryMapEntry->PhysicalStart <= PrivateData->StartingAddr) && + (MemoryMapEntry->PhysicalStart + + MultU64x32 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SIZE) + >= PrivateData->StartingAddr + PrivateData->Size)) { + MemoryFound = TRUE; + DEBUG (( + EFI_D_INFO, + "RamDiskPublishNfit: RAM disk with reserved meomry type, will publish to NFIT.\n" + )); + break; + } + MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + } + FreePool (MemoryMap); + + if (!MemoryFound) { + return EFI_NOT_FOUND; + } + + // + // Determine whether there is a NFIT already in the ACPI table. + // + Status = EFI_SUCCESS; + TableIndex = 0; + TableKey = 0; + TableHeader = NULL; + + while (!EFI_ERROR (Status)) { + Status = mAcpiSdtProtocol->GetAcpiTable ( + TableIndex, + (EFI_ACPI_SDT_HEADER **)&TableHeader, + &TableVersion, + &TableKey + ); + if (!EFI_ERROR (Status)) { + TableIndex++; + + if (((EFI_ACPI_SDT_HEADER *)TableHeader)->Signature == + EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE) { + break; + } + } + } + + if (!EFI_ERROR (Status)) { + // + // A NFIT is already in the ACPI table. + // + DEBUG (( + EFI_D_INFO, + "RamDiskPublishNfit: A NFIT is already exist in the ACPI Table.\n" + )); + + NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)TableHeader; + NfitLen = NfitHeader->Length + sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE); + Nfit = AllocateZeroPool (NfitLen); + if (Nfit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Nfit, TableHeader, NfitHeader->Length); + + // + // Update the NFIT head pointer. + // + NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)Nfit; + + // + // Uninstall the origin NFIT from the ACPI table. + // + Status = mAcpiTableProtocol->UninstallAcpiTable ( + mAcpiTableProtocol, + TableKey + ); + ASSERT_EFI_ERROR (Status); + + if (EFI_ERROR (Status)) { + FreePool (Nfit); + return Status; + } + + // + // Append the System Physical Address (SPA) Range Structure at the end + // of the origin NFIT. + // + SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *) + ((UINT8 *)Nfit + NfitHeader->Length); + + // + // Update the length field of the NFIT + // + NfitHeader->Length = NfitLen; + + // + // The checksum will be updated after the new contents are appended. + // + NfitHeader->Checksum = 0; + } else { + // + // Assumption is made that if no NFIT is in the ACPI table, there is no + // NVDIMM root device in the \SB scope. + // Therefore, a NVDIMM root device will be reported via Secondary System + // Description Table (SSDT). + // + Status = RamDiskPublishSsdt (); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // No NFIT is in the ACPI table, we will create one here. + // + DEBUG (( + EFI_D_INFO, + "RamDiskPublishNfit: No NFIT is in the ACPI Table, will create one.\n" + )); + + NfitLen = sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE) + + sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE); + Nfit = AllocateZeroPool (NfitLen); + if (Nfit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *) + ((UINT8 *)Nfit + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE)); + + NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)Nfit; + NfitHeader->Signature = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE; + NfitHeader->Length = NfitLen; + NfitHeader->Revision = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_REVISION; + NfitHeader->Checksum = 0; + NfitHeader->OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + NfitHeader->CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + NfitHeader->CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + CurrentData = PcdGet64 (PcdAcpiDefaultOemTableId); + CopyMem (NfitHeader->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (NfitHeader->OemId)); + CopyMem (&NfitHeader->OemTableId, &CurrentData, sizeof (UINT64)); + } + + // + // Fill in the content of the SPA Range Structure. + // + SpaRange->Type = EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE_TYPE; + SpaRange->Length = sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE); + SpaRange->SystemPhysicalAddressRangeBase = PrivateData->StartingAddr; + SpaRange->SystemPhysicalAddressRangeLength = PrivateData->Size; + CopyGuid (&SpaRange->AddressRangeTypeGUID, &PrivateData->TypeGuid); + + Checksum = CalculateCheckSum8((UINT8 *)Nfit, NfitHeader->Length); + NfitHeader->Checksum = Checksum; + + // + // Publish the NFIT to the ACPI table. + // Note, since the NFIT might be modified by other driver, therefore, we + // do not track the returning TableKey from the InstallAcpiTable(). + // + Status = mAcpiTableProtocol->InstallAcpiTable ( + mAcpiTableProtocol, + Nfit, + NfitHeader->Length, + &TableKey + ); + ASSERT_EFI_ERROR (Status); + + FreePool (Nfit); + + if (EFI_ERROR (Status)) { + return Status; + } + + PrivateData->InNfit = TRUE; + + return EFI_SUCCESS; +} + + +/** + Unpublish the RAM disk NVDIMM Firmware Interface Table (NFIT) from the + ACPI table. + + @param[in] PrivateData Points to RAM disk private data. + + @retval EFI_SUCCESS The RAM disk NFIT has been unpublished. + @retval others The RAM disk NFIT has not been unpublished. + +**/ +EFI_STATUS +RamDiskUnpublishNfit ( + IN RAM_DISK_PRIVATE_DATA *PrivateData + ) +{ + EFI_STATUS Status; + UINTN TableIndex; + VOID *TableHeader; + EFI_ACPI_TABLE_VERSION TableVersion; + UINTN TableKey; + EFI_ACPI_DESCRIPTION_HEADER *NewNfitHeader; + EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE + *SpaRange; + VOID *NewNfit; + VOID *NewNfitPtr; + EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *NfitStructHeader; + UINT32 NewNfitLen; + UINT32 RemainLen; + UINT8 Checksum; + + // + // Find the NFIT in the ACPI table. + // + Status = EFI_SUCCESS; + TableIndex = 0; + TableKey = 0; + TableHeader = NULL; + + while (!EFI_ERROR (Status)) { + Status = mAcpiSdtProtocol->GetAcpiTable ( + TableIndex, + (EFI_ACPI_SDT_HEADER **)&TableHeader, + &TableVersion, + &TableKey + ); + if (!EFI_ERROR (Status)) { + TableIndex++; + + if (((EFI_ACPI_SDT_HEADER *)TableHeader)->Signature == + EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE) { + break; + } + } + } + + if (EFI_ERROR (Status)) { + // + // No NFIT is found in the ACPI table. + // + return EFI_NOT_FOUND; + } + + NewNfitLen = ((EFI_ACPI_DESCRIPTION_HEADER *)TableHeader)->Length - + sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE); + + // + // After removing this RAM disk from the NFIT, if no other structure is in + // the NFIT, we just remove the NFIT and the SSDT which is used to report + // the NVDIMM root device. + // + if (NewNfitLen == sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE)) { + // + // Remove the NFIT. + // + Status = mAcpiTableProtocol->UninstallAcpiTable ( + mAcpiTableProtocol, + TableKey + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Remove the SSDT which is used by RamDiskDxe driver to report the NVDIMM + // root device. + // We do not care the return status since this SSDT might already be + // uninstalled by other drivers to update the information of the NVDIMM + // root device. + // + if (mRamDiskSsdtTableKeyValid) { + mRamDiskSsdtTableKeyValid = FALSE; + + mAcpiTableProtocol->UninstallAcpiTable ( + mAcpiTableProtocol, + mRamDiskSsdtTableKey + ); + } + + return EFI_SUCCESS; + } + + NewNfit = AllocateZeroPool (NewNfitLen); + if (NewNfit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get a copy of the old NFIT header content. + // + CopyMem (NewNfit, TableHeader, sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE)); + NewNfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)NewNfit; + NewNfitHeader->Length = NewNfitLen; + NewNfitHeader->Checksum = 0; + + // + // Copy the content of required NFIT structures. + // + NewNfitPtr = (UINT8 *)NewNfit + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE); + RemainLen = NewNfitLen - sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE); + NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *) + ((UINT8 *)TableHeader + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE)); + while (RemainLen > 0) { + if ((NfitStructHeader->Type == EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE_TYPE) && + (NfitStructHeader->Length == sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE))) { + SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)NfitStructHeader; + + if ((SpaRange->SystemPhysicalAddressRangeBase == PrivateData->StartingAddr) && + (SpaRange->SystemPhysicalAddressRangeLength == PrivateData->Size) && + (CompareGuid (&SpaRange->AddressRangeTypeGUID, &PrivateData->TypeGuid))) { + // + // Skip the SPA Range Structure for the RAM disk to be unpublished + // from NFIT. + // + NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *) + ((UINT8 *)NfitStructHeader + NfitStructHeader->Length); + continue; + } + } + + // + // Copy the content of origin NFIT. + // + CopyMem (NewNfitPtr, NfitStructHeader, NfitStructHeader->Length); + NewNfitPtr = (UINT8 *)NewNfitPtr + NfitStructHeader->Length; + + // + // Move to the header of next NFIT structure. + // + RemainLen -= NfitStructHeader->Length; + NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *) + ((UINT8 *)NfitStructHeader + NfitStructHeader->Length); + } + + Checksum = CalculateCheckSum8((UINT8 *)NewNfit, NewNfitHeader->Length); + NewNfitHeader->Checksum = Checksum; + + Status = mAcpiTableProtocol->UninstallAcpiTable ( + mAcpiTableProtocol, + TableKey + ); + ASSERT_EFI_ERROR (Status); + + if (EFI_ERROR (Status)) { + FreePool (NewNfit); + return Status; + } + + // + // Publish the NFIT to the ACPI table. + // Note, since the NFIT might be modified by other driver, therefore, we + // do not track the returning TableKey from the InstallAcpiTable(). + // + Status = mAcpiTableProtocol->InstallAcpiTable ( + mAcpiTableProtocol, + NewNfit, + NewNfitLen, + &TableKey + ); + ASSERT_EFI_ERROR (Status); + + FreePool (NewNfit); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Register a RAM disk with specified address, size and type. + + @param[in] RamDiskBase The base address of registered RAM disk. + @param[in] RamDiskSize The size of registered RAM disk. + @param[in] RamDiskType The type of registered RAM disk. The GUID can be + any of the values defined in section 9.3.6.9, or a + vendor defined GUID. + @param[in] ParentDevicePath + Pointer to the parent device path. If there is no + parent device path then ParentDevicePath is NULL. + @param[out] DevicePath On return, points to a pointer to the device path + of the RAM disk device. + If ParentDevicePath is not NULL, the returned + DevicePath is created by appending a RAM disk node + to the parent device path. If ParentDevicePath is + NULL, the returned DevicePath is a RAM disk device + path without appending. This function is + responsible for allocating the buffer DevicePath + with the boot service AllocatePool(). + + @retval EFI_SUCCESS The RAM disk is registered successfully. + @retval EFI_INVALID_PARAMETER DevicePath or RamDiskType is NULL. + RamDiskSize is 0. + @retval EFI_ALREADY_STARTED A Device Path Protocol instance to be created + is already present in the handle database. + @retval EFI_OUT_OF_RESOURCES The RAM disk register operation fails due to + resource limitation. + +**/ +EFI_STATUS +EFIAPI +RamDiskRegister ( + IN UINT64 RamDiskBase, + IN UINT64 RamDiskSize, + IN EFI_GUID *RamDiskType, + IN EFI_DEVICE_PATH *ParentDevicePath OPTIONAL, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + EFI_STATUS Status; + RAM_DISK_PRIVATE_DATA *PrivateData; + RAM_DISK_PRIVATE_DATA *RegisteredPrivateData; + MEDIA_RAM_DISK_DEVICE_PATH *RamDiskDevNode; + UINTN DevicePathSize; + LIST_ENTRY *Entry; + + if ((0 == RamDiskSize) || (NULL == RamDiskType) || (NULL == DevicePath)) { + return EFI_INVALID_PARAMETER; + } + + // + // Add check to prevent data read across the memory boundary + // + if ((RamDiskSize > MAX_UINTN) || + (RamDiskBase > MAX_UINTN - RamDiskSize + 1)) { + return EFI_INVALID_PARAMETER; + } + + RamDiskDevNode = NULL; + + // + // Create a new RAM disk instance and initialize its private data + // + PrivateData = AllocateCopyPool ( + sizeof (RAM_DISK_PRIVATE_DATA), + &mRamDiskPrivateDataTemplate + ); + if (NULL == PrivateData) { + return EFI_OUT_OF_RESOURCES; + } + + PrivateData->StartingAddr = RamDiskBase; + PrivateData->Size = RamDiskSize; + CopyGuid (&PrivateData->TypeGuid, RamDiskType); + InitializeListHead (&PrivateData->ThisInstance); + + // + // Generate device path information for the registered RAM disk + // + RamDiskDevNode = AllocateCopyPool ( + sizeof (MEDIA_RAM_DISK_DEVICE_PATH), + &mRamDiskDeviceNodeTemplate + ); + if (NULL == RamDiskDevNode) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + RamDiskInitDeviceNode (PrivateData, RamDiskDevNode); + + *DevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) RamDiskDevNode + ); + if (NULL == *DevicePath) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + PrivateData->DevicePath = *DevicePath; + + // + // Check whether the created device path is already present in the handle + // database + // + if (!IsListEmpty(&RegisteredRamDisks)) { + DevicePathSize = GetDevicePathSize (PrivateData->DevicePath); + + BASE_LIST_FOR_EACH (Entry, &RegisteredRamDisks) { + RegisteredPrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); + if (DevicePathSize == GetDevicePathSize (RegisteredPrivateData->DevicePath)) { + // + // Compare device path + // + if ((CompareMem ( + PrivateData->DevicePath, + RegisteredPrivateData->DevicePath, + DevicePathSize)) == 0) { + *DevicePath = NULL; + Status = EFI_ALREADY_STARTED; + goto ErrorExit; + } + } + } + } + + // + // Fill Block IO protocol informations for the RAM disk + // + RamDiskInitBlockIo (PrivateData); + + // + // Install EFI_DEVICE_PATH_PROTOCOL & EFI_BLOCK_IO(2)_PROTOCOL on a new + // handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &PrivateData->Handle, + &gEfiBlockIoProtocolGuid, + &PrivateData->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &PrivateData->BlockIo2, + &gEfiDevicePathProtocolGuid, + PrivateData->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + // + // Insert the newly created one to the registered RAM disk list + // + InsertTailList (&RegisteredRamDisks, &PrivateData->ThisInstance); + + gBS->ConnectController (PrivateData->Handle, NULL, NULL, TRUE); + + FreePool (RamDiskDevNode); + + if ((mAcpiTableProtocol != NULL) && (mAcpiSdtProtocol != NULL)) { + RamDiskPublishNfit (PrivateData); + } + + return EFI_SUCCESS; + +ErrorExit: + if (RamDiskDevNode != NULL) { + FreePool (RamDiskDevNode); + } + + if (PrivateData != NULL) { + if (PrivateData->DevicePath) { + FreePool (PrivateData->DevicePath); + } + + FreePool (PrivateData); + } + + return Status; +} + + +/** + Unregister a RAM disk specified by DevicePath. + + @param[in] DevicePath A pointer to the device path that describes a RAM + Disk device. + + @retval EFI_SUCCESS The RAM disk is unregistered successfully. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_UNSUPPORTED The device specified by DevicePath is not a + valid ramdisk device path and not supported + by the driver. + @retval EFI_NOT_FOUND The RAM disk pointed by DevicePath doesn't + exist. + +**/ +EFI_STATUS +EFIAPI +RamDiskUnregister ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + BOOLEAN Found; + UINT64 StartingAddr; + UINT64 EndingAddr; + EFI_DEVICE_PATH_PROTOCOL *Header; + MEDIA_RAM_DISK_DEVICE_PATH *RamDiskDevNode; + RAM_DISK_PRIVATE_DATA *PrivateData; + + if (NULL == DevicePath) { + return EFI_INVALID_PARAMETER; + } + + // + // Locate the RAM disk device node. + // + RamDiskDevNode = NULL; + Header = DevicePath; + do { + // + // Test if the current device node is a RAM disk. + // + if ((MEDIA_DEVICE_PATH == Header->Type) && + (MEDIA_RAM_DISK_DP == Header->SubType)) { + RamDiskDevNode = (MEDIA_RAM_DISK_DEVICE_PATH *) Header; + + break; + } + + Header = NextDevicePathNode (Header); + } while ((Header->Type != END_DEVICE_PATH_TYPE)); + + if (NULL == RamDiskDevNode) { + return EFI_UNSUPPORTED; + } + + Found = FALSE; + StartingAddr = ReadUnaligned64 ((UINT64 *) &(RamDiskDevNode->StartingAddr[0])); + EndingAddr = ReadUnaligned64 ((UINT64 *) &(RamDiskDevNode->EndingAddr[0])); + + if (!IsListEmpty(&RegisteredRamDisks)) { + BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &RegisteredRamDisks) { + PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); + + // + // Unregister the RAM disk given by its starting address, ending address + // and type guid. + // + if ((StartingAddr == PrivateData->StartingAddr) && + (EndingAddr == PrivateData->StartingAddr + PrivateData->Size - 1) && + (CompareGuid (&RamDiskDevNode->TypeGuid, &PrivateData->TypeGuid))) { + // + // Remove the content for this RAM disk in NFIT. + // + if (PrivateData->InNfit) { + RamDiskUnpublishNfit (PrivateData); + } + + // + // Uninstall the EFI_DEVICE_PATH_PROTOCOL & EFI_BLOCK_IO(2)_PROTOCOL + // + gBS->UninstallMultipleProtocolInterfaces ( + PrivateData->Handle, + &gEfiBlockIoProtocolGuid, + &PrivateData->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &PrivateData->BlockIo2, + &gEfiDevicePathProtocolGuid, + (EFI_DEVICE_PATH_PROTOCOL *) PrivateData->DevicePath, + NULL + ); + + RemoveEntryList (&PrivateData->ThisInstance); + + if (RamDiskCreateHii == PrivateData->CreateMethod) { + // + // If a RAM disk is created within HII, then the RamDiskDxe driver + // driver is responsible for freeing the allocated memory for the + // RAM disk. + // + FreePool ((VOID *)(UINTN) PrivateData->StartingAddr); + } + + FreePool (PrivateData->DevicePath); + FreePool (PrivateData); + Found = TRUE; + + break; + } + } + } + + if (TRUE == Found) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c new file mode 100644 index 000000000..abbd4f768 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/ComponentName.c @@ -0,0 +1,179 @@ +/** @file + UEFI Component Name protocol for UDF/ECMA-167 file system driver. + + Copyright (C) 2014-2017 Paulo Alcantara + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Udf.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUdfComponentName = { + UdfComponentNameGetDriverName, + UdfComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdfComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UdfComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UdfComponentNameGetControllerName, + "en" +}; + +// +// Driver name table for Udf module. +// It is shared by the implementation of ComponentName & ComponentName2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdfDriverNameTable[] = { + { + "eng;en", + L"UDF File System Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UdfComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUdfDriverNameTable, + DriverName, + (BOOLEAN)(This == &gUdfComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UdfComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/File.c b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/File.c new file mode 100644 index 000000000..4ad7bb93d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/File.c @@ -0,0 +1,904 @@ +/** @file + Handle operations in files and directories from UDF/ECMA-167 file systems. + + Copyright (C) 2014-2017 Paulo Alcantara + Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Udf.h" + +EFI_FILE_PROTOCOL gUdfFileIoOps = { + EFI_FILE_PROTOCOL_REVISION, + UdfOpen, + UdfClose, + UdfDelete, + UdfRead, + UdfWrite, + UdfGetPosition, + UdfSetPosition, + UdfGetInfo, + UdfSetInfo, + UdfFlush, + NULL, + NULL, + NULL, + NULL +}; + +#define _ROOT_FILE(_PrivData) (_PrivData)->Root +#define _PARENT_FILE(_PrivData) \ + ((_PrivData)->IsRootDirectory ? (_PrivData)->Root : &(_PrivData)->File) +#define _FILE(_PrivData) _PARENT_FILE(_PrivData) + +/** + Open the root directory on a volume. + + @param This Protocol instance pointer. + @param Root Returns an Open file handle for the root directory + + @retval EFI_SUCCESS The device was opened. + @retval EFI_UNSUPPORTED This volume does not support the file system. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of + resources. + +**/ +EFI_STATUS +EFIAPI +UdfOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **Root + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + PRIVATE_UDF_SIMPLE_FS_DATA *PrivFsData; + PRIVATE_UDF_FILE_DATA *PrivFileData; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (This == NULL || Root == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Error_Invalid_Params; + } + + PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (This); + + if (PrivFsData->OpenFiles == 0) { + // + // There is no more open files. Read volume information again since it was + // cleaned up on the last UdfClose() call. + // + Status = ReadUdfVolumeInformation ( + PrivFsData->BlockIo, + PrivFsData->DiskIo, + &PrivFsData->Volume + ); + if (EFI_ERROR (Status)) { + goto Error_Read_Udf_Volume; + } + } + + CleanupFileInformation (&PrivFsData->Root); + + // + // Find root directory file. + // + Status = FindRootDirectory ( + PrivFsData->BlockIo, + PrivFsData->DiskIo, + &PrivFsData->Volume, + &PrivFsData->Root + ); + if (EFI_ERROR (Status)) { + goto Error_Find_Root_Dir; + } + + PrivFileData = + (PRIVATE_UDF_FILE_DATA *) AllocateZeroPool (sizeof (PRIVATE_UDF_FILE_DATA)); + if (PrivFileData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error_Alloc_Priv_File_Data; + } + + PrivFileData->Signature = PRIVATE_UDF_FILE_DATA_SIGNATURE; + PrivFileData->SimpleFs = This; + PrivFileData->Root = &PrivFsData->Root; + PrivFileData->IsRootDirectory = TRUE; + + CopyMem ((VOID *)&PrivFileData->FileIo, (VOID *)&gUdfFileIoOps, + sizeof (EFI_FILE_PROTOCOL)); + + *Root = &PrivFileData->FileIo; + + PrivFsData->OpenFiles++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +Error_Alloc_Priv_File_Data: + CleanupFileInformation (&PrivFsData->Root); + +Error_Find_Root_Dir: + +Error_Read_Udf_Volume: +Error_Invalid_Params: + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Opens a new file relative to the source file's location. + + @param This The protocol instance pointer. + @param NewHandle Returns File Handle for FileName. + @param FileName Null terminated string. "\", ".", and ".." are supported. + @param OpenMode Open mode for file. + @param Attributes Only used for EFI_FILE_MODE_CREATE. + + @retval EFI_SUCCESS The device was opened. + @retval EFI_NOT_FOUND The specified file could not be found on the + device. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_MEDIA_CHANGED The media has changed. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of + resources. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +UdfOpen ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + PRIVATE_UDF_FILE_DATA *PrivFileData; + PRIVATE_UDF_SIMPLE_FS_DATA *PrivFsData; + CHAR16 FilePath[UDF_PATH_LENGTH]; + UDF_FILE_INFO File; + PRIVATE_UDF_FILE_DATA *NewPrivFileData; + CHAR16 *TempFileName; + + ZeroMem (FilePath, sizeof FilePath); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (This == NULL || NewHandle == NULL || FileName == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Error_Invalid_Params; + } + + if (OpenMode != EFI_FILE_MODE_READ) { + Status = EFI_WRITE_PROTECTED; + goto Error_Invalid_Params; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs); + + // + // Build full path + // + if (*FileName == L'\\') { + StrCpyS (FilePath, UDF_PATH_LENGTH, FileName); + } else { + StrCpyS (FilePath, UDF_PATH_LENGTH, PrivFileData->AbsoluteFileName); + StrCatS (FilePath, UDF_PATH_LENGTH, L"\\"); + StrCatS (FilePath, UDF_PATH_LENGTH, FileName); + } + + MangleFileName (FilePath); + if (FilePath[0] == L'\0') { + Status = EFI_NOT_FOUND; + goto Error_Bad_FileName; + } + + Status = FindFile ( + PrivFsData->BlockIo, + PrivFsData->DiskIo, + &PrivFsData->Volume, + FilePath, + _ROOT_FILE (PrivFileData), + _PARENT_FILE (PrivFileData), + &_PARENT_FILE(PrivFileData)->FileIdentifierDesc->Icb, + &File + ); + if (EFI_ERROR (Status)) { + goto Error_Find_File; + } + + NewPrivFileData = + (PRIVATE_UDF_FILE_DATA *)AllocateZeroPool (sizeof (PRIVATE_UDF_FILE_DATA)); + if (NewPrivFileData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error_Alloc_New_Priv_File_Data; + } + + CopyMem ((VOID *)NewPrivFileData, (VOID *)PrivFileData, + sizeof (PRIVATE_UDF_FILE_DATA)); + CopyMem ((VOID *)&NewPrivFileData->File, &File, sizeof (UDF_FILE_INFO)); + + NewPrivFileData->IsRootDirectory = FALSE; + + StrCpyS (NewPrivFileData->AbsoluteFileName, UDF_PATH_LENGTH, FilePath); + FileName = NewPrivFileData->AbsoluteFileName; + + while ((TempFileName = StrStr (FileName, L"\\")) != NULL) { + FileName = TempFileName + 1; + } + + StrCpyS (NewPrivFileData->FileName, UDF_FILENAME_LENGTH, FileName); + + Status = GetFileSize ( + PrivFsData->BlockIo, + PrivFsData->DiskIo, + &PrivFsData->Volume, + &NewPrivFileData->File, + &NewPrivFileData->FileSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: GetFileSize() fails with status - %r.\n", + __FUNCTION__, Status + )); + goto Error_Get_File_Size; + } + + NewPrivFileData->FilePosition = 0; + ZeroMem ((VOID *)&NewPrivFileData->ReadDirInfo, + sizeof (UDF_READ_DIRECTORY_INFO)); + + *NewHandle = &NewPrivFileData->FileIo; + + PrivFsData->OpenFiles++; + + gBS->RestoreTPL (OldTpl); + + return Status; + +Error_Get_File_Size: + FreePool ((VOID *)NewPrivFileData); + +Error_Alloc_New_Priv_File_Data: + CleanupFileInformation (&File); + +Error_Find_File: +Error_Bad_FileName: +Error_Invalid_Params: + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Read data from the file. + + @param This Protocol instance pointer. + @param BufferSize On input size of buffer, on output amount of data in + buffer. + @param Buffer The buffer in which data is read. + + @retval EFI_SUCCESS Data was read. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TO_SMALL BufferSize is too small. BufferSize contains + required size. + +**/ +EFI_STATUS +EFIAPI +UdfRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + PRIVATE_UDF_FILE_DATA *PrivFileData; + PRIVATE_UDF_SIMPLE_FS_DATA *PrivFsData; + UDF_VOLUME_INFO *Volume; + UDF_FILE_INFO *Parent; + UDF_READ_DIRECTORY_INFO *ReadDirInfo; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + UDF_FILE_INFO FoundFile; + UDF_FILE_IDENTIFIER_DESCRIPTOR *NewFileIdentifierDesc; + VOID *NewFileEntryData; + CHAR16 FileName[UDF_FILENAME_LENGTH]; + UINT64 FileSize; + UINT64 BufferSizeUint64; + + ZeroMem (FileName, sizeof FileName); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (This == NULL || BufferSize == NULL || (*BufferSize != 0 && + Buffer == NULL)) { + Status = EFI_INVALID_PARAMETER; + goto Error_Invalid_Params; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs); + + BlockIo = PrivFsData->BlockIo; + DiskIo = PrivFsData->DiskIo; + Volume = &PrivFsData->Volume; + ReadDirInfo = &PrivFileData->ReadDirInfo; + NewFileIdentifierDesc = NULL; + NewFileEntryData = NULL; + + Parent = _PARENT_FILE (PrivFileData); + + Status = EFI_VOLUME_CORRUPTED; + + if (IS_FID_NORMAL_FILE (Parent->FileIdentifierDesc)) { + if (PrivFileData->FilePosition > PrivFileData->FileSize) { + // + // File's position is beyond the EOF + // + Status = EFI_DEVICE_ERROR; + goto Error_File_Beyond_The_Eof; + } + + if (PrivFileData->FilePosition == PrivFileData->FileSize) { + *BufferSize = 0; + Status = EFI_SUCCESS; + goto Done; + } + + BufferSizeUint64 = *BufferSize; + + Status = ReadFileData ( + BlockIo, + DiskIo, + Volume, + Parent, + PrivFileData->FileSize, + &PrivFileData->FilePosition, + Buffer, + &BufferSizeUint64 + ); + ASSERT (BufferSizeUint64 <= MAX_UINTN); + *BufferSize = (UINTN)BufferSizeUint64; + } else if (IS_FID_DIRECTORY_FILE (Parent->FileIdentifierDesc)) { + if (ReadDirInfo->FidOffset == 0 && PrivFileData->FilePosition > 0) { + Status = EFI_DEVICE_ERROR; + *BufferSize = 0; + goto Done; + } + + for (;;) { + Status = ReadDirectoryEntry ( + BlockIo, + DiskIo, + Volume, + &Parent->FileIdentifierDesc->Icb, + Parent->FileEntry, + ReadDirInfo, + &NewFileIdentifierDesc + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_DEVICE_ERROR) { + FreePool (ReadDirInfo->DirectoryData); + ZeroMem ((VOID *)ReadDirInfo, sizeof (UDF_READ_DIRECTORY_INFO)); + + *BufferSize = 0; + Status = EFI_SUCCESS; + } + + goto Done; + } + // + // After calling function ReadDirectoryEntry(), if 'NewFileIdentifierDesc' + // is NULL, then the 'Status' must be EFI_OUT_OF_RESOURCES. Hence, if the + // code reaches here, 'NewFileIdentifierDesc' must be not NULL. + // + // The ASSERT here is for addressing a false positive NULL pointer + // dereference issue raised from static analysis. + // + ASSERT (NewFileIdentifierDesc != NULL); + + if (!IS_FID_PARENT_FILE (NewFileIdentifierDesc)) { + break; + } + + FreePool ((VOID *)NewFileIdentifierDesc); + } + + Status = FindFileEntry ( + BlockIo, + DiskIo, + Volume, + &NewFileIdentifierDesc->Icb, + &NewFileEntryData + ); + if (EFI_ERROR (Status)) { + goto Error_Find_Fe; + } + ASSERT (NewFileEntryData != NULL); + + if (FE_ICB_FILE_TYPE (NewFileEntryData) == UdfFileEntrySymlink) { + Status = ResolveSymlink ( + BlockIo, + DiskIo, + Volume, + Parent, + NewFileEntryData, + &FoundFile + ); + if (EFI_ERROR (Status)) { + goto Error_Resolve_Symlink; + } + + FreePool ((VOID *)NewFileEntryData); + NewFileEntryData = FoundFile.FileEntry; + + Status = GetFileNameFromFid (NewFileIdentifierDesc, ARRAY_SIZE (FileName), FileName); + if (EFI_ERROR (Status)) { + FreePool ((VOID *)FoundFile.FileIdentifierDesc); + goto Error_Get_FileName; + } + + FreePool ((VOID *)NewFileIdentifierDesc); + NewFileIdentifierDesc = FoundFile.FileIdentifierDesc; + } else { + FoundFile.FileIdentifierDesc = NewFileIdentifierDesc; + FoundFile.FileEntry = NewFileEntryData; + + Status = GetFileNameFromFid (FoundFile.FileIdentifierDesc, ARRAY_SIZE (FileName), FileName); + if (EFI_ERROR (Status)) { + goto Error_Get_FileName; + } + } + + Status = GetFileSize ( + BlockIo, + DiskIo, + Volume, + &FoundFile, + &FileSize + ); + if (EFI_ERROR (Status)) { + goto Error_Get_File_Size; + } + + Status = SetFileInfo ( + &FoundFile, + FileSize, + FileName, + BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + goto Error_Set_File_Info; + } + + PrivFileData->FilePosition++; + Status = EFI_SUCCESS; + } else if (IS_FID_DELETED_FILE (Parent->FileIdentifierDesc)) { + // + // Code should never reach here. + // + ASSERT (FALSE); + Status = EFI_DEVICE_ERROR; + } + +Error_Set_File_Info: +Error_Get_File_Size: +Error_Get_FileName: +Error_Resolve_Symlink: + if (NewFileEntryData != NULL) { + FreePool (NewFileEntryData); + } + +Error_Find_Fe: + if (NewFileIdentifierDesc != NULL) { + FreePool ((VOID *)NewFileIdentifierDesc); + } + +Done: +Error_File_Beyond_The_Eof: +Error_Invalid_Params: + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Close the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The file was closed. + +**/ +EFI_STATUS +EFIAPI +UdfClose ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + PRIVATE_UDF_FILE_DATA *PrivFileData; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = EFI_SUCCESS; + + if (This == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + if (!PrivFileData->IsRootDirectory) { + CleanupFileInformation (&PrivFileData->File); + + if (PrivFileData->ReadDirInfo.DirectoryData != NULL) { + FreePool (PrivFileData->ReadDirInfo.DirectoryData); + } + } + + FreePool ((VOID *)PrivFileData); + +Exit: + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Close and delete the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The file was closed and deleted. + @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not + deleted. + +**/ +EFI_STATUS +EFIAPI +UdfDelete ( + IN EFI_FILE_PROTOCOL *This + ) +{ + PRIVATE_UDF_FILE_DATA *PrivFileData; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + (VOID)PrivFileData->FileIo.Close(This); + + return EFI_WARN_DELETE_FAILURE; +} + +/** + Write data to a file. + + @param This Protocol instance pointer. + @param BufferSize On input size of buffer, on output amount of data in + buffer. + @param Buffer The buffer in which data to write. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Writes to Open directory are not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +UdfWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Get file's current position. + + @param This Protocol instance pointer. + @param Position Byte position from the start of the file. + + @retval EFI_SUCCESS Position was updated. + @retval EFI_UNSUPPORTED Seek request for directories is not valid. + +**/ +EFI_STATUS +EFIAPI +UdfGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + PRIVATE_UDF_FILE_DATA *PrivFileData; + + if (This == NULL || Position == NULL) { + return EFI_INVALID_PARAMETER; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + // + // As per UEFI spec, if the file handle is a directory, then the current file + // position has no meaning and the operation is not supported. + // + if (IS_FID_DIRECTORY_FILE (PrivFileData->File.FileIdentifierDesc)) { + return EFI_UNSUPPORTED; + } + + // + // The file is not a directory. So, return its position. + // + *Position = PrivFileData->FilePosition; + + return EFI_SUCCESS; +} + +/** + Set file's current position. + + @param This Protocol instance pointer. + @param Position Byte position from the start of the file. + + @retval EFI_SUCCESS Position was updated. + @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open. + +**/ +EFI_STATUS +EFIAPI +UdfSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + EFI_STATUS Status; + PRIVATE_UDF_FILE_DATA *PrivFileData; + UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_UNSUPPORTED; + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + FileIdentifierDesc = _FILE (PrivFileData)->FileIdentifierDesc; + ASSERT (FileIdentifierDesc != NULL); + if (IS_FID_DIRECTORY_FILE (FileIdentifierDesc)) { + // + // If the file handle is a directory, the _only_ position that may be set is + // zero. This has no effect of starting the read proccess of the directory + // entries over. + // + if (Position == 0) { + PrivFileData->FilePosition = Position; + PrivFileData->ReadDirInfo.FidOffset = 0; + Status = EFI_SUCCESS; + } + } else if (IS_FID_NORMAL_FILE (FileIdentifierDesc)) { + // + // Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to be + // set to the EOF. + // + if (Position == 0xFFFFFFFFFFFFFFFF) { + PrivFileData->FilePosition = PrivFileData->FileSize; + } else { + PrivFileData->FilePosition = Position; + } + + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Get information about a file. + + @param This Protocol instance pointer. + @param InformationType Type of information to return in Buffer. + @param BufferSize On input size of buffer, on output amount of data in + buffer. + @param Buffer The buffer to return data. + + @retval EFI_SUCCESS Data was returned. + @retval EFI_UNSUPPORTED InformationType is not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in + BufferSize. + +**/ +EFI_STATUS +EFIAPI +UdfGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PRIVATE_UDF_FILE_DATA *PrivFileData; + PRIVATE_UDF_SIMPLE_FS_DATA *PrivFsData; + EFI_FILE_SYSTEM_INFO *FileSystemInfo; + UINTN FileSystemInfoLength; + UINT64 VolumeSize; + UINT64 FreeSpaceSize; + EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel; + UINTN FileSystemVolumeLabelLength; + CHAR16 VolumeLabel[64]; + + if (This == NULL || InformationType == NULL || BufferSize == NULL || + (*BufferSize != 0 && Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs); + + Status = EFI_UNSUPPORTED; + + if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Status = SetFileInfo ( + _FILE (PrivFileData), + PrivFileData->FileSize, + PrivFileData->FileName, + BufferSize, + Buffer + ); + } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + Status = GetVolumeLabel (&PrivFsData->Volume, ARRAY_SIZE (VolumeLabel), VolumeLabel); + if (EFI_ERROR (Status)) { + return Status; + } + + FileSystemInfoLength = StrSize (VolumeLabel) + + sizeof (EFI_FILE_SYSTEM_INFO); + if (*BufferSize < FileSystemInfoLength) { + *BufferSize = FileSystemInfoLength; + return EFI_BUFFER_TOO_SMALL; + } + + FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; + StrCpyS ( + FileSystemInfo->VolumeLabel, + (*BufferSize - SIZE_OF_EFI_FILE_SYSTEM_INFO) / sizeof (CHAR16), + VolumeLabel + ); + Status = GetVolumeSize ( + PrivFsData->BlockIo, + PrivFsData->DiskIo, + &PrivFsData->Volume, + &VolumeSize, + &FreeSpaceSize + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FileSystemInfo->Size = FileSystemInfoLength; + FileSystemInfo->ReadOnly = TRUE; + FileSystemInfo->BlockSize = + PrivFsData->Volume.LogicalVolDesc.LogicalBlockSize; + FileSystemInfo->VolumeSize = VolumeSize; + FileSystemInfo->FreeSpace = FreeSpaceSize; + + *BufferSize = FileSystemInfoLength; + Status = EFI_SUCCESS; + } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + Status = GetVolumeLabel (&PrivFsData->Volume, ARRAY_SIZE (VolumeLabel), VolumeLabel); + if (EFI_ERROR (Status)) { + return Status; + } + + FileSystemVolumeLabelLength = StrSize (VolumeLabel) + + sizeof (EFI_FILE_SYSTEM_VOLUME_LABEL); + if (*BufferSize < FileSystemVolumeLabelLength) { + *BufferSize = FileSystemVolumeLabelLength; + return EFI_BUFFER_TOO_SMALL; + } + + FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer; + StrCpyS ( + FileSystemVolumeLabel->VolumeLabel, + (*BufferSize - SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL) / sizeof (CHAR16), + VolumeLabel + ); + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Set information about a file. + + @param This Protocol instance pointer. + @param InformationType Type of information in Buffer. + @param BufferSize Size of buffer. + @param Buffer The data to write. + + @retval EFI_SUCCESS Data was set. + @retval EFI_UNSUPPORTED InformationType is not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + +**/ +EFI_STATUS +EFIAPI +UdfSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return EFI_WRITE_PROTECTED; +} + +/** + Flush data back for the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS Data was flushed. + @retval EFI_UNSUPPORTED Writes to Open directory are not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +UdfFlush ( + IN EFI_FILE_PROTOCOL *This + ) +{ + return EFI_WRITE_PROTECTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c new file mode 100644 index 000000000..ee236ccde --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c @@ -0,0 +1,214 @@ +/** @file + Helper functions for mangling file names in UDF/ECMA-167 file systems. + + Copyright (C) 2014-2017 Paulo Alcantara + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Udf.h" + +/** + Trim the leading and trailing spaces for a give Unicode string. + + @param[in] String The Unicode string to trim. + + @return A pointer to the trimmed string. + +**/ +CHAR16 * +TrimString ( + IN CHAR16 *String + ) +{ + CHAR16 *TempString; + + for ( ; *String != L'\0' && *String == L' '; String++) { + ; + } + + TempString = String + StrLen (String) - 1; + while ((TempString >= String) && (*TempString == L' ')) { + TempString--; + } + + *(TempString + 1) = L'\0'; + + return String; +} + +/** + Replace the content of a Unicode string with the content of another Unicode + string. + + @param[in] Destination A pointer to a Unicode string. + @param[in] Source A pointer to a Unicode string. + +**/ +VOID +ReplaceLeft ( + IN CHAR16 *Destination, + IN CONST CHAR16 *Source + ) +{ + CONST CHAR16 *EndString; + + EndString = Source + StrLen (Source); + while (Source <= EndString) { + *Destination++ = *Source++; + } +} + +/** + Remove one or more consecutive backslashes starting from the second character + of a given Unicode string. + + @param[in] String A pointer to a Unicode string. + + @return A pointer to the modified string. + +**/ +CHAR16 * +ExcludeTrailingBackslashes ( + IN CHAR16 *String + ) +{ + CHAR16 *TempString; + + switch (*(String + 1)) { + case L'\\': + break; + case L'\0': + default: + String++; + goto Exit; + } + + TempString = String; + while (*TempString != L'\0' && *TempString == L'\\') { + TempString++; + } + + if (TempString - 1 > String) { + ReplaceLeft (String + 1, TempString); + } + + String++; + +Exit: + return String; +} + +/** + Mangle a filename by cutting off trailing whitespaces, "\\", "." and "..". + + @param[in] FileName Filename. + + @retval The mangled Filename. + +**/ +CHAR16 * +MangleFileName ( + IN CHAR16 *FileName + ) +{ + CHAR16 *FileNameSavedPointer; + CHAR16 *TempFileName; + UINTN BackslashesNo; + + if (FileName == NULL || *FileName == L'\0') { + FileName = NULL; + goto Exit; + } + + FileName = TrimString (FileName); + if (*FileName == L'\0') { + goto Exit; + } + + if ((StrLen (FileName) > 1) && (FileName[StrLen (FileName) - 1] == L'\\')) { + FileName[StrLen (FileName) - 1] = L'\0'; + } + + FileNameSavedPointer = FileName; + + if (FileName[0] == L'.') { + if (FileName[1] == L'.') { + if (FileName[2] == L'\0') { + goto Exit; + } else { + FileName += 2; + } + } else if (FileName[1] == L'\0') { + goto Exit; + } + } + + while (*FileName != L'\0') { + if (*FileName == L'\\') { + FileName = ExcludeTrailingBackslashes (FileName); + } else if (*FileName == L'.') { + switch (*(FileName + 1)) { + case L'\0': + *FileName = L'\0'; + break; + case L'\\': + TempFileName = FileName + 1; + TempFileName = ExcludeTrailingBackslashes (TempFileName); + ReplaceLeft (FileName, TempFileName); + break; + case '.': + if ((*(FileName - 1) != L'\\') && ((*(FileName + 2) != L'\\') || + (*(FileName + 2) != L'\0'))) { + FileName++; + continue; + } + + BackslashesNo = 0; + TempFileName = FileName - 1; + while (TempFileName >= FileNameSavedPointer) { + if (*TempFileName == L'\\') { + if (++BackslashesNo == 2) { + break; + } + } + + TempFileName--; + } + + TempFileName++; + + if ((*TempFileName == L'.') && (*(TempFileName + 1) == L'.')) { + FileName += 2; + } else { + if (*(FileName + 2) != L'\0') { + ReplaceLeft (TempFileName, FileName + 3); + if (*(TempFileName - 1) == L'\\') { + FileName = TempFileName; + ExcludeTrailingBackslashes (TempFileName - 1); + TempFileName = FileName; + } + } else { + *TempFileName = L'\0'; + } + + FileName = TempFileName; + } + + break; + default: + FileName++; + } + } else { + FileName++; + } + } + + FileName = FileNameSavedPointer; + if ((StrLen (FileName) > 1) && (FileName [StrLen (FileName) - 1] == L'\\')) { + FileName [StrLen (FileName) - 1] = L'\0'; + } + +Exit: + return FileName; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c new file mode 100644 index 000000000..e9e55cb2b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c @@ -0,0 +1,2924 @@ +/** @file + Handle on-disk format and volume structures in UDF/ECMA-167 file systems. + + Copyright (C) 2014-2017 Paulo Alcantara + Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Udf.h" + +// +// Vendor-Defined Device Path GUID for UDF file system +// +EFI_GUID gUdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID; + +/** + Find the anchor volume descriptor pointer. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[out] AnchorPoint Anchor volume descriptor pointer. + + @retval EFI_SUCCESS Anchor volume descriptor pointer found. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval other Anchor volume descriptor pointer not found. + +**/ +EFI_STATUS +FindAnchorVolumeDescriptorPointer ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + OUT UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + EFI_LBA EndLBA; + EFI_LBA DescriptorLBAs[4]; + UINTN Index; + UDF_DESCRIPTOR_TAG *DescriptorTag; + + BlockSize = BlockIo->Media->BlockSize; + EndLBA = BlockIo->Media->LastBlock; + DescriptorLBAs[0] = 256; + DescriptorLBAs[1] = EndLBA - 256; + DescriptorLBAs[2] = EndLBA; + DescriptorLBAs[3] = 512; + + for (Index = 0; Index < ARRAY_SIZE (DescriptorLBAs); Index++) { + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (DescriptorLBAs[Index], BlockSize), + sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER), + (VOID *)AnchorPoint + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DescriptorTag = &AnchorPoint->DescriptorTag; + + // + // Check if read LBA has a valid AVDP descriptor. + // + if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) { + return EFI_SUCCESS; + } + } + // + // No AVDP found. + // + return EFI_VOLUME_CORRUPTED; +} + +/** + Save the content of Logical Volume Descriptors and Partitions Descriptors in + memory. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] AnchorPoint Anchor volume descriptor pointer. + @param[out] Volume UDF volume information structure. + + @retval EFI_SUCCESS The descriptors were saved. + @retval EFI_OUT_OF_RESOURCES The descriptors were not saved due to lack of + resources. + @retval other The descriptors were not saved due to + ReadDisk error. + +**/ +EFI_STATUS +StartMainVolumeDescriptorSequence ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint, + OUT UDF_VOLUME_INFO *Volume + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + UDF_EXTENT_AD *ExtentAd; + EFI_LBA SeqStartBlock; + EFI_LBA SeqEndBlock; + BOOLEAN StopSequence; + VOID *Buffer; + UDF_DESCRIPTOR_TAG *DescriptorTag; + UINT32 LogicalBlockSize; + + BlockSize = BlockIo->Media->BlockSize; + ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent; + + // + // Allocate buffer for reading disk blocks + // + Buffer = AllocateZeroPool ((UINTN)BlockSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // The logical partition created by Partition driver is relative to the main + // VDS extent location, so we start the Main Volume Descriptor Sequence at + // LBA 0. + // + // We don't need to check again if we have valid Volume Descriptors here since + // Partition driver already did. + // + SeqStartBlock = 0; + SeqEndBlock = SeqStartBlock + DivU64x32 ((UINT64)ExtentAd->ExtentLength, + BlockSize); + StopSequence = FALSE; + for (; SeqStartBlock < SeqEndBlock && !StopSequence; SeqStartBlock++) { + // + // Read disk block + // + Status = BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + SeqStartBlock, + BlockSize, + Buffer + ); + if (EFI_ERROR (Status)) { + goto Out_Free; + } + + DescriptorTag = Buffer; + + switch (DescriptorTag->TagIdentifier) { + case UdfPartitionDescriptor: + // + // Save Partition Descriptor + // + CopyMem (&Volume->PartitionDesc, Buffer, sizeof (Volume->PartitionDesc)); + break; + + case UdfLogicalVolumeDescriptor: + // + // Save Logical Volume Descriptor + // + CopyMem (&Volume->LogicalVolDesc, Buffer, sizeof (Volume->LogicalVolDesc)); + break; + + case UdfTerminatingDescriptor: + StopSequence = TRUE; + break; + + default: + ; + } + } + + // + // Determine FE (File Entry) size + // + LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize; + if (LogicalBlockSize >= UDF_LOGICAL_SECTOR_SIZE) { + Volume->FileEntrySize = (UINTN)LogicalBlockSize; + } else { + Volume->FileEntrySize = UDF_LOGICAL_SECTOR_SIZE; + } + + Status = EFI_SUCCESS; + +Out_Free: + // + // Free block read buffer + // + FreePool (Buffer); + + return Status; +} + +/** + Return a Partition Descriptor given a Long Allocation Descriptor. This is + necessary to calculate the right extent (LongAd) offset which is added up + with partition's starting location. + + @param[in] Volume Volume information pointer. + @param[in] LongAd Long Allocation Descriptor pointer. + + @return A pointer to a Partition Descriptor. + +**/ +UDF_PARTITION_DESCRIPTOR * +GetPdFromLongAd ( + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd + ) +{ + UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc; + UINT16 PartitionNum; + + LogicalVolDesc = &Volume->LogicalVolDesc; + + switch (LogicalVolDesc->DomainIdentifier.Suffix.Domain.UdfRevision) { + case 0x0102: + case 0x0150: + case 0x0200: + case 0x0201: + case 0x0250: + case 0x0260: + // + // UDF 1.02 specification: + // + // There shall be exactly one prevailing Logical Volume Descriptor recorded + // per Volume Set. The Partition Maps field shall contain only Type 1 + // Partition Maps. + // + // UDF 1.50 through 2.60 specs say: + // + // For the purpose of interchange partition maps shall be limited to + // Partition Map type 1, except type 2 maps as described in the document. + // + // NOTE: Only one Type 1 (Physical) Partition is supported. It has been + // checked already in Partition driver for existence of a single Type 1 + // Partition map. Hence, the 'PartitionReferenceNumber' field (the index + // used to access Partition Maps data within the Logical Volume Descriptor) + // in the Long Allocation Descriptor should be 0 to indicate there is only + // one partition. + // + if (LongAd->ExtentLocation.PartitionReferenceNumber != 0) { + return NULL; + } + // + // Since only one partition, get the first one directly. + // + PartitionNum = *(UINT16 *)((UINTN)&LogicalVolDesc->PartitionMaps[4]); + break; + + default: + // + // Unsupported UDF revision + // + return NULL; + } + + // + // Check if partition number matches Partition Descriptor found in Main Volume + // Descriptor Sequence. + // + if (Volume->PartitionDesc.PartitionNumber == PartitionNum) { + return &Volume->PartitionDesc; + } + + return NULL; +} + +/** + Return logical sector number of a given Long Allocation Descriptor. + + @param[in] Volume Volume information pointer. + @param[in] LongAd Long Allocation Descriptor pointer. + @param[out] Lsn Logical sector number pointer. + + @retval EFI_SUCCESS Logical sector number successfully returned. + @retval EFI_UNSUPPORTED Logical sector number is not returned due to + unrecognized format. + +**/ +EFI_STATUS +GetLongAdLsn ( + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd, + OUT UINT64 *Lsn + ) +{ + UDF_PARTITION_DESCRIPTOR *PartitionDesc; + + PartitionDesc = GetPdFromLongAd (Volume, LongAd); + if (PartitionDesc == NULL) { + DEBUG (( + DEBUG_ERROR, + "%a: Fail to get the Partition Descriptor from the given Long Allocation Descriptor.\n", + __FUNCTION__ + )); + return EFI_UNSUPPORTED; + } + + *Lsn = (UINT64)PartitionDesc->PartitionStartingLocation - + Volume->MainVdsStartLocation + + LongAd->ExtentLocation.LogicalBlockNumber; + + return EFI_SUCCESS; +} + +/** + Return logical sector number of a given Short Allocation Descriptor. + + @param[in] Volume Volume pointer. + @param[in] PartitionDesc Partition Descriptor pointer. + @param[in] ShortAd Short Allocation Descriptor pointer. + + @return The logical sector number of a given Short Allocation Descriptor. + +**/ +UINT64 +GetShortAdLsn ( + IN UDF_VOLUME_INFO *Volume, + IN UDF_PARTITION_DESCRIPTOR *PartitionDesc, + IN UDF_SHORT_ALLOCATION_DESCRIPTOR *ShortAd + ) +{ + return (UINT64)PartitionDesc->PartitionStartingLocation - + Volume->MainVdsStartLocation + ShortAd->ExtentPosition; +} + +/** + Find File Set Descriptor of a given Logical Volume Descriptor. + + The found FSD will contain the extent (LogicalVolumeContentsUse) where our + root directory is. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume Volume information pointer. + + @retval EFI_SUCCESS File Set Descriptor pointer found. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval other File Set Descriptor pointer not found. + +**/ +EFI_STATUS +FindFileSetDescriptor ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume + ) +{ + EFI_STATUS Status; + UINT64 Lsn; + UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc; + UDF_DESCRIPTOR_TAG *DescriptorTag; + + LogicalVolDesc = &Volume->LogicalVolDesc; + Status = GetLongAdLsn (Volume, &LogicalVolDesc->LogicalVolumeContentsUse, &Lsn); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // As per UDF 2.60 specification: + // + // There shall be exactly one File Set Descriptor recorded per Logical + // Volume. + // + // Read disk block + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (Lsn, LogicalVolDesc->LogicalBlockSize), + sizeof (Volume->FileSetDesc), + &Volume->FileSetDesc + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DescriptorTag = &Volume->FileSetDesc.DescriptorTag; + + // + // Check if read block is a File Set Descriptor + // + if (DescriptorTag->TagIdentifier != UdfFileSetDescriptor) { + return EFI_VOLUME_CORRUPTED; + } + + return EFI_SUCCESS; +} + +/** + Read Volume and File Structure on an UDF file system. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[out] Volume Volume information pointer. + + @retval EFI_SUCCESS Volume and File Structure were read. + @retval other Volume and File Structure were not read. + +**/ +EFI_STATUS +ReadVolumeFileStructure ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + OUT UDF_VOLUME_INFO *Volume + ) +{ + EFI_STATUS Status; + UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER AnchorPoint; + UDF_EXTENT_AD *ExtentAd; + + // + // Find Anchor Volume Descriptor Pointer + // + Status = FindAnchorVolumeDescriptorPointer ( + BlockIo, + DiskIo, + &AnchorPoint + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Save Main VDS start block number + // + ExtentAd = &AnchorPoint.MainVolumeDescriptorSequenceExtent; + + Volume->MainVdsStartLocation = (UINT64)ExtentAd->ExtentLocation; + + // + // Start Main Volume Descriptor Sequence. + // + Status = StartMainVolumeDescriptorSequence ( + BlockIo, + DiskIo, + &AnchorPoint, + Volume + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +/** + Calculate length of a given File Identifier Descriptor. + + @param[in] FileIdentifierDesc File Identifier Descriptor pointer. + + @return The length of a given File Identifier Descriptor. + +**/ +UINT64 +GetFidDescriptorLength ( + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc + ) +{ + return (UINT64)( + (INTN)((OFFSET_OF (UDF_FILE_IDENTIFIER_DESCRIPTOR, Data[0]) + 3 + + FileIdentifierDesc->LengthOfFileIdentifier + + FileIdentifierDesc->LengthOfImplementationUse) >> 2) << 2 + ); +} + +/** + Duplicate a given File Identifier Descriptor. + + @param[in] FileIdentifierDesc File Identifier Descriptor pointer. + @param[out] NewFileIdentifierDesc The duplicated File Identifier Descriptor. + +**/ +VOID +DuplicateFid ( + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc, + OUT UDF_FILE_IDENTIFIER_DESCRIPTOR **NewFileIdentifierDesc + ) +{ + *NewFileIdentifierDesc = + (UDF_FILE_IDENTIFIER_DESCRIPTOR *)AllocateCopyPool ( + (UINTN) GetFidDescriptorLength (FileIdentifierDesc), FileIdentifierDesc); +} + +/** + Duplicate either a given File Entry or a given Extended File Entry. + + @param[in] BlockIo BlockIo interface. + @param[in] Volume Volume information pointer. + @param[in] FileEntry (Extended) File Entry pointer. + @param[out] NewFileEntry The duplicated (Extended) File Entry. + +**/ +VOID +DuplicateFe ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN UDF_VOLUME_INFO *Volume, + IN VOID *FileEntry, + OUT VOID **NewFileEntry + ) +{ + *NewFileEntry = AllocateCopyPool (Volume->FileEntrySize, FileEntry); +} + +/** + Get raw data + length of a given File Entry or Extended File Entry. + + The file's recorded data can contain either real file content (inline) or + a sequence of extents (or Allocation Descriptors) which tells where file's + content is stored in. + + NOTE: The FE/EFE can be thought it was an inode. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The (Extended) File Entry is external input, so this routine will do basic + validation for (Extended) File Entry and report status. + + @param[in] FileEntryData (Extended) File Entry pointer. + @param[in] FileEntrySize Size of the (Extended) File Entry specified + by FileEntryData. + @param[out] Data Buffer contains the raw data of a given + (Extended) File Entry. + @param[out] Length Length of the data in Buffer. + + @retval EFI_SUCCESS Raw data and size of the FE/EFE was read. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + +**/ +EFI_STATUS +GetFileEntryData ( + IN VOID *FileEntryData, + IN UINTN FileEntrySize, + OUT VOID **Data, + OUT UINT64 *Length + ) +{ + UDF_DESCRIPTOR_TAG *DescriptorTag; + UDF_EXTENDED_FILE_ENTRY *ExtendedFileEntry; + UDF_FILE_ENTRY *FileEntry; + + DescriptorTag = FileEntryData; + + if (DescriptorTag->TagIdentifier == UdfExtendedFileEntry) { + ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)FileEntryData; + + *Length = ExtendedFileEntry->InformationLength; + *Data = (VOID *)((UINT8 *)ExtendedFileEntry->Data + + ExtendedFileEntry->LengthOfExtendedAttributes); + } else if (DescriptorTag->TagIdentifier == UdfFileEntry) { + FileEntry = (UDF_FILE_ENTRY *)FileEntryData; + + *Length = FileEntry->InformationLength; + *Data = (VOID *)((UINT8 *)FileEntry->Data + + FileEntry->LengthOfExtendedAttributes); + } + + if ((*Length > FileEntrySize) || + ((UINTN)FileEntryData > (UINTN)(*Data)) || + ((UINTN)(*Data) - (UINTN)FileEntryData > FileEntrySize - *Length)) { + return EFI_VOLUME_CORRUPTED; + } + return EFI_SUCCESS; +} + +/** + Get Allocation Descriptors' data information from a given FE/EFE. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The (Extended) File Entry is external input, so this routine will do basic + validation for (Extended) File Entry and report status. + + @param[in] FileEntryData (Extended) File Entry pointer. + @param[in] FileEntrySize Size of the (Extended) File Entry specified + by FileEntryData. + @param[out] AdsData Buffer contains the Allocation Descriptors' + data from a given FE/EFE. + @param[out] Length Length of the data in AdsData. + + @retval EFI_SUCCESS The data and size of Allocation Descriptors + were read from the FE/EFE. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + +**/ +EFI_STATUS +GetAdsInformation ( + IN VOID *FileEntryData, + IN UINTN FileEntrySize, + OUT VOID **AdsData, + OUT UINT64 *Length + ) +{ + UDF_DESCRIPTOR_TAG *DescriptorTag; + UDF_EXTENDED_FILE_ENTRY *ExtendedFileEntry; + UDF_FILE_ENTRY *FileEntry; + + DescriptorTag = FileEntryData; + + if (DescriptorTag->TagIdentifier == UdfExtendedFileEntry) { + ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)FileEntryData; + + *Length = ExtendedFileEntry->LengthOfAllocationDescriptors; + *AdsData = (VOID *)((UINT8 *)ExtendedFileEntry->Data + + ExtendedFileEntry->LengthOfExtendedAttributes); + } else if (DescriptorTag->TagIdentifier == UdfFileEntry) { + FileEntry = (UDF_FILE_ENTRY *)FileEntryData; + + *Length = FileEntry->LengthOfAllocationDescriptors; + *AdsData = (VOID *)((UINT8 *)FileEntry->Data + + FileEntry->LengthOfExtendedAttributes); + } + + if ((*Length > FileEntrySize) || + ((UINTN)FileEntryData > (UINTN)(*AdsData)) || + ((UINTN)(*AdsData) - (UINTN)FileEntryData > FileEntrySize - *Length)) { + return EFI_VOLUME_CORRUPTED; + } + return EFI_SUCCESS; +} + +/** + Read next Long Allocation Descriptor from a given file's data. + + @param[in] Data File's data pointer. + @param[in,out] Offset Starting offset of the File's data to read. + @param[in] Length Length of the data to read. + @param[out] FoundLongAd Long Allocation Descriptor pointer. + + @retval EFI_SUCCESS A Long Allocation Descriptor was found. + @retval EFI_DEVICE_ERROR No more Long Allocation Descriptors. + +**/ +EFI_STATUS +GetLongAdFromAds ( + IN VOID *Data, + IN OUT UINT64 *Offset, + IN UINT64 Length, + OUT UDF_LONG_ALLOCATION_DESCRIPTOR **FoundLongAd + ) +{ + UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd; + UDF_EXTENT_FLAGS ExtentFlags; + + for (;;) { + if (*Offset >= Length) { + // + // No more Long Allocation Descriptors. + // + return EFI_DEVICE_ERROR; + } + + LongAd = + (UDF_LONG_ALLOCATION_DESCRIPTOR *)((UINT8 *)Data + *Offset); + + // + // If it's either an indirect AD (Extended Alllocation Descriptor) or an + // allocated AD, then return it. + // + ExtentFlags = GET_EXTENT_FLAGS (LongAdsSequence, LongAd); + if (ExtentFlags == ExtentIsNextExtent || + ExtentFlags == ExtentRecordedAndAllocated) { + break; + } + + // + // This AD is either not recorded but allocated, or not recorded and not + // allocated. Skip it. + // + *Offset += AD_LENGTH (LongAdsSequence); + } + + *FoundLongAd = LongAd; + + return EFI_SUCCESS; +} + +/** + Read next Short Allocation Descriptor from a given file's data. + + @param[in] Data File's data pointer. + @param[in,out] Offset Starting offset of the File's data to read. + @param[in] Length Length of the data to read. + @param[out] FoundShortAd Short Allocation Descriptor pointer. + + @retval EFI_SUCCESS A Short Allocation Descriptor was found. + @retval EFI_DEVICE_ERROR No more Short Allocation Descriptors. + +**/ +EFI_STATUS +GetShortAdFromAds ( + IN VOID *Data, + IN OUT UINT64 *Offset, + IN UINT64 Length, + OUT UDF_SHORT_ALLOCATION_DESCRIPTOR **FoundShortAd + ) +{ + UDF_SHORT_ALLOCATION_DESCRIPTOR *ShortAd; + UDF_EXTENT_FLAGS ExtentFlags; + + for (;;) { + if (*Offset >= Length) { + // + // No more Short Allocation Descriptors. + // + return EFI_DEVICE_ERROR; + } + + ShortAd = + (UDF_SHORT_ALLOCATION_DESCRIPTOR *)((UINT8 *)Data + *Offset); + + // + // If it's either an indirect AD (Extended Alllocation Descriptor) or an + // allocated AD, then return it. + // + ExtentFlags = GET_EXTENT_FLAGS (ShortAdsSequence, ShortAd); + if (ExtentFlags == ExtentIsNextExtent || + ExtentFlags == ExtentRecordedAndAllocated) { + break; + } + + // + // This AD is either not recorded but allocated, or not recorded and not + // allocated. Skip it. + // + *Offset += AD_LENGTH (ShortAdsSequence); + } + + *FoundShortAd = ShortAd; + + return EFI_SUCCESS; +} + +/** + Get either a Short Allocation Descriptor or a Long Allocation Descriptor from + file's data. + + @param[in] RecordingFlags Flag to indicate the type of descriptor. + @param[in] Data File's data pointer. + @param[in,out] Offset Starting offset of the File's data to read. + @param[in] Length Length of the data to read. + @param[out] FoundAd Allocation Descriptor pointer. + + @retval EFI_SUCCESS A Short Allocation Descriptor was found. + @retval EFI_DEVICE_ERROR No more Allocation Descriptors. + Invalid type of descriptor was given. + +**/ +EFI_STATUS +GetAllocationDescriptor ( + IN UDF_FE_RECORDING_FLAGS RecordingFlags, + IN VOID *Data, + IN OUT UINT64 *Offset, + IN UINT64 Length, + OUT VOID **FoundAd + ) +{ + if (RecordingFlags == LongAdsSequence) { + return GetLongAdFromAds ( + Data, + Offset, + Length, + (UDF_LONG_ALLOCATION_DESCRIPTOR **)FoundAd + ); + } else if (RecordingFlags == ShortAdsSequence) { + return GetShortAdFromAds ( + Data, + Offset, + Length, + (UDF_SHORT_ALLOCATION_DESCRIPTOR **)FoundAd + ); + } + + // + // Code should never reach here. + // + ASSERT (FALSE); + return EFI_DEVICE_ERROR; +} + +/** + Return logical sector number of either Short or Long Allocation Descriptor. + + @param[in] RecordingFlags Flag to indicate the type of descriptor. + @param[in] Volume Volume information pointer. + @param[in] ParentIcb Long Allocation Descriptor pointer. + @param[in] Ad Allocation Descriptor pointer. + @param[out] Lsn Logical sector number pointer. + + @retval EFI_SUCCESS Logical sector number of the given Allocation + Descriptor successfully returned. + @retval EFI_UNSUPPORTED Logical sector number of the given Allocation + Descriptor is not returned due to unrecognized + format. + +**/ +EFI_STATUS +GetAllocationDescriptorLsn ( + IN UDF_FE_RECORDING_FLAGS RecordingFlags, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb, + IN VOID *Ad, + OUT UINT64 *Lsn + ) +{ + UDF_PARTITION_DESCRIPTOR *PartitionDesc; + + if (RecordingFlags == LongAdsSequence) { + return GetLongAdLsn (Volume, (UDF_LONG_ALLOCATION_DESCRIPTOR *)Ad, Lsn); + } else if (RecordingFlags == ShortAdsSequence) { + PartitionDesc = GetPdFromLongAd (Volume, ParentIcb); + if (PartitionDesc == NULL) { + return EFI_UNSUPPORTED; + } + + *Lsn = GetShortAdLsn ( + Volume, + PartitionDesc, + (UDF_SHORT_ALLOCATION_DESCRIPTOR *)Ad + ); + return EFI_SUCCESS; + } + + // + // Code should never reach here. + // + ASSERT (FALSE); + return EFI_UNSUPPORTED; +} + +/** + Return offset + length of a given indirect Allocation Descriptor (AED). + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume Volume information pointer. + @param[in] ParentIcb Long Allocation Descriptor pointer. + @param[in] RecordingFlags Flag to indicate the type of descriptor. + @param[in] Ad Allocation Descriptor pointer. + @param[out] Offset Offset of a given indirect Allocation + Descriptor. + @param[out] Length Length of a given indirect Allocation + Descriptor. + + @retval EFI_SUCCESS The offset and length were returned. + @retval EFI_OUT_OF_RESOURCES The offset and length were not returned due + to lack of resources. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval other The offset and length were not returned. + +**/ +EFI_STATUS +GetAedAdsOffset ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb, + IN UDF_FE_RECORDING_FLAGS RecordingFlags, + IN VOID *Ad, + OUT UINT64 *Offset, + OUT UINT64 *Length + ) +{ + EFI_STATUS Status; + UINT32 ExtentLength; + UINT64 Lsn; + VOID *Data; + UINT32 LogicalBlockSize; + UDF_ALLOCATION_EXTENT_DESCRIPTOR *AllocExtDesc; + UDF_DESCRIPTOR_TAG *DescriptorTag; + + ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad); + Status = GetAllocationDescriptorLsn (RecordingFlags, + Volume, + ParentIcb, + Ad, + &Lsn); + if (EFI_ERROR (Status)) { + return Status; + } + + Data = AllocatePool (ExtentLength); + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize; + + // + // Read extent. + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (Lsn, LogicalBlockSize), + ExtentLength, + Data + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + AllocExtDesc = (UDF_ALLOCATION_EXTENT_DESCRIPTOR *)Data; + + DescriptorTag = &AllocExtDesc->DescriptorTag; + + // + // Check if read extent contains a valid tag identifier for AED. + // + if (DescriptorTag->TagIdentifier != UdfAllocationExtentDescriptor) { + Status = EFI_VOLUME_CORRUPTED; + goto Exit; + } + + // + // Get AED's block offset and its length. + // + *Offset = MultU64x32 (Lsn, LogicalBlockSize) + + sizeof (UDF_ALLOCATION_EXTENT_DESCRIPTOR); + *Length = AllocExtDesc->LengthOfAllocationDescriptors; + +Exit: + FreePool (Data); + + return Status; +} + +/** + Read Allocation Extent Descriptor into memory. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume Volume information pointer. + @param[in] ParentIcb Long Allocation Descriptor pointer. + @param[in] RecordingFlags Flag to indicate the type of descriptor. + @param[in] Ad Allocation Descriptor pointer. + @param[out] Data Buffer that contains the Allocation Extent + Descriptor. + @param[out] Length Length of Data. + + @retval EFI_SUCCESS The Allocation Extent Descriptor was read. + @retval EFI_OUT_OF_RESOURCES The Allocation Extent Descriptor was not read + due to lack of resources. + @retval other Fail to read the disk. + +**/ +EFI_STATUS +GetAedAdsData ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb, + IN UDF_FE_RECORDING_FLAGS RecordingFlags, + IN VOID *Ad, + OUT VOID **Data, + OUT UINT64 *Length + ) +{ + EFI_STATUS Status; + UINT64 Offset; + + // + // Get AED's offset + length. + // + Status = GetAedAdsOffset ( + BlockIo, + DiskIo, + Volume, + ParentIcb, + RecordingFlags, + Ad, + &Offset, + Length + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Allocate buffer to read in AED's data. + // + *Data = AllocatePool ((UINTN) (*Length)); + if (*Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + Offset, + (UINTN) (*Length), + *Data + ); +} + +/** + Function used to serialise reads of Allocation Descriptors. + + @param[in] RecordingFlags Flag to indicate the type of descriptor. + @param[in] Ad Allocation Descriptor pointer. + @param[in, out] Buffer Buffer to hold the next Allocation Descriptor. + @param[in] Length Length of Buffer. + + @retval EFI_SUCCESS Buffer was grown to hold the next Allocation + Descriptor. + @retval EFI_OUT_OF_RESOURCES Buffer was not grown due to lack of resources. + +**/ +EFI_STATUS +GrowUpBufferToNextAd ( + IN UDF_FE_RECORDING_FLAGS RecordingFlags, + IN VOID *Ad, + IN OUT VOID **Buffer, + IN UINT64 Length + ) +{ + UINT32 ExtentLength; + + ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad); + + if (*Buffer == NULL) { + *Buffer = AllocatePool (ExtentLength); + if (*Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + *Buffer = ReallocatePool ((UINTN) Length, (UINTN) (Length + ExtentLength), *Buffer); + if (*Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + return EFI_SUCCESS; +} + +/** + Read data or size of either a File Entry or an Extended File Entry. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume Volume information pointer. + @param[in] ParentIcb Long Allocation Descriptor pointer. + @param[in] FileEntryData FE/EFE structure pointer. + @param[in, out] ReadFileInfo Read file information pointer. + + @retval EFI_SUCCESS Data or size of a FE/EFE was read. + @retval EFI_OUT_OF_RESOURCES Data or size of a FE/EFE was not read due to + lack of resources. + @retval EFI_INVALID_PARAMETER The read file flag given in ReadFileInfo is + invalid. + @retval EFI_UNSUPPORTED The FE recording flag given in FileEntryData + is not supported. + @retval other Data or size of a FE/EFE was not read. + +**/ +EFI_STATUS +ReadFile ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb, + IN VOID *FileEntryData, + IN OUT UDF_READ_FILE_INFO *ReadFileInfo + ) +{ + EFI_STATUS Status; + UINT32 LogicalBlockSize; + VOID *Data; + VOID *DataBak; + UINT64 Length; + VOID *Ad; + UINT64 AdOffset; + UINT64 Lsn; + BOOLEAN DoFreeAed; + UINT64 FilePosition; + UINT64 Offset; + UINT64 DataOffset; + UINT64 BytesLeft; + UINT64 DataLength; + BOOLEAN FinishedSeeking; + UINT32 ExtentLength; + UDF_FE_RECORDING_FLAGS RecordingFlags; + + LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize; + DoFreeAed = FALSE; + + // + // set BytesLeft to suppress incorrect compiler/analyzer warnings + // + BytesLeft = 0; + DataOffset = 0; + FilePosition = 0; + FinishedSeeking = FALSE; + Data = NULL; + + switch (ReadFileInfo->Flags) { + case ReadFileGetFileSize: + case ReadFileAllocateAndRead: + // + // Initialise ReadFileInfo structure for either getting file size, or + // reading file's recorded data. + // + ReadFileInfo->ReadLength = 0; + ReadFileInfo->FileData = NULL; + break; + case ReadFileSeekAndRead: + // + // About to seek a file and/or read its data. + // + Length = ReadFileInfo->FileSize - ReadFileInfo->FilePosition; + if (ReadFileInfo->FileDataSize > Length) { + // + // About to read beyond the EOF -- truncate it. + // + ReadFileInfo->FileDataSize = Length; + } + + // + // Initialise data to start seeking and/or reading a file. + // + BytesLeft = ReadFileInfo->FileDataSize; + DataOffset = 0; + FilePosition = 0; + FinishedSeeking = FALSE; + + break; + } + + RecordingFlags = GET_FE_RECORDING_FLAGS (FileEntryData); + switch (RecordingFlags) { + case InlineData: + // + // There are no extents for this FE/EFE. All data is inline. + // + Status = GetFileEntryData (FileEntryData, Volume->FileEntrySize, &Data, &Length); + if (EFI_ERROR (Status)) { + return Status; + } + + if (ReadFileInfo->Flags == ReadFileGetFileSize) { + ReadFileInfo->ReadLength = Length; + } else if (ReadFileInfo->Flags == ReadFileAllocateAndRead) { + // + // Allocate buffer for starting read data. + // + ReadFileInfo->FileData = AllocatePool ((UINTN) Length); + if (ReadFileInfo->FileData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Read all inline data into ReadFileInfo->FileData + // + CopyMem (ReadFileInfo->FileData, Data, (UINTN) Length); + ReadFileInfo->ReadLength = Length; + } else if (ReadFileInfo->Flags == ReadFileSeekAndRead) { + // + // If FilePosition is non-zero, seek file to FilePosition, read + // FileDataSize bytes and then updates FilePosition. + // + CopyMem ( + ReadFileInfo->FileData, + (VOID *)((UINT8 *)Data + ReadFileInfo->FilePosition), + (UINTN) ReadFileInfo->FileDataSize + ); + + ReadFileInfo->FilePosition += ReadFileInfo->FileDataSize; + } else { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + break; + + case LongAdsSequence: + case ShortAdsSequence: + // + // This FE/EFE contains a run of Allocation Descriptors. Get data + size + // for start reading them out. + // + Status = GetAdsInformation (FileEntryData, Volume->FileEntrySize, &Data, &Length); + if (EFI_ERROR (Status)) { + return Status; + } + + AdOffset = 0; + + for (;;) { + // + // Read AD. + // + Status = GetAllocationDescriptor ( + RecordingFlags, + Data, + &AdOffset, + Length, + &Ad + ); + if (Status == EFI_DEVICE_ERROR) { + Status = EFI_SUCCESS; + goto Done; + } + + // + // Check if AD is an indirect AD. If so, read Allocation Extent + // Descriptor and its extents (ADs). + // + if (GET_EXTENT_FLAGS (RecordingFlags, Ad) == ExtentIsNextExtent) { + DataBak = Data; + Status = GetAedAdsData ( + BlockIo, + DiskIo, + Volume, + ParentIcb, + RecordingFlags, + Ad, + &Data, + &Length + ); + + if (!DoFreeAed) { + DoFreeAed = TRUE; + } else { + FreePool (DataBak); + } + + if (EFI_ERROR (Status)) { + goto Error_Get_Aed; + } + ASSERT (Data != NULL); + + AdOffset = 0; + continue; + } + + ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad); + + Status = GetAllocationDescriptorLsn (RecordingFlags, + Volume, + ParentIcb, + Ad, + &Lsn); + if (EFI_ERROR (Status)) { + goto Done; + } + + switch (ReadFileInfo->Flags) { + case ReadFileGetFileSize: + ReadFileInfo->ReadLength += ExtentLength; + break; + case ReadFileAllocateAndRead: + // + // Increase FileData (if necessary) to read next extent. + // + Status = GrowUpBufferToNextAd ( + RecordingFlags, + Ad, + &ReadFileInfo->FileData, + ReadFileInfo->ReadLength + ); + if (EFI_ERROR (Status)) { + goto Error_Alloc_Buffer_To_Next_Ad; + } + + // + // Read extent's data into FileData. + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (Lsn, LogicalBlockSize), + ExtentLength, + (VOID *)((UINT8 *)ReadFileInfo->FileData + + ReadFileInfo->ReadLength) + ); + if (EFI_ERROR (Status)) { + goto Error_Read_Disk_Blk; + } + + ReadFileInfo->ReadLength += ExtentLength; + break; + case ReadFileSeekAndRead: + // + // Seek file first before reading in its data. + // + if (FinishedSeeking) { + Offset = 0; + goto Skip_File_Seek; + } + + if (FilePosition + ExtentLength < ReadFileInfo->FilePosition) { + FilePosition += ExtentLength; + goto Skip_Ad; + } + + if (FilePosition + ExtentLength > ReadFileInfo->FilePosition) { + Offset = ReadFileInfo->FilePosition - FilePosition; + } else { + Offset = 0; + } + + // + // Done with seeking file. Start reading its data. + // + FinishedSeeking = TRUE; + + Skip_File_Seek: + // + // Make sure we don't read more data than really wanted. + // + if (ExtentLength - Offset > BytesLeft) { + DataLength = BytesLeft; + } else { + DataLength = ExtentLength - Offset; + } + + // + // Read extent's data into FileData. + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + Offset + MultU64x32 (Lsn, LogicalBlockSize), + (UINTN) DataLength, + (VOID *)((UINT8 *)ReadFileInfo->FileData + + DataOffset) + ); + if (EFI_ERROR (Status)) { + goto Error_Read_Disk_Blk; + } + + // + // Update current file's position. + // + DataOffset += DataLength; + ReadFileInfo->FilePosition += DataLength; + + BytesLeft -= DataLength; + if (BytesLeft == 0) { + // + // There is no more file data to read. + // + Status = EFI_SUCCESS; + goto Done; + } + + break; + } + + Skip_Ad: + // + // Point to the next AD (extent). + // + AdOffset += AD_LENGTH (RecordingFlags); + } + + break; + case ExtendedAdsSequence: + // FIXME: Not supported. Got no volume with it, yet. + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + break; + + default: + // + // A flag value reserved by the ECMA-167 standard (3rd Edition - June + // 1997); 14.6 ICB Tag; 14.6.8 Flags (RBP 18); was found. + // + Status = EFI_UNSUPPORTED; + break; + } + +Done: + if (DoFreeAed) { + FreePool (Data); + } + + return Status; + +Error_Read_Disk_Blk: +Error_Alloc_Buffer_To_Next_Ad: + if (ReadFileInfo->Flags != ReadFileSeekAndRead) { + FreePool (ReadFileInfo->FileData); + } + + if (DoFreeAed) { + FreePool (Data); + } + +Error_Get_Aed: + return Status; +} + +/** + Find a file by its filename from a given Parent file. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume Volume information pointer. + @param[in] FileName File name string. + @param[in] Parent Parent directory file. + @param[in] Icb Long Allocation Descriptor pointer. + @param[out] File Found file. + + @retval EFI_SUCCESS The file was found. + @retval EFI_INVALID_PARAMETER One or more input parameters are invalid. + @retval EFI_NOT_FOUND The file was not found. + +**/ +EFI_STATUS +InternalFindFile ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN CHAR16 *FileName, + IN UDF_FILE_INFO *Parent, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb, + OUT UDF_FILE_INFO *File + ) +{ + EFI_STATUS Status; + UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc; + UDF_READ_DIRECTORY_INFO ReadDirInfo; + BOOLEAN Found; + CHAR16 FoundFileName[UDF_FILENAME_LENGTH]; + VOID *CompareFileEntry; + + // + // Check if both Parent->FileIdentifierDesc and Icb are NULL. + // + if ((Parent->FileIdentifierDesc == NULL) && (Icb == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check if parent file is really directory. + // + if (FE_ICB_FILE_TYPE (Parent->FileEntry) != UdfFileEntryDirectory) { + return EFI_NOT_FOUND; + } + + // + // If FileName is current file or working directory, just duplicate Parent's + // FE/EFE and FID descriptors. + // + if (StrCmp (FileName, L".") == 0) { + if (Parent->FileIdentifierDesc == NULL) { + return EFI_INVALID_PARAMETER; + } + + DuplicateFe (BlockIo, Volume, Parent->FileEntry, &File->FileEntry); + if (File->FileEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DuplicateFid (Parent->FileIdentifierDesc, &File->FileIdentifierDesc); + if (File->FileIdentifierDesc == NULL) { + FreePool (File->FileEntry); + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; + } + + // + // Start directory listing. + // + ZeroMem ((VOID *)&ReadDirInfo, sizeof (UDF_READ_DIRECTORY_INFO)); + Found = FALSE; + + for (;;) { + Status = ReadDirectoryEntry ( + BlockIo, + DiskIo, + Volume, + (Parent->FileIdentifierDesc != NULL) ? + &Parent->FileIdentifierDesc->Icb : + Icb, + Parent->FileEntry, + &ReadDirInfo, + &FileIdentifierDesc + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_DEVICE_ERROR) { + Status = EFI_NOT_FOUND; + } + + break; + } + // + // After calling function ReadDirectoryEntry(), if 'FileIdentifierDesc' is + // NULL, then the 'Status' must be EFI_OUT_OF_RESOURCES. Hence, if the code + // reaches here, 'FileIdentifierDesc' must be not NULL. + // + // The ASSERT here is for addressing a false positive NULL pointer + // dereference issue raised from static analysis. + // + ASSERT (FileIdentifierDesc != NULL); + + if (FileIdentifierDesc->FileCharacteristics & PARENT_FILE) { + // + // This FID contains the location (FE/EFE) of the parent directory of this + // directory (Parent), and if FileName is either ".." or "\\", then it's + // the expected FID. + // + if (StrCmp (FileName, L"..") == 0 || StrCmp (FileName, L"\\") == 0) { + Found = TRUE; + break; + } + } else { + Status = GetFileNameFromFid (FileIdentifierDesc, ARRAY_SIZE (FoundFileName), FoundFileName); + if (EFI_ERROR (Status)) { + break; + } + + if (StrCmp (FileName, FoundFileName) == 0) { + // + // FID has been found. Prepare to find its respective FE/EFE. + // + Found = TRUE; + break; + } + } + + FreePool ((VOID *)FileIdentifierDesc); + } + + if (ReadDirInfo.DirectoryData != NULL) { + // + // Free all allocated resources for the directory listing. + // + FreePool (ReadDirInfo.DirectoryData); + } + + if (Found) { + Status = EFI_SUCCESS; + + File->FileIdentifierDesc = FileIdentifierDesc; + + // + // If the requested file is root directory, then the FE/EFE was already + // retrieved in UdfOpenVolume() function, thus no need to find it again. + // + // Otherwise, find FE/EFE from the respective FID. + // + if (StrCmp (FileName, L"\\") != 0) { + Status = FindFileEntry ( + BlockIo, + DiskIo, + Volume, + &FileIdentifierDesc->Icb, + &CompareFileEntry + ); + if (EFI_ERROR (Status)) { + goto Error_Find_Fe; + } + + // + // Make sure that both Parent's FE/EFE and found FE/EFE are not equal. + // + if (CompareMem ((VOID *)Parent->FileEntry, (VOID *)CompareFileEntry, + Volume->FileEntrySize) != 0) { + File->FileEntry = CompareFileEntry; + } else { + FreePool ((VOID *)FileIdentifierDesc); + FreePool ((VOID *)CompareFileEntry); + Status = EFI_NOT_FOUND; + } + } + } + + return Status; + +Error_Find_Fe: + FreePool ((VOID *)FileIdentifierDesc); + + return Status; +} + +/** + Read volume information on a medium which contains a valid UDF file system. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[out] Volume UDF volume information structure. + + @retval EFI_SUCCESS Volume information read. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The volume was not read due to lack of resources. + +**/ +EFI_STATUS +ReadUdfVolumeInformation ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + OUT UDF_VOLUME_INFO *Volume + ) +{ + EFI_STATUS Status; + + // + // Read all necessary UDF volume information and keep it private to the driver + // + Status = ReadVolumeFileStructure ( + BlockIo, + DiskIo, + Volume + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find File Set Descriptor + // + Status = FindFileSetDescriptor (BlockIo, DiskIo, Volume); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +/** + Find the root directory on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[out] File Root directory file. + + @retval EFI_SUCCESS Root directory found. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The root directory was not found due to lack of + resources. + +**/ +EFI_STATUS +FindRootDirectory ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + OUT UDF_FILE_INFO *File + ) +{ + EFI_STATUS Status; + UDF_FILE_INFO Parent; + + Status = FindFileEntry ( + BlockIo, + DiskIo, + Volume, + &Volume->FileSetDesc.RootDirectoryIcb, + &File->FileEntry + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Parent.FileEntry = File->FileEntry; + Parent.FileIdentifierDesc = NULL; + + Status = FindFile ( + BlockIo, + DiskIo, + Volume, + L"\\", + NULL, + &Parent, + &Volume->FileSetDesc.RootDirectoryIcb, + File + ); + if (EFI_ERROR (Status)) { + FreePool (File->FileEntry); + } + + return Status; +} + +/** + Find either a File Entry or a Extended File Entry from a given ICB. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] Icb ICB of the FID. + @param[out] FileEntry File Entry or Extended File Entry. + + @retval EFI_SUCCESS File Entry or Extended File Entry found. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The FE/EFE entry was not found due to lack of + resources. + +**/ +EFI_STATUS +FindFileEntry ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb, + OUT VOID **FileEntry + ) +{ + EFI_STATUS Status; + UINT64 Lsn; + UINT32 LogicalBlockSize; + UDF_DESCRIPTOR_TAG *DescriptorTag; + VOID *ReadBuffer; + + Status = GetLongAdLsn (Volume, Icb, &Lsn); + if (EFI_ERROR (Status)) { + return Status; + } + + LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize; + + ReadBuffer = AllocateZeroPool (Volume->FileEntrySize); + if (ReadBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Read extent. + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (Lsn, LogicalBlockSize), + Volume->FileEntrySize, + ReadBuffer + ); + if (EFI_ERROR (Status)) { + goto Error_Read_Disk_Blk; + } + + DescriptorTag = ReadBuffer; + + // + // Check if the read extent contains a valid Tag Identifier for the expected + // FE/EFE. + // + if (DescriptorTag->TagIdentifier != UdfFileEntry && + DescriptorTag->TagIdentifier != UdfExtendedFileEntry) { + Status = EFI_VOLUME_CORRUPTED; + goto Error_Invalid_Fe; + } + + *FileEntry = ReadBuffer; + return EFI_SUCCESS; + +Error_Invalid_Fe: +Error_Read_Disk_Blk: + FreePool (ReadBuffer); + + return Status; +} + +/** + Find a file given its absolute path on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] FilePath File's absolute path. + @param[in] Root Root directory file. + @param[in] Parent Parent directory file. + @param[in] Icb ICB of Parent. + @param[out] File Found file. + + @retval EFI_SUCCESS FilePath was found. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The FilePath file was not found due to lack of + resources. + +**/ +EFI_STATUS +FindFile ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN CHAR16 *FilePath, + IN UDF_FILE_INFO *Root, + IN UDF_FILE_INFO *Parent, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb, + OUT UDF_FILE_INFO *File + ) +{ + EFI_STATUS Status; + CHAR16 FileName[UDF_FILENAME_LENGTH]; + CHAR16 *FileNamePointer; + UDF_FILE_INFO PreviousFile; + VOID *FileEntry; + + Status = EFI_NOT_FOUND; + + CopyMem ((VOID *)&PreviousFile, (VOID *)Parent, sizeof (UDF_FILE_INFO)); + while (*FilePath != L'\0') { + FileNamePointer = FileName; + while (*FilePath != L'\0' && *FilePath != L'\\') { + if ((((UINTN)FileNamePointer - (UINTN)FileName) / sizeof (CHAR16)) >= + (ARRAY_SIZE (FileName) - 1)) { + return EFI_NOT_FOUND; + } + + *FileNamePointer++ = *FilePath++; + } + + *FileNamePointer = L'\0'; + if (FileName[0] == L'\0') { + // + // Open root directory. + // + if (Root == NULL) { + // + // There is no file found for the root directory yet. So, find only its + // FID by now. + // + // See UdfOpenVolume() function. + // + Status = InternalFindFile (BlockIo, + DiskIo, + Volume, + L"\\", + &PreviousFile, + Icb, + File); + } else { + // + // We've already a file pointer (Root) for the root directory. Duplicate + // its FE/EFE and FID descriptors. + // + Status = EFI_SUCCESS; + DuplicateFe (BlockIo, Volume, Root->FileEntry, &File->FileEntry); + if (File->FileEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + // + // File->FileEntry is not NULL. + // + DuplicateFid (Root->FileIdentifierDesc, &File->FileIdentifierDesc); + if (File->FileIdentifierDesc == NULL) { + FreePool (File->FileEntry); + Status = EFI_OUT_OF_RESOURCES; + } + } + } + } else { + // + // No root directory. Find filename from the current directory. + // + Status = InternalFindFile (BlockIo, + DiskIo, + Volume, + FileName, + &PreviousFile, + Icb, + File); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If the found file is a symlink, then find its respective FE/EFE and + // FID descriptors. + // + if (FE_ICB_FILE_TYPE (File->FileEntry) == UdfFileEntrySymlink) { + FreePool ((VOID *)File->FileIdentifierDesc); + + FileEntry = File->FileEntry; + + Status = ResolveSymlink (BlockIo, + DiskIo, + Volume, + &PreviousFile, + FileEntry, + File); + + FreePool (FileEntry); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent, + sizeof (UDF_FILE_INFO)) != 0) { + CleanupFileInformation (&PreviousFile); + } + + CopyMem ((VOID *)&PreviousFile, (VOID *)File, sizeof (UDF_FILE_INFO)); + if (*FilePath != L'\0' && *FilePath == L'\\') { + FilePath++; + } + } + + return Status; +} + +/** + Read a directory entry at a time on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] ParentIcb ICB of the parent file. + @param[in] FileEntryData FE/EFE of the parent file. + @param[in, out] ReadDirInfo Next read directory listing structure + information. + @param[out] FoundFid File Identifier Descriptor pointer. + + @retval EFI_SUCCESS Directory entry read. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The directory entry was not read due to lack of + resources. + +**/ +EFI_STATUS +ReadDirectoryEntry ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb, + IN VOID *FileEntryData, + IN OUT UDF_READ_DIRECTORY_INFO *ReadDirInfo, + OUT UDF_FILE_IDENTIFIER_DESCRIPTOR **FoundFid + ) +{ + EFI_STATUS Status; + UDF_READ_FILE_INFO ReadFileInfo; + UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc; + + if (ReadDirInfo->DirectoryData == NULL) { + // + // The directory's recorded data has not been read yet. So let's cache it + // into memory and the next calls won't need to read it again. + // + ReadFileInfo.Flags = ReadFileAllocateAndRead; + + Status = ReadFile ( + BlockIo, + DiskIo, + Volume, + ParentIcb, + FileEntryData, + &ReadFileInfo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill in ReadDirInfo structure with the read directory's data information. + // + ReadDirInfo->DirectoryData = ReadFileInfo.FileData; + ReadDirInfo->DirectoryLength = ReadFileInfo.ReadLength; + } + + do { + if (ReadDirInfo->FidOffset >= ReadDirInfo->DirectoryLength) { + // + // There are no longer FIDs for this directory. By returning + // EFI_DEVICE_ERROR to the callee will indicate end of directory + // listening. + // + return EFI_DEVICE_ERROR; + } + + // + // Get FID for this entry. + // + FileIdentifierDesc = GET_FID_FROM_ADS (ReadDirInfo->DirectoryData, + ReadDirInfo->FidOffset); + // + // Update FidOffset to point to next FID. + // + ReadDirInfo->FidOffset += GetFidDescriptorLength (FileIdentifierDesc); + } while (FileIdentifierDesc->FileCharacteristics & DELETED_FILE); + + DuplicateFid (FileIdentifierDesc, FoundFid); + if (*FoundFid == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Get a filename (encoded in OSTA-compressed format) from a File Identifier + Descriptor on an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The File Identifier Descriptor is external input, so this routine will do + basic validation for File Identifier Descriptor and report status. + + @param[in] FileIdentifierDesc File Identifier Descriptor pointer. + @param[in] CharMax The maximum number of FileName Unicode char, + including terminating null char. + @param[out] FileName Decoded filename. + + @retval EFI_SUCCESS Filename decoded and read. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The string buffer FileName cannot hold the + decoded filename. +**/ +EFI_STATUS +GetFileNameFromFid ( + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc, + IN UINTN CharMax, + OUT CHAR16 *FileName + ) +{ + UINT8 *OstaCompressed; + UINT8 CompressionId; + UINT8 Length; + UINTN Index; + CHAR16 *FileNameBak; + + if (CharMax == 0) { + return EFI_BUFFER_TOO_SMALL; + } + + OstaCompressed = + (UINT8 *)( + (UINT8 *)FileIdentifierDesc->Data + + FileIdentifierDesc->LengthOfImplementationUse + ); + + CompressionId = OstaCompressed[0]; + if (!IS_VALID_COMPRESSION_ID (CompressionId)) { + return EFI_VOLUME_CORRUPTED; + } + + FileNameBak = FileName; + + // + // Decode filename. + // + Length = FileIdentifierDesc->LengthOfFileIdentifier; + if (CompressionId == 16) { + if (((UINTN)Length >> 1) > CharMax) { + return EFI_BUFFER_TOO_SMALL; + } + } else { + if ((Length != 0) && ((UINTN)Length - 1 > CharMax)) { + return EFI_BUFFER_TOO_SMALL; + } + } + + for (Index = 1; Index < Length; Index++) { + if (CompressionId == 16) { + *FileName = OstaCompressed[Index++] << 8; + } else { + *FileName = 0; + } + + if (Index < Length) { + *FileName |= (CHAR16)(OstaCompressed[Index]); + } + + FileName++; + } + + Index = ((UINTN)FileName - (UINTN)FileNameBak) / sizeof (CHAR16); + if (Index > CharMax - 1) { + Index = CharMax - 1; + } + FileNameBak[Index] = L'\0'; + + return EFI_SUCCESS; +} + +/** + Resolve a symlink file on an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The Path Component is external input, so this routine will do basic + validation for Path Component and report status. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] Parent Parent file. + @param[in] FileEntryData FE/EFE structure pointer. + @param[out] File Resolved file. + + @retval EFI_SUCCESS Symlink file resolved. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The symlink file was not resolved due to lack of + resources. + +**/ +EFI_STATUS +ResolveSymlink ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_FILE_INFO *Parent, + IN VOID *FileEntryData, + OUT UDF_FILE_INFO *File + ) +{ + EFI_STATUS Status; + UDF_READ_FILE_INFO ReadFileInfo; + UINT8 *Data; + UINT64 Length; + UINT8 *EndData; + UDF_PATH_COMPONENT *PathComp; + UINT8 PathCompLength; + CHAR16 FileName[UDF_FILENAME_LENGTH]; + CHAR16 *Char; + UINTN Index; + UINT8 CompressionId; + UDF_FILE_INFO PreviousFile; + BOOLEAN NotParent; + BOOLEAN NotFile; + + ZeroMem ((VOID *)File, sizeof (UDF_FILE_INFO)); + + // + // Symlink files on UDF volumes do not contain so much data other than + // Path Components which resolves to real filenames, so it's OK to read in + // all its data here -- usually the data will be inline with the FE/EFE for + // lower filenames. + // + ReadFileInfo.Flags = ReadFileAllocateAndRead; + + Status = ReadFile ( + BlockIo, + DiskIo, + Volume, + &Parent->FileIdentifierDesc->Icb, + FileEntryData, + &ReadFileInfo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Length = ReadFileInfo.ReadLength; + + Data = (UINT8 *)ReadFileInfo.FileData; + EndData = Data + Length; + + CopyMem ((VOID *)&PreviousFile, (VOID *)Parent, sizeof (UDF_FILE_INFO)); + + for (;;) { + PathComp = (UDF_PATH_COMPONENT *)Data; + + PathCompLength = PathComp->LengthOfComponentIdentifier; + + switch (PathComp->ComponentType) { + case 1: + // + // This Path Component specifies the root directory hierarchy subject to + // agreement between the originator and recipient of the medium. Skip it. + // + // Fall through. + // + case 2: + // + // "\\." of the current directory. Read next Path Component. + // + goto Next_Path_Component; + case 3: + // + // ".." (parent directory). Go to it. + // + CopyMem ((VOID *)FileName, L"..", 6); + break; + case 4: + // + // "." (current file). Duplicate both FE/EFE and FID of this file. + // + DuplicateFe (BlockIo, Volume, PreviousFile.FileEntry, &File->FileEntry); + if (File->FileEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error_Find_File; + } + + DuplicateFid (PreviousFile.FileIdentifierDesc, + &File->FileIdentifierDesc); + if (File->FileIdentifierDesc == NULL) { + FreePool (File->FileEntry); + Status = EFI_OUT_OF_RESOURCES; + goto Error_Find_File; + } + goto Next_Path_Component; + case 5: + // + // This Path Component identifies an object, either a file or a + // directory or an alias. + // + // Decode it from the compressed data in ComponentIdentifier and find + // respective path. + // + CompressionId = PathComp->ComponentIdentifier[0]; + if (!IS_VALID_COMPRESSION_ID (CompressionId)) { + return EFI_VOLUME_CORRUPTED; + } + + if ((UINTN)PathComp->ComponentIdentifier + PathCompLength > (UINTN)EndData) { + return EFI_VOLUME_CORRUPTED; + } + + Char = FileName; + for (Index = 1; Index < PathCompLength; Index++) { + if (CompressionId == 16) { + *Char = *(UINT8 *)((UINT8 *)PathComp->ComponentIdentifier + + Index) << 8; + Index++; + } else { + if (Index > ARRAY_SIZE (FileName)) { + return EFI_UNSUPPORTED; + } + *Char = 0; + } + + if (Index < Length) { + *Char |= (CHAR16)(*(UINT8 *)((UINT8 *)PathComp->ComponentIdentifier + Index)); + } + + Char++; + } + + Index = ((UINTN)Char - (UINTN)FileName) / sizeof (CHAR16); + if (Index > ARRAY_SIZE (FileName) - 1) { + Index = ARRAY_SIZE (FileName) - 1; + } + FileName[Index] = L'\0'; + break; + default: + // + // According to the ECMA-167 standard (3rd Edition - June 1997), Section + // 14.16.1.1, all other values are reserved. + // + Status = EFI_VOLUME_CORRUPTED; + goto Error_Find_File; + } + + // + // Find file from the read filename in symlink's file data. + // + Status = InternalFindFile ( + BlockIo, + DiskIo, + Volume, + FileName, + &PreviousFile, + NULL, + File + ); + if (EFI_ERROR (Status)) { + goto Error_Find_File; + } + + Next_Path_Component: + Data += sizeof (UDF_PATH_COMPONENT) + PathCompLength; + if (Data >= EndData) { + break; + } + + // + // Check the content in the file info pointed by File. + // + if ((File->FileEntry == NULL) || (File->FileIdentifierDesc == NULL)) { + Status = EFI_VOLUME_CORRUPTED; + goto Error_Find_File; + } + + NotParent = (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent, + sizeof (UDF_FILE_INFO)) != 0); + NotFile = (CompareMem ((VOID *)&PreviousFile, (VOID *)File, + sizeof (UDF_FILE_INFO)) != 0); + + if (NotParent && NotFile) { + CleanupFileInformation (&PreviousFile); + } + + if (NotFile) { + CopyMem ((VOID *)&PreviousFile, (VOID *)File, sizeof (UDF_FILE_INFO)); + } + } + + // + // Unmap the symlink file. + // + FreePool (ReadFileInfo.FileData); + + // + // Check the content in the resolved file info. + // + if ((File->FileEntry == NULL) || (File->FileIdentifierDesc == NULL)) { + return EFI_VOLUME_CORRUPTED; + } + + return EFI_SUCCESS; + +Error_Find_File: + if (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent, + sizeof (UDF_FILE_INFO)) != 0) { + CleanupFileInformation (&PreviousFile); + } + + FreePool (ReadFileInfo.FileData); + + return Status; +} + +/** + Clean up in-memory UDF file information. + + @param[in] File File information pointer. + +**/ +VOID +CleanupFileInformation ( + IN UDF_FILE_INFO *File + ) +{ + if (File->FileEntry != NULL) { + FreePool (File->FileEntry); + } + if (File->FileIdentifierDesc != NULL) { + FreePool ((VOID *)File->FileIdentifierDesc); + } + + ZeroMem ((VOID *)File, sizeof (UDF_FILE_INFO)); +} + +/** + Find a file from its absolute path on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] File File information structure. + @param[out] Size Size of the file. + + @retval EFI_SUCCESS File size calculated and set in Size. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The file size was not calculated due to lack of + resources. + +**/ +EFI_STATUS +GetFileSize ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_FILE_INFO *File, + OUT UINT64 *Size + ) +{ + EFI_STATUS Status; + UDF_READ_FILE_INFO ReadFileInfo; + + ReadFileInfo.Flags = ReadFileGetFileSize; + + Status = ReadFile ( + BlockIo, + DiskIo, + Volume, + &File->FileIdentifierDesc->Icb, + File->FileEntry, + &ReadFileInfo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *Size = ReadFileInfo.ReadLength; + + return EFI_SUCCESS; +} + +/** + Set information about a file on an UDF volume. + + @param[in] File File pointer. + @param[in] FileSize Size of the file. + @param[in] FileName Filename of the file. + @param[in, out] BufferSize Size of the returned file infomation. + @param[out] Buffer Data of the returned file information. + + @retval EFI_SUCCESS File information set. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The file information was not set due to lack of + resources. + +**/ +EFI_STATUS +SetFileInfo ( + IN UDF_FILE_INFO *File, + IN UINT64 FileSize, + IN CHAR16 *FileName, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + UINTN FileInfoLength; + EFI_FILE_INFO *FileInfo; + UDF_FILE_ENTRY *FileEntry; + UDF_EXTENDED_FILE_ENTRY *ExtendedFileEntry; + UDF_DESCRIPTOR_TAG *DescriptorTag; + + // + // Calculate the needed size for the EFI_FILE_INFO structure. + // + FileInfoLength = sizeof (EFI_FILE_INFO) + ((FileName != NULL) ? + StrSize (FileName) : + sizeof (CHAR16)); + if (*BufferSize < FileInfoLength) { + // + // The given Buffer has no size enough for EFI_FILE_INFO structure. + // + *BufferSize = FileInfoLength; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Buffer now contains room enough to store EFI_FILE_INFO structure. + // Now, fill it in with all necessary information about the file. + // + FileInfo = (EFI_FILE_INFO *)Buffer; + FileInfo->Size = FileInfoLength; + FileInfo->Attribute &= ~EFI_FILE_VALID_ATTR; + FileInfo->Attribute |= EFI_FILE_READ_ONLY; + + if (IS_FID_DIRECTORY_FILE (File->FileIdentifierDesc)) { + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + } else if (IS_FID_NORMAL_FILE (File->FileIdentifierDesc)) { + FileInfo->Attribute |= EFI_FILE_ARCHIVE; + } + + if (IS_FID_HIDDEN_FILE (File->FileIdentifierDesc)) { + FileInfo->Attribute |= EFI_FILE_HIDDEN; + } + + DescriptorTag = File->FileEntry; + + if (DescriptorTag->TagIdentifier == UdfFileEntry) { + FileEntry = (UDF_FILE_ENTRY *)File->FileEntry; + + // + // Check if FE has the system attribute set. + // + if (FileEntry->IcbTag.Flags & (1 << 10)) { + FileInfo->Attribute |= EFI_FILE_SYSTEM; + } + + FileInfo->FileSize = FileSize; + FileInfo->PhysicalSize = FileSize; + + FileInfo->CreateTime.Year = FileEntry->AccessTime.Year; + FileInfo->CreateTime.Month = FileEntry->AccessTime.Month; + FileInfo->CreateTime.Day = FileEntry->AccessTime.Day; + FileInfo->CreateTime.Hour = FileEntry->AccessTime.Hour; + FileInfo->CreateTime.Minute = FileEntry->AccessTime.Minute; + FileInfo->CreateTime.Second = FileEntry->AccessTime.Second; + FileInfo->CreateTime.Nanosecond = + FileEntry->AccessTime.HundredsOfMicroseconds; + + FileInfo->LastAccessTime.Year = + FileEntry->AccessTime.Year; + FileInfo->LastAccessTime.Month = + FileEntry->AccessTime.Month; + FileInfo->LastAccessTime.Day = + FileEntry->AccessTime.Day; + FileInfo->LastAccessTime.Hour = + FileEntry->AccessTime.Hour; + FileInfo->LastAccessTime.Minute = + FileEntry->AccessTime.Minute; + FileInfo->LastAccessTime.Second = + FileEntry->AccessTime.Second; + FileInfo->LastAccessTime.Nanosecond = + FileEntry->AccessTime.HundredsOfMicroseconds; + } else if (DescriptorTag->TagIdentifier == UdfExtendedFileEntry) { + ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)File->FileEntry; + + // + // Check if EFE has the system attribute set. + // + if (ExtendedFileEntry->IcbTag.Flags & (1 << 10)) { + FileInfo->Attribute |= EFI_FILE_SYSTEM; + } + + FileInfo->FileSize = FileSize; + FileInfo->PhysicalSize = FileSize; + + FileInfo->CreateTime.Year = ExtendedFileEntry->CreationTime.Year; + FileInfo->CreateTime.Month = ExtendedFileEntry->CreationTime.Month; + FileInfo->CreateTime.Day = ExtendedFileEntry->CreationTime.Day; + FileInfo->CreateTime.Hour = ExtendedFileEntry->CreationTime.Hour; + FileInfo->CreateTime.Minute = ExtendedFileEntry->CreationTime.Second; + FileInfo->CreateTime.Second = ExtendedFileEntry->CreationTime.Second; + FileInfo->CreateTime.Nanosecond = + ExtendedFileEntry->AccessTime.HundredsOfMicroseconds; + + FileInfo->LastAccessTime.Year = + ExtendedFileEntry->AccessTime.Year; + FileInfo->LastAccessTime.Month = + ExtendedFileEntry->AccessTime.Month; + FileInfo->LastAccessTime.Day = + ExtendedFileEntry->AccessTime.Day; + FileInfo->LastAccessTime.Hour = + ExtendedFileEntry->AccessTime.Hour; + FileInfo->LastAccessTime.Minute = + ExtendedFileEntry->AccessTime.Minute; + FileInfo->LastAccessTime.Second = + ExtendedFileEntry->AccessTime.Second; + FileInfo->LastAccessTime.Nanosecond = + ExtendedFileEntry->AccessTime.HundredsOfMicroseconds; + } + + FileInfo->CreateTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE; + FileInfo->CreateTime.Daylight = EFI_TIME_ADJUST_DAYLIGHT; + FileInfo->LastAccessTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE; + FileInfo->LastAccessTime.Daylight = EFI_TIME_ADJUST_DAYLIGHT; + + CopyMem ((VOID *)&FileInfo->ModificationTime, + (VOID *)&FileInfo->LastAccessTime, + sizeof (EFI_TIME)); + + if (FileName != NULL) { + StrCpyS (FileInfo->FileName, StrLen (FileName) + 1, FileName); + } else { + FileInfo->FileName[0] = '\0'; + } + + *BufferSize = FileInfoLength; + + return EFI_SUCCESS; +} + +/** + Get volume label of an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The File Set Descriptor is external input, so this routine will do basic + validation for File Set Descriptor and report status. + + @param[in] Volume Volume information pointer. + @param[in] CharMax The maximum number of Unicode char in String, + including terminating null char. + @param[out] String String buffer pointer to store the volume label. + + @retval EFI_SUCCESS Volume label is returned. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The string buffer String cannot hold the + volume label. + +**/ +EFI_STATUS +GetVolumeLabel ( + IN UDF_VOLUME_INFO *Volume, + IN UINTN CharMax, + OUT CHAR16 *String + ) +{ + UDF_FILE_SET_DESCRIPTOR *FileSetDesc; + UINTN Index; + UINT8 *OstaCompressed; + UINT8 CompressionId; + CHAR16 *StringBak; + + FileSetDesc = &Volume->FileSetDesc; + + OstaCompressed = &FileSetDesc->LogicalVolumeIdentifier[0]; + + CompressionId = OstaCompressed[0]; + if (!IS_VALID_COMPRESSION_ID (CompressionId)) { + return EFI_VOLUME_CORRUPTED; + } + + StringBak = String; + for (Index = 1; Index < 128; Index++) { + if (CompressionId == 16) { + if ((Index >> 1) > CharMax) { + return EFI_BUFFER_TOO_SMALL; + } + + *String = *(UINT8 *)(OstaCompressed + Index) << 8; + Index++; + } else { + if (Index > CharMax) { + return EFI_BUFFER_TOO_SMALL; + } + + *String = 0; + } + + if (Index < 128) { + *String |= (CHAR16)(*(UINT8 *)(OstaCompressed + Index)); + } + + // + // Unlike FID Identifiers, Logical Volume Identifier is stored in a + // NULL-terminated OSTA compressed format, so we must check for the NULL + // character. + // + if (*String == L'\0') { + break; + } + + String++; + } + + Index = ((UINTN)String - (UINTN)StringBak) / sizeof (CHAR16); + if (Index > CharMax - 1) { + Index = CharMax - 1; + } + StringBak[Index] = L'\0'; + + return EFI_SUCCESS; +} + +/** + Get volume and free space size information of an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The Logical Volume Descriptor and the Logical Volume Integrity Descriptor are + external inputs, so this routine will do basic validation for both descriptors + and report status. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[out] VolumeSize Volume size. + @param[out] FreeSpaceSize Free space size. + + @retval EFI_SUCCESS Volume and free space size calculated. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The volume and free space size were not + calculated due to lack of resources. + +**/ +EFI_STATUS +GetVolumeSize ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + OUT UINT64 *VolumeSize, + OUT UINT64 *FreeSpaceSize + ) +{ + EFI_STATUS Status; + UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc; + UDF_EXTENT_AD *ExtentAd; + UINT64 Lsn; + UINT32 LogicalBlockSize; + UDF_LOGICAL_VOLUME_INTEGRITY *LogicalVolInt; + UDF_DESCRIPTOR_TAG *DescriptorTag; + UINTN Index; + UINTN Length; + UINT32 LsnsNo; + + LogicalVolDesc = &Volume->LogicalVolDesc; + + ExtentAd = &LogicalVolDesc->IntegritySequenceExtent; + + if ((ExtentAd->ExtentLength == 0) || + (ExtentAd->ExtentLength < sizeof (UDF_LOGICAL_VOLUME_INTEGRITY))) { + return EFI_VOLUME_CORRUPTED; + } + + LogicalVolInt = AllocatePool (ExtentAd->ExtentLength); + if (LogicalVolInt == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get location of Logical Volume Integrity Descriptor + // + Lsn = (UINT64)ExtentAd->ExtentLocation - Volume->MainVdsStartLocation; + + LogicalBlockSize = LogicalVolDesc->LogicalBlockSize; + + // + // Read disk block + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32 (Lsn, LogicalBlockSize), + ExtentAd->ExtentLength, + LogicalVolInt + ); + if (EFI_ERROR (Status)) { + goto Out_Free; + } + + DescriptorTag = &LogicalVolInt->DescriptorTag; + + // + // Check if read block is a Logical Volume Integrity Descriptor + // + if (DescriptorTag->TagIdentifier != UdfLogicalVolumeIntegrityDescriptor) { + Status = EFI_VOLUME_CORRUPTED; + goto Out_Free; + } + + if ((LogicalVolInt->NumberOfPartitions > MAX_UINT32 / sizeof (UINT32) / 2) || + (LogicalVolInt->NumberOfPartitions * sizeof (UINT32) * 2 > + ExtentAd->ExtentLength - sizeof (UDF_LOGICAL_VOLUME_INTEGRITY))) { + Status = EFI_VOLUME_CORRUPTED; + goto Out_Free; + } + + *VolumeSize = 0; + *FreeSpaceSize = 0; + + Length = LogicalVolInt->NumberOfPartitions; + for (Index = 0; Index < Length; Index += sizeof (UINT32)) { + LsnsNo = *(UINT32 *)((UINT8 *)LogicalVolInt->Data + Index); + // + // Check if size is not specified + // + if (LsnsNo == 0xFFFFFFFFUL) { + continue; + } + // + // Accumulate free space size + // + *FreeSpaceSize += MultU64x32 ((UINT64)LsnsNo, LogicalBlockSize); + } + + Length = LogicalVolInt->NumberOfPartitions * sizeof (UINT32) * 2; + for (; Index < Length; Index += sizeof (UINT32)) { + LsnsNo = *(UINT32 *)((UINT8 *)LogicalVolInt->Data + Index); + // + // Check if size is not specified + // + if (LsnsNo == 0xFFFFFFFFUL) { + continue; + } + // + // Accumulate used volume space + // + *VolumeSize += MultU64x32 ((UINT64)LsnsNo, LogicalBlockSize); + } + + Status = EFI_SUCCESS; + +Out_Free: + // + // Free Logical Volume Integrity Descriptor + // + FreePool (LogicalVolInt); + + return Status; +} + +/** + Seek a file and read its data into memory on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] File File information structure. + @param[in] FileSize Size of the file. + @param[in, out] FilePosition File position. + @param[in, out] Buffer File data. + @param[in, out] BufferSize Read size. + + @retval EFI_SUCCESS File seeked and read. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The file's recorded data was not read due to lack + of resources. + +**/ +EFI_STATUS +ReadFileData ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_FILE_INFO *File, + IN UINT64 FileSize, + IN OUT UINT64 *FilePosition, + IN OUT VOID *Buffer, + IN OUT UINT64 *BufferSize + ) +{ + EFI_STATUS Status; + UDF_READ_FILE_INFO ReadFileInfo; + + ReadFileInfo.Flags = ReadFileSeekAndRead; + ReadFileInfo.FilePosition = *FilePosition; + ReadFileInfo.FileData = Buffer; + ReadFileInfo.FileDataSize = *BufferSize; + ReadFileInfo.FileSize = FileSize; + + Status = ReadFile ( + BlockIo, + DiskIo, + Volume, + &File->FileIdentifierDesc->Icb, + File->FileEntry, + &ReadFileInfo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *BufferSize = ReadFileInfo.FileDataSize; + *FilePosition = ReadFileInfo.FilePosition; + + return EFI_SUCCESS; +} + +/** + Check if ControllerHandle supports an UDF file system. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + + @retval EFI_SUCCESS UDF file system found. + @retval EFI_UNSUPPORTED UDF file system not found. + +**/ +EFI_STATUS +SupportUdfFileSystem ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + EFI_DEVICE_PATH_PROTOCOL *LastDevicePathNode; + EFI_GUID *VendorDefinedGuid; + + // + // Open Device Path protocol on ControllerHandle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = EFI_UNSUPPORTED; + + // + // Get last Device Path node + // + LastDevicePathNode = NULL; + DevicePathNode = DevicePath; + while (!IsDevicePathEnd (DevicePathNode)) { + LastDevicePathNode = DevicePathNode; + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + // + // Check if last Device Path node contains a Vendor-Defined Media Device Path + // of an UDF file system. + // + if (LastDevicePathNode != NULL && + DevicePathType (LastDevicePathNode) == MEDIA_DEVICE_PATH && + DevicePathSubType (LastDevicePathNode) == MEDIA_VENDOR_DP) { + VendorDefinedGuid = (EFI_GUID *)((UINTN)LastDevicePathNode + + OFFSET_OF (VENDOR_DEVICE_PATH, Guid)); + if (CompareGuid (VendorDefinedGuid, &gUdfDevPathGuid)) { + Status = EFI_SUCCESS; + } + } + + // + // Close Device Path protocol on ControllerHandle + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c new file mode 100644 index 000000000..d15f44971 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.c @@ -0,0 +1,331 @@ +/** @file + UDF/ECMA-167 file system driver. + + Copyright (C) 2014-2017 Paulo Alcantara + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Udf.h" + +// +// UDF filesystem driver's Global Variables. +// +EFI_DRIVER_BINDING_PROTOCOL gUdfDriverBinding = { + UdfDriverBindingSupported, + UdfDriverBindingStart, + UdfDriverBindingStop, + 0x10, + NULL, + NULL +}; + +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gUdfSimpleFsTemplate = { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, + UdfOpenVolume +}; + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a BlockIo and DiskIo protocol or a BlockIo2 protocol can be + supported. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UdfDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DISK_IO_PROTOCOL *DiskIo; + + // + // Open DiskIo protocol on ControllerHandle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **)&DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close DiskIo protocol on ControllerHandle + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + // + // Test whether ControllerHandle supports BlockIo protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Start this driver on ControllerHandle by opening a Block IO or a Block IO2 + or both, and Disk IO protocol, reading Device Path, and creating a child + handle with a Disk IO and device path protocol. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on + ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UdfDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + PRIVATE_UDF_SIMPLE_FS_DATA *PrivFsData; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Open BlockIo protocol on ControllerHandle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **)&BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + + // + // Open DiskIo protocol on ControllerHandle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **)&DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT_EFI_ERROR (Status); + + // + // Check if ControllerHandle supports an UDF file system + // + Status = SupportUdfFileSystem (This, ControllerHandle); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Initialize private file system structure + // + PrivFsData = + (PRIVATE_UDF_SIMPLE_FS_DATA *) + AllocateZeroPool (sizeof (PRIVATE_UDF_SIMPLE_FS_DATA)); + if (PrivFsData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Create new child handle + // + PrivFsData->Signature = PRIVATE_UDF_SIMPLE_FS_DATA_SIGNATURE; + PrivFsData->BlockIo = BlockIo; + PrivFsData->DiskIo = DiskIo; + PrivFsData->Handle = ControllerHandle; + + // + // Set up SimpleFs protocol + // + CopyMem ((VOID *)&PrivFsData->SimpleFs, (VOID *)&gUdfSimpleFsTemplate, + sizeof (EFI_SIMPLE_FILE_SYSTEM_PROTOCOL)); + + // + // Install child handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &PrivFsData->Handle, + &gEfiSimpleFileSystemProtocolGuid, + &PrivFsData->SimpleFs, + NULL + ); + +Exit: + if (EFI_ERROR (Status)) { + // + // Close DiskIo protocol on ControllerHandle + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + // + // Close BlockIo protocol on ControllerHandle + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +UdfDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + PRIVATE_UDF_SIMPLE_FS_DATA *PrivFsData; + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFs; + + // + // Open SimpleFs protocol on ControllerHandle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID **)&SimpleFs, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (SimpleFs); + + // + // Uninstall child handle + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + PrivFsData->Handle, + &gEfiSimpleFileSystemProtocolGuid, + &PrivFsData->SimpleFs, + NULL + ); + + FreePool ((VOID *)PrivFsData); + } + + if (!EFI_ERROR (Status)) { + // + // Close DiskIo protocol on ControllerHandle + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + // + // Close BlockIo protocol on ControllerHandle + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + + return Status; +} + +/** + The user Entry Point for UDF file system driver. The user code starts with + this 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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeUdf ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUdfDriverBinding, + ImageHandle, + &gUdfComponentName, + &gUdfComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h new file mode 100644 index 000000000..6c623a904 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h @@ -0,0 +1,1215 @@ +/** @file + UDF/ECMA-167 file system driver. + + Copyright (C) 2014-2017 Paulo Alcantara + Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _UDF_H_ +#define _UDF_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// +// C5BD4D42-1A76-4996-8956-73CDA326CD0A +// +#define EFI_UDF_DEVICE_PATH_GUID \ + { 0xC5BD4D42, 0x1A76, 0x4996, \ + { 0x89, 0x56, 0x73, 0xCD, 0xA3, 0x26, 0xCD, 0x0A } \ + } + +#define FE_ICB_FILE_TYPE(_Ptr) \ + (UDF_FILE_ENTRY_TYPE)( \ + ((UDF_DESCRIPTOR_TAG *)(_Ptr))->TagIdentifier == UdfFileEntry ? \ + ((UDF_FILE_ENTRY *)(_Ptr))->IcbTag.FileType : \ + ((UDF_EXTENDED_FILE_ENTRY *)(_Ptr))->IcbTag.FileType) + +typedef enum { + UdfFileEntryDirectory = 4, + UdfFileEntryStandardFile = 5, + UdfFileEntrySymlink = 12, +} UDF_FILE_ENTRY_TYPE; + +#define HIDDEN_FILE (1 << 0) +#define DIRECTORY_FILE (1 << 1) +#define DELETED_FILE (1 << 2) +#define PARENT_FILE (1 << 3) + +#define IS_FID_HIDDEN_FILE(_Fid) \ + (BOOLEAN)((_Fid)->FileCharacteristics & HIDDEN_FILE) +#define IS_FID_DIRECTORY_FILE(_Fid) \ + (BOOLEAN)((_Fid)->FileCharacteristics & DIRECTORY_FILE) +#define IS_FID_DELETED_FILE(_Fid) \ + (BOOLEAN)((_Fid)->FileCharacteristics & DELETED_FILE) +#define IS_FID_PARENT_FILE(_Fid) \ + (BOOLEAN)((_Fid)->FileCharacteristics & PARENT_FILE) +#define IS_FID_NORMAL_FILE(_Fid) \ + (BOOLEAN)(!IS_FID_DIRECTORY_FILE (_Fid) && \ + !IS_FID_PARENT_FILE (_Fid)) + +typedef enum { + ShortAdsSequence, + LongAdsSequence, + ExtendedAdsSequence, + InlineData +} UDF_FE_RECORDING_FLAGS; + +#define GET_FE_RECORDING_FLAGS(_Fe) \ + ((UDF_FE_RECORDING_FLAGS)((UDF_ICB_TAG *)( \ + (UINT8 *)(_Fe) + \ + sizeof (UDF_DESCRIPTOR_TAG)))->Flags & 0x07) + +typedef enum { + ExtentRecordedAndAllocated, + ExtentNotRecordedButAllocated, + ExtentNotRecordedNotAllocated, + ExtentIsNextExtent, +} UDF_EXTENT_FLAGS; + +#define AD_LENGTH(_RecFlags) \ + ((_RecFlags) == ShortAdsSequence ? \ + ((UINT64)(sizeof (UDF_SHORT_ALLOCATION_DESCRIPTOR))) : \ + ((UINT64)(sizeof (UDF_LONG_ALLOCATION_DESCRIPTOR)))) + +#define GET_EXTENT_FLAGS(_RecFlags, _Ad) \ + ((_RecFlags) == ShortAdsSequence ? \ + ((UDF_EXTENT_FLAGS)((((UDF_SHORT_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength >> \ + 30) & 0x3)) : \ + ((UDF_EXTENT_FLAGS)((((UDF_LONG_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength >> \ + 30) & 0x3))) + +#define GET_EXTENT_LENGTH(_RecFlags, _Ad) \ + ((_RecFlags) == ShortAdsSequence ? \ + ((UINT32)((((UDF_SHORT_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength & \ + ~0xC0000000UL))) : \ + ((UINT32)((((UDF_LONG_ALLOCATION_DESCRIPTOR *)(_Ad))->ExtentLength & \ + ~0xC0000000UL)))) + +#define UDF_FILENAME_LENGTH 128 +#define UDF_PATH_LENGTH 512 + +#define GET_FID_FROM_ADS(_Data, _Offs) \ + ((UDF_FILE_IDENTIFIER_DESCRIPTOR *)((UINT8 *)(_Data) + (_Offs))) + +#define IS_VALID_COMPRESSION_ID(_CompId) \ + ((BOOLEAN)((_CompId) == 8 || (_CompId) == 16)) + +#define UDF_STANDARD_IDENTIFIER_LENGTH 5 + +#pragma pack(1) + +typedef struct { + UINT8 StandardIdentifier[UDF_STANDARD_IDENTIFIER_LENGTH]; +} UDF_STANDARD_IDENTIFIER; + +#pragma pack() + +typedef enum { + ReadFileGetFileSize, + ReadFileAllocateAndRead, + ReadFileSeekAndRead, +} UDF_READ_FILE_FLAGS; + +typedef struct { + VOID *FileData; + UDF_READ_FILE_FLAGS Flags; + UINT64 FileDataSize; + UINT64 FilePosition; + UINT64 FileSize; + UINT64 ReadLength; +} UDF_READ_FILE_INFO; + +#pragma pack(1) + +typedef struct { + UINT16 TypeAndTimezone; + INT16 Year; + UINT8 Month; + UINT8 Day; + UINT8 Hour; + UINT8 Minute; + UINT8 Second; + UINT8 Centiseconds; + UINT8 HundredsOfMicroseconds; + UINT8 Microseconds; +} UDF_TIMESTAMP; + +typedef struct { + UDF_DESCRIPTOR_TAG DescriptorTag; + UINT32 PrevAllocationExtentDescriptor; + UINT32 LengthOfAllocationDescriptors; +} UDF_ALLOCATION_EXTENT_DESCRIPTOR; + +typedef struct { + UINT8 StructureType; + UINT8 StandardIdentifier[UDF_STANDARD_IDENTIFIER_LENGTH]; + UINT8 StructureVersion; + UINT8 Reserved; + UINT8 StructureData[2040]; +} UDF_VOLUME_DESCRIPTOR; + +typedef struct { + UDF_DESCRIPTOR_TAG DescriptorTag; + UDF_TIMESTAMP RecordingDateTime; + UINT32 IntegrityType; + UDF_EXTENT_AD NextIntegrityExtent; + UINT8 LogicalVolumeContentsUse[32]; + UINT32 NumberOfPartitions; + UINT32 LengthOfImplementationUse; + UINT8 Data[0]; +} UDF_LOGICAL_VOLUME_INTEGRITY; + +typedef struct { + UDF_DESCRIPTOR_TAG DescriptorTag; + UINT32 VolumeDescriptorSequenceNumber; + UINT16 PartitionFlags; + UINT16 PartitionNumber; + UDF_ENTITY_ID PartitionContents; + UINT8 PartitionContentsUse[128]; + UINT32 AccessType; + UINT32 PartitionStartingLocation; + UINT32 PartitionLength; + UDF_ENTITY_ID ImplementationIdentifier; + UINT8 ImplementationUse[128]; + UINT8 Reserved[156]; +} UDF_PARTITION_DESCRIPTOR; + +typedef struct { + UDF_DESCRIPTOR_TAG DescriptorTag; + UDF_TIMESTAMP RecordingDateAndTime; + UINT16 InterchangeLevel; + UINT16 MaximumInterchangeLevel; + UINT32 CharacterSetList; + UINT32 MaximumCharacterSetList; + UINT32 FileSetNumber; + UINT32 FileSetDescriptorNumber; + UDF_CHAR_SPEC LogicalVolumeIdentifierCharacterSet; + UINT8 LogicalVolumeIdentifier[128]; + UDF_CHAR_SPEC FileSetCharacterSet; + UINT8 FileSetIdentifier[32]; + UINT8 CopyrightFileIdentifier[32]; + UINT8 AbstractFileIdentifier[32]; + UDF_LONG_ALLOCATION_DESCRIPTOR RootDirectoryIcb; + UDF_ENTITY_ID DomainIdentifier; + UDF_LONG_ALLOCATION_DESCRIPTOR NextExtent; + UDF_LONG_ALLOCATION_DESCRIPTOR SystemStreamDirectoryIcb; + UINT8 Reserved[32]; +} UDF_FILE_SET_DESCRIPTOR; + +typedef struct { + UINT32 ExtentLength; + UINT32 ExtentPosition; +} UDF_SHORT_ALLOCATION_DESCRIPTOR; + +typedef struct { + UDF_DESCRIPTOR_TAG DescriptorTag; + UINT16 FileVersionNumber; + UINT8 FileCharacteristics; + UINT8 LengthOfFileIdentifier; + UDF_LONG_ALLOCATION_DESCRIPTOR Icb; + UINT16 LengthOfImplementationUse; + UINT8 Data[0]; +} UDF_FILE_IDENTIFIER_DESCRIPTOR; + +typedef struct { + UINT32 PriorRecordNumberOfDirectEntries; + UINT16 StrategyType; + UINT16 StrategyParameter; + UINT16 MaximumNumberOfEntries; + UINT8 Reserved; + UINT8 FileType; + UDF_LB_ADDR ParentIcbLocation; + UINT16 Flags; +} UDF_ICB_TAG; + +typedef struct { + UDF_DESCRIPTOR_TAG DescriptorTag; + UDF_ICB_TAG IcbTag; + UINT32 Uid; + UINT32 Gid; + UINT32 Permissions; + UINT16 FileLinkCount; + UINT8 RecordFormat; + UINT8 RecordDisplayAttributes; + UINT32 RecordLength; + UINT64 InformationLength; + UINT64 LogicalBlocksRecorded; + UDF_TIMESTAMP AccessTime; + UDF_TIMESTAMP ModificationTime; + UDF_TIMESTAMP AttributeTime; + UINT32 CheckPoint; + UDF_LONG_ALLOCATION_DESCRIPTOR ExtendedAttributeIcb; + UDF_ENTITY_ID ImplementationIdentifier; + UINT64 UniqueId; + UINT32 LengthOfExtendedAttributes; + UINT32 LengthOfAllocationDescriptors; + UINT8 Data[0]; // L_EA + L_AD +} UDF_FILE_ENTRY; + +typedef struct { + UDF_DESCRIPTOR_TAG DescriptorTag; + UDF_ICB_TAG IcbTag; + UINT32 Uid; + UINT32 Gid; + UINT32 Permissions; + UINT16 FileLinkCount; + UINT8 RecordFormat; + UINT8 RecordDisplayAttributes; + UINT32 RecordLength; + UINT64 InformationLength; + UINT64 ObjectSize; + UINT64 LogicalBlocksRecorded; + UDF_TIMESTAMP AccessTime; + UDF_TIMESTAMP ModificationTime; + UDF_TIMESTAMP CreationTime; + UDF_TIMESTAMP AttributeTime; + UINT32 CheckPoint; + UINT32 Reserved; + UDF_LONG_ALLOCATION_DESCRIPTOR ExtendedAttributeIcb; + UDF_LONG_ALLOCATION_DESCRIPTOR StreamDirectoryIcb; + UDF_ENTITY_ID ImplementationIdentifier; + UINT64 UniqueId; + UINT32 LengthOfExtendedAttributes; + UINT32 LengthOfAllocationDescriptors; + UINT8 Data[0]; // L_EA + L_AD +} UDF_EXTENDED_FILE_ENTRY; + +typedef struct { + UINT8 ComponentType; + UINT8 LengthOfComponentIdentifier; + UINT16 ComponentFileVersionNumber; + UINT8 ComponentIdentifier[0]; +} UDF_PATH_COMPONENT; + +#pragma pack() + +// +// UDF filesystem driver's private data +// +typedef struct { + UINT64 MainVdsStartLocation; + UDF_LOGICAL_VOLUME_DESCRIPTOR LogicalVolDesc; + UDF_PARTITION_DESCRIPTOR PartitionDesc; + UDF_FILE_SET_DESCRIPTOR FileSetDesc; + UINTN FileEntrySize; +} UDF_VOLUME_INFO; + +typedef struct { + VOID *FileEntry; + UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc; +} UDF_FILE_INFO; + +typedef struct { + VOID *DirectoryData; + UINT64 DirectoryLength; + UINT64 FidOffset; +} UDF_READ_DIRECTORY_INFO; + +#define PRIVATE_UDF_FILE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'f', 'f') + +#define PRIVATE_UDF_FILE_DATA_FROM_THIS(a) \ + CR ( \ + a, \ + PRIVATE_UDF_FILE_DATA, \ + FileIo, \ + PRIVATE_UDF_FILE_DATA_SIGNATURE \ + ) + +typedef struct { + UINTN Signature; + BOOLEAN IsRootDirectory; + UDF_FILE_INFO *Root; + UDF_FILE_INFO File; + UDF_READ_DIRECTORY_INFO ReadDirInfo; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFs; + EFI_FILE_PROTOCOL FileIo; + CHAR16 AbsoluteFileName[UDF_PATH_LENGTH]; + CHAR16 FileName[UDF_FILENAME_LENGTH]; + UINT64 FileSize; + UINT64 FilePosition; +} PRIVATE_UDF_FILE_DATA; + +#define PRIVATE_UDF_SIMPLE_FS_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'f', 's') + +#define PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS(a) \ + CR ( \ + a, \ + PRIVATE_UDF_SIMPLE_FS_DATA, \ + SimpleFs, \ + PRIVATE_UDF_SIMPLE_FS_DATA_SIGNATURE \ + ) + +typedef struct { + UINTN Signature; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs; + UDF_VOLUME_INFO Volume; + UDF_FILE_INFO Root; + UINTN OpenFiles; + EFI_HANDLE Handle; +} PRIVATE_UDF_SIMPLE_FS_DATA; + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gUdfDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gUdfComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gUdfComponentName2; + +// +// Function Prototypes +// + +/** + Open the root directory on a volume. + + @param This Protocol instance pointer. + @param Root Returns an Open file handle for the root directory + + @retval EFI_SUCCESS The device was opened. + @retval EFI_UNSUPPORTED This volume does not support the file system. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. + +**/ +EFI_STATUS +EFIAPI +UdfOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **Root + ); + +/** + Opens a new file relative to the source file's location. + + @param This The protocol instance pointer. + @param NewHandle Returns File Handle for FileName. + @param FileName Null terminated string. "\", ".", and ".." are supported. + @param OpenMode Open mode for file. + @param Attributes Only used for EFI_FILE_MODE_CREATE. + + @retval EFI_SUCCESS The device was opened. + @retval EFI_NOT_FOUND The specified file could not be found on the device. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_MEDIA_CHANGED The media has changed. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +UdfOpen ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ); + +/** + Read data from the file. + + @param This Protocol instance pointer. + @param BufferSize On input size of buffer, on output amount of data in buffer. + @param Buffer The buffer in which data is read. + + @retval EFI_SUCCESS Data was read. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TO_SMALL BufferSize is too small. BufferSize contains required size. + +**/ +EFI_STATUS +EFIAPI +UdfRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Close the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The file was closed. + +**/ +EFI_STATUS +EFIAPI +UdfClose ( + IN EFI_FILE_PROTOCOL *This + ); + +/** + Close and delete the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The file was closed and deleted. + @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not + deleted. + +**/ +EFI_STATUS +EFIAPI +UdfDelete ( + IN EFI_FILE_PROTOCOL *This + ); + +/** + Write data to a file. + + @param This Protocol instance pointer. + @param BufferSize On input size of buffer, on output amount of data in buffer. + @param Buffer The buffer in which data to write. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Writes to Open directory are not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +UdfWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +/** + Get file's current position. + + @param This Protocol instance pointer. + @param Position Byte position from the start of the file. + + @retval EFI_SUCCESS Position was updated. + @retval EFI_UNSUPPORTED Seek request for directories is not valid. + +**/ +EFI_STATUS +EFIAPI +UdfGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ); + +/** + Set file's current position. + + @param This Protocol instance pointer. + @param Position Byte position from the start of the file. + + @retval EFI_SUCCESS Position was updated. + @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open. + +**/ +EFI_STATUS +EFIAPI +UdfSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ); + +/** + Get information about a file. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The File Set Descriptor is external input, so this routine will do basic + validation for File Set Descriptor and report status. + + @param This Protocol instance pointer. + @param InformationType Type of information to return in Buffer. + @param BufferSize On input size of buffer, on output amount of data in + buffer. + @param Buffer The buffer to return data. + + @retval EFI_SUCCESS Data was returned. + @retval EFI_UNSUPPORTED InformationType is not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in + BufferSize. + +**/ +EFI_STATUS +EFIAPI +UdfGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Set information about a file. + + @param This Protocol instance pointer. + @param InformationType Type of information in Buffer. + @param BufferSize Size of buffer. + @param Buffer The data to write. + + @retval EFI_SUCCESS Data was set. + @retval EFI_UNSUPPORTED InformationType is not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + +**/ +EFI_STATUS +EFIAPI +UdfSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush data back for the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS Data was flushed. + @retval EFI_UNSUPPORTED Writes to Open directory are not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +UdfFlush ( + IN EFI_FILE_PROTOCOL *This + ); + +/** + Read volume information on a medium which contains a valid UDF file system. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[out] Volume UDF volume information structure. + + @retval EFI_SUCCESS Volume information read. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The volume was not read due to lack of resources. + +**/ +EFI_STATUS +ReadUdfVolumeInformation ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + OUT UDF_VOLUME_INFO *Volume + ); + +/** + Find the root directory on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[out] File Root directory file. + + @retval EFI_SUCCESS Root directory found. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The root directory was not found due to lack of + resources. + +**/ +EFI_STATUS +FindRootDirectory ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + OUT UDF_FILE_INFO *File + ); + +/** + Find either a File Entry or a Extended File Entry from a given ICB. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] Icb ICB of the FID. + @param[out] FileEntry File Entry or Extended File Entry. + + @retval EFI_SUCCESS File Entry or Extended File Entry found. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The FE/EFE entry was not found due to lack of + resources. + +**/ +EFI_STATUS +FindFileEntry ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb, + OUT VOID **FileEntry + ); + +/** + Find a file given its absolute path on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] FilePath File's absolute path. + @param[in] Root Root directory file. + @param[in] Parent Parent directory file. + @param[in] Icb ICB of Parent. + @param[out] File Found file. + + @retval EFI_SUCCESS FilePath was found. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The FilePath file was not found due to lack of + resources. + +**/ +EFI_STATUS +FindFile ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN CHAR16 *FilePath, + IN UDF_FILE_INFO *Root, + IN UDF_FILE_INFO *Parent, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb, + OUT UDF_FILE_INFO *File + ); + +/** + Read a directory entry at a time on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] ParentIcb ICB of the parent file. + @param[in] FileEntryData FE/EFE of the parent file. + @param[in, out] ReadDirInfo Next read directory listing structure + information. + @param[out] FoundFid File Identifier Descriptor pointer. + + @retval EFI_SUCCESS Directory entry read. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The directory entry was not read due to lack of + resources. + +**/ +EFI_STATUS +ReadDirectoryEntry ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb, + IN VOID *FileEntryData, + IN OUT UDF_READ_DIRECTORY_INFO *ReadDirInfo, + OUT UDF_FILE_IDENTIFIER_DESCRIPTOR **FoundFid + ); + +/** + Get a filename (encoded in OSTA-compressed format) from a File Identifier + Descriptor on an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The File Identifier Descriptor is external input, so this routine will do + basic validation for File Identifier Descriptor and report status. + + @param[in] FileIdentifierDesc File Identifier Descriptor pointer. + @param[in] CharMax The maximum number of FileName Unicode char, + including terminating null char. + @param[out] FileName Decoded filename. + + @retval EFI_SUCCESS Filename decoded and read. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The string buffer FileName cannot hold the + decoded filename. +**/ +EFI_STATUS +GetFileNameFromFid ( + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc, + IN UINTN CharMax, + OUT CHAR16 *FileName + ); + +/** + Resolve a symlink file on an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The Path Component is external input, so this routine will do basic + validation for Path Component and report status. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] Parent Parent file. + @param[in] FileEntryData FE/EFE structure pointer. + @param[out] File Resolved file. + + @retval EFI_SUCCESS Symlink file resolved. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The symlink file was not resolved due to lack of + resources. + +**/ +EFI_STATUS +ResolveSymlink ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_FILE_INFO *Parent, + IN VOID *FileEntryData, + OUT UDF_FILE_INFO *File + ); + +/** + Clean up in-memory UDF file information. + + @param[in] File File information pointer. + +**/ +VOID +CleanupFileInformation ( + IN UDF_FILE_INFO *File + ); + +/** + Find a file from its absolute path on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] File File information structure. + @param[out] Size Size of the file. + + @retval EFI_SUCCESS File size calculated and set in Size. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The file size was not calculated due to lack of + resources. + +**/ +EFI_STATUS +GetFileSize ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_FILE_INFO *File, + OUT UINT64 *Size + ); + +/** + Set information about a file on an UDF volume. + + @param[in] File File pointer. + @param[in] FileSize Size of the file. + @param[in] FileName Filename of the file. + @param[in, out] BufferSize Size of the returned file infomation. + @param[out] Buffer Data of the returned file information. + + @retval EFI_SUCCESS File information set. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The file information was not set due to lack of + resources. + +**/ +EFI_STATUS +SetFileInfo ( + IN UDF_FILE_INFO *File, + IN UINT64 FileSize, + IN CHAR16 *FileName, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Get volume label of an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The File Set Descriptor is external input, so this routine will do basic + validation for File Set Descriptor and report status. + + @param[in] Volume Volume information pointer. + @param[in] CharMax The maximum number of Unicode char in String, + including terminating null char. + @param[out] String String buffer pointer to store the volume label. + + @retval EFI_SUCCESS Volume label is returned. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The string buffer String cannot hold the + volume label. + +**/ +EFI_STATUS +GetVolumeLabel ( + IN UDF_VOLUME_INFO *Volume, + IN UINTN CharMax, + OUT CHAR16 *String + ); + +/** + Get volume and free space size information of an UDF volume. + + @attention This is boundary function that may receive untrusted input. + @attention The input is from FileSystem. + + The Logical Volume Descriptor and the Logical Volume Integrity Descriptor are + external inputs, so this routine will do basic validation for both descriptors + and report status. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[out] VolumeSize Volume size. + @param[out] FreeSpaceSize Free space size. + + @retval EFI_SUCCESS Volume and free space size calculated. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The volume and free space size were not + calculated due to lack of resources. + +**/ +EFI_STATUS +GetVolumeSize ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + OUT UINT64 *VolumeSize, + OUT UINT64 *FreeSpaceSize + ); + +/** + Seek a file and read its data into memory on an UDF volume. + + @param[in] BlockIo BlockIo interface. + @param[in] DiskIo DiskIo interface. + @param[in] Volume UDF volume information structure. + @param[in] File File information structure. + @param[in] FileSize Size of the file. + @param[in, out] FilePosition File position. + @param[in, out] Buffer File data. + @param[in, out] BufferSize Read size. + + @retval EFI_SUCCESS File seeked and read. + @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES The file's recorded data was not read due to lack + of resources. + +**/ +EFI_STATUS +ReadFileData ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UDF_VOLUME_INFO *Volume, + IN UDF_FILE_INFO *File, + IN UINT64 FileSize, + IN OUT UINT64 *FilePosition, + IN OUT VOID *Buffer, + IN OUT UINT64 *BufferSize + ); + +/** + Check if ControllerHandle supports an UDF file system. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + + @retval EFI_SUCCESS UDF file system found. + @retval EFI_UNSUPPORTED UDF file system not found. + +**/ +EFI_STATUS +SupportUdfFileSystem ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle + ); + +/** + Mangle a filename by cutting off trailing whitespaces, "\\", "." and "..". + + @param[in] FileName Filename. + + @retval The mangled Filename. + +**/ +CHAR16 * +MangleFileName ( + IN CHAR16 *FileName + ); + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a BlockIo and DiskIo protocol can be supported. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +UdfDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on ControllerHandle by opening a Block IO and Disk IO + protocol, reading Device Path, and creating a child handle with a + Disk IO and device path protocol. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +UdfDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +UdfDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UdfComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UdfComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif // _UDF_H_ diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf new file mode 100644 index 000000000..16c411b6b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf @@ -0,0 +1,62 @@ +## @file +# UDF/ECMA-167 file system driver. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (C) 2014-2017 Paulo Alcantara +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UdfDxe + FILE_GUID = 905f13b0-8f91-4b0a-bd76-e1e78f9422e4 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeUdf + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gUdfDriverBinding +# COMPONENT_NAME = gUdfComponentName +# COMPONENT_NAME2 = gUdfComponentName2 +# + +[Sources] + ComponentName.c + FileSystemOperations.c + FileName.c + File.c + Udf.c + Udf.h + + +[Packages] + MdePkg/MdePkg.dec + + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + + +[Guids] + gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## Protocol + gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## Protocol + gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## Protocol + + +[Protocols] + gEfiSimpleFileSystemProtocolGuid ## BY_START + gEfiDevicePathProtocolGuid ## BY_START + gEfiBlockIoProtocolGuid ## TO_START + gEfiDiskIoProtocolGuid ## TO_START diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf new file mode 100644 index 000000000..f97ffdbcd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf @@ -0,0 +1,54 @@ +## @file +# English module that provides Unicode Collation supports. +# +# This driver installs Unicode ISO 639-2 Collation and +# RFC 4646 Unicode Collation 2 protocols based on feature flags +# PcdUnicodeCollationSupport & PcdUnicodeCollation2Support respectively. +# It allows code running in the boot services environment to perform lexical +# comparison functions on Unicode strings for English languages. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EnglishDxe + MODULE_UNI_FILE = EnglishDxe.uni + FILE_GUID = CD3BAFB6-50FB-4fe8-8E4E-AB74D2C1A600 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeUnicodeCollationEng + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + UnicodeCollationEng.c + UnicodeCollationEng.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + PcdLib + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdUnicodeCollationSupport ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdUnicodeCollation2Support ## CONSUMES + +[Protocols] + gEfiUnicodeCollationProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdUnicodeCollationSupport ## SOMETIMES_PRODUCES + gEfiUnicodeCollation2ProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdUnicodeCollation2Support ## PRODUCES + +[UserExtensions.TianoCore."ExtraFiles"] + EnglishDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.uni b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.uni new file mode 100644 index 000000000..400d5185b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.uni @@ -0,0 +1,20 @@ +// /** @file +// English module that provides Unicode Collation supports. +// +// This driver installs Unicode ISO 639-2 Collation and +// RFC 4646 Unicode Collation 2 protocols based on feature flags +// PcdUnicodeCollationSupport & PcdUnicodeCollation2Support respectively. +// It allows code running in the boot services environment to perform lexical +// comparison functions on Unicode strings for English languages. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Provides Unicode Collation support" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver installs Unicode ISO 639-2 Collation and RFC 4646 Unicode Collation 2 protocols based on feature flags PcdUnicodeCollationSupport & PcdUnicodeCollation2Support respectively. It allows code running in the boot services environment to perform lexical comparison functions on Unicode strings for English languages." + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxeExtra.uni new file mode 100644 index 000000000..ec4e8f586 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// EnglishDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"English Language Support" + + diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.c b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.c new file mode 100644 index 000000000..b959cb1e9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.c @@ -0,0 +1,467 @@ +/** @file + Driver to implement English version of Unicode Collation Protocol. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "UnicodeCollationEng.h" + +CHAR8 mEngUpperMap[MAP_TABLE_SIZE]; +CHAR8 mEngLowerMap[MAP_TABLE_SIZE]; +CHAR8 mEngInfoMap[MAP_TABLE_SIZE]; + +CHAR8 mOtherChars[] = { + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '\\', + '.', + '_', + '^', + '$', + '~', + '!', + '#', + '%', + '&', + '-', + '{', + '}', + '(', + ')', + '@', + '`', + '\'', + '\0' +}; + +EFI_HANDLE mHandle = NULL; + +// +// EFI Unicode Collation Protocol supporting ISO 639-2 language code +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_COLLATION_PROTOCOL UnicodeEng = { + EngStriColl, + EngMetaiMatch, + EngStrLwr, + EngStrUpr, + EngFatToStr, + EngStrToFat, + "eng" +}; + +// +// EFI Unicode Collation2 Protocol supporting RFC 4646 language code +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_COLLATION_PROTOCOL Unicode2Eng = { + EngStriColl, + EngMetaiMatch, + EngStrLwr, + EngStrUpr, + EngFatToStr, + EngStrToFat, + "en" +}; + +/** + The user Entry Point for English module. + + This function initializes unicode character mapping and then installs Unicode + Collation & Unicode Collation 2 Protocols based on the feature flags. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeUnicodeCollationEng ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Index2; + + // + // Initialize mapping tables for the supported languages + // + for (Index = 0; Index < MAP_TABLE_SIZE; Index++) { + mEngUpperMap[Index] = (CHAR8) Index; + mEngLowerMap[Index] = (CHAR8) Index; + mEngInfoMap[Index] = 0; + + if ((Index >= 'a' && Index <= 'z') || (Index >= 0xe0 && Index <= 0xf6) || (Index >= 0xf8 && Index <= 0xfe)) { + + Index2 = Index - 0x20; + mEngUpperMap[Index] = (CHAR8) Index2; + mEngLowerMap[Index2] = (CHAR8) Index; + + mEngInfoMap[Index] |= CHAR_FAT_VALID; + mEngInfoMap[Index2] |= CHAR_FAT_VALID; + } + } + + for (Index = 0; mOtherChars[Index] != 0; Index++) { + Index2 = mOtherChars[Index]; + mEngInfoMap[Index2] |= CHAR_FAT_VALID; + } + + if (FeaturePcdGet (PcdUnicodeCollation2Support)) { + if (FeaturePcdGet (PcdUnicodeCollationSupport)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEfiUnicodeCollationProtocolGuid, + &UnicodeEng, + &gEfiUnicodeCollation2ProtocolGuid, + &Unicode2Eng, + NULL + ); + ASSERT_EFI_ERROR (Status); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEfiUnicodeCollation2ProtocolGuid, + &Unicode2Eng, + NULL + ); + ASSERT_EFI_ERROR (Status); + } + } else { + if (FeaturePcdGet (PcdUnicodeCollationSupport)) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEfiUnicodeCollationProtocolGuid, + &UnicodeEng, + NULL + ); + ASSERT_EFI_ERROR (Status); + } else { + // + // This module must support to produce at least one of Unicode Collation Protocol + // and Unicode Collation 2 Protocol. + // + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + } + } + + return Status; +} + + +/** + Performs a case-insensitive comparison of two Null-terminated strings. + + @param This Protocol instance pointer. + @param Str1 A pointer to a Null-terminated string. + @param Str2 A pointer to a Null-terminated string. + + @retval 0 Str1 is equivalent to Str2 + @retval > 0 Str1 is lexically greater than Str2 + @retval < 0 Str1 is lexically less than Str2 + +**/ +INTN +EFIAPI +EngStriColl ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ) +{ + while (*Str1 != 0) { + if (TO_UPPER (*Str1) != TO_UPPER (*Str2)) { + break; + } + + Str1 += 1; + Str2 += 1; + } + + return TO_UPPER (*Str1) - TO_UPPER (*Str2); +} + + +/** + Converts all the characters in a Null-terminated string to + lower case characters. + + @param This Protocol instance pointer. + @param Str A pointer to a Null-terminated string. + +**/ +VOID +EFIAPI +EngStrLwr ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN OUT CHAR16 *Str + ) +{ + while (*Str != 0) { + *Str = TO_LOWER (*Str); + Str += 1; + } +} + + +/** + Converts all the characters in a Null-terminated string to upper + case characters. + + @param This Protocol instance pointer. + @param Str A pointer to a Null-terminated string. + +**/ +VOID +EFIAPI +EngStrUpr ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN OUT CHAR16 *Str + ) +{ + while (*Str != 0) { + *Str = TO_UPPER (*Str); + Str += 1; + } +} + +/** + Performs a case-insensitive comparison of a Null-terminated + pattern string and a Null-terminated string. + + @param This Protocol instance pointer. + @param String A pointer to a Null-terminated string. + @param Pattern A pointer to a Null-terminated pattern string. + + @retval TRUE Pattern was found in String. + @retval FALSE Pattern was not found in String. + +**/ +BOOLEAN +EFIAPI +EngMetaiMatch ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN CHAR16 *String, + IN CHAR16 *Pattern + ) +{ + CHAR16 CharC; + CHAR16 CharP; + CHAR16 Index3; + + for (;;) { + CharP = *Pattern; + Pattern += 1; + + switch (CharP) { + case 0: + // + // End of pattern. If end of string, TRUE match + // + if (*String != 0) { + return FALSE; + } else { + return TRUE; + } + + case '*': + // + // Match zero or more chars + // + while (*String != 0) { + if (EngMetaiMatch (This, String, Pattern)) { + return TRUE; + } + + String += 1; + } + + return EngMetaiMatch (This, String, Pattern); + + case '?': + // + // Match any one char + // + if (*String == 0) { + return FALSE; + } + + String += 1; + break; + + case '[': + // + // Match char set + // + CharC = *String; + if (CharC == 0) { + // + // syntax problem + // + return FALSE; + } + + Index3 = 0; + CharP = *Pattern++; + while (CharP != 0) { + if (CharP == ']') { + return FALSE; + } + + if (CharP == '-') { + // + // if range of chars, get high range + // + CharP = *Pattern; + if (CharP == 0 || CharP == ']') { + // + // syntax problem + // + return FALSE; + } + + if (TO_UPPER (CharC) >= TO_UPPER (Index3) && TO_UPPER (CharC) <= TO_UPPER (CharP)) { + // + // if in range, it's a match + // + break; + } + } + + Index3 = CharP; + if (TO_UPPER (CharC) == TO_UPPER (CharP)) { + // + // if char matches + // + break; + } + + CharP = *Pattern++; + } + // + // skip to end of match char set + // + while ((CharP != 0) && (CharP != ']')) { + CharP = *Pattern; + Pattern += 1; + } + + String += 1; + break; + + default: + CharC = *String; + if (TO_UPPER (CharC) != TO_UPPER (CharP)) { + return FALSE; + } + + String += 1; + break; + } + } +} + + +/** + Converts an 8.3 FAT file name in an OEM character set to a Null-terminated string. + + @param This Protocol instance pointer. + @param FatSize The size of the string Fat in bytes. + @param Fat A pointer to a Null-terminated string that contains an 8.3 file + name using an 8-bit OEM character set. + @param String A pointer to a Null-terminated string. The string must + be preallocated to hold FatSize characters. + +**/ +VOID +EFIAPI +EngFatToStr ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN UINTN FatSize, + IN CHAR8 *Fat, + OUT CHAR16 *String + ) +{ + // + // No DBCS issues, just expand and add null terminate to end of string + // + while ((*Fat != 0) && (FatSize != 0)) { + *String = *Fat; + String += 1; + Fat += 1; + FatSize -= 1; + } + + *String = 0; +} + + +/** + Converts a Null-terminated string to legal characters in a FAT + filename using an OEM character set. + + @param This Protocol instance pointer. + @param String A pointer to a Null-terminated string. The string must + be preallocated to hold FatSize characters. + @param FatSize The size of the string Fat in bytes. + @param Fat A pointer to a Null-terminated string that contains an 8.3 file + name using an OEM character set. + + @retval TRUE Fat is a Long File Name + @retval FALSE Fat is an 8.3 file name + +**/ +BOOLEAN +EFIAPI +EngStrToFat ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN CHAR16 *String, + IN UINTN FatSize, + OUT CHAR8 *Fat + ) +{ + BOOLEAN SpecialCharExist; + + SpecialCharExist = FALSE; + while ((*String != 0) && (FatSize != 0)) { + // + // Skip '.' or ' ' when making a fat name + // + if (*String != '.' && *String != ' ') { + // + // If this is a valid fat char, move it. + // Otherwise, move a '_' and flag the fact that the name needs a long file name. + // + if (*String < MAP_TABLE_SIZE && ((mEngInfoMap[*String] & CHAR_FAT_VALID) != 0)) { + *Fat = mEngUpperMap[*String]; + } else { + *Fat = '_'; + SpecialCharExist = TRUE; + } + + Fat += 1; + FatSize -= 1; + } + + String += 1; + } + // + // Do not terminate that fat string + // + return SpecialCharExist; +} diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.h b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.h new file mode 100644 index 000000000..8a38909e6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/UnicodeCollationEng.h @@ -0,0 +1,181 @@ +/** @file + Head file for Unicode Collation Protocol (English) + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _UNICODE_COLLATION_ENG_H_ +#define _UNICODE_COLLATION_ENG_H_ + + + +#include + +#include + +#include +#include +#include +#include + +// +// Bit mask to indicate the validity of character in FAT file name. +// +#define CHAR_FAT_VALID 0x01 + +// +// Maximum FAT table size. +// +#define MAP_TABLE_SIZE 0x100 + +// +// Macro to map character a to upper case. +// +#define TO_UPPER(a) (CHAR16) ((a) <= 0xFF ? mEngUpperMap[a] : (a)) + +// +// Macro to map character a to lower case. +// +#define TO_LOWER(a) (CHAR16) ((a) <= 0xFF ? mEngLowerMap[a] : (a)) + +// +// Prototypes +// +/** + Performs a case-insensitive comparison of two Null-terminated strings. + + @param This Protocol instance pointer. + @param Str1 A pointer to a Null-terminated string. + @param Str2 A pointer to a Null-terminated string. + + @retval 0 Str1 is equivalent to Str2 + @retval > 0 Str1 is lexically greater than Str2 + @retval < 0 Str1 is lexically less than Str2 + +**/ +INTN +EFIAPI +EngStriColl ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ); + +/** + Performs a case-insensitive comparison of a Null-terminated + pattern string and a Null-terminated string. + + @param This Protocol instance pointer. + @param String A pointer to a Null-terminated string. + @param Pattern A pointer to a Null-terminated pattern string. + + @retval TRUE Pattern was found in String. + @retval FALSE Pattern was not found in String. + +**/ +BOOLEAN +EFIAPI +EngMetaiMatch ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN CHAR16 *String, + IN CHAR16 *Pattern + ); + +/** + Converts all the characters in a Null-terminated string to + lower case characters. + + @param This Protocol instance pointer. + @param Str A pointer to a Null-terminated string. + +**/ +VOID +EFIAPI +EngStrLwr ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN OUT CHAR16 *Str + ); + +/** + Converts all the characters in a Null-terminated string to upper + case characters. + + @param This Protocol instance pointer. + @param Str A pointer to a Null-terminated string. + +**/ +VOID +EFIAPI +EngStrUpr ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN OUT CHAR16 *Str + ); + +/** + Converts an 8.3 FAT file name in an OEM character set to a Null-terminated string. + + @param This Protocol instance pointer. + @param FatSize The size of the string Fat in bytes. + @param Fat A pointer to a Null-terminated string that contains an 8.3 file + name using an 8-bit OEM character set. + @param String A pointer to a Null-terminated string. The string must + be preallocated to hold FatSize characters. + +**/ +VOID +EFIAPI +EngFatToStr ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN UINTN FatSize, + IN CHAR8 *Fat, + OUT CHAR16 *String + ); + +/** + Converts a Null-terminated string to legal characters in a FAT + filename using an OEM character set. + + @param This Protocol instance pointer. + @param String A pointer to a Null-terminated string. The string must + be preallocated to hold FatSize characters. + @param FatSize The size of the string Fat in bytes. + @param Fat A pointer to a Null-terminated string that contains an 8.3 file + name using an OEM character set. + + @retval TRUE Fat is a Long File Name + @retval FALSE Fat is an 8.3 file name + +**/ +BOOLEAN +EFIAPI +EngStrToFat ( + IN EFI_UNICODE_COLLATION_PROTOCOL *This, + IN CHAR16 *String, + IN UINTN FatSize, + OUT CHAR8 *Fat + ); + +/** + The user Entry Point for English module. + + This function initializes unicode character mapping and then installs Unicode + Collation & Unicode Collation 2 Protocols based on the feature flags. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeUnicodeCollationEng ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +#endif + diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngine.uni b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngine.uni new file mode 100644 index 000000000..abf00de1d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngine.uni @@ -0,0 +1,16 @@ +// /** @file +// The DXE driver produces FORM DISPLAY ENGIEN protocol. +// +// A generic Timestamp driver producing Timestamp Protocol using UEFI APIs. +// +// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Generic Timestamp driver producing Timestamp Protocol using UEFI APIs." + +#string STR_MODULE_DESCRIPTION #language en-US "A generic Timestamp driver producing Timestamp Protocol using UEFI APIs." + diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf new file mode 100644 index 000000000..07e19140b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf @@ -0,0 +1,62 @@ +## @file +# The DXE driver produces FORM DISPLAY ENGIEN protocol. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DisplayEngine + MODULE_UNI_FILE = DisplayEngine.uni + FILE_GUID = E660EA85-058E-4b55-A54B-F02F83A24707 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeDisplayEngine + UNLOAD_IMAGE = UnloadDisplayEngine +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + FormDisplayStr.uni + FormDisplay.c + FormDisplay.h + ProcessOptions.c + InputHandler.c + Popup.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + DebugLib + BaseMemoryLib + BaseLib + PrintLib + HiiLib + MemoryAllocationLib + CustomizedDisplayLib + +[Protocols] + gEdkiiFormDisplayEngineProtocolGuid ## PRODUCES + gEdkiiFormBrowserEx2ProtocolGuid ## CONSUMES + gEfiHiiPopupProtocolGuid ## PRODUCES + +[Depex] + gEfiHiiDatabaseProtocolGuid AND gEfiHiiConfigRoutingProtocolGuid AND gEdkiiFormBrowserEx2ProtocolGuid + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserGrayOutTextStatement ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowerGrayOutReadOnlyMenu ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + DisplayEngineExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineExtra.uni b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineExtra.uni new file mode 100644 index 000000000..a6cc8306b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// DisplayEngine Localized Strings and Content +// +// Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"DisplayEngine DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c new file mode 100644 index 000000000..3b034a1c8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c @@ -0,0 +1,4259 @@ +/** @file +Entry and initialization module for the browser. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2014, Hewlett-Packard Development Company, L.P.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FormDisplay.h" + +// +// Search table for UiDisplayMenu() +// +SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = { + { + SCAN_UP, + UiUp, + }, + { + SCAN_DOWN, + UiDown, + }, + { + SCAN_PAGE_UP, + UiPageUp, + }, + { + SCAN_PAGE_DOWN, + UiPageDown, + }, + { + SCAN_ESC, + UiReset, + }, + { + SCAN_LEFT, + UiLeft, + }, + { + SCAN_RIGHT, + UiRight, + } +}; + +UINTN mScanCodeNumber = ARRAY_SIZE (gScanCodeToOperation); + +SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = { + { + UiNoOperation, + CfUiNoOperation, + }, + { + UiSelect, + CfUiSelect, + }, + { + UiUp, + CfUiUp, + }, + { + UiDown, + CfUiDown, + }, + { + UiLeft, + CfUiLeft, + }, + { + UiRight, + CfUiRight, + }, + { + UiReset, + CfUiReset, + }, + { + UiPageUp, + CfUiPageUp, + }, + { + UiPageDown, + CfUiPageDown + }, + { + UiHotKey, + CfUiHotKey + } +}; + +EFI_GUID gDisplayEngineGuid = { + 0xE38C1029, 0xE38F, 0x45b9, {0x8F, 0x0D, 0xE2, 0xE6, 0x0B, 0xC9, 0xB2, 0x62} +}; + +BOOLEAN gMisMatch; +EFI_SCREEN_DESCRIPTOR gStatementDimensions; +BOOLEAN mStatementLayoutIsChanged = TRUE; +USER_INPUT *gUserInput; +FORM_DISPLAY_ENGINE_FORM *gFormData; +EFI_HII_HANDLE gHiiHandle; +UINT16 gDirection; +LIST_ENTRY gMenuOption; +DISPLAY_HIGHLIGHT_MENU_INFO gHighligthMenuInfo = {0}; +BOOLEAN mIsFirstForm = TRUE; +FORM_ENTRY_INFO gOldFormEntry = {0}; + +// +// Browser Global Strings +// +CHAR16 *gReconnectConfirmChanges; +CHAR16 *gReconnectFail; +CHAR16 *gReconnectRequired; +CHAR16 *gChangesOpt; +CHAR16 *gFormNotFound; +CHAR16 *gNoSubmitIf; +CHAR16 *gBrowserError; +CHAR16 *gSaveFailed; +CHAR16 *gNoSubmitIfFailed; +CHAR16 *gSaveProcess; +CHAR16 *gSaveNoSubmitProcess; +CHAR16 *gDiscardChange; +CHAR16 *gJumpToFormSet; +CHAR16 *gCheckError; +CHAR16 *gPromptForData; +CHAR16 *gPromptForPassword; +CHAR16 *gPromptForNewPassword; +CHAR16 *gConfirmPassword; +CHAR16 *gConfirmError; +CHAR16 *gPassowordInvalid; +CHAR16 *gPressEnter; +CHAR16 *gEmptyString; +CHAR16 *gMiniString; +CHAR16 *gOptionMismatch; +CHAR16 *gFormSuppress; +CHAR16 *gProtocolNotFound; +CHAR16 *gConfirmDefaultMsg; +CHAR16 *gConfirmSubmitMsg; +CHAR16 *gConfirmDiscardMsg; +CHAR16 *gConfirmResetMsg; +CHAR16 *gConfirmExitMsg; +CHAR16 *gConfirmSubmitMsg2nd; +CHAR16 *gConfirmDefaultMsg2nd; +CHAR16 *gConfirmResetMsg2nd; +CHAR16 *gConfirmExitMsg2nd; +CHAR16 *gConfirmOpt; +CHAR16 *gConfirmOptYes; +CHAR16 *gConfirmOptNo; +CHAR16 *gConfirmOptOk; +CHAR16 *gConfirmOptCancel; +CHAR16 *gYesOption; +CHAR16 *gNoOption; +CHAR16 *gOkOption; +CHAR16 *gCancelOption; +CHAR16 *gErrorPopup; +CHAR16 *gWarningPopup; +CHAR16 *gInfoPopup; +CHAR16 *gConfirmMsgConnect; +CHAR16 *gConfirmMsgEnd; +CHAR16 *gPasswordUnsupported; +CHAR16 gModalSkipColumn; +CHAR16 gPromptBlockWidth; +CHAR16 gOptionBlockWidth; +CHAR16 gHelpBlockWidth; +CHAR16 *mUnknownString; + +FORM_DISPLAY_DRIVER_PRIVATE_DATA mPrivateData = { + FORM_DISPLAY_DRIVER_SIGNATURE, + NULL, + { + FormDisplay, + DriverClearDisplayPage, + ConfirmDataChange + }, + { + EFI_HII_POPUP_PROTOCOL_REVISION, + CreatePopup + } +}; + + +/** + Get the string based on the StringId and HII Package List Handle. + + @param Token The String's ID. + @param HiiHandle The package list in the HII database to search for + the specified string. + + @return The output string. + +**/ +CHAR16 * +GetToken ( + IN EFI_STRING_ID Token, + IN EFI_HII_HANDLE HiiHandle + ) +{ + EFI_STRING String; + + String = HiiGetString (HiiHandle, Token, NULL); + if (String == NULL) { + String = AllocateCopyPool (StrSize (mUnknownString), mUnknownString); + ASSERT (String != NULL); + } + + return (CHAR16 *) String; +} + + +/** + Initialize the HII String Token to the correct values. + +**/ +VOID +InitializeDisplayStrings ( + VOID + ) +{ + gReconnectConfirmChanges = GetToken (STRING_TOKEN (RECONNECT_CONFIRM_CHANGES), gHiiHandle); + mUnknownString = GetToken (STRING_TOKEN (UNKNOWN_STRING), gHiiHandle); + gSaveFailed = GetToken (STRING_TOKEN (SAVE_FAILED), gHiiHandle); + gNoSubmitIfFailed = GetToken (STRING_TOKEN (NO_SUBMIT_IF_CHECK_FAILED), gHiiHandle); + gReconnectFail = GetToken (STRING_TOKEN (RECONNECT_FAILED), gHiiHandle); + gReconnectRequired = GetToken (STRING_TOKEN (RECONNECT_REQUIRED), gHiiHandle); + gChangesOpt = GetToken (STRING_TOKEN (RECONNECT_CHANGES_OPTIONS), gHiiHandle); + gSaveProcess = GetToken (STRING_TOKEN (DISCARD_OR_JUMP), gHiiHandle); + gSaveNoSubmitProcess = GetToken (STRING_TOKEN (DISCARD_OR_CHECK), gHiiHandle); + gDiscardChange = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_DISCARD), gHiiHandle); + gJumpToFormSet = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_JUMP), gHiiHandle); + gCheckError = GetToken (STRING_TOKEN (DISCARD_OR_CHECK_CHECK), gHiiHandle); + gPromptForData = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle); + gPromptForPassword = GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD), gHiiHandle); + gPromptForNewPassword = GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD), gHiiHandle); + gConfirmPassword = GetToken (STRING_TOKEN (CONFIRM_PASSWORD), gHiiHandle); + gConfirmError = GetToken (STRING_TOKEN (CONFIRM_ERROR), gHiiHandle); + gPassowordInvalid = GetToken (STRING_TOKEN (PASSWORD_INVALID), gHiiHandle); + gPressEnter = GetToken (STRING_TOKEN (PRESS_ENTER), gHiiHandle); + gEmptyString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); + gMiniString = GetToken (STRING_TOKEN (MINI_STRING), gHiiHandle); + gOptionMismatch = GetToken (STRING_TOKEN (OPTION_MISMATCH), gHiiHandle); + gFormSuppress = GetToken (STRING_TOKEN (FORM_SUPPRESSED), gHiiHandle); + gProtocolNotFound = GetToken (STRING_TOKEN (PROTOCOL_NOT_FOUND), gHiiHandle); + gFormNotFound = GetToken (STRING_TOKEN (STATUS_BROWSER_FORM_NOT_FOUND), gHiiHandle); + gNoSubmitIf = GetToken (STRING_TOKEN (STATUS_BROWSER_NO_SUBMIT_IF), gHiiHandle); + gBrowserError = GetToken (STRING_TOKEN (STATUS_BROWSER_ERROR), gHiiHandle); + gConfirmDefaultMsg = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE), gHiiHandle); + gConfirmDiscardMsg = GetToken (STRING_TOKEN (CONFIRM_DISCARD_MESSAGE), gHiiHandle); + gConfirmSubmitMsg = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE), gHiiHandle); + gConfirmResetMsg = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE), gHiiHandle); + gConfirmExitMsg = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE), gHiiHandle); + gConfirmDefaultMsg2nd = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE_2ND), gHiiHandle); + gConfirmSubmitMsg2nd = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE_2ND), gHiiHandle); + gConfirmResetMsg2nd = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE_2ND), gHiiHandle); + gConfirmExitMsg2nd = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE_2ND), gHiiHandle); + gConfirmOpt = GetToken (STRING_TOKEN (CONFIRM_OPTION), gHiiHandle); + gConfirmOptYes = GetToken (STRING_TOKEN (CONFIRM_OPTION_YES), gHiiHandle); + gConfirmOptNo = GetToken (STRING_TOKEN (CONFIRM_OPTION_NO), gHiiHandle); + gConfirmOptOk = GetToken (STRING_TOKEN (CONFIRM_OPTION_OK), gHiiHandle); + gConfirmOptCancel = GetToken (STRING_TOKEN (CONFIRM_OPTION_CANCEL), gHiiHandle); + gYesOption = GetToken (STRING_TOKEN (YES_SELECTABLE_OPTION), gHiiHandle); + gNoOption = GetToken (STRING_TOKEN (NO_SELECTABLE_OPTION), gHiiHandle); + gOkOption = GetToken (STRING_TOKEN (OK_SELECTABLE_OPTION), gHiiHandle); + gCancelOption = GetToken (STRING_TOKEN (CANCEL_SELECTABLE_OPTION), gHiiHandle); + gErrorPopup = GetToken (STRING_TOKEN (ERROR_POPUP_STRING), gHiiHandle); + gWarningPopup = GetToken (STRING_TOKEN (WARNING_POPUP_STRING), gHiiHandle); + gInfoPopup = GetToken (STRING_TOKEN (INFO_POPUP_STRING), gHiiHandle); + gConfirmMsgConnect = GetToken (STRING_TOKEN (CONFIRM_OPTION_CONNECT), gHiiHandle); + gConfirmMsgEnd = GetToken (STRING_TOKEN (CONFIRM_OPTION_END), gHiiHandle); + gPasswordUnsupported = GetToken (STRING_TOKEN (PASSWORD_NOT_SUPPORTED ), gHiiHandle); +} + +/** + Free up the resource allocated for all strings required + by Setup Browser. + +**/ +VOID +FreeDisplayStrings ( + VOID + ) +{ + FreePool (mUnknownString); + FreePool (gEmptyString); + FreePool (gSaveFailed); + FreePool (gNoSubmitIfFailed); + FreePool (gReconnectFail); + FreePool (gReconnectRequired); + FreePool (gChangesOpt); + FreePool (gReconnectConfirmChanges); + FreePool (gSaveProcess); + FreePool (gSaveNoSubmitProcess); + FreePool (gDiscardChange); + FreePool (gJumpToFormSet); + FreePool (gCheckError); + FreePool (gPromptForData); + FreePool (gPromptForPassword); + FreePool (gPromptForNewPassword); + FreePool (gConfirmPassword); + FreePool (gConfirmError); + FreePool (gPassowordInvalid); + FreePool (gPressEnter); + FreePool (gMiniString); + FreePool (gOptionMismatch); + FreePool (gFormSuppress); + FreePool (gProtocolNotFound); + FreePool (gBrowserError); + FreePool (gNoSubmitIf); + FreePool (gFormNotFound); + FreePool (gConfirmDefaultMsg); + FreePool (gConfirmSubmitMsg); + FreePool (gConfirmDiscardMsg); + FreePool (gConfirmResetMsg); + FreePool (gConfirmExitMsg); + FreePool (gConfirmDefaultMsg2nd); + FreePool (gConfirmSubmitMsg2nd); + FreePool (gConfirmResetMsg2nd); + FreePool (gConfirmExitMsg2nd); + FreePool (gConfirmOpt); + FreePool (gConfirmOptYes); + FreePool (gConfirmOptNo); + FreePool (gConfirmOptOk); + FreePool (gConfirmOptCancel); + FreePool (gYesOption); + FreePool (gNoOption); + FreePool (gOkOption); + FreePool (gCancelOption); + FreePool (gErrorPopup); + FreePool (gWarningPopup); + FreePool (gInfoPopup); + FreePool (gConfirmMsgConnect); + FreePool (gConfirmMsgEnd); + FreePool (gPasswordUnsupported); +} + +/** + Get prompt string id from the opcode data buffer. + + @param OpCode The input opcode buffer. + + @return The prompt string id. + +**/ +EFI_STRING_ID +GetPrompt ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + EFI_IFR_STATEMENT_HEADER *Header; + + if (OpCode->Length <= sizeof (EFI_IFR_OP_HEADER)) { + return 0; + } + + Header = (EFI_IFR_STATEMENT_HEADER *) (OpCode + 1); + + return Header->Prompt; +} + +/** + Get the supported width for a particular op-code + + @param MenuOption The menu option. + @param AdjustWidth The width which is saved for the space. + + @return Returns the number of CHAR16 characters that is support. + +**/ +UINT16 +GetWidth ( + IN UI_MENU_OPTION *MenuOption, + OUT UINT16 *AdjustWidth + ) +{ + CHAR16 *String; + UINTN Size; + EFI_IFR_TEXT *TestOp; + UINT16 ReturnWidth; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + + Statement = MenuOption->ThisTag; + + // + // For modal form, clean the entire row. + // + if ((gFormData->Attribute & HII_DISPLAY_MODAL) != 0) { + if (AdjustWidth != NULL) { + *AdjustWidth = LEFT_SKIPPED_COLUMNS; + } + return (UINT16)(gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gModalSkipColumn + LEFT_SKIPPED_COLUMNS)); + } + + Size = 0; + + // + // See if the second text parameter is really NULL + // + if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) { + TestOp = (EFI_IFR_TEXT *) Statement->OpCode; + if (TestOp->TextTwo != 0) { + String = GetToken (TestOp->TextTwo, gFormData->HiiHandle); + Size = StrLen (String); + FreePool (String); + } + } + + if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) || + (Statement->OpCode->OpCode == EFI_IFR_REF_OP) || + (Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) || + (Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) || + (Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) || + // + // Allow a wide display if text op-code and no secondary text op-code + // + ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0)) + ) { + + // + // Return the space width. + // + if (AdjustWidth != NULL) { + *AdjustWidth = 2; + } + // + // Keep consistent with current behavior. + // + ReturnWidth = (UINT16) (gPromptBlockWidth + gOptionBlockWidth - 2); + } else { + if (AdjustWidth != NULL) { + *AdjustWidth = 1; + } + + ReturnWidth = (UINT16) (gPromptBlockWidth - 1); + } + + // + // For nest in statement, should the subtitle indent. + // + if (MenuOption->NestInStatement) { + ReturnWidth -= SUBTITLE_INDENT; + } + + return ReturnWidth; +} + +/** + Will copy LineWidth amount of a string in the OutputString buffer and return the + number of CHAR16 characters that were copied into the OutputString buffer. + The output string format is: + Glyph Info + String info + '\0'. + + In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g. + + @param InputString String description for this option. + @param LineWidth Width of the desired string to extract in CHAR16 + characters + @param GlyphWidth The glyph width of the begin of the char in the string. + @param Index Where in InputString to start the copy process + @param OutputString Buffer to copy the string into + + @return Returns the number of CHAR16 characters that were copied into the OutputString + buffer, include extra glyph info and '\0' info. + +**/ +UINT16 +GetLineByWidth ( + IN CHAR16 *InputString, + IN UINT16 LineWidth, + IN OUT UINT16 *GlyphWidth, + IN OUT UINTN *Index, + OUT CHAR16 **OutputString + ) +{ + UINT16 StrOffset; + UINT16 GlyphOffset; + UINT16 OriginalGlyphWidth; + BOOLEAN ReturnFlag; + UINT16 LastSpaceOffset; + UINT16 LastGlyphWidth; + + if (InputString == NULL || Index == NULL || OutputString == NULL) { + return 0; + } + + if (LineWidth == 0 || *GlyphWidth == 0) { + return 0; + } + + // + // Save original glyph width. + // + OriginalGlyphWidth = *GlyphWidth; + LastGlyphWidth = OriginalGlyphWidth; + ReturnFlag = FALSE; + LastSpaceOffset = 0; + + // + // NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen. + // To avoid displaying this empty line in screen, just skip the two CHARs here. + // + if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) { + *Index = *Index + 2; + } + + // + // Fast-forward the string and see if there is a carriage-return in the string + // + for (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) { + switch (InputString[*Index + StrOffset]) { + case NARROW_CHAR: + *GlyphWidth = 1; + break; + + case WIDE_CHAR: + *GlyphWidth = 2; + break; + + case CHAR_CARRIAGE_RETURN: + case CHAR_LINEFEED: + case CHAR_NULL: + ReturnFlag = TRUE; + break; + + default: + GlyphOffset = GlyphOffset + *GlyphWidth; + + // + // Record the last space info in this line. Will be used in rewind. + // + if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) { + LastSpaceOffset = StrOffset; + LastGlyphWidth = *GlyphWidth; + } + break; + } + + if (ReturnFlag) { + break; + } + } + + // + // Rewind the string from the maximum size until we see a space to break the line + // + if (GlyphOffset > LineWidth) { + // + // Rewind the string to last space char in this line. + // + if (LastSpaceOffset != 0) { + StrOffset = LastSpaceOffset; + *GlyphWidth = LastGlyphWidth; + } else { + // + // Roll back to last char in the line width. + // + StrOffset--; + } + } + + // + // The CHAR_NULL has process last time, this time just return 0 to stand for the end. + // + if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) { + return 0; + } + + // + // Need extra glyph info and '\0' info, so +2. + // + *OutputString = AllocateZeroPool ((StrOffset + 2) * sizeof(CHAR16)); + if (*OutputString == NULL) { + return 0; + } + + // + // Save the glyph info at the begin of the string, will used by Print function. + // + if (OriginalGlyphWidth == 1) { + *(*OutputString) = NARROW_CHAR; + } else { + *(*OutputString) = WIDE_CHAR; + } + + CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16)); + + if (InputString[*Index + StrOffset] == CHAR_SPACE) { + // + // Skip the space info at the begin of next line. + // + *Index = (UINT16) (*Index + StrOffset + 1); + } else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) { + // + // Skip the /n or /n/r info. + // + if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) { + *Index = (UINT16) (*Index + StrOffset + 2); + } else { + *Index = (UINT16) (*Index + StrOffset + 1); + } + } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) { + // + // Skip the /r or /r/n info. + // + if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) { + *Index = (UINT16) (*Index + StrOffset + 2); + } else { + *Index = (UINT16) (*Index + StrOffset + 1); + } + } else { + *Index = (UINT16) (*Index + StrOffset); + } + + // + // Include extra glyph info and '\0' info, so +2. + // + return StrOffset + 2; +} + +/** + Add one menu option by specified description and context. + + @param Statement Statement of this Menu Option. + @param MenuItemCount The index for this Option in the Menu. + @param NestIn Whether this statement is nest in another statement. + +**/ +VOID +UiAddMenuOption ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN UINT16 *MenuItemCount, + IN BOOLEAN NestIn + ) +{ + UI_MENU_OPTION *MenuOption; + UINTN Index; + UINTN Count; + UINT16 NumberOfLines; + UINT16 GlyphWidth; + UINT16 Width; + UINTN ArrayEntry; + CHAR16 *OutputString; + EFI_STRING_ID PromptId; + + NumberOfLines = 1; + ArrayEntry = 0; + GlyphWidth = 1; + Count = 1; + MenuOption = NULL; + + PromptId = GetPrompt (Statement->OpCode); + ASSERT (PromptId != 0); + + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + Count = 3; + } + + for (Index = 0; Index < Count; Index++) { + MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION)); + ASSERT (MenuOption); + + MenuOption->Signature = UI_MENU_OPTION_SIGNATURE; + MenuOption->Description = GetToken (PromptId, gFormData->HiiHandle); + MenuOption->Handle = gFormData->HiiHandle; + MenuOption->ThisTag = Statement; + MenuOption->NestInStatement = NestIn; + MenuOption->EntryNumber = *MenuItemCount; + + MenuOption->Sequence = Index; + + if ((Statement->Attribute & HII_DISPLAY_GRAYOUT) != 0) { + MenuOption->GrayOut = TRUE; + } else { + MenuOption->GrayOut = FALSE; + } + + if ((Statement->Attribute & HII_DISPLAY_LOCK) != 0 || (gFormData->Attribute & HII_DISPLAY_LOCK) != 0) { + MenuOption->GrayOut = TRUE; + } + + // + // If the form or the question has the lock attribute, deal same as grayout. + // + if ((gFormData->Attribute & HII_DISPLAY_LOCK) != 0 || (Statement->Attribute & HII_DISPLAY_LOCK) != 0) { + MenuOption->GrayOut = TRUE; + } + + switch (Statement->OpCode->OpCode) { + case EFI_IFR_ORDERED_LIST_OP: + case EFI_IFR_ONE_OF_OP: + case EFI_IFR_NUMERIC_OP: + case EFI_IFR_TIME_OP: + case EFI_IFR_DATE_OP: + case EFI_IFR_CHECKBOX_OP: + case EFI_IFR_PASSWORD_OP: + case EFI_IFR_STRING_OP: + // + // User could change the value of these items + // + MenuOption->IsQuestion = TRUE; + break; + case EFI_IFR_TEXT_OP: + if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) { + // + // Initializing GrayOut option as TRUE for Text setup options + // so that those options will be Gray in colour and un selectable. + // + MenuOption->GrayOut = TRUE; + } + break; + default: + MenuOption->IsQuestion = FALSE; + break; + } + + if ((Statement->Attribute & HII_DISPLAY_READONLY) != 0) { + MenuOption->ReadOnly = TRUE; + if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu)) { + MenuOption->GrayOut = TRUE; + } + } + + if (Index == 0 && + (Statement->OpCode->OpCode != EFI_IFR_DATE_OP) && + (Statement->OpCode->OpCode != EFI_IFR_TIME_OP)) { + Width = GetWidth (MenuOption, NULL); + for (; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth,&ArrayEntry, &OutputString) != 0x0000;) { + // + // If there is more string to process print on the next row and increment the Skip value + // + if (StrLen (&MenuOption->Description[ArrayEntry]) != 0) { + NumberOfLines++; + } + FreePool (OutputString); + } + } else { + // + // Add three MenuOptions for Date/Time + // Data format : [01/02/2004] [11:22:33] + // Line number : 0 0 1 0 0 1 + // + NumberOfLines = 0; + } + + if (Index == 2) { + // + // Override LineNumber for the MenuOption in Date/Time sequence + // + MenuOption->Skip = 1; + } else { + MenuOption->Skip = NumberOfLines; + } + + InsertTailList (&gMenuOption, &MenuOption->Link); + } + + (*MenuItemCount)++; +} + +/** + Create the menu list base on the form data info. + +**/ +VOID +ConvertStatementToMenu ( + VOID + ) +{ + UINT16 MenuItemCount; + LIST_ENTRY *Link; + LIST_ENTRY *NestLink; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + FORM_DISPLAY_ENGINE_STATEMENT *NestStatement; + + MenuItemCount = 0; + InitializeListHead (&gMenuOption); + + Link = GetFirstNode (&gFormData->StatementListHead); + while (!IsNull (&gFormData->StatementListHead, Link)) { + Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&gFormData->StatementListHead, Link); + + // + // Skip the opcode not recognized by Display core. + // + if (Statement->OpCode->OpCode == EFI_IFR_GUID_OP) { + continue; + } + + UiAddMenuOption (Statement, &MenuItemCount, FALSE); + + // + // Check the statement nest in this host statement. + // + NestLink = GetFirstNode (&Statement->NestStatementList); + while (!IsNull (&Statement->NestStatementList, NestLink)) { + NestStatement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (NestLink); + NestLink = GetNextNode (&Statement->NestStatementList, NestLink); + + // + // Skip the opcode not recognized by Display core. + // + if (NestStatement->OpCode->OpCode == EFI_IFR_GUID_OP) { + continue; + } + + UiAddMenuOption (NestStatement, &MenuItemCount, TRUE); + } + } +} + +/** + Count the storage space of a Unicode string. + + This function handles the Unicode string with NARROW_CHAR + and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR + does not count in the resultant output. If a WIDE_CHAR is + hit, then 2 Unicode character will consume an output storage + space with size of CHAR16 till a NARROW_CHAR is hit. + + If String is NULL, then ASSERT (). + + @param String The input string to be counted. + + @return Storage space for the input string. + +**/ +UINTN +GetStringWidth ( + IN CHAR16 *String + ) +{ + UINTN Index; + UINTN Count; + UINTN IncrementValue; + + ASSERT (String != NULL); + if (String == NULL) { + return 0; + } + + Index = 0; + Count = 0; + IncrementValue = 1; + + do { + // + // Advance to the null-terminator or to the first width directive + // + for (; + (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0); + Index++, Count = Count + IncrementValue + ) + ; + + // + // We hit the null-terminator, we now have a count + // + if (String[Index] == 0) { + break; + } + // + // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed + // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2) + // + if (String[Index] == NARROW_CHAR) { + // + // Skip to the next character + // + Index++; + IncrementValue = 1; + } else { + // + // Skip to the next character + // + Index++; + IncrementValue = 2; + } + } while (String[Index] != 0); + + // + // Increment by one to include the null-terminator in the size + // + Count++; + + return Count * sizeof (CHAR16); +} + +/** + Base on the input option string to update the skip value for a menu option. + + @param MenuOption The MenuOption to be checked. + @param OptionString The input option string. + +**/ +VOID +UpdateSkipInfoForMenu ( + IN UI_MENU_OPTION *MenuOption, + IN CHAR16 *OptionString + ) +{ + UINTN Index; + UINT16 Width; + UINTN Row; + CHAR16 *OutputString; + UINT16 GlyphWidth; + + Width = (UINT16) gOptionBlockWidth - 1; + GlyphWidth = 1; + Row = 1; + + for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if (StrLen (&OptionString[Index]) != 0) { + Row++; + } + + FreePool (OutputString); + } + + if ((Row > MenuOption->Skip) && + (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_DATE_OP) && + (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_TIME_OP)) { + MenuOption->Skip = Row; + } +} + +/** + Update display lines for a Menu Option. + + @param MenuOption The MenuOption to be checked. + +**/ +VOID +UpdateOptionSkipLines ( + IN UI_MENU_OPTION *MenuOption + ) +{ + CHAR16 *OptionString; + + OptionString = NULL; + + ProcessOptions (MenuOption, FALSE, &OptionString, TRUE); + if (OptionString != NULL) { + UpdateSkipInfoForMenu (MenuOption, OptionString); + + FreePool (OptionString); + } + + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) { + OptionString = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle); + + if (OptionString != NULL) { + UpdateSkipInfoForMenu (MenuOption, OptionString); + + FreePool (OptionString); + } + } +} + +/** + Check whether this Menu Option could be print. + + Check Prompt string, option string or text two string not NULL. + + This is an internal function. + + @param MenuOption The MenuOption to be checked. + + @retval TRUE This Menu Option is printable. + @retval FALSE This Menu Option could not be printable. + +**/ +BOOLEAN +PrintableMenu ( + UI_MENU_OPTION *MenuOption + ) +{ + EFI_STATUS Status; + EFI_STRING OptionString; + + OptionString = NULL; + + if (MenuOption->Description[0] != '\0') { + return TRUE; + } + + Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE); + if (EFI_ERROR (Status)) { + return FALSE; + } + if (OptionString != NULL && OptionString[0] != '\0') { + FreePool (OptionString); + return TRUE; + } + + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) { + OptionString = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle); + ASSERT (OptionString != NULL); + if (OptionString[0] != '\0'){ + FreePool (OptionString); + return TRUE; + } + } + + return FALSE; +} + +/** + Check whether this Menu Option could be highlighted. + + This is an internal function. + + @param MenuOption The MenuOption to be checked. + + @retval TRUE This Menu Option is selectable. + @retval FALSE This Menu Option could not be selected. + +**/ +BOOLEAN +IsSelectable ( + UI_MENU_OPTION *MenuOption + ) +{ + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) || + MenuOption->GrayOut || MenuOption->ReadOnly || !PrintableMenu (MenuOption)) { + return FALSE; + } else { + return TRUE; + } +} + +/** + Move to next selectable statement. + + This is an internal function. + + @param GoUp The navigation direction. TRUE: up, FALSE: down. + @param CurrentPosition Current position. + @param GapToTop Gap position to top or bottom. + @param FindInForm Whether find menu in current form or beyond. + + @return The row distance from current MenuOption to next selectable MenuOption. + + @retval -1 Reach the begin of the menu, still can't find the selectable menu. + @retval Value Find the selectable menu, maybe the truly selectable, maybe the + first menu showing beyond current form or last menu showing in + current form. + The value is the line number between the new selected menu and the + current select menu, not include the new selected menu. + +**/ +INTN +MoveToNextStatement ( + IN BOOLEAN GoUp, + IN OUT LIST_ENTRY **CurrentPosition, + IN UINTN GapToTop, + IN BOOLEAN FindInForm + ) +{ + INTN Distance; + LIST_ENTRY *Pos; + UI_MENU_OPTION *NextMenuOption; + UI_MENU_OPTION *PreMenuOption; + + Distance = 0; + Pos = *CurrentPosition; + + if (Pos == &gMenuOption) { + return -1; + } + + PreMenuOption = MENU_OPTION_FROM_LINK (Pos); + + while (TRUE) { + NextMenuOption = MENU_OPTION_FROM_LINK (Pos); + // + // NextMenuOption->Row == 0 means this menu has not calculate + // the NextMenuOption->Skip value yet, just calculate here. + // + if (NextMenuOption->Row == 0) { + UpdateOptionSkipLines (NextMenuOption); + } + + // + // Check whether the menu is beyond current showing form, + // return the first one beyond the showing form. + // + if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) { + if (FindInForm) { + NextMenuOption = PreMenuOption; + } + break; + } + + // + // return the selectable menu in the showing form. + // + if (IsSelectable (NextMenuOption)) { + break; + } + + Distance += NextMenuOption->Skip; + + // + // Arrive at begin of the menu list. + // + if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) { + Distance = -1; + break; + } + + Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink); + PreMenuOption = NextMenuOption; + } + + *CurrentPosition = &NextMenuOption->Link; + return Distance; +} + + +/** + Process option string for date/time opcode. + + @param MenuOption Menu option point to date/time. + @param OptionString Option string input for process. + @param AddOptCol Whether need to update MenuOption->OptCol. + +**/ +VOID +ProcessStringForDateTime ( + UI_MENU_OPTION *MenuOption, + CHAR16 *OptionString, + BOOLEAN AddOptCol + ) +{ + UINTN Index; + UINTN Count; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + EFI_IFR_DATE *Date; + EFI_IFR_TIME *Time; + + ASSERT (MenuOption != NULL && OptionString != NULL); + + Statement = MenuOption->ThisTag; + Date = NULL; + Time = NULL; + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) { + Date = (EFI_IFR_DATE *) Statement->OpCode; + } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + Time = (EFI_IFR_TIME *) Statement->OpCode; + } + + // + // If leading spaces on OptionString - remove the spaces + // + for (Index = 0; OptionString[Index] == L' '; Index++) { + // + // Base on the blockspace to get the option column info. + // + if (AddOptCol) { + MenuOption->OptCol++; + } + } + + for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { + OptionString[Count] = OptionString[Index]; + Count++; + } + OptionString[Count] = CHAR_NULL; + + // + // Enable to suppress field in the opcode base on the flag. + // + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) { + // + // OptionString format is: <**: **: ****> + // |month|day|year| + // 4 3 5 + // + if ((Date->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) { + // + // At this point, only "<**:" in the optionstring. + // Clean the day's ** field, after clean, the format is "< :" + // + SetUnicodeMem (&OptionString[1], 2, L' '); + } else if ((Date->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) { + // + // At this point, only "**:" in the optionstring. + // Clean the month's "**" field, after clean, the format is " :" + // + SetUnicodeMem (&OptionString[0], 2, L' '); + } else if ((Date->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) { + // + // At this point, only "****>" in the optionstring. + // Clean the year's "****" field, after clean, the format is " >" + // + SetUnicodeMem (&OptionString[0], 4, L' '); + } + } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + // + // OptionString format is: <**: **: **> + // |hour|minute|second| + // 4 3 3 + // + if ((Time->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) { + // + // At this point, only "<**:" in the optionstring. + // Clean the hour's ** field, after clean, the format is "< :" + // + SetUnicodeMem (&OptionString[1], 2, L' '); + } else if ((Time->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) { + // + // At this point, only "**:" in the optionstring. + // Clean the minute's "**" field, after clean, the format is " :" + // + SetUnicodeMem (&OptionString[0], 2, L' '); + } else if ((Time->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) { + // + // At this point, only "**>" in the optionstring. + // Clean the second's "**" field, after clean, the format is " >" + // + SetUnicodeMem (&OptionString[0], 2, L' '); + } + } +} + + +/** + Adjust Data and Time position accordingly. + Data format : [01/02/2004] [11:22:33] + Line number : 0 0 1 0 0 1 + + This is an internal function. + + @param DirectionUp the up or down direction. False is down. True is + up. + @param CurrentPosition Current position. On return: Point to the last + Option (Year or Second) if up; Point to the first + Option (Month or Hour) if down. + + @return Return line number to pad. It is possible that we stand on a zero-advance + @return data or time opcode, so pad one line when we judge if we are going to scroll outside. + +**/ +UINTN +AdjustDateAndTimePosition ( + IN BOOLEAN DirectionUp, + IN OUT LIST_ENTRY **CurrentPosition + ) +{ + UINTN Count; + LIST_ENTRY *NewPosition; + UI_MENU_OPTION *MenuOption; + UINTN PadLineNumber; + + PadLineNumber = 0; + NewPosition = *CurrentPosition; + MenuOption = MENU_OPTION_FROM_LINK (NewPosition); + + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || + (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) { + // + // Calculate the distance from current position to the last Date/Time MenuOption + // + Count = 0; + while (MenuOption->Skip == 0) { + Count++; + NewPosition = NewPosition->ForwardLink; + MenuOption = MENU_OPTION_FROM_LINK (NewPosition); + PadLineNumber = 1; + } + + NewPosition = *CurrentPosition; + if (DirectionUp) { + // + // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended + // to be one that back to the previous set of MenuOptions, we need to advance to the first + // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate + // checking can be done. + // + while (Count++ < 2) { + NewPosition = NewPosition->BackLink; + } + } else { + // + // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended + // to be one that progresses to the next set of MenuOptions, we need to advance to the last + // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate + // checking can be done. + // + while (Count-- > 0) { + NewPosition = NewPosition->ForwardLink; + } + } + + *CurrentPosition = NewPosition; + } + + return PadLineNumber; +} + +/** + Get step info from numeric opcode. + + @param[in] OpCode The input numeric op code. + + @return step info for this opcode. +**/ +UINT64 +GetFieldFromNum ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + EFI_IFR_NUMERIC *NumericOp; + UINT64 Step; + + NumericOp = (EFI_IFR_NUMERIC *) OpCode; + + switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + Step = NumericOp->data.u8.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_2: + Step = NumericOp->data.u16.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_4: + Step = NumericOp->data.u32.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_8: + Step = NumericOp->data.u64.Step; + break; + + default: + Step = 0; + break; + } + + return Step; +} + +/** + Find the registered HotKey based on KeyData. + + @param[in] KeyData A pointer to a buffer that describes the keystroke + information for the hot key. + + @return The registered HotKey context. If no found, NULL will return. +**/ +BROWSER_HOT_KEY * +GetHotKeyFromRegisterList ( + IN EFI_INPUT_KEY *KeyData + ) +{ + LIST_ENTRY *Link; + BROWSER_HOT_KEY *HotKey; + + Link = GetFirstNode (&gFormData->HotKeyListHead); + while (!IsNull (&gFormData->HotKeyListHead, Link)) { + HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); + + if (HotKey->KeyData->ScanCode == KeyData->ScanCode) { + return HotKey; + } + + Link = GetNextNode (&gFormData->HotKeyListHead, Link); + } + + return NULL; +} + + +/** + Determine if the menu is the last menu that can be selected. + + This is an internal function. + + @param Direction The scroll direction. False is down. True is up. + @param CurrentPos The current focus. + + @return FALSE -- the menu isn't the last menu that can be selected. + @return TRUE -- the menu is the last menu that can be selected. + +**/ +BOOLEAN +ValueIsScroll ( + IN BOOLEAN Direction, + IN LIST_ENTRY *CurrentPos + ) +{ + LIST_ENTRY *Temp; + + Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink; + + if (Temp == &gMenuOption) { + return TRUE; + } + + return FALSE; +} + +/** + Wait for a given event to fire, or for an optional timeout to expire. + + @param Event The event to wait for + + @retval UI_EVENT_TYPE The type of the event which is trigged. + +**/ +UI_EVENT_TYPE +UiWaitForEvent ( + IN EFI_EVENT Event + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN EventNum; + UINT64 Timeout; + EFI_EVENT TimerEvent; + EFI_EVENT WaitList[3]; + UI_EVENT_TYPE EventType; + + TimerEvent = NULL; + Timeout = FormExitTimeout(gFormData); + + if (Timeout != 0) { + Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent); + + // + // Set the timer event + // + gBS->SetTimer ( + TimerEvent, + TimerRelative, + Timeout + ); + } + + WaitList[0] = Event; + EventNum = 1; + if (gFormData->FormRefreshEvent != NULL) { + WaitList[EventNum] = gFormData->FormRefreshEvent; + EventNum ++; + } + + if (Timeout != 0) { + WaitList[EventNum] = TimerEvent; + EventNum ++; + } + + Status = gBS->WaitForEvent (EventNum, WaitList, &Index); + ASSERT_EFI_ERROR (Status); + + switch (Index) { + case 0: + EventType = UIEventKey; + break; + + case 1: + if (gFormData->FormRefreshEvent != NULL) { + EventType = UIEventDriver; + } else { + ASSERT (Timeout != 0 && EventNum == 2); + EventType = UIEventTimeOut; + } + break; + + default: + ASSERT (Index == 2 && EventNum == 3); + EventType = UIEventTimeOut; + break; + } + + if (Timeout != 0) { + gBS->CloseEvent (TimerEvent); + } + + return EventType; +} + +/** + Get question id info from the input opcode header. + + @param OpCode The input opcode header pointer. + + @retval The question id for this opcode. + +**/ +EFI_QUESTION_ID +GetQuestionIdInfo ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + EFI_IFR_QUESTION_HEADER *QuestionHeader; + + if (OpCode->Length < sizeof (EFI_IFR_OP_HEADER) + sizeof (EFI_IFR_QUESTION_HEADER)) { + return 0; + } + + QuestionHeader = (EFI_IFR_QUESTION_HEADER *)((UINT8 *) OpCode + sizeof(EFI_IFR_OP_HEADER)); + + return QuestionHeader->QuestionId; +} + + +/** + Find the top of screen menu base on the current menu. + + @param CurPos Current input menu. + @param Rows Totol screen rows. + @param SkipValue SkipValue for this new form. + + @retval TopOfScreen Top of screen menu for the new form. + +**/ +LIST_ENTRY * +FindTopOfScreenMenu ( + IN LIST_ENTRY *CurPos, + IN UINTN Rows, + OUT UINTN *SkipValue + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *TopOfScreen; + UI_MENU_OPTION *PreviousMenuOption; + + Link = CurPos; + PreviousMenuOption = NULL; + + while (Link->BackLink != &gMenuOption) { + Link = Link->BackLink; + PreviousMenuOption = MENU_OPTION_FROM_LINK (Link); + if (PreviousMenuOption->Row == 0) { + UpdateOptionSkipLines (PreviousMenuOption); + } + if (Rows <= PreviousMenuOption->Skip) { + break; + } + Rows = Rows - PreviousMenuOption->Skip; + } + + if (Link->BackLink == &gMenuOption) { + TopOfScreen = gMenuOption.ForwardLink; + if (PreviousMenuOption != NULL && Rows < PreviousMenuOption->Skip) { + *SkipValue = PreviousMenuOption->Skip - Rows; + } else { + *SkipValue = 0; + } + } else { + TopOfScreen = Link; + *SkipValue = PreviousMenuOption->Skip - Rows; + } + + return TopOfScreen; +} + +/** + Get the index info for this opcode. + + @param OpCode The input opcode for the statement. + + @retval The index of this statement. + +**/ +UINTN +GetIndexInfoForOpcode ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + LIST_ENTRY *NewPos; + UI_MENU_OPTION *MenuOption; + UINTN Index; + + NewPos = gMenuOption.ForwardLink; + Index = 0; + + for (NewPos = gMenuOption.ForwardLink; NewPos != &gMenuOption; NewPos = NewPos->ForwardLink){ + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + + if (CompareMem (MenuOption->ThisTag->OpCode, OpCode, OpCode->Length) == 0) { + if (MenuOption->ThisTag->OpCode == OpCode) { + return Index; + } + + Index ++; + } + } + + return Index; +} + +/** + Is this the saved highlight statement. + + @param HighLightedStatement The input highlight statement. + + @retval TRUE This is the highlight statement. + @retval FALSE This is not the highlight statement. + +**/ +BOOLEAN +IsSavedHighlightStatement ( + IN FORM_DISPLAY_ENGINE_STATEMENT *HighLightedStatement + ) +{ + if ((gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle) && + (gFormData->FormId == gHighligthMenuInfo.FormId)) { + if (gHighligthMenuInfo.HLTQuestionId != 0) { + return (BOOLEAN) (gHighligthMenuInfo.HLTQuestionId == GetQuestionIdInfo (HighLightedStatement->OpCode)); + } else { + if (CompareMem (gHighligthMenuInfo.HLTOpCode, HighLightedStatement->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) { + if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(HighLightedStatement->OpCode)) { + return TRUE; + } else { + return FALSE; + } + } + } + } + + return FALSE; +} + +/** + Is this the highlight menu. + + @param MenuOption The input Menu option. + + @retval TRUE This is the highlight menu option. + @retval FALSE This is not the highlight menu option. + +**/ +BOOLEAN +IsHighLightMenuOption ( + IN UI_MENU_OPTION *MenuOption + ) +{ + if (gHighligthMenuInfo.HLTQuestionId != 0) { + if (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.HLTQuestionId) { + return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence); + } + } else { + if(CompareMem (gHighligthMenuInfo.HLTOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) { + if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) { + return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence); + } else { + return FALSE; + } + } + } + + return FALSE; +} + +/** + Find the highlight menu. + + If the input is NULL, base on the record highlight info in + gHighligthMenuInfo to find the last highlight menu. + + @param HighLightedStatement The input highlight statement. + + @retval The highlight menu index. + +**/ +LIST_ENTRY * +FindHighLightMenuOption ( + IN FORM_DISPLAY_ENGINE_STATEMENT *HighLightedStatement + ) +{ + LIST_ENTRY *NewPos; + UI_MENU_OPTION *MenuOption; + + NewPos = gMenuOption.ForwardLink; + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + + if (HighLightedStatement != NULL) { + while (MenuOption->ThisTag != HighLightedStatement) { + NewPos = NewPos->ForwardLink; + if (NewPos == &gMenuOption) { + // + // Not Found it, break + // + break; + } + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + } + + // + // Must find the highlight statement. + // + ASSERT (NewPos != &gMenuOption); + + } else { + while (!IsHighLightMenuOption (MenuOption)) { + NewPos = NewPos->ForwardLink; + if (NewPos == &gMenuOption) { + // + // Not Found it, break + // + break; + } + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + } + + // + // Highlight statement has disappear (suppressed/disableed) + // + if (NewPos == &gMenuOption) { + NewPos = NULL; + } + } + + return NewPos; +} + +/** + Is this the Top of screen menu. + + @param MenuOption The input Menu option. + + @retval TRUE This is the Top of screen menu option. + @retval FALSE This is not the Top of screen menu option. + +**/ +BOOLEAN +IsTopOfScreeMenuOption ( + IN UI_MENU_OPTION *MenuOption + ) +{ + if (gHighligthMenuInfo.TOSQuestionId != 0) { + return (BOOLEAN) (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.TOSQuestionId); + } + + if(CompareMem (gHighligthMenuInfo.TOSOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.TOSOpCode->Length) == 0) { + if (gHighligthMenuInfo.TOSIndex == 0 || gHighligthMenuInfo.TOSIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) { + return TRUE; + } else { + return FALSE; + } + } + + return FALSE; +} + +/** + Calculate the distance between two menus and include the skip value of StartMenu. + + @param StartMenu The link_entry pointer to start menu. + @param EndMenu The link_entry pointer to end menu. + +**/ +UINTN +GetDistanceBetweenMenus( + IN LIST_ENTRY *StartMenu, + IN LIST_ENTRY *EndMenu +) +{ + LIST_ENTRY *Link; + UI_MENU_OPTION *MenuOption; + UINTN Distance; + + Distance = 0; + + Link = StartMenu; + while (Link != EndMenu) { + MenuOption = MENU_OPTION_FROM_LINK (Link); + if (MenuOption->Row == 0) { + UpdateOptionSkipLines (MenuOption); + } + Distance += MenuOption->Skip; + Link = Link->BackLink; + } + return Distance; +} + +/** + Find the top of screen menu base on the previous record menu info. + + @param HighLightMenu The link_entry pointer to highlight menu. + + @retval Return the the link_entry pointer top of screen menu. + +**/ +LIST_ENTRY * +FindTopOfScreenMenuOption ( + IN LIST_ENTRY *HighLightMenu + ) +{ + LIST_ENTRY *NewPos; + UI_MENU_OPTION *MenuOption; + UINTN TopRow; + UINTN BottomRow; + + TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT; + BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT; + + NewPos = gMenuOption.ForwardLink; + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + + while (!IsTopOfScreeMenuOption(MenuOption)) { + NewPos = NewPos->ForwardLink; + if (NewPos == &gMenuOption) { + // + // Not Found it, break + // + break; + } + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + } + + // + // Last time top of screen menu has disappeared. + // + if (NewPos == &gMenuOption) { + return NULL; + } + // + // Check whether highlight menu and top of screen menu can be shown within one page, + // if can't, return NULL to re-calcaulate the top of scrren menu. Because some new menus + // may be dynamically inserted between highlightmenu and previous top of screen menu, + // So previous record top of screen menu is not appropriate for current display. + // + if (GetDistanceBetweenMenus (HighLightMenu, NewPos) + 1 > BottomRow - TopRow) { + return NULL; + } + + return NewPos; +} + +/** + Find the first menu which will be show at the top. + + @param FormData The data info for this form. + @param TopOfScreen The link_entry pointer to top menu. + @param HighlightMenu The menu which will be highlight. + @param SkipValue The skip value for the top menu. + +**/ +VOID +FindTopMenu ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT LIST_ENTRY **TopOfScreen, + OUT LIST_ENTRY **HighlightMenu, + OUT UINTN *SkipValue + ) +{ + UINTN TopRow; + UINTN BottomRow; + UI_MENU_OPTION *MenuOption; + UINTN TmpValue; + + TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT; + BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT; + // + // When option mismatch happens,there exist two cases,one is reenter the form, just like the if case below, + // and the other is exit current form and enter last form, it can be covered by the else case. + // + if (gMisMatch && gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle && gFormData->FormId == gHighligthMenuInfo.FormId) { + // + // Reenter caused by option mismatch or auto exit caused by refresh form(refresh interval/guid), + // base on the record highlight info to find the highlight menu. + // + + *HighlightMenu = FindHighLightMenuOption(NULL); + if (*HighlightMenu != NULL) { + // + // Update skip info for this highlight menu. + // + MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu); + UpdateOptionSkipLines (MenuOption); + + // + // Found the last time highlight menu. + // + *TopOfScreen = FindTopOfScreenMenuOption(*HighlightMenu); + if (*TopOfScreen != NULL) { + // + // Found the last time selectable top of screen menu. + // + AdjustDateAndTimePosition(TRUE, TopOfScreen); + MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen); + UpdateOptionSkipLines (MenuOption); + + *SkipValue = gHighligthMenuInfo.SkipValue; + } else { + // + // Not found last time top of screen menu, so base on current highlight menu + // to find the new top of screen menu. + // Make the current highlight menu at the bottom of the form to calculate the + // top of screen menu. + // + if (MenuOption->Skip >= BottomRow - TopRow) { + *TopOfScreen = *HighlightMenu; + TmpValue = 0; + } else { + *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue); + } + + *SkipValue = TmpValue; + } + } else { + // + // Last time highlight menu has disappear, find the first highlightable menu as the default one. + // + *HighlightMenu = gMenuOption.ForwardLink; + if (!IsListEmpty (&gMenuOption)) { + MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE); + } + *TopOfScreen = gMenuOption.ForwardLink; + *SkipValue = 0; + } + + } else if (FormData->HighLightedStatement != NULL) { + if (IsSavedHighlightStatement (FormData->HighLightedStatement)) { + // + // Input highlight menu is same as last time highlight menu. + // Base on last time highlight menu to set the top of screen menu and highlight menu. + // + *HighlightMenu = FindHighLightMenuOption(NULL); + ASSERT (*HighlightMenu != NULL); + + // + // Update skip info for this highlight menu. + // + MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu); + UpdateOptionSkipLines (MenuOption); + + *TopOfScreen = FindTopOfScreenMenuOption(*HighlightMenu); + if (*TopOfScreen == NULL) { + // + // Not found last time top of screen menu, so base on current highlight menu + // to find the new top of screen menu. + // Make the current highlight menu at the bottom of the form to calculate the + // top of screen menu. + // + if (MenuOption->Skip >= BottomRow - TopRow) { + *TopOfScreen = *HighlightMenu; + TmpValue = 0; + } else { + *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue); + } + + *SkipValue = TmpValue; + } else { + AdjustDateAndTimePosition(TRUE, TopOfScreen); + MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen); + UpdateOptionSkipLines (MenuOption); + + *SkipValue = gHighligthMenuInfo.SkipValue; + } + AdjustDateAndTimePosition(TRUE, TopOfScreen); + } else { + // + // Input highlight menu is not save as last time highlight menu. + // + *HighlightMenu = FindHighLightMenuOption(FormData->HighLightedStatement); + MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu); + UpdateOptionSkipLines (MenuOption); + + // + // Make the current highlight menu at the bottom of the form to calculate the + // top of screen menu. + // + if (MenuOption->Skip >= BottomRow - TopRow) { + *TopOfScreen = *HighlightMenu; + TmpValue = 0; + } else { + *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue); + } + + *SkipValue = TmpValue; + } + AdjustDateAndTimePosition(TRUE, TopOfScreen); + } else { + // + // If not has input highlight statement, just return the first one in this form. + // + *TopOfScreen = gMenuOption.ForwardLink; + *HighlightMenu = gMenuOption.ForwardLink; + if (!IsListEmpty (&gMenuOption)) { + MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE); + } + *SkipValue = 0; + } + + gMisMatch = FALSE; + + // + // First enter to show the menu, update highlight info. + // + UpdateHighlightMenuInfo (*HighlightMenu, *TopOfScreen, *SkipValue); +} + +/** + Record the highlight menu and top of screen menu info. + + @param Highlight The menu opton which is highlight. + @param TopOfScreen The menu opton which is at the top of the form. + @param SkipValue The skip line info for the top of screen menu. + +**/ +VOID +UpdateHighlightMenuInfo ( + IN LIST_ENTRY *Highlight, + IN LIST_ENTRY *TopOfScreen, + IN UINTN SkipValue + ) +{ + UI_MENU_OPTION *MenuOption; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + + gHighligthMenuInfo.HiiHandle = gFormData->HiiHandle; + gHighligthMenuInfo.FormId = gFormData->FormId; + gHighligthMenuInfo.SkipValue = (UINT16)SkipValue; + + if (!IsListEmpty (&gMenuOption)) { + MenuOption = MENU_OPTION_FROM_LINK (Highlight); + Statement = MenuOption->ThisTag; + + gUserInput->SelectedStatement = Statement; + + gHighligthMenuInfo.HLTSequence = MenuOption->Sequence; + gHighligthMenuInfo.HLTQuestionId = GetQuestionIdInfo(Statement->OpCode); + if (gHighligthMenuInfo.HLTQuestionId == 0) { + // + // if question id == 0, save the opcode buffer.. + // + if (gHighligthMenuInfo.HLTOpCode != NULL) { + FreePool (gHighligthMenuInfo.HLTOpCode); + } + gHighligthMenuInfo.HLTOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode); + ASSERT (gHighligthMenuInfo.HLTOpCode != NULL); + + gHighligthMenuInfo.HLTIndex = GetIndexInfoForOpcode(Statement->OpCode); + } + + MenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); + Statement = MenuOption->ThisTag; + + gHighligthMenuInfo.TOSQuestionId = GetQuestionIdInfo(Statement->OpCode); + if (gHighligthMenuInfo.TOSQuestionId == 0) { + // + // if question id == 0, save the opcode buffer.. + // + if (gHighligthMenuInfo.TOSOpCode != NULL) { + FreePool (gHighligthMenuInfo.TOSOpCode); + } + gHighligthMenuInfo.TOSOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode); + ASSERT (gHighligthMenuInfo.TOSOpCode != NULL); + + gHighligthMenuInfo.TOSIndex = GetIndexInfoForOpcode(Statement->OpCode); + } + } else { + gUserInput->SelectedStatement = NULL; + + gHighligthMenuInfo.HLTSequence = 0; + gHighligthMenuInfo.HLTQuestionId = 0; + if (gHighligthMenuInfo.HLTOpCode != NULL) { + FreePool (gHighligthMenuInfo.HLTOpCode); + } + gHighligthMenuInfo.HLTOpCode = NULL; + gHighligthMenuInfo.HLTIndex = 0; + + gHighligthMenuInfo.TOSQuestionId = 0; + if (gHighligthMenuInfo.TOSOpCode != NULL) { + FreePool (gHighligthMenuInfo.TOSOpCode); + } + gHighligthMenuInfo.TOSOpCode = NULL; + gHighligthMenuInfo.TOSIndex = 0; + } +} + +/** + Update attribut for this menu. + + @param MenuOption The menu opton which this attribut used to. + @param Highlight Whether this menu will be highlight. + +**/ +VOID +SetDisplayAttribute ( + IN UI_MENU_OPTION *MenuOption, + IN BOOLEAN Highlight + ) +{ + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + + Statement = MenuOption->ThisTag; + + if (Highlight) { + gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); + return; + } + + if (MenuOption->GrayOut) { + gST->ConOut->SetAttribute (gST->ConOut, GetGrayedTextColor ()); + } else { + if (Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) { + gST->ConOut->SetAttribute (gST->ConOut, GetSubTitleTextColor ()); + } else { + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + } + } +} + +/** + Print string for this menu option. + + @param MenuOption The menu opton which this attribut used to. + @param Col The column that this string will be print at. + @param Row The row that this string will be print at. + @param String The string which need to print. + @param Width The width need to print, if string is less than the + width, the block space will be used. + @param Highlight Whether this menu will be highlight. + +**/ +VOID +DisplayMenuString ( + IN UI_MENU_OPTION *MenuOption, + IN UINTN Col, + IN UINTN Row, + IN CHAR16 *String, + IN UINTN Width, + IN BOOLEAN Highlight + ) +{ + UINTN Length; + + // + // Print string with normal color. + // + if (!Highlight) { + PrintStringAtWithWidth (Col, Row, String, Width); + return; + } + + // + // Print the highlight menu string. + // First print the highlight string. + // + SetDisplayAttribute(MenuOption, TRUE); + Length = PrintStringAt (Col, Row, String); + + // + // Second, clean the empty after the string. + // + SetDisplayAttribute(MenuOption, FALSE); + PrintStringAtWithWidth (Col + Length, Row, L"", Width - Length); +} + +/** + Check whether this menu can has option string. + + @param MenuOption The menu opton which this attribut used to. + + @retval TRUE This menu option can have option string. + @retval FALSE This menu option can't have option string. + +**/ +BOOLEAN +HasOptionString ( + IN UI_MENU_OPTION *MenuOption + ) +{ + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + CHAR16 *String; + UINTN Size; + EFI_IFR_TEXT *TestOp; + + Size = 0; + Statement = MenuOption->ThisTag; + + // + // See if the second text parameter is really NULL + // + if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) { + TestOp = (EFI_IFR_TEXT *) Statement->OpCode; + if (TestOp->TextTwo != 0) { + String = GetToken (TestOp->TextTwo, gFormData->HiiHandle); + Size = StrLen (String); + FreePool (String); + } + } + + if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) || + (Statement->OpCode->OpCode == EFI_IFR_REF_OP) || + (Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) || + (Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) || + (Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) || + // + // Allow a wide display if text op-code and no secondary text op-code + // + ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0)) + ) { + + return FALSE; + } + + return TRUE; +} + +/** + Double confirm with user about the action. + + @param Action The user input action. + + @retval TRUE User confirm with the input or not need user confirm. + @retval FALSE User want ignore this input. + +**/ +BOOLEAN +FxConfirmPopup ( + IN UINT32 Action + ) +{ + EFI_INPUT_KEY Key; + CHAR16 *CfmStr; + UINTN CfmStrLen; + UINT32 CheckFlags; + BOOLEAN RetVal; + UINTN CatLen; + UINTN MaxLen; + + CfmStrLen = 0; + CatLen = StrLen (gConfirmMsgConnect); + + // + // Below action need extra popup dialog to confirm. + // + CheckFlags = BROWSER_ACTION_DISCARD | + BROWSER_ACTION_DEFAULT | + BROWSER_ACTION_SUBMIT | + BROWSER_ACTION_RESET | + BROWSER_ACTION_EXIT; + + // + // Not need to confirm with user, just return TRUE. + // + if ((Action & CheckFlags) == 0) { + return TRUE; + } + + if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) { + CfmStrLen += StrLen (gConfirmDiscardMsg); + } + + if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) { + if (CfmStrLen != 0) { + CfmStrLen += CatLen; + } + + CfmStrLen += StrLen (gConfirmDefaultMsg); + } + + if ((Action & BROWSER_ACTION_SUBMIT) == BROWSER_ACTION_SUBMIT) { + if (CfmStrLen != 0) { + CfmStrLen += CatLen; + } + + CfmStrLen += StrLen (gConfirmSubmitMsg); + } + + if ((Action & BROWSER_ACTION_RESET) == BROWSER_ACTION_RESET) { + if (CfmStrLen != 0) { + CfmStrLen += CatLen; + } + + CfmStrLen += StrLen (gConfirmResetMsg); + } + + if ((Action & BROWSER_ACTION_EXIT) == BROWSER_ACTION_EXIT) { + if (CfmStrLen != 0) { + CfmStrLen += CatLen; + } + + CfmStrLen += StrLen (gConfirmExitMsg); + } + + // + // Allocate buffer to save the string. + // String + "?" + "\0" + // + MaxLen = CfmStrLen + 1 + 1; + CfmStr = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + ASSERT (CfmStr != NULL); + + if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) { + StrCpyS (CfmStr, MaxLen, gConfirmDiscardMsg); + } + + if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) { + if (CfmStr[0] != 0) { + StrCatS (CfmStr, MaxLen, gConfirmMsgConnect); + StrCatS (CfmStr, MaxLen, gConfirmDefaultMsg2nd); + } else { + StrCpyS (CfmStr, MaxLen, gConfirmDefaultMsg); + } + } + + if ((Action & BROWSER_ACTION_SUBMIT) == BROWSER_ACTION_SUBMIT) { + if (CfmStr[0] != 0) { + StrCatS (CfmStr, MaxLen, gConfirmMsgConnect); + StrCatS (CfmStr, MaxLen, gConfirmSubmitMsg2nd); + } else { + StrCpyS (CfmStr, MaxLen, gConfirmSubmitMsg); + } + } + + if ((Action & BROWSER_ACTION_RESET) == BROWSER_ACTION_RESET) { + if (CfmStr[0] != 0) { + StrCatS (CfmStr, MaxLen, gConfirmMsgConnect); + StrCatS (CfmStr, MaxLen, gConfirmResetMsg2nd); + } else { + StrCpyS (CfmStr, MaxLen, gConfirmResetMsg); + } + } + + if ((Action & BROWSER_ACTION_EXIT) == BROWSER_ACTION_EXIT) { + if (CfmStr[0] != 0) { + StrCatS (CfmStr, MaxLen, gConfirmMsgConnect); + StrCatS (CfmStr, MaxLen, gConfirmExitMsg2nd); + } else { + StrCpyS (CfmStr, MaxLen, gConfirmExitMsg); + } + } + + StrCatS (CfmStr, MaxLen, gConfirmMsgEnd); + + do { + CreateDialog (&Key, gEmptyString, CfmStr, gConfirmOpt, gEmptyString, NULL); + } while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) && + ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptNo[0] | UPPER_LOWER_CASE_OFFSET)) && + (Key.ScanCode != SCAN_ESC)); + + if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) { + RetVal = TRUE; + } else { + RetVal = FALSE; + } + + FreePool (CfmStr); + + return RetVal; +} + +/** + Print string for this menu option. + + @param MenuOption The menu opton which this attribut used to. + @param SkipWidth The skip width between the left to the start of the prompt. + @param BeginCol The begin column for one menu. + @param SkipLine The skip line for this menu. + @param BottomRow The bottom row for this form. + @param Highlight Whether this menu will be highlight. + @param UpdateCol Whether need to update the column info for Date/Time. + + @retval EFI_SUCESSS Process the user selection success. + +**/ +EFI_STATUS +DisplayOneMenu ( + IN UI_MENU_OPTION *MenuOption, + IN UINTN SkipWidth, + IN UINTN BeginCol, + IN UINTN SkipLine, + IN UINTN BottomRow, + IN BOOLEAN Highlight, + IN BOOLEAN UpdateCol + ) +{ + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + UINTN Index; + UINT16 Width; + UINT16 PromptWidth; + CHAR16 *StringPtr; + CHAR16 *OptionString; + CHAR16 *OutputString; + UINT16 GlyphWidth; + UINTN Temp; + UINTN Temp2; + UINTN Temp3; + EFI_STATUS Status; + UINTN Row; + BOOLEAN IsProcessingFirstRow; + UINTN Col; + UINTN PromptLineNum; + UINTN OptionLineNum; + CHAR16 AdjustValue; + UINTN MaxRow; + + Statement = MenuOption->ThisTag; + Temp = SkipLine; + Temp2 = SkipLine; + Temp3 = SkipLine; + AdjustValue = 0; + PromptLineNum = 0; + OptionLineNum = 0; + MaxRow = 0; + IsProcessingFirstRow = TRUE; + + // + // Set default color. + // + SetDisplayAttribute (MenuOption, FALSE); + + // + // 1. Paint the option string. + // + Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + if (OptionString != NULL) { + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + // + // Adjust option string for date/time opcode. + // + ProcessStringForDateTime(MenuOption, OptionString, UpdateCol); + } + + Width = (UINT16) gOptionBlockWidth - 1; + Row = MenuOption->Row; + GlyphWidth = 1; + OptionLineNum = 0; + + for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if (((Temp2 == 0)) && (Row <= BottomRow)) { + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + // + // For date/time question, it has three menu options for this qustion. + // The first/second menu options with the skip value is 0. the last one + // with skip value is 1. + // + if (MenuOption->Skip != 0) { + // + // For date/ time, print the last past (year for date and second for time) + // - 7 means skip [##/##/ for date and [##:##: for time. + // + DisplayMenuString (MenuOption,MenuOption->OptCol, Row, OutputString, Width + 1 - 7, Highlight); + } else { + // + // For date/ time, print the first and second past (year for date and second for time) + // The OutputString has a NARROW_CHAR or WIDE_CHAR at the begin of the string, + // so need to - 1 to remove it, otherwise, it will clean 1 extr char follow it. + DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, StrLen (OutputString) - 1, Highlight); + } + } else { + DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight); + } + OptionLineNum++; + } + + // + // If there is more string to process print on the next row and increment the Skip value + // + if (StrLen (&OptionString[Index]) != 0) { + if (Temp2 == 0) { + Row++; + // + // Since the Number of lines for this menu entry may or may not be reflected accurately + // since the prompt might be 1 lines and option might be many, and vice versa, we need to do + // some testing to ensure we are keeping this in-sync. + // + // If the difference in rows is greater than or equal to the skip value, increase the skip value + // + if ((Row - MenuOption->Row) >= MenuOption->Skip) { + MenuOption->Skip++; + } + } + } + + FreePool (OutputString); + if (Temp2 != 0) { + Temp2--; + } + } + + Highlight = FALSE; + + FreePool (OptionString); + } + + // + // 2. Paint the description. + // + PromptWidth = GetWidth (MenuOption, &AdjustValue); + Row = MenuOption->Row; + GlyphWidth = 1; + PromptLineNum = 0; + + if (MenuOption->Description == NULL || MenuOption->Description[0] == '\0') { + PrintStringAtWithWidth (BeginCol, Row, L"", PromptWidth + AdjustValue + SkipWidth); + PromptLineNum++; + } else { + for (Index = 0; GetLineByWidth (MenuOption->Description, PromptWidth, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if ((Temp == 0) && (Row <= BottomRow)) { + // + // 1.Clean the start LEFT_SKIPPED_COLUMNS + // + PrintStringAtWithWidth (BeginCol, Row, L"", SkipWidth); + + if (Statement->OpCode->OpCode == EFI_IFR_REF_OP && MenuOption->Col >= 2 && IsProcessingFirstRow) { + // + // Print Arrow for Goto button. + // + PrintCharAt ( + MenuOption->Col - 2, + Row, + GEOMETRICSHAPE_RIGHT_TRIANGLE + ); + IsProcessingFirstRow = FALSE; + } + DisplayMenuString (MenuOption, MenuOption->Col, Row, OutputString, PromptWidth + AdjustValue, Highlight); + PromptLineNum ++; + } + // + // If there is more string to process print on the next row and increment the Skip value + // + if (StrLen (&MenuOption->Description[Index]) != 0) { + if (Temp == 0) { + Row++; + } + } + + FreePool (OutputString); + if (Temp != 0) { + Temp--; + } + } + + Highlight = FALSE; + } + + + // + // 3. If this is a text op with secondary text information + // + if ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo != 0)) { + StringPtr = GetToken (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo, gFormData->HiiHandle); + + Width = (UINT16) gOptionBlockWidth - 1; + Row = MenuOption->Row; + GlyphWidth = 1; + OptionLineNum = 0; + + for (Index = 0; GetLineByWidth (StringPtr, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if ((Temp3 == 0) && (Row <= BottomRow)) { + DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight); + OptionLineNum++; + } + // + // If there is more string to process print on the next row and increment the Skip value + // + if (StrLen (&StringPtr[Index]) != 0) { + if (Temp3 == 0) { + Row++; + // + // If the rows for text two is greater than or equal to the skip value, increase the skip value + // + if ((Row - MenuOption->Row) >= MenuOption->Skip) { + MenuOption->Skip++; + } + } + } + + FreePool (OutputString); + if (Temp3 != 0) { + Temp3--; + } + } + + FreePool (StringPtr); + } + + // + // 4.Line number for Option string and prompt string are not equal. + // Clean the column whose line number is less. + // + if (HasOptionString(MenuOption) && (OptionLineNum != PromptLineNum)) { + Col = OptionLineNum < PromptLineNum ? MenuOption->OptCol : BeginCol; + Row = (OptionLineNum < PromptLineNum ? OptionLineNum : PromptLineNum) + MenuOption->Row; + Width = (UINT16) (OptionLineNum < PromptLineNum ? gOptionBlockWidth : PromptWidth + AdjustValue + SkipWidth); + MaxRow = (OptionLineNum < PromptLineNum ? PromptLineNum : OptionLineNum) + MenuOption->Row - 1; + + while (Row <= MaxRow) { + DisplayMenuString (MenuOption, Col, Row++, L"", Width, FALSE); + } + } + + return EFI_SUCCESS; +} + +/** + Display menu and wait for user to select one menu option, then return it. + If AutoBoot is enabled, then if user doesn't select any option, + after period of time, it will automatically return the first menu option. + + @param FormData The current form data info. + + @retval EFI_SUCESSS Process the user selection success. + @retval EFI_NOT_FOUND Process option string for orderedlist/Oneof fail. + +**/ +EFI_STATUS +UiDisplayMenu ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + UINTN SkipValue; + INTN Difference; + UINTN DistanceValue; + UINTN Row; + UINTN Col; + UINTN Temp; + UINTN Temp2; + UINTN TopRow; + UINTN BottomRow; + UINTN Index; + CHAR16 *StringPtr; + CHAR16 *StringRightPtr; + CHAR16 *StringErrorPtr; + CHAR16 *OptionString; + CHAR16 *HelpString; + CHAR16 *HelpHeaderString; + CHAR16 *HelpBottomString; + BOOLEAN NewLine; + BOOLEAN Repaint; + BOOLEAN UpArrow; + BOOLEAN DownArrow; + EFI_STATUS Status; + EFI_INPUT_KEY Key; + LIST_ENTRY *Link; + LIST_ENTRY *NewPos; + LIST_ENTRY *TopOfScreen; + LIST_ENTRY *SavedListEntry; + UI_MENU_OPTION *MenuOption; + UI_MENU_OPTION *NextMenuOption; + UI_MENU_OPTION *SavedMenuOption; + UI_CONTROL_FLAG ControlFlag; + UI_SCREEN_OPERATION ScreenOperation; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + BROWSER_HOT_KEY *HotKey; + UINTN HelpPageIndex; + UINTN HelpPageCount; + UINTN RowCount; + UINTN HelpLine; + UINTN HelpHeaderLine; + UINTN HelpBottomLine; + BOOLEAN MultiHelpPage; + UINT16 EachLineWidth; + UINT16 HeaderLineWidth; + UINT16 BottomLineWidth; + EFI_STRING_ID HelpInfo; + UI_EVENT_TYPE EventType; + BOOLEAN SkipHighLight; + EFI_HII_VALUE *StatementValue; + + EventType = UIEventNone; + Status = EFI_SUCCESS; + HelpString = NULL; + HelpHeaderString = NULL; + HelpBottomString = NULL; + OptionString = NULL; + ScreenOperation = UiNoOperation; + NewLine = TRUE; + HelpPageCount = 0; + HelpLine = 0; + RowCount = 0; + HelpBottomLine = 0; + HelpHeaderLine = 0; + HelpPageIndex = 0; + MultiHelpPage = FALSE; + EachLineWidth = 0; + HeaderLineWidth = 0; + BottomLineWidth = 0; + UpArrow = FALSE; + DownArrow = FALSE; + SkipValue = 0; + SkipHighLight = FALSE; + + NextMenuOption = NULL; + SavedMenuOption = NULL; + HotKey = NULL; + Repaint = TRUE; + MenuOption = NULL; + gModalSkipColumn = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 6; + + ZeroMem (&Key, sizeof (EFI_INPUT_KEY)); + + TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT; + BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT - 1; + + Row = TopRow; + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gModalSkipColumn; + } else { + Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS; + } + + FindTopMenu(FormData, &TopOfScreen, &NewPos, &SkipValue); + if (!IsListEmpty (&gMenuOption)) { + NextMenuOption = MENU_OPTION_FROM_LINK (NewPos); + gUserInput->SelectedStatement = NextMenuOption->ThisTag; + } + + gST->ConOut->EnableCursor (gST->ConOut, FALSE); + + ControlFlag = CfInitialization; + while (TRUE) { + switch (ControlFlag) { + case CfInitialization: + if ((gOldFormEntry.HiiHandle != FormData->HiiHandle) || + (!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid))) { + // + // Clear Statement range if different formset is painted. + // + ClearLines ( + gStatementDimensions.LeftColumn, + gStatementDimensions.RightColumn, + TopRow - SCROLL_ARROW_HEIGHT, + BottomRow + SCROLL_ARROW_HEIGHT, + GetFieldTextColor () + ); + + } + ControlFlag = CfRepaint; + break; + + case CfRepaint: + ControlFlag = CfRefreshHighLight; + + if (Repaint) { + // + // Display menu + // + DownArrow = FALSE; + UpArrow = FALSE; + Row = TopRow; + + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + + // + // 1. Check whether need to print the arrow up. + // + if (!ValueIsScroll (TRUE, TopOfScreen)) { + UpArrow = TRUE; + } + + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn); + } else { + PrintStringAtWithWidth(gStatementDimensions.LeftColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn); + } + if (UpArrow) { + gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ()); + PrintCharAt ( + gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1, + TopRow - SCROLL_ARROW_HEIGHT, + ARROW_UP + ); + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + } + + // + // 2.Paint the menu. + // + for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) { + MenuOption = MENU_OPTION_FROM_LINK (Link); + MenuOption->Row = Row; + MenuOption->Col = Col; + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + MenuOption->OptCol = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth + gModalSkipColumn; + } else { + MenuOption->OptCol = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth; + } + + if (MenuOption->NestInStatement) { + MenuOption->Col += SUBTITLE_INDENT; + } + + // + // Save the highlight menu, will be used in CfRefreshHighLight case. + // + if (Link == NewPos) { + SavedMenuOption = MenuOption; + SkipHighLight = TRUE; + } + + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + Status = DisplayOneMenu (MenuOption, + MenuOption->Col - gStatementDimensions.LeftColumn, + gStatementDimensions.LeftColumn + gModalSkipColumn, + Link == TopOfScreen ? SkipValue : 0, + BottomRow, + (BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)), + TRUE + ); + } else { + Status = DisplayOneMenu (MenuOption, + MenuOption->Col - gStatementDimensions.LeftColumn, + gStatementDimensions.LeftColumn, + Link == TopOfScreen ? SkipValue : 0, + BottomRow, + (BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)), + TRUE + ); + } + + if (EFI_ERROR (Status)) { + if (gMisMatch) { + return EFI_SUCCESS; + } else { + return Status; + } + } + // + // 3. Update the row info which will be used by next menu. + // + if (Link == TopOfScreen) { + Row += MenuOption->Skip - SkipValue; + } else { + Row += MenuOption->Skip; + } + + if (Row > BottomRow) { + if (!ValueIsScroll (FALSE, Link)) { + DownArrow = TRUE; + } + + Row = BottomRow + 1; + break; + } + } + + // + // 3. Menus in this form may not cover all form, clean the remain field. + // + while (Row <= BottomRow) { + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, Row++, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn); + } else { + PrintStringAtWithWidth(gStatementDimensions.LeftColumn, Row++, L"", gStatementDimensions.RightColumn - gHelpBlockWidth - gStatementDimensions.LeftColumn); + } + } + + // + // 4. Print the down arrow row. + // + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * + gModalSkipColumn); + } else { + PrintStringAtWithWidth(gStatementDimensions.LeftColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn); + } + if (DownArrow) { + gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ()); + PrintCharAt ( + gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1, + BottomRow + SCROLL_ARROW_HEIGHT, + ARROW_DOWN + ); + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + } + + MenuOption = NULL; + } + break; + + case CfRefreshHighLight: + + // + // MenuOption: Last menu option that need to remove hilight + // MenuOption is set to NULL in Repaint + // NewPos: Current menu option that need to hilight + // + ControlFlag = CfUpdateHelpString; + + ASSERT (NewPos != NULL); + UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); + + if (SkipHighLight) { + SkipHighLight = FALSE; + MenuOption = SavedMenuOption; + RefreshKeyHelp(gFormData, SavedMenuOption->ThisTag, FALSE); + break; + } + + if (IsListEmpty (&gMenuOption)) { + // + // No menu option, just update the hotkey filed. + // + RefreshKeyHelp(gFormData, NULL, FALSE); + break; + } + + if (MenuOption != NULL && TopOfScreen == &MenuOption->Link) { + Temp = SkipValue; + } else { + Temp = 0; + } + if (NewPos == TopOfScreen) { + Temp2 = SkipValue; + } else { + Temp2 = 0; + } + + if (MenuOption == NULL || NewPos != &MenuOption->Link) { + if (MenuOption != NULL) { + // + // Remove the old highlight menu. + // + Status = DisplayOneMenu (MenuOption, + MenuOption->Col - gStatementDimensions.LeftColumn, + gStatementDimensions.LeftColumn, + Temp, + BottomRow, + FALSE, + FALSE + ); + } + + // + // This is the current selected statement + // + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + RefreshKeyHelp(gFormData, MenuOption->ThisTag, FALSE); + + if (!IsSelectable (MenuOption)) { + break; + } + + Status = DisplayOneMenu (MenuOption, + MenuOption->Col - gStatementDimensions.LeftColumn, + gStatementDimensions.LeftColumn, + Temp2, + BottomRow, + TRUE, + FALSE + ); + } + break; + + case CfUpdateHelpString: + ControlFlag = CfPrepareToReadKey; + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + break; + } + + // + // NewLine means only update highlight menu (remove old highlight and highlith + // the new one), not need to full repain the form. + // + if (Repaint || NewLine) { + if (IsListEmpty (&gMenuOption)) { + // + // Don't print anything if no mwnu option. + // + StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); + } else { + // + // Don't print anything if it is a NULL help token + // + ASSERT(MenuOption != NULL); + HelpInfo = ((EFI_IFR_STATEMENT_HEADER *) ((CHAR8 *)MenuOption->ThisTag->OpCode + sizeof (EFI_IFR_OP_HEADER)))->Help; + Statement = MenuOption->ThisTag; + StatementValue = &Statement->CurrentValue; + if (HelpInfo == 0 || !IsSelectable (MenuOption)) { + if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP && StatementValue->Value.date.Month== 0xff)||(Statement->OpCode->OpCode == EFI_IFR_TIME_OP && StatementValue->Value.time.Hour == 0xff)){ + StringPtr = GetToken (STRING_TOKEN (GET_TIME_FAIL), gHiiHandle); + } else { + StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); + } + } else { + if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP && StatementValue->Value.date.Month== 0xff)||(Statement->OpCode->OpCode == EFI_IFR_TIME_OP && StatementValue->Value.time.Hour == 0xff)){ + StringRightPtr = GetToken (HelpInfo, gFormData->HiiHandle); + StringErrorPtr = GetToken (STRING_TOKEN (GET_TIME_FAIL), gHiiHandle); + StringPtr = AllocateZeroPool ((StrLen (StringRightPtr) + StrLen (StringErrorPtr)+ 1 ) * sizeof (CHAR16)); + StrCpyS (StringPtr, StrLen (StringRightPtr) + StrLen (StringErrorPtr) + 1, StringRightPtr); + StrCatS (StringPtr, StrLen (StringRightPtr) + StrLen (StringErrorPtr) + 1, StringErrorPtr); + FreePool (StringRightPtr); + FreePool (StringErrorPtr); + } else { + StringPtr = GetToken (HelpInfo, gFormData->HiiHandle); + } + } + } + + RowCount = BottomRow - TopRow + 1; + HelpPageIndex = 0; + // + // 1.Calculate how many line the help string need to print. + // + if (HelpString != NULL) { + FreePool (HelpString); + HelpString = NULL; + } + HelpLine = ProcessHelpString (StringPtr, &HelpString, &EachLineWidth, RowCount); + FreePool (StringPtr); + + if (HelpLine > RowCount) { + MultiHelpPage = TRUE; + StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP), gHiiHandle); + if (HelpHeaderString != NULL) { + FreePool (HelpHeaderString); + HelpHeaderString = NULL; + } + HelpHeaderLine = ProcessHelpString (StringPtr, &HelpHeaderString, &HeaderLineWidth, 0); + FreePool (StringPtr); + StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN), gHiiHandle); + if (HelpBottomString != NULL) { + FreePool (HelpBottomString); + HelpBottomString = NULL; + } + HelpBottomLine = ProcessHelpString (StringPtr, &HelpBottomString, &BottomLineWidth, 0); + FreePool (StringPtr); + // + // Calculate the help page count. + // + if (HelpLine > 2 * RowCount - 2) { + HelpPageCount = (HelpLine - RowCount + 1) / (RowCount - 2) + 1; + if ((HelpLine - RowCount + 1) % (RowCount - 2) != 0) { + HelpPageCount += 1; + } + } else { + HelpPageCount = 2; + } + } else { + MultiHelpPage = FALSE; + } + } + + // + // Check whether need to show the 'More(U/u)' at the begin. + // Base on current direct info, here shows aligned to the right side of the column. + // If the direction is multi line and aligned to right side may have problem, so + // add ASSERT code here. + // + if (HelpPageIndex > 0) { + gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ()); + for (Index = 0; Index < HelpHeaderLine; Index++) { + ASSERT (HelpHeaderLine == 1); + ASSERT (GetStringWidth (HelpHeaderString) / 2 < ((UINT32) gHelpBlockWidth - 1)); + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + gEmptyString, + gHelpBlockWidth + ); + PrintStringAt ( + gStatementDimensions.RightColumn - GetStringWidth (HelpHeaderString) / 2 - 1, + Index + TopRow, + &HelpHeaderString[Index * HeaderLineWidth] + ); + } + } + + gST->ConOut->SetAttribute (gST->ConOut, GetHelpTextColor ()); + // + // Print the help string info. + // + if (!MultiHelpPage) { + for (Index = 0; Index < HelpLine; Index++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + &HelpString[Index * EachLineWidth], + gHelpBlockWidth + ); + } + for (; Index < RowCount; Index ++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + gEmptyString, + gHelpBlockWidth + ); + } + gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow); + } else { + if (HelpPageIndex == 0) { + for (Index = 0; Index < RowCount - HelpBottomLine; Index++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + &HelpString[Index * EachLineWidth], + gHelpBlockWidth + ); + } + } else { + for (Index = 0; (Index < RowCount - HelpBottomLine - HelpHeaderLine) && + (Index + HelpPageIndex * (RowCount - 2) + 1 < HelpLine); Index++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow + HelpHeaderLine, + &HelpString[(Index + HelpPageIndex * (RowCount - 2) + 1)* EachLineWidth], + gHelpBlockWidth + ); + } + if (HelpPageIndex == HelpPageCount - 1) { + for (; Index < RowCount - HelpHeaderLine; Index ++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow + HelpHeaderLine, + gEmptyString, + gHelpBlockWidth + ); + } + gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow); + } + } + } + + // + // Check whether need to print the 'More(D/d)' at the bottom. + // Base on current direct info, here shows aligned to the right side of the column. + // If the direction is multi line and aligned to right side may have problem, so + // add ASSERT code here. + // + if (HelpPageIndex < HelpPageCount - 1 && MultiHelpPage) { + gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ()); + for (Index = 0; Index < HelpBottomLine; Index++) { + ASSERT (HelpBottomLine == 1); + ASSERT (GetStringWidth (HelpBottomString) / 2 < ((UINT32) gHelpBlockWidth - 1)); + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + BottomRow + Index - HelpBottomLine + 1, + gEmptyString, + gHelpBlockWidth + ); + PrintStringAt ( + gStatementDimensions.RightColumn - GetStringWidth (HelpBottomString) / 2 - 1, + BottomRow + Index - HelpBottomLine + 1, + &HelpBottomString[Index * BottomLineWidth] + ); + } + } + // + // Reset this flag every time we finish using it. + // + Repaint = FALSE; + NewLine = FALSE; + break; + + case CfPrepareToReadKey: + ControlFlag = CfReadKey; + ScreenOperation = UiNoOperation; + break; + + case CfReadKey: + ControlFlag = CfScreenOperation; + + // + // Wait for user's selection + // + while (TRUE) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (!EFI_ERROR (Status)) { + EventType = UIEventKey; + break; + } + + // + // If we encounter error, continue to read another key in. + // + if (Status != EFI_NOT_READY) { + continue; + } + + EventType = UiWaitForEvent(gST->ConIn->WaitForKey); + if (EventType == UIEventKey) { + gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + } + break; + } + + if (EventType == UIEventDriver) { + gMisMatch = TRUE; + gUserInput->Action = BROWSER_ACTION_NONE; + ControlFlag = CfExit; + break; + } + + if (EventType == UIEventTimeOut) { + gUserInput->Action = BROWSER_ACTION_FORM_EXIT; + ControlFlag = CfExit; + break; + } + + switch (Key.UnicodeChar) { + case CHAR_CARRIAGE_RETURN: + if(MenuOption == NULL || MenuOption->GrayOut || MenuOption->ReadOnly) { + ControlFlag = CfReadKey; + break; + } + + ScreenOperation = UiSelect; + gDirection = 0; + break; + + // + // We will push the adjustment of these numeric values directly to the input handler + // NOTE: we won't handle manual input numeric + // + case '+': + case '-': + // + // If the screen has no menu items, and the user didn't select UiReset + // ignore the selection and go back to reading keys. + // + ASSERT(MenuOption != NULL); + if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) { + ControlFlag = CfReadKey; + break; + } + + Statement = MenuOption->ThisTag; + if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP) + || (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) + || ((Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (GetFieldFromNum(Statement->OpCode) != 0)) + ){ + if (Key.UnicodeChar == '+') { + gDirection = SCAN_RIGHT; + } else { + gDirection = SCAN_LEFT; + } + + Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE); + if (OptionString != NULL) { + FreePool (OptionString); + } + if (EFI_ERROR (Status)) { + // + // Repaint to clear possible error prompt pop-up + // + Repaint = TRUE; + NewLine = TRUE; + } else { + ControlFlag = CfExit; + } + } + break; + + case '^': + ScreenOperation = UiUp; + break; + + case 'V': + case 'v': + ScreenOperation = UiDown; + break; + + case ' ': + if(IsListEmpty (&gMenuOption)) { + ControlFlag = CfReadKey; + break; + } + + ASSERT(MenuOption != NULL); + if (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) { + ScreenOperation = UiSelect; + } + break; + + case 'D': + case 'd': + if (!MultiHelpPage) { + ControlFlag = CfReadKey; + break; + } + ControlFlag = CfUpdateHelpString; + HelpPageIndex = HelpPageIndex < HelpPageCount - 1 ? HelpPageIndex + 1 : HelpPageCount - 1; + break; + + case 'U': + case 'u': + if (!MultiHelpPage) { + ControlFlag = CfReadKey; + break; + } + ControlFlag = CfUpdateHelpString; + HelpPageIndex = HelpPageIndex > 0 ? HelpPageIndex - 1 : 0; + break; + + case CHAR_NULL: + for (Index = 0; Index < mScanCodeNumber; Index++) { + if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) { + ScreenOperation = gScanCodeToOperation[Index].ScreenOperation; + break; + } + } + + if (((FormData->Attribute & HII_DISPLAY_MODAL) != 0) && (Key.ScanCode == SCAN_ESC || Index == mScanCodeNumber)) { + // + // ModalForm has no ESC key and Hot Key. + // + ControlFlag = CfReadKey; + } else if (Index == mScanCodeNumber) { + // + // Check whether Key matches the registered hot key. + // + HotKey = NULL; + HotKey = GetHotKeyFromRegisterList (&Key); + if (HotKey != NULL) { + ScreenOperation = UiHotKey; + } + } + break; + } + break; + + case CfScreenOperation: + if ((ScreenOperation != UiReset) && (ScreenOperation != UiHotKey)) { + // + // If the screen has no menu items, and the user didn't select UiReset or UiHotKey + // ignore the selection and go back to reading keys. + // + if (IsListEmpty (&gMenuOption)) { + ControlFlag = CfReadKey; + break; + } + } + + for (Index = 0; + Index < ARRAY_SIZE (gScreenOperationToControlFlag); + Index++ + ) { + if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) { + ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag; + break; + } + } + break; + + case CfUiSelect: + ControlFlag = CfRepaint; + + ASSERT(MenuOption != NULL); + Statement = MenuOption->ThisTag; + if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) { + break; + } + + switch (Statement->OpCode->OpCode) { + case EFI_IFR_REF_OP: + case EFI_IFR_ACTION_OP: + case EFI_IFR_RESET_BUTTON_OP: + ControlFlag = CfExit; + break; + + default: + // + // Editable Questions: oneof, ordered list, checkbox, numeric, string, password + // + RefreshKeyHelp (gFormData, Statement, TRUE); + Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE); + + if (OptionString != NULL) { + FreePool (OptionString); + } + + if (EFI_ERROR (Status)) { + Repaint = TRUE; + NewLine = TRUE; + RefreshKeyHelp (gFormData, Statement, FALSE); + break; + } else { + ControlFlag = CfExit; + break; + } + } + break; + + case CfUiReset: + // + // We come here when someone press ESC + // If the policy is not exit front page when user press ESC, process here. + // + if (!FormExitPolicy()) { + Repaint = TRUE; + NewLine = TRUE; + ControlFlag = CfRepaint; + break; + } + + gUserInput->Action = BROWSER_ACTION_FORM_EXIT; + ControlFlag = CfExit; + break; + + case CfUiHotKey: + ControlFlag = CfRepaint; + + ASSERT (HotKey != NULL); + + if (FxConfirmPopup(HotKey->Action)) { + gUserInput->Action = HotKey->Action; + if ((HotKey->Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) { + gUserInput->DefaultId = HotKey->DefaultId; + } + ControlFlag = CfExit; + } else { + Repaint = TRUE; + NewLine = TRUE; + ControlFlag = CfRepaint; + } + + break; + + case CfUiLeft: + ControlFlag = CfRepaint; + ASSERT(MenuOption != NULL); + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) { + if (MenuOption->Sequence != 0) { + // + // In the middle or tail of the Date/Time op-code set, go left. + // + ASSERT(NewPos != NULL); + NewPos = NewPos->BackLink; + } + } + break; + + case CfUiRight: + ControlFlag = CfRepaint; + ASSERT(MenuOption != NULL); + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) { + if (MenuOption->Sequence != 2) { + // + // In the middle or tail of the Date/Time op-code set, go left. + // + ASSERT(NewPos != NULL); + NewPos = NewPos->ForwardLink; + } + } + break; + + case CfUiUp: + ControlFlag = CfRepaint; + NewLine = TRUE; + + SavedListEntry = NewPos; + ASSERT(NewPos != NULL); + + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + ASSERT (MenuOption != NULL); + + // + // Adjust Date/Time position before we advance forward. + // + AdjustDateAndTimePosition (TRUE, &NewPos); + + NewPos = NewPos->BackLink; + // + // Find next selectable menu or the first menu beyond current form. + // + Difference = MoveToNextStatement (TRUE, &NewPos, MenuOption->Row - TopRow, FALSE); + if (Difference < 0) { + // + // We hit the begining MenuOption that can be focused + // so we simply scroll to the top. + // + Repaint = TRUE; + if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) { + TopOfScreen = gMenuOption.ForwardLink; + NewPos = SavedListEntry; + SkipValue = 0; + } else { + // + // Scroll up to the last page when we have arrived at top page. + // + TopOfScreen = FindTopOfScreenMenu (gMenuOption.BackLink, BottomRow - TopRow, &SkipValue); + NewPos = gMenuOption.BackLink; + MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow, TRUE); + } + } else { + NextMenuOption = MENU_OPTION_FROM_LINK (NewPos); + + if (MenuOption->Row < TopRow + Difference + NextMenuOption->Skip) { + // + // Previous focus MenuOption is above the TopOfScreen, so we need to scroll + // + TopOfScreen = NewPos; + Repaint = TRUE; + SkipValue = 0; + } + + // + // Check whether new highlight menu is selectable, if not, keep highlight on the old one. + // + // BottomRow - TopRow + 1 means the total rows current forms supported. + // Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu + // and new top menu. New top menu will all shows in next form, but last highlight menu + // may only shows 1 line. + 1 at right part means at least need to keep 1 line for the + // last highlight menu. + // + if (!IsSelectable(NextMenuOption) && IsSelectable(MenuOption) && + (BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) { + NewPos = SavedListEntry; + } + } + + UpdateStatusBar (INPUT_ERROR, FALSE); + + // + // If we encounter a Date/Time op-code set, rewind to the first op-code of the set. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + + UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); + break; + + case CfUiPageUp: + // + // SkipValue means lines is skipped when show the top menu option. + // + ControlFlag = CfRepaint; + NewLine = TRUE; + Repaint = TRUE; + + Link = TopOfScreen; + // + // First minus the menu of the top screen, it's value is SkipValue. + // + if (SkipValue >= BottomRow - TopRow + 1) { + // + // SkipValue > (BottomRow - TopRow + 1) means current menu has more than one + // form of options to be show, so just update the SkipValue to show the next + // parts of options. + // + SkipValue -= BottomRow - TopRow + 1; + NewPos = TopOfScreen; + break; + } else { + Index = (BottomRow + 1) - SkipValue - TopRow; + } + + TopOfScreen = FindTopOfScreenMenu(TopOfScreen, Index, &SkipValue); + NewPos = TopOfScreen; + MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, FALSE); + + UpdateStatusBar (INPUT_ERROR, FALSE); + + // + // If we encounter a Date/Time op-code set, rewind to the first op-code of the set. + // Don't do this when we are already in the first page. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + + UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); + break; + + case CfUiPageDown: + // + // SkipValue means lines is skipped when show the top menu option. + // + ControlFlag = CfRepaint; + NewLine = TRUE; + Repaint = TRUE; + + Link = TopOfScreen; + NextMenuOption = MENU_OPTION_FROM_LINK (Link); + Index = TopRow + NextMenuOption->Skip - SkipValue; + // + // Count to the menu option which will show at the top of the next form. + // + while ((Index <= BottomRow + 1) && (Link->ForwardLink != &gMenuOption)) { + Link = Link->ForwardLink; + NextMenuOption = MENU_OPTION_FROM_LINK (Link); + Index = Index + NextMenuOption->Skip; + } + + if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow + 1)) { + // + // Highlight on the last menu which can be highlight. + // + Repaint = FALSE; + MoveToNextStatement (TRUE, &Link, Index - TopRow, TRUE); + } else { + // + // Calculate the skip line for top of screen menu. + // + if (Link == TopOfScreen) { + // + // The top of screen menu option occupies the entire form. + // + SkipValue += BottomRow - TopRow + 1; + } else { + SkipValue = NextMenuOption->Skip - (Index - (BottomRow + 1)); + } + TopOfScreen = Link; + MenuOption = NULL; + // + // Move to the Next selectable menu. + // + MoveToNextStatement (FALSE, &Link, BottomRow - TopRow, TRUE); + } + + // + // Save the menu as the next highlight menu. + // + NewPos = Link; + + UpdateStatusBar (INPUT_ERROR, FALSE); + + // + // If we encounter a Date/Time op-code set, rewind to the first op-code of the set. + // Don't do this when we are already in the last page. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + + UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); + break; + + case CfUiDown: + // + // SkipValue means lines is skipped when show the top menu option. + // NewPos points to the menu which is highlighted now. + // + ControlFlag = CfRepaint; + NewLine = TRUE; + + if (NewPos == TopOfScreen) { + Temp2 = SkipValue; + } else { + Temp2 = 0; + } + + SavedListEntry = NewPos; + // + // Since the behavior of hitting the down arrow on a Date/Time op-code is intended + // to be one that progresses to the next set of op-codes, we need to advance to the last + // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate + // checking can be done. The only other logic we need to introduce is that if a Date/Time + // op-code is the last entry in the menu, we need to rewind back to the first op-code of + // the Date/Time op-code. + // + AdjustDateAndTimePosition (FALSE, &NewPos); + + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + NewPos = NewPos->ForwardLink; + // + // Find the next selectable menu. + // + if (MenuOption->Row + MenuOption->Skip - Temp2 > BottomRow + 1) { + if (gMenuOption.ForwardLink == NewPos || &gMenuOption == NewPos) { + Difference = -1; + } else { + Difference = 0; + } + } else { + Difference = MoveToNextStatement (FALSE, &NewPos, BottomRow + 1 - (MenuOption->Row + MenuOption->Skip - Temp2), FALSE); + } + if (Difference < 0) { + // + // Scroll to the first page. + // + if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) { + TopOfScreen = gMenuOption.ForwardLink; + Repaint = TRUE; + MenuOption = NULL; + } else { + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + } + NewPos = gMenuOption.ForwardLink; + MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, TRUE); + + SkipValue = 0; + // + // If we are at the end of the list and sitting on a Date/Time op, rewind to the head. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + + UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); + break; + } + + // + // Get next selected menu info. + // + AdjustDateAndTimePosition (FALSE, &NewPos); + NextMenuOption = MENU_OPTION_FROM_LINK (NewPos); + if (NextMenuOption->Row == 0) { + UpdateOptionSkipLines (NextMenuOption); + } + + // + // Calculate new highlight menu end row. + // + Temp = (MenuOption->Row + MenuOption->Skip - Temp2) + Difference + NextMenuOption->Skip - 1; + if (Temp > BottomRow) { + // + // Get the top screen menu info. + // + AdjustDateAndTimePosition (FALSE, &TopOfScreen); + SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); + + // + // Current Top screen menu occupy (SavedMenuOption->Skip - SkipValue) rows. + // Full shows the new selected menu need to skip (Temp - BottomRow - 1) rows. + // + if ((Temp - BottomRow) >= (SavedMenuOption->Skip - SkipValue)) { + // + // Skip the top op-code + // + TopOfScreen = TopOfScreen->ForwardLink; + DistanceValue = (Temp - BottomRow) - (SavedMenuOption->Skip - SkipValue); + + SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); + + // + // If we have a remainder, skip that many more op-codes until we drain the remainder + // Special case is the selected highlight menu has more than one form of menus. + // + while (DistanceValue >= SavedMenuOption->Skip && TopOfScreen != NewPos) { + // + // Since the Difference is greater than or equal to this op-code's skip value, skip it + // + DistanceValue = DistanceValue - (INTN) SavedMenuOption->Skip; + TopOfScreen = TopOfScreen->ForwardLink; + SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); + } + // + // Since we will act on this op-code in the next routine, and increment the + // SkipValue, set the skips to one less than what is required. + // + if (TopOfScreen != NewPos) { + SkipValue = DistanceValue; + } else { + SkipValue = 0; + } + } else { + // + // Since we will act on this op-code in the next routine, and increment the + // SkipValue, set the skips to one less than what is required. + // + SkipValue += Temp - BottomRow; + } + Repaint = TRUE; + } else if (!IsSelectable (NextMenuOption)) { + // + // Continue to go down until scroll to next page or the selectable option is found. + // + ScreenOperation = UiDown; + ControlFlag = CfScreenOperation; + break; + } + + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + + // + // Check whether new highlight menu is selectable, if not, keep highlight on the old one. + // + // BottomRow - TopRow + 1 means the total rows current forms supported. + // Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu + // and new top menu. New top menu will all shows in next form, but last highlight menu + // may only shows 1 line. + 1 at right part means at least need to keep 1 line for the + // last highlight menu. + // + if (!IsSelectable (NextMenuOption) && IsSelectable (MenuOption) && + (BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) { + NewPos = SavedListEntry; + } + + UpdateStatusBar (INPUT_ERROR, FALSE); + + // + // If we are at the end of the list and sitting on a Date/Time op, rewind to the head. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + + UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); + break; + + case CfUiNoOperation: + ControlFlag = CfRepaint; + break; + + case CfExit: + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + if (HelpString != NULL) { + FreePool (HelpString); + } + if (HelpHeaderString != NULL) { + FreePool (HelpHeaderString); + } + if (HelpBottomString != NULL) { + FreePool (HelpBottomString); + } + return EFI_SUCCESS; + + default: + break; + } + } +} + +/** + Free the UI Menu Option structure data. + + @param MenuOptionList Point to the menu option list which need to be free. + +**/ +VOID +FreeMenuOptionData( + LIST_ENTRY *MenuOptionList + ) +{ + LIST_ENTRY *Link; + UI_MENU_OPTION *Option; + + // + // Free menu option list + // + while (!IsListEmpty (MenuOptionList)) { + Link = GetFirstNode (MenuOptionList); + Option = MENU_OPTION_FROM_LINK (Link); + if (Option->Description != NULL){ + FreePool(Option->Description); + } + RemoveEntryList (&Option->Link); + FreePool (Option); + } +} + +/** + + Base on the browser status info to show an pop up message. + +**/ +VOID +BrowserStatusProcess ( + VOID + ) +{ + CHAR16 *ErrorInfo; + EFI_INPUT_KEY Key; + EFI_EVENT WaitList[2]; + EFI_EVENT RefreshIntervalEvent; + EFI_EVENT TimeOutEvent; + UINT8 TimeOut; + EFI_STATUS Status; + UINTN Index; + WARNING_IF_CONTEXT EventContext; + EFI_IFR_OP_HEADER *OpCodeBuf; + EFI_STRING_ID StringToken; + CHAR16 DiscardChange; + CHAR16 JumpToFormSet; + CHAR16 *PrintString; + + if (gFormData->BrowserStatus == BROWSER_SUCCESS) { + return; + } + + StringToken = 0; + TimeOutEvent = NULL; + RefreshIntervalEvent = NULL; + OpCodeBuf = NULL; + if (gFormData->HighLightedStatement != NULL) { + OpCodeBuf = gFormData->HighLightedStatement->OpCode; + } + + if (gFormData->BrowserStatus == (BROWSER_WARNING_IF)) { + ASSERT (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_WARNING_IF_OP); + + TimeOut = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->TimeOut; + StringToken = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->Warning; + } else { + TimeOut = 0; + if ((gFormData->BrowserStatus == (BROWSER_NO_SUBMIT_IF)) && + (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_NO_SUBMIT_IF_OP)) { + StringToken = ((EFI_IFR_NO_SUBMIT_IF *) OpCodeBuf)->Error; + } else if ((gFormData->BrowserStatus == (BROWSER_INCONSISTENT_IF)) && + (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_INCONSISTENT_IF_OP)) { + StringToken = ((EFI_IFR_INCONSISTENT_IF *) OpCodeBuf)->Error; + } + } + + if (StringToken != 0) { + ErrorInfo = GetToken (StringToken, gFormData->HiiHandle); + } else if (gFormData->ErrorString != NULL) { + // + // Only used to compatible with old setup browser. + // Not use this field in new browser core. + // + ErrorInfo = gFormData->ErrorString; + } else { + switch (gFormData->BrowserStatus) { + case BROWSER_SUBMIT_FAIL: + ErrorInfo = gSaveFailed; + break; + + case BROWSER_FORM_NOT_FOUND: + ErrorInfo = gFormNotFound; + break; + + case BROWSER_FORM_SUPPRESS: + ErrorInfo = gFormSuppress; + break; + + case BROWSER_PROTOCOL_NOT_FOUND: + ErrorInfo = gProtocolNotFound; + break; + + case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF: + ErrorInfo = gNoSubmitIfFailed; + break; + + case BROWSER_RECONNECT_FAIL: + ErrorInfo = gReconnectFail; + break; + + case BROWSER_RECONNECT_SAVE_CHANGES: + ErrorInfo = gReconnectConfirmChanges; + break; + + case BROWSER_RECONNECT_REQUIRED: + ErrorInfo = gReconnectRequired; + break; + + default: + ErrorInfo = gBrowserError; + break; + } + } + + switch (gFormData->BrowserStatus) { + case BROWSER_SUBMIT_FAIL: + case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF: + case BROWSER_RECONNECT_SAVE_CHANGES: + ASSERT (gUserInput != NULL); + if (gFormData->BrowserStatus == (BROWSER_SUBMIT_FAIL)) { + PrintString = gSaveProcess; + JumpToFormSet = gJumpToFormSet[0]; + DiscardChange = gDiscardChange[0]; + } else if (gFormData->BrowserStatus == (BROWSER_RECONNECT_SAVE_CHANGES)){ + PrintString = gChangesOpt; + JumpToFormSet = gConfirmOptYes[0]; + DiscardChange = gConfirmOptNo[0]; + } else { + PrintString = gSaveNoSubmitProcess; + JumpToFormSet = gCheckError[0]; + DiscardChange = gDiscardChange[0]; + } + + do { + CreateDialog (&Key, gEmptyString, ErrorInfo, PrintString, gEmptyString, NULL); + } while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (DiscardChange | UPPER_LOWER_CASE_OFFSET)) && + ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (JumpToFormSet | UPPER_LOWER_CASE_OFFSET))); + + if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (DiscardChange | UPPER_LOWER_CASE_OFFSET)) { + gUserInput->Action = BROWSER_ACTION_DISCARD; + } else { + gUserInput->Action = BROWSER_ACTION_GOTO; + } + break; + + default: + if (TimeOut == 0) { + do { + CreateDialog (&Key, gEmptyString, ErrorInfo, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } else { + Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK, EmptyEventProcess, NULL, &TimeOutEvent); + ASSERT_EFI_ERROR (Status); + + EventContext.SyncEvent = TimeOutEvent; + EventContext.TimeOut = &TimeOut; + EventContext.ErrorInfo = ErrorInfo; + + Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, RefreshTimeOutProcess, &EventContext, &RefreshIntervalEvent); + ASSERT_EFI_ERROR (Status); + + // + // Show the dialog first to avoid long time not reaction. + // + gBS->SignalEvent (RefreshIntervalEvent); + + Status = gBS->SetTimer (RefreshIntervalEvent, TimerPeriodic, ONE_SECOND); + ASSERT_EFI_ERROR (Status); + + while (TRUE) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (!EFI_ERROR (Status) && Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + break; + } + + if (Status != EFI_NOT_READY) { + continue; + } + + WaitList[0] = TimeOutEvent; + WaitList[1] = gST->ConIn->WaitForKey; + + Status = gBS->WaitForEvent (2, WaitList, &Index); + ASSERT_EFI_ERROR (Status); + + if (Index == 0) { + // + // Timeout occur, close the hoot time out event. + // + break; + } + } + + gBS->CloseEvent (TimeOutEvent); + gBS->CloseEvent (RefreshIntervalEvent); + } + break; + } + + if (StringToken != 0) { + FreePool (ErrorInfo); + } +} + +/** + Display one form, and return user input. + + @param FormData Form Data to be shown. + @param UserInputData User input data. + + @retval EFI_SUCCESS 1.Form Data is shown, and user input is got. + 2.Error info has show and return. + @retval EFI_INVALID_PARAMETER The input screen dimension is not valid + @retval EFI_NOT_FOUND New form data has some error. +**/ +EFI_STATUS +EFIAPI +FormDisplay ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT USER_INPUT *UserInputData + ) +{ + EFI_STATUS Status; + + ASSERT (FormData != NULL); + if (FormData == NULL) { + return EFI_INVALID_PARAMETER; + } + + gUserInput = UserInputData; + gFormData = FormData; + + // + // Process the status info first. + // + BrowserStatusProcess(); + if (gFormData->BrowserStatus != BROWSER_SUCCESS) { + // + // gFormData->BrowserStatus != BROWSER_SUCCESS, means only need to print the error info, return here. + // + return EFI_SUCCESS; + } + + Status = DisplayPageFrame (FormData, &gStatementDimensions); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Global Widths should be initialized before any MenuOption creation + // or the GetWidth() used in UiAddMenuOption() will return incorrect value. + // + // + // Left right + // |<-.->|<-.........->|<- .........->|<-...........->| + // Skip Prompt Option Help + // + gOptionBlockWidth = (CHAR16) ((gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 3) + 1; + gHelpBlockWidth = (CHAR16) (gOptionBlockWidth - 1 - LEFT_SKIPPED_COLUMNS); + gPromptBlockWidth = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gOptionBlockWidth - 1) - 1); + + ConvertStatementToMenu(); + + // + // Check whether layout is changed. + // + if (mIsFirstForm + || (gOldFormEntry.HiiHandle != FormData->HiiHandle) + || (!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid)) + || (gOldFormEntry.FormId != FormData->FormId)) { + mStatementLayoutIsChanged = TRUE; + } else { + mStatementLayoutIsChanged = FALSE; + } + + Status = UiDisplayMenu(FormData); + + // + // Backup last form info. + // + mIsFirstForm = FALSE; + gOldFormEntry.HiiHandle = FormData->HiiHandle; + CopyGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid); + gOldFormEntry.FormId = FormData->FormId; + + // + //Free the Ui menu option list. + // + FreeMenuOptionData(&gMenuOption); + + return Status; +} + +/** + Clear Screen to the initial state. +**/ +VOID +EFIAPI +DriverClearDisplayPage ( + VOID + ) +{ + ClearDisplayPage (); + mIsFirstForm = TRUE; +} + +/** + Set Buffer to Value for Size bytes. + + @param Buffer Memory to set. + @param Size Number of bytes to set + @param Value Value of the set operation. + +**/ +VOID +SetUnicodeMem ( + IN VOID *Buffer, + IN UINTN Size, + IN CHAR16 Value + ) +{ + CHAR16 *Ptr; + + Ptr = Buffer; + while ((Size--) != 0) { + *(Ptr++) = Value; + } +} + +/** + Initialize Setup Browser driver. + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCCESS The Setup Browser module is initialized correctly.. + @return Other value if failed to initialize the Setup Browser module. + +**/ +EFI_STATUS +EFIAPI +InitializeDisplayEngine ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY HotKey; + EFI_STRING NewString; + EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2; + + // + // Publish our HII data + // + gHiiHandle = HiiAddPackages ( + &gDisplayEngineGuid, + ImageHandle, + DisplayEngineStrings, + NULL + ); + ASSERT (gHiiHandle != NULL); + + // + // Install Form Display protocol + // + Status = gBS->InstallProtocolInterface ( + &mPrivateData.Handle, + &gEdkiiFormDisplayEngineProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivateData.FromDisplayProt + ); + ASSERT_EFI_ERROR (Status); + + // + // Install HII Popup Protocol. + // + Status = gBS->InstallProtocolInterface ( + &mPrivateData.Handle, + &gEfiHiiPopupProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivateData.HiiPopup + ); + ASSERT_EFI_ERROR (Status); + + InitializeDisplayStrings(); + + ZeroMem (&gHighligthMenuInfo, sizeof (gHighligthMenuInfo)); + ZeroMem (&gOldFormEntry, sizeof (gOldFormEntry)); + + // + // Use BrowserEx2 protocol to register HotKey. + // + Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **) &FormBrowserEx2); + if (!EFI_ERROR (Status)) { + // + // Register the default HotKey F9 and F10 again. + // + HotKey.UnicodeChar = CHAR_NULL; + HotKey.ScanCode = SCAN_F10; + NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL); + ASSERT (NewString != NULL); + FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString); + FreePool (NewString); + + HotKey.ScanCode = SCAN_F9; + NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL); + ASSERT (NewString != NULL); + FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString); + FreePool (NewString); + } + + return EFI_SUCCESS; +} + +/** + This is the default unload handle for display core drivers. + + @param[in] ImageHandle The drivers' driver image. + + @retval EFI_SUCCESS The image is unloaded. + @retval Others Failed to unload the image. + +**/ +EFI_STATUS +EFIAPI +UnloadDisplayEngine ( + IN EFI_HANDLE ImageHandle + ) +{ + HiiRemovePackages(gHiiHandle); + + FreeDisplayStrings (); + + if (gHighligthMenuInfo.HLTOpCode != NULL) { + FreePool (gHighligthMenuInfo.HLTOpCode); + } + + if (gHighligthMenuInfo.TOSOpCode != NULL) { + FreePool (gHighligthMenuInfo.TOSOpCode); + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h new file mode 100644 index 000000000..e6d3ae417 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h @@ -0,0 +1,741 @@ +/** @file + FormDiplay protocol to show Form + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __FORM_DISPLAY_H__ +#define __FORM_DISPLAY_H__ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +// +// This is the generated header file which includes whatever needs to be exported (strings + IFR) +// +extern UINT8 DisplayEngineStrings[]; +extern EFI_SCREEN_DESCRIPTOR gStatementDimensions; +extern USER_INPUT *gUserInput; +extern FORM_DISPLAY_ENGINE_FORM *gFormData; +extern EFI_HII_HANDLE gHiiHandle; +extern UINT16 gDirection; +extern LIST_ENTRY gMenuOption; +extern CHAR16 *gConfirmOptYes; +extern CHAR16 *gConfirmOptNo; +extern CHAR16 *gConfirmOptOk; +extern CHAR16 *gConfirmOptCancel; +extern CHAR16 *gYesOption; +extern CHAR16 *gNoOption; +extern CHAR16 *gOkOption; +extern CHAR16 *gCancelOption; +extern CHAR16 *gErrorPopup; +extern CHAR16 *gWarningPopup; +extern CHAR16 *gInfoPopup; + +// +// Browser Global Strings +// +extern CHAR16 *gSaveFailed; +extern CHAR16 *gPromptForData; +extern CHAR16 *gPromptForPassword; +extern CHAR16 *gPromptForNewPassword; +extern CHAR16 *gConfirmPassword; +extern CHAR16 *gConfirmError; +extern CHAR16 *gPassowordInvalid; +extern CHAR16 *gPressEnter; +extern CHAR16 *gEmptyString; +extern CHAR16 *gMiniString; +extern CHAR16 *gOptionMismatch; +extern CHAR16 *gFormSuppress; +extern CHAR16 *gProtocolNotFound; +extern CHAR16 *gPasswordUnsupported; + +extern CHAR16 gPromptBlockWidth; +extern CHAR16 gOptionBlockWidth; +extern CHAR16 gHelpBlockWidth; +extern CHAR16 *mUnknownString; +extern BOOLEAN gMisMatch; + +// +// Screen definitions +// + +#define LEFT_SKIPPED_COLUMNS 3 +#define SCROLL_ARROW_HEIGHT 1 +#define POPUP_PAD_SPACE_COUNT 5 +#define POPUP_FRAME_WIDTH 2 + +#define UPPER_LOWER_CASE_OFFSET 0x20 + +// +// Display definitions +// +#define LEFT_ONEOF_DELIMITER L'<' +#define RIGHT_ONEOF_DELIMITER L'>' + +#define LEFT_NUMERIC_DELIMITER L'[' +#define RIGHT_NUMERIC_DELIMITER L']' + +#define LEFT_CHECKBOX_DELIMITER L'[' +#define RIGHT_CHECKBOX_DELIMITER L']' + +#define CHECK_ON L'X' +#define CHECK_OFF L' ' + +#define TIME_SEPARATOR L':' +#define DATE_SEPARATOR L'/' + +#define SUBTITLE_INDENT 2 + +// +// This is the Input Error Message +// +#define INPUT_ERROR 1 + +// +// This is the NV RAM update required Message +// +#define NV_UPDATE_REQUIRED 2 +// +// Time definitions +// +#define ONE_SECOND 10000000 + +// +// It take 23 characters including the NULL to print a 64 bits number with "[" and "]". +// pow(2, 64) = [18446744073709551616] +// with extra '-' flat, set the width to 24. +// +#define MAX_NUMERIC_INPUT_WIDTH 24 + +#define EFI_HII_EXPRESSION_INCONSISTENT_IF 0 +#define EFI_HII_EXPRESSION_NO_SUBMIT_IF 1 +#define EFI_HII_EXPRESSION_GRAY_OUT_IF 2 +#define EFI_HII_EXPRESSION_SUPPRESS_IF 3 +#define EFI_HII_EXPRESSION_DISABLE_IF 4 + +// +// Character definitions +// +#define CHAR_SPACE 0x0020 + +#define FORM_DISPLAY_DRIVER_SIGNATURE SIGNATURE_32 ('F', 'D', 'D', 'V') +typedef struct { + UINT32 Signature; + + EFI_HANDLE Handle; + + // + // Produced protocol + // + EDKII_FORM_DISPLAY_ENGINE_PROTOCOL FromDisplayProt; + EFI_HII_POPUP_PROTOCOL HiiPopup; +} FORM_DISPLAY_DRIVER_PRIVATE_DATA; + + +typedef enum { + UiNoOperation, + UiSelect, + UiUp, + UiDown, + UiLeft, + UiRight, + UiReset, + UiPrevious, + UiPageUp, + UiPageDown, + UiHotKey, + UiMaxOperation +} UI_SCREEN_OPERATION; + +typedef enum { + CfInitialization, + CfCheckSelection, + CfRepaint, + CfRefreshHighLight, + CfUpdateHelpString, + CfPrepareToReadKey, + CfReadKey, + CfScreenOperation, + CfUiSelect, + CfUiReset, + CfUiLeft, + CfUiRight, + CfUiUp, + CfUiPageUp, + CfUiPageDown, + CfUiDown, + CfUiNoOperation, + CfExit, + CfUiHotKey, + CfMaxControlFlag +} UI_CONTROL_FLAG; + +typedef enum { + UIEventNone, + UIEventKey, + UIEventTimeOut, + UIEventDriver +} UI_EVENT_TYPE; + +typedef struct { + UINT16 ScanCode; + UI_SCREEN_OPERATION ScreenOperation; +} SCAN_CODE_TO_SCREEN_OPERATION; + +typedef struct { + UI_SCREEN_OPERATION ScreenOperation; + UI_CONTROL_FLAG ControlFlag; +} SCREEN_OPERATION_T0_CONTROL_FLAG; + +typedef struct { + EFI_HII_HANDLE HiiHandle; + UINT16 FormId; + + // + // Info for the highlight question. + // HLT means highlight + // + // If one statement has questionid, save questionid info to find the question. + // If one statement not has questionid info, save the opcode info to find the + // statement. If more than one statement has same opcode in one form(just like + // empty subtitle info may has more than one info one form), also use Index + // info to find the statement. + // + EFI_QUESTION_ID HLTQuestionId; + EFI_IFR_OP_HEADER *HLTOpCode; + UINTN HLTIndex; + UINTN HLTSequence; + + // + // Info for the top of screen question. + // TOS means Top Of Screen + // + EFI_QUESTION_ID TOSQuestionId; + EFI_IFR_OP_HEADER *TOSOpCode; + UINTN TOSIndex; + + UINT16 SkipValue; +} DISPLAY_HIGHLIGHT_MENU_INFO; + +typedef struct { + EFI_EVENT SyncEvent; + UINT8 *TimeOut; + CHAR16 *ErrorInfo; +} WARNING_IF_CONTEXT; + +#define UI_MENU_OPTION_SIGNATURE SIGNATURE_32 ('u', 'i', 'm', 'm') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + EFI_HII_HANDLE Handle; + FORM_DISPLAY_ENGINE_STATEMENT *ThisTag; + UINT16 EntryNumber; + + UINTN Row; + UINTN Col; + UINTN OptCol; + CHAR16 *Description; + UINTN Skip; // Number of lines + + // + // Display item sequence for date/time + // Date: Month/Day/Year + // Sequence: 0 1 2 + // + // Time: Hour : Minute : Second + // Sequence: 0 1 2 + // + // + UINTN Sequence; + + BOOLEAN GrayOut; + BOOLEAN ReadOnly; + + // + // Whether user could change value of this item + // + BOOLEAN IsQuestion; + BOOLEAN NestInStatement; +} UI_MENU_OPTION; + +#define MENU_OPTION_FROM_LINK(a) CR (a, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE) + +#define USER_SELECTABLE_OPTION_OK_WIDTH StrLen (gOkOption) +#define USER_SELECTABLE_OPTION_OK_CAL_WIDTH (StrLen (gOkOption) + StrLen (gCancelOption)) +#define USER_SELECTABLE_OPTION_YES_NO_WIDTH (StrLen (gYesOption) + StrLen (gNoOption)) +#define USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH (StrLen (gYesOption) + StrLen (gNoOption) + StrLen (gCancelOption)) + +#define USER_SELECTABLE_OPTION_SKIP_WIDTH 2 + +// +// +-------------------------------------------+ // POPUP_BORDER } +// | ERROR/WARNING/INFO | // POPUP_STYLE_STRING_HEIGHT } POPUP_HEADER_HEIGHT +// |-------------------------------------------| // POPUP_EMPTY_LINE_HEIGHT } +// | popup messages | +// | | // POPUP_EMPTY_LINE_HEIGHT } +// | user selectable options | // POPUP_USER_SELECTABLE_OPTION_HEIGHT } POPUP_FOOTER_HEIGHT +// +-------------------------------------------+ // POPUP_BORDER } +// +#define POPUP_BORDER 1 +#define POPUP_EMPTY_LINE_HEIGHT 1 +#define POPUP_STYLE_STRING_HEIGHT 1 +#define POPUP_USER_SELECTABLE_OPTION_HEIGHT 1 + +#define POPUP_HEADER_HEIGHT (POPUP_BORDER + POPUP_STYLE_STRING_HEIGHT + POPUP_EMPTY_LINE_HEIGHT) +#define POPUP_FOOTER_HEIGHT (POPUP_EMPTY_LINE_HEIGHT + POPUP_USER_SELECTABLE_OPTION_HEIGHT + POPUP_BORDER) + +#define USER_SELECTABLE_OPTION_SIGNATURE SIGNATURE_32 ('u', 's', 's', 'o') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + EFI_HII_POPUP_SELECTION OptionType; + CHAR16 *OptionString; + // + // Display item sequence for user select options + // Ok: Ok + // Sequence: 0 + // + // Ok/Cancel: Ok : Cancel + // Sequence: 0 1 + // + // Yes/No: Yes : No + // Sequence: 0 1 + // + // Yes/No/Cancel: Yes : No: Cancel + // Sequence: 0 1 2 + // + UINTN Sequence; + UINTN OptionRow; + UINTN OptionCol; + UINTN MaxSequence; + UINTN MinSequence; +} USER_SELECTABLE_OPTION; + +#define SELECTABLE_OPTION_FROM_LINK(a) CR (a, USER_SELECTABLE_OPTION, Link, USER_SELECTABLE_OPTION_SIGNATURE) + +/** + Print Question Value according to it's storage width and display attributes. + + @param Question The Question to be printed. + @param FormattedNumber Buffer for output string. + @param BufferSize The FormattedNumber buffer size in bytes. + + @retval EFI_SUCCESS Print success. + @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number. + +**/ +EFI_STATUS +PrintFormattedNumber ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN OUT CHAR16 *FormattedNumber, + IN UINTN BufferSize + ); + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ); + +/** + Return data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + + @retval Value The data to be returned + +**/ +UINT64 +GetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index + ); + +/** + Search an Option of a Question by its value. + + @param Question The Question + @param OptionValue Value for Option to be searched. + + @retval Pointer Pointer to the found Option. + @retval NULL Option not found. + +**/ +DISPLAY_QUESTION_OPTION * +ValueToOption ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN EFI_HII_VALUE *OptionValue + ); + +/** + Compare two Hii value. + + @param Value1 Expression value to compare on left-hand. + @param Value2 Expression value to compare on right-hand. + @param Result Return value after compare. + retval 0 Two operators equal. + return Positive value if Value1 is greater than Value2. + retval Negative value if Value1 is less than Value2. + @param HiiHandle Only required for string compare. + + @retval other Could not perform compare on two values. + @retval EFI_SUCCESS Compare the value success. + +**/ +EFI_STATUS +CompareHiiValue ( + IN EFI_HII_VALUE *Value1, + IN EFI_HII_VALUE *Value2, + OUT INTN *Result, + IN EFI_HII_HANDLE HiiHandle OPTIONAL + ); + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param ... A series of text strings that displayed in the pop-up. + +**/ +VOID +EFIAPI +CreateMultiStringPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + ... + ); + +/** + Will copy LineWidth amount of a string in the OutputString buffer and return the + number of CHAR16 characters that were copied into the OutputString buffer. + The output string format is: + Glyph Info + String info + '\0'. + + In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g. + + @param InputString String description for this option. + @param LineWidth Width of the desired string to extract in CHAR16 + characters + @param GlyphWidth The glyph width of the begin of the char in the string. + @param Index Where in InputString to start the copy process + @param OutputString Buffer to copy the string into + + @return Returns the number of CHAR16 characters that were copied into the OutputString + buffer, include extra glyph info and '\0' info. + +**/ +UINT16 +GetLineByWidth ( + IN CHAR16 *InputString, + IN UINT16 LineWidth, + IN OUT UINT16 *GlyphWidth, + IN OUT UINTN *Index, + OUT CHAR16 **OutputString + ); + + +/** + Get the string based on the StringId and HII Package List Handle. + + @param Token The String's ID. + @param HiiHandle The Hii handle for this string package. + + @return The output string. + +**/ +CHAR16 * +GetToken ( + IN EFI_STRING_ID Token, + IN EFI_HII_HANDLE HiiHandle + ); + +/** + Count the storage space of a Unicode string. + + This function handles the Unicode string with NARROW_CHAR + and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR + does not count in the resultant output. If a WIDE_CHAR is + hit, then 2 Unicode character will consume an output storage + space with size of CHAR16 till a NARROW_CHAR is hit. + + If String is NULL, then ASSERT (). + + @param String The input string to be counted. + + @return Storage space for the input string. + +**/ +UINTN +GetStringWidth ( + IN CHAR16 *String + ); + +/** + This routine reads a numeric value from the user input. + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If numerical input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetNumericInput ( + IN UI_MENU_OPTION *MenuOption + ); + +/** + Get string or password input from user. + + @param MenuOption Pointer to the current input menu. + @param Prompt The prompt string shown on popup window. + @param StringPtr Old user input and destination for use input string. + + @retval EFI_SUCCESS If string input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +ReadString ( + IN UI_MENU_OPTION *MenuOption, + IN CHAR16 *Prompt, + IN OUT CHAR16 *StringPtr + ); + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param Marker The variable argument list for the list of string to be printed. + +**/ +VOID +CreateSharedPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + IN VA_LIST Marker + ); + +/** + Wait for a key to be pressed by user. + + @param Key The key which is pressed by user. + + @retval EFI_SUCCESS The function always completed successfully. + +**/ +EFI_STATUS +WaitForKeyStroke ( + OUT EFI_INPUT_KEY *Key + ); + +/** + Get selection for OneOf and OrderedList (Left/Right will be ignored). + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If Option input is processed successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetSelectionInputPopUp ( + IN UI_MENU_OPTION *MenuOption + ); + +/** + Process the help string: Split StringPtr to several lines of strings stored in + FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth. + + @param StringPtr The entire help string. + @param FormattedString The oupput formatted string. + @param EachLineWidth The max string length of each line in the formatted string. + @param RowCount TRUE: if Question is selected. + +**/ +UINTN +ProcessHelpString ( + IN CHAR16 *StringPtr, + OUT CHAR16 **FormattedString, + OUT UINT16 *EachLineWidth, + IN UINTN RowCount + ); + +/** + Process a Question's Option (whether selected or un-selected). + + @param MenuOption The MenuOption for this Question. + @param Selected TRUE: if Question is selected. + @param OptionString Pointer of the Option String to be displayed. + @param SkipErrorValue Whether need to return when value without option for it. + + @retval EFI_SUCCESS Question Option process success. + @retval Other Question Option process fail. + +**/ +EFI_STATUS +ProcessOptions ( + IN UI_MENU_OPTION *MenuOption, + IN BOOLEAN Selected, + OUT CHAR16 **OptionString, + IN BOOLEAN SkipErrorValue + ); + +/** + Set Buffer to Value for Size bytes. + + @param Buffer Memory to set. + @param Size Number of bytes to set + @param Value Value of the set operation. + +**/ +VOID +SetUnicodeMem ( + IN VOID *Buffer, + IN UINTN Size, + IN CHAR16 Value + ); + +/** + Display one form, and return user input. + + @param FormData Form Data to be shown. + @param UserInputData User input data. + + @retval EFI_SUCCESS Form Data is shown, and user input is got. +**/ +EFI_STATUS +EFIAPI +FormDisplay ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT USER_INPUT *UserInputData + ); + +/** + Clear Screen to the initial state. +**/ +VOID +EFIAPI +DriverClearDisplayPage ( + VOID + ); + +/** + Exit Display and Clear Screen to the original state. + +**/ +VOID +EFIAPI +ExitDisplay ( + VOID + ); + +/** + Process nothing. + + @param Event The Event need to be process + @param Context The context of the event. + +**/ +VOID +EFIAPI +EmptyEventProcess ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Process for the refresh interval statement. + + @param Event The Event need to be process + @param Context The context of the event. + +**/ +VOID +EFIAPI +RefreshTimeOutProcess ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Record the highlight menu and top of screen menu info. + + @param Highlight The menu opton which is highlight. + @param TopOfScreen The menu opton which is at the top of the form. + @param SkipValue The skip line info for the top of screen menu. + +**/ +VOID +UpdateHighlightMenuInfo ( + IN LIST_ENTRY *Highlight, + IN LIST_ENTRY *TopOfScreen, + IN UINTN SkipValue + ); + +/** + Displays a popup window. + + @param This A pointer to the EFI_HII_POPUP_PROTOCOL instance. + @param PopupStyle Popup style to use. + @param PopupType Type of the popup to display. + @param HiiHandle HII handle of the string pack containing Message + @param Message A message to display in the popup box. + @param UserSelection User selection. + + @retval EFI_SUCCESS The popup box was successfully displayed. + @retval EFI_INVALID_PARAMETER HiiHandle and Message do not define a valid HII string. + @retval EFI_INVALID_PARAMETER PopupType is not one of the values defined by this specification. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to display the popup box. + +**/ +EFI_STATUS +EFIAPI +CreatePopup ( + IN EFI_HII_POPUP_PROTOCOL *This, + IN EFI_HII_POPUP_STYLE PopupStyle, + IN EFI_HII_POPUP_TYPE PopupType, + IN EFI_HII_HANDLE HiiHandle, + IN EFI_STRING_ID Message, + OUT EFI_HII_POPUP_SELECTION *UserSelection OPTIONAL + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplayStr.uni b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplayStr.uni new file mode 100644 index 000000000..5e3c35a2c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplayStr.uni @@ -0,0 +1,134 @@ +// *++ +// +// Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// Module Name: +// +// SetupBrowserStr.uni +// +// Abstract: +// +// String definitions for Browser. +// +// --*/ + + +/=# + +#langdef en-US "English" +#langdef fr-FR "Français" + +#string UNKNOWN_STRING #language en-US "!" + #language fr-FR "!" +#string STATUS_BROWSER_ERROR #language en-US "Browser met some error, return!" + #language fr-FR "Browser met some error, return!" +#string STATUS_BROWSER_FORM_NOT_FOUND #language en-US "Form not found, return!" + #language fr-FR "Form not found, return!" +#string STATUS_BROWSER_NO_SUBMIT_IF #language en-US "Not allowed to submit, return!" + #language fr-FR "Not allowed to submit, return!" +#string FUNCTION_NINE_STRING #language en-US "F9=Reset to Defaults" + #language fr-FR "F9=Reset à Défauts" +#string FUNCTION_TEN_STRING #language en-US "F10=Save" + #language fr-FR "F10=Économiser" +#string SAVE_FAILED #language en-US "Failed to Save" + #language fr-FR "Échouer à économiser" +#string NO_SUBMIT_IF_CHECK_FAILED #language en-US "NO_SUBMIT_IF check fail." + #language fr-FR "NO_SUBMIT_IF check fail." +#string ADJUST_HELP_PAGE_DOWN #language en-US "More (D/d)" + #language fr-FR "More (D/d)" +#string ADJUST_HELP_PAGE_UP #language en-US "More (U/u)" + #language fr-FR "More (U/u)" +#string PROMPT_FOR_PASSWORD #language en-US "Please type in your password" + #language fr-FR "S'il vous plaît tape votre mot de passe" +#string PROMPT_FOR_NEW_PASSWORD #language en-US "Please type in your new password" + #language fr-FR "S'il vous plaît tape votre nouveau mot de passe" +#string CONFIRM_PASSWORD #language en-US "Please confirm your new password" + #language fr-FR "S'il vous plaît confirmer votre nouveau mot de passe" +#string CONFIRM_ERROR #language en-US "Passwords are not the same" + #language fr-FR "Les mots de passe ne sont pas pareils" +#string PASSWORD_INVALID #language en-US "Incorrect password" + #language fr-FR "Mauvais mot de passe" +#string PRESS_ENTER #language en-US "Press ENTER to continue" + #language fr-FR "La presse ENTRE continuer" +#string PROMPT_FOR_DATA #language en-US "Please type in your data" + #language fr-FR "S'il vous plaît tape vos données" +#string EMPTY_STRING #language en-US "" + #language fr-FR "" +#string MINI_STRING #language en-US "Please enter enough characters" + #language fr-FR "Veuillez écrire assez de caractères" +#string OPTION_MISMATCH #language en-US "Question value mismatch with Option value!" + #language fr-FR "Question valeur décalage avec l'option valeur!" +#string FORM_SUPPRESSED #language en-US "Form is suppressed. Nothing is displayed." + #language fr-FR "Form is suppressed. Nothing is displayed." +#string PROTOCOL_NOT_FOUND #language en-US "Convert string to device path fail. Can't goto the destination." + #language fr-FR "Convert string to device path fail. Can't goto the destination." +#string DISCARD_OR_JUMP #language en-US "Press D(d) to discard changes for this form, Press G(g) to go to this form" + #language fr-FR "Press D(d) to discard changes for this form, Press G(g) to go to this form" +#string DISCARD_OR_JUMP_DISCARD #language en-US "D (d)" + #language fr-FR "D (d)" +#string DISCARD_OR_JUMP_JUMP #language en-US "G (g)" + #language fr-FR "G (g)" +#string DISCARD_OR_CHECK #language en-US "Press D(d) to discard changes for this form, Press C(c) to check the error" + #language fr-FR "Press D(d) to discard changes for this form, Press C(c) to check the error" +#string DISCARD_OR_CHECK_CHECK #language en-US "C (c)" + #language fr-FR "C (c)" +#string CONFIRM_DISCARD_MESSAGE #language en-US "Discard configuration changes" + #language fr-FR "Discard configuration changes" +#string CONFIRM_DEFAULT_MESSAGE #language en-US "Load default configuration" + #language fr-FR "Load default configuration" +#string CONFIRM_DEFAULT_MESSAGE_2ND #language en-US "load default configuration" + #language fr-FR "load default configuration" +#string CONFIRM_SUBMIT_MESSAGE #language en-US "Save configuration changes" + #language fr-FR "Save configuration changes" +#string CONFIRM_SUBMIT_MESSAGE_2ND #language en-US "save configuration changes" + #language fr-FR "save configuration changes" +#string CONFIRM_RESET_MESSAGE #language en-US "Reset" + #language fr-FR "Reset" +#string CONFIRM_RESET_MESSAGE_2ND #language en-US "reset" + #language fr-FR "reset" +#string CONFIRM_EXIT_MESSAGE #language en-US "Exit" + #language fr-FR "Exit" +#string CONFIRM_EXIT_MESSAGE_2ND #language en-US "exit" + #language fr-FR "exit" +#string CONFIRM_OPTION #language en-US "Press 'Y' to confirm, 'N'/'ESC' to ignore." + #language fr-FR "Press 'Y' to confirm, 'N'/'ESC' to ignore." +#string CONFIRM_OPTION_YES #language en-US "Y (y)" + #language fr-FR "Y (y)" +#string CONFIRM_OPTION_NO #language en-US "N (n)" + #language fr-FR "N (n)" +#string CONFIRM_OPTION_OK #language en-US "O (o)" + #language fr-FR "O (o)" +#string CONFIRM_OPTION_CANCEL #language en-US "C (c)" + #language fr-FR "C (c)" +#string CONFIRM_OPTION_CONNECT #language en-US " and " + #language fr-FR " and " +#string CONFIRM_OPTION_END #language en-US "?" + #language fr-FR "?" +#string RECONNECT_FAILED #language en-US "Reconnect the controller failed!" + #language fr-FR "Reconnect the controller failed!" +#string RECONNECT_CONFIRM_CHANGES #language en-US "Reconnect is required, confirm the changes then exit and reconnect" + #language fr-FR "Reconnect is required, confirm the changes then exit and reconnect" +#string RECONNECT_CHANGES_OPTIONS #language en-US "Press 'Y' to save, 'N' to discard" + #language fr-FR "Press 'Y' to save, 'N' to discard" +#string RECONNECT_REQUIRED #language en-US "Reconnect is required, exit and reconnect" + #language fr-FR "Reconnect is required, exit and reconnect" +#string GET_TIME_FAIL #language en-US " Get date/time fail, display ??." + #language fr-FR " Get data/time fail, display ??." +#string PASSWORD_NOT_SUPPORTED #language en-US "Unsupported! Because no interactieve flag or no ConfigAccess protocol!" + #language fr-FR "Unsupported! Because no interactieve flag or no ConfigAccess protocol!" +#string OK_SELECTABLE_OPTION #language en-US "[ Ok ]" + #language fr-FR "[ Ok ]" +#string CANCEL_SELECTABLE_OPTION #language en-US "[Cancel]" + #language fr-FR "[Cancel]" +#string YES_SELECTABLE_OPTION #language en-US "[ Yes ]" + #language fr-FR "[ Yes ]" +#string NO_SELECTABLE_OPTION #language en-US "[ No ]" + #language fr-FR "[ No ]" +#string ERROR_POPUP_STRING #language en-US "ERROR" + #language fr-FR "ERROR" +#string WARNING_POPUP_STRING #language en-US "WARNING" + #language fr-FR "WARNING" +#string INFO_POPUP_STRING #language en-US "INFO" + #language fr-FR "INFO" + diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c new file mode 100644 index 000000000..722c56aa2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c @@ -0,0 +1,1664 @@ +/** @file +Implementation for handling user input from the User Interfaces. + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FormDisplay.h" + +/** + Get maximum and minimum info from this opcode. + + @param OpCode Pointer to the current input opcode. + @param Minimum The minimum size info for this opcode. + @param Maximum The maximum size info for this opcode. + +**/ +VOID +GetFieldFromOp ( + IN EFI_IFR_OP_HEADER *OpCode, + OUT UINTN *Minimum, + OUT UINTN *Maximum + ) +{ + EFI_IFR_STRING *StringOp; + EFI_IFR_PASSWORD *PasswordOp; + if (OpCode->OpCode == EFI_IFR_STRING_OP) { + StringOp = (EFI_IFR_STRING *) OpCode; + *Minimum = StringOp->MinSize; + *Maximum = StringOp->MaxSize; + } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) { + PasswordOp = (EFI_IFR_PASSWORD *) OpCode; + *Minimum = PasswordOp->MinSize; + *Maximum = PasswordOp->MaxSize; + } else { + *Minimum = 0; + *Maximum = 0; + } +} + +/** + Get string or password input from user. + + @param MenuOption Pointer to the current input menu. + @param Prompt The prompt string shown on popup window. + @param StringPtr Old user input and destination for use input string. + + @retval EFI_SUCCESS If string input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +ReadString ( + IN UI_MENU_OPTION *MenuOption, + IN CHAR16 *Prompt, + IN OUT CHAR16 *StringPtr + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + CHAR16 NullCharacter; + UINTN ScreenSize; + CHAR16 Space[2]; + CHAR16 KeyPad[2]; + CHAR16 *TempString; + CHAR16 *BufferedString; + UINTN Index; + UINTN Index2; + UINTN Count; + UINTN Start; + UINTN Top; + UINTN DimensionsWidth; + UINTN DimensionsHeight; + UINTN CurrentCursor; + BOOLEAN CursorVisible; + UINTN Minimum; + UINTN Maximum; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + BOOLEAN IsPassword; + UINTN MaxLen; + + DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; + DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow; + + NullCharacter = CHAR_NULL; + ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16); + Space[0] = L' '; + Space[1] = CHAR_NULL; + + Question = MenuOption->ThisTag; + GetFieldFromOp(Question->OpCode, &Minimum, &Maximum); + + if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) { + IsPassword = TRUE; + } else { + IsPassword = FALSE; + } + + MaxLen = Maximum + 1; + TempString = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + ASSERT (TempString); + + if (ScreenSize < (Maximum + 1)) { + ScreenSize = Maximum + 1; + } + + if ((ScreenSize + 2) > DimensionsWidth) { + ScreenSize = DimensionsWidth - 2; + } + + BufferedString = AllocateZeroPool (ScreenSize * 2); + ASSERT (BufferedString); + + Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1; + Top = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1; + + // + // Display prompt for string + // + // CreateDialog (NULL, "", Prompt, Space, "", NULL); + CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); + + CursorVisible = gST->ConOut->Mode->CursorVisible; + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + CurrentCursor = GetStringWidth (StringPtr) / 2 - 1; + if (CurrentCursor != 0) { + // + // Show the string which has beed saved before. + // + SetUnicodeMem (BufferedString, ScreenSize - 1, L' '); + PrintStringAt (Start + 1, Top + 3, BufferedString); + + if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) { + Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2; + } else { + Index = 0; + } + + if (IsPassword) { + gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3); + } + + for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) { + BufferedString[Count] = StringPtr[Index]; + + if (IsPassword) { + PrintCharAt ((UINTN)-1, (UINTN)-1, L'*'); + } + } + + if (!IsPassword) { + PrintStringAt (Start + 1, Top + 3, BufferedString); + } + + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3); + } + + do { + Status = WaitForKeyStroke (&Key); + ASSERT_EFI_ERROR (Status); + + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); + switch (Key.UnicodeChar) { + case CHAR_NULL: + switch (Key.ScanCode) { + case SCAN_LEFT: + if (CurrentCursor > 0) { + CurrentCursor--; + } + break; + + case SCAN_RIGHT: + if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) { + CurrentCursor++; + } + break; + + case SCAN_ESC: + FreePool (TempString); + FreePool (BufferedString); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); + return EFI_DEVICE_ERROR; + + case SCAN_DELETE: + for (Index = CurrentCursor; StringPtr[Index] != CHAR_NULL; Index++) { + StringPtr[Index] = StringPtr[Index + 1]; + PrintCharAt (Start + Index + 1, Top + 3, IsPassword && StringPtr[Index] != CHAR_NULL? L'*' : StringPtr[Index]); + } + break; + + default: + break; + } + + break; + + case CHAR_CARRIAGE_RETURN: + if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) { + + FreePool (TempString); + FreePool (BufferedString); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); + return EFI_SUCCESS; + } else { + // + // Simply create a popup to tell the user that they had typed in too few characters. + // To save code space, we can then treat this as an error and return back to the menu. + // + do { + CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + FreePool (TempString); + FreePool (BufferedString); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); + return EFI_DEVICE_ERROR; + } + + + case CHAR_BACKSPACE: + if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) { + for (Index = 0; Index < CurrentCursor - 1; Index++) { + TempString[Index] = StringPtr[Index]; + } + Count = GetStringWidth (StringPtr) / 2 - 1; + if (Count >= CurrentCursor) { + for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) { + TempString[Index] = StringPtr[Index2]; + } + TempString[Index] = CHAR_NULL; + } + // + // Effectively truncate string by 1 character + // + StrCpyS (StringPtr, MaxLen, TempString); + CurrentCursor --; + } + + default: + // + // If it is the beginning of the string, don't worry about checking maximum limits + // + if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) { + StrnCpyS (StringPtr, MaxLen, &Key.UnicodeChar, 1); + CurrentCursor++; + } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) { + KeyPad[0] = Key.UnicodeChar; + KeyPad[1] = CHAR_NULL; + Count = GetStringWidth (StringPtr) / 2 - 1; + if (CurrentCursor < Count) { + for (Index = 0; Index < CurrentCursor; Index++) { + TempString[Index] = StringPtr[Index]; + } + TempString[Index] = CHAR_NULL; + StrCatS (TempString, MaxLen, KeyPad); + StrCatS (TempString, MaxLen, StringPtr + CurrentCursor); + StrCpyS (StringPtr, MaxLen, TempString); + } else { + StrCatS (StringPtr, MaxLen, KeyPad); + } + CurrentCursor++; + } + + // + // If the width of the input string is now larger than the screen, we nee to + // adjust the index to start printing portions of the string + // + SetUnicodeMem (BufferedString, ScreenSize - 1, L' '); + PrintStringAt (Start + 1, Top + 3, BufferedString); + + if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) { + Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2; + } else { + Index = 0; + } + + if (IsPassword) { + gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3); + } + + for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) { + BufferedString[Count] = StringPtr[Index]; + + if (IsPassword) { + PrintCharAt ((UINTN)-1, (UINTN)-1, L'*'); + } + } + + if (!IsPassword) { + PrintStringAt (Start + 1, Top + 3, BufferedString); + } + break; + } + + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3); + } while (TRUE); + +} + +/** + Adjust the value to the correct one. Rules follow the sample: + like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01 + Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28 + + @param QuestionValue Pointer to current question. + @param Sequence The sequence of the field in the question. +**/ +VOID +AdjustQuestionValue ( + IN EFI_HII_VALUE *QuestionValue, + IN UINT8 Sequence + ) +{ + UINT8 Month; + UINT16 Year; + UINT8 Maximum; + UINT8 Minimum; + + Month = QuestionValue->Value.date.Month; + Year = QuestionValue->Value.date.Year; + Minimum = 1; + + switch (Month) { + case 2: + if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) { + Maximum = 29; + } else { + Maximum = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + Maximum = 30; + break; + default: + Maximum = 31; + break; + } + + // + // Change the month area. + // + if (Sequence == 0) { + if (QuestionValue->Value.date.Day > Maximum) { + QuestionValue->Value.date.Day = Maximum; + } + } + + // + // Change the Year area. + // + if (Sequence == 2) { + if (QuestionValue->Value.date.Day > Maximum) { + QuestionValue->Value.date.Day = Minimum; + } + } +} + +/** + Get field info from numeric opcode. + + @param OpCode Pointer to the current input opcode. + @param IntInput Whether question shows with EFI_IFR_DISPLAY_INT_DEC type. + @param QuestionValue Input question value, with EFI_HII_VALUE type. + @param Value Return question value, always return UINT64 type. + @param Minimum The minimum size info for this opcode. + @param Maximum The maximum size info for this opcode. + @param Step The step size info for this opcode. + @param StorageWidth The storage width info for this opcode. + +**/ +VOID +GetValueFromNum ( + IN EFI_IFR_OP_HEADER *OpCode, + IN BOOLEAN IntInput, + IN EFI_HII_VALUE *QuestionValue, + OUT UINT64 *Value, + OUT UINT64 *Minimum, + OUT UINT64 *Maximum, + OUT UINT64 *Step, + OUT UINT16 *StorageWidth +) +{ + EFI_IFR_NUMERIC *NumericOp; + + NumericOp = (EFI_IFR_NUMERIC *) OpCode; + + switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + if (IntInput) { + *Minimum = (INT64) (INT8) NumericOp->data.u8.MinValue; + *Maximum = (INT64) (INT8) NumericOp->data.u8.MaxValue; + *Value = (INT64) (INT8) QuestionValue->Value.u8; + } else { + *Minimum = NumericOp->data.u8.MinValue; + *Maximum = NumericOp->data.u8.MaxValue; + *Value = QuestionValue->Value.u8; + } + *Step = NumericOp->data.u8.Step; + *StorageWidth = (UINT16) sizeof (UINT8); + break; + + case EFI_IFR_NUMERIC_SIZE_2: + if (IntInput) { + *Minimum = (INT64) (INT16) NumericOp->data.u16.MinValue; + *Maximum = (INT64) (INT16) NumericOp->data.u16.MaxValue; + *Value = (INT64) (INT16) QuestionValue->Value.u16; + } else { + *Minimum = NumericOp->data.u16.MinValue; + *Maximum = NumericOp->data.u16.MaxValue; + *Value = QuestionValue->Value.u16; + } + *Step = NumericOp->data.u16.Step; + *StorageWidth = (UINT16) sizeof (UINT16); + break; + + case EFI_IFR_NUMERIC_SIZE_4: + if (IntInput) { + *Minimum = (INT64) (INT32) NumericOp->data.u32.MinValue; + *Maximum = (INT64) (INT32) NumericOp->data.u32.MaxValue; + *Value = (INT64) (INT32) QuestionValue->Value.u32; + } else { + *Minimum = NumericOp->data.u32.MinValue; + *Maximum = NumericOp->data.u32.MaxValue; + *Value = QuestionValue->Value.u32; + } + *Step = NumericOp->data.u32.Step; + *StorageWidth = (UINT16) sizeof (UINT32); + break; + + case EFI_IFR_NUMERIC_SIZE_8: + if (IntInput) { + *Minimum = (INT64) NumericOp->data.u64.MinValue; + *Maximum = (INT64) NumericOp->data.u64.MaxValue; + *Value = (INT64) QuestionValue->Value.u64; + } else { + *Minimum = NumericOp->data.u64.MinValue; + *Maximum = NumericOp->data.u64.MaxValue; + *Value = QuestionValue->Value.u64; + } + *Step = NumericOp->data.u64.Step; + *StorageWidth = (UINT16) sizeof (UINT64); + break; + + default: + break; + } + + if (*Maximum == 0) { + *Maximum = (UINT64) -1; + } +} + +/** + This routine reads a numeric value from the user input. + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If numerical input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetNumericInput ( + IN UI_MENU_OPTION *MenuOption + ) +{ + UINTN Column; + UINTN Row; + CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH]; + CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1]; + UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3]; + UINTN Count; + UINTN Loop; + BOOLEAN ManualInput; + BOOLEAN HexInput; + BOOLEAN IntInput; + BOOLEAN Negative; + BOOLEAN ValidateFail; + BOOLEAN DateOrTime; + UINTN InputWidth; + UINT64 EditValue; + UINT64 Step; + UINT64 Minimum; + UINT64 Maximum; + UINTN EraseLen; + UINT8 Digital; + EFI_INPUT_KEY Key; + EFI_HII_VALUE *QuestionValue; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + EFI_IFR_NUMERIC *NumericOp; + UINT16 StorageWidth; + + Column = MenuOption->OptCol; + Row = MenuOption->Row; + PreviousNumber[0] = 0; + Count = 0; + InputWidth = 0; + Digital = 0; + StorageWidth = 0; + Minimum = 0; + Maximum = 0; + NumericOp = NULL; + IntInput = FALSE; + HexInput = FALSE; + Negative = FALSE; + ValidateFail = FALSE; + + Question = MenuOption->ThisTag; + QuestionValue = &Question->CurrentValue; + ZeroMem (InputText, MAX_NUMERIC_INPUT_WIDTH * sizeof (CHAR16)); + + // + // Only two case, user can enter to this function: Enter and +/- case. + // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT + // + ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE); + + if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) { + DateOrTime = TRUE; + } else { + DateOrTime = FALSE; + } + + // + // Prepare Value to be edit + // + EraseLen = 0; + EditValue = 0; + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + Step = 1; + Minimum = 1; + + switch (MenuOption->Sequence) { + case 0: + Maximum = 12; + EraseLen = 4; + EditValue = QuestionValue->Value.date.Month; + break; + + case 1: + switch (QuestionValue->Value.date.Month) { + case 2: + if ((QuestionValue->Value.date.Year % 4) == 0 && + ((QuestionValue->Value.date.Year % 100) != 0 || + (QuestionValue->Value.date.Year % 400) == 0)) { + Maximum = 29; + } else { + Maximum = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + Maximum = 30; + break; + default: + Maximum = 31; + break; + } + + EraseLen = 3; + EditValue = QuestionValue->Value.date.Day; + break; + + case 2: + Maximum = 0xffff; + EraseLen = 5; + EditValue = QuestionValue->Value.date.Year; + break; + + default: + break; + } + } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + Step = 1; + Minimum = 0; + + switch (MenuOption->Sequence) { + case 0: + Maximum = 23; + EraseLen = 4; + EditValue = QuestionValue->Value.time.Hour; + break; + + case 1: + Maximum = 59; + EraseLen = 3; + EditValue = QuestionValue->Value.time.Minute; + break; + + case 2: + Maximum = 59; + EraseLen = 3; + EditValue = QuestionValue->Value.time.Second; + break; + + default: + break; + } + } else { + ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP); + NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode; + GetValueFromNum(Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth); + EraseLen = gOptionBlockWidth; + } + + if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) { + if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX){ + HexInput = TRUE; + } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0){ + // + // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number. + // + IntInput = TRUE; + } + } + + // + // Enter from "Enter" input, clear the old word showing. + // + if (ManualInput) { + if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) { + if (HexInput) { + InputWidth = StorageWidth * 2; + } else { + switch (StorageWidth) { + case 1: + InputWidth = 3; + break; + + case 2: + InputWidth = 5; + break; + + case 4: + InputWidth = 10; + break; + + case 8: + InputWidth = 20; + break; + + default: + InputWidth = 0; + break; + } + + if (IntInput) { + // + // Support an extra '-' for negative number. + // + InputWidth += 1; + } + } + + InputText[0] = LEFT_NUMERIC_DELIMITER; + SetUnicodeMem (InputText + 1, InputWidth, L' '); + ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH); + InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; + InputText[InputWidth + 2] = L'\0'; + + PrintStringAt (Column, Row, InputText); + Column++; + } + + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + if (MenuOption->Sequence == 2) { + InputWidth = 4; + } else { + InputWidth = 2; + } + + if (MenuOption->Sequence == 0) { + InputText[0] = LEFT_NUMERIC_DELIMITER; + SetUnicodeMem (InputText + 1, InputWidth, L' '); + InputText[InputWidth + 1] = DATE_SEPARATOR; + InputText[InputWidth + 2] = L'\0'; + } else if (MenuOption->Sequence == 1){ + SetUnicodeMem (InputText, InputWidth, L' '); + InputText[InputWidth] = DATE_SEPARATOR; + InputText[InputWidth + 1] = L'\0'; + } else { + SetUnicodeMem (InputText, InputWidth, L' '); + InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER; + InputText[InputWidth + 1] = L'\0'; + } + + PrintStringAt (Column, Row, InputText); + if (MenuOption->Sequence == 0) { + Column++; + } + } + + if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + InputWidth = 2; + + if (MenuOption->Sequence == 0) { + InputText[0] = LEFT_NUMERIC_DELIMITER; + SetUnicodeMem (InputText + 1, InputWidth, L' '); + InputText[InputWidth + 1] = TIME_SEPARATOR; + InputText[InputWidth + 2] = L'\0'; + } else if (MenuOption->Sequence == 1){ + SetUnicodeMem (InputText, InputWidth, L' '); + InputText[InputWidth] = TIME_SEPARATOR; + InputText[InputWidth + 1] = L'\0'; + } else { + SetUnicodeMem (InputText, InputWidth, L' '); + InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER; + InputText[InputWidth + 1] = L'\0'; + } + + PrintStringAt (Column, Row, InputText); + if (MenuOption->Sequence == 0) { + Column++; + } + } + } + + // + // First time we enter this handler, we need to check to see if + // we were passed an increment or decrement directive + // + do { + Key.UnicodeChar = CHAR_NULL; + if (gDirection != 0) { + Key.ScanCode = gDirection; + gDirection = 0; + goto TheKey2; + } + + WaitForKeyStroke (&Key); + +TheKey2: + switch (Key.UnicodeChar) { + + case '+': + case '-': + if (ManualInput && IntInput) { + // + // In Manual input mode, check whether input the negative flag. + // + if (Key.UnicodeChar == '-') { + if (Negative) { + break; + } + Negative = TRUE; + PrintCharAt (Column++, Row, Key.UnicodeChar); + } + } else { + if (Key.UnicodeChar == '+') { + Key.ScanCode = SCAN_RIGHT; + } else { + Key.ScanCode = SCAN_LEFT; + } + Key.UnicodeChar = CHAR_NULL; + goto TheKey2; + } + break; + + case CHAR_NULL: + switch (Key.ScanCode) { + case SCAN_LEFT: + case SCAN_RIGHT: + if (DateOrTime && !ManualInput) { + // + // By setting this value, we will return back to the caller. + // We need to do this since an auto-refresh will destroy the adjustment + // based on what the real-time-clock is showing. So we always commit + // upon changing the value. + // + gDirection = SCAN_DOWN; + } + + if ((Step != 0) && !ManualInput) { + if (Key.ScanCode == SCAN_LEFT) { + if (IntInput) { + if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) { + EditValue = EditValue - Step; + } else if ((INT64) EditValue > (INT64) Minimum){ + EditValue = Minimum; + } else { + EditValue = Maximum; + } + } else { + if (EditValue >= Minimum + Step) { + EditValue = EditValue - Step; + } else if (EditValue > Minimum){ + EditValue = Minimum; + } else { + EditValue = Maximum; + } + } + } else if (Key.ScanCode == SCAN_RIGHT) { + if (IntInput) { + if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) { + EditValue = EditValue + Step; + } else if ((INT64) EditValue < (INT64) Maximum) { + EditValue = Maximum; + } else { + EditValue = Minimum; + } + } else { + if (EditValue + Step <= Maximum) { + EditValue = EditValue + Step; + } else if (EditValue < Maximum) { + EditValue = Maximum; + } else { + EditValue = Minimum; + } + } + } + + ZeroMem (FormattedNumber, 21 * sizeof (CHAR16)); + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + if (MenuOption->Sequence == 2) { + // + // Year + // + UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue); + } else { + // + // Month/Day + // + UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue); + } + + if (MenuOption->Sequence == 0) { + ASSERT (EraseLen >= 2); + FormattedNumber[EraseLen - 2] = DATE_SEPARATOR; + } else if (MenuOption->Sequence == 1) { + ASSERT (EraseLen >= 1); + FormattedNumber[EraseLen - 1] = DATE_SEPARATOR; + } + } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue); + + if (MenuOption->Sequence == 0) { + ASSERT (EraseLen >= 2); + FormattedNumber[EraseLen - 2] = TIME_SEPARATOR; + } else if (MenuOption->Sequence == 1) { + ASSERT (EraseLen >= 1); + FormattedNumber[EraseLen - 1] = TIME_SEPARATOR; + } + } else { + QuestionValue->Value.u64 = EditValue; + PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16)); + } + + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + for (Loop = 0; Loop < EraseLen; Loop++) { + PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" "); + } + gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); + + if (MenuOption->Sequence == 0) { + PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER); + Column = MenuOption->OptCol + 1; + } + + PrintStringAt (Column, Row, FormattedNumber); + + if (!DateOrTime || MenuOption->Sequence == 2) { + PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER); + } + } + + goto EnterCarriageReturn; + + case SCAN_UP: + case SCAN_DOWN: + goto EnterCarriageReturn; + + case SCAN_ESC: + return EFI_DEVICE_ERROR; + + default: + break; + } + + break; + +EnterCarriageReturn: + + case CHAR_CARRIAGE_RETURN: + // + // Validate input value with Minimum value. + // + ValidateFail = FALSE; + if (IntInput) { + // + // After user input Enter, need to check whether the input value. + // If input a negative value, should compare with maximum value. + // else compare with the minimum value. + // + if (Negative) { + ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE; + } else { + ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE; + } + + if (ValidateFail) { + UpdateStatusBar (INPUT_ERROR, TRUE); + break; + } + } else if (EditValue < Minimum) { + UpdateStatusBar (INPUT_ERROR, TRUE); + break; + } + + UpdateStatusBar (INPUT_ERROR, FALSE); + CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE)); + QuestionValue = &gUserInput->InputValue; + // + // Store Edit value back to Question + // + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + switch (MenuOption->Sequence) { + case 0: + QuestionValue->Value.date.Month = (UINT8) EditValue; + break; + + case 1: + QuestionValue->Value.date.Day = (UINT8) EditValue; + break; + + case 2: + QuestionValue->Value.date.Year = (UINT16) EditValue; + break; + + default: + break; + } + } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + switch (MenuOption->Sequence) { + case 0: + QuestionValue->Value.time.Hour = (UINT8) EditValue; + break; + + case 1: + QuestionValue->Value.time.Minute = (UINT8) EditValue; + break; + + case 2: + QuestionValue->Value.time.Second = (UINT8) EditValue; + break; + + default: + break; + } + } else { + // + // Numeric + // + QuestionValue->Value.u64 = EditValue; + } + + // + // Adjust the value to the correct one. + // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01 + // 2013.03.29 -> 2013.02.29 -> 2013.02.28 + // + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP && + (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) { + AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence); + } + + return EFI_SUCCESS; + + case CHAR_BACKSPACE: + if (ManualInput) { + if (Count == 0) { + if (Negative) { + Negative = FALSE; + Column--; + PrintStringAt (Column, Row, L" "); + } + break; + } + // + // Remove a character + // + EditValue = PreviousNumber[Count - 1]; + UpdateStatusBar (INPUT_ERROR, FALSE); + Count--; + Column--; + PrintStringAt (Column, Row, L" "); + } + break; + + default: + if (ManualInput) { + if (HexInput) { + if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) { + Digital = (UINT8) (Key.UnicodeChar - L'0'); + } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) { + Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A); + } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) { + Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A); + } else { + UpdateStatusBar (INPUT_ERROR, TRUE); + break; + } + } else { + if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') { + UpdateStatusBar (INPUT_ERROR, TRUE); + break; + } + } + + // + // If Count exceed input width, there is no way more is valid + // + if (Count >= InputWidth) { + break; + } + // + // Someone typed something valid! + // + if (Count != 0) { + if (HexInput) { + EditValue = LShiftU64 (EditValue, 4) + Digital; + } else if (IntInput && Negative) { + // + // Save the negative number. + // + EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1; + } else { + EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0'); + } + } else { + if (HexInput) { + EditValue = Digital; + } else if (IntInput && Negative) { + // + // Save the negative number. + // + EditValue = ~(Key.UnicodeChar - L'0') + 1; + } else { + EditValue = Key.UnicodeChar - L'0'; + } + } + + if (IntInput) { + ValidateFail = FALSE; + // + // When user input a new value, should check the current value. + // If user input a negative value, should compare it with minimum + // value, else compare it with maximum value. + // + if (Negative) { + ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE; + } else { + ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE; + } + + if (ValidateFail) { + UpdateStatusBar (INPUT_ERROR, TRUE); + ASSERT (Count < ARRAY_SIZE (PreviousNumber)); + EditValue = PreviousNumber[Count]; + break; + } + } else { + if (EditValue > Maximum) { + UpdateStatusBar (INPUT_ERROR, TRUE); + ASSERT (Count < ARRAY_SIZE (PreviousNumber)); + EditValue = PreviousNumber[Count]; + break; + } + } + + UpdateStatusBar (INPUT_ERROR, FALSE); + + Count++; + ASSERT (Count < (ARRAY_SIZE (PreviousNumber))); + PreviousNumber[Count] = EditValue; + + gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); + PrintCharAt (Column, Row, Key.UnicodeChar); + Column++; + } + break; + } + } while (TRUE); +} + +/** + Adjust option order base on the question value. + + @param Question Pointer to current question. + @param PopUpMenuLines The line number of the pop up menu. + + @retval EFI_SUCCESS If Option input is processed successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +AdjustOptionOrder ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + OUT UINTN *PopUpMenuLines + ) +{ + UINTN Index; + EFI_IFR_ORDERED_LIST *OrderList; + UINT8 *ValueArray; + UINT8 ValueType; + LIST_ENTRY *Link; + DISPLAY_QUESTION_OPTION *OneOfOption; + EFI_HII_VALUE *HiiValueArray; + + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + ValueArray = Question->CurrentValue.Buffer; + ValueType = OneOfOption->OptionOpCode->Type; + OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; + + for (Index = 0; Index < OrderList->MaxContainers; Index++) { + if (GetArrayData (ValueArray, ValueType, Index) == 0) { + break; + } + } + + *PopUpMenuLines = Index; + + // + // Prepare HiiValue array + // + HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE)); + ASSERT (HiiValueArray != NULL); + + for (Index = 0; Index < *PopUpMenuLines; Index++) { + HiiValueArray[Index].Type = ValueType; + HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index); + } + + for (Index = 0; Index < *PopUpMenuLines; Index++) { + OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]); + if (OneOfOption == NULL) { + return EFI_NOT_FOUND; + } + + RemoveEntryList (&OneOfOption->Link); + + // + // Insert to head. + // + InsertHeadList (&Question->OptionListHead, &OneOfOption->Link); + } + + FreePool (HiiValueArray); + + return EFI_SUCCESS; +} + +/** + Base on the type to compare the value. + + @param Value1 The first value need to compare. + @param Value2 The second value need to compare. + @param Type The value type for above two values. + + @retval TRUE The two value are same. + @retval FALSE The two value are different. + +**/ +BOOLEAN +IsValuesEqual ( + IN EFI_IFR_TYPE_VALUE *Value1, + IN EFI_IFR_TYPE_VALUE *Value2, + IN UINT8 Type + ) +{ + switch (Type) { + case EFI_IFR_TYPE_BOOLEAN: + case EFI_IFR_TYPE_NUM_SIZE_8: + return (BOOLEAN) (Value1->u8 == Value2->u8); + + case EFI_IFR_TYPE_NUM_SIZE_16: + return (BOOLEAN) (Value1->u16 == Value2->u16); + + case EFI_IFR_TYPE_NUM_SIZE_32: + return (BOOLEAN) (Value1->u32 == Value2->u32); + + case EFI_IFR_TYPE_NUM_SIZE_64: + return (BOOLEAN) (Value1->u64 == Value2->u64); + + default: + ASSERT (FALSE); + return FALSE; + } +} + +/** + Base on the type to set the value. + + @param Dest The dest value. + @param Source The source value. + @param Type The value type for above two values. + +**/ +VOID +SetValuesByType ( + OUT EFI_IFR_TYPE_VALUE *Dest, + IN EFI_IFR_TYPE_VALUE *Source, + IN UINT8 Type + ) +{ + switch (Type) { + case EFI_IFR_TYPE_BOOLEAN: + Dest->b = Source->b; + break; + + case EFI_IFR_TYPE_NUM_SIZE_8: + Dest->u8 = Source->u8; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + Dest->u16 = Source->u16; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + Dest->u32 = Source->u32; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + Dest->u64 = Source->u64; + break; + + default: + ASSERT (FALSE); + break; + } +} + +/** + Get selection for OneOf and OrderedList (Left/Right will be ignored). + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If Option input is processed successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetSelectionInputPopUp ( + IN UI_MENU_OPTION *MenuOption + ) +{ + EFI_INPUT_KEY Key; + UINTN Index; + CHAR16 *StringPtr; + CHAR16 *TempStringPtr; + UINTN Index2; + UINTN TopOptionIndex; + UINTN HighlightOptionIndex; + UINTN Start; + UINTN End; + UINTN Top; + UINTN Bottom; + UINTN PopUpMenuLines; + UINTN MenuLinesInView; + UINTN PopUpWidth; + CHAR16 Character; + INT32 SavedAttribute; + BOOLEAN ShowDownArrow; + BOOLEAN ShowUpArrow; + UINTN DimensionsWidth; + LIST_ENTRY *Link; + BOOLEAN OrderedList; + UINT8 *ValueArray; + UINT8 *ReturnValue; + UINT8 ValueType; + EFI_HII_VALUE HiiValue; + DISPLAY_QUESTION_OPTION *OneOfOption; + DISPLAY_QUESTION_OPTION *CurrentOption; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + INTN Result; + EFI_IFR_ORDERED_LIST *OrderList; + + DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; + + ValueArray = NULL; + ValueType = 0; + CurrentOption = NULL; + ShowDownArrow = FALSE; + ShowUpArrow = FALSE; + + ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE)); + + Question = MenuOption->ThisTag; + if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) { + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + ValueArray = Question->CurrentValue.Buffer; + ValueType = OneOfOption->OptionOpCode->Type; + OrderedList = TRUE; + OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; + } else { + OrderedList = FALSE; + OrderList = NULL; + } + + // + // Calculate Option count + // + PopUpMenuLines = 0; + if (OrderedList) { + AdjustOptionOrder(Question, &PopUpMenuLines); + } else { + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + PopUpMenuLines++; + Link = GetNextNode (&Question->OptionListHead, Link); + } + } + + // + // Get the number of one of options present and its size + // + PopUpWidth = 0; + HighlightOptionIndex = 0; + Link = GetFirstNode (&Question->OptionListHead); + for (Index = 0; Index < PopUpMenuLines; Index++) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + if (StrLen (StringPtr) > PopUpWidth) { + PopUpWidth = StrLen (StringPtr); + } + FreePool (StringPtr); + HiiValue.Type = OneOfOption->OptionOpCode->Type; + SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type); + if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { + // + // Find current selected Option for OneOf + // + HighlightOptionIndex = Index; + } + + Link = GetNextNode (&Question->OptionListHead, Link); + } + + // + // Perform popup menu initialization. + // + PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT; + + SavedAttribute = gST->ConOut->Mode->Attribute; + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + + if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) { + PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH; + } + + Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn; + End = Start + PopUpWidth + POPUP_FRAME_WIDTH; + Top = gStatementDimensions.TopRow; + Bottom = gStatementDimensions.BottomRow - 1; + + MenuLinesInView = Bottom - Top - 1; + if (MenuLinesInView >= PopUpMenuLines) { + Top = Top + (MenuLinesInView - PopUpMenuLines) / 2; + Bottom = Top + PopUpMenuLines + 1; + } else { + ShowDownArrow = TRUE; + } + + if (HighlightOptionIndex > (MenuLinesInView - 1)) { + TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1; + } else { + TopOptionIndex = 0; + } + + do { + // + // Clear that portion of the screen + // + ClearLines (Start, End, Top, Bottom, GetPopupColor ()); + + // + // Draw "One of" pop-up menu + // + Character = BOXDRAW_DOWN_RIGHT; + PrintCharAt (Start, Top, Character); + for (Index = Start; Index + 2 < End; Index++) { + if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) { + Character = GEOMETRICSHAPE_UP_TRIANGLE; + } else { + Character = BOXDRAW_HORIZONTAL; + } + + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + Character = BOXDRAW_VERTICAL; + for (Index = Top + 1; Index < Bottom; Index++) { + PrintCharAt (Start, Index, Character); + PrintCharAt (End - 1, Index, Character); + } + + // + // Move to top Option + // + Link = GetFirstNode (&Question->OptionListHead); + for (Index = 0; Index < TopOptionIndex; Index++) { + Link = GetNextNode (&Question->OptionListHead, Link); + } + + // + // Display the One of options + // + Index2 = Top + 1; + for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + // + // If the string occupies multiple lines, truncate it to fit in one line, + // and append a "..." for indication. + // + if (StrLen (StringPtr) > (PopUpWidth - 1)) { + TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1)); + ASSERT ( TempStringPtr != NULL ); + CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5))); + FreePool (StringPtr); + StringPtr = TempStringPtr; + StrCatS (StringPtr, PopUpWidth - 1, L"..."); + } + + if (Index == HighlightOptionIndex) { + // + // Highlight the selected one + // + CurrentOption = OneOfOption; + + gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ()); + PrintStringAt (Start + 2, Index2, StringPtr); + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + } else { + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + PrintStringAt (Start + 2, Index2, StringPtr); + } + + Index2++; + FreePool (StringPtr); + } + + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (Start, Bottom, Character); + for (Index = Start; Index + 2 < End; Index++) { + if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) { + Character = GEOMETRICSHAPE_DOWN_TRIANGLE; + } else { + Character = BOXDRAW_HORIZONTAL; + } + + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + + // + // Get User selection + // + Key.UnicodeChar = CHAR_NULL; + if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) { + Key.ScanCode = gDirection; + gDirection = 0; + goto TheKey; + } + + WaitForKeyStroke (&Key); + +TheKey: + switch (Key.UnicodeChar) { + case '+': + if (OrderedList) { + if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) { + // + // Highlight reaches the top of the popup window, scroll one menu item. + // + TopOptionIndex--; + ShowDownArrow = TRUE; + } + + if (TopOptionIndex == 0) { + ShowUpArrow = FALSE; + } + + if (HighlightOptionIndex > 0) { + HighlightOptionIndex--; + + ASSERT (CurrentOption != NULL); + SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link); + } + } + break; + + case '-': + // + // If an ordered list op-code, we will allow for a popup of +/- keys + // to create an ordered list of items + // + if (OrderedList) { + if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) && + (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) { + // + // Highlight reaches the bottom of the popup window, scroll one menu item. + // + TopOptionIndex++; + ShowUpArrow = TRUE; + } + + if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) { + ShowDownArrow = FALSE; + } + + if (HighlightOptionIndex < (PopUpMenuLines - 1)) { + HighlightOptionIndex++; + + ASSERT (CurrentOption != NULL); + SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink); + } + } + break; + + case CHAR_NULL: + switch (Key.ScanCode) { + case SCAN_UP: + case SCAN_DOWN: + if (Key.ScanCode == SCAN_UP) { + if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) { + // + // Highlight reaches the top of the popup window, scroll one menu item. + // + TopOptionIndex--; + ShowDownArrow = TRUE; + } + + if (TopOptionIndex == 0) { + ShowUpArrow = FALSE; + } + + if (HighlightOptionIndex > 0) { + HighlightOptionIndex--; + } + } else { + if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) && + (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) { + // + // Highlight reaches the bottom of the popup window, scroll one menu item. + // + TopOptionIndex++; + ShowUpArrow = TRUE; + } + + if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) { + ShowDownArrow = FALSE; + } + + if (HighlightOptionIndex < (PopUpMenuLines - 1)) { + HighlightOptionIndex++; + } + } + break; + + case SCAN_ESC: + gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); + + // + // Restore link list order for orderedlist + // + if (OrderedList) { + HiiValue.Type = ValueType; + HiiValue.Value.u64 = 0; + for (Index = 0; Index < OrderList->MaxContainers; Index++) { + HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index); + if (HiiValue.Value.u64 == 0) { + break; + } + + OneOfOption = ValueToOption (Question, &HiiValue); + if (OneOfOption == NULL) { + return EFI_NOT_FOUND; + } + + RemoveEntryList (&OneOfOption->Link); + InsertTailList (&Question->OptionListHead, &OneOfOption->Link); + } + } + + return EFI_DEVICE_ERROR; + + default: + break; + } + + break; + + case CHAR_CARRIAGE_RETURN: + // + // return the current selection + // + if (OrderedList) { + ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen); + ASSERT (ReturnValue != NULL); + Index = 0; + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64); + + Index++; + if (Index > OrderList->MaxContainers) { + break; + } + } + if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) { + FreePool (ReturnValue); + return EFI_DEVICE_ERROR; + } else { + gUserInput->InputValue.Buffer = ReturnValue; + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + } + } else { + ASSERT (CurrentOption != NULL); + gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type; + if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) { + return EFI_DEVICE_ERROR; + } else { + SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type); + } + } + + gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); + + return EFI_SUCCESS; + + default: + break; + } + } while (TRUE); + +} + diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/Popup.c b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/Popup.c new file mode 100644 index 000000000..a597a5d8a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/Popup.c @@ -0,0 +1,724 @@ +/** @file +Implementation for Hii Popup Protocol. + +Copyright (c) 2017, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FormDisplay.h" + +EFI_SCREEN_DESCRIPTOR gPopupDimensions; +LIST_ENTRY gUserSelectableOptions; +EFI_STRING gMessageString; +UINTN gMesStrLineNum; +UINTN gMaxRowWidth; + +/** + Free the user selectable option structure data. + + @param OptionList Point to the selectable option list which need to be freed. + +**/ +VOID +FreeSelectableOptions( + LIST_ENTRY *OptionList + ) +{ + LIST_ENTRY *Link; + USER_SELECTABLE_OPTION *SelectableOption; + + while (!IsListEmpty (OptionList)) { + Link = GetFirstNode (OptionList); + SelectableOption = SELECTABLE_OPTION_FROM_LINK (Link); + RemoveEntryList (&SelectableOption->Link); + FreePool (SelectableOption); + } +} + +/** + Display one selectable option. + + @param SelectableOption The selectable option need to be drew. + @param Highlight Whether the option need to be highlighted. + +**/ +VOID +DisplayOneSelectableOption( + IN USER_SELECTABLE_OPTION *SelectableOption, + IN BOOLEAN Highlight + ) +{ + if (Highlight) { + gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); + } + PrintStringAt (SelectableOption->OptionCol, SelectableOption->OptionRow, SelectableOption->OptionString); + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); +} + +/** + Add one selectable option to option list. This is the work function for AddUserSelectableOptions. + + @param PopupType The option need to be drew. + @param OptionType The type of this selection option. + @param OptionString Point to the option string that to be shown. + @param OptionCol The column that the option need to be drew at. + @param OptionRow The row that the option need to be drew at. + + @retval EFI_SUCCESS This function implement successfully. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available. + +**/ +EFI_STATUS +AddOneSelectableOption ( + IN EFI_HII_POPUP_TYPE PopupType, + IN EFI_HII_POPUP_SELECTION OptionType, + IN CHAR16 *OptionString, + IN UINTN OptionCol, + IN UINTN OptionRow + ) +{ + USER_SELECTABLE_OPTION *UserSelectableOption; + + UserSelectableOption = AllocateZeroPool (sizeof (USER_SELECTABLE_OPTION)); + if (UserSelectableOption == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Initialize the user selectable option based on the PopupType and OptionType. + // And then add the option to the option list gUserSelectableOptions. + // + UserSelectableOption->Signature = USER_SELECTABLE_OPTION_SIGNATURE; + UserSelectableOption->OptionString = OptionString; + UserSelectableOption->OptionType = OptionType; + UserSelectableOption->OptionCol = OptionCol; + UserSelectableOption->OptionRow = OptionRow; + UserSelectableOption->MinSequence = 0; + + switch (PopupType) { + case EfiHiiPopupTypeOk: + UserSelectableOption->MaxSequence = 0; + UserSelectableOption->Sequence= 0; + break; + case EfiHiiPopupTypeOkCancel: + UserSelectableOption->MaxSequence = 1; + if (OptionType == EfiHiiPopupSelectionOk) { + UserSelectableOption->Sequence= 0; + } else { + UserSelectableOption->Sequence= 1; + } + break; + case EfiHiiPopupTypeYesNo: + UserSelectableOption->MaxSequence = 1; + if (OptionType == EfiHiiPopupSelectionYes) { + UserSelectableOption->Sequence = 0; + } else { + UserSelectableOption->Sequence = 1; + } + break; + case EfiHiiPopupTypeYesNoCancel: + UserSelectableOption->MaxSequence = 2; + if (OptionType == EfiHiiPopupSelectionYes) { + UserSelectableOption->Sequence = 0; + } else if (OptionType == EfiHiiPopupSelectionNo){ + UserSelectableOption->Sequence = 1; + } else { + UserSelectableOption->Sequence = 2; + } + break; + default: + break; + } + InsertTailList (&gUserSelectableOptions, &UserSelectableOption->Link); + + return EFI_SUCCESS; +} + +/** + Add user selectable options to option list for different types of Popup. + + @param PopupType Type of the popup to display. + + @retval EFI_SUCCESS This function implement successfully. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available. + +**/ +EFI_STATUS +AddUserSelectableOptions ( + IN EFI_HII_POPUP_TYPE PopupType + ) +{ + EFI_STATUS Status; + UINTN EndCol; + UINTN StartCol; + UINTN OptionCol; + UINTN OptionRow; + UINTN ColDimension; + + Status = EFI_SUCCESS; + EndCol = gPopupDimensions.RightColumn; + StartCol = gPopupDimensions.LeftColumn; + OptionRow = gPopupDimensions.BottomRow - POPUP_BORDER; + ColDimension = EndCol - StartCol + 1; + + InitializeListHead (&gUserSelectableOptions); + + switch (PopupType) { + case EfiHiiPopupTypeOk: + // + // Add [Ok] option to the option list. + // + OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_OK_WIDTH) / 2; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionOk, gOkOption, OptionCol, OptionRow); + break; + case EfiHiiPopupTypeOkCancel: + // + // Add [Ok] and [Cancel] options to the option list. + // + OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_OK_CAL_WIDTH) / 3; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionOk, gOkOption, OptionCol, OptionRow); + OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_OK_CAL_WIDTH) / 3 - (GetStringWidth (gCancelOption) -2) / 2 + 1; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionCancel, gCancelOption, OptionCol, OptionRow); + break; + case EfiHiiPopupTypeYesNo: + // + // Add [Yes] and [No] options to the option list. + // + OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_YES_NO_WIDTH) / 3; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionYes, gYesOption, OptionCol, OptionRow); + OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_YES_NO_WIDTH) / 3 - (GetStringWidth (gNoOption)- 2) / 2 + 1; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionNo, gNoOption, OptionCol, OptionRow); + break; + case EfiHiiPopupTypeYesNoCancel: + // + // Add [Yes], [No] and [Cancel] options to the option list. + // + OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH) / 4; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionYes, gYesOption, OptionCol, OptionRow); + OptionCol = StartCol + (ColDimension - (GetStringWidth (gNoOption) -2) / 2) / 2; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionNo, gNoOption, OptionCol, OptionRow); + OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH) / 4 - (GetStringWidth (gCancelOption) - 2) / 2 + 1; + Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionCancel, gCancelOption, OptionCol, OptionRow); + break; + default: + break; + } + return Status; +} + +/** + Show selectable options to user and get the one that user select. + + @param PopupType Type of the popup to display. + @param UserSelection User selection. + +**/ +VOID +GetUserSelection ( + IN EFI_HII_POPUP_TYPE PopupType, + OUT EFI_HII_POPUP_SELECTION *UserSelection + ) +{ + LIST_ENTRY *HighlightPos; + LIST_ENTRY *Link; + USER_SELECTABLE_OPTION *SelectableOption; + USER_SELECTABLE_OPTION *HighlightOption; + EFI_INPUT_KEY KeyValue; + EFI_STATUS Status; + + // + // Display user selectable options in gUserSelectableOptions and get the option which user selects. + // + HighlightPos = gUserSelectableOptions.ForwardLink; + do { + for (Link = gUserSelectableOptions.ForwardLink; Link != &gUserSelectableOptions; Link = Link->ForwardLink) { + SelectableOption = SELECTABLE_OPTION_FROM_LINK (Link); + DisplayOneSelectableOption (SelectableOption, (BOOLEAN)(Link == HighlightPos)); + } + // + //If UserSelection is NULL, there is no need to handle the key user input, just return. + // + if (UserSelection == NULL) { + return; + } + + Status = WaitForKeyStroke (&KeyValue); + ASSERT_EFI_ERROR (Status); + + HighlightOption = SELECTABLE_OPTION_FROM_LINK (HighlightPos); + switch (KeyValue.UnicodeChar) { + case CHAR_NULL: + switch (KeyValue.ScanCode) { + case SCAN_RIGHT: + if (HighlightOption->Sequence < HighlightOption->MaxSequence) { + HighlightPos = HighlightPos->ForwardLink; + } else { + HighlightPos = gUserSelectableOptions.ForwardLink; + } + break; + case SCAN_LEFT: + if (HighlightOption->Sequence > HighlightOption->MinSequence) { + HighlightPos = HighlightPos->BackLink; + } else { + HighlightPos = gUserSelectableOptions.BackLink; + } + break; + default: + break; + } + break; + + case CHAR_CARRIAGE_RETURN: + *UserSelection = HighlightOption->OptionType; + return; + default: + if (((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptYes | UPPER_LOWER_CASE_OFFSET)) && + (PopupType == EfiHiiPopupTypeYesNo || PopupType == EfiHiiPopupTypeYesNoCancel)) { + *UserSelection = EfiHiiPopupSelectionYes; + return; + } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptNo| UPPER_LOWER_CASE_OFFSET) && + (PopupType == EfiHiiPopupTypeYesNo || PopupType == EfiHiiPopupTypeYesNoCancel)){ + *UserSelection = EfiHiiPopupSelectionNo; + return; + } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptOk | UPPER_LOWER_CASE_OFFSET) && + (PopupType == EfiHiiPopupTypeOk || PopupType == EfiHiiPopupTypeOkCancel)){ + *UserSelection = EfiHiiPopupSelectionOk; + return; + } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptCancel| UPPER_LOWER_CASE_OFFSET) && + (PopupType == EfiHiiPopupTypeOkCancel || PopupType == EfiHiiPopupTypeYesNoCancel)){ + *UserSelection = EfiHiiPopupSelectionCancel; + return; + } + break; + } + } while (TRUE); +} + +/** + Get the offset in the input string when the width reaches to a fixed one. + + The input string may contain NARROW_CHAR and WIDE_CHAR. + Notice: the input string doesn't contain line break characters. + + @param String The input string to be counted. + @param MaxWidth The max length this function supported. + @param Offset The max index of the string can be show out. If string's width less than MaxWidth, offset will point to the "\0" of the string. + +**/ +VOID +GetStringOffsetWithWidth ( + IN CHAR16 *String, + IN UINTN MaxWidth, + OUT UINTN *Offset + ) +{ + UINTN StringWidth; + UINTN CharWidth; + UINTN StrOffset; + + StringWidth = 0; + CharWidth = 1; + + for (StrOffset = 0; String[StrOffset] != CHAR_NULL; StrOffset++) { + switch (String[StrOffset]) { + case NARROW_CHAR: + CharWidth = 1; + break; + case WIDE_CHAR: + CharWidth = 2; + break; + default: + StringWidth += CharWidth; + if (StringWidth >= MaxWidth) { + *Offset = StrOffset; + return; + } + } + } + *Offset = StrOffset; +} + +/** + Parse the message to check if it contains line break characters. + For once call, caller can get the string for one line and the width of the string. + This function call be called recursively to parse the whole InputString. + + (Notice: current implementation, it only checks \r, \n characters, it deals \r,\n,\n\r same as \r\n.) + + @param InputString String description for this option. + @param OutputString Buffer to copy the string into, caller is responsible for freeing the buffer. + @param OutputStrWidth The width of OutputString. + @param Index Where in InputString to start the copy process + + @return Returns the number of CHAR16 characters that were copied into the OutputString buffer, include the '\0' info. + +**/ +UINTN +ParseMessageString ( + IN CHAR16 *InputString, + OUT CHAR16 **OutputString, + OUT UINTN *OutputStrWidth, + IN OUT UINTN *Index + ) +{ + UINTN StrOffset; + + if (InputString == NULL || Index == NULL || OutputString == NULL) { + return 0; + } + + *OutputStrWidth = 0; + + // + //Check the string to see if there are line break characters in the string + // + for (StrOffset = 0; + InputString[*Index + StrOffset] != CHAR_CARRIAGE_RETURN && InputString[*Index + StrOffset] != CHAR_LINEFEED && InputString[*Index + StrOffset] != CHAR_NULL; + StrOffset++ + ); + + // + // The CHAR_NULL has process last time, this time just return 0 to stand for finishing parsing the InputString. + // + if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) { + return 0; + } + + // + // Copy the string to OutputString buffer and calculate the width of OutputString. + // + *OutputString = AllocateZeroPool ((StrOffset + 1) * sizeof(CHAR16)); + if (*OutputString == NULL) { + return 0; + } + CopyMem ((*OutputString), &InputString[*Index], StrOffset * sizeof(CHAR16)); + *OutputStrWidth = (GetStringWidth (*OutputString) -2) / 2; + + // + // Update the value of Index, can be used for marking where to check the input string for next call. + // + if (InputString[*Index + StrOffset] == CHAR_LINEFEED) { + // + // Skip the /n or /n/r info. + // + if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) { + *Index = (*Index + StrOffset + 2); + } else { + *Index = (*Index + StrOffset + 1); + } + } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) { + // + // Skip the /r or /r/n info. + // + if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) { + *Index = (*Index + StrOffset + 2); + } else { + *Index = (*Index + StrOffset + 1); + } + } else { + *Index = (*Index + StrOffset); + } + + return StrOffset + 1; +} + +/** + Calculate the position of the popup. + + @param PopupType Type of the popup to display. + @param ScreenForPopup The screen dimensions for the popup. + +**/ +VOID +CalculatePopupPosition ( + IN EFI_HII_POPUP_TYPE PopupType, + OUT EFI_SCREEN_DESCRIPTOR *ScreenForPopup + ) +{ + CHAR16 *OutputString; + UINTN StringIndex; + UINTN OutputStrWidth; + UINTN OptionRowWidth; + UINTN Columns; + UINTN Rows; + + OptionRowWidth = 0; + + // + // Calculate the row number which is needed to show the message string and the max width of the string in one row. + // + for (StringIndex = 0; ParseMessageString (gMessageString, &OutputString, &OutputStrWidth, &StringIndex) != 0;) { + gMesStrLineNum ++; + if (gMaxRowWidth < OutputStrWidth) { + gMaxRowWidth = OutputStrWidth; + } + FreePool (OutputString); + } + + // + // Calculate the row width for the selectable options.(OptionRowWidth = Number * SkipWidth + OptionWidth) + // + if (PopupType == EfiHiiPopupTypeOk) { + OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *2 + USER_SELECTABLE_OPTION_OK_WIDTH; + } else if (PopupType == EfiHiiPopupTypeOkCancel) { + OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *3 + USER_SELECTABLE_OPTION_OK_CAL_WIDTH; + } else if (PopupType == EfiHiiPopupTypeYesNo) { + OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *3 + USER_SELECTABLE_OPTION_YES_NO_WIDTH; + } else if (PopupType == EfiHiiPopupTypeYesNoCancel) { + OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *4 + USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH; + } + if (OptionRowWidth > gMaxRowWidth) { + gMaxRowWidth = OptionRowWidth; + } + + // + // Avialble row width for message string = screen width - left popup border width - right popup border width. + // Avialble line number for message string = screen height - 1 - popup header height - popup footer height. + // (Notice: screen height - 1 because in current UI page, the bottom row of srceen is usded to show Status Bar,not for form itself. + // So we don't use the bottom row for popup either. If macro STATUS_BAR_HEIGHT changed, we also need to update the height here.) + // + // Select the smaller one between actual dimension of message string and the avialble dimension for message string. + // + gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Columns, &Rows); + gMaxRowWidth = MIN (gMaxRowWidth, Columns - 2 * POPUP_BORDER); + gMesStrLineNum = MIN (gMesStrLineNum, Rows -1 - POPUP_FOOTER_HEIGHT - POPUP_HEADER_HEIGHT); + + // + // Calculate the start column, end column, top row and bottom row for the popup. + // + ScreenForPopup->LeftColumn = (Columns -2 * POPUP_BORDER - gMaxRowWidth) / 2; + ScreenForPopup->RightColumn = ScreenForPopup->LeftColumn + gMaxRowWidth + 2 * POPUP_BORDER - 1; + ScreenForPopup->TopRow = (Rows - 1 - POPUP_FOOTER_HEIGHT - POPUP_HEADER_HEIGHT - gMesStrLineNum) / 2; + ScreenForPopup->BottomRow = ScreenForPopup->TopRow + gMesStrLineNum + POPUP_FOOTER_HEIGHT + POPUP_HEADER_HEIGHT - 1; +} + +/** + Draw the Message box. + +-------------------------------------------+ + | ERROR/WARNING/INFO | + |-------------------------------------------| + | popup messages | + | | + | user selectable options | + +-------------------------------------------+ + + @param PopupStyle Popup style to use. + +**/ +EFI_STATUS +DrawMessageBox ( + IN EFI_HII_POPUP_STYLE PopupStyle + ) +{ + UINTN Index; + UINTN Length; + UINTN EndCol; + UINTN TopRow; + UINTN StartCol; + UINTN BottomRow; + CHAR16 Character; + UINTN DisplayRow; + UINTN StringIndex; + CHAR16 *TempString; + CHAR16 *OutputString; + UINTN ColDimension; + UINTN OutputStrWidth; + UINTN DrawMesStrRowNum; + + EndCol = gPopupDimensions.RightColumn; + TopRow = gPopupDimensions.TopRow; + StartCol = gPopupDimensions.LeftColumn; + BottomRow = gPopupDimensions.BottomRow; + ColDimension = EndCol - StartCol + 1; + DrawMesStrRowNum = 0; + + // + // 1. Draw the top of the message box. + // + Character = BOXDRAW_DOWN_RIGHT; + PrintCharAt (StartCol, TopRow, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = StartCol; Index + 1 < EndCol; Index++) { + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + + // + // 2. Draw the prompt string for different popup styles. + // + Character = BOXDRAW_VERTICAL; + DisplayRow = TopRow + POPUP_BORDER; + ClearLines (StartCol, EndCol, DisplayRow, DisplayRow, GetPopupColor ()); + PrintCharAt (StartCol, DisplayRow, Character); + PrintCharAt (EndCol, DisplayRow, Character); + if (PopupStyle == EfiHiiPopupStyleError) { + PrintStringAt ((ColDimension - (GetStringWidth (gErrorPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gErrorPopup); + } else if (PopupStyle == EfiHiiPopupStyleWarning) { + PrintStringAt ((ColDimension - (GetStringWidth (gWarningPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gWarningPopup); + } else { + PrintStringAt ((ColDimension - (GetStringWidth (gInfoPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gInfoPopup); + } + + // + // 3. Draw the horizontal line below the prompt string for different popup styles. + // + DisplayRow = TopRow + POPUP_BORDER + POPUP_STYLE_STRING_HEIGHT; + ClearLines (StartCol, EndCol, DisplayRow, DisplayRow, GetPopupColor ()); + Character = BOXDRAW_HORIZONTAL; + for (Index = StartCol + 1; Index < EndCol; Index++) { + PrintCharAt (Index, DisplayRow, Character); + } + Character = BOXDRAW_VERTICAL; + PrintCharAt (StartCol, DisplayRow, Character); + PrintCharAt (EndCol, DisplayRow, Character); + + // + // 4. Draw the mesage string. + // + DisplayRow = TopRow + POPUP_HEADER_HEIGHT; + for (Index = DisplayRow ,StringIndex = 0; ParseMessageString (gMessageString, &OutputString, &OutputStrWidth, &StringIndex) != 0 && DrawMesStrRowNum < gMesStrLineNum;) { + ClearLines (StartCol, EndCol, Index, Index, GetPopupColor ()); + PrintCharAt (StartCol, Index, Character); + PrintCharAt (EndCol, Index, Character); + if (OutputStrWidth > gMaxRowWidth) { + // + //OutputStrWidth > MaxMesStrWidth, cut off the string and print print ... instead. + // + GetStringOffsetWithWidth (OutputString, gMaxRowWidth, &Length); + TempString = AllocateZeroPool ((Length + 1) * sizeof (CHAR16)); + if (TempString == NULL) { + FreePool (OutputString); + return EFI_OUT_OF_RESOURCES; + } + StrnCpyS (TempString, Length + 1, OutputString, Length - 3); + StrCatS (TempString, Length + 1, L"..."); + PrintStringAt ((ColDimension - gMaxRowWidth) / 2 + StartCol, Index, TempString); + FreePool (TempString); + } else { + PrintStringAt ((ColDimension - OutputStrWidth) / 2 + StartCol, Index, OutputString); + } + Index ++; + DrawMesStrRowNum ++; + FreePool (OutputString); + } + + // + // 5. Draw an empty line after message string. + // + ClearLines (StartCol, EndCol, Index, Index, GetPopupColor ()); + PrintCharAt (StartCol, Index, Character); + PrintCharAt (EndCol, Index, Character); + // + // Check whether the actual string row number beyond the MesStrRowNum, if yes, print the ...... in the row. + // + if (OutputStrWidth > 0 && DrawMesStrRowNum >= gMesStrLineNum) { + PrintStringAt ((ColDimension - StrLen (L"......")) / 2 + StartCol, Index, L"......"); + } + + // + // 6. Draw an empty line which is used to show user selectable options, will draw concrete option strings in function GetUserSelection(). + // + Character = BOXDRAW_VERTICAL; + DisplayRow = BottomRow - POPUP_BORDER; + ClearLines (StartCol, EndCol, DisplayRow, DisplayRow, GetPopupColor ()); + PrintCharAt (StartCol, DisplayRow, Character); + PrintCharAt (EndCol, DisplayRow, Character); + + // + // 7. Draw the bottom of the message box. + // + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (StartCol, BottomRow, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = StartCol; Index + 1 < EndCol; Index++) { + PrintCharAt ((UINTN)-1, (UINTN) -1, Character); + } + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN)-1, (UINTN) -1, Character); + + return EFI_SUCCESS; +} + +/** + Displays a popup window. + + @param This A pointer to the EFI_HII_POPUP_PROTOCOL instance. + @param PopupStyle Popup style to use. + @param PopupType Type of the popup to display. + @param HiiHandle HII handle of the string pack containing Message + @param Message A message to display in the popup box. + @param UserSelection User selection. + + @retval EFI_SUCCESS The popup box was successfully displayed. + @retval EFI_INVALID_PARAMETER HiiHandle and Message do not define a valid HII string. + @retval EFI_INVALID_PARAMETER PopupType is not one of the values defined by this specification. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to display the popup box. + +**/ +EFI_STATUS +EFIAPI +CreatePopup ( + IN EFI_HII_POPUP_PROTOCOL *This, + IN EFI_HII_POPUP_STYLE PopupStyle, + IN EFI_HII_POPUP_TYPE PopupType, + IN EFI_HII_HANDLE HiiHandle, + IN EFI_STRING_ID Message, + OUT EFI_HII_POPUP_SELECTION *UserSelection OPTIONAL + ) +{ + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; + EFI_SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode; + EFI_STATUS Status; + + if ((PopupType < EfiHiiPopupTypeOk) || (PopupType > EfiHiiPopupTypeYesNoCancel)) { + return EFI_INVALID_PARAMETER; + } + + if((HiiHandle == NULL) || (Message == 0)) { + return EFI_INVALID_PARAMETER; + } + + gMessageString = HiiGetString (HiiHandle, Message, NULL); + if(gMessageString == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConOut = gST->ConOut; + gMaxRowWidth = 0; + gMesStrLineNum = 0; + + CopyMem (&SavedConsoleMode, ConOut->Mode, sizeof (SavedConsoleMode)); + ConOut->EnableCursor (ConOut, FALSE); + ConOut->SetAttribute (ConOut, GetPopupColor ()); + + CalculatePopupPosition (PopupType, &gPopupDimensions); + + Status = DrawMessageBox (PopupStyle); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Add user selectable options to option list: gUserSelectableOptions + // + Status = AddUserSelectableOptions (PopupType); + if (EFI_ERROR (Status)) { + goto Done; + } + + GetUserSelection (PopupType, UserSelection); + +Done: + // + // Restore Conout attributes and free the resources allocate before. + // + ConOut->EnableCursor (ConOut, SavedConsoleMode.CursorVisible); + ConOut->SetCursorPosition (ConOut, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow); + ConOut->SetAttribute (ConOut, SavedConsoleMode.Attribute); + FreeSelectableOptions (&gUserSelectableOptions); + FreePool (gMessageString); + + return Status; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c new file mode 100644 index 000000000..c02e36a63 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c @@ -0,0 +1,1593 @@ +/** @file +Implementation for handling the User Interface option processing. + + +Copyright (c) 2004 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FormDisplay.h" + +#define MAX_TIME_OUT_LEN 0x10 + +/** + Concatenate a narrow string to another string. + + @param Destination The destination string. + @param DestMax The Max length of destination string. + @param Source The source string. The string to be concatenated. + to the end of Destination. + +**/ +VOID +NewStrCat ( + IN OUT CHAR16 *Destination, + IN UINTN DestMax, + IN CHAR16 *Source + ) +{ + UINTN Length; + + for (Length = 0; Destination[Length] != 0; Length++) + ; + + // + // We now have the length of the original string + // We can safely assume for now that we are concatenating a narrow value to this string. + // For instance, the string is "XYZ" and cat'ing ">" + // If this assumption changes, we need to make this routine a bit more complex + // + Destination[Length] = NARROW_CHAR; + Length++; + + StrCpyS (Destination + Length, DestMax - Length, Source); +} + +/** + Get UINT64 type value. + + @param Value Input Hii value. + + @retval UINT64 Return the UINT64 type value. + +**/ +UINT64 +HiiValueToUINT64 ( + IN EFI_HII_VALUE *Value + ) +{ + UINT64 RetVal; + + RetVal = 0; + + switch (Value->Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + RetVal = Value->Value.u8; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + RetVal = Value->Value.u16; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + RetVal = Value->Value.u32; + break; + + case EFI_IFR_TYPE_BOOLEAN: + RetVal = Value->Value.b; + break; + + case EFI_IFR_TYPE_DATE: + RetVal = *(UINT64*) &Value->Value.date; + break; + + case EFI_IFR_TYPE_TIME: + RetVal = (*(UINT64*) &Value->Value.time) & 0xffffff; + break; + + default: + RetVal = Value->Value.u64; + break; + } + + return RetVal; +} + +/** + Check whether this value type can be transfer to EFI_IFR_TYPE_BUFFER type. + + EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to + EFI_IFR_TYPE_BUFFER when do the value compare. + + @param Value Expression value to compare on. + + @retval TRUE This value type can be transter to EFI_IFR_TYPE_BUFFER type. + @retval FALSE This value type can't be transter to EFI_IFR_TYPE_BUFFER type. + +**/ +BOOLEAN +IsTypeInBuffer ( + IN EFI_HII_VALUE *Value + ) +{ + switch (Value->Type) { + case EFI_IFR_TYPE_BUFFER: + case EFI_IFR_TYPE_DATE: + case EFI_IFR_TYPE_TIME: + case EFI_IFR_TYPE_REF: + return TRUE; + + default: + return FALSE; + } +} + +/** + Check whether this value type can be transfer to EFI_IFR_TYPE_UINT64 + + @param Value Expression value to compare on. + + @retval TRUE This value type can be transter to EFI_IFR_TYPE_BUFFER type. + @retval FALSE This value type can't be transter to EFI_IFR_TYPE_BUFFER type. + +**/ +BOOLEAN +IsTypeInUINT64 ( + IN EFI_HII_VALUE *Value + ) +{ + switch (Value->Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + case EFI_IFR_TYPE_NUM_SIZE_16: + case EFI_IFR_TYPE_NUM_SIZE_32: + case EFI_IFR_TYPE_NUM_SIZE_64: + case EFI_IFR_TYPE_BOOLEAN: + return TRUE; + + default: + return FALSE; + } +} + +/** + Return the buffer length and buffer pointer for this value. + + EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to + EFI_IFR_TYPE_BUFFER when do the value compare. + + @param Value Expression value to compare on. + @param Buf Return the buffer pointer. + @param BufLen Return the buffer length. + +**/ +VOID +GetBufAndLenForValue ( + IN EFI_HII_VALUE *Value, + OUT UINT8 **Buf, + OUT UINT16 *BufLen + ) +{ + switch (Value->Type) { + case EFI_IFR_TYPE_BUFFER: + *Buf = Value->Buffer; + *BufLen = Value->BufferLen; + break; + + case EFI_IFR_TYPE_DATE: + *Buf = (UINT8 *) (&Value->Value.date); + *BufLen = (UINT16) sizeof (EFI_HII_DATE); + break; + + case EFI_IFR_TYPE_TIME: + *Buf = (UINT8 *) (&Value->Value.time); + *BufLen = (UINT16) sizeof (EFI_HII_TIME); + break; + + case EFI_IFR_TYPE_REF: + *Buf = (UINT8 *) (&Value->Value.ref); + *BufLen = (UINT16) sizeof (EFI_HII_REF); + break; + + default: + *Buf = NULL; + *BufLen = 0; + } +} + +/** + Compare two Hii value. + + @param Value1 Expression value to compare on left-hand. + @param Value2 Expression value to compare on right-hand. + @param Result Return value after compare. + retval 0 Two operators equal. + return Positive value if Value1 is greater than Value2. + retval Negative value if Value1 is less than Value2. + @param HiiHandle Only required for string compare. + + @retval other Could not perform compare on two values. + @retval EFI_SUCCESS Compare the value success. + +**/ +EFI_STATUS +CompareHiiValue ( + IN EFI_HII_VALUE *Value1, + IN EFI_HII_VALUE *Value2, + OUT INTN *Result, + IN EFI_HII_HANDLE HiiHandle OPTIONAL + ) +{ + INT64 Temp64; + CHAR16 *Str1; + CHAR16 *Str2; + UINTN Len; + UINT8 *Buf1; + UINT16 Buf1Len; + UINT8 *Buf2; + UINT16 Buf2Len; + + if (Value1->Type == EFI_IFR_TYPE_STRING && Value2->Type == EFI_IFR_TYPE_STRING) { + if (Value1->Value.string == 0 || Value2->Value.string == 0) { + // + // StringId 0 is reserved + // + return EFI_INVALID_PARAMETER; + } + + if (Value1->Value.string == Value2->Value.string) { + *Result = 0; + return EFI_SUCCESS; + } + + Str1 = GetToken (Value1->Value.string, HiiHandle); + if (Str1 == NULL) { + // + // String not found + // + return EFI_NOT_FOUND; + } + + Str2 = GetToken (Value2->Value.string, HiiHandle); + if (Str2 == NULL) { + FreePool (Str1); + return EFI_NOT_FOUND; + } + + *Result = StrCmp (Str1, Str2); + + FreePool (Str1); + FreePool (Str2); + + return EFI_SUCCESS; + } + + // + // Take types(date, time, ref, buffer) as buffer + // + if (IsTypeInBuffer(Value1) && IsTypeInBuffer(Value2)) { + GetBufAndLenForValue(Value1, &Buf1, &Buf1Len); + GetBufAndLenForValue(Value2, &Buf2, &Buf2Len); + + Len = Buf1Len > Buf2Len ? Buf2Len : Buf1Len; + *Result = CompareMem (Buf1, Buf2, Len); + if ((*Result == 0) && (Buf1Len != Buf2Len)) { + // + // In this case, means base on samll number buffer, the data is same + // So which value has more data, which value is bigger. + // + *Result = Buf1Len > Buf2Len ? 1 : -1; + } + return EFI_SUCCESS; + } + + // + // Take remain types(integer, boolean, date/time) as integer + // + if (IsTypeInUINT64(Value1) && IsTypeInUINT64(Value2)) { + Temp64 = HiiValueToUINT64(Value1) - HiiValueToUINT64(Value2); + if (Temp64 > 0) { + *Result = 1; + } else if (Temp64 < 0) { + *Result = -1; + } else { + *Result = 0; + } + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + +/** + Search an Option of a Question by its value. + + @param Question The Question + @param OptionValue Value for Option to be searched. + + @retval Pointer Pointer to the found Option. + @retval NULL Option not found. + +**/ +DISPLAY_QUESTION_OPTION * +ValueToOption ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN EFI_HII_VALUE *OptionValue + ) +{ + LIST_ENTRY *Link; + DISPLAY_QUESTION_OPTION *Option; + INTN Result; + EFI_HII_VALUE Value; + + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + ZeroMem (&Value, sizeof (EFI_HII_VALUE)); + Value.Type = Option->OptionOpCode->Type; + CopyMem (&Value.Value, &Option->OptionOpCode->Value, Option->OptionOpCode->Header.Length - OFFSET_OF (EFI_IFR_ONE_OF_OPTION, Value)); + + if ((CompareHiiValue (&Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { + return Option; + } + + Link = GetNextNode (&Question->OptionListHead, Link); + } + + return NULL; +} + + +/** + Return data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + + @retval Value The data to be returned + +**/ +UINT64 +GetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index + ) +{ + UINT64 Data; + + ASSERT (Array != NULL); + + Data = 0; + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + Data = (UINT64) *(((UINT8 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + Data = (UINT64) *(((UINT16 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + Data = (UINT64) *(((UINT32 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + Data = (UINT64) *(((UINT64 *) Array) + Index); + break; + + default: + break; + } + + return Data; +} + + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ) +{ + + ASSERT (Array != NULL); + + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + *(((UINT8 *) Array) + Index) = (UINT8) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + *(((UINT16 *) Array) + Index) = (UINT16) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + *(((UINT32 *) Array) + Index) = (UINT32) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + *(((UINT64 *) Array) + Index) = (UINT64) Value; + break; + + default: + break; + } +} + +/** + Check whether this value already in the array, if yes, return the index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Value The value to be find. + @param Index The index in the array which has same value with Value. + + @retval TRUE Found the value in the array. + @retval FALSE Not found the value. + +**/ +BOOLEAN +FindArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINT64 Value, + OUT UINTN *Index OPTIONAL + ) +{ + UINTN Count; + UINT64 TmpValue; + UINT64 ValueComp; + + ASSERT (Array != NULL); + + Count = 0; + TmpValue = 0; + + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + ValueComp = (UINT8) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + ValueComp = (UINT16) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + ValueComp = (UINT32) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + ValueComp = (UINT64) Value; + break; + + default: + ValueComp = 0; + break; + } + + while ((TmpValue = GetArrayData (Array, Type, Count)) != 0) { + if (ValueComp == TmpValue) { + if (Index != NULL) { + *Index = Count; + } + return TRUE; + } + + Count ++; + } + + return FALSE; +} + +/** + Print Question Value according to it's storage width and display attributes. + + @param Question The Question to be printed. + @param FormattedNumber Buffer for output string. + @param BufferSize The FormattedNumber buffer size in bytes. + + @retval EFI_SUCCESS Print success. + @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number. + +**/ +EFI_STATUS +PrintFormattedNumber ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN OUT CHAR16 *FormattedNumber, + IN UINTN BufferSize + ) +{ + INT64 Value; + CHAR16 *Format; + EFI_HII_VALUE *QuestionValue; + EFI_IFR_NUMERIC *NumericOp; + + if (BufferSize < (21 * sizeof (CHAR16))) { + return EFI_BUFFER_TOO_SMALL; + } + + QuestionValue = &Question->CurrentValue; + NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode; + + Value = (INT64) QuestionValue->Value.u64; + switch (NumericOp->Flags & EFI_IFR_DISPLAY) { + case EFI_IFR_DISPLAY_INT_DEC: + switch (QuestionValue->Type) { + case EFI_IFR_NUMERIC_SIZE_1: + Value = (INT64) ((INT8) QuestionValue->Value.u8); + break; + + case EFI_IFR_NUMERIC_SIZE_2: + Value = (INT64) ((INT16) QuestionValue->Value.u16); + break; + + case EFI_IFR_NUMERIC_SIZE_4: + Value = (INT64) ((INT32) QuestionValue->Value.u32); + break; + + case EFI_IFR_NUMERIC_SIZE_8: + default: + break; + } + + if (Value < 0) { + Value = -Value; + Format = L"-%ld"; + } else { + Format = L"%ld"; + } + break; + + case EFI_IFR_DISPLAY_UINT_DEC: + Format = L"%ld"; + break; + + case EFI_IFR_DISPLAY_UINT_HEX: + Format = L"%lx"; + break; + + default: + return EFI_UNSUPPORTED; + } + + UnicodeSPrint (FormattedNumber, BufferSize, Format, Value); + + return EFI_SUCCESS; +} + + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param Marker The variable argument list for the list of string to be printed. + +**/ +VOID +CreateSharedPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + IN VA_LIST Marker + ) +{ + UINTN Index; + UINTN Count; + CHAR16 Character; + UINTN Start; + UINTN End; + UINTN Top; + UINTN Bottom; + CHAR16 *String; + UINTN DimensionsWidth; + UINTN DimensionsHeight; + + DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; + DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow; + + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + + if ((RequestedWidth + 2) > DimensionsWidth) { + RequestedWidth = DimensionsWidth - 2; + } + + // + // Subtract the PopUp width from total Columns, allow for one space extra on + // each end plus a border. + // + Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gStatementDimensions.LeftColumn + 1; + End = Start + RequestedWidth + 1; + + Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gStatementDimensions.TopRow - 1; + Bottom = Top + NumberOfLines + 2; + + Character = BOXDRAW_DOWN_RIGHT; + PrintCharAt (Start, Top, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = Start; Index + 2 < End; Index++) { + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + Character = BOXDRAW_VERTICAL; + + Count = 0; + for (Index = Top; Index + 2 < Bottom; Index++, Count++) { + String = VA_ARG (Marker, CHAR16*); + + // + // This will clear the background of the line - we never know who might have been + // here before us. This differs from the next clear in that it used the non-reverse + // video for normal printing. + // + if (GetStringWidth (String) / 2 > 1) { + ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ()); + } + + // + // Passing in a space results in the assumption that this is where typing will occur + // + if (String[0] == L' ') { + ClearLines (Start + 1, End - 1, Index + 1, Index + 1, GetPopupInverseColor ()); + } + + // + // Passing in a NULL results in a blank space + // + if (String[0] == CHAR_NULL) { + ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ()); + } + + PrintStringAt ( + ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gStatementDimensions.LeftColumn + 1, + Index + 1, + String + ); + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + PrintCharAt (Start, Index + 1, Character); + PrintCharAt (End - 1, Index + 1, Character); + } + + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (Start, Bottom - 1, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = Start; Index + 2 < End; Index++) { + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); +} + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param ... A series of text strings that displayed in the pop-up. + +**/ +VOID +EFIAPI +CreateMultiStringPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + ... + ) +{ + VA_LIST Marker; + + VA_START (Marker, NumberOfLines); + + CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker); + + VA_END (Marker); +} + +/** + Process nothing. + + @param Event The Event need to be process + @param Context The context of the event. + +**/ +VOID +EFIAPI +EmptyEventProcess ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +} + +/** + Process for the refresh interval statement. + + @param Event The Event need to be process + @param Context The context of the event. + +**/ +VOID +EFIAPI +RefreshTimeOutProcess ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + WARNING_IF_CONTEXT *EventInfo; + CHAR16 TimeOutString[MAX_TIME_OUT_LEN]; + + EventInfo = (WARNING_IF_CONTEXT *) Context; + + if (*(EventInfo->TimeOut) == 0) { + gBS->CloseEvent (Event); + + gBS->SignalEvent (EventInfo->SyncEvent); + return; + } + + UnicodeSPrint(TimeOutString, MAX_TIME_OUT_LEN, L"%d", *(EventInfo->TimeOut)); + + CreateDialog (NULL, gEmptyString, EventInfo->ErrorInfo, gPressEnter, gEmptyString, TimeOutString, NULL); + + *(EventInfo->TimeOut) -= 1; +} + +/** + Display error message for invalid password. + +**/ +VOID +PasswordInvalid ( + VOID + ) +{ + EFI_INPUT_KEY Key; + + // + // Invalid password, prompt error message + // + do { + CreateDialog (&Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); +} + +/** + Process password op code. + + @param MenuOption The menu for current password op code. + + @retval EFI_SUCCESS Question Option process success. + @retval Other Question Option process fail. + +**/ +EFI_STATUS +PasswordProcess ( + IN UI_MENU_OPTION *MenuOption + ) +{ + CHAR16 *StringPtr; + CHAR16 *TempString; + UINTN Maximum; + EFI_STATUS Status; + EFI_IFR_PASSWORD *PasswordInfo; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + EFI_INPUT_KEY Key; + + Question = MenuOption->ThisTag; + PasswordInfo = (EFI_IFR_PASSWORD *) Question->OpCode; + Maximum = PasswordInfo->MaxSize; + Status = EFI_SUCCESS; + + StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16)); + ASSERT (StringPtr); + + // + // Use a NULL password to test whether old password is required + // + *StringPtr = 0; + Status = Question->PasswordCheck (gFormData, Question, StringPtr); + if (Status == EFI_NOT_AVAILABLE_YET || Status == EFI_UNSUPPORTED) { + // + // Password can't be set now. + // + if (Status == EFI_UNSUPPORTED) { + do { + CreateDialog (&Key, gEmptyString, gPasswordUnsupported, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + } + FreePool (StringPtr); + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + // + // Old password exist, ask user for the old password + // + Status = ReadString (MenuOption, gPromptForPassword, StringPtr); + if (EFI_ERROR (Status)) { + ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); + FreePool (StringPtr); + return Status; + } + + // + // Check user input old password + // + Status = Question->PasswordCheck (gFormData, Question, StringPtr); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_READY) { + // + // Typed in old password incorrect + // + PasswordInvalid (); + } else { + Status = EFI_SUCCESS; + } + ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); + FreePool (StringPtr); + return Status; + } + } + + // + // Ask for new password + // + ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); + Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr); + if (EFI_ERROR (Status)) { + // + // Reset state machine for password + // + Question->PasswordCheck (gFormData, Question, NULL); + ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); + FreePool (StringPtr); + return Status; + } + + // + // Confirm new password + // + TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16)); + ASSERT (TempString); + Status = ReadString (MenuOption, gConfirmPassword, TempString); + if (EFI_ERROR (Status)) { + // + // Reset state machine for password + // + Question->PasswordCheck (gFormData, Question, NULL); + ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); + ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16)); + FreePool (StringPtr); + FreePool (TempString); + return Status; + } + + // + // Compare two typed-in new passwords + // + if (StrCmp (StringPtr, TempString) == 0) { + gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr); + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL); + + Status = EFI_SUCCESS; + } else { + // + // Reset state machine for password + // + Question->PasswordCheck (gFormData, Question, NULL); + + // + // Two password mismatch, prompt error message + // + do { + CreateDialog (&Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + Status = EFI_INVALID_PARAMETER; + } + ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16)); + ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); + FreePool (TempString); + FreePool (StringPtr); + + return Status; +} + +/** + Print some debug message about mismatched menu info. + + @param MenuOption The MenuOption for this Question. + +**/ +VOID +PrintMismatchMenuInfo ( + IN UI_MENU_OPTION *MenuOption +) +{ + CHAR16 *FormTitleStr; + CHAR16 *FormSetTitleStr; + CHAR16 *OneOfOptionStr; + CHAR16 *QuestionName; + LIST_ENTRY *Link; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + EFI_IFR_ORDERED_LIST *OrderList; + UINT8 Index; + EFI_HII_VALUE HiiValue; + EFI_HII_VALUE *QuestionValue; + DISPLAY_QUESTION_OPTION *Option; + UINT8 *ValueArray; + UINT8 ValueType; + EFI_IFR_FORM_SET *FormsetBuffer; + UINTN FormsetBufferSize; + + Question = MenuOption->ThisTag; + HiiGetFormSetFromHiiHandle (gFormData->HiiHandle, &FormsetBuffer, &FormsetBufferSize); + + FormSetTitleStr = GetToken (FormsetBuffer->FormSetTitle, gFormData->HiiHandle); + FormTitleStr = GetToken (gFormData->FormTitle, gFormData->HiiHandle); + + DEBUG ((DEBUG_ERROR, "\n[%a]: Mismatch Formset : Formset Guid = %g, FormSet title = %s\n", gEfiCallerBaseName, &gFormData->FormSetGuid, FormSetTitleStr)); + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Form : FormId = %d, Form title = %s.\n", gEfiCallerBaseName, gFormData->FormId, FormTitleStr)); + + if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) { + QuestionName = GetToken (((EFI_IFR_ORDERED_LIST*)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle); + Link = GetFirstNode (&Question->OptionListHead); + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + ValueType = Option->OptionOpCode->Type; + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error : OrderedList value in the array doesn't match with option value.\n", gEfiCallerBaseName)); + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: Name = %s.\n", gEfiCallerBaseName, QuestionName)); + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: OrderedList array value :\n", gEfiCallerBaseName)); + + OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; + for (Index = 0; Index < OrderList->MaxContainers; Index++) { + ValueArray = Question->CurrentValue.Buffer; + HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index); + DEBUG ((DEBUG_ERROR, " Value[%d] =%ld.\n", Index, HiiValue.Value.u64)); + } + } else if (Question->OpCode->OpCode == EFI_IFR_ONE_OF_OP) { + QuestionName = GetToken (((EFI_IFR_ONE_OF*)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle); + QuestionValue = &Question->CurrentValue; + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error : OneOf value doesn't match with option value.\n", gEfiCallerBaseName)); + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : Name = %s.\n", gEfiCallerBaseName, QuestionName)); + switch (QuestionValue->Type) { + case EFI_IFR_TYPE_NUM_SIZE_64: + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %ld.\n",gEfiCallerBaseName, QuestionValue->Value.u64)); + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %d.\n",gEfiCallerBaseName, QuestionValue->Value.u32)); + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %d.\n",gEfiCallerBaseName, QuestionValue->Value.u16)); + break; + + case EFI_IFR_TYPE_NUM_SIZE_8: + DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %d.\n",gEfiCallerBaseName, QuestionValue->Value.u8)); + break; + + default: + ASSERT (FALSE); + break; + } + } + + Index = 0; + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + OneOfOptionStr = GetToken (Option->OptionOpCode->Option, gFormData->HiiHandle); + switch (Option->OptionOpCode->Type) { + case EFI_IFR_TYPE_NUM_SIZE_64: + DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %ld, Option Name = %s.\n",gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u64, OneOfOptionStr)); + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %d, Option Name = %s.\n",gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u32, OneOfOptionStr)); + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %d, Option Name = %s.\n",gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u16, OneOfOptionStr)); + break; + + case EFI_IFR_TYPE_NUM_SIZE_8: + DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %d, Option Name = %s.\n",gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u8, OneOfOptionStr)); + break; + + default: + ASSERT (FALSE); + break; + } + Link = GetNextNode (&Question->OptionListHead, Link); + Index++; + } +} + +/** + Process a Question's Option (whether selected or un-selected). + + @param MenuOption The MenuOption for this Question. + @param Selected TRUE: if Question is selected. + @param OptionString Pointer of the Option String to be displayed. + @param SkipErrorValue Whether need to return when value without option for it. + + @retval EFI_SUCCESS Question Option process success. + @retval Other Question Option process fail. + +**/ +EFI_STATUS +ProcessOptions ( + IN UI_MENU_OPTION *MenuOption, + IN BOOLEAN Selected, + OUT CHAR16 **OptionString, + IN BOOLEAN SkipErrorValue + ) +{ + EFI_STATUS Status; + CHAR16 *StringPtr; + UINTN Index; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + CHAR16 FormattedNumber[21]; + UINT16 Number; + CHAR16 Character[2]; + EFI_INPUT_KEY Key; + UINTN BufferSize; + DISPLAY_QUESTION_OPTION *OneOfOption; + LIST_ENTRY *Link; + EFI_HII_VALUE HiiValue; + EFI_HII_VALUE *QuestionValue; + DISPLAY_QUESTION_OPTION *Option; + UINTN Index2; + UINT8 *ValueArray; + UINT8 ValueType; + EFI_IFR_ORDERED_LIST *OrderList; + BOOLEAN ValueInvalid; + UINTN MaxLen; + + Status = EFI_SUCCESS; + + StringPtr = NULL; + Character[1] = L'\0'; + *OptionString = NULL; + ValueInvalid = FALSE; + + ZeroMem (FormattedNumber, 21 * sizeof (CHAR16)); + BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow; + + Question = MenuOption->ThisTag; + QuestionValue = &Question->CurrentValue; + + switch (Question->OpCode->OpCode) { + case EFI_IFR_ORDERED_LIST_OP: + + // + // Check whether there are Options of this OrderedList + // + if (IsListEmpty (&Question->OptionListHead)) { + break; + } + + OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; + + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + ValueType = OneOfOption->OptionOpCode->Type; + ValueArray = Question->CurrentValue.Buffer; + + if (Selected) { + // + // Go ask for input + // + Status = GetSelectionInputPopUp (MenuOption); + } else { + // + // We now know how many strings we will have, so we can allocate the + // space required for the array or strings. + // + MaxLen = OrderList->MaxContainers * BufferSize / sizeof (CHAR16); + *OptionString = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + ASSERT (*OptionString); + + HiiValue.Type = ValueType; + HiiValue.Value.u64 = 0; + for (Index = 0; Index < OrderList->MaxContainers; Index++) { + HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index); + if (HiiValue.Value.u64 == 0) { + // + // Values for the options in ordered lists should never be a 0 + // + break; + } + + OneOfOption = ValueToOption (Question, &HiiValue); + if (OneOfOption == NULL) { + // + // Print debug msg for the mistach menu. + // + PrintMismatchMenuInfo (MenuOption); + + if (SkipErrorValue) { + // + // Just try to get the option string, skip the value which not has option. + // + continue; + } + + // + // Show error message + // + do { + CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + // + // The initial value of the orderedlist is invalid, force to be valid value + // Exit current DisplayForm with new value. + // + gUserInput->SelectedStatement = Question; + gMisMatch = TRUE; + ValueArray = AllocateZeroPool (Question->CurrentValue.BufferLen); + ASSERT (ValueArray != NULL); + gUserInput->InputValue.Buffer = ValueArray; + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + + Link = GetFirstNode (&Question->OptionListHead); + Index2 = 0; + while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) { + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64); + Index2++; + } + SetArrayData (ValueArray, ValueType, Index2, 0); + + FreePool (*OptionString); + *OptionString = NULL; + return EFI_NOT_FOUND; + } + + Character[0] = LEFT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], MaxLen, Character); + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + NewStrCat (OptionString[0], MaxLen, StringPtr); + Character[0] = RIGHT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], MaxLen, Character); + Character[0] = CHAR_CARRIAGE_RETURN; + NewStrCat (OptionString[0], MaxLen, Character); + FreePool (StringPtr); + } + + // + // If valid option more than the max container, skip these options. + // + if (Index >= OrderList->MaxContainers) { + break; + } + + // + // Search the other options, try to find the one not in the container. + // + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) { + continue; + } + + // + // Print debug msg for the mistach menu. + // + PrintMismatchMenuInfo (MenuOption); + + if (SkipErrorValue) { + // + // Not report error, just get the correct option string info. + // + Character[0] = LEFT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], MaxLen, Character); + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + NewStrCat (OptionString[0], MaxLen, StringPtr); + Character[0] = RIGHT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], MaxLen, Character); + Character[0] = CHAR_CARRIAGE_RETURN; + NewStrCat (OptionString[0], MaxLen, Character); + FreePool (StringPtr); + + continue; + } + + if (!ValueInvalid) { + ValueInvalid = TRUE; + // + // Show error message + // + do { + CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + // + // The initial value of the orderedlist is invalid, force to be valid value + // Exit current DisplayForm with new value. + // + gUserInput->SelectedStatement = Question; + gMisMatch = TRUE; + ValueArray = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer); + ASSERT (ValueArray != NULL); + gUserInput->InputValue.Buffer = ValueArray; + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + } + + SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64); + } + + if (ValueInvalid) { + FreePool (*OptionString); + *OptionString = NULL; + return EFI_NOT_FOUND; + } + } + break; + + case EFI_IFR_ONE_OF_OP: + // + // Check whether there are Options of this OneOf + // + if (IsListEmpty (&Question->OptionListHead)) { + break; + } + if (Selected) { + // + // Go ask for input + // + Status = GetSelectionInputPopUp (MenuOption); + } else { + MaxLen = BufferSize / sizeof(CHAR16); + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + OneOfOption = ValueToOption (Question, QuestionValue); + if (OneOfOption == NULL) { + // + // Print debug msg for the mistach menu. + // + PrintMismatchMenuInfo (MenuOption); + + if (SkipErrorValue) { + // + // Not report error, just get the correct option string info. + // + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + } else { + // + // Show error message + // + do { + CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + // + // Force the Question value to be valid + // Exit current DisplayForm with new value. + // + Link = GetFirstNode (&Question->OptionListHead); + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + gUserInput->InputValue.Type = Option->OptionOpCode->Type; + switch (gUserInput->InputValue.Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + gUserInput->InputValue.Value.u8 = Option->OptionOpCode->Value.u8; + break; + case EFI_IFR_TYPE_NUM_SIZE_16: + CopyMem (&gUserInput->InputValue.Value.u16, &Option->OptionOpCode->Value.u16, sizeof (UINT16)); + break; + case EFI_IFR_TYPE_NUM_SIZE_32: + CopyMem (&gUserInput->InputValue.Value.u32, &Option->OptionOpCode->Value.u32, sizeof (UINT32)); + break; + case EFI_IFR_TYPE_NUM_SIZE_64: + CopyMem (&gUserInput->InputValue.Value.u64, &Option->OptionOpCode->Value.u64, sizeof (UINT64)); + break; + default: + ASSERT (FALSE); + break; + } + gUserInput->SelectedStatement = Question; + gMisMatch = TRUE; + FreePool (*OptionString); + *OptionString = NULL; + return EFI_NOT_FOUND; + } + } + + Character[0] = LEFT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], MaxLen, Character); + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + NewStrCat (OptionString[0], MaxLen, StringPtr); + Character[0] = RIGHT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], MaxLen, Character); + + FreePool (StringPtr); + } + break; + + case EFI_IFR_CHECKBOX_OP: + if (Selected) { + // + // Since this is a BOOLEAN operation, flip it upon selection + // + gUserInput->InputValue.Type = QuestionValue->Type; + gUserInput->InputValue.Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE); + + // + // Perform inconsistent check + // + return EFI_SUCCESS; + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + *OptionString[0] = LEFT_CHECKBOX_DELIMITER; + + if (QuestionValue->Value.b) { + *(OptionString[0] + 1) = CHECK_ON; + } else { + *(OptionString[0] + 1) = CHECK_OFF; + } + *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER; + } + break; + + case EFI_IFR_NUMERIC_OP: + if (Selected) { + // + // Go ask for input + // + Status = GetNumericInput (MenuOption); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + *OptionString[0] = LEFT_NUMERIC_DELIMITER; + + // + // Formatted print + // + PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16)); + Number = (UINT16) GetStringWidth (FormattedNumber); + CopyMem (OptionString[0] + 1, FormattedNumber, Number); + + *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER; + } + break; + + case EFI_IFR_DATE_OP: + if (Selected) { + // + // This is similar to numerics + // + Status = GetNumericInput (MenuOption); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + switch (MenuOption->Sequence) { + case 0: + *OptionString[0] = LEFT_NUMERIC_DELIMITER; + if (QuestionValue->Value.date.Month == 0xff){ + UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??"); + } else { + UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month); + } + *(OptionString[0] + 3) = DATE_SEPARATOR; + break; + + case 1: + SetUnicodeMem (OptionString[0], 4, L' '); + if (QuestionValue->Value.date.Day == 0xff){ + UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??"); + } else { + UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day); + } + *(OptionString[0] + 6) = DATE_SEPARATOR; + break; + + case 2: + SetUnicodeMem (OptionString[0], 7, L' '); + if (QuestionValue->Value.date.Year == 0xff){ + UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"????"); + } else { + UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year); + } + *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER; + break; + } + } + break; + + case EFI_IFR_TIME_OP: + if (Selected) { + // + // This is similar to numerics + // + Status = GetNumericInput (MenuOption); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + switch (MenuOption->Sequence) { + case 0: + *OptionString[0] = LEFT_NUMERIC_DELIMITER; + if (QuestionValue->Value.time.Hour == 0xff){ + UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??"); + } else { + UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour); + } + *(OptionString[0] + 3) = TIME_SEPARATOR; + break; + + case 1: + SetUnicodeMem (OptionString[0], 4, L' '); + if (QuestionValue->Value.time.Minute == 0xff){ + UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??"); + } else { + UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute); + } + *(OptionString[0] + 6) = TIME_SEPARATOR; + break; + + case 2: + SetUnicodeMem (OptionString[0], 7, L' '); + if (QuestionValue->Value.time.Second == 0xff){ + UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"??"); + } else { + UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second); + } + *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER; + break; + } + } + break; + + case EFI_IFR_STRING_OP: + if (Selected) { + StringPtr = AllocateZeroPool (Question->CurrentValue.BufferLen + sizeof (CHAR16)); + ASSERT (StringPtr); + CopyMem(StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen); + + Status = ReadString (MenuOption, gPromptForData, StringPtr); + if (EFI_ERROR (Status)) { + FreePool (StringPtr); + return Status; + } + + gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr); + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL); + FreePool (StringPtr); + return EFI_SUCCESS; + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + if (((CHAR16 *) Question->CurrentValue.Buffer)[0] == 0x0000) { + *(OptionString[0]) = '_'; + } else { + if (Question->CurrentValue.BufferLen < BufferSize) { + BufferSize = Question->CurrentValue.BufferLen; + } + CopyMem (OptionString[0], (CHAR16 *) Question->CurrentValue.Buffer, BufferSize); + } + } + break; + + case EFI_IFR_PASSWORD_OP: + if (Selected) { + Status = PasswordProcess (MenuOption); + } + break; + + default: + break; + } + + return Status; +} + + +/** + Process the help string: Split StringPtr to several lines of strings stored in + FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth. + + @param StringPtr The entire help string. + @param FormattedString The oupput formatted string. + @param EachLineWidth The max string length of each line in the formatted string. + @param RowCount TRUE: if Question is selected. + +**/ +UINTN +ProcessHelpString ( + IN CHAR16 *StringPtr, + OUT CHAR16 **FormattedString, + OUT UINT16 *EachLineWidth, + IN UINTN RowCount + ) +{ + UINTN Index; + CHAR16 *OutputString; + UINTN TotalRowNum; + UINTN CheckedNum; + UINT16 GlyphWidth; + UINT16 LineWidth; + UINT16 MaxStringLen; + UINT16 StringLen; + + TotalRowNum = 0; + CheckedNum = 0; + GlyphWidth = 1; + Index = 0; + MaxStringLen = 0; + StringLen = 0; + + // + // Set default help string width. + // + LineWidth = (UINT16) (gHelpBlockWidth - 1); + + // + // Get row number of the String. + // + while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) { + if (StringLen > MaxStringLen) { + MaxStringLen = StringLen; + } + + TotalRowNum ++; + FreePool (OutputString); + } + *EachLineWidth = MaxStringLen; + + *FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16)); + ASSERT (*FormattedString != NULL); + + // + // Generate formatted help string array. + // + GlyphWidth = 1; + Index = 0; + while((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) { + CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16)); + CheckedNum ++; + FreePool (OutputString); + } + + return TotalRowNum; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthConfigureVfr.Vfr b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthConfigureVfr.Vfr new file mode 100644 index 000000000..9fc7ed34e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthConfigureVfr.Vfr @@ -0,0 +1,33 @@ +///** @file +// +// VFR to produce the formset used by BDS. This form only lists +// the Configure Required driver health instances. +// +// Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent + +//**/ +#include "DriverHealthManagerVfr.h" + +formset + guid = DRIVER_HEALTH_CONFIGURE_FORMSET_GUID, + title = STRING_TOKEN(STR_FORM_TITLE), + help = STRING_TOKEN(STR_FORM_HELP), + classguid = DRIVER_HEALTH_CONFIGURE_FORMSET_GUID, + + form formid = DRIVER_HEALTH_FORM_ID, + title = STRING_TOKEN(STR_FORM_TITLE); + + label LABEL_BEGIN; + label LABEL_END; + + suppressif TRUE; + text + help = STRING_TOKEN(STR_NULL), + text = STRING_TOKEN(STR_NULL), + flags = INTERACTIVE, + key = QUESTION_ID_REFRESH_CONFIGURE; + endif; + + endform; +endformset; diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.c b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.c new file mode 100644 index 000000000..92c738a3f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.c @@ -0,0 +1,987 @@ +/** @file + This module produces two driver health manager forms. + One will be used by BDS core to configure the Configured Required + driver health instances, the other will be automatically included by + firmware setup (UI). + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2018 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DriverHealthManagerDxe.h" +#include "DriverHealthManagerVfr.h" + +EFI_HII_CONFIG_ACCESS_PROTOCOL mDriverHealthManagerConfigAccess = { + DriverHealthManagerFakeExtractConfig, + DriverHealthManagerFakeRouteConfig, + DriverHealthManagerCallback +}; + +EFI_GUID mDriverHealthManagerForm = DRIVER_HEALTH_MANAGER_FORMSET_GUID; + +FORM_DEVICE_PATH mDriverHealthManagerFormDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + EFI_CALLER_ID_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +EFI_HII_HANDLE mDriverHealthManagerHiiHandle; +EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *mDriverHealthManagerHealthInfo = NULL; +UINTN mDriverHealthManagerHealthInfoCount = 0; +EFI_HII_DATABASE_PROTOCOL *mDriverHealthManagerDatabase; + + +extern UINT8 DriverHealthManagerVfrBin[]; +extern UINT8 DriverHealthConfigureVfrBin[]; + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request A null-terminated Unicode string in format. + @param Progress On return, points to a character in the Request string. + Points to the string's null terminator if request was successful. + Points to the most recent '&' before the first failing name/value + pair (or the beginning of the string if the failure is in the + first name/value pair) if the request was not successful. + @param Results A null-terminated Unicode string in format which + has all values filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +DriverHealthManagerFakeExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + *Progress = Request; + return EFI_NOT_FOUND; +} + +/** + This function processes the results of changes in configuration. + + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration A null-terminated Unicode string in format. + @param Progress A pointer to a string filled in with the offset of the most + recent '&' before the first failing name/value pair (or the + beginning of the string if the failure is in the first + name/value pair) or the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +DriverHealthManagerFakeRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Configuration; + + return EFI_NOT_FOUND; +} + +/** + + Install the health manager forms. + One will be used by BDS core to configure the Configured Required + driver health instances, the other will be automatically included by + firmware setup (UI). + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCEESS The health manager forms are successfully installed. + +**/ +EFI_STATUS +EFIAPI +InitializeDriverHealthManager ( + EFI_HANDLE ImageHandle, + EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + Status = gBS->LocateProtocol ( + &gEfiHiiDatabaseProtocolGuid, + NULL, + (VOID **) &mDriverHealthManagerDatabase + ); + ASSERT_EFI_ERROR (Status); + + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiDevicePathProtocolGuid, + &mDriverHealthManagerFormDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &mDriverHealthManagerConfigAccess, + NULL + ); + ASSERT_EFI_ERROR (Status); + + + // + // Publish Driver Health HII data. + // + mDriverHealthManagerHiiHandle = HiiAddPackages ( + &gEfiCallerIdGuid, + Handle, + DriverHealthManagerVfrBin, + DriverHealthConfigureVfrBin, + STRING_ARRAY_NAME, + NULL + ); + ASSERT (mDriverHealthManagerHiiHandle != NULL); + + return EFI_SUCCESS; +} + +/** + + Select the best matching language according to front page policy for best user experience. + + This function supports both ISO 639-2 and RFC 4646 language codes, but language + code types may not be mixed in a single call to this function. + + @param SupportedLanguages A pointer to a Null-terminated ASCII string that + contains a set of language codes in the format + specified by Iso639Language. + @param Iso639Language If TRUE, then all language codes are assumed to be + in ISO 639-2 format. If FALSE, then all language + codes are assumed to be in RFC 4646 language format. + + @retval NULL The best matching language could not be found in SupportedLanguages. + @retval NULL There are not enough resources available to return the best matching + language. + @retval Other A pointer to a Null-terminated ASCII string that is the best matching + language in SupportedLanguages. +**/ +CHAR8 * +DriverHealthManagerSelectBestLanguage ( + IN CHAR8 *SupportedLanguages, + IN BOOLEAN Iso639Language + ) +{ + CHAR8 *LanguageVariable; + CHAR8 *BestLanguage; + + GetEfiGlobalVariable2 (Iso639Language ? L"Lang" : L"PlatformLang", (VOID**)&LanguageVariable, NULL); + + BestLanguage = GetBestLanguage( + SupportedLanguages, + Iso639Language, + (LanguageVariable != NULL) ? LanguageVariable : "", + Iso639Language ? "eng" : "en-US", + NULL + ); + if (LanguageVariable != NULL) { + FreePool (LanguageVariable); + } + + return BestLanguage; +} + + + +/** + + This is an internal worker function to get the Component Name (2) protocol interface + and the language it supports. + + @param ProtocolGuid A pointer to an EFI_GUID. It points to Component Name (2) protocol GUID. + @param DriverBindingHandle The handle on which the Component Name (2) protocol instance is retrieved. + @param ComponentName A pointer to the Component Name (2) protocol interface. + @param SupportedLanguage The best suitable language that matches the SupportedLangues interface for the + located Component Name (2) instance. + + @retval EFI_SUCCESS The Component Name (2) protocol instance is successfully located and we find + the best matching language it support. + @retval EFI_UNSUPPORTED The input Language is not supported by the Component Name (2) protocol. + @retval Other Some error occurs when locating Component Name (2) protocol instance or finding + the supported language. + +**/ +EFI_STATUS +DriverHealthManagerGetComponentNameWorker ( + IN EFI_GUID *ProtocolGuid, + IN EFI_HANDLE DriverBindingHandle, + OUT EFI_COMPONENT_NAME_PROTOCOL **ComponentName, + OUT CHAR8 **SupportedLanguage + ) +{ + EFI_STATUS Status; + + // + // Locate Component Name (2) protocol on the driver binging handle. + // + Status = gBS->OpenProtocol ( + DriverBindingHandle, + ProtocolGuid, + (VOID **) ComponentName, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Apply shell policy to select the best language. + // + *SupportedLanguage = DriverHealthManagerSelectBestLanguage ( + (*ComponentName)->SupportedLanguages, + (BOOLEAN) (ProtocolGuid == &gEfiComponentNameProtocolGuid) + ); + if (*SupportedLanguage == NULL) { + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +/** + + This is an internal worker function to get driver name from Component Name (2) protocol interface. + + @param ProtocolGuid A pointer to an EFI_GUID. It points to Component Name (2) protocol GUID. + @param DriverBindingHandle The handle on which the Component Name (2) protocol instance is retrieved. + @param DriverName A pointer to the Unicode string to return. This Unicode string is the name + of the driver specified by This. + + @retval EFI_SUCCESS The driver name is successfully retrieved from Component Name (2) protocol + interface. + @retval Other The driver name cannot be retrieved from Component Name (2) protocol + interface. + +**/ +EFI_STATUS +DriverHealthManagerGetDriverNameWorker ( + IN EFI_GUID *ProtocolGuid, + IN EFI_HANDLE DriverBindingHandle, + OUT CHAR16 **DriverName + ) +{ + EFI_STATUS Status; + CHAR8 *BestLanguage; + EFI_COMPONENT_NAME_PROTOCOL *ComponentName; + + // + // Retrieve Component Name (2) protocol instance on the driver binding handle and + // find the best language this instance supports. + // + Status = DriverHealthManagerGetComponentNameWorker ( + ProtocolGuid, + DriverBindingHandle, + &ComponentName, + &BestLanguage + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the driver name from Component Name (2) protocol instance on the driver binging handle. + // + Status = ComponentName->GetDriverName ( + ComponentName, + BestLanguage, + DriverName + ); + FreePool (BestLanguage); + + return Status; +} + +/** + This function gets driver name from Component Name 2 protocol interface and Component Name protocol interface + in turn. It first tries UEFI 2.0 Component Name 2 protocol interface and try to get the driver name. + If the attempt fails, it then gets the driver name from EFI 1.1 Component Name protocol for backward + compatibility support. + + @param DriverBindingHandle The handle on which the Component Name (2) protocol instance is retrieved. + + @return A pointer to the Unicode string to return. This Unicode string is the name of the controller + specified by ControllerHandle and ChildHandle. + + +**/ +CHAR16 * +DriverHealthManagerGetDriverName ( + IN EFI_HANDLE DriverBindingHandle + ) +{ + EFI_STATUS Status; + CHAR16 *DriverName; + + // + // Get driver name from UEFI 2.0 Component Name 2 protocol interface. + // + Status = DriverHealthManagerGetDriverNameWorker (&gEfiComponentName2ProtocolGuid, DriverBindingHandle, &DriverName); + if (EFI_ERROR (Status)) { + // + // If it fails to get the driver name from Component Name protocol interface, we should fall back on + // EFI 1.1 Component Name protocol interface. + // + Status = DriverHealthManagerGetDriverNameWorker (&gEfiComponentNameProtocolGuid, DriverBindingHandle, &DriverName); + } + + if (!EFI_ERROR (Status)) { + return AllocateCopyPool (StrSize (DriverName), DriverName); + } else { + return ConvertDevicePathToText (DevicePathFromHandle (DriverBindingHandle), FALSE, TRUE); + } +} + + + +/** + This function gets controller name from Component Name 2 protocol interface and Component Name protocol interface + in turn. It first tries UEFI 2.0 Component Name 2 protocol interface and try to get the controller name. + If the attempt fails, it then gets the controller name from EFI 1.1 Component Name protocol for backward + compatibility support. + + @param ProtocolGuid A pointer to an EFI_GUID. It points to Component Name (2) protocol GUID. + @param DriverBindingHandle The handle on which the Component Name (2) protocol instance is retrieved. + @param ControllerHandle The handle of a controller that the driver specified by This is managing. + This handle specifies the controller whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name of. This is an + optional parameter that may be NULL. It will be NULL for device drivers. + It will also be NULL for bus drivers that attempt to retrieve the name + of the bus controller. It will not be NULL for a bus driver that attempts + to retrieve the name of a child controller. + @param ControllerName A pointer to the Unicode string to return. This Unicode string + is the name of the controller specified by ControllerHandle and ChildHandle. + + @retval EFI_SUCCESS The controller name is successfully retrieved from Component Name (2) protocol + interface. + @retval Other The controller name cannot be retrieved from Component Name (2) protocol. + +**/ +EFI_STATUS +DriverHealthManagerGetControllerNameWorker ( + IN EFI_GUID *ProtocolGuid, + IN EFI_HANDLE DriverBindingHandle, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + CHAR8 *BestLanguage; + EFI_COMPONENT_NAME_PROTOCOL *ComponentName; + + // + // Retrieve Component Name (2) protocol instance on the driver binding handle and + // find the best language this instance supports. + // + Status = DriverHealthManagerGetComponentNameWorker ( + ProtocolGuid, + DriverBindingHandle, + &ComponentName, + &BestLanguage + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the controller name from Component Name (2) protocol instance on the driver binging handle. + // + Status = ComponentName->GetControllerName ( + ComponentName, + ControllerHandle, + ChildHandle, + BestLanguage, + ControllerName + ); + FreePool (BestLanguage); + + return Status; +} + +/** + + This function gets controller name from Component Name 2 protocol interface and Component Name protocol interface + in turn. It first tries UEFI 2.0 Component Name 2 protocol interface and try to get the controller name. + If the attempt fails, it then gets the controller name from EFI 1.1 Component Name protocol for backward + compatibility support. + + @param DriverBindingHandle The handle on which the Component Name (2) protocol instance is retrieved. + @param ControllerHandle The handle of a controller that the driver specified by DriverBindingHandle is managing. + This handle specifies the controller whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name of. This is an + optional parameter that may be NULL. It will be NULL for device drivers. + It will also be NULL for bus drivers that attempt to retrieve the name + of the bus controller. It will not be NULL for a bus driver that attempts + to retrieve the name of a child controller. + + @return A pointer to the Unicode string to return. This Unicode string is the name of the controller + specified by ControllerHandle and ChildHandle. +**/ +CHAR16 * +DriverHealthManagerGetControllerName ( + IN EFI_HANDLE DriverBindingHandle, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + CHAR16 *ControllerName; + + // + // Get controller name from UEFI 2.0 Component Name 2 protocol interface. + // + Status = DriverHealthManagerGetControllerNameWorker ( + &gEfiComponentName2ProtocolGuid, + DriverBindingHandle, + ControllerHandle, + ChildHandle, + &ControllerName + ); + if (EFI_ERROR (Status)) { + // + // If it fails to get the controller name from Component Name protocol interface, we should fall back on + // EFI 1.1 Component Name protocol interface. + // + Status = DriverHealthManagerGetControllerNameWorker ( + &gEfiComponentNameProtocolGuid, + DriverBindingHandle, + ControllerHandle, + ChildHandle, + &ControllerName + ); + } + + if (!EFI_ERROR (Status)) { + return AllocateCopyPool (StrSize (ControllerName), ControllerName); + } else { + return ConvertDevicePathToText (DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle), FALSE, TRUE); + } +} + +/** + The repair notify function. + @param Value A value between 0 and Limit that identifies the current progress + of the repair operation. + @param Limit The maximum value of Value for the current repair operation. + If Limit is 0, then the completion progress is indeterminate. + For example, a driver that wants to specify progress in percent + would use a Limit value of 100. + + @retval EFI_SUCCESS Successfully return from the notify function. +**/ +EFI_STATUS +EFIAPI +DriverHealthManagerRepairNotify ( + IN UINTN Value, + IN UINTN Limit + ) +{ + DEBUG ((EFI_D_INFO, "[DriverHealthManagement]RepairNotify: %d/%d\n", Value, Limit)); + return EFI_SUCCESS; +} + +/** + Look for the formset GUID which has the gEfiHiiDriverHealthFormsetGuid class GUID in the specified HII package list. + + @param Handle Handle to the HII package list. + @param FormsetGuid Return the formset GUID. + + @retval EFI_SUCCESS The formset is found successfully. + @retval EFI_NOT_FOUND The formset cannot be found. +**/ +EFI_STATUS +DriverHealthManagerGetFormsetId ( + IN EFI_HII_HANDLE Handle, + OUT EFI_GUID *FormsetGuid + ) +{ + EFI_STATUS Status; + EFI_HII_PACKAGE_LIST_HEADER *HiiPackageList; + UINTN BufferSize; + UINT8 *Package; + UINT8 *OpCodeData; + UINT32 Offset; + UINT32 Offset2; + EFI_HII_PACKAGE_HEADER PackageHeader; + UINT8 Index; + UINT8 NumberOfClassGuid; + EFI_GUID *ClassGuid; + + // + // Get HII PackageList + // + BufferSize = 0; + HiiPackageList = NULL; + Status = mDriverHealthManagerDatabase->ExportPackageLists (mDriverHealthManagerDatabase, Handle, &BufferSize, HiiPackageList); + if (Status == EFI_BUFFER_TOO_SMALL) { + HiiPackageList = AllocatePool (BufferSize); + ASSERT (HiiPackageList != NULL); + + Status = mDriverHealthManagerDatabase->ExportPackageLists (mDriverHealthManagerDatabase, Handle, &BufferSize, HiiPackageList); + } + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (HiiPackageList != NULL); + + // + // Get Form package from this HII package List + // + for (Offset = sizeof (EFI_HII_PACKAGE_LIST_HEADER); Offset < ReadUnaligned32 (&HiiPackageList->PackageLength); Offset += PackageHeader.Length) { + Package = ((UINT8 *) HiiPackageList) + Offset; + CopyMem (&PackageHeader, Package, sizeof (EFI_HII_PACKAGE_HEADER)); + + if (PackageHeader.Type == EFI_HII_PACKAGE_FORMS) { + // + // Search FormSet in this Form Package + // + + for (Offset2 = sizeof (EFI_HII_PACKAGE_HEADER); Offset2 < PackageHeader.Length; Offset2 += ((EFI_IFR_OP_HEADER *) OpCodeData)->Length) { + OpCodeData = Package + Offset2; + + if ((((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode == EFI_IFR_FORM_SET_OP) && + (((EFI_IFR_OP_HEADER *) OpCodeData)->Length > OFFSET_OF (EFI_IFR_FORM_SET, Flags))) { + // + // Try to compare against formset class GUID + // + NumberOfClassGuid = (UINT8) (((EFI_IFR_FORM_SET *) OpCodeData)->Flags & 0x3); + ClassGuid = (EFI_GUID *) (OpCodeData + sizeof (EFI_IFR_FORM_SET)); + for (Index = 0; Index < NumberOfClassGuid; Index++) { + if (CompareGuid (&gEfiHiiDriverHealthFormsetGuid, &ClassGuid[Index])) { + CopyMem (FormsetGuid, &((EFI_IFR_FORM_SET *) OpCodeData)->Guid, sizeof (EFI_GUID)); + FreePool (HiiPackageList); + return EFI_SUCCESS; + } + } + } + } + } + } + + // + // Form package not found in this Package List + // + FreePool (HiiPackageList); + return EFI_NOT_FOUND; +} + +/** + Processes a single controller using the EFI Driver Health Protocol associated with + that controller. + + @param DriverHealth A pointer to the EFI_DRIVER_HEALTH_PROTOCOL instance. + @param ControllerHandle The class guid specifies which form set will be displayed. + @param ChildHandle The handle of the child controller to retrieve the health + status on. This is an optional parameter that may be NULL. + @param HealthStatus The health status of the controller. + @param MessageList An array of warning or error messages associated + with the controller specified by ControllerHandle and + ChildHandle. This is an optional parameter that may be NULL. + @param FormHiiHandle The HII handle for an HII form associated with the + controller specified by ControllerHandle and ChildHandle. +**/ +VOID +DriverHealthManagerProcessSingleControllerHealth ( + IN EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth, + IN EFI_HANDLE ControllerHandle, OPTIONAL + IN EFI_HANDLE ChildHandle, OPTIONAL + IN EFI_DRIVER_HEALTH_STATUS HealthStatus, + IN EFI_DRIVER_HEALTH_HII_MESSAGE **MessageList, OPTIONAL + IN EFI_HII_HANDLE FormHiiHandle + ) +{ + EFI_STATUS Status; + + ASSERT (HealthStatus != EfiDriverHealthStatusConfigurationRequired); + // + // If the module need to be repaired or reconfiguration, will process it until + // reach a terminal status. The status from EfiDriverHealthStatusRepairRequired after repair + // will be in (Health, Failed, Configuration Required). + // + switch (HealthStatus) { + + case EfiDriverHealthStatusRepairRequired: + Status = DriverHealth->Repair ( + DriverHealth, + ControllerHandle, + ChildHandle, + DriverHealthManagerRepairNotify + ); + break; + + case EfiDriverHealthStatusRebootRequired: + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + break; + + case EfiDriverHealthStatusReconnectRequired: + Status = gBS->DisconnectController (ControllerHandle, NULL, NULL); + if (EFI_ERROR (Status)) { + // + // Disconnect failed. Need to promote reconnect to a reboot. + // + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } else { + gBS->ConnectController (ControllerHandle, NULL, NULL, TRUE); + } + break; + + default: + break; + } +} + +/** + Update the form to include the driver health instances. + + @param ConfigureOnly Only include the configure required driver health instances + when TRUE, include all the driver health instances otherwise. +**/ +VOID +DriverHealthManagerUpdateForm ( + BOOLEAN ConfigureOnly + ) +{ + EFI_STATUS Status; + EFI_IFR_GUID_LABEL *StartLabel; + EFI_IFR_GUID_LABEL *EndLabel; + VOID *StartOpCodeHandle; + VOID *EndOpCodeHandle; + UINTN Index; + EFI_STRING_ID Prompt; + EFI_STRING_ID Help; + CHAR16 String[512]; + UINTN StringCount; + EFI_STRING TmpString; + EFI_STRING DriverName; + EFI_STRING ControllerName; + UINTN MessageIndex; + EFI_HANDLE DriverHandle; + EFI_STRING_ID DevicePath; + EFI_GUID FormsetGuid; + + EfiBootManagerFreeDriverHealthInfo (mDriverHealthManagerHealthInfo, mDriverHealthManagerHealthInfoCount); + mDriverHealthManagerHealthInfo = EfiBootManagerGetDriverHealthInfo (&mDriverHealthManagerHealthInfoCount); + + // + // Allocate space for creation of UpdateData Buffer + // + StartOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (StartOpCodeHandle != NULL); + + EndOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (EndOpCodeHandle != NULL); + + // + // Create Hii Extend Label OpCode as the start opcode + // + StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); + StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + StartLabel->Number = LABEL_BEGIN; + + // + // Create Hii Extend Label OpCode as the end opcode + // + EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); + EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + EndLabel->Number = LABEL_END; + + for (Index = 0; Index < mDriverHealthManagerHealthInfoCount; Index++) { + if (ConfigureOnly && mDriverHealthManagerHealthInfo[Index].HealthStatus != EfiDriverHealthStatusConfigurationRequired) { + continue; + } + DriverName = DriverHealthManagerGetDriverName (mDriverHealthManagerHealthInfo[Index].DriverHealthHandle); + ASSERT (DriverName != NULL); + + if (mDriverHealthManagerHealthInfo[Index].ControllerHandle == NULL) { + // + // The ControllerHandle is set to NULL and the HealthStatus is set to EfiDriverHealthStatusHealthy + // if all the controllers managed by the driver are in healthy state. + // + ASSERT (mDriverHealthManagerHealthInfo[Index].HealthStatus == EfiDriverHealthStatusHealthy); + UnicodeSPrint (String, sizeof (String), L"%s", DriverName); + } else { + ControllerName = DriverHealthManagerGetControllerName ( + mDriverHealthManagerHealthInfo[Index].DriverHealthHandle, + mDriverHealthManagerHealthInfo[Index].ControllerHandle, + mDriverHealthManagerHealthInfo[Index].ChildHandle + ); + ASSERT (ControllerName != NULL); + UnicodeSPrint (String, sizeof (String), L"%s %s", DriverName, ControllerName); + FreePool (ControllerName); + } + FreePool (DriverName); + + Prompt = HiiSetString (mDriverHealthManagerHiiHandle, 0, String, NULL); + + switch(mDriverHealthManagerHealthInfo[Index].HealthStatus) { + case EfiDriverHealthStatusRepairRequired: + TmpString = HiiGetString (mDriverHealthManagerHiiHandle, STRING_TOKEN (STR_REPAIR_REQUIRED), NULL); + break; + case EfiDriverHealthStatusConfigurationRequired: + TmpString = HiiGetString (mDriverHealthManagerHiiHandle, STRING_TOKEN (STR_CONFIGURATION_REQUIRED), NULL); + break; + case EfiDriverHealthStatusFailed: + TmpString = HiiGetString (mDriverHealthManagerHiiHandle, STRING_TOKEN (STR_FAILED), NULL); + break; + case EfiDriverHealthStatusReconnectRequired: + TmpString = HiiGetString (mDriverHealthManagerHiiHandle, STRING_TOKEN (STR_RECONNECT_REQUIRED), NULL); + break; + case EfiDriverHealthStatusRebootRequired: + TmpString = HiiGetString (mDriverHealthManagerHiiHandle, STRING_TOKEN (STR_REBOOT_REQUIRED), NULL); + break; + default: + ASSERT (mDriverHealthManagerHealthInfo[Index].HealthStatus == EfiDriverHealthStatusHealthy); + TmpString = HiiGetString (mDriverHealthManagerHiiHandle, STRING_TOKEN (STR_HEALTHY), NULL); + break; + } + StringCount = UnicodeSPrint (String, sizeof (String), L"%s\n", TmpString); + FreePool (TmpString); + + // + // Add the message of the Module itself provided as the help. + // + if (mDriverHealthManagerHealthInfo[Index].MessageList != NULL) { + for (MessageIndex = 0; mDriverHealthManagerHealthInfo[Index].MessageList[MessageIndex].HiiHandle != NULL; MessageIndex++) { + TmpString = HiiGetString ( + mDriverHealthManagerHealthInfo[Index].MessageList[MessageIndex].HiiHandle, + mDriverHealthManagerHealthInfo[Index].MessageList[MessageIndex].StringId, + NULL + ); + StringCount += UnicodeSPrint (String + StringCount, sizeof (String) - sizeof (String[0]) * StringCount, L"\n%s", TmpString); + FreePool (TmpString); + } + } + Help = HiiSetString (mDriverHealthManagerHiiHandle, 0, String, NULL); + + switch (mDriverHealthManagerHealthInfo[Index].HealthStatus) { + case EfiDriverHealthStatusConfigurationRequired: + Status = mDriverHealthManagerDatabase->GetPackageListHandle ( + mDriverHealthManagerDatabase, + mDriverHealthManagerHealthInfo[Index].HiiHandle, + &DriverHandle + ); + ASSERT_EFI_ERROR (Status); + TmpString = ConvertDevicePathToText (DevicePathFromHandle (DriverHandle), FALSE, TRUE); + DevicePath = HiiSetString (mDriverHealthManagerHiiHandle, 0, TmpString, NULL); + FreePool (TmpString); + + Status = DriverHealthManagerGetFormsetId (mDriverHealthManagerHealthInfo[Index].HiiHandle, &FormsetGuid); + ASSERT_EFI_ERROR (Status); + + HiiCreateGotoExOpCode ( + StartOpCodeHandle, + 0, + Prompt, + Help, + 0, + 0, + 0, + &FormsetGuid, + DevicePath + ); + break; + + case EfiDriverHealthStatusRepairRequired: + case EfiDriverHealthStatusReconnectRequired: + case EfiDriverHealthStatusRebootRequired: + HiiCreateActionOpCode ( + StartOpCodeHandle, + (EFI_QUESTION_ID) (Index + QUESTION_ID_DRIVER_HEALTH_BASE), + Prompt, + Help, + EFI_IFR_FLAG_CALLBACK, + 0 + ); + break; + + default: + ASSERT (mDriverHealthManagerHealthInfo[Index].HealthStatus == EfiDriverHealthStatusHealthy || + mDriverHealthManagerHealthInfo[Index].HealthStatus == EfiDriverHealthStatusFailed); + HiiCreateTextOpCode ( + StartOpCodeHandle, + Prompt, + Help, + 0 + ); + break; + } + } + + Status = HiiUpdateForm ( + mDriverHealthManagerHiiHandle, + ConfigureOnly ? PcdGetPtr (PcdDriverHealthConfigureForm) : &mDriverHealthManagerForm, + DRIVER_HEALTH_FORM_ID, + StartOpCodeHandle, + EndOpCodeHandle + ); + ASSERT_EFI_ERROR (Status); + + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); +} + +/** + Called when the form is closing to remove the dynamicly added string from the HII package list. +**/ +VOID +DriverHealthManagerCleanDynamicString ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HII_PACKAGE_LIST_HEADER *HiiPackageList; + UINTN BufferSize; + EFI_HII_PACKAGE_HEADER *PackageHeader; + UINT32 FixedStringSize; + + FixedStringSize = *(UINT32 *) &STRING_ARRAY_NAME - sizeof (UINT32); + BufferSize = sizeof (EFI_HII_PACKAGE_LIST_HEADER) + FixedStringSize + sizeof (EFI_HII_PACKAGE_HEADER); + HiiPackageList = AllocatePool (BufferSize); + ASSERT (HiiPackageList != NULL); + + HiiPackageList->PackageLength = (UINT32) BufferSize; + CopyMem (&HiiPackageList->PackageListGuid, &gEfiCallerIdGuid, sizeof (EFI_GUID)); + + PackageHeader = (EFI_HII_PACKAGE_HEADER *) (HiiPackageList + 1); + CopyMem (PackageHeader, STRING_ARRAY_NAME + sizeof (UINT32), FixedStringSize); + + PackageHeader = (EFI_HII_PACKAGE_HEADER *) ((UINT8 *) PackageHeader + PackageHeader->Length); + PackageHeader->Type = EFI_HII_PACKAGE_END; + PackageHeader->Length = sizeof (EFI_HII_PACKAGE_HEADER); + + Status = mDriverHealthManagerDatabase->UpdatePackageList ( + mDriverHealthManagerDatabase, + mDriverHealthManagerHiiHandle, + HiiPackageList + ); + ASSERT_EFI_ERROR (Status); + + // + // Form package not found in this Package List + // + FreePool (HiiPackageList); +} + +/** + This function is invoked if user selected a interactive opcode from Driver Health's + Formset. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original exporting driver + so that it can identify the type of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original exporting driver. + @param ActionRequest On return, points to the action requested by the callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_INVALID_PARAMETER The setup browser call this function with invalid parameters. + +**/ +EFI_STATUS +EFIAPI +DriverHealthManagerCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + UINTN Index; + + if (QuestionId == QUESTION_ID_REFRESH_MANAGER || QuestionId == QUESTION_ID_REFRESH_CONFIGURE) { + if (Action == EFI_BROWSER_ACTION_FORM_OPEN) { + DriverHealthManagerUpdateForm ((BOOLEAN) (QuestionId == QUESTION_ID_REFRESH_CONFIGURE)); + } else if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) { + DriverHealthManagerCleanDynamicString (); + } + return EFI_SUCCESS; + } + + if (Action != EFI_BROWSER_ACTION_CHANGED) { + // + // Do nothing for other UEFI Action. Only do call back when data is changed. + // + return EFI_UNSUPPORTED; + } + + if ((Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((EFI_D_ERROR, "QuestionId = %x\n", QuestionId)); + + // + // We will have returned from processing a callback - user either hit ESC to exit, or selected + // a target to display. + // Process the diver health status states here. + // + Index = QuestionId - QUESTION_ID_DRIVER_HEALTH_BASE; + ASSERT (Index < mDriverHealthManagerHealthInfoCount); + // + // Process the driver's healthy status for the specify module + // + DriverHealthManagerProcessSingleControllerHealth ( + mDriverHealthManagerHealthInfo[Index].DriverHealth, + mDriverHealthManagerHealthInfo[Index].ControllerHandle, + mDriverHealthManagerHealthInfo[Index].ChildHandle, + mDriverHealthManagerHealthInfo[Index].HealthStatus, + &(mDriverHealthManagerHealthInfo[Index].MessageList), + mDriverHealthManagerHealthInfo[Index].HiiHandle + ); + + DriverHealthManagerUpdateForm ((BOOLEAN) (QuestionId == QUESTION_ID_REFRESH_CONFIGURE)); + + return EFI_SUCCESS; +} + + diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.h b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.h new file mode 100644 index 000000000..f4a09d03a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.h @@ -0,0 +1,127 @@ +/** @file + This module produces two driver health manager forms. + One will be used by BDS core to configure the Configured Required + driver health instances, the other will be automatically included by + firmware setup (UI). + +Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DRIVER_HEALTH_MANAGEMENT_DXE_H_ +#define _DRIVER_HEALTH_MANAGEMENT_DXE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// +/// HII specific Vendor Device Path definition. +/// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} FORM_DEVICE_PATH; + +/** + This function is invoked if user selected a interactive opcode from Driver Health's + Formset. The decision by user is saved to gCallbackKey for later processing. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original exporting driver + so that it can identify the type of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original exporting driver. + @param ActionRequest On return, points to the action requested by the callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_INVALID_PARAMETER The setup browser call this function with invalid parameters. + +**/ +EFI_STATUS +EFIAPI +DriverHealthManagerCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ); + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request A null-terminated Unicode string in format. + @param Progress On return, points to a character in the Request string. + Points to the string's null terminator if request was successful. + Points to the most recent '&' before the first failing name/value + pair (or the beginning of the string if the failure is in the + first name/value pair) if the request was not successful. + @param Results A null-terminated Unicode string in format which + has all values filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +DriverHealthManagerFakeExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ); + +/** + This function processes the results of changes in configuration. + + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration A null-terminated Unicode string in format. + @param Progress A pointer to a string filled in with the offset of the most + recent '&' before the first failing name/value pair (or the + beginning of the string if the failure is in the first + name/value pair) or the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +DriverHealthManagerFakeRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ); +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf new file mode 100644 index 000000000..f6310b234 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf @@ -0,0 +1,74 @@ +## @file +# Driver Health Manager DXE driver. +# +# This module produces two driver health manager forms. +# One will be used by BDS core to configure the Configured Required +# driver health instances, the other will be automatically included by +# firmware setup (UI). +# +# Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## +################################################################################ +# +# Defines Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DriverHealthManagerDxe + MODULE_UNI_FILE = DriverHealthManagerDxe.uni + FILE_GUID = EBF8ED7C-0DD1-4787-84F1-F48D537DCACF + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeDriverHealthManager + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + + +[Sources.common] + DriverHealthManagerDxe.h + DriverHealthManagerDxe.c + DriverHealthManagerStrings.uni + DriverHealthManagerVfr.Vfr + DriverHealthManagerVfr.h + DriverHealthConfigureVfr.Vfr + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + HiiLib + UefiBootManagerLib + PcdLib + DevicePathLib + +[Protocols] + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + +[Guids] + gEfiHiiDriverHealthFormsetGuid ## CONSUMES ## GUID + gEfiIfrTianoGuid ## CONSUMES ## HII + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdDriverHealthConfigureForm ## CONSUMES + +[Depex] + gEfiHiiDatabaseProtocolGuid AND gEfiFormBrowser2ProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + DriverHealthManagerDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.uni b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.uni new file mode 100644 index 000000000..67677fcb4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.uni @@ -0,0 +1,18 @@ +// /** @file +// Driver Health Manager DXE driver. +// +// This module produces two driver health manager forms. +// One will be used by BDS core to configure the Configured Required +// driver health instances, the other will be automatically included by +// firmware setup (UI). +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Driver Health Manager DXE driver" + +#string STR_MODULE_DESCRIPTION #language en-US "This module produces two driver health manager forms. One will be used by BDS core to configure the Configured Required driver health instances, the other will be automatically included by firmware setup (UI)." + diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxeExtra.uni new file mode 100644 index 000000000..d71e2722b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxeExtra.uni @@ -0,0 +1,19 @@ +// /** @file +// Driver Health Manager DXE driver. +// +// This module produces two driver health manager forms. +// One will be used by BDS core to configure the Configured Required +// driver health instances, the other will be automatically included by +// firmware setup (UI). +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Driver Health Manager DXE driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerStrings.uni b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerStrings.uni new file mode 100644 index 000000000..0bdf68095 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerStrings.uni @@ -0,0 +1,34 @@ +///** @file +// +// String definitions for the DriverHealthManager. +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent + + +//**/ + +/=# + +#langdef en-US "English" +#langdef fr-FR "Français" + +#string STR_FORM_TITLE #language en-US "Driver Health Manager" + #language fr-FR "Driver Health Manager" +#string STR_FORM_HELP #language en-US "List all the Driver Health instances to manage" + #language fr-FR "List all the Driver Health instances to manage" +#string STR_NULL #language en-US "" + #lauguage fr-FR "" + +#string STR_REPAIR_REQUIRED #language en-US "Repair Required." + #language fr-FR "Repair Required." +#string STR_CONFIGURATION_REQUIRED #language en-US "Configuration Required." + #language fr-FR "Configuration Required." +#string STR_FAILED #language en-US "Failed." + #language fr-FR "Failed." +#string STR_RECONNECT_REQUIRED #language en-US "Reconnect Required." + #language fr-FR "Reconnect Required." +#string STR_REBOOT_REQUIRED #language en-US "Reboot Required." + #language fr-FR "Reboot Required." +#string STR_HEALTHY #language en-US "Healthy." + #language fr-FR "Healthy." diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.Vfr b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.Vfr new file mode 100644 index 000000000..4c19bfcba --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.Vfr @@ -0,0 +1,32 @@ +///** @file +// +// VFR to produce the formset used by UI. +// +// Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +//**/ + +#include "DriverHealthManagerVfr.h" + +formset + guid = DRIVER_HEALTH_MANAGER_FORMSET_GUID, + title = STRING_TOKEN(STR_FORM_TITLE), + help = STRING_TOKEN(STR_FORM_HELP), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + form formid = DRIVER_HEALTH_FORM_ID, + title = STRING_TOKEN(STR_FORM_TITLE); + + label LABEL_BEGIN; + label LABEL_END; + + suppressif TRUE; + text + help = STRING_TOKEN(STR_NULL), + text = STRING_TOKEN(STR_NULL), + flags = INTERACTIVE, + key = QUESTION_ID_REFRESH_MANAGER; + endif; + + endform; +endformset; diff --git a/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.h b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.h new file mode 100644 index 000000000..65255c57c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerVfr.h @@ -0,0 +1,26 @@ +/** @file + Definition shared by VFR file and C file. + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DRIVER_HEALTH_VFR_H_ +#define _DRIVER_HEALTH_VFR_H_ +#include + +#define DRIVER_HEALTH_MANAGER_FORMSET_GUID { 0xcfb3b000, 0x0b63, 0x444b, { 0xb1, 0xd1, 0x12, 0xd5, 0xd9, 0x5d, 0xc4, 0xfc } } +#define DRIVER_HEALTH_CONFIGURE_FORMSET_GUID { 0x4296d9f4, 0xf6fc, 0x4dde, { 0x86, 0x85, 0x8c, 0xe2, 0xd7, 0x9d, 0x90, 0xf0 } } + +#define LABEL_BEGIN 0x2000 +#define LABEL_END 0x2001 + +#define DRIVER_HEALTH_FORM_ID 0x1001 + +#define QUESTION_ID_REFRESH_MANAGER 0x0001 +#define QUESTION_ID_REFRESH_CONFIGURE 0x0002 + +#define QUESTION_ID_DRIVER_HEALTH_BASE 0x0003 + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.c b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.c new file mode 100644 index 000000000..f98797225 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.c @@ -0,0 +1,2239 @@ +/** @file +This is an example of how a driver might export data to the HII protocol to be +later utilized by the Setup Protocol + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "DriverSample.h" + +#define DISPLAY_ONLY_MY_ITEM 0x0002 + +CHAR16 VariableName[] = L"MyIfrNVData"; +CHAR16 MyEfiVar[] = L"MyEfiVar"; +CHAR16 MyEfiBitVar[] = L"MyEfiBitVar"; +CHAR16 MyEfiUnionVar[] = L"MyEfiUnionVar"; + +EFI_HANDLE DriverHandle[2] = {NULL, NULL}; +DRIVER_SAMPLE_PRIVATE_DATA *mPrivateData = NULL; +EFI_EVENT mEvent; + +HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath0 = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + DRIVER_SAMPLE_FORMSET_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath1 = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + DRIVER_SAMPLE_INVENTORY_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ) +{ + + ASSERT (Array != NULL); + + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + *(((UINT8 *) Array) + Index) = (UINT8) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + *(((UINT16 *) Array) + Index) = (UINT16) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + *(((UINT32 *) Array) + Index) = (UINT32) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + *(((UINT64 *) Array) + Index) = (UINT64) Value; + break; + + default: + break; + } +} + +/** + Notification function for keystrokes. + + @param[in] KeyData The key that was pressed. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +EFIAPI +NotificationFunction( + IN EFI_KEY_DATA *KeyData + ) +{ + gBS->SignalEvent (mEvent); + + return EFI_SUCCESS; +} + +/** + Function to start monitoring for CTRL-C using SimpleTextInputEx. + + @retval EFI_SUCCESS The feature is enabled. + @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available. +**/ +EFI_STATUS +EFIAPI +InternalStartMonitor( + VOID + ) +{ + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; + EFI_KEY_DATA KeyData; + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN HandleIndex; + VOID *NotifyHandle; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextInputExProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { + Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &SimpleEx); + ASSERT_EFI_ERROR (Status); + + KeyData.KeyState.KeyToggleState = 0; + KeyData.Key.ScanCode = 0; + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; + KeyData.Key.UnicodeChar = L'c'; + + Status = SimpleEx->RegisterKeyNotify( + SimpleEx, + &KeyData, + NotificationFunction, + &NotifyHandle); + if (EFI_ERROR (Status)) { + break; + } + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; + Status = SimpleEx->RegisterKeyNotify( + SimpleEx, + &KeyData, + NotificationFunction, + &NotifyHandle); + if (EFI_ERROR (Status)) { + break; + } + } + + return EFI_SUCCESS; +} + +/** + Function to stop monitoring for CTRL-C using SimpleTextInputEx. + + @retval EFI_SUCCESS The feature is enabled. + @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available. +**/ +EFI_STATUS +EFIAPI +InternalStopMonitor( + VOID + ) +{ + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; + EFI_STATUS Status; + EFI_HANDLE *Handles; + EFI_KEY_DATA KeyData; + UINTN HandleCount; + UINTN HandleIndex; + VOID *NotifyHandle; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextInputExProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { + Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &SimpleEx); + ASSERT_EFI_ERROR (Status); + + KeyData.KeyState.KeyToggleState = 0; + KeyData.Key.ScanCode = 0; + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; + KeyData.Key.UnicodeChar = L'c'; + + Status = SimpleEx->RegisterKeyNotify( + SimpleEx, + &KeyData, + NotificationFunction, + &NotifyHandle); + if (!EFI_ERROR (Status)) { + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, NotifyHandle); + } + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; + Status = SimpleEx->RegisterKeyNotify( + SimpleEx, + &KeyData, + NotificationFunction, + &NotifyHandle); + if (!EFI_ERROR (Status)) { + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, NotifyHandle); + } + } + return EFI_SUCCESS; +} + +/** + Update names of Name/Value storage to current language. + + @param PrivateData Points to the driver private data. + + @retval EFI_SUCCESS All names are successfully updated. + @retval EFI_NOT_FOUND Failed to get Name from HII database. + +**/ +EFI_STATUS +LoadNameValueNames ( + IN DRIVER_SAMPLE_PRIVATE_DATA *PrivateData + ) +{ + UINTN Index; + + // + // Get Name/Value name string of current language + // + for (Index = 0; Index < NAME_VALUE_NAME_NUMBER; Index++) { + PrivateData->NameValueName[Index] = HiiGetString ( + PrivateData->HiiHandle[0], + PrivateData->NameStringId[Index], + NULL + ); + if (PrivateData->NameValueName[Index] == NULL) { + return EFI_NOT_FOUND; + } + } + + return EFI_SUCCESS; +} + + +/** + Get the value of in format, i.e. the value of OFFSET + or WIDTH or VALUE. + ::= 'OFFSET='&'WIDTH='&'VALUE'= + + This is a internal function. + + @param StringPtr String in format and points to the + first character of . + @param Number The output value. Caller takes the responsibility + to free memory. + @param Len Length of the , in characters. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to store neccessary + structures. + @retval EFI_SUCCESS Value of is outputted in Number + successfully. + +**/ +EFI_STATUS +GetValueOfNumber ( + IN EFI_STRING StringPtr, + OUT UINT8 **Number, + OUT UINTN *Len + ) +{ + EFI_STRING TmpPtr; + UINTN Length; + EFI_STRING Str; + UINT8 *Buf; + EFI_STATUS Status; + UINT8 DigitUint8; + UINTN Index; + CHAR16 TemStr[2]; + + if (StringPtr == NULL || *StringPtr == L'\0' || Number == NULL || Len == NULL) { + return EFI_INVALID_PARAMETER; + } + + Buf = NULL; + + TmpPtr = StringPtr; + while (*StringPtr != L'\0' && *StringPtr != L'&') { + StringPtr++; + } + *Len = StringPtr - TmpPtr; + Length = *Len + 1; + + Str = (EFI_STRING) AllocateZeroPool (Length * sizeof (CHAR16)); + if (Str == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + CopyMem (Str, TmpPtr, *Len * sizeof (CHAR16)); + *(Str + *Len) = L'\0'; + + Length = (Length + 1) / 2; + Buf = (UINT8 *) AllocateZeroPool (Length); + if (Buf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Length = *Len; + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0; Index < Length; Index ++) { + TemStr[0] = Str[Length - Index - 1]; + DigitUint8 = (UINT8) StrHexToUint64 (TemStr); + if ((Index & 1) == 0) { + Buf [Index/2] = DigitUint8; + } else { + Buf [Index/2] = (UINT8) ((DigitUint8 << 4) + Buf [Index/2]); + } + } + + *Number = Buf; + Status = EFI_SUCCESS; + +Exit: + if (Str != NULL) { + FreePool (Str); + } + + return Status; +} + +/** + Create altcfg string. + + @param Result The request result string. + @param ConfigHdr The request head info. format. + @param Offset The offset of the parameter int he structure. + @param Width The width of the parameter. + + + @retval The string with altcfg info append at the end. +**/ +EFI_STRING +CreateAltCfgString ( + IN EFI_STRING Result, + IN EFI_STRING ConfigHdr, + IN UINTN Offset, + IN UINTN Width + ) +{ + EFI_STRING StringPtr; + EFI_STRING TmpStr; + UINTN NewLen; + + NewLen = StrLen (Result); + // + // String Len = ConfigResp + AltConfig + AltConfig + 1("\0") + // + NewLen = (NewLen + ((1 + StrLen (ConfigHdr) + 8 + 4) + (8 + 4 + 7 + 4 + 7 + 4)) * 2 + 1) * sizeof (CHAR16); + StringPtr = AllocateZeroPool (NewLen); + if (StringPtr == NULL) { + return NULL; + } + + TmpStr = StringPtr; + if (Result != NULL) { + StrCpyS (StringPtr, NewLen / sizeof (CHAR16), Result); + StringPtr += StrLen (Result); + FreePool (Result); + } + + UnicodeSPrint ( + StringPtr, + (1 + StrLen (ConfigHdr) + 8 + 4 + 1) * sizeof (CHAR16), + L"&%s&ALTCFG=%04x", + ConfigHdr, + EFI_HII_DEFAULT_CLASS_STANDARD + ); + StringPtr += StrLen (StringPtr); + + UnicodeSPrint ( + StringPtr, + (8 + 4 + 7 + 4 + 7 + 4 + 1) * sizeof (CHAR16), + L"&OFFSET=%04x&WIDTH=%04x&VALUE=%04x", + Offset, + Width, + DEFAULT_CLASS_STANDARD_VALUE + ); + StringPtr += StrLen (StringPtr); + + UnicodeSPrint ( + StringPtr, + (1 + StrLen (ConfigHdr) + 8 + 4 + 1) * sizeof (CHAR16), + L"&%s&ALTCFG=%04x", + ConfigHdr, + EFI_HII_DEFAULT_CLASS_MANUFACTURING + ); + StringPtr += StrLen (StringPtr); + + UnicodeSPrint ( + StringPtr, + (8 + 4 + 7 + 4 + 7 + 4 + 1) * sizeof (CHAR16), + L"&OFFSET=%04x&WIDTH=%04x&VALUE=%04x", + Offset, + Width, + DEFAULT_CLASS_MANUFACTURING_VALUE + ); + StringPtr += StrLen (StringPtr); + + return TmpStr; +} + +/** + Check whether need to add the altcfg string. if need to add, add the altcfg + string. + + @param RequestResult The request result string. + @param ConfigRequestHdr The request head info. format. + +**/ +VOID +AppendAltCfgString ( + IN OUT EFI_STRING *RequestResult, + IN EFI_STRING ConfigRequestHdr + ) +{ + EFI_STRING StringPtr; + UINTN Length; + UINT8 *TmpBuffer; + UINTN Offset; + UINTN Width; + UINTN BlockSize; + UINTN ValueOffset; + UINTN ValueWidth; + EFI_STATUS Status; + + TmpBuffer = NULL; + StringPtr = *RequestResult; + StringPtr = StrStr (StringPtr, L"OFFSET"); + BlockSize = sizeof (DRIVER_SAMPLE_CONFIGURATION); + ValueOffset = OFFSET_OF (DRIVER_SAMPLE_CONFIGURATION, GetDefaultValueFromAccess); + ValueWidth = sizeof (((DRIVER_SAMPLE_CONFIGURATION *)0)->GetDefaultValueFromAccess); + + if (StringPtr == NULL) { + return; + } + + while (*StringPtr != 0 && StrnCmp (StringPtr, L"OFFSET=", StrLen (L"OFFSET=")) == 0) { + StringPtr += StrLen (L"OFFSET="); + // + // Get Offset + // + Status = GetValueOfNumber (StringPtr, &TmpBuffer, &Length); + if (EFI_ERROR (Status)) { + return; + } + Offset = 0; + CopyMem ( + &Offset, + TmpBuffer, + (((Length + 1) / 2) < sizeof (UINTN)) ? ((Length + 1) / 2) : sizeof (UINTN) + ); + FreePool (TmpBuffer); + + StringPtr += Length; + if (StrnCmp (StringPtr, L"&WIDTH=", StrLen (L"&WIDTH=")) != 0) { + return; + } + StringPtr += StrLen (L"&WIDTH="); + + // + // Get Width + // + Status = GetValueOfNumber (StringPtr, &TmpBuffer, &Length); + if (EFI_ERROR (Status)) { + return; + } + Width = 0; + CopyMem ( + &Width, + TmpBuffer, + (((Length + 1) / 2) < sizeof (UINTN)) ? ((Length + 1) / 2) : sizeof (UINTN) + ); + FreePool (TmpBuffer); + + StringPtr += Length; + if (StrnCmp (StringPtr, L"&VALUE=", StrLen (L"&VALUE=")) != 0) { + return; + } + StringPtr += StrLen (L"&VALUE="); + + // + // Get Value + // + Status = GetValueOfNumber (StringPtr, &TmpBuffer, &Length); + if (EFI_ERROR (Status)) { + return; + } + StringPtr += Length; + + // + // Skip the character "&" before "OFFSET". + // + StringPtr ++; + + // + // Calculate Value and convert it to hex string. + // + if (Offset + Width > BlockSize) { + return; + } + + if (Offset <= ValueOffset && Offset + Width >= ValueOffset + ValueWidth) { + *RequestResult = CreateAltCfgString(*RequestResult, ConfigRequestHdr, ValueOffset, ValueWidth); + return; + } + } +} + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request A null-terminated Unicode string in + format. + @param Progress On return, points to a character in the Request + string. Points to the string's null terminator if + request was successful. Points to the most recent + '&' before the first failing name/value pair (or + the beginning of the string if the failure is in + the first name/value pair) if the request was not + successful. + @param Results A null-terminated Unicode string in + format which has all values filled + in for the names in the Request string. String to + be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +ExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + DRIVER_SAMPLE_PRIVATE_DATA *PrivateData; + EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting; + EFI_STRING ConfigRequest; + EFI_STRING ConfigRequestHdr; + UINTN Size; + EFI_STRING Value; + UINTN ValueStrLen; + CHAR16 BackupChar; + CHAR16 *StrPointer; + BOOLEAN AllocatedRequest; + + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Initialize the local variables. + // + ConfigRequestHdr = NULL; + ConfigRequest = NULL; + Size = 0; + *Progress = Request; + AllocatedRequest = FALSE; + + PrivateData = DRIVER_SAMPLE_PRIVATE_FROM_THIS (This); + HiiConfigRouting = PrivateData->HiiConfigRouting; + + // + // Get Buffer Storage data from EFI variable. + // Try to get the current setting from variable. + // + BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION); + Status = gRT->GetVariable ( + VariableName, + &gDriverSampleFormSetGuid, + NULL, + &BufferSize, + &PrivateData->Configuration + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + if (Request == NULL) { + // + // Request is set to NULL, construct full request string. + // + + // + // Allocate and fill a buffer large enough to hold the template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator + // + ConfigRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, VariableName, PrivateData->DriverHandle[0]); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + ASSERT (ConfigRequest != NULL); + AllocatedRequest = TRUE; + UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); + FreePool (ConfigRequestHdr); + ConfigRequestHdr = NULL; + } else { + // + // Check routing data in . + // Note: if only one Storage is used, then this checking could be skipped. + // + if (!HiiIsConfigHdrMatch (Request, &gDriverSampleFormSetGuid, NULL)) { + return EFI_NOT_FOUND; + } + // + // Check whether request for EFI Varstore. EFI varstore get data + // through hii database, not support in this path. + // + if (HiiIsConfigHdrMatch(Request, &gDriverSampleFormSetGuid, MyEfiVar)) { + return EFI_UNSUPPORTED; + } + if (HiiIsConfigHdrMatch(Request, &gDriverSampleFormSetGuid, MyEfiBitVar)) { + return EFI_UNSUPPORTED; + } + if (HiiIsConfigHdrMatch(Request, &gDriverSampleFormSetGuid, MyEfiUnionVar)) { + return EFI_UNSUPPORTED; + } + + // + // Set Request to the unified request string. + // + ConfigRequest = Request; + // + // Check whether Request includes Request Element. + // + if (StrStr (Request, L"OFFSET") == NULL) { + // + // Check Request Element does exist in Reques String + // + StrPointer = StrStr (Request, L"PATH"); + if (StrPointer == NULL) { + return EFI_INVALID_PARAMETER; + } + if (StrStr (StrPointer, L"&") == NULL) { + Size = (StrLen (Request) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + ASSERT (ConfigRequest != NULL); + AllocatedRequest = TRUE; + UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", Request, (UINT64)BufferSize); + } + } + } + + // + // Check if requesting Name/Value storage + // + if (StrStr (ConfigRequest, L"OFFSET") == NULL) { + // + // Update Name/Value storage Names + // + Status = LoadNameValueNames (PrivateData); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Allocate memory for , e.g. Name0=0x11, Name1=0x1234, Name2="ABCD" + // ::=&Name0&Name1&Name2 + // ::=&Name0=11&Name1=1234&Name2=0041004200430044 + // + BufferSize = (StrLen (ConfigRequest) + + 1 + sizeof (PrivateData->Configuration.NameValueVar0) * 2 + + 1 + sizeof (PrivateData->Configuration.NameValueVar1) * 2 + + 1 + sizeof (PrivateData->Configuration.NameValueVar2) * 2 + 1) * sizeof (CHAR16); + *Results = AllocateZeroPool (BufferSize); + ASSERT (*Results != NULL); + StrCpyS (*Results, BufferSize / sizeof (CHAR16), ConfigRequest); + Value = *Results; + + // + // Append value of NameValueVar0, type is UINT8 + // + if ((Value = StrStr (*Results, PrivateData->NameValueName[0])) != NULL) { + Value += StrLen (PrivateData->NameValueName[0]); + ValueStrLen = ((sizeof (PrivateData->Configuration.NameValueVar0) * 2) + 1); + CopyMem (Value + ValueStrLen, Value, StrSize (Value)); + + BackupChar = Value[ValueStrLen]; + *Value++ = L'='; + UnicodeValueToStringS ( + Value, + BufferSize - ((UINTN)Value - (UINTN)*Results), + PREFIX_ZERO | RADIX_HEX, + PrivateData->Configuration.NameValueVar0, + sizeof (PrivateData->Configuration.NameValueVar0) * 2 + ); + Value += StrnLenS (Value, (BufferSize - ((UINTN)Value - (UINTN)*Results)) / sizeof (CHAR16)); + *Value = BackupChar; + } + + // + // Append value of NameValueVar1, type is UINT16 + // + if ((Value = StrStr (*Results, PrivateData->NameValueName[1])) != NULL) { + Value += StrLen (PrivateData->NameValueName[1]); + ValueStrLen = ((sizeof (PrivateData->Configuration.NameValueVar1) * 2) + 1); + CopyMem (Value + ValueStrLen, Value, StrSize (Value)); + + BackupChar = Value[ValueStrLen]; + *Value++ = L'='; + UnicodeValueToStringS ( + Value, + BufferSize - ((UINTN)Value - (UINTN)*Results), + PREFIX_ZERO | RADIX_HEX, + PrivateData->Configuration.NameValueVar1, + sizeof (PrivateData->Configuration.NameValueVar1) * 2 + ); + Value += StrnLenS (Value, (BufferSize - ((UINTN)Value - (UINTN)*Results)) / sizeof (CHAR16)); + *Value = BackupChar; + } + + // + // Append value of NameValueVar2, type is CHAR16 * + // + if ((Value = StrStr (*Results, PrivateData->NameValueName[2])) != NULL) { + Value += StrLen (PrivateData->NameValueName[2]); + ValueStrLen = StrLen (PrivateData->Configuration.NameValueVar2) * 4 + 1; + CopyMem (Value + ValueStrLen, Value, StrSize (Value)); + + *Value++ = L'='; + // + // Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044" + // + StrPointer = (CHAR16 *) PrivateData->Configuration.NameValueVar2; + for (; *StrPointer != L'\0'; StrPointer++) { + UnicodeValueToStringS ( + Value, + BufferSize - ((UINTN)Value - (UINTN)*Results), + PREFIX_ZERO | RADIX_HEX, + *StrPointer, + 4 + ); + Value += StrnLenS (Value, (BufferSize - ((UINTN)Value - (UINTN)*Results)) / sizeof (CHAR16)); + } + } + + Status = EFI_SUCCESS; + } else { + // + // Convert buffer data to by helper function BlockToConfig() + // + Status = HiiConfigRouting->BlockToConfig ( + HiiConfigRouting, + ConfigRequest, + (UINT8 *) &PrivateData->Configuration, + BufferSize, + Results, + Progress + ); + if (!EFI_ERROR (Status)) { + ConfigRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, VariableName, PrivateData->DriverHandle[0]); + AppendAltCfgString(Results, ConfigRequestHdr); + } + } + + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + } + + if (ConfigRequestHdr != NULL) { + FreePool (ConfigRequestHdr); + } + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return Status; +} + + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration A null-terminated Unicode string in + format. + @param Progress A pointer to a string filled in with the offset of + the most recent '&' before the first failing + name/value pair (or the beginning of the string if + the failure is in the first name/value pair) or + the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this + driver. + +**/ +EFI_STATUS +EFIAPI +RouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + DRIVER_SAMPLE_PRIVATE_DATA *PrivateData; + EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting; + CHAR16 *Value; + CHAR16 *StrPtr; + CHAR16 TemStr[5]; + UINT8 *DataBuffer; + UINT8 DigitUint8; + UINTN Index; + CHAR16 *StrBuffer; + + if (Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + PrivateData = DRIVER_SAMPLE_PRIVATE_FROM_THIS (This); + HiiConfigRouting = PrivateData->HiiConfigRouting; + *Progress = Configuration; + + // + // Check routing data in . + // Note: if only one Storage is used, then this checking could be skipped. + // + if (!HiiIsConfigHdrMatch (Configuration, &gDriverSampleFormSetGuid, NULL)) { + return EFI_NOT_FOUND; + } + + // + // Check whether request for EFI Varstore. EFI varstore get data + // through hii database, not support in this path. + // + if (HiiIsConfigHdrMatch(Configuration, &gDriverSampleFormSetGuid, MyEfiVar)) { + return EFI_UNSUPPORTED; + } + if (HiiIsConfigHdrMatch(Configuration, &gDriverSampleFormSetGuid, MyEfiBitVar)) { + return EFI_UNSUPPORTED; + } + if (HiiIsConfigHdrMatch(Configuration, &gDriverSampleFormSetGuid, MyEfiUnionVar)) { + return EFI_UNSUPPORTED; + } + + // + // Get Buffer Storage data from EFI variable + // + BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION); + Status = gRT->GetVariable ( + VariableName, + &gDriverSampleFormSetGuid, + NULL, + &BufferSize, + &PrivateData->Configuration + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check if configuring Name/Value storage + // + if (StrStr (Configuration, L"OFFSET") == NULL) { + // + // Update Name/Value storage Names + // + Status = LoadNameValueNames (PrivateData); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Convert value for NameValueVar0 + // + if ((Value = StrStr (Configuration, PrivateData->NameValueName[0])) != NULL) { + // + // Skip "Name=" + // + Value += StrLen (PrivateData->NameValueName[0]); + Value++; + // + // Get Value String + // + StrPtr = StrStr (Value, L"&"); + if (StrPtr == NULL) { + StrPtr = Value + StrLen (Value); + } + // + // Convert Value to Buffer data + // + DataBuffer = (UINT8 *) &PrivateData->Configuration.NameValueVar0; + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0, StrPtr --; StrPtr >= Value; StrPtr --, Index ++) { + TemStr[0] = *StrPtr; + DigitUint8 = (UINT8) StrHexToUint64 (TemStr); + if ((Index & 1) == 0) { + DataBuffer [Index/2] = DigitUint8; + } else { + DataBuffer [Index/2] = (UINT8) ((UINT8) (DigitUint8 << 4) + DataBuffer [Index/2]); + } + } + } + + // + // Convert value for NameValueVar1 + // + if ((Value = StrStr (Configuration, PrivateData->NameValueName[1])) != NULL) { + // + // Skip "Name=" + // + Value += StrLen (PrivateData->NameValueName[1]); + Value++; + // + // Get Value String + // + StrPtr = StrStr (Value, L"&"); + if (StrPtr == NULL) { + StrPtr = Value + StrLen (Value); + } + // + // Convert Value to Buffer data + // + DataBuffer = (UINT8 *) &PrivateData->Configuration.NameValueVar1; + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0, StrPtr --; StrPtr >= Value; StrPtr --, Index ++) { + TemStr[0] = *StrPtr; + DigitUint8 = (UINT8) StrHexToUint64 (TemStr); + if ((Index & 1) == 0) { + DataBuffer [Index/2] = DigitUint8; + } else { + DataBuffer [Index/2] = (UINT8) ((UINT8) (DigitUint8 << 4) + DataBuffer [Index/2]); + } + } + } + + // + // Convert value for NameValueVar2 + // + if ((Value = StrStr (Configuration, PrivateData->NameValueName[2])) != NULL) { + // + // Skip "Name=" + // + Value += StrLen (PrivateData->NameValueName[2]); + Value++; + // + // Get Value String + // + StrPtr = StrStr (Value, L"&"); + if (StrPtr == NULL) { + StrPtr = Value + StrLen (Value); + } + // + // Convert Config String to Unicode String, e.g "0041004200430044" => "ABCD" + // + StrBuffer = (CHAR16 *) PrivateData->Configuration.NameValueVar2; + ZeroMem (TemStr, sizeof (TemStr)); + while (Value < StrPtr) { + StrnCpyS (TemStr, sizeof (TemStr) / sizeof (CHAR16), Value, 4); + *(StrBuffer++) = (CHAR16) StrHexToUint64 (TemStr); + Value += 4; + } + *StrBuffer = L'\0'; + } + + // + // Store Buffer Storage back to EFI variable + // + Status = gRT->SetVariable( + VariableName, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (DRIVER_SAMPLE_CONFIGURATION), + &PrivateData->Configuration + ); + + return Status; + } + + // + // Convert to buffer data by helper function ConfigToBlock() + // + BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION); + Status = HiiConfigRouting->ConfigToBlock ( + HiiConfigRouting, + Configuration, + (UINT8 *) &PrivateData->Configuration, + &BufferSize, + Progress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Store Buffer Storage back to EFI variable + // + Status = gRT->SetVariable( + VariableName, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (DRIVER_SAMPLE_CONFIGURATION), + &PrivateData->Configuration + ); + + return Status; +} + + +/** + This function processes the results of changes in configuration. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original + exporting driver. + @param ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. + +**/ +EFI_STATUS +EFIAPI +DriverCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + DRIVER_SAMPLE_PRIVATE_DATA *PrivateData; + EFI_STATUS Status; + VOID *StartOpCodeHandle; + VOID *OptionsOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + EFI_INPUT_KEY Key; + DRIVER_SAMPLE_CONFIGURATION *Configuration; + MY_EFI_VARSTORE_DATA *EfiData; + EFI_FORM_ID FormId; + EFI_STRING Progress; + EFI_STRING Results; + UINT32 ProgressErr; + CHAR16 *TmpStr; + UINTN Index; + UINT64 BufferValue; + EFI_HII_POPUP_SELECTION UserSelection; + + UserSelection = 0xFF; + + if (((Value == NULL) && (Action != EFI_BROWSER_ACTION_FORM_OPEN) && (Action != EFI_BROWSER_ACTION_FORM_CLOSE))|| + (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + + FormId = 0; + ProgressErr = 0; + Status = EFI_SUCCESS; + BufferValue = 3; + PrivateData = DRIVER_SAMPLE_PRIVATE_FROM_THIS (This); + + switch (Action) { + case EFI_BROWSER_ACTION_FORM_OPEN: + { + if (QuestionId == 0x1234) { + // + // Sample CallBack for UEFI FORM_OPEN action: + // Add Save action into Form 3 when Form 1 is opened. + // This will be done only in FORM_OPEN CallBack of question with ID 0x1234 from Form 1. + // + PrivateData = DRIVER_SAMPLE_PRIVATE_FROM_THIS (This); + + // + // Initialize the container for dynamic opcodes + // + StartOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (StartOpCodeHandle != NULL); + + // + // Create Hii Extend Label OpCode as the start opcode + // + StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); + StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + StartLabel->Number = LABEL_UPDATE2; + + HiiCreateActionOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + 0x1238, // Question ID + STRING_TOKEN(STR_SAVE_TEXT), // Prompt text + STRING_TOKEN(STR_SAVE_TEXT), // Help text + EFI_IFR_FLAG_CALLBACK, // Question flag + 0 // Action String ID + ); + + HiiUpdateForm ( + PrivateData->HiiHandle[0], // HII handle + &gDriverSampleFormSetGuid, // Formset GUID + 0x3, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + NULL // Insert data + ); + + HiiFreeOpCodeHandle (StartOpCodeHandle); + } + + if (QuestionId == 0x1247) { + Status = InternalStartMonitor (); + ASSERT_EFI_ERROR (Status); + } + } + break; + + case EFI_BROWSER_ACTION_FORM_CLOSE: + { + if (QuestionId == 0x5678) { + // + // Sample CallBack for UEFI FORM_CLOSE action: + // Show up a pop-up to specify Form 3 will be closed when exit Form 3. + // + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"You are going to leave third Form!", + L"Press ESC or ENTER to continue ...", + L"", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + } + + if (QuestionId == 0x1247) { + Status = InternalStopMonitor (); + ASSERT_EFI_ERROR (Status); + } + } + break; + + case EFI_BROWSER_ACTION_RETRIEVE: + { + switch (QuestionId ) { + case 0x1248: + if (Type != EFI_IFR_TYPE_REF) { + return EFI_INVALID_PARAMETER; + } + Value->ref.FormId = 0x3; + break; + + case 0x5678: + case 0x1247: + // + // We will reach here once the Question is refreshed + // + + // + // Initialize the container for dynamic opcodes + // + StartOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (StartOpCodeHandle != NULL); + + // + // Create Hii Extend Label OpCode as the start opcode + // + StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); + StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + if (QuestionId == 0x5678) { + StartLabel->Number = LABEL_UPDATE2; + FormId = 0x03; + PrivateData->Configuration.DynamicRefresh++; + } else if (QuestionId == 0x1247 ) { + StartLabel->Number = LABEL_UPDATE3; + FormId = 0x06; + PrivateData->Configuration.RefreshGuidCount++; + } + + HiiCreateActionOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + 0x1237, // Question ID + STRING_TOKEN(STR_EXIT_TEXT), // Prompt text + STRING_TOKEN(STR_EXIT_TEXT), // Help text + EFI_IFR_FLAG_CALLBACK, // Question flag + 0 // Action String ID + ); + + HiiUpdateForm ( + PrivateData->HiiHandle[0], // HII handle + &gDriverSampleFormSetGuid, // Formset GUID + FormId, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + NULL // Insert data + ); + + HiiFreeOpCodeHandle (StartOpCodeHandle); + + // + // Refresh the Question value + // + Status = gRT->SetVariable( + VariableName, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (DRIVER_SAMPLE_CONFIGURATION), + &PrivateData->Configuration + ); + + if (QuestionId == 0x5678) { + // + // Update uncommitted data of Browser + // + EfiData = AllocateZeroPool (sizeof (MY_EFI_VARSTORE_DATA)); + ASSERT (EfiData != NULL); + if (HiiGetBrowserData (&gDriverSampleFormSetGuid, MyEfiVar, sizeof (MY_EFI_VARSTORE_DATA), (UINT8 *) EfiData)) { + EfiData->Field8 = 111; + HiiSetBrowserData ( + &gDriverSampleFormSetGuid, + MyEfiVar, + sizeof (MY_EFI_VARSTORE_DATA), + (UINT8 *) EfiData, + NULL + ); + } + FreePool (EfiData); + } + break; + } + } + break; + + case EFI_BROWSER_ACTION_DEFAULT_STANDARD: + { + switch (QuestionId) { + case 0x1240: + Value->u8 = DEFAULT_CLASS_STANDARD_VALUE; + break; + + case 0x1252: + for (Index = 0; Index < 3; Index ++) { + SetArrayData (Value, EFI_IFR_TYPE_NUM_SIZE_8, Index, BufferValue--); + } + break; + + case 0x6666: + Value->u8 = 12; + break; + + default: + Status = EFI_UNSUPPORTED; + break; + } + } + break; + + case EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING: + { + switch (QuestionId) { + case 0x1240: + Value->u8 = DEFAULT_CLASS_MANUFACTURING_VALUE; + break; + + case 0x6666: + Value->u8 = 13; + break; + + default: + Status = EFI_UNSUPPORTED; + break; + } + } + break; + + case EFI_BROWSER_ACTION_CHANGING: + { + switch (QuestionId) { + case 0x1249: + { + if (Type != EFI_IFR_TYPE_REF) { + return EFI_INVALID_PARAMETER; + } + + Value->ref.FormId = 0x1234; + } + break; + case 0x1234: + // + // Initialize the container for dynamic opcodes + // + StartOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (StartOpCodeHandle != NULL); + + EndOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (EndOpCodeHandle != NULL); + + // + // Create Hii Extend Label OpCode as the start opcode + // + StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); + StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + StartLabel->Number = LABEL_UPDATE1; + + // + // Create Hii Extend Label OpCode as the end opcode + // + EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); + EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + EndLabel->Number = LABEL_END; + + HiiCreateActionOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + 0x1237, // Question ID + STRING_TOKEN(STR_EXIT_TEXT), // Prompt text + STRING_TOKEN(STR_EXIT_TEXT), // Help text + EFI_IFR_FLAG_CALLBACK, // Question flag + 0 // Action String ID + ); + + // + // Create Option OpCode + // + OptionsOpCodeHandle = HiiAllocateOpCodeHandle (); + ASSERT (OptionsOpCodeHandle != NULL); + + HiiCreateOneOfOptionOpCode ( + OptionsOpCodeHandle, + STRING_TOKEN (STR_BOOT_OPTION1), + 0, + EFI_IFR_NUMERIC_SIZE_1, + 1 + ); + + HiiCreateOneOfOptionOpCode ( + OptionsOpCodeHandle, + STRING_TOKEN (STR_BOOT_OPTION2), + 0, + EFI_IFR_NUMERIC_SIZE_1, + 2 + ); + + // + // Prepare initial value for the dynamic created oneof Question + // + PrivateData->Configuration.DynamicOneof = 2; + Status = gRT->SetVariable( + VariableName, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (DRIVER_SAMPLE_CONFIGURATION), + &PrivateData->Configuration + ); + + // + // Set initial vlaue of dynamic created oneof Question in Form Browser + // + Configuration = AllocateZeroPool (sizeof (DRIVER_SAMPLE_CONFIGURATION)); + ASSERT (Configuration != NULL); + if (HiiGetBrowserData (&gDriverSampleFormSetGuid, VariableName, sizeof (DRIVER_SAMPLE_CONFIGURATION), (UINT8 *) Configuration)) { + Configuration->DynamicOneof = 2; + + // + // Update uncommitted data of Browser + // + HiiSetBrowserData ( + &gDriverSampleFormSetGuid, + VariableName, + sizeof (DRIVER_SAMPLE_CONFIGURATION), + (UINT8 *) Configuration, + NULL + ); + } + FreePool (Configuration); + + HiiCreateOneOfOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + 0x8001, // Question ID (or call it "key") + CONFIGURATION_VARSTORE_ID, // VarStore ID + (UINT16) DYNAMIC_ONE_OF_VAR_OFFSET, // Offset in Buffer Storage + STRING_TOKEN (STR_ONE_OF_PROMPT), // Question prompt text + STRING_TOKEN (STR_ONE_OF_HELP), // Question help text + EFI_IFR_FLAG_CALLBACK, // Question flag + EFI_IFR_NUMERIC_SIZE_1, // Data type of Question Value + OptionsOpCodeHandle, // Option Opcode list + NULL // Default Opcode is NULl + ); + + HiiCreateOrderedListOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + 0x8002, // Question ID + CONFIGURATION_VARSTORE_ID, // VarStore ID + (UINT16) DYNAMIC_ORDERED_LIST_VAR_OFFSET, // Offset in Buffer Storage + STRING_TOKEN (STR_BOOT_OPTIONS), // Question prompt text + STRING_TOKEN (STR_BOOT_OPTIONS), // Question help text + EFI_IFR_FLAG_RESET_REQUIRED, // Question flag + 0, // Ordered list flag, e.g. EFI_IFR_UNIQUE_SET + EFI_IFR_NUMERIC_SIZE_1, // Data type of Question value + 5, // Maximum container + OptionsOpCodeHandle, // Option Opcode list + NULL // Default Opcode is NULl + ); + + HiiCreateTextOpCode ( + StartOpCodeHandle, + STRING_TOKEN(STR_TEXT_SAMPLE_HELP), + STRING_TOKEN(STR_TEXT_SAMPLE_HELP), + STRING_TOKEN(STR_TEXT_SAMPLE_STRING) + ); + + HiiCreateDateOpCode ( + StartOpCodeHandle, + 0x8004, + 0x0, + 0x0, + STRING_TOKEN(STR_DATE_SAMPLE_HELP), + STRING_TOKEN(STR_DATE_SAMPLE_HELP), + 0, + QF_DATE_STORAGE_TIME, + NULL + ); + + HiiCreateTimeOpCode ( + StartOpCodeHandle, + 0x8005, + 0x0, + 0x0, + STRING_TOKEN(STR_TIME_SAMPLE_HELP), + STRING_TOKEN(STR_TIME_SAMPLE_HELP), + 0, + QF_TIME_STORAGE_TIME, + NULL + ); + + HiiCreateGotoOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + 1, // Target Form ID + STRING_TOKEN (STR_GOTO_FORM1), // Prompt text + STRING_TOKEN (STR_GOTO_HELP), // Help text + 0, // Question flag + 0x8003 // Question ID + ); + + HiiUpdateForm ( + PrivateData->HiiHandle[0], // HII handle + &gDriverSampleFormSetGuid, // Formset GUID + 0x1234, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (OptionsOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); + break; + + default: + break; + } + } + break; + + case EFI_BROWSER_ACTION_CHANGED: + switch (QuestionId) { + case 0x1237: + // + // User press "Exit now", request Browser to exit + // + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + break; + + case 0x1238: + // + // User press "Save now", request Browser to save the uncommitted data. + // + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; + break; + + case 0x1241: + case 0x1246: + // + // User press "Submit current form and Exit now", request Browser to submit current form and exit + // + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT; + break; + + case 0x1242: + // + // User press "Discard current form now", request Browser to discard the uncommitted data. + // + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD; + break; + + case 0x1243: + // + // User press "Submit current form now", request Browser to save the uncommitted data. + // + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + break; + + case 0x1244: + case 0x1245: + // + // User press "Discard current form and Exit now", request Browser to discard the uncommitted data and exit. + // + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; + break; + + case 0x1231: + // + // 1. Check to see whether system support keyword. + // + Status = PrivateData->HiiKeywordHandler->GetData (PrivateData->HiiKeywordHandler, + L"NAMESPACE=x-UEFI-ns", + L"KEYWORD=iSCSIBootEnable", + &Progress, + &ProgressErr, + &Results + ); + if (EFI_ERROR (Status)) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"This system not support this keyword!", + L"Press ENTER to continue ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + Status = EFI_SUCCESS; + break; + } + + // + // 2. If system support this keyword, just try to change value. + // + + // + // Change value from '0' to '1' or from '1' to '0' + // + TmpStr = StrStr (Results, L"&VALUE="); + ASSERT (TmpStr != NULL); + TmpStr += StrLen (L"&VALUE="); + TmpStr++; + if (*TmpStr == L'0') { + *TmpStr = L'1'; + } else { + *TmpStr = L'0'; + } + + // + // 3. Call the keyword handler protocol to change the value. + // + Status = PrivateData->HiiKeywordHandler->SetData (PrivateData->HiiKeywordHandler, + Results, + &Progress, + &ProgressErr + ); + if (EFI_ERROR (Status)) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Set keyword to the system failed!", + L"Press ENTER to continue ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + Status = EFI_SUCCESS; + break; + } + break; + + case 0x1330: + Status = mPrivateData->HiiPopup->CreatePopup ( + mPrivateData->HiiPopup, + EfiHiiPopupStyleInfo, + EfiHiiPopupTypeYesNo, + mPrivateData->HiiHandle[0], + STRING_TOKEN (STR_POPUP_STRING), + &UserSelection + ); + if (!EFI_ERROR (Status)) { + if (UserSelection == EfiHiiPopupSelectionYes) { + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + } + } + break; + + default: + break; + } + break; + + case EFI_BROWSER_ACTION_SUBMITTED: + { + if (QuestionId == 0x1250) { + // + // Sample CallBack for EFI_BROWSER_ACTION_SUBMITTED action: + // Show up a pop-up to show SUBMITTED callback has been triggered. + // + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"EfiVarstore value has been submitted!", + L"Press ESC or ENTER to continue ...", + L"", + NULL + ); + } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN)); + } + } + break; + + default: + Status = EFI_UNSUPPORTED; + break; + } + + return Status; +} + +/** + Main entry for this driver. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to SystemTable. + + @retval EFI_SUCESS This function always complete successfully. + +**/ +EFI_STATUS +EFIAPI +DriverSampleInit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HII_HANDLE HiiHandle[2]; + EFI_SCREEN_DESCRIPTOR Screen; + EFI_HII_DATABASE_PROTOCOL *HiiDatabase; + EFI_HII_STRING_PROTOCOL *HiiString; + EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2; + EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting; + EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *HiiKeywordHandler; + EFI_HII_POPUP_PROTOCOL *PopupHandler; + CHAR16 *NewString; + UINTN BufferSize; + DRIVER_SAMPLE_CONFIGURATION *Configuration; + BOOLEAN ActionFlag; + EFI_STRING ConfigRequestHdr; + EFI_STRING NameRequestHdr; + MY_EFI_VARSTORE_DATA *VarStoreConfig; + MY_EFI_BITS_VARSTORE_DATA *BitsVarStoreConfig; + MY_EFI_UNION_DATA *UnionConfig; + EFI_INPUT_KEY HotKey; + EDKII_FORM_BROWSER_EXTENSION_PROTOCOL *FormBrowserEx; + + // + // Initialize the local variables. + // + ConfigRequestHdr = NULL; + NewString = NULL; + + // + // Initialize screen dimensions for SendForm(). + // Remove 3 characters from top and bottom + // + ZeroMem (&Screen, sizeof (EFI_SCREEN_DESCRIPTOR)); + gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Screen.RightColumn, &Screen.BottomRow); + + Screen.TopRow = 3; + Screen.BottomRow = Screen.BottomRow - 3; + + // + // Initialize driver private data + // + mPrivateData = AllocateZeroPool (sizeof (DRIVER_SAMPLE_PRIVATE_DATA)); + if (mPrivateData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + mPrivateData->Signature = DRIVER_SAMPLE_PRIVATE_SIGNATURE; + + mPrivateData->ConfigAccess.ExtractConfig = ExtractConfig; + mPrivateData->ConfigAccess.RouteConfig = RouteConfig; + mPrivateData->ConfigAccess.Callback = DriverCallback; + + // + // Locate Hii Database protocol + // + Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **) &HiiDatabase); + if (EFI_ERROR (Status)) { + return Status; + } + mPrivateData->HiiDatabase = HiiDatabase; + + // + // Locate HiiString protocol + // + Status = gBS->LocateProtocol (&gEfiHiiStringProtocolGuid, NULL, (VOID **) &HiiString); + if (EFI_ERROR (Status)) { + return Status; + } + mPrivateData->HiiString = HiiString; + + // + // Locate Formbrowser2 protocol + // + Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2); + if (EFI_ERROR (Status)) { + return Status; + } + mPrivateData->FormBrowser2 = FormBrowser2; + + // + // Locate ConfigRouting protocol + // + Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **) &HiiConfigRouting); + if (EFI_ERROR (Status)) { + return Status; + } + mPrivateData->HiiConfigRouting = HiiConfigRouting; + + // + // Locate keyword handler protocol + // + Status = gBS->LocateProtocol (&gEfiConfigKeywordHandlerProtocolGuid, NULL, (VOID **) &HiiKeywordHandler); + if (EFI_ERROR (Status)) { + return Status; + } + mPrivateData->HiiKeywordHandler = HiiKeywordHandler; + + // + // Locate HiiPopup protocol + // + Status = gBS->LocateProtocol (&gEfiHiiPopupProtocolGuid, NULL, (VOID **) &PopupHandler); + if (EFI_ERROR (Status)) { + return Status; + } + mPrivateData->HiiPopup = PopupHandler; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &DriverHandle[0], + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath0, + &gEfiHiiConfigAccessProtocolGuid, + &mPrivateData->ConfigAccess, + NULL + ); + ASSERT_EFI_ERROR (Status); + + mPrivateData->DriverHandle[0] = DriverHandle[0]; + + // + // Publish our HII data + // + HiiHandle[0] = HiiAddPackages ( + &gDriverSampleFormSetGuid, + DriverHandle[0], + DriverSampleStrings, + VfrBin, + NULL + ); + if (HiiHandle[0] == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + mPrivateData->HiiHandle[0] = HiiHandle[0]; + + // + // Publish another Fromset + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &DriverHandle[1], + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath1, + &gEfiHiiConfigAccessProtocolGuid, + &mPrivateData->ConfigAccess, + NULL + ); + ASSERT_EFI_ERROR (Status); + + mPrivateData->DriverHandle[1] = DriverHandle[1]; + + HiiHandle[1] = HiiAddPackages ( + &gDriverSampleInventoryGuid, + DriverHandle[1], + DriverSampleStrings, + InventoryBin, + NULL + ); + if (HiiHandle[1] == NULL) { + DriverSampleUnload (ImageHandle); + return EFI_OUT_OF_RESOURCES; + } + + mPrivateData->HiiHandle[1] = HiiHandle[1]; + + // + // Update the device path string. + // + NewString = ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*)&mHiiVendorDevicePath0, FALSE, FALSE); + if (HiiSetString (HiiHandle[0], STRING_TOKEN (STR_DEVICE_PATH), NewString, NULL) == 0) { + DriverSampleUnload (ImageHandle); + return EFI_OUT_OF_RESOURCES; + } + if (NewString != NULL) { + FreePool (NewString); + } + + // + // Very simple example of how one would update a string that is already + // in the HII database + // + NewString = L"700 Mhz"; + + if (HiiSetString (HiiHandle[0], STRING_TOKEN (STR_CPU_STRING2), NewString, NULL) == 0) { + DriverSampleUnload (ImageHandle); + return EFI_OUT_OF_RESOURCES; + } + + HiiSetString (HiiHandle[0], 0, NewString, NULL); + + // + // Initialize Name/Value name String ID + // + mPrivateData->NameStringId[0] = STR_NAME_VALUE_VAR_NAME0; + mPrivateData->NameStringId[1] = STR_NAME_VALUE_VAR_NAME1; + mPrivateData->NameStringId[2] = STR_NAME_VALUE_VAR_NAME2; + + // + // Initialize configuration data + // + Configuration = &mPrivateData->Configuration; + ZeroMem (Configuration, sizeof (DRIVER_SAMPLE_CONFIGURATION)); + + // + // Try to read NV config EFI variable first + // + ConfigRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, VariableName, DriverHandle[0]); + ASSERT (ConfigRequestHdr != NULL); + + NameRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, NULL, DriverHandle[0]); + ASSERT (NameRequestHdr != NULL); + + BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION); + Status = gRT->GetVariable (VariableName, &gDriverSampleFormSetGuid, NULL, &BufferSize, Configuration); + if (EFI_ERROR (Status)) { + // + // Store zero data Buffer Storage to EFI variable + // + Status = gRT->SetVariable( + VariableName, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (DRIVER_SAMPLE_CONFIGURATION), + Configuration + ); + if (EFI_ERROR (Status)) { + DriverSampleUnload (ImageHandle); + return Status; + } + // + // EFI variable for NV config doesn't exit, we should build this variable + // based on default values stored in IFR + // + ActionFlag = HiiSetToDefaults (NameRequestHdr, EFI_HII_DEFAULT_CLASS_STANDARD); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + + ActionFlag = HiiSetToDefaults (ConfigRequestHdr, EFI_HII_DEFAULT_CLASS_STANDARD); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } else { + // + // EFI variable does exist and Validate Current Setting + // + ActionFlag = HiiValidateSettings (NameRequestHdr); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + + ActionFlag = HiiValidateSettings (ConfigRequestHdr); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } + FreePool (ConfigRequestHdr); + + // + // Initialize efi varstore configuration data + // + VarStoreConfig = &mPrivateData->VarStoreConfig; + ZeroMem (VarStoreConfig, sizeof (MY_EFI_VARSTORE_DATA)); + + ConfigRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, MyEfiVar, DriverHandle[0]); + ASSERT (ConfigRequestHdr != NULL); + + BufferSize = sizeof (MY_EFI_VARSTORE_DATA); + Status = gRT->GetVariable (MyEfiVar, &gDriverSampleFormSetGuid, NULL, &BufferSize, VarStoreConfig); + if (EFI_ERROR (Status)) { + // + // Store zero data to EFI variable Storage. + // + Status = gRT->SetVariable( + MyEfiVar, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (MY_EFI_VARSTORE_DATA), + VarStoreConfig + ); + if (EFI_ERROR (Status)) { + DriverSampleUnload (ImageHandle); + return Status; + } + // + // EFI variable for NV config doesn't exit, we should build this variable + // based on default values stored in IFR + // + ActionFlag = HiiSetToDefaults (ConfigRequestHdr, EFI_HII_DEFAULT_CLASS_STANDARD); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } else { + // + // EFI variable does exist and Validate Current Setting + // + ActionFlag = HiiValidateSettings (ConfigRequestHdr); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } + FreePool (ConfigRequestHdr); + + // + // Initialize Bits efi varstore configuration data + // + BitsVarStoreConfig = &mPrivateData->BitsVarStoreConfig; + ZeroMem (BitsVarStoreConfig, sizeof (MY_EFI_BITS_VARSTORE_DATA)); + + ConfigRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, MyEfiBitVar, DriverHandle[0]); + ASSERT (ConfigRequestHdr != NULL); + + BufferSize = sizeof (MY_EFI_BITS_VARSTORE_DATA); + Status = gRT->GetVariable (MyEfiBitVar, &gDriverSampleFormSetGuid, NULL, &BufferSize, BitsVarStoreConfig); + if (EFI_ERROR (Status)) { + // + // Store zero data to EFI variable Storage. + // + Status = gRT->SetVariable( + MyEfiBitVar, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (MY_EFI_BITS_VARSTORE_DATA), + BitsVarStoreConfig + ); + if (EFI_ERROR (Status)) { + DriverSampleUnload (ImageHandle); + return Status; + } + // + // EFI variable for NV config doesn't exit, we should build this variable + // based on default values stored in IFR + // + ActionFlag = HiiSetToDefaults (ConfigRequestHdr, EFI_HII_DEFAULT_CLASS_STANDARD); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } else { + // + // EFI variable does exist and Validate Current Setting + // + ActionFlag = HiiValidateSettings (ConfigRequestHdr); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } + FreePool (ConfigRequestHdr); + + // + // Initialize Union efi varstore configuration data + // + UnionConfig = &mPrivateData->UnionConfig; + ZeroMem (UnionConfig, sizeof (MY_EFI_UNION_DATA)); + + ConfigRequestHdr = HiiConstructConfigHdr (&gDriverSampleFormSetGuid, MyEfiUnionVar, DriverHandle[0]); + ASSERT (ConfigRequestHdr != NULL); + + BufferSize = sizeof (MY_EFI_UNION_DATA); + Status = gRT->GetVariable (MyEfiUnionVar, &gDriverSampleFormSetGuid, NULL, &BufferSize, UnionConfig); + if (EFI_ERROR (Status)) { + // + // Store zero data to EFI variable Storage. + // + Status = gRT->SetVariable( + MyEfiUnionVar, + &gDriverSampleFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (MY_EFI_UNION_DATA), + UnionConfig + ); + if (EFI_ERROR (Status)) { + DriverSampleUnload (ImageHandle); + return Status; + } + // + // EFI variable for NV config doesn't exit, we should build this variable + // based on default values stored in IFR + // + ActionFlag = HiiSetToDefaults (ConfigRequestHdr, EFI_HII_DEFAULT_CLASS_STANDARD); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } else { + // + // EFI variable does exist and Validate Current Setting + // + ActionFlag = HiiValidateSettings (ConfigRequestHdr); + if (!ActionFlag) { + DriverSampleUnload (ImageHandle); + return EFI_INVALID_PARAMETER; + } + } + FreePool (ConfigRequestHdr); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + EfiEventEmptyFunction, + NULL, + &gEfiIfrRefreshIdOpGuid, + &mEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // Example of how to use BrowserEx protocol to register HotKey. + // + Status = gBS->LocateProtocol (&gEdkiiFormBrowserExProtocolGuid, NULL, (VOID **) &FormBrowserEx); + if (!EFI_ERROR (Status)) { + // + // First unregister the default hot key F9 and F10. + // + HotKey.UnicodeChar = CHAR_NULL; + HotKey.ScanCode = SCAN_F9; + FormBrowserEx->RegisterHotKey (&HotKey, 0, 0, NULL); + HotKey.ScanCode = SCAN_F10; + FormBrowserEx->RegisterHotKey (&HotKey, 0, 0, NULL); + + // + // Register the default HotKey F9 and F10 again. + // + HotKey.ScanCode = SCAN_F10; + NewString = HiiGetString (mPrivateData->HiiHandle[0], STRING_TOKEN (FUNCTION_TEN_STRING), NULL); + ASSERT (NewString != NULL); + FormBrowserEx->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString); + HotKey.ScanCode = SCAN_F9; + NewString = HiiGetString (mPrivateData->HiiHandle[0], STRING_TOKEN (FUNCTION_NINE_STRING), NULL); + ASSERT (NewString != NULL); + FormBrowserEx->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString); + } + + // + // In default, this driver is built into Flash device image, + // the following code doesn't run. + // + + // + // Example of how to display only the item we sent to HII + // When this driver is not built into Flash device image, + // it need to call SendForm to show front page by itself. + // + if (DISPLAY_ONLY_MY_ITEM <= 1) { + // + // Have the browser pull out our copy of the data, and only display our data + // + Status = FormBrowser2->SendForm ( + FormBrowser2, + &(HiiHandle[DISPLAY_ONLY_MY_ITEM]), + 1, + NULL, + 0, + NULL, + NULL + ); + + HiiRemovePackages (HiiHandle[0]); + + HiiRemovePackages (HiiHandle[1]); + } + + return EFI_SUCCESS; +} + +/** + Unloads the application and its installed protocol. + + @param[in] ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. +**/ +EFI_STATUS +EFIAPI +DriverSampleUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + UINTN Index; + + ASSERT (mPrivateData != NULL); + + if (DriverHandle[0] != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + DriverHandle[0], + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath0, + &gEfiHiiConfigAccessProtocolGuid, + &mPrivateData->ConfigAccess, + NULL + ); + DriverHandle[0] = NULL; + } + + if (DriverHandle[1] != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + DriverHandle[1], + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath1, + &gEfiHiiConfigAccessProtocolGuid, + &mPrivateData->ConfigAccess, + NULL + ); + DriverHandle[1] = NULL; + } + + if (mPrivateData->HiiHandle[0] != NULL) { + HiiRemovePackages (mPrivateData->HiiHandle[0]); + } + + if (mPrivateData->HiiHandle[1] != NULL) { + HiiRemovePackages (mPrivateData->HiiHandle[1]); + } + + for (Index = 0; Index < NAME_VALUE_NAME_NUMBER; Index++) { + if (mPrivateData->NameValueName[Index] != NULL) { + FreePool (mPrivateData->NameValueName[Index]); + } + } + FreePool (mPrivateData); + mPrivateData = NULL; + + gBS->CloseEvent (mEvent); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.h b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.h new file mode 100644 index 000000000..f4d2437d8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.h @@ -0,0 +1,122 @@ +/** @file + +Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + DriverSample.h + +Abstract: + + +Revision History + + +**/ + +#ifndef _DRIVER_SAMPLE_H_ +#define _DRIVER_SAMPLE_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "NVDataStruc.h" + +// +// This is the generated IFR binary data for each formset defined in VFR. +// This data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 VfrBin[]; +extern UINT8 InventoryBin[]; + +// +// This is the generated String package data for all .UNI files. +// This data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 DriverSampleStrings[]; + +#define DYNAMIC_ONE_OF_VAR_OFFSET OFFSET_OF (DRIVER_SAMPLE_CONFIGURATION, DynamicOneof) +#define DYNAMIC_ORDERED_LIST_VAR_OFFSET OFFSET_OF (DRIVER_SAMPLE_CONFIGURATION, DynamicOrderedList) + +#define DEFAULT_CLASS_MANUFACTURING_VALUE 0xFF +#define DEFAULT_CLASS_STANDARD_VALUE 0x0 + +// +// Number of name in Name/Value storage +// +#define NAME_VALUE_NAME_NUMBER 3 + +#define DRIVER_SAMPLE_PRIVATE_SIGNATURE SIGNATURE_32 ('D', 'S', 'p', 's') + +typedef struct { + UINTN Signature; + + EFI_HANDLE DriverHandle[2]; + EFI_HII_HANDLE HiiHandle[2]; + DRIVER_SAMPLE_CONFIGURATION Configuration; + MY_EFI_VARSTORE_DATA VarStoreConfig; + MY_EFI_BITS_VARSTORE_DATA BitsVarStoreConfig; + MY_EFI_UNION_DATA UnionConfig; + + // + // Name/Value storage Name list + // + EFI_STRING_ID NameStringId[NAME_VALUE_NAME_NUMBER]; + EFI_STRING NameValueName[NAME_VALUE_NAME_NUMBER]; + + // + // Consumed protocol + // + EFI_HII_DATABASE_PROTOCOL *HiiDatabase; + EFI_HII_STRING_PROTOCOL *HiiString; + EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting; + EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *HiiKeywordHandler; + EFI_HII_POPUP_PROTOCOL *HiiPopup; + + EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2; + + // + // Produced protocol + // + EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess; +} DRIVER_SAMPLE_PRIVATE_DATA; + +#define DRIVER_SAMPLE_PRIVATE_FROM_THIS(a) CR (a, DRIVER_SAMPLE_PRIVATE_DATA, ConfigAccess, DRIVER_SAMPLE_PRIVATE_SIGNATURE) + +#pragma pack(1) + +/// +/// HII specific Vendor Device Path definition. +/// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +#pragma pack() + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.uni b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.uni new file mode 100644 index 000000000..e7f16c418 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSample.uni @@ -0,0 +1,17 @@ +// /** @file +// This is a sample HII driver. +// +// This driver shows how HII protocol, VFR and UNI files are used to create a HII +// driver which can be dipslayed and configured by a UEFI HII Form Browser. +// +// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "A sample HII driver" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver shows how HII protocol, VFR and UNI files are used to create a HII driver that can be displayed and configured by a UEFI HII Form Browser." + diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf new file mode 100644 index 000000000..a277f641d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf @@ -0,0 +1,96 @@ +## @file +# This is a sample HII driver. +# +# This driver shows how HII protocol, VFR and UNI files are used to create a HII +# driver which can be dipslayed and configured by a UEFI HII Form Browser. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DriverSample + MODULE_UNI_FILE = DriverSample.uni + FILE_GUID = FE3542FE-C1D3-4EF8-657C-8048606FF671 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DriverSampleInit + UNLOAD_IMAGE = DriverSampleUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + DriverSample.c + InventoryStrings.uni + NVDataStruc.h + VfrStrings.uni + DriverSample.h + Inventory.vfr + Vfr.vfr + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + HiiLib + PrintLib + UefiLib + DevicePathLib + +[Guids] + gEfiIfrTianoGuid ## PRODUCES ## UNDEFINED + gDriverSampleInventoryGuid ## CONSUMES ## HII + ## SOMETIMES_PRODUCES ## Event + ## CONSUMES ## Event + gEfiIfrRefreshIdOpGuid + ## CONSUMES ## HII + ## PRODUCES ## Variable:L"MyIfrNVData" + ## SOMETIMES_CONSUMES ## Variable:L"MyIfrNVData" + ## PRODUCES ## Variable:L"MyEfiVar" + ## SOMETIMES_CONSUMES ## Variable:L"MyEfiVar" + ## PRODUCES ## GUID # HiiConstructConfigHdr MyEfiVar + ## PRODUCES ## GUID # HiiConstructConfigHdr MyIfrNVData + ## SOMETIMES_CONSUMES ## GUID # HiiIsConfigHdrMatch MyEfiVar + ## SOMETIMES_CONSUMES ## GUID # HiiIsConfigHdrMatch MyIfrNVData + ## SOMETIMES_PRODUCES ## GUID # HiiGetBrowserData MyIfrNVData + ## SOMETIMES_CONSUMES ## GUID # HiiSetBrowserData MyIfrNVData + ## SOMETIMES_PRODUCES ## GUID # HiiGetBrowserData MyEfiVar + ## SOMETIMES_CONSUMES ## GUID # HiiSetBrowserData MyEfiVar + gDriverSampleFormSetGuid + +[Protocols] + ## PRODUCES # DriverSampleFormSet + ## PRODUCES # DriverSampleInventory + gEfiDevicePathProtocolGuid + gEfiHiiStringProtocolGuid ## CONSUMES + gEfiHiiConfigRoutingProtocolGuid ## CONSUMES + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiFormBrowser2ProtocolGuid ## CONSUMES + gEfiHiiDatabaseProtocolGuid ## CONSUMES + gEfiSimpleTextInputExProtocolGuid ## SOMETIMES_CONSUMES + gEdkiiFormBrowserExProtocolGuid ## CONSUMES + gEfiConfigKeywordHandlerProtocolGuid ## CONSUMES + gEfiHiiPopupProtocolGuid ## CONSUMES + +[Depex] + gEfiSimpleTextOutProtocolGuid AND gEfiHiiDatabaseProtocolGuid AND gEfiVariableArchProtocolGuid AND gEfiVariableWriteArchProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + DriverSampleExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleExtra.uni b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleExtra.uni new file mode 100644 index 000000000..15a8598e2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/DriverSampleExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// DriverSample Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"HII Sample DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Inventory.vfr b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Inventory.vfr new file mode 100644 index 000000000..dd3578a62 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Inventory.vfr @@ -0,0 +1,111 @@ +///** @file +// +// Sample Inventory Data +// +// Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +//**/ + +#include "NVDataStruc.h" + +formset + guid = DRIVER_SAMPLE_INVENTORY_GUID, + title = STRING_TOKEN(STR_INV_FORM_SET_TITLE), + help = STRING_TOKEN(STR_INV_FORM_SET_HELP), + + form formid = 1, + title = STRING_TOKEN(STR_INV_FORM1_TITLE); // note formid is a variable (for readability) (UINT16) - also added Form to the line to signify the Op-Code + + text + help = STRING_TOKEN(STR_INV_VERSION_HELP), + text = STRING_TOKEN(STR_INV_VERSION_TEXT), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT2), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT3), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT4), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + subtitle text = STRING_TOKEN(STR_INV_EMPTY_STRING); + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT5), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT6), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT7), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT8), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT9), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT10), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_INV_EMPTY_STRING), + text = STRING_TOKEN(STR_INV_VERSION_TEXT11), + text = STRING_TOKEN(STR_INV_EMPTY_STRING), + flags = 0, + key = 0; + + text + help = STRING_TOKEN(STR_CHECK_KEYWORD_SUPPORT), + text = STRING_TOKEN(STR_CHECK_KEYWORD_SUPPORT), + flags = INTERACTIVE, + key = 0x1231; + + subtitle text = STRING_TOKEN(STR_INV_EMPTY_STRING); + + subtitle text = STRING_TOKEN(STR_INV_VERSION_TEXT12); + + endform; + +endformset; diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/InventoryStrings.uni b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/InventoryStrings.uni new file mode 100644 index 000000000..836229699 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/InventoryStrings.uni @@ -0,0 +1,60 @@ +// *++ +// +// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// Module Name: +// +// InventoryStrings.uni +// +// Abstract: +// +// String definitions for Inventory file. +// +// Revision History: +// +// --*/ + + +/=# + +#langdef en-US "English" +#langdef fr-FR "Francais" + + +#string STR_INV_FORM_SET_TITLE #language en-US "ABC Information Sample" + #language fr-FR "Mi motor Español de arreglo" +#string STR_INV_FORM_SET_HELP #language en-US "The ABC Network Controller version information, which includes Firmware versions as well as supported characteristics" + #language fr-FR "The ABC Network Controller version information, which includes Firmware versions as well as supported characteristics" +#string STR_INV_FORM1_TITLE #language en-US "ABC Network Controller Version Data" + #language fr-FR "Mi Primero Arreglo Página" +#string STR_INV_VERSION_TEXT #language en-US "Firmware Revision Date: 02/03/2002" + #language fr-FR "Firmware Revision Date: 02/03/2002" +#string STR_INV_VERSION_HELP #language en-US "The date of the revision of the Firmware being used." + #language fr-FR "The date of the revision of the Firmware being used." +#string STR_INV_VERSION_TEXT2 #language en-US "Major Version: 6.32.5" + #language fr-FR "Major Version: 6.32.5" +#string STR_INV_VERSION_TEXT3 #language en-US "Patch Version: 1.02.53" + #language fr-FR "Patch Version: 1.02.53" +#string STR_INV_VERSION_TEXT4 #language en-US "Characteristics: 10/100 Mb/s" + #language fr-FR "Characteristics: 10/100 Mb/s" +#string STR_INV_VERSION_TEXT5 #language en-US " 3.3 V power usage" + #language fr-FR " 3.3 V power usage" +#string STR_INV_VERSION_TEXT6 #language en-US " 3K Transmit FIFO" + #language fr-FR " 3K Transmit FIFO" +#string STR_INV_VERSION_TEXT7 #language en-US " 3K Receive FIFO" + #language fr-FR " 3K Receive FIFO" +#string STR_INV_VERSION_TEXT8 #language en-US " TCP/UDP checksum offload" + #language fr-FR " TCP/UDP checksum offload" +#string STR_INV_VERSION_TEXT9 #language en-US " 128K Flash" + #language fr-FR " 128K Flash" +#string STR_INV_VERSION_TEXT10 #language en-US " 32-bit PCI" + #language fr-FR " 32-bit PCI" +#string STR_INV_VERSION_TEXT11 #language en-US " Intel® 82540EM" + #language fr-FR " Intel® 82540EM" +#string STR_INV_VERSION_TEXT12 #language en-US "Press ESC to exit." + #language fr-FR "Press ESC to exit." +#string STR_INV_EMPTY_STRING #language en-US "" + #language fr-FR "" + + diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/NVDataStruc.h b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/NVDataStruc.h new file mode 100644 index 000000000..861761768 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/NVDataStruc.h @@ -0,0 +1,129 @@ +/** @file + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + NVDataStruc.h + +Abstract: + + NVData structure used by the sample driver + +Revision History: + + +**/ + +#ifndef _NVDATASTRUC_H_ +#define _NVDATASTRUC_H_ + +#include +#include +#include +#include + +#define CONFIGURATION_VARSTORE_ID 0x1234 +#define BITS_VARSTORE_ID 0x2345 + +#pragma pack(1) + +// +// !!! For a structure with a series of bit fields and used as a storage in vfr file, and if the bit fields do not add up to the size of the defined type. +// In the C code use sizeof() to get the size the strucure, the results may vary form the compiler(VS,GCC...). +// But the size of the storage calculated by VfrCompiler is fixed (calculate with alignment). +// To avoid above case, we need to make the total bit width in the structure aligned with the size of the defined type for these bit fields. We can: +// 1. Add bit field (with/without name) with remianing with for padding. +// 2. Add unnamed bit field with 0 for padding, the amount of padding is determined by the alignment characteristics of the members of the structure. +// +typedef struct { + UINT16 NestByteField; + UINT8 : 1; // unamed field can be used for padding + UINT8 NestBitCheckbox : 1; + UINT8 NestBitOneof : 2; + UINT8 : 0; // Special width 0 can be used to force alignment at the next word boundary + UINT8 NestBitNumeric : 4; +} MY_BITS_DATA; + +typedef union { + UINT8 UnionNumeric; + UINT8 UnionNumericAlias; +} MY_EFI_UNION_DATA; + +typedef struct { + UINT16 MyStringData[40]; + UINT16 SomethingHiddenForHtml; + UINT8 HowOldAreYouInYearsManual; + UINT16 HowTallAreYouManual; + UINT8 HowOldAreYouInYears; + UINT16 HowTallAreYou; + UINT8 MyFavoriteNumber; + UINT8 TestLateCheck; + UINT8 TestLateCheck2; + UINT8 QuestionAboutTreeHugging; + UINT8 ChooseToActivateNuclearWeaponry; + UINT8 SuppressGrayOutSomething; + UINT8 OrderedList[8]; + UINT16 BootOrder[8]; + UINT8 BootOrderLarge; + UINT8 DynamicRefresh; + UINT8 DynamicOneof; + UINT8 DynamicOrderedList[5]; + UINT8 Reserved; + EFI_HII_REF RefData; + UINT8 NameValueVar0; + UINT16 NameValueVar1; + UINT16 NameValueVar2[20]; + UINT8 SerialPortNo; + UINT8 SerialPortStatus; + UINT16 SerialPortIo; + UINT8 SerialPortIrq; + UINT8 GetDefaultValueFromCallBack; + UINT8 GetDefaultValueFromAccess; + EFI_HII_TIME Time; + UINT8 RefreshGuidCount; + UINT8 Match2; + UINT8 GetDefaultValueFromCallBackForOrderedList[3]; + UINT8 BitCheckbox : 1; + UINT8 ReservedBits: 7; // Reserved bit fields for padding. + UINT16 BitOneof : 6; + UINT16 : 0; // Width 0 used to force alignment. + UINT16 BitNumeric : 12; + MY_BITS_DATA MyBitData; + MY_EFI_UNION_DATA MyUnionData; +} DRIVER_SAMPLE_CONFIGURATION; + +// +// 2nd NV data structure definition +// +typedef struct { + UINT8 Field8; + UINT16 Field16; + UINT8 OrderedList[3]; + UINT16 SubmittedCallback; +} MY_EFI_VARSTORE_DATA; + +// +// 3rd NV data structure definition +// +typedef struct { + MY_BITS_DATA BitsData; + UINT32 EfiBitGrayoutTest : 5; + UINT32 EfiBitNumeric : 4; + UINT32 EfiBitOneof : 10; + UINT32 EfiBitCheckbox : 1; + UINT32 : 0; // Width 0 used to force alignment. +} MY_EFI_BITS_VARSTORE_DATA; + +// +// Labels definition +// +#define LABEL_UPDATE1 0x1234 +#define LABEL_UPDATE2 0x2234 +#define LABEL_UPDATE3 0x3234 +#define LABEL_END 0x2223 + +#pragma pack() + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Vfr.vfr b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Vfr.vfr new file mode 100644 index 000000000..65a65d4d1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/Vfr.vfr @@ -0,0 +1,932 @@ +///** @file +// +// Sample Setup formset. +// +// Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +//**/ + + +#include +#include "NVDataStruc.h" + +// +// Formset class used by Device Manager +// +#define EFI_NON_DEVICE_CLASS 0x00 +#define EFI_DISK_DEVICE_CLASS 0x01 +#define EFI_VIDEO_DEVICE_CLASS 0x02 +#define EFI_NETWORK_DEVICE_CLASS 0x04 +#define EFI_INPUT_DEVICE_CLASS 0x08 +#define EFI_ON_BOARD_DEVICE_CLASS 0x10 +#define EFI_OTHER_DEVICE_CLASS 0x20 + +// +// Formset subclass +// +#define EFI_SETUP_APPLICATION_SUBCLASS 0x00 +#define EFI_GENERAL_APPLICATION_SUBCLASS 0x01 +#define EFI_FRONT_PAGE_SUBCLASS 0x02 +#define EFI_SINGLE_USE_SUBCLASS 0x03 + +#define EFI_USER_INFO_ACCESS_SETUP_ADMIN_GUID \ + { 0x85b75607, 0xf7ce, 0x471e, { 0xb7, 0xe4, 0x2a, 0xea, 0x5f, 0x72, 0x32, 0xee } } + +#define PERL_GUID \ + { 0x63E60A51, 0x497D, 0xD427, {0xC4, 0xA5, 0xB8, 0xAB, 0xDC, 0x3A, 0xAE, 0xB6 }} + +// +// Labels definition +// +#define LABEL_1_VALUE 0x01 +#define LABEL_2_VALUE 0x1000 +#define LABEL_UPDATE_BBS 0x2222 + +formset + guid = DRIVER_SAMPLE_FORMSET_GUID, + title = STRING_TOKEN(STR_FORM_SET_TITLE), + help = STRING_TOKEN(STR_FORM_SET_TITLE_HELP), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + // + // Notes: VfrCompiler will insert a Standard Default Storage declaration + // after the formset declaration. >00000040: 5C 06 00 00 00 00. + // So we don't need to declare the Standard Default. + // Please check the vfr.lst file for details. + // To enable list file for VFR, add "-l" to VfrCompile in [Build.Visual-Form-Representation-File] as follows: + // VfrCompile -l --no-pre-processing --output-directory ${d_path} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii + // + + // + // Define a Buffer Storage (EFI_IFR_VARSTORE) + // + varstore DRIVER_SAMPLE_CONFIGURATION, // This is the data structure type + varid = CONFIGURATION_VARSTORE_ID, // Optional VarStore ID + name = MyIfrNVData, // Define referenced name in vfr + guid = DRIVER_SAMPLE_FORMSET_GUID; // GUID of this buffer storage + + // + // Define a EFI variable Storage (EFI_IFR_VARSTORE_EFI) + // + efivarstore MY_EFI_VARSTORE_DATA, + attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, // EFI variable attribures + name = MyEfiVar, + guid = DRIVER_SAMPLE_FORMSET_GUID; + + // + // Define a Buffer Storage (EFI_IFR_VARSTORE) + // + efivarstore MY_EFI_BITS_VARSTORE_DATA, // This is the data structure type + attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, // EFI variable attribures + name = MyEfiBitVar, // Define referenced name in vfr + guid = DRIVER_SAMPLE_FORMSET_GUID; // GUID of this buffer storage + + efivarstore MY_EFI_UNION_DATA, + attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, // EFI variable attribures + name = MyEfiUnionVar, + guid = DRIVER_SAMPLE_FORMSET_GUID; + + // + // Define a Name/Value Storage (EFI_IFR_VARSTORE_NAME_VALUE) + // + namevaluevarstore MyNameValueVar, // Define storage reference name in vfr + name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME0), // Define Name list of this storage, refer it by MyNameValueVar[0] + name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME1), // Define Name list of this storage, refer it by MyNameValueVar[1] + name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME2), // Define Name list of this storage, refer it by MyNameValueVar[2] + guid = DRIVER_SAMPLE_FORMSET_GUID; // GUID of this Name/Value storage + + defaultstore MyStandardDefault, + prompt = STRING_TOKEN(STR_STANDARD_DEFAULT_PROMPT), + attribute = 0x0000; // Default ID: 0000 standard default + + defaultstore MyManufactureDefault, + prompt = STRING_TOKEN(STR_MANUFACTURE_DEFAULT_PROMPT), + attribute = 0x0001; // Default ID: 0001 manufacture default + + // + // Define a Form (EFI_IFR_FORM) + // + form formid = 1, // Form ID + title = STRING_TOKEN(STR_FORM1_TITLE); // Form title + + subtitle text = STRING_TOKEN(STR_SUBTITLE_TEXT); + + subtitle text = STRING_TOKEN(STR_SUBTITLE_TEXT2); + + // + // Define a display only text (EFI_IFR_TEXT) + // + text + help = STRING_TOKEN(STR_TEXT_HELP), // Help string + text = STRING_TOKEN(STR_CPU_STRING), // Prompt string + text = STRING_TOKEN(STR_CPU_STRING2); // TextTwo + + // + // Define action button (EFI_IFR_ACTION) + // + text + help = STRING_TOKEN(STR_EXIT_TEXT), + text = STRING_TOKEN(STR_EXIT_TEXT), + flags = INTERACTIVE, // VfrCompiler will generate opcode EFI_IFR_ACTION for Text marked as INTERACTIVE + key = 0x1237; + + text + help = STRING_TOKEN(STR_SAVE_TEXT), + text = STRING_TOKEN(STR_SAVE_TEXT), + flags = INTERACTIVE, + key = 0x1238; + + text + help = STRING_TOKEN(STR_SAVE_CURRENT), + text = STRING_TOKEN(STR_SAVE_CURRENT), + flags = INTERACTIVE, + key = 0x1243; + + text + help = STRING_TOKEN(STR_DISCARD_CURRENT_AND_EXIT), + text = STRING_TOKEN(STR_DISCARD_CURRENT_AND_EXIT), + flags = INTERACTIVE, + key = 0x1244; + // + // Define oneof (EFI_IFR_ONE_OF) + // + oneof name = MyOneOf, // Define reference name for Question + varid = MyIfrNVData.SuppressGrayOutSomething, // Use "DataStructure.Member" to reference Buffer Storage + prompt = STRING_TOKEN(STR_ONE_OF_PROMPT), + help = STRING_TOKEN(STR_ONE_OF_HELP), + // + // Define an option (EFI_IFR_ONE_OF_OPTION) + // + option text = STRING_TOKEN(STR_ONE_OF_TEXT4), value = 0x0, flags = 0; + option text = STRING_TOKEN(STR_ONE_OF_TEXT5), value = 0x1, flags = 0; + // + // DEFAULT indicate this option will be marked with EFI_IFR_OPTION_DEFAULT + // + option text = STRING_TOKEN(STR_ONE_OF_TEXT6), value = 0x2, flags = DEFAULT; + endoneof; + + oneof varid = MyIfrNVData.BootOrderLarge, + prompt = STRING_TOKEN(STR_ONE_OF_PROMPT), + help = STRING_TOKEN(STR_ONE_OF_HELP), + default value = cond (pushthis == 0 ? 0 : cond ((questionref(MyOneOf) >> 0x4 & 0xF00) == 0x0 + 0x2 ? 0 : 1)), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0x0, flags = 0; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 0x1, flags = 0; + endoneof; + + grayoutif ideqval MyIfrNVData.SuppressGrayOutSomething == 0x1; + suppressif questionref(MyOneOf) == 0x0; + + checkbox varid = MyIfrNVData.ChooseToActivateNuclearWeaponry, + prompt = STRING_TOKEN(STR_CHECK_BOX_PROMPT), + help = STRING_TOKEN(STR_CHECK_BOX_HELP), + // + // CHECKBOX_DEFAULT indicate this checkbox is marked with EFI_IFR_CHECKBOX_DEFAULT + // CHECKBOX_DEFAULT_MFG indicate EFI_IFR_CHECKBOX_DEFAULT_MFG. + // + flags = CHECKBOX_DEFAULT | CHECKBOX_DEFAULT_MFG, + default = TRUE, + endcheckbox; + endif; + endif; + + // + // Ordered list: + // sizeof(MyIfrNVData) storage must be UINT8 array, and + // size written for the variable must be size of the entire + // variable. + // + // + suppressif ideqval MyIfrNVData.SuppressGrayOutSomething == 0x0; + + // + // label is defined as an anchor where you want to insert some dynamic + // opcodes created on-the-fly + // + label LABEL_UPDATE_BBS; + + orderedlist + varid = MyIfrNVData.BootOrder, + prompt = STRING_TOKEN(STR_BOOT_OPTIONS), + help = STRING_TOKEN(STR_NULL_STRING), + flags = RESET_REQUIRED, + option text = STRING_TOKEN(STR_BOOT_OPTION2), value = 2, flags = 0; + option text = STRING_TOKEN(STR_BOOT_OPTION1), value = 1, flags = 0; + option text = STRING_TOKEN(STR_BOOT_OPTION3), value = 3, flags = 0; + suppressif ideqval MyIfrNVData.BootOrderLarge == 0; + option text = STRING_TOKEN(STR_BOOT_OPTION4), value = 4, flags = 0; + endif; + endlist; + + // + // label should be paired with each other + // + label LABEL_END; + + endif; // end suppressif + + disableif ideqval MyIfrNVData.SuppressGrayOutSomething == 0x2; + orderedlist + varid = MyIfrNVData.OrderedList, + prompt = STRING_TOKEN(STR_TEST_OPCODE), + help = STRING_TOKEN(STR_TEXT_HELP), + flags = RESET_REQUIRED, + option text = STRING_TOKEN(STR_ONE_OF_TEXT1), value = 3, flags = 0; + option text = STRING_TOKEN(STR_ONE_OF_TEXT2), value = 2, flags = 0; + option text = STRING_TOKEN(STR_ONE_OF_TEXT3), value = 1, flags = 0; + default = {1,2,3}, + endlist; + endif; + + label 100; + + // + // Define a hyperlink (EFI_IFR_REF) + // + goto 0x1234, // Destination Form ID + prompt = STRING_TOKEN(STR_GOTO_DYNAMIC), // Prompt string + help = STRING_TOKEN(STR_GOTO_HELP), // Help string + flags = INTERACTIVE, // INTERACTIVE indicate it's marked with EFI_IFR_FLAG_CALLBACK + key = 0x1234; // Question ID which will be passed-in in COnfigAccess.Callback() + + goto 0x1234, + prompt = STRING_TOKEN(STR_GOTO_DYNAMIC2), + help = STRING_TOKEN(STR_GOTO_HELP), + flags = INTERACTIVE, + key = 0x1235; + + oneof varid = MyIfrNVData.TestLateCheck, + prompt = STRING_TOKEN(STR_TEST_OPCODE), + help = STRING_TOKEN(STR_ONE_OF_HELP), + flags = RESET_REQUIRED, + option text = STRING_TOKEN(STR_ONE_OF_TEXT1), value = 0, flags = 0; + option text = STRING_TOKEN(STR_ONE_OF_TEXT2), value = 1, flags = DEFAULT; + warningif prompt = STRING_TOKEN(STR_WARNING_POPUP), timeout = 5, + ideqval MyIfrNVData.TestLateCheck == 0 + endif; + + endoneof; + + oneof varid = MyIfrNVData.TestLateCheck2, + prompt = STRING_TOKEN(STR_TEST_OPCODE2), + help = STRING_TOKEN(STR_ONE_OF_HELP), + flags = RESET_REQUIRED, + option text = STRING_TOKEN(STR_ONE_OF_TEXT1), value = 0, flags = DEFAULT; + option text = STRING_TOKEN(STR_ONE_OF_TEXT2), value = 1, flags = 0; + + inconsistentif prompt = STRING_TOKEN(STR_ERROR_POPUP), + ideqid MyIfrNVData.TestLateCheck == MyIfrNVData.TestLateCheck2 + endif; + + endoneof; + + oneof varid = MyIfrNVData.QuestionAboutTreeHugging, + prompt = STRING_TOKEN(STR_ONE_OF_PROMPT_KEYWORD), + help = STRING_TOKEN(STR_ONE_OF_HELP), + flags = RESET_REQUIRED, + option text = STRING_TOKEN(STR_ONE_OF_TEXT1), value = 0, flags = 0; + option text = STRING_TOKEN(STR_ONE_OF_TEXT2), value = 1, flags = DEFAULT; + option text = STRING_TOKEN(STR_ONE_OF_TEXT3), value = 3, flags = 0; + endoneof; + + // + // Define a string (EFI_IFR_STRING) + // + string varid = MyIfrNVData.MyStringData, + prompt = STRING_TOKEN(STR_MY_STRING_PROMPT2), + help = STRING_TOKEN(STR_MY_STRING_HELP2), + flags = INTERACTIVE, + key = 0x1236, + minsize = 6, + maxsize = 40, + inconsistentif prompt = STRING_TOKEN(STR_STRING_CHECK_ERROR_POPUP), + pushthis != stringref(STRING_TOKEN(STR_STRING_CHECK)) + endif; + endstring; + + // + // Define a numeric (EFI_IFR_NUMERIC) + // + numeric varid = MyIfrNVData.HowOldAreYouInYearsManual, + prompt = STRING_TOKEN(STR_NUMERIC_READONLY_PROMPT), + help = STRING_TOKEN(STR_NUMERIC_HELP0), + flags = READ_ONLY, // READ_ONLY indicate it's marked with EFI_IFR_FLAG_READ_ONLY + minimum = 0, + maximum = 0xf0, + step = 0, // Stepping of 0 equates to a manual entering + // of a value, otherwise it will be adjusted by "+"/"-" + default = 21, // defaultstore could be used to specify the default type + // If no defaultstore is specified, it implies Standard Default + + endnumeric; + + numeric varid = MyIfrNVData.HowOldAreYouInYearsManual, + prompt = STRING_TOKEN(STR_NUMERIC_MANUAL_PROMPT), + help = STRING_TOKEN(STR_NUMERIC_HELP0), + minimum = 0, + maximum = 0xf0, + step = 0, + default value = questionrefval(devicepath = STRING_TOKEN (STR_DEVICE_PATH), guid = DRIVER_SAMPLE_FORMSET_GUID, 0x1111), + + inconsistentif prompt = STRING_TOKEN(STR_ERROR_POPUP), + ideqval MyIfrNVData.HowOldAreYouInYearsManual == 99 + OR + ideqid MyIfrNVData.HowOldAreYouInYearsManual == MyEfiVar.Field8 + OR + ideqvallist MyIfrNVData.HowOldAreYouInYearsManual == 1 3 5 7 + endif; + + endnumeric; + + numeric varid = MyEfiVar.Field8, // Reference of EFI variable storage + questionid = 0x1111, + prompt = STRING_TOKEN(STR_TALL_HEX_PROMPT), + help = STRING_TOKEN(STR_NUMERIC_HELP1), + flags = DISPLAY_UINT_HEX | INTERACTIVE, // Display in HEX format (if not specified, default is in decimal format) + minimum = 0, + maximum = 250, + default = 18, defaultstore = MyStandardDefault, // This is standard default value + default = 19, defaultstore = MyManufactureDefault, // This is manufacture default value + + endnumeric; + + // + // Define numeric using Name/Value Storage + // + numeric varid = MyNameValueVar[0], // This numeric take NameValueVar0 as storage + prompt = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME0), + help = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME0_HELP), + // + // Size should be defined for numeric when use Name/Value storage + // Valid value for numerice size are: NUMERIC_SIZE_1, NUMERIC_SIZE_2, NUMERIC_SIZE_4 and NUMERIC_SIZE_8 + // + flags = NUMERIC_SIZE_1, // Size of this numeric is 1 byte + minimum = 0, + maximum = 0xff, + step = 0, + locked, + default = 16, defaultstore = MyStandardDefault, // This is standard default value + default = 17, defaultstore = MyManufactureDefault, // This is manufacture default value + endnumeric; + + numeric varid = MyNameValueVar[1], // This numeric take NameValueVar1 as storage + prompt = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME1), + help = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME1_HELP), + flags = NUMERIC_SIZE_2, // Size of this numeric is 2 bytes + minimum = 0, + maximum = 0xffff, + step = 0, + default = 18, defaultstore = MyStandardDefault, // This is standard default value + default = 19, defaultstore = MyManufactureDefault, // This is manufacture default value + endnumeric; + + // + // Define string using Name/Value Storage + // + string varid = MyNameValueVar[2], // This string take NameValueVar2 as storage + prompt = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME2), + help = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME2_HELP), + minsize = 2, + maxsize = 0x14, + endstring; + + oneof varid = MyEfiVar.Field16, + prompt = STRING_TOKEN(STR_ONE_OF_PROMPT), + help = STRING_TOKEN(STR_NUMERIC_NUM_HELP), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0x0, flags = 0; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 0x1, flags = DEFAULT; + endoneof; + + label LABEL_1_VALUE; + label LABEL_2_VALUE; + + grayoutif ideqval MyIfrNVData.HowOldAreYouInYearsManual == 23 AND ideqval MyIfrNVData.SuppressGrayOutSomething == 0x1; + numeric varid = MyIfrNVData.HowOldAreYouInYears, + prompt = STRING_TOKEN(STR_NUMERIC_STEP_PROMPT), + help = STRING_TOKEN(STR_NUMERIC_HELP2), + minimum = 0, + maximum = 243, + step = 1, + default = 18, defaultstore = MyStandardDefault, // This is standard default value + default = 19, defaultstore = MyManufactureDefault, // This is manufacture default value + + endnumeric; + endif; + + numeric varid = MyIfrNVData.GetDefaultValueFromAccess, + questionid = 0x1239, + prompt = STRING_TOKEN(STR_DEFAULT_VALUE_FROM_ACCESS_PROMPT), + help = STRING_TOKEN(STR_DEFAULT_VALUE_FROM_ACCESS_HELP), + flags = DISPLAY_UINT_HEX | INTERACTIVE, + minimum = 0, + maximum = 255, + step = 1, + default = 18, + endnumeric; + + numeric varid = MyIfrNVData.GetDefaultValueFromCallBack, + questionid = 0x1240, + prompt = STRING_TOKEN(STR_DEFAULT_VALUE_FROM_CALLBACK_PROMPT), + help = STRING_TOKEN(STR_DEFAULT_VALUE_FROM_CALLBACK_HELP), + flags = DISPLAY_UINT_HEX | INTERACTIVE, + minimum = 0, + maximum = 255, + step = 1, + default = 18, + endnumeric; + + orderedlist + varid = MyIfrNVData.GetDefaultValueFromCallBackForOrderedList, + questionid = 0x1252, + prompt = STRING_TOKEN(STR_DEFAULT_VALUE_FROM_CALLBACK_PROMPT), + help = STRING_TOKEN(STR_DEFAULT_VALUE_FROM_CALLBACK_HELP), + flags = INTERACTIVE, + option text = STRING_TOKEN(STR_ONE_OF_TEXT1), value = 1, flags = 0; + option text = STRING_TOKEN(STR_ONE_OF_TEXT2), value = 2, flags = 0; + option text = STRING_TOKEN(STR_ONE_OF_TEXT3), value = 3, flags = 0; + endlist; + + resetbutton + defaultstore = MyStandardDefault, + prompt = STRING_TOKEN(STR_STANDARD_DEFAULT_PROMPT), + help = STRING_TOKEN(STR_STANDARD_DEFAULT_HELP), + endresetbutton; + + resetbutton + defaultstore = MyManufactureDefault, + prompt = STRING_TOKEN(STR_MANUFACTURE_DEFAULT_PROMPT), + help = STRING_TOKEN(STR_MANUFACTURE_DEFAULT_HELP), + endresetbutton; + + // + // Sample use case for IFR Security op-code + // + grayoutif NOT security (EFI_USER_INFO_ACCESS_SETUP_ADMIN_GUID); + text + help = STRING_TOKEN(STR_TEXT_SECRUITY_TEST_HELP), + text = STRING_TOKEN(STR_TEXT_SECRUITY_TEST_TEXT); + endif; + + numeric varid = MyEfiVar.SubmittedCallback, + questionid = 0x1250, + prompt = STRING_TOKEN(STR_SUBMITTED_CALLBACK_TEST_PROMPT), + help = STRING_TOKEN(STR_SUBMITTED_CALLBACK_TEST_HELP), + flags = INTERACTIVE, + minimum = 0, + maximum = 255, + default = 18, + endnumeric; + + text + help = STRING_TOKEN(STR_POPUP_TEST_HELP), + text = STRING_TOKEN(STR_POPUP_TEST_PROMPT), + flags = INTERACTIVE, + key = 0x1330; + + goto 2, + prompt = STRING_TOKEN(STR_GOTO_FORM2), //SecondSetupPage // this too has no end-op and basically it's a jump to a form ONLY + help = STRING_TOKEN(STR_GOTO_HELP); + + goto 3, + prompt = STRING_TOKEN(STR_GOTO_FORM3), //ThirdSetupPage // this too has no end-op and basically it's a jump to a form ONLY + help = STRING_TOKEN(STR_GOTO_HELP); + + goto 4, + prompt = STRING_TOKEN(STR_GOTO_FORM4), //FourthSetupPage // this too has no end-op and basically it's a jump to a form ONLY + help = STRING_TOKEN(STR_GOTO_HELP); + + goto 5, + prompt = STRING_TOKEN(STR_GOTO_FORM5), //FifthSetupPage // this too has no end-op and basically it's a jump to a form ONLY + help = STRING_TOKEN(STR_GOTO_FORM5_HELP); + + goto 6, + prompt = STRING_TOKEN(STR_GOTO_FORM6), //SixthSetupPage // this too has no end-op and basically it's a jump to a form ONLY + help = STRING_TOKEN(STR_GOTO_HELP); + + goto + formsetguid = DRIVER_SAMPLE_INVENTORY_GUID, + formid = 0x1, + question = 0x1, + prompt = STRING_TOKEN(STR_GOTO_ANOTHER_FORMSET), + help = STRING_TOKEN(STR_GOTO_ANOTHER_FORMSET_HELP); + + guidop + guid = DRIVER_SAMPLE_FORMSET_GUID, + datatype = MY_EFI_VARSTORE_DATA, + data.Field8 = 0x21, + data.Field16 = 0x2121, + data.OrderedList[0] = 0x21, + endguidop; + + goto 7, + prompt = STRING_TOKEN(STR_GOTO_FORM7), + help = STRING_TOKEN(STR_GOTO_FORM7_HELP); + + endform; + + suppressif ideqval MyIfrNVData.BootOrderLarge == 0; + form formid = 2, // SecondSetupPage, + title = STRING_TOKEN(STR_FORM2_TITLE); // note formid is a variable (for readability) (UINT16) - also added Form to the line to signify the Op-Code + + date + name = Date, + prompt = STRING_TOKEN(STR_DATE_PROMPT), + help = STRING_TOKEN(STR_DATE_HELP), + flags = STORAGE_TIME, + default = 2004/1/1, + + inconsistentif prompt = STRING_TOKEN(STR_ERROR_POPUP), + ideqval Date.Day == 31 + AND + ideqvallist Date.Month == 2 4 6 9 11 + endif; + + // + // If the day is 30 AND month is 2 + // + inconsistentif prompt = STRING_TOKEN(STR_ERROR_POPUP), + ideqval Date.Day == 30 + AND + ideqval Date.Month == 2 + endif; + + // + // If the day is 29 AND month is 2 AND it year is NOT a leapyear + // + inconsistentif prompt = STRING_TOKEN(STR_ERROR_POPUP), + ideqval Date.Day == 0x1D + AND + ideqval Date.Month == 2 + AND + NOT + ideqvallist Date.Year == 2004 2008 20012 20016 2020 2024 2028 2032 2036 + endif; + + enddate; + + text + help = STRING_TOKEN(STR_SAVE_CURRENT_AND_EXIT), + text = STRING_TOKEN(STR_SAVE_CURRENT_AND_EXIT), + flags = INTERACTIVE, + key = 0x1241; + + text + help = STRING_TOKEN(STR_DISCARD_CURRENT), + text = STRING_TOKEN(STR_DISCARD_CURRENT), + flags = INTERACTIVE, + key = 0x1242; + + time + prompt = STRING_TOKEN(STR_TIME_PROMPT), + help = STRING_TOKEN(STR_TIME_HELP), + flags = STORAGE_TIME, + endtime; + + time + name = MyTime, + varid = MyIfrNVData.Time, + prompt = STRING_TOKEN(STR_TIME_PROMPT), + help = STRING_TOKEN(STR_TIME_PROMPT), + flags = STORAGE_NORMAL | SECOND_SUPPRESS, + default = 15:33:33, + endtime; + + checkbox varid = MyIfrNVData.ChooseToActivateNuclearWeaponry, + prompt = STRING_TOKEN(STR_CHECK_BOX_PROMPT), + help = STRING_TOKEN(STR_CHECK_BOX_HELP), + flags = CHECKBOX_DEFAULT, + endcheckbox; + + text + help = STRING_TOKEN(STR_TEXT_HELP), + text = STRING_TOKEN(STR_TEXT_TEXT_1); + + text + help = STRING_TOKEN(STR_TEXT_HELP), + text = STRING_TOKEN(STR_TEXT_TEXT_1), + text = STRING_TOKEN(STR_TEXT_TEXT_2); + + goto 1, + prompt = STRING_TOKEN(STR_GOTO_FORM1), //MainSetupPage // this too has no end-op and basically it's a jump to a form ONLY + help = STRING_TOKEN(STR_GOTO_HELP); + + goto + varid = MyIfrNVData.RefData, + prompt = STRING_TOKEN(STR_GOTO_DYNAMIC3), + help = STRING_TOKEN(STR_GOTO_DYNAMIC3_HELP), + flags = INTERACTIVE, + key = 0x1248, + // + // Set the defult value, format is QuestionId; FormId; FormsetGuid; Device Path String Token + // + default = 0;0;ZERO_GUID;STRING_TOKEN(STR_NULL_STRING), + ; // goto opcode end flag. + + goto + prompt = STRING_TOKEN(STR_GOTO_DYNAMIC4), + help = STRING_TOKEN(STR_GOTO_DYNAMIC4_HELP), + flags = INTERACTIVE, + key = 0x1249; + + endform; + endif; + + form formid = 3, title = STRING_TOKEN(STR_FORM3_TITLE); // note formid is a variable (for readability) (UINT16) - also added Form to the line to signify the Op-Code + + suppressif ideqval MyEfiVar.Field8 == 111; + text + help = STRING_TOKEN(STR_TEXT_HELP), + text = STRING_TOKEN(STR_TEXT_TEXT_1); + endif; + + goto 1, + prompt = STRING_TOKEN(STR_GOTO_FORM1), //MainSetupPage + help = STRING_TOKEN(STR_GOTO_HELP); + + numeric varid = MyIfrNVData.DynamicRefresh, + prompt = STRING_TOKEN(STR_NUMERIC_MANUAL_PROMPT), + help = STRING_TOKEN(STR_NUMERIC_HELP0), + flags = INTERACTIVE, + key = 0x5678, + minimum = 0, + maximum = 0xff, + step = 0, + default = 0, + refresh interval = 3 // Refresh interval in seconds + endnumeric; + + grayoutif match2 (stringref(STRING_TOKEN(STR_PATTERN)), stringref(STRING_TOKEN(STR_STRING)), PERL_GUID); + numeric + varid = MyIfrNVData.Match2, + prompt = STRING_TOKEN(STR_MATCH2_PROMPT), + help = STRING_TOKEN(STR_MATCH2_HELP), + minimum = 0, + maximum = 243, + endnumeric; + endif; + + label LABEL_UPDATE2; + label LABEL_END; + + endform; + + formmap formid = 4, + maptitle = STRING_TOKEN(STR_SAMPL_MAP_METHOD); + mapguid = DRIVER_SAMPLE_FORMSET_GUID; + maptitle = STRING_TOKEN(STR_STANDARD_MAP_METHOD); + mapguid = EFI_HII_STANDARD_FORM_GUID; + + oneof varid = MyIfrNVData.SerialPortNo, + prompt = STRING_TOKEN(STR_SERIAL_PORT), + help = STRING_TOKEN(STR_ONE_OF_HELP), + + read cond (get(MyIfrNVData.SerialPortStatus) != 0 ? 0 : cond ((get(MyIfrNVData.SerialPortIo) & 0xF00) >> 0x8 == get(MyIfrNVData.SerialPortIrq) - 1 ? UNDEFINED : map (get(MyIfrNVData.SerialPortIo) : 0x3f8,1; 0x2F8,2; 0x3E8,3; 0x2E8,4;))); + write set(MyIfrNVData.SerialPortStatus, pushthis != 0) AND set(MyIfrNVData.SerialPortIo, map (pushthis : 1,0x3F8; 2,0x2F8; 3,0x3E8; 4,0x2E8;)) AND set (MyIfrNVData.SerialPortIrq, map (pushthis: 1,4; 2,3; 3,4; 4,3;)); + + option text = STRING_TOKEN(STR_SERIAL_PORT_DISABLE), value = 0x0, flags = DEFAULT; + option text = STRING_TOKEN(STR_SERIAL_PORT1), value = 0x1, flags = 0; + option text = STRING_TOKEN(STR_SERIAL_PORT2), value = 0x2, flags = 0; + option text = STRING_TOKEN(STR_SERIAL_PORT3), value = 0x3, flags = 0; + option text = STRING_TOKEN(STR_SERIAL_PORT4), value = 0x4, flags = 0; + endoneof; + + grayoutif TRUE; + checkbox varid = MyIfrNVData.SerialPortStatus, + prompt = STRING_TOKEN(STR_SERIAL_PORT_STATUS), + help = STRING_TOKEN(STR_CHECK_BOX_HELP), + endcheckbox; + endif; + + grayoutif TRUE; + suppressif ideqval MyIfrNVData.SerialPortStatus == 0; + oneof varid = MyIfrNVData.SerialPortIo, + prompt = STRING_TOKEN(STR_SERIAL_PORT_IO_ADDRESS), + help = STRING_TOKEN(STR_ONE_OF_HELP), + + option text = STRING_TOKEN(STR_SERIAL_PORT1_IOADDR), value = 0x3F8, flags = DEFAULT; + option text = STRING_TOKEN(STR_SERIAL_PORT2_IOADDR), value = 0x2F8, flags = 0; + option text = STRING_TOKEN(STR_SERIAL_PORT3_IOADDR), value = 0x3E8, flags = 0; + option text = STRING_TOKEN(STR_SERIAL_PORT4_IOADDR), value = 0x2E8, flags = 0; + endoneof; + endif; + endif; + + grayoutif TRUE; + suppressif ideqval MyIfrNVData.SerialPortStatus == 0; + oneof varid = MyIfrNVData.SerialPortIrq, + prompt = STRING_TOKEN(STR_SERIAL_PORT_IRQ), + help = STRING_TOKEN(STR_ONE_OF_HELP), + + option text = STRING_TOKEN(STR_SERIAL_PORT13_IRQ), value = 0x4, flags = DEFAULT; + option text = STRING_TOKEN(STR_SERIAL_PORT24_IRQ), value = 0x3, flags = 0; + endoneof; + endif; + endif; + + goto 1, + prompt = STRING_TOKEN(STR_GOTO_FORM1), //MainSetupPage + help = STRING_TOKEN(STR_GOTO_HELP); + + endform; + + form formid = 5, // Modal form + title = STRING_TOKEN(STR_MODAL_FORM_TITLE); + // + // This form is a modal form. + // + modal; + text + help = STRING_TOKEN(STR_EXIT_TEXT), + text = STRING_TOKEN(STR_EXIT_TEXT), + flags = INTERACTIVE, // VfrCompiler will generate opcode EFI_IFR_ACTION for Text marked as INTERACTIVE + key = 0x1245; + + text + help = STRING_TOKEN(STR_SAVE_TEXT), + text = STRING_TOKEN(STR_SAVE_TEXT), + flags = INTERACTIVE, // VfrCompiler will generate opcode EFI_IFR_ACTION for Text marked as INTERACTIVE + key = 0x1246; + endform; + + form formid = 6, // Form to show the refresh guid group op-code + title = STRING_TOKEN(STR_FORM6_TITLE); + + text + help = STRING_TOKEN(STR_TEXT_REFRESH_GUID), + text = STRING_TOKEN(STR_TEXT_REFRESH_GUID); + + numeric varid = MyIfrNVData.RefreshGuidCount, + prompt = STRING_TOKEN(STR_TEXT_REFRESH_GUID_COUNT), + help = STRING_TOKEN(STR_NUMERIC_HELP0), + flags = INTERACTIVE, + key = 0x1247, + minimum = 0, + maximum = 0xff, + step = 0, + default = 0, + refreshguid = EFI_IFR_REFRESH_ID_OP_GUID, + endnumeric; + + label LABEL_UPDATE3; + label LABEL_END; + + endform; + + form formid = 0x1234, // Dynamically created page, + title = STRING_TOKEN(STR_DYNAMIC_TITLE); // note formid is a variable (for readability) (UINT16) - also added Form to the line to signify the Op-Code + + label LABEL_UPDATE1; + // + // This is where we will insert dynamic created opcodes + // + label LABEL_END; + + endform; + + + form formid = 7, // Form to show the question refer to union and bit Varstore + title = STRING_TOKEN(STR_FORM7_TITLE); + + subtitle text = STRING_TOKEN(STR_NEST_BIT_EFI_VARSTORE); + + checkbox varid = MyEfiBitVar.BitsData.NestBitCheckbox, + prompt = STRING_TOKEN(STR_BIT_NEST_CHECK_BOX_PROMPT), + help = STRING_TOKEN(STR_BIT_NEST_CHECK_BOX_HELP), + flags = CHECKBOX_DEFAULT, + endcheckbox; + + oneof varid = MyEfiBitVar.BitsData.NestBitOneof, + prompt = STRING_TOKEN(STR_ONE_OF_BIT_NEST_PROMPT), + help = STRING_TOKEN(STR_ONE_OF_BIT_NEST_HELP), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0, flags = MANUFACTURING; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 1, flags = DEFAULT; + endoneof; + + numeric varid = MyEfiBitVar.BitsData.NestBitNumeric, + questionid = 0x6666, + prompt = STRING_TOKEN(STR_BIT_NEST_NUMERIC_PROMPT), + help = STRING_TOKEN(STR_BIT_NEST_NUMERIC_DEFAULT_HELP), + flags = DISPLAY_UINT_HEX | INTERACTIVE, + minimum = 2, + maximum = 15, + step = 1, + endnumeric; + + oneof varid = MyEfiBitVar.BitsData.NestByteField, + prompt = STRING_TOKEN(BYTE_QUESTION_NEST_BIT_PROMPT), + help = STRING_TOKEN(BYTE_QUESTION_NEST_BIT_HELP), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0, flags = MANUFACTURING; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 1, flags = DEFAULT; + endoneof; + + subtitle text = STRING_TOKEN(STR_SUBTITLE_TEXT2); + subtitle text = STRING_TOKEN(STR_BIT_EFI_VARSTORE); + + checkbox varid = MyEfiBitVar.EfiBitCheckbox, + prompt = STRING_TOKEN(STR_BIT_CHECK_BOX_PROMPT), + help = STRING_TOKEN(STR_BIT_CHECK_BOX_HELP), + flags = CHECKBOX_DEFAULT, + endcheckbox; + + grayoutif ideqval MyEfiBitVar.EfiBitGrayoutTest == 0; + numeric varid = MyEfiBitVar.EfiBitNumeric, + prompt = STRING_TOKEN(STR_BIT_NUMERIC_PROMPT), + help = STRING_TOKEN(STR_BIT_NUMERIC_HELP), + minimum = 0, + maximum = 7, + step = 0, + default = 4, defaultstore = MyStandardDefault, + default = 5, defaultstore = MyManufactureDefault, + endnumeric; + endif; + + oneof varid = MyEfiBitVar.EfiBitOneof, + questionid = 0x9999, + prompt = STRING_TOKEN(STR_ONE_OF_BIT_PROMPT), + help = STRING_TOKEN(STR_ONE_OF_BIT_HELP), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0x0, flags = MANUFACTURING; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 0x1, flags = DEFAULT; + endoneof; + + subtitle text = STRING_TOKEN(STR_SUBTITLE_TEXT2); + subtitle text = STRING_TOKEN(STR_NEST_BIT_VARSTORE); + checkbox varid = MyIfrNVData.MyBitData.NestBitCheckbox, + prompt = STRING_TOKEN(STR_BIT_NEST_CHECK_BOX_PROMPT), + help = STRING_TOKEN(STR_BIT_NEST_CHECK_BOX_HELP), + flags = CHECKBOX_DEFAULT, + endcheckbox; + + oneof varid = MyIfrNVData.MyBitData.NestBitOneof, + prompt = STRING_TOKEN(STR_ONE_OF_BIT_NEST_PROMPT), + help = STRING_TOKEN(STR_ONE_OF_BIT_NEST_HELP), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0, flags = MANUFACTURING; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 1, flags = DEFAULT; + endoneof; + + numeric varid = MyIfrNVData.MyBitData.NestBitNumeric, + prompt = STRING_TOKEN(STR_BIT_NEST_NUMERIC_PROMPT), + help = STRING_TOKEN(STR_BIT_NEST_NUMERIC_HELP), + minimum = 0, + maximum = 7, + step = 0, + default = 6, defaultstore = MyStandardDefault, + default = 7, defaultstore = MyManufactureDefault, + endnumeric; + + oneof varid = MyIfrNVData.MyBitData.NestByteField, + prompt = STRING_TOKEN(BYTE_QUESTION_NEST_BIT_PROMPT), + help = STRING_TOKEN(BYTE_QUESTION_NEST_BIT_HELP), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0, flags = MANUFACTURING; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 1, flags = DEFAULT; + endoneof; + + subtitle text = STRING_TOKEN(STR_SUBTITLE_TEXT2); + subtitle text = STRING_TOKEN(STR_BIT_VARSTORE); + + oneof varid = MyIfrNVData.BitOneof, + prompt = STRING_TOKEN(STR_ONE_OF_BIT_PROMPT), + help = STRING_TOKEN(STR_ONE_OF_BIT_HELP), + option text = STRING_TOKEN(STR_BOOT_ORDER1), value = 0, flags = MANUFACTURING; + option text = STRING_TOKEN(STR_BOOT_ORDER2), value = 1, flags = DEFAULT; + endoneof; + + checkbox varid = MyIfrNVData.BitCheckbox, + prompt = STRING_TOKEN(STR_BIT_CHECK_BOX_PROMPT), + help = STRING_TOKEN(STR_BIT_CHECK_BOX_HELP), + flags = CHECKBOX_DEFAULT, + endcheckbox; + + numeric varid = MyIfrNVData.BitNumeric, + prompt = STRING_TOKEN(STR_BIT_NUMERIC_PROMPT), + help = STRING_TOKEN(STR_BUFFER_BIT_NUMERIC_HELP), + minimum = 0, + maximum = 20, + step = 0, + default = 16, defaultstore = MyStandardDefault, + default = 17, defaultstore = MyManufactureDefault, + endnumeric; + + subtitle text = STRING_TOKEN(STR_SUBTITLE_TEXT2); + subtitle text = STRING_TOKEN(STR_UNION_EFI_VARSTORE); + + numeric varid = MyEfiUnionVar.UnionNumeric, + prompt = STRING_TOKEN(STR_UNION_BYTE_NUMERIC_PROMPT), + help = STRING_TOKEN(STR_UNION_BYTE_NUMERIC_HELP), + minimum = 0, + maximum = 20, + step = 0, + default = 7, defaultstore = MyStandardDefault, + default = 8, defaultstore = MyManufactureDefault, + endnumeric; + + guidop + guid = DRIVER_SAMPLE_FORMSET_GUID, + datatype = MY_EFI_BITS_VARSTORE_DATA, + data.EfiBitNumeric = 1, + data.EfiBitOneof = 1, + data.EfiBitCheckbox = 1, + endguidop; + + endform; + +endformset; diff --git a/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/VfrStrings.uni b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/VfrStrings.uni new file mode 100644 index 000000000..bafa194c6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/DriverSampleDxe/VfrStrings.uni @@ -0,0 +1,428 @@ +// *++ + // +// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// Module Name: +// +// VfrStrings.vfr +// +// Abstract: +// +// String definitions for Sample Setup formset. +// +// Revision History: +// +// --*/ + + +/=# + +#langdef en-US "English" +#langdef fr-FR "Francais" +#langdef x-UEFI-ns "UefiNameSpace" + + +#string STR_FORM_SET_TITLE #language en-US "Browser Testcase Engine" + #language fr-FR "Browser Testcase Engine" +#string STR_FORM_SET_TITLE_HELP #language en-US "This is a sample driver which is used to test the browser op-code operations. This is for development purposes and not to be distributed in any form other than a test application. Here is a set of wide \wideAAAAAAAAA\narrow and narrow AAA!" + #language fr-FR "This is a sample driver which is used to test the browser op-code operations. This is for development purposes and not to be distributed in any form other than a test application. Here is a set of wide \wideAAAAAAAAA\narrow and narrow AAA!" +#string STR_FORM1_TITLE #language en-US "My First Setup Page" + #language fr-FR "Mi Primero Arreglo Página" +#string STR_FORM2_TITLE #language en-US "My Second Setup Page" + #language fr-FR "Mi Segunda Paginación De la Disposición" +#string STR_FORM3_TITLE #language en-US "My Third Setup Page" + #language fr-FR "Mi Tercera Paginación De la Disposición" +#string STR_FORM6_TITLE #language en-US "My Sixth Setup Page" + #language fr-FR "My Sixth Setup Page" +#string STR_DYNAMIC_TITLE #language en-US "My Dynamic Page" + #language fr-FR "My Dynamic Page Spanish" +#string STR_SUBTITLE_TEXT #language en-US "My subtitle text" + #language fr-FR "Mi texto del subtítulo" +#string STR_SUBTITLE_TEXT2 #language en-US " " + #language fr-FR " " +#string STR_CPU_STRING #language en-US "My CPU Speed is " + #language fr-FR "My CPU Speed is " +#string STR_CPU_STRING2 #language en-US " " + #language fr-FR " " +#string STR_NUMERIC_NUM_PROMPT #language en-US "Please select the number" + #language fr-FR "Please select the number" +#string STR_NUMERIC_NUM_HELP #language en-US "Check the input number, test the efi buffer varstore" + #language fr-FR "Check the input number, test the efi buffer varstore" +#string STR_ONE_OF_PROMPT #language en-US "My one-of prompt #1" + #language fr-FR "Mi uno- de guía # 1" +#string STR_ONE_OF_PROMPT_KEYWORD #language en-US "My Keyword Namespace Test" + #language fr-FR "My Keyword Namespace Test" + #language x-UEFI-ns "iSCSIBootEnable" +#string STR_CHECK_KEYWORD_SUPPORT #language en-US "Check iSCSI Boot Enable" + #language fr-FR "Check iSCSI Boot Enable" +#string STR_ONE_OF_HELP #language en-US "My one-of help is going to be a long string to test out the efficiency of the ability of the I am tired of typing capabilities" + #language fr-FR "Mi uno- de ayuda va a ser una cadena larga a probar fuera de la eficacia de la capacidad del yo es cansada de capacidades el pulsar." +#string STR_ONE_OF_TEXT1 #language en-US "My one-of text #1" + #language fr-FR "Mi uno- del texto # 1" +#string STR_ONE_OF_TEXT2 #language en-US "My one-of text #2" + #language fr-FR "Mi uno- del texto # 2" +#string STR_ONE_OF_TEXT3 #language en-US "My one-of text #3" + #language fr-FR "Mi uno- del texto # 3" +#string STR_ONE_OF_TEXT4 #language en-US "Suppress the Checkbox" + #language fr-FR "Mi uno- del texto # 4" +#string STR_ONE_OF_TEXT5 #language en-US "GrayOut the Checkbox" + #language fr-FR "Mi uno- del texto # 5" +#string STR_ONE_OF_TEXT6 #language en-US "Enable Checkbox" + #language fr-FR "Mi uno- del texto # 6" +#string STR_BOOT_ORDER1 #language en-US "SmallBootList" + #language fr-FR "Mi uno- del texto # 7" +#string STR_BOOT_ORDER2 #language en-US "LargeBootList" + #language fr-FR "Mi uno- del texto # 8" +#string STR_CHECK_BOX_PROMPT #language en-US "Activate this check box" + #language fr-FR "Active Las Armas Nucleares" +#string STR_CHECK_BOX_HELP #language en-US "This is the help message for the activation of check boxes. This is not to be confused with activating Czech boxes, since one can never tell what the ramifications are of activating foreign controlled boxes are." + #language fr-FR "Éste es el mensaje de la ayuda para la activación del armamento nuclear. Cuál es exactamente resistente calcular fuera sobre de eso?" +#string STR_CHECK_DYNAMIC_PROMPT #language en-US "Activate Dynamic check box" + #language fr-FR "Activate Dynamico check box" +#string STR_CHECK_DYNAMIC_HELP #language en-US "This is the help message for the activation of check boxes. This is not to be confused with activating Czech boxes, since one can never tell what the ramifications are of activating foreign controlled boxes are." + #language fr-FR "Spanish - This is the help message for the activation of check boxes. This is not to be confused with activating Czech boxes, since one can never tell what the ramifications are of activating foreign controlled boxes are." +#string STR_NUMERIC_PROMPT #language en-US "How old are you?" + #language fr-FR "Cómo viejo es usted?" +#string STR_NUMERIC_STEP_PROMPT #language en-US "How old are you? (Step)" + #language fr-FR "Cómo viejo es usted?(Step)" +#string STR_NUMERIC_PROMPT1 #language en-US "How tall are you?" + #language fr-FR "Cómo viejo es usted?" +#string STR_NUMERIC_READONLY_PROMPT #language en-US "How old are you?(Readonly)" + #language fr-FR "Cómo viejo es usted?(Readonly)" +#string STR_NUMERIC_MANUAL_PROMPT #language en-US "How old are you? (Manual)" + #language fr-FR "Cómo viejo es usted? (Manual)" +#string STR_TALL_HEX_PROMPT #language en-US "How tall are you? (Hex)" + #language fr-FR "Cómo viejo es usted? (Hex)" +#string STR_MYIFRNVDATA2_HEX_PROMPT #language en-US "MyIfrNVData2 Uint8? (Hex)" +#string STR_MYIFRNVDATA2_HEX_HELP #language en-US "MyIfrNVData2 Uint8? (Hex) Help" +#string STR_NUMERIC_HELP0 #language en-US "This is the help for those who are too old to understand the question. Type how old you are in a numeric value. The valid range in this case is from 0 to 240. Let's see if you actually read this help and figure that out." + #language fr-FR "This is the help for those who are too old to understand the question. Type how old you are in a numeric value. The valid range in this case is from 0 to 240. Let's see if you actually read this help and figure that out." +#string STR_NUMERIC_HELP1 #language en-US "This is the help for those who are curious about body height. Type how tall you are in a numeric value. The valid range in this case is from 0 to 250. Let's see if you actually read this help and figure that out." + #language fr-FR "This is the help for those who are curious about body height. Type how tall you are in a numeric value. The valid range in this case is from 0 to 250. Let's see if you actually read this help and figure that out." +#string STR_NUMERIC_HELP2 #language en-US "This is the help for those who are too old to understand the question. Adjust how old you are step by step. The valid range in this case is from 0 to 243 in step of 1. Let's see if you actually read this help and figure that out." + #language fr-FR "This is the help for those who are too old to understand the question. Adjust how old you are step by step. The valid range in this case is from 0 to 243 in step of 1. Let's see if you actually read this help and figure that out." +#string STR_NUMERIC_HELP3 #language en-US "This is the help for those who are curious about body height. Type how tall you are in a numeric value. The valid range in this case is from 0 to 190. Let's see if you actually read this help and figure that out." + #language fr-FR "Ésta es la ayuda para los que sean demasiado viejos entender la pregunta. Pulse cómo es viejo usted está en años." + +#string STR_PASSWORD_PROMPT #language en-US "Set the system password" + #language fr-FR "Cuál es la palabra mágica?" +#string STR_TEXT_SECRUITY_TEST_TEXT #language en-US "Access only permitted for Admin" + #language fr-FR "Access only permitted for Admin" +#string STR_TEXT_SECRUITY_TEST_HELP #language en-US "If this label is not gray, then current user has admin access setup permission. If this label is gray, then current user has no admin access setup permission." + #language fr-FR "If this label is not gray, then current user has admin access setup permission. If this label is gray, then current user has no admin access setup permission." +#string STR_GOTO_FORM1 #language en-US "Enter Page 1" + #language fr-FR "Vaya a paginar 1" +#string STR_GOTO_FORM2 #language en-US "Enter Page 2" + #language fr-FR "Vaya a paginar 2" +#string STR_GOTO_FORM3 #language en-US "Enter Page 3" + #language fr-FR "Vaya a paginar 3" +#string STR_GOTO_FORM4 #language en-US "Enter Page 4 Formmap Page" + #language fr-FR "Vaya a paginar 4" +#string STR_GOTO_FORM6 #language en-US "Enter Page 6" + #language fr-FR "Enter Page 6" +#string STR_GOTO_FORM5 #language en-US "Enter Page 5 Modal Page" + #language fr-FR "Enter Page 5 Modal Page" +#string STR_GOTO_FORM5_HELP #language en-US "Enter this form to double confirm the selection, must select on option before exit!" + #language fr-FR "Enter this form to double confirm the selection, must select on option before exit!" +#string STR_GOTO_DYNAMIC #language en-US "Goto Dynamic Page +" + #language fr-FR "Vaya a página dynamico" +#string STR_GOTO_DYNAMIC2 #language en-US "Goto Fresh Dynamic Page" + #language fr-FR "Vaya a página fresca dynamico" +#string STR_GOTO_DYNAMIC3 #language en-US "Update dest through retrieve" + #language fr-FR "Update dest through retrieve" +#string STR_GOTO_DYNAMIC4 #language en-US "Update dest through changing" + #language fr-FR "Update dest through changing" +#string STR_GOTO_DYNAMIC3_HELP #language en-US "Update the destination through "retrieve" call back type before user select it." + #language fr-FR "Update the destination through "retrieve" call back type before user select it." +#string STR_GOTO_DYNAMIC4_HELP #language en-US "Update the destination through "changing" call back type when user select it." + #language fr-FR "Update the destination through "changing" call back type when user select it." +#string STR_ERROR_INCONSISTENT #language en-US "This is my inconsistent error message" + #language fr-FR "Éste es mi mensaje de error contrario." +#string STR_ERROR_POPUP #language en-US "You typed in something bad!" + #language fr-FR "Esto es un mensaje de error del popup." +#string STR_MY_STRING_DEFAULT #language en-US "my password" + #language fr-FR "my password" +#string STR_MY_STRING_PROMPT2 #language en-US "String - Interactive" + #language fr-FR "String - interactive" +#string STR_MY_STRING_HELP2 #language en-US "This is my string help - Interactive" + #language fr-FR "This is my string help - interactive" +#string STR_TEXT_TEXT_1 #language en-US "This is my 1st text string" + #language fr-FR "This is my 1st text string" +#string STR_TEXT_TEXT_2 #language en-US "This is my 2nd text string that I am testing" + #language fr-FR "This is my 2nd text string that I am testing" +#string STR_DATE_PROMPT #language en-US "System Date" + #language fr-FR "Fecha del sistema" +#string STR_DATE_HELP #language en-US "This is the help for the Date (month/day/year). (Error checking will be done against month/day/year combinations that are not supported.)" + #language fr-FR "Esto es la ayuda para el Fecha (mes/día/las). (Verificar de error se hará contra vez mes/día/las combinaciones de año que no se sostienen.)" +#string STR_TIME_PROMPT #language en-US "System Time" + #language fr-FR "Tiempo del sistema" +#string STR_TIME_HELP #language en-US "This is the help for the Time (hour/minute/second)." + #language fr-FR "Esto es la ayuda para el Tiempo (hora/minuto/segundo)." +#string STR_RESTORE_DEFAULTS_PROMPT #language en-US "This is my restore defaults prompt" + #language fr-FR "This is my Spanish restore defaults prompt" +#string STR_RESTORE_DEFAULTS_HELP #language en-US "This is my restore defaults help" + #language fr-FR "Ésta es mi ayuda española de los defectos del restore" +#string STR_SAVE_DEFAULTS_PROMPT #language en-US "This is my save defaults prompt" + #language fr-FR "This is my Spanish save defaults prompt" +#string STR_SAVE_DEFAULTS_HELP #language en-US "This is my save defaults help" + #language fr-FR "This is my Spanish save defaults help" +#string STR_TEXT_HELP #language en-US "This is my text help" + #language fr-FR "This is my Spanish text help" +#string STR_GOTO_HELP #language en-US "This is my goto help" + #language fr-FR "This is my Spanish goto help" +#string STR_BANNER_TITLE #language en-US "This is my English banner title" + #language fr-FR "Éste es mi título español de la bandera" +#string STR_SUPPRESS_IF_TEXT1 #language en-US "This is my English suppress-if text1" + #language fr-FR "This is my Spanish suppress-if text1" +#string STR_SUPPRESS_IF_TEXT2 #language en-US "This is my English suppress-if text2" + #language fr-FR "This is my Spanish suppress-if text2" +#string STR_GRAYOUT_IF_TEXT1 #language en-US "This is my English grayout-if text1" + #language fr-FR "This is my Spanish grayout-if text1" +#string STR_GRAYOUT_IF_TEXT2 #language en-US "This is my English grayout-if text2" + #language fr-FR "This is my Spanish grayout-if text2" +#string STR_INVENTORY_HELP #language en-US "This is my English inventory help string" + #language fr-FR "This is my Spanish inventory help string" +#string STR_INVENTORY_TEXT1 #language en-US "This is my English inventory text1 string" + #language fr-FR "This is my Spanish inventory text1 string" +#string STR_INVENTORY_TEXT2 #language en-US "This is my English inventory text2 string" + #language fr-FR "This is my Spanish inventory text2 string" +#string STR_TEST_OPCODE #language en-US "Pick 1" + #language fr-FR "Pick 1" +#string STR_TEST_OPCODE2 #language en-US "Pick 2" + #language fr-FR "Pick 2" +#string STR_EXIT_TEXT #language en-US "Exit now!" + #language fr-FR "Salir ahora!" +#string STR_SAVE_TEXT #language en-US "Save now!" + #language fr-FR "(French)Save now!" +#string STR_RESET_TEXT #language en-US "Reset now!" + #language fr-FR "(French)Reset now!" +#string STR_DISCARD_CURRENT #language en-US "Discard current form now!" + #language fr-FR "(French)Discard current form now!" +#string STR_SAVE_CURRENT #language en-US "Save current form now!" + #language fr-FR "(French)Save current form now!" +#string STR_DISCARD_CURRENT_AND_EXIT #language en-US "Discard current form and exit now!" + #language fr-FR "(French)Discard current form and exit now!" +#string STR_SAVE_CURRENT_AND_EXIT #language en-US "Save current form and exit now!" + #language fr-FR "(French)Save current form and exit now!" +#string STR_NULL_STRING #language en-US "" + #language fr-FR "" +#string STR_TEST_STRING #language en-US "This is a test string" + #language fr-FR "Esto es una secuencia de la prueba" +#string STR_GRAYOUT_TEST #language en-US "Grayout VarEq test" + #language fr-FR "Grayout VarEq test" +#string STR_SUPPRESS_TEST #language en-US "Suppress VarEq test" + #language fr-FR "Suppress VarEq test" +#string STR_CLEAR_TEST #language en-US "Clear VarEq test" + #language fr-FR "Clear VarEq test" +#string STR_STANDARD_DEFAULT_PROMPT #language en-US "Reset to Standard Default" + #language fr-FR "Reset to Standard Default" +#string STR_STANDARD_DEFAULT_HELP #language en-US "This will reset all the Questions to their standard default value" + #language fr-FR "This will reset all the Questions to their standard default value" +#string STR_MANUFACTURE_DEFAULT_PROMPT #language en-US "Reset to Manufacture Default" + #language fr-FR "Reset to Manufacture Default" +#string STR_MANUFACTURE_DEFAULT_HELP #language en-US "This will reset all the Questions to their manufacture default value" + #language fr-FR "This will reset all the Questions to their manufacture default value" +#string STR_NAME_VALUE_VAR_NAME0_HELP #language en-US "This numeric(UINT8) use Name/Value storage, Name is NameValueVar0" + #language fr-FR "This numeric(UINT8) use Name/Value storage, Name is NameValueVar0" +#string STR_NAME_VALUE_VAR_NAME1_HELP #language en-US "This numeric(UINT16) use Name/Value storage, Name is NameValueVar1" + #language fr-FR "This numeric(UINT16) use Name/Value storage, Name is NameValueVar1" +#string STR_NAME_VALUE_VAR_NAME2_HELP #language en-US "This string use Name/Value storage, Name is NameValueVar2" + #language fr-FR "This string use Name/Value storage, Name is NameValueVar2" +#string STR_STRING_CHECK #language en-US "STRING" + #language fr-FR "STRING" +#string STR_STRING_CHECK_ERROR_POPUP #language en-US "String you typed in is not correct!" + #language fr-FR "String you typed in is not correct!" +#string STR_TEXT_REFRESH_GUID #language en-US "Add new menu by press "Ctrl + C"" + #language fr-FR "Add new menu by press "Ctrl + C"" +#string STR_TEXT_REFRESH_GUID_COUNT #language en-US "Refresh guid event count" + #language fr-FR "Refresh guid event count" +#string STR_MODAL_FORM_TITLE #language en-US "First Modal Form" + #language fr-FR "First Modal Form" +#string STR_EXIT_THROUGH_FORM_DISCARD_EXIT #language en-US "Exit through form discard and exit" + #language fr-FR "Exit through form discard and exit" +#string STR_EXIT_THROUGH_FORM_SUBMIT_EXIT #language en-US "Exit through form submit and exit" + #language fr-FR "Exit through form submit and exit" +#string STR_DEFAULT_VALUE_FROM_CALLBACK_PROMPT #language en-US "Get Call Back default" + #language fr-FR "Get Call Back default" +#string STR_DEFAULT_VALUE_FROM_CALLBACK_HELP #language en-US "This is the help for getting default value from call back function" + #language fr-FR "This is the help for getting default value from call back function" +#string STR_DEFAULT_VALUE_FROM_ACCESS_PROMPT #language en-US "Get ExtractConfig default" + #language fr-FR "Get ExtractConfig default" +#string STR_DEFAULT_VALUE_FROM_ACCESS_HELP #language en-US "This is the help for getting default value from ExtractConfig function" + #language fr-FR "This is the help for getting default value from ExtractConfig function" +#string STR_GOTO_ANOTHER_FORMSET #language en-US "Goto ABC Information Sample FormSet" + #language fr-FR "Goto ABC Information Sample FormSet" +#string STR_GOTO_ANOTHER_FORMSET_HELP #language en-US "When select this option, browser will go to another formset." + #language fr-FR "When select this option, browser will go to another formset." +#string STR_DEVICE_PATH #language en-US "" + #language fr-FR "" +#string STR_SUBMITTED_CALLBACK_TEST_PROMPT #language en-US "Submitted callback test" + #language fr-FR "Submitted callback test" +#string STR_SUBMITTED_CALLBACK_TEST_HELP #language en-US "Change the value and press F10 to submmit will pop up a dialogue to show SUBMITTED Callback has been triggered" + #language fr-FR "Change the value and press F10 to submmit will pop up a dialogue to show SUBMITTED Callback has been triggered" +#string STR_POPUP_TEST_PROMPT #language en-US "Select it to invoke Hii Popup Protocol" + #language fr-FR "Select it to invoke Hii Popup Protocol" +#string STR_POPUP_TEST_HELP #language en-US "Select this question will pop up a message box, then user can decide whether exit curret form or not" + #language fr-FR "Select this question will pop up a message box, then user can decide whether exit curret form or not" +#string STR_POPUP_STRING #language en-US "Are you sure to exit current form?" + #language fr-FR "Are you sure to exit current form?" +// +// Form 7 to show Questions which refer to Union Bit varstore +// +#string STR_FORM7_TITLE #language en-US "Form to Show Questions with union and bit VarStore" + #language fr-FR "Form to Show Questions with union and bit VarStore" +#string STR_GOTO_FORM7 #language en-US "Enter Page 7" + #language fr-FR "Enter Page 7" +#string STR_GOTO_FORM7_HELP #language en-US "This Form is to Show Questions with union and bit VarStore" + #language fr-FR "This Form is to Show Questions with union and bit VarStore" +#string STR_NEST_BIT_EFI_VARSTORE #language en-US "Nested BIT fields in efivarstore" + #language fr-FR "Nested BIT fields in efivarstore" +#string STR_BIT_EFI_VARSTORE #language en-US "BIT fields in efivarstore" + #language fr-FR "BIT fields in efivarstore" +#string STR_NEST_BIT_VARSTORE #language en-US "Nested BIT fields in bufferstore" + #language fr-FR "Nested BIT fields in bufferstore" +#string STR_BIT_VARSTORE #language en-US "BIT fields in bufferstore" + #language fr-FR "BIT fields in bufferstore" +#string STR_UNION_EFI_VARSTORE #language en-US "Union efivarstore" + #language fr-FR "Union efivarstore" +#string STR_BIT_NEST_CHECK_BOX_PROMPT #language en-US "NEST_BIT check box" + #language fr-FR "NEST_BIT check box" +#string STR_BIT_NEST_CHECK_BOX_HELP #language en-US "The check box refer to nested bit field, the default is checked" + #language fr-FR "The check box refer to nested bit field, the default is checked" +#string STR_ONE_OF_BIT_NEST_PROMPT #language en-US "NEST_BIT one-of" + #language fr-FR "NEST_BIT one-of" +#string STR_ONE_OF_BIT_NEST_HELP #language en-US "The oneof refer to nested bit field" + #language fr-FR "The oneof refer to nested bit field" +#string STR_BIT_NEST_NUMERIC_PROMPT #language en-US "NEST_BIT numeric" + #language fr-FR "NEST_BIT numeric" +#string STR_BIT_NEST_NUMERIC_HELP #language en-US "The numeric refer to nested bit field, the Standard default is 6 Manufacture default is 7" + #language fr-FR "The numeric refer to nested bit field, the Standard default is 6 Manufacture default is 7" +#string BYTE_QUESTION_NEST_BIT_PROMPT #language en-US "Use byte field in NEST_BIT structure" + #language fr-FR "Use byte field in NEST_BIT structure" +#string BYTE_QUESTION_NEST_BIT_HELP #language en-US "The Question refer to byte field in NEST_BIT structure" + #language fr-FR "The Question refer to byte field in NEST_BIT structure" +#string STR_BIT_NEST_NUMERIC_DEFAULT_HELP #language en-US "NEST_BIT numeric, default value form callback function, the Standard default is C Manufacture default is D" + #language fr-FR "NEST_BIT numeric, default value form callback function, the Standard default is C Manufacture default is D" +#string STR_BIT_CHECK_BOX_PROMPT #language en-US "BIT check box" + #language fr-FR "BIT check box" +#string STR_BIT_CHECK_BOX_HELP #language en-US "The check box refer to bit field, the default is checked" + #language fr-FR "The check box refer to bit field, the default is checked" +#string STR_ONE_OF_BIT_PROMPT #language en-US "BIT one-of" + #language fr-FR "BIT one-of" +#string STR_ONE_OF_BIT_HELP #language en-US "The one-of refer to bit field" + #language fr-FR "The one-of refer to bit field" +#string STR_BIT_NUMERIC_PROMPT #language en-US "BIT numeric" + #language fr-FR "BIT numeric" +#string STR_BIT_NUMERIC_HELP #language en-US "The numeric refer to bit field, the Standard default is 4 Manufacture default is 5" + #language fr-FR "The numeric refer to bit field the Standard default is 4 Manufacture default is 5" +#string STR_BUFFER_BIT_NUMERIC_HELP #language en-US "The numeric refer to bit field, the Standard default is 16 Manufacture default is 17" + #language fr-FR "The numeric refer to bit field, the Standard default is 16 Manufacture default is 17" +#string BYTE_QUESTION_BIT_PROMPT #language en-US "Use byte field in BIT structure" + #language fr-FR "Use byte field in BIT structure" +#string BYTE_QUESTION_BIT_HELP #language en-US "The question refer to byte field in BIT structure" + #language fr-FR "The question refer to byte field in BIT structure" +#string STR_UNION_BYTE_NUMERIC_PROMPT #language en-US "UNION EfiVarStore byte numeric" + #language fr-FR "UNION EfiVarStore byte numeric" +#string STR_UNION_BYTE_NUMERIC_HELP #language en-US "Question refer to byte field in UNION type efivastore, the Standard default is 7 Manufacture default is 8" + #language fr-FR "Question refer to byte field in UNION type efivastore, the Standard default is 7 Manufacture default is 8" +// Boot Order +#string STR_BOOT_TITLE #language en-US "Boot" +#string STR_BOOT_OPTIONS #language en-US "Boot Order" +#string STR_BOOT_OPTION1 #language en-US "IDE HDD" +#string STR_BOOT_OPTION2 #language en-US "ATAPI CD" +#string STR_BOOT_OPTION3 #language en-US "PXE" +#string STR_BOOT_OPTION4 #language en-US "USB" + +#string STR_VAR_NAME #language en-US "MyVar" + +#string STR_DEFAULTSTORE_MFG #language en-US "Manufacture Default" + #language fr-FR "Manufacture Default" + +#string STR_DEFAULTSTORE_SAFE #language en-US "Safe Default" + #language fr-FR "Safe Default" + +#string STR_STANDARD_DEFAULT_PROMPT #language en-US "Standard Default" + #language fr-FR "Standard Default" +#string STR_MANUFACTURE_DEFAULT_PROMPT #language en-US "Manufacture Default" + #language fr-FR "Manufacture Default" + +// +// Name list of Name/Value storage +// +#string STR_NAME_VALUE_VAR_NAME0 #language en-US "NameValueVar0" + #language fr-FR "NameValueVar0 (fr-FR)" +#string STR_NAME_VALUE_VAR_NAME1 #language en-US "NameValueVar1" + #language fr-FR "NameValueVar1 (fr-FR)" +#string STR_NAME_VALUE_VAR_NAME2 #language en-US "NameValueVar2" + #language fr-FR "NameValueVar2 (fr-FR)" +// +// Form Map method +// +#string STR_SAMPL_MAP_METHOD #language en-US "Sample Map Form" + #language fr-FR "Sample Map Form" +#string STR_STANDARD_MAP_METHOD #language en-US "UEFI Standard Map Form" + #language fr-FR "UEFI Standard Map Form" +// +// Serial Port +// +#string STR_SERIAL_PORT #language en-US "Serial port number" + #language fr-FR "Serial port number" +#string STR_SERIAL_PORT_DISABLE #language en-US "Serial port disable" + #language fr-FR "Serial port disable" +#string STR_SERIAL_PORT1 #language en-US "Serial port 1" + #language fr-FR "Serial port 1" +#string STR_SERIAL_PORT2 #language en-US "Serial port 2" + #language fr-FR "Serial port 2" +#string STR_SERIAL_PORT3 #language en-US "Serial port 3" + #language fr-FR "Serial port 3" +#string STR_SERIAL_PORT4 #language en-US "Serial port 4" + #language fr-FR "Serial port 4" + +#string STR_SERIAL_PORT_STATUS #language en-US "Serial port is enable" + #language fr-FR "Serial port is enable" +#string STR_SERIAL_PORT_IO_ADDRESS #language en-US "Serial port IO address" + #language fr-FR "Serial port IO address" +#string STR_SERIAL_PORT_IRQ #language en-US "Serial port IRQ value" + #language fr-FR "Serial port IRQ value" + +#string STR_SERIAL_PORT1_IOADDR #language en-US "3F8" + #language fr-FR "3F8" +#string STR_SERIAL_PORT2_IOADDR #language en-US "2F8" + #language fr-FR "2F8" +#string STR_SERIAL_PORT3_IOADDR #language en-US "3E8" + #language fr-FR "3E8" +#string STR_SERIAL_PORT4_IOADDR #language en-US "2E8" + #language fr-FR "2E8" + +#string STR_SERIAL_PORT13_IRQ #language en-US "4" + #language fr-FR "4" +#string STR_SERIAL_PORT24_IRQ #language en-US "3" + #language fr-FR "3" + +// +// TEXT/DATE/TIME +// +#string STR_TEXT_SAMPLE_HELP #language en-US "Text Help" + #language fr-FR "Text Help" +#string STR_TEXT_SAMPLE_STRING #language en-US "Text Sample" + #language fr-FR "Text Sample" +#string STR_DATE_SAMPLE_HELP #language en-US "Date Sample" + #language fr-FR "Date Sample" +#string STR_TIME_SAMPLE_HELP #language en-US "Time Sample" + #language fr-FR "Time Sample" + +#string FUNCTION_NINE_STRING #language en-US "F9=Reset to Defaults" + #language fr-FR "F9=Reset à Défauts" +#string FUNCTION_TEN_STRING #language en-US "F10=Save" + #language fr-FR "F10=Économiser" +#string STR_WARNING_POPUP #language en-US "Change value requires to reset the system." + #language fr-FR "Change value requires to reset the system." +#string STR_MATCH2_PROMPT #language en-US "Match2 Test" + #language fr-FR "Match2 Test" +#string STR_MATCH2_HELP #language en-US "This question used to test the match2 opcode." + #language fr-FR "This question used to test the match2 opcode." +#string STR_STRING #language en-US "Match2 Test" + #language fr-FR "Match2 Test" +#string STR_PATTERN #language en-US "Match2" + #language fr-FR "Match2" diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcLowLevel.S b/roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcLowLevel.S new file mode 100644 index 000000000..a84d5b330 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcLowLevel.S @@ -0,0 +1,156 @@ +///** @file +// +// This code provides low level routines that support the Virtual Machine +// for option ROMs. +// +// Copyright (c) 2016, Linaro, Ltd. All rights reserved.
+// Copyright (c) 2015, The Linux Foundation. All rights reserved.
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +//**/ + +ASM_GLOBAL ASM_PFX(EbcLLCALLEXNative) +ASM_GLOBAL ASM_PFX(EbcLLEbcInterpret) +ASM_GLOBAL ASM_PFX(EbcLLExecuteEbcImageEntryPoint) + +ASM_GLOBAL ASM_PFX(mEbcInstructionBufferTemplate) + +//**************************************************************************** +// EbcLLCALLEX +// +// This function is called to execute an EBC CALLEX instruction. +// This instruction requires that we thunk out to external native +// code. For AArch64, we copy the VM stack into the main stack and then pop +// the first 8 arguments off according to the AArch64 Procedure Call Standard +// On return, we restore the stack pointer to its original location. +// +//**************************************************************************** +// UINTN EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr) +ASM_PFX(EbcLLCALLEXNative): + mov x8, x0 // Preserve x0 + mov x9, x1 // Preserve x1 + + // + // If the EBC stack frame is smaller than or equal to 64 bytes, we know there + // are no stacked arguments #9 and beyond that we need to copy to the native + // stack. In this case, we can perform a tail call which is much more + // efficient, since there is no need to touch the native stack at all. + // + sub x3, x2, x1 // Length = NewStackPointer - FramePtr + cmp x3, #64 + b.gt 1f + + // + // While probably harmless in practice, we should not access the VM stack + // outside of the interval [NewStackPointer, FramePtr), which means we + // should not blindly fill all 8 argument registers with VM stack data. + // So instead, calculate how many argument registers we can fill based on + // the size of the VM stack frame, and skip the remaining ones. + // + adr x0, 0f // Take address of 'br' instruction below + bic x3, x3, #7 // Ensure correct alignment + sub x0, x0, x3, lsr #1 // Subtract 4 bytes for each arg to unstack + br x0 // Skip remaining argument registers + + ldr x7, [x9, #56] // Call with 8 arguments + ldr x6, [x9, #48] // | + ldr x5, [x9, #40] // | + ldr x4, [x9, #32] // | + ldr x3, [x9, #24] // | + ldr x2, [x9, #16] // | + ldr x1, [x9, #8] // V + ldr x0, [x9] // Call with 1 argument + +0: br x8 // Call with no arguments + + // + // More than 64 bytes: we need to build the full native stack frame and copy + // the part of the VM stack exceeding 64 bytes (which may contain stacked + // arguments) to the native stack + // +1: stp x29, x30, [sp, #-16]! + mov x29, sp + + // + // Ensure that the stack pointer remains 16 byte aligned, + // even if the size of the VM stack frame is not a multiple of 16 + // + add x1, x1, #64 // Skip over [potential] reg params + tbz x3, #3, 2f // Multiple of 16? + ldr x4, [x2, #-8]! // No? Then push one word + str x4, [sp, #-16]! // ... but use two slots + b 3f + +2: ldp x4, x5, [x2, #-16]! + stp x4, x5, [sp, #-16]! +3: cmp x2, x1 + b.gt 2b + + ldp x0, x1, [x9] + ldp x2, x3, [x9, #16] + ldp x4, x5, [x9, #32] + ldp x6, x7, [x9, #48] + + blr x8 + + mov sp, x29 + ldp x29, x30, [sp], #16 + ret + +//**************************************************************************** +// EbcLLEbcInterpret +// +// This function is called by the thunk code to handle an Native to EBC call +// This can handle up to 16 arguments (1-8 on in x0-x7, 9-16 are on the stack) +// x16 contains the Entry point that will be the first stacked argument when +// EBCInterpret is called. +// +//**************************************************************************** +ASM_PFX(EbcLLEbcInterpret): + stp x29, x30, [sp, #-16]! + mov x29, sp + + // push the entry point and the address of args #9 - #16 onto the stack + add x17, sp, #16 + stp x16, x17, [sp, #-16]! + + // call C-code + bl ASM_PFX(EbcInterpret) + + add sp, sp, #16 + ldp x29, x30, [sp], #16 + ret + +//**************************************************************************** +// EbcLLExecuteEbcImageEntryPoint +// +// This function is called by the thunk code to handle the image entry point +// x16 contains the Entry point that will be the third argument when +// ExecuteEbcImageEntryPoint is called. +// +//**************************************************************************** +ASM_PFX(EbcLLExecuteEbcImageEntryPoint): + mov x2, x16 + + // tail call to C code + b ASM_PFX(ExecuteEbcImageEntryPoint) + +//**************************************************************************** +// mEbcInstructionBufferTemplate +//**************************************************************************** + .section ".rodata", "a" + .align 3 +ASM_PFX(mEbcInstructionBufferTemplate): + adr x17, 0f + ldp x16, x17, [x17] + br x17 + + // + // Add a magic code here to help the VM recognize the thunk. + // + hlt #0xEBC + +0: .quad 0 // EBC_ENTRYPOINT_SIGNATURE + .quad 0 // EBC_LL_EBC_ENTRYPOINT_SIGNATURE diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcSupport.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcSupport.c new file mode 100644 index 000000000..a9512bd85 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/AArch64/EbcSupport.c @@ -0,0 +1,475 @@ +/** @file + This module contains EBC support routines that are customized based on + the target AArch64 processor. + +Copyright (c) 2016, Linaro, Ltd. All rights reserved.
+Copyright (c) 2015, The Linux Foundation. All rights reserved.
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EbcInt.h" +#include "EbcExecute.h" +#include "EbcDebuggerHook.h" + +// +// Amount of space that is not used in the stack +// +#define STACK_REMAIN_SIZE (1024 * 4) + +#pragma pack(1) +typedef struct { + UINT32 Instr[3]; + UINT32 Magic; + UINT64 EbcEntryPoint; + UINT64 EbcLlEntryPoint; +} EBC_INSTRUCTION_BUFFER; +#pragma pack() + +extern CONST EBC_INSTRUCTION_BUFFER mEbcInstructionBufferTemplate; + +/** + Begin executing an EBC image. + This is used for Ebc Thunk call. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcLLEbcInterpret ( + VOID + ); + +/** + Begin executing an EBC image. + This is used for Ebc image entrypoint. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcLLExecuteEbcImageEntryPoint ( + VOID + ); + +/** + Pushes a 64 bit unsigned value to the VM stack. + + @param VmPtr The pointer to current VM context. + @param Arg The value to be pushed. + +**/ +VOID +PushU64 ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Arg + ) +{ + // + // Advance the VM stack down, and then copy the argument to the stack. + // Hope it's aligned. + // + VmPtr->Gpr[0] -= sizeof (UINT64); + *(UINT64 *) VmPtr->Gpr[0] = Arg; + return; +} + + +/** + Begin executing an EBC image. + + This is a thunk function. + + @param Arg1 The 1st argument. + @param Arg2 The 2nd argument. + @param Arg3 The 3rd argument. + @param Arg4 The 4th argument. + @param Arg5 The 5th argument. + @param Arg6 The 6th argument. + @param Arg7 The 7th argument. + @param Arg8 The 8th argument. + @param EntryPoint The entrypoint of EBC code. + @param Args9_16[] Array containing arguments #9 to #16. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcInterpret ( + IN UINTN Arg1, + IN UINTN Arg2, + IN UINTN Arg3, + IN UINTN Arg4, + IN UINTN Arg5, + IN UINTN Arg6, + IN UINTN Arg7, + IN UINTN Arg8, + IN UINTN EntryPoint, + IN CONST UINTN Args9_16[] + ) +{ + // + // Create a new VM context on the stack + // + VM_CONTEXT VmContext; + UINTN Addr; + EFI_STATUS Status; + UINTN StackIndex; + + // + // Get the EBC entry point + // + Addr = EntryPoint; + + // + // Now clear out our context + // + ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT)); + + // + // Set the VM instruction pointer to the correct location in memory. + // + VmContext.Ip = (VMIP) Addr; + + // + // Initialize the stack pointer for the EBC. Get the current system stack + // pointer and adjust it down by the max needed for the interpreter. + // + + // + // Adjust the VM's stack pointer down. + // + + Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex); + if (EFI_ERROR(Status)) { + return Status; + } + VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE); + VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE); + VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0]; + VmContext.Gpr[0] -= sizeof (UINTN); + + // + // Align the stack on a natural boundary. + // + VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1); + + // + // Put a magic value in the stack gap, then adjust down again. + // + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE; + VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0]; + + // + // The stack upper to LowStackTop is belong to the VM. + // + VmContext.LowStackTop = (UINTN) VmContext.Gpr[0]; + + // + // For the worst case, assume there are 4 arguments passed in registers, store + // them to VM's stack. + // + PushU64 (&VmContext, (UINT64) Args9_16[7]); + PushU64 (&VmContext, (UINT64) Args9_16[6]); + PushU64 (&VmContext, (UINT64) Args9_16[5]); + PushU64 (&VmContext, (UINT64) Args9_16[4]); + PushU64 (&VmContext, (UINT64) Args9_16[3]); + PushU64 (&VmContext, (UINT64) Args9_16[2]); + PushU64 (&VmContext, (UINT64) Args9_16[1]); + PushU64 (&VmContext, (UINT64) Args9_16[0]); + PushU64 (&VmContext, (UINT64) Arg8); + PushU64 (&VmContext, (UINT64) Arg7); + PushU64 (&VmContext, (UINT64) Arg6); + PushU64 (&VmContext, (UINT64) Arg5); + PushU64 (&VmContext, (UINT64) Arg4); + PushU64 (&VmContext, (UINT64) Arg3); + PushU64 (&VmContext, (UINT64) Arg2); + PushU64 (&VmContext, (UINT64) Arg1); + + // + // Interpreter assumes 64-bit return address is pushed on the stack. + // AArch64 does not do this so pad the stack accordingly. + // + PushU64 (&VmContext, (UINT64) 0); + PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL); + + // + // For AArch64, this is where we say our return address is + // + VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0]; + + // + // We need to keep track of where the EBC stack starts. This way, if the EBC + // accesses any stack variables above its initial stack setting, then we know + // it's accessing variables passed into it, which means the data is on the + // VM's stack. + // When we're called, on the stack (high to low) we have the parameters, the + // return address, then the saved ebp. Save the pointer to the return address. + // EBC code knows that's there, so should look above it for function parameters. + // The offset is the size of locals (VMContext + Addr + saved ebp). + // Note that the interpreter assumes there is a 16 bytes of return address on + // the stack too, so adjust accordingly. + // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr)); + // + + // + // Begin executing the EBC code + // + EbcDebuggerHookEbcInterpret (&VmContext); + EbcExecute (&VmContext); + + // + // Return the value in R[7] unless there was an error + // + ReturnEBCStack(StackIndex); + return (UINT64) VmContext.Gpr[7]; +} + + +/** + Begin executing an EBC image. + + @param ImageHandle image handle for the EBC application we're executing + @param SystemTable standard system table passed into an driver's entry + point + @param EntryPoint The entrypoint of EBC code. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +ExecuteEbcImageEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable, + IN UINTN EntryPoint + ) +{ + // + // Create a new VM context on the stack + // + VM_CONTEXT VmContext; + UINTN Addr; + EFI_STATUS Status; + UINTN StackIndex; + + // + // Get the EBC entry point + // + Addr = EntryPoint; + + // + // Now clear out our context + // + ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT)); + + // + // Save the image handle so we can track the thunks created for this image + // + VmContext.ImageHandle = ImageHandle; + VmContext.SystemTable = SystemTable; + + // + // Set the VM instruction pointer to the correct location in memory. + // + VmContext.Ip = (VMIP) Addr; + + // + // Initialize the stack pointer for the EBC. Get the current system stack + // pointer and adjust it down by the max needed for the interpreter. + // + + Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex); + if (EFI_ERROR(Status)) { + return Status; + } + VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE); + VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE); + VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0]; + VmContext.Gpr[0] -= sizeof (UINTN); + + + // + // Put a magic value in the stack gap, then adjust down again + // + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE; + VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0]; + + // + // Align the stack on a natural boundary + VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof(UINTN) - 1); + // + VmContext.LowStackTop = (UINTN) VmContext.Gpr[0]; + + // + // Simply copy the image handle and system table onto the EBC stack. + // Greatly simplifies things by not having to spill the args. + // + PushU64 (&VmContext, (UINT64) SystemTable); + PushU64 (&VmContext, (UINT64) ImageHandle); + + // + // VM pushes 16-bytes for return address. Simulate that here. + // + PushU64 (&VmContext, (UINT64) 0); + PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL); + + // + // For AArch64, this is where we say our return address is + // + VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0]; + + // + // Entry function needn't access high stack context, simply + // put the stack pointer here. + // + + // + // Begin executing the EBC code + // + EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext); + EbcExecute (&VmContext); + + // + // Return the value in R[7] unless there was an error + // + ReturnEBCStack(StackIndex); + return (UINT64) VmContext.Gpr[7]; +} + + +/** + Create thunks for an EBC image entry point, or an EBC protocol service. + + @param ImageHandle Image handle for the EBC image. If not null, then + we're creating a thunk for an image entry point. + @param EbcEntryPoint Address of the EBC code that the thunk is to call + @param Thunk Returned thunk we create here + @param Flags Flags indicating options for creating the thunk + + @retval EFI_SUCCESS The thunk was created successfully. + @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit + aligned. + @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC + Thunk. + @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough. + +**/ +EFI_STATUS +EbcCreateThunks ( + IN EFI_HANDLE ImageHandle, + IN VOID *EbcEntryPoint, + OUT VOID **Thunk, + IN UINT32 Flags + ) +{ + EBC_INSTRUCTION_BUFFER *InstructionBuffer; + + // + // Check alignment of pointer to EBC code + // + if ((UINT32) (UINTN) EbcEntryPoint & 0x01) { + return EFI_INVALID_PARAMETER; + } + + InstructionBuffer = EbcAllocatePoolForThunk (sizeof (EBC_INSTRUCTION_BUFFER)); + if (InstructionBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Give them the address of our buffer we're going to fix up + // + *Thunk = InstructionBuffer; + + // + // Copy whole thunk instruction buffer template + // + CopyMem (InstructionBuffer, &mEbcInstructionBufferTemplate, + sizeof (EBC_INSTRUCTION_BUFFER)); + + // + // Patch EbcEntryPoint and EbcLLEbcInterpret + // + InstructionBuffer->EbcEntryPoint = (UINT64)EbcEntryPoint; + if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) { + InstructionBuffer->EbcLlEntryPoint = (UINT64)EbcLLExecuteEbcImageEntryPoint; + } else { + InstructionBuffer->EbcLlEntryPoint = (UINT64)EbcLLEbcInterpret; + } + + // + // Add the thunk to the list for this image. Do this last since the add + // function flushes the cache for us. + // + EbcAddImageThunk (ImageHandle, InstructionBuffer, + sizeof (EBC_INSTRUCTION_BUFFER)); + + return EFI_SUCCESS; +} + + +/** + This function is called to execute an EBC CALLEX instruction. + The function check the callee's content to see whether it is common native + code or a thunk to another piece of EBC code. + If the callee is common native code, use EbcLLCAllEXASM to manipulate, + otherwise, set the VM->IP to target EBC code directly to avoid another VM + be startup which cost time and stack space. + + @param VmPtr Pointer to a VM context. + @param FuncAddr Callee's address + @param NewStackPointer New stack pointer after the call + @param FramePtr New frame pointer after the call + @param Size The size of call instruction + +**/ +VOID +EbcLLCALLEX ( + IN VM_CONTEXT *VmPtr, + IN UINTN FuncAddr, + IN UINTN NewStackPointer, + IN VOID *FramePtr, + IN UINT8 Size + ) +{ + CONST EBC_INSTRUCTION_BUFFER *InstructionBuffer; + + // + // Processor specific code to check whether the callee is a thunk to EBC. + // + InstructionBuffer = (EBC_INSTRUCTION_BUFFER *)FuncAddr; + + if (CompareMem (InstructionBuffer, &mEbcInstructionBufferTemplate, + sizeof(EBC_INSTRUCTION_BUFFER) - 2 * sizeof (UINT64)) == 0) { + // + // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and + // put our return address and frame pointer on the VM stack. + // Then set the VM's IP to new EBC code. + // + VmPtr->Gpr[0] -= 8; + VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr); + VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0]; + VmPtr->Gpr[0] -= 8; + VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size)); + + VmPtr->Ip = (VMIP) InstructionBuffer->EbcEntryPoint; + } else { + // + // The callee is not a thunk to EBC, call native code, + // and get return value. + // + VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr); + + // + // Advance the IP. + // + VmPtr->Ip += Size; + } +} + diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf new file mode 100644 index 000000000..e94231760 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf @@ -0,0 +1,108 @@ +## @file +# EFI Byte Code (EBC) Debugger. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EbcDebugger + MODULE_UNI_FILE = EbcDebugger.uni + FILE_GUID = 8296AF37-D183-4416-B3B6-19D2A80AD4A8 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeEbcDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + EbcDebuggerHook.h + EbcInt.c + EbcInt.h + EbcExecute.c + EbcExecute.h + EbcDebugger/Edb.c + EbcDebugger/Edb.h + EbcDebugger/EdbCommon.h + EbcDebugger/EdbCmdBranch.c + EbcDebugger/EdbCmdBreak.c + EbcDebugger/EdbCmdBreakpoint.c + EbcDebugger/EdbCmdGo.c + EbcDebugger/EdbCmdHelp.c + EbcDebugger/EdbCmdMemory.c + EbcDebugger/EdbCmdRegister.c + EbcDebugger/EdbCmdQuit.c + EbcDebugger/EdbCmdScope.c + EbcDebugger/EdbCmdStep.c + EbcDebugger/EdbCmdSymbol.c + EbcDebugger/EdbCmdExtIo.c + EbcDebugger/EdbCmdExtPci.c + EbcDebugger/EdbCommand.c + EbcDebugger/EdbCommand.h + EbcDebugger/EdbDisasm.c + EbcDebugger/EdbDisasm.h + EbcDebugger/EdbDisasmSupport.c + EbcDebugger/EdbDisasmSupport.h + EbcDebugger/EdbSymbol.c + EbcDebugger/EdbSymbol.h + EbcDebugger/EdbHook.c + EbcDebugger/EdbHook.h + EbcDebugger/EdbSupport.h + EbcDebugger/EdbSupportUI.c + EbcDebugger/EdbSupportString.c + EbcDebugger/EdbSupportFile.c + +[Sources.Ia32] + Ia32/EbcSupport.c + Ia32/EbcLowLevel.nasm + +[Sources.X64] + X64/EbcSupport.c + X64/EbcLowLevel.nasm + +[Sources.AARCH64] + AArch64/EbcSupport.c + AArch64/EbcLowLevel.S + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + DebugLib + BaseLib + CacheMaintenanceLib + PeCoffLib + +[Protocols] + gEfiDebugSupportProtocolGuid ## PRODUCES + gEfiEbcProtocolGuid ## PRODUCES + gEfiDebuggerConfigurationProtocolGuid ## PRODUCES + gEfiEbcVmTestProtocolGuid ## SOMETIMES_PRODUCES + gEfiEbcSimpleDebuggerProtocolGuid ## SOMETIMES_CONSUMES + gEfiPciRootBridgeIoProtocolGuid ## SOMETIMES_CONSUMES + gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES + gEdkiiPeCoffImageEmulatorProtocolGuid ## PRODUCES + +[Guids] + gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## GUID + gEfiDebugImageInfoTableGuid ## SOMETIMES_CONSUMES ## GUID + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + EbcDebuggerExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.uni b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.uni new file mode 100644 index 000000000..11776211d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger.uni @@ -0,0 +1,13 @@ +// /** @file +// EFI Byte Code (EBC) Debugger. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "EFI Byte Code (EBC) Debugger application" + +#string STR_MODULE_DESCRIPTION #language en-US "This application enables the debugging of EBC runtimes." diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EbcDebuggerConfig.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EbcDebuggerConfig.c new file mode 100644 index 000000000..966dfb717 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EbcDebuggerConfig.c @@ -0,0 +1,247 @@ +/** @file + Configuration application for the EBC Debugger. + + Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#include "EdbCommon.h" +#include "EdbSupport.h" + +/** + + The function that displays the utility usage message. + +**/ +VOID +PrintUsage ( + VOID + ) +{ + Print ( + L"EbcDebuggerConfig Version 1.0\n" + L"Copyright (C) Intel Corp 2007-2016. All rights reserved.\n" + L"\n" + L"Configure EbcDebugger in EFI Shell Environment.\n" + L"\n" + L"usage: EdbCfg \n" + L" CommandList:\n" + L" BO[C|CX|R|E|T|K] - Enable/Disable BOC/BOCX/BOR/BOE/BOT/BOK.\n" +// L" SHOWINFO - Show Debugger Information.\n" + L"\n" + ); + return; +} + +/** + + The function is to show some information. + + @param DebuggerConfiguration Point to the EFI_DEBUGGER_CONFIGURATION_PROTOCOL. + +**/ +VOID +EdbShowInfo ( + EFI_DEBUGGER_CONFIGURATION_PROTOCOL *DebuggerConfiguration + ) +{ + Print (L"Not supported!\n"); + return ; +} + +/** + + EdbConfigBreak function. + + @param DebuggerConfiguration Point to the EFI_DEBUGGER_CONFIGURATION_PROTOCOL. + @param Command Point to the command. + @param CommandArg The argument for this command. + +**/ +VOID +EdbConfigBreak ( + EFI_DEBUGGER_CONFIGURATION_PROTOCOL *DebuggerConfiguration, + CHAR16 *Command, + CHAR16 *CommandArg + ) +{ + EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate; + + DebuggerPrivate = (EFI_DEBUGGER_PRIVATE_DATA *)DebuggerConfiguration->DebuggerPrivateData; + + if (StriCmp (Command, L"BOC") == 0) { + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOC) == EFI_DEBUG_FLAG_EBC_BOC) { + Print (L"BOC on\n"); + } else { + Print (L"BOC off\n"); + } + } else if (StriCmp (CommandArg, L"ON") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOC; + } else if (StriCmp (CommandArg, L"OFF") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOC; + } else { + Print (L"Invalid parameter\n"); + } + } else if (StriCmp (Command, L"BOCX") == 0) { + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOCX) == EFI_DEBUG_FLAG_EBC_BOCX) { + Print (L"BOCX on\n"); + } else { + Print (L"BOCX off\n"); + } + } else if (StriCmp (CommandArg, L"ON") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOCX; + } else if (StriCmp (CommandArg, L"OFF") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOCX; + } else { + Print (L"Invalid parameter\n"); + } + } else if (StriCmp (Command, L"BOR") == 0) { + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOR) == EFI_DEBUG_FLAG_EBC_BOR) { + Print (L"BOR on\n"); + } else { + Print (L"BOR off\n"); + } + } else if (StriCmp (CommandArg, L"ON") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOR; + } else if (StriCmp (CommandArg, L"OFF") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOR; + } else { + Print (L"Invalid parameter\n"); + } + } else if (StriCmp (Command, L"BOE") == 0) { + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOE) == EFI_DEBUG_FLAG_EBC_BOE) { + Print (L"BOE on\n"); + } else { + Print (L"BOE off\n"); + } + } else if (StriCmp (CommandArg, L"ON") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOE; + } else if (StriCmp (CommandArg, L"OFF") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOE; + } else { + Print (L"Invalid parameter\n"); + } + } else if (StriCmp (Command, L"BOT") == 0) { + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOT) == EFI_DEBUG_FLAG_EBC_BOT) { + Print (L"BOT on\n"); + } else { + Print (L"BOT off\n"); + } + } else if (StriCmp (CommandArg, L"ON") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOT; + } else if (StriCmp (CommandArg, L"OFF") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOT; + } else { + Print (L"Invalid parameter\n"); + } + } else if (StriCmp (Command, L"BOK") == 0) { + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOK) == EFI_DEBUG_FLAG_EBC_BOK) { + Print (L"BOK on\n"); + } else { + Print (L"BOK off\n"); + } + } else if (StriCmp (CommandArg, L"ON") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOK; + } else if (StriCmp (CommandArg, L"OFF") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOK; + } else { + Print (L"Invalid parameter\n"); + } + } + return ; +} + +/** + Alter the EBC Debugger configuration. + + @param[in] ImageHandle The image handle. + @param[in] SystemTable The system table. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_INVALID_PARAMETER Usage error. + @retval EFI_NOT_FOUND A running debugger cannot be located. +**/ +EFI_STATUS +EFIAPI +InitializeEbcDebuggerConfig ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + UINTN Argc; + CHAR16 **Argv; + EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters; + EFI_DEBUGGER_CONFIGURATION_PROTOCOL *DebuggerConfiguration; + EFI_STATUS Status; + + Status = gBS->HandleProtocol ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID**)&ShellParameters + ); + if (EFI_ERROR(Status)) { + Print (L"Please use UEFI Shell to run this application.\n"); + return EFI_INVALID_PARAMETER; + } + + Argc = ShellParameters->Argc; + Argv = ShellParameters->Argv; + + if (Argc < 2) { + PrintUsage (); + return EFI_INVALID_PARAMETER; + } + + if (Argc == 2) { + if ((StrCmp (Argv[1], L"/?") == 0) || + (StrCmp (Argv[1], L"-?") == 0) || + (StrCmp (Argv[1], L"-h") == 0) || + (StrCmp (Argv[1], L"-H") == 0) ) { + PrintUsage (); + return EFI_SUCCESS; + } + } + + Status = gBS->LocateProtocol ( + &gEfiDebuggerConfigurationProtocolGuid, + NULL, + (VOID**)&DebuggerConfiguration + ); + if (EFI_ERROR(Status)) { + Print (L"Error: DebuggerConfiguration protocol not found.\n"); + return EFI_NOT_FOUND; + } + + if (StriCmp (Argv[1], L"SHOWINFO") == 0) { + EdbShowInfo (DebuggerConfiguration); + return EFI_SUCCESS; + } + + if (((Argc == 2) || (Argc == 3)) && + ((StriCmp (Argv[1], L"BOC") == 0) || + (StriCmp (Argv[1], L"BOCX") == 0) || + (StriCmp (Argv[1], L"BOR") == 0) || + (StriCmp (Argv[1], L"BOE") == 0) || + (StriCmp (Argv[1], L"BOT") == 0) || + (StriCmp (Argv[1], L"BOK") == 0))) { + if (Argc == 3) { + EdbConfigBreak (DebuggerConfiguration, Argv[1], Argv[2]); + } else { + EdbConfigBreak (DebuggerConfiguration, Argv[1], NULL); + } + return EFI_SUCCESS; + } + + Print (L"Error: Invalid Command.\n"); + return EFI_INVALID_PARAMETER; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.c new file mode 100644 index 000000000..611b2de5d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.c @@ -0,0 +1,585 @@ +/** @file + +Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include "Edb.h" + +EFI_DEBUGGER_PRIVATE_DATA mDebuggerPrivate = { + EFI_DEBUGGER_SIGNATURE, // Signature + IsaEbc, // Isa + (EBC_DEBUGGER_MAJOR_VERSION << 16) | + EBC_DEBUGGER_MINOR_VERSION, // EfiDebuggerRevision + (VM_MAJOR_VERSION << 16) | + VM_MINOR_VERSION, // EbcVmRevision + { + EFI_DEBUGGER_CONFIGURATION_VERSION, + &mDebuggerPrivate, + }, // DebuggerConfiguration + NULL, // DebugImageInfoTableHeader + NULL, // Vol + NULL, // PciRootBridgeIo + mDebuggerCommandSet, // DebuggerCommandSet + {0}, // DebuggerSymbolContext + 0, // DebuggerBreakpointCount + {{0}}, // DebuggerBreakpointContext + 0, // CallStackEntryCount + {{0}}, // CallStackEntry + 0, // TraceEntryCount + {{0}}, // TraceEntry + {0}, // StepContext + {0}, // GoTilContext + 0, // InstructionScope + EFI_DEBUG_DEFAULT_INSTRUCTION_NUMBER, // InstructionNumber + EFI_DEBUG_FLAG_EBC_BOE | EFI_DEBUG_FLAG_EBC_BOT, // FeatureFlags + 0, // StatusFlags + FALSE, // EnablePageBreak + NULL // BreakEvent +}; + +CHAR16 *mExceptionStr[] = { + L"EXCEPT_EBC_UNDEFINED", + L"EXCEPT_EBC_DIVIDE_ERROR", + L"EXCEPT_EBC_DEBUG", + L"EXCEPT_EBC_BREAKPOINT", + L"EXCEPT_EBC_OVERFLOW", + L"EXCEPT_EBC_INVALID_OPCODE", + L"EXCEPT_EBC_STACK_FAULT", + L"EXCEPT_EBC_ALIGNMENT_CHECK", + L"EXCEPT_EBC_INSTRUCTION_ENCODING", + L"EXCEPT_EBC_BAD_BREAK", + L"EXCEPT_EBC_SINGLE_STEP", +}; + +/** + + Clear all the breakpoint. + + @param DebuggerPrivate EBC Debugger private data structure + @param NeedRemove Whether need to remove all the breakpoint + +**/ +VOID +EdbClearAllBreakpoint ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN BOOLEAN NeedRemove + ) +{ + UINTN Index; + + // + // Patch all the breakpoint + // + for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { + if (DebuggerPrivate->DebuggerBreakpointContext[Index].State) { + CopyMem ( + (VOID *)(UINTN)DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress, + &DebuggerPrivate->DebuggerBreakpointContext[Index].OldInstruction, + sizeof(UINT16) + ); + } + } + + // + // Zero Breakpoint context, if need to remove all breakpoint + // + if (NeedRemove) { + DebuggerPrivate->DebuggerBreakpointCount = 0; + ZeroMem (DebuggerPrivate->DebuggerBreakpointContext, sizeof(DebuggerPrivate->DebuggerBreakpointContext)); + } + + // + // Done + // + return ; +} + +/** + + Set all the breakpoint. + + @param DebuggerPrivate EBC Debugger private data structure + +**/ +VOID +EdbSetAllBreakpoint ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate + ) +{ + UINTN Index; + UINT16 Data16; + + // + // Set all the breakpoint (BREAK(3) : 0x0300) + // + Data16 = 0x0300; + for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { + if (DebuggerPrivate->DebuggerBreakpointContext[Index].State) { + CopyMem ( + (VOID *)(UINTN)DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress, + &Data16, + sizeof(UINT16) + ); + } + } + + // + // Check if current break is caused by breakpoint set. + // If so, we need to patch memory back to let user see the real memory. + // + if (DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].BreakpointAddress != 0) { + CopyMem ( + (VOID *)(UINTN)DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].BreakpointAddress, + &DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].OldInstruction, + sizeof(UINT16) + ); + DebuggerPrivate->StatusFlags &= ~EFI_DEBUG_FLAG_EBC_B_BP; + } + + // + // Done + // + return ; +} + +/** + + Check all the breakpoint, if match, then set status flag, and record current breakpoint. + Then clear all breakpoint to let user see a clean memory + + @param DebuggerPrivate EBC Debugger private data structure + @param SystemContext EBC system context. + +**/ +VOID +EdbCheckBreakpoint ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINT64 Address; + UINTN Index; + BOOLEAN IsHitBreakpoint; + + // + // Roll back IP for breakpoint instruction (BREAK(3) : 0x0300) + // + Address = SystemContext.SystemContextEbc->Ip - sizeof(UINT16); + + // + // Check if the breakpoint is hit + // + IsHitBreakpoint = FALSE; + for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { + if ((DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress == Address) && + (DebuggerPrivate->DebuggerBreakpointContext[Index].State)) { + IsHitBreakpoint = TRUE; + break; + } + } + + if (IsHitBreakpoint) { + // + // If hit, record current breakpoint + // + DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX] = DebuggerPrivate->DebuggerBreakpointContext[Index]; + DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].State = TRUE; + // + // Update: IP and Instruction (NOTE: Since we not allow set breakpoint to BREAK 3, this update is safe) + // + SystemContext.SystemContextEbc->Ip = Address; + // + // Set Flags + // + DebuggerPrivate->StatusFlags |= EFI_DEBUG_FLAG_EBC_BP; + } else { + // + // If not hit, check whether current IP is in breakpoint list, + // because STEP will be triggered before execute the instruction. + // We should not patch it in de-init. + // + Address = SystemContext.SystemContextEbc->Ip; + + // + // Check if the breakpoint is hit + // + IsHitBreakpoint = FALSE; + for (Index = 0; (Index < DebuggerPrivate->DebuggerBreakpointCount) && (Index < EFI_DEBUGGER_BREAKPOINT_MAX); Index++) { + if ((DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress == Address) && + (DebuggerPrivate->DebuggerBreakpointContext[Index].State)) { + IsHitBreakpoint = TRUE; + break; + } + } + + if (IsHitBreakpoint) { + // + // If hit, record current breakpoint + // + DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX] = DebuggerPrivate->DebuggerBreakpointContext[Index]; + DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX].State = TRUE; + // + // Do not set Breakpoint flag. We record the address here just let it not patch breakpoint address when de-init. + // + } else { + // + // Zero current breakpoint + // + ZeroMem ( + &DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX], + sizeof(DebuggerPrivate->DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX]) + ); + } + } + + // + // Done + // + return ; +} + +/** + clear all the symbol. + + @param DebuggerPrivate EBC Debugger private data structure + +**/ +VOID +EdbClearSymbol ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate + ) +{ + EFI_DEBUGGER_SYMBOL_CONTEXT *DebuggerSymbolContext; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + UINTN ObjectIndex; + UINTN Index; + + // + // Go throuth each object + // + DebuggerSymbolContext = &DebuggerPrivate->DebuggerSymbolContext; + for (ObjectIndex = 0; ObjectIndex < DebuggerSymbolContext->ObjectCount; ObjectIndex++) { + Object = &DebuggerSymbolContext->Object[ObjectIndex]; + // + // Go throuth each entry + // + for (Index = 0; Index < Object->EntryCount; Index++) { + ZeroMem (&Object->Entry[Index], sizeof(Object->Entry[Index])); + } + ZeroMem (Object->Name, sizeof(Object->Name)); + Object->EntryCount = 0; + Object->BaseAddress = 0; + Object->StartEntrypointRVA = 0; + Object->MainEntrypointRVA = 0; + // + // Free source buffer + // + for (Index = 0; Object->SourceBuffer[Index] != NULL; Index++) { + gBS->FreePool (Object->SourceBuffer[Index]); + Object->SourceBuffer[Index] = NULL; + } + } + DebuggerSymbolContext->ObjectCount = 0; + + return ; +} + +/** + + Initialize Debugger private data structure + + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + @param Initialized Whether the DebuggerPrivate data is initialized. + +**/ +EFI_STATUS +InitDebuggerPrivateData ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN BOOLEAN Initialized + ) +{ + // + // clear STEP flag in any condition. + // + if (SystemContext.SystemContextEbc->Flags & ((UINT64) VMFLAGS_STEP)) { + SystemContext.SystemContextEbc->Flags &= ~((UINT64) VMFLAGS_STEP); + } + + if (!Initialized) { + // + // Initialize everything + // + DebuggerPrivate->InstructionNumber = EFI_DEBUG_DEFAULT_INSTRUCTION_NUMBER; + + DebuggerPrivate->DebuggerBreakpointCount = 0; + ZeroMem (DebuggerPrivate->DebuggerBreakpointContext, sizeof(DebuggerPrivate->DebuggerBreakpointContext)); + +// DebuggerPrivate->StatusFlags = 0; + + DebuggerPrivate->DebuggerSymbolContext.DisplaySymbol = TRUE; + DebuggerPrivate->DebuggerSymbolContext.DisplayCodeOnly = FALSE; + DebuggerPrivate->DebuggerSymbolContext.ObjectCount = 0; + } else { + // + // Already initialized, just check Breakpoint here. + // + if (ExceptionType == EXCEPT_EBC_BREAKPOINT) { + EdbCheckBreakpoint (DebuggerPrivate, SystemContext); + } + + // + // Clear all breakpoint + // + EdbClearAllBreakpoint (DebuggerPrivate, FALSE); + } + + // + // Set Scope to currentl IP. (Note: Check Breakpoint may change Ip) + // + DebuggerPrivate->InstructionScope = SystemContext.SystemContextEbc->Ip; + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + De-initialize Debugger private data structure. + + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + @param Initialized Whether the DebuggerPrivate data is initialized. + +**/ +EFI_STATUS +DeinitDebuggerPrivateData ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN BOOLEAN Initialized + ) +{ + if (!Initialized) { + // + // If it does not want initialized state, de-init everything + // + DebuggerPrivate->FeatureFlags = EFI_DEBUG_FLAG_EBC_BOE | EFI_DEBUG_FLAG_EBC_BOT; + DebuggerPrivate->CallStackEntryCount = 0; + DebuggerPrivate->TraceEntryCount = 0; + ZeroMem (DebuggerPrivate->CallStackEntry, sizeof(DebuggerPrivate->CallStackEntry)); + ZeroMem (DebuggerPrivate->TraceEntry, sizeof(DebuggerPrivate->TraceEntry)); + + // + // Clear all breakpoint + // + EdbClearAllBreakpoint (DebuggerPrivate, TRUE); + + // + // Clear symbol + // + EdbClearSymbol (DebuggerPrivate); + } else { + // + // If it wants to keep initialized state, just set breakpoint. + // + EdbSetAllBreakpoint (DebuggerPrivate); + } + + // + // Clear Step context + // + ZeroMem (&mDebuggerPrivate.StepContext, sizeof(mDebuggerPrivate.StepContext)); + DebuggerPrivate->StatusFlags = 0; + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Print the reason of current break to EbcDebugger. + + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + @param Initialized Whether the DebuggerPrivate data is initialized. + +**/ +VOID +PrintExceptionReason ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN BOOLEAN Initialized + ) +{ + // + // Print break status + // + if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_GT) == EFI_DEBUG_FLAG_EBC_GT) { + EDBPrint (L"Break on GoTil\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOC) == EFI_DEBUG_FLAG_EBC_BOC) { + EDBPrint (L"Break on CALL\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOCX) == EFI_DEBUG_FLAG_EBC_BOCX) { + EDBPrint (L"Break on CALLEX\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOR) == EFI_DEBUG_FLAG_EBC_BOR) { + EDBPrint (L"Break on RET\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOE) == EFI_DEBUG_FLAG_EBC_BOE) { + EDBPrint (L"Break on Entrypoint\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOT) == EFI_DEBUG_FLAG_EBC_BOT) { + EDBPrint (L"Break on Thunk\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_STEPOVER) == EFI_DEBUG_FLAG_EBC_STEPOVER) { + EDBPrint (L"Break on StepOver\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_STEPOUT) == EFI_DEBUG_FLAG_EBC_STEPOUT) { + EDBPrint (L"Break on StepOut\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BP) == EFI_DEBUG_FLAG_EBC_BP) { + EDBPrint (L"Break on Breakpoint\n"); + } else if ((DebuggerPrivate->StatusFlags & EFI_DEBUG_FLAG_EBC_BOK) == EFI_DEBUG_FLAG_EBC_BOK) { + EDBPrint (L"Break on Key\n"); + } else { + EDBPrint (L"Exception Type - %x", (UINTN)ExceptionType); + if ((ExceptionType >= EXCEPT_EBC_UNDEFINED) && (ExceptionType <= EXCEPT_EBC_STEP)) { + EDBPrint (L" (%s)\n", mExceptionStr[ExceptionType]); + } else { + EDBPrint (L"\n"); + } + } + + return ; +} + +/** + + The default Exception Callback for the VM interpreter. + In this function, we report status code, and print debug information + about EBC_CONTEXT, then dead loop. + + @param ExceptionType Exception type. + @param SystemContext EBC system context. + +**/ +VOID +EFIAPI +EdbExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + CHAR16 InputBuffer[EFI_DEBUG_INPUS_BUFFER_SIZE]; + CHAR16 *CommandArg; + EFI_DEBUGGER_COMMAND DebuggerCommand; + EFI_DEBUG_STATUS DebugStatus; + STATIC BOOLEAN mInitialized; + + mInitialized = FALSE; + + DEBUG ((DEBUG_ERROR, "Hello EBC Debugger!\n")); + + if (!mInitialized) { + // + // Print version + // + EDBPrint ( + L"EBC Interpreter Version - %d.%d\n", + (UINTN)VM_MAJOR_VERSION, + (UINTN)VM_MINOR_VERSION + ); + EDBPrint ( + L"EBC Debugger Version - %d.%d\n", + (UINTN)EBC_DEBUGGER_MAJOR_VERSION, + (UINTN)EBC_DEBUGGER_MINOR_VERSION + ); + } + // + // Init Private Data + // + InitDebuggerPrivateData (&mDebuggerPrivate, ExceptionType, SystemContext, mInitialized); + + // + // EDBPrint basic info + // + PrintExceptionReason (&mDebuggerPrivate, ExceptionType, SystemContext, mInitialized); + + EdbShowDisasm (&mDebuggerPrivate, SystemContext); + // EFI_BREAKPOINT (); + + if (!mInitialized) { + // + // Interactive with user + // + EDBPrint (L"\nPlease enter command now, \'h\' for help.\n"); + EDBPrint (L"(Using -b <...> to enable page break.)\n"); + } + mInitialized = TRUE; + + // + // Dispatch each command + // + while (TRUE) { + // + // Get user input + // + Input (L"\n\r" EFI_DEBUG_PROMPT_STRING, InputBuffer, EFI_DEBUG_INPUS_BUFFER_SIZE); + EDBPrint (L"\n"); + + // + // Get command + // + DebuggerCommand = MatchDebuggerCommand (InputBuffer, &CommandArg); + if (DebuggerCommand == NULL) { + EDBPrint (L"ERROR: Command not found!\n"); + continue; + } + + // + // Check PageBreak; + // + if (CommandArg != NULL) { + if (StriCmp (CommandArg, L"-b") == 0) { + CommandArg = StrGetNextTokenLine (L" "); + mDebuggerPrivate.EnablePageBreak = TRUE; + } + } + + // + // Dispatch command + // + DebugStatus = DebuggerCommand (CommandArg, &mDebuggerPrivate, ExceptionType, SystemContext); + mDebuggerPrivate.EnablePageBreak = FALSE; + + // + // Check command return status + // + if (DebugStatus == EFI_DEBUG_RETURN) { + mInitialized = FALSE; + break; + } else if (DebugStatus == EFI_DEBUG_BREAK) { + break; + } else if (DebugStatus == EFI_DEBUG_CONTINUE) { + continue; + } else { + ASSERT (FALSE); + } + } + + // + // Deinit Private Data + // + DeinitDebuggerPrivateData (&mDebuggerPrivate, ExceptionType, SystemContext, mInitialized); + + DEBUG ((DEBUG_ERROR, "Goodbye EBC Debugger!\n")); + + return; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.h new file mode 100644 index 000000000..34253b3a1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/Edb.h @@ -0,0 +1,60 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#ifndef _EFI_EDB_H_ +#define _EFI_EDB_H_ + +#include "EdbCommon.h" + +#define EBC_DEBUGGER_MAJOR_VERSION 1 +#define EBC_DEBUGGER_MINOR_VERSION 0 + +#define EFI_DEBUG_RETURN 1 +#define EFI_DEBUG_BREAK 2 +#define EFI_DEBUG_CONTINUE 3 + +/** + Driver Entry point. + + @param ImageHandle ImageHandle of the loaded driver. + @param SystemTable Pointer to the EFI System Table. + +**/ +EFI_STATUS +EfiDebuggerEntrypoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + + The default Exception Callback for the VM interpreter. + In this function, we report status code, and print debug information + about EBC_CONTEXT, then dead loop. + + @param ExceptionType Exception type. + @param SystemContext EBC system context. + +**/ +VOID +EFIAPI +EdbExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + +extern EFI_DEBUGGER_PRIVATE_DATA mDebuggerPrivate; + +#include "EdbSupport.h" +#include "EdbCommand.h" +#include "EdbDisasm.h" +#include "EdbDisasmSupport.h" +#include "EdbSymbol.h" +#include "EdbHook.h" + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBranch.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBranch.c new file mode 100644 index 000000000..500bb41da --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBranch.c @@ -0,0 +1,306 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +CHAR16 *mBranchTypeStr[] = { + L"(CALL)", + L"(CALLEX)", + L"(RET)", + L"(JMP)", + L"(JMP8)", +}; + +/** + + Comvert Branch Type to string. + + @param Type Branch Type + + @retval String string of Branch Type. + +**/ +CHAR16 * +EdbBranchTypeToStr ( + IN EFI_DEBUGGER_BRANCH_TYPE Type + ) +{ + if (Type < 0 || Type >= EfiDebuggerBranchTypeEbcMax) { + return L"(Unknown Type)"; + } + + return mBranchTypeStr [Type]; +} + +/** + + DebuggerCommand - CallStack. + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerCallStack ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + INTN Index; + UINTN SubIndex; + CHAR8 *FuncName; + EFI_DEBUGGER_CALLSTACK_CONTEXT *CallStackEntry; + BOOLEAN ShowParameter; + UINTN ParameterNumber; + + ShowParameter = FALSE; + ParameterNumber = EFI_DEBUGGER_CALL_DEFAULT_PARAMETER; + + // + // Check argument + // + if (CommandArg != NULL) { + if (StriCmp (CommandArg, L"c") == 0) { + // + // Clear Call-Stack + // + DebuggerPrivate->CallStackEntryCount = 0; + ZeroMem (DebuggerPrivate->CallStackEntry, sizeof(DebuggerPrivate->CallStackEntry)); + EDBPrint (L"Call-Stack is cleared\n"); + return EFI_DEBUG_CONTINUE; + } else if (StriCmp (CommandArg, L"p") == 0) { + // + // Print Call-Stack with parameter + // + ShowParameter = TRUE; + CommandArg = StrGetNextTokenLine (L" "); + if (CommandArg != NULL) { + // + // Try to get the parameter number + // + ParameterNumber = Atoi (CommandArg); + if (ParameterNumber > 16) { + EDBPrint (L"Call-Stack argument Invalid\n"); + return EFI_DEBUG_CONTINUE; + } + } + } else { + EDBPrint (L"Call-Stack argument Invalid\n"); + return EFI_DEBUG_CONTINUE; + } + } + + // + // Check CallStack Entry Count + // + if (DebuggerPrivate->CallStackEntryCount == 0) { + EDBPrint (L"No Call-Stack\n"); + return EFI_DEBUG_CONTINUE; + } else if (DebuggerPrivate->CallStackEntryCount > EFI_DEBUGGER_CALLSTACK_MAX) { + EDBPrint (L"Call-Stack Crash, re-initialize!\n"); + DebuggerPrivate->CallStackEntryCount = 0; + return EFI_DEBUG_CONTINUE; + } + + // + // Go through each CallStack entry and print + // + EDBPrint (L"Call-Stack (TOP):\n"); + EDBPrint (L" Caller Callee Name\n"); + EDBPrint (L" ================== ================== ========\n"); +//EDBPrint (L" 0x00000000FFFFFFFF 0xFFFFFFFF00000000 EfiMain\n"); + for (Index = (INTN)(DebuggerPrivate->CallStackEntryCount - 1); Index >= 0; Index--) { + // + // Get CallStack and print + // + CallStackEntry = &DebuggerPrivate->CallStackEntry[Index]; + EDBPrint ( + L" 0x%016lx 0x%016lx", + CallStackEntry->SourceAddress, + CallStackEntry->DestAddress + ); + FuncName = FindSymbolStr ((UINTN)CallStackEntry->DestAddress); + if (FuncName != NULL) { + EDBPrint (L" %a()", FuncName); + } + EDBPrint (L"\n"); + + if (ShowParameter) { + // + // Print parameter + // + if (sizeof(UINTN) == sizeof(UINT64)) { + EDBPrint ( + L" Parameter Address (0x%016lx) (\n", + CallStackEntry->ParameterAddr + ); + if (ParameterNumber == 0) { + EDBPrint (L" )\n"); + continue; + } + // + // Print each parameter + // + for (SubIndex = 0; SubIndex < ParameterNumber - 1; SubIndex++) { + if (SubIndex % 2 == 0) { + EDBPrint (L" "); + } + EDBPrint ( + L"0x%016lx, ", + CallStackEntry->Parameter[SubIndex] + ); + if (SubIndex % 2 == 1) { + EDBPrint (L"\n"); + } + } + if (SubIndex % 2 == 0) { + EDBPrint (L" "); + } + EDBPrint ( + L"0x%016lx\n", + CallStackEntry->Parameter[SubIndex] + ); + EDBPrint (L" )\n"); + // + // break only for parameter + // + if ((((DebuggerPrivate->CallStackEntryCount - Index) % (16 / ParameterNumber)) == 0) && + (Index != 0)) { + if (SetPageBreak ()) { + break; + } + } + } else { + EDBPrint ( + L" Parameter Address (0x%08x) (\n", + CallStackEntry->ParameterAddr + ); + if (ParameterNumber == 0) { + EDBPrint (L" )\n"); + continue; + } + // + // Print each parameter + // + for (SubIndex = 0; SubIndex < ParameterNumber - 1; SubIndex++) { + if (SubIndex % 4 == 0) { + EDBPrint (L" "); + } + EDBPrint ( + L"0x%08x, ", + CallStackEntry->Parameter[SubIndex] + ); + if (SubIndex % 4 == 3) { + EDBPrint (L"\n"); + } + } + if (SubIndex % 4 == 0) { + EDBPrint (L" "); + } + EDBPrint ( + L"0x%08x\n", + CallStackEntry->Parameter[SubIndex] + ); + EDBPrint (L" )\n"); + // + // break only for parameter + // + if ((((DebuggerPrivate->CallStackEntryCount - Index) % (32 / ParameterNumber)) == 0) && + (Index != 0)) { + if (SetPageBreak ()) { + break; + } + } + } + } + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - InstructionBranch. + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerInstructionBranch ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Index; + + // + // Check argument + // + if (CommandArg != NULL) { + if (StriCmp (CommandArg, L"c") == 0) { + // + // Clear Trace + // + DebuggerPrivate->TraceEntryCount = 0; + ZeroMem (DebuggerPrivate->TraceEntry, sizeof(DebuggerPrivate->TraceEntry)); + EDBPrint (L"Instruction Trace is cleared\n"); + } else { + EDBPrint (L"Trace argument Invalid\n"); + } + return EFI_DEBUG_CONTINUE; + } + + // + // Check Trace Entry Count + // + if (DebuggerPrivate->TraceEntryCount == 0) { + EDBPrint (L"No Instruction Trace\n"); + return EFI_DEBUG_CONTINUE; + } else if (DebuggerPrivate->TraceEntryCount > EFI_DEBUGGER_TRACE_MAX) { + EDBPrint (L"Instruction Trace Crash, re-initialize!\n"); + DebuggerPrivate->TraceEntryCount = 0; + return EFI_DEBUG_CONTINUE; + } + + // + // Go through each Trace entry and print + // + EDBPrint (L"Instruction Trace (->Latest):\n"); + EDBPrint (L" Source Addr Destination Addr Type\n"); + EDBPrint (L" ================== ================== ========\n"); +//EDBPrint (L" 0x00000000FFFFFFFF 0xFFFFFFFF00000000 (CALLEX)\n"); + for (Index = 0; Index < DebuggerPrivate->TraceEntryCount; Index++) { + EDBPrint ( + L" 0x%016lx 0x%016lx %s\n", + DebuggerPrivate->TraceEntry[Index].SourceAddress, + DebuggerPrivate->TraceEntry[Index].DestAddress, + EdbBranchTypeToStr (DebuggerPrivate->TraceEntry[Index].Type) + ); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreak.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreak.c new file mode 100644 index 000000000..696703c4b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreak.c @@ -0,0 +1,288 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + + +/** + + DebuggerCommand - BreakOnCALL. + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakOnCALL ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Check argument + // + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOC) == EFI_DEBUG_FLAG_EBC_BOC) { + EDBPrint (L"BOC on\n"); + } else { + EDBPrint (L"BOC off\n"); + } + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOC; + EDBPrint (L"BOC on\n"); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOC; + EDBPrint (L"BOC off\n"); + } else { + EDBPrint (L"BOC - argument error\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand BreakOnCALLEX. + + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exceptiont type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakOnCALLEX ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Check argument + // + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOCX) == EFI_DEBUG_FLAG_EBC_BOCX) { + EDBPrint (L"BOCX on\n"); + } else { + EDBPrint (L"BOCX off\n"); + } + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOCX; + EDBPrint (L"BOCX on\n"); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOCX; + EDBPrint (L"BOCX off\n"); + } else { + EDBPrint (L"BOCX - argument error\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - BreakOnRET. + + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakOnRET ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Check argument + // + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOR) == EFI_DEBUG_FLAG_EBC_BOR) { + EDBPrint (L"BOR on\n"); + } else { + EDBPrint (L"BOR off\n"); + } + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOR; + EDBPrint (L"BOR on\n"); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOR; + EDBPrint (L"BOR off\n"); + } else { + EDBPrint (L"BOR - argument error\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - BreakOnEntrypoint. + + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakOnEntrypoint ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Check argument + // + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOE) == EFI_DEBUG_FLAG_EBC_BOE) { + EDBPrint (L"BOE on\n"); + } else { + EDBPrint (L"BOE off\n"); + } + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOE; + EDBPrint (L"BOE on\n"); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOE; + EDBPrint (L"BOE off\n"); + } else { + EDBPrint (L"BOE - argument error\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + + DebuggerCommand - BreakOnThunk. + + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakOnThunk ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Check argument + // + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOT) == EFI_DEBUG_FLAG_EBC_BOT) { + EDBPrint (L"BOT on\n"); + } else { + EDBPrint (L"BOT off\n"); + } + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOT; + EDBPrint (L"BOT on\n"); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOT; + EDBPrint (L"BOT off\n"); + } else { + EDBPrint (L"BOT - argument error\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - BreakOnKey. + + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakOnKey ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Check argument + // + if (CommandArg == NULL) { + if ((DebuggerPrivate->FeatureFlags & EFI_DEBUG_FLAG_EBC_BOK) == EFI_DEBUG_FLAG_EBC_BOK) { + EDBPrint (L"BOK on\n"); + } else { + EDBPrint (L"BOK off\n"); + } + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_BOK; + EDBPrint (L"BOK on\n"); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOK; + EDBPrint (L"BOK off\n"); + } else { + EDBPrint (L"BOK - argument error\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreakpoint.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreakpoint.c new file mode 100644 index 000000000..e0c797be2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdBreakpoint.c @@ -0,0 +1,542 @@ +/** @file + +Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + Check whether current IP is EBC BREAK3 instruction. + + @param Address EBC IP address. + + @retval TRUE Current IP is EBC BREAK3 instruction + @retval FALSE Current IP is not EBC BREAK3 instruction + +**/ +BOOLEAN +IsEBCBREAK3 ( + IN UINTN Address + ) +{ + if (GET_OPCODE(Address) != OPCODE_BREAK) { + return FALSE; + } + + if (GET_OPERANDS (Address) != 3) { + return FALSE; + } else { + return TRUE; + } +} + +/** + + Check whether the Address is already set in breakpoint. + + @param DebuggerPrivate EBC Debugger private data structure + @param Address Breakpoint Address + + @retval TRUE breakpoint is found + @retval FALSE breakpoint is not found + +**/ +BOOLEAN +DebuggerBreakpointIsDuplicated ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN UINTN Address + ) +{ + UINTN Index; + + // + // Go through each breakpoint context + // + for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) { + if (DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress == Address) { + // + // Found it + // + return TRUE; + } + } + + // + // Not found + // + return FALSE; +} + +/** + + Add this breakpoint. + + @param DebuggerPrivate EBC Debugger private data structure + @param Address Breakpoint Address + + @retval EFI_SUCCESS breakpoint added successfully + @retval EFI_ALREADY_STARTED breakpoint is already added + @retval EFI_OUT_OF_RESOURCES all the breakpoint entries are used + +**/ +EFI_STATUS +DebuggerBreakpointAdd ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN UINTN Address + ) +{ + // + // Check duplicated breakpoint + // + if (DebuggerBreakpointIsDuplicated (DebuggerPrivate, Address)) { + EDBPrint (L"Breakpoint duplicated!\n"); + return EFI_ALREADY_STARTED; + } + + // + // Check whether the address is a breakpoint 3 instruction + // + if (IsEBCBREAK3 (Address)) { + EDBPrint (L"Breakpoint can not be set on BREAK 3 instruction!\n"); + return EFI_ALREADY_STARTED; + } + + if (DebuggerPrivate->DebuggerBreakpointCount >= EFI_DEBUGGER_BREAKPOINT_MAX) { + EDBPrint (L"Breakpoint out of resource!\n"); + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the breakpoint + // + DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].BreakpointAddress = Address; + DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].State = TRUE; + DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].OldInstruction = 0; + CopyMem ( + &DebuggerPrivate->DebuggerBreakpointContext[DebuggerPrivate->DebuggerBreakpointCount].OldInstruction, + (VOID *)Address, + sizeof(UINT16) + ); + + DebuggerPrivate->DebuggerBreakpointCount ++; + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Delete this breakpoint. + + @param DebuggerPrivate EBC Debugger private data structure + @param Index Breakpoint Index + + @retval EFI_SUCCESS breakpoint deleted successfully + @retval EFI_NOT_FOUND breakpoint not found + +**/ +EFI_STATUS +DebuggerBreakpointDel ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN UINTN Index + ) +{ + UINTN BpIndex; + + if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) || + (Index >= DebuggerPrivate->DebuggerBreakpointCount)) { + return EFI_NOT_FOUND; + } + + // + // Delete this breakpoint + // + for (BpIndex = Index; BpIndex < DebuggerPrivate->DebuggerBreakpointCount - 1; BpIndex++) { + DebuggerPrivate->DebuggerBreakpointContext[BpIndex] = DebuggerPrivate->DebuggerBreakpointContext[BpIndex + 1]; + } + ZeroMem ( + &DebuggerPrivate->DebuggerBreakpointContext[BpIndex], + sizeof(DebuggerPrivate->DebuggerBreakpointContext[BpIndex]) + ); + + DebuggerPrivate->DebuggerBreakpointCount --; + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Disable this breakpoint. + + @param DebuggerPrivate EBC Debugger private data structure + @param Index Breakpoint Index + + @retval EFI_SUCCESS breakpoint disabled successfully + @retval EFI_NOT_FOUND breakpoint not found + +**/ +EFI_STATUS +DebuggerBreakpointDis ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN UINTN Index + ) +{ + if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) || + (Index >= DebuggerPrivate->DebuggerBreakpointCount)) { + return EFI_NOT_FOUND; + } + + // + // Disable this breakpoint + // + DebuggerPrivate->DebuggerBreakpointContext[Index].State = FALSE; + + return EFI_SUCCESS; +} + +/** + + Enable this breakpoint. + + @param DebuggerPrivate - EBC Debugger private data structure + @param Index - Breakpoint Index + + @retval EFI_SUCCESS - breakpoint enabled successfully + @retval EFI_NOT_FOUND - breakpoint not found + +**/ +EFI_STATUS +DebuggerBreakpointEn ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN UINTN Index + ) +{ + if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) || + (Index >= DebuggerPrivate->DebuggerBreakpointCount)) { + return EFI_NOT_FOUND; + } + + // + // Enable this breakpoint + // + DebuggerPrivate->DebuggerBreakpointContext[Index].State = TRUE; + + return EFI_SUCCESS; +} + +/** + + DebuggerCommand - BreakpointList. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakpointList ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Index; + + // + // Check breakpoint cound + // + if (DebuggerPrivate->DebuggerBreakpointCount == 0) { + EDBPrint (L"No Breakpoint\n"); + return EFI_DEBUG_CONTINUE; + } else if (DebuggerPrivate->DebuggerBreakpointCount > EFI_DEBUGGER_BREAKPOINT_MAX) { + EDBPrint (L"Breakpoint too many!\n"); + DebuggerPrivate->DebuggerBreakpointCount = 0; + return EFI_DEBUG_CONTINUE; + } + + // + // Go through each breakpoint + // + EDBPrint (L"Breakpoint :\n"); + EDBPrint (L" Index Address Status\n"); + EDBPrint (L"======= ================== ========\n"); +//EDBPrint (L" 1 0xFFFFFFFF00000000 *\n"); +//EDBPrint (L" 12 0x00000000FFFFFFFF\n"); + for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) { + // + // Print the breakpoint + // + EDBPrint (L" %2d 0x%016lx", Index, DebuggerPrivate->DebuggerBreakpointContext[Index].BreakpointAddress); + if (DebuggerPrivate->DebuggerBreakpointContext[Index].State) { + EDBPrint (L" *\n"); + } else { + EDBPrint (L"\n"); + } + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - BreakpointSet. + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakpointSet ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Address; + EFI_STATUS Status; + + if (CommandArg == NULL) { + EDBPrint (L"BreakpointSet Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Get breakpoint address + // + Status = Symboltoi (CommandArg, &Address); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + Address = Xtoi(CommandArg); + } else { + // + // Something wrong, let Symboltoi print error info. + // + EDBPrint (L"Command Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + } + + // + // Add breakpoint + // + Status = DebuggerBreakpointAdd (DebuggerPrivate, Address); + if (EFI_ERROR(Status)) { + EDBPrint (L"BreakpointSet error!\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - BreakpointClear + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakpointClear ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Index; + EFI_STATUS Status; + + if (CommandArg == NULL) { + EDBPrint (L"BreakpointClear Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + if (StriCmp (CommandArg, L"*") == 0) { + // + // delete all breakpoint + // + DebuggerPrivate->DebuggerBreakpointCount = 0; + ZeroMem (DebuggerPrivate->DebuggerBreakpointContext, sizeof(DebuggerPrivate->DebuggerBreakpointContext)); + EDBPrint (L"All the Breakpoint is cleared\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Get breakpoint index + // + Index = Atoi(CommandArg); + if (Index == (UINTN) -1) { + EDBPrint (L"BreakpointClear Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + if ((Index >= EFI_DEBUGGER_BREAKPOINT_MAX) || + (Index >= DebuggerPrivate->DebuggerBreakpointCount)) { + EDBPrint (L"BreakpointClear error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Delete breakpoint + // + Status = DebuggerBreakpointDel (DebuggerPrivate, Index); + if (EFI_ERROR(Status)) { + EDBPrint (L"BreakpointClear error!\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - BreakpointDisable + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakpointDisable ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Index; + EFI_STATUS Status; + + if (CommandArg == NULL) { + EDBPrint (L"BreakpointDisable Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + if (StriCmp (CommandArg, L"*") == 0) { + // + // disable all breakpoint + // + for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) { + Status = DebuggerBreakpointDis (DebuggerPrivate, Index); + } + EDBPrint (L"All the Breakpoint is disabled\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Get breakpoint index + // + Index = Atoi(CommandArg); + if (Index == (UINTN) -1) { + EDBPrint (L"BreakpointDisable Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Disable breakpoint + // + Status = DebuggerBreakpointDis (DebuggerPrivate, Index); + if (EFI_ERROR(Status)) { + EDBPrint (L"BreakpointDisable error!\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + DebuggerCommand - BreakpointEnable. + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerBreakpointEnable ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Index; + EFI_STATUS Status; + + if (CommandArg == NULL) { + EDBPrint (L"BreakpointEnable Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + if (StriCmp (CommandArg, L"*") == 0) { + // + // enable all breakpoint + // + for (Index = 0; Index < DebuggerPrivate->DebuggerBreakpointCount; Index++) { + Status = DebuggerBreakpointEn (DebuggerPrivate, Index); + } + EDBPrint (L"All the Breakpoint is enabled\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Get breakpoint index + // + Index = Atoi(CommandArg); + if (Index == (UINTN) -1) { + EDBPrint (L"BreakpointEnable Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Enable breakpoint + // + Status = DebuggerBreakpointEn (DebuggerPrivate, Index); + if (EFI_ERROR(Status)) { + EDBPrint (L"BreakpointEnable error!\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtIo.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtIo.c new file mode 100644 index 000000000..d5390d13a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtIo.c @@ -0,0 +1,176 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + DebuggerCommand - IB. + + @param CommandArg The argument for this command + @param DebuggerPrivate EBC Debugger private data structure + @param ExceptionType Exception type. + @param SystemContext EBC system context. + + @retval EFI_DEBUG_CONTINUE formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtIoIB ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + + +/** + + DebuggerCommand - IW. + + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtIoIW ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - ID. + + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtIoID ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - OB. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtIoOB ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + + +/** + + DebuggerCommand - OW. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtIoOW ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + + +/** + + DebuggerCommand - OD. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtIoOD ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtPci.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtPci.c new file mode 100644 index 000000000..93e8b503e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdExtPci.c @@ -0,0 +1,145 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + DebuggerCommand - PCIL. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtPciPCIL ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - PCID. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtPciPCID ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - CFGB. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtPciCFGB ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + + +/** + + DebuggerCommand - CFGW. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtPciCFGW ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - CFGD. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerExtPciCFGD ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EDBPrint (L"Unsupported\n"); + // + // TBD + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdGo.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdGo.c new file mode 100644 index 000000000..e5cf857a2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdGo.c @@ -0,0 +1,76 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + + +**/ + +#include "Edb.h" + +/** + + DebuggerCommand - Go. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_BREAK - formal return value + @retval EFI_DEBUG_CONTINUE - something wrong + +**/ +EFI_DEBUG_STATUS +DebuggerGo ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Address; + CHAR16 *CommandStr; + EFI_STATUS Status; + + // + // Check argument + // + if (CommandArg != NULL) { + if (StriCmp (CommandArg, L"til") == 0) { + CommandStr = StrGetNextTokenLine (L" "); + if (CommandStr != NULL) { + // + // Enable GoTil break now + // set BreakAddress, and set feature flag. + // + Status = Symboltoi (CommandStr, &Address); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + Address = Xtoi(CommandStr); + } else { + // + // Something wrong, let Symboltoi print error info. + // + EDBPrint (L"Command Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + } + DebuggerPrivate->GoTilContext.BreakAddress = Address; + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_GT; + } else { + EDBPrint (L"Command Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + } else { + EDBPrint (L"Command Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + } + + // + // Done + // + return EFI_DEBUG_BREAK; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdHelp.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdHelp.c new file mode 100644 index 000000000..74e1befee --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdHelp.c @@ -0,0 +1,68 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + DebuggerCommand - Help. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerHelp ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN Index; + + // + // if no argument, print all the command title + // + if (CommandArg == NULL) { + for (Index = 0; DebuggerPrivate->DebuggerCommandSet[Index].CommandName != NULL; Index++) { + EDBPrint (DebuggerPrivate->DebuggerCommandSet[Index].ClassName); + if (StrCmp (DebuggerPrivate->DebuggerCommandSet[Index].CommandTitle, L"") != 0) { + EDBPrint (L" "); + EDBPrint (DebuggerPrivate->DebuggerCommandSet[Index].CommandTitle); + } + } + return EFI_DEBUG_CONTINUE; + } + + // + // If there is argument, the argument should be command name. + // Find the command and print the detail information. + // + for (Index = 0; DebuggerPrivate->DebuggerCommandSet[Index].CommandName != NULL; Index++) { + if (StriCmp (CommandArg, DebuggerPrivate->DebuggerCommandSet[Index].CommandName) == 0) { + EDBPrint (DebuggerPrivate->DebuggerCommandSet[Index].CommandHelp); + EDBPrint (DebuggerPrivate->DebuggerCommandSet[Index].CommandSyntax); + return EFI_DEBUG_CONTINUE; + } + } + + // + // Command not found. + // + EDBPrint (L"No help info for this command\n"); + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdMemory.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdMemory.c new file mode 100644 index 000000000..42bd8093f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdMemory.c @@ -0,0 +1,578 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + + +/** + + Display memory unit. + + @param Address - Memory Address + @param Width - Memory Width + + @return Length of the memory unit + +**/ +UINTN +EdbDisplayMemoryUnit ( + IN UINTN Address, + IN EDB_DATA_WIDTH Width + ) +{ + UINT8 Data8; + UINT16 Data16; + UINT32 Data32; + UINT64 Data64; + + // + // Print according to width + // + switch (Width) { + case EdbWidthUint8: + CopyMem (&Data8, (VOID *)Address, sizeof(UINT8)); + EDBPrint (L"%02x ", Data8); + return sizeof(UINT8); + case EdbWidthUint16: + CopyMem (&Data16, (VOID *)Address, sizeof(UINT16)); + EDBPrint (L"%04x ", Data16); + return sizeof(UINT16); + case EdbWidthUint32: + CopyMem (&Data32, (VOID *)Address, sizeof(UINT32)); + EDBPrint (L"%08x ", Data32); + return sizeof(UINT32); + case EdbWidthUint64: + CopyMem (&Data64, (VOID *)Address, sizeof(UINT64)); + EDBPrint (L"%016lx ", Data64); + return sizeof(UINT64); + default: + ASSERT (FALSE); + break; + } + + // + // something wrong + // + return 0; +} + +/** + + Display memory. + + @param Address - Memory Address + @param Count - Memory Count + @param Width - Memory Width + +**/ +VOID +EdbDisplayMemory ( + IN UINTN Address, + IN UINTN Count, + IN EDB_DATA_WIDTH Width + ) +{ + UINTN LineNumber; + UINTN ByteNumber; + UINTN LineIndex; + UINTN ByteIndex; + UINTN NumberInLine; + + if (Count == 0) { + return ; + } + + // + // Get line number and byte number + // + switch (Width) { + case EdbWidthUint8: + NumberInLine = 16; + break; + case EdbWidthUint16: + NumberInLine = 8; + break; + case EdbWidthUint32: + NumberInLine = 4; + break; + case EdbWidthUint64: + NumberInLine = 2; + break; + default: + return; + } + + LineNumber = Count / NumberInLine; + ByteNumber = Count % NumberInLine; + if (ByteNumber == 0) { + LineNumber -= 1; + ByteNumber = NumberInLine; + } + + // + // Print each line + // + for (LineIndex = 0; LineIndex < LineNumber; LineIndex++) { + + // + // Break check + // + if (((LineIndex % EFI_DEBUGGER_LINE_NUMBER_IN_PAGE) == 0) && + (LineIndex != 0)) { + if (SetPageBreak ()) { + break; + } + } + + EDBPrint (EDB_PRINT_ADDRESS_FORMAT, (UINTN)Address); + for (ByteIndex = 0; ByteIndex < NumberInLine; ByteIndex++) { + Address += EdbDisplayMemoryUnit (Address, Width); + } + EDBPrint (L"\n"); + } + + // + // Break check + // + if (((LineIndex % EFI_DEBUGGER_LINE_NUMBER_IN_PAGE) == 0) && + (LineIndex != 0)) { + if (SetPageBreak ()) { + return; + } + } + + // + // Print last line + // + EDBPrint (EDB_PRINT_ADDRESS_FORMAT, (UINTN)Address); + for (ByteIndex = 0; ByteIndex < ByteNumber; ByteIndex++) { + Address += EdbDisplayMemoryUnit (Address, Width); + } + + return ; +} + +/** + + Entry memory. + + @param Address - Memory Address + @param Value - Memory Value + @param Width - Memory Width + +**/ +VOID +EdbEnterMemory ( + IN UINTN Address, + IN VOID *Value, + IN EDB_DATA_WIDTH Width + ) +{ + switch (Width) { + case EdbWidthUint8: + CopyMem ((VOID *)Address, Value, sizeof(UINT8)); + break; + case EdbWidthUint16: + CopyMem ((VOID *)Address, Value, sizeof(UINT16)); + break; + case EdbWidthUint32: + CopyMem ((VOID *)Address, Value, sizeof(UINT32)); + break; + case EdbWidthUint64: + CopyMem ((VOID *)Address, Value, sizeof(UINT64)); + break; + default: + break; + } + + return ; +} + +/** + + Get memory address and count. + + @param CommandArg - The argument for this command + @param Address - Memory Address + @param Count - Memory Count + + @retval EFI_SUCCESS - memory address and count are got + @retval EFI_INVALID_PARAMETER - something wrong + +**/ +EFI_STATUS +EdbGetMemoryAddressCount ( + IN CHAR16 *CommandArg, + IN UINTN *Address, + IN UINTN *Count + ) +{ + CHAR16 *CommandStr; + UINTN MemAddress; + EFI_STATUS Status; + + // + // Get Address + // + CommandStr = CommandArg; + if (CommandStr == NULL) { + EDBPrint (L"Memory: Address error!\n"); + return EFI_INVALID_PARAMETER; + } + Status = Symboltoi (CommandStr, &MemAddress); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + MemAddress = Xtoi(CommandStr); + } else { + // + // Something wrong, let Symboltoi print error info. + // + EDBPrint (L"Command Argument error!\n"); + return EFI_INVALID_PARAMETER; + } + } + *Address = MemAddress; + + // + // Get Count + // + CommandStr = StrGetNextTokenLine (L" "); + if (CommandStr == NULL) { + *Count = 1; + } else { + *Count = Xtoi(CommandStr); + } + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Get memory address and value. + + @param CommandArg - The argument for this command + @param Address - Memory Address + @param Value - Memory Value + + @retval EFI_SUCCESS - memory address and value are got + @retval EFI_INVALID_PARAMETER - something wrong + +**/ +EFI_STATUS +EdbGetMemoryAddressValue ( + IN CHAR16 *CommandArg, + IN UINTN *Address, + IN UINT64 *Value + ) +{ + CHAR16 *CommandStr; + UINTN MemAddress; + EFI_STATUS Status; + + // + // Get Address + // + CommandStr = CommandArg; + if (CommandStr == NULL) { + EDBPrint (L"Memory: Address error!\n"); + return EFI_INVALID_PARAMETER; + } + Status = Symboltoi (CommandStr, &MemAddress); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + MemAddress = Xtoi(CommandStr); + } else { + // + // Something wrong, let Symboltoi print error info. + // + EDBPrint (L"Command Argument error!\n"); + return EFI_INVALID_PARAMETER; + } + } + *Address = MemAddress; + + // + // Get Value + // + CommandStr = StrGetNextTokenLine (L" "); + if (CommandStr == NULL) { + EDBPrint (L"Memory: Value error!\n"); + return EFI_INVALID_PARAMETER; + } + *Value = LXtoi(CommandStr); + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Display memory. + + @param CommandArg - The argument for this command + @param Width - Memory Width + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryDisplay ( + IN CHAR16 *CommandArg, + IN EDB_DATA_WIDTH Width + ) +{ + EFI_STATUS Status; + UINTN Address; + UINTN Count; + + // + // Get memory address and count + // + Status = EdbGetMemoryAddressCount (CommandArg, &Address, &Count); + if (EFI_ERROR(Status)) { + return EFI_DEBUG_CONTINUE; + } + + // + // Display memory + // + EdbDisplayMemory (Address, Count, Width); + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + Enter memory. + + @param CommandArg - The argument for this command + @param Width - Memory Width + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryEnter ( + IN CHAR16 *CommandArg, + IN EDB_DATA_WIDTH Width + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT64 Value; + + // + // Get memory address and value + // + Status = EdbGetMemoryAddressValue (CommandArg, &Address, &Value); + if (EFI_ERROR(Status)) { + return EFI_DEBUG_CONTINUE; + } + + // + // Enter memory + // + EdbEnterMemory (Address, &Value, Width); + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - DB. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryDB ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryDisplay (CommandArg, EdbWidthUint8); +} + +/** + + DebuggerCommand - DW. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryDW ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryDisplay (CommandArg, EdbWidthUint16); +} + +/** + + DebuggerCommand - DD. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryDD ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryDisplay (CommandArg, EdbWidthUint32); +} + +/** + + DebuggerCommand - DQ. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryDQ ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryDisplay (CommandArg, EdbWidthUint64); +} + +/** + + DebuggerCommand - EB. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryEB ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryEnter (CommandArg, EdbWidthUint8); +} + +/** + + DebuggerCommand - EW. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Interrupt type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryEW ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryEnter (CommandArg, EdbWidthUint16); +} + +/** + + DebuggerCommand - ED. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryED ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryEnter (CommandArg, EdbWidthUint32); +} + +/** + + DebuggerCommand - EQ. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerMemoryEQ ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return DebuggerMemoryEnter (CommandArg, EdbWidthUint64); +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdQuit.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdQuit.c new file mode 100644 index 000000000..76daf8540 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdQuit.c @@ -0,0 +1,38 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + EdbCmdQuit.c + +Abstract: + + +**/ + +#include "Edb.h" + +/** + + DebuggerCommand - Quit + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_RETURN - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerQuit ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + return EFI_DEBUG_RETURN; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdRegister.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdRegister.c new file mode 100644 index 000000000..2ced0e4dd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdRegister.c @@ -0,0 +1,118 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + DebuggerCommand - Register. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerRegister ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + CHAR16 *RegName; + CHAR16 *RegValStr; + UINT64 RegVal; + + // + // Check Argument, NULL means print all register + // + if (CommandArg == 0) { + EDBPrint ( + L" R0 - 0x%016lx, R1 - 0x%016lx\n", + SystemContext.SystemContextEbc->R0, + SystemContext.SystemContextEbc->R1 + ); + EDBPrint ( + L" R2 - 0x%016lx, R3 - 0x%016lx\n", + SystemContext.SystemContextEbc->R2, + SystemContext.SystemContextEbc->R3 + ); + EDBPrint ( + L" R4 - 0x%016lx, R5 - 0x%016lx\n", + SystemContext.SystemContextEbc->R4, + SystemContext.SystemContextEbc->R5 + ); + EDBPrint ( + L" R6 - 0x%016lx, R7 - 0x%016lx\n", + SystemContext.SystemContextEbc->R6, + SystemContext.SystemContextEbc->R7 + ); + EDBPrint ( + L" Flags - 0x%016lx, ControlFlags - 0x%016lx\n", + SystemContext.SystemContextEbc->Flags, + SystemContext.SystemContextEbc->ControlFlags + ); + EDBPrint ( + L" Ip - 0x%016lx\n", + SystemContext.SystemContextEbc->Ip + ); + return EFI_DEBUG_CONTINUE; + } + + // + // Get register name + // + RegName = CommandArg; + // + // Get register value + // + RegValStr = StrGetNextTokenLine (L" "); + if (RegValStr == NULL) { + EDBPrint (L"Invalid Register Value\n"); + return EFI_DEBUG_CONTINUE; + } + RegVal = LXtoi (RegValStr); + + // + // Assign register value + // + if (StriCmp (RegName, L"R0") == 0) { + SystemContext.SystemContextEbc->R0 = RegVal; + } else if (StriCmp (RegName, L"R1") == 0) { + SystemContext.SystemContextEbc->R1 = RegVal; + } else if (StriCmp (RegName, L"R2") == 0) { + SystemContext.SystemContextEbc->R2 = RegVal; + } else if (StriCmp (RegName, L"R3") == 0) { + SystemContext.SystemContextEbc->R3 = RegVal; + } else if (StriCmp (RegName, L"R4") == 0) { + SystemContext.SystemContextEbc->R4 = RegVal; + } else if (StriCmp (RegName, L"R5") == 0) { + SystemContext.SystemContextEbc->R5 = RegVal; + } else if (StriCmp (RegName, L"R6") == 0) { + SystemContext.SystemContextEbc->R6 = RegVal; + } else if (StriCmp (RegName, L"R7") == 0) { + SystemContext.SystemContextEbc->R7 = RegVal; + } else if (StriCmp (RegName, L"Flags") == 0) { + SystemContext.SystemContextEbc->Flags = RegVal; + } else if (StriCmp (RegName, L"ControlFlags") == 0) { + SystemContext.SystemContextEbc->ControlFlags = RegVal; + } else if (StriCmp (RegName, L"Ip") == 0) { + SystemContext.SystemContextEbc->Ip = RegVal; + } else { + EDBPrint (L"Invalid Register - %s\n", RegName); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdScope.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdScope.c new file mode 100644 index 000000000..8240ab624 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdScope.c @@ -0,0 +1,99 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + DebuggerCommand - Scope. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerScope ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EFI_STATUS Status; + UINTN Address; + + if (CommandArg == NULL) { + EDBPrint (L"Scope: invalid Address\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Load new scope + // + Status = Symboltoi (CommandArg, &Address); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + Address = Xtoi(CommandArg); + } else { + // + // Something wrong, let Symboltoi print error info. + // + EDBPrint (L"Command Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + } + DebuggerPrivate->InstructionScope = Address; + EDBPrint (L"Scope: 0x%x\n", DebuggerPrivate->InstructionScope); + EdbShowDisasm (DebuggerPrivate, SystemContext); + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - List. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerList ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + if (CommandArg == NULL) { + EdbShowDisasm (DebuggerPrivate, SystemContext); + } else { + // + // Load new list number + // + DebuggerPrivate->InstructionNumber = Atoi(CommandArg); + EDBPrint (L"List Number: %d\n", DebuggerPrivate->InstructionNumber); + EdbShowDisasm (DebuggerPrivate, SystemContext); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdStep.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdStep.c new file mode 100644 index 000000000..441f53676 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdStep.c @@ -0,0 +1,156 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + Check whether current IP is EBC CALL instruction (NOTE: CALLEX is exclusive) + + @param Address - EBC IP address. + + @retval TRUE - Current IP is EBC CALL instruction + @retval FALSE - Current IP is not EBC CALL instruction + +**/ +BOOLEAN +IsEBCCALL ( + IN UINTN Address + ) +{ + if (GET_OPCODE(Address) != OPCODE_CALL) { + return FALSE; + } + + if (GET_OPERANDS (Address) & OPERAND_M_NATIVE_CALL) { + return FALSE; + } else { + return TRUE; + } +} + +/** + + Check whether current IP is EBC RET instruction. + + @param Address - EBC IP address. + + @retval TRUE - Current IP is EBC RET instruction + @retval FALSE - Current IP is not EBC RET instruction + +**/ +BOOLEAN +IsEBCRET ( + IN UINTN Address + ) +{ + if (GET_OPCODE(Address) != OPCODE_RET) { + return FALSE; + } + + if (GET_OPERANDS (Address) != 0) { + return FALSE; + } else { + return TRUE; + } +} + +/** + + DebuggerCommand - StepInto. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerStepInto ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + SystemContext.SystemContextEbc->Flags |= VMFLAGS_STEP; + + return EFI_DEBUG_BREAK; +} + +/** + + DebuggerCommand - StepOver. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerStepOver ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + if (IsEBCCALL((UINTN)SystemContext.SystemContextEbc->Ip)) { + // + // Check CALL (NOTE: CALLEX is exclusive) + // + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_STEPOVER; + } else { + // + // Other instruction including CALLEX + // + SystemContext.SystemContextEbc->Flags |= VMFLAGS_STEP; + } + + return EFI_DEBUG_BREAK; +} + +/** + + DebuggerCommand - StepOut. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerStepOut ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + if (IsEBCRET((UINTN)SystemContext.SystemContextEbc->Ip)) { + // + // Check RET + // + SystemContext.SystemContextEbc->Flags |= VMFLAGS_STEP; + } else { + // + // Other instruction + // + DebuggerPrivate->FeatureFlags |= EFI_DEBUG_FLAG_EBC_STEPOUT; + } + + return EFI_DEBUG_BREAK; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdSymbol.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdSymbol.c new file mode 100644 index 000000000..7b453fa98 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCmdSymbol.c @@ -0,0 +1,862 @@ +/** @file + +Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + Get file name from full path. + + @param FullPath - full file path + + @return file name + +**/ +CHAR16 * +GetFileNameFromFullPath ( + IN CHAR16 *FullPath + ) +{ + CHAR16 *FileName; + CHAR16 *TempFileName; + + FileName = FullPath; + TempFileName = StrGetNewTokenLine (FullPath, L"\\"); + + while (TempFileName != NULL) { + FileName = TempFileName; + TempFileName = StrGetNextTokenLine (L"\\"); + PatchForStrTokenBefore (TempFileName, L'\\'); + } + + return FileName; +} + +/** + + Get dir name from full path. + + @param FullPath - full file path + + @return dir name + +**/ +CHAR16 * +GetDirNameFromFullPath ( + IN CHAR16 *FullPath + ) +{ + CHAR16 *FileName; + + FileName = GetFileNameFromFullPath (FullPath); + if (FileName != FullPath) { + *(FileName - 1) = 0; + return FullPath; + } + + return L""; +} + +/** + + Construct full path according to dir and file path. + + @param DirPath - dir path + @param FilePath - file path + @param Size - dir max size + + @return Full file name + +**/ +CHAR16 * +ConstructFullPath ( + IN CHAR16 *DirPath, + IN CHAR16 *FilePath, + IN UINTN Size + ) +{ + UINTN DirPathSize; + + DirPathSize = StrLen(DirPath); + *(DirPath + DirPathSize) = L'\\'; + StrnCatS (DirPath, DirPathSize + Size + 1, FilePath, Size); + + *(DirPath + DirPathSize + Size + 1) = 0; + + return DirPath; +} + +CHAR16 *mSymbolTypeStr[] = { + L"( F)", + L"(SF)", + L"(GV)", + L"(SV)", +}; + +/** + + Comvert Symbol Type to string. + + @param Type - Symbol Type + + @return String + +**/ +CHAR16 * +EdbSymbolTypeToStr ( + IN EFI_DEBUGGER_SYMBOL_TYPE Type + ) +{ + if (Type < 0 || Type >= EfiDebuggerSymbolTypeMax) { + return L"(?)"; + } + + return mSymbolTypeStr [Type]; +} + +/** + + Find the symbol according to address and display symbol. + + @param Address - SymbolAddress + @param DebuggerPrivate - EBC Debugger private data structure + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerDisplaySymbolAccrodingToAddress ( + IN UINTN Address, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate + ) +{ + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + UINTN CandidateAddress; + + // + // Find the nearest symbol address + // + CandidateAddress = EbdFindSymbolAddress (Address, EdbMatchSymbolTypeNearestAddress, &Object, &Entry); + if (CandidateAddress == 0 || CandidateAddress == (UINTN) -1 || Entry == NULL) { + EDBPrint (L"Symbole at Address not found!\n"); + return EFI_DEBUG_CONTINUE; + } else if (Address != CandidateAddress) { + EDBPrint (L"Symbole at Address not found, print nearest one!\n"); + } + + // + // Display symbol + // + EDBPrint (L"Symbol File Name: %s\n", Object->Name); + if (sizeof(UINTN) == sizeof(UINT64)) { + EDBPrint (L" Address Type Symbol\n"); + EDBPrint (L" ================== ==== ========\n"); +// EDBPrint (L" 0xFFFFFFFF00000000 ( F) TestMain\n"); + EDBPrint ( + L" 0x%016lx %s %a\n", + (UINT64)Entry->Rva + Object->BaseAddress, + EdbSymbolTypeToStr (Entry->Type), + Entry->Name + ); + } else { + EDBPrint (L" Address Type Symbol\n"); + EDBPrint (L" ========== ==== ========\n"); +// EDBPrint (L" 0xFFFF0000 ( F) TestMain\n"); + EDBPrint ( + L" 0x%08x %s %a\n", + Entry->Rva + Object->BaseAddress, + EdbSymbolTypeToStr (Entry->Type), + Entry->Name + ); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + Find the symbol according to name and display symbol. + + @param SymbolFileName - The Symbol File Name, NULL means for all + @param SymbolName - The Symbol Name, NULL means for all + @param DebuggerPrivate - EBC Debugger private data structure + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerDisplaySymbolAccrodingToName ( + IN CHAR16 *SymbolFileName, + IN CHAR16 *SymbolName, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate + ) +{ + UINTN Index; + UINTN SubIndex; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + + if (DebuggerPrivate->DebuggerSymbolContext.ObjectCount == 0) { + EDBPrint (L"No Symbol File!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Go throuth each symbol file + // + Object = DebuggerPrivate->DebuggerSymbolContext.Object; + for (Index = 0; Index < DebuggerPrivate->DebuggerSymbolContext.ObjectCount; Index++, Object++) { + if ((SymbolFileName != NULL) && + (StriCmp (SymbolFileName, Object->Name) != 0)) { + continue; + } + + // + // Break each symbol file + // + if (Index != 0) { + if (SetPageBreak ()) { + break; + } + } + + EDBPrint (L"Symbol File Name: %s\n", Object->Name); + if (Object->EntryCount == 0) { + EDBPrint (L"No Symbol!\n"); + continue; + } + Entry = Object->Entry; + if (sizeof(UINTN) == sizeof(UINT64)) { + EDBPrint (L" Address Type Symbol\n"); + EDBPrint (L" ================== ==== ========\n"); +// EDBPrint (L" 0xFFFFFFFF00000000 ( F) TestMain (EbcTest.obj)\n"); + } else { + EDBPrint (L" Address Type Symbol\n"); + EDBPrint (L" ========== ==== ========\n"); +// EDBPrint (L" 0xFFFF0000 ( F) TestMain (EbcTest.obj)\n"); + } + + // + // Go through each symbol name + // + for (SubIndex = 0; SubIndex < Object->EntryCount; SubIndex++, Entry++) { + if ((SymbolName != NULL) && + (StrCmpUnicodeAndAscii (SymbolName, Entry->Name) != 0)) { + continue; + } + + // + // Break symbol + // + if (((SubIndex % EFI_DEBUGGER_LINE_NUMBER_IN_PAGE) == 0) && + (SubIndex != 0)) { + if (SetPageBreak ()) { + break; + } + } + + if (sizeof(UINTN) == sizeof(UINT64)) { + EDBPrint ( + L" 0x%016lx %s %a (%a)\n", + (UINT64)Entry->Rva + Object->BaseAddress, + EdbSymbolTypeToStr (Entry->Type), + Entry->Name, + Entry->ObjName + ); + } else { + EDBPrint ( + L" 0x%08x %s %a (%a)\n", + Entry->Rva + Object->BaseAddress, + EdbSymbolTypeToStr (Entry->Type), + Entry->Name, + Entry->ObjName + ); + } + } + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - ListSymbol. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerListSymbol ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + CHAR16 *SymbolFileName; + CHAR16 *SymbolName; + CHAR16 *CommandStr; + UINTN Address; + + SymbolFileName = NULL; + SymbolName = NULL; + CommandStr = CommandArg; + + // + // display symbol according to address + // + if (CommandStr != NULL) { + if ((StriCmp (CommandStr, L"F") != 0) && + (StriCmp (CommandStr, L"S") != 0)) { + Address = Xtoi (CommandStr); + return DebuggerDisplaySymbolAccrodingToAddress (Address, DebuggerPrivate); + } + } + + // + // Get SymbolFileName + // + if (CommandStr != NULL) { + if (StriCmp (CommandStr, L"F") == 0) { + CommandStr = StrGetNextTokenLine (L" "); + if (CommandStr == NULL) { + EDBPrint (L"Symbol File Name missing!\n"); + return EFI_DEBUG_CONTINUE; + } else { + SymbolFileName = CommandStr; + CommandStr = StrGetNextTokenLine (L" "); + } + } + } + // + // Get SymbolName + // + if (CommandStr != NULL) { + if (StriCmp (CommandStr, L"S") == 0) { + CommandStr = StrGetNextTokenLine (L" "); + if (CommandStr == NULL) { + EDBPrint (L"Symbol Name missing!\n"); + return EFI_DEBUG_CONTINUE; + } else { + SymbolName = CommandStr; + CommandStr = StrGetNextTokenLine (L" "); + } + } + } + if (CommandStr != NULL) { + EDBPrint (L"Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // display symbol according to name + // + return DebuggerDisplaySymbolAccrodingToName (SymbolFileName, SymbolName, DebuggerPrivate); +} + +/** + + DebuggerCommand - LoadSymbol. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerLoadSymbol ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN BufferSize; + VOID *Buffer; + EFI_STATUS Status; + CHAR16 *FileName; + CHAR16 *CommandArg2; + BOOLEAN IsLoadCode; + CHAR16 *DirName; + CHAR16 CodFile[EFI_DEBUGGER_SYMBOL_NAME_MAX]; + CHAR16 *CodFileName; + UINTN Index; + + // + // Check the argument + // + if (CommandArg == NULL) { + EDBPrint (L"SymbolFile not found!\n"); + return EFI_DEBUG_CONTINUE; + } + IsLoadCode = FALSE; + CommandArg2 = StrGetNextTokenLine (L" "); + if (CommandArg2 != NULL) { + if (StriCmp (CommandArg2, L"a") == 0) { + IsLoadCode = TRUE; + } else { + EDBPrint (L"Argument error!\n"); + return EFI_DEBUG_CONTINUE; + } + } + + if (StrLen (CommandArg) <= 4) { + EDBPrint (L"SymbolFile name error!\n"); + return EFI_DEBUG_CONTINUE; + } + if (StriCmp (CommandArg + (StrLen (CommandArg) - 4), L".map") != 0) { + EDBPrint (L"SymbolFile name error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Read MAP file to memory + // + Status = ReadFileToBuffer (DebuggerPrivate, CommandArg, &BufferSize, &Buffer, TRUE); + if (EFI_ERROR(Status)) { + EDBPrint (L"SymbolFile read error!\n"); + return EFI_DEBUG_CONTINUE; + } + + FileName = GetFileNameFromFullPath (CommandArg); + // + // Load Symbol + // + Status = EdbLoadSymbol (DebuggerPrivate, FileName, BufferSize, Buffer); + if (EFI_ERROR(Status)) { + EDBPrint (L"LoadSymbol error!\n"); + gBS->FreePool (Buffer); + return EFI_DEBUG_CONTINUE; + } + gBS->FreePool (Buffer); + + // + // Patch Symbol for RVA + // + Status = EdbPatchSymbolRVA (DebuggerPrivate, FileName, EdbEbcImageRvaSearchTypeLast); + if (EFI_ERROR(Status)) { + EDBPrint (L"PatchSymbol RVA - %r! Using the RVA in symbol file.\n", Status); + } else { + DEBUG ((DEBUG_ERROR, "PatchSymbol RVA successfully!\n")); + } + + if (!IsLoadCode) { + return EFI_DEBUG_CONTINUE; + } + + // + // load each cod file + // + DirName = GetDirNameFromFullPath (CommandArg); + ZeroMem (CodFile, sizeof(CodFile)); + if (StrCmp (DirName, L"") != 0) { + StrCpyS (CodFile, sizeof(CodFile), DirName); + } else { + DirName = L"\\"; + } + + // + // Go throuth each file under this dir + // + Index = 0; + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + while (CodFileName != NULL) { + ZeroMem (CodFile, sizeof(CodFile)); + if (StrCmp (DirName, L"\\") != 0) { + StrCpyS (CodFile, sizeof(CodFile), DirName); + } + + // + // read cod file to memory + // + Status = ReadFileToBuffer (DebuggerPrivate, ConstructFullPath (CodFile, CodFileName, EFI_DEBUGGER_SYMBOL_NAME_MAX - StrLen (CodFile) - 2), &BufferSize, &Buffer, FALSE); + if (EFI_ERROR(Status)) { + EDBPrint (L"CodeFile read error!\n"); + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + continue; + } + + // + // Load Code + // + Status = EdbLoadCode (DebuggerPrivate, FileName, CodFileName, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + EDBPrint (L"LoadCode error!\n"); + gBS->FreePool (Buffer); + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + continue; + } + + // + // Record the buffer + // + Status = EdbAddCodeBuffer (DebuggerPrivate, FileName, CodFileName, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + EDBPrint (L"AddCodeBuffer error!\n"); + gBS->FreePool (Buffer); + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + continue; + } + + // + // Get next file + // + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - UnloadSymbol + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerUnloadSymbol ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EFI_STATUS Status; + CHAR16 *FileName; + CHAR16 *DirName; + CHAR16 CodFile[EFI_DEBUGGER_SYMBOL_NAME_MAX]; + CHAR16 *CodFileName; + UINTN Index; + VOID *BufferPtr; + + // + // Check the argument + // + if (CommandArg == NULL) { + EDBPrint (L"SymbolFile not found!\n"); + return EFI_DEBUG_CONTINUE; + } + + FileName = GetFileNameFromFullPath (CommandArg); + + // + // Unload Code + // + DirName = GetDirNameFromFullPath (CommandArg); + ZeroMem (CodFile, sizeof(CodFile)); + if (StrCmp (DirName, L"") != 0) { + StrCpyS (CodFile, sizeof(CodFile), DirName); + } else { + DirName = L"\\"; + } + + // + // Go through each file under this dir + // + Index = 0; + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + while (CodFileName != NULL) { + ZeroMem (CodFile, sizeof(CodFile)); + if (StrCmp (DirName, L"\\") != 0) { + StrCpyS (CodFile, sizeof(CodFile), DirName); + } + + // + // Unload Code + // + Status = EdbUnloadCode (DebuggerPrivate, FileName, CodFileName, &BufferPtr); + if (EFI_ERROR (Status)) { + EDBPrint (L"UnloadCode error!\n"); + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + continue; + } + + // + // Delete the code buffer + // + Status = EdbDeleteCodeBuffer (DebuggerPrivate, FileName, CodFileName, BufferPtr); + if (EFI_ERROR (Status)) { + EDBPrint (L"DeleteCodeBuffer error!\n"); + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + continue; + } + + // + // Get next file + // + CodFileName = GetFileNameUnderDir (DebuggerPrivate, DirName, L".cod", &Index); + } + + // + // Unload Symbol + // + Status = EdbUnloadSymbol (DebuggerPrivate, FileName); + if (EFI_ERROR(Status)) { + EDBPrint (L"UnloadSymbol error!\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - DisplaySymbol. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerDisplaySymbol ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + if (CommandArg == NULL) { + DebuggerPrivate->DebuggerSymbolContext.DisplaySymbol = !DebuggerPrivate->DebuggerSymbolContext.DisplaySymbol; + EdbShowDisasm (DebuggerPrivate, SystemContext); + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->DebuggerSymbolContext.DisplaySymbol = TRUE; + EdbShowDisasm (DebuggerPrivate, SystemContext); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->DebuggerSymbolContext.DisplaySymbol = FALSE; + EdbShowDisasm (DebuggerPrivate, SystemContext); + } else { + EDBPrint (L"DisplaySymbol - argument error\n"); + } + + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - LoadCode. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerLoadCode ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN BufferSize; + VOID *Buffer; + EFI_STATUS Status; + CHAR16 *CommandArg2; + CHAR16 *FileName; + CHAR16 *MapFileName; + + // + // Check the argument + // + if (CommandArg == NULL) { + EDBPrint (L"CodeFile not found!\n"); + return EFI_DEBUG_CONTINUE; + } + CommandArg2 = StrGetNextTokenLine (L" "); + if (CommandArg2 == NULL) { + EDBPrint (L"SymbolFile not found!\n"); + return EFI_DEBUG_CONTINUE; + } + + if (StrLen (CommandArg) <= 4) { + EDBPrint (L"CodeFile name error!\n"); + return EFI_DEBUG_CONTINUE; + } + if (StriCmp (CommandArg + (StrLen (CommandArg) - 4), L".cod") != 0) { + EDBPrint (L"CodeFile name error!\n"); + return EFI_DEBUG_CONTINUE; + } + if (StrLen (CommandArg2) <= 4) { + EDBPrint (L"SymbolFile name error!\n"); + return EFI_DEBUG_CONTINUE; + } + if (StriCmp (CommandArg2 + (StrLen (CommandArg2) - 4), L".map") != 0) { + EDBPrint (L"SymbolFile name error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // read cod file to memory + // + Status = ReadFileToBuffer (DebuggerPrivate, CommandArg, &BufferSize, &Buffer, TRUE); + if (EFI_ERROR(Status)) { + EDBPrint (L"CodeFile read error!\n"); + return EFI_DEBUG_CONTINUE; + } + + FileName = GetFileNameFromFullPath (CommandArg); + MapFileName = GetFileNameFromFullPath (CommandArg2); + // + // Load Code + // + Status = EdbLoadCode (DebuggerPrivate, MapFileName, FileName, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + EDBPrint (L"LoadCode error!\n"); + gBS->FreePool (Buffer); + return EFI_DEBUG_CONTINUE; + } + + // + // Record the buffer + // + Status = EdbAddCodeBuffer (DebuggerPrivate, MapFileName, FileName, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + EDBPrint (L"AddCodeBuffer error!\n"); + gBS->FreePool (Buffer); + return EFI_DEBUG_CONTINUE; + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - UnloadCode. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerUnloadCode ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + CHAR16 *CommandArg2; + CHAR16 *FileName; + CHAR16 *MapFileName; + EFI_STATUS Status; + VOID *BufferPtr; + + // + // Check the argument + // + if (CommandArg == NULL) { + EDBPrint (L"CodeFile not found!\n"); + return EFI_DEBUG_CONTINUE; + } + CommandArg2 = StrGetNextTokenLine (L" "); + if (CommandArg2 == NULL) { + EDBPrint (L"SymbolFile not found!\n"); + return EFI_DEBUG_CONTINUE; + } + + FileName = GetFileNameFromFullPath (CommandArg); + MapFileName = GetFileNameFromFullPath (CommandArg2); + + // + // Unload Code + // + Status = EdbUnloadCode (DebuggerPrivate, MapFileName, FileName, &BufferPtr); + if (EFI_ERROR (Status)) { + EDBPrint (L"UnloadCode error!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Delete Code buffer + // + Status = EdbDeleteCodeBuffer (DebuggerPrivate, MapFileName, FileName, BufferPtr); + if (EFI_ERROR (Status)) { + EDBPrint (L"DeleteCodeBuffer error!\n"); + } + + // + // Done + // + return EFI_DEBUG_CONTINUE; +} + +/** + + DebuggerCommand - DisplayCode. + + @param CommandArg - The argument for this command + @param DebuggerPrivate - EBC Debugger private data structure + @param ExceptionType - Exception type. + @param SystemContext - EBC system context. + + @retval EFI_DEBUG_CONTINUE - formal return value + +**/ +EFI_DEBUG_STATUS +DebuggerDisplayCode ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + if (CommandArg == NULL) { + DebuggerPrivate->DebuggerSymbolContext.DisplayCodeOnly = !DebuggerPrivate->DebuggerSymbolContext.DisplayCodeOnly; + EdbShowDisasm (DebuggerPrivate, SystemContext); + } else if (StriCmp (CommandArg, L"on") == 0) { + DebuggerPrivate->DebuggerSymbolContext.DisplayCodeOnly = TRUE; + EdbShowDisasm (DebuggerPrivate, SystemContext); + } else if (StriCmp (CommandArg, L"off") == 0) { + DebuggerPrivate->DebuggerSymbolContext.DisplayCodeOnly = FALSE; + EdbShowDisasm (DebuggerPrivate, SystemContext); + } else { + EDBPrint (L"DisplayCode - argument error\n"); + } + + return EFI_DEBUG_CONTINUE; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.c new file mode 100644 index 000000000..5597a7e15 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.c @@ -0,0 +1,656 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +// +// Debugger Command Table +// +EFI_DEBUGGER_COMMAND_SET mDebuggerCommandSet[] = { + // + // Execution + // + { + L"G", + L"G/[F5] - continue to run the program\n", + L"The go command is used to cause the debugger to not interrupt execution of the EBC image. The debugger will only break execution of the interpreter if an exception is encountered (including an EBC breakpoint).\n\n", + L"G [til ]\n" + L" (No Argument) - It means continue run the program.\n" + L" til - It means continuing run the program till IP is the Address.\n" + L"
- The hexical address user want to break at.\n" + L" - The symbol name for target address user want to break at. It has following format [MapFileName:]SymbolName\n", + L"Execution:\n", + {SCAN_F5, CHAR_NULL}, + DebuggerGo + }, + { + L"T", + L"T/[F8] - step into\n", + L"The step into command will cause the EBC debugger to step a single instruction. If the instruction is a call to internal code (CALL), then the debugger will break at the new function CALL.\n\n", + L"T\n" + L" (No Argument)\n", + L"", + {SCAN_F8, CHAR_NULL}, + DebuggerStepInto + }, + { + L"P", + L"P/[F10] - step over\n", + L"The step over command will cause the EBC debugger to step a single instruction. If the instruction is a call to internal code (CALL), then the external call will be made and the debugger will break at the instruction following the CALL.\n\n", + L"P\n" + L" (No Argument)\n", + L"", + {SCAN_F10, CHAR_NULL}, + DebuggerStepOver + }, + { + L"O", + L"O/[F11] - step out\n", + L"The step out command causes the EBC debugger to step out function calls. The function will be executed, but the debugger will stop after the called function returns.\n\n", + L"O\n" + L" (No Argument)\n", + L"", + {SCAN_F11, CHAR_NULL}, + DebuggerStepOut + }, + { + L"Q", + L"Q - reset the debugger to default value and go\n", + L"The quit command will reset the debugger to default value and go.\n\n", + L"Q\n" + L" (No Argument)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerQuit + }, + // + // Break + // + { + L"BOC", + L"BO[C|CX|R|E|T|K] - break on CALL/CALLEX/RET/Entrypoint/Native Thunk/Key\n", + L"Enabling break-on-call will cause the debugger to halt execution and display the debugger prompt prior to executing any EBC CALL (to EBC) instructions.\n\n", + L"BOC [on|off]\n" + L" (No Argument) - show current state\n" + L" on - enable break-on-call\n" + L" off - disable break-on-call\n", + L"Break:\n", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakOnCALL + }, + { + L"BOCX", + L"", + L"Enabling break-on-callex will cause the debugger to halt execution and display the debugger prompt prior to executing EBC CALLEX (thunk out) instructions.\n\n", + L"BOCX [on|off]\n" + L" (No Argument) - show current state\n" + L" on - enable break-on-callex\n" + L" off - disable break-on-callex\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakOnCALLEX + }, + { + L"BOR", + L"", + L"Enabling break-on-return will cause the debugger to halt execution and display the debugger prompt prior to executing EBC RET instructions.\n\n", + L"BOR [on|off]\n" + L" (No Argument) - show current state\n" + L" on - enable break-on-return\n" + L" off - disable break-on-return\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakOnRET + }, + { + L"BOE", + L"", + L"Enabling break-on-entrypoint will cause the debugger to halt execution and display the debugger prompt prior to start a driver entry point. (Default is on)\n\n", + L"BOE [on|off]\n" + L" (No Argument) - show current state\n" + L" on - enable break-on-entrypoint\n" + L" off - disable break-on-entrypoint\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakOnEntrypoint + }, + { + L"BOT", + L"", + L"Enabling break-on-thunk will cause the debugger to halt execution and display the debugger prompt prior to start native call EBC thunk. (Default is on)\n\n", + L"BOT [on|off]\n" + L" (No Argument) - show current state\n" + L" on - enable break-on-thunk\n" + L" off - disable break-on-thunk\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakOnThunk + }, + { + L"BOK", + L"", + L"Enabling break-on-key will cause the debugger to halt execution and display the debugger prompt after press any key.\n\n", + L"BOK [on|off]\n" + L" (No Argument) - show current state\n" + L" on - enable break-on-key\n" + L" off - disable break-on-key\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakOnKey + }, + { + L"BL", + L"B[L|P|C|D|E] - breakpoint list/set/clear/disable/enable\n", + L"List Breakpoint\n\n", + L"BL\n" + L" (No Argument) - show the state for current breakpoint\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakpointList + }, + { + L"BP", + L"", + L"Set Breakpoint\n\n", + L"BP \n" + L"
- Hexical breakpoint address\n" + L" - Symbol name for breakpoint address. It has following format [MapFileName:]SymbolName.\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakpointSet + }, + { + L"BC", + L"", + L"Clear Breakpoint\n\n", + L"BC |*\n" + L" - Decimal breakpoint index, which can be got from BL command\n" + L" * - For all the breakpoint\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakpointClear + }, + { + L"BD", + L"", + L"Disable Breakpoint\n\n", + L"BD |*\n" + L" - Decimal breakpoint index, which can be got from BL command\n" + L" * - For all the breakpoint\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakpointDisable + }, + { + L"BE", + L"", + L"Enable Breakpoint\n\n", + L"BE |*\n" + L" - Decimal breakpoint index, which can be got from BL command\n" + L" * - For all the breakpoint\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerBreakpointEnable + }, + // + // Information + // + { + L"K", + L"K - show/clear call-stack\n", + L"The call-stack command will show or clear the current call-stack.\n\n", + L"K [p []|c]\n" + L" (No Argument) - Show current call-stack\n" + L" p - Show current call-stack with parameters\n" + L" ParameterNum - Decimal call-stack parameters number, 8 by default, 16 as max\n" + L" c - Clear current call-stack\n", + L"Information:\n", + {SCAN_NULL, CHAR_NULL}, + DebuggerCallStack + }, + { + L"TRACE", + L"TRACE - show/clear trace instruction branch\n", + L"The trace command will show or clear the latest instruction branch.\n\n", + L"TRACE [c]\n" + L" (No Argument) - Show current instruction branch\n" + L" c - Clear current instruction branch\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerInstructionBranch + }, + { + L"R", + L"R/[F2] - display/modify register\n", + L"The register command is used to display or modify the contents of EBC VM registers. (R0~R7, Flags, IP)\n\n", + L"R [ ]\n" + L" (No Argument) - Display all registers\n" + L" - EBC VM register name (R0~R7, Flags, ControlFlags, and IP\n" + L" - The Hexical value of register\n", + L"", + {SCAN_F2, CHAR_NULL}, + DebuggerRegister + }, + { + L"L", + L"L/[F4] - show/load instruction assembly count\n", + L"The list assembly command will disassemble instructions starting with the current EBC VM instruction pointer. (by default 5 instructions)\n\n", + L"L []\n" + L" (No Argument) - List current assembly code\n" + L" Count - The decimal instruction assembly count\n", + L"", + {SCAN_F4, CHAR_NULL}, + DebuggerList + }, + { + L"SCOPE", + L"SCOPE - load scope address\n", + L"The scope command will disassemble instructions starting with the Scope. (by default current EBC VM IP)\n\n", + L"SCOPE \n" + L"
- The Hexical address where user wants to see the assembly code\n" + L" - Symbol name for scope address. It has following format [MapFileName:]SymbolName.\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerScope + }, + { + L"DB", + L"[D|E][B|W|D|Q] - display/modify memory\n", + L"Display BYTES Memory\n\n", + L"DB []\n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory count (not set means 1)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryDB + }, + { + L"DW", + L"", + L"Display WORDS Memory\n\n", + L"DW []\n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory count (not set means 1)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryDW + }, + { + L"DD", + L"", + L"Display DWORDS Memory\n\n", + L"DD []\n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory count (not set means 1)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryDD + }, + { + L"DQ", + L"", + L"Display QWORDS Memory\n\n", + L"DQ []\n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory count (not set means 1)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryDQ + }, + { + L"EB", + L"", + L"Enter BYTES Memory\n\n", + L"EB \n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory value\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryEB + }, + { + L"EW", + L"", + L"Enter WORDS Memory\n\n", + L"EW \n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory value\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryEW + }, + { + L"ED", + L"", + L"Enter DWORDS Memory\n\n", + L"ED \n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory value\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryED + }, + { + L"EQ", + L"", + L"Enter QWORDS Memory\n\n", + L"EQ \n" + L"
- The hexical memory address\n" + L" - Symbol name for memory address. It has following format [MapFileName:]SymbolName.\n" + L" - The hexical memory value\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerMemoryEQ + }, + // + // Symbol + // + { + L"LN", + L"LN - list the symbol\n", + L"The show symbol command will list all the current symbol. It can list the symbol in one symbol file, or list the same symbol in all the files. It can also list the symbol according to nearest address.\n\n", + L"LN [[F ] [S ]] |
\n" + L" (No Argument) - List all the symbol\n" + L" F - List the symbol in this symbol file only\n" + L" S - List this symbol only\n" + L"
- The hexical memory address, which user want to find the symbol for.\n", + L"Symbol:\n", + {SCAN_NULL, CHAR_NULL}, + DebuggerListSymbol + }, + { + L"LOADSYMBOL", + L"[UN]LOADSYMBOL - load/unload the symbol file\n", + L"The load symbol command will load the ebc map file. Then it parses the function name and global variable, and the print real name when do the disassembly. (Symbol file name should be XXX.MAP)\n\n", + L"LOADSYMBOL [a]\n" + L" SymbolFile - The EBC symbol file (Its name should be XXX.MAP)\n" + L" a - Automatically load code files in the same dir\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerLoadSymbol + }, + { + L"UNLOADSYMBOL", + L"", + L"The unload symbol command will unload the ebc map and cod file. After that the name will not be print.\n\n", + L"UNLOADSYMBOL \n" + L" SymbolFile - The EBC symbol file (Its name should be XXX.MAP)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerUnloadSymbol + }, + { + L"LOADCODE", + L"[UN]LOADCODE - load/unload the code file\n", + L"The load code command will load the ebc cod file. Then it parses the cod file, and the print source code when do the disassembly. (Code file name should be XXX.COD)\n\n", + L"LOADCODE \n" + L" CodeFile - The EBC code file (Its name should be XXX.COD)\n" + L" SymbolFile - The EBC symbol file (Its name should be XXX.MAP)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerLoadCode + }, + { + L"UNLOADCODE", + L"", + L"The unload code command will unload the ebc cod file. After that the source code will not be print.\n\n", + L"UNLOADCODE \n" + L" CodeFile - The EBC code file (Its name should be XXX.COD)\n" + L" SymbolFile - The EBC symbol file (Its name should be XXX.MAP)\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerUnloadCode + }, + { + L"DISPLAYSYMBOL", + L"DISPLAYSYMBOL/[F3] - disable/enable the symbol output\n", + L"", + L"The display symbol command will configure the symbol show or not-show when disassembly.\n\n" + L"DISPLAYSYMBOL [on|off]\n" + L" (No Argument) - swtich symbol output state to another one\n" + L" on - enable symbol output\n" + L" off - disable symbol output\n", + L"", + {SCAN_F3, CHAR_NULL}, + DebuggerDisplaySymbol + }, + { + L"DISPLAYCODE", + L"DISPLAYCODE/[F6] - disable/enable the source code only output\n", + L"", + L"The display code command will configure the source code only show or misc source code with assembly.\n\n" + L"DISPLAYCODE [on|off]\n" + L" (No Argument) - swtich source only output state to another one\n" + L" on - enable source only output\n" + L" off - disable source only output\n", + L"", + {SCAN_F6, CHAR_NULL}, + DebuggerDisplayCode + }, + // + // Other + // + { + L"H", + L"", + L"The help command will print help information for each command\n\n", + L"H []\n", + L"", + {SCAN_F1, CHAR_NULL}, + DebuggerHelp + }, +/* + // + // Extended + // + { + L"!IB", + L"![I|O][B|W|D] - display/modify IO\n", + L"", + L"!IB
\n", + L"Extended:\n", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtIoIB + }, + { + L"!IW", + L"", + L"", + L"!IW
\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtIoIW + }, + { + L"!ID", + L"", + L"", + L"!ID
\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtIoID + }, + { + L"!OB", + L"", + L"", + L"!OB
\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtIoOB + }, + { + L"!OW", + L"", + L"", + L"!OW
\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtIoOW + }, + { + L"!OD", + L"", + L"", + L"!OD
\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtIoOD + }, + { + L"!PCIL", + L"!PCIL - list PCI device, with BAR\n", + L"", + L"!PCIL [B]\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtPciPCIL + }, + { + L"!PCID", + L"!PCID - show PCI space\n", + L"", + L"!PCID Bus Device Function [H|B|E]\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtPciPCID + }, + { + L"!CFGB", + L"!CFG[B|W|D] - show/modify PCI space", + L"", + L"!CFGB
[]\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtPciCFGB + }, + { + L"!CFGW", + L"", + L"", + L"!CFGW
[]\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtPciCFGW + }, + { + L"!CFGD", + L"", + L"", + L"!CFGD
[]\n", + L"", + {SCAN_NULL, CHAR_NULL}, + DebuggerExtPciCFGD + }, +*/ + { + NULL, + NULL, + NULL, + NULL, + NULL, + {SCAN_NULL, CHAR_NULL}, + NULL + }, +}; + +/** + + Find the command according to name. + + @param CommandName - Command Name + @param CommandArg - Command Argument + + @return Not NULL - The DebuggerCommand is found successfully + @return NULL - not found + +**/ +EFI_DEBUGGER_COMMAND +MatchDebuggerCommand ( + IN CHAR16 *CommandName, + IN CHAR16 **CommandArg + ) +{ + UINTN Index; + CHAR16 *Temp; + + // + // Get Command Name + // + Temp = StrGetNewTokenLine (CommandName, L" "); + CommandName = Temp; + // + // Get Command Argument + // + Temp = StrGetNextTokenLine (L" "); + *CommandArg = Temp; + + if (CommandName == NULL) { + return NULL; + } + + // + // Go through each command, check the CommandName + // + for (Index = 0; mDebuggerCommandSet[Index].CommandName != NULL; Index++) { + if (StriCmp (CommandName, mDebuggerCommandSet[Index].CommandName) == 0) { + // + // Found + // + return mDebuggerCommandSet[Index].CommandFunc; + } + } + + // + // Not found + // + return NULL; +} + +/** + + Find the command name according to the function key. + + @param CommandKey - Command Function Key + + @return Not NULL - The DebuggerName is found successfully + @return NULL - not found + +**/ +CHAR16 * +GetCommandNameByKey ( + IN EFI_INPUT_KEY CommandKey + ) +{ + UINTN Index; + + // + // Go through each command, check the CommandKey + // + for (Index = 0; mDebuggerCommandSet[Index].CommandName != NULL; Index++) { + if ((mDebuggerCommandSet[Index].CommandKey.UnicodeChar == CommandKey.UnicodeChar) && + (mDebuggerCommandSet[Index].CommandKey.ScanCode == CommandKey.ScanCode)) { + // + // Found + // + return mDebuggerCommandSet[Index].CommandName; + } + } + + // + // Not found + // + return NULL; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.h new file mode 100644 index 000000000..ac8c23adf --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommand.h @@ -0,0 +1,115 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#ifndef _EFI_EDB_COMMAND_H_ +#define _EFI_EDB_COMMAND_H_ + +typedef enum { + EdbWidthUint8, + EdbWidthUint16, + EdbWidthUint32, + EdbWidthUint64, + EdbWidthMax +} EDB_DATA_WIDTH; + +/** + + Find the command according to name. + + @param CommandName - Command Name + @param CommandArg - Command Argument + + @return Not NULL - The DebuggerCommand is found successfully + @return NULL - not found + +**/ +EFI_DEBUGGER_COMMAND +MatchDebuggerCommand ( + IN CHAR16 *CommandName, + IN CHAR16 **CommandArg + ); + +/** + + Find the command name according to the function key. + + @param CommandKey - Command Function Key + + @return Not NULL - The DebuggerName is found successfully + @return NULL - not found + +**/ +CHAR16 * +GetCommandNameByKey ( + IN EFI_INPUT_KEY CommandKey + ); + +// +// Definition for Command Table +// +#define EDB_COMMAND_DEFINE(func) \ +EFI_DEBUG_STATUS \ +func ( \ + IN CHAR16 *CommandArg, \ + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, \ + IN EFI_EXCEPTION_TYPE ExceptionType, \ + IN OUT EFI_SYSTEM_CONTEXT SystemContext \ + ) + +EDB_COMMAND_DEFINE (DebuggerCallStack); +EDB_COMMAND_DEFINE (DebuggerInstructionBranch); +EDB_COMMAND_DEFINE (DebuggerBreakOnCALL); +EDB_COMMAND_DEFINE (DebuggerBreakOnCALLEX); +EDB_COMMAND_DEFINE (DebuggerBreakOnRET); +EDB_COMMAND_DEFINE (DebuggerBreakOnEntrypoint); +EDB_COMMAND_DEFINE (DebuggerBreakOnThunk); +EDB_COMMAND_DEFINE (DebuggerBreakOnKey); +EDB_COMMAND_DEFINE (DebuggerBreakpointList); +EDB_COMMAND_DEFINE (DebuggerBreakpointSet); +EDB_COMMAND_DEFINE (DebuggerBreakpointClear); +EDB_COMMAND_DEFINE (DebuggerBreakpointDisable); +EDB_COMMAND_DEFINE (DebuggerBreakpointEnable); +EDB_COMMAND_DEFINE (DebuggerGo); +EDB_COMMAND_DEFINE (DebuggerHelp); +EDB_COMMAND_DEFINE (DebuggerMemoryDB); +EDB_COMMAND_DEFINE (DebuggerMemoryDW); +EDB_COMMAND_DEFINE (DebuggerMemoryDD); +EDB_COMMAND_DEFINE (DebuggerMemoryDQ); +EDB_COMMAND_DEFINE (DebuggerMemoryEB); +EDB_COMMAND_DEFINE (DebuggerMemoryEW); +EDB_COMMAND_DEFINE (DebuggerMemoryED); +EDB_COMMAND_DEFINE (DebuggerMemoryEQ); +EDB_COMMAND_DEFINE (DebuggerQuit); +EDB_COMMAND_DEFINE (DebuggerRegister); +EDB_COMMAND_DEFINE (DebuggerScope); +EDB_COMMAND_DEFINE (DebuggerList); +EDB_COMMAND_DEFINE (DebuggerStepInto); +EDB_COMMAND_DEFINE (DebuggerStepOver); +EDB_COMMAND_DEFINE (DebuggerStepOut); +EDB_COMMAND_DEFINE (DebuggerListSymbol); +EDB_COMMAND_DEFINE (DebuggerLoadSymbol); +EDB_COMMAND_DEFINE (DebuggerUnloadSymbol); +EDB_COMMAND_DEFINE (DebuggerDisplaySymbol); +EDB_COMMAND_DEFINE (DebuggerLoadCode); +EDB_COMMAND_DEFINE (DebuggerUnloadCode); +EDB_COMMAND_DEFINE (DebuggerDisplayCode); +EDB_COMMAND_DEFINE (DebuggerExtIoIB); +EDB_COMMAND_DEFINE (DebuggerExtIoIW); +EDB_COMMAND_DEFINE (DebuggerExtIoID); +EDB_COMMAND_DEFINE (DebuggerExtIoOB); +EDB_COMMAND_DEFINE (DebuggerExtIoOW); +EDB_COMMAND_DEFINE (DebuggerExtIoOD); +EDB_COMMAND_DEFINE (DebuggerExtPciPCIL); +EDB_COMMAND_DEFINE (DebuggerExtPciPCID); +EDB_COMMAND_DEFINE (DebuggerExtPciCFGB); +EDB_COMMAND_DEFINE (DebuggerExtPciCFGW); +EDB_COMMAND_DEFINE (DebuggerExtPciCFGD); + +extern EFI_DEBUGGER_COMMAND_SET mDebuggerCommandSet[]; + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommon.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommon.h new file mode 100644 index 000000000..924a2f755 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbCommon.h @@ -0,0 +1,239 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_EDB_COMMON_H_ +#define _EFI_EDB_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef UINTN EFI_DEBUG_STATUS; + +typedef struct _EFI_DEBUGGER_PRIVATE_DATA EFI_DEBUGGER_PRIVATE_DATA; + +// +// Definition for Debugger Command +// +typedef +EFI_DEBUG_STATUS +(* EFI_DEBUGGER_COMMAND) ( + IN CHAR16 *CommandArg, + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext +); + +typedef struct { + CHAR16 *CommandName; + CHAR16 *CommandTitle; + CHAR16 *CommandHelp; + CHAR16 *CommandSyntax; + CHAR16 *ClassName; + EFI_INPUT_KEY CommandKey; + EFI_DEBUGGER_COMMAND CommandFunc; +} EFI_DEBUGGER_COMMAND_SET; + +// +// Definition for Debugger Symbol +// +#define EFI_DEBUGGER_SYMBOL_NAME_MAX 256 +#define EFI_DEBUGGER_SYMBOL_ENTRY_MAX 512 +#define EFI_DEBUGGER_SYMBOL_OBJECT_MAX 32 + +// +// We have following SYMBOL data structure: +// +// SYMBOL_CONTEXT -> SYMBOL_OBJECT -> SYMBOL_ENTRY (FuncXXX, 0xXXX) +// SYMBOL_ENTRY (VarYYY, 0xYYY) +// SYMBOL_ENTRY +// +// SYMBOL_OBJECT -> SYMBOL_ENTRY +// SYMBOL_ENTRY +// +// SYMBOL_OBJECT -> SYMBOL_ENTRY +// SYMBOL_ENTRY +// + +typedef enum { + EfiDebuggerSymbolFunction, + EfiDebuggerSymbolStaticFunction, + EfiDebuggerSymbolGlobalVariable, + EfiDebuggerSymbolStaticVariable, + EfiDebuggerSymbolTypeMax, +} EFI_DEBUGGER_SYMBOL_TYPE; + +typedef struct { + CHAR8 Name[EFI_DEBUGGER_SYMBOL_NAME_MAX]; + UINTN Rva; + EFI_DEBUGGER_SYMBOL_TYPE Type; + CHAR8 ObjName[EFI_DEBUGGER_SYMBOL_NAME_MAX]; + CHAR8 *CodBuffer; + UINTN CodBufferSize; + UINTN FuncOffsetBase; + CHAR8 *SourceBuffer; +} EFI_DEBUGGER_SYMBOL_ENTRY; + +typedef struct { + CHAR16 Name[EFI_DEBUGGER_SYMBOL_NAME_MAX]; + UINTN EntryCount; + UINTN MaxEntryCount; + UINTN BaseAddress; + UINTN StartEntrypointRVA; + UINTN MainEntrypointRVA; + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + VOID **SourceBuffer; +} EFI_DEBUGGER_SYMBOL_OBJECT; + +typedef struct { + UINTN ObjectCount; + UINTN MaxObjectCount; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + BOOLEAN DisplaySymbol; + BOOLEAN DisplayCodeOnly; +} EFI_DEBUGGER_SYMBOL_CONTEXT; + +// +// Definition for Debugger Breakpoint +// +#define EFI_DEBUGGER_BREAKPOINT_MAX 0x10 + +typedef struct { + EFI_PHYSICAL_ADDRESS BreakpointAddress; + UINT64 OldInstruction; // UINT64 is enough for an instruction + BOOLEAN State; +} EFI_DEBUGGER_BREAKPOINT_CONTEXT; + +// +// Definition for Debugger Call-Stack +// +#define EFI_DEBUGGER_CALLSTACK_MAX 0x10 + +typedef enum { + EfiDebuggerBranchTypeEbcCall, + EfiDebuggerBranchTypeEbcCallEx, + EfiDebuggerBranchTypeEbcRet, + EfiDebuggerBranchTypeEbcJmp, + EfiDebuggerBranchTypeEbcJmp8, + EfiDebuggerBranchTypeEbcMax, +} EFI_DEBUGGER_BRANCH_TYPE; + +#define EFI_DEBUGGER_CALL_MAX_PARAMETER 0x16 +#define EFI_DEBUGGER_CALL_DEFAULT_PARAMETER 0x8 + +typedef struct { + EFI_PHYSICAL_ADDRESS SourceAddress; + EFI_PHYSICAL_ADDRESS DestAddress; + // + // We save all parameter here, because code may update the parameter as local variable. + // + UINTN ParameterAddr; + UINTN Parameter[EFI_DEBUGGER_CALL_MAX_PARAMETER]; + EFI_DEBUGGER_BRANCH_TYPE Type; +} EFI_DEBUGGER_CALLSTACK_CONTEXT; + +// +// Definition for Debugger Trace +// +#define EFI_DEBUGGER_TRACE_MAX 0x10 + +typedef struct { + EFI_PHYSICAL_ADDRESS SourceAddress; + EFI_PHYSICAL_ADDRESS DestAddress; + EFI_DEBUGGER_BRANCH_TYPE Type; +} EFI_DEBUGGER_TRACE_CONTEXT; + +// +// Definition for Debugger Step +// +typedef struct { + EFI_PHYSICAL_ADDRESS BreakAddress; + EFI_PHYSICAL_ADDRESS FramePointer; +} EFI_DEBUGGER_STEP_CONTEXT; + +// +// Definition for Debugger GoTil +// +typedef struct { + EFI_PHYSICAL_ADDRESS BreakAddress; +} EFI_DEBUGGER_GOTIL_CONTEXT; + +// +// Definition for Debugger private data structure +// +#define EFI_DEBUGGER_SIGNATURE SIGNATURE_32 ('e', 'd', 'b', '!') + +#define EFI_DEBUG_DEFAULT_INSTRUCTION_NUMBER 5 + +#define EFI_DEBUG_BREAK_TIMER_INTERVAL 10000000 // 1 second + +#define EFI_DEBUG_FLAG_EBC 0x80000000 +#define EFI_DEBUG_FLAG_EBC_B_BOC 0x1 +#define EFI_DEBUG_FLAG_EBC_B_BOCX 0x2 +#define EFI_DEBUG_FLAG_EBC_B_BOR 0x4 +#define EFI_DEBUG_FLAG_EBC_B_BOE 0x8 +#define EFI_DEBUG_FLAG_EBC_B_BOT 0x10 +#define EFI_DEBUG_FLAG_EBC_B_STEPOVER 0x20 +#define EFI_DEBUG_FLAG_EBC_B_STEPOUT 0x40 +#define EFI_DEBUG_FLAG_EBC_B_BP 0x80 +#define EFI_DEBUG_FLAG_EBC_B_GT 0x100 +#define EFI_DEBUG_FLAG_EBC_B_BOK 0x200 +#define EFI_DEBUG_FLAG_EBC_BOC (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_BOC) +#define EFI_DEBUG_FLAG_EBC_BOCX (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_BOCX) +#define EFI_DEBUG_FLAG_EBC_BOR (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_BOR) +#define EFI_DEBUG_FLAG_EBC_BOE (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_BOE) +#define EFI_DEBUG_FLAG_EBC_BOT (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_BOT) +#define EFI_DEBUG_FLAG_EBC_STEPOVER (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_STEPOVER) +#define EFI_DEBUG_FLAG_EBC_STEPOUT (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_STEPOUT) +#define EFI_DEBUG_FLAG_EBC_BP (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_BP) +#define EFI_DEBUG_FLAG_EBC_GT (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_GT) +#define EFI_DEBUG_FLAG_EBC_BOK (EFI_DEBUG_FLAG_EBC | EFI_DEBUG_FLAG_EBC_B_BOK) + +// +// Debugger private data structure +// +typedef struct _EFI_DEBUGGER_PRIVATE_DATA { + UINT32 Signature; + EFI_INSTRUCTION_SET_ARCHITECTURE Isa; + UINT32 EfiDebuggerRevision; + UINT32 EbcVmRevision; + EFI_DEBUGGER_CONFIGURATION_PROTOCOL DebuggerConfiguration; + EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *DebugImageInfoTableHeader; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Vol; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + EFI_DEBUGGER_COMMAND_SET *DebuggerCommandSet; + EFI_DEBUGGER_SYMBOL_CONTEXT DebuggerSymbolContext; + UINTN DebuggerBreakpointCount; + EFI_DEBUGGER_BREAKPOINT_CONTEXT DebuggerBreakpointContext[EFI_DEBUGGER_BREAKPOINT_MAX + 1]; + UINTN CallStackEntryCount; + EFI_DEBUGGER_CALLSTACK_CONTEXT CallStackEntry[EFI_DEBUGGER_CALLSTACK_MAX + 1]; + UINTN TraceEntryCount; + EFI_DEBUGGER_TRACE_CONTEXT TraceEntry[EFI_DEBUGGER_TRACE_MAX + 1]; + EFI_DEBUGGER_STEP_CONTEXT StepContext; + EFI_DEBUGGER_GOTIL_CONTEXT GoTilContext; + EFI_PHYSICAL_ADDRESS InstructionScope; + UINTN InstructionNumber; + UINT32 FeatureFlags; + UINT32 StatusFlags; + BOOLEAN EnablePageBreak; + EFI_EVENT BreakEvent; +} EFI_DEBUGGER_PRIVATE_DATA; + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.c new file mode 100644 index 000000000..7d933cae7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.c @@ -0,0 +1,1770 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +// +// Debugger Disasm definition +// +#define EDB_DISASM_DEFINE(func) \ +UINTN \ +func ( \ + IN EFI_PHYSICAL_ADDRESS InstructionAddress, \ + IN EFI_SYSTEM_CONTEXT SystemContext, \ + OUT CHAR16 **DisasmString \ + ) + +EDB_DISASM_DEFINE (EdbDisasmBREAK); +EDB_DISASM_DEFINE (EdbDisasmJMP); +EDB_DISASM_DEFINE (EdbDisasmJMP8); +EDB_DISASM_DEFINE (EdbDisasmCALL); +EDB_DISASM_DEFINE (EdbDisasmRET); +EDB_DISASM_DEFINE (EdbDisasmCMP); +EDB_DISASM_DEFINE (EdbDisasmUnsignedDataManip); +EDB_DISASM_DEFINE (EdbDisasmSignedDataManip); +EDB_DISASM_DEFINE (EdbDisasmMOVxx); +EDB_DISASM_DEFINE (EdbDisasmMOVsnw); +EDB_DISASM_DEFINE (EdbDisasmMOVsnd); +EDB_DISASM_DEFINE (EdbDisasmLOADSP); +EDB_DISASM_DEFINE (EdbDisasmSTORESP); +EDB_DISASM_DEFINE (EdbDisasmPUSH); +EDB_DISASM_DEFINE (EdbDisasmPOP); +EDB_DISASM_DEFINE (EdbDisasmCMPI); +EDB_DISASM_DEFINE (EdbDisasmPUSHn); +EDB_DISASM_DEFINE (EdbDisasmPOPn); +EDB_DISASM_DEFINE (EdbDisasmMOVI); +EDB_DISASM_DEFINE (EdbDisasmMOVIn); +EDB_DISASM_DEFINE (EdbDisasmMOVREL); + +// +// Debugger Disasm Table +// +EDB_DISASM_INSTRUCTION mEdbDisasmInstructionTable[] = { + EdbDisasmBREAK, // opcode 0x00 BREAK + EdbDisasmJMP, // opcode 0x01 JMP + EdbDisasmJMP8, // opcode 0x02 JMP8 + EdbDisasmCALL, // opcode 0x03 CALL + EdbDisasmRET, // opcode 0x04 RET + EdbDisasmCMP, // opcode 0x05 CMPEQ + EdbDisasmCMP, // opcode 0x06 CMPLTE + EdbDisasmCMP, // opcode 0x07 CMPGTE + EdbDisasmCMP, // opcode 0x08 CMPULTE + EdbDisasmCMP, // opcode 0x09 CMPUGTE + EdbDisasmUnsignedDataManip, // opcode 0x0A NOT + EdbDisasmSignedDataManip, // opcode 0x0B NEG + EdbDisasmSignedDataManip, // opcode 0x0C ADD + EdbDisasmSignedDataManip, // opcode 0x0D SUB + EdbDisasmSignedDataManip, // opcode 0x0E MUL + EdbDisasmUnsignedDataManip, // opcode 0x0F MULU + EdbDisasmSignedDataManip, // opcode 0x10 DIV + EdbDisasmUnsignedDataManip, // opcode 0x11 DIVU + EdbDisasmSignedDataManip, // opcode 0x12 MOD + EdbDisasmUnsignedDataManip, // opcode 0x13 MODU + EdbDisasmUnsignedDataManip, // opcode 0x14 AND + EdbDisasmUnsignedDataManip, // opcode 0x15 OR + EdbDisasmUnsignedDataManip, // opcode 0x16 XOR + EdbDisasmUnsignedDataManip, // opcode 0x17 SHL + EdbDisasmUnsignedDataManip, // opcode 0x18 SHR + EdbDisasmSignedDataManip, // opcode 0x19 ASHR + EdbDisasmUnsignedDataManip, // opcode 0x1A EXTNDB + EdbDisasmUnsignedDataManip, // opcode 0x1B EXTNDW + EdbDisasmUnsignedDataManip, // opcode 0x1C EXTNDD + EdbDisasmMOVxx, // opcode 0x1D MOVBW + EdbDisasmMOVxx, // opcode 0x1E MOVWW + EdbDisasmMOVxx, // opcode 0x1F MOVDW + EdbDisasmMOVxx, // opcode 0x20 MOVQW + EdbDisasmMOVxx, // opcode 0x21 MOVBD + EdbDisasmMOVxx, // opcode 0x22 MOVWD + EdbDisasmMOVxx, // opcode 0x23 MOVDD + EdbDisasmMOVxx, // opcode 0x24 MOVQD + EdbDisasmMOVsnw, // opcode 0x25 MOVSNW + EdbDisasmMOVsnd, // opcode 0x26 MOVSND + NULL, // opcode 0x27 + EdbDisasmMOVxx, // opcode 0x28 MOVQQ + EdbDisasmLOADSP, // opcode 0x29 LOADSP + EdbDisasmSTORESP, // opcode 0x2A STORESP + EdbDisasmPUSH, // opcode 0x2B PUSH + EdbDisasmPOP, // opcode 0x2C POP + EdbDisasmCMPI, // opcode 0x2D CMPIEQ + EdbDisasmCMPI, // opcode 0x2E CMPILTE + EdbDisasmCMPI, // opcode 0x2F CMPIGTE + EdbDisasmCMPI, // opcode 0x30 CMPIULTE + EdbDisasmCMPI, // opcode 0x31 CMPIUGTE + EdbDisasmMOVxx, // opcode 0x32 MOVNW + EdbDisasmMOVxx, // opcode 0x33 MOVND + NULL, // opcode 0x34 + EdbDisasmPUSHn, // opcode 0x35 PUSHN + EdbDisasmPOPn, // opcode 0x36 POPN + EdbDisasmMOVI, // opcode 0x37 MOVI + EdbDisasmMOVIn, // opcode 0x38 MOVIN + EdbDisasmMOVREL, // opcode 0x39 MOVREL +}; + +/** + + Disasm instruction - BREAK. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmBREAK ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_BREAK); + + if (*(UINT8 *)(UINTN)(InstructionAddress + 1) > 6) { + return 0; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"BREAK"); + EdbPrintDatan (*(UINT8 *)(UINTN)(InstructionAddress + 1)); + + EdbPostInstructionString (); + } + + return 2; +} + +extern CONST UINT8 mJMPLen[]; + +/** + + Disasm instruction - JMP. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmJMP ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT32 Data32; + UINT64 Data64; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_JMP); + + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + Size = (UINTN)mJMPLen[(Modifiers >> 6) & 0x03]; + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"JMP"); +// if (Modifiers & OPCODE_M_IMMDATA64) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + if ((Modifiers & CONDITION_M_CONDITIONAL) != 0) { + if ((Modifiers & JMP_M_CS) != 0) { + EdbPrintInstructionName (L"cs"); + } else { + EdbPrintInstructionName (L"cc"); + } + } + + InstructionAddress += 2; + if ((Modifiers & OPCODE_M_IMMDATA64) != 0) { + CopyMem (&Data64, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT64)); + if ((Modifiers & OPCODE_M_IMMDATA) != 0) { + EdbPrintData64 (Data64); + } else { + return 0; + } + } else { + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + EdbPrintRegister1 (Operands); + + if ((Operands & OPERAND_M_INDIRECT1) == 0) { + if ((Modifiers & OPCODE_M_IMMDATA) == 0) { + Data32 = 0; + } + EdbPrintImmDatan (Data32); + } else { + EdbPrintRawIndexData32 (Data32); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - JMP8. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmJMP8 ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_JMP8); + Modifiers = GET_MODIFIERS (InstructionAddress); + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"JMP8"); + if ((Modifiers & CONDITION_M_CONDITIONAL) != 0) { + if ((Modifiers & JMP_M_CS) != 0) { + EdbPrintInstructionName (L"cs"); + } else { + EdbPrintInstructionName (L"cc"); + } + } + + EdbPrintData8 (*(UINT8 *)(UINTN)(InstructionAddress + 1)); + + EdbPostInstructionString (); + } + + return 2; +} + +/** + + Disasm instruction - CALL. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmCALL ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT32 Data32; + UINT64 Data64; + UINT64 Ip; + UINTN Result; + EFI_PHYSICAL_ADDRESS SavedInstructionAddress; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_CALL); + SavedInstructionAddress = InstructionAddress; + + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + Size = (UINTN)mJMPLen[(Modifiers >> 6) & 0x03]; + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"CALL"); +// if (Modifiers & OPCODE_M_IMMDATA64) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + if ((Operands & OPERAND_M_NATIVE_CALL) != 0) { + EdbPrintInstructionName (L"EX"); + } +// if ((Operands & OPERAND_M_RELATIVE_ADDR) == 0) { +// EdbPrintInstructionName (L"a"); +// } + + InstructionAddress += 2; + if ((Modifiers & OPCODE_M_IMMDATA64) != 0) { + CopyMem (&Data64, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT64)); + Ip = Data64; + if ((Modifiers & OPCODE_M_IMMDATA) != 0) { + Result = EdbFindAndPrintSymbol ((UINTN)Ip); + if (Result == 0) { + EdbPrintData64 (Data64); + } + } else { + return 0; + } + } else { + if ((Modifiers & OPCODE_M_IMMDATA) != 0) { + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + } else { + Data32 = 0; + } + + if ((Operands & OPERAND_M_OP1) == 0) { + Ip = (UINT64)Data32; + } else { + Ip = GetRegisterValue (SystemContext, (Operands & OPERAND_M_OP1)); + } + + if ((Operands & OPERAND_M_INDIRECT1) == 0) { + if ((Operands & OPERAND_M_RELATIVE_ADDR) != 0) { + Result = EdbFindAndPrintSymbol ((UINTN)(SavedInstructionAddress + Ip + Size)); + } else { + Result = EdbFindAndPrintSymbol ((UINTN)Ip); + } + if (Result == 0) { + EdbPrintRegister1 (Operands); + if ((Modifiers & OPCODE_M_IMMDATA) != 0) { + EdbPrintImmData32 (Data32); + } + } + } else { + EdbPrintRegister1 (Operands); + if ((Modifiers & OPCODE_M_IMMDATA) != 0) { + EdbPrintRawIndexData32 (Data32); + } + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - RET. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmRET ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_RET); + + if (*(UINT8 *)(UINTN)(InstructionAddress + 1) != 0) { + return 0; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"RET"); + + EdbPostInstructionString (); + } + + return 2; +} + +/** + + Disasm instruction - CMP. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmCMP ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Opcode; + UINT8 Modifiers; + UINT8 Operands; + UINT16 Data16; + UINTN Size; + + ASSERT ( + (GET_OPCODE(InstructionAddress) == OPCODE_CMPEQ) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPLTE) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPGTE) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPULTE) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPUGTE) + ); + + Opcode = GET_OPCODE (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + if ((Modifiers & OPCODE_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"CMP"); +// if (Modifiers & OPCODE_M_64BIT) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + switch (Opcode) { + case OPCODE_CMPEQ: + EdbPrintInstructionName (L"eq"); + break; + case OPCODE_CMPLTE: + EdbPrintInstructionName (L"lte"); + break; + case OPCODE_CMPGTE: + EdbPrintInstructionName (L"gte"); + break; + case OPCODE_CMPULTE: + EdbPrintInstructionName (L"ulte"); + break; + case OPCODE_CMPUGTE: + EdbPrintInstructionName (L"ugte"); + break; + } + + EdbPrintRegister1 (Operands); + InstructionAddress += 2; + + EdbPrintComma (); + EdbPrintRegister2 (Operands); + + if ((Modifiers & OPCODE_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT2) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - Unsigned Data Manipulate. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmUnsignedDataManip ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Opcode; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + + ASSERT ( + (GET_OPCODE(InstructionAddress) == OPCODE_NOT) || + (GET_OPCODE(InstructionAddress) == OPCODE_MULU) || + (GET_OPCODE(InstructionAddress) == OPCODE_DIVU) || + (GET_OPCODE(InstructionAddress) == OPCODE_MODU) || + (GET_OPCODE(InstructionAddress) == OPCODE_AND) || + (GET_OPCODE(InstructionAddress) == OPCODE_OR) || + (GET_OPCODE(InstructionAddress) == OPCODE_XOR) || + (GET_OPCODE(InstructionAddress) == OPCODE_SHL) || + (GET_OPCODE(InstructionAddress) == OPCODE_SHR) || + (GET_OPCODE(InstructionAddress) == OPCODE_EXTNDB) || + (GET_OPCODE(InstructionAddress) == OPCODE_EXTNDW) || + (GET_OPCODE(InstructionAddress) == OPCODE_EXTNDD) + ); + + Opcode = GET_OPCODE (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + if ((Modifiers & DATAMANIP_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + switch (Opcode) { + case OPCODE_NOT: + EdbPrintInstructionName (L"NOT"); + break; + case OPCODE_MULU: + EdbPrintInstructionName (L"MULU"); + break; + case OPCODE_DIVU: + EdbPrintInstructionName (L"DIVU"); + break; + case OPCODE_MODU: + EdbPrintInstructionName (L"MODU"); + break; + case OPCODE_AND: + EdbPrintInstructionName (L"AND"); + break; + case OPCODE_OR: + EdbPrintInstructionName (L"OR"); + break; + case OPCODE_XOR: + EdbPrintInstructionName (L"XOR"); + break; + case OPCODE_SHL: + EdbPrintInstructionName (L"SHL"); + break; + case OPCODE_SHR: + EdbPrintInstructionName (L"SHR"); + break; + case OPCODE_EXTNDB: + EdbPrintInstructionName (L"EXTNDB"); + break; + case OPCODE_EXTNDW: + EdbPrintInstructionName (L"EXTNDW"); + break; + case OPCODE_EXTNDD: + EdbPrintInstructionName (L"EXTNDD"); + break; + } +// if (Modifiers & DATAMANIP_M_64) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + + EdbPrintRegister1 (Operands); + EdbPrintComma (); + EdbPrintRegister2 (Operands); + + InstructionAddress += 2; + if ((Modifiers & DATAMANIP_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT2) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - Signed Data Manipulate, + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmSignedDataManip ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Opcode; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + + ASSERT ( + (GET_OPCODE(InstructionAddress) == OPCODE_NEG) || + (GET_OPCODE(InstructionAddress) == OPCODE_ADD) || + (GET_OPCODE(InstructionAddress) == OPCODE_SUB) || + (GET_OPCODE(InstructionAddress) == OPCODE_MUL) || + (GET_OPCODE(InstructionAddress) == OPCODE_DIV) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOD) || + (GET_OPCODE(InstructionAddress) == OPCODE_ASHR) + ); + + Opcode = GET_OPCODE (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + if ((Modifiers & DATAMANIP_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + switch (Opcode) { + case OPCODE_NEG: + EdbPrintInstructionName (L"NEG"); + break; + case OPCODE_ADD: + EdbPrintInstructionName (L"ADD"); + break; + case OPCODE_SUB: + EdbPrintInstructionName (L"SUB"); + break; + case OPCODE_MUL: + EdbPrintInstructionName (L"MUL"); + break; + case OPCODE_DIV: + EdbPrintInstructionName (L"DIV"); + break; + case OPCODE_MOD: + EdbPrintInstructionName (L"MOD"); + break; + case OPCODE_ASHR: + EdbPrintInstructionName (L"ASHR"); + break; + } +// if (Modifiers & DATAMANIP_M_64) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + + EdbPrintRegister1 (Operands); + EdbPrintComma (); + EdbPrintRegister2 (Operands); + + InstructionAddress += 2; + if ((Modifiers & DATAMANIP_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT2) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - MOVxx. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmMOVxx ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Opcode; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + UINT32 Data32; + UINT64 Data64; + + ASSERT ( + (GET_OPCODE(InstructionAddress) == OPCODE_MOVBW) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVWW) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVDW) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVQW) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVBD) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVWD) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVDD) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVQD) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVQQ) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVNW) || + (GET_OPCODE(InstructionAddress) == OPCODE_MOVND) + ); + + Opcode = GET_OPCODE (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + Size = 2; + if ((Modifiers & (OPCODE_M_IMMED_OP1 | OPCODE_M_IMMED_OP2)) != 0) { + if ((Opcode <= OPCODE_MOVQW) || (Opcode == OPCODE_MOVNW)) { + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + Size += 2; + } + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + Size += 2; + } + } else if (((Opcode <= OPCODE_MOVQD) || (Opcode == OPCODE_MOVND)) != 0) { + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + Size += 4; + } + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + Size += 4; + } + } else if (Opcode == OPCODE_MOVQQ) { + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + Size += 8; + } + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + Size += 8; + } + } + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"MOV"); + switch (Opcode) { + case OPCODE_MOVBW: + EdbPrintInstructionName (L"bw"); + break; + case OPCODE_MOVWW: + EdbPrintInstructionName (L"ww"); + break; + case OPCODE_MOVDW: + EdbPrintInstructionName (L"dw"); + break; + case OPCODE_MOVQW: + EdbPrintInstructionName (L"qw"); + break; + case OPCODE_MOVBD: + EdbPrintInstructionName (L"bd"); + break; + case OPCODE_MOVWD: + EdbPrintInstructionName (L"wd"); + break; + case OPCODE_MOVDD: + EdbPrintInstructionName (L"dd"); + break; + case OPCODE_MOVQD: + EdbPrintInstructionName (L"qd"); + break; + case OPCODE_MOVQQ: + EdbPrintInstructionName (L"qq"); + break; + case OPCODE_MOVNW: + EdbPrintInstructionName (L"nw"); + break; + case OPCODE_MOVND: + EdbPrintInstructionName (L"nd"); + break; + } + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + if ((Opcode <= OPCODE_MOVQW) || (Opcode == OPCODE_MOVNW)) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + InstructionAddress += 2; + EdbPrintRawIndexData16 (Data16); + } else if ((Opcode <= OPCODE_MOVQD) || (Opcode == OPCODE_MOVND)) { + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + InstructionAddress += 4; + EdbPrintRawIndexData32 (Data32); + } else if (Opcode == OPCODE_MOVQQ) { + CopyMem (&Data64, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT64)); + InstructionAddress += 8; + EdbPrintRawIndexData64 (Data64); + } + } + + EdbPrintComma (); + EdbPrintRegister2 (Operands); + + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + if ((Opcode <= OPCODE_MOVQW) || (Opcode == OPCODE_MOVNW)) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + EdbPrintRawIndexData16 (Data16); + } else if ((Opcode <= OPCODE_MOVQD) || (Opcode == OPCODE_MOVND)) { + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + EdbPrintRawIndexData32 (Data32); + } else if (Opcode == OPCODE_MOVQQ) { + CopyMem (&Data64, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT64)); + EdbPrintRawIndexData64 (Data64); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - MOVsnw. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmMOVsnw ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_MOVSNW); + + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + Size = 2; + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + Size += 2; + } + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + Size += 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"MOVsnw"); + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + InstructionAddress += 2; + EdbPrintRawIndexData16 (Data16); + } + + EdbPrintComma (); + EdbPrintRegister2 (Operands); + + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT2) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - MOVsnd. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmMOVsnd ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT32 Data32; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_MOVSND); + + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + Size = 2; + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + Size += 4; + } + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + Size += 4; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"MOVsnd"); + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Modifiers & OPCODE_M_IMMED_OP1) != 0) { + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + InstructionAddress += 4; + EdbPrintRawIndexData32 (Data32); + } + + EdbPrintComma (); + EdbPrintRegister2 (Operands); + + if ((Modifiers & OPCODE_M_IMMED_OP2) != 0) { + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + if ((Operands & OPERAND_M_INDIRECT2) != 0) { + EdbPrintRawIndexData32 (Data32); + } else { + EdbPrintImmDatan (Data32); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - LOADSP. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmLOADSP ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Operands; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_LOADSP); + + Operands = GET_OPERANDS (InstructionAddress); + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"LOADSP"); + + EdbPrintDedicatedRegister1 (Operands); + + EdbPrintRegister2 (Operands); + + EdbPostInstructionString (); + } + + return 2; +} + +/** + + Disasm instruction - STORESP. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmSTORESP ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Operands; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_STORESP); + + Operands = GET_OPERANDS (InstructionAddress); + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"STORESP"); + + EdbPrintRegister1 (Operands); + + EdbPrintDedicatedRegister2 (Operands); + + EdbPostInstructionString (); + } + + return 2; +} + + +/** + + Disasm instruction - PUSH. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmPUSH ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_PUSH); + + Operands = GET_OPERANDS (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"PUSH"); +// if (Modifiers & PUSHPOP_M_64) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT1) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - POP. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmPOP ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_POP); + + Operands = GET_OPERANDS (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"POP"); +// if (Modifiers & PUSHPOP_M_64) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT1) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - CMPI. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmCMPI ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Opcode; + UINT8 Operands; + UINT16 Data16; + UINT32 Data32; + UINTN Size; + + ASSERT ( + (GET_OPCODE(InstructionAddress) == OPCODE_CMPIEQ) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPILTE) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPIGTE) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPIULTE) || + (GET_OPCODE(InstructionAddress) == OPCODE_CMPIUGTE) + ); + + Modifiers = GET_MODIFIERS (InstructionAddress); + Opcode = GET_OPCODE (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + + if ((Operands & 0xE0) != 0) { + return 0; + } + + Size = 2; + if ((Operands & OPERAND_M_CMPI_INDEX) != 0) { + Size += 2; + } + if ((Modifiers & OPCODE_M_CMPI32_DATA) != 0) { + Size += 4; + } else { + Size += 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"CMPI"); +// if (Modifiers & OPCODE_M_CMPI64) { +// EdbPrintInstructionName (L"64"); +// } else { +// EdbPrintInstructionName (L"32"); +// } + if ((Modifiers & OPCODE_M_CMPI32_DATA) != 0) { + EdbPrintInstructionName (L"d"); + } else { + EdbPrintInstructionName (L"w"); + } + switch (Opcode) { + case OPCODE_CMPIEQ: + EdbPrintInstructionName (L"eq"); + break; + case OPCODE_CMPILTE: + EdbPrintInstructionName (L"lte"); + break; + case OPCODE_CMPIGTE: + EdbPrintInstructionName (L"gte"); + break; + case OPCODE_CMPIULTE: + EdbPrintInstructionName (L"ulte"); + break; + case OPCODE_CMPIUGTE: + EdbPrintInstructionName (L"ugte"); + break; + } + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Operands & OPERAND_M_CMPI_INDEX) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + InstructionAddress += 2; + EdbPrintRawIndexData16 (Data16); + } + + EdbPrintComma (); + + if ((Modifiers & OPCODE_M_CMPI32_DATA) != 0) { + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + EdbPrintDatan (Data32); + } else { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + EdbPrintDatan (Data16); + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - PUSHn. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmPUSHn ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_PUSHN); + + Operands = GET_OPERANDS (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"PUSHn"); + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT1) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - POPn. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmPOPn ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_POPN); + + Operands = GET_OPERANDS (InstructionAddress); + Modifiers = GET_MODIFIERS (InstructionAddress); + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"POPn"); + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Modifiers & PUSHPOP_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + if ((Operands & OPERAND_M_INDIRECT1) != 0) { + EdbPrintRawIndexData16 (Data16); + } else { + EdbPrintImmDatan (Data16); + } + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - MOVI. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmMOVI ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + UINT32 Data32; + UINT64 Data64; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_MOVI); + + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + + if ((Operands & MOVI_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { + Size += 2; + } else if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { + Size += 4; + } else if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { + Size += 8; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"MOVI"); + switch (Operands & MOVI_M_MOVEWIDTH) { + case MOVI_MOVEWIDTH8: + EdbPrintInstructionName (L"b"); + break; + case MOVI_MOVEWIDTH16: + EdbPrintInstructionName (L"w"); + break; + case MOVI_MOVEWIDTH32: + EdbPrintInstructionName (L"d"); + break; + case MOVI_MOVEWIDTH64: + EdbPrintInstructionName (L"q"); + break; + } + switch (Modifiers & MOVI_M_DATAWIDTH) { + case MOVI_DATAWIDTH16: + EdbPrintInstructionName (L"w"); + break; + case MOVI_DATAWIDTH32: + EdbPrintInstructionName (L"d"); + break; + case MOVI_DATAWIDTH64: + EdbPrintInstructionName (L"q"); + break; + } + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Operands & MOVI_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + InstructionAddress += 2; + EdbPrintRawIndexData16 (Data16); + } + + EdbPrintComma (); + + switch (Modifiers & MOVI_M_DATAWIDTH) { + case MOVI_DATAWIDTH16: + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + EdbPrintDatan (Data16); + break; + case MOVI_DATAWIDTH32: + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + EdbPrintDatan (Data32); + break; + case MOVI_DATAWIDTH64: + CopyMem (&Data64, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT64)); + EdbPrintData64n (Data64); + break; + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - MOVIn. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmMOVIn ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + UINT32 Data32; + UINT64 Data64; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_MOVIN); + + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + + if ((Operands & MOVI_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { + Size += 2; + } else if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { + Size += 4; + } else if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { + Size += 8; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"MOVIn"); + switch (Modifiers & MOVI_M_DATAWIDTH) { + case MOVI_DATAWIDTH16: + EdbPrintInstructionName (L"w"); + break; + case MOVI_DATAWIDTH32: + EdbPrintInstructionName (L"d"); + break; + case MOVI_DATAWIDTH64: + EdbPrintInstructionName (L"q"); + break; + } + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Operands & MOVI_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + InstructionAddress += 2; + EdbPrintRawIndexData16 (Data16); + } + + EdbPrintComma (); + + switch (Modifiers & MOVI_M_DATAWIDTH) { + case MOVI_DATAWIDTH16: + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + EdbPrintRawIndexData16 (Data16); + break; + case MOVI_DATAWIDTH32: + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + EdbPrintRawIndexData32 (Data32); + break; + case MOVI_DATAWIDTH64: + CopyMem (&Data64, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT64)); + EdbPrintRawIndexData64 (Data64); + break; + } + + EdbPostInstructionString (); + } + + return Size; +} + +/** + + Disasm instruction - MOVREL. + + @param InstructionAddress - The instruction address + @param SystemContext - EBC system context. + @param DisasmString - The instruction string + + @return Instruction length + +**/ +UINTN +EdbDisasmMOVREL ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisasmString + ) +{ + UINT8 Modifiers; + UINT8 Operands; + UINTN Size; + UINT16 Data16; + UINT32 Data32; + UINT64 Data64; + UINTN Result; + EFI_PHYSICAL_ADDRESS SavedInstructionAddress; + + ASSERT (GET_OPCODE(InstructionAddress) == OPCODE_MOVREL); + SavedInstructionAddress = InstructionAddress; + + Modifiers = GET_MODIFIERS (InstructionAddress); + Operands = GET_OPERANDS (InstructionAddress); + + if ((Operands & MOVI_M_IMMDATA) != 0) { + Size = 4; + } else { + Size = 2; + } + if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { + Size += 2; + } else if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { + Size += 4; + } else if ((Modifiers & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { + Size += 8; + } else { + return 0; + } + + // + // Construct Disasm String + // + if (DisasmString != NULL) { + *DisasmString = EdbPreInstructionString (); + + EdbPrintInstructionName (L"MOVrel"); + switch (Modifiers & MOVI_M_DATAWIDTH) { + case MOVI_DATAWIDTH16: + EdbPrintInstructionName (L"w"); + break; + case MOVI_DATAWIDTH32: + EdbPrintInstructionName (L"d"); + break; + case MOVI_DATAWIDTH64: + EdbPrintInstructionName (L"q"); + break; + } + + EdbPrintRegister1 (Operands); + + InstructionAddress += 2; + if ((Operands & MOVI_M_IMMDATA) != 0) { + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + InstructionAddress += 2; + EdbPrintRawIndexData16 (Data16); + } + + EdbPrintComma (); + + switch (Modifiers & MOVI_M_DATAWIDTH) { + case MOVI_DATAWIDTH16: + CopyMem (&Data16, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT16)); + Result = EdbFindAndPrintSymbol ((UINTN)(SavedInstructionAddress + Size + (INT16)Data16)); + if (Result == 0) { + EdbPrintData16 (Data16); + } + break; + case MOVI_DATAWIDTH32: + CopyMem (&Data32, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT32)); + Result = EdbFindAndPrintSymbol ((UINTN)(SavedInstructionAddress + Size + (INT32)Data32)); + if (Result == 0) { + EdbPrintData32 (Data32); + } + break; + case MOVI_DATAWIDTH64: + CopyMem (&Data64, (VOID *)(UINTN)(InstructionAddress), sizeof(UINT64)); + if (sizeof(UINTN) == sizeof(UINT64)) { + Result = EdbFindAndPrintSymbol ((UINTN)(SavedInstructionAddress + Size + (INT64)Data64)); + } else { + Result = 0; + } + if (Result == 0) { + EdbPrintData64 (Data64); + } + break; + } + + EdbPostInstructionString (); + } + + return Size; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.h new file mode 100644 index 000000000..43fa5f4b8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasm.h @@ -0,0 +1,30 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#ifndef _EFI_EDB_DISASM_H_ +#define _EFI_EDB_DISASM_H_ + +#include + +// +// Definition for instruction OPCODE, MODIFIER, and OPERAND +// +#define GET_OPCODE(Addr) (UINT8)((*(UINT8 *)(UINTN)(Addr)) & 0x3F) +#define GET_MODIFIERS(Addr) (UINT8)((*(UINT8 *)(UINTN)(Addr)) & 0xC0) +#define GET_OPCODE_BYTE(Addr) (UINT8)(*(UINT8 *)(UINTN)(Addr)) +#define GET_OPERANDS(Addr) (UINT8)(*(UINT8 *)(UINTN)((Addr) + 1)) + +typedef +UINTN +(* EDB_DISASM_INSTRUCTION) ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN EFI_SYSTEM_CONTEXT SystemContext, + OUT CHAR16 **DisAsmString + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.c new file mode 100644 index 000000000..feb3fb121 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.c @@ -0,0 +1,1211 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +extern EDB_DISASM_INSTRUCTION mEdbDisasmInstructionTable[]; + +typedef struct { + CHAR16 Name[EDB_INSTRUCTION_NAME_MAX_LENGTH]; + CHAR16 Content[EDB_INSTRUCTION_CONTENT_MAX_LENGTH]; + CHAR16 Tail; +} EDB_INSTRUCTION_STRING; + +EDB_INSTRUCTION_STRING mInstructionString; +UINTN mInstructionNameOffset; +UINTN mInstructionContentOffset; + +/** + + Set offset for Instruction name and content. + + @param InstructionNameOffset - Instruction name offset + @param InstructionContentOffset - Instruction content offset + +**/ +VOID +EdbSetOffset ( + IN UINTN InstructionNameOffset, + IN UINTN InstructionContentOffset + ) +{ + mInstructionNameOffset = InstructionNameOffset; + mInstructionContentOffset = InstructionContentOffset; + + return ; +} + +/** + + Pre instruction string construction. + + @return Instruction string + +**/ +CHAR16 * +EdbPreInstructionString ( + VOID + ) +{ + ZeroMem (&mInstructionString, sizeof(mInstructionString)); + mInstructionNameOffset = 0; + mInstructionContentOffset = 0; + + return (CHAR16 *)&mInstructionString; +} + +/** + + Post instruction string construction. + + @return Instruction string + +**/ +CHAR16 * +EdbPostInstructionString ( + VOID + ) +{ + CHAR16 *Char; + + for (Char = (CHAR16 *)&mInstructionString; Char < &mInstructionString.Tail; Char++) { + if (*Char == 0) { + *Char = L' '; + } + } + mInstructionString.Tail = 0; + + mInstructionNameOffset = 0; + mInstructionContentOffset = 0; + + return (CHAR16 *)&mInstructionString; +} + +/** + + Get Sign, NaturalUnits, and ConstantUnits of the WORD data. + + @param Data16 - WORD data + @param NaturalUnits - Natural Units of the WORD + @param ConstantUnits - Constant Units of the WORD + + @return Sign value of WORD + +**/ +BOOLEAN +EdbGetNaturalIndex16 ( + IN UINT16 Data16, + OUT UINTN *NaturalUnits, + OUT UINTN *ConstantUnits + ) +{ + BOOLEAN Sign; + UINTN NaturalUnitBit; + + Sign = (BOOLEAN)(Data16 >> 15); + NaturalUnitBit = (UINTN)((Data16 >> 12) & 0x7); + NaturalUnitBit *= 2; + Data16 = Data16 & 0xFFF; + *NaturalUnits = (UINTN)(Data16 & ((1 << NaturalUnitBit) - 1)); + *ConstantUnits = (UINTN)((Data16 >> NaturalUnitBit) & ((1 << (12 - NaturalUnitBit)) - 1)); + + return Sign; +} + +/** + + Get Sign, NaturalUnits, and ConstantUnits of the DWORD data. + + @param Data32 - DWORD data + @param NaturalUnits - Natural Units of the DWORD + @param ConstantUnits - Constant Units of the DWORD + + @return Sign value of DWORD + +**/ +BOOLEAN +EdbGetNaturalIndex32 ( + IN UINT32 Data32, + OUT UINTN *NaturalUnits, + OUT UINTN *ConstantUnits + ) +{ + BOOLEAN Sign; + UINTN NaturalUnitBit; + + Sign = (BOOLEAN)(Data32 >> 31); + NaturalUnitBit = (UINTN)((Data32 >> 28) & 0x7); + NaturalUnitBit *= 4; + Data32 = Data32 & 0xFFFFFFF; + *NaturalUnits = (UINTN)(Data32 & ((1 << NaturalUnitBit) - 1)); + *ConstantUnits = (UINTN)((Data32 >> NaturalUnitBit) & ((1 << (28 - NaturalUnitBit)) - 1)); + + return Sign; +} + +/** + + Get Sign, NaturalUnits, and ConstantUnits of the QWORD data. + + @param Data64 - QWORD data + @param NaturalUnits - Natural Units of the QWORD + @param ConstantUnits - Constant Units of the QWORD + + @return Sign value of QWORD + +**/ +BOOLEAN +EdbGetNaturalIndex64 ( + IN UINT64 Data64, + OUT UINT64 *NaturalUnits, + OUT UINT64 *ConstantUnits + ) +{ + BOOLEAN Sign; + UINTN NaturalUnitBit; + + Sign = (BOOLEAN)RShiftU64 (Data64, 63); + NaturalUnitBit = (UINTN)(RShiftU64 (Data64, 60) & 0x7); + NaturalUnitBit *= 8; + Data64 = RShiftU64 (LShiftU64 (Data64, 4), 4); + *NaturalUnits = (UINT64)(Data64 & (LShiftU64 (1, NaturalUnitBit) - 1)); + *ConstantUnits = (UINT64)(RShiftU64 (Data64, NaturalUnitBit) & (LShiftU64 (1, (60 - NaturalUnitBit)) - 1)); + + return Sign; +} + +/** + + Get Bit Width of the value. + + @param Value - data + + @return Bit width + +**/ +UINT8 +EdbGetBitWidth ( + IN UINT64 Value + ) +{ + if (Value >= 10000000000000) { + return 14; + } else if (Value >= 1000000000000) { + return 13; + } else if (Value >= 100000000000) { + return 12; + } else if (Value >= 10000000000) { + return 11; + } else if (Value >= 1000000000) { + return 10; + } else if (Value >= 100000000) { + return 9; + } else if (Value >= 10000000) { + return 8; + } else if (Value >= 1000000) { + return 7; + } else if (Value >= 100000) { + return 6; + } else if (Value >= 10000) { + return 5; + } else if (Value >= 1000) { + return 4; + } else if (Value >= 100) { + return 3; + } else if (Value >= 10) { + return 2; + } else { + return 1; + } +} + +/** + + Print the instruction name. + + @param Name - instruction name + + @return Instruction name offset + +**/ +UINTN +EdbPrintInstructionName ( + IN CHAR16 *Name + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Name, + EDB_INSTRUCTION_NAME_MAX_SIZE, + mInstructionNameOffset, + L"%s", + Name + ); + mInstructionNameOffset += StrLen (Name); + + return mInstructionNameOffset; +} + +/** + + Print register 1 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintRegister1 ( + IN UINT8 Operands + ) +{ + if ((Operands & OPERAND_M_INDIRECT1) != 0) { + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"@" + ); + mInstructionContentOffset += 1; + } + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"R%d", + (UINTN)(Operands & OPERAND_M_OP1) + ); + mInstructionContentOffset += 2; + + return mInstructionContentOffset; +} + +/** + + Print register 2 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintRegister2 ( + IN UINT8 Operands + ) +{ + if ((Operands & OPERAND_M_INDIRECT2) != 0) { + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"@" + ); + mInstructionContentOffset += 1; + } + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"R%d", + (UINTN)((Operands & OPERAND_M_OP2) >> 4) + ); + mInstructionContentOffset += 2; + + return mInstructionContentOffset; +} + +/** + + Print dedicated register 1 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintDedicatedRegister1 ( + IN UINT8 Operands + ) +{ + switch (Operands & OPERAND_M_OP1) { + case 0: + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"[FLAGS]" + ); + mInstructionContentOffset += 7; + break; + case 1: + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"[IP]" + ); + mInstructionContentOffset += 4; + break; + } + + return mInstructionContentOffset; +} + +/** + + Print dedicated register 2 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintDedicatedRegister2 ( + IN UINT8 Operands + ) +{ + switch ((Operands & OPERAND_M_OP2) >> 4) { + case 0: + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"[FLAGS]" + ); + mInstructionContentOffset += 7; + break; + case 1: + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"[IP]" + ); + mInstructionContentOffset += 4; + break; + } + + return mInstructionContentOffset; +} + +/** + + Print the hexical UINTN index data to instruction content. + + @param Sign - Signed bit of UINTN data + @param NaturalUnits - natural units of UINTN data + @param ConstantUnits - natural units of UINTN data + + @return Instruction content offset + +**/ +UINTN +EdbPrintIndexData ( + IN BOOLEAN Sign, + IN UINTN NaturalUnits, + IN UINTN ConstantUnits + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(%s%d,%s%d)", + Sign ? L"-" : L"+", + NaturalUnits, + Sign ? L"-" : L"+", + ConstantUnits + ); + mInstructionContentOffset = mInstructionContentOffset + 5 + EdbGetBitWidth (NaturalUnits) + EdbGetBitWidth (ConstantUnits); + + return mInstructionContentOffset; +} + +/** + + Print the hexical QWORD index data to instruction content. + + @param Sign - Signed bit of QWORD data + @param NaturalUnits - natural units of QWORD data + @param ConstantUnits - natural units of QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintIndexData64 ( + IN BOOLEAN Sign, + IN UINT64 NaturalUnits, + IN UINT64 ConstantUnits + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(%s%ld,%s%ld)", + Sign ? L"-" : L"+", + NaturalUnits, + Sign ? L"-" : L"+", + ConstantUnits + ); + mInstructionContentOffset = mInstructionContentOffset + 5 + EdbGetBitWidth (NaturalUnits) + EdbGetBitWidth (ConstantUnits); + + return mInstructionContentOffset; +} + +/** + + Print the hexical WORD raw index data to instruction content. + + @param Data16 - WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintRawIndexData16 ( + IN UINT16 Data16 + ) +{ + BOOLEAN Sign; + UINTN NaturalUnits; + UINTN ConstantUnits; + UINTN Offset; + + Sign = EdbGetNaturalIndex16 (Data16, &NaturalUnits, &ConstantUnits); + Offset = EdbPrintIndexData (Sign, NaturalUnits, ConstantUnits); + + return Offset; +} + +/** + + Print the hexical DWORD raw index data to instruction content. + + @param Data32 - DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintRawIndexData32 ( + IN UINT32 Data32 + ) +{ + BOOLEAN Sign; + UINTN NaturalUnits; + UINTN ConstantUnits; + UINTN Offset; + + Sign = EdbGetNaturalIndex32 (Data32, &NaturalUnits, &ConstantUnits); + Offset = EdbPrintIndexData (Sign, NaturalUnits, ConstantUnits); + + return Offset; +} + +/** + + Print the hexical QWORD raw index data to instruction content. + + @param Data64 - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintRawIndexData64 ( + IN UINT64 Data64 + ) +{ + BOOLEAN Sign; + UINT64 NaturalUnits; + UINT64 ConstantUnits; + UINTN Offset; + + Sign = EdbGetNaturalIndex64 (Data64, &NaturalUnits, &ConstantUnits); + Offset = EdbPrintIndexData64 (Sign, NaturalUnits, ConstantUnits); + + return Offset; +} + +/** + + Print the hexical BYTE immediate data to instruction content. + + @param Data - BYTE data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData8 ( + IN UINT8 Data + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(0x%02x)", + (UINTN)Data + ); + mInstructionContentOffset += 6; + + return mInstructionContentOffset; +} + +/** + + Print the hexical WORD immediate data to instruction content. + + @param Data - WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData16 ( + IN UINT16 Data + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(0x%04x)", + (UINTN)Data + ); + mInstructionContentOffset += 8; + + return mInstructionContentOffset; +} + +/** + + Print the hexical DWORD immediate data to instruction content. + + @param Data - DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData32 ( + IN UINT32 Data + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(0x%08x)", + (UINTN)Data + ); + mInstructionContentOffset += 12; + + return mInstructionContentOffset; +} + +/** + + Print the hexical QWORD immediate data to instruction content. + + @param Data - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData64 ( + IN UINT64 Data + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(0x%016lx)", + Data + ); + mInstructionContentOffset += 20; + + return mInstructionContentOffset; +} + +/** + + Print the decimal UINTN immediate data to instruction content. + + @param Data - UINTN data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmDatan ( + IN UINTN Data + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(%d)", + (UINTN)Data + ); + mInstructionContentOffset = mInstructionContentOffset + 2 + EdbGetBitWidth (Data); + + return mInstructionContentOffset; +} + +/** + + Print the decimal QWORD immediate data to instruction content. + + @param Data64 - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData64n ( + IN UINT64 Data64 + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"(%ld)", + Data64 + ); + mInstructionContentOffset = mInstructionContentOffset + 2 + EdbGetBitWidth (Data64); + + return mInstructionContentOffset; +} + +/** + + Print the hexical BYTE to instruction content. + + @param Data8 - BYTE data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData8 ( + IN UINT8 Data8 + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"0x%02x", + (UINTN)Data8 + ); + mInstructionContentOffset += 4; + + return mInstructionContentOffset; +} + +/** + + Print the hexical WORD to instruction content. + + @param Data16 - WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData16 ( + IN UINT16 Data16 + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"0x%04x", + (UINTN)Data16 + ); + mInstructionContentOffset += 6; + + return mInstructionContentOffset; +} + +/** + + Print the hexical DWORD to instruction content. + + @param Data32 - DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData32 ( + IN UINT32 Data32 + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"0x%08x", + (UINTN)Data32 + ); + mInstructionContentOffset += 10; + + return mInstructionContentOffset; +} + +/** + + Print the hexical QWORD to instruction content. + + @param Data64 - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData64 ( + IN UINT64 Data64 + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"0x%016lx", + (UINT64)Data64 + ); + mInstructionContentOffset += 18; + + return mInstructionContentOffset; +} + +/** + + Print the decimal unsigned UINTN to instruction content. + + @param Data - unsigned UINTN data + + @return Instruction content offset + +**/ +UINTN +EdbPrintDatan ( + IN UINTN Data + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"%d", + (UINTN)Data + ); + mInstructionContentOffset = mInstructionContentOffset + EdbGetBitWidth (Data); + + return mInstructionContentOffset; +} + +/** + + Print the decimal unsigned QWORD to instruction content. + + @param Data64 - unsigned QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData64n ( + IN UINT64 Data64 + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"%ld", + Data64 + ); + mInstructionContentOffset = mInstructionContentOffset + EdbGetBitWidth (Data64); + + return mInstructionContentOffset; +} + +/** + + Print the decimal signed BYTE to instruction content. + + @param Data8 - signed BYTE data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData8s ( + IN UINT8 Data8 + ) +{ + BOOLEAN Sign; + + Sign = (BOOLEAN)(Data8 >> 7); + + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"%s%d", + Sign ? L"-" : L"+", + (UINTN)(Data8 & 0x7F) + ); + mInstructionContentOffset = mInstructionContentOffset + 1 + EdbGetBitWidth (Data8 & 0x7F); + + return mInstructionContentOffset; +} + +/** + + Print the decimal signed WORD to instruction content. + + @param Data16 - signed WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData16s ( + IN UINT16 Data16 + ) +{ + BOOLEAN Sign; + + Sign = (BOOLEAN)(Data16 >> 15); + + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"%s%d", + Sign ? L"-" : L"+", + (UINTN)(Data16 & 0x7FFF) + ); + mInstructionContentOffset = mInstructionContentOffset + 1 + EdbGetBitWidth (Data16 & 0x7FFF); + + return mInstructionContentOffset; +} + +/** + + Print the decimal signed DWORD to instruction content. + + @param Data32 - signed DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData32s ( + IN UINT32 Data32 + ) +{ + BOOLEAN Sign; + + Sign = (BOOLEAN)(Data32 >> 31); + + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"%s%d", + Sign ? L"-" : L"+", + (UINTN)(Data32 & 0x7FFFFFFF) + ); + mInstructionContentOffset = mInstructionContentOffset + 1 + EdbGetBitWidth (Data32 & 0x7FFFFFFF); + + return mInstructionContentOffset; +} + +/** + + Print the decimal signed QWORD to instruction content. + + @param Data64 - signed QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData64s ( + IN UINT64 Data64 + ) +{ + BOOLEAN Sign; + INT64 Data64s; + + Sign = (BOOLEAN)RShiftU64 (Data64, 63); + Data64s = (INT64)RShiftU64 (LShiftU64 (Data64, 1), 1); + + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"%s%ld", + Sign ? L"-" : L"+", + (UINT64)Data64s + ); + mInstructionContentOffset = mInstructionContentOffset + 1 + EdbGetBitWidth (Data64s); + + return mInstructionContentOffset; +} + +/** + + Print the comma to instruction content. + + @return Instruction content offset + +**/ +UINTN +EdbPrintComma ( + VOID + ) +{ + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L", " + ); + mInstructionContentOffset += 2; + + return mInstructionContentOffset; +} + +/** + + Find the symbol string according to address, then print it. + + @param Address - instruction address + + @retval 1 - symbol string is found and printed + @retval 0 - symbol string not found + +**/ +UINTN +EdbFindAndPrintSymbol ( + IN UINTN Address + ) +{ + CHAR8 *SymbolStr; + + SymbolStr = FindSymbolStr (Address); + if (SymbolStr != NULL) { + EDBSPrintWithOffset ( + mInstructionString.Content, + EDB_INSTRUCTION_CONTENT_MAX_SIZE, + mInstructionContentOffset, + L"[%a]", + SymbolStr + ); + return 1; + } + + return 0; +} + +/** + + Print the EBC byte code. + + @param InstructionAddress - instruction address + @param InstructionNumber - instruction number + +**/ +VOID +EdbPrintRaw ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN UINTN InstructionNumber + ) +{ + UINTN LineNumber; + UINTN ByteNumber; + UINTN LineIndex; + UINTN ByteIndex; + CHAR8 *SymbolStr; + + if (InstructionNumber == 0) { + return ; + } + + LineNumber = InstructionNumber / EDB_BYTECODE_NUMBER_IN_LINE; + ByteNumber = InstructionNumber % EDB_BYTECODE_NUMBER_IN_LINE; + if (ByteNumber == 0) { + LineNumber -= 1; + ByteNumber = EDB_BYTECODE_NUMBER_IN_LINE; + } + + // + // Print Symbol + // + SymbolStr = FindSymbolStr ((UINTN)InstructionAddress); + if (SymbolStr != NULL) { + EDBPrint (L"[%a]:\n", SymbolStr); + } + + for (LineIndex = 0; LineIndex < LineNumber; LineIndex++) { + EDBPrint (EDB_PRINT_ADDRESS_FORMAT, (UINTN)InstructionAddress); + for (ByteIndex = 0; ByteIndex < EDB_BYTECODE_NUMBER_IN_LINE; ByteIndex++) { + EDBPrint (L"%02x ", *(UINT8 *)(UINTN)InstructionAddress); + InstructionAddress += 1; + } + EDBPrint (L"\n"); + } + + EDBPrint (EDB_PRINT_ADDRESS_FORMAT, (UINTN)InstructionAddress); + for (ByteIndex = 0; ByteIndex < ByteNumber; ByteIndex++) { + EDBPrint (L"%02x ", *(UINT8 *)(UINTN)InstructionAddress); + InstructionAddress += 1; + } + for (ByteIndex = 0; ByteIndex < EDB_BYTECODE_NUMBER_IN_LINE - ByteNumber; ByteIndex++) { + EDBPrint (L" "); + } + + return ; +} + +/** + + Print the EBC asm code. + + @param DebuggerPrivate - EBC Debugger private data structure + @param SystemContext - EBC system context. + + @retval EFI_SUCCESS - show disasm successfully + +**/ +EFI_STATUS +EdbShowDisasm ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EFI_PHYSICAL_ADDRESS InstructionAddress; + UINTN InstructionNumber; + UINTN InstructionLength; + UINT8 Opcode; + CHAR16 *InstructionString; +// UINTN Result; + + InstructionAddress = DebuggerPrivate->InstructionScope; + for (InstructionNumber = 0; InstructionNumber < DebuggerPrivate->InstructionNumber; InstructionNumber++) { + + // + // Break each 0x10 instruction + // + if (((InstructionNumber % EFI_DEBUGGER_LINE_NUMBER_IN_PAGE) == 0) && + (InstructionNumber != 0)) { + if (SetPageBreak ()) { + break; + } + } + + Opcode = GET_OPCODE(InstructionAddress); + if ((Opcode < OPCODE_MAX) && (mEdbDisasmInstructionTable[Opcode] != NULL)) { + InstructionLength = mEdbDisasmInstructionTable [Opcode] (InstructionAddress, SystemContext, &InstructionString); + if (InstructionLength != 0) { + + // + // Print Source + // +// Result = EdbPrintSource ((UINTN)InstructionAddress, FALSE); + + if (!DebuggerPrivate->DebuggerSymbolContext.DisplayCodeOnly) { + + EdbPrintRaw (InstructionAddress, InstructionLength); + if (InstructionString != NULL) { + EDBPrint (L"%s\n", InstructionString); + } else { + EDBPrint (L"%s\n", L""); + } + } + + EdbPrintSource ((UINTN)InstructionAddress, TRUE); + + InstructionAddress += InstructionLength; + } else { + // + // Something wrong with OPCODE + // + EdbPrintRaw (InstructionAddress, EDB_BYTECODE_NUMBER_IN_LINE); + EDBPrint (L"%s\n", L""); + break; + } + } else { + // + // Something wrong with OPCODE + // + EdbPrintRaw (InstructionAddress, EDB_BYTECODE_NUMBER_IN_LINE); + EDBPrint (L"%s\n", L""); + break; + } + } + + return EFI_SUCCESS; +} + +/** + + Get register value according to the system context, and register index. + + @param SystemContext - EBC system context. + @param Index - EBC register index + + @return register value + +**/ +UINT64 +GetRegisterValue ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINT8 Index + ) +{ + switch (Index) { + case 0: + return SystemContext.SystemContextEbc->R0; + case 1: + return SystemContext.SystemContextEbc->R1; + case 2: + return SystemContext.SystemContextEbc->R2; + case 3: + return SystemContext.SystemContextEbc->R3; + case 4: + return SystemContext.SystemContextEbc->R4; + case 5: + return SystemContext.SystemContextEbc->R5; + case 6: + return SystemContext.SystemContextEbc->R6; + case 7: + return SystemContext.SystemContextEbc->R7; + default: + ASSERT (FALSE); + break; + } + return 0; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h new file mode 100644 index 000000000..b1e7a468e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h @@ -0,0 +1,567 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#ifndef _EFI_EDB_DISASM_SUPPORT_H_ +#define _EFI_EDB_DISASM_SUPPORT_H_ + +#include + +#define EDB_BYTECODE_NUMBER_IN_LINE 5 + +#ifdef EFI32 +#define EDB_PRINT_ADDRESS_FORMAT L"%08x: " +#else +// To use 012l instead of 016l because space is not enough +#define EDB_PRINT_ADDRESS_FORMAT L"%012lx: " +#endif + +#define OPCODE_MAX 0x40 + +#define EDB_INSTRUCTION_NAME_MAX_LENGTH 10 +#define EDB_INSTRUCTION_NAME_MAX_SIZE (EDB_INSTRUCTION_NAME_MAX_LENGTH * sizeof(CHAR16)) +#define EDB_INSTRUCTION_CONTENT_MAX_LENGTH 30 +#define EDB_INSTRUCTION_CONTENT_MAX_SIZE (EDB_INSTRUCTION_CONTENT_MAX_LENGTH * sizeof(CHAR16)) + +/** + + Set offset for Instruction name and content. + + @param InstructionNameOffset - Instruction name offset + @param InstructionContentOffset - Instruction content offset + +**/ +VOID +EdbSetOffset ( + IN UINTN InstructionNameOffset, + IN UINTN InstructionContentOffset + ); + +/** + + Pre instruction string construction. + + @return Instruction string + +**/ +CHAR16 * +EdbPreInstructionString ( + VOID + ); + +/** + + Post instruction string construction. + + @return Instruction string + +**/ +CHAR16 * +EdbPostInstructionString ( + VOID + ); + +/** + + Print the instruction name. + + @param Name - instruction name + + @return Instruction name offset + +**/ +UINTN +EdbPrintInstructionName ( + IN CHAR16 *Name + ); + +/** + + Get Sign, NaturalUnits, and ConstantUnits of the WORD data. + + @param Data16 - WORD data + @param NaturalUnits - Natural Units of the WORD + @param ConstantUnits - Constant Units of the WORD + + @return Sign value of WORD + +**/ +BOOLEAN +EdbGetNaturalIndex16 ( + IN UINT16 Data16, + OUT UINTN *NaturalUnits, + OUT UINTN *ConstantUnits + ); + +/** + + Get Sign, NaturalUnits, and ConstantUnits of the DWORD data. + + @param Data32 - DWORD data + @param NaturalUnits - Natural Units of the DWORD + @param ConstantUnits - Constant Units of the DWORD + + @return Sign value of DWORD + +**/ +BOOLEAN +EdbGetNaturalIndex32 ( + IN UINT32 Data32, + OUT UINTN *NaturalUnits, + OUT UINTN *ConstantUnits + ); + +/** + + Get Sign, NaturalUnits, and ConstantUnits of the QWORD data. + + @param Data64 - QWORD data + @param NaturalUnits - Natural Units of the QWORD + @param ConstantUnits - Constant Units of the QWORD + + @return Sign value of QWORD + +**/ +BOOLEAN +EdbGetNaturalIndex64 ( + IN UINT64 Data64, + OUT UINT64 *NaturalUnits, + OUT UINT64 *ConstantUnits + ); + +/** + + Print the hexical WORD raw index data to instruction content. + + @param Data16 - WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintRawIndexData16 ( + IN UINT16 Data16 + ); + +/** + + Print the hexical DWORD raw index data to instruction content. + + @param Data32 - DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintRawIndexData32 ( + IN UINT32 Data32 + ); + +/** + + Print the hexical QWORD raw index data to instruction content. + + @param Data64 - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintRawIndexData64 ( + IN UINT64 Data64 + ); + +/** + + Print register 1 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintRegister1 ( + IN UINT8 Operands + ); + +/** + + Print register 2 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintRegister2 ( + IN UINT8 Operands + ); + +/** + + Print dedicated register 1 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintDedicatedRegister1 ( + IN UINT8 Operands + ); + +/** + + Print dedicated register 2 in operands. + + @param Operands - instruction operands + + @return Instruction content offset + +**/ +UINTN +EdbPrintDedicatedRegister2 ( + IN UINT8 Operands + ); + +/** + + Print the hexical UINTN index data to instruction content. + + @param Sign - Signed bit of UINTN data + @param NaturalUnits - natural units of UINTN data + @param ConstantUnits - natural units of UINTN data + + @return Instruction content offset + +**/ +UINTN +EdbPrintIndexData ( + IN BOOLEAN Sign, + IN UINTN NaturalUnits, + IN UINTN ConstantUnits + ); + +/** + + Print the hexical QWORD index data to instruction content. + + @param Sign - Signed bit of QWORD data + @param NaturalUnits - natural units of QWORD data + @param ConstantUnits - natural units of QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintIndexData64 ( + IN BOOLEAN Sign, + IN UINT64 NaturalUnits, + IN UINT64 ConstantUnits + ); + +/** + + Print the hexical BYTE immediate data to instruction content. + + @param Data - BYTE data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData8 ( + IN UINT8 Data + ); + +/** + + Print the hexical WORD immediate data to instruction content. + + @param Data - WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData16 ( + IN UINT16 Data + ); + +/** + + Print the hexical DWORD immediate data to instruction content. + + @param Data - DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData32 ( + IN UINT32 Data + ); + +/** + + Print the hexical QWORD immediate data to instruction content. + + @param Data - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData64 ( + IN UINT64 Data + ); + +/** + + Print the decimal UINTN immediate data to instruction content. + + @param Data - UINTN data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmDatan ( + IN UINTN Data + ); + +/** + + Print the decimal QWORD immediate data to instruction content. + + @param Data64 - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintImmData64n ( + IN UINT64 Data64 + ); + +/** + + Print the hexical BYTE to instruction content. + + @param Data8 - BYTE data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData8 ( + IN UINT8 Data8 + ); + +/** + + Print the hexical WORD to instruction content. + + @param Data16 - WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData16 ( + IN UINT16 Data16 + ); + +/** + + Print the hexical DWORD to instruction content. + + @param Data32 - DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData32 ( + IN UINT32 Data32 + ); + +/** + + Print the hexical QWORD to instruction content. + + @param Data64 - QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData64 ( + IN UINT64 Data64 + ); + +/** + + Print the decimal unsigned UINTN to instruction content. + + @param Data - unsigned UINTN data + + @return Instruction content offset + +**/ +UINTN +EdbPrintDatan ( + IN UINTN Data + ); + +/** + + Print the decimal unsigned QWORD to instruction content. + + @param Data64 - unsigned QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData64n ( + IN UINT64 Data64 + ); + +/** + + Print the decimal signed BYTE to instruction content. + + @param Data8 - signed BYTE data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData8s ( + IN UINT8 Data8 + ); + +/** + + Print the decimal signed WORD to instruction content. + + @param Data16 - signed WORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData16s ( + IN UINT16 Data16 + ); + +/** + + Print the decimal signed DWORD to instruction content. + + @param Data32 - signed DWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData32s ( + IN UINT32 Data32 + ); + +/** + + Print the decimal signed QWORD to instruction content. + + @param Data64 - signed QWORD data + + @return Instruction content offset + +**/ +UINTN +EdbPrintData64s ( + IN UINT64 Data64 + ); + +/** + + Print the comma to instruction content. + + @return Instruction content offset + +**/ +UINTN +EdbPrintComma ( + VOID + ); + +/** + + Find the symbol string according to address, then print it. + + @param Address - instruction address + + @retval 1 - symbol string is found and printed + @retval 0 - symbol string not found + +**/ +UINTN +EdbFindAndPrintSymbol ( + IN UINTN Address + ); + +/** + + Print the EBC byte code. + + @param InstructionAddress - instruction address + @param InstructionNumber - instruction number + +**/ +VOID +EdbPrintRaw ( + IN EFI_PHYSICAL_ADDRESS InstructionAddress, + IN UINTN InstructionNumber + ); + +/** + + Print the EBC asm code. + + @param DebuggerPrivate - EBC Debugger private data structure + @param SystemContext - EBC system context. + + @retval EFI_SUCCESS - show disasm successfully + +**/ +EFI_STATUS +EdbShowDisasm ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + + Get register value according to the system context, and register index. + + @param SystemContext - EBC system context. + @param Index - EBC register index + + @return register value + +**/ +UINT64 +GetRegisterValue ( + IN EFI_SYSTEM_CONTEXT SystemContext, + IN UINT8 Index + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.c new file mode 100644 index 000000000..83257a2c2 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.c @@ -0,0 +1,833 @@ +/** @file + +Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + Check the Hook flag, and trigger exception if match. + + @param VmPtr - EbcDebuggerCheckHookFlag + @param Flag - Feature flag + +**/ +VOID +EbcDebuggerCheckHookFlag ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Flag + ) +{ + if ((mDebuggerPrivate.FeatureFlags & Flag) == Flag) { + mDebuggerPrivate.StatusFlags = Flag; + EbcDebugSignalException ( + EXCEPT_EBC_BREAKPOINT, + EXCEPTION_FLAG_NONE, + VmPtr + ); + } + return ; +} + +/** + + It will record soruce address for Callstack entry. + + @param SourceEntry - Source address + @param Type - Branch type + +**/ +VOID +EbcDebuggerPushCallstackSource ( + IN UINT64 SourceEntry, + IN EFI_DEBUGGER_BRANCH_TYPE Type + ) +{ + if (mDebuggerPrivate.CallStackEntryCount > EFI_DEBUGGER_CALLSTACK_MAX) { + ASSERT (FALSE); + mDebuggerPrivate.CallStackEntryCount = EFI_DEBUGGER_CALLSTACK_MAX; + } + // + // Record the new callstack entry + // + mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].SourceAddress = SourceEntry; + mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].Type = Type; + + // + // Do not change CallStackEntryCount + // + + return ; +} + +/** + + It will record parameter for Callstack entry. + + @param ParameterAddress - The address for the parameter + @param Type - Branch type + +**/ +VOID +EbcDebuggerPushCallstackParameter ( + IN UINT64 ParameterAddress, + IN EFI_DEBUGGER_BRANCH_TYPE Type + ) +{ + if (mDebuggerPrivate.CallStackEntryCount > EFI_DEBUGGER_CALLSTACK_MAX) { + ASSERT (FALSE); + mDebuggerPrivate.CallStackEntryCount = EFI_DEBUGGER_CALLSTACK_MAX; + } + // + // Record the new callstack parameter + // + mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].ParameterAddr = (UINTN)ParameterAddress; + CopyMem ( + mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].Parameter, + (VOID *)(UINTN)ParameterAddress, + sizeof(mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].Parameter) + ); + + // + // Do not change CallStackEntryCount + // + + return ; +} + +/** + + It will record source address for callstack entry. + + @param DestEntry - Source address + @param Type - Branch type + +**/ +VOID +EbcDebuggerPushCallstackDest ( + IN UINT64 DestEntry, + IN EFI_DEBUGGER_BRANCH_TYPE Type + ) +{ + UINTN Index; + + if (mDebuggerPrivate.CallStackEntryCount < EFI_DEBUGGER_CALLSTACK_MAX) { + // + // If there is empty entry for callstack, add it + // + ASSERT (mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].Type == Type); + mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].DestAddress = DestEntry; + mDebuggerPrivate.CallStackEntryCount ++; + } else { + // + // If there is no empty entry for callstack, throw the oldest one + // + ASSERT (mDebuggerPrivate.CallStackEntry[EFI_DEBUGGER_TRACE_MAX].Type == Type); + for (Index = 0; Index < EFI_DEBUGGER_CALLSTACK_MAX; Index++) { + CopyMem (&mDebuggerPrivate.CallStackEntry[Index], + &mDebuggerPrivate.CallStackEntry[Index + 1], + sizeof (mDebuggerPrivate.CallStackEntry[Index]) + ); + } + mDebuggerPrivate.CallStackEntry[EFI_DEBUGGER_CALLSTACK_MAX - 1].DestAddress = DestEntry; + mDebuggerPrivate.CallStackEntryCount = EFI_DEBUGGER_CALLSTACK_MAX; + } + + return ; +} + +/** + + It will throw the newest Callstack entry. + +**/ +VOID +EbcDebuggerPopCallstack ( + VOID + ) +{ + if ((mDebuggerPrivate.CallStackEntryCount > 0) && + (mDebuggerPrivate.CallStackEntryCount <= EFI_DEBUGGER_CALLSTACK_MAX)) { + // + // Throw the newest one + // + mDebuggerPrivate.CallStackEntryCount --; + mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].SourceAddress = 0; + mDebuggerPrivate.CallStackEntry[mDebuggerPrivate.CallStackEntryCount].DestAddress = 0; + } else if (mDebuggerPrivate.CallStackEntryCount == 0) { + // + // NOT assert here because it is reasonable, because when we start to build + // callstack, we do not know how many function already called. + // + } else { + ASSERT (FALSE); + } + + return ; +} + +/** + + It will record source address for trace entry. + + @param SourceEntry - Source address + @param Type - Branch type + +**/ +VOID +EbcDebuggerPushTraceSourceEntry ( + IN UINT64 SourceEntry, + IN EFI_DEBUGGER_BRANCH_TYPE Type + ) +{ + if (mDebuggerPrivate.TraceEntryCount > EFI_DEBUGGER_TRACE_MAX) { + ASSERT (FALSE); + mDebuggerPrivate.TraceEntryCount = EFI_DEBUGGER_TRACE_MAX; + } + // + // Record the new trace entry + // + mDebuggerPrivate.TraceEntry[mDebuggerPrivate.TraceEntryCount].SourceAddress = SourceEntry; + mDebuggerPrivate.TraceEntry[mDebuggerPrivate.TraceEntryCount].Type = Type; + + // + // Do not change TraceEntryCount + // + + return ; +} + +/** + + It will record destination address for trace entry. + + @param DestEntry - Destination address + @param Type - Branch type + +**/ +VOID +EbcDebuggerPushTraceDestEntry ( + IN UINT64 DestEntry, + IN EFI_DEBUGGER_BRANCH_TYPE Type + ) +{ + UINTN Index; + + if (mDebuggerPrivate.TraceEntryCount < EFI_DEBUGGER_TRACE_MAX) { + // + // If there is empty entry for trace, add it + // + ASSERT (mDebuggerPrivate.TraceEntry[mDebuggerPrivate.TraceEntryCount].Type == Type); + mDebuggerPrivate.TraceEntry[mDebuggerPrivate.TraceEntryCount].DestAddress = DestEntry; + mDebuggerPrivate.TraceEntryCount ++; + } else { + // + // If there is no empty entry for trace, throw the oldest one + // + ASSERT (mDebuggerPrivate.TraceEntry[EFI_DEBUGGER_TRACE_MAX].Type == Type); + for (Index = 0; Index < EFI_DEBUGGER_TRACE_MAX; Index++) { + mDebuggerPrivate.TraceEntry[Index] = mDebuggerPrivate.TraceEntry[Index + 1]; + } + mDebuggerPrivate.TraceEntry[EFI_DEBUGGER_CALLSTACK_MAX - 1].DestAddress = DestEntry; + mDebuggerPrivate.TraceEntryCount = EFI_DEBUGGER_TRACE_MAX; + } + + return ; +} + +/** + + It will record address for StepEntry, if STEPOVER or STEPOUT is enabled. + + @param Entry - Break Address + @param FramePtr - Break Frame pointer + @param Flag - for STEPOVER or STEPOUT + +**/ +VOID +EbcDebuggerPushStepEntry ( + IN UINT64 Entry, + IN UINT64 FramePtr, + IN UINT32 Flag + ) +{ + // + // Check StepOver + // + if ((Flag == EFI_DEBUG_FLAG_EBC_STEPOVER) && + ((mDebuggerPrivate.FeatureFlags & EFI_DEBUG_FLAG_EBC_STEPOVER) == EFI_DEBUG_FLAG_EBC_STEPOVER)) { + mDebuggerPrivate.StepContext.BreakAddress = Entry; + mDebuggerPrivate.StepContext.FramePointer = FramePtr; + mDebuggerPrivate.FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_STEPOVER; + } + // + // Check StepOut + // + if ((Flag == EFI_DEBUG_FLAG_EBC_STEPOUT) && + ((mDebuggerPrivate.FeatureFlags & EFI_DEBUG_FLAG_EBC_STEPOUT) == EFI_DEBUG_FLAG_EBC_STEPOUT)) { + mDebuggerPrivate.StepContext.BreakAddress = Entry; + mDebuggerPrivate.StepContext.FramePointer = FramePtr; + mDebuggerPrivate.FeatureFlags &= ~EFI_DEBUG_FLAG_EBC_B_STEPOUT; + } +} + + +/** + Notify the callback function when an event is triggered. + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. + +**/ +VOID +EFIAPI +EbcDebuggerBreakEventFunc ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + if ((mDebuggerPrivate.FeatureFlags & EFI_DEBUG_FLAG_EBC_BOK) != EFI_DEBUG_FLAG_EBC_BOK) { + return ; + } + + Status = gBS->CheckEvent (gST->ConIn->WaitForKey); + if (Status == EFI_SUCCESS) { + mDebuggerPrivate.StatusFlags = EFI_DEBUG_FLAG_EBC_BOK; + } +} + +/** + + The hook in InitializeEbcDriver. + It will init the EbcDebuggerPrivate data structure. + + @param Handle - The EbcDebugProtocol handle. + @param EbcDebugProtocol - The EbcDebugProtocol interface. + +**/ +VOID +EbcDebuggerHookInit ( + IN EFI_HANDLE Handle, + IN EFI_DEBUG_SUPPORT_PROTOCOL *EbcDebugProtocol + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + + + // + // Register all exception handler + // + for (Index = EXCEPT_EBC_UNDEFINED; Index <= EXCEPT_EBC_STEP; Index++) { + EbcDebugProtocol->RegisterExceptionCallback ( + EbcDebugProtocol, + 0, + NULL, + Index + ); + EbcDebugProtocol->RegisterExceptionCallback ( + EbcDebugProtocol, + 0, + EdbExceptionHandler, + Index + ); + } + + // + // Init Symbol + // + Object = AllocateZeroPool (sizeof(EFI_DEBUGGER_SYMBOL_OBJECT) * EFI_DEBUGGER_SYMBOL_OBJECT_MAX); + ASSERT (Object != NULL); + mDebuggerPrivate.DebuggerSymbolContext.Object = Object; + mDebuggerPrivate.DebuggerSymbolContext.ObjectCount = 0; + mDebuggerPrivate.DebuggerSymbolContext.MaxObjectCount = EFI_DEBUGGER_SYMBOL_OBJECT_MAX; + for (Index = 0; Index < EFI_DEBUGGER_SYMBOL_OBJECT_MAX; Index++) { + Entry = AllocateZeroPool (sizeof(EFI_DEBUGGER_SYMBOL_ENTRY) * EFI_DEBUGGER_SYMBOL_ENTRY_MAX); + ASSERT (Entry != NULL); + Object[Index].Entry = Entry; + Object[Index].MaxEntryCount = EFI_DEBUGGER_SYMBOL_ENTRY_MAX; + Object[Index].SourceBuffer = AllocateZeroPool (sizeof(VOID *) * (EFI_DEBUGGER_SYMBOL_ENTRY_MAX + 1)); + ASSERT (Object[Index].SourceBuffer != NULL); + } + + // + // locate PciRootBridgeIo + // + Status = gBS->LocateProtocol ( + &gEfiPciRootBridgeIoProtocolGuid, + NULL, + (VOID**) &mDebuggerPrivate.PciRootBridgeIo + ); + + // + // locate DebugImageInfoTable + // + Status = EfiGetSystemConfigurationTable ( + &gEfiDebugImageInfoTableGuid, + (VOID**) &mDebuggerPrivate.DebugImageInfoTableHeader + ); + + // + // Register Debugger Configuration Protocol, for config in shell + // + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEfiDebuggerConfigurationProtocolGuid, + EFI_NATIVE_INTERFACE, + &mDebuggerPrivate.DebuggerConfiguration + ); + + // + // + // Create break event + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + EbcDebuggerBreakEventFunc, + NULL, + &mDebuggerPrivate.BreakEvent + ); + if (!EFI_ERROR (Status)) { + Status = gBS->SetTimer ( + mDebuggerPrivate.BreakEvent, + TimerPeriodic, + EFI_DEBUG_BREAK_TIMER_INTERVAL + ); + } + + return ; +} + +/** + + The hook in UnloadImage for EBC Interpreter. + It clean up the environment. + +**/ +VOID +EbcDebuggerHookUnload ( + VOID + ) +{ + UINTN Index; + UINTN SubIndex; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + + // + // Close the break event + // + if (mDebuggerPrivate.BreakEvent != NULL) { + gBS->CloseEvent (mDebuggerPrivate.BreakEvent); + } + + // + // Clean up the symbol + // + Object = mDebuggerPrivate.DebuggerSymbolContext.Object; + for (Index = 0; Index < EFI_DEBUGGER_SYMBOL_OBJECT_MAX; Index++) { + // + // Clean up Entry + // + gBS->FreePool (Object[Index].Entry); + Object[Index].Entry = NULL; + Object[Index].EntryCount = 0; + // + // Clean up source buffer + // + for (SubIndex = 0; Object[Index].SourceBuffer[SubIndex] != NULL; SubIndex++) { + gBS->FreePool (Object[Index].SourceBuffer[SubIndex]); + Object[Index].SourceBuffer[SubIndex] = NULL; + } + gBS->FreePool (Object[Index].SourceBuffer); + Object[Index].SourceBuffer = NULL; + } + + // + // Clean up Object + // + gBS->FreePool (Object); + mDebuggerPrivate.DebuggerSymbolContext.Object = NULL; + mDebuggerPrivate.DebuggerSymbolContext.ObjectCount = 0; + + // + // Done + // + return ; +} + +/** + + The hook in EbcUnloadImage. + Currently do nothing here. + + @param Handle - The EbcImage handle. + +**/ +VOID +EbcDebuggerHookEbcUnloadImage ( + IN EFI_HANDLE Handle + ) +{ + return ; +} + +/** + + The hook in ExecuteEbcImageEntryPoint. + It will record the call-stack entry. (-1 means EbcImageEntryPoint call) + and trigger Exception if BOE enabled. + + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteEbcImageEntryPoint ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerPushCallstackSource ((UINT64)(UINTN)-1, EfiDebuggerBranchTypeEbcCall); + EbcDebuggerPushCallstackParameter ((UINT64)(UINTN)VmPtr->Gpr[0], EfiDebuggerBranchTypeEbcCall); + EbcDebuggerPushCallstackDest ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCall); + EbcDebuggerCheckHookFlag (VmPtr, EFI_DEBUG_FLAG_EBC_BOE); + return ; +} + +/** + + The hook in ExecuteEbcImageEntryPoint. + It will record the call-stack entry. (-2 means EbcInterpret call) + and trigger Exception if BOT enabled. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookEbcInterpret ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerPushCallstackSource ((UINT64)(UINTN)-2, EfiDebuggerBranchTypeEbcCall); + EbcDebuggerPushCallstackParameter ((UINT64)(UINTN)VmPtr->Gpr[0], EfiDebuggerBranchTypeEbcCall); + EbcDebuggerPushCallstackDest ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCall); + EbcDebuggerCheckHookFlag (VmPtr, EFI_DEBUG_FLAG_EBC_BOT); + return ; +} + +/** + + The hook in EbcExecute, before ExecuteFunction. + It will trigger Exception if GoTil, StepOver, or StepOut hit. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteStart ( + IN VM_CONTEXT *VmPtr + ) +{ + EFI_TPL CurrentTpl; + + // + // Check Ip for GoTil + // + if (mDebuggerPrivate.GoTilContext.BreakAddress == (UINT64)(UINTN)VmPtr->Ip) { + mDebuggerPrivate.StatusFlags = EFI_DEBUG_FLAG_EBC_GT; + mDebuggerPrivate.GoTilContext.BreakAddress = 0; + EbcDebugSignalException ( + EXCEPT_EBC_BREAKPOINT, + EXCEPTION_FLAG_NONE, + VmPtr + ); + mDebuggerPrivate.StatusFlags &= ~EFI_DEBUG_FLAG_EBC_B_GT; + return ; + } + // + // Check ReturnAddress for StepOver + // + if ((mDebuggerPrivate.StepContext.BreakAddress == (UINT64)(UINTN)VmPtr->Ip) && + (mDebuggerPrivate.StepContext.FramePointer == (UINT64)(UINTN)VmPtr->FramePtr)) { + mDebuggerPrivate.StatusFlags = EFI_DEBUG_FLAG_EBC_STEPOVER; + mDebuggerPrivate.StepContext.BreakAddress = 0; + mDebuggerPrivate.StepContext.FramePointer = 0; + EbcDebugSignalException ( + EXCEPT_EBC_BREAKPOINT, + EXCEPTION_FLAG_NONE, + VmPtr + ); + mDebuggerPrivate.StatusFlags &= ~EFI_DEBUG_FLAG_EBC_B_STEPOVER; + } + // + // Check FramePtr for StepOut + // + if (mDebuggerPrivate.StepContext.BreakAddress == (UINT64)(UINTN)VmPtr->FramePtr) { + mDebuggerPrivate.StatusFlags = EFI_DEBUG_FLAG_EBC_STEPOUT; + mDebuggerPrivate.StepContext.BreakAddress = 0; + mDebuggerPrivate.StepContext.FramePointer = 0; + EbcDebugSignalException ( + EXCEPT_EBC_BREAKPOINT, + EXCEPTION_FLAG_NONE, + VmPtr + ); + mDebuggerPrivate.StatusFlags &= ~EFI_DEBUG_FLAG_EBC_B_STEPOUT; + } + // + // Check Flags for BreakOnKey + // + if (mDebuggerPrivate.StatusFlags == EFI_DEBUG_FLAG_EBC_BOK) { + // + // Only break when the current TPL <= TPL_APPLICATION + // + CurrentTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + gBS->RestoreTPL (CurrentTpl); + if (CurrentTpl <= TPL_APPLICATION) { + EbcDebugSignalException ( + EXCEPT_EBC_BREAKPOINT, + EXCEPTION_FLAG_NONE, + VmPtr + ); + mDebuggerPrivate.StatusFlags &= ~EFI_DEBUG_FLAG_EBC_B_BOK; + } + } + return ; +} + +/** + + The hook in EbcExecute, after ExecuteFunction. + It will record StepOut Entry if need. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + UINTN Address; + + // + // Use FramePtr as checkpoint for StepOut + // + CopyMem (&Address, (VOID *)((UINTN)VmPtr->FramePtr), sizeof(Address)); + EbcDebuggerPushStepEntry (Address, (UINT64)(UINTN)VmPtr->FramePtr, EFI_DEBUG_FLAG_EBC_STEPOUT); + + return ; +} + +/** + + The hook in ExecuteCALL, before move IP. + It will trigger Exception if BOC enabled, + and record Callstack, and trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLStart ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerCheckHookFlag (VmPtr, EFI_DEBUG_FLAG_EBC_BOC); + EbcDebuggerPushCallstackSource ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCall); + EbcDebuggerPushCallstackParameter ((UINT64)(UINTN)VmPtr->Gpr[0], EfiDebuggerBranchTypeEbcCall); + EbcDebuggerPushTraceSourceEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCall); + return ; +} + +/** + + The hook in ExecuteCALL, after move IP. + It will record Callstack, trace information + and record StepOver/StepOut Entry if need. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT64 Address; + UINTN FramePtr; + + EbcDebuggerPushCallstackDest ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCall); + EbcDebuggerPushTraceDestEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCall); + + // + // Get Old FramePtr + // + CopyMem (&FramePtr, (VOID *)((UINTN)VmPtr->FramePtr), sizeof(FramePtr)); + + // + // Use ReturnAddress as checkpoint for StepOver + // + CopyMem (&Address, (VOID *)(UINTN)VmPtr->Gpr[0], sizeof(Address)); + EbcDebuggerPushStepEntry (Address, FramePtr, EFI_DEBUG_FLAG_EBC_STEPOVER); + + // + // Use FramePtr as checkpoint for StepOut + // + Address = 0; + CopyMem (&Address, (VOID *)(FramePtr), sizeof(UINTN)); + EbcDebuggerPushStepEntry (Address, FramePtr, EFI_DEBUG_FLAG_EBC_STEPOUT); + + return ; +} + +/** + + The hook in ExecuteCALL, before call EbcLLCALLEX. + It will trigger Exception if BOCX enabled, + and record Callstack information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEXStart ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerCheckHookFlag (VmPtr, EFI_DEBUG_FLAG_EBC_BOCX); +// EbcDebuggerPushCallstackSource ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCallEx); +// EbcDebuggerPushCallstackParameter ((UINT64)(UINTN)VmPtr->R[0], EfiDebuggerBranchTypeEbcCallEx); + EbcDebuggerPushTraceSourceEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCallEx); + return ; +} + +/** + + The hook in ExecuteCALL, after call EbcLLCALLEX. + It will record trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEXEnd ( + IN VM_CONTEXT *VmPtr + ) +{ +// EbcDebuggerPushCallstackDest ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCallEx); + EbcDebuggerPushTraceDestEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcCallEx); + return ; +} + +/** + + The hook in ExecuteRET, before move IP. + It will trigger Exception if BOR enabled, + and record Callstack, and trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookRETStart ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerCheckHookFlag (VmPtr, EFI_DEBUG_FLAG_EBC_BOR); + EbcDebuggerPopCallstack (); + EbcDebuggerPushTraceSourceEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcRet); + return ; +} + +/** + + The hook in ExecuteRET, after move IP. + It will record trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookRETEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerPushTraceDestEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcRet); + return ; +} + +/** + + The hook in ExecuteJMP, before move IP. + It will record trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMPStart ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerPushTraceSourceEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcJmp); + return ; +} + +/** + + The hook in ExecuteJMP, after move IP. + It will record trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMPEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerPushTraceDestEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcJmp); + return ; +} + +/** + + The hook in ExecuteJMP8, before move IP. + It will record trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMP8Start ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerPushTraceSourceEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcJmp8); + return ; +} + +/** + + The hook in ExecuteJMP8, after move IP. + It will record trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMP8End ( + IN VM_CONTEXT *VmPtr + ) +{ + EbcDebuggerPushTraceDestEntry ((UINT64)(UINTN)VmPtr->Ip, EfiDebuggerBranchTypeEbcJmp8); + return ; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.h new file mode 100644 index 000000000..6de50e0a7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbHook.h @@ -0,0 +1,14 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_EDB_HOOKER_H_ +#define _EFI_EDB_HOOKER_H_ + +#include +#include "EbcDebuggerHook.h" + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupport.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupport.h new file mode 100644 index 000000000..ba8b936b3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupport.h @@ -0,0 +1,477 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#ifndef _EFI_EDB_SUPPORT_H_ +#define _EFI_EDB_SUPPORT_H_ + +#include + +#define EFI_DEBUG_PROMPT_STRING L"EDB > " +#define EFI_DEBUG_PROMPT_COLUMN 5 +#define EFI_DEBUG_INPUS_BUFFER_SIZE 64 + +#define EFI_DEBUGGER_LINE_NUMBER_IN_PAGE 0x10 + +#define EFI_DEBUG_MAX_PRINT_BUFFER (80 * 4) + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +Xtoi ( + CHAR16 *Str + ); + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINT64 +EFIAPI +LXtoi ( + CHAR16 *Str + ); + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +Atoi ( + CHAR16 *Str + ); + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +AsciiXtoi ( + CHAR8 *Str + ); + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +AsciiAtoi ( + CHAR8 *Str + ); + +/** + Compare the Unicode and Ascii string pointed by String to the string pointed by String2. + + @param String - Unicode String to process + + @param String2 - Ascii string to process + + @return Return a positive integer if String is lexicall greater than String2; Zero if + the two strings are identical; and a negative interger if String is lexically + less than String2. + +**/ +INTN +EFIAPI +StrCmpUnicodeAndAscii ( + IN CHAR16 *String, + IN CHAR8 *String2 + ); + +/** + + Compare the Unicode string pointed by String to the string pointed by String2. + + @param String - Unicode String to process + @param String2 - Unicode string to process + + @return Return a positive integer if String is lexically greater than String2; Zero if + the two strings are identical; and a negative integer if String is lexically + less than String2. + +**/ +INTN +EFIAPI +StriCmp ( + IN CHAR16 *String, + IN CHAR16 *String2 + ); + +/** + + Compare the Unicode and Ascii string pointed by String to the string pointed by String2. + + @param String - Unicode String to process + @param String2 - Ascii string to process + + @return Return a positive integer if String is lexically greater than String2; Zero if + the two strings are identical; and a negative integer if String is lexically + less than String2. + +**/ +INTN +EFIAPI +StriCmpUnicodeAndAscii ( + IN CHAR16 *String, + IN CHAR8 *String2 + ); + +/** + + Verify if the string is end with the sub string. + + @param Str - The string where to search the sub string + @param SubStr - The substring. + +**/ +BOOLEAN +EFIAPI +StrEndWith ( + IN CHAR16 *Str, + IN CHAR16 *SubStr + ); + +/** + Duplicate a string. + + @param Src The string to be duplicated. + +**/ +CHAR16 * +EFIAPI +StrDuplicate ( + IN CHAR16 *Src + ); + +/** + + Find the next token after one or more specified characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNewTokenLine ( + IN CHAR16 *String, + IN CHAR16 *CharSet + ); + +/** + + Find the next token after one or more specified characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNextTokenLine ( + IN CHAR16 *CharSet + ); + +/** + + Find the next token after one specificed characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNewTokenField ( + IN CHAR16 *String, + IN CHAR16 *CharSet + ); + +/** + + Find the next token after one specificed characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNextTokenField ( + IN CHAR16 *CharSet + ); + +/** + + Patch a character to the end of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForStrTokenAfter ( + IN CHAR16 *Buffer, + IN CHAR16 Patch + ); + +/** + Patch a character at the beginning of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForStrTokenBefore ( + IN CHAR16 *Buffer, + IN CHAR16 Patch + ); + +/** + + Find the next token after one or more specified characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNewTokenLine ( + IN CHAR8 *String, + IN CHAR8 *CharSet + ); + +/** + + Find the next token after one or more specified characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNextTokenLine ( + IN CHAR8 *CharSet + ); + +/** + + Find the next token after one specificed characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNewTokenField ( + IN CHAR8 *String, + IN CHAR8 *CharSet + ); + +/** + + Find the next token after one specificed characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNextTokenField ( + IN CHAR8 *CharSet + ); + +/** + + Patch a character to the end of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForAsciiStrTokenAfter ( + IN CHAR8 *Buffer, + IN CHAR8 Patch + ); + +/** + Patch a character at the beginning of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForAsciiStrTokenBefore ( + IN CHAR8 *Buffer, + IN CHAR8 Patch + ); + +/** + + Shell Library. + Get user input. + + @param Prompt The prompt string. + @param InStr Point to the input string. + @param StrLen The max length of string user can input. + +**/ +VOID +EFIAPI +Input ( + IN CHAR16 *Prompt OPTIONAL, + OUT CHAR16 *InStr, + IN UINTN StrLen + ); + +/** + + SetPageBreak. + +**/ +BOOLEAN +EFIAPI +SetPageBreak ( + VOID + ); + +/** + Print a Unicode string to the output device. + + @param Format A Null-terminated Unicode format string. + @param ... The variable argument list that contains pointers to Null- + terminated Unicode strings to be printed + +**/ +UINTN +EFIAPI +EDBPrint ( + IN CONST CHAR16 *Format, + ... + ); + +/** + Print a Unicode string to the output buffer. + + @param Buffer A pointer to the output buffer for the produced Null-terminated + Unicode string. + @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. + @param Format A Null-terminated Unicode format string. + @param ... The variable argument list that contains pointers to Null- + terminated Unicode strings to be printed + +**/ +UINTN +EFIAPI +EDBSPrint ( + OUT CHAR16 *Buffer, + IN INTN BufferSize, + IN CONST CHAR16 *Format, + ... + ); + +/** + Print a Unicode string to the output buffer with specified offset.. + + @param Buffer A pointer to the output buffer for the produced Null-terminated + Unicode string. + @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. + @param Offset The offset of the buffer. + @param Format A Null-terminated Unicode format string. + @param ... The variable argument list that contains pointers to Null- + terminated Unicode strings to be printed + +**/ +UINTN +EFIAPI +EDBSPrintWithOffset ( + OUT CHAR16 *Buffer, + IN INTN BufferSize, + IN UINTN Offset, + IN CONST CHAR16 *Format, + ... + ); + +/** + + Read a file. + If ScanFs is FLASE, it will use DebuggerPrivate->Vol as default Fs. + If ScanFs is TRUE, it will scan all FS and check the file. + If there is only one file match the name, it will be read. + If there is more than one file match the name, it will return Error. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - The file to be read. + @param BufferSize - The file buffer size + @param Buffer - The file buffer + @param ScanFs - Need Scan all FS + + @retval EFI_SUCCESS - read file successfully + @retval EFI_NOT_FOUND - file not found + @retval EFI_NO_MAPPING - there is duplicated files found + +**/ +EFI_STATUS +EFIAPI +ReadFileToBuffer ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName, + OUT UINTN *BufferSize, + OUT VOID **Buffer, + IN BOOLEAN ScanFs + ); + +/** + + Get file name under this dir with index + + @param DebuggerPrivate - EBC Debugger private data structure + @param DirName - The dir to be read. + @param FileName - The file name pattern under this dir + @param Index - The file index under this dir + + @return File Name which match the pattern and index. + +**/ +CHAR16 * +EFIAPI +GetFileNameUnderDir ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *DirName, + IN CHAR16 *FileName, + IN OUT UINTN *Index + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportFile.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportFile.c new file mode 100644 index 000000000..ee2f8fc2f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportFile.c @@ -0,0 +1,384 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + Read a file. + + @param Vol - File System Volume + @param FileName - The file to be read. + @param BufferSize - The file buffer size + @param Buffer - The file buffer + + @retval EFI_SUCCESS - read file successfully + @retval EFI_NOT_FOUND - file not found + +**/ +EFI_STATUS +EFIAPI +ReadFileFromVol ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Vol, + IN CHAR16 *FileName, + OUT UINTN *BufferSize, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + EFI_FILE_HANDLE RootDir; + EFI_FILE_HANDLE Handle; + UINTN FileInfoSize; + EFI_FILE_INFO *FileInfo; + UINTN TempBufferSize; + VOID *TempBuffer; + + // + // Open the root directory + // + Status = Vol->OpenVolume (Vol, &RootDir); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the file + // + Status = RootDir->Open ( + RootDir, + &Handle, + FileName, + EFI_FILE_MODE_READ, + 0 + ); + if (EFI_ERROR (Status)) { + RootDir->Close (RootDir); + return Status; + } + + RootDir->Close (RootDir); + + // + // Get the file information + // + FileInfoSize = sizeof(EFI_FILE_INFO) + 1024; + + FileInfo = AllocateZeroPool (FileInfoSize); + if (FileInfo == NULL) { + Handle->Close (Handle); + return Status; + } + + Status = Handle->GetInfo ( + Handle, + &gEfiFileInfoGuid, + &FileInfoSize, + FileInfo + ); + if (EFI_ERROR (Status)) { + Handle->Close (Handle); + gBS->FreePool (FileInfo); + return Status; + } + + // + // Allocate buffer for the file data. The last CHAR16 is for L'\0' + // + TempBufferSize = (UINTN) FileInfo->FileSize + sizeof(CHAR16); + TempBuffer = AllocateZeroPool (TempBufferSize); + if (TempBuffer == NULL) { + Handle->Close (Handle); + gBS->FreePool (FileInfo); + return Status; + } + + gBS->FreePool (FileInfo); + + // + // Read the file data to the buffer + // + Status = Handle->Read ( + Handle, + &TempBufferSize, + TempBuffer + ); + if (EFI_ERROR (Status)) { + Handle->Close (Handle); + gBS->FreePool (TempBuffer); + return Status; + } + + Handle->Close (Handle); + + *BufferSize = TempBufferSize; + *Buffer = TempBuffer; + return EFI_SUCCESS; +} + +/** + + Read a file. + If ScanFs is FLASE, it will use DebuggerPrivate->Vol as default Fs. + If ScanFs is TRUE, it will scan all FS and check the file. + If there is only one file match the name, it will be read. + If there is more than one file match the name, it will return Error. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - The file to be read. + @param BufferSize - The file buffer size + @param Buffer - The file buffer + @param ScanFs - Need Scan all FS + + @retval EFI_SUCCESS - read file successfully + @retval EFI_NOT_FOUND - file not found + @retval EFI_NO_MAPPING - there is duplicated files found + +**/ +EFI_STATUS +EFIAPI +ReadFileToBuffer ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName, + OUT UINTN *BufferSize, + OUT VOID **Buffer, + IN BOOLEAN ScanFs + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Vol; + UINTN TempBufferSize; + VOID *TempBuffer; + UINTN NoHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + + // + // Check parameters + // + if ((FileName == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // not scan fs + // + if (!ScanFs) { + if (DebuggerPrivate->Vol == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Read file directly from Vol + // + return ReadFileFromVol (DebuggerPrivate->Vol, FileName, BufferSize, Buffer); + } + + // + // need scan fs + // + + // + // Get all Vol handle + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &NoHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status) && (NoHandles == 0)) { + return EFI_NOT_FOUND; + } + + // + // Walk through each Vol + // + DebuggerPrivate->Vol = NULL; + *BufferSize = 0; + *Buffer = NULL; + for (Index = 0; Index < NoHandles; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiSimpleFileSystemProtocolGuid, + (VOID**) &Vol + ); + if (EFI_ERROR(Status)) { + continue; + } + + Status = ReadFileFromVol (Vol, FileName, &TempBufferSize, &TempBuffer); + if (!EFI_ERROR (Status)) { + // + // Read file OK, check duplication + // + if (DebuggerPrivate->Vol != NULL) { + // + // Find the duplicated file + // + gBS->FreePool (TempBuffer); + gBS->FreePool (*Buffer); + EDBPrint (L"Duplicated FileName found!\n"); + return EFI_NO_MAPPING; + } else { + // + // Record value + // + DebuggerPrivate->Vol = Vol; + *BufferSize = TempBufferSize; + *Buffer = TempBuffer; + } + } + } + + // + // Scan Fs done + // + if (DebuggerPrivate->Vol == NULL) { + return EFI_NOT_FOUND; + } + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Get file name under this dir with index + + @param DebuggerPrivate - EBC Debugger private data structure + @param DirName - The dir to be read. + @param FileName - The file name pattern under this dir + @param Index - The file index under this dir + + @return File Name which match the pattern and index. + +**/ +CHAR16 * +EFIAPI +GetFileNameUnderDir ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *DirName, + IN CHAR16 *FileName, + IN OUT UINTN *Index + ) +{ + EFI_STATUS Status; + EFI_FILE_HANDLE RootDir; + EFI_FILE_HANDLE Handle; + UINTN FileInfoSize; + EFI_FILE_INFO *FileInfo; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Vol; + VOID *TempName; + UINTN FileIndex; + + if (DebuggerPrivate->Vol == NULL) { + Status = gBS->LocateProtocol ( + &gEfiSimpleFileSystemProtocolGuid, + NULL, + (VOID**) &DebuggerPrivate->Vol + ); + if (EFI_ERROR(Status)) { + return NULL; + } + } + Vol = DebuggerPrivate->Vol; + + // + // Open the root directory + // + Status = Vol->OpenVolume (Vol, &RootDir); + if (EFI_ERROR (Status)) { + return NULL; + } + + // + // Open the file + // + Status = RootDir->Open ( + RootDir, + &Handle, + DirName, + EFI_FILE_MODE_READ, + EFI_FILE_DIRECTORY + ); + if (EFI_ERROR (Status)) { + RootDir->Close (RootDir); + return NULL; + } + RootDir->Close (RootDir); + + // + // Set Dir Position + // + Status = Handle->SetPosition (Handle, 0); + if (EFI_ERROR (Status)) { + Handle->Close (Handle); + return NULL; + } + + // + // Get the file information + // + FileInfoSize = sizeof(EFI_FILE_INFO) + 1024; + + FileInfo = AllocateZeroPool (FileInfoSize); + if (FileInfo == NULL) { + Handle->Close (Handle); + return NULL; + } + + // + // Walk through each file in the directory + // + FileIndex = 0; + TempName = NULL; + while (TRUE) { + // + // Read a file entry + // + FileInfoSize = sizeof(EFI_FILE_INFO) + 1024; + + Status = Handle->Read ( + Handle, + &FileInfoSize, + FileInfo + ); + if (EFI_ERROR (Status) || (FileInfoSize == 0)) { + break; + } + + if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) { + // + // This is a file + // + + // + // Only deal with the EFI key file + // + if (!StrEndWith (FileInfo->FileName, FileName)) { + continue; + } + + if (FileIndex == *Index) { + TempName = StrDuplicate (FileInfo->FileName); + *Index = *Index + 1; + break; + } + FileIndex ++; + } + } + + // + // Free resources + // + gBS->FreePool (FileInfo); + Handle->Close (Handle); + + return TempName; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportString.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportString.c new file mode 100644 index 000000000..9679a2300 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportString.c @@ -0,0 +1,1020 @@ +/** @file + +Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +Xtoi ( + CHAR16 *Str + ) +{ + UINTN RetVal; + CHAR16 TempChar; + UINTN MaxVal; + + ASSERT (Str != NULL); + + MaxVal = (UINTN) -1 >> 4; + // + // skip preceeding white space + // + while (*Str != '\0' && *Str == ' ') { + Str += 1; + } + // + // skip preceeding zeros + // + while (*Str != '\0' && *Str == '0') { + Str += 1; + } + // + // skip preceeding white space + // + if (*Str != '\0' && (*Str == 'x' || *Str == 'X')) { + Str += 1; + } + // + // convert hex digits + // + RetVal = 0; + TempChar = *(Str++); + while (TempChar != '\0') { + if (TempChar >= 'a' && TempChar <= 'f') { + TempChar -= 'a' - 'A'; + } + + if ((TempChar >= '0' && TempChar <= '9') || (TempChar >= 'A' && TempChar <= 'F')) { + if (RetVal > MaxVal) { + return (UINTN) -1; + } + + RetVal = (RetVal << 4) | (TempChar - (TempChar >= 'A' ? 'A' - 10 : '0')); + } else { + break; + } + + TempChar = *(Str++); + } + + return RetVal; +} + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINT64 +EFIAPI +LXtoi ( + CHAR16 *Str + ) +{ + UINT64 RetVal; + CHAR16 TempChar; + UINT64 MaxVal; + + ASSERT (Str != NULL); + + MaxVal = RShiftU64 ((UINT64) -1, 4); + // + // skip preceeding white space + // + while (*Str != '\0' && *Str == ' ') { + Str += 1; + } + // + // skip preceeding zeros + // + while (*Str != '\0' && *Str == '0') { + Str += 1; + } + // + // skip preceeding white space + // + if (*Str != '\0' && (*Str == 'x' || *Str == 'X')) { + Str += 1; + } + // + // convert hex digits + // + RetVal = 0; + TempChar = *(Str++); + while (TempChar != '\0') { + if (TempChar >= 'a' && TempChar <= 'f') { + TempChar -= 'a' - 'A'; + } + + if ((TempChar >= '0' && TempChar <= '9') || (TempChar >= 'A' && TempChar <= 'F')) { + if (RetVal > MaxVal) { + return (UINT64) -1; + } + + RetVal = LShiftU64 (RetVal, 4); + RetVal = RetVal + (TempChar - (TempChar >= 'A' ? 'A' - 10 : '0')); + } else { + break; + } + + TempChar = *(Str++); + } + + return RetVal; +} + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +Atoi ( + CHAR16 *Str + ) +{ + UINTN RetVal; + CHAR16 TempChar; + UINTN MaxVal; + UINTN ResteVal; + + ASSERT (Str != NULL); + + MaxVal = (UINTN) -1 / 10; + ResteVal = (UINTN) -1 % 10; + // + // skip preceeding white space + // + while (*Str != '\0' && *Str == ' ') { + Str += 1; + } + // + // convert digits + // + RetVal = 0; + TempChar = *(Str++); + while (TempChar != '\0') { + if (TempChar >= '0' && TempChar <= '9') { + if (RetVal > MaxVal || (RetVal == MaxVal && TempChar - '0' > (INTN) ResteVal)) { + return (UINTN) -1; + } + + RetVal = (RetVal * 10) + TempChar - '0'; + } else { + break; + } + + TempChar = *(Str++); + } + + return RetVal; +} + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +AsciiXtoi ( + CHAR8 *Str + ) +{ + UINTN RetVal; + CHAR8 TempChar; + UINTN MaxVal; + + ASSERT (Str != NULL); + + MaxVal = (UINTN) -1 >> 4; + // + // skip preceeding white space + // + while (*Str != '\0' && *Str == ' ') { + Str += 1; + } + // + // skip preceeding zeros + // + while (*Str != '\0' && *Str == '0') { + Str += 1; + } + // + // skip preceeding white space + // + if (*Str != '\0' && (*Str == 'x' || *Str == 'X')) { + Str += 1; + } + // + // convert hex digits + // + RetVal = 0; + TempChar = *(Str++); + while (TempChar != '\0') { + if (TempChar >= 'a' && TempChar <= 'f') { + TempChar -= 'a' - 'A'; + } + + if ((TempChar >= '0' && TempChar <= '9') || (TempChar >= 'A' && TempChar <= 'F')) { + if (RetVal > MaxVal) { + return (UINTN) -1; + } + + RetVal = (RetVal << 4) | (TempChar - (TempChar >= 'A' ? 'A' - 10 : '0')); + } else { + break; + } + + TempChar = *(Str++); + } + + return RetVal; +} + +/** + + Convert hex string to uint. + + @param Str - The string + +**/ +UINTN +EFIAPI +AsciiAtoi ( + CHAR8 *Str + ) +{ + UINTN RetVal; + CHAR8 TempChar; + UINTN MaxVal; + UINTN ResteVal; + + ASSERT (Str != NULL); + + MaxVal = (UINTN) -1 / 10; + ResteVal = (UINTN) -1 % 10; + // + // skip preceeding white space + // + while (*Str != '\0' && *Str == ' ') { + Str += 1; + } + // + // convert digits + // + RetVal = 0; + TempChar = *(Str++); + while (TempChar != '\0') { + if (TempChar >= '0' && TempChar <= '9') { + if (RetVal > MaxVal || (RetVal == MaxVal && TempChar - '0' > (INTN) ResteVal)) { + return (UINTN) -1; + } + + RetVal = (RetVal * 10) + TempChar - '0'; + } else { + break; + } + + TempChar = *(Str++); + } + + return RetVal; +} + + +/** + Compare the Unicode and Ascii string pointed by String to the string pointed by String2. + + @param String - Unicode String to process + + @param String2 - Ascii string to process + + @return Return a positive integer if String is lexicall greater than String2; Zero if + the two strings are identical; and a negative interger if String is lexically + less than String2. + +**/ +INTN +EFIAPI +StrCmpUnicodeAndAscii ( + IN CHAR16 *String, + IN CHAR8 *String2 + ) +{ + while (*String != '\0') { + if (*String != (CHAR16)*String2) { + break; + } + + String += 1; + String2 += 1; + } + + return (*String - (CHAR16)*String2); +} + +/** + + Compare the Unicode string pointed by String to the string pointed by String2. + + @param String - Unicode String to process + @param String2 - Unicode string to process + + @return Return a positive integer if String is lexically greater than String2; Zero if + the two strings are identical; and a negative integer if String is lexically + less than String2. + +**/ +INTN +EFIAPI +StriCmp ( + IN CHAR16 *String, + IN CHAR16 *String2 + ) +{ + while ((*String != L'\0') && + (CharToUpper (*String) == CharToUpper (*String2))) { + String++; + String2++; + } + + return CharToUpper (*String) - CharToUpper (*String2); +} + +/** + + Compare the Unicode and Ascii string pointed by String to the string pointed by String2. + + @param String - Unicode String to process + @param String2 - Ascii string to process + + @return Return a positive integer if String is lexically greater than String2; Zero if + the two strings are identical; and a negative integer if String is lexically + less than String2. + +**/ +INTN +EFIAPI +StriCmpUnicodeAndAscii ( + IN CHAR16 *String, + IN CHAR8 *String2 + ) +{ + while ((*String != L'\0') && + (CharToUpper (*String) == (CHAR16)AsciiCharToUpper (*String2))) { + String++; + String2++; + } + + return CharToUpper (*String) - (CHAR16)AsciiCharToUpper (*String2); +} + +/** + + Verify if the string is end with the sub string. + + @param Str - The string where to search the sub string + @param SubStr - The substring. + +**/ +BOOLEAN +EFIAPI +StrEndWith ( + IN CHAR16 *Str, + IN CHAR16 *SubStr + ) +{ + CHAR16 *Temp; + + if ((Str == NULL) || (SubStr == NULL) || (StrLen(Str) < StrLen(SubStr))) { + return FALSE; + } + + Temp = Str + StrLen(Str) - StrLen(SubStr); + + // + // Compare + // + if (StriCmp (Temp, SubStr) == 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Duplicate a string. + + @param Src The string to be duplicated. + +**/ +CHAR16 * +EFIAPI +StrDuplicate ( + IN CHAR16 *Src + ) +{ + CHAR16 *Dest; + UINTN Size; + + Size = (StrLen(Src) + 1) * sizeof(CHAR16); + Dest = AllocateZeroPool (Size); + if (Dest != NULL) { + CopyMem (Dest, Src, Size); + } + return Dest; +} + + +CHAR16 *mLineBuffer = NULL; +CHAR16 *mFieldBuffer = NULL; + +/** + + Find the first substring. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +UINTN +EFIAPI +StrSpn ( + IN CHAR16 *String, + IN CHAR16 *CharSet + ) +{ + UINTN Count; + CHAR16 *Str1; + CHAR16 *Str2; + + Count = 0; + + for (Str1 = String; *Str1 != L'\0'; Str1 ++) { + for (Str2 = CharSet; *Str2 != L'\0'; Str2 ++) { + if (*Str1 == *Str2) { + break; + } + } + + if (*Str2 == L'\0') { + return Count; + } + + Count ++; + } + + return Count; +} + +/** + + Searches a string for the first occurrence of a character contained in a + specified buffer. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrBrk ( + IN CHAR16 *String, + IN CHAR16 *CharSet + ) +{ + CHAR16 *Str1; + CHAR16 *Str2; + + for (Str1 = String; *Str1 != L'\0'; Str1 ++) { + for (Str2 = CharSet; *Str2 != L'\0'; Str2 ++) { + if (*Str1 == *Str2) { + return (CHAR16 *) Str1; + } + } + } + + return NULL; +} + +/** + + Find the next token after one or more specified characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrTokenLine ( + IN CHAR16 *String OPTIONAL, + IN CHAR16 *CharSet + ) +{ + CHAR16 *Begin; + CHAR16 *End; + + Begin = (String == NULL) ? mLineBuffer : String; + if (Begin == NULL) { + return NULL; + } + + Begin += StrSpn (Begin, CharSet); + if (*Begin == L'\0') { + mLineBuffer = NULL; + return NULL; + } + + End = StrBrk (Begin, CharSet); + if ((End != NULL) && (*End != L'\0')) { + *End = L'\0'; + End ++; + } + + mLineBuffer = End; + return Begin; +} + +/** + + Find the next token after one specificed characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrTokenField ( + IN CHAR16 *String OPTIONAL, + IN CHAR16 *CharSet + ) +{ + CHAR16 *Begin; + CHAR16 *End; + + + Begin = (String == NULL) ? mFieldBuffer : String; + if (Begin == NULL) { + return NULL; + } + + if (*Begin == L'\0') { + mFieldBuffer = NULL; + return NULL; + } + + End = StrBrk (Begin, CharSet); + if ((End != NULL) && (*End != L'\0')) { + *End = L'\0'; + End ++; + } + + mFieldBuffer = End; + return Begin; +} + +/** + + Find the next token after one or more specified characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNewTokenLine ( + IN CHAR16 *String, + IN CHAR16 *CharSet + ) +{ + return StrTokenLine (String, CharSet); +} + +/** + + Find the next token after one or more specified characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNextTokenLine ( + IN CHAR16 *CharSet + ) +{ + return StrTokenLine (NULL, CharSet); +} + +/** + + Find the next token after one specificed characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNewTokenField ( + IN CHAR16 *String, + IN CHAR16 *CharSet + ) +{ + return StrTokenField (String, CharSet); +} + +/** + + Find the next token after one specificed characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR16 * +EFIAPI +StrGetNextTokenField ( + IN CHAR16 *CharSet + ) +{ + return StrTokenField (NULL, CharSet); +} + +/** + + Patch a character to the end of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForStrTokenAfter ( + IN CHAR16 *Buffer, + IN CHAR16 Patch + ) +{ + CHAR16 *Str; + + if (Buffer == NULL) { + return ; + } + + Str = Buffer; + while (*Str != 0) { + Str ++; + } + *Str = Patch; + + while (*(Str ++) != '\0') { + if (*Str == 0) { + *Str = Patch; + } else { + break; + } + } + + return ; +} + +/** + Patch a character at the beginning of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForStrTokenBefore ( + IN CHAR16 *Buffer, + IN CHAR16 Patch + ) +{ + CHAR16 *Str; + + if (Buffer == NULL) { + return ; + } + + Str = Buffer; + while (*(Str --) != '\0') { + if ((*Str == 0) || (*Str == Patch)) { + *Str = Patch; + } else { + break; + } + } + + return ; +} + +CHAR8 *mAsciiLineBuffer = NULL; +CHAR8 *mAsciiFieldBuffer = NULL; + +/** + + Find the first substring. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +UINTN +EFIAPI +AsciiStrSpn ( + IN CHAR8 *String, + IN CHAR8 *CharSet + ) +{ + UINTN Count; + CHAR8 *Str1; + CHAR8 *Str2; + + Count = 0; + + for (Str1 = String; *Str1 != '\0'; Str1 ++) { + for (Str2 = CharSet; *Str2 != '\0'; Str2 ++) { + if (*Str1 == *Str2) { + break; + } + } + + if (*Str2 == '\0') { + return Count; + } + + Count ++; + } + + return Count; +} + +/** + Searches a string for the first occurrence of a character contained in a + specified buffer. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrBrk ( + IN CHAR8 *String, + IN CHAR8 *CharSet + ) +{ + CHAR8 *Str1; + CHAR8 *Str2; + + for (Str1 = String; *Str1 != '\0'; Str1 ++) { + for (Str2 = CharSet; *Str2 != '\0'; Str2 ++) { + if (*Str1 == *Str2) { + return (CHAR8 *) Str1; + } + } + } + + return NULL; +} + +/** + + Find the next token after one or more specified characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrTokenLine ( + IN CHAR8 *String OPTIONAL, + IN CHAR8 *CharSet + ) +{ + CHAR8 *Begin; + CHAR8 *End; + + Begin = (String == NULL) ? mAsciiLineBuffer : String; + if (Begin == NULL) { + return NULL; + } + + Begin += AsciiStrSpn (Begin, CharSet); + if (*Begin == '\0') { + mAsciiLineBuffer = NULL; + return NULL; + } + + End = AsciiStrBrk (Begin, CharSet); + if ((End != NULL) && (*End != '\0')) { + *End = '\0'; + End ++; + } + + mAsciiLineBuffer = End; + return Begin; +} + +/** + + Find the next token after one specificed characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrTokenField ( + IN CHAR8 *String OPTIONAL, + IN CHAR8 *CharSet + ) +{ + CHAR8 *Begin; + CHAR8 *End; + + + Begin = (String == NULL) ? mAsciiFieldBuffer : String; + if (Begin == NULL) { + return NULL; + } + + if (*Begin == '\0') { + mAsciiFieldBuffer = NULL; + return NULL; + } + + End = AsciiStrBrk (Begin, CharSet); + if ((End != NULL) && (*End != '\0')) { + *End = '\0'; + End ++; + } + + mAsciiFieldBuffer = End; + return Begin; +} + +/** + + Find the next token after one or more specified characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNewTokenLine ( + IN CHAR8 *String, + IN CHAR8 *CharSet + ) +{ + return AsciiStrTokenLine (String, CharSet); +} + +/** + + Find the next token after one or more specified characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNextTokenLine ( + IN CHAR8 *CharSet + ) +{ + return AsciiStrTokenLine (NULL, CharSet); +} + +/** + + Find the next token after one specificed characters. + + @param String Point to the string where to find the substring. + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNewTokenField ( + IN CHAR8 *String, + IN CHAR8 *CharSet + ) +{ + return AsciiStrTokenField (String, CharSet); +} + +/** + + Find the next token after one specificed characters. + + @param CharSet Point to the string to be found. + +**/ +CHAR8 * +EFIAPI +AsciiStrGetNextTokenField ( + IN CHAR8 *CharSet + ) +{ + return AsciiStrTokenField (NULL, CharSet); +} + +/** + + Patch a character to the end of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForAsciiStrTokenAfter ( + IN CHAR8 *Buffer, + IN CHAR8 Patch + ) +{ + CHAR8 *Str; + + if (Buffer == NULL) { + return ; + } + + Str = Buffer; + while (*Str != 0) { + Str ++; + } + *Str = Patch; + + while (*(Str ++) != '\0') { + if (*Str == 0) { + *Str = Patch; + } else { + break; + } + } + + return ; +} + +/** + Patch a character at the beginning of a string. + + @param Buffer The string to be patched. + @param Patch The patch character. + +**/ +VOID +EFIAPI +PatchForAsciiStrTokenBefore ( + IN CHAR8 *Buffer, + IN CHAR8 Patch + ) +{ + CHAR8 *Str; + + if (Buffer == NULL) { + return ; + } + + Str = Buffer; + while (*(Str --) != '\0') { + if ((*Str == 0) || (*Str == Patch)) { + *Str = Patch; + } else { + break; + } + } + + return ; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportUI.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportUI.c new file mode 100644 index 000000000..dd881a021 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSupportUI.c @@ -0,0 +1,754 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + Set the current coordinates of the cursor position. + + @param ConOut Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. + @param Column The position to set the cursor to. + @param Row The position to set the cursor to. + @param LineLength Length of a line. + @param TotalRow Total row of a screen. + @param Str Point to the string. + @param StrPos The position of the string. + @param Len The length of the string. + +**/ +VOID +EFIAPI +SetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, + IN UINTN Column, + IN INTN Row, + IN UINTN LineLength, + IN UINTN TotalRow, + IN CHAR16 *Str, + IN UINTN StrPos, + IN UINTN Len + ); + +/** + + Function waits for a given event to fire, or for an optional timeout to expire. + + @param Event - The event to wait for + @param Timeout - An optional timeout value in 100 ns units. + + @retval EFI_SUCCESS - Event fired before Timeout expired. + @retval EFI_TIME_OUT - Timout expired before Event fired.. + +**/ +EFI_STATUS +EFIAPI +WaitForSingleEvent ( + IN EFI_EVENT Event, + IN UINT64 Timeout OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_EVENT TimerEvent; + EFI_EVENT WaitList[2]; + + if (Timeout != 0) { + // + // Create a timer event + // + Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent); + if (!EFI_ERROR (Status)) { + // + // Set the timer event + // + gBS->SetTimer ( + TimerEvent, + TimerRelative, + Timeout + ); + + // + // Wait for the original event or the timer + // + WaitList[0] = Event; + WaitList[1] = TimerEvent; + Status = gBS->WaitForEvent (2, WaitList, &Index); + gBS->CloseEvent (TimerEvent); + + // + // If the timer expired, change the return to timed out + // + if (!EFI_ERROR (Status) && Index == 1) { + Status = EFI_TIMEOUT; + } + } + } else { + // + // No timeout... just wait on the event + // + Status = gBS->WaitForEvent (1, &Event, &Index); + ASSERT (!EFI_ERROR (Status)); + ASSERT (Index == 0); + } + + return Status; +} + +/** + + Move the cursor position one character backward. + + @param LineLength Length of a line. Get it by calling QueryMode + @param Column Current column of the cursor position + @param Row Current row of the cursor position + +**/ +VOID +EFIAPI +ConMoveCursorBackward ( + IN UINTN LineLength, + IN OUT UINTN *Column, + IN OUT UINTN *Row + ) +{ + ASSERT (Column != NULL); + ASSERT (Row != NULL); + // + // If current column is 0, move to the last column of the previous line, + // otherwise, just decrement column. + // + if (*Column == 0) { + (*Column) = LineLength - 1; + // + // if (*Row > 0) { + // + (*Row)--; + // + // } + // + } else { + (*Column)--; + } +} + +/** + + Move the cursor position one character backward. + + @param LineLength Length of a line. Get it by calling QueryMode + @param TotalRow Total row of a screen, get by calling QueryMode + @param Column Current column of the cursor position + @param Row Current row of the cursor position + +**/ +VOID +EFIAPI +ConMoveCursorForward ( + IN UINTN LineLength, + IN UINTN TotalRow, + IN OUT UINTN *Column, + IN OUT UINTN *Row + ) +{ + ASSERT (Column != NULL); + ASSERT (Row != NULL); + // + // If current column is at line end, move to the first column of the nest + // line, otherwise, just increment column. + // + (*Column)++; + if (*Column >= LineLength) { + (*Column) = 0; + if ((*Row) < TotalRow - 1) { + (*Row)++; + } + } +} + +CHAR16 mBackupSpace[EFI_DEBUG_INPUS_BUFFER_SIZE]; +CHAR16 mInputBufferHistory[EFI_DEBUG_INPUS_BUFFER_SIZE]; + +/** + + Get user input. + + @param Prompt The prompt string. + @param InStr Point to the input string. + @param StrLength The max length of string user can input. + +**/ +VOID +EFIAPI +Input ( + IN CHAR16 *Prompt OPTIONAL, + OUT CHAR16 *InStr, + IN UINTN StrLength + ) +{ + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; + BOOLEAN Done; + UINTN Column; + UINTN Row; + UINTN StartColumn; + UINTN Update; + UINTN Delete; + UINTN Len; + UINTN StrPos; + UINTN Index; + UINTN LineLength; + UINTN TotalRow; + UINTN SkipLength; + UINTN OutputLength; + UINTN TailRow; + UINTN TailColumn; + EFI_INPUT_KEY Key; + BOOLEAN InsertMode; + BOOLEAN NeedAdjust; + UINTN SubIndex; + CHAR16 *CommandStr; + + ConOut = gST->ConOut; + ConIn = gST->ConIn; + + ASSERT (ConOut != NULL); + ASSERT (ConIn != NULL); + ASSERT (InStr != NULL); + + if (Prompt != NULL) { + ConOut->OutputString (ConOut, Prompt); + } + // + // Read a line from the console + // + Len = 0; + StrPos = 0; + OutputLength = 0; + Update = 0; + Delete = 0; + InsertMode = TRUE; + NeedAdjust = FALSE; + + // + // If buffer is not large enough to hold a CHAR16, do nothing. + // + if (StrLength < 1) { + return ; + } + // + // Get the screen setting and the current cursor location + // + StartColumn = ConOut->Mode->CursorColumn; + Column = StartColumn; + Row = ConOut->Mode->CursorRow; + ConOut->QueryMode (ConOut, ConOut->Mode->Mode, &LineLength, &TotalRow); + if (LineLength == 0) { + return ; + } + + SetMem (InStr, StrLength * sizeof (CHAR16), 0); + Done = FALSE; + do { + // + // Read a key + // + WaitForSingleEvent (ConIn->WaitForKey, 0); + ConIn->ReadKeyStroke (ConIn, &Key); + + switch (Key.UnicodeChar) { + case CHAR_CARRIAGE_RETURN: + // + // All done, print a newline at the end of the string + // + TailRow = Row + (Len - StrPos + Column) / LineLength; + TailColumn = (Len - StrPos + Column) % LineLength; + Done = TRUE; + break; + + case CHAR_BACKSPACE: + if (StrPos != 0) { + // + // If not move back beyond string beginning, move all characters behind + // the current position one character forward + // + StrPos -= 1; + Update = StrPos; + Delete = 1; + CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos)); + + // + // Adjust the current column and row + // + ConMoveCursorBackward (LineLength, &Column, &Row); + + NeedAdjust = TRUE; + } + break; + + default: + if (Key.UnicodeChar >= ' ') { + // + // If we are at the buffer's end, drop the key + // + if (Len == StrLength - 1 && (InsertMode || StrPos == Len)) { + break; + } + // + // If in insert mode, move all characters behind the current position + // one character backward to make space for this character. Then store + // the character. + // + if (InsertMode) { + for (Index = Len; Index > StrPos; Index -= 1) { + InStr[Index] = InStr[Index - 1]; + } + } + + InStr[StrPos] = Key.UnicodeChar; + Update = StrPos; + + StrPos += 1; + OutputLength = 1; + } + break; + + case 0: + switch (Key.ScanCode) { + case SCAN_DELETE: + // + // Move characters behind current position one character forward + // + if (Len != 0) { + Update = StrPos; + Delete = 1; + CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos)); + + NeedAdjust = TRUE; + } + break; + + case SCAN_LEFT: + // + // Adjust current cursor position + // + if (StrPos != 0) { + StrPos -= 1; + ConMoveCursorBackward (LineLength, &Column, &Row); + } + break; + + case SCAN_RIGHT: + // + // Adjust current cursor position + // + if (StrPos < Len) { + StrPos += 1; + ConMoveCursorForward (LineLength, TotalRow, &Column, &Row); + } + break; + + case SCAN_HOME: + // + // Move current cursor position to the beginning of the command line + // + Row -= (StrPos + StartColumn) / LineLength; + Column = StartColumn; + StrPos = 0; + break; + + case SCAN_END: + // + // Move current cursor position to the end of the command line + // + TailRow = Row + (Len - StrPos + Column) / LineLength; + TailColumn = (Len - StrPos + Column) % LineLength; + Row = TailRow; + Column = TailColumn; + StrPos = Len; + break; + + case SCAN_ESC: + // + // Prepare to clear the current command line + // + InStr[0] = 0; + Update = 0; + Delete = Len; + Row -= (StrPos + StartColumn) / LineLength; + Column = StartColumn; + OutputLength = 0; + + NeedAdjust = TRUE; + break; + + case SCAN_INSERT: + // + // Toggle the SEnvInsertMode flag + // + InsertMode = (BOOLEAN)!InsertMode; + break; + + case SCAN_UP: + case SCAN_DOWN: + // + // show history + // + CopyMem (InStr, mInputBufferHistory, StrLength * sizeof(CHAR16)); + StrPos = StrLen (mInputBufferHistory); + Update = 0; + Delete = 0; + OutputLength = 0; + + TailRow = Row + (StrPos + StartColumn) / LineLength; + TailColumn = (StrPos + StartColumn) % LineLength; + Row = TailRow; + Column = TailColumn; + NeedAdjust = FALSE; + + ConOut->SetCursorPosition (ConOut, StartColumn, Row); + for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) { + mBackupSpace[SubIndex] = L' '; + } + EDBPrint (mBackupSpace); + SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0); + + ConOut->SetCursorPosition (ConOut, StartColumn, Row); + Len = StrPos; + + break; + + case SCAN_F1: + case SCAN_F2: + case SCAN_F3: + case SCAN_F4: + case SCAN_F5: + case SCAN_F6: + case SCAN_F7: + case SCAN_F8: + case SCAN_F9: + case SCAN_F10: + case SCAN_F11: + case SCAN_F12: + CommandStr = GetCommandNameByKey (Key); + if (CommandStr != NULL) { + StrnCpyS (InStr, StrLength, CommandStr, StrLength - 1); + return ; + } + break; + } + } + + if (Done) { + break; + } + // + // If we need to update the output do so now + // + if (Update != -1) { + if (NeedAdjust) { + ConOut->SetCursorPosition (ConOut, Column, Row); + for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) { + mBackupSpace[SubIndex] = L' '; + } + EDBPrint (mBackupSpace); + SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0); + ConOut->SetCursorPosition (ConOut, Column, Row); + NeedAdjust = FALSE; + } + EDBPrint (InStr + Update); + Len = StrLen (InStr); + + if (Delete != 0) { + SetMem (InStr + Len, Delete * sizeof (CHAR16), 0x00); + } + + if (StrPos > Len) { + StrPos = Len; + } + + Update = (UINTN) -1; + + // + // After using print to reflect newly updates, if we're not using + // BACKSPACE and DELETE, we need to move the cursor position forward, + // so adjust row and column here. + // + if (Key.UnicodeChar != CHAR_BACKSPACE && !(Key.UnicodeChar == 0 && Key.ScanCode == SCAN_DELETE)) { + // + // Calulate row and column of the tail of current string + // + TailRow = Row + (Len - StrPos + Column + OutputLength) / LineLength; + TailColumn = (Len - StrPos + Column + OutputLength) % LineLength; + + // + // If the tail of string reaches screen end, screen rolls up, so if + // Row does not equal TailRow, Row should be decremented + // + // (if we are recalling commands using UPPER and DOWN key, and if the + // old command is too long to fit the screen, TailColumn must be 79. + // + if (TailColumn == 0 && TailRow >= TotalRow && (UINTN) Row != TailRow) { + Row--; + } + // + // Calculate the cursor position after current operation. If cursor + // reaches line end, update both row and column, otherwise, only + // column will be changed. + // + if (Column + OutputLength >= LineLength) { + SkipLength = OutputLength - (LineLength - Column); + + Row += SkipLength / LineLength + 1; + if ((UINTN) Row > TotalRow - 1) { + Row = TotalRow - 1; + } + + Column = SkipLength % LineLength; + } else { + Column += OutputLength; + } + } + + Delete = 0; + } + // + // Set the cursor position for this key + // + SetCursorPosition (ConOut, Column, Row, LineLength, TotalRow, InStr, StrPos, Len); + } while (!Done); + + CopyMem (mInputBufferHistory, InStr, StrLength * sizeof(CHAR16)); + + // + // Return the data to the caller + // + return ; +} + +/** + Set the current coordinates of the cursor position. + + @param ConOut Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. + @param Column The position to set the cursor to. + @param Row The position to set the cursor to. + @param LineLength Length of a line. + @param TotalRow Total row of a screen. + @param Str Point to the string. + @param StrPos The position of the string. + @param Len The length of the string. + +**/ +VOID +EFIAPI +SetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, + IN UINTN Column, + IN INTN Row, + IN UINTN LineLength, + IN UINTN TotalRow, + IN CHAR16 *Str, + IN UINTN StrPos, + IN UINTN Len + ) +{ + CHAR16 Backup; + + ASSERT (ConOut != NULL); + ASSERT (Str != NULL); + + Backup = 0; + if (Row >= 0) { + ConOut->SetCursorPosition (ConOut, Column, Row); + return ; + } + + if (Len - StrPos > Column * Row) { + Backup = *(Str + StrPos + Column * Row); + *(Str + StrPos + Column * Row) = 0; + } + + EDBPrint (L"%s", Str + StrPos); + if (Len - StrPos > Column * Row) { + *(Str + StrPos + Column * Row) = Backup; + } + + ConOut->SetCursorPosition (ConOut, 0, 0); +} + +/** + + SetPageBreak. + +**/ +BOOLEAN +EFIAPI +SetPageBreak ( + VOID + ) +{ + EFI_INPUT_KEY Key; + CHAR16 Str[3]; + BOOLEAN OmitPrint; + + // + // Check + // + if (!mDebuggerPrivate.EnablePageBreak) { + return FALSE; + } + + gST->ConOut->OutputString (gST->ConOut, L"Press ENTER to continue, 'q' to exit:"); + + OmitPrint = FALSE; + // + // Wait for user input + // + Str[0] = ' '; + Str[1] = 0; + Str[2] = 0; + for (;;) { + WaitForSingleEvent (gST->ConIn->WaitForKey, 0); + gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + // + // handle control keys + // + if (Key.UnicodeChar == CHAR_NULL) { + if (Key.ScanCode == SCAN_ESC) { + gST->ConOut->OutputString (gST->ConOut, L"\r\n"); + OmitPrint = TRUE; + break; + } + + continue; + } + + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + gST->ConOut->OutputString (gST->ConOut, L"\r\n"); + break; + } + // + // Echo input + // + Str[1] = Key.UnicodeChar; + if (Str[1] == CHAR_BACKSPACE) { + continue; + } + + gST->ConOut->OutputString (gST->ConOut, Str); + + if ((Str[1] == L'q') || (Str[1] == L'Q')) { + OmitPrint = TRUE; + } else { + OmitPrint = FALSE; + } + + Str[0] = CHAR_BACKSPACE; + } + + return OmitPrint; +} + +/** + Print a Unicode string to the output device. + + @param Format A Null-terminated Unicode format string. + @param ... The variable argument list that contains pointers to Null- + terminated Unicode strings to be printed + +**/ +UINTN +EFIAPI +EDBPrint ( + IN CONST CHAR16 *Format, + ... + ) +{ + UINTN Return; + VA_LIST Marker; + CHAR16 Buffer[EFI_DEBUG_MAX_PRINT_BUFFER]; + + VA_START (Marker, Format); + Return = UnicodeVSPrint (Buffer, sizeof (Buffer), Format, Marker); + VA_END (Marker); + + if (gST->ConOut != NULL) { + // + // To be extra safe make sure ConOut has been initialized + // + gST->ConOut->OutputString (gST->ConOut, Buffer); + } + + return Return; +} + +/** + Print a Unicode string to the output buffer. + + @param Buffer A pointer to the output buffer for the produced Null-terminated + Unicode string. + @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. + @param Format A Null-terminated Unicode format string. + @param ... The variable argument list that contains pointers to Null- + terminated Unicode strings to be printed + +**/ +UINTN +EFIAPI +EDBSPrint ( + OUT CHAR16 *Buffer, + IN INTN BufferSize, + IN CONST CHAR16 *Format, + ... + ) +{ + UINTN Return; + VA_LIST Marker; + + ASSERT (BufferSize > 0); + + VA_START (Marker, Format); + Return = UnicodeVSPrint (Buffer, (UINTN)BufferSize, Format, Marker); + VA_END (Marker); + + return Return; +} + +/** + Print a Unicode string to the output buffer with specified offset.. + + @param Buffer A pointer to the output buffer for the produced Null-terminated + Unicode string. + @param BufferSize The size, in bytes, of the output buffer specified by StartOfBuffer. + @param Offset The offset of the buffer. + @param Format A Null-terminated Unicode format string. + @param ... The variable argument list that contains pointers to Null- + terminated Unicode strings to be printed + +**/ +UINTN +EFIAPI +EDBSPrintWithOffset ( + OUT CHAR16 *Buffer, + IN INTN BufferSize, + IN UINTN Offset, + IN CONST CHAR16 *Format, + ... + ) +{ + UINTN Return; + VA_LIST Marker; + + ASSERT (BufferSize - (Offset * sizeof(CHAR16)) > 0); + + VA_START (Marker, Format); + Return = UnicodeVSPrint (Buffer + Offset, (UINTN)(BufferSize - (Offset * sizeof(CHAR16))), Format, Marker); + VA_END (Marker); + + return Return; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.c new file mode 100644 index 000000000..90a9b9fbd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.c @@ -0,0 +1,2230 @@ +/** @file + +Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#include "Edb.h" + +/** + + Load single symbol entry. + + @param Object - Symbol file object + @param Name - Symbol name + @param ObjName - Object name + @param Address - Symbol address + @param Type - Symbol type + + @retval EFI_SUCCESS - add single symbol entry successfully + +**/ +EFI_STATUS +EdbLoadSymbolSingleEntry ( + IN EFI_DEBUGGER_SYMBOL_OBJECT *Object, + IN CHAR8 *Name, + IN CHAR8 *ObjName, + IN UINTN Address, + IN EFI_DEBUGGER_SYMBOL_TYPE Type + ) +{ + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + + // + // Check Count VS MaxCount + // + if (Object->EntryCount >= Object->MaxEntryCount) { + // + // reallocate (for codebuffer too) + // TBD + // + return EFI_OUT_OF_RESOURCES; + } + + Entry = &Object->Entry[Object->EntryCount]; + + // + // Print Debug info + // + if (sizeof (UINTN) == sizeof(UINT64)) { + DEBUG ((DEBUG_ERROR, " Symbol: %a, Address: 0x%016lx (%d)\n", Name, (UINT64)Address, (UINTN)Type)); + } else { + DEBUG ((DEBUG_ERROR, " Symbol: %a, Address: 0x%08x (%d)\n", Name, Address, (UINTN)Type)); + } + + // + // Fill the entry - name, RVA, type + // + AsciiStrnCpyS (Entry->Name, sizeof(Entry->Name), Name, sizeof(Entry->Name) - 1); + if (ObjName != NULL) { + AsciiStrnCpyS (Entry->ObjName, sizeof(Entry->ObjName), ObjName, sizeof(Entry->ObjName) - 1); + } + Entry->Rva = Address % EFI_DEBUGGER_DEFAULT_LINK_IMAGEBASE; + Entry->Type = Type; + + // + // Increase Count + // + Object->EntryCount++; + + // + // Done + // + return EFI_SUCCESS; +} + +typedef enum { + EdbEbcMapParseStateUninitialized, + EdbEbcMapParseStateSymbolStart, + EdbEbcMapParseStateSeHandlerSymbol, + EdbEbcMapParseStateFunctionSymbol, + EdbEbcMapParseStateVarbssInitSymbol, + EdbEbcMapParseStateCrtSymbol, + EdbEbcMapParseStateVariableSymbol, + EdbEbcMapParseStateStaticFunctionSymbol, + EdbEbcMapParseStateMax, +} EDB_EBC_MAP_PARSE_STATE; + +typedef enum { + EdbEbcSymbolParseStateUninitialized, + EdbEbcSymbolParseStateReadyForName, + EdbEbcSymbolParseStateReadyForRVA, + EdbEbcSymbolParseStateReadyForType, + EdbEbcSymbolParseStateReadyForObject, + EdbEbcSymbolParseStateMax, +} EDB_EBC_SYMBOL_PARSE_STATE; + +/** + + The following code depends on the MAP file generated by IEC compiler (actually Microsoft linker). + + Sample as follows: EbcTest.map +=============================================================================== + EbcTest + + Timestamp is 45b02718 (Fri Jan 19 10:04:08 2007) + + Preferred load address is 10000000 + + Start Length Name Class + 0001:00000000 00000370H .text CODE + 0002:00000000 00000030H _VARBSS_INIT CODE + 0003:00000000 00000004H .CRT$TSA DATA + 0003:00000004 00000004H .CRT$TSC DATA + 0003:00000008 00000004H .CRT$X DATA + 0003:0000000c 00000008H .CRT$XCU DATA + 0003:00000014 00000004H .CRT$Z DATA + 0003:00000020 0000001cH .rdata DATA + 0003:0000003c 00000000H .edata DATA + 0003:0000003c 00000056H .rdata$debug DATA + 0004:00000000 00000070H .data DATA + 0004:00000070 00000020H .bss DATA + + Address Publics by Value Rva+Base Lib:Object + + 0000:00000000 ___safe_se_handler_table 00000000 + 0000:00000000 ___safe_se_handler_count 00000000 + 0001:00000042 TestSubRoutine 10000442 f EbcTest.obj + 0001:0000011a EfiMain 1000051a f EbcTest.obj + 0001:00000200 TestSubRoutineSub 10000600 f EbcTestSub.obj + 0001:00000220 EfiStart 10000620 f EbcLib:EbcLib.obj + 0002:00000000 varbss_init_C:\efi_src\TIANO\Edk\Sample\Universal\Ebc\Dxe\EbcTest\EbcTest$c45b02717 10000800 f EbcTest.obj + 0002:00000020 varbss_init_C:\efi_src\TIANO\Edk\Sample\Universal\Ebc\Dxe\EbcTest\EbcTestSub$c45af77f3 10000820 f EbcTestSub.obj + 0003:00000000 CrtThunkBegin 10000a00 EbcLib:EbcLib.obj + 0003:00000004 CrtThunkEnd 10000a04 EbcLib:EbcLib.obj + 0003:00000008 CrtBegin 10000a08 EbcLib:EbcLib.obj + 0003:00000014 CrtEnd 10000a14 EbcLib:EbcLib.obj + 0004:00000070 TestStr 10000c70 EbcTest.obj + 0004:00000078 TestVariable1 10000c78 EbcTest.obj + 0004:00000080 TestSubVariableSub 10000c80 EbcTestSub.obj + + entry point at 0001:00000220 + + Static symbols + + 0001:00000000 TestSubRoutine2 10000400 f EbcTest.obj +=============================================================================== + +**/ + +/** + + Load symbol entry by Iec. + + @param Object - Symbol file object + @param BufferSize - Symbol file buffer size + @param Buffer - Symbol file buffer + + @retval EFI_SUCCESS - add symbol entry successfully + +**/ +EFI_STATUS +EdbLoadSymbolEntryByIec ( + IN EFI_DEBUGGER_SYMBOL_OBJECT *Object, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + CHAR8 *LineBuffer; + CHAR8 *FieldBuffer; + EDB_EBC_MAP_PARSE_STATE MapParseState; + EDB_EBC_SYMBOL_PARSE_STATE SymbolParseState; + CHAR8 *Name; + CHAR8 *ObjName; + UINTN Address; + EFI_DEBUGGER_SYMBOL_TYPE Type; + + + // + // Begin to parse the Buffer + // + LineBuffer = AsciiStrGetNewTokenLine (Buffer, "\n\r"); + MapParseState = EdbEbcMapParseStateUninitialized; + // + // Check each line + // + while (LineBuffer != NULL) { + FieldBuffer = AsciiStrGetNewTokenField (LineBuffer, " "); + SymbolParseState = EdbEbcSymbolParseStateUninitialized; + // + // Init entry value + // + Name = NULL; + ObjName = NULL; + Address = 0; + Type = EfiDebuggerSymbolTypeMax; + // + // Check each field + // + while (FieldBuffer != NULL) { + if (AsciiStrCmp (FieldBuffer, "") == 0) { + FieldBuffer = AsciiStrGetNextTokenField (" "); + continue; + } + // + // check "Address" + // + if (AsciiStrCmp (FieldBuffer, "Address") == 0) { + MapParseState = EdbEbcMapParseStateSymbolStart; + break; + } + // + // check "Static" + // + if (AsciiStrCmp (FieldBuffer, "Static") == 0) { + MapParseState = EdbEbcMapParseStateStaticFunctionSymbol; + break; + } + + if (MapParseState == EdbEbcMapParseStateUninitialized) { + // + // Do not parse anything until get "Address" or "Static" + // + break; + } + if (AsciiStrCmp (FieldBuffer, "entry") == 0) { + // + // Skip entry point + // + break; + } + + // + // Now we start to parse this line for Name, Address, and Object + // + switch (SymbolParseState) { + case EdbEbcSymbolParseStateUninitialized: + // + // Get the Address + // + SymbolParseState = EdbEbcSymbolParseStateReadyForName; + break; + case EdbEbcSymbolParseStateReadyForName: + // + // Get the Name + // + if (AsciiStrnCmp (FieldBuffer, "___safe_se_handler", AsciiStrLen ("___safe_se_handler")) == 0) { + // + // skip SeHandler + // + MapParseState = EdbEbcMapParseStateSeHandlerSymbol; + goto ExitFieldParse; + } else if (AsciiStrnCmp (FieldBuffer, "varbss_init", AsciiStrLen ("varbss_init")) == 0) { + // + // check VarbssInit + // + MapParseState = EdbEbcMapParseStateVarbssInitSymbol; +// goto ExitFieldParse; + Name = FieldBuffer; + SymbolParseState = EdbEbcSymbolParseStateReadyForRVA; + } else if (AsciiStrnCmp (FieldBuffer, "Crt", AsciiStrLen ("Crt")) == 0) { + // + // check Crt + // + MapParseState = EdbEbcMapParseStateCrtSymbol; +// goto ExitFieldParse; + Name = FieldBuffer; + SymbolParseState = EdbEbcSymbolParseStateReadyForRVA; + } else { + // + // Now, it is normal function + // + switch (MapParseState) { + case EdbEbcMapParseStateSeHandlerSymbol: + MapParseState = EdbEbcMapParseStateFunctionSymbol; + break; + case EdbEbcMapParseStateCrtSymbol: + MapParseState = EdbEbcMapParseStateVariableSymbol; + break; + case EdbEbcMapParseStateFunctionSymbol: + case EdbEbcMapParseStateVariableSymbol: + case EdbEbcMapParseStateStaticFunctionSymbol: + break; + default: + ASSERT (FALSE); + break; + } + Name = FieldBuffer; + SymbolParseState = EdbEbcSymbolParseStateReadyForRVA; + } + break; + case EdbEbcSymbolParseStateReadyForRVA: + // + // Get the RVA + // + Address = AsciiXtoi (FieldBuffer); + SymbolParseState = EdbEbcSymbolParseStateReadyForType; + break; + case EdbEbcSymbolParseStateReadyForType: + // + // Get the Type. This is optional, only for "f". + // + if (AsciiStrCmp (FieldBuffer, "f") == 0) { + SymbolParseState = EdbEbcSymbolParseStateReadyForObject; + switch (MapParseState) { + case EdbEbcMapParseStateFunctionSymbol: + case EdbEbcMapParseStateVarbssInitSymbol: + Type = EfiDebuggerSymbolFunction; + break; + case EdbEbcMapParseStateStaticFunctionSymbol: + Type = EfiDebuggerSymbolStaticFunction; + break; + default: + ASSERT (FALSE); + break; + } + break; + } + // + // Else it should be Object. + // let it bypass here + // + case EdbEbcSymbolParseStateReadyForObject: + switch (Type) { + case EfiDebuggerSymbolTypeMax: + switch (MapParseState) { + case EdbEbcMapParseStateVariableSymbol: + case EdbEbcMapParseStateCrtSymbol: + Type = EfiDebuggerSymbolGlobalVariable; + break; + case EdbEbcMapParseStateSeHandlerSymbol: + // + // do nothing here + // + break; + default: + ASSERT (FALSE); + break; + } + break; + case EfiDebuggerSymbolFunction: + case EfiDebuggerSymbolStaticFunction: + break; + default: + ASSERT (FALSE); + break; + } + // + // Get the Object + // + ObjName = FieldBuffer; + SymbolParseState = EdbEbcSymbolParseStateUninitialized; + break; + default: + ASSERT (FALSE); + break; + } + + // + // Get the next field + // + FieldBuffer = AsciiStrGetNextTokenField (" "); + } + + // + // Add the entry if we get everything. + // + if ((Name != NULL) && (Type != EfiDebuggerSymbolTypeMax)) { + EdbLoadSymbolSingleEntry (Object, Name, ObjName, Address, Type); + } + +ExitFieldParse: + // + // Get the next line + // + LineBuffer = AsciiStrGetNextTokenLine ("\n\r"); + } + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Load symbol entry. + + @param Object - Symbol file object + @param BufferSize - Symbol file buffer size + @param Buffer - Symbol file buffer + + @retval EFI_SUCCESS - add symbol entry successfully + +**/ +EFI_STATUS +EdbLoadSymbolEntry ( + IN EFI_DEBUGGER_SYMBOL_OBJECT *Object, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + // + // MAP file format depends on the compiler (actually linker). + // + // It is possible to check the different MAP file format in this routine. + // Now only IEC is supported. + // + return EdbLoadSymbolEntryByIec (Object, BufferSize, Buffer); +} + +/** + + Find symbol file by name. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - Symbol file name + @param Index - Symbol file index + + @return Object + +**/ +EFI_DEBUGGER_SYMBOL_OBJECT * +EdbFindSymbolFile ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName, + IN OUT UINTN *Index OPTIONAL + ) +{ + UINTN ObjectIndex; + + // + // Check each Object + // + for (ObjectIndex = 0; ObjectIndex < DebuggerPrivate->DebuggerSymbolContext.ObjectCount; ObjectIndex++) { + if (StrCmp (FileName, DebuggerPrivate->DebuggerSymbolContext.Object[ObjectIndex].Name) == 0) { + // + // Name match, found it + // + if (Index != NULL) { + *Index = ObjectIndex; + } + return &DebuggerPrivate->DebuggerSymbolContext.Object[ObjectIndex]; + } + } + + // + // Not found + // + return NULL; +} + +/** + + Find symbol by address. + + @param Address - Symbol address + @param Type - Search type + @param RetObject - Symbol object + @param RetEntry - Symbol entry + + @return Nearest symbol address + +**/ +UINTN +EbdFindSymbolAddress ( + IN UINTN Address, + IN EDB_MATCH_SYMBOL_TYPE Type, + OUT EFI_DEBUGGER_SYMBOL_OBJECT **RetObject, + OUT EFI_DEBUGGER_SYMBOL_ENTRY **RetEntry + ) +{ + UINTN Index; + UINTN SubIndex; + UINTN CandidateLowerAddress; + UINTN CandidateUpperAddress; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + EFI_DEBUGGER_SYMBOL_ENTRY *LowEntry; + EFI_DEBUGGER_SYMBOL_ENTRY *UpperEntry; + EFI_DEBUGGER_SYMBOL_OBJECT *LowObject; + EFI_DEBUGGER_SYMBOL_OBJECT *UpperObject; + + if ((Type < 0) || (Type >= EdbMatchSymbolTypeMax)) { + return 0; + } + + // + // Init + // + CandidateLowerAddress = 0; + CandidateUpperAddress = (UINTN)-1; + LowEntry = NULL; + UpperEntry = NULL; + LowObject = NULL; + UpperObject = NULL; + + // + // Go through each object + // + Object = mDebuggerPrivate.DebuggerSymbolContext.Object; + for (Index = 0; Index < mDebuggerPrivate.DebuggerSymbolContext.ObjectCount; Index++, Object++) { + if (Object->EntryCount == 0) { + continue; + } + // + // Go through each entry + // + Entry = Object->Entry; + for (SubIndex = 0; SubIndex < Object->EntryCount; SubIndex++, Entry++) { + if (Address != Entry->Rva + Object->BaseAddress) { + // + // Check for nearest address + // + if (Address > Entry->Rva + Object->BaseAddress) { + // + // Record it if Current RVA < Address + // + if (CandidateLowerAddress < Entry->Rva + Object->BaseAddress) { + CandidateLowerAddress = Entry->Rva + Object->BaseAddress; + LowEntry = Entry; + LowObject = Object; + } + } else { + // + // Record it if Current RVA > Address + // + if (CandidateUpperAddress > Entry->Rva + Object->BaseAddress) { + CandidateUpperAddress = Entry->Rva + Object->BaseAddress; + UpperEntry = Entry; + UpperObject = Object; + } + } + continue; + } + // + // address match, return directly + // + *RetEntry = Entry; + *RetObject = Object; + return Address; + } + } + + // + // No Match, provide latest symbol + // + + if ((Address - CandidateLowerAddress) < EFI_DEBUGGER_MAX_SYMBOL_ADDRESS_DELTA_VALUE) { + // + // Check for lower address + // + if (((Type == EdbMatchSymbolTypeNearestAddress) && + ((CandidateUpperAddress - Address) > (Address - CandidateLowerAddress))) || + (Type == EdbMatchSymbolTypeLowerAddress)) { + // + // return nearest lower address + // + *RetEntry = LowEntry; + *RetObject = LowObject; + return CandidateLowerAddress; + } + } + + if ((CandidateUpperAddress - Address) < EFI_DEBUGGER_MAX_SYMBOL_ADDRESS_DELTA_VALUE) { + // + // Check for upper address + // + if (((Type == EdbMatchSymbolTypeNearestAddress) && + ((CandidateUpperAddress - Address) < (Address - CandidateLowerAddress))) || + (Type == EdbMatchSymbolTypeUpperAddress)) { + // + // return nearest upper address + // + *RetEntry = UpperEntry; + *RetObject = UpperObject; + return CandidateUpperAddress; + } + } + + // + // No match and nearest one, return NULL + // + return 0; +} + +/** + + Unload symbol file by name. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - Symbol file name + + @retval EFI_SUCCESS - unload symbol successfully + +**/ +EFI_STATUS +EdbUnloadSymbol ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName + ) +{ + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + UINTN ObjectIndex; + UINTN Index; + EFI_DEBUGGER_SYMBOL_ENTRY *OldEntry; + UINTN OldEntryCount; + UINTN MaxEntryCount; + VOID **OldSourceBuffer; + + // + // Find Symbol + // + Object = EdbFindSymbolFile (DebuggerPrivate, FileName, &ObjectIndex); + if (Object == NULL) { + EDBPrint (L"SymbolFile is not loaded!\n"); + return EFI_DEBUG_CONTINUE; + } + + // + // Record old data + // + Object = DebuggerPrivate->DebuggerSymbolContext.Object; + OldEntry = Object->Entry; + OldSourceBuffer = Object->SourceBuffer; + MaxEntryCount = Object->MaxEntryCount; + OldEntryCount = Object->EntryCount; + + // + // Remove the matched Object + // + for (Index = ObjectIndex; Index < DebuggerPrivate->DebuggerSymbolContext.ObjectCount - 1; Index++) { + CopyMem (&Object[Index], &Object[Index + 1], sizeof(EFI_DEBUGGER_SYMBOL_OBJECT)); + } + ZeroMem (&Object[Index], sizeof(Object[Index])); + + // + // Move old data to new place + // + Object[Index].Entry = OldEntry; + Object[Index].SourceBuffer = OldSourceBuffer; + Object[Index].MaxEntryCount = MaxEntryCount; + DebuggerPrivate->DebuggerSymbolContext.ObjectCount --; + + // + // Clean old entry data + // + for (Index = 0; Index < OldEntryCount; Index++) { + ZeroMem (&OldEntry[Index], sizeof(OldEntry[Index])); + } + + // + // Free OldSourceBuffer + // + for (Index = 0; OldSourceBuffer[Index] != NULL; Index++) { + gBS->FreePool (OldSourceBuffer[Index]); + OldSourceBuffer[Index] = NULL; + } + + return EFI_SUCCESS; +} + +/** + + Load symbol file by name. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - Symbol file name + @param BufferSize - Symbol file buffer size + @param Buffer - Symbol file buffer + + @retval EFI_SUCCESS - load symbol successfully + +**/ +EFI_STATUS +EdbLoadSymbol ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + EFI_STATUS Status; + + // + // Check duplicated File + // + Object = EdbFindSymbolFile (DebuggerPrivate, FileName, NULL); + if (Object != NULL) { + Status = EdbUnloadSymbol (DebuggerPrivate, FileName); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "Unload Duplicated Symbol File Error!\n")); + return Status; + } + } + + // + // Check Count VS MaxCount + // + if (DebuggerPrivate->DebuggerSymbolContext.ObjectCount >= DebuggerPrivate->DebuggerSymbolContext.MaxObjectCount) { + // + // reallocate + // TBD + // + return EFI_OUT_OF_RESOURCES; + } + + Object = &DebuggerPrivate->DebuggerSymbolContext.Object[DebuggerPrivate->DebuggerSymbolContext.ObjectCount]; + + // + // Init Object + // + Object->EntryCount = 0; + Object->MaxEntryCount = EFI_DEBUGGER_SYMBOL_ENTRY_MAX; + + // + // Load SymbolEntry + // + DEBUG ((DEBUG_ERROR, "Symbol File: %s\n", FileName)); + Status = EdbLoadSymbolEntry (Object, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill Object value + // + StrnCpyS (Object->Name, sizeof(Object->Name) / sizeof(CHAR16), + FileName, (sizeof(Object->Name) / sizeof(CHAR16)) - 1); + Object->BaseAddress = 0; + + // + // Increase the object count + // + DebuggerPrivate->DebuggerSymbolContext.ObjectCount ++; + + return EFI_SUCCESS; +} + +/** + + Located PDB path name in PE image. + + @param ImageBase - base of PE to search + + @return Pointer into image at offset of PDB file name if PDB file name is found, + Otherwise a pointer to an empty string. + +**/ +CHAR8 * +GetPdbPath ( + VOID *ImageBase + ) +{ + CHAR8 *PdbPath; + UINT32 DirCount; + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_IMAGE_OPTIONAL_HEADER_UNION *NtHdr; + EFI_IMAGE_OPTIONAL_HEADER32 *OptionalHdr32; + EFI_IMAGE_OPTIONAL_HEADER64 *OptionalHdr64; + EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry; + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry; + VOID *CodeViewEntryPointer; + + // + // Init value + // + CodeViewEntryPointer = NULL; + PdbPath = NULL; + DosHdr = ImageBase; + + // + // Check magic + // + if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { + return NULL; + } + NtHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((UINT8 *) DosHdr + DosHdr->e_lfanew); + // + // Check Machine, filter for EBC + // + if (NtHdr->Pe32.FileHeader.Machine != EFI_IMAGE_MACHINE_EBC) { + // + // If not EBC, return NULL + // + return NULL; + } + + // + // Get DirectoryEntry + // EBC spec says PE32+, but implementation uses PE32. So check dynamically here. + // + if (NtHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + OptionalHdr32 = (VOID *) &NtHdr->Pe32.OptionalHeader; + DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHdr32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); + } else if (NtHdr->Pe32Plus.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + OptionalHdr64 = (VOID *) &NtHdr->Pe32Plus.OptionalHeader; + DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHdr64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); + } else { + return NULL; + } + if (DirectoryEntry->VirtualAddress == 0) { + return NULL; + } + // + // Go through DirectoryEntry + // + for (DirCount = 0; + (DirCount < DirectoryEntry->Size / sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) && CodeViewEntryPointer == NULL; + DirCount++ + ) { + DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) (DirectoryEntry->VirtualAddress + (UINTN) ImageBase + DirCount * sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)); + if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) { + // + // Match DebugEntry, only CODEVIEW_SIGNATURE_NB10 and CODEVIEW_SIGNATURE_RSDS are supported. + // + CodeViewEntryPointer = (VOID *) ((UINTN) DebugEntry->RVA + (UINTN) ImageBase); + switch (*(UINT32 *) CodeViewEntryPointer) { + case CODEVIEW_SIGNATURE_NB10: + PdbPath = (CHAR8 *) CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY); + break; + case CODEVIEW_SIGNATURE_RSDS: + PdbPath = (CHAR8 *) CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY); + break; + default: + break; + } + } + } + + // + // Done successfully + // + return PdbPath; +} + +/** + + Check whether PDB file and MAP file have same name. + + @param PdbFileName - PDB file name + @param MapFileName - MAP file name + + @retval TRUE - PDB and MAP file name match + @retval FALSE - PDB and MAP file name not match + +**/ +BOOLEAN +MatchPdbAndMap ( + IN CHAR8 *PdbFileName, + IN CHAR16 *MapFileName + ) +{ + UINTN PdbNameSize; + UINTN MapNameSize; + CHAR8 *PurePdbFileName; + UINTN Index; + + // + // remove dir name + // + PurePdbFileName = PdbFileName; + for (Index = 0; PdbFileName[Index] != 0; Index++) { + if (PdbFileName[Index] == '\\') { + PurePdbFileName = &PdbFileName[Index + 1]; + } + } + PdbFileName = PurePdbFileName; + + // + // get size + // + PdbNameSize = AsciiStrLen (PdbFileName); + MapNameSize = StrLen (MapFileName); + + if (PdbNameSize != MapNameSize) { + return FALSE; + } + + // + // check the name + // + for (Index = 0; Index < MapNameSize - 4; Index++) { + if ((PdbFileName[Index] | 0x20) != (MapFileName[Index] | 0x20)) { + return FALSE; + } + } + + return TRUE; +} + +// +// BUGBUG: work-around start +// +typedef struct { + EFI_DEBUG_IMAGE_INFO *EfiDebugImageInfoTable; + volatile UINT32 UpdateStatus; + UINT32 TableSize; +} EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD; + +EFI_DEBUG_IMAGE_INFO_TABLE_HEADER mDebugImageInfoTableHeader; + +/** +For compatibility consideration, we handle 2 cases: + +1) IA32: + Old: New: + +------------------------+ +------------------------+ + | EfiDebugImageInfoTable | | UpdateStatus | + +------------------------+ +------------------------+ + | UpdateStatus | | TableSize | + +------------------------+ +------------------------+ + | TableSize | | EfiDebugImageInfoTable | + +------------------------+ +------------------------+ + +2) X64 and IPF: + Old: New: + +------------------------+ +------------------------+ + | EfiDebugImageInfoTable | | UpdateStatus | + | | +------------------------+ + | | | TableSize | + +------------------------+ +------------------------+ + | UpdateStatus | | EfiDebugImageInfoTable | + +------------------------+ | | + | TableSize | | | + +------------------------+ +------------------------+ + + @param DebugImageInfoTableHeader Point to the EFI_DEBUG_IMAGE_INFO_TABLE_HEADER structure. + +**/ +VOID +EdbFixDebugImageInfoTable ( + IN OUT EFI_DEBUG_IMAGE_INFO_TABLE_HEADER **DebugImageInfoTableHeader + ) +{ + mDebugImageInfoTableHeader.EfiDebugImageInfoTable = ((EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD *)(*DebugImageInfoTableHeader))->EfiDebugImageInfoTable; + mDebugImageInfoTableHeader.UpdateStatus = ((EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD *)(*DebugImageInfoTableHeader))->UpdateStatus; + mDebugImageInfoTableHeader.TableSize = ((EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD *)(*DebugImageInfoTableHeader))->TableSize; + + if ((*DebugImageInfoTableHeader)->UpdateStatus > 3) { + *DebugImageInfoTableHeader = &mDebugImageInfoTableHeader; + return ; + } + + if ((*DebugImageInfoTableHeader)->TableSize % (EFI_PAGE_SIZE / (sizeof (VOID *))) != 0) { + *DebugImageInfoTableHeader = &mDebugImageInfoTableHeader; + return ; + } + + return ; +} +// +// BUGBUG: work-around end +// + +/** + + Patch symbol RVA. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - Symbol file name + @param SearchType - Search type for Object + + @retval EFI_SUCCESS - Patch symbol RVA successfully + @retval EFI_NOT_FOUND - Symbol RVA base not found + +**/ +EFI_STATUS +EdbPatchSymbolRVA ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName, + IN EDB_EBC_IMAGE_RVA_SEARCH_TYPE SearchType + ) +{ + EFI_STATUS Status; + UINTN ImageNumber; + EFI_DEBUG_IMAGE_INFO *ImageTable; + CHAR8 *PdbPath; + VOID *ImageBase; + VOID *CandidateImageBase; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + + if (SearchType < 0 || SearchType >= EdbEbcImageRvaSearchTypeMax) { + return EFI_INVALID_PARAMETER; + } + + // + // Get the related object + // + Object = EdbFindSymbolFile (DebuggerPrivate, FileName, NULL); + if (Object == NULL) { + return EFI_NOT_FOUND; + } + + // + // Try again to get DebugImageInfoTable + // + if (mDebuggerPrivate.DebugImageInfoTableHeader == NULL) { + Status = EfiGetSystemConfigurationTable ( + &gEfiDebugImageInfoTableGuid, + (VOID **) &mDebuggerPrivate.DebugImageInfoTableHeader + ); + if (EFI_ERROR (Status)) { + EDBPrint (L"DebugImageInfoTable not found!\n"); + return Status; + } + } + DEBUG ((DEBUG_ERROR, "DebugImageInfoTableHeader: %x\n", mDebuggerPrivate.DebugImageInfoTableHeader)); + + // + // BUGBUG: work-around start + // + EdbFixDebugImageInfoTable (&mDebuggerPrivate.DebugImageInfoTableHeader); + // + // BUGBUG: work-around end + // + + // + // Go through DebugImageInfoTable for each Image + // + CandidateImageBase = NULL; + ImageTable = mDebuggerPrivate.DebugImageInfoTableHeader->EfiDebugImageInfoTable; + for (ImageNumber = 0; ImageNumber < mDebuggerPrivate.DebugImageInfoTableHeader->TableSize; ImageNumber++) { + if (ImageTable[ImageNumber].NormalImage == NULL) { + continue; + } + ImageBase = ImageTable[ImageNumber].NormalImage->LoadedImageProtocolInstance->ImageBase; + // + // Get PDB path + // + PdbPath = GetPdbPath (ImageBase); + if (PdbPath == NULL) { + continue; + } + // + // Check PDB name + // + if (!MatchPdbAndMap (PdbPath, FileName)) { + continue; + } + DEBUG ((DEBUG_ERROR, "ImageBase: %x\n", ImageBase)); + + // + // Check SearchType + // + if (SearchType == EdbEbcImageRvaSearchTypeAny || SearchType == EdbEbcImageRvaSearchTypeFirst) { + // + // Assign base address and return + // + Object->BaseAddress = (UINTN)ImageBase; + return EFI_SUCCESS; + } + + // + // Get CandidateImageBase for EdbEbcImageRvaSearchTypeLast + // + CandidateImageBase = ImageBase; + } + + // + // Check EdbEbcImageRvaSearchTypeLast + // + if (SearchType == EdbEbcImageRvaSearchTypeLast) { + if (CandidateImageBase == NULL) { + return EFI_NOT_FOUND; + } + // + // Assign base address and return + // + Object->BaseAddress = (UINTN)CandidateImageBase; + return EFI_SUCCESS; + } + + // + // No match + // + return EFI_NOT_FOUND; +} + +/** + + Check whether OBJ file and COD file have same name. + + @param ObjFileName - OBJ file name + @param CodFileName - COD file name + + @retval TRUE - OBJ and COD file name match + @retval FALSE - OBJ and COD file name not match + +**/ +BOOLEAN +MatchObjAndCod ( + IN CHAR8 *ObjFileName, + IN CHAR16 *CodFileName + ) +{ + UINTN ObjNameSize; + UINTN CodNameSize; + CHAR8 *PureObjFileName; + UINTN Index; + + // + // remove library name + // + PureObjFileName = ObjFileName; + for (Index = 0; ObjFileName[Index] != 0; Index++) { + if (ObjFileName[Index] == ':') { + PureObjFileName = &ObjFileName[Index + 1]; + break; + } + } + ObjFileName = PureObjFileName; + + // + // get size + // + ObjNameSize = AsciiStrLen (ObjFileName); + CodNameSize = StrLen (CodFileName); + + if (ObjNameSize != CodNameSize) { + return FALSE; + } + + // + // check the name + // + for (Index = 0; Index < CodNameSize - 4; Index++) { + if ((ObjFileName[Index] | 0x20) != (CodFileName[Index] | 0x20)) { + return FALSE; + } + } + + return TRUE; +} + +typedef enum { + EdbEbcCodParseStateUninitialized, + EdbEbcCodParseStateSymbolInitialized, + EdbEbcCodParseStateSymbolStart, + EdbEbcCodParseStateSymbolEnd, + EdbEbcCodParseStateMax, +} EDB_EBC_COD_PARSE_STATE; + +/** + + The following code depends on the COD file generated by IEC compiler. + +**/ + +/** + + Load code by symbol by Iec. + + @param Name - Symbol file name + @param Buffer - Symbol file buffer + @param BufferSize - Symbol file buffer size + @param CodeBufferSize - Code buffer size + @param FuncOffset - Code funcion offset + + @return CodeBuffer + +**/ +CHAR8 * +EdbLoadCodBySymbolByIec ( + IN CHAR8 *Name, + IN VOID *Buffer, + IN UINTN BufferSize, + OUT UINTN *CodeBufferSize, + OUT UINTN *FuncOffset + ) +{ + CHAR8 *LineBuffer; + CHAR8 *FieldBuffer; + VOID *BufferStart; + VOID *BufferEnd; + UINTN Offset; + EDB_EBC_COD_PARSE_STATE CodParseState; + CHAR8 Char[2]; + + // + // Init + // + Char[0] = 9; + Char[1] = 0; + LineBuffer = AsciiStrGetNewTokenLine (Buffer, "\n\r"); + Offset = (UINTN)-1; + BufferStart = NULL; + BufferEnd = NULL; + CodParseState = EdbEbcCodParseStateUninitialized; + + // + // Check each line + // + while (LineBuffer != NULL) { + switch (CodParseState) { + case EdbEbcCodParseStateUninitialized: + // + // check mark_begin, begin to check line after this match + // + if (AsciiStrCmp (LineBuffer, "; mark_begin;") == 0) { + CodParseState = EdbEbcCodParseStateSymbolInitialized; + } + LineBuffer = AsciiStrGetNextTokenLine ("\n\r"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + break; + + case EdbEbcCodParseStateSymbolInitialized: + // + // check mark_end, not check line after this match + // + if (AsciiStrCmp (LineBuffer, "; mark_end;") == 0) { + CodParseState = EdbEbcCodParseStateUninitialized; + LineBuffer = AsciiStrGetNextTokenLine ("\n\r"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + break; + } + + // + // not check this line if the first char is as follows + // + if ((*LineBuffer == 0) || + (*LineBuffer == '$') || + (*LineBuffer == ';') || + (*LineBuffer == '_') || + (*LineBuffer == ' ')) { + LineBuffer = AsciiStrGetNextTokenLine ("\n\r"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + break; + } + + // + // get function name, function name is followed by char 0x09. + // + FieldBuffer = AsciiStrGetNewTokenField (LineBuffer, Char); + ASSERT (FieldBuffer != NULL); + if (AsciiStriCmp (FieldBuffer, Name) == 0) { + BufferStart = FieldBuffer; + CodParseState = EdbEbcCodParseStateSymbolStart; + } + PatchForAsciiStrTokenAfter (FieldBuffer, 0x9); + + // + // Get next line + // + LineBuffer = AsciiStrGetNextTokenLine ("\n\r"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + break; + + case EdbEbcCodParseStateSymbolStart: + // + // check mark_end, if this match, means the function is found successfully. + // + if (AsciiStrCmp (LineBuffer, "; mark_end;") == 0) { + CodParseState = EdbEbcCodParseStateSymbolEnd; + // + // prepare CodeBufferSize, FuncOffset, and FuncStart to return + // + BufferEnd = LineBuffer + sizeof("; mark_end;") - 1; + *CodeBufferSize = (UINTN)BufferEnd - (UINTN)BufferStart; + *FuncOffset = Offset; + PatchForAsciiStrTokenAfter (LineBuffer, '\n'); + return BufferStart; + } + + // + // Get function offset + // + if ((Offset == (UINTN)-1) && + (*LineBuffer == ' ')) { + FieldBuffer = AsciiStrGetNewTokenField (LineBuffer + 2, " "); + Offset = AsciiXtoi (FieldBuffer); + PatchForAsciiStrTokenAfter (FieldBuffer, ' '); + } + + // + // Get next line + // + LineBuffer = AsciiStrGetNextTokenLine ("\n\r"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + break; + + case EdbEbcCodParseStateSymbolEnd: + break; + + default: + break; + } + } + + // + // no function found + // + return NULL; +} + +/** + + Load code by symbol. + + @param Name - Symbol file name + @param Buffer - Symbol file buffer + @param BufferSize - Symbol file buffer size + @param CodeBufferSize - Code buffer size + @param FuncOffset - Code funcion offset + + @return CodeBuffer + +**/ +CHAR8 * +EdbLoadCodBySymbol ( + IN CHAR8 *Name, + IN VOID *Buffer, + IN UINTN BufferSize, + OUT UINTN *CodeBufferSize, + OUT UINTN *FuncOffset + ) +{ + // + // COD file format depends on the compiler. + // + // It is possible to check the different COD file format in this routine. + // Now only IEC is supported. + // + return EdbLoadCodBySymbolByIec (Name, Buffer, BufferSize, CodeBufferSize, FuncOffset); +} + +/** + + Find code from object. + + @param DebuggerPrivate EBC Debugger private data structure + @param Object - Symbol object + @param FileName - File name + +**/ +VOID * +EdbFindCodeFromObject ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN EFI_DEBUGGER_SYMBOL_OBJECT *Object, + IN CHAR16 *FileName + ) +{ + UINTN EntryIndex; + + // + // Go througn each Entry in this Object + // + for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) { + // + // This check is for Function only + // + if ((Object->Entry[EntryIndex].Type != EfiDebuggerSymbolFunction) && + (Object->Entry[EntryIndex].Type != EfiDebuggerSymbolStaticFunction)) { + continue; + } + // + // Skip match varbss_init function, because they has no source code + // + if (AsciiStrnCmp (Object->Entry[EntryIndex].Name, "varbss_init", sizeof("varbss_init") - 1) == 0) { + continue; + } + // + // check the name + // + if (!MatchObjAndCod (Object->Entry[EntryIndex].ObjName, FileName)) { + continue; + } + // + // found it, return source buffer + // + if (Object->Entry[EntryIndex].CodBuffer != NULL) { + return Object->Entry[EntryIndex].SourceBuffer; + } + } + + // + // not found + // + return NULL; +} + +/** + + Load code. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param FileName - Code file name + @param BufferSize - Code file buffer size + @param Buffer - Code file buffer + + @retval EFI_SUCCESS - Code loaded successfully + +**/ +EFI_STATUS +EdbLoadCode ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *FileName, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + UINTN ObjectIndex; + UINTN EntryIndex; + VOID *SourceBuffer; + EFI_STATUS Status; + + // + // Find Symbol + // + Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, &ObjectIndex); + if (Object == NULL) { + EDBPrint (L"SymbolFile is not loaded!\n"); + return EFI_NOT_FOUND; + } else { + // + // Check duplicated File + // + SourceBuffer = EdbFindCodeFromObject (DebuggerPrivate, Object, FileName); + if (SourceBuffer != NULL) { + // + // unnload duplicated code + // + Status = EdbUnloadCode (DebuggerPrivate, MapFileName, FileName, &SourceBuffer); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "Unload Duplicated Code File Error!\n")); + return Status; + } + Status = EdbDeleteCodeBuffer (DebuggerPrivate, MapFileName, FileName, SourceBuffer); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "Delete Duplicated Code File Error!\n")); + return Status; + } + } + } + + // + // Go through each SymbolEntry + // + for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) { + // + // load symbol for function only + // + if ((Object->Entry[EntryIndex].Type != EfiDebuggerSymbolFunction) && + (Object->Entry[EntryIndex].Type != EfiDebuggerSymbolStaticFunction)) { + continue; + } + // + // skip varbss_init + // + if (AsciiStrnCmp (Object->Entry[EntryIndex].Name, "varbss_init", sizeof("varbss_init") - 1) == 0) { + continue; + } + // + // Check the name + // + if (!MatchObjAndCod (Object->Entry[EntryIndex].ObjName, FileName)) { + continue; + } + // + // load code for this symbol + // + Object->Entry[EntryIndex].CodBuffer = EdbLoadCodBySymbol ( + Object->Entry[EntryIndex].Name, + Buffer, + BufferSize, + &Object->Entry[EntryIndex].CodBufferSize, + &Object->Entry[EntryIndex].FuncOffsetBase + ); + if (Object->Entry[EntryIndex].CodBuffer != NULL) { + Object->Entry[EntryIndex].SourceBuffer = Buffer; + } + } + + // + // patch end '\0' for each code buffer + // + for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) { + if (Object->Entry[EntryIndex].CodBuffer != NULL) { + *((UINT8 *)Object->Entry[EntryIndex].CodBuffer + Object->Entry[EntryIndex].CodBufferSize) = 0; + DEBUG ((DEBUG_ERROR, " CodeSymbol: %a, FuncOffset: 0x05%x\n", Object->Entry[EntryIndex].Name, Object->Entry[EntryIndex].FuncOffsetBase)); +// DEBUG ((DEBUG_ERROR, " [CODE]:\n%a\n", Object->Entry[EntryIndex].CodBuffer)); + } + } + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Unload code. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param FileName - Code file name + @param Buffer - Code file buffer + + @retval EFI_SUCCESS - Code unloaded successfully + +**/ +EFI_STATUS +EdbUnloadCode ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *FileName, + OUT VOID **Buffer + ) +{ + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + UINTN ObjectIndex; + UINTN EntryIndex; + + // + // Find Symbol + // + Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, &ObjectIndex); + if (Object == NULL) { + EDBPrint (L"SymbolFile is not loaded!\n"); + return EFI_NOT_FOUND; + } + + // + // Find code + // + *Buffer = EdbFindCodeFromObject (DebuggerPrivate, Object, FileName); + if (*Buffer == NULL) { + EDBPrint (L"CodeFile is not loaded!\n"); + return EFI_NOT_FOUND; + } + + // + // go through each entry + // + for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) { + if ((Object->Entry[EntryIndex].Type != EfiDebuggerSymbolFunction) && + (Object->Entry[EntryIndex].Type != EfiDebuggerSymbolStaticFunction)) { + continue; + } + if (AsciiStrnCmp (Object->Entry[EntryIndex].Name, "varbss_init", sizeof("varbss_init") - 1) == 0) { + continue; + } + if (!MatchObjAndCod (Object->Entry[EntryIndex].ObjName, FileName)) { + continue; + } + // + // clean up the buffer + // + Object->Entry[EntryIndex].CodBuffer = NULL; + Object->Entry[EntryIndex].CodBufferSize = 0; + Object->Entry[EntryIndex].FuncOffsetBase = 0; + Object->Entry[EntryIndex].SourceBuffer = NULL; + } + + // + // Done + // + return EFI_SUCCESS; +} + +/** + + Add code buffer. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param CodeFileName - Code file name + @param SourceBufferSize- Code buffer size + @param SourceBuffer - Code buffer + + @retval EFI_SUCCESS - CodeBuffer added successfully + +**/ +EFI_STATUS +EdbAddCodeBuffer ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *CodeFileName, + IN UINTN SourceBufferSize, + IN VOID *SourceBuffer + ) +{ + UINTN Index; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + + // + // Find Symbol + // + Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, NULL); + if (Object == NULL) { + EDBPrint (L"SymbolFile is not loaded!\n"); + return EFI_NOT_FOUND; + } + + // + // Add it to last entry + // + for (Index = 0; Object->SourceBuffer[Index] != NULL; Index++) { + ; + } + Object->SourceBuffer[Index] = SourceBuffer; + + return EFI_SUCCESS; +} + +/** + + Delete code buffer. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param CodeFileName - Code file name + @param SourceBuffer - Code buffer + + @retval EFI_SUCCESS - CodeBuffer deleted successfully + +**/ +EFI_STATUS +EdbDeleteCodeBuffer ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *CodeFileName, + IN VOID *SourceBuffer + ) +{ + UINTN Index; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + + // + // Find Symbol + // + Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, NULL); + if (Object == NULL) { + EDBPrint (L"SymbolFile is not loaded!\n"); + return EFI_NOT_FOUND; + } + + for (Index = 0; Object->SourceBuffer[Index] != NULL; Index++) { + // + // free the buffer if match + // + if (Object->SourceBuffer[Index] == SourceBuffer) { + gBS->FreePool (SourceBuffer); + break; + } + } + + if (Object->SourceBuffer[Index] == NULL) { + // + // not return NOT_FOUND + // + return EFI_SUCCESS; + } + + // + // remove the entry + // + Object->SourceBuffer[Index] = NULL; + for (Index = Index + 1; Object->SourceBuffer[Index] != NULL; Index++) { + Object->SourceBuffer[Index - 1] = Object->SourceBuffer[Index]; + } + Object->SourceBuffer[Index - 1] = NULL; + + return EFI_SUCCESS; +} + +/** + + Find the symbol string according to address. + + @param Address - Symbol address + + @return Symbol string + +**/ +CHAR8 * +FindSymbolStr ( + IN UINTN Address + ) +{ + UINTN ObjectIndex; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + UINTN EntryIndex; + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + + // + // need we display symbol + // + if (!mDebuggerPrivate.DebuggerSymbolContext.DisplaySymbol) { + return NULL; + } + + // + // Go through each object and entry + // + Object = mDebuggerPrivate.DebuggerSymbolContext.Object; + for (ObjectIndex = 0; ObjectIndex < mDebuggerPrivate.DebuggerSymbolContext.ObjectCount; ObjectIndex++) { + Entry = Object[ObjectIndex].Entry; + for (EntryIndex = 0; EntryIndex < Object[ObjectIndex].EntryCount; EntryIndex++) { + // + // if Address match, return Name + // + if (Address == (Entry[EntryIndex].Rva + Object[ObjectIndex].BaseAddress)) { + return Entry[EntryIndex].Name; + } + } + } + + // + // not found + // + return NULL; +} + +/** + + Get line number and offset from this line in code file. + + @param Line - Line buffer in code file + @param Offset - Offset to functin entry + + @return Line number + +**/ +UINTN +EdbGetLineNumberAndOffsetFromThisLine ( + IN VOID *Line, + OUT UINTN *Offset + ) +{ + UINTN LineNumber; + CHAR8 *LineBuffer; + CHAR8 *FieldBuffer; + + LineNumber = (UINTN)-1; + LineBuffer = Line; + *Offset = (UINTN)-1; + + while (LineBuffer != NULL) { + // + // Check candidate + // + if (*LineBuffer != ' ') { + return (UINTN)-1; + } + + // + // Get Offset + // + if (*(LineBuffer + 2) != ' ') { + if (*Offset == (UINTN)-1) { + FieldBuffer = AsciiStrGetNewTokenField (LineBuffer + 2, " "); + *Offset = AsciiXtoi (FieldBuffer); + PatchForAsciiStrTokenAfter (FieldBuffer, ' '); + } + } + + // + // 1. assembly instruction + // + FieldBuffer = AsciiStrGetNewTokenField (LineBuffer, ":"); + // + // 2. file path + // + FieldBuffer = AsciiStrGetNextTokenField (":"); + PatchForAsciiStrTokenBefore (FieldBuffer, ':'); + if (FieldBuffer == NULL) { + // + // candidate found + // + LineNumber = 0; + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + continue; + } + // + // 3. line number + // + FieldBuffer = AsciiStrGetNextTokenField (":"); + PatchForAsciiStrTokenBefore (FieldBuffer, ':'); + if (FieldBuffer == NULL) { + // + // impossible, TBD? + // + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + continue; + } + + LineNumber = AsciiAtoi (FieldBuffer); + // + // Not patch after + // + + return LineNumber; + } + + return (UINTN)-1; +} + +typedef enum { + EdbEbcLineSearchTypeAny, + EdbEbcLineSearchTypeFirst, + EdbEbcLineSearchTypeLast, + EdbEbcLineSearchTypeMax, +} EDB_EBC_LINE_SEARCH_TYPE; + +/** + + Get line number from this code file. + + @param Entry - Symbol entry + @param FuncOffset - Offset to functin entry + @param SearchType - Search type for the code + + @return Line number + +**/ +UINTN +EdbGetLineNumberFromCode ( + IN EFI_DEBUGGER_SYMBOL_ENTRY *Entry, + IN UINTN FuncOffset, + IN EDB_EBC_LINE_SEARCH_TYPE SearchType + ) +{ + CHAR8 *LineBuffer; + UINTN LineNumber; + UINTN Offset; + UINTN CandidateLineNumber; + UINTN CandidateOffset; + + if (SearchType < 0 || SearchType >= EdbEbcLineSearchTypeMax) { + return (UINTN)-1; + } + + LineNumber = (UINTN)-1; + CandidateLineNumber = (UINTN)-1; + CandidateOffset = (UINTN)-1; + LineBuffer = AsciiStrGetNewTokenLine (Entry->CodBuffer, "\n"); + while (LineBuffer != NULL) { + if (*LineBuffer != ' ') { + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + continue; + } + + // + // Get Info + // + LineNumber = EdbGetLineNumberAndOffsetFromThisLine (LineBuffer, &Offset); + + // + // Check offset + // + if (Offset != FuncOffset) { + // + // Check last offset match + // + if (CandidateOffset == FuncOffset) { + if (SearchType == EdbEbcLineSearchTypeLast) { + PatchForAsciiStrTokenAfter (LineBuffer, '\n'); + if (CandidateLineNumber != LineNumber) { + return CandidateLineNumber; + } else { + return (UINTN)-1; + } + } else { + // + // impossible, TBD? + // + } + } + + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + CandidateLineNumber = LineNumber; + continue; + } + + // + // Offset match, more check + // + if (SearchType == EdbEbcLineSearchTypeAny) { + PatchForAsciiStrTokenAfter (LineBuffer, '\n'); + return LineNumber; + } + + if (SearchType == EdbEbcLineSearchTypeFirst) { + // + // Check last line + // + PatchForAsciiStrTokenAfter (LineBuffer, '\n'); + if (CandidateLineNumber != LineNumber) { + return LineNumber; + } else { + return (UINTN)-1; + } + } + + CandidateLineNumber = LineNumber; + CandidateOffset = Offset; + + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + } + + // + // Check last offset match + // + if (CandidateOffset == FuncOffset) { + if (SearchType == EdbEbcLineSearchTypeLast) { + return CandidateLineNumber; + } + } + + return (UINTN)-1; +} + +/** + + Get the source string from this code file by line. + + @param Entry - Symbol entry + @param LineNumber - line number + @param FuncEnd - Function end + + @return Funtion start + +**/ +VOID * +EdbGetSourceStrFromCodeByLine ( + IN EFI_DEBUGGER_SYMBOL_ENTRY *Entry, + IN UINTN LineNumber, + IN VOID **FuncEnd + ) +{ + CHAR8 *LineBuffer; + CHAR8 *FieldBuffer; + VOID *FuncStart; + UINTN Number; + + FuncStart = NULL; + LineBuffer = AsciiStrGetNewTokenLine (Entry->CodBuffer, "\n"); + while (LineBuffer != NULL) { + if (*LineBuffer != ';') { + if (FuncStart != NULL) { + // + // Over + // + *FuncEnd = LineBuffer - 1; + PatchForAsciiStrTokenAfter (LineBuffer, '\n'); + return FuncStart; + } + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + continue; + } + + // + // Check LineNumber + // + FieldBuffer = AsciiStrGetNewTokenField (LineBuffer + 1, " "); + Number = AsciiAtoi (FieldBuffer); + PatchForAsciiStrTokenAfter (FieldBuffer, ' '); + if (Number != LineNumber) { + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + continue; + } + + // + // Line match, get line number + // + if (FuncStart == NULL) { + FuncStart = LineBuffer; + } + + LineBuffer = AsciiStrGetNextTokenLine ("\n"); + PatchForAsciiStrTokenBefore (LineBuffer, '\n'); + } + + return NULL; +} + +/** + + Get source string from this code file. + + @param Entry - Symbol entry + @param FuncOffset - Offset to functin entry + @param FuncEnd - Function end + + @retval Funtion start + +**/ +VOID * +EdbGetSourceStrFromCode ( + IN EFI_DEBUGGER_SYMBOL_ENTRY *Entry, + IN UINTN FuncOffset, + IN VOID **FuncEnd + ) +{ + UINTN LineNumber; + + // + // Only search the last line, then display + // + LineNumber = EdbGetLineNumberFromCode (Entry, FuncOffset, EdbEbcLineSearchTypeLast); + if (LineNumber == (UINTN)-1) { + return NULL; + } + + return EdbGetSourceStrFromCodeByLine (Entry, LineNumber, FuncEnd); +} + +/** + + Print source. + + @param Address - Instruction address + @param IsPrint - Whether need to print + + @retval 1 - find the source + @retval 0 - not find the source + +**/ +UINTN +EdbPrintSource ( + IN UINTN Address, + IN BOOLEAN IsPrint + ) +{ + UINTN SymbolAddress; + EFI_DEBUGGER_SYMBOL_OBJECT *RetObject; + EFI_DEBUGGER_SYMBOL_ENTRY *RetEntry; + UINTN FuncOffset; + UINT8 *FuncStart; + UINT8 *FuncEnd; + UINT8 *FuncIndex; + CHAR8 Buffer[EFI_DEBUG_MAX_PRINT_BUFFER]; + UINTN BufferSize; + + // + // need we display symbol + // + if (!mDebuggerPrivate.DebuggerSymbolContext.DisplaySymbol) { + return 0 ; + } + + // + // find the symbol address + // + SymbolAddress = EbdFindSymbolAddress ( + Address, + EdbMatchSymbolTypeLowerAddress, + &RetObject, + &RetEntry + ); + if (SymbolAddress == 0 || RetEntry == NULL) { + return 0 ; + } + + FuncOffset = Address - SymbolAddress + RetEntry->FuncOffsetBase; + + // + // Get Func String + // + FuncStart = EdbGetSourceStrFromCode (RetEntry, FuncOffset, (VOID**) &FuncEnd); + if (FuncStart == NULL) { + return 0 ; + } + + // + // check whether need to real print + // + if (!IsPrint) { + return 1; + } + + *(UINT8 *)FuncEnd = 0; + + // + // seperate buffer by \n, so that \r can be added. + // + FuncIndex = FuncStart; + while (*FuncIndex != 0) { + if (*FuncIndex == '\n') { + if ((FuncIndex - FuncStart) < (EFI_DEBUG_MAX_PRINT_BUFFER - 3)) { + BufferSize = FuncIndex - FuncStart; + } else { + BufferSize = EFI_DEBUG_MAX_PRINT_BUFFER - 3; + } + if (BufferSize != 0) { + CopyMem (Buffer, FuncStart, BufferSize); + } + Buffer[BufferSize] = 0; + EDBPrint (L"%a\n", Buffer); + FuncStart = FuncIndex + 1; + FuncIndex = FuncStart; + } else { + FuncIndex ++; + } + } + + // + // Patch the end + // + *(UINT8 *)FuncEnd = '\n'; + + return 1 ; +} + +/** + + Get Mapfile and SymbolName from one symbol format: [MapFileName:]SymbolName. + + @param Symbol - whole Symbol name + @param MapfileName - the mapfile name in the symbol + @param SymbolName - the symbol name in the symbol + +**/ +VOID +GetMapfileAndSymbol ( + IN CHAR16 *Symbol, + OUT CHAR16 **MapfileName, + OUT CHAR16 **SymbolName + ) +{ + CHAR16 *Ch; + + *MapfileName = NULL; + *SymbolName = Symbol; + + for (Ch = Symbol; *Ch != 0; Ch++) { + // + // Find split char + // + if (*Ch == L':') { + *MapfileName = Symbol; + *Ch = 0; + *SymbolName = Ch + 1; + break; + } + } + + return ; +} + +/** + + Convert a symbol to an address. + + @param Symbol - Symbol name + @param Address - Symbol address + + @retval EFI_SUCCESS - symbol found and address returned. + @retval EFI_NOT_FOUND - symbol not found + @retval EFI_NO_MAPPING - duplicated symbol not found + +**/ +EFI_STATUS +Symboltoi ( + IN CHAR16 *Symbol, + OUT UINTN *Address + ) +{ + UINTN ObjectIndex; + EFI_DEBUGGER_SYMBOL_OBJECT *Object; + UINTN EntryIndex; + EFI_DEBUGGER_SYMBOL_ENTRY *Entry; + CHAR16 *SymbolName; + CHAR16 *MapfileName; + + // + // Split one symbol to mapfile name and symbol name + // + GetMapfileAndSymbol (Symbol, &MapfileName, &SymbolName); + + *Address = 0; + // + // Go through each object + // + Object = mDebuggerPrivate.DebuggerSymbolContext.Object; + for (ObjectIndex = 0; ObjectIndex < mDebuggerPrivate.DebuggerSymbolContext.ObjectCount; ObjectIndex++) { + // + // Check MapfileName + // + if ((MapfileName != NULL) && (StriCmp (Object[ObjectIndex].Name, MapfileName) != 0)) { + continue; + } + // + // Go through each entry + // + Entry = Object[ObjectIndex].Entry; + for (EntryIndex = 0; EntryIndex < Object[ObjectIndex].EntryCount; EntryIndex++) { + // + // Check SymbolName (case sensitive) + // + if (StrCmpUnicodeAndAscii (SymbolName, Entry[EntryIndex].Name) == 0) { + if ((*Address != 0) && (MapfileName == NULL)) { + // + // Find the duplicated symbol + // + EDBPrint (L"Duplicated Symbol found!\n"); + return EFI_NO_MAPPING; + } else { + // + // record Address + // + *Address = (Entry[EntryIndex].Rva + Object[ObjectIndex].BaseAddress); + } + } + } + } + + if (*Address == 0) { + // + // Not found + // + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.h new file mode 100644 index 000000000..f82e5b76c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbSymbol.h @@ -0,0 +1,244 @@ +/** @file + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + + +**/ + +#ifndef _EFI_EDB_SYMBOL_H_ +#define _EFI_EDB_SYMBOL_H_ + +#include + +// +// The default base address is 0x10000000 +// +#define EFI_DEBUGGER_DEFAULT_LINK_IMAGEBASE 0x10000000 + +#define EFI_DEBUGGER_MAX_SYMBOL_ADDRESS_DELTA_VALUE 0x100000 // 1 M delta + +typedef enum { + EdbMatchSymbolTypeSameAdderss, + EdbMatchSymbolTypeNearestAddress, + EdbMatchSymbolTypeLowerAddress, + EdbMatchSymbolTypeUpperAddress, + EdbMatchSymbolTypeMax, +} EDB_MATCH_SYMBOL_TYPE; + +typedef enum { + EdbEbcImageRvaSearchTypeAny, + EdbEbcImageRvaSearchTypeFirst, + EdbEbcImageRvaSearchTypeLast, + EdbEbcImageRvaSearchTypeMax, +} EDB_EBC_IMAGE_RVA_SEARCH_TYPE; + +/** + + Find symbol by address. + + @param Address - Symbol address + @param Type - Search type + @param RetObject - Symbol object + @param RetEntry - Symbol entry + + @return Nearest symbol address + +**/ +UINTN +EbdFindSymbolAddress ( + IN UINTN Address, + IN EDB_MATCH_SYMBOL_TYPE Type, + OUT EFI_DEBUGGER_SYMBOL_OBJECT **Object, + OUT EFI_DEBUGGER_SYMBOL_ENTRY **Entry + ); + +/** + + Load symbol file by name. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - Symbol file name + @param BufferSize - Symbol file buffer size + @param Buffer - Symbol file buffer + + @retval EFI_SUCCESS - load symbol successfully + +**/ +EFI_STATUS +EdbLoadSymbol ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + + Unload symbol file by name. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - Symbol file name + + @retval EFI_SUCCESS - unload symbol successfully + +**/ +EFI_STATUS +EdbUnloadSymbol ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName + ); + +/** + + Patch symbol RVA. + + @param DebuggerPrivate - EBC Debugger private data structure + @param FileName - Symbol file name + @param SearchType - Search type for Object + + @retval EFI_SUCCESS - Patch symbol RVA successfully + @retval EFI_NOT_FOUND - Symbol RVA base not found + +**/ +EFI_STATUS +EdbPatchSymbolRVA ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *FileName, + IN EDB_EBC_IMAGE_RVA_SEARCH_TYPE SearchType + ); + +/** + + Load code. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param FileName - Code file name + @param BufferSize - Code file buffer size + @param Buffer - Code file buffer + + @retval EFI_SUCCESS - Code loaded successfully + +**/ +EFI_STATUS +EdbLoadCode ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *FileName, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + + Unload code. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param FileName - Code file name + @param Buffer - Code file buffer + + @retval EFI_SUCCESS - Code unloaded successfully + +**/ +EFI_STATUS +EdbUnloadCode ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *FileName, + OUT VOID **Buffer + ); + +/** + + Add code buffer. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param CodeFileName - Code file name + @param SourceBufferSize- Code buffer size + @param SourceBuffer - Code buffer + + @retval EFI_SUCCESS - CodeBuffer added successfully + +**/ +EFI_STATUS +EdbAddCodeBuffer ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *CodeFileName, + IN UINTN SourceBufferSize, + IN VOID *SourceBuffer + ); + +/** + + Delete code buffer. + + @param DebuggerPrivate - EBC Debugger private data structure + @param MapFileName - Symbol file name + @param CodeFileName - Code file name + @param SourceBuffer - Code buffer + + @retval EFI_SUCCESS - CodeBuffer deleted successfully + +**/ +EFI_STATUS +EdbDeleteCodeBuffer ( + IN EFI_DEBUGGER_PRIVATE_DATA *DebuggerPrivate, + IN CHAR16 *MapFileName, + IN CHAR16 *CodeFileName, + IN VOID *SourceBuffer + ); + +/** + + Find the symbol string according to address. + + @param Address - Symbol address + + @return Symbol string + +**/ +CHAR8 * +FindSymbolStr ( + IN UINTN Address + ); + +/** + + Print source. + + @param Address - Instruction address + @param IsPrint - Whether need to print + + @retval 1 - find the source + @retval 0 - not find the source + +**/ +UINTN +EdbPrintSource ( + IN UINTN Address, + IN BOOLEAN IsPrint + ); + +/** + + Convert a symbol to an address. + + @param Symbol - Symbol name + @param Address - Symbol address + + @retval EFI_SUCCESS - symbol found and address returned. + @retval EFI_NOT_FOUND - symbol not found + @retval EFI_NO_MAPPING - duplicated symbol not found + +**/ +EFI_STATUS +Symboltoi ( + IN CHAR16 *Symbol, + OUT UINTN *Address + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf new file mode 100644 index 000000000..22cf64d95 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf @@ -0,0 +1,57 @@ +## @file +# EBC Debugger configuration application. +# +# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EdbCfg + MODULE_UNI_FILE = EbcDebuggerConfig.uni + FILE_GUID = 8CFC5233-23C6-49e3-8A2D-7E581AB305BA + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeEbcDebuggerConfig + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + EbcDebugger/EbcDebuggerConfig.c + EbcDebugger/EdbCommon.h + EbcDebugger/EdbSupportString.c + EbcDebugger/EdbSupport.h + EbcDebugger/EdbCommand.h + EbcDebugger/EdbHook.h + EbcDebugger/Edb.h + EbcDebugger/EdbDisasmSupport.h + EbcDebugger/EdbDisasm.h + EbcDebugger/EdbSymbol.h + EbcDebuggerHook.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiLib + BaseLib + DebugLib + UefiApplicationEntryPoint + +[Protocols] + gEfiDebuggerConfigurationProtocolGuid ## CONSUMES + gEfiShellParametersProtocolGuid ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + EbcDebuggerConfigExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.uni b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.uni new file mode 100644 index 000000000..c8592407c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.uni @@ -0,0 +1,13 @@ +// /** @file +// EBC Debugger configuration application. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "EBC Debugger configuration application" + +#string STR_MODULE_DESCRIPTION #language en-US "This application allows configuring the EBC Debugger." diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfigExtra.uni b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfigExtra.uni new file mode 100644 index 000000000..1d532c86a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfigExtra.uni @@ -0,0 +1,12 @@ +// /** @file +// EBC Debugger configuration application. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"EBC Debugger Config" diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerExtra.uni b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerExtra.uni new file mode 100644 index 000000000..944abae21 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerExtra.uni @@ -0,0 +1,12 @@ +// /** @file +// EFI Byte Code (EBC) Debugger +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"EFI Byte Code (EBC) Debugger" diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.c new file mode 100644 index 000000000..b516147ff --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.c @@ -0,0 +1,267 @@ +/** @file + Contains the empty version of the EBC Debugger hooks, to be used when + compiling the regular EBC VM module. + As debugging is not needed for the standard EBC VM, all calls are left empty. + + The EBC Debugger defines its own version for these calls in EbdHooks.c. + + Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EbcDebuggerHook.h" + +/** + + The hook in InitializeEbcDriver. + + @param Handle - The EbcDebugProtocol handle. + @param EbcDebugProtocol - The EbcDebugProtocol interface. + +**/ +VOID +EbcDebuggerHookInit ( + IN EFI_HANDLE Handle, + IN EFI_DEBUG_SUPPORT_PROTOCOL *EbcDebugProtocol + ) +{ + return; +} + +/** + +The hook in UnloadImage for EBC Interpreter. + +**/ +VOID +EbcDebuggerHookUnload ( + VOID + ) +{ + return; +} + +/** + + The hook in EbcUnloadImage. + Currently do nothing here. + + @param Handle The EbcImage handle. + +**/ +VOID +EbcDebuggerHookEbcUnloadImage ( + IN EFI_HANDLE Handle + ) +{ + return; +} + +/** + + The hook in ExecuteEbcImageEntryPoint. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteEbcImageEntryPoint ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteEbcImageEntryPoint. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookEbcInterpret ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + The hook in EbcExecute, before ExecuteFunction. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteStart ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + The hook in EbcExecute, after ExecuteFunction. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteCALL, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLStart ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteCALL, after move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteCALL, before call EbcLLCALLEX. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEXStart ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteCALL, after call EbcLLCALLEX. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEXEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteRET, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookRETStart ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteRET, after move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookRETEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteJMP, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMPStart ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteJMP, after move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMPEnd ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteJMP8, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMP8Start ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} + +/** + + The hook in ExecuteJMP8, after move IP.. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMP8End ( + IN VM_CONTEXT *VmPtr + ) +{ + return; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.h new file mode 100644 index 000000000..b166e505a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDebuggerHook.h @@ -0,0 +1,241 @@ +/** @file + Prototypes for the EBC Debugger hooks. + + Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_EBC_DEBUGGER_HOOK_H_ +#define _EFI_EBC_DEBUGGER_HOOK_H_ + +#include + +#include +#include + +/** + The VM interpreter calls this function when an exception is detected. + + @param ExceptionType Specifies the processor exception detected. + @param ExceptionFlags Specifies the exception context. + @param VmPtr Pointer to a VM context for passing info to the + EFI debugger. + + @retval EFI_SUCCESS This function completed successfully. + +**/ +EFI_STATUS +EbcDebugSignalException ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EXCEPTION_FLAGS ExceptionFlags, + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in InitializeEbcDriver. + + @param Handle - The EbcDebugProtocol handle. + @param EbcDebugProtocol - The EbcDebugProtocol interface. + +**/ +VOID +EbcDebuggerHookInit ( + IN EFI_HANDLE Handle, + IN EFI_DEBUG_SUPPORT_PROTOCOL *EbcDebugProtocol + ); + +/** + +The hook in UnloadImage for EBC Interpreter. + +**/ +VOID +EbcDebuggerHookUnload ( + VOID + ); + +/** + + The hook in EbcUnloadImage. + Currently do nothing here. + + @param Handle The EbcImage handle. + +**/ +VOID +EbcDebuggerHookEbcUnloadImage ( + IN EFI_HANDLE Handle + ); + + +/** + + Hooks in EbcSupport.c + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteEbcImageEntryPoint ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteEbcImageEntryPoint. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookEbcInterpret ( + IN VM_CONTEXT *VmPtr + ); + + +/** + The hook in EbcExecute, before ExecuteFunction. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteStart ( + IN VM_CONTEXT *VmPtr + ); + +/** + The hook in EbcExecute, after ExecuteFunction. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookExecuteEnd ( + IN VM_CONTEXT *VmPtr + ); + +/** + The hook in ExecuteCALL, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLStart ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteCALL, after move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEnd ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteCALL, before call EbcLLCALLEX. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEXStart ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteCALL, after call EbcLLCALLEX. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookCALLEXEnd ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteRET, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookRETStart ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteRET, after move IP. + It will record trace information. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookRETEnd ( + IN VM_CONTEXT *VmPtr + ); + + +/** + + The hook in ExecuteJMP, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMPStart ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteJMP, after move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMPEnd ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteJMP8, before move IP. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMP8Start ( + IN VM_CONTEXT *VmPtr + ); + +/** + + The hook in ExecuteJMP8, after move IP.. + + @param VmPtr - pointer to VM context. + +**/ +VOID +EbcDebuggerHookJMP8End ( + IN VM_CONTEXT *VmPtr + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf new file mode 100644 index 000000000..a38700df5 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf @@ -0,0 +1,81 @@ +## @file +# Module that produces EBC Interprete and EBC Debug Support protocols. +# +# This module implements EFI Byte Code (EBC) Virtual Machine that can provide +# platform and processor-independent mechanisms for loading and executing EFI +# device drivers. +# +# Copyright (c) 2015, The Linux Foundation. All rights reserved. +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EbcDxe + MODULE_UNI_FILE = EbcDxe.uni + FILE_GUID = 13AC6DD0-73D0-11D4-B06B-00AA00BD6DE7 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeEbcDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + EbcDebuggerHook.h + EbcDebuggerHook.c + EbcExecute.h + EbcExecute.c + EbcInt.h + EbcInt.c + +[Sources.Ia32] + Ia32/EbcSupport.c + Ia32/EbcLowLevel.nasm + +[Sources.X64] + X64/EbcSupport.c + X64/EbcLowLevel.nasm + +[Sources.AARCH64] + AArch64/EbcSupport.c + AArch64/EbcLowLevel.S + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + CacheMaintenanceLib + MemoryAllocationLib + PeCoffLib + UefiBootServicesTableLib + BaseMemoryLib + UefiDriverEntryPoint + DebugLib + BaseLib + + +[Protocols] + gEfiDebugSupportProtocolGuid ## PRODUCES + gEfiEbcProtocolGuid ## PRODUCES + gEdkiiPeCoffImageEmulatorProtocolGuid ## PRODUCES + gEfiEbcVmTestProtocolGuid ## SOMETIMES_PRODUCES + gEfiEbcSimpleDebuggerProtocolGuid ## SOMETIMES_CONSUMES + +[Depex] + TRUE + +# [Event] +# +# Periodic timer event to support EFI debug support protocol for EBC image. +# +# EVENT_TYPE_PERIODIC_TIMER ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + EbcDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.uni b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.uni new file mode 100644 index 000000000..746d8b911 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxe.uni @@ -0,0 +1,18 @@ +// /** @file +// Module that produces EBC Interprete and EBC Debug Support protocols. +// +// This module implements EFI Byte Code (EBC) Virtual Machine that can provide +// platform and processor-independent mechanisms for loading and executing EFI +// device drivers. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Produces EBC Interpreter and EBC Debug Support protocols" + +#string STR_MODULE_DESCRIPTION #language en-US "This module implements EFI Byte Code (EBC) Virtual Machine that can provide platform and processor-independent mechanisms for loading and executing UEFI device drivers." + diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxeExtra.uni new file mode 100644 index 000000000..7b47f15a7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// EbcDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"EFI Byte Code DXE Interpreter" + + diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.c new file mode 100644 index 000000000..1c4a4f515 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.c @@ -0,0 +1,5383 @@ +/** @file + Contains code that implements the virtual machine. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EbcInt.h" +#include "EbcExecute.h" +#include "EbcDebuggerHook.h" + + +// +// Define some useful data size constants to allow switch statements based on +// size of operands or data. +// +#define DATA_SIZE_INVALID 0 +#define DATA_SIZE_8 1 +#define DATA_SIZE_16 2 +#define DATA_SIZE_32 4 +#define DATA_SIZE_64 8 +#define DATA_SIZE_N 48 // 4 or 8 +// +// Structure we'll use to dispatch opcodes to execute functions. +// +typedef struct { + EFI_STATUS (*ExecuteFunction) (IN VM_CONTEXT * VmPtr); +} +VM_TABLE_ENTRY; + +typedef +UINT64 +(*DATA_MANIP_EXEC_FUNCTION) ( + IN VM_CONTEXT * VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Decode a 16-bit index to determine the offset. Given an index value: + + b15 - sign bit + b14:12 - number of bits in this index assigned to natural units (=a) + ba:11 - constant units = ConstUnits + b0:a - natural units = NaturalUnits + + Given this info, the offset can be computed by: + offset = sign_bit * (ConstUnits + NaturalUnits * sizeof(UINTN)) + + Max offset is achieved with index = 0x7FFF giving an offset of + 0x27B (32-bit machine) or 0x477 (64-bit machine). + Min offset is achieved with index = + + @param VmPtr A pointer to VM context. + @param CodeOffset Offset from IP of the location of the 16-bit index + to decode. + + @return The decoded offset. + +**/ +INT16 +VmReadIndex16 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 CodeOffset + ); + +/** + Decode a 32-bit index to determine the offset. + + @param VmPtr A pointer to VM context. + @param CodeOffset Offset from IP of the location of the 32-bit index + to decode. + + @return Converted index per EBC VM specification. + +**/ +INT32 +VmReadIndex32 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 CodeOffset + ); + +/** + Decode a 64-bit index to determine the offset. + + @param VmPtr A pointer to VM context.s + @param CodeOffset Offset from IP of the location of the 64-bit index + to decode. + + @return Converted index per EBC VM specification + +**/ +INT64 +VmReadIndex64 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 CodeOffset + ); + +/** + Reads 8-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 8-bit value from the memory address. + +**/ +UINT8 +VmReadMem8 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ); + +/** + Reads 16-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 16-bit value from the memory address. + +**/ +UINT16 +VmReadMem16 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ); + +/** + Reads 32-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 32-bit value from the memory address. + +**/ +UINT32 +VmReadMem32 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ); + +/** + Reads 64-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 64-bit value from the memory address. + +**/ +UINT64 +VmReadMem64 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ); + +/** + Read a natural value from memory. May or may not be aligned. + + @param VmPtr current VM context + @param Addr the address to read from + + @return The natural value at address Addr. + +**/ +UINTN +VmReadMemN ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ); + +/** + Writes 8-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem8 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT8 Data + ); + +/** + Writes 16-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem16 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT16 Data + ); + +/** + Writes 32-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem32 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT32 Data + ); + +/** + Reads 16-bit unsigned data from the code stream. + + This routine provides the ability to read raw unsigned data from the code + stream. + + @param VmPtr A pointer to VM context + @param Offset Offset from current IP to the raw data to read. + + @return The raw unsigned 16-bit value from the code stream. + +**/ +UINT16 +VmReadCode16 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ); + +/** + Reads 32-bit unsigned data from the code stream. + + This routine provides the ability to read raw unsigned data from the code + stream. + + @param VmPtr A pointer to VM context + @param Offset Offset from current IP to the raw data to read. + + @return The raw unsigned 32-bit value from the code stream. + +**/ +UINT32 +VmReadCode32 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ); + +/** + Reads 64-bit unsigned data from the code stream. + + This routine provides the ability to read raw unsigned data from the code + stream. + + @param VmPtr A pointer to VM context + @param Offset Offset from current IP to the raw data to read. + + @return The raw unsigned 64-bit value from the code stream. + +**/ +UINT64 +VmReadCode64 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ); + +/** + Reads 8-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT8 +VmReadImmed8 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ); + +/** + Reads 16-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT16 +VmReadImmed16 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ); + +/** + Reads 32-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT32 +VmReadImmed32 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ); + +/** + Reads 64-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT64 +VmReadImmed64 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ); + +/** + Given an address that EBC is going to read from or write to, return + an appropriate address that accounts for a gap in the stack. + The stack for this application looks like this (high addr on top) + [EBC entry point arguments] + [VM stack] + [EBC stack] + The EBC assumes that its arguments are at the top of its stack, which + is where the VM stack is really. Therefore if the EBC does memory + accesses into the VM stack area, then we need to convert the address + to point to the EBC entry point arguments area. Do this here. + + @param VmPtr A Pointer to VM context. + @param Addr Address of interest + + @return The unchanged address if it's not in the VM stack region. Otherwise, + adjust for the stack gap and return the modified address. + +**/ +UINTN +ConvertStackAddr ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ); + +/** + Execute all the EBC data manipulation instructions. + Since the EBC data manipulation instructions all have the same basic form, + they can share the code that does the fetch of operands and the write-back + of the result. This function performs the fetch of the operands (even if + both are not needed to be fetched, like NOT instruction), dispatches to the + appropriate subfunction, then writes back the returned result. + + Format: + INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} + + @param VmPtr A pointer to VM context. + @param IsSignedOp Indicates whether the operand is signed or not. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteDataManip ( + IN VM_CONTEXT *VmPtr, + IN BOOLEAN IsSignedOp + ); + +// +// Functions that execute VM opcodes +// +/** + Execute the EBC BREAK instruction. + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteBREAK ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the JMP instruction. + + Instruction syntax: + JMP64{cs|cc} Immed64 + JMP32{cs|cc} {@}R1 {Immed32|Index32} + + Encoding: + b0.7 - immediate data present + b0.6 - 1 = 64 bit immediate data + 0 = 32 bit immediate data + b1.7 - 1 = conditional + b1.6 1 = CS (condition set) + 0 = CC (condition clear) + b1.4 1 = relative address + 0 = absolute address + b1.3 1 = operand1 indirect + b1.2-0 operand 1 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteJMP ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC JMP8 instruction. + + Instruction syntax: + JMP8{cs|cc} Offset/2 + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteJMP8 ( + IN VM_CONTEXT *VmPtr + ); + +/** + Implements the EBC CALL instruction. + + Instruction format: + CALL64 Immed64 + CALL32 {@}R1 {Immed32|Index32} + CALLEX64 Immed64 + CALLEX16 {@}R1 {Immed32} + + If Rx == R0, then it's a PC relative call to PC = PC + imm32. + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteCALL ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC RET instruction. + + Instruction syntax: + RET + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteRET ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC CMP instruction. + + Instruction syntax: + CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteCMP ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC CMPI instruction + + Instruction syntax: + CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteCMPI ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the MOVxx instructions. + + Instruction format: + + MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32} + MOVqq {@}R1 {Index64}, {@}R2 {Index64} + + Copies contents of [R2] -> [R1], zero extending where required. + + First character indicates the size of the move. + Second character indicates the size of the index(s). + + Invalid to have R1 direct with index. + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVxx ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC MOVI. + + Instruction syntax: + + MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64 + + First variable character specifies the move size + Second variable character specifies size of the immediate data + + Sign-extend the immediate data to the size of the operation, and zero-extend + if storing to a register. + + Operand1 direct with index/immed is invalid. + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVI ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC MOV immediate natural. This instruction moves an immediate + index value into a register or memory location. + + Instruction syntax: + + MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVIn ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC MOVREL instruction. + Dest <- Ip + ImmData + + Instruction syntax: + + MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVREL ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC PUSHn instruction + + Instruction syntax: + PUSHn {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePUSHn ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC PUSH instruction. + + Instruction syntax: + PUSH[32|64] {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePUSH ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC POPn instruction. + + Instruction syntax: + POPn {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePOPn ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC POP instruction. + + Instruction syntax: + POPn {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePOP ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute all the EBC signed data manipulation instructions. + Since the EBC data manipulation instructions all have the same basic form, + they can share the code that does the fetch of operands and the write-back + of the result. This function performs the fetch of the operands (even if + both are not needed to be fetched, like NOT instruction), dispatches to the + appropriate subfunction, then writes back the returned result. + + Format: + INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} + + @param VmPtr A pointer to VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteSignedDataManip ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute all the EBC unsigned data manipulation instructions. + Since the EBC data manipulation instructions all have the same basic form, + they can share the code that does the fetch of operands and the write-back + of the result. This function performs the fetch of the operands (even if + both are not needed to be fetched, like NOT instruction), dispatches to the + appropriate subfunction, then writes back the returned result. + + Format: + INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} + + @param VmPtr A pointer to VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteUnsignedDataManip ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC LOADSP instruction. + + Instruction syntax: + LOADSP SP1, R2 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteLOADSP ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC STORESP instruction. + + Instruction syntax: + STORESP Rx, FLAGS|IP + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteSTORESP ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC MOVsnw instruction. This instruction loads a signed + natural value from memory or register to another memory or register. On + 32-bit machines, the value gets sign-extended to 64 bits if the destination + is a register. + + Instruction syntax: + + MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32} + + 0:7 1=>operand1 index present + 0:6 1=>operand2 index present + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVsnd ( + IN VM_CONTEXT *VmPtr + ); + +/** + Execute the EBC MOVsnw instruction. This instruction loads a signed + natural value from memory or register to another memory or register. On + 32-bit machines, the value gets sign-extended to 64 bits if the destination + is a register. + + Instruction syntax: + + MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16} + + 0:7 1=>operand1 index present + 0:6 1=>operand2 index present + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVsnw ( + IN VM_CONTEXT *VmPtr + ); + +// +// Data manipulation subfunctions +// +/** + Execute the EBC NOT instruction.s + + Instruction syntax: + NOT[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return ~Op2 + +**/ +UINT64 +ExecuteNOT ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC NEG instruction. + + Instruction syntax: + NEG[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op2 * -1 + +**/ +UINT64 +ExecuteNEG ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC ADD instruction. + + Instruction syntax: + ADD[32|64] {@}R1, {@}R2 {Index16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 + Op2 + +**/ +UINT64 +ExecuteADD ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC SUB instruction. + + Instruction syntax: + SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 - Op2 + +**/ +UINT64 +ExecuteSUB ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC MUL instruction. + + Instruction syntax: + SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 * Op2 + +**/ +UINT64 +ExecuteMUL ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC MULU instruction + + Instruction syntax: + MULU[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (unsigned)Op1 * (unsigned)Op2 + +**/ +UINT64 +ExecuteMULU ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC DIV instruction. + + Instruction syntax: + DIV[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 / Op2 + +**/ +UINT64 +ExecuteDIV ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC DIVU instruction + + Instruction syntax: + DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (unsigned)Op1 / (unsigned)Op2 + +**/ +UINT64 +ExecuteDIVU ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC MOD instruction. + + Instruction syntax: + MOD[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 MODULUS Op2 + +**/ +UINT64 +ExecuteMOD ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC MODU instruction. + + Instruction syntax: + MODU[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 UNSIGNED_MODULUS Op2 + +**/ +UINT64 +ExecuteMODU ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC AND instruction. + + Instruction syntax: + AND[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 AND Op2 + +**/ +UINT64 +ExecuteAND ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC OR instruction. + + Instruction syntax: + OR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 OR Op2 + +**/ +UINT64 +ExecuteOR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC XOR instruction. + + Instruction syntax: + XOR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 XOR Op2 + +**/ +UINT64 +ExecuteXOR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC SHL shift left instruction. + + Instruction syntax: + SHL[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 << Op2 + +**/ +UINT64 +ExecuteSHL ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC SHR instruction. + + Instruction syntax: + SHR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 >> Op2 (unsigned operands) + +**/ +UINT64 +ExecuteSHR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC ASHR instruction. + + Instruction syntax: + ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 >> Op2 (signed) + +**/ +UINT64 +ExecuteASHR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC EXTNDB instruction to sign-extend a byte value. + + Instruction syntax: + EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (INT64)(INT8)Op2 + +**/ +UINT64 +ExecuteEXTNDB ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC EXTNDW instruction to sign-extend a 16-bit value. + + Instruction syntax: + EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (INT64)(INT16)Op2 + +**/ +UINT64 +ExecuteEXTNDW ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +/** + Execute the EBC EXTNDD instruction to sign-extend a 32-bit value. + + Instruction syntax: + EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (INT64)(INT32)Op2 + +**/ +UINT64 +ExecuteEXTNDD ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ); + +// +// Once we retrieve the operands for the data manipulation instructions, +// call these functions to perform the operation. +// +CONST DATA_MANIP_EXEC_FUNCTION mDataManipDispatchTable[] = { + ExecuteNOT, + ExecuteNEG, + ExecuteADD, + ExecuteSUB, + ExecuteMUL, + ExecuteMULU, + ExecuteDIV, + ExecuteDIVU, + ExecuteMOD, + ExecuteMODU, + ExecuteAND, + ExecuteOR, + ExecuteXOR, + ExecuteSHL, + ExecuteSHR, + ExecuteASHR, + ExecuteEXTNDB, + ExecuteEXTNDW, + ExecuteEXTNDD, +}; + +CONST VM_TABLE_ENTRY mVmOpcodeTable[] = { + { ExecuteBREAK }, // opcode 0x00 + { ExecuteJMP }, // opcode 0x01 + { ExecuteJMP8 }, // opcode 0x02 + { ExecuteCALL }, // opcode 0x03 + { ExecuteRET }, // opcode 0x04 + { ExecuteCMP }, // opcode 0x05 CMPeq + { ExecuteCMP }, // opcode 0x06 CMPlte + { ExecuteCMP }, // opcode 0x07 CMPgte + { ExecuteCMP }, // opcode 0x08 CMPulte + { ExecuteCMP }, // opcode 0x09 CMPugte + { ExecuteUnsignedDataManip }, // opcode 0x0A NOT + { ExecuteSignedDataManip }, // opcode 0x0B NEG + { ExecuteSignedDataManip }, // opcode 0x0C ADD + { ExecuteSignedDataManip }, // opcode 0x0D SUB + { ExecuteSignedDataManip }, // opcode 0x0E MUL + { ExecuteUnsignedDataManip }, // opcode 0x0F MULU + { ExecuteSignedDataManip }, // opcode 0x10 DIV + { ExecuteUnsignedDataManip }, // opcode 0x11 DIVU + { ExecuteSignedDataManip }, // opcode 0x12 MOD + { ExecuteUnsignedDataManip }, // opcode 0x13 MODU + { ExecuteUnsignedDataManip }, // opcode 0x14 AND + { ExecuteUnsignedDataManip }, // opcode 0x15 OR + { ExecuteUnsignedDataManip }, // opcode 0x16 XOR + { ExecuteUnsignedDataManip }, // opcode 0x17 SHL + { ExecuteUnsignedDataManip }, // opcode 0x18 SHR + { ExecuteSignedDataManip }, // opcode 0x19 ASHR + { ExecuteUnsignedDataManip }, // opcode 0x1A EXTNDB + { ExecuteUnsignedDataManip }, // opcode 0x1B EXTNDW + { ExecuteUnsignedDataManip }, // opcode 0x1C EXTNDD + { ExecuteMOVxx }, // opcode 0x1D MOVBW + { ExecuteMOVxx }, // opcode 0x1E MOVWW + { ExecuteMOVxx }, // opcode 0x1F MOVDW + { ExecuteMOVxx }, // opcode 0x20 MOVQW + { ExecuteMOVxx }, // opcode 0x21 MOVBD + { ExecuteMOVxx }, // opcode 0x22 MOVWD + { ExecuteMOVxx }, // opcode 0x23 MOVDD + { ExecuteMOVxx }, // opcode 0x24 MOVQD + { ExecuteMOVsnw }, // opcode 0x25 MOVsnw + { ExecuteMOVsnd }, // opcode 0x26 MOVsnd + { NULL }, // opcode 0x27 + { ExecuteMOVxx }, // opcode 0x28 MOVqq + { ExecuteLOADSP }, // opcode 0x29 LOADSP SP1, R2 + { ExecuteSTORESP }, // opcode 0x2A STORESP R1, SP2 + { ExecutePUSH }, // opcode 0x2B PUSH {@}R1 [imm16] + { ExecutePOP }, // opcode 0x2C POP {@}R1 [imm16] + { ExecuteCMPI }, // opcode 0x2D CMPIEQ + { ExecuteCMPI }, // opcode 0x2E CMPILTE + { ExecuteCMPI }, // opcode 0x2F CMPIGTE + { ExecuteCMPI }, // opcode 0x30 CMPIULTE + { ExecuteCMPI }, // opcode 0x31 CMPIUGTE + { ExecuteMOVxx }, // opcode 0x32 MOVN + { ExecuteMOVxx }, // opcode 0x33 MOVND + { NULL }, // opcode 0x34 + { ExecutePUSHn }, // opcode 0x35 + { ExecutePOPn }, // opcode 0x36 + { ExecuteMOVI }, // opcode 0x37 - mov immediate data + { ExecuteMOVIn }, // opcode 0x38 - mov immediate natural + { ExecuteMOVREL }, // opcode 0x39 - move data relative to PC + { NULL }, // opcode 0x3a + { NULL }, // opcode 0x3b + { NULL }, // opcode 0x3c + { NULL }, // opcode 0x3d + { NULL }, // opcode 0x3e + { NULL } // opcode 0x3f +}; + +// +// Length of JMP instructions, depending on upper two bits of opcode. +// +CONST UINT8 mJMPLen[] = { 2, 2, 6, 10 }; + +/** + Given a pointer to a new VM context, execute one or more instructions. This + function is only used for test purposes via the EBC VM test protocol. + + @param This A pointer to the EFI_EBC_VM_TEST_PROTOCOL structure. + @param VmPtr A pointer to a VM context. + @param InstructionCount A pointer to a UINTN value holding the number of + instructions to execute. If it holds value of 0, + then the instruction to be executed is 1. + + @retval EFI_UNSUPPORTED At least one of the opcodes is not supported. + @retval EFI_SUCCESS All of the instructions are executed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcExecuteInstructions ( + IN EFI_EBC_VM_TEST_PROTOCOL *This, + IN VM_CONTEXT *VmPtr, + IN OUT UINTN *InstructionCount + ) +{ + UINTN ExecFunc; + EFI_STATUS Status; + UINTN InstructionsLeft; + UINTN SavedInstructionCount; + + Status = EFI_SUCCESS; + + if (*InstructionCount == 0) { + InstructionsLeft = 1; + } else { + InstructionsLeft = *InstructionCount; + } + + SavedInstructionCount = *InstructionCount; + *InstructionCount = 0; + + // + // Index into the opcode table using the opcode byte for this instruction. + // This gives you the execute function, which we first test for null, then + // call it if it's not null. + // + while (InstructionsLeft != 0) { + ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction; + if (ExecFunc == (UINTN) NULL) { + EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr); + return EFI_UNSUPPORTED; + } else { + mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction (VmPtr); + *InstructionCount = *InstructionCount + 1; + } + + // + // Decrement counter if applicable + // + if (SavedInstructionCount != 0) { + InstructionsLeft--; + } + } + + return Status; +} + + +/** + Execute an EBC image from an entry point or from a published protocol. + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED At least one of the opcodes is not supported. + @retval EFI_SUCCESS All of the instructions are executed successfully. + +**/ +EFI_STATUS +EbcExecute ( + IN VM_CONTEXT *VmPtr + ) +{ + UINTN ExecFunc; + UINT8 StackCorrupted; + EFI_STATUS Status; + EFI_EBC_SIMPLE_DEBUGGER_PROTOCOL *EbcSimpleDebugger; + + mVmPtr = VmPtr; + EbcSimpleDebugger = NULL; + Status = EFI_SUCCESS; + StackCorrupted = 0; + + // + // Make sure the magic value has been put on the stack before we got here. + // + if (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE) { + StackCorrupted = 1; + } + + VmPtr->FramePtr = (VOID *) ((UINT8 *) (UINTN) VmPtr->Gpr[0] + 8); + + // + // Try to get the debug support for EBC + // + DEBUG_CODE_BEGIN (); + Status = gBS->LocateProtocol ( + &gEfiEbcSimpleDebuggerProtocolGuid, + NULL, + (VOID **) &EbcSimpleDebugger + ); + if (EFI_ERROR (Status)) { + EbcSimpleDebugger = NULL; + } + DEBUG_CODE_END (); + + // + // Save the start IP for debug. For example, if we take an exception we + // can print out the location of the exception relative to the entry point, + // which could then be used in a disassembly listing to find the problem. + // + VmPtr->EntryPoint = (VOID *) VmPtr->Ip; + + // + // We'll wait for this flag to know when we're done. The RET + // instruction sets it if it runs out of stack. + // + VmPtr->StopFlags = 0; + while ((VmPtr->StopFlags & STOPFLAG_APP_DONE) == 0) { + // + // If we've found a simple debugger protocol, call it + // + DEBUG_CODE_BEGIN (); + if (EbcSimpleDebugger != NULL) { + EbcSimpleDebugger->Debugger (EbcSimpleDebugger, VmPtr); + } + DEBUG_CODE_END (); + + // + // Use the opcode bits to index into the opcode dispatch table. If the + // function pointer is null then generate an exception. + // + ExecFunc = (UINTN) mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction; + if (ExecFunc == (UINTN) NULL) { + EbcDebugSignalException (EXCEPT_EBC_INVALID_OPCODE, EXCEPTION_FLAG_FATAL, VmPtr); + Status = EFI_UNSUPPORTED; + goto Done; + } + + EbcDebuggerHookExecuteStart (VmPtr); + + // + // The EBC VM is a strongly ordered processor, so perform a fence operation before + // and after each instruction is executed. + // + MemoryFence (); + + mVmOpcodeTable[(*VmPtr->Ip & OPCODE_M_OPCODE)].ExecuteFunction (VmPtr); + + MemoryFence (); + + EbcDebuggerHookExecuteEnd (VmPtr); + + // + // If the step flag is set, signal an exception and continue. We don't + // clear it here. Assuming the debugger is responsible for clearing it. + // + if (VMFLAG_ISSET (VmPtr, VMFLAGS_STEP)) { + EbcDebugSignalException (EXCEPT_EBC_STEP, EXCEPTION_FLAG_NONE, VmPtr); + } + // + // Make sure stack has not been corrupted. Only report it once though. + // + if ((StackCorrupted == 0) && (*VmPtr->StackMagicPtr != (UINTN) VM_STACK_KEY_VALUE)) { + EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT, EXCEPTION_FLAG_FATAL, VmPtr); + StackCorrupted = 1; + } + if ((StackCorrupted == 0) && ((UINT64)VmPtr->Gpr[0] <= (UINT64)(UINTN) VmPtr->StackTop)) { + EbcDebugSignalException (EXCEPT_EBC_STACK_FAULT, EXCEPTION_FLAG_FATAL, VmPtr); + StackCorrupted = 1; + } + } + +Done: + mVmPtr = NULL; + + return Status; +} + + +/** + Execute the MOVxx instructions. + + Instruction format: + + MOV[b|w|d|q|n]{w|d} {@}R1 {Index16|32}, {@}R2 {Index16|32} + MOVqq {@}R1 {Index64}, {@}R2 {Index64} + + Copies contents of [R2] -> [R1], zero extending where required. + + First character indicates the size of the move. + Second character indicates the size of the index(s). + + Invalid to have R1 direct with index. + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVxx ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 OpcMasked; + UINT8 Operands; + UINT8 Size; + UINT8 MoveSize; + INT16 Index16; + INT32 Index32; + INT64 Index64Op1; + INT64 Index64Op2; + UINT64 Data64; + UINT64 DataMask; + UINTN Source; + + Opcode = GETOPCODE (VmPtr); + OpcMasked = (UINT8) (Opcode & OPCODE_M_OPCODE); + + // + // Get the operands byte so we can get R1 and R2 + // + Operands = GETOPERANDS (VmPtr); + + // + // Assume no indexes + // + Index64Op1 = 0; + Index64Op2 = 0; + Data64 = 0; + + // + // Determine if we have an index/immediate data. Base instruction size + // is 2 (opcode + operands). Add to this size each index specified. + // + Size = 2; + if ((Opcode & (OPCODE_M_IMMED_OP1 | OPCODE_M_IMMED_OP2)) != 0) { + // + // Determine size of the index from the opcode. Then get it. + // + if ((OpcMasked <= OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVNW)) { + // + // MOVBW, MOVWW, MOVDW, MOVQW, and MOVNW have 16-bit immediate index. + // Get one or both index values. + // + if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { + Index16 = VmReadIndex16 (VmPtr, 2); + Index64Op1 = (INT64) Index16; + Size += sizeof (UINT16); + } + + if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { + Index16 = VmReadIndex16 (VmPtr, Size); + Index64Op2 = (INT64) Index16; + Size += sizeof (UINT16); + } + } else if ((OpcMasked <= OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVND)) { + // + // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index + // + if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { + Index32 = VmReadIndex32 (VmPtr, 2); + Index64Op1 = (INT64) Index32; + Size += sizeof (UINT32); + } + + if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { + Index32 = VmReadIndex32 (VmPtr, Size); + Index64Op2 = (INT64) Index32; + Size += sizeof (UINT32); + } + } else if (OpcMasked == OPCODE_MOVQQ) { + // + // MOVqq -- only form with a 64-bit index + // + if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { + Index64Op1 = VmReadIndex64 (VmPtr, 2); + Size += sizeof (UINT64); + } + + if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { + Index64Op2 = VmReadIndex64 (VmPtr, Size); + Size += sizeof (UINT64); + } + } else { + // + // Obsolete MOVBQ, MOVWQ, MOVDQ, and MOVNQ have 64-bit immediate index + // + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + } + // + // Determine the size of the move, and create a mask for it so we can + // clear unused bits. + // + if ((OpcMasked == OPCODE_MOVBW) || (OpcMasked == OPCODE_MOVBD)) { + MoveSize = DATA_SIZE_8; + DataMask = 0xFF; + } else if ((OpcMasked == OPCODE_MOVWW) || (OpcMasked == OPCODE_MOVWD)) { + MoveSize = DATA_SIZE_16; + DataMask = 0xFFFF; + } else if ((OpcMasked == OPCODE_MOVDW) || (OpcMasked == OPCODE_MOVDD)) { + MoveSize = DATA_SIZE_32; + DataMask = 0xFFFFFFFF; + } else if ((OpcMasked == OPCODE_MOVQW) || (OpcMasked == OPCODE_MOVQD) || (OpcMasked == OPCODE_MOVQQ)) { + MoveSize = DATA_SIZE_64; + DataMask = (UINT64)~0; + } else if ((OpcMasked == OPCODE_MOVNW) || (OpcMasked == OPCODE_MOVND)) { + MoveSize = DATA_SIZE_N; + DataMask = (UINT64)~0 >> (64 - 8 * sizeof (UINTN)); + } else { + // + // We were dispatched to this function and we don't recognize the opcode + // + EbcDebugSignalException (EXCEPT_EBC_UNDEFINED, EXCEPTION_FLAG_FATAL, VmPtr); + return EFI_UNSUPPORTED; + } + // + // Now get the source address + // + if (OPERAND2_INDIRECT (Operands)) { + // + // Indirect form @R2. Compute address of operand2 + // + Source = (UINTN) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index64Op2); + // + // Now get the data from the source. Always 0-extend and let the compiler + // sign-extend where required. + // + switch (MoveSize) { + case DATA_SIZE_8: + Data64 = (UINT64) (UINT8) VmReadMem8 (VmPtr, Source); + break; + + case DATA_SIZE_16: + Data64 = (UINT64) (UINT16) VmReadMem16 (VmPtr, Source); + break; + + case DATA_SIZE_32: + Data64 = (UINT64) (UINT32) VmReadMem32 (VmPtr, Source); + break; + + case DATA_SIZE_64: + Data64 = (UINT64) VmReadMem64 (VmPtr, Source); + break; + + case DATA_SIZE_N: + Data64 = (UINT64) (UINTN) VmReadMemN (VmPtr, Source); + break; + + default: + // + // not reached + // + break; + } + } else { + // + // Not indirect source: MOVxx {@}Rx, Ry [Index] + // + Data64 = (UINT64) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index64Op2); + // + // Did Operand2 have an index? If so, treat as two signed values since + // indexes are signed values. + // + if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { + // + // NOTE: need to find a way to fix this, most likely by changing the VM + // implementation to remove the stack gap. To do that, we'd need to + // allocate stack space for the VM and actually set the system + // stack pointer to the allocated buffer when the VM starts. + // + // Special case -- if someone took the address of a function parameter + // then we need to make sure it's not in the stack gap. We can identify + // this situation if (Operand2 register == 0) && (Operand2 is direct) + // && (Index applies to Operand2) && (Index > 0) && (Operand1 register != 0) + // Situations that to be aware of: + // * stack adjustments at beginning and end of functions R0 = R0 += stacksize + // + if ((OPERAND2_REGNUM (Operands) == 0) && + (!OPERAND2_INDIRECT (Operands)) && + (Index64Op2 > 0) && + (OPERAND1_REGNUM (Operands) == 0) && + (OPERAND1_INDIRECT (Operands)) + ) { + Data64 = (UINT64) ConvertStackAddr (VmPtr, (UINTN) (INT64) Data64); + } + } + } + // + // Now write it back + // + if (OPERAND1_INDIRECT (Operands)) { + // + // Reuse the Source variable to now be dest. + // + Source = (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index64Op1); + // + // Do the write based on the size + // + switch (MoveSize) { + case DATA_SIZE_8: + VmWriteMem8 (VmPtr, Source, (UINT8) Data64); + break; + + case DATA_SIZE_16: + VmWriteMem16 (VmPtr, Source, (UINT16) Data64); + break; + + case DATA_SIZE_32: + VmWriteMem32 (VmPtr, Source, (UINT32) Data64); + break; + + case DATA_SIZE_64: + VmWriteMem64 (VmPtr, Source, Data64); + break; + + case DATA_SIZE_N: + VmWriteMemN (VmPtr, Source, (UINTN) Data64); + break; + + default: + // + // not reached + // + break; + } + } else { + // + // Operand1 direct. + // Make sure we didn't have an index on operand1. + // + if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + // + // Direct storage in register. Clear unused bits and store back to + // register. + // + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Data64 & DataMask; + } + // + // Advance the instruction pointer + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC BREAK instruction. + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteBREAK ( + IN VM_CONTEXT *VmPtr + ) +{ + EFI_STATUS Status; + UINT8 Operands; + VOID *EbcEntryPoint; + VOID *Thunk; + UINT64 U64EbcEntryPoint; + INT32 Offset; + + Thunk = NULL; + Operands = GETOPERANDS (VmPtr); + switch (Operands) { + // + // Runaway program break. Generate an exception and terminate + // + case 0: + EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr); + break; + + // + // Get VM version -- return VM revision number in R7 + // + case 1: + // + // Bits: + // 63-17 = 0 + // 16-8 = Major version + // 7-0 = Minor version + // + VmPtr->Gpr[7] = GetVmVersion (); + break; + + // + // Debugger breakpoint + // + case 3: + VmPtr->StopFlags |= STOPFLAG_BREAKPOINT; + // + // See if someone has registered a handler + // + EbcDebugSignalException ( + EXCEPT_EBC_BREAKPOINT, + EXCEPTION_FLAG_NONE, + VmPtr + ); + break; + + // + // System call, which there are none, so NOP it. + // + case 4: + break; + + // + // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot) + // "offset from self" pointer to the EBC entry point. + // After we're done, *(UINT64 *)R7 will be the address of the new thunk. + // + case 5: + Offset = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[7]); + U64EbcEntryPoint = (UINT64) (VmPtr->Gpr[7] + Offset + 4); + EbcEntryPoint = (VOID *) (UINTN) U64EbcEntryPoint; + + // + // Now create a new thunk + // + Status = EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, 0); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Finally replace the EBC entry point memory with the thunk address + // + VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[7], (UINT64) (UINTN) Thunk); + break; + + // + // Compiler setting version per value in R7 + // + case 6: + VmPtr->CompilerVersion = (UINT32) VmPtr->Gpr[7]; + // + // Check compiler version against VM version? + // + break; + + // + // Unhandled break code. Signal exception. + // + default: + EbcDebugSignalException (EXCEPT_EBC_BAD_BREAK, EXCEPTION_FLAG_FATAL, VmPtr); + break; + } + // + // Advance IP + // + VmPtr->Ip += 2; + return EFI_SUCCESS; +} + + +/** + Execute the JMP instruction. + + Instruction syntax: + JMP64{cs|cc} Immed64 + JMP32{cs|cc} {@}R1 {Immed32|Index32} + + Encoding: + b0.7 - immediate data present + b0.6 - 1 = 64 bit immediate data + 0 = 32 bit immediate data + b1.7 - 1 = conditional + b1.6 1 = CS (condition set) + 0 = CC (condition clear) + b1.4 1 = relative address + 0 = absolute address + b1.3 1 = operand1 indirect + b1.2-0 operand 1 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteJMP ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 CompareSet; + UINT8 ConditionFlag; + UINT8 Size; + UINT8 Operand; + UINT64 Data64; + INT32 Index32; + UINTN Addr; + + Operand = GETOPERANDS (VmPtr); + Opcode = GETOPCODE (VmPtr); + + // + // Get instruction length from the opcode. The upper two bits are used here + // to index into the length array. + // + Size = mJMPLen[(Opcode >> 6) & 0x03]; + + // + // Decode instruction conditions + // If we haven't met the condition, then simply advance the IP and return. + // + CompareSet = (UINT8) (((Operand & JMP_M_CS) != 0) ? 1 : 0); + ConditionFlag = (UINT8) VMFLAG_ISSET (VmPtr, VMFLAGS_CC); + if ((Operand & CONDITION_M_CONDITIONAL) != 0) { + if (CompareSet != ConditionFlag) { + EbcDebuggerHookJMPStart (VmPtr); + VmPtr->Ip += Size; + EbcDebuggerHookJMPEnd (VmPtr); + return EFI_SUCCESS; + } + } + // + // Check for 64-bit form and do it right away since it's the most + // straight-forward form. + // + if ((Opcode & OPCODE_M_IMMDATA64) != 0) { + // + // Double check for immediate-data, which is required. If not there, + // then signal an exception + // + if ((Opcode & OPCODE_M_IMMDATA) == 0) { + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_ERROR, + VmPtr + ); + return EFI_UNSUPPORTED; + } + // + // 64-bit immediate data is full address. Read the immediate data, + // check for alignment, and jump absolute. + // + Data64 = (UINT64) VmReadImmed64 (VmPtr, 2); + if (!IS_ALIGNED ((UINTN) Data64, sizeof (UINT16))) { + EbcDebugSignalException ( + EXCEPT_EBC_ALIGNMENT_CHECK, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + + return EFI_UNSUPPORTED; + } + + // + // Take jump -- relative or absolute + // + EbcDebuggerHookJMPStart (VmPtr); + if ((Operand & JMP_M_RELATIVE) != 0) { + VmPtr->Ip += (UINTN) Data64 + Size; + } else { + VmPtr->Ip = (VMIP) (UINTN) Data64; + } + EbcDebuggerHookJMPEnd (VmPtr); + + return EFI_SUCCESS; + } + // + // 32-bit forms: + // Get the index if there is one. May be either an index, or an immediate + // offset depending on indirect operand. + // JMP32 @R1 Index32 -- immediate data is an index + // JMP32 R1 Immed32 -- immedate data is an offset + // + if ((Opcode & OPCODE_M_IMMDATA) != 0) { + if (OPERAND1_INDIRECT (Operand)) { + Index32 = VmReadIndex32 (VmPtr, 2); + } else { + Index32 = VmReadImmed32 (VmPtr, 2); + } + } else { + Index32 = 0; + } + // + // Get the register data. If R == 0, then special case where it's ignored. + // + if (OPERAND1_REGNUM (Operand) == 0) { + Data64 = 0; + } else { + Data64 = (UINT64) OPERAND1_REGDATA (VmPtr, Operand); + } + // + // Decode the forms + // + if (OPERAND1_INDIRECT (Operand)) { + // + // Form: JMP32 @Rx {Index32} + // + Addr = VmReadMemN (VmPtr, (UINTN) Data64 + Index32); + if (!IS_ALIGNED ((UINTN) Addr, sizeof (UINT16))) { + EbcDebugSignalException ( + EXCEPT_EBC_ALIGNMENT_CHECK, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + + return EFI_UNSUPPORTED; + } + + EbcDebuggerHookJMPStart (VmPtr); + if ((Operand & JMP_M_RELATIVE) != 0) { + VmPtr->Ip += (UINTN) Addr + Size; + } else { + VmPtr->Ip = (VMIP) Addr; + } + EbcDebuggerHookJMPEnd (VmPtr); + + } else { + // + // Form: JMP32 Rx {Immed32} + // + Addr = (UINTN) (Data64 + Index32); + if (!IS_ALIGNED ((UINTN) Addr, sizeof (UINT16))) { + EbcDebugSignalException ( + EXCEPT_EBC_ALIGNMENT_CHECK, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + + return EFI_UNSUPPORTED; + } + + EbcDebuggerHookJMPStart (VmPtr); + if ((Operand & JMP_M_RELATIVE) != 0) { + VmPtr->Ip += (UINTN) Addr + Size; + } else { + VmPtr->Ip = (VMIP) Addr; + } + EbcDebuggerHookJMPEnd (VmPtr); + + } + + return EFI_SUCCESS; +} + + +/** + Execute the EBC JMP8 instruction. + + Instruction syntax: + JMP8{cs|cc} Offset/2 + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteJMP8 ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 ConditionFlag; + UINT8 CompareSet; + INT8 Offset; + + // + // Decode instruction. + // + Opcode = GETOPCODE (VmPtr); + CompareSet = (UINT8) (((Opcode & JMP_M_CS) != 0) ? 1 : 0); + ConditionFlag = (UINT8) VMFLAG_ISSET (VmPtr, VMFLAGS_CC); + + // + // If we haven't met the condition, then simply advance the IP and return + // + if ((Opcode & CONDITION_M_CONDITIONAL) != 0) { + if (CompareSet != ConditionFlag) { + EbcDebuggerHookJMP8Start (VmPtr); + VmPtr->Ip += 2; + EbcDebuggerHookJMP8End (VmPtr); + return EFI_SUCCESS; + } + } + // + // Get the offset from the instruction stream. It's relative to the + // following instruction, and divided by 2. + // + Offset = VmReadImmed8 (VmPtr, 1); + // + // Want to check for offset == -2 and then raise an exception? + // + EbcDebuggerHookJMP8Start (VmPtr); + VmPtr->Ip += (Offset * 2) + 2; + EbcDebuggerHookJMP8End (VmPtr); + return EFI_SUCCESS; +} + + +/** + Execute the EBC MOVI. + + Instruction syntax: + + MOVI[b|w|d|q][w|d|q] {@}R1 {Index16}, ImmData16|32|64 + + First variable character specifies the move size + Second variable character specifies size of the immediate data + + Sign-extend the immediate data to the size of the operation, and zero-extend + if storing to a register. + + Operand1 direct with index/immed is invalid. + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVI ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT8 Size; + INT16 Index16; + INT64 ImmData64; + UINT64 Op1; + UINT64 Mask64; + + // + // Get the opcode and operands byte so we can get R1 and R2 + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + // + // Get the index (16-bit) if present + // + if ((Operands & MOVI_M_IMMDATA) != 0) { + Index16 = VmReadIndex16 (VmPtr, 2); + Size = 4; + } else { + Index16 = 0; + Size = 2; + } + // + // Extract the immediate data. Sign-extend always. + // + if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { + ImmData64 = (INT64) (INT16) VmReadImmed16 (VmPtr, Size); + Size += 2; + } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { + ImmData64 = (INT64) (INT32) VmReadImmed32 (VmPtr, Size); + Size += 4; + } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { + ImmData64 = (INT64) VmReadImmed64 (VmPtr, Size); + Size += 8; + } else { + // + // Invalid encoding + // + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + // + // Now write back the result + // + if (!OPERAND1_INDIRECT (Operands)) { + // + // Operand1 direct. Make sure it didn't have an index. + // + if ((Operands & MOVI_M_IMMDATA) != 0) { + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + // + // Writing directly to a register. Clear unused bits. + // + if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH8) { + Mask64 = 0x000000FF; + } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH16) { + Mask64 = 0x0000FFFF; + } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH32) { + Mask64 = 0x00000000FFFFFFFF; + } else { + Mask64 = (UINT64)~0; + } + + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmData64 & Mask64; + } else { + // + // Get the address then write back based on size of the move + // + Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; + if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH8) { + VmWriteMem8 (VmPtr, (UINTN) Op1, (UINT8) ImmData64); + } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH16) { + VmWriteMem16 (VmPtr, (UINTN) Op1, (UINT16) ImmData64); + } else if ((Operands & MOVI_M_MOVEWIDTH) == MOVI_MOVEWIDTH32) { + VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) ImmData64); + } else { + VmWriteMem64 (VmPtr, (UINTN) Op1, (UINT64) ImmData64); + } + } + // + // Advance the instruction pointer + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC MOV immediate natural. This instruction moves an immediate + index value into a register or memory location. + + Instruction syntax: + + MOVIn[w|d|q] {@}R1 {Index16}, Index16|32|64 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVIn ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT8 Size; + INT16 Index16; + INT16 ImmedIndex16; + INT32 ImmedIndex32; + INT64 ImmedIndex64; + UINT64 Op1; + + // + // Get the opcode and operands byte so we can get R1 and R2 + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + // + // Get the operand1 index (16-bit) if present + // + if ((Operands & MOVI_M_IMMDATA) != 0) { + Index16 = VmReadIndex16 (VmPtr, 2); + Size = 4; + } else { + Index16 = 0; + Size = 2; + } + // + // Extract the immediate data and convert to a 64-bit index. + // + if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { + ImmedIndex16 = VmReadIndex16 (VmPtr, Size); + ImmedIndex64 = (INT64) ImmedIndex16; + Size += 2; + } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { + ImmedIndex32 = VmReadIndex32 (VmPtr, Size); + ImmedIndex64 = (INT64) ImmedIndex32; + Size += 4; + } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { + ImmedIndex64 = VmReadIndex64 (VmPtr, Size); + Size += 8; + } else { + // + // Invalid encoding + // + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + // + // Now write back the result + // + if (!OPERAND1_INDIRECT (Operands)) { + // + // Check for MOVIn R1 Index16, Immed (not indirect, with index), which + // is illegal + // + if ((Operands & MOVI_M_IMMDATA) != 0) { + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmedIndex64; + } else { + // + // Get the address + // + Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; + VmWriteMemN (VmPtr, (UINTN) Op1, (UINTN)(INTN) ImmedIndex64); + } + // + // Advance the instruction pointer + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC MOVREL instruction. + Dest <- Ip + ImmData + + Instruction syntax: + + MOVREL[w|d|q] {@}R1 {Index16}, ImmData16|32|64 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVREL ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT8 Size; + INT16 Index16; + INT64 ImmData64; + UINT64 Op1; + UINT64 Op2; + + // + // Get the opcode and operands byte so we can get R1 and R2 + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + // + // Get the Operand 1 index (16-bit) if present + // + if ((Operands & MOVI_M_IMMDATA) != 0) { + Index16 = VmReadIndex16 (VmPtr, 2); + Size = 4; + } else { + Index16 = 0; + Size = 2; + } + // + // Get the immediate data. + // + if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) { + ImmData64 = (INT64) VmReadImmed16 (VmPtr, Size); + Size += 2; + } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) { + ImmData64 = (INT64) VmReadImmed32 (VmPtr, Size); + Size += 4; + } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) { + ImmData64 = VmReadImmed64 (VmPtr, Size); + Size += 8; + } else { + // + // Invalid encoding + // + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + // + // Compute the value and write back the result + // + Op2 = (UINT64) ((INT64) ((UINT64) (UINTN) VmPtr->Ip) + (INT64) ImmData64 + Size); + if (!OPERAND1_INDIRECT (Operands)) { + // + // Check for illegal combination of operand1 direct with immediate data + // + if ((Operands & MOVI_M_IMMDATA) != 0) { + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (VM_REGISTER) Op2; + } else { + // + // Get the address = [Rx] + Index16 + // Write back the result. Always a natural size write, since + // we're talking addresses here. + // + Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; + VmWriteMemN (VmPtr, (UINTN) Op1, (UINTN) Op2); + } + // + // Advance the instruction pointer + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC MOVsnw instruction. This instruction loads a signed + natural value from memory or register to another memory or register. On + 32-bit machines, the value gets sign-extended to 64 bits if the destination + is a register. + + Instruction syntax: + + MOVsnw {@}R1 {Index16}, {@}R2 {Index16|Immed16} + + 0:7 1=>operand1 index present + 0:6 1=>operand2 index present + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVsnw ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT8 Size; + INT16 Op1Index; + INT16 Op2Index; + UINT64 Op2; + + // + // Get the opcode and operand bytes + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + Op1Index = Op2Index = 0; + + // + // Get the indexes if present. + // + Size = 2; + if ((Opcode & OPCODE_M_IMMED_OP1) !=0) { + if (OPERAND1_INDIRECT (Operands)) { + Op1Index = VmReadIndex16 (VmPtr, 2); + } else { + // + // Illegal form operand1 direct with index: MOVsnw R1 Index16, {@}R2 + // + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + + Size += sizeof (UINT16); + } + + if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { + if (OPERAND2_INDIRECT (Operands)) { + Op2Index = VmReadIndex16 (VmPtr, Size); + } else { + Op2Index = VmReadImmed16 (VmPtr, Size); + } + + Size += sizeof (UINT16); + } + // + // Get the data from the source. + // + Op2 = (UINT64)(INT64)(INTN)(VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Op2Index); + if (OPERAND2_INDIRECT (Operands)) { + Op2 = (UINT64)(INT64)(INTN)VmReadMemN (VmPtr, (UINTN) Op2); + } + // + // Now write back the result. + // + if (!OPERAND1_INDIRECT (Operands)) { + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2; + } else { + VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2); + } + // + // Advance the instruction pointer + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC MOVsnw instruction. This instruction loads a signed + natural value from memory or register to another memory or register. On + 32-bit machines, the value gets sign-extended to 64 bits if the destination + is a register. + + Instruction syntax: + + MOVsnd {@}R1 {Indx32}, {@}R2 {Index32|Immed32} + + 0:7 1=>operand1 index present + 0:6 1=>operand2 index present + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteMOVsnd ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT8 Size; + INT32 Op1Index; + INT32 Op2Index; + UINT64 Op2; + + // + // Get the opcode and operand bytes + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + Op1Index = Op2Index = 0; + + // + // Get the indexes if present. + // + Size = 2; + if ((Opcode & OPCODE_M_IMMED_OP1) != 0) { + if (OPERAND1_INDIRECT (Operands)) { + Op1Index = VmReadIndex32 (VmPtr, 2); + } else { + // + // Illegal form operand1 direct with index: MOVsnd R1 Index16,.. + // + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return EFI_UNSUPPORTED; + } + + Size += sizeof (UINT32); + } + + if ((Opcode & OPCODE_M_IMMED_OP2) != 0) { + if (OPERAND2_INDIRECT (Operands)) { + Op2Index = VmReadIndex32 (VmPtr, Size); + } else { + Op2Index = VmReadImmed32 (VmPtr, Size); + } + + Size += sizeof (UINT32); + } + // + // Get the data from the source. + // + Op2 = (UINT64)(INT64)(INTN)(INT64)(VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Op2Index); + if (OPERAND2_INDIRECT (Operands)) { + Op2 = (UINT64)(INT64)(INTN)(INT64)VmReadMemN (VmPtr, (UINTN) Op2); + } + // + // Now write back the result. + // + if (!OPERAND1_INDIRECT (Operands)) { + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2; + } else { + VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2); + } + // + // Advance the instruction pointer + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC PUSHn instruction + + Instruction syntax: + PUSHn {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePUSHn ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + INT16 Index16; + UINTN DataN; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + // + // Get index if present + // + if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { + if (OPERAND1_INDIRECT (Operands)) { + Index16 = VmReadIndex16 (VmPtr, 2); + } else { + Index16 = VmReadImmed16 (VmPtr, 2); + } + + VmPtr->Ip += 4; + } else { + Index16 = 0; + VmPtr->Ip += 2; + } + // + // Get the data to push + // + if (OPERAND1_INDIRECT (Operands)) { + DataN = VmReadMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16)); + } else { + DataN = (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16); + } + // + // Adjust the stack down. + // + VmPtr->Gpr[0] -= sizeof (UINTN); + VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], DataN); + return EFI_SUCCESS; +} + + +/** + Execute the EBC PUSH instruction. + + Instruction syntax: + PUSH[32|64] {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePUSH ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT32 Data32; + UINT64 Data64; + INT16 Index16; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + // + // Get immediate index if present, then advance the IP. + // + if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { + if (OPERAND1_INDIRECT (Operands)) { + Index16 = VmReadIndex16 (VmPtr, 2); + } else { + Index16 = VmReadImmed16 (VmPtr, 2); + } + + VmPtr->Ip += 4; + } else { + Index16 = 0; + VmPtr->Ip += 2; + } + // + // Get the data to push + // + if ((Opcode & PUSHPOP_M_64) != 0) { + if (OPERAND1_INDIRECT (Operands)) { + Data64 = VmReadMem64 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16)); + } else { + Data64 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; + } + // + // Adjust the stack down, then write back the data + // + VmPtr->Gpr[0] -= sizeof (UINT64); + VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], Data64); + } else { + // + // 32-bit data + // + if (OPERAND1_INDIRECT (Operands)) { + Data32 = VmReadMem32 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16)); + } else { + Data32 = (UINT32) VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16; + } + // + // Adjust the stack down and write the data + // + VmPtr->Gpr[0] -= sizeof (UINT32); + VmWriteMem32 (VmPtr, (UINTN) VmPtr->Gpr[0], Data32); + } + + return EFI_SUCCESS; +} + + +/** + Execute the EBC POPn instruction. + + Instruction syntax: + POPn {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePOPn ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + INT16 Index16; + UINTN DataN; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + // + // Get immediate data if present, and advance the IP + // + if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { + if (OPERAND1_INDIRECT (Operands)) { + Index16 = VmReadIndex16 (VmPtr, 2); + } else { + Index16 = VmReadImmed16 (VmPtr, 2); + } + + VmPtr->Ip += 4; + } else { + Index16 = 0; + VmPtr->Ip += 2; + } + // + // Read the data off the stack, then adjust the stack pointer + // + DataN = VmReadMemN (VmPtr, (UINTN) VmPtr->Gpr[0]); + VmPtr->Gpr[0] += sizeof (UINTN); + // + // Do the write-back + // + if (OPERAND1_INDIRECT (Operands)) { + VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16), DataN); + } else { + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (INT64) (UINT64) (UINTN) (DataN + Index16); + } + + return EFI_SUCCESS; +} + + +/** + Execute the EBC POP instruction. + + Instruction syntax: + POPn {@}R1 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecutePOP ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + INT16 Index16; + INT32 Data32; + UINT64 Data64; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + // + // Get immediate data if present, and advance the IP. + // + if ((Opcode & PUSHPOP_M_IMMDATA) != 0) { + if (OPERAND1_INDIRECT (Operands)) { + Index16 = VmReadIndex16 (VmPtr, 2); + } else { + Index16 = VmReadImmed16 (VmPtr, 2); + } + + VmPtr->Ip += 4; + } else { + Index16 = 0; + VmPtr->Ip += 2; + } + // + // Get the data off the stack, then write it to the appropriate location + // + if ((Opcode & PUSHPOP_M_64) != 0) { + // + // Read the data off the stack, then adjust the stack pointer + // + Data64 = VmReadMem64 (VmPtr, (UINTN) VmPtr->Gpr[0]); + VmPtr->Gpr[0] += sizeof (UINT64); + // + // Do the write-back + // + if (OPERAND1_INDIRECT (Operands)) { + VmWriteMem64 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16), Data64); + } else { + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Data64 + Index16; + } + } else { + // + // 32-bit pop. Read it off the stack and adjust the stack pointer + // + Data32 = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[0]); + VmPtr->Gpr[0] += sizeof (UINT32); + // + // Do the write-back + // + if (OPERAND1_INDIRECT (Operands)) { + VmWriteMem32 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Index16), Data32); + } else { + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (INT64) Data32 + Index16; + } + } + + return EFI_SUCCESS; +} + + +/** + Implements the EBC CALL instruction. + + Instruction format: + CALL64 Immed64 + CALL32 {@}R1 {Immed32|Index32} + CALLEX64 Immed64 + CALLEX16 {@}R1 {Immed32} + + If Rx == R0, then it's a PC relative call to PC = PC + imm32. + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteCALL ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + INT32 Immed32; + UINT8 Size; + INT64 Immed64; + VOID *FramePtr; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + if ((Operands & OPERAND_M_NATIVE_CALL) != 0) { + EbcDebuggerHookCALLEXStart (VmPtr); + } else { + EbcDebuggerHookCALLStart (VmPtr); + } + + // + // Assign these as well to avoid compiler warnings + // + Immed64 = 0; + Immed32 = 0; + + FramePtr = VmPtr->FramePtr; + // + // Determine the instruction size, and get immediate data if present + // + if ((Opcode & OPCODE_M_IMMDATA) != 0) { + if ((Opcode & OPCODE_M_IMMDATA64) != 0) { + Immed64 = VmReadImmed64 (VmPtr, 2); + Size = 10; + } else { + // + // If register operand is indirect, then the immediate data is an index + // + if (OPERAND1_INDIRECT (Operands)) { + Immed32 = VmReadIndex32 (VmPtr, 2); + } else { + Immed32 = VmReadImmed32 (VmPtr, 2); + } + + Size = 6; + } + } else { + Size = 2; + } + // + // If it's a call to EBC, adjust the stack pointer down 16 bytes and + // put our return address and frame pointer on the VM stack. + // + if ((Operands & OPERAND_M_NATIVE_CALL) == 0) { + VmPtr->Gpr[0] -= 8; + VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr); + VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0]; + VmPtr->Gpr[0] -= 8; + VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size)); + } + // + // If 64-bit data, then absolute jump only + // + if ((Opcode & OPCODE_M_IMMDATA64) != 0) { + // + // Native or EBC call? + // + if ((Operands & OPERAND_M_NATIVE_CALL) == 0) { + VmPtr->Ip = (VMIP) (UINTN) Immed64; + } else { + // + // Call external function, get the return value, and advance the IP + // + EbcLLCALLEX (VmPtr, (UINTN) Immed64, (UINTN) VmPtr->Gpr[0], FramePtr, Size); + } + } else { + // + // Get the register data. If operand1 == 0, then ignore register and + // take immediate data as relative or absolute address. + // Compiler should take care of upper bits if 32-bit machine. + // + if (OPERAND1_REGNUM (Operands) != 0) { + Immed64 = (UINT64) (UINTN) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; + } + // + // Get final address + // + if (OPERAND1_INDIRECT (Operands)) { + Immed64 = (INT64) (UINT64) (UINTN) VmReadMemN (VmPtr, (UINTN) (Immed64 + Immed32)); + } else { + Immed64 += Immed32; + } + // + // Now determine if external call, and then if relative or absolute + // + if ((Operands & OPERAND_M_NATIVE_CALL) == 0) { + // + // EBC call. Relative or absolute? If relative, then it's relative to the + // start of the next instruction. + // + if ((Operands & OPERAND_M_RELATIVE_ADDR) != 0) { + VmPtr->Ip += Immed64 + Size; + } else { + VmPtr->Ip = (VMIP) (UINTN) Immed64; + } + } else { + // + // Native call. Relative or absolute? + // + if ((Operands & OPERAND_M_RELATIVE_ADDR) != 0) { + EbcLLCALLEX (VmPtr, (UINTN) (Immed64 + VmPtr->Ip + Size), (UINTN) VmPtr->Gpr[0], FramePtr, Size); + } else { + if ((VmPtr->StopFlags & STOPFLAG_BREAK_ON_CALLEX) != 0) { + CpuBreakpoint (); + } + + EbcLLCALLEX (VmPtr, (UINTN) Immed64, (UINTN) VmPtr->Gpr[0], FramePtr, Size); + } + } + } + + if ((Operands & OPERAND_M_NATIVE_CALL) != 0) { + EbcDebuggerHookCALLEXEnd (VmPtr); + } else { + EbcDebuggerHookCALLEnd (VmPtr); + } + + return EFI_SUCCESS; +} + + +/** + Execute the EBC RET instruction. + + Instruction syntax: + RET + + @param VmPtr A pointer to a VM context. + + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteRET ( + IN VM_CONTEXT *VmPtr + ) +{ + + EbcDebuggerHookRETStart (VmPtr); + + // + // If we're at the top of the stack, then simply set the done + // flag and return + // + if (VmPtr->StackRetAddr == (UINT64) VmPtr->Gpr[0]) { + VmPtr->StopFlags |= STOPFLAG_APP_DONE; + } else { + // + // Pull the return address off the VM app's stack and set the IP + // to it + // + if (!IS_ALIGNED ((UINTN) VmPtr->Gpr[0], sizeof (UINT16))) { + EbcDebugSignalException ( + EXCEPT_EBC_ALIGNMENT_CHECK, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + } + // + // Restore the IP and frame pointer from the stack + // + VmPtr->Ip = (VMIP) (UINTN) VmReadMem64 (VmPtr, (UINTN) VmPtr->Gpr[0]); + VmPtr->Gpr[0] += 8; + VmPtr->FramePtr = (VOID *) VmReadMemN (VmPtr, (UINTN) VmPtr->Gpr[0]); + VmPtr->Gpr[0] += 8; + } + + + EbcDebuggerHookRETEnd (VmPtr); + + return EFI_SUCCESS; +} + + +/** + Execute the EBC CMP instruction. + + Instruction syntax: + CMP[32|64][eq|lte|gte|ulte|ugte] R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteCMP ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT8 Size; + INT16 Index16; + UINT32 Flag; + INT64 Op2; + INT64 Op1; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + // + // Get the register data we're going to compare to + // + Op1 = VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; + // + // Get immediate data + // + if ((Opcode & OPCODE_M_IMMDATA) != 0) { + if (OPERAND2_INDIRECT (Operands)) { + Index16 = VmReadIndex16 (VmPtr, 2); + } else { + Index16 = VmReadImmed16 (VmPtr, 2); + } + + Size = 4; + } else { + Index16 = 0; + Size = 2; + } + // + // Now get Op2 + // + if (OPERAND2_INDIRECT (Operands)) { + if ((Opcode & OPCODE_M_64BIT) != 0) { + Op2 = (INT64) VmReadMem64 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16)); + } else { + // + // 32-bit operations. 0-extend the values for all cases. + // + Op2 = (INT64) (UINT64) ((UINT32) VmReadMem32 (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16))); + } + } else { + Op2 = VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16; + } + // + // Now do the compare + // + Flag = 0; + if ((Opcode & OPCODE_M_64BIT) != 0) { + // + // 64-bit compares + // + switch (Opcode & OPCODE_M_OPCODE) { + case OPCODE_CMPEQ: + if (Op1 == Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPLTE: + if (Op1 <= Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPGTE: + if (Op1 >= Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPULTE: + if ((UINT64) Op1 <= (UINT64) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPUGTE: + if ((UINT64) Op1 >= (UINT64) Op2) { + Flag = 1; + } + break; + + default: + ASSERT (0); + } + } else { + // + // 32-bit compares + // + switch (Opcode & OPCODE_M_OPCODE) { + case OPCODE_CMPEQ: + if ((INT32) Op1 == (INT32) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPLTE: + if ((INT32) Op1 <= (INT32) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPGTE: + if ((INT32) Op1 >= (INT32) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPULTE: + if ((UINT32) Op1 <= (UINT32) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPUGTE: + if ((UINT32) Op1 >= (UINT32) Op2) { + Flag = 1; + } + break; + + default: + ASSERT (0); + } + } + // + // Now set the flag accordingly for the comparison + // + if (Flag != 0) { + VMFLAG_SET (VmPtr, VMFLAGS_CC); + } else { + VMFLAG_CLEAR (VmPtr, (UINT64)VMFLAGS_CC); + } + // + // Advance the IP + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC CMPI instruction + + Instruction syntax: + CMPI[32|64]{w|d}[eq|lte|gte|ulte|ugte] {@}Rx {Index16}, Immed16|Immed32 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteCMPI ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Opcode; + UINT8 Operands; + UINT8 Size; + INT64 Op1; + INT64 Op2; + INT16 Index16; + UINT32 Flag; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + // + // Get operand1 index if present + // + Size = 2; + if ((Operands & OPERAND_M_CMPI_INDEX) != 0) { + Index16 = VmReadIndex16 (VmPtr, 2); + Size += 2; + } else { + Index16 = 0; + } + // + // Get operand1 data we're going to compare to + // + Op1 = (INT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; + if (OPERAND1_INDIRECT (Operands)) { + // + // Indirect operand1. Fetch 32 or 64-bit value based on compare size. + // + if ((Opcode & OPCODE_M_CMPI64) != 0) { + Op1 = (INT64) VmReadMem64 (VmPtr, (UINTN) Op1 + Index16); + } else { + Op1 = (INT64) VmReadMem32 (VmPtr, (UINTN) Op1 + Index16); + } + } else { + // + // Better not have been an index with direct. That is, CMPI R1 Index,... + // is illegal. + // + if ((Operands & OPERAND_M_CMPI_INDEX) != 0) { + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_ERROR, + VmPtr + ); + VmPtr->Ip += Size; + return EFI_UNSUPPORTED; + } + } + // + // Get immediate data -- 16- or 32-bit sign extended + // + if ((Opcode & OPCODE_M_CMPI32_DATA) != 0) { + Op2 = (INT64) VmReadImmed32 (VmPtr, Size); + Size += 4; + } else { + // + // 16-bit immediate data. Sign extend always. + // + Op2 = (INT64) ((INT16) VmReadImmed16 (VmPtr, Size)); + Size += 2; + } + // + // Now do the compare + // + Flag = 0; + if ((Opcode & OPCODE_M_CMPI64) != 0) { + // + // 64 bit comparison + // + switch (Opcode & OPCODE_M_OPCODE) { + case OPCODE_CMPIEQ: + if (Op1 == (INT64) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPILTE: + if (Op1 <= (INT64) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPIGTE: + if (Op1 >= (INT64) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPIULTE: + if ((UINT64) Op1 <= (UINT64) ((UINT32) Op2)) { + Flag = 1; + } + break; + + case OPCODE_CMPIUGTE: + if ((UINT64) Op1 >= (UINT64) ((UINT32) Op2)) { + Flag = 1; + } + break; + + default: + ASSERT (0); + } + } else { + // + // 32-bit comparisons + // + switch (Opcode & OPCODE_M_OPCODE) { + case OPCODE_CMPIEQ: + if ((INT32) Op1 == Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPILTE: + if ((INT32) Op1 <= Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPIGTE: + if ((INT32) Op1 >= Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPIULTE: + if ((UINT32) Op1 <= (UINT32) Op2) { + Flag = 1; + } + break; + + case OPCODE_CMPIUGTE: + if ((UINT32) Op1 >= (UINT32) Op2) { + Flag = 1; + } + break; + + default: + ASSERT (0); + } + } + // + // Now set the flag accordingly for the comparison + // + if (Flag != 0) { + VMFLAG_SET (VmPtr, VMFLAGS_CC); + } else { + VMFLAG_CLEAR (VmPtr, (UINT64)VMFLAGS_CC); + } + // + // Advance the IP + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC NOT instruction.s + + Instruction syntax: + NOT[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return ~Op2 + +**/ +UINT64 +ExecuteNOT ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + return ~Op2; +} + + +/** + Execute the EBC NEG instruction. + + Instruction syntax: + NEG[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op2 * -1 + +**/ +UINT64 +ExecuteNEG ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + return ~Op2 + 1; +} + + +/** + Execute the EBC ADD instruction. + + Instruction syntax: + ADD[32|64] {@}R1, {@}R2 {Index16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 + Op2 + +**/ +UINT64 +ExecuteADD ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + return Op1 + Op2; +} + + +/** + Execute the EBC SUB instruction. + + Instruction syntax: + SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 - Op2 + +**/ +UINT64 +ExecuteSUB ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return (UINT64) ((INT64) ((INT64) Op1 - (INT64) Op2)); + } else { + return (UINT64) ((INT64) ((INT32) ((INT32) Op1 - (INT32) Op2))); + } +} + + +/** + Execute the EBC MUL instruction. + + Instruction syntax: + SUB[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 * Op2 + +**/ +UINT64 +ExecuteMUL ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return MultS64x64 ((INT64)Op1, (INT64)Op2); + } else { + return (UINT64) ((INT64) ((INT32) ((INT32) Op1 * (INT32) Op2))); + } +} + + +/** + Execute the EBC MULU instruction + + Instruction syntax: + MULU[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (unsigned)Op1 * (unsigned)Op2 + +**/ +UINT64 +ExecuteMULU ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return MultU64x64 (Op1, Op2); + } else { + return (UINT64) ((UINT32) ((UINT32) Op1 * (UINT32) Op2)); + } +} + + +/** + Execute the EBC DIV instruction. + + Instruction syntax: + DIV[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 / Op2 + +**/ +UINT64 +ExecuteDIV ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + INT64 Remainder; + + // + // Check for divide-by-0 + // + if (Op2 == 0) { + EbcDebugSignalException ( + EXCEPT_EBC_DIVIDE_ERROR, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + + return 0; + } else { + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return (UINT64) (DivS64x64Remainder (Op1, Op2, &Remainder)); + } else { + return (UINT64) ((INT64) ((INT32) Op1 / (INT32) Op2)); + } + } +} + + +/** + Execute the EBC DIVU instruction + + Instruction syntax: + DIVU[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (unsigned)Op1 / (unsigned)Op2 + +**/ +UINT64 +ExecuteDIVU ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + UINT64 Remainder; + + // + // Check for divide-by-0 + // + if (Op2 == 0) { + EbcDebugSignalException ( + EXCEPT_EBC_DIVIDE_ERROR, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return 0; + } else { + // + // Get the destination register + // + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return (UINT64) (DivU64x64Remainder (Op1, Op2, &Remainder)); + } else { + return (UINT64) ((UINT32) Op1 / (UINT32) Op2); + } + } +} + + +/** + Execute the EBC MOD instruction. + + Instruction syntax: + MOD[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 MODULUS Op2 + +**/ +UINT64 +ExecuteMOD ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + INT64 Remainder; + + // + // Check for divide-by-0 + // + if (Op2 == 0) { + EbcDebugSignalException ( + EXCEPT_EBC_DIVIDE_ERROR, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return 0; + } else { + DivS64x64Remainder ((INT64)Op1, (INT64)Op2, &Remainder); + return Remainder; + } +} + + +/** + Execute the EBC MODU instruction. + + Instruction syntax: + MODU[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 UNSIGNED_MODULUS Op2 + +**/ +UINT64 +ExecuteMODU ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + UINT64 Remainder; + + // + // Check for divide-by-0 + // + if (Op2 == 0) { + EbcDebugSignalException ( + EXCEPT_EBC_DIVIDE_ERROR, + EXCEPTION_FLAG_FATAL, + VmPtr + ); + return 0; + } else { + DivU64x64Remainder (Op1, Op2, &Remainder); + return Remainder; + } +} + + +/** + Execute the EBC AND instruction. + + Instruction syntax: + AND[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 AND Op2 + +**/ +UINT64 +ExecuteAND ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + return Op1 & Op2; +} + + +/** + Execute the EBC OR instruction. + + Instruction syntax: + OR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 OR Op2 + +**/ +UINT64 +ExecuteOR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + return Op1 | Op2; +} + + +/** + Execute the EBC XOR instruction. + + Instruction syntax: + XOR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 XOR Op2 + +**/ +UINT64 +ExecuteXOR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + return Op1 ^ Op2; +} + + +/** + Execute the EBC SHL shift left instruction. + + Instruction syntax: + SHL[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 << Op2 + +**/ +UINT64 +ExecuteSHL ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return LShiftU64 (Op1, (UINTN)Op2); + } else { + return (UINT64) ((UINT32) ((UINT32) Op1 << (UINT32) Op2)); + } +} + + +/** + Execute the EBC SHR instruction. + + Instruction syntax: + SHR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 >> Op2 (unsigned operands) + +**/ +UINT64 +ExecuteSHR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return RShiftU64 (Op1, (UINTN)Op2); + } else { + return (UINT64) ((UINT32) Op1 >> (UINT32) Op2); + } +} + + +/** + Execute the EBC ASHR instruction. + + Instruction syntax: + ASHR[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return Op1 >> Op2 (signed) + +**/ +UINT64 +ExecuteASHR ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + if ((*VmPtr->Ip & DATAMANIP_M_64) != 0) { + return ARShiftU64 (Op1, (UINTN)Op2); + } else { + return (UINT64) ((INT64) ((INT32) Op1 >> (UINT32) Op2)); + } +} + + +/** + Execute the EBC EXTNDB instruction to sign-extend a byte value. + + Instruction syntax: + EXTNDB[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (INT64)(INT8)Op2 + +**/ +UINT64 +ExecuteEXTNDB ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + INT8 Data8; + INT64 Data64; + // + // Convert to byte, then return as 64-bit signed value to let compiler + // sign-extend the value + // + Data8 = (INT8) Op2; + Data64 = (INT64) Data8; + + return (UINT64) Data64; +} + + +/** + Execute the EBC EXTNDW instruction to sign-extend a 16-bit value. + + Instruction syntax: + EXTNDW[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (INT64)(INT16)Op2 + +**/ +UINT64 +ExecuteEXTNDW ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + INT16 Data16; + INT64 Data64; + // + // Convert to word, then return as 64-bit signed value to let compiler + // sign-extend the value + // + Data16 = (INT16) Op2; + Data64 = (INT64) Data16; + + return (UINT64) Data64; +} +// +// Execute the EBC EXTNDD instruction. +// +// Format: EXTNDD {@}Rx, {@}Ry [Index16|Immed16] +// EXTNDD Dest, Source +// +// Operation: Dest <- SignExtended((DWORD)Source)) +// + +/** + Execute the EBC EXTNDD instruction to sign-extend a 32-bit value. + + Instruction syntax: + EXTNDD[32|64] {@}R1, {@}R2 {Index16|Immed16} + + @param VmPtr A pointer to a VM context. + @param Op1 Operand 1 from the instruction + @param Op2 Operand 2 from the instruction + + @return (INT64)(INT32)Op2 + +**/ +UINT64 +ExecuteEXTNDD ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Op1, + IN UINT64 Op2 + ) +{ + INT32 Data32; + INT64 Data64; + // + // Convert to 32-bit value, then return as 64-bit signed value to let compiler + // sign-extend the value + // + Data32 = (INT32) Op2; + Data64 = (INT64) Data32; + + return (UINT64) Data64; +} + + +/** + Execute all the EBC signed data manipulation instructions. + Since the EBC data manipulation instructions all have the same basic form, + they can share the code that does the fetch of operands and the write-back + of the result. This function performs the fetch of the operands (even if + both are not needed to be fetched, like NOT instruction), dispatches to the + appropriate subfunction, then writes back the returned result. + + Format: + INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} + + @param VmPtr A pointer to VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteSignedDataManip ( + IN VM_CONTEXT *VmPtr + ) +{ + // + // Just call the data manipulation function with a flag indicating this + // is a signed operation. + // + return ExecuteDataManip (VmPtr, TRUE); +} + + +/** + Execute all the EBC unsigned data manipulation instructions. + Since the EBC data manipulation instructions all have the same basic form, + they can share the code that does the fetch of operands and the write-back + of the result. This function performs the fetch of the operands (even if + both are not needed to be fetched, like NOT instruction), dispatches to the + appropriate subfunction, then writes back the returned result. + + Format: + INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} + + @param VmPtr A pointer to VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteUnsignedDataManip ( + IN VM_CONTEXT *VmPtr + ) +{ + // + // Just call the data manipulation function with a flag indicating this + // is not a signed operation. + // + return ExecuteDataManip (VmPtr, FALSE); +} + + +/** + Execute all the EBC data manipulation instructions. + Since the EBC data manipulation instructions all have the same basic form, + they can share the code that does the fetch of operands and the write-back + of the result. This function performs the fetch of the operands (even if + both are not needed to be fetched, like NOT instruction), dispatches to the + appropriate subfunction, then writes back the returned result. + + Format: + INSTRUCITON[32|64] {@}R1, {@}R2 {Immed16|Index16} + + @param VmPtr A pointer to VM context. + @param IsSignedOp Indicates whether the operand is signed or not. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteDataManip ( + IN VM_CONTEXT *VmPtr, + IN BOOLEAN IsSignedOp + ) +{ + UINT8 Opcode; + INT16 Index16; + UINT8 Operands; + UINT8 Size; + UINT64 Op1; + UINT64 Op2; + INTN DataManipDispatchTableIndex; + + // + // Get opcode and operands + // + Opcode = GETOPCODE (VmPtr); + Operands = GETOPERANDS (VmPtr); + + // + // Determine if we have immediate data by the opcode + // + if ((Opcode & DATAMANIP_M_IMMDATA) != 0) { + // + // Index16 if Ry is indirect, or Immed16 if Ry direct. + // + if (OPERAND2_INDIRECT (Operands)) { + Index16 = VmReadIndex16 (VmPtr, 2); + } else { + Index16 = VmReadImmed16 (VmPtr, 2); + } + + Size = 4; + } else { + Index16 = 0; + Size = 2; + } + // + // Now get operand2 (source). It's of format {@}R2 {Index16|Immed16} + // + Op2 = (UINT64) VmPtr->Gpr[OPERAND2_REGNUM (Operands)] + Index16; + if (OPERAND2_INDIRECT (Operands)) { + // + // Indirect form: @R2 Index16. Fetch as 32- or 64-bit data + // + if ((Opcode & DATAMANIP_M_64) != 0) { + Op2 = VmReadMem64 (VmPtr, (UINTN) Op2); + } else { + // + // Read as signed value where appropriate. + // + if (IsSignedOp) { + Op2 = (UINT64) (INT64) ((INT32) VmReadMem32 (VmPtr, (UINTN) Op2)); + } else { + Op2 = (UINT64) VmReadMem32 (VmPtr, (UINTN) Op2); + } + } + } else { + if ((Opcode & DATAMANIP_M_64) == 0) { + if (IsSignedOp) { + Op2 = (UINT64) (INT64) ((INT32) Op2); + } else { + Op2 = (UINT64) ((UINT32) Op2); + } + } + } + // + // Get operand1 (destination and sometimes also an actual operand) + // of form {@}R1 + // + Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; + if (OPERAND1_INDIRECT (Operands)) { + if ((Opcode & DATAMANIP_M_64) != 0) { + Op1 = VmReadMem64 (VmPtr, (UINTN) Op1); + } else { + if (IsSignedOp) { + Op1 = (UINT64) (INT64) ((INT32) VmReadMem32 (VmPtr, (UINTN) Op1)); + } else { + Op1 = (UINT64) VmReadMem32 (VmPtr, (UINTN) Op1); + } + } + } else { + if ((Opcode & DATAMANIP_M_64) == 0) { + if (IsSignedOp) { + Op1 = (UINT64) (INT64) ((INT32) Op1); + } else { + Op1 = (UINT64) ((UINT32) Op1); + } + } + } + // + // Dispatch to the computation function + // + DataManipDispatchTableIndex = (Opcode & OPCODE_M_OPCODE) - OPCODE_NOT; + if ((DataManipDispatchTableIndex < 0) || + (DataManipDispatchTableIndex >= ARRAY_SIZE (mDataManipDispatchTable))) { + EbcDebugSignalException ( + EXCEPT_EBC_INVALID_OPCODE, + EXCEPTION_FLAG_ERROR, + VmPtr + ); + // + // Advance and return + // + VmPtr->Ip += Size; + return EFI_UNSUPPORTED; + } else { + Op2 = mDataManipDispatchTable[DataManipDispatchTableIndex](VmPtr, Op1, Op2); + } + // + // Write back the result. + // + if (OPERAND1_INDIRECT (Operands)) { + Op1 = (UINT64) VmPtr->Gpr[OPERAND1_REGNUM (Operands)]; + if ((Opcode & DATAMANIP_M_64) != 0) { + VmWriteMem64 (VmPtr, (UINTN) Op1, Op2); + } else { + VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) Op2); + } + } else { + // + // Storage back to a register. Write back, clearing upper bits (as per + // the specification) if 32-bit operation. + // + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2; + if ((Opcode & DATAMANIP_M_64) == 0) { + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] &= 0xFFFFFFFF; + } + } + // + // Advance the instruction pointer + // + VmPtr->Ip += Size; + return EFI_SUCCESS; +} + + +/** + Execute the EBC LOADSP instruction. + + Instruction syntax: + LOADSP SP1, R2 + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteLOADSP ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Operands; + + // + // Get the operands + // + Operands = GETOPERANDS (VmPtr); + + // + // Do the operation + // + switch (OPERAND1_REGNUM (Operands)) { + // + // Set flags + // + case 0: + // + // Spec states that this instruction will not modify reserved bits in + // the flags register. + // + VmPtr->Flags = (VmPtr->Flags &~VMFLAGS_ALL_VALID) | (VmPtr->Gpr[OPERAND2_REGNUM (Operands)] & VMFLAGS_ALL_VALID); + break; + + default: + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_WARNING, + VmPtr + ); + VmPtr->Ip += 2; + return EFI_UNSUPPORTED; + } + + VmPtr->Ip += 2; + return EFI_SUCCESS; +} + + +/** + Execute the EBC STORESP instruction. + + Instruction syntax: + STORESP Rx, FLAGS|IP + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED The opcodes/operands is not supported. + @retval EFI_SUCCESS The instruction is executed successfully. + +**/ +EFI_STATUS +ExecuteSTORESP ( + IN VM_CONTEXT *VmPtr + ) +{ + UINT8 Operands; + + // + // Get the operands + // + Operands = GETOPERANDS (VmPtr); + + // + // Do the operation + // + switch (OPERAND2_REGNUM (Operands)) { + // + // Get flags + // + case 0: + // + // Retrieve the value in the flags register, then clear reserved bits + // + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (UINT64) (VmPtr->Flags & VMFLAGS_ALL_VALID); + break; + + // + // Get IP -- address of following instruction + // + case 1: + VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (UINT64) (UINTN) VmPtr->Ip + 2; + break; + + default: + EbcDebugSignalException ( + EXCEPT_EBC_INSTRUCTION_ENCODING, + EXCEPTION_FLAG_WARNING, + VmPtr + ); + VmPtr->Ip += 2; + return EFI_UNSUPPORTED; + break; + } + + VmPtr->Ip += 2; + return EFI_SUCCESS; +} + + +/** + Decode a 16-bit index to determine the offset. Given an index value: + + b15 - sign bit + b14:12 - number of bits in this index assigned to natural units (=a) + ba:11 - constant units = ConstUnits + b0:a - natural units = NaturalUnits + + Given this info, the offset can be computed by: + offset = sign_bit * (ConstUnits + NaturalUnits * sizeof(UINTN)) + + Max offset is achieved with index = 0x7FFF giving an offset of + 0x27B (32-bit machine) or 0x477 (64-bit machine). + Min offset is achieved with index = + + @param VmPtr A pointer to VM context. + @param CodeOffset Offset from IP of the location of the 16-bit index + to decode. + + @return The decoded offset. + +**/ +INT16 +VmReadIndex16 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 CodeOffset + ) +{ + UINT16 Index; + INT16 Offset; + INT16 ConstUnits; + INT16 NaturalUnits; + INT16 NBits; + INT16 Mask; + + // + // First read the index from the code stream + // + Index = VmReadCode16 (VmPtr, CodeOffset); + + // + // Get the mask for NaturalUnits. First get the number of bits from the index. + // + NBits = (INT16) ((Index & 0x7000) >> 12); + + // + // Scale it for 16-bit indexes + // + NBits *= 2; + + // + // Now using the number of bits, create a mask. + // + Mask = (INT16) ((INT16)~0 << NBits); + + // + // Now using the mask, extract NaturalUnits from the lower bits of the index. + // + NaturalUnits = (INT16) (Index &~Mask); + + // + // Now compute ConstUnits + // + ConstUnits = (INT16) (((Index &~0xF000) & Mask) >> NBits); + + Offset = (INT16) (NaturalUnits * sizeof (UINTN) + ConstUnits); + + // + // Now set the sign + // + if ((Index & 0x8000) != 0) { + // + // Do it the hard way to work around a bogus compiler warning + // + // Offset = -1 * Offset; + // + Offset = (INT16) ((INT32) Offset * -1); + } + + return Offset; +} + + +/** + Decode a 32-bit index to determine the offset. + + @param VmPtr A pointer to VM context. + @param CodeOffset Offset from IP of the location of the 32-bit index + to decode. + + @return Converted index per EBC VM specification. + +**/ +INT32 +VmReadIndex32 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 CodeOffset + ) +{ + UINT32 Index; + INT32 Offset; + INT32 ConstUnits; + INT32 NaturalUnits; + INT32 NBits; + INT32 Mask; + + Index = VmReadImmed32 (VmPtr, CodeOffset); + + // + // Get the mask for NaturalUnits. First get the number of bits from the index. + // + NBits = (Index & 0x70000000) >> 28; + + // + // Scale it for 32-bit indexes + // + NBits *= 4; + + // + // Now using the number of bits, create a mask. + // + Mask = (INT32)~0 << NBits; + + // + // Now using the mask, extract NaturalUnits from the lower bits of the index. + // + NaturalUnits = Index &~Mask; + + // + // Now compute ConstUnits + // + ConstUnits = ((Index &~0xF0000000) & Mask) >> NBits; + + Offset = NaturalUnits * sizeof (UINTN) + ConstUnits; + + // + // Now set the sign + // + if ((Index & 0x80000000) != 0) { + Offset = Offset * -1; + } + + return Offset; +} + + +/** + Decode a 64-bit index to determine the offset. + + @param VmPtr A pointer to VM context.s + @param CodeOffset Offset from IP of the location of the 64-bit index + to decode. + + @return Converted index per EBC VM specification + +**/ +INT64 +VmReadIndex64 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 CodeOffset + ) +{ + UINT64 Index; + INT64 Offset; + INT64 ConstUnits; + INT64 NaturalUnits; + INT64 NBits; + INT64 Mask; + + Index = VmReadCode64 (VmPtr, CodeOffset); + + // + // Get the mask for NaturalUnits. First get the number of bits from the index. + // + NBits = RShiftU64 ((Index & 0x7000000000000000ULL), 60); + + // + // Scale it for 64-bit indexes (multiply by 8 by shifting left 3) + // + NBits = LShiftU64 ((UINT64)NBits, 3); + + // + // Now using the number of bits, create a mask. + // + Mask = (LShiftU64 ((UINT64)~0, (UINTN)NBits)); + + // + // Now using the mask, extract NaturalUnits from the lower bits of the index. + // + NaturalUnits = Index &~Mask; + + // + // Now compute ConstUnits + // + ConstUnits = ARShiftU64 (((Index &~0xF000000000000000ULL) & Mask), (UINTN)NBits); + + Offset = MultU64x64 ((UINT64) NaturalUnits, sizeof (UINTN)) + ConstUnits; + + // + // Now set the sign + // + if ((Index & 0x8000000000000000ULL) != 0) { + Offset = MultS64x64 (Offset, -1); + } + + return Offset; +} + + +/** + Writes 8-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem8 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT8 Data + ) +{ + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + *(UINT8 *) Addr = Data; + return EFI_SUCCESS; +} + +/** + Writes 16-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem16 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT16 Data + ) +{ + EFI_STATUS Status; + + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + + // + // Do a simple write if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINT16))) { + *(UINT16 *) Addr = Data; + } else { + // + // Write as two bytes + // + MemoryFence (); + if ((Status = VmWriteMem8 (VmPtr, Addr, (UINT8) Data)) != EFI_SUCCESS) { + return Status; + } + + MemoryFence (); + if ((Status = VmWriteMem8 (VmPtr, Addr + 1, (UINT8) (Data >> 8))) != EFI_SUCCESS) { + return Status; + } + + MemoryFence (); + } + + return EFI_SUCCESS; +} + + +/** + Writes 32-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem32 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT32 Data + ) +{ + EFI_STATUS Status; + + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + + // + // Do a simple write if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINT32))) { + *(UINT32 *) Addr = Data; + } else { + // + // Write as two words + // + MemoryFence (); + if ((Status = VmWriteMem16 (VmPtr, Addr, (UINT16) Data)) != EFI_SUCCESS) { + return Status; + } + + MemoryFence (); + if ((Status = VmWriteMem16 (VmPtr, Addr + sizeof (UINT16), (UINT16) (Data >> 16))) != EFI_SUCCESS) { + return Status; + } + + MemoryFence (); + } + + return EFI_SUCCESS; +} + + +/** + Writes 64-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem64 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT64 Data + ) +{ + EFI_STATUS Status; + + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + + // + // Do a simple write if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINT64))) { + *(UINT64 *) Addr = Data; + } else { + // + // Write as two 32-bit words + // + MemoryFence (); + if ((Status = VmWriteMem32 (VmPtr, Addr, (UINT32) Data)) != EFI_SUCCESS) { + return Status; + } + + MemoryFence (); + if ((Status = VmWriteMem32 (VmPtr, Addr + sizeof (UINT32), (UINT32) RShiftU64(Data, 32))) != EFI_SUCCESS) { + return Status; + } + + MemoryFence (); + } + + return EFI_SUCCESS; +} + + +/** + Writes UINTN data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMemN ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINTN Data + ) +{ + EFI_STATUS Status; + UINTN Index; + + Status = EFI_SUCCESS; + + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + + // + // Do a simple write if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINTN))) { + *(UINTN *) Addr = Data; + } else { + for (Index = 0; Index < sizeof (UINTN) / sizeof (UINT32); Index++) { + MemoryFence (); + Status = VmWriteMem32 (VmPtr, Addr + Index * sizeof (UINT32), (UINT32) Data); + MemoryFence (); + Data = (UINTN) RShiftU64 ((UINT64)Data, 32); + } + } + + return Status; +} + + +/** + Reads 8-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT8 +VmReadImmed8 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ) +{ + // + // Simply return the data in flat memory space + // + return * (INT8 *) (VmPtr->Ip + Offset); +} + +/** + Reads 16-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT16 +VmReadImmed16 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ) +{ + // + // Read direct if aligned + // + if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (INT16))) { + return * (INT16 *) (VmPtr->Ip + Offset); + } else { + // + // All code word reads should be aligned + // + EbcDebugSignalException ( + EXCEPT_EBC_ALIGNMENT_CHECK, + EXCEPTION_FLAG_WARNING, + VmPtr + ); + } + // + // Return unaligned data + // + return (INT16) (*(UINT8 *) (VmPtr->Ip + Offset) + (*(UINT8 *) (VmPtr->Ip + Offset + 1) << 8)); +} + + +/** + Reads 32-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT32 +VmReadImmed32 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ) +{ + UINT32 Data; + + // + // Read direct if aligned + // + if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT32))) { + return * (INT32 *) (VmPtr->Ip + Offset); + } + // + // Return unaligned data + // + Data = (UINT32) VmReadCode16 (VmPtr, Offset); + Data |= (UINT32)(VmReadCode16 (VmPtr, Offset + 2) << 16); + return Data; +} + + +/** + Reads 64-bit immediate value at the offset. + + This routine is called by the EBC execute + functions to read EBC immediate values from the code stream. + Since we can't assume alignment, each tries to read in the biggest + chunks size available, but will revert to smaller reads if necessary. + + @param VmPtr A pointer to a VM context. + @param Offset offset from IP of the code bytes to read. + + @return Signed data of the requested size from the specified address. + +**/ +INT64 +VmReadImmed64 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ) +{ + UINT64 Data64; + UINT32 Data32; + UINT8 *Ptr; + + // + // Read direct if aligned + // + if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT64))) { + return * (UINT64 *) (VmPtr->Ip + Offset); + } + // + // Return unaligned data. + // + Ptr = (UINT8 *) &Data64; + Data32 = VmReadCode32 (VmPtr, Offset); + *(UINT32 *) Ptr = Data32; + Ptr += sizeof (Data32); + Data32 = VmReadCode32 (VmPtr, Offset + sizeof (UINT32)); + *(UINT32 *) Ptr = Data32; + return Data64; +} + + +/** + Reads 16-bit unsigned data from the code stream. + + This routine provides the ability to read raw unsigned data from the code + stream. + + @param VmPtr A pointer to VM context + @param Offset Offset from current IP to the raw data to read. + + @return The raw unsigned 16-bit value from the code stream. + +**/ +UINT16 +VmReadCode16 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ) +{ + // + // Read direct if aligned + // + if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT16))) { + return * (UINT16 *) (VmPtr->Ip + Offset); + } else { + // + // All code word reads should be aligned + // + EbcDebugSignalException ( + EXCEPT_EBC_ALIGNMENT_CHECK, + EXCEPTION_FLAG_WARNING, + VmPtr + ); + } + // + // Return unaligned data + // + return (UINT16) (*(UINT8 *) (VmPtr->Ip + Offset) + (*(UINT8 *) (VmPtr->Ip + Offset + 1) << 8)); +} + + +/** + Reads 32-bit unsigned data from the code stream. + + This routine provides the ability to read raw unsigned data from the code + stream. + + @param VmPtr A pointer to VM context + @param Offset Offset from current IP to the raw data to read. + + @return The raw unsigned 32-bit value from the code stream. + +**/ +UINT32 +VmReadCode32 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ) +{ + UINT32 Data; + // + // Read direct if aligned + // + if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT32))) { + return * (UINT32 *) (VmPtr->Ip + Offset); + } + // + // Return unaligned data + // + Data = (UINT32) VmReadCode16 (VmPtr, Offset); + Data |= (VmReadCode16 (VmPtr, Offset + 2) << 16); + return Data; +} + + +/** + Reads 64-bit unsigned data from the code stream. + + This routine provides the ability to read raw unsigned data from the code + stream. + + @param VmPtr A pointer to VM context + @param Offset Offset from current IP to the raw data to read. + + @return The raw unsigned 64-bit value from the code stream. + +**/ +UINT64 +VmReadCode64 ( + IN VM_CONTEXT *VmPtr, + IN UINT32 Offset + ) +{ + UINT64 Data64; + UINT32 Data32; + UINT8 *Ptr; + + // + // Read direct if aligned + // + if (IS_ALIGNED ((UINTN) VmPtr->Ip + Offset, sizeof (UINT64))) { + return * (UINT64 *) (VmPtr->Ip + Offset); + } + // + // Return unaligned data. + // + Ptr = (UINT8 *) &Data64; + Data32 = VmReadCode32 (VmPtr, Offset); + *(UINT32 *) Ptr = Data32; + Ptr += sizeof (Data32); + Data32 = VmReadCode32 (VmPtr, Offset + sizeof (UINT32)); + *(UINT32 *) Ptr = Data32; + return Data64; +} + + +/** + Reads 8-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 8-bit value from the memory address. + +**/ +UINT8 +VmReadMem8 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ) +{ + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + // + // Simply return the data in flat memory space + // + return * (UINT8 *) Addr; +} + +/** + Reads 16-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 16-bit value from the memory address. + +**/ +UINT16 +VmReadMem16 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ) +{ + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + // + // Read direct if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINT16))) { + return * (UINT16 *) Addr; + } + // + // Return unaligned data + // + return (UINT16) (*(UINT8 *) Addr + (*(UINT8 *) (Addr + 1) << 8)); +} + +/** + Reads 32-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 32-bit value from the memory address. + +**/ +UINT32 +VmReadMem32 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ) +{ + UINT32 Data; + + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + // + // Read direct if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINT32))) { + return * (UINT32 *) Addr; + } + // + // Return unaligned data + // + Data = (UINT32) VmReadMem16 (VmPtr, Addr); + Data |= (VmReadMem16 (VmPtr, Addr + 2) << 16); + return Data; +} + +/** + Reads 64-bit data form the memory address. + + @param VmPtr A pointer to VM context. + @param Addr The memory address. + + @return The 64-bit value from the memory address. + +**/ +UINT64 +VmReadMem64 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ) +{ + UINT64 Data; + UINT32 Data32; + + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + + // + // Read direct if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINT64))) { + return * (UINT64 *) Addr; + } + // + // Return unaligned data. Assume little endian. + // + Data32 = VmReadMem32 (VmPtr, Addr); + Data = (UINT64) VmReadMem32 (VmPtr, Addr + sizeof (UINT32)); + Data = LShiftU64 (Data, 32) | Data32; + return Data; +} + + +/** + Given an address that EBC is going to read from or write to, return + an appropriate address that accounts for a gap in the stack. + The stack for this application looks like this (high addr on top) + [EBC entry point arguments] + [VM stack] + [EBC stack] + The EBC assumes that its arguments are at the top of its stack, which + is where the VM stack is really. Therefore if the EBC does memory + accesses into the VM stack area, then we need to convert the address + to point to the EBC entry point arguments area. Do this here. + + @param VmPtr A Pointer to VM context. + @param Addr Address of interest + + @return The unchanged address if it's not in the VM stack region. Otherwise, + adjust for the stack gap and return the modified address. + +**/ +UINTN +ConvertStackAddr ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ) +{ + ASSERT(((Addr < VmPtr->LowStackTop) || (Addr > VmPtr->HighStackBottom))); + return Addr; +} + + +/** + Read a natural value from memory. May or may not be aligned. + + @param VmPtr current VM context + @param Addr the address to read from + + @return The natural value at address Addr. + +**/ +UINTN +VmReadMemN ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr + ) +{ + UINTN Data; + volatile UINT32 Size; + UINT8 *FromPtr; + UINT8 *ToPtr; + // + // Convert the address if it's in the stack gap + // + Addr = ConvertStackAddr (VmPtr, Addr); + // + // Read direct if aligned + // + if (IS_ALIGNED (Addr, sizeof (UINTN))) { + return * (UINTN *) Addr; + } + // + // Return unaligned data + // + Data = 0; + FromPtr = (UINT8 *) Addr; + ToPtr = (UINT8 *) &Data; + + for (Size = 0; Size < sizeof (Data); Size++) { + *ToPtr = *FromPtr; + ToPtr++; + FromPtr++; + } + + return Data; +} + +/** + Returns the version of the EBC virtual machine. + + @return The 64-bit version of EBC virtual machine. + +**/ +UINT64 +GetVmVersion ( + VOID + ) +{ + return (UINT64) (((VM_MAJOR_VERSION & 0xFFFF) << 16) | ((VM_MINOR_VERSION & 0xFFFF))); +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.h new file mode 100644 index 000000000..1cb68bc53 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcExecute.h @@ -0,0 +1,135 @@ +/** @file + Header file for Virtual Machine support. Contains EBC defines that can + be of use to a disassembler for the most part. Also provides function + prototypes for VM functions. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EBC_EXECUTE_H_ +#define _EBC_EXECUTE_H_ + +// +// Macros to check and set alignment +// +#define ASSERT_ALIGNED(addr, size) ASSERT (!((UINT32) (addr) & (size - 1))) +#define IS_ALIGNED(addr, size) !((UINT32) (addr) & (size - 1)) + +// +// Debug macro +// +#define EBCMSG(s) gST->ConOut->OutputString (gST->ConOut, s) + + +/** + Execute an EBC image from an entry point or from a published protocol. + + @param VmPtr A pointer to a VM context. + + @retval EFI_UNSUPPORTED At least one of the opcodes is not supported. + @retval EFI_SUCCESS All of the instructions are executed successfully. + +**/ +EFI_STATUS +EbcExecute ( + IN VM_CONTEXT *VmPtr + ); + + + +/** + Returns the version of the EBC virtual machine. + + @return The 64-bit version of EBC virtual machine. + +**/ +UINT64 +GetVmVersion ( + VOID + ); + +/** + Writes UINTN data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMemN ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINTN Data + ); + +/** + Writes 64-bit data to memory address. + + This routine is called by the EBC data + movement instructions that write to memory. Since these writes + may be to the stack, which looks like (high address on top) this, + + [EBC entry point arguments] + [VM stack] + [EBC stack] + + we need to detect all attempts to write to the EBC entry point argument + stack area and adjust the address (which will initially point into the + VM stack) to point into the EBC entry point arguments. + + @param VmPtr A pointer to a VM context. + @param Addr Address to write to. + @param Data Value to write to Addr. + + @retval EFI_SUCCESS The instruction is executed successfully. + @retval Other Some error occurs when writing data to the address. + +**/ +EFI_STATUS +VmWriteMem64 ( + IN VM_CONTEXT *VmPtr, + IN UINTN Addr, + IN UINT64 Data + ); + +/** + Given a pointer to a new VM context, execute one or more instructions. This + function is only used for test purposes via the EBC VM test protocol. + + @param This A pointer to the EFI_EBC_VM_TEST_PROTOCOL structure. + @param VmPtr A pointer to a VM context. + @param InstructionCount A pointer to a UINTN value holding the number of + instructions to execute. If it holds value of 0, + then the instruction to be executed is 1. + + @retval EFI_UNSUPPORTED At least one of the opcodes is not supported. + @retval EFI_SUCCESS All of the instructions are executed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcExecuteInstructions ( + IN EFI_EBC_VM_TEST_PROTOCOL *This, + IN VM_CONTEXT *VmPtr, + IN OUT UINTN *InstructionCount + ); + +#endif // ifndef _EBC_EXECUTE_H_ diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.c new file mode 100644 index 000000000..eced1d5c7 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.c @@ -0,0 +1,1542 @@ +/** @file + Top level module for the EBC virtual machine implementation. + Provides auxiliary support routines for the VM. That is, routines + that are not particularly related to VM execution of EBC instructions. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EbcInt.h" +#include "EbcExecute.h" +#include "EbcDebuggerHook.h" + +// +// We'll keep track of all thunks we create in a linked list. Each +// thunk is tied to an image handle, so we have a linked list of +// image handles, with each having a linked list of thunks allocated +// to that image handle. +// +typedef struct _EBC_THUNK_LIST EBC_THUNK_LIST; +struct _EBC_THUNK_LIST { + VOID *ThunkBuffer; + EBC_THUNK_LIST *Next; +}; + +typedef struct _EBC_IMAGE_LIST EBC_IMAGE_LIST; +struct _EBC_IMAGE_LIST { + EBC_IMAGE_LIST *Next; + EFI_HANDLE ImageHandle; + EBC_THUNK_LIST *ThunkList; +}; + +/** + This routine is called by the core when an image is being unloaded from + memory. Basically we now have the opportunity to do any necessary cleanup. + Typically this will include freeing any memory allocated for thunk-creation. + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param ImageHandle Handle of image for which the thunk is being + created. + + @retval EFI_INVALID_PARAMETER The ImageHandle passed in was not found in the + internal list of EBC image handles. + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcUnloadImage ( + IN EFI_EBC_PROTOCOL *This, + IN EFI_HANDLE ImageHandle + ); + +/** + This is the top-level routine plugged into the EBC protocol. Since thunks + are very processor-specific, from here we dispatch directly to the very + processor-specific routine EbcCreateThunks(). + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param ImageHandle Handle of image for which the thunk is being + created. The EBC interpreter may use this to + keep track of any resource allocations + performed in loading and executing the image. + @param EbcEntryPoint Address of the actual EBC entry point or + protocol service the thunk should call. + @param Thunk Returned pointer to a thunk created. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Image entry point is not 2-byte aligned. + @retval EFI_OUT_OF_RESOURCES Memory could not be allocated for the thunk. + +**/ +EFI_STATUS +EFIAPI +EbcCreateThunk ( + IN EFI_EBC_PROTOCOL *This, + IN EFI_HANDLE ImageHandle, + IN VOID *EbcEntryPoint, + OUT VOID **Thunk + ); + +/** + Called to get the version of the interpreter. + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param Version Pointer to where to store the returned version + of the interpreter. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Version pointer is NULL. + +**/ +EFI_STATUS +EFIAPI +EbcGetVersion ( + IN EFI_EBC_PROTOCOL *This, + IN OUT UINT64 *Version + ); + +/** + To install default Callback function for the VM interpreter. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + + @retval EFI_SUCCESS The function completed successfully. + @retval Others Some error occurs when creating periodic event. + +**/ +EFI_STATUS +EFIAPI +InitializeEbcCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This + ); + +/** + The default Exception Callback for the VM interpreter. + In this function, we report status code, and print debug information + about EBC_CONTEXT, then dead loop. + + @param InterruptType Interrupt type. + @param SystemContext EBC system context. + +**/ +VOID +EFIAPI +CommonEbcExceptionHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + The periodic callback function for EBC VM interpreter, which is used + to support the EFI debug support protocol. + + @param Event The Periodic Callback Event. + @param Context It should be the address of VM_CONTEXT pointer. + +**/ +VOID +EFIAPI +EbcPeriodicNotifyFunction ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + The VM interpreter calls this function on a periodic basis to support + the EFI debug support protocol. + + @param VmPtr Pointer to a VM context for passing info to the + debugger. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcDebugPeriodic ( + IN VM_CONTEXT *VmPtr + ); + +// +// These two functions and the GUID are used to produce an EBC test protocol. +// This functionality is definitely not required for execution. +// +/** + Produces an EBC VM test protocol that can be used for regression tests. + + @param IHandle Handle on which to install the protocol. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +InitEbcVmTestProtocol ( + IN EFI_HANDLE *IHandle + ); + +/** + Returns the EFI_UNSUPPORTED Status. + + @return EFI_UNSUPPORTED This function always return EFI_UNSUPPORTED status. + +**/ +EFI_STATUS +EFIAPI +EbcVmTestUnsupported ( + VOID + ); + +/** + Registers a callback function that the EBC interpreter calls to flush the + processor instruction cache following creation of thunks. + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param Flush Pointer to a function of type EBC_ICACH_FLUSH. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcRegisterICacheFlush ( + IN EFI_EBC_PROTOCOL *This, + IN EBC_ICACHE_FLUSH Flush + ); + +/** + This EBC debugger protocol service is called by the debug agent + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the + maximum supported processor index is returned. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcDebugGetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ); + +/** + This protocol service is called by the debug agent to register a function + for us to call on a periodic basis. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param ProcessorIndex Specifies which processor the callback function + applies to. + @param PeriodicCallback A pointer to a function of type + PERIODIC_CALLBACK that is the main periodic + entry point of the debug agent. It receives as a + parameter a pointer to the full context of the + interrupted execution thread. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a + callback function was previously registered. + @retval EFI_INVALID_PARAMETER Null PeriodicCallback parameter when no + callback function was previously registered. + +**/ +EFI_STATUS +EFIAPI +EbcDebugRegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ); + +/** + This protocol service is called by the debug agent to register a function + for us to call when we detect an exception. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param ProcessorIndex Specifies which processor the callback function + applies to. + @param ExceptionCallback A pointer to a function of type + EXCEPTION_CALLBACK that is called when the + processor exception specified by ExceptionType + occurs. Passing NULL unregisters any previously + registered function associated with + ExceptionType. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL ExceptionCallback parameter when a + callback function was previously registered. + @retval EFI_INVALID_PARAMETER ExceptionType parameter is negative or exceeds + MAX_EBC_EXCEPTION. + @retval EFI_INVALID_PARAMETER Null ExceptionCallback parameter when no + callback function was previously registered. + +**/ +EFI_STATUS +EFIAPI +EbcDebugRegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + This EBC debugger protocol service is called by the debug agent. Required + for DebugSupport compliance but is only stubbed out for EBC. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param ProcessorIndex Specifies which processor the callback function + applies to. + @param Start StartSpecifies the physical base of the memory + range to be invalidated. + @param Length Specifies the minimum number of bytes in the + processor's instruction cache to invalidate. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcDebugInvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ); + +// +// We have one linked list of image handles for the whole world. Since +// there should only be one interpreter, make them global. They must +// also be global since the execution of an EBC image does not provide +// a This pointer. +// +EBC_IMAGE_LIST *mEbcImageList = NULL; + +// +// Callback function to flush the icache after thunk creation +// +EBC_ICACHE_FLUSH mEbcICacheFlush; + +// +// These get set via calls by the debug agent +// +EFI_PERIODIC_CALLBACK mDebugPeriodicCallback = NULL; +EFI_EXCEPTION_CALLBACK mDebugExceptionCallback[MAX_EBC_EXCEPTION + 1] = {NULL}; + +VOID *mStackBuffer[MAX_STACK_NUM]; +EFI_HANDLE mStackBufferIndex[MAX_STACK_NUM]; +UINTN mStackNum = 0; + +// +// Event for Periodic callback +// +EFI_EVENT mEbcPeriodicEvent; +VM_CONTEXT *mVmPtr = NULL; + +/** + Check whether the emulator supports executing a certain PE/COFF image + + @param[in] This This pointer for EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL + structure + @param[in] ImageType Whether the image is an application, a boot time + driver or a runtime driver. + @param[in] DevicePath Path to device where the image originated + (e.g., a PCI option ROM) + + @retval TRUE The image is supported by the emulator + @retval FALSE The image is not supported by the emulator. +**/ +BOOLEAN +EFIAPI +EbcIsImageSupported ( + IN EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *This, + IN UINT16 ImageType, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL + ) +{ + if (ImageType != EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION && + ImageType != EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) { + return FALSE; + } + return TRUE; +} + +/** + Register a supported PE/COFF image with the emulator. After this call + completes successfully, the PE/COFF image may be started as usual, and + it is the responsibility of the emulator implementation that any branch + into the code section of the image (including returns from functions called + from the foreign code) is executed as if it were running on the machine + type it was built for. + + @param[in] This This pointer for + EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL structure + @param[in] ImageBase The base address in memory of the PE/COFF image + @param[in] ImageSize The size in memory of the PE/COFF image + @param[in,out] EntryPoint The entry point of the PE/COFF image. Passed by + reference so that the emulator may modify it. + + @retval EFI_SUCCESS The image was registered with the emulator and + can be started as usual. + @retval other The image could not be registered. + + If the PE/COFF machine type or image type are not supported by the emulator, + then ASSERT(). +**/ +EFI_STATUS +EFIAPI +EbcRegisterImage ( + IN EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN OUT EFI_IMAGE_ENTRY_POINT *EntryPoint + ) +{ + DEBUG_CODE_BEGIN (); + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + EFI_STATUS Status; + + ZeroMem (&ImageContext, sizeof (ImageContext)); + + ImageContext.Handle = (VOID *)(UINTN)ImageBase; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (ImageContext.Machine == EFI_IMAGE_MACHINE_EBC); + ASSERT (ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION || + ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER); + DEBUG_CODE_END (); + + EbcRegisterICacheFlush (NULL, + (EBC_ICACHE_FLUSH)InvalidateInstructionCacheRange); + + return EbcCreateThunk (NULL, (VOID *)(UINTN)ImageBase, + (VOID *)(UINTN)*EntryPoint, (VOID **)EntryPoint); +} + +/** + Unregister a PE/COFF image that has been registered with the emulator. + This should be done before the image is unloaded from memory. + + @param[in] This This pointer for EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL + structure + @param[in] ImageBase The base address in memory of the PE/COFF image + + @retval EFI_SUCCESS The image was unregistered with the emulator. + @retval other Image could not be unloaded. +**/ +EFI_STATUS +EFIAPI +EbcUnregisterImage ( + IN EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS ImageBase + ) +{ + return EbcUnloadImage (NULL, (VOID *)(UINTN)ImageBase); +} + +STATIC EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL mPeCoffEmuProtocol = { + EbcIsImageSupported, + EbcRegisterImage, + EbcUnregisterImage, + EDKII_PECOFF_IMAGE_EMULATOR_VERSION, + EFI_IMAGE_MACHINE_EBC +}; + +/** + Initializes the VM EFI interface. Allocates memory for the VM interface + and registers the VM protocol. + + @param ImageHandle EFI image handle. + @param SystemTable Pointer to the EFI system table. + + @return Standard EFI status code. + +**/ +EFI_STATUS +EFIAPI +InitializeEbcDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_EBC_PROTOCOL *EbcProtocol; + EFI_EBC_PROTOCOL *OldEbcProtocol; + EFI_STATUS Status; + EFI_DEBUG_SUPPORT_PROTOCOL *EbcDebugProtocol; + EFI_HANDLE *HandleBuffer; + UINTN NumHandles; + UINTN Index; + BOOLEAN Installed; + + EbcProtocol = NULL; + EbcDebugProtocol = NULL; + + // + // Allocate memory for our protocol. Then fill in the blanks. + // + EbcProtocol = AllocatePool (sizeof (EFI_EBC_PROTOCOL)); + + if (EbcProtocol == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + EbcProtocol->CreateThunk = EbcCreateThunk; + EbcProtocol->UnloadImage = EbcUnloadImage; + EbcProtocol->RegisterICacheFlush = EbcRegisterICacheFlush; + EbcProtocol->GetVersion = EbcGetVersion; + mEbcICacheFlush = NULL; + + // + // Find any already-installed EBC protocols and uninstall them + // + Installed = FALSE; + HandleBuffer = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiEbcProtocolGuid, + NULL, + &NumHandles, + &HandleBuffer + ); + if (Status == EFI_SUCCESS) { + // + // Loop through the handles + // + for (Index = 0; Index < NumHandles; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiEbcProtocolGuid, + (VOID **) &OldEbcProtocol + ); + if (Status == EFI_SUCCESS) { + if (gBS->ReinstallProtocolInterface ( + HandleBuffer[Index], + &gEfiEbcProtocolGuid, + OldEbcProtocol, + EbcProtocol + ) == EFI_SUCCESS) { + Installed = TRUE; + } + } + } + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + HandleBuffer = NULL; + } + // + // Add the protocol so someone can locate us if we haven't already. + // + if (!Installed) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiEbcProtocolGuid, EbcProtocol, + &gEdkiiPeCoffImageEmulatorProtocolGuid, &mPeCoffEmuProtocol, + NULL + ); + if (EFI_ERROR (Status)) { + FreePool (EbcProtocol); + return Status; + } + } + + Status = InitEBCStack(); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + // + // Allocate memory for our debug protocol. Then fill in the blanks. + // + EbcDebugProtocol = AllocatePool (sizeof (EFI_DEBUG_SUPPORT_PROTOCOL)); + + if (EbcDebugProtocol == NULL) { + goto ErrorExit; + } + + EbcDebugProtocol->Isa = IsaEbc; + EbcDebugProtocol->GetMaximumProcessorIndex = EbcDebugGetMaximumProcessorIndex; + EbcDebugProtocol->RegisterPeriodicCallback = EbcDebugRegisterPeriodicCallback; + EbcDebugProtocol->RegisterExceptionCallback = EbcDebugRegisterExceptionCallback; + EbcDebugProtocol->InvalidateInstructionCache = EbcDebugInvalidateInstructionCache; + + // + // Add the protocol so the debug agent can find us + // + Status = gBS->InstallProtocolInterface ( + &ImageHandle, + &gEfiDebugSupportProtocolGuid, + EFI_NATIVE_INTERFACE, + EbcDebugProtocol + ); + // + // This is recoverable, so free the memory and continue. + // + if (EFI_ERROR (Status)) { + FreePool (EbcDebugProtocol); + goto ErrorExit; + } + // + // Install EbcDebugSupport Protocol Successfully + // Now we need to initialize the Ebc default Callback + // + Status = InitializeEbcCallback (EbcDebugProtocol); + + // + // Produce a VM test interface protocol. Not required for execution. + // + DEBUG_CODE_BEGIN (); + InitEbcVmTestProtocol (&ImageHandle); + DEBUG_CODE_END (); + + EbcDebuggerHookInit (ImageHandle, EbcDebugProtocol); + + return EFI_SUCCESS; + +ErrorExit: + FreeEBCStack(); + HandleBuffer = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiEbcProtocolGuid, + NULL, + &NumHandles, + &HandleBuffer + ); + if (Status == EFI_SUCCESS) { + // + // Loop through the handles + // + for (Index = 0; Index < NumHandles; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiEbcProtocolGuid, + (VOID **) &OldEbcProtocol + ); + if (Status == EFI_SUCCESS) { + gBS->UninstallProtocolInterface ( + HandleBuffer[Index], + &gEfiEbcProtocolGuid, + OldEbcProtocol + ); + } + } + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + HandleBuffer = NULL; + } + + FreePool (EbcProtocol); + + return Status; +} + + +/** + This is the top-level routine plugged into the EBC protocol. Since thunks + are very processor-specific, from here we dispatch directly to the very + processor-specific routine EbcCreateThunks(). + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param ImageHandle Handle of image for which the thunk is being + created. The EBC interpreter may use this to + keep track of any resource allocations + performed in loading and executing the image. + @param EbcEntryPoint Address of the actual EBC entry point or + protocol service the thunk should call. + @param Thunk Returned pointer to a thunk created. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Image entry point is not 2-byte aligned. + @retval EFI_OUT_OF_RESOURCES Memory could not be allocated for the thunk. + +**/ +EFI_STATUS +EFIAPI +EbcCreateThunk ( + IN EFI_EBC_PROTOCOL *This, + IN EFI_HANDLE ImageHandle, + IN VOID *EbcEntryPoint, + OUT VOID **Thunk + ) +{ + EFI_STATUS Status; + + Status = EbcCreateThunks ( + ImageHandle, + EbcEntryPoint, + Thunk, + FLAG_THUNK_ENTRY_POINT + ); + return Status; +} + + +/** + This EBC debugger protocol service is called by the debug agent + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the + maximum supported processor index is returned. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcDebugGetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +{ + *MaxProcessorIndex = 0; + return EFI_SUCCESS; +} + + +/** + This protocol service is called by the debug agent to register a function + for us to call on a periodic basis. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param ProcessorIndex Specifies which processor the callback function + applies to. + @param PeriodicCallback A pointer to a function of type + PERIODIC_CALLBACK that is the main periodic + entry point of the debug agent. It receives as a + parameter a pointer to the full context of the + interrupted execution thread. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a + callback function was previously registered. + @retval EFI_INVALID_PARAMETER Null PeriodicCallback parameter when no + callback function was previously registered. + +**/ +EFI_STATUS +EFIAPI +EbcDebugRegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +{ + if ((mDebugPeriodicCallback == NULL) && (PeriodicCallback == NULL)) { + return EFI_INVALID_PARAMETER; + } + if ((mDebugPeriodicCallback != NULL) && (PeriodicCallback != NULL)) { + return EFI_ALREADY_STARTED; + } + + mDebugPeriodicCallback = PeriodicCallback; + return EFI_SUCCESS; +} + + +/** + This protocol service is called by the debug agent to register a function + for us to call when we detect an exception. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param ProcessorIndex Specifies which processor the callback function + applies to. + @param ExceptionCallback A pointer to a function of type + EXCEPTION_CALLBACK that is called when the + processor exception specified by ExceptionType + occurs. Passing NULL unregisters any previously + registered function associated with + ExceptionType. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL ExceptionCallback parameter when a + callback function was previously registered. + @retval EFI_INVALID_PARAMETER ExceptionType parameter is negative or exceeds + MAX_EBC_EXCEPTION. + @retval EFI_INVALID_PARAMETER Null ExceptionCallback parameter when no + callback function was previously registered. + +**/ +EFI_STATUS +EFIAPI +EbcDebugRegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + if ((ExceptionType < 0) || (ExceptionType > MAX_EBC_EXCEPTION)) { + return EFI_INVALID_PARAMETER; + } + if ((mDebugExceptionCallback[ExceptionType] == NULL) && (ExceptionCallback == NULL)) { + return EFI_INVALID_PARAMETER; + } + if ((mDebugExceptionCallback[ExceptionType] != NULL) && (ExceptionCallback != NULL)) { + return EFI_ALREADY_STARTED; + } + mDebugExceptionCallback[ExceptionType] = ExceptionCallback; + return EFI_SUCCESS; +} + + +/** + This EBC debugger protocol service is called by the debug agent. Required + for DebugSupport compliance but is only stubbed out for EBC. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + @param ProcessorIndex Specifies which processor the callback function + applies to. + @param Start StartSpecifies the physical base of the memory + range to be invalidated. + @param Length Specifies the minimum number of bytes in the + processor's instruction cache to invalidate. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcDebugInvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ) +{ + return EFI_SUCCESS; +} + + +/** + The VM interpreter calls this function when an exception is detected. + + @param ExceptionType Specifies the processor exception detected. + @param ExceptionFlags Specifies the exception context. + @param VmPtr Pointer to a VM context for passing info to the + EFI debugger. + + @retval EFI_SUCCESS This function completed successfully. + +**/ +EFI_STATUS +EbcDebugSignalException ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EXCEPTION_FLAGS ExceptionFlags, + IN VM_CONTEXT *VmPtr + ) +{ + EFI_SYSTEM_CONTEXT_EBC EbcContext; + EFI_SYSTEM_CONTEXT SystemContext; + + ASSERT ((ExceptionType >= 0) && (ExceptionType <= MAX_EBC_EXCEPTION)); + // + // Save the exception in the context passed in + // + VmPtr->ExceptionFlags |= ExceptionFlags; + VmPtr->LastException = (UINTN) ExceptionType; + // + // If it's a fatal exception, then flag it in the VM context in case an + // attached debugger tries to return from it. + // + if ((ExceptionFlags & EXCEPTION_FLAG_FATAL) != 0) { + VmPtr->StopFlags |= STOPFLAG_APP_DONE; + } + + // + // If someone's registered for exception callbacks, then call them. + // + // EBC driver will register default exception callback to report the + // status code via the status code API + // + if (mDebugExceptionCallback[ExceptionType] != NULL) { + + // + // Initialize the context structure + // + EbcContext.R0 = (UINT64) VmPtr->Gpr[0]; + EbcContext.R1 = (UINT64) VmPtr->Gpr[1]; + EbcContext.R2 = (UINT64) VmPtr->Gpr[2]; + EbcContext.R3 = (UINT64) VmPtr->Gpr[3]; + EbcContext.R4 = (UINT64) VmPtr->Gpr[4]; + EbcContext.R5 = (UINT64) VmPtr->Gpr[5]; + EbcContext.R6 = (UINT64) VmPtr->Gpr[6]; + EbcContext.R7 = (UINT64) VmPtr->Gpr[7]; + EbcContext.Ip = (UINT64)(UINTN)VmPtr->Ip; + EbcContext.Flags = VmPtr->Flags; + EbcContext.ControlFlags = 0; + SystemContext.SystemContextEbc = &EbcContext; + + mDebugExceptionCallback[ExceptionType] (ExceptionType, SystemContext); + // + // Restore the context structure and continue to execute + // + VmPtr->Gpr[0] = EbcContext.R0; + VmPtr->Gpr[1] = EbcContext.R1; + VmPtr->Gpr[2] = EbcContext.R2; + VmPtr->Gpr[3] = EbcContext.R3; + VmPtr->Gpr[4] = EbcContext.R4; + VmPtr->Gpr[5] = EbcContext.R5; + VmPtr->Gpr[6] = EbcContext.R6; + VmPtr->Gpr[7] = EbcContext.R7; + VmPtr->Ip = (VMIP)(UINTN)EbcContext.Ip; + VmPtr->Flags = EbcContext.Flags; + } + + return EFI_SUCCESS; +} + + +/** + To install default Callback function for the VM interpreter. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL + instance. + + @retval EFI_SUCCESS The function completed successfully. + @retval Others Some error occurs when creating periodic event. + +**/ +EFI_STATUS +EFIAPI +InitializeEbcCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This + ) +{ + INTN Index; + EFI_STATUS Status; + + // + // For ExceptionCallback + // + for (Index = 0; Index <= MAX_EBC_EXCEPTION; Index++) { + EbcDebugRegisterExceptionCallback ( + This, + 0, + CommonEbcExceptionHandler, + Index + ); + } + + // + // For PeriodicCallback + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + EbcPeriodicNotifyFunction, + &mVmPtr, + &mEbcPeriodicEvent + ); + if (EFI_ERROR(Status)) { + return Status; + } + + Status = gBS->SetTimer ( + mEbcPeriodicEvent, + TimerPeriodic, + EBC_VM_PERIODIC_CALLBACK_RATE + ); + if (EFI_ERROR(Status)) { + return Status; + } + + return EFI_SUCCESS; +} + + +/** + The default Exception Callback for the VM interpreter. + In this function, we report status code, and print debug information + about EBC_CONTEXT, then dead loop. + + @param InterruptType Interrupt type. + @param SystemContext EBC system context. + +**/ +VOID +EFIAPI +CommonEbcExceptionHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // We print debug information to let user know what happen. + // + DEBUG (( + EFI_D_ERROR, + "EBC Interrupter Version - 0x%016lx\n", + (UINT64) (((VM_MAJOR_VERSION & 0xFFFF) << 16) | ((VM_MINOR_VERSION & 0xFFFF))) + )); + DEBUG (( + EFI_D_ERROR, + "Exception Type - 0x%016lx\n", + (UINT64)(UINTN)InterruptType + )); + DEBUG (( + EFI_D_ERROR, + " R0 - 0x%016lx, R1 - 0x%016lx\n", + SystemContext.SystemContextEbc->R0, + SystemContext.SystemContextEbc->R1 + )); + DEBUG (( + EFI_D_ERROR, + " R2 - 0x%016lx, R3 - 0x%016lx\n", + SystemContext.SystemContextEbc->R2, + SystemContext.SystemContextEbc->R3 + )); + DEBUG (( + EFI_D_ERROR, + " R4 - 0x%016lx, R5 - 0x%016lx\n", + SystemContext.SystemContextEbc->R4, + SystemContext.SystemContextEbc->R5 + )); + DEBUG (( + EFI_D_ERROR, + " R6 - 0x%016lx, R7 - 0x%016lx\n", + SystemContext.SystemContextEbc->R6, + SystemContext.SystemContextEbc->R7 + )); + DEBUG (( + EFI_D_ERROR, + " Flags - 0x%016lx\n", + SystemContext.SystemContextEbc->Flags + )); + DEBUG (( + EFI_D_ERROR, + " ControlFlags - 0x%016lx\n", + SystemContext.SystemContextEbc->ControlFlags + )); + DEBUG (( + EFI_D_ERROR, + " Ip - 0x%016lx\n\n", + SystemContext.SystemContextEbc->Ip + )); + + // + // We deadloop here to make it easy to debug this issue. + // + CpuDeadLoop (); + + return ; +} + + +/** + The periodic callback function for EBC VM interpreter, which is used + to support the EFI debug support protocol. + + @param Event The Periodic Callback Event. + @param Context It should be the address of VM_CONTEXT pointer. + +**/ +VOID +EFIAPI +EbcPeriodicNotifyFunction ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + VM_CONTEXT *VmPtr; + + VmPtr = *(VM_CONTEXT **)Context; + + if (VmPtr != NULL) { + EbcDebugPeriodic (VmPtr); + } + + return ; +} + + +/** + The VM interpreter calls this function on a periodic basis to support + the EFI debug support protocol. + + @param VmPtr Pointer to a VM context for passing info to the + debugger. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcDebugPeriodic ( + IN VM_CONTEXT *VmPtr + ) +{ + EFI_SYSTEM_CONTEXT_EBC EbcContext; + EFI_SYSTEM_CONTEXT SystemContext; + + // + // If someone's registered for periodic callbacks, then call them. + // + if (mDebugPeriodicCallback != NULL) { + + // + // Initialize the context structure + // + EbcContext.R0 = (UINT64) VmPtr->Gpr[0]; + EbcContext.R1 = (UINT64) VmPtr->Gpr[1]; + EbcContext.R2 = (UINT64) VmPtr->Gpr[2]; + EbcContext.R3 = (UINT64) VmPtr->Gpr[3]; + EbcContext.R4 = (UINT64) VmPtr->Gpr[4]; + EbcContext.R5 = (UINT64) VmPtr->Gpr[5]; + EbcContext.R6 = (UINT64) VmPtr->Gpr[6]; + EbcContext.R7 = (UINT64) VmPtr->Gpr[7]; + EbcContext.Ip = (UINT64)(UINTN)VmPtr->Ip; + EbcContext.Flags = VmPtr->Flags; + EbcContext.ControlFlags = 0; + SystemContext.SystemContextEbc = &EbcContext; + + mDebugPeriodicCallback (SystemContext); + + // + // Restore the context structure and continue to execute + // + VmPtr->Gpr[0] = EbcContext.R0; + VmPtr->Gpr[1] = EbcContext.R1; + VmPtr->Gpr[2] = EbcContext.R2; + VmPtr->Gpr[3] = EbcContext.R3; + VmPtr->Gpr[4] = EbcContext.R4; + VmPtr->Gpr[5] = EbcContext.R5; + VmPtr->Gpr[6] = EbcContext.R6; + VmPtr->Gpr[7] = EbcContext.R7; + VmPtr->Ip = (VMIP)(UINTN)EbcContext.Ip; + VmPtr->Flags = EbcContext.Flags; + } + + return EFI_SUCCESS; +} + + +/** + This routine is called by the core when an image is being unloaded from + memory. Basically we now have the opportunity to do any necessary cleanup. + Typically this will include freeing any memory allocated for thunk-creation. + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param ImageHandle Handle of image for which the thunk is being + created. + + @retval EFI_INVALID_PARAMETER The ImageHandle passed in was not found in the + internal list of EBC image handles. + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcUnloadImage ( + IN EFI_EBC_PROTOCOL *This, + IN EFI_HANDLE ImageHandle + ) +{ + EBC_THUNK_LIST *ThunkList; + EBC_THUNK_LIST *NextThunkList; + EBC_IMAGE_LIST *ImageList; + EBC_IMAGE_LIST *PrevImageList; + // + // First go through our list of known image handles and see if we've already + // created an image list element for this image handle. + // + ReturnEBCStackByHandle(ImageHandle); + PrevImageList = NULL; + for (ImageList = mEbcImageList; ImageList != NULL; ImageList = ImageList->Next) { + if (ImageList->ImageHandle == ImageHandle) { + break; + } + // + // Save the previous so we can connect the lists when we remove this one + // + PrevImageList = ImageList; + } + + if (ImageList == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Free up all the thunk buffers and thunks list elements for this image + // handle. + // + ThunkList = ImageList->ThunkList; + while (ThunkList != NULL) { + NextThunkList = ThunkList->Next; + FreePool (ThunkList->ThunkBuffer); + FreePool (ThunkList); + ThunkList = NextThunkList; + } + // + // Now remove this image list element from the chain + // + if (PrevImageList == NULL) { + // + // Remove from head + // + mEbcImageList = ImageList->Next; + } else { + PrevImageList->Next = ImageList->Next; + } + // + // Now free up the image list element + // + FreePool (ImageList); + + EbcDebuggerHookEbcUnloadImage (ImageHandle); + + return EFI_SUCCESS; +} + + +/** + Add a thunk to our list of thunks for a given image handle. + Also flush the instruction cache since we've written thunk code + to memory that will be executed eventually. + + @param ImageHandle The image handle to which the thunk is tied. + @param ThunkBuffer The buffer that has been created/allocated. + @param ThunkSize The size of the thunk memory allocated. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EbcAddImageThunk ( + IN EFI_HANDLE ImageHandle, + IN VOID *ThunkBuffer, + IN UINT32 ThunkSize + ) +{ + EBC_THUNK_LIST *ThunkList; + EBC_IMAGE_LIST *ImageList; + EFI_STATUS Status; + + // + // It so far so good, then flush the instruction cache + // + if (mEbcICacheFlush != NULL) { + Status = mEbcICacheFlush ((EFI_PHYSICAL_ADDRESS) (UINTN) ThunkBuffer, ThunkSize); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // Go through our list of known image handles and see if we've already + // created a image list element for this image handle. + // + for (ImageList = mEbcImageList; ImageList != NULL; ImageList = ImageList->Next) { + if (ImageList->ImageHandle == ImageHandle) { + break; + } + } + + if (ImageList == NULL) { + // + // Allocate a new one + // + ImageList = AllocatePool (sizeof (EBC_IMAGE_LIST)); + + if (ImageList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ImageList->ThunkList = NULL; + ImageList->ImageHandle = ImageHandle; + ImageList->Next = mEbcImageList; + mEbcImageList = ImageList; + } + // + // Ok, now create a new thunk element to add to the list + // + ThunkList = AllocatePool (sizeof (EBC_THUNK_LIST)); + + if (ThunkList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Add it to the head of the list + // + ThunkList->Next = ImageList->ThunkList; + ThunkList->ThunkBuffer = ThunkBuffer; + ImageList->ThunkList = ThunkList; + return EFI_SUCCESS; +} + +/** + Registers a callback function that the EBC interpreter calls to flush the + processor instruction cache following creation of thunks. + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param Flush Pointer to a function of type EBC_ICACH_FLUSH. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EFIAPI +EbcRegisterICacheFlush ( + IN EFI_EBC_PROTOCOL *This, + IN EBC_ICACHE_FLUSH Flush + ) +{ + mEbcICacheFlush = Flush; + return EFI_SUCCESS; +} + +/** + Called to get the version of the interpreter. + + @param This A pointer to the EFI_EBC_PROTOCOL instance. + @param Version Pointer to where to store the returned version + of the interpreter. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Version pointer is NULL. + +**/ +EFI_STATUS +EFIAPI +EbcGetVersion ( + IN EFI_EBC_PROTOCOL *This, + IN OUT UINT64 *Version + ) +{ + if (Version == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Version = GetVmVersion (); + return EFI_SUCCESS; +} + +/** + Returns the stack index and buffer assosicated with the Handle parameter. + + @param Handle The EFI handle as the index to the EBC stack. + @param StackBuffer A pointer to hold the returned stack buffer. + @param BufferIndex A pointer to hold the returned stack index. + + @retval EFI_OUT_OF_RESOURCES The Handle parameter does not correspond to any + existing EBC stack. + @retval EFI_SUCCESS The stack index and buffer were found and + returned to the caller. + +**/ +EFI_STATUS +GetEBCStack( + IN EFI_HANDLE Handle, + OUT VOID **StackBuffer, + OUT UINTN *BufferIndex + ) +{ + UINTN Index; + EFI_TPL OldTpl; + OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL); + for (Index = 0; Index < mStackNum; Index ++) { + if (mStackBufferIndex[Index] == NULL) { + mStackBufferIndex[Index] = Handle; + break; + } + } + gBS->RestoreTPL(OldTpl); + if (Index == mStackNum) { + return EFI_OUT_OF_RESOURCES; + } + *BufferIndex = Index; + *StackBuffer = mStackBuffer[Index]; + return EFI_SUCCESS; +} + +/** + Returns from the EBC stack by stack Index. + + @param Index Specifies which EBC stack to return from. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +ReturnEBCStack( + IN UINTN Index + ) +{ + mStackBufferIndex[Index] = NULL; + return EFI_SUCCESS; +} + +/** + Returns from the EBC stack associated with the Handle parameter. + + @param Handle Specifies the EFI handle to find the EBC stack with. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +ReturnEBCStackByHandle( + IN EFI_HANDLE Handle + ) +{ + UINTN Index; + for (Index = 0; Index < mStackNum; Index ++) { + if (mStackBufferIndex[Index] == Handle) { + break; + } + } + if (Index == mStackNum) { + return EFI_NOT_FOUND; + } + mStackBufferIndex[Index] = NULL; + return EFI_SUCCESS; +} + +/** + Allocates memory to hold all the EBC stacks. + + @retval EFI_SUCCESS The EBC stacks were allocated successfully. + @retval EFI_OUT_OF_RESOURCES Not enough memory available for EBC stacks. + +**/ +EFI_STATUS +InitEBCStack ( + VOID + ) +{ + for (mStackNum = 0; mStackNum < MAX_STACK_NUM; mStackNum ++) { + mStackBuffer[mStackNum] = AllocatePool(STACK_POOL_SIZE); + mStackBufferIndex[mStackNum] = NULL; + if (mStackBuffer[mStackNum] == NULL) { + break; + } + } + if (mStackNum == 0) { + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; +} + + +/** + Free all EBC stacks allocated before. + + @retval EFI_SUCCESS All the EBC stacks were freed. + +**/ +EFI_STATUS +FreeEBCStack( + VOID + ) +{ + UINTN Index; + for (Index = 0; Index < mStackNum; Index ++) { + FreePool(mStackBuffer[Index]); + } + return EFI_SUCCESS; +} + +/** + Produces an EBC VM test protocol that can be used for regression tests. + + @param IHandle Handle on which to install the protocol. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +InitEbcVmTestProtocol ( + IN EFI_HANDLE *IHandle + ) +{ + EFI_HANDLE Handle; + EFI_STATUS Status; + EFI_EBC_VM_TEST_PROTOCOL *EbcVmTestProtocol; + + // + // Allocate memory for the protocol, then fill in the fields + // + EbcVmTestProtocol = AllocatePool (sizeof (EFI_EBC_VM_TEST_PROTOCOL)); + if (EbcVmTestProtocol == NULL) { + return EFI_OUT_OF_RESOURCES; + } + EbcVmTestProtocol->Execute = (EBC_VM_TEST_EXECUTE) EbcExecuteInstructions; + + DEBUG_CODE_BEGIN (); + EbcVmTestProtocol->Assemble = (EBC_VM_TEST_ASM) EbcVmTestUnsupported; + EbcVmTestProtocol->Disassemble = (EBC_VM_TEST_DASM) EbcVmTestUnsupported; + DEBUG_CODE_END (); + + // + // Publish the protocol + // + Handle = NULL; + Status = gBS->InstallProtocolInterface (&Handle, &gEfiEbcVmTestProtocolGuid, EFI_NATIVE_INTERFACE, EbcVmTestProtocol); + if (EFI_ERROR (Status)) { + FreePool (EbcVmTestProtocol); + } + return Status; +} + + +/** + Returns the EFI_UNSUPPORTED Status. + + @return EFI_UNSUPPORTED This function always return EFI_UNSUPPORTED status. + +**/ +EFI_STATUS +EFIAPI +EbcVmTestUnsupported ( + VOID + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Allocates a buffer of type EfiBootServicesCode. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +EbcAllocatePoolForThunk ( + IN UINTN AllocationSize + ) +{ + VOID *Buffer; + EFI_STATUS Status; + + Status = gBS->AllocatePool (EfiBootServicesCode, AllocationSize, &Buffer); + if (EFI_ERROR (Status)) { + return NULL; + } + return Buffer; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.h b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.h new file mode 100644 index 000000000..16f5ed4eb --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/EbcInt.h @@ -0,0 +1,260 @@ +/** @file + Main routines for the EBC interpreter. Includes the initialization and + main interpreter routines. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EBC_INT_H_ +#define _EBC_INT_H_ + + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +extern VM_CONTEXT *mVmPtr; + +// +// Flags passed to the internal create-thunks function. +// +#define FLAG_THUNK_ENTRY_POINT 0x01 // thunk for an image entry point +#define FLAG_THUNK_PROTOCOL 0x00 // thunk for an EBC protocol service +// +// Put this value at the bottom of the VM's stack gap so we can check it on +// occasion to make sure the stack has not been corrupted. +// +#define VM_STACK_KEY_VALUE 0xDEADBEEF + +/** + Create thunks for an EBC image entry point, or an EBC protocol service. + + @param ImageHandle Image handle for the EBC image. If not null, then + we're creating a thunk for an image entry point. + @param EbcEntryPoint Address of the EBC code that the thunk is to call + @param Thunk Returned thunk we create here + @param Flags Flags indicating options for creating the thunk + + @retval EFI_SUCCESS The thunk was created successfully. + @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit + aligned. + @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC + Thunk. + @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough. + +**/ +EFI_STATUS +EbcCreateThunks ( + IN EFI_HANDLE ImageHandle, + IN VOID *EbcEntryPoint, + OUT VOID **Thunk, + IN UINT32 Flags + ); + +/** + Add a thunk to our list of thunks for a given image handle. + Also flush the instruction cache since we've written thunk code + to memory that will be executed eventually. + + @param ImageHandle The image handle to which the thunk is tied. + @param ThunkBuffer The buffer that has been created/allocated. + @param ThunkSize The size of the thunk memory allocated. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +EbcAddImageThunk ( + IN EFI_HANDLE ImageHandle, + IN VOID *ThunkBuffer, + IN UINT32 ThunkSize + ); + +// +// Define a constant of how often to call the debugger periodic callback +// function. +// +#define EFI_TIMER_UNIT_1MS (1000 * 10) +#define EBC_VM_PERIODIC_CALLBACK_RATE (1000 * EFI_TIMER_UNIT_1MS) +#define STACK_POOL_SIZE (1024 * 1020) +#define MAX_STACK_NUM 4 + +// +// External low level functions that are native-processor dependent +// +/** + The VM thunk code stuffs an EBC entry point into a processor + register. Since we can't use inline assembly to get it from + the interpreter C code, stuff it into the return value + register and return. + + @return The contents of the register in which the entry point is passed. + +**/ +UINTN +EFIAPI +EbcLLGetEbcEntryPoint ( + VOID + ); + +/** + This function is called to execute an EBC CALLEX instruction. + This instruction requires that we thunk out to external native + code. For x64, we switch stacks, copy the arguments to the stack + and jump to the specified function. + On return, we restore the stack pointer to its original location. + Destroys no working registers. + + @param CallAddr The function address. + @param EbcSp The new EBC stack pointer. + @param FramePtr The frame pointer. + + @return The unmodified value returned by the native code. + +**/ +INT64 +EFIAPI +EbcLLCALLEXNative ( + IN UINTN CallAddr, + IN UINTN EbcSp, + IN VOID *FramePtr + ); + +/** + This function is called to execute an EBC CALLEX instruction. + The function check the callee's content to see whether it is common native + code or a thunk to another piece of EBC code. + If the callee is common native code, use EbcLLCAllEXASM to manipulate, + otherwise, set the VM->IP to target EBC code directly to avoid another VM + be startup which cost time and stack space. + + @param VmPtr Pointer to a VM context. + @param FuncAddr Callee's address + @param NewStackPointer New stack pointer after the call + @param FramePtr New frame pointer after the call + @param Size The size of call instruction + +**/ +VOID +EbcLLCALLEX ( + IN VM_CONTEXT *VmPtr, + IN UINTN FuncAddr, + IN UINTN NewStackPointer, + IN VOID *FramePtr, + IN UINT8 Size + ); + +/** + Returns the stack index and buffer assosicated with the Handle parameter. + + @param Handle The EFI handle as the index to the EBC stack. + @param StackBuffer A pointer to hold the returned stack buffer. + @param BufferIndex A pointer to hold the returned stack index. + + @retval EFI_OUT_OF_RESOURCES The Handle parameter does not correspond to any + existing EBC stack. + @retval EFI_SUCCESS The stack index and buffer were found and + returned to the caller. + +**/ +EFI_STATUS +GetEBCStack( + IN EFI_HANDLE Handle, + OUT VOID **StackBuffer, + OUT UINTN *BufferIndex + ); + +/** + Returns from the EBC stack by stack Index. + + @param Index Specifies which EBC stack to return from. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +ReturnEBCStack( + IN UINTN Index + ); + +/** + Allocates memory to hold all the EBC stacks. + + @retval EFI_SUCCESS The EBC stacks were allocated successfully. + @retval EFI_OUT_OF_RESOURCES Not enough memory available for EBC stacks. + +**/ +EFI_STATUS +InitEBCStack ( + VOID + ); + +/** + Free all EBC stacks allocated before. + + @retval EFI_SUCCESS All the EBC stacks were freed. + +**/ +EFI_STATUS +FreeEBCStack( + VOID + ); + +/** + Returns from the EBC stack associated with the Handle parameter. + + @param Handle Specifies the EFI handle to find the EBC stack with. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +ReturnEBCStackByHandle( + IN EFI_HANDLE Handle + ); + +typedef struct { + EFI_EBC_PROTOCOL *This; + VOID *EntryPoint; + EFI_HANDLE ImageHandle; + VM_CONTEXT VmContext; +} EFI_EBC_THUNK_DATA; + +#define EBC_PROTOCOL_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('e', 'b', 'c', 'p') + + +#define EBC_PROTOCOL_PRIVATE_DATA_FROM_THIS(a) \ + CR(a, EBC_PROTOCOL_PRIVATE_DATA, EbcProtocol, EBC_PROTOCOL_PRIVATE_DATA_SIGNATURE) + + +/** + Allocates a buffer of type EfiBootServicesCode. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +EbcAllocatePoolForThunk ( + IN UINTN AllocationSize + ); + +#endif // #ifndef _EBC_INT_H_ diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcLowLevel.nasm b/roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcLowLevel.nasm new file mode 100644 index 000000000..60e41428b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcLowLevel.nasm @@ -0,0 +1,191 @@ +;/** @file +; +; This code provides low level routines that support the Virtual Machine +; for option ROMs. +; +; Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;**/ + +;--------------------------------------------------------------------------- +; Equate files needed. +;--------------------------------------------------------------------------- + +;--------------------------------------------------------------------------- +; Assembler options +;--------------------------------------------------------------------------- + +SECTION .text +extern ASM_PFX(CopyMem) +extern ASM_PFX(EbcInterpret) +extern ASM_PFX(ExecuteEbcImageEntryPoint) + +;**************************************************************************** +; EbcLLCALLEXNative +; +; This function is called to execute an EBC CALLEX instruction +; to native code. +; This instruction requires that we thunk out to external native +; code. For IA32, we simply switch stacks and jump to the +; specified function. On return, we restore the stack pointer +; to its original location. +; +; Destroys no working registers. +;**************************************************************************** +; INT64 EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr) +global ASM_PFX(EbcLLCALLEXNative) +ASM_PFX(EbcLLCALLEXNative): + push ebp + push ebx + mov ebp, esp ; standard function prolog + + ; Get function address in a register + ; mov ecx, FuncAddr => mov ecx, dword ptr [FuncAddr] + mov ecx, dword [esp + 0xC] + + ; Set stack pointer to new value + ; mov eax, NewStackPointer => mov eax, dword ptr [NewSp] + mov eax, dword [esp + 0x14] + mov edx, dword [esp + 0x10] + sub eax, edx + sub esp, eax + mov ebx, esp + push ecx + push eax + push edx + push ebx + call ASM_PFX(CopyMem) + pop eax + pop eax + pop eax + pop ecx + + ; Now call the external routine + call ecx + + ; ebp is preserved by the callee. In this function it + ; equals the original esp, so set them equal + mov esp, ebp + + ; Standard function epilog + mov esp, ebp + pop ebx + pop ebp + ret + +;**************************************************************************** +; EbcLLEbcInterpret +; +; Begin executing an EBC image. +;**************************************************************************** +; UINT64 EbcLLEbcInterpret(VOID) +global ASM_PFX(EbcLLEbcInterpret) +ASM_PFX(EbcLLEbcInterpret): + ; + ;; mov eax, 0xca112ebc + ;; mov eax, EbcEntryPoint + ;; mov ecx, EbcLLEbcInterpret + ;; jmp ecx + ; + ; Caller uses above instruction to jump here + ; The stack is below: + ; +-----------+ + ; | RetAddr | + ; +-----------+ + ; |EntryPoint | (EAX) + ; +-----------+ + ; | Arg1 | <- EDI + ; +-----------+ + ; | Arg2 | + ; +-----------+ + ; | ... | + ; +-----------+ + ; | Arg16 | + ; +-----------+ + ; | EDI | + ; +-----------+ + ; | ESI | + ; +-----------+ + ; | EBP | <- EBP + ; +-----------+ + ; | RetAddr | <- ESP is here + ; +-----------+ + ; | Arg1 | <- ESI + ; +-----------+ + ; | Arg2 | + ; +-----------+ + ; | ... | + ; +-----------+ + ; | Arg16 | + ; +-----------+ + ; + + ; Construct new stack + push ebp + mov ebp, esp + push esi + push edi + sub esp, 0x40 + push eax + mov esi, ebp + add esi, 8 + mov edi, esp + add edi, 4 + mov ecx, 16 + rep movsd + + ; call C-code + call ASM_PFX(EbcInterpret) + add esp, 0x44 + pop edi + pop esi + pop ebp + ret + +;**************************************************************************** +; EbcLLExecuteEbcImageEntryPoint +; +; Begin executing an EBC image. +;**************************************************************************** +; UINT64 EbcLLExecuteEbcImageEntryPoint(VOID) +global ASM_PFX(EbcLLExecuteEbcImageEntryPoint) +ASM_PFX(EbcLLExecuteEbcImageEntryPoint): + ; + ;; mov eax, 0xca112ebc + ;; mov eax, EbcEntryPoint + ;; mov ecx, EbcLLExecuteEbcImageEntryPoint + ;; jmp ecx + ; + ; Caller uses above instruction to jump here + ; The stack is below: + ; +-----------+ + ; | RetAddr | + ; +-----------+ + ; |EntryPoint | (EAX) + ; +-----------+ + ; |ImageHandle| + ; +-----------+ + ; |SystemTable| + ; +-----------+ + ; | RetAddr | <- ESP is here + ; +-----------+ + ; |ImageHandle| + ; +-----------+ + ; |SystemTable| + ; +-----------+ + ; + + ; Construct new stack + mov [esp - 0xC], eax + mov eax, [esp + 0x4] + mov [esp - 0x8], eax + mov eax, [esp + 0x8] + mov [esp - 0x4], eax + + ; call C-code + sub esp, 0xC + call ASM_PFX(ExecuteEbcImageEntryPoint) + add esp, 0xC + ret + diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcSupport.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcSupport.c new file mode 100644 index 000000000..a25139536 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/Ia32/EbcSupport.c @@ -0,0 +1,526 @@ +/** @file + This module contains EBC support routines that are customized based on + the target ia32 processor. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EbcInt.h" +#include "EbcExecute.h" +#include "EbcDebuggerHook.h" + +// +// NOTE: This is the stack size allocated for the interpreter +// when it executes an EBC image. The requirements can change +// based on whether or not a debugger is present, and other +// platform-specific configurations. +// +#define VM_STACK_SIZE (1024 * 4) + +#define STACK_REMAIN_SIZE (1024 * 4) + +// +// This is instruction buffer used to create EBC thunk +// +#define EBC_ENTRYPOINT_SIGNATURE 0xAFAFAFAF +#define EBC_LL_EBC_ENTRYPOINT_SIGNATURE 0xFAFAFAFA +UINT8 mInstructionBufferTemplate[] = { + // + // Add a magic code here to help the VM recognize the thunk.. + // mov eax, 0xca112ebc => B8 BC 2E 11 CA + // + 0xB8, 0xBC, 0x2E, 0x11, 0xCA, + // + // Add code bytes to load up a processor register with the EBC entry point. + // mov eax, EbcEntryPoint => B8 XX XX XX XX (To be fixed at runtime) + // These 4 bytes of the thunk entry is the address of the EBC + // entry point. + // + 0xB8, + (UINT8)(EBC_ENTRYPOINT_SIGNATURE & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF), + // + // Stick in a load of ecx with the address of appropriate VM function. + // mov ecx, EbcLLEbcInterpret => B9 XX XX XX XX (To be fixed at runtime) + // + 0xB9, + (UINT8)(EBC_LL_EBC_ENTRYPOINT_SIGNATURE & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF), + // + // Stick in jump opcode bytes + // jmp ecx => FF E1 + // + 0xFF, 0xE1, +}; + +/** + Begin executing an EBC image. + This is used for Ebc Thunk call. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcLLEbcInterpret ( + VOID + ); + +/** + Begin executing an EBC image. + This is used for Ebc image entrypoint. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcLLExecuteEbcImageEntryPoint ( + VOID + ); + +/** + This function is called to execute an EBC CALLEX instruction. + The function check the callee's content to see whether it is common native + code or a thunk to another piece of EBC code. + If the callee is common native code, use EbcLLCAllEXASM to manipulate, + otherwise, set the VM->IP to target EBC code directly to avoid another VM + be startup which cost time and stack space. + + @param VmPtr Pointer to a VM context. + @param FuncAddr Callee's address + @param NewStackPointer New stack pointer after the call + @param FramePtr New frame pointer after the call + @param Size The size of call instruction + +**/ +VOID +EbcLLCALLEX ( + IN VM_CONTEXT *VmPtr, + IN UINTN FuncAddr, + IN UINTN NewStackPointer, + IN VOID *FramePtr, + IN UINT8 Size + ) +{ + UINTN IsThunk; + UINTN TargetEbcAddr; + UINT8 InstructionBuffer[sizeof(mInstructionBufferTemplate)]; + UINTN Index; + UINTN IndexOfEbcEntrypoint; + + IsThunk = 1; + TargetEbcAddr = 0; + IndexOfEbcEntrypoint = 0; + + // + // Processor specific code to check whether the callee is a thunk to EBC. + // + CopyMem (InstructionBuffer, (VOID *)FuncAddr, sizeof(InstructionBuffer)); + // + // Fill the signature according to mInstructionBufferTemplate + // + for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) { + if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_ENTRYPOINT_SIGNATURE) { + *(UINTN *)&InstructionBuffer[Index] = EBC_ENTRYPOINT_SIGNATURE; + IndexOfEbcEntrypoint = Index; + } + if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) { + *(UINTN *)&InstructionBuffer[Index] = EBC_LL_EBC_ENTRYPOINT_SIGNATURE; + } + } + // + // Check if we need thunk to native + // + if (CompareMem (InstructionBuffer, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate)) != 0) { + IsThunk = 0; + } + + if (IsThunk == 1){ + // + // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and + // put our return address and frame pointer on the VM stack. + // Then set the VM's IP to new EBC code. + // + VmPtr->Gpr[0] -= 8; + VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr); + VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0]; + VmPtr->Gpr[0] -= 8; + VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size)); + + CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + IndexOfEbcEntrypoint, sizeof(UINTN)); + VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr; + } else { + // + // The callee is not a thunk to EBC, call native code, + // and get return value. + // + VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr); + + // + // Advance the IP. + // + VmPtr->Ip += Size; + } +} + + +/** + Begin executing an EBC image. + + This is a thunk function. Microsoft x64 compiler only provide fast_call + calling convention, so the first four arguments are passed by rcx, rdx, + r8, and r9, while other arguments are passed in stack. + + @param EntryPoint The entrypoint of EBC code. + @param Arg1 The 1st argument. + @param Arg2 The 2nd argument. + @param Arg3 The 3rd argument. + @param Arg4 The 4th argument. + @param Arg5 The 5th argument. + @param Arg6 The 6th argument. + @param Arg7 The 7th argument. + @param Arg8 The 8th argument. + @param Arg9 The 9th argument. + @param Arg10 The 10th argument. + @param Arg11 The 11th argument. + @param Arg12 The 12th argument. + @param Arg13 The 13th argument. + @param Arg14 The 14th argument. + @param Arg15 The 15th argument. + @param Arg16 The 16th argument. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcInterpret ( + IN UINTN EntryPoint, + IN UINTN Arg1, + IN UINTN Arg2, + IN UINTN Arg3, + IN UINTN Arg4, + IN UINTN Arg5, + IN UINTN Arg6, + IN UINTN Arg7, + IN UINTN Arg8, + IN UINTN Arg9, + IN UINTN Arg10, + IN UINTN Arg11, + IN UINTN Arg12, + IN UINTN Arg13, + IN UINTN Arg14, + IN UINTN Arg15, + IN UINTN Arg16 + ) +{ + // + // Create a new VM context on the stack + // + VM_CONTEXT VmContext; + UINTN Addr; + EFI_STATUS Status; + UINTN StackIndex; + + // + // Get the EBC entry point + // + Addr = EntryPoint; + + // + // Now clear out our context + // + ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT)); + + // + // Set the VM instruction pointer to the correct location in memory. + // + VmContext.Ip = (VMIP) Addr; + // + // Initialize the stack pointer for the EBC. Get the current system stack + // pointer and adjust it down by the max needed for the interpreter. + // + + // + // Align the stack on a natural boundary + // + + // + // Allocate stack pool + // + Status = GetEBCStack((EFI_HANDLE)-1, &VmContext.StackPool, &StackIndex); + if (EFI_ERROR(Status)) { + return Status; + } + VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE); + VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE); + VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0]; + VmContext.Gpr[0] &= ~((VM_REGISTER)(sizeof (UINTN) - 1)); + VmContext.Gpr[0] -= sizeof (UINTN); + + // + // Put a magic value in the stack gap, then adjust down again + // + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE; + VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0]; + VmContext.LowStackTop = (UINTN) VmContext.Gpr[0]; + + // + // For IA32, this is where we say our return address is + // + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg16; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg15; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg14; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg13; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg12; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg11; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg10; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg9; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg8; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg7; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg6; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg5; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg4; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg3; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg2; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) Arg1; + VmContext.Gpr[0] -= 16; + VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0]; + + // + // We need to keep track of where the EBC stack starts. This way, if the EBC + // accesses any stack variables above its initial stack setting, then we know + // it's accessing variables passed into it, which means the data is on the + // VM's stack. + // When we're called, on the stack (high to low) we have the parameters, the + // return address, then the saved ebp. Save the pointer to the return address. + // EBC code knows that's there, so should look above it for function parameters. + // The offset is the size of locals (VMContext + Addr + saved ebp). + // Note that the interpreter assumes there is a 16 bytes of return address on + // the stack too, so adjust accordingly. + // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr)); + // + + // + // Begin executing the EBC code + // + EbcDebuggerHookEbcInterpret (&VmContext); + EbcExecute (&VmContext); + + // + // Return the value in Gpr[7] unless there was an error + // + ReturnEBCStack(StackIndex); + return (UINT64) VmContext.Gpr[7]; +} + + +/** + Begin executing an EBC image. + + @param EntryPoint The entrypoint of EBC code. + @param ImageHandle image handle for the EBC application we're executing + @param SystemTable standard system table passed into an driver's entry + point + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +ExecuteEbcImageEntryPoint ( + IN UINTN EntryPoint, + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + // + // Create a new VM context on the stack + // + VM_CONTEXT VmContext; + UINTN Addr; + EFI_STATUS Status; + UINTN StackIndex; + + // + // Get the EBC entry point + // + Addr = EntryPoint; + + // + // Now clear out our context + // + ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT)); + + // + // Save the image handle so we can track the thunks created for this image + // + VmContext.ImageHandle = ImageHandle; + VmContext.SystemTable = SystemTable; + + // + // Set the VM instruction pointer to the correct location in memory. + // + VmContext.Ip = (VMIP) Addr; + + // + // Initialize the stack pointer for the EBC. Get the current system stack + // pointer and adjust it down by the max needed for the interpreter. + // + + // + // Allocate stack pool + // + Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex); + if (EFI_ERROR(Status)) { + return Status; + } + VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE); + VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE); + VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0]; + VmContext.Gpr[0] -= sizeof (UINTN); + + // + // Put a magic value in the stack gap, then adjust down again + // + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE; + VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0]; + + // + // Align the stack on a natural boundary + // VmContext.Gpr[0] &= ~(sizeof(UINTN) - 1); + // + VmContext.LowStackTop = (UINTN) VmContext.Gpr[0]; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) SystemTable; + VmContext.Gpr[0] -= sizeof (UINTN); + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) ImageHandle; + + VmContext.Gpr[0] -= 16; + VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0]; + // + // VM pushes 16-bytes for return address. Simulate that here. + // + + // + // Begin executing the EBC code + // + EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext); + EbcExecute (&VmContext); + + // + // Return the value in Gpr[7] unless there was an error + // + ReturnEBCStack(StackIndex); + return (UINT64) VmContext.Gpr[7]; +} + + +/** + Create thunks for an EBC image entry point, or an EBC protocol service. + + @param ImageHandle Image handle for the EBC image. If not null, then + we're creating a thunk for an image entry point. + @param EbcEntryPoint Address of the EBC code that the thunk is to call + @param Thunk Returned thunk we create here + @param Flags Flags indicating options for creating the thunk + + @retval EFI_SUCCESS The thunk was created successfully. + @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit + aligned. + @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC + Thunk. + @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough. + +**/ +EFI_STATUS +EbcCreateThunks ( + IN EFI_HANDLE ImageHandle, + IN VOID *EbcEntryPoint, + OUT VOID **Thunk, + IN UINT32 Flags + ) +{ + UINT8 *Ptr; + UINT8 *ThunkBase; + UINT32 Index; + INT32 ThunkSize; + + // + // Check alignment of pointer to EBC code + // + if ((UINT32) (UINTN) EbcEntryPoint & 0x01) { + return EFI_INVALID_PARAMETER; + } + + ThunkSize = sizeof(mInstructionBufferTemplate); + + Ptr = EbcAllocatePoolForThunk (sizeof(mInstructionBufferTemplate)); + + if (Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr); + // + // Save the start address so we can add a pointer to it to a list later. + // + ThunkBase = Ptr; + + // + // Give them the address of our buffer we're going to fix up + // + *Thunk = (VOID *) Ptr; + + // + // Copy whole thunk instruction buffer template + // + CopyMem (Ptr, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate)); + + // + // Patch EbcEntryPoint and EbcLLEbcInterpret + // + for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) { + if (*(UINTN *)&Ptr[Index] == EBC_ENTRYPOINT_SIGNATURE) { + *(UINTN *)&Ptr[Index] = (UINTN)EbcEntryPoint; + } + if (*(UINTN *)&Ptr[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) { + if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) { + *(UINTN *)&Ptr[Index] = (UINTN)EbcLLExecuteEbcImageEntryPoint; + } else { + *(UINTN *)&Ptr[Index] = (UINTN)EbcLLEbcInterpret; + } + } + } + + // + // Add the thunk to the list for this image. Do this last since the add + // function flushes the cache for us. + // + EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcLowLevel.nasm b/roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcLowLevel.nasm new file mode 100644 index 000000000..3a8707616 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcLowLevel.nasm @@ -0,0 +1,236 @@ +;/** @file +; +; This code provides low level routines that support the Virtual Machine. +; for option ROMs. +; +; Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+; Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;**/ + +;--------------------------------------------------------------------------- +; Equate files needed. +;--------------------------------------------------------------------------- + +DEFAULT REL +SECTION .text + +extern ASM_PFX(CopyMem) +extern ASM_PFX(EbcInterpret) +extern ASM_PFX(ExecuteEbcImageEntryPoint) + +;**************************************************************************** +; EbcLLCALLEX +; +; This function is called to execute an EBC CALLEX instruction. +; This instruction requires that we thunk out to external native +; code. For x64, we switch stacks, copy the arguments to the stack +; and jump to the specified function. +; On return, we restore the stack pointer to its original location. +; +; Destroys no working registers. +;**************************************************************************** +; INT64 EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr) +global ASM_PFX(EbcLLCALLEXNative) +ASM_PFX(EbcLLCALLEXNative): + push rbp + push rbx + mov rbp, rsp + ; Function prolog + + ; Copy FuncAddr to a preserved register. + mov rbx, rcx + + ; Set stack pointer to new value + sub r8, rdx + + ; + ; Fix X64 native function call prolog. Prepare space for at least 4 arguments, + ; even if the native function's arguments are less than 4. + ; + ; From MSDN x64 Software Conventions, Overview of x64 Calling Conventions: + ; "The caller is responsible for allocating space for parameters to the + ; callee, and must always allocate sufficient space for the 4 register + ; parameters, even if the callee doesn't have that many parameters. + ; This aids in the simplicity of supporting C unprototyped functions, + ; and vararg C/C++ functions." + ; + cmp r8, 0x20 + jae skip_expansion + mov r8, dword 0x20 +skip_expansion: + + sub rsp, r8 + + ; + ; Fix X64 native function call 16-byte alignment. + ; + ; From MSDN x64 Software Conventions, Stack Usage: + ; "The stack will always be maintained 16-byte aligned, except within + ; the prolog (for example, after the return address is pushed)." + ; + and rsp, ~ 0xf + + mov rcx, rsp + sub rsp, 0x20 + call ASM_PFX(CopyMem) + add rsp, 0x20 + + ; Considering the worst case, load 4 potiential arguments + ; into registers. + mov rcx, qword [rsp] + mov rdx, qword [rsp+0x8] + mov r8, qword [rsp+0x10] + mov r9, qword [rsp+0x18] + + ; Now call the external routine + call rbx + + ; Function epilog + mov rsp, rbp + pop rbx + pop rbp + ret + +;**************************************************************************** +; EbcLLEbcInterpret +; +; Begin executing an EBC image. +;**************************************************************************** +; UINT64 EbcLLEbcInterpret(VOID) +global ASM_PFX(EbcLLEbcInterpret) +ASM_PFX(EbcLLEbcInterpret): + ; + ;; mov rax, ca112ebccall2ebch + ;; mov r10, EbcEntryPoint + ;; mov r11, EbcLLEbcInterpret + ;; jmp r11 + ; + ; Caller uses above instruction to jump here + ; The stack is below: + ; +-----------+ + ; | RetAddr | + ; +-----------+ + ; |EntryPoint | (R10) + ; +-----------+ + ; | Arg1 | <- RDI + ; +-----------+ + ; | Arg2 | + ; +-----------+ + ; | ... | + ; +-----------+ + ; | Arg16 | + ; +-----------+ + ; | Dummy | + ; +-----------+ + ; | RDI | + ; +-----------+ + ; | RSI | + ; +-----------+ + ; | RBP | <- RBP + ; +-----------+ + ; | RetAddr | <- RSP is here + ; +-----------+ + ; | Scratch1 | (RCX) <- RSI + ; +-----------+ + ; | Scratch2 | (RDX) + ; +-----------+ + ; | Scratch3 | (R8) + ; +-----------+ + ; | Scratch4 | (R9) + ; +-----------+ + ; | Arg5 | + ; +-----------+ + ; | Arg6 | + ; +-----------+ + ; | ... | + ; +-----------+ + ; | Arg16 | + ; +-----------+ + ; + + ; save old parameter to stack + mov [rsp + 0x8], rcx + mov [rsp + 0x10], rdx + mov [rsp + 0x18], r8 + mov [rsp + 0x20], r9 + + ; Construct new stack + push rbp + mov rbp, rsp + push rsi + push rdi + push rbx + sub rsp, 0x80 + push r10 + mov rsi, rbp + add rsi, 0x10 + mov rdi, rsp + add rdi, 8 + mov rcx, dword 16 + rep movsq + + ; build new paramater calling convention + mov r9, [rsp + 0x18] + mov r8, [rsp + 0x10] + mov rdx, [rsp + 0x8] + mov rcx, r10 + + ; call C-code + call ASM_PFX(EbcInterpret) + add rsp, 0x88 + pop rbx + pop rdi + pop rsi + pop rbp + ret + +;**************************************************************************** +; EbcLLExecuteEbcImageEntryPoint +; +; Begin executing an EBC image. +;**************************************************************************** +; UINT64 EbcLLExecuteEbcImageEntryPoint(VOID) +global ASM_PFX(EbcLLExecuteEbcImageEntryPoint) +ASM_PFX(EbcLLExecuteEbcImageEntryPoint): + ; + ;; mov rax, ca112ebccall2ebch + ;; mov r10, EbcEntryPoint + ;; mov r11, EbcLLExecuteEbcImageEntryPoint + ;; jmp r11 + ; + ; Caller uses above instruction to jump here + ; The stack is below: + ; +-----------+ + ; | RetAddr | + ; +-----------+ + ; |EntryPoint | (R10) + ; +-----------+ + ; |ImageHandle| + ; +-----------+ + ; |SystemTable| + ; +-----------+ + ; | Dummy | + ; +-----------+ + ; | Dummy | + ; +-----------+ + ; | RetAddr | <- RSP is here + ; +-----------+ + ; |ImageHandle| (RCX) + ; +-----------+ + ; |SystemTable| (RDX) + ; +-----------+ + ; + + ; build new paramater calling convention + mov r8, rdx + mov rdx, rcx + mov rcx, r10 + + ; call C-code + sub rsp, 0x28 + call ASM_PFX(ExecuteEbcImageEntryPoint) + add rsp, 0x28 + ret + diff --git a/roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c b/roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c new file mode 100644 index 000000000..25ca8dbc4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EbcDxe/X64/EbcSupport.c @@ -0,0 +1,570 @@ +/** @file + This module contains EBC support routines that are customized based on + the target x64 processor. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EbcInt.h" +#include "EbcExecute.h" +#include "EbcDebuggerHook.h" + +// +// NOTE: This is the stack size allocated for the interpreter +// when it executes an EBC image. The requirements can change +// based on whether or not a debugger is present, and other +// platform-specific configurations. +// +#define VM_STACK_SIZE (1024 * 8) + +#define STACK_REMAIN_SIZE (1024 * 4) + +// +// This is instruction buffer used to create EBC thunk +// +#define EBC_ENTRYPOINT_SIGNATURE 0xAFAFAFAFAFAFAFAFull +#define EBC_LL_EBC_ENTRYPOINT_SIGNATURE 0xFAFAFAFAFAFAFAFAull +UINT8 mInstructionBufferTemplate[] = { + // + // Add a magic code here to help the VM recognize the thunk.. + // mov rax, 0xca112ebcca112ebc => 48 B8 BC 2E 11 CA BC 2E 11 CA + // + 0x48, 0xB8, 0xBC, 0x2E, 0x11, 0xCA, 0xBC, 0x2E, 0x11, 0xCA, + // + // Add code bytes to load up a processor register with the EBC entry point. + // mov r10, EbcEntryPoint => 49 BA XX XX XX XX XX XX XX XX (To be fixed at runtime) + // These 8 bytes of the thunk entry is the address of the EBC + // entry point. + // + 0x49, 0xBA, + (UINT8)(EBC_ENTRYPOINT_SIGNATURE & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF), + (UINT8)((EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF), + // + // Stick in a load of r11 with the address of appropriate VM function. + // mov r11, EbcLLEbcInterpret => 49 BB XX XX XX XX XX XX XX XX (To be fixed at runtime) + // + 0x49, 0xBB, + (UINT8)(EBC_LL_EBC_ENTRYPOINT_SIGNATURE & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 8) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 16) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 24) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 32) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 40) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 48) & 0xFF), + (UINT8)((EBC_LL_EBC_ENTRYPOINT_SIGNATURE >> 56) & 0xFF), + // + // Stick in jump opcode bytes + // jmp r11 => 41 FF E3 + // + 0x41, 0xFF, 0xE3, +}; + +/** + Begin executing an EBC image. + This is used for Ebc Thunk call. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcLLEbcInterpret ( + VOID + ); + +/** + Begin executing an EBC image. + This is used for Ebc image entrypoint. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcLLExecuteEbcImageEntryPoint ( + VOID + ); + +/** + Pushes a 64 bit unsigned value to the VM stack. + + @param VmPtr The pointer to current VM context. + @param Arg The value to be pushed. + +**/ +VOID +PushU64 ( + IN VM_CONTEXT *VmPtr, + IN UINT64 Arg + ) +{ + // + // Advance the VM stack down, and then copy the argument to the stack. + // Hope it's aligned. + // + VmPtr->Gpr[0] -= sizeof (UINT64); + *(UINT64 *) VmPtr->Gpr[0] = Arg; + return; +} + + +/** + Begin executing an EBC image. + + This is a thunk function. Microsoft x64 compiler only provide fast_call + calling convention, so the first four arguments are passed by rcx, rdx, + r8, and r9, while other arguments are passed in stack. + + @param EntryPoint The entrypoint of EBC code. + @param Arg1 The 1st argument. + @param Arg2 The 2nd argument. + @param Arg3 The 3rd argument. + @param Arg4 The 4th argument. + @param Arg5 The 5th argument. + @param Arg6 The 6th argument. + @param Arg7 The 7th argument. + @param Arg8 The 8th argument. + @param Arg9 The 9th argument. + @param Arg10 The 10th argument. + @param Arg11 The 11th argument. + @param Arg12 The 12th argument. + @param Arg13 The 13th argument. + @param Arg14 The 14th argument. + @param Arg15 The 15th argument. + @param Arg16 The 16th argument. + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +EbcInterpret ( + IN UINTN EntryPoint, + IN UINTN Arg1, + IN UINTN Arg2, + IN UINTN Arg3, + IN UINTN Arg4, + IN UINTN Arg5, + IN UINTN Arg6, + IN UINTN Arg7, + IN UINTN Arg8, + IN UINTN Arg9, + IN UINTN Arg10, + IN UINTN Arg11, + IN UINTN Arg12, + IN UINTN Arg13, + IN UINTN Arg14, + IN UINTN Arg15, + IN UINTN Arg16 + ) +{ + // + // Create a new VM context on the stack + // + VM_CONTEXT VmContext; + UINTN Addr; + EFI_STATUS Status; + UINTN StackIndex; + + // + // Get the EBC entry point + // + Addr = EntryPoint; + + // + // Now clear out our context + // + ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT)); + + // + // Set the VM instruction pointer to the correct location in memory. + // + VmContext.Ip = (VMIP) Addr; + + // + // Initialize the stack pointer for the EBC. Get the current system stack + // pointer and adjust it down by the max needed for the interpreter. + // + + // + // Adjust the VM's stack pointer down. + // + + Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex); + if (EFI_ERROR(Status)) { + return Status; + } + VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE); + VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE); + VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0]; + VmContext.Gpr[0] -= sizeof (UINTN); + + // + // Align the stack on a natural boundary. + // + VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1); + + // + // Put a magic value in the stack gap, then adjust down again. + // + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE; + VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0]; + + // + // The stack upper to LowStackTop is belong to the VM. + // + VmContext.LowStackTop = (UINTN) VmContext.Gpr[0]; + + // + // For the worst case, assume there are 4 arguments passed in registers, store + // them to VM's stack. + // + PushU64 (&VmContext, (UINT64) Arg16); + PushU64 (&VmContext, (UINT64) Arg15); + PushU64 (&VmContext, (UINT64) Arg14); + PushU64 (&VmContext, (UINT64) Arg13); + PushU64 (&VmContext, (UINT64) Arg12); + PushU64 (&VmContext, (UINT64) Arg11); + PushU64 (&VmContext, (UINT64) Arg10); + PushU64 (&VmContext, (UINT64) Arg9); + PushU64 (&VmContext, (UINT64) Arg8); + PushU64 (&VmContext, (UINT64) Arg7); + PushU64 (&VmContext, (UINT64) Arg6); + PushU64 (&VmContext, (UINT64) Arg5); + PushU64 (&VmContext, (UINT64) Arg4); + PushU64 (&VmContext, (UINT64) Arg3); + PushU64 (&VmContext, (UINT64) Arg2); + PushU64 (&VmContext, (UINT64) Arg1); + + // + // Interpreter assumes 64-bit return address is pushed on the stack. + // The x64 does not do this so pad the stack accordingly. + // + PushU64 (&VmContext, (UINT64) 0); + PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL); + + // + // For x64, this is where we say our return address is + // + VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0]; + + // + // We need to keep track of where the EBC stack starts. This way, if the EBC + // accesses any stack variables above its initial stack setting, then we know + // it's accessing variables passed into it, which means the data is on the + // VM's stack. + // When we're called, on the stack (high to low) we have the parameters, the + // return address, then the saved ebp. Save the pointer to the return address. + // EBC code knows that's there, so should look above it for function parameters. + // The offset is the size of locals (VMContext + Addr + saved ebp). + // Note that the interpreter assumes there is a 16 bytes of return address on + // the stack too, so adjust accordingly. + // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr)); + // + + // + // Begin executing the EBC code + // + EbcDebuggerHookEbcInterpret (&VmContext); + EbcExecute (&VmContext); + + // + // Return the value in Gpr[7] unless there was an error + // + ReturnEBCStack(StackIndex); + return (UINT64) VmContext.Gpr[7]; +} + + +/** + Begin executing an EBC image. + + @param EntryPoint The entrypoint of EBC code. + @param ImageHandle image handle for the EBC application we're executing + @param SystemTable standard system table passed into an driver's entry + point + + @return The value returned by the EBC application we're going to run. + +**/ +UINT64 +EFIAPI +ExecuteEbcImageEntryPoint ( + IN UINTN EntryPoint, + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + // + // Create a new VM context on the stack + // + VM_CONTEXT VmContext; + UINTN Addr; + EFI_STATUS Status; + UINTN StackIndex; + + // + // Get the EBC entry point + // + Addr = EntryPoint; + + // + // Now clear out our context + // + ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT)); + + // + // Save the image handle so we can track the thunks created for this image + // + VmContext.ImageHandle = ImageHandle; + VmContext.SystemTable = SystemTable; + + // + // Set the VM instruction pointer to the correct location in memory. + // + VmContext.Ip = (VMIP) Addr; + + // + // Initialize the stack pointer for the EBC. Get the current system stack + // pointer and adjust it down by the max needed for the interpreter. + // + + Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex); + if (EFI_ERROR(Status)) { + return Status; + } + VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE); + VmContext.Gpr[0] = (UINT64) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE); + VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0]; + VmContext.Gpr[0] -= sizeof (UINTN); + + + // + // Put a magic value in the stack gap, then adjust down again + // + *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE; + VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0]; + + // + // Align the stack on a natural boundary + VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof(UINTN) - 1); + // + VmContext.LowStackTop = (UINTN) VmContext.Gpr[0]; + + // + // Simply copy the image handle and system table onto the EBC stack. + // Greatly simplifies things by not having to spill the args. + // + PushU64 (&VmContext, (UINT64) SystemTable); + PushU64 (&VmContext, (UINT64) ImageHandle); + + // + // VM pushes 16-bytes for return address. Simulate that here. + // + PushU64 (&VmContext, (UINT64) 0); + PushU64 (&VmContext, (UINT64) 0x1234567887654321ULL); + + // + // For x64, this is where we say our return address is + // + VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0]; + + // + // Entry function needn't access high stack context, simply + // put the stack pointer here. + // + + // + // Begin executing the EBC code + // + EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext); + EbcExecute (&VmContext); + + // + // Return the value in Gpr[7] unless there was an error + // + ReturnEBCStack(StackIndex); + return (UINT64) VmContext.Gpr[7]; +} + + +/** + Create thunks for an EBC image entry point, or an EBC protocol service. + + @param ImageHandle Image handle for the EBC image. If not null, then + we're creating a thunk for an image entry point. + @param EbcEntryPoint Address of the EBC code that the thunk is to call + @param Thunk Returned thunk we create here + @param Flags Flags indicating options for creating the thunk + + @retval EFI_SUCCESS The thunk was created successfully. + @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit + aligned. + @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC + Thunk. + @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough. + +**/ +EFI_STATUS +EbcCreateThunks ( + IN EFI_HANDLE ImageHandle, + IN VOID *EbcEntryPoint, + OUT VOID **Thunk, + IN UINT32 Flags + ) +{ + UINT8 *Ptr; + UINT8 *ThunkBase; + UINT32 Index; + INT32 ThunkSize; + + // + // Check alignment of pointer to EBC code + // + if ((UINT32) (UINTN) EbcEntryPoint & 0x01) { + return EFI_INVALID_PARAMETER; + } + + ThunkSize = sizeof(mInstructionBufferTemplate); + + Ptr = EbcAllocatePoolForThunk (sizeof(mInstructionBufferTemplate)); + + if (Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr); + // + // Save the start address so we can add a pointer to it to a list later. + // + ThunkBase = Ptr; + + // + // Give them the address of our buffer we're going to fix up + // + *Thunk = (VOID *) Ptr; + + // + // Copy whole thunk instruction buffer template + // + CopyMem (Ptr, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate)); + + // + // Patch EbcEntryPoint and EbcLLEbcInterpret + // + for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) { + if (*(UINTN *)&Ptr[Index] == EBC_ENTRYPOINT_SIGNATURE) { + *(UINTN *)&Ptr[Index] = (UINTN)EbcEntryPoint; + } + if (*(UINTN *)&Ptr[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) { + if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) { + *(UINTN *)&Ptr[Index] = (UINTN)EbcLLExecuteEbcImageEntryPoint; + } else { + *(UINTN *)&Ptr[Index] = (UINTN)EbcLLEbcInterpret; + } + } + } + + // + // Add the thunk to the list for this image. Do this last since the add + // function flushes the cache for us. + // + EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize); + + return EFI_SUCCESS; +} + + +/** + This function is called to execute an EBC CALLEX instruction. + The function check the callee's content to see whether it is common native + code or a thunk to another piece of EBC code. + If the callee is common native code, use EbcLLCAllEXASM to manipulate, + otherwise, set the VM->IP to target EBC code directly to avoid another VM + be startup which cost time and stack space. + + @param VmPtr Pointer to a VM context. + @param FuncAddr Callee's address + @param NewStackPointer New stack pointer after the call + @param FramePtr New frame pointer after the call + @param Size The size of call instruction + +**/ +VOID +EbcLLCALLEX ( + IN VM_CONTEXT *VmPtr, + IN UINTN FuncAddr, + IN UINTN NewStackPointer, + IN VOID *FramePtr, + IN UINT8 Size + ) +{ + UINTN IsThunk; + UINTN TargetEbcAddr; + UINT8 InstructionBuffer[sizeof(mInstructionBufferTemplate)]; + UINTN Index; + UINTN IndexOfEbcEntrypoint; + + IsThunk = 1; + TargetEbcAddr = 0; + IndexOfEbcEntrypoint = 0; + + // + // Processor specific code to check whether the callee is a thunk to EBC. + // + CopyMem (InstructionBuffer, (VOID *)FuncAddr, sizeof(InstructionBuffer)); + // + // Fill the signature according to mInstructionBufferTemplate + // + for (Index = 0; Index < sizeof(mInstructionBufferTemplate) - sizeof(UINTN); Index++) { + if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_ENTRYPOINT_SIGNATURE) { + *(UINTN *)&InstructionBuffer[Index] = EBC_ENTRYPOINT_SIGNATURE; + IndexOfEbcEntrypoint = Index; + } + if (*(UINTN *)&mInstructionBufferTemplate[Index] == EBC_LL_EBC_ENTRYPOINT_SIGNATURE) { + *(UINTN *)&InstructionBuffer[Index] = EBC_LL_EBC_ENTRYPOINT_SIGNATURE; + } + } + // + // Check if we need thunk to native + // + if (CompareMem (InstructionBuffer, mInstructionBufferTemplate, sizeof(mInstructionBufferTemplate)) != 0) { + IsThunk = 0; + } + + if (IsThunk == 1){ + // + // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and + // put our return address and frame pointer on the VM stack. + // Then set the VM's IP to new EBC code. + // + VmPtr->Gpr[0] -= 8; + VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr); + VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0]; + VmPtr->Gpr[0] -= 8; + VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size)); + + CopyMem (&TargetEbcAddr, (UINT8 *)FuncAddr + IndexOfEbcEntrypoint, sizeof(UINTN)); + VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr; + } else { + // + // The callee is not a thunk to EBC, call native code, + // and get return value. + // + VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr); + + // + // Advance the IP. + // + VmPtr->Ip += Size; + } +} + diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c new file mode 100644 index 000000000..a386a9770 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c @@ -0,0 +1,661 @@ +/** @file + Esrt management module. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "EsrtImpl.h" + + +// +// Module globals. +// + +ESRT_PRIVATE_DATA mPrivate; + +ESRT_MANAGEMENT_PROTOCOL mEsrtManagementProtocolTemplate = { + EsrtDxeGetEsrtEntry, + EsrtDxeUpdateEsrtEntry, + EsrtDxeRegisterEsrtEntry, + EsrtDxeUnRegisterEsrtEntry, + EsrtDxeSyncFmp, + EsrtDxeLockEsrtRepository + }; + +/** + Get ESRT entry from ESRT Cache by FwClass Guid + + @param[in] FwClass FwClass of Esrt entry to get + @param[in, out] Entry Esrt entry returned + + @retval EFI_SUCCESS The variable saving this Esrt Entry exists. + @retval EF_NOT_FOUND No correct variable found. + @retval EFI_WRITE_PROTECTED ESRT Cache repository is locked + +**/ +EFI_STATUS +EFIAPI +EsrtDxeGetEsrtEntry( + IN EFI_GUID *FwClass, + IN OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + EFI_STATUS Status; + + if (FwClass == NULL || Entry == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find in Non-FMP Cached Esrt Repository + // + Status = GetEsrtEntry( + FwClass, + ESRT_FROM_NONFMP, + Entry + ); + + EfiReleaseLock(&mPrivate.NonFmpLock); + + if (EFI_ERROR(Status)) { + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find in FMP Cached Esrt NV Variable + // + Status = GetEsrtEntry( + FwClass, + ESRT_FROM_FMP, + Entry + ); + + EfiReleaseLock(&mPrivate.FmpLock); + } + + return Status; +} + +/** + Update one ESRT entry in ESRT Cache. + + @param[in] Entry Esrt entry to be updated + + @retval EFI_SUCCESS Successfully update an ESRT entry in cache. + @retval EFI_INVALID_PARAMETER Entry does't exist in ESRT Cache + @retval EFI_WRITE_PROTECTED ESRT Cache repositoy is locked + +**/ +EFI_STATUS +EFIAPI +EsrtDxeUpdateEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + EFI_STATUS Status; + + if (Entry == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = UpdateEsrtEntry(Entry, ESRT_FROM_FMP); + + if (!EFI_ERROR(Status)) { + EfiReleaseLock(&mPrivate.FmpLock); + return Status; + } + EfiReleaseLock(&mPrivate.FmpLock); + + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = UpdateEsrtEntry(Entry, ESRT_FROM_NONFMP); + + EfiReleaseLock(&mPrivate.NonFmpLock); + + return Status; +} + +/** + Non-FMP instance to unregister Esrt Entry from ESRT Cache. + + @param[in] FwClass FwClass of Esrt entry to Unregister + + @retval EFI_SUCCESS Insert all entries Successfully + @retval EFI_NOT_FOUND Entry of FwClass does not exsit + +**/ +EFI_STATUS +EFIAPI +EsrtDxeUnRegisterEsrtEntry( + IN EFI_GUID *FwClass + ) +{ + EFI_STATUS Status; + + if (FwClass == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DeleteEsrtEntry(FwClass, ESRT_FROM_NONFMP); + + EfiReleaseLock(&mPrivate.NonFmpLock); + + return Status; +} + +/** + Non-FMP instance to register one ESRT entry into ESRT Cache. + + @param[in] Entry Esrt entry to be set + + @retval EFI_SUCCESS Successfully set a variable. + @retval EFI_INVALID_PARAMETER ESRT Entry is already exist + @retval EFI_OUT_OF_RESOURCES Non-FMP ESRT repository is full + +**/ +EFI_STATUS +EFIAPI +EsrtDxeRegisterEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_ENTRY EsrtEntryTmp; + + if (Entry == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = GetEsrtEntry( + &Entry->FwClass, + ESRT_FROM_NONFMP, + &EsrtEntryTmp + ); + + if (Status == EFI_NOT_FOUND) { + Status = InsertEsrtEntry(Entry, ESRT_FROM_NONFMP); + } + + EfiReleaseLock(&mPrivate.NonFmpLock); + + return Status; +} + +/** + This function syn up Cached ESRT with data from FMP instances + Function should be called after Connect All in order to locate all FMP protocols + installed. + + @retval EFI_SUCCESS Successfully sync cache repository from FMP instances + @retval EFI_NOT_FOUND No FMP Instance are found + @retval EFI_OUT_OF_RESOURCES Resource allocaton fail + +**/ +EFI_STATUS +EFIAPI +EsrtDxeSyncFmp( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index1; + UINTN Index2; + UINTN Index3; + EFI_HANDLE *HandleBuffer; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL **FmpBuf; + UINTN NumberOfHandles; + UINTN *DescriptorSizeBuf; + EFI_FIRMWARE_IMAGE_DESCRIPTOR **FmpImageInfoBuf; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo; + UINT8 *FmpImageInfoCountBuf; + UINT32 *FmpImageInfoDescriptorVerBuf; + UINTN ImageInfoSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepositoryNew; + UINTN EntryNumNew; + + NumberOfHandles = 0; + EntryNumNew = 0; + FmpBuf = NULL; + HandleBuffer = NULL; + FmpImageInfoBuf = NULL; + FmpImageInfoCountBuf = NULL; + PackageVersionName = NULL; + DescriptorSizeBuf = NULL; + FmpImageInfoDescriptorVerBuf = NULL; + EsrtRepositoryNew = NULL; + + // + // Get image information from all FMP protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + + + if (Status == EFI_NOT_FOUND) { + EntryNumNew = 0; + goto UPDATE_REPOSITORY; + } else if (EFI_ERROR(Status)){ + goto END; + } + + // + // Allocate buffer to hold new FMP ESRT Cache repository + // + EsrtRepositoryNew = AllocateZeroPool(PcdGet32(PcdMaxFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + if (EsrtRepositoryNew == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpBuf = AllocatePool(sizeof(EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) * NumberOfHandles); + if (FmpBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpImageInfoBuf = AllocateZeroPool(sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR *) * NumberOfHandles); + if (FmpImageInfoBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpImageInfoCountBuf = AllocateZeroPool(sizeof(UINT8) * NumberOfHandles); + if (FmpImageInfoCountBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + DescriptorSizeBuf = AllocateZeroPool(sizeof(UINTN) * NumberOfHandles); + if (DescriptorSizeBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + FmpImageInfoDescriptorVerBuf = AllocateZeroPool(sizeof(UINT32) * NumberOfHandles); + if (FmpImageInfoDescriptorVerBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + + // + // Get all FmpImageInfo Descriptor into FmpImageInfoBuf + // + for (Index1 = 0; Index1 < NumberOfHandles; Index1++){ + Status = gBS->HandleProtocol( + HandleBuffer[Index1], + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&FmpBuf[Index1] + ); + + if (EFI_ERROR(Status)) { + continue; + } + + ImageInfoSize = 0; + Status = FmpBuf[Index1]->GetImageInfo ( + FmpBuf[Index1], + &ImageInfoSize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + FmpImageInfoBuf[Index1] = AllocateZeroPool(ImageInfoSize); + if (FmpImageInfoBuf[Index1] == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto END; + } + } else { + continue; + } + + PackageVersionName = NULL; + Status = FmpBuf[Index1]->GetImageInfo ( + FmpBuf[Index1], + &ImageInfoSize, + FmpImageInfoBuf[Index1], + &FmpImageInfoDescriptorVerBuf[Index1], + &FmpImageInfoCountBuf[Index1], + &DescriptorSizeBuf[Index1], + &PackageVersion, + &PackageVersionName + ); + + // + // If FMP GetInformation interface failed, skip this resource + // + if (EFI_ERROR(Status)){ + FmpImageInfoCountBuf[Index1] = 0; + continue; + } + + if (PackageVersionName != NULL) { + FreePool(PackageVersionName); + } + } + + // + // Create new FMP cache repository based on FmpImageInfoBuf + // + for (Index2 = 0; Index2 < NumberOfHandles; Index2++){ + TempFmpImageInfo = FmpImageInfoBuf[Index2]; + for (Index3 = 0; Index3 < FmpImageInfoCountBuf[Index2]; Index3++){ + if ((TempFmpImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) != 0 + && (TempFmpImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_IN_USE) != 0){ + // + // Always put the first smallest version of Image info into ESRT cache + // + for(Index1 = 0; Index1 < EntryNumNew; Index1++) { + if (CompareGuid(&EsrtRepositoryNew[Index1].FwClass, &TempFmpImageInfo->ImageTypeId)) { + if(EsrtRepositoryNew[Index1].FwVersion > TempFmpImageInfo->Version) { + SetEsrtEntryFromFmpInfo(&EsrtRepositoryNew[Index1], TempFmpImageInfo, FmpImageInfoDescriptorVerBuf[Index2]); + } + break; + } + } + // + // New ImageTypeId can't be found in EsrtRepositoryNew. Create a new one + // + if (Index1 == EntryNumNew){ + SetEsrtEntryFromFmpInfo(&EsrtRepositoryNew[EntryNumNew], TempFmpImageInfo, FmpImageInfoDescriptorVerBuf[Index2]); + EntryNumNew++; + if (EntryNumNew >= PcdGet32(PcdMaxFmpEsrtCacheNum)) { + break; + } + } + } + + // + // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version + // + TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSizeBuf[Index2]); + } + } + +UPDATE_REPOSITORY: + + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gRT->SetVariable( + EFI_ESRT_FMP_VARIABLE_NAME, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + EntryNumNew * sizeof(EFI_SYSTEM_RESOURCE_ENTRY), + EsrtRepositoryNew + ); + + EfiReleaseLock(&mPrivate.FmpLock); + +END: + if (EsrtRepositoryNew != NULL) { + FreePool(EsrtRepositoryNew); + } + + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + } + + if (FmpBuf != NULL) { + FreePool(FmpBuf); + } + + if (FmpImageInfoCountBuf != NULL) { + FreePool(FmpImageInfoCountBuf); + } + + if (DescriptorSizeBuf != NULL) { + FreePool(DescriptorSizeBuf); + } + + if (FmpImageInfoDescriptorVerBuf != NULL) { + FreePool(FmpImageInfoDescriptorVerBuf); + } + + if (FmpImageInfoBuf != NULL) { + for (Index1 = 0; Index1 < NumberOfHandles; Index1++){ + if (FmpImageInfoBuf[Index1] != NULL) { + FreePool(FmpImageInfoBuf[Index1]); + } + } + FreePool(FmpImageInfoBuf); + } + + return Status; +} + +/** + This function locks up Esrt repository to be readonly. It should be called + before gEfiEndOfDxeEventGroupGuid event signaled + + @retval EFI_SUCCESS Locks up FMP Non-FMP repository successfully + +**/ +EFI_STATUS +EFIAPI +EsrtDxeLockEsrtRepository( + VOID + ) +{ + EFI_STATUS Status; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + // + // Mark ACPI_GLOBAL_VARIABLE variable to read-only if the Variable Lock protocol exists + // + Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock); + if (!EFI_ERROR (Status)) { + Status = VariableLock->RequestToLock (VariableLock, EFI_ESRT_FMP_VARIABLE_NAME, &gEfiCallerIdGuid); + DEBUG((EFI_D_INFO, "EsrtDxe Lock EsrtFmp Variable Status 0x%x", Status)); + + Status = VariableLock->RequestToLock (VariableLock, EFI_ESRT_NONFMP_VARIABLE_NAME, &gEfiCallerIdGuid); + DEBUG((EFI_D_INFO, "EsrtDxe Lock EsrtNonFmp Variable Status 0x%x", Status)); + } + + return Status; +} + +/** + Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to + install the Esrt Table into system configuration table + + @param[in] Event The Event that is being processed. + @param[in] Context The Event Context. + +**/ +VOID +EFIAPI +EsrtReadyToBootEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_TABLE *EsrtTable; + EFI_SYSTEM_RESOURCE_ENTRY *FmpEsrtRepository; + EFI_SYSTEM_RESOURCE_ENTRY *NonFmpEsrtRepository; + UINTN FmpRepositorySize; + UINTN NonFmpRepositorySize; + + + FmpEsrtRepository = NULL; + NonFmpEsrtRepository = NULL; + FmpRepositorySize = 0; + NonFmpRepositorySize = 0; + + Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock); + if (EFI_ERROR (Status)) { + return; + } + + Status = GetVariable2 ( + EFI_ESRT_NONFMP_VARIABLE_NAME, + &gEfiCallerIdGuid, + (VOID **) &NonFmpEsrtRepository, + &NonFmpRepositorySize + ); + + if (EFI_ERROR(Status)) { + NonFmpRepositorySize = 0; + } + + if (NonFmpRepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { + DEBUG((EFI_D_ERROR, "NonFmp Repository Corrupt. Need to rebuild NonFmp Repository.\n")); + NonFmpRepositorySize = 0; + } + + EfiReleaseLock(&mPrivate.NonFmpLock); + + Status = EfiAcquireLockOrFail (&mPrivate.FmpLock); + Status = GetVariable2 ( + EFI_ESRT_FMP_VARIABLE_NAME, + &gEfiCallerIdGuid, + (VOID **) &FmpEsrtRepository, + &FmpRepositorySize + ); + + if (EFI_ERROR(Status)) { + FmpRepositorySize = 0; + } + + if (FmpRepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { + DEBUG((EFI_D_ERROR, "Fmp Repository Corrupt. Need to rebuild Fmp Repository.\n")); + FmpRepositorySize = 0; + } + + EfiReleaseLock(&mPrivate.FmpLock); + + // + // Skip ESRT table publish if no ESRT entry exists + // + if (NonFmpRepositorySize + FmpRepositorySize == 0) { + goto EXIT; + } + + EsrtTable = AllocatePool(sizeof(EFI_SYSTEM_RESOURCE_TABLE) + NonFmpRepositorySize + FmpRepositorySize); + if (EsrtTable == NULL) { + DEBUG ((EFI_D_ERROR, "Esrt table memory allocation failure\n")); + goto EXIT; + } + + EsrtTable->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION; + EsrtTable->FwResourceCount = (UINT32)((NonFmpRepositorySize + FmpRepositorySize) / sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + EsrtTable->FwResourceCountMax = PcdGet32(PcdMaxNonFmpEsrtCacheNum) + PcdGet32(PcdMaxFmpEsrtCacheNum); + + if (NonFmpRepositorySize != 0 && NonFmpEsrtRepository != NULL) { + CopyMem(EsrtTable + 1, NonFmpEsrtRepository, NonFmpRepositorySize); + } + + if (FmpRepositorySize != 0 && FmpEsrtRepository != NULL) { + CopyMem((UINT8 *)(EsrtTable + 1) + NonFmpRepositorySize, FmpEsrtRepository, FmpRepositorySize); + } + + // + // Publish Esrt to system config table + // + Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, EsrtTable); + + // + // Only one successful install + // + gBS->CloseEvent(Event); + +EXIT: + + if (FmpEsrtRepository != NULL) { + FreePool(FmpEsrtRepository); + } + + if (NonFmpEsrtRepository != NULL) { + FreePool(NonFmpEsrtRepository); + } +} + +/** + The module Entry Point of the Esrt DXE driver that manages cached ESRT repository + & publishes ESRT table + + @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 entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +EsrtDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + EfiInitializeLock (&mPrivate.FmpLock, TPL_CALLBACK); + EfiInitializeLock (&mPrivate.NonFmpLock, TPL_CALLBACK); + + // + // Install Esrt management Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mPrivate.Handle, + &gEsrtManagementProtocolGuid, + &mEsrtManagementProtocolTemplate, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Register notify function to install Esrt Table on ReadyToBoot Event. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + EsrtReadyToBootEventNotify, + NULL, + &gEfiEventReadyToBootGuid, + &mPrivate.Event + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.inf b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.inf new file mode 100644 index 000000000..6b9eec95f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.inf @@ -0,0 +1,66 @@ +## @file +# Esrt DXE driver that manages cached ESRT repository & publishes ESRT table +# +# This driver produces EsrtManagement protocol to manage cache ESRT repository for FMP/Non-FMP instances. +# ESRT table based on repository is published on gEfiEventReadyToBootGuid. +# +# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EsrtDxe + MODULE_UNI_FILE = EsrtDxe.uni + FILE_GUID = 999BD818-7DF7-4A9A-A502-9B75033E6A0F + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = EsrtDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + EsrtImpl.h + EsrtImpl.c + EsrtDxe.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseMemoryLib + UefiLib + PcdLib + DebugLib + MemoryAllocationLib + DxeServicesTableLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + +[Guids] + gEfiSystemResourceTableGuid ## PRODUCES ## SystemTable + gEfiEventReadyToBootGuid ## CONSUMES ## Event + +[Protocols] + gEfiFirmwareManagementProtocolGuid ## SOMETIMES_CONSUMES + gEsrtManagementProtocolGuid ## PRODUCES + gEdkiiVariableLockProtocolGuid ## SOMETIMES_CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxFmpEsrtCacheNum ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxNonFmpEsrtCacheNum ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSystemRebootAfterCapsuleProcessFlag ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSystemFmpCapsuleImageTypeIdGuid ## CONSUMES + +[Depex] + gEfiVariableArchProtocolGuid AND gEfiVariableWriteArchProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + EsrtDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.uni b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.uni new file mode 100644 index 000000000..35bb39a29 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// Esrt DXE driver that manages cached ESRT repository & publishes ESRT table +// +// This driver produces EsrtManagement protocol to manage cache ESRT repository for FMP/Non-FMP instances. +// ESRT table based on repository is published on gEfiEventReadyToBootGuid. +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Esrt DXE driver that manages cached ESRT repository & publishes ESRT table" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver produces EsrtManagement protocol to manage cache ESRT repository for FMP/Non-FMP instances.

\n" + "ESRT table based on repository is published on EsrtReadyToBootEventNotify.
" diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxeExtra.uni new file mode 100644 index 000000000..629a6845a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// EsrtDxe Localized Strings and Content +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Esrt DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.c b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.c new file mode 100644 index 000000000..fff17b98f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.c @@ -0,0 +1,475 @@ +/** @file + Esrt management implementation. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EsrtImpl.h" + +/** + Find Esrt Entry stored in ESRT repository. + + @param[in] FwClass Firmware class guid in Esrt entry + @param[in] Attribute Esrt from Non FMP or FMP instance + @param[out] Entry Esrt entry returned + + @retval EFI_SUCCESS Successfully find an Esrt entry + @retval EF_NOT_FOUND No Esrt entry found + +**/ +EFI_STATUS +GetEsrtEntry ( + IN EFI_GUID *FwClass, + IN UINTN Attribute, + OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + EFI_STATUS Status; + CHAR16 *VariableName; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; + UINTN RepositorySize; + UINTN Index; + UINTN EsrtNum; + + EsrtRepository = NULL; + + // + // Get Esrt index buffer + // + if (Attribute == ESRT_FROM_FMP) { + VariableName = EFI_ESRT_FMP_VARIABLE_NAME; + } else { + VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; + } + + Status = GetVariable2 ( + VariableName, + &gEfiCallerIdGuid, + (VOID **) &EsrtRepository, + &RepositorySize + ); + + if (EFI_ERROR(Status)) { + goto EXIT; + } + + if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { + DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); + Status = EFI_ABORTED; + goto EXIT; + } + + Status = EFI_NOT_FOUND; + EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY); + for (Index = 0; Index < EsrtNum; Index++) { + if (CompareGuid(FwClass, &EsrtRepository[Index].FwClass)) { + CopyMem(Entry, &EsrtRepository[Index], sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + Status = EFI_SUCCESS; + break; + } + } + +EXIT: + if (EsrtRepository != NULL) { + FreePool(EsrtRepository); + } + + return Status; +} + +/** + Insert a new ESRT entry into ESRT Cache repository. + + @param[in] Entry Esrt entry to be set + @param[in] Attribute Esrt from Esrt private protocol or FMP instance + + @retval EFI_SUCCESS Successfully set a variable. + +**/ +EFI_STATUS +InsertEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry, + UINTN Attribute + ) +{ + EFI_STATUS Status; + CHAR16 *VariableName; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; + UINTN RepositorySize; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepositoryNew; + + EsrtRepository = NULL; + EsrtRepositoryNew = NULL; + + // + // Get Esrt index buffer + // + if (Attribute == ESRT_FROM_FMP) { + VariableName = EFI_ESRT_FMP_VARIABLE_NAME; + } else { + VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; + } + + Status = GetVariable2 ( + VariableName, + &gEfiCallerIdGuid, + (VOID **) &EsrtRepository, + &RepositorySize + ); + + if (Status == EFI_NOT_FOUND) { + // + // If not exist, create new Esrt cache repository + // + Status = gRT->SetVariable( + VariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof(EFI_SYSTEM_RESOURCE_ENTRY), + Entry + ); + return Status; + + } else if (Status == EFI_SUCCESS) { + // + // if exist, update Esrt cache repository + // + if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { + DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); + // + // Repository is corrupt. Clear Repository before insert new entry + // + Status = gRT->SetVariable( + VariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + EsrtRepository + ); + FreePool(EsrtRepository); + RepositorySize = 0; + EsrtRepository = NULL; + } + + // + // Check Repository size constraint + // + if ((Attribute == ESRT_FROM_FMP && RepositorySize >= PcdGet32(PcdMaxFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) + ||(Attribute == ESRT_FROM_NONFMP && RepositorySize >= PcdGet32(PcdMaxNonFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) ) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + EsrtRepositoryNew = AllocatePool(RepositorySize + sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + if (EsrtRepositoryNew == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if (RepositorySize != 0 && EsrtRepository != NULL) { + CopyMem(EsrtRepositoryNew, EsrtRepository, RepositorySize); + } + CopyMem((UINT8 *)EsrtRepositoryNew + RepositorySize, Entry, sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + + Status = gRT->SetVariable( + VariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + RepositorySize + sizeof(EFI_SYSTEM_RESOURCE_ENTRY), + EsrtRepositoryNew + ); + } + +EXIT: + if (EsrtRepository != NULL) { + FreePool(EsrtRepository); + } + + if (EsrtRepositoryNew != NULL) { + FreePool(EsrtRepositoryNew); + } + + return Status; +} + +/** + Delete ESRT Entry from ESRT repository. + + @param[in] FwClass FwClass of Esrt entry to delete + @param[in] Attribute Esrt from Esrt private protocol or FMP instance + + @retval EFI_SUCCESS Insert all entries Successfully + @retval EFI_NOT_FOUND ESRT entry with FwClass doesn't exsit + +**/ +EFI_STATUS +DeleteEsrtEntry( + IN EFI_GUID *FwClass, + IN UINTN Attribute + ) +{ + EFI_STATUS Status; + CHAR16 *VariableName; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; + UINTN RepositorySize; + UINTN Index; + UINTN EsrtNum; + + EsrtRepository = NULL; + + // + // Get Esrt index buffer + // + if (Attribute == ESRT_FROM_FMP) { + VariableName = EFI_ESRT_FMP_VARIABLE_NAME; + } else { + VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; + } + + Status = GetVariable2 ( + VariableName, + &gEfiCallerIdGuid, + (VOID **) &EsrtRepository, + &RepositorySize + ); + + if (EFI_ERROR(Status)) { + goto EXIT; + } + + if (EsrtRepository == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if ((RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) != 0) { + DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); + // + // Repository is corrupt. Clear Repository before insert new entry + // + Status = gRT->SetVariable( + VariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + EsrtRepository + ); + goto EXIT; + } + + Status = EFI_NOT_FOUND; + EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY); + for (Index = 0; Index < EsrtNum; Index++) { + // + // Delete Esrt entry if it is found in repository + // + if (CompareGuid(FwClass, &EsrtRepository[Index].FwClass)) { + // + // If delete Esrt entry is not at the rail + // + if (Index < EsrtNum - 1) { + CopyMem(&EsrtRepository[Index], &EsrtRepository[Index + 1], (EsrtNum - Index - 1) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + } + + // + // Update New Repository + // + Status = gRT->SetVariable( + VariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + (EsrtNum - 1) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY), + EsrtRepository + ); + break; + } + } + +EXIT: + if (EsrtRepository != NULL) { + FreePool(EsrtRepository); + } + + return Status; + +} + +/** + Update one ESRT entry in ESRT repository + + @param[in] Entry Esrt entry to be set + @param[in] Attribute Esrt from Non Esrt or FMP instance + + @retval EFI_SUCCESS Successfully Update a variable. + @retval EFI_NOT_FOUND The Esrt enry doesn't exist + +**/ +EFI_STATUS +UpdateEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry, + UINTN Attribute + ) +{ + EFI_STATUS Status; + CHAR16 *VariableName; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepository; + UINTN RepositorySize; + UINTN Index; + UINTN EsrtNum; + + EsrtRepository = NULL; + + // + // Get Esrt index buffer + // + if (Attribute == ESRT_FROM_FMP) { + VariableName = EFI_ESRT_FMP_VARIABLE_NAME; + } else { + VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME; + } + + Status = GetVariable2 ( + VariableName, + &gEfiCallerIdGuid, + (VOID **) &EsrtRepository, + &RepositorySize + ); + + if (EsrtRepository == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if (!EFI_ERROR(Status)) { + // + // if exist, update Esrt cache repository + // + if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) { + DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n")); + // + // Repository is corrupt. Clear Repository before insert new entry + // + Status = gRT->SetVariable( + VariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + EsrtRepository + ); + Status = EFI_NOT_FOUND; + goto EXIT; + } + + Status = EFI_NOT_FOUND; + EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY); + for (Index = 0; Index < EsrtNum; Index++) { + // + // Update Esrt entry if it is found in repository + // + if (CompareGuid(&Entry->FwClass, &EsrtRepository[Index].FwClass)) { + + CopyMem(&EsrtRepository[Index], Entry, sizeof(EFI_SYSTEM_RESOURCE_ENTRY)); + // + // Update New Repository + // + Status = gRT->SetVariable( + VariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + RepositorySize, + EsrtRepository + ); + break; + } + } + } + +EXIT: + if (EsrtRepository != NULL) { + FreePool(EsrtRepository); + } + + return Status; +} + +/** + Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo. + + @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR + + @return TRUE It is a system FMP. + @return FALSE It is a device FMP. +**/ +BOOLEAN +IsSystemFmp ( + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo + ) +{ + GUID *Guid; + UINTN Count; + UINTN Index; + + Guid = PcdGetPtr(PcdSystemFmpCapsuleImageTypeIdGuid); + Count = PcdGetSize(PcdSystemFmpCapsuleImageTypeIdGuid)/sizeof(GUID); + + for (Index = 0; Index < Count; Index++, Guid++) { + if (CompareGuid(&FmpImageInfo->ImageTypeId, Guid)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Init one ESRT entry according to input FmpImageInfo (V1, V2, V3) . + + @param[in, out] EsrtEntry Esrt entry to be Init + @param[in] FmpImageInfo FMP image info descriptor + @param[in] DescriptorVersion FMP Image info descriptor version + +**/ +VOID +SetEsrtEntryFromFmpInfo ( + IN OUT EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry, + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo, + IN UINT32 DescriptorVersion + ) +{ + EsrtEntry->FwVersion = FmpImageInfo->Version; + EsrtEntry->FwClass = FmpImageInfo->ImageTypeId; + if (IsSystemFmp(FmpImageInfo)) { + EsrtEntry->FwType = ESRT_FW_TYPE_SYSTEMFIRMWARE; + } else { + EsrtEntry->FwType = ESRT_FW_TYPE_DEVICEFIRMWARE; + } + EsrtEntry->LowestSupportedFwVersion = 0; + EsrtEntry->CapsuleFlags = 0; + EsrtEntry->LastAttemptVersion = 0; + EsrtEntry->LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; + + if (DescriptorVersion >= 2) { + // + // LowestSupportedImageVersion only available in FMP V2 or higher + // + EsrtEntry->LowestSupportedFwVersion = FmpImageInfo->LowestSupportedImageVersion; + } + + if (DescriptorVersion >= 3) { + // + // LastAttemptVersion & LastAttemptStatus only available in FMP V3 or higher + // + EsrtEntry->LastAttemptVersion = FmpImageInfo->LastAttemptVersion; + EsrtEntry->LastAttemptStatus = FmpImageInfo->LastAttemptStatus; + } + + // + // Set capsule customized flag + // + if ((FmpImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0 + && (FmpImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0) { + EsrtEntry->CapsuleFlags = PcdGet16(PcdSystemRebootAfterCapsuleProcessFlag); + } +} diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.h b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.h new file mode 100644 index 000000000..68d0d2ba5 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtDxe/EsrtImpl.h @@ -0,0 +1,238 @@ +/** @file + Esrt management implementation head file. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DXE_ESRT_IMPL_H_ +#define _DXE_ESRT_IMPL_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// +// Name of Variable for Non-FMP ESRT Repository +// +#define EFI_ESRT_NONFMP_VARIABLE_NAME L"EsrtNonFmp" + +// +// Name of Variable for FMP +// +#define EFI_ESRT_FMP_VARIABLE_NAME L"EsrtFmp" + +// +// Attribute of Cached ESRT entry +// +#define ESRT_FROM_FMP 0x00000001 +#define ESRT_FROM_NONFMP 0x00000002 + +typedef struct { + EFI_HANDLE Handle; + // + // Ready to boot event + // + EFI_EVENT Event; + + // + // Updates to Fmp storage must be locked. + // + EFI_LOCK FmpLock; + + // + // Update to Non-Fmp storage must be locked + // + EFI_LOCK NonFmpLock; +} ESRT_PRIVATE_DATA; + + +/** + Find Esrt Entry stored in ESRT repository. + + @param[in] FwClass Firmware class guid in Esrt entry + @param[in] Attribute Esrt from Non FMP or FMP instance + @param[out] Entry Esrt entry returned + + @retval EFI_SUCCESS Successfully find an Esrt entry + @retval EF_NOT_FOUND No Esrt entry found + +**/ +EFI_STATUS +GetEsrtEntry ( + IN EFI_GUID *FwClass, + IN UINTN Attribute, + OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry + ); + +/** + Insert a new ESRT entry into ESRT Cache repository. + + @param[in] Entry Esrt entry to be set + @param[in] Attribute Esrt from Esrt private protocol or FMP instance + + @retval EFI_SUCCESS Successfully set a variable. + +**/ +EFI_STATUS +InsertEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry, + UINTN Attribute + ); + +/** + Delete ESRT Entry from ESRT repository. + + @param[in] FwClass FwClass of Esrt entry to delete + @param[in] Attribute Esrt from Esrt private protocol or FMP instance + + @retval EFI_SUCCESS Insert all entries Successfully + @retval EFI_NOT_FOUND ESRT entry with FwClass doesn't exsit + +**/ +EFI_STATUS +DeleteEsrtEntry( + IN EFI_GUID *FwClass, + IN UINTN Attribute + ); + +/** + Update one ESRT entry in ESRT repository + + @param[in] Entry Esrt entry to be set + @param[in] Attribute Esrt from Non Esrt or FMP instance + + @retval EFI_SUCCESS Successfully Update a variable. + @retval EFI_NOT_FOUND The Esrt enry doesn't exist + +**/ +EFI_STATUS +UpdateEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry, + UINTN Attribute + ); + +/** + Init one ESRT entry according to input FmpImageInfo (V1, V2, V3) . + + @param[in, out] EsrtEntry Esrt entry to be Init + @param[in] FmpImageInfo FMP image info descriptor + @param[in] DescriptorVersion FMP Image info descriptor version + +**/ +VOID +SetEsrtEntryFromFmpInfo ( + IN OUT EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry, + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo, + IN UINT32 DescriptorVersion + ); + +/** + Get ESRT entry from ESRT Cache by FwClass Guid + + @param[in] FwClass FwClass of Esrt entry to get + @param[in, out] Entry Esrt entry returned + + @retval EFI_SUCCESS The variable saving this Esrt Entry exists. + @retval EF_NOT_FOUND No correct variable found. + @retval EFI_WRITE_PROTECTED ESRT Cache repository is locked + +**/ +EFI_STATUS +EFIAPI +EsrtDxeGetEsrtEntry( + IN EFI_GUID *FwClass, + IN OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry + ); + +/** + Update one ESRT entry in ESRT Cache. + + @param[in] Entry Esrt entry to be updated + + @retval EFI_SUCCESS Successfully update an ESRT entry in cache. + @retval EFI_INVALID_PARAMETER Entry does't exist in ESRT Cache + @retval EFI_WRITE_PROTECTED ESRT Cache is locked + +**/ +EFI_STATUS +EFIAPI +EsrtDxeUpdateEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry + ); + +/** + Non-FMP instance to unregister Esrt Entry from ESRT Cache. + + @param[in] FwClass FwClass of Esrt entry to Unregister + + @retval EFI_SUCCESS Insert all entries Successfully + @retval EFI_NOT_FOUND Entry of FwClass does not exsit + +**/ +EFI_STATUS +EFIAPI +EsrtDxeUnRegisterEsrtEntry( + IN EFI_GUID *FwClass + ); + +/** + Non-FMP instance to register one ESRT entry into ESRT Cache. + + @param[in] Entry Esrt entry to be set + + @retval EFI_SUCCESS Successfully set a variable. + @retval EFI_INVALID_PARAMETER ESRT Entry is already exist +**/ +EFI_STATUS +EFIAPI +EsrtDxeRegisterEsrtEntry( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry + ); + +/** + This function syn up Cached ESRT with data from FMP instances + Function should be called after Connect All in order to locate all FMP protocols + installed. + + @retval EFI_SUCCESS Successfully sync cache repository from FMP instances + @retval EFI_NOT_FOUND No FMP Instance are found + @retval EFI_OUT_OF_RESOURCES Resource allocaton fail + +**/ +EFI_STATUS +EFIAPI +EsrtDxeSyncFmp( + VOID + ); + +/** + This function locks up Esrt repository to be readonly. It should be called + before gEfiEndOfDxeEventGroupGuid event signaled + + @retval EFI_SUCCESS Locks up FMP Non-FMP repository successfully + +**/ +EFI_STATUS +EFIAPI +EsrtDxeLockEsrtRepository( + VOID + ); + +#endif // #ifndef _EFI_ESRT_IMPL_H_ + diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c new file mode 100644 index 000000000..4670349f8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c @@ -0,0 +1,558 @@ +/** @file + Publishes ESRT table from Firmware Management Protocol instances + + Copyright (c) 2016, Microsoft Corporation + Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+ + All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// +/// Structure for array of unique GUID/HardwareInstance pairs from the +/// current set of EFI_FIRMWARE_IMAGE_DESCRIPTORs from all FMP Protocols. +/// +typedef struct { + /// + /// A unique GUID identifying the firmware image type. + /// + EFI_GUID ImageTypeGuid; + /// + /// An optional number to identify the unique hardware instance within the + /// system for devices that may have multiple instances whenever possible. + /// + UINT64 HardwareInstance; +} GUID_HARDWAREINSTANCE_PAIR; + +/** + Print ESRT to debug console. + + @param[in] Table Pointer to the ESRT table. + +**/ +VOID +EFIAPI +PrintTable ( + IN EFI_SYSTEM_RESOURCE_TABLE *Table + ); + +/** + Install EFI System Resource Table into the UEFI Configuration Table + + @param[in] Table Pointer to the ESRT. + + @return Status code. + +**/ +EFI_STATUS +InstallEfiSystemResourceTableInUefiConfigurationTable ( + IN EFI_SYSTEM_RESOURCE_TABLE *Table + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + if (Table->FwResourceCount == 0) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it has zero Entries. \n")); + Status = EFI_UNSUPPORTED; + } else { + // + // Install the pointer into config table + // + Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, Table); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table. Status: %r. \n", Status)); + } else { + DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Installed ESRT table. \n")); + } + } + return Status; +} + +/** + Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo. + + @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR + + @return TRUE It is a system FMP. + @return FALSE It is a device FMP. +**/ +BOOLEAN +IsSystemFmp ( + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo + ) +{ + GUID *Guid; + UINTN Count; + UINTN Index; + + Guid = PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid); + Count = PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID); + + for (Index = 0; Index < Count; Index++, Guid++) { + if (CompareGuid (&FmpImageInfo->ImageTypeId, Guid)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Function to create a single ESRT Entry and add it to the ESRT with + a given FMP descriptor. If the GUID is already in the ESRT, then the ESRT + entry is updated. + + @param[in,out] Table Pointer to the ESRT Table. + @param[in,out] HardwareInstances Pointer to the GUID_HARDWAREINSTANCE_PAIR. + @param[in,out] NumberOfDescriptors The number of EFI_FIRMWARE_IMAGE_DESCRIPTORs. + @param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] FmpVersion FMP Version. + + @retval EFI_SUCCESS FmpImageInfoBuf was use to fill in a new ESRT entry + in Table. + @retval EFI_SUCCESS The ImageTypeId GUID in FmpImageInfoBuf matches an + existing ESRT entry in Table, and the information + from FmpImageInfoBuf was merged into the the existing + ESRT entry. + @retval EFI_UNSPOORTED The GUID/HardareInstance in FmpImageInfoBuf has is a + duplicate. + +**/ +EFI_STATUS +CreateEsrtEntry ( + IN OUT EFI_SYSTEM_RESOURCE_TABLE *Table, + IN OUT GUID_HARDWAREINSTANCE_PAIR *HardwareInstances, + IN OUT UINT32 *NumberOfDescriptors, + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf, + IN UINT32 FmpVersion + ) +{ + UINTN Index; + EFI_SYSTEM_RESOURCE_ENTRY *Entry; + UINT64 FmpHardwareInstance; + + FmpHardwareInstance = 0; + if (FmpVersion >= 3) { + FmpHardwareInstance = FmpImageInfoBuf->HardwareInstance; + } + + // + // Check to see of FmpImageInfoBuf GUID/HardwareInstance is unique + // + for (Index = 0; Index < *NumberOfDescriptors; Index++) { + if (CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId)) { + if (HardwareInstances[Index].HardwareInstance == FmpHardwareInstance) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Duplicate firmware image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance)); + ASSERT ( + !CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId) || + HardwareInstances[Index].HardwareInstance != FmpHardwareInstance + ); + return EFI_UNSUPPORTED; + } + } + } + + // + // Record new GUID/HardwareInstance pair + // + CopyGuid (&HardwareInstances[*NumberOfDescriptors].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId); + HardwareInstances[*NumberOfDescriptors].HardwareInstance = FmpHardwareInstance; + *NumberOfDescriptors = *NumberOfDescriptors + 1; + + DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Add new image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance)); + + // + // Check to see if GUID is already in the ESRT table + // + Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1); + for (Index = 0; Index < Table->FwResourceCount; Index++, Entry++) { + if (!CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) { + continue; + } + DEBUG ((DEBUG_INFO, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass)); + + // + // Set ESRT FwVersion to the smaller of the two values + // + Entry->FwVersion = MIN (FmpImageInfoBuf->Version, Entry->FwVersion); + + // + // VERSION 2 has Lowest Supported + // + if (FmpVersion >= 2) { + // + // Set ESRT LowestSupportedFwVersion to the smaller of the two values + // + Entry->LowestSupportedFwVersion = + MIN ( + FmpImageInfoBuf->LowestSupportedImageVersion, + Entry->LowestSupportedFwVersion + ); + } + + // + // VERSION 3 supports last attempt values + // + if (FmpVersion >= 3) { + // + // Update the ESRT entry with the last attempt status and last attempt + // version from the first FMP instance whose last attempt status is not + // SUCCESS. If all FMP instances are SUCCESS, then set version to the + // smallest value from all FMP instances. + // + if (Entry->LastAttemptStatus == LAST_ATTEMPT_STATUS_SUCCESS) { + if (FmpImageInfoBuf->LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS) { + Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus; + Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion; + } else { + Entry->LastAttemptVersion = + MIN ( + FmpImageInfoBuf->LastAttemptVersion, + Entry->LastAttemptVersion + ); + } + } + } + + return EFI_SUCCESS; + } + + // + // Add a new ESRT Table Entry + // + Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1) + Table->FwResourceCount; + + CopyGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId); + + if (IsSystemFmp (FmpImageInfoBuf)) { + DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Found an ESRT entry for a System Device.\n")); + Entry->FwType = (UINT32)(ESRT_FW_TYPE_SYSTEMFIRMWARE); + } else { + Entry->FwType = (UINT32)(ESRT_FW_TYPE_DEVICEFIRMWARE); + } + + Entry->FwVersion = FmpImageInfoBuf->Version; + Entry->LowestSupportedFwVersion = 0; + Entry->CapsuleFlags = 0; + Entry->LastAttemptVersion = 0; + Entry->LastAttemptStatus = 0; + + // + // VERSION 2 has Lowest Supported + // + if (FmpVersion >= 2) { + Entry->LowestSupportedFwVersion = FmpImageInfoBuf->LowestSupportedImageVersion; + } + + // + // VERSION 3 supports last attempt values + // + if (FmpVersion >= 3) { + Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion; + Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus; + } + + // + // Increment the number of active ESRT Table Entries + // + Table->FwResourceCount++; + + return EFI_SUCCESS; +} + +/** + Function to retrieve the EFI_FIRMWARE_IMAGE_DESCRIPTOR from an FMP Instance. + The returned buffer is allocated using AllocatePool() and must be freed by the + caller using FreePool(). + + @param[in] Fmp Pointer to an EFI_FIRMWARE_MANAGEMENT_PROTOCOL. + @param[out] FmpImageInfoDescriptorVer Pointer to the version number associated + with the returned EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[out] FmpImageInfoCount Pointer to the number of the returned + EFI_FIRMWARE_IMAGE_DESCRIPTORs. + @param[out] DescriptorSize Pointer to the size, in bytes, of each + returned EFI_FIRMWARE_IMAGE_DESCRIPTOR. + + @return Pointer to the retrieved EFI_FIRMWARE_IMAGE_DESCRIPTOR. If the + descriptor can not be retrieved, then NULL is returned. + +**/ +EFI_FIRMWARE_IMAGE_DESCRIPTOR * +FmpGetFirmwareImageDescriptor ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp, + OUT UINT32 *FmpImageInfoDescriptorVer, + OUT UINT8 *FmpImageInfoCount, + OUT UINTN *DescriptorSize + ) +{ + EFI_STATUS Status; + UINTN ImageInfoSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + + ImageInfoSize = 0; + Status = Fmp->GetImageInfo ( + Fmp, // FMP Pointer + &ImageInfoSize, // Buffer Size (in this case 0) + NULL, // NULL so we can get size + FmpImageInfoDescriptorVer, // DescriptorVersion + FmpImageInfoCount, // DescriptorCount + DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status)); + return NULL; + } + + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for FMP descriptor.\n")); + return NULL; + } + + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, // FMP Pointer + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + FmpImageInfoDescriptorVer, // DescriptorVersion + FmpImageInfoCount, // DescriptorCount + DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + if (PackageVersionName != NULL) { + FreePool (PackageVersionName); + } + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status)); + FreePool (FmpImageInfoBuf); + return NULL; + } + + return FmpImageInfoBuf; +} + +/** + Function to create ESRT based on FMP Instances. + Create ESRT table, get the descriptors from FMP Instance and + create ESRT entries (ESRE). + + @return Pointer to the ESRT created. + +**/ +EFI_SYSTEM_RESOURCE_TABLE * +CreateFmpBasedEsrt ( + VOID + ) +{ + EFI_STATUS Status; + UINTN NoProtocols; + VOID **Buffer; + UINTN Index; + UINT32 FmpImageInfoDescriptorVer; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + UINT32 NumberOfDescriptors; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *OrgFmpImageInfoBuf; + EFI_SYSTEM_RESOURCE_TABLE *Table; + GUID_HARDWAREINSTANCE_PAIR *HardwareInstances; + + Status = EFI_SUCCESS; + NoProtocols = 0; + Buffer = NULL; + FmpImageInfoBuf = NULL; + OrgFmpImageInfoBuf = NULL; + Table = NULL; + HardwareInstances = NULL; + + Status = EfiLocateProtocolBuffer ( + &gEfiFirmwareManagementProtocolGuid, + &NoProtocols, + &Buffer + ); + if (EFI_ERROR(Status) || (Buffer == NULL)) { + return NULL; + } + + // + // Count the total number of EFI_FIRMWARE_IMAGE_DESCRIPTORs + // + for (Index = 0, NumberOfDescriptors = 0; Index < NoProtocols; Index++) { + FmpImageInfoBuf = FmpGetFirmwareImageDescriptor ( + (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index], + &FmpImageInfoDescriptorVer, + &FmpImageInfoCount, + &DescriptorSize + ); + if (FmpImageInfoBuf != NULL) { + NumberOfDescriptors += FmpImageInfoCount; + FreePool (FmpImageInfoBuf); + } + } + + // + // Allocate ESRT Table and GUID/HardwareInstance table + // + Table = AllocateZeroPool ( + (NumberOfDescriptors * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE) + ); + if (Table == NULL) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n")); + FreePool (Buffer); + return NULL; + } + + HardwareInstances = AllocateZeroPool (NumberOfDescriptors * sizeof (GUID_HARDWAREINSTANCE_PAIR)); + if (HardwareInstances == NULL) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for HW Instance Table.\n")); + FreePool (Table); + FreePool (Buffer); + return NULL; + } + + // + // Initialize ESRT Table + // + Table->FwResourceCount = 0; + Table->FwResourceCountMax = NumberOfDescriptors; + Table->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION; + + NumberOfDescriptors = 0; + for (Index = 0; Index < NoProtocols; Index++) { + FmpImageInfoBuf = FmpGetFirmwareImageDescriptor ( + (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index], + &FmpImageInfoDescriptorVer, + &FmpImageInfoCount, + &DescriptorSize + ); + if (FmpImageInfoBuf == NULL) { + continue; + } + + // + // Check each descriptor and read from the one specified + // + OrgFmpImageInfoBuf = FmpImageInfoBuf; + while (FmpImageInfoCount > 0) { + // + // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore. + // + if ((FmpImageInfoBuf->AttributesSetting & FmpImageInfoBuf->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) == IMAGE_ATTRIBUTE_IN_USE) { + // + // Create ESRT entry + // + CreateEsrtEntry (Table, HardwareInstances, &NumberOfDescriptors, FmpImageInfoBuf, FmpImageInfoDescriptorVer); + } + FmpImageInfoCount--; + // + // Increment the buffer pointer ahead by the size of the descriptor + // + FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize); + } + + FreePool (OrgFmpImageInfoBuf); + OrgFmpImageInfoBuf = NULL; + } + + FreePool (Buffer); + FreePool (HardwareInstances); + return Table; +} + +/** + Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to + install the Efi System Resource Table. + + @param[in] Event The Event that is being processed. + @param[in] Context The Event Context. + +**/ +VOID +EFIAPI +EsrtReadyToBootEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_TABLE *Table; + + Table = CreateFmpBasedEsrt (); + if (Table != NULL) { + // + // Print table on debug builds + // + DEBUG_CODE_BEGIN (); + PrintTable (Table); + DEBUG_CODE_END (); + + Status = InstallEfiSystemResourceTableInUefiConfigurationTable (Table); + if (EFI_ERROR (Status)) { + FreePool (Table); + } + } else { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n")); + } + + // + // Close the event to prevent it be signalled again. + // + gBS->CloseEvent (Event); +} + +/** + The module Entry Point of the Efi System Resource Table DXE driver. + + @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 entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +EsrtFmpEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT EsrtReadyToBootEvent; + + // + // Register notify function to install ESRT on ReadyToBoot Event. + // + Status = EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + EsrtReadyToBootEventNotify, + NULL, + &EsrtReadyToBootEvent + ); + + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to register for ready to boot\n")); + } + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDebugPrint.c b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDebugPrint.c new file mode 100644 index 000000000..e97279376 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDebugPrint.c @@ -0,0 +1,144 @@ +/** @file + Publishes ESRT table from Firmware Management Protocol instances + + Copyright (c) 2016, Microsoft Corporation + Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +/** + Function to print a single ESRT Entry (ESRE) to the debug console. + + Print Format: + | 00000000-0000-0000-0000-000000000000 | SSSSSSSSSSSS | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | + + @param[in] Entry - Pointer to an ESRE entry + @retval EFI_SUCCESS + EFI_INVALID_PARAMETER +**/ +EFI_STATUS +EFIAPI +PrintOutEsrtEntry ( + IN EFI_SYSTEM_RESOURCE_ENTRY *Entry + ) +{ + if (Entry == NULL) { + DEBUG ((DEBUG_INFO, "| ERROR: Invalid resource entry pointer ")); + DEBUG ((DEBUG_INFO, " |\n")); + return EFI_INVALID_PARAMETER; + } + + // + // GUID FW Class (36 chars plus table formatting) + // + DEBUG ((DEBUG_INFO, "| %g |", &Entry->FwClass)); + + // + // Entry Type (12 chars plus table formatting) + // + switch (Entry->FwType) { + case (ESRT_FW_TYPE_SYSTEMFIRMWARE) : + DEBUG ((DEBUG_INFO, " System FW |")); + break; + case (ESRT_FW_TYPE_DEVICEFIRMWARE) : + DEBUG ((DEBUG_INFO, " Device FW |")); + break; + case (ESRT_FW_TYPE_UEFIDRIVER) : + DEBUG ((DEBUG_INFO, " Uefi Driver |")); + break; + case (ESRT_FW_TYPE_UNKNOWN) : + DEBUG ((DEBUG_INFO, " Unknown Type |")); + break; + default: + DEBUG ((DEBUG_INFO, " ? 0x%8X |", Entry->FwType)); + break; + } + + // + // FW Version (10 char UINT32 string plus table formatting) + // Lowest Supported Version (10 char UINT32 string plus table formatting) + // Capsule Flags (10 char UINT32 string plus table formatting) + // Last Attempt Version (10 char UINT32 string plus table formatting) + // Last Attempt Status (10 char UINT32 string plus table formatting) + // + DEBUG ((DEBUG_INFO, + " 0x%8X | 0x%8X | 0x%8X | 0x%8X | 0x%8X |\n", + Entry->FwVersion, + Entry->LowestSupportedFwVersion, + Entry->CapsuleFlags, + Entry->LastAttemptVersion, + Entry->LastAttemptStatus + )); + + return EFI_SUCCESS; +} + +/** + Function to print the ESRT table to the debug console. + + @param[in] Table - Pointer to the ESRT table +**/ +VOID +EFIAPI +PrintTable ( + IN EFI_SYSTEM_RESOURCE_TABLE *Table + ) +{ + EFI_SYSTEM_RESOURCE_ENTRY *Entry; + UINTN Index; + + Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(((UINT8 *)Table) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)); + + // + // Print ESRT table information + // + DEBUG ((DEBUG_INFO, "ESRT Table Information:\n")); + if (Table == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: Invalid table pointer\n")); + return; + } + + DEBUG ((DEBUG_INFO, "+--------------------------------------------------------+\n")); + DEBUG ((DEBUG_INFO, "| Firmware Resource Count : 0x%08x |\n", Table->FwResourceCount)); + DEBUG ((DEBUG_INFO, "| Firmware Resource Count Max : 0x%08x |\n", Table->FwResourceCountMax)); + DEBUG ((DEBUG_INFO, "| Firmware Resource Entry Version : 0x%016x |\n", Table->FwResourceVersion)); + DEBUG ((DEBUG_INFO, "+--------------------------------------------------------+\n")); + + // + // Print table entry information + // + DEBUG ((DEBUG_INFO, "ESRT Table Entries:\n")); + if (Table->FwResourceVersion != EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION) { + DEBUG ((DEBUG_INFO, "ERROR: Unsupported Resource Entry Version\n")); + return; + } + + DEBUG ((DEBUG_INFO, "+--------------------------------------+--------------+------------")); + DEBUG ((DEBUG_INFO, "+------------+------------+------------+------------+\n")); + DEBUG ((DEBUG_INFO, "| | | ")); + DEBUG ((DEBUG_INFO, "| Lowest | | Last | Last |\n")); + DEBUG ((DEBUG_INFO, "| | Firmware | ")); + DEBUG ((DEBUG_INFO, "| Supported | Capsule | Attempted | Attempted |\n")); + DEBUG ((DEBUG_INFO, "| CLASS GUID | Type | Version ")); + DEBUG ((DEBUG_INFO, "| Version | Flags | Version | Status |\n")); + DEBUG ((DEBUG_INFO, "+--------------------------------------+--------------+------------")); + DEBUG ((DEBUG_INFO, "+------------+------------+------------+------------+\n")); + + for (Index = 0; Index < Table->FwResourceCount; Index++) { + PrintOutEsrtEntry (&(Entry[Index])); + } + + DEBUG ((DEBUG_INFO, "+--------------------------------------+--------------+------------")); + DEBUG ((DEBUG_INFO, "+------------+------------+------------+------------+\n")); +} + diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.inf b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.inf new file mode 100644 index 000000000..89120c181 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.inf @@ -0,0 +1,57 @@ +## @file +# Publishes ESRT table from Firmware Management Protocol instances +# +# Copyright (c) 2016, Microsoft Corporation +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# +# All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EsrtFmpDxe + MODULE_UNI_FILE = EsrtFmpDxe.uni + FILE_GUID = FF626DA9-17EE-4949-A8B8-B10FA0044E9F + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = EsrtFmpEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + EsrtFmp.c + EsrtFmpDebugPrint.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiLib + UefiBootServicesTableLib + DebugLib + PcdLib + +[Protocols] + gEfiFirmwareManagementProtocolGuid ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSystemFmpCapsuleImageTypeIdGuid ## CONSUMES + +[Guids] + gEfiSystemResourceTableGuid ## PRODUCES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + EsrtFmpDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.uni b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.uni new file mode 100644 index 000000000..7d97bc204 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.uni @@ -0,0 +1,13 @@ +// /** @file +// Publishes ESRT table from Firmware Management Protocol instances +// +// Copyright (c) 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Publishes ESRT table from Firmware Management Protocol instances" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver publishes the ESRT table from Firmware Management Protocol instances.

\n" + "The ESRT table is published when the Ready To Boot event is signaled.
" diff --git a/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxeExtra.uni new file mode 100644 index 000000000..e6ad5d689 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxeExtra.uni @@ -0,0 +1,12 @@ +// /** @file +// EsrtFmpDxe Localized Strings and Content +// +// Copyright (c) 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"ESRT FMP DXE Driver" diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c new file mode 100644 index 000000000..484763225 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c @@ -0,0 +1,887 @@ +/** @file + + These are the common Fault Tolerant Write (FTW) functions that are shared + by DXE FTW driver and SMM FTW driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FaultTolerantWrite.h" + +// +// Fault Tolerant Write Protocol API +// +/** + Query the largest block that may be updated in a fault tolerant manner. + + + @param This The pointer to this protocol instance. + @param BlockSize A pointer to a caller allocated UINTN that is updated to + indicate the size of the largest block that can be updated. + + @return EFI_SUCCESS The function completed successfully + +**/ +EFI_STATUS +EFIAPI +FtwGetMaxBlockSize ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT UINTN *BlockSize + ) +{ + EFI_FTW_DEVICE *FtwDevice; + + if (!FeaturePcdGet(PcdFullFtwServiceEnable)) { + return EFI_UNSUPPORTED; + } + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + *BlockSize = FtwDevice->SpareAreaLength; + + return EFI_SUCCESS; +} + +/** + Allocates space for the protocol to maintain information about writes. + Since writes must be completed in a fault tolerant manner and multiple + updates will require more resources to be successful, this function + enables the protocol to ensure that enough space exists to track + information about the upcoming writes. + + All writes must be completed or aborted before another fault tolerant write can occur. + + @param This The pointer to this protocol instance. + @param CallerId The GUID identifying the write. + @param PrivateDataSize The size of the caller's private data + that must be recorded for each write. + @param NumberOfWrites The number of fault tolerant block writes + that will need to occur. + + @return EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED All allocated writes have not been completed. + +**/ +EFI_STATUS +EFIAPI +FtwAllocate ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_GUID *CallerId, + IN UINTN PrivateDataSize, + IN UINTN NumberOfWrites + ) +{ + EFI_STATUS Status; + UINTN Offset; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Check if there is enough space for the coming allocation + // + if (FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) { + DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId)); + return EFI_BUFFER_TOO_SMALL; + } + // + // Find the last write header and record. + // If the FtwHeader is complete, skip the completed last write header/records + // + FtwHeader = FtwDevice->FtwLastWriteHeader; + + // + // Previous write has not completed, access denied. + // + if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) { + return EFI_ACCESS_DENIED; + } + // + // If workspace is not enough, then reclaim workspace + // + Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace; + if (Offset + FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) { + Status = FtwReclaimWorkSpace (FtwDevice, TRUE); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + FtwHeader = FtwDevice->FtwLastWriteHeader; + } + // + // Prepare FTW write header, + // overwrite the buffer and write to workspace. + // + FtwHeader->WritesAllocated = FTW_INVALID_STATE; + FtwHeader->Complete = FTW_INVALID_STATE; + CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID)); + FtwHeader->NumberOfWrites = NumberOfWrites; + FtwHeader->PrivateDataSize = PrivateDataSize; + FtwHeader->HeaderAllocated = FTW_VALID_STATE; + + Status = WriteWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER), + (UINT8 *) FtwHeader + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Update Header->WriteAllocated as VALID + // + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + WRITES_ALLOCATED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + DEBUG ( + (EFI_D_INFO, + "Ftw: Allocate() success, Caller:%g, # %d\n", + CallerId, + NumberOfWrites) + ); + + return EFI_SUCCESS; +} + + +/** + Write a record with fault tolerant manner. + Since the content has already backuped in spare block, the write is + guaranteed to be completed with fault tolerant manner. + + @param This The pointer to this protocol instance. + @param Fvb The FVB protocol that provides services for + reading, writing, and erasing the target block. + @param BlockSize The size of the block. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FtwWriteRecord ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb, + IN UINTN BlockSize + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + UINTN Offset; + UINTN NumberOfWriteBlocks; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + // + // Spare Complete but Destination not complete, + // Recover the target block with the spare block. + // + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + // + // IF target block is working block, THEN Flush Spare Block To Working Block; + // ELSE flush spare block to target block, which may be boot block after all. + // + if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) { + // + // If target block is working block, + // it also need to set SPARE_COMPLETED to spare block. + // + Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwBackupFvb, + FtwDevice->SpareBlockSize, + FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare, + FtwDevice->FtwWorkSpaceBaseInSpare + Offset, + SPARE_COMPLETED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Status = FlushSpareBlockToWorkingBlock (FtwDevice); + } else if (IsBootBlock (FtwDevice, Fvb)) { + // + // Update boot block + // + Status = FlushSpareBlockToBootBlock (FtwDevice); + } else { + // + // Update blocks other than working block or boot block + // + NumberOfWriteBlocks = FTW_BLOCKS ((UINTN) (Record->Offset + Record->Length), BlockSize); + Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba, BlockSize, NumberOfWriteBlocks); + } + + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Record the DestionationComplete in record + // + Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + DEST_COMPLETED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Record->DestinationComplete = FTW_VALID_STATE; + + // + // If this is the last Write in these write sequence, + // set the complete flag of write header. + // + if (IsLastRecordOfWrites (Header, Record)) { + Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + WRITES_COMPLETED + ); + Header->Complete = FTW_VALID_STATE; + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + + return EFI_SUCCESS; +} + +/** + Starts a target block update. This function will record data about write + in fault tolerant storage and will complete the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param This The pointer to this protocol instance. + @param Lba The logical block address of the target block. + @param Offset The offset within the target block to place the data. + @param Length The number of bytes to write to the target block. + @param PrivateData A pointer to private data that the caller requires to + complete any pending writes in the event of a fault. + @param FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block. + Offset + *NumBytes > SpareAreaLength. + @retval EFI_ACCESS_DENIED No writes have been allocated. + @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource. + @retval EFI_NOT_FOUND Cannot find FVB protocol by handle. + +**/ +EFI_STATUS +EFIAPI +FtwWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN VOID *PrivateData, + IN EFI_HANDLE FvBlockHandle, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + UINTN MyLength; + UINTN MyOffset; + UINTN MyBufferSize; + UINT8 *MyBuffer; + UINTN SpareBufferSize; + UINT8 *SpareBuffer; + UINTN Index; + UINT8 *Ptr; + EFI_PHYSICAL_ADDRESS FvbPhysicalAddress; + UINTN BlockSize; + UINTN NumberOfBlocks; + UINTN NumberOfWriteBlocks; + UINTN WriteLength; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) { + if (PrivateData == NULL) { + // + // Ftw Write Header is not allocated. + // No additional private data, the private data size is zero. Number of record can be set to 1. + // + Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Ftw Write Header is not allocated + // Additional private data is not NULL, the private data size can't be determined. + // + DEBUG ((EFI_D_ERROR, "Ftw: no allocates space for write record!\n")); + DEBUG ((EFI_D_ERROR, "Ftw: Allocate service should be called before Write service!\n")); + return EFI_NOT_READY; + } + } + + // + // If Record is out of the range of Header, return access denied. + // + if (((UINTN) Record - (UINTN) Header) > FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) { + return EFI_ACCESS_DENIED; + } + + // + // Check the COMPLETE flag of last write header + // + if (Header->Complete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + if (Record->DestinationComplete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) { + return EFI_NOT_READY; + } + + // + // Get the FVB protocol by handle + // + Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get FVB physical address - %r\n", Status)); + return EFI_ABORTED; + } + + // + // Now, one FVB has one type of BlockSize. + // + Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get block size - %r\n", Status)); + return EFI_ABORTED; + } + + NumberOfWriteBlocks = FTW_BLOCKS (Offset + Length, BlockSize); + DEBUG ((EFI_D_INFO, "Ftw: Write(), BlockSize - 0x%x, NumberOfWriteBlock - 0x%x\n", BlockSize, NumberOfWriteBlocks)); + WriteLength = NumberOfWriteBlocks * BlockSize; + + // + // Check if the input data can fit within the spare block. + // + if (WriteLength > FtwDevice->SpareAreaLength) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Set BootBlockUpdate FLAG if it's updating boot block. + // + if (IsBootBlock (FtwDevice, Fvb)) { + Record->BootBlockUpdate = FTW_VALID_STATE; + // + // Boot Block and Spare Block should have same block size and block numbers. + // + ASSERT ((BlockSize == FtwDevice->SpareBlockSize) && (NumberOfWriteBlocks == FtwDevice->NumberOfSpareBlock)); + } + // + // Write the record to the work space. + // + Record->Lba = Lba; + Record->Offset = Offset; + Record->Length = Length; + Record->RelativeOffset = (INT64) (FvbPhysicalAddress + (UINTN) Lba * BlockSize) - (INT64) FtwDevice->SpareAreaAddress; + if (PrivateData != NULL) { + CopyMem ((Record + 1), PrivateData, (UINTN) Header->PrivateDataSize); + } + + MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + MyLength = FTW_RECORD_SIZE (Header->PrivateDataSize); + + Status = WriteWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + MyOffset, + MyLength, + (UINT8 *) Record + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Record has written to working block, then do the data. + // + // + // Allocate a memory buffer + // + MyBufferSize = WriteLength; + MyBuffer = AllocatePool (MyBufferSize); + if (MyBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Read all original data from target block to memory buffer + // + Ptr = MyBuffer; + for (Index = 0; Index < NumberOfWriteBlocks; Index += 1) { + MyLength = BlockSize; + Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + } + // + // Overwrite the updating range data with + // the input buffer content + // + CopyMem (MyBuffer + Offset, Buffer, Length); + + // + // Try to keep the content of spare block + // Save spare block into a spare backup memory buffer (Sparebuffer) + // + SpareBufferSize = FtwDevice->SpareAreaLength; + SpareBuffer = AllocatePool (SpareBufferSize); + if (SpareBuffer == NULL) { + FreePool (MyBuffer); + return EFI_OUT_OF_RESOURCES; + } + + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + MyLength = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + } + // + // Write the memory buffer to spare block + // Do not assume Spare Block and Target Block have same block size + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = MyBuffer; + for (Index = 0; MyBufferSize > 0; Index += 1) { + if (MyBufferSize > FtwDevice->SpareBlockSize) { + MyLength = FtwDevice->SpareBlockSize; + } else { + MyLength = MyBufferSize; + } + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (MyBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + MyBufferSize -= MyLength; + } + // + // Free MyBuffer + // + FreePool (MyBuffer); + + // + // Set the SpareComplete in the FTW record, + // + MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + MyOffset, + SPARE_COMPLETED + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Record->SpareComplete = FTW_VALID_STATE; + + // + // Since the content has already backuped in spare block, the write is + // guaranteed to be completed with fault tolerant manner. + // + Status = FtwWriteRecord (This, Fvb, BlockSize); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + // + // Restore spare backup buffer into spare block , if no failure happened during FtwWrite. + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + MyLength = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += MyLength; + } + // + // All success. + // + FreePool (SpareBuffer); + + DEBUG ( + (EFI_D_INFO, + "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n", + Lba, + Offset, + Length) + ); + + return EFI_SUCCESS; +} + +/** + Restarts a previously interrupted write. The caller must provide the + block protocol needed to complete the interrupted write. + + @param This The pointer to this protocol instance. + @param FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ACCESS_DENIED No pending writes exist + @retval EFI_NOT_FOUND FVB protocol not found by the handle + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +EFIAPI +FtwRestart ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_HANDLE FvBlockHandle + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + UINTN BlockSize; + UINTN NumberOfBlocks; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + // + // Spare Complete but Destination not complete, + // Recover the targt block with the spare block. + // + Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Now, one FVB has one type of BlockSize + // + Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Restart(), Get block size - %r\n", Status)); + return EFI_ABORTED; + } + + // + // Check the COMPLETE flag of last write header + // + if (Header->Complete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + // + // Check the flags of last write record + // + if (Record->DestinationComplete == FTW_VALID_STATE) { + return EFI_ACCESS_DENIED; + } + + if ((Record->SpareComplete != FTW_VALID_STATE)) { + return EFI_ABORTED; + } + + // + // Since the content has already backuped in spare block, the write is + // guaranteed to be completed with fault tolerant manner. + // + Status = FtwWriteRecord (This, Fvb, BlockSize); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + // + // Erase Spare block + // This is restart, no need to keep spareblock content. + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__)); + return EFI_SUCCESS; +} + +/** + Aborts all previous allocated writes. + + @param This The pointer to this protocol instance. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwAbort ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + UINTN Offset; + EFI_FTW_DEVICE *FtwDevice; + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + if (FtwDevice->FtwLastWriteHeader->HeaderAllocated != FTW_VALID_STATE) { + return EFI_NOT_FOUND; + } + + if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) { + return EFI_NOT_FOUND; + } + // + // Update the complete state of the header as VALID and abort. + // + Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace; + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + Offset, + WRITES_COMPLETED + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE; + + DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__)); + return EFI_SUCCESS; +} + +/** + Starts a target block update. This records information about the write + in fault tolerant storage and will complete the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param This The pointer to this protocol instance. + @param CallerId The GUID identifying the last write. + @param Lba The logical block address of the last write. + @param Offset The offset within the block of the last write. + @param Length The length of the last write. + @param PrivateDataSize bytes from the private data + stored for this write. + @param PrivateData A pointer to a buffer. The function will copy + @param Complete A Boolean value with TRUE indicating + that the write was completed. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully + @retval EFI_NOT_FOUND No allocated writes exist + @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough + +**/ +EFI_STATUS +EFIAPI +FtwGetLastWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT EFI_GUID *CallerId, + OUT EFI_LBA *Lba, + OUT UINTN *Offset, + OUT UINTN *Length, + IN OUT UINTN *PrivateDataSize, + OUT VOID *PrivateData, + OUT BOOLEAN *Complete + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + EFI_FAULT_TOLERANT_WRITE_RECORD *Record; + + if (!FeaturePcdGet(PcdFullFtwServiceEnable)) { + return EFI_UNSUPPORTED; + } + + FtwDevice = FTW_CONTEXT_FROM_THIS (This); + + Status = WorkSpaceRefresh (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Header = FtwDevice->FtwLastWriteHeader; + Record = FtwDevice->FtwLastWriteRecord; + + // + // If Header is incompleted and the last record has completed, then + // call Abort() to set the Header->Complete FLAG. + // + if ((Header->Complete != FTW_VALID_STATE) && + (Record->DestinationComplete == FTW_VALID_STATE) && + IsLastRecordOfWrites (Header, Record) + ) { + + Status = FtwAbort (This); + *Complete = TRUE; + return EFI_NOT_FOUND; + } + // + // If there is no write header/record, return not found. + // + if (Header->HeaderAllocated != FTW_VALID_STATE) { + *Complete = TRUE; + return EFI_NOT_FOUND; + } + // + // If this record SpareComplete has not set, then it can not restart. + // + if (Record->SpareComplete != FTW_VALID_STATE) { + Status = GetPreviousRecordOfWrites (Header, &Record); + if (EFI_ERROR (Status)) { + FtwAbort (This); + *Complete = TRUE; + return EFI_NOT_FOUND; + } + ASSERT (Record != NULL); + } + + // + // Fill all the requested values + // + CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID)); + *Lba = Record->Lba; + *Offset = (UINTN) Record->Offset; + *Length = (UINTN) Record->Length; + *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE); + + if (*PrivateDataSize < Header->PrivateDataSize) { + *PrivateDataSize = (UINTN) Header->PrivateDataSize; + PrivateData = NULL; + Status = EFI_BUFFER_TOO_SMALL; + } else { + *PrivateDataSize = (UINTN) Header->PrivateDataSize; + CopyMem (PrivateData, Record + 1, *PrivateDataSize); + Status = EFI_SUCCESS; + } + + DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__)); + + return Status; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h new file mode 100644 index 000000000..fe1c9f373 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h @@ -0,0 +1,784 @@ +/** @file + + The internal header file includes the common header files, defines + internal structure and functions used by Ftw module. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_FAULT_TOLERANT_WRITE_H_ +#define _EFI_FAULT_TOLERANT_WRITE_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// +// Flash erase polarity is 1 +// +#define FTW_ERASE_POLARITY 1 + +#define FTW_ERASED_BYTE ((UINT8) (255)) +#define FTW_POLARITY_REVERT ((UINT8) (255)) + +#define HEADER_ALLOCATED 0x1 +#define WRITES_ALLOCATED 0x2 +#define WRITES_COMPLETED 0x4 + +#define BOOT_BLOCK_UPDATE 0x1 +#define SPARE_COMPLETED 0x2 +#define DEST_COMPLETED 0x4 + +#define FTW_BLOCKS(Length, BlockSize) ((UINTN) ((Length) / (BlockSize) + (((Length) & ((BlockSize) - 1)) ? 1 : 0))) + +#define FTW_DEVICE_SIGNATURE SIGNATURE_32 ('F', 'T', 'W', 'D') + +// +// EFI Fault tolerant protocol private data structure +// +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + EFI_FAULT_TOLERANT_WRITE_PROTOCOL FtwInstance; + EFI_PHYSICAL_ADDRESS WorkSpaceAddress; // Base address of working space range in flash. + EFI_PHYSICAL_ADDRESS SpareAreaAddress; // Base address of spare range in flash. + UINTN WorkSpaceLength; // Size of working space range in flash. + UINTN NumberOfWorkSpaceBlock; // Number of the blocks in work block for work space. + UINTN WorkBlockSize; // Block size in bytes of the work blocks in flash + UINTN SpareAreaLength; // Size of spare range in flash. + UINTN NumberOfSpareBlock; // Number of the blocks in spare block. + UINTN SpareBlockSize; // Block size in bytes of the spare blocks in flash + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader;// Pointer to Working Space Header in memory buffer + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwLastWriteHeader;// Pointer to last record header in memory buffer + EFI_FAULT_TOLERANT_WRITE_RECORD *FtwLastWriteRecord;// Pointer to last record in memory buffer + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FtwFvBlock; // FVB of working block + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FtwBackupFvb; // FVB of spare block + EFI_LBA FtwSpareLba; // Start LBA of spare block + EFI_LBA FtwWorkBlockLba; // Start LBA of working block that contains working space in its last block. + UINTN NumberOfWorkBlock; // Number of the blocks in work block. + EFI_LBA FtwWorkSpaceLba; // Start LBA of working space + UINTN FtwWorkSpaceBase; // Offset into the FtwWorkSpaceLba block. + UINTN FtwWorkSpaceSize; // Size of working space range that stores write record. + EFI_LBA FtwWorkSpaceLbaInSpare; // Start LBA of working space in spare block. + UINTN FtwWorkSpaceBaseInSpare;// Offset into the FtwWorkSpaceLbaInSpare block. + UINT8 *FtwWorkSpace; // Point to Work Space in memory buffer + // + // Following a buffer of FtwWorkSpace[FTW_WORK_SPACE_SIZE], + // Allocated with EFI_FTW_DEVICE. + // +} EFI_FTW_DEVICE; + +#define FTW_CONTEXT_FROM_THIS(a) CR (a, EFI_FTW_DEVICE, FtwInstance, FTW_DEVICE_SIGNATURE) + +// +// Driver entry point +// +/** + This function is the entry point of the Fault Tolerant Write driver. + + @param ImageHandle A handle for the image that is initializing this driver + @param SystemTable A pointer to the EFI system table + + @return EFI_SUCCESS FTW has finished the initialization + @retval EFI_NOT_FOUND Locate FVB protocol error + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_VOLUME_CORRUPTED Firmware volume is error + @retval EFI_ABORTED FTW initialization error + +**/ +EFI_STATUS +EFIAPI +InitializeFaultTolerantWrite ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Fault Tolerant Write Protocol API +// + +/** + Query the largest block that may be updated in a fault tolerant manner. + + + @param This Indicates a pointer to the calling context. + @param BlockSize A pointer to a caller allocated UINTN that is updated to + indicate the size of the largest block that can be updated. + + @return EFI_SUCCESS The function completed successfully + +**/ +EFI_STATUS +EFIAPI +FtwGetMaxBlockSize ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT UINTN *BlockSize + ); + +/** + Allocates space for the protocol to maintain information about writes. + Since writes must be completed in a fault tolerant manner and multiple + updates will require more resources to be successful, this function + enables the protocol to ensure that enough space exists to track + information about the upcoming writes. + + All writes must be completed or aborted before another fault tolerant write can occur. + + @param This Indicates a pointer to the calling context. + @param CallerId The GUID identifying the write. + @param PrivateDataSize The size of the caller's private data + that must be recorded for each write. + @param NumberOfWrites The number of fault tolerant block writes + that will need to occur. + + @return EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED All allocated writes have not been completed. + +**/ +EFI_STATUS +EFIAPI +FtwAllocate ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_GUID *CallerId, + IN UINTN PrivateDataSize, + IN UINTN NumberOfWrites + ); + +/** + Starts a target block update. This function will record data about write + in fault tolerant storage and will complete the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + + @param This Calling context + @param Lba The logical block address of the target block. + @param Offset The offset within the target block to place the data. + @param Length The number of bytes to write to the target block. + @param PrivateData A pointer to private data that the caller requires to + complete any pending writes in the event of a fault. + @param FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block. + Offset + *NumBytes > SpareAreaLength. + @retval EFI_ACCESS_DENIED No writes have been allocated. + @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource. + @retval EFI_NOT_FOUND Cannot find FVB protocol by handle. + +**/ +EFI_STATUS +EFIAPI +FtwWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN VOID *PrivateData, + IN EFI_HANDLE FvBlockHandle, + IN VOID *Buffer + ); + +/** + Restarts a previously interrupted write. The caller must provide the + block protocol needed to complete the interrupted write. + + @param This Calling context. + @param FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ACCESS_DENIED No pending writes exist + @retval EFI_NOT_FOUND FVB protocol not found by the handle + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +EFIAPI +FtwRestart ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_HANDLE FvBlockHandle + ); + +/** + Aborts all previous allocated writes. + + @param This Calling context + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwAbort ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This + ); + +/** + Starts a target block update. This records information about the write + in fault tolerant storage and will complete the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param This Indicates a pointer to the calling context. + @param CallerId The GUID identifying the last write. + @param Lba The logical block address of the last write. + @param Offset The offset within the block of the last write. + @param Length The length of the last write. + @param PrivateDataSize bytes from the private data + stored for this write. + @param PrivateData A pointer to a buffer. The function will copy + @param Complete A Boolean value with TRUE indicating + that the write was completed. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully + @retval EFI_NOT_FOUND No allocated writes exist + @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough + +**/ +EFI_STATUS +EFIAPI +FtwGetLastWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT EFI_GUID *CallerId, + OUT EFI_LBA *Lba, + OUT UINTN *Offset, + OUT UINTN *Length, + IN OUT UINTN *PrivateDataSize, + OUT VOID *PrivateData, + OUT BOOLEAN *Complete + ); + +/** + Erase spare block. + + @param FtwDevice The private data of FTW driver + + @retval EFI_SUCCESS The erase request was successfully completed. + @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state. + @retval EFI_DEVICE_ERROR The block device is not functioning + correctly and could not be written. + The firmware device may have been + partially erased. + @retval EFI_INVALID_PARAMETER One or more of the LBAs listed + in the variable argument list do + not exist in the firmware volume. + + +**/ +EFI_STATUS +FtwEraseSpareBlock ( + IN EFI_FTW_DEVICE *FtwDevice + ); + +/** + Retrieve the proper FVB protocol interface by HANDLE. + + + @param FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param FvBlock The interface of FVB protocol + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FtwGetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ); + +/** + + Is it in working block? + + @param FtwDevice The private data of FTW driver + @param FvBlock Fvb protocol instance + @param Lba The block specified + + @return A BOOLEAN value indicating in working block or not. + +**/ +BOOLEAN +IsWorkingBlock ( + EFI_FTW_DEVICE *FtwDevice, + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + EFI_LBA Lba + ); + +/** + + Is it in boot block? + + @param FtwDevice The private data of FTW driver + @param FvBlock Fvb protocol instance + + @return A BOOLEAN value indicating in boot block or not. + +**/ +BOOLEAN +IsBootBlock ( + EFI_FTW_DEVICE *FtwDevice, + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock + ); + +/** + Copy the content of spare block to a target block. Size is FTW_BLOCK_SIZE. + Spare block is accessed by FTW backup FVB protocol interface. + Target block is accessed by FvBlock protocol interface. + + + @param FtwDevice The private data of FTW driver + @param FvBlock FVB Protocol interface to access target block + @param Lba Lba of the target block + @param BlockSize The size of the block + @param NumberOfBlocks The number of consecutive blocks starting with Lba + + @retval EFI_SUCCESS Spare block content is copied to target block + @retval EFI_INVALID_PARAMETER Input parameter error + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FlushSpareBlockToTargetBlock ( + EFI_FTW_DEVICE *FtwDevice, + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + EFI_LBA Lba, + UINTN BlockSize, + UINTN NumberOfBlocks + ); + +/** + Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE. + Spare block is accessed by FTW backup FVB protocol interface. LBA is + FtwDevice->FtwSpareLba. + Working block is accessed by FTW working FVB protocol interface. LBA is + FtwDevice->FtwWorkBlockLba. + + Since the working block header is important when FTW initializes, the + state of the operation should be handled carefully. The Crc value is + calculated without STATE element. + + @param FtwDevice The private data of FTW driver + + @retval EFI_SUCCESS Spare block content is copied to target block + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FlushSpareBlockToWorkingBlock ( + EFI_FTW_DEVICE *FtwDevice + ); + +/** + Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE. + Spare block is accessed by FTW working FVB protocol interface. + Target block is accessed by FvBlock protocol interface. + + FTW will do extra work on boot block update. + FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL, + which is produced by a chipset driver. + FTW updating boot block steps may be: + 1. GetRangeLocation(), if the Range is inside the boot block, FTW know + that boot block will be update. It shall add a FLAG in the working block. + 2. When spare block is ready, + 3. SetSwapState(SWAPPED) + 4. erasing boot block, + 5. programming boot block until the boot block is ok. + 6. SetSwapState(UNSWAPPED) + FTW shall not allow to update boot block when battery state is error. + + @param FtwDevice The private data of FTW driver + + @retval EFI_SUCCESS Spare block content is copied to boot block + @retval EFI_INVALID_PARAMETER Input parameter error + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FlushSpareBlockToBootBlock ( + EFI_FTW_DEVICE *FtwDevice + ); + +/** + Update a bit of state on a block device. The location of the bit is + calculated by the (Lba, Offset, bit). Here bit is determined by the + the name of a certain bit. + + + @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock + @param BlockSize The size of the block + @param Lba Lba of a block + @param Offset Offset on the Lba + @param NewBit New value that will override the old value if it can be change + + @retval EFI_SUCCESS A state bit has been updated successfully + @retval Others Access block device error. + Notes: + Assume all bits of State are inside the same BYTE. + @retval EFI_ABORTED Read block fail + +**/ +EFI_STATUS +FtwUpdateFvState ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINT8 NewBit + ); + +/** + Get the last Write Header pointer. + The last write header is the header whose 'complete' state hasn't been set. + After all, this header may be a EMPTY header entry for next Allocate. + + + @param FtwWorkSpaceHeader Pointer of the working block header + @param FtwWorkSpaceSize Size of the work space + @param FtwWriteHeader Pointer to retrieve the last write header + + @retval EFI_SUCCESS Get the last write record successfully + @retval EFI_ABORTED The FTW work space is damaged + +**/ +EFI_STATUS +FtwGetLastWriteHeader ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader, + IN UINTN FtwWorkSpaceSize, + OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader + ); + +/** + Get the last Write Record pointer. The last write Record is the Record + whose DestinationCompleted state hasn't been set. After all, this Record + may be a EMPTY record entry for next write. + + + @param FtwWriteHeader Pointer to the write record header + @param FtwWriteRecord Pointer to retrieve the last write record + + @retval EFI_SUCCESS Get the last write record successfully + @retval EFI_ABORTED The FTW work space is damaged + +**/ +EFI_STATUS +FtwGetLastWriteRecord ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader, + OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord + ); + +/** + To check if FtwRecord is the first record of FtwHeader. + + @param FtwHeader Pointer to the write record header + @param FtwRecord Pointer to the write record + + @retval TRUE FtwRecord is the first Record of the FtwHeader + @retval FALSE FtwRecord is not the first Record of the FtwHeader + +**/ +BOOLEAN +IsFirstRecordOfWrites ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader, + IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord + ); + +/** + To check if FtwRecord is the last record of FtwHeader. Because the + FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be + determined if it is the last record of FtwHeader. + + @param FtwHeader Pointer to the write record header + @param FtwRecord Pointer to the write record + + @retval TRUE FtwRecord is the last Record of the FtwHeader + @retval FALSE FtwRecord is not the last Record of the FtwHeader + +**/ +BOOLEAN +IsLastRecordOfWrites ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader, + IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord + ); + +/** + To check if FtwRecord is the first record of FtwHeader. + + @param FtwHeader Pointer to the write record header + @param FtwRecord Pointer to retrieve the previous write record + + @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return. + @retval EFI_SUCCESS The previous write record is found. + +**/ +EFI_STATUS +GetPreviousRecordOfWrites ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader, + IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord + ); + +/** + + Check whether a flash buffer is erased. + + @param Buffer Buffer to check + @param BufferSize Size of the buffer + + @return A BOOLEAN value indicating erased or not. + +**/ +BOOLEAN +IsErasedFlashBuffer ( + IN UINT8 *Buffer, + IN UINTN BufferSize + ); +/** + Initialize a work space when there is no work space. + + @param WorkingHeader Pointer of working block header + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +InitWorkSpaceHeader ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader + ); +/** + Read from working block to refresh the work space in memory. + + @param FtwDevice Point to private data of FTW driver + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +WorkSpaceRefresh ( + IN EFI_FTW_DEVICE *FtwDevice + ); +/** + Check to see if it is a valid work space. + + + @param WorkingHeader Pointer of working block header + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + +**/ +BOOLEAN +IsValidWorkSpace ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader + ); +/** + Reclaim the work space on the working block. + + @param FtwDevice Point to private data of FTW driver + @param PreserveRecord Whether to preserve the working record is needed + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FtwReclaimWorkSpace ( + IN EFI_FTW_DEVICE *FtwDevice, + IN BOOLEAN PreserveRecord + ); + +/** + + Get firmware volume block by address. + + + @param Address Address specified the block + @param FvBlock The block caller wanted + + @retval EFI_SUCCESS The protocol instance if found. + @retval EFI_NOT_FOUND Block not found + +**/ +EFI_HANDLE +GetFvbByAddress ( + IN EFI_PHYSICAL_ADDRESS Address, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ); + +/** + Retrieve the proper Swap Address Range protocol interface. + + @param[out] SarProtocol The interface of SAR protocol + + @retval EFI_SUCCESS The SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +FtwGetSarProtocol ( + OUT VOID **SarProtocol + ); + +/** + Function returns an array of handles that support the FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ); + + +/** + Allocate private data for FTW driver and initialize it. + + @param[out] FtwData Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW device successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +InitFtwDevice ( + OUT EFI_FTW_DEVICE **FtwData + ); + + +/** + Initialization for Fault Tolerant Write is done in this handler. + + @param[in, out] FtwDevice Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW protocol successfully. + @retval EFI_NOT_FOUND No proper FVB protocol was found. + +**/ +EFI_STATUS +InitFtwProtocol ( + IN OUT EFI_FTW_DEVICE *FtwDevice + ); + +/** + Initialize a local work space header. + + Since Signature and WriteQueueSize have been known, Crc can be calculated out, + then the work space header will be fixed. +**/ +VOID +InitializeLocalWorkSpaceHeader ( + VOID + ); + +/** + Read work space data from work block or spare block. + + @param FvBlock FVB Protocol interface to access the block. + @param BlockSize The size of the block. + @param Lba Lba of the block. + @param Offset The offset within the block. + @param Length The number of bytes to read from the block. + @param Buffer The data is read. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +ReadWorkSpaceData ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + OUT UINT8 *Buffer + ); + +/** + Write data to work block. + + @param FvBlock FVB Protocol interface to access the block. + @param BlockSize The size of the block. + @param Lba Lba of the block. + @param Offset The offset within the block to place the data. + @param Length The number of bytes to write to the block. + @param Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +WriteWorkSpaceData ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN UINT8 *Buffer + ); + +/** + Internal implementation of CRC32. Depending on the execution context + (traditional SMM or DXE vs standalone MM), this function is implemented + via a call to the CalculateCrc32 () boot service, or via a library + call. + + If Buffer is NULL, then ASSERT(). + If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param[in] Buffer A pointer to the buffer on which the 32-bit CRC is + to be computed. + @param[in] Length The number of bytes in the buffer Data. + + @retval Crc32 The 32-bit CRC was computed for the data buffer. + +**/ +UINT32 +FtwCalculateCrc32 ( + IN VOID *Buffer, + IN UINTN Length + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c new file mode 100644 index 000000000..de38ea028 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c @@ -0,0 +1,277 @@ +/** @file + + This is a simple fault tolerant write driver. + + This boot service protocol only provides fault tolerant write capability for + block devices. The protocol has internal non-volatile intermediate storage + of the data and private information. It should be able to recover + automatically from a critical fault, such as power failure. + + The implementation uses an FTW (Fault Tolerant Write) Work Space. + This work space is a memory copy of the work space on the Working Block, + the size of the work space is the FTW_WORK_SPACE_SIZE bytes. + + The work space stores each write record as EFI_FTW_RECORD structure. + The spare block stores the write buffer before write to the target block. + + The write record has three states to specify the different phase of write operation. + 1) WRITE_ALLOCATED is that the record is allocated in write space. + The information of write operation is stored in write record structure. + 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup. + 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block. + + This driver operates the data as the whole size of spare block. + It first read the SpareAreaLength data from the target block into the spare memory buffer. + Then copy the write buffer data into the spare memory buffer. + Then write the spare memory buffer into the spare block. + Final copy the data from the spare block to the target block. + + To make this drive work well, the following conditions must be satisfied: + 1. The write NumBytes data must be fit within Spare area. + Offset + NumBytes <= SpareAreaLength + 2. The whole flash range has the same block size. + 3. Working block is an area which contains working space in its last block and has the same size as spare block. + 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 6. Any write data area (SpareAreaLength Area) which the data will be written into must be + in the single one Firmware Volume Block range which FVB protocol is produced on. + 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged. + The spare area must be enough large to store the write data before write them into the target range. + If one of them is not satisfied, FtwWrite may fail. + Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include "FaultTolerantWrite.h" +VOID *mFvbRegistration = NULL; + + +/** + Retrieve the FVB protocol interface by HANDLE. + + @param[in] FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param[out] FvBlock The interface of FVB protocol + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the FVB protocol. + @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. + +**/ +EFI_STATUS +FtwGetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ) +{ + // + // To get the FVB protocol interface on the handle + // + return gBS->HandleProtocol ( + FvBlockHandle, + &gEfiFirmwareVolumeBlockProtocolGuid, + (VOID **) FvBlock + ); +} + +/** + Retrieve the Swap Address Range protocol interface. + + @param[out] SarProtocol The interface of SAR protocol + + @retval EFI_SUCCESS The SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +FtwGetSarProtocol ( + OUT VOID **SarProtocol + ) +{ + EFI_STATUS Status; + + // + // Locate Swap Address Range protocol + // + Status = gBS->LocateProtocol ( + &gEfiSwapAddressRangeProtocolGuid, + NULL, + SarProtocol + ); + return Status; +} + +/** + Function returns an array of handles that support the FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + + // + // Locate all handles of Fvb protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolumeBlockProtocolGuid, + NULL, + NumberHandles, + Buffer + ); + return Status; +} + + +/** + Firmware Volume Block Protocol notification event handler. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +FvbNotificationEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + EFI_FTW_DEVICE *FtwDevice; + + // + // Just return to avoid installing FaultTolerantWriteProtocol again + // if Fault Tolerant Write protocol has been installed. + // + Status = gBS->LocateProtocol ( + &gEfiFaultTolerantWriteProtocolGuid, + NULL, + (VOID **) &FtwProtocol + ); + if (!EFI_ERROR (Status)) { + return ; + } + + // + // Found proper FVB protocol and initialize FtwDevice for protocol installation + // + FtwDevice = (EFI_FTW_DEVICE *)Context; + Status = InitFtwProtocol (FtwDevice); + if (EFI_ERROR(Status)) { + return ; + } + + // + // Install protocol interface + // + Status = gBS->InstallProtocolInterface ( + &FtwDevice->Handle, + &gEfiFaultTolerantWriteProtocolGuid, + EFI_NATIVE_INTERFACE, + &FtwDevice->FtwInstance + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (Status); + + return; +} + + +/** + This function is the entry point of the Fault Tolerant Write driver. + + @param[in] ImageHandle A handle for the image that is initializing this driver + @param[in] SystemTable A pointer to the EFI system table + + @retval EFI_SUCCESS The initialization finished successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +EFIAPI +FaultTolerantWriteInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + + FtwDevice = NULL; + + // + // Allocate private data structure for FTW protocol and do some initialization + // + Status = InitFtwDevice (&FtwDevice); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Register FvbNotificationEvent () notify function. + // + EfiCreateProtocolNotifyEvent ( + &gEfiFirmwareVolumeBlockProtocolGuid, + TPL_CALLBACK, + FvbNotificationEvent, + (VOID *)FtwDevice, + &mFvbRegistration + ); + + return EFI_SUCCESS; +} + +/** + Internal implementation of CRC32. Depending on the execution context + (traditional SMM or DXE vs standalone MM), this function is implemented + via a call to the CalculateCrc32 () boot service, or via a library + call. + + If Buffer is NULL, then ASSERT(). + If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param[in] Buffer A pointer to the buffer on which the 32-bit CRC is to be computed. + @param[in] Length The number of bytes in the buffer Data. + + @retval Crc32 The 32-bit CRC was computed for the data buffer. + +**/ +UINT32 +FtwCalculateCrc32 ( + IN VOID *Buffer, + IN UINTN Length + ) +{ + EFI_STATUS Status; + UINT32 ReturnValue; + + Status = gBS->CalculateCrc32 (Buffer, Length, &ReturnValue); + ASSERT_EFI_ERROR (Status); + + return ReturnValue; +} diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf new file mode 100644 index 000000000..96165614d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf @@ -0,0 +1,86 @@ +## @file +# Fault Tolerant Write Dxe Driver. +# +# This driver installs Fault Tolerant Write (FTW) protocol, +# which provides fault tolerant write capability for block devices. +# Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FaultTolerantWriteDxe + MODULE_UNI_FILE = FaultTolerantWriteDxe.uni + FILE_GUID = FE5CEA76-4F72-49e8-986F-2CD899DFFE5D + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FaultTolerantWriteInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + FtwMisc.c + UpdateWorkingBlock.c + FaultTolerantWrite.c + FaultTolerantWriteDxe.c + FaultTolerantWrite.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiDriverEntryPoint + DebugLib + UefiLib + PcdLib + ReportStatusCodeLib + +[Guids] + # + # Signature in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER + # + ## CONSUMES ## GUID + ## PRODUCES ## GUID + gEdkiiWorkingBlockSignatureGuid + +[Protocols] + gEfiSwapAddressRangeProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## SOMETIMES_CONSUMES + ## NOTIFY + ## CONSUMES + gEfiFirmwareVolumeBlockProtocolGuid + gEfiFaultTolerantWriteProtocolGuid ## PRODUCES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ## CONSUMES + +# +# gBS->CalculateCrc32() is consumed in EntryPoint. +# PI spec said: When the DXE Foundation is notified that the EFI_RUNTIME_ARCH_PROTOCOL +# has been installed, then the Boot Service CalculateCrc32() is available. +# So add gEfiRuntimeArchProtocolGuid Depex here. +# +[Depex] + gEfiFirmwareVolumeBlockProtocolGuid AND gEfiRuntimeArchProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + FaultTolerantWriteDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni new file mode 100644 index 000000000..274313558 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.uni @@ -0,0 +1,18 @@ +// /** @file +// Fault Tolerant Write Dxe Driver. +// +// This driver installs Fault Tolerant Write (FTW) protocol, +// which provides fault tolerant write capability for block devices. +// Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access. +// +// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Fault Tolerant Write Dxe Driver." + +#string STR_MODULE_DESCRIPTION #language en-US "Installs Fault Tolerant Write (FTW) protocol, which provides fault tolerant write capability for block devices. Its implementation depends on the full functionality FVB protocol that support read, write/erase flash access." + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni new file mode 100644 index 000000000..b05a88df0 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FaultTolerantWriteDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Fault Tolerant Flash Write DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c new file mode 100644 index 000000000..9612b3948 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c @@ -0,0 +1,636 @@ +/** @file + + This is a simple fault tolerant write driver that is intended to use in the SMM environment. + + This boot service protocol only provides fault tolerant write capability for + block devices. The protocol has internal non-volatile intermediate storage + of the data and private information. It should be able to recover + automatically from a critical fault, such as power failure. + + The implementation uses an FTW (Fault Tolerant Write) Work Space. + This work space is a memory copy of the work space on the Working Block, + the size of the work space is the FTW_WORK_SPACE_SIZE bytes. + + The work space stores each write record as EFI_FTW_RECORD structure. + The spare block stores the write buffer before write to the target block. + + The write record has three states to specify the different phase of write operation. + 1) WRITE_ALLOCATED is that the record is allocated in write space. + The information of write operation is stored in write record structure. + 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup. + 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block. + + This driver operates the data as the whole size of spare block. + It first read the SpareAreaLength data from the target block into the spare memory buffer. + Then copy the write buffer data into the spare memory buffer. + Then write the spare memory buffer into the spare block. + Final copy the data from the spare block to the target block. + + To make this drive work well, the following conditions must be satisfied: + 1. The write NumBytes data must be fit within Spare area. + Offset + NumBytes <= SpareAreaLength + 2. The whole flash range has the same block size. + 3. Working block is an area which contains working space in its last block and has the same size as spare block. + 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 6. Any write data area (SpareAreaLength Area) which the data will be written into must be + in the single one Firmware Volume Block range which FVB protocol is produced on. + 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged. + The spare area must be enough large to store the write data before write them into the target range. + If one of them is not satisfied, FtwWrite may fail. + Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1. + + Caution: This module requires additional review when modified. + This driver need to make sure the CommBuffer is not in the SMRAM range. + +Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include "FaultTolerantWrite.h" +#include "FaultTolerantWriteSmmCommon.h" +#include + +VOID *mFvbRegistration = NULL; +EFI_FTW_DEVICE *mFtwDevice = NULL; + +/// +/// The flag to indicate whether the platform has left the DXE phase of execution. +/// +BOOLEAN mEndOfDxe = FALSE; + +/** + Retrieve the SMM FVB protocol interface by HANDLE. + + @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for + reading, writing, and erasing the target block. + @param[out] FvBlock The interface of SMM FVB protocol + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol. + @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. + +**/ +EFI_STATUS +FtwGetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ) +{ + // + // To get the SMM FVB protocol interface on the handle + // + return gMmst->MmHandleProtocol ( + FvBlockHandle, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + (VOID **) FvBlock + ); +} + +/** + Retrieve the SMM Swap Address Range protocol interface. + + @param[out] SarProtocol The interface of SMM SAR protocol + + @retval EFI_SUCCESS The SMM SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SMM SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +FtwGetSarProtocol ( + OUT VOID **SarProtocol + ) +{ + EFI_STATUS Status; + + // + // Locate Smm Swap Address Range protocol + // + Status = gMmst->MmLocateProtocol ( + &gEfiSmmSwapAddressRangeProtocolGuid, + NULL, + SarProtocol + ); + return Status; +} + +/** + Function returns an array of handles that support the SMM FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support SMM FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No SMM FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + if ((NumberHandles == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + *NumberHandles = 0; + *Buffer = NULL; + Status = gMmst->MmLocateHandle ( + ByProtocol, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + NULL, + &BufferSize, + *Buffer + ); + if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_FOUND; + } + + *Buffer = AllocatePool (BufferSize); + if (*Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gMmst->MmLocateHandle ( + ByProtocol, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + NULL, + &BufferSize, + *Buffer + ); + + *NumberHandles = BufferSize / sizeof(EFI_HANDLE); + if (EFI_ERROR(Status)) { + *NumberHandles = 0; + FreePool (*Buffer); + *Buffer = NULL; + } + + return Status; +} + + +/** + Get the handle of the SMM FVB protocol by the FVB base address and attributes. + + @param[in] Address The base address of SMM FVB protocol. + @param[in] Attributes The attributes of the SMM FVB protocol. + @param[out] SmmFvbHandle The handle of the SMM FVB protocol. + + @retval EFI_SUCCESS The FVB handle is found. + @retval EFI_ABORTED The FVB protocol is not found. + +**/ +EFI_STATUS +GetFvbByAddressAndAttribute ( + IN EFI_PHYSICAL_ADDRESS Address, + IN EFI_FVB_ATTRIBUTES_2 Attributes, + OUT EFI_HANDLE *SmmFvbHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + EFI_PHYSICAL_ADDRESS FvbBaseAddress; + EFI_FVB_ATTRIBUTES_2 FvbAttributes; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + + HandleBuffer = NULL; + + // + // Locate all handles of SMM Fvb protocol. + // + Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + // + // Find the proper SMM Fvb handle by the address and attributes. + // + for (Index = 0; Index < HandleCount; Index++) { + Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb); + if (EFI_ERROR (Status)) { + break; + } + // + // Compare the address. + // + Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); + if (EFI_ERROR (Status)) { + continue; + } + if (Address != FvbBaseAddress) { + continue; + } + + // + // Compare the attribute. + // + Status = Fvb->GetAttributes (Fvb, &FvbAttributes); + if (EFI_ERROR (Status)) { + continue; + } + if (Attributes != FvbAttributes) { + continue; + } + + // + // Found the proper FVB handle. + // + *SmmFvbHandle = HandleBuffer[Index]; + FreePool (HandleBuffer); + return EFI_SUCCESS; + } + + FreePool (HandleBuffer); + return EFI_ABORTED; +} + +/** + Communication service SMI Handler entry. + + This SMI handler provides services for the fault tolerant write wrapper driver. + + Caution: This function requires additional review when modified. + This driver need to make sure the CommBuffer is not in the SMRAM range. + Also in FTW_FUNCTION_GET_LAST_WRITE case, check SmmFtwGetLastWriteHeader->Data + + SmmFtwGetLastWriteHeader->PrivateDataSize within communication buffer. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] RegisterContext Points to an optional handler context which was specified when the + handler was registered. + @param[in, out] CommBuffer A pointer to a collection of data in memory that will be conveyed + from a non-SMM environment into an SMM environment. + @param[in, out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. + +**/ +EFI_STATUS +EFIAPI +SmmFaultTolerantWriteHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader; + SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *SmmGetMaxBlockSizeHeader; + SMM_FTW_ALLOCATE_HEADER *SmmFtwAllocateHeader; + SMM_FTW_WRITE_HEADER *SmmFtwWriteHeader; + SMM_FTW_RESTART_HEADER *SmmFtwRestartHeader; + SMM_FTW_GET_LAST_WRITE_HEADER *SmmFtwGetLastWriteHeader; + VOID *PrivateData; + EFI_HANDLE SmmFvbHandle; + UINTN InfoSize; + UINTN CommBufferPayloadSize; + UINTN PrivateDataSize; + UINTN Length; + UINTN TempCommBufferSize; + + // + // If input is invalid, stop processing this SMI + // + if (CommBuffer == NULL || CommBufferSize == NULL) { + return EFI_SUCCESS; + } + + TempCommBufferSize = *CommBufferSize; + + if (TempCommBufferSize < SMM_FTW_COMMUNICATE_HEADER_SIZE) { + DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + CommBufferPayloadSize = TempCommBufferSize - SMM_FTW_COMMUNICATE_HEADER_SIZE; + + if (!FtwSmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { + DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer in SMRAM or overflow!\n")); + return EFI_SUCCESS; + } + + SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *)CommBuffer; + + if (mEndOfDxe) { + // + // It will be not safe to expose the operations after End Of Dxe. + // + DEBUG ((EFI_D_ERROR, "SmmFtwHandler: Not safe to do the operation: %x after End Of Dxe, so access denied!\n", SmmFtwFunctionHeader->Function)); + SmmFtwFunctionHeader->ReturnStatus = EFI_ACCESS_DENIED; + return EFI_SUCCESS; + } + + switch (SmmFtwFunctionHeader->Function) { + case FTW_FUNCTION_GET_MAX_BLOCK_SIZE: + if (CommBufferPayloadSize < sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER)) { + DEBUG ((EFI_D_ERROR, "GetMaxBlockSize: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmmGetMaxBlockSizeHeader = (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *) SmmFtwFunctionHeader->Data; + + Status = FtwGetMaxBlockSize ( + &mFtwDevice->FtwInstance, + &SmmGetMaxBlockSizeHeader->BlockSize + ); + break; + + case FTW_FUNCTION_ALLOCATE: + if (CommBufferPayloadSize < sizeof (SMM_FTW_ALLOCATE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Allocate: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmmFtwAllocateHeader = (SMM_FTW_ALLOCATE_HEADER *) SmmFtwFunctionHeader->Data; + Status = FtwAllocate ( + &mFtwDevice->FtwInstance, + &SmmFtwAllocateHeader->CallerId, + SmmFtwAllocateHeader->PrivateDataSize, + SmmFtwAllocateHeader->NumberOfWrites + ); + break; + + case FTW_FUNCTION_WRITE: + if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) { + DEBUG ((EFI_D_ERROR, "Write: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmmFtwWriteHeader = (SMM_FTW_WRITE_HEADER *) SmmFtwFunctionHeader->Data; + Length = SmmFtwWriteHeader->Length; + PrivateDataSize = SmmFtwWriteHeader->PrivateDataSize; + if (((UINTN)(~0) - Length < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) || + ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length)) { + // + // Prevent InfoSize overflow + // + Status = EFI_ACCESS_DENIED; + break; + } + InfoSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length + PrivateDataSize; + + // + // SMRAM range check already covered before + // + if (InfoSize > CommBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "Write: Data size exceed communication buffer size limit!\n")); + Status = EFI_ACCESS_DENIED; + break; + } + + if (PrivateDataSize == 0) { + PrivateData = NULL; + } else { + PrivateData = (VOID *)&SmmFtwWriteHeader->Data[Length]; + } + Status = GetFvbByAddressAndAttribute ( + SmmFtwWriteHeader->FvbBaseAddress, + SmmFtwWriteHeader->FvbAttributes, + &SmmFvbHandle + ); + if (!EFI_ERROR (Status)) { + // + // The SpeculationBarrier() call here is to ensure the previous + // range/content checks for the CommBuffer have been completed before + // calling into FtwWrite(). + // + SpeculationBarrier (); + Status = FtwWrite( + &mFtwDevice->FtwInstance, + SmmFtwWriteHeader->Lba, + SmmFtwWriteHeader->Offset, + Length, + PrivateData, + SmmFvbHandle, + SmmFtwWriteHeader->Data + ); + } + break; + + case FTW_FUNCTION_RESTART: + if (CommBufferPayloadSize < sizeof (SMM_FTW_RESTART_HEADER)) { + DEBUG ((EFI_D_ERROR, "Restart: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmmFtwRestartHeader = (SMM_FTW_RESTART_HEADER *) SmmFtwFunctionHeader->Data; + Status = GetFvbByAddressAndAttribute ( + SmmFtwRestartHeader->FvbBaseAddress, + SmmFtwRestartHeader->FvbAttributes, + &SmmFvbHandle + ); + if (!EFI_ERROR (Status)) { + Status = FtwRestart (&mFtwDevice->FtwInstance, SmmFvbHandle); + } + break; + + case FTW_FUNCTION_ABORT: + Status = FtwAbort (&mFtwDevice->FtwInstance); + break; + + case FTW_FUNCTION_GET_LAST_WRITE: + if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)) { + DEBUG ((EFI_D_ERROR, "GetLastWrite: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + SmmFtwGetLastWriteHeader = (SMM_FTW_GET_LAST_WRITE_HEADER *) SmmFtwFunctionHeader->Data; + PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize; + if ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)){ + // + // Prevent InfoSize overflow + // + Status = EFI_ACCESS_DENIED; + break; + } + InfoSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + PrivateDataSize; + + // + // SMRAM range check already covered before + // + if (InfoSize > CommBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n")); + Status = EFI_ACCESS_DENIED; + break; + } + + Status = FtwGetLastWrite ( + &mFtwDevice->FtwInstance, + &SmmFtwGetLastWriteHeader->CallerId, + &SmmFtwGetLastWriteHeader->Lba, + &SmmFtwGetLastWriteHeader->Offset, + &SmmFtwGetLastWriteHeader->Length, + &PrivateDataSize, + (VOID *)SmmFtwGetLastWriteHeader->Data, + &SmmFtwGetLastWriteHeader->Complete + ); + SmmFtwGetLastWriteHeader->PrivateDataSize = PrivateDataSize; + break; + + default: + Status = EFI_UNSUPPORTED; + } + + SmmFtwFunctionHeader->ReturnStatus = Status; + + return EFI_SUCCESS; +} + + +/** + SMM Firmware Volume Block Protocol notification event handler. + + @param[in] Protocol Points to the protocol's unique identifier + @param[in] Interface Points to the interface instance + @param[in] Handle The handle on which the interface was installed + + @retval EFI_SUCCESS SmmEventCallback runs successfully + + **/ +EFI_STATUS +EFIAPI +FvbNotificationEvent ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + EFI_HANDLE SmmFtwHandle; + + // + // Just return to avoid install SMM FaultTolerantWriteProtocol again + // if SMM Fault Tolerant Write protocol had been installed. + // + Status = gMmst->MmLocateProtocol ( + &gEfiSmmFaultTolerantWriteProtocolGuid, + NULL, + (VOID **) &FtwProtocol + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + // + // Found proper FVB protocol and initialize FtwDevice for protocol installation + // + Status = InitFtwProtocol (mFtwDevice); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Install protocol interface + // + Status = gMmst->MmInstallProtocolInterface ( + &mFtwDevice->Handle, + &gEfiSmmFaultTolerantWriteProtocolGuid, + EFI_NATIVE_INTERFACE, + &mFtwDevice->FtwInstance + ); + ASSERT_EFI_ERROR (Status); + + /// + /// Register SMM FTW SMI handler + /// + Status = gMmst->MmiHandlerRegister (SmmFaultTolerantWriteHandler, &gEfiSmmFaultTolerantWriteProtocolGuid, &SmmFtwHandle); + ASSERT_EFI_ERROR (Status); + + // + // Notify the Ftw wrapper driver SMM Ftw is ready + // + FtwNotifySmmReady (); + + return EFI_SUCCESS; +} + +/** + SMM END_OF_DXE protocol notification event handler. + + @param Protocol Points to the protocol's unique identifier + @param Interface Points to the interface instance + @param Handle The handle on which the interface was installed + + @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully + +**/ +EFI_STATUS +EFIAPI +MmEndOfDxeCallback ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + mEndOfDxe = TRUE; + return EFI_SUCCESS; +} + +/** + Shared entry point of the module + + @retval EFI_SUCCESS The initialization finished successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist +**/ +EFI_STATUS +MmFaultTolerantWriteInitialize ( + VOID + ) +{ + EFI_STATUS Status; + VOID *MmEndOfDxeRegistration; + + // + // Allocate private data structure for SMM FTW protocol and do some initialization + // + Status = InitFtwDevice (&mFtwDevice); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function. + // + Status = gMmst->MmRegisterProtocolNotify ( + &gEfiMmEndOfDxeProtocolGuid, + MmEndOfDxeCallback, + &MmEndOfDxeRegistration + ); + ASSERT_EFI_ERROR (Status); + + // + // Register FvbNotificationEvent () notify function. + // + Status = gMmst->MmRegisterProtocolNotify ( + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + FvbNotificationEvent, + &mFvbRegistration + ); + ASSERT_EFI_ERROR (Status); + + FvbNotificationEvent (NULL, NULL, NULL); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf new file mode 100644 index 000000000..8cc602847 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf @@ -0,0 +1,95 @@ +## @file +# Fault Tolerant Write Smm Driver. +# +# This driver installs SMM Fault Tolerant Write (FTW) protocol, which provides fault +# tolerant write capability in SMM environment for block devices. Its implementation +# depends on the full functionality SMM FVB protocol that support read, write/erase +# flash access. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmFaultTolerantWriteDxe + MODULE_UNI_FILE = SmmFaultTolerantWriteDxe.uni + FILE_GUID = 470CB248-E8AC-473c-BB4F-81069A1FE6FD + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SmmFaultTolerantWriteInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + FtwMisc.c + UpdateWorkingBlock.c + FaultTolerantWrite.c + FaultTolerantWriteTraditionalMm.c + FaultTolerantWriteSmm.c + FaultTolerantWrite.h + FaultTolerantWriteSmmCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MmServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiDriverEntryPoint + DebugLib + UefiLib + PcdLib + ReportStatusCodeLib + SmmMemLib + BaseLib + +[Guids] + # + # Signature in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER + # + ## CONSUMES ## GUID + ## PRODUCES ## GUID + gEdkiiWorkingBlockSignatureGuid + +[Protocols] + gEfiSmmSwapAddressRangeProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## SOMETIMES_CONSUMES + ## NOTIFY + ## CONSUMES + gEfiSmmFirmwareVolumeBlockProtocolGuid + ## PRODUCES + ## UNDEFINED # SmiHandlerRegister + gEfiSmmFaultTolerantWriteProtocolGuid + gEfiMmEndOfDxeProtocolGuid ## CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ## CONSUMES + +# +# gBS->CalculateCrc32() is consumed in EntryPoint. +# PI spec said: When the DXE Foundation is notified that the EFI_RUNTIME_ARCH_PROTOCOL +# has been installed, then the Boot Service CalculateCrc32() is available. +# So add gEfiRuntimeArchProtocolGuid Depex here. +# +[Depex] + gEfiSmmFirmwareVolumeBlockProtocolGuid AND gEfiRuntimeArchProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + SmmFaultTolerantWriteDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h new file mode 100644 index 000000000..5f0eec050 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmCommon.h @@ -0,0 +1,113 @@ +/** @file + + The common header file for SMM FTW module and SMM FTW DXE Module. + +Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __SMM_FTW_COMMON_H__ +#define __SMM_FTW_COMMON_H__ + +#include +#include + +#define FTW_FUNCTION_GET_MAX_BLOCK_SIZE 1 +#define FTW_FUNCTION_ALLOCATE 2 +#define FTW_FUNCTION_WRITE 3 +#define FTW_FUNCTION_RESTART 4 +#define FTW_FUNCTION_ABORT 5 +#define FTW_FUNCTION_GET_LAST_WRITE 6 + +typedef struct { + UINTN Function; + EFI_STATUS ReturnStatus; + UINT8 Data[1]; +} SMM_FTW_COMMUNICATE_FUNCTION_HEADER; + +/// +/// Size of SMM communicate header, without including the payload. +/// +#define SMM_COMMUNICATE_HEADER_SIZE (OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data)) + +/// +/// Size of SMM FTW communicate function header, without including the payload. +/// +#define SMM_FTW_COMMUNICATE_HEADER_SIZE (OFFSET_OF (SMM_FTW_COMMUNICATE_FUNCTION_HEADER, Data)) + +typedef struct { + UINTN BlockSize; +} SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER; + +typedef struct { + EFI_GUID CallerId; + UINTN PrivateDataSize; + UINTN NumberOfWrites; +} SMM_FTW_ALLOCATE_HEADER; + +typedef struct { + EFI_LBA Lba; + UINTN Offset; + UINTN PrivateDataSize; + EFI_PHYSICAL_ADDRESS FvbBaseAddress; + EFI_FVB_ATTRIBUTES_2 FvbAttributes; + UINTN Length; + UINT8 Data[1]; +} SMM_FTW_WRITE_HEADER; + +typedef struct { + EFI_PHYSICAL_ADDRESS FvbBaseAddress; + EFI_FVB_ATTRIBUTES_2 FvbAttributes; +} SMM_FTW_RESTART_HEADER; + +typedef struct { + EFI_GUID CallerId; + EFI_LBA Lba; + UINTN Offset; + UINTN Length; + UINTN PrivateDataSize; + BOOLEAN Complete; + UINT8 Data[1]; +} SMM_FTW_GET_LAST_WRITE_HEADER; + +/** + Shared entry point of the module. + + @retval EFI_SUCCESS The initialization finished successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +MmFaultTolerantWriteInitialize ( + VOID + ); + +/** + This function checks if the buffer is valid per processor architecture and + does not overlap with SMRAM. + + @param Buffer The buffer start address to be checked. + @param Length The buffer length to be checked. + + @retval TRUE This buffer is valid per processor architecture and does not + overlap with SMRAM. + @retval FALSE This buffer is not valid per processor architecture or overlaps + with SMRAM. +**/ +BOOLEAN +FtwSmmIsBufferOutsideSmmValid ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ); + +/** + Notify the system that the SMM FTW driver is ready. +**/ +VOID +FtwNotifySmmReady ( + VOID + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c new file mode 100644 index 000000000..24c209502 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.c @@ -0,0 +1,559 @@ +/** @file + + Implement the Fault Tolerant Write (FTW) protocol based on SMM FTW + module. + +Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FaultTolerantWriteSmmDxe.h" + +EFI_HANDLE mHandle = NULL; +EFI_MM_COMMUNICATION2_PROTOCOL *mMmCommunication2 = NULL; +UINTN mPrivateDataSize = 0; + +EFI_FAULT_TOLERANT_WRITE_PROTOCOL mFaultTolerantWriteDriver = { + FtwGetMaxBlockSize, + FtwAllocate, + FtwWrite, + FtwRestart, + FtwAbort, + FtwGetLastWrite +}; + +/** + Initialize the communicate buffer using DataSize and Function number. + + @param[out] CommunicateBuffer The communicate buffer. Caller should free it after use. + @param[out] DataPtr Points to the data in the communicate buffer. Caller should not free it. + @param[in] DataSize The payload size. + @param[in] Function The function number used to initialize the communicate header. + +**/ +VOID +InitCommunicateBuffer ( + OUT VOID **CommunicateBuffer, + OUT VOID **DataPtr, + IN UINTN DataSize, + IN UINTN Function + ) +{ + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader; + + // + // The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE + DataSize. + // + SmmCommunicateHeader = AllocateZeroPool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE); + ASSERT (SmmCommunicateHeader != NULL); + + // + // Prepare data buffer. + // + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFaultTolerantWriteProtocolGuid); + SmmCommunicateHeader->MessageLength = DataSize + SMM_FTW_COMMUNICATE_HEADER_SIZE; + + SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data; + SmmFtwFunctionHeader->Function = Function; + + *CommunicateBuffer = SmmCommunicateHeader; + if (DataPtr != NULL) { + *DataPtr = SmmFtwFunctionHeader->Data; + } +} + + +/** + Send the data in communicate buffer to SMI handler and get response. + + @param[in, out] SmmCommunicateHeader The communicate buffer. + @param[in] DataSize The payload size. + +**/ +EFI_STATUS +SendCommunicateBuffer ( + IN OUT EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader, + IN UINTN DataSize + ) +{ + EFI_STATUS Status; + UINTN CommSize; + SMM_FTW_COMMUNICATE_FUNCTION_HEADER *SmmFtwFunctionHeader; + + CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE; + Status = mMmCommunication2->Communicate (mMmCommunication2, + SmmCommunicateHeader, + SmmCommunicateHeader, + &CommSize); + ASSERT_EFI_ERROR (Status); + + SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data; + return SmmFtwFunctionHeader->ReturnStatus; +} + + +/** + Get the FvbBaseAddress and FvbAttributes from the FVB handle FvbHandle. + + @param[in] FvbHandle The handle of FVB protocol that provides services. + @param[out] FvbBaseAddress The base address of the FVB attached with FvbHandle. + @param[out] FvbAttributes The attributes of the FVB attached with FvbHandle. + + @retval EFI_SUCCESS The function completed successfully. + @retval Others The function could not complete successfully. + +**/ +EFI_STATUS +ConvertFvbHandle ( + IN EFI_HANDLE FvbHandle, + OUT EFI_PHYSICAL_ADDRESS *FvbBaseAddress, + OUT EFI_FVB_ATTRIBUTES_2 *FvbAttributes + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + + Status = gBS->HandleProtocol (FvbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **) &Fvb); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Fvb->GetPhysicalAddress (Fvb, FvbBaseAddress); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Fvb->GetAttributes (Fvb, FvbAttributes); + return Status; +} + + +/** + Get the size of the largest block that can be updated in a fault-tolerant manner. + + @param[in] This Indicates a pointer to the calling context. + @param[out] BlockSize A pointer to a caller-allocated UINTN that is + updated to indicate the size of the largest block + that can be updated. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +EFIAPI +FtwGetMaxBlockSize ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT UINTN *BlockSize + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *SmmFtwBlockSizeHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER); + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwBlockSizeHeader, PayloadSize, FTW_FUNCTION_GET_MAX_BLOCK_SIZE); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + + // + // Get data from SMM + // + *BlockSize = SmmFtwBlockSizeHeader->BlockSize; + FreePool (SmmCommunicateHeader); + + return Status; +} + + +/** + Allocates space for the protocol to maintain information about writes. + Since writes must be completed in a fault-tolerant manner and multiple + writes require more resources to be successful, this function + enables the protocol to ensure that enough space exists to track + information about upcoming writes. + + @param[in] This A pointer to the calling context. + @param[in] CallerId The GUID identifying the write. + @param[in] PrivateDataSize The size of the caller's private data that must be + recorded for each write. + @param[in] NumberOfWrites The number of fault tolerant block writes that will + need to occur. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED Not all allocated writes have been completed. All + writes must be completed or aborted before another + fault tolerant write can occur. + +**/ +EFI_STATUS +EFIAPI +FtwAllocate ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_GUID *CallerId, + IN UINTN PrivateDataSize, + IN UINTN NumberOfWrites + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_ALLOCATE_HEADER *SmmFtwAllocateHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = sizeof (SMM_FTW_ALLOCATE_HEADER); + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwAllocateHeader, PayloadSize, FTW_FUNCTION_ALLOCATE); + CopyGuid (&SmmFtwAllocateHeader->CallerId, CallerId); + SmmFtwAllocateHeader->PrivateDataSize = PrivateDataSize; + SmmFtwAllocateHeader->NumberOfWrites = NumberOfWrites; + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + if (!EFI_ERROR( Status)) { + mPrivateDataSize = PrivateDataSize; + } + + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Starts a target block update. This records information about the write + in fault tolerant storage, and will complete the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param[in] This The calling context. + @param[in] Lba The logical block address of the target block. + @param[in] Offset The offset within the target block to place the + data. + @param[in] Length The number of bytes to write to the target block. + @param[in] PrivateData A pointer to private data that the caller requires + to complete any pending writes in the event of a + fault. + @param[in] FvBlockHandle The handle of FVB protocol that provides services + for reading, writing, and erasing the target block. + @param[in] Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_BAD_BUFFER_SIZE The write would span a block boundary, which is not + a valid action. + @retval EFI_ACCESS_DENIED No writes have been allocated. + @retval EFI_NOT_READY The last write has not been completed. Restart() + must be called to complete it. + +**/ +EFI_STATUS +EFIAPI +FtwWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN VOID *PrivateData, + IN EFI_HANDLE FvBlockHandle, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_WRITE_HEADER *SmmFtwWriteHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length; + if (PrivateData != NULL) { + // + // The private data buffer size should be the same one in FtwAllocate API. + // + PayloadSize += mPrivateDataSize; + } + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwWriteHeader, PayloadSize, FTW_FUNCTION_WRITE); + + // + // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address + // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data. + // + Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwWriteHeader->FvbBaseAddress, &SmmFtwWriteHeader->FvbAttributes); + if (EFI_ERROR (Status)) { + FreePool (SmmCommunicateHeader); + return EFI_ABORTED; + } + + SmmFtwWriteHeader->Lba = Lba; + SmmFtwWriteHeader->Offset = Offset; + SmmFtwWriteHeader->Length = Length; + CopyMem (SmmFtwWriteHeader->Data, Buffer, Length); + if (PrivateData == NULL) { + SmmFtwWriteHeader->PrivateDataSize = 0; + } else { + SmmFtwWriteHeader->PrivateDataSize = mPrivateDataSize; + CopyMem (&SmmFtwWriteHeader->Data[Length], PrivateData, mPrivateDataSize); + } + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Restarts a previously interrupted write. The caller must provide the + block protocol needed to complete the interrupted write. + + @param[in] This The calling context. + @param[in] FvBlockHandle The handle of FVB protocol that provides services. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED No pending writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwRestart ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_HANDLE FvBlockHandle + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_RESTART_HEADER *SmmFtwRestartHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = sizeof (SMM_FTW_RESTART_HEADER); + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwRestartHeader, PayloadSize, FTW_FUNCTION_RESTART); + + // + // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address + // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data. + // + Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwRestartHeader->FvbBaseAddress, &SmmFtwRestartHeader->FvbAttributes); + if (EFI_ERROR (Status)) { + FreePool (SmmCommunicateHeader); + return EFI_ABORTED; + } + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Aborts all previously allocated writes. + + @param[in] This The calling context. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwAbort ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + + // + // Initialize the communicate buffer. + // + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, NULL, 0, FTW_FUNCTION_ABORT); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, 0); + + FreePool (SmmCommunicateHeader); + return Status; +} + + +/** + Starts a target block update. This function records information about the write + in fault-tolerant storage and completes the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param[in] This Indicates a pointer to the calling context. + @param[out] CallerId The GUID identifying the last write. + @param[out] Lba The logical block address of the last write. + @param[out] Offset The offset within the block of the last write. + @param[out] Length The length of the last write. + @param[in, out] PrivateDataSize On input, the size of the PrivateData buffer. On + output, the size of the private data stored for + this write. + @param[out] PrivateData A pointer to a buffer. The function will copy + PrivateDataSize bytes from the private data stored + for this write. + @param[out] Complete A Boolean value with TRUE indicating that the write + was completed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwGetLastWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT EFI_GUID *CallerId, + OUT EFI_LBA *Lba, + OUT UINTN *Offset, + OUT UINTN *Length, + IN OUT UINTN *PrivateDataSize, + OUT VOID *PrivateData, + OUT BOOLEAN *Complete + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_FTW_GET_LAST_WRITE_HEADER *SmmFtwGetLastWriteHeader; + + // + // Initialize the communicate buffer. + // + PayloadSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + *PrivateDataSize; + InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwGetLastWriteHeader, PayloadSize, FTW_FUNCTION_GET_LAST_WRITE); + SmmFtwGetLastWriteHeader->PrivateDataSize = *PrivateDataSize; + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize); + + // + // Get data from SMM + // + *PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize; + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + *Lba = SmmFtwGetLastWriteHeader->Lba; + *Offset = SmmFtwGetLastWriteHeader->Offset; + *Length = SmmFtwGetLastWriteHeader->Length; + *Complete = SmmFtwGetLastWriteHeader->Complete; + CopyGuid (CallerId, &SmmFtwGetLastWriteHeader->CallerId); + if (Status == EFI_SUCCESS) { + CopyMem (PrivateData, SmmFtwGetLastWriteHeader->Data, *PrivateDataSize); + } + } else if (Status == EFI_NOT_FOUND) { + *Complete = SmmFtwGetLastWriteHeader->Complete; + } + + FreePool (SmmCommunicateHeader); + return Status; +} + +/** + SMM Fault Tolerant Write Protocol notification event handler. + + Install Fault Tolerant Write Protocol. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +SmmFtwReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + + // + // Just return to avoid install SMM FaultTolerantWriteProtocol again + // if Fault Tolerant Write protocol had been installed. + // + Status = gBS->LocateProtocol (&gEfiFaultTolerantWriteProtocolGuid, NULL, (VOID **)&FtwProtocol); + if (!EFI_ERROR (Status)) { + return; + } + + Status = gBS->LocateProtocol (&gEfiMmCommunication2ProtocolGuid, NULL, (VOID **) &mMmCommunication2); + ASSERT_EFI_ERROR (Status); + + // + // Install protocol interface + // + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiFaultTolerantWriteProtocolGuid, + EFI_NATIVE_INTERFACE, + &mFaultTolerantWriteDriver + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (Status); +} + + +/** + The driver entry point for Fault Tolerant Write driver. + + The function does the necessary initialization work. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI system table. + + @retval EFI_SUCCESS This funtion always return EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +FaultTolerantWriteSmmInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *SmmFtwRegistration; + + // + // Smm FTW driver is ready + // + EfiCreateProtocolNotifyEvent ( + &gEfiSmmFaultTolerantWriteProtocolGuid, + TPL_CALLBACK, + SmmFtwReady, + NULL, + &SmmFtwRegistration + ); + + return EFI_SUCCESS; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h new file mode 100644 index 000000000..a15644abd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.h @@ -0,0 +1,196 @@ +/** @file + + The internal header file includes the common header files, defines + internal structure and functions used by FTW module. + +Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __SMM_FTW_DXE_H__ +#define __SMM_FTW_DXE_H__ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "FaultTolerantWriteSmmCommon.h" + +/** + Get the size of the largest block that can be updated in a fault-tolerant manner. + + @param[in] This Indicates a pointer to the calling context. + @param[out] BlockSize A pointer to a caller-allocated UINTN that is + updated to indicate the size of the largest block + that can be updated. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +EFIAPI +FtwGetMaxBlockSize ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT UINTN *BlockSize + ); + + +/** + Allocates space for the protocol to maintain information about writes. + Since writes must be completed in a fault-tolerant manner and multiple + writes require more resources to be successful, this function + enables the protocol to ensure that enough space exists to track + information about upcoming writes. + + @param[in] This A pointer to the calling context. + @param[in] CallerId The GUID identifying the write. + @param[in] PrivateDataSize The size of the caller's private data that must be + recorded for each write. + @param[in] NumberOfWrites The number of fault tolerant block writes that will + need to occur. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED Not all allocated writes have been completed. All + writes must be completed or aborted before another + fault tolerant write can occur. + +**/ +EFI_STATUS +EFIAPI +FtwAllocate ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_GUID *CallerId, + IN UINTN PrivateDataSize, + IN UINTN NumberOfWrites + ); + + +/** + Starts a target block update. This records information about the write + in fault tolerant storage, and will complete the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param[in] This The calling context. + @param[in] Lba The logical block address of the target block. + @param[in] Offset The offset within the target block to place the + data. + @param[in] Length The number of bytes to write to the target block. + @param[in] PrivateData A pointer to private data that the caller requires + to complete any pending writes in the event of a + fault. + @param[in] FvBlockHandle The handle of FVB protocol that provides services + for reading, writing, and erasing the target block. + @param[in] Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_BAD_BUFFER_SIZE The write would span a block boundary, which is not + a valid action. + @retval EFI_ACCESS_DENIED No writes have been allocated. + @retval EFI_NOT_READY The last write has not been completed. Restart() + must be called to complete it. + +**/ +EFI_STATUS +EFIAPI +FtwWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN VOID *PrivateData, + IN EFI_HANDLE FvBlockHandle, + IN VOID *Buffer + ); + + +/** + Restarts a previously interrupted write. The caller must provide the + block protocol needed to complete the interrupted write. + + @param[in] This The calling context. + @param[in] FvBlockHandle The handle of FVB protocol that provides services. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_ACCESS_DENIED No pending writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwRestart ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + IN EFI_HANDLE FvBlockHandle + ); + + +/** + Aborts all previously allocated writes. + + @param This The calling context. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwAbort ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This + ); + + +/** + Starts a target block update. This function records information about the write + in fault-tolerant storage and completes the write in a recoverable + manner, ensuring at all times that either the original contents or + the modified contents are available. + + @param[in] This Indicates a pointer to the calling context. + @param[out] CallerId The GUID identifying the last write. + @param[out] Lba The logical block address of the last write. + @param[out] Offset The offset within the block of the last write. + @param[out] Length The length of the last write. + @param[in, out] PrivateDataSize On input, the size of the PrivateData buffer. On + output, the size of the private data stored for + this write. + @param[out] PrivateData A pointer to a buffer. The function will copy + PrivateDataSize bytes from the private data stored + for this write. + @param[out] Complete A Boolean value with TRUE indicating that the write + was completed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + @retval EFI_NOT_FOUND No allocated writes exist. + +**/ +EFI_STATUS +EFIAPI +FtwGetLastWrite ( + IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This, + OUT EFI_GUID *CallerId, + OUT EFI_LBA *Lba, + OUT UINTN *Offset, + OUT UINTN *Length, + IN OUT UINTN *PrivateDataSize, + OUT VOID *PrivateData, + OUT BOOLEAN *Complete + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf new file mode 100644 index 000000000..f0dd82965 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf @@ -0,0 +1,59 @@ +## @file +# This module is the Runtime DXE part corresponding to SMM Fault Tolerant Write (FTW) module. +# +# It installs FTW protocol and works with SMM FTW module together. +# The FTW protocol will not work after End Of Dxe because it will be not safe to expose +# the related operations in SMM handler in SMM FTW module. You can use the FTW protocol +# before End Of Dxe or use FaultTolerantWriteDxe module instead if you really want to. +# +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FaultTolerantWriteSmmDxe + MODULE_UNI_FILE = FaultTolerantWriteSmmDxe.uni + FILE_GUID = 98948C4A-70F2-4035-8E9F-5927493CFC07 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FaultTolerantWriteSmmInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + FaultTolerantWriteSmmDxe.c + FaultTolerantWriteSmmDxe.h + FaultTolerantWriteSmmCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + UefiBootServicesTableLib + DebugLib + DxeServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEfiFaultTolerantWriteProtocolGuid ## PRODUCES + gEfiMmCommunication2ProtocolGuid ## CONSUMES + ## NOTIFY + ## UNDEFINED # Used to do smm communication + ## CONSUMES + gEfiSmmFaultTolerantWriteProtocolGuid + gEfiFirmwareVolumeBlockProtocolGuid ## CONSUMES + +[Depex] + gEfiMmCommunication2ProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + FaultTolerantWriteSmmDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni new file mode 100644 index 000000000..84ee45ccb --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.uni @@ -0,0 +1,19 @@ +// /** @file +// This module is the Runtime DXE part corresponding to SMM Fault Tolerant Write (FTW) module. +// +// It installs FTW protocol and works with SMM FTW module together. +// The FTW protocol will not work after End Of Dxe because it will be not safe to expose +// the related operations in SMM handler in SMM FTW module. You can use the FTW protocol +// before End Of Dxe or use FaultTolerantWriteDxe module instead if you really want to. +// +// Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "The Runtime DXE part corresponding to the SMM Fault Tolerant Write (FTW) module" + +#string STR_MODULE_DESCRIPTION #language en-US "It installs FTW protocol and works with SMM FTW module together." + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni new file mode 100644 index 000000000..5807b10f3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FaultTolerantWriteSmmDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Fault Tolerant Flash Write SMM/DXE Bridge Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.c new file mode 100644 index 000000000..5af49e040 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.c @@ -0,0 +1,90 @@ +/** @file + + Parts of the SMM/MM implementation that are specific to standalone MM + +Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include "FaultTolerantWrite.h" +#include "FaultTolerantWriteSmmCommon.h" + +/** + This function checks if the buffer is valid per processor architecture and + does not overlap with SMRAM. + + @param Buffer The buffer start address to be checked. + @param Length The buffer length to be checked. + + @retval TRUE This buffer is valid per processor architecture and does not + overlap with SMRAM. + @retval FALSE This buffer is not valid per processor architecture or overlaps + with SMRAM. +**/ +BOOLEAN +FtwSmmIsBufferOutsideSmmValid ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ) +{ + return TRUE; +} + +/** + Internal implementation of CRC32. Depending on the execution context + (standalone SMM or DXE vs standalone MM), this function is implemented + via a call to the CalculateCrc32 () boot service, or via a library + call. + + If Buffer is NULL, then ASSERT(). + If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param[in] Buffer A pointer to the buffer on which the 32-bit CRC is to be computed. + @param[in] Length The number of bytes in the buffer Data. + + @retval Crc32 The 32-bit CRC was computed for the data buffer. + +**/ +UINT32 +FtwCalculateCrc32 ( + IN VOID *Buffer, + IN UINTN Length + ) +{ + return CalculateCrc32 (Buffer, Length); +} + +/** + Notify the system that the SMM FTW driver is ready. +**/ +VOID +FtwNotifySmmReady ( + VOID + ) +{ +} + +/** + This function is the entry point of the Fault Tolerant Write driver. + + @param[in] ImageHandle A handle for the image that is initializing this driver + @param[in] MmSystemTable A pointer to the MM system table + + @retval EFI_SUCCESS The initialization finished successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +EFIAPI +StandaloneMmFaultTolerantWriteInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *MmSystemTable + ) +{ + return MmFaultTolerantWriteInitialize (); +} diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.inf b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.inf new file mode 100644 index 000000000..d0fab7d94 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.inf @@ -0,0 +1,85 @@ + ## @file +# Fault Tolerant Write Smm Driver. +# +# This driver installs SMM Fault Tolerant Write (FTW) protocol, which provides fault +# tolerant write capability in SMM environment for block devices. Its implementation +# depends on the full functionality SMM FVB protocol that support read, write/erase +# flash access. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = FaultTolerantWriteStandaloneMm + FILE_GUID = 3aade4ec-63cc-4a48-a928-5a374dd463eb + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + ENTRY_POINT = StandaloneMmFaultTolerantWriteInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 +# + +[Sources] + FtwMisc.c + UpdateWorkingBlock.c + FaultTolerantWrite.c + FaultTolerantWriteStandaloneMm.c + FaultTolerantWriteSmm.c + FaultTolerantWrite.h + FaultTolerantWriteSmmCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + StandaloneMmPkg/StandaloneMmPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + MmServicesTableLib + PcdLib + ReportStatusCodeLib + StandaloneMmDriverEntryPoint + +[Guids] + # + # Signature in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER + # + ## CONSUMES ## GUID + ## PRODUCES ## GUID + gEdkiiWorkingBlockSignatureGuid + +[Protocols] + gEfiSmmSwapAddressRangeProtocolGuid | gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## SOMETIMES_CONSUMES + ## NOTIFY + ## CONSUMES + gEfiSmmFirmwareVolumeBlockProtocolGuid + ## PRODUCES + ## UNDEFINED # SmiHandlerRegister + gEfiSmmFaultTolerantWriteProtocolGuid + gEfiMmEndOfDxeProtocolGuid ## CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ## CONSUMES + +[Depex] + TRUE diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteTraditionalMm.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteTraditionalMm.c new file mode 100644 index 000000000..10e56e9a3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteTraditionalMm.c @@ -0,0 +1,108 @@ +/** @file + + Parts of the SMM/MM implementation that are specific to traditional MM + +Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2018, Linaro, Ltd. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include "FaultTolerantWrite.h" +#include "FaultTolerantWriteSmmCommon.h" + +/** + This function checks if the buffer is valid per processor architecture and + does not overlap with SMRAM. + + @param Buffer The buffer start address to be checked. + @param Length The buffer length to be checked. + + @retval TRUE This buffer is valid per processor architecture and does not + overlap with SMRAM. + @retval FALSE This buffer is not valid per processor architecture or overlaps + with SMRAM. +**/ +BOOLEAN +FtwSmmIsBufferOutsideSmmValid ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ) +{ + return SmmIsBufferOutsideSmmValid (Buffer, Length); +} + +/** + Internal implementation of CRC32. Depending on the execution context + (traditional SMM or DXE vs standalone MM), this function is implemented + via a call to the CalculateCrc32 () boot service, or via a library + call. + + If Buffer is NULL, then ASSERT(). + If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param[in] Buffer A pointer to the buffer on which the 32-bit CRC is + to be computed. + @param[in] Length The number of bytes in the buffer Data. + + @retval Crc32 The 32-bit CRC was computed for the data buffer. + +**/ +UINT32 +FtwCalculateCrc32 ( + IN VOID *Buffer, + IN UINTN Length + ) +{ + EFI_STATUS Status; + UINT32 ReturnValue; + + Status = gBS->CalculateCrc32 (Buffer, Length, &ReturnValue); + ASSERT_EFI_ERROR (Status); + + return ReturnValue; +} + +/** + Notify the system that the SMM FTW driver is ready. +**/ +VOID +FtwNotifySmmReady ( + VOID + ) +{ + EFI_HANDLE FtwHandle; + EFI_STATUS Status; + + FtwHandle = NULL; + Status = gBS->InstallProtocolInterface ( + &FtwHandle, + &gEfiSmmFaultTolerantWriteProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); +} + +/** + This function is the entry point of the Fault Tolerant Write driver. + + @param[in] ImageHandle A handle for the image that is initializing this driver + @param[in] SystemTable A pointer to the EFI system table + + @retval EFI_SUCCESS The initialization finished successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +EFIAPI +SmmFaultTolerantWriteInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return MmFaultTolerantWriteInitialize (); +} diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c new file mode 100644 index 000000000..ed73f887a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c @@ -0,0 +1,1378 @@ +/** @file + + Internal generic functions to operate flash block. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FaultTolerantWrite.h" + +/** + + Check whether a flash buffer is erased. + + @param Buffer Buffer to check + @param BufferSize Size of the buffer + + @return A BOOLEAN value indicating erased or not. + +**/ +BOOLEAN +IsErasedFlashBuffer ( + IN UINT8 *Buffer, + IN UINTN BufferSize + ) +{ + BOOLEAN IsEmpty; + UINT8 *Ptr; + UINTN Index; + + Ptr = Buffer; + IsEmpty = TRUE; + for (Index = 0; Index < BufferSize; Index += 1) { + if (*Ptr++ != FTW_ERASED_BYTE) { + IsEmpty = FALSE; + break; + } + } + + return IsEmpty; +} + +/** + To erase the block with specified blocks. + + + @param FtwDevice The private data of FTW driver + @param FvBlock FVB Protocol interface + @param Lba Lba of the firmware block + @param NumberOfBlocks The number of consecutive blocks starting with Lba + + @retval EFI_SUCCESS Block LBA is Erased successfully + @retval Others Error occurs + +**/ +EFI_STATUS +FtwEraseBlock ( + IN EFI_FTW_DEVICE *FtwDevice, + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + EFI_LBA Lba, + UINTN NumberOfBlocks + ) +{ + return FvBlock->EraseBlocks ( + FvBlock, + Lba, + NumberOfBlocks, + EFI_LBA_LIST_TERMINATOR + ); +} + +/** + Erase spare block. + + @param FtwDevice The private data of FTW driver + + @retval EFI_SUCCESS The erase request was successfully completed. + @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state. + @retval EFI_DEVICE_ERROR The block device is not functioning + correctly and could not be written. + The firmware device may have been + partially erased. + @retval EFI_INVALID_PARAMETER One or more of the LBAs listed + in the variable argument list do + not exist in the firmware volume. + + +**/ +EFI_STATUS +FtwEraseSpareBlock ( + IN EFI_FTW_DEVICE *FtwDevice + ) +{ + return FtwDevice->FtwBackupFvb->EraseBlocks ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba, + FtwDevice->NumberOfSpareBlock, + EFI_LBA_LIST_TERMINATOR + ); +} + +/** + + Is it in working block? + + @param FtwDevice The private data of FTW driver + @param FvBlock Fvb protocol instance + @param Lba The block specified + + @return A BOOLEAN value indicating in working block or not. + +**/ +BOOLEAN +IsWorkingBlock ( + EFI_FTW_DEVICE *FtwDevice, + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + EFI_LBA Lba + ) +{ + // + // If matching the following condition, the target block is in working block. + // 1. Target block is on the FV of working block (Using the same FVB protocol instance). + // 2. Lba falls into the range of working block. + // + return (BOOLEAN) + ( + (FvBlock == FtwDevice->FtwFvBlock) && + (Lba >= FtwDevice->FtwWorkBlockLba) && + (Lba <= FtwDevice->FtwWorkSpaceLba) + ); +} + +/** + + Get firmware volume block by address. + + + @param Address Address specified the block + @param FvBlock The block caller wanted + + @retval EFI_SUCCESS The protocol instance if found. + @retval EFI_NOT_FOUND Block not found + +**/ +EFI_HANDLE +GetFvbByAddress ( + IN EFI_PHYSICAL_ADDRESS Address, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + EFI_PHYSICAL_ADDRESS FvbBaseAddress; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + EFI_HANDLE FvbHandle; + UINTN BlockSize; + UINTN NumberOfBlocks; + + *FvBlock = NULL; + FvbHandle = NULL; + HandleBuffer = NULL; + // + // Locate all handles of Fvb protocol + // + Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return NULL; + } + // + // Get the FVB to access variable store + // + for (Index = 0; Index < HandleCount; Index += 1) { + Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb); + if (EFI_ERROR (Status)) { + break; + } + // + // Compare the address and select the right one + // + Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Now, one FVB has one type of BlockSize + // + Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks); + if (EFI_ERROR (Status)) { + continue; + } + + if ((Address >= FvbBaseAddress) && (Address < (FvbBaseAddress + BlockSize * NumberOfBlocks))) { + *FvBlock = Fvb; + FvbHandle = HandleBuffer[Index]; + break; + } + } + + FreePool (HandleBuffer); + return FvbHandle; +} + +/** + + Is it in boot block? + + @param FtwDevice The private data of FTW driver + @param FvBlock Fvb protocol instance + + @return A BOOLEAN value indicating in boot block or not. + +**/ +BOOLEAN +IsBootBlock ( + EFI_FTW_DEVICE *FtwDevice, + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock + ) +{ + EFI_STATUS Status; + EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol; + EFI_PHYSICAL_ADDRESS BootBlockBase; + UINTN BootBlockSize; + EFI_PHYSICAL_ADDRESS BackupBlockBase; + UINTN BackupBlockSize; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb; + BOOLEAN IsSwapped; + EFI_HANDLE FvbHandle; + + if (!FeaturePcdGet(PcdFullFtwServiceEnable)) { + return FALSE; + } + + Status = FtwGetSarProtocol ((VOID **) &SarProtocol); + if (EFI_ERROR (Status)) { + return FALSE; + } + // + // Get the boot block range + // + Status = SarProtocol->GetRangeLocation ( + SarProtocol, + &BootBlockBase, + &BootBlockSize, + &BackupBlockBase, + &BackupBlockSize + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = SarProtocol->GetSwapState (SarProtocol, &IsSwapped); + if (EFI_ERROR (Status)) { + return FALSE; + } + // + // Get FVB by address + // + if (!IsSwapped) { + FvbHandle = GetFvbByAddress (BootBlockBase, &BootFvb); + } else { + FvbHandle = GetFvbByAddress (BackupBlockBase, &BootFvb); + } + + if (FvbHandle == NULL) { + return FALSE; + } + // + // Compare the Fvb + // + return (BOOLEAN) (FvBlock == BootFvb); +} + +/** + Copy the content of spare block to a boot block. Size is FTW_BLOCK_SIZE. + Spare block is accessed by FTW working FVB protocol interface. + Target block is accessed by FvBlock protocol interface. + + FTW will do extra work on boot block update. + FTW should depend on a protocol of EFI_ADDRESS_RANGE_SWAP_PROTOCOL, + which is produced by a chipset driver. + FTW updating boot block steps may be: + 1. GetRangeLocation(), if the Range is inside the boot block, FTW know + that boot block will be update. It shall add a FLAG in the working block. + 2. When spare block is ready, + 3. SetSwapState(SWAPPED) + 4. erasing boot block, + 5. programming boot block until the boot block is ok. + 6. SetSwapState(UNSWAPPED) + FTW shall not allow to update boot block when battery state is error. + + @param FtwDevice The private data of FTW driver + + @retval EFI_SUCCESS Spare block content is copied to boot block + @retval EFI_INVALID_PARAMETER Input parameter error + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FlushSpareBlockToBootBlock ( + EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + UINTN Length; + UINT8 *Buffer; + UINTN Count; + UINT8 *Ptr; + UINTN Index; + BOOLEAN TopSwap; + EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SarProtocol; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *BootFvb; + EFI_LBA BootLba; + + if (!FeaturePcdGet(PcdFullFtwServiceEnable)) { + return EFI_UNSUPPORTED; + } + + // + // Locate swap address range protocol + // + Status = FtwGetSarProtocol ((VOID **) &SarProtocol); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Allocate a memory buffer + // + Length = FtwDevice->SpareAreaLength; + Buffer = AllocatePool (Length); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get TopSwap bit state + // + Status = SarProtocol->GetSwapState (SarProtocol, &TopSwap); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Get Top Swapped status - %r\n", Status)); + FreePool (Buffer); + return EFI_ABORTED; + } + + if (TopSwap) { + // + // Get FVB of current boot block + // + if (GetFvbByAddress (FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength, &BootFvb) == NULL) { + FreePool (Buffer); + return EFI_ABORTED; + } + // + // Read data from current boot block + // + BootLba = 0; + Ptr = Buffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Count = FtwDevice->SpareBlockSize; + Status = BootFvb->Read ( + BootFvb, + BootLba + Index, + 0, + &Count, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Ptr += Count; + } + } else { + // + // Read data from spare block + // + Ptr = Buffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Count = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Count, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Ptr += Count; + } + // + // Set TopSwap bit + // + Status = SarProtocol->SetSwapState (SarProtocol, TRUE); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + } + // + // Erase current spare block + // Because TopSwap is set, this actually erase the top block (boot block)! + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return EFI_ABORTED; + } + // + // Write memory buffer to current spare block. Still top block. + // + Ptr = Buffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Count = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Count, + Ptr + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: FVB Write boot block - %r\n", Status)); + FreePool (Buffer); + return Status; + } + + Ptr += Count; + } + + FreePool (Buffer); + + // + // Clear TopSwap bit + // + Status = SarProtocol->SetSwapState (SarProtocol, FALSE); + + return Status; +} + +/** + Copy the content of spare block to a target block. + Spare block is accessed by FTW backup FVB protocol interface. + Target block is accessed by FvBlock protocol interface. + + + @param FtwDevice The private data of FTW driver + @param FvBlock FVB Protocol interface to access target block + @param Lba Lba of the target block + @param BlockSize The size of the block + @param NumberOfBlocks The number of consecutive blocks starting with Lba + + @retval EFI_SUCCESS Spare block content is copied to target block + @retval EFI_INVALID_PARAMETER Input parameter error + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FlushSpareBlockToTargetBlock ( + EFI_FTW_DEVICE *FtwDevice, + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + EFI_LBA Lba, + UINTN BlockSize, + UINTN NumberOfBlocks + ) +{ + EFI_STATUS Status; + UINTN Length; + UINT8 *Buffer; + UINTN Count; + UINT8 *Ptr; + UINTN Index; + + if ((FtwDevice == NULL) || (FvBlock == NULL)) { + return EFI_INVALID_PARAMETER; + } + // + // Allocate a memory buffer + // + Length = FtwDevice->SpareAreaLength; + Buffer = AllocatePool (Length); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Read all content of spare block to memory buffer + // + Ptr = Buffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Count = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Count, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Ptr += Count; + } + // + // Erase the target block + // + Status = FtwEraseBlock (FtwDevice, FvBlock, Lba, NumberOfBlocks); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return EFI_ABORTED; + } + // + // Write memory buffer to block, using the FvBlock protocol interface + // + Ptr = Buffer; + for (Index = 0; Index < NumberOfBlocks; Index += 1) { + Count = BlockSize; + Status = FvBlock->Write (FvBlock, Lba + Index, 0, &Count, Ptr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status)); + FreePool (Buffer); + return Status; + } + + Ptr += Count; + } + + FreePool (Buffer); + + return Status; +} + +/** + Copy the content of spare block to working block. Size is FTW_BLOCK_SIZE. + Spare block is accessed by FTW backup FVB protocol interface. LBA is + FtwDevice->FtwSpareLba. + Working block is accessed by FTW working FVB protocol interface. LBA is + FtwDevice->FtwWorkBlockLba. + + Since the working block header is important when FTW initializes, the + state of the operation should be handled carefully. The Crc value is + calculated without STATE element. + + @param FtwDevice The private data of FTW driver + + @retval EFI_SUCCESS Spare block content is copied to target block + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FlushSpareBlockToWorkingBlock ( + EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + UINTN Length; + UINT8 *Buffer; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader; + UINTN Count; + UINT8 *Ptr; + UINTN Index; + + // + // Allocate a memory buffer + // + Length = FtwDevice->SpareAreaLength; + Buffer = AllocatePool (Length); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // To guarantee that the WorkingBlockValid is set on spare block + // + // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, + // WorkingBlockValid); + // To skip Signature and Crc: sizeof(EFI_GUID)+sizeof(UINT32). + // + FtwUpdateFvState ( + FtwDevice->FtwBackupFvb, + FtwDevice->SpareBlockSize, + FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare, + FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32), + WORKING_BLOCK_VALID + ); + // + // Read from spare block to memory buffer + // + Ptr = Buffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Count = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Count, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Ptr += Count; + } + // + // Clear the CRC and STATE, copy data from spare to working block. + // + WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (Buffer + (UINTN) FtwDevice->FtwWorkSpaceLbaInSpare * FtwDevice->SpareBlockSize + FtwDevice->FtwWorkSpaceBaseInSpare); + InitWorkSpaceHeader (WorkingBlockHeader); + WorkingBlockHeader->WorkingBlockValid = FTW_ERASE_POLARITY; + WorkingBlockHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY; + + // + // target block is working block, then + // Set WorkingBlockInvalid in EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER + // before erase the working block. + // + // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, + // WorkingBlockInvalid); + // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to + // skip Signature and Crc. + // + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32), + WORKING_BLOCK_INVALID + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return EFI_ABORTED; + } + + FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE; + + // + // Erase the working block + // + Status = FtwEraseBlock (FtwDevice, FtwDevice->FtwFvBlock, FtwDevice->FtwWorkBlockLba, FtwDevice->NumberOfWorkBlock); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return EFI_ABORTED; + } + // + // Write memory buffer to working block, using the FvBlock protocol interface + // + Ptr = Buffer; + for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) { + Count = FtwDevice->WorkBlockSize; + Status = FtwDevice->FtwFvBlock->Write ( + FtwDevice->FtwFvBlock, + FtwDevice->FtwWorkBlockLba + Index, + 0, + &Count, + Ptr + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: FVB Write block - %r\n", Status)); + FreePool (Buffer); + return Status; + } + + Ptr += Count; + } + // + // Since the memory buffer will not be used, free memory Buffer. + // + FreePool (Buffer); + + // + // Update the VALID of the working block + // + // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, WorkingBlockValid); + // So hardcode offset as sizeof(EFI_GUID)+sizeof(UINT32) to skip Signature and Crc. + // + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32), + WORKING_BLOCK_VALID + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_INVALID_STATE; + FtwDevice->FtwWorkSpaceHeader->WorkingBlockValid = FTW_VALID_STATE; + + return EFI_SUCCESS; +} + +/** + Update a bit of state on a block device. The location of the bit is + calculated by the (Lba, Offset, bit). Here bit is determined by the + the name of a certain bit. + + + @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock + @param BlockSize The size of the block + @param Lba Lba of a block + @param Offset Offset on the Lba + @param NewBit New value that will override the old value if it can be change + + @retval EFI_SUCCESS A state bit has been updated successfully + @retval Others Access block device error. + Notes: + Assume all bits of State are inside the same BYTE. + @retval EFI_ABORTED Read block fail + +**/ +EFI_STATUS +FtwUpdateFvState ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINT8 NewBit + ) +{ + EFI_STATUS Status; + UINT8 State; + UINTN Length; + + // + // Calculate the real Offset and Lba to write. + // + while (Offset >= BlockSize) { + Offset -= BlockSize; + Lba++; + } + + // + // Read state from device, assume State is only one byte. + // + Length = sizeof (UINT8); + Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + State ^= FTW_POLARITY_REVERT; + State = (UINT8) (State | NewBit); + State ^= FTW_POLARITY_REVERT; + + // + // Write state back to device + // + Length = sizeof (UINT8); + Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State); + + return Status; +} + +/** + Get the last Write Header pointer. + The last write header is the header whose 'complete' state hasn't been set. + After all, this header may be a EMPTY header entry for next Allocate. + + + @param FtwWorkSpaceHeader Pointer of the working block header + @param FtwWorkSpaceSize Size of the work space + @param FtwWriteHeader Pointer to retrieve the last write header + + @retval EFI_SUCCESS Get the last write record successfully + @retval EFI_ABORTED The FTW work space is damaged + +**/ +EFI_STATUS +FtwGetLastWriteHeader ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader, + IN UINTN FtwWorkSpaceSize, + OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader + ) +{ + UINTN Offset; + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; + + *FtwWriteHeader = NULL; + FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1); + Offset = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER); + + while (FtwHeader->Complete == FTW_VALID_STATE) { + Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize); + // + // If Offset exceed the FTW work space boudary, return error. + // + if (Offset >= FtwWorkSpaceSize) { + *FtwWriteHeader = FtwHeader; + return EFI_ABORTED; + } + + FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset); + } + // + // Last write header is found + // + *FtwWriteHeader = FtwHeader; + + return EFI_SUCCESS; +} + +/** + Get the last Write Record pointer. The last write Record is the Record + whose DestinationCompleted state hasn't been set. After all, this Record + may be a EMPTY record entry for next write. + + + @param FtwWriteHeader Pointer to the write record header + @param FtwWriteRecord Pointer to retrieve the last write record + + @retval EFI_SUCCESS Get the last write record successfully + @retval EFI_ABORTED The FTW work space is damaged + +**/ +EFI_STATUS +FtwGetLastWriteRecord ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader, + OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord + ) +{ + UINTN Index; + EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord; + + *FtwWriteRecord = NULL; + FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1); + + // + // Try to find the last write record "that has not completed" + // + for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) { + if (FtwRecord->DestinationComplete != FTW_VALID_STATE) { + // + // The last write record is found + // + *FtwWriteRecord = FtwRecord; + return EFI_SUCCESS; + } + + FtwRecord++; + + if (FtwWriteHeader->PrivateDataSize != 0) { + FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize); + } + } + // + // if Index == NumberOfWrites, then + // the last record has been written successfully, + // but the Header->Complete Flag has not been set. + // also return the last record. + // + if (Index == FtwWriteHeader->NumberOfWrites) { + *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize)); + return EFI_SUCCESS; + } + + return EFI_ABORTED; +} + +/** + To check if FtwRecord is the first record of FtwHeader. + + @param FtwHeader Pointer to the write record header + @param FtwRecord Pointer to the write record + + @retval TRUE FtwRecord is the first Record of the FtwHeader + @retval FALSE FtwRecord is not the first Record of the FtwHeader + +**/ +BOOLEAN +IsFirstRecordOfWrites ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader, + IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord + ) +{ + UINT8 *Head; + UINT8 *Ptr; + + Head = (UINT8 *) FtwHeader; + Ptr = (UINT8 *) FtwRecord; + + Head += sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER); + return (BOOLEAN) (Head == Ptr); +} + +/** + To check if FtwRecord is the last record of FtwHeader. Because the + FtwHeader has NumberOfWrites & PrivateDataSize, the FtwRecord can be + determined if it is the last record of FtwHeader. + + @param FtwHeader Pointer to the write record header + @param FtwRecord Pointer to the write record + + @retval TRUE FtwRecord is the last Record of the FtwHeader + @retval FALSE FtwRecord is not the last Record of the FtwHeader + +**/ +BOOLEAN +IsLastRecordOfWrites ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader, + IN EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord + ) +{ + UINT8 *Head; + UINT8 *Ptr; + + Head = (UINT8 *) FtwHeader; + Ptr = (UINT8 *) FtwRecord; + + Head += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites - 1, FtwHeader->PrivateDataSize); + return (BOOLEAN) (Head == Ptr); +} + +/** + To check if FtwRecord is the first record of FtwHeader. + + @param FtwHeader Pointer to the write record header + @param FtwRecord Pointer to retrieve the previous write record + + @retval EFI_ACCESS_DENIED Input record is the first record, no previous record is return. + @retval EFI_SUCCESS The previous write record is found. + +**/ +EFI_STATUS +GetPreviousRecordOfWrites ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader, + IN OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwRecord + ) +{ + UINT8 *Ptr; + + if (IsFirstRecordOfWrites (FtwHeader, *FtwRecord)) { + *FtwRecord = NULL; + return EFI_ACCESS_DENIED; + } + + Ptr = (UINT8 *) (*FtwRecord); + Ptr -= FTW_RECORD_SIZE (FtwHeader->PrivateDataSize); + *FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr; + return EFI_SUCCESS; +} + +/** + Allocate private data for FTW driver and initialize it. + + @param[out] FtwData Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW device successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +InitFtwDevice ( + OUT EFI_FTW_DEVICE **FtwData + ) +{ + EFI_FTW_DEVICE *FtwDevice; + + // + // Allocate private data of this driver, + // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE]. + // + FtwDevice = AllocateZeroPool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize)); + if (FtwDevice == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE. + // + FtwDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize); + FtwDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize); + if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) { + DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n")); + FreePool (FtwDevice); + return EFI_INVALID_PARAMETER; + } + + FtwDevice->Signature = FTW_DEVICE_SIGNATURE; + FtwDevice->FtwFvBlock = NULL; + FtwDevice->FtwBackupFvb = NULL; + FtwDevice->FtwWorkSpaceLba = (EFI_LBA) (-1); + FtwDevice->FtwSpareLba = (EFI_LBA) (-1); + + FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64); + if (FtwDevice->WorkSpaceAddress == 0) { + FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase); + } + + FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64); + if (FtwDevice->SpareAreaAddress == 0) { + FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase); + } + + *FtwData = FtwDevice; + return EFI_SUCCESS; +} + + +/** + Find the proper Firmware Volume Block protocol for FTW operation. + + @param[in, out] FtwDevice Pointer to the FTW device structure + + @retval EFI_SUCCESS Find the FVB protocol successfully. + @retval EFI_NOT_FOUND No proper FVB protocol was found. + @retval EFI_ABORTED Some data can not be got or be invalid. + +**/ +EFI_STATUS +FindFvbForFtw ( + IN OUT EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + EFI_PHYSICAL_ADDRESS FvbBaseAddress; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + EFI_FVB_ATTRIBUTES_2 Attributes; + UINT32 LbaIndex; + UINTN BlockSize; + UINTN NumberOfBlocks; + + HandleBuffer = NULL; + + // + // Get all FVB handle. + // + Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Get the FVB to access variable store + // + Fvb = NULL; + for (Index = 0; Index < HandleCount; Index += 1) { + Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_FOUND; + break; + } + + // + // Ensure this FVB protocol support Write operation. + // + Status = Fvb->GetAttributes (Fvb, &Attributes); + if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) { + continue; + } + // + // Compare the address and select the right one + // + Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Now, one FVB has one type of BlockSize. + // + Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks); + if (EFI_ERROR (Status)) { + continue; + } + + if ((FtwDevice->FtwFvBlock == NULL) && (FtwDevice->WorkSpaceAddress >= FvbBaseAddress) && + ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) { + FtwDevice->FtwFvBlock = Fvb; + // + // To get the LBA of work space + // + for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) { + if ((FtwDevice->WorkSpaceAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1))) + && (FtwDevice->WorkSpaceAddress < (FvbBaseAddress + BlockSize * LbaIndex))) { + FtwDevice->FtwWorkSpaceLba = LbaIndex - 1; + // + // Get the Work space size and Base(Offset) + // + FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength; + FtwDevice->WorkBlockSize = BlockSize; + FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (FvbBaseAddress + FtwDevice->WorkBlockSize * (LbaIndex - 1))); + FtwDevice->NumberOfWorkSpaceBlock = FTW_BLOCKS (FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize, FtwDevice->WorkBlockSize); + if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) { + // + // Check the alignment of work space address and length, they should be block size aligned when work space size is larger than one block size. + // + if (((FtwDevice->WorkSpaceAddress & (FtwDevice->WorkBlockSize - 1)) != 0) || + ((FtwDevice->WorkSpaceLength & (FtwDevice->WorkBlockSize - 1)) != 0)) { + DEBUG ((EFI_D_ERROR, "Ftw: Work space address or length is not block size aligned when work space size is larger than one block size\n")); + FreePool (HandleBuffer); + ASSERT (FALSE); + return EFI_ABORTED; + } + } else if ((FtwDevice->FtwWorkSpaceBase + FtwDevice->FtwWorkSpaceSize) > FtwDevice->WorkBlockSize) { + DEBUG ((EFI_D_ERROR, "Ftw: The work space range should not span blocks when work space size is less than one block size\n")); + FreePool (HandleBuffer); + ASSERT (FALSE); + return EFI_ABORTED; + } + break; + } + } + } + + if ((FtwDevice->FtwBackupFvb == NULL) && (FtwDevice->SpareAreaAddress >= FvbBaseAddress) && + ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (FvbBaseAddress + BlockSize * NumberOfBlocks))) { + FtwDevice->FtwBackupFvb = Fvb; + // + // To get the LBA of spare + // + for (LbaIndex = 1; LbaIndex <= NumberOfBlocks; LbaIndex += 1) { + if ((FtwDevice->SpareAreaAddress >= (FvbBaseAddress + BlockSize * (LbaIndex - 1))) + && (FtwDevice->SpareAreaAddress < (FvbBaseAddress + BlockSize * LbaIndex))) { + // + // Get the NumberOfSpareBlock and BlockSize + // + FtwDevice->FtwSpareLba = LbaIndex - 1; + FtwDevice->SpareBlockSize = BlockSize; + FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->SpareBlockSize; + // + // Check the range of spare area to make sure that it's in FV range + // + if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > NumberOfBlocks) { + DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n")); + FreePool (HandleBuffer); + ASSERT (FALSE); + return EFI_ABORTED; + } + // + // Check the alignment of spare area address and length, they should be block size aligned + // + if (((FtwDevice->SpareAreaAddress & (FtwDevice->SpareBlockSize - 1)) != 0) || + ((FtwDevice->SpareAreaLength & (FtwDevice->SpareBlockSize - 1)) != 0)) { + DEBUG ((EFI_D_ERROR, "Ftw: Spare area address or length is not block size aligned\n")); + FreePool (HandleBuffer); + // + // Report Status Code EFI_SW_EC_ABORTED. + // + REPORT_STATUS_CODE ((EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ABORTED)); + ASSERT (FALSE); + CpuDeadLoop (); + } + break; + } + } + } + } + FreePool (HandleBuffer); + + if ((FtwDevice->FtwBackupFvb == NULL) || (FtwDevice->FtwFvBlock == NULL) || + (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) || (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))) { + return EFI_ABORTED; + } + DEBUG ((EFI_D_INFO, "Ftw: FtwWorkSpaceLba - 0x%lx, WorkBlockSize - 0x%x, FtwWorkSpaceBase - 0x%x\n", FtwDevice->FtwWorkSpaceLba, FtwDevice->WorkBlockSize, FtwDevice->FtwWorkSpaceBase)); + DEBUG ((EFI_D_INFO, "Ftw: FtwSpareLba - 0x%lx, SpareBlockSize - 0x%x\n", FtwDevice->FtwSpareLba, FtwDevice->SpareBlockSize)); + + return EFI_SUCCESS; +} + + +/** + Initialization for Fault Tolerant Write protocol. + + @param[in, out] FtwDevice Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW protocol successfully. + @retval EFI_NOT_FOUND No proper FVB protocol was found. + +**/ +EFI_STATUS +InitFtwProtocol ( + IN OUT EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; + UINTN Offset; + EFI_HANDLE FvbHandle; + EFI_LBA WorkSpaceLbaOffset; + + // + // Find the right SMM Fvb protocol instance for FTW. + // + Status = FindFvbForFtw (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Calculate the start LBA of working block. + // + if (FtwDevice->FtwWorkSpaceSize >= FtwDevice->WorkBlockSize) { + // + // Working block is a standalone area which only contains working space. + // + FtwDevice->NumberOfWorkBlock = FtwDevice->NumberOfWorkSpaceBlock; + } else { + // + // Working block is an area which + // contains working space in its last block and has the same size as spare + // block, unless there are not enough blocks before the block that contains + // working space. + // + FtwDevice->NumberOfWorkBlock = (UINTN) (FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock); + while (FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize > FtwDevice->SpareAreaLength) { + FtwDevice->NumberOfWorkBlock--; + } + } + FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba + FtwDevice->NumberOfWorkSpaceBlock - FtwDevice->NumberOfWorkBlock; + DEBUG ((EFI_D_INFO, "Ftw: NumberOfWorkBlock - 0x%x, FtwWorkBlockLba - 0x%lx\n", FtwDevice->NumberOfWorkBlock, FtwDevice->FtwWorkBlockLba)); + + // + // Calcualte the LBA and base of work space in spare block. + // Note: Do not assume Spare Block and Work Block have same block size. + // + WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba; + FtwDevice->FtwWorkSpaceLbaInSpare = (EFI_LBA) (((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) / FtwDevice->SpareBlockSize); + FtwDevice->FtwWorkSpaceBaseInSpare = ((UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + FtwDevice->FtwWorkSpaceBase) % FtwDevice->SpareBlockSize; + DEBUG ((EFI_D_INFO, "Ftw: WorkSpaceLbaInSpare - 0x%lx, WorkSpaceBaseInSpare - 0x%x\n", FtwDevice->FtwWorkSpaceLbaInSpare, FtwDevice->FtwWorkSpaceBaseInSpare)); + + // + // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE. + // + FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1); + FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace; + + FtwDevice->FtwLastWriteHeader = NULL; + FtwDevice->FtwLastWriteRecord = NULL; + + InitializeLocalWorkSpaceHeader (); + + // + // Refresh the working space data from working block + // + Status = WorkSpaceRefresh (FtwDevice); + ASSERT_EFI_ERROR (Status); + // + // If the working block workspace is not valid, try the spare block + // + if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) { + // + // Read from spare block + // + Status = ReadWorkSpaceData ( + FtwDevice->FtwBackupFvb, + FtwDevice->SpareBlockSize, + FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare, + FtwDevice->FtwWorkSpaceBaseInSpare, + FtwDevice->FtwWorkSpaceSize, + FtwDevice->FtwWorkSpace + ); + ASSERT_EFI_ERROR (Status); + + // + // If spare block is valid, then replace working block content. + // + if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) { + Status = FlushSpareBlockToWorkingBlock (FtwDevice); + DEBUG ((EFI_D_INFO, "Ftw: Restart working block update in %a() - %r\n", + __FUNCTION__, Status)); + FtwAbort (&FtwDevice->FtwInstance); + // + // Refresh work space. + // + Status = WorkSpaceRefresh (FtwDevice); + ASSERT_EFI_ERROR (Status); + } else { + DEBUG ((EFI_D_INFO, + "Ftw: Both working and spare blocks are invalid, init workspace\n")); + // + // If both are invalid, then initialize work space. + // + SetMem ( + FtwDevice->FtwWorkSpace, + FtwDevice->FtwWorkSpaceSize, + FTW_ERASED_BYTE + ); + InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader); + // + // Initialize the work space + // + Status = FtwReclaimWorkSpace (FtwDevice, FALSE); + ASSERT_EFI_ERROR (Status); + } + } + // + // If the FtwDevice->FtwLastWriteRecord is 1st record of write header && + // (! SpareComplete) THEN call Abort(). + // + if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) && + (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) && + IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord) + ) { + DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n")); + FtwAbort (&FtwDevice->FtwInstance); + } + // + // If Header is incompleted and the last record has completed, then + // call Abort() to set the Header->Complete FLAG. + // + if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) && + (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) && + IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord) + ) { + DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n")); + FtwAbort (&FtwDevice->FtwInstance); + } + // + // To check the workspace buffer following last Write header/records is EMPTY or not. + // If it's not EMPTY, FTW also need to call reclaim(). + // + FtwHeader = FtwDevice->FtwLastWriteHeader; + Offset = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace; + if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) { + Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize); + } + + if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) { + Status = FtwReclaimWorkSpace (FtwDevice, TRUE); + ASSERT_EFI_ERROR (Status); + } + + // + // Restart if it's boot block + // + if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) && + (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) + ) { + if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) { + Status = FlushSpareBlockToBootBlock (FtwDevice); + DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status)); + ASSERT_EFI_ERROR (Status); + FtwAbort (&FtwDevice->FtwInstance); + } else { + // + // if (SpareCompleted) THEN Restart to fault tolerant write. + // + FvbHandle = NULL; + FvbHandle = GetFvbByAddress ((EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) FtwDevice->SpareAreaAddress + FtwDevice->FtwLastWriteRecord->RelativeOffset), &Fvb); + if (FvbHandle != NULL) { + Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle); + DEBUG ((EFI_D_ERROR, "Ftw: Restart last write - %r\n", Status)); + ASSERT_EFI_ERROR (Status); + } + FtwAbort (&FtwDevice->FtwInstance); + } + } + // + // Hook the protocol API + // + FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize; + FtwDevice->FtwInstance.Allocate = FtwAllocate; + FtwDevice->FtwInstance.Write = FtwWrite; + FtwDevice->FtwInstance.Restart = FtwRestart; + FtwDevice->FtwInstance.Abort = FtwAbort; + FtwDevice->FtwInstance.GetLastWrite = FtwGetLastWrite; + + return EFI_SUCCESS; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni new file mode 100644 index 000000000..da8b40e6d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxe.uni @@ -0,0 +1,19 @@ +// /** @file +// Fault Tolerant Write Smm Driver. +// +// This driver installs SMM Fault Tolerant Write (FTW) protocol, which provides fault +// tolerant write capability in SMM environment for block devices. Its implementation +// depends on the full functionality SMM FVB protocol that support read, write/erase +// flash access. +// +// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Fault Tolerant Write Smm Driver." + +#string STR_MODULE_DESCRIPTION #language en-US "Installs SMM Fault Tolerant Write (FTW) protocol, which provides fault tolerant write capability in SMM environment for block devices. Its implementation depends on the full functionality SMM FVB protocol that support read, write/erase flash access." + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni new file mode 100644 index 000000000..29455445b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/SmmFaultTolerantWriteDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// SmmFaultTolerantWriteDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SMM Fault Tolerant Flash Write Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c new file mode 100644 index 000000000..04cb38afd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c @@ -0,0 +1,607 @@ +/** @file + + Internal functions to operate Working Block Space. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "FaultTolerantWrite.h" + +EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0}; + +/** + Initialize a local work space header. + + Since Signature and WriteQueueSize have been known, Crc can be calculated out, + then the work space header will be fixed. +**/ +VOID +InitializeLocalWorkSpaceHeader ( + VOID + ) +{ + // + // Check signature with gEdkiiWorkingBlockSignatureGuid. + // + if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) { + // + // The local work space header has been initialized. + // + return; + } + + SetMem ( + &mWorkingBlockHeader, + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER), + FTW_ERASED_BYTE + ); + + // + // Here using gEdkiiWorkingBlockSignatureGuid as the signature. + // + CopyMem ( + &mWorkingBlockHeader.Signature, + &gEdkiiWorkingBlockSignatureGuid, + sizeof (EFI_GUID) + ); + mWorkingBlockHeader.WriteQueueSize = PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER); + + // + // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE. + // + + // + // Calculate the Crc of woking block header + // + mWorkingBlockHeader.Crc = FtwCalculateCrc32 (&mWorkingBlockHeader, + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)); + + mWorkingBlockHeader.WorkingBlockValid = FTW_VALID_STATE; + mWorkingBlockHeader.WorkingBlockInvalid = FTW_INVALID_STATE; +} + +/** + Check to see if it is a valid work space. + + + @param WorkingHeader Pointer of working block header + + @retval TRUE The work space is valid. + @retval FALSE The work space is invalid. + +**/ +BOOLEAN +IsValidWorkSpace ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader + ) +{ + if (WorkingHeader == NULL) { + return FALSE; + } + + if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) { + return TRUE; + } + + DEBUG ((EFI_D_INFO, "Ftw: Work block header check mismatch\n")); + return FALSE; +} + +/** + Initialize a work space when there is no work space. + + @param WorkingHeader Pointer of working block header + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +InitWorkSpaceHeader ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader + ) +{ + if (WorkingHeader == NULL) { + return EFI_INVALID_PARAMETER; + } + + CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)); + + return EFI_SUCCESS; +} + +/** + Read work space data from work block or spare block. + + @param FvBlock FVB Protocol interface to access the block. + @param BlockSize The size of the block. + @param Lba Lba of the block. + @param Offset The offset within the block. + @param Length The number of bytes to read from the block. + @param Buffer The data is read. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +ReadWorkSpaceData ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + OUT UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINTN MyLength; + + // + // Calculate the real Offset and Lba to write. + // + while (Offset >= BlockSize) { + Offset -= BlockSize; + Lba++; + } + + Ptr = Buffer; + while (Length > 0) { + if ((Offset + Length) > BlockSize) { + MyLength = BlockSize - Offset; + } else { + MyLength = Length; + } + + Status = FvBlock->Read ( + FvBlock, + Lba, + Offset, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + Offset = 0; + Length -= MyLength; + Ptr += MyLength; + Lba++; + } + + return EFI_SUCCESS; +} + +/** + Write work space data to work block. + + @param FvBlock FVB Protocol interface to access the block. + @param BlockSize The size of the block. + @param Lba Lba of the block. + @param Offset The offset within the block to place the data. + @param Length The number of bytes to write to the block. + @param Buffer The data to write. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +WriteWorkSpaceData ( + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, + IN UINTN BlockSize, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN Length, + IN UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINTN MyLength; + + // + // Calculate the real Offset and Lba to write. + // + while (Offset >= BlockSize) { + Offset -= BlockSize; + Lba++; + } + + Ptr = Buffer; + while (Length > 0) { + if ((Offset + Length) > BlockSize) { + MyLength = BlockSize - Offset; + } else { + MyLength = Length; + } + + Status = FvBlock->Write ( + FvBlock, + Lba, + Offset, + &MyLength, + Ptr + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + Offset = 0; + Length -= MyLength; + Ptr += MyLength; + Lba++; + } + return EFI_SUCCESS; +} + +/** + Read from working block to refresh the work space in memory. + + @param FtwDevice Point to private data of FTW driver + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_ABORTED The function could not complete successfully. + +**/ +EFI_STATUS +WorkSpaceRefresh ( + IN EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + UINTN RemainingSpaceSize; + + // + // Initialize WorkSpace as FTW_ERASED_BYTE + // + SetMem ( + FtwDevice->FtwWorkSpace, + FtwDevice->FtwWorkSpaceSize, + FTW_ERASED_BYTE + ); + + // + // Read from working block + // + Status = ReadWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase, + FtwDevice->FtwWorkSpaceSize, + FtwDevice->FtwWorkSpace + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + // + // Refresh the FtwLastWriteHeader + // + Status = FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace); + DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize)); + // + // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain + // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header + // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data), + // it needs to reclaim work space. + // + if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) { + // + // reclaim work space in working block. + // + Status = FtwReclaimWorkSpace (FtwDevice, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status)); + return EFI_ABORTED; + } + // + // Read from working block again + // + Status = ReadWorkSpaceData ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase, + FtwDevice->FtwWorkSpaceSize, + FtwDevice->FtwWorkSpace + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + Status = FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + // + // Refresh the FtwLastWriteRecord + // + Status = FtwGetLastWriteRecord ( + FtwDevice->FtwLastWriteHeader, + &FtwDevice->FtwLastWriteRecord + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Reclaim the work space on the working block. + + @param FtwDevice Point to private data of FTW driver + @param PreserveRecord Whether to preserve the working record is needed + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_ABORTED The function could not complete successfully + +**/ +EFI_STATUS +FtwReclaimWorkSpace ( + IN EFI_FTW_DEVICE *FtwDevice, + IN BOOLEAN PreserveRecord + ) +{ + EFI_STATUS Status; + UINTN Length; + EFI_FAULT_TOLERANT_WRITE_HEADER *Header; + UINT8 *TempBuffer; + UINTN TempBufferSize; + UINTN SpareBufferSize; + UINT8 *SpareBuffer; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader; + UINTN Index; + UINT8 *Ptr; + EFI_LBA WorkSpaceLbaOffset; + + DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n")); + + WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba; + + // + // Read all original data from working block to a memory buffer + // + TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize; + TempBuffer = AllocateZeroPool (TempBufferSize); + if (TempBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ptr = TempBuffer; + for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) { + Length = FtwDevice->WorkBlockSize; + Status = FtwDevice->FtwFvBlock->Read ( + FtwDevice->FtwFvBlock, + FtwDevice->FtwWorkBlockLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + } + // + // Clean up the workspace, remove all the completed records. + // + Ptr = TempBuffer + + (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + + FtwDevice->FtwWorkSpaceBase; + + // + // Clear the content of buffer that will save the new work space data + // + SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE); + + // + // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer + // + CopyMem ( + Ptr, + FtwDevice->FtwWorkSpaceHeader, + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER) + ); + if (PreserveRecord) { + // + // Get the last record following the header, + // + Status = FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + Header = FtwDevice->FtwLastWriteHeader; + if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) { + CopyMem ( + Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER), + FtwDevice->FtwLastWriteHeader, + FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize) + ); + } + } + + CopyMem ( + FtwDevice->FtwWorkSpace, + Ptr, + FtwDevice->FtwWorkSpaceSize + ); + + FtwGetLastWriteHeader ( + FtwDevice->FtwWorkSpaceHeader, + FtwDevice->FtwWorkSpaceSize, + &FtwDevice->FtwLastWriteHeader + ); + + FtwGetLastWriteRecord ( + FtwDevice->FtwLastWriteHeader, + &FtwDevice->FtwLastWriteRecord + ); + + // + // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID + // + WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer + + (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + + FtwDevice->FtwWorkSpaceBase); + WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE; + WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE; + + // + // Try to keep the content of spare block + // Save spare block into a spare backup memory buffer (Sparebuffer) + // + SpareBufferSize = FtwDevice->SpareAreaLength; + SpareBuffer = AllocatePool (SpareBufferSize); + if (SpareBuffer == NULL) { + FreePool (TempBuffer); + return EFI_OUT_OF_RESOURCES; + } + + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Length = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + } + // + // Write the memory buffer to spare block + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = TempBuffer; + for (Index = 0; TempBufferSize > 0; Index += 1) { + if (TempBufferSize > FtwDevice->SpareBlockSize) { + Length = FtwDevice->SpareBlockSize; + } else { + Length = TempBufferSize; + } + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (TempBuffer); + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + TempBufferSize -= Length; + } + // + // Free TempBuffer + // + FreePool (TempBuffer); + + // + // Set the WorkingBlockValid in spare block + // + Status = FtwUpdateFvState ( + FtwDevice->FtwBackupFvb, + FtwDevice->SpareBlockSize, + FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare, + FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32), + WORKING_BLOCK_VALID + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + // + // Before erase the working block, set WorkingBlockInvalid in working block. + // + // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, + // WorkingBlockInvalid); + // + Status = FtwUpdateFvState ( + FtwDevice->FtwFvBlock, + FtwDevice->WorkBlockSize, + FtwDevice->FtwWorkSpaceLba, + FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32), + WORKING_BLOCK_INVALID + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE; + + // + // Write the spare block to working block + // + Status = FlushSpareBlockToWorkingBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return Status; + } + // + // Restore spare backup buffer into spare block , if no failure happened during FtwWrite. + // + Status = FtwEraseSpareBlock (FtwDevice); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + Ptr = SpareBuffer; + for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { + Length = FtwDevice->SpareBlockSize; + Status = FtwDevice->FtwBackupFvb->Write ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba + Index, + 0, + &Length, + Ptr + ); + if (EFI_ERROR (Status)) { + FreePool (SpareBuffer); + return EFI_ABORTED; + } + + Ptr += Length; + } + + FreePool (SpareBuffer); + + DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n")); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.c b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.c new file mode 100644 index 000000000..439d5b01e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.c @@ -0,0 +1,315 @@ +/** @file + This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform + the check for FTW last write data has been done. + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +EFI_PEI_PPI_DESCRIPTOR mPpiListVariable = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiFaultTolerantWriteGuid, + NULL +}; + +/** + Get the last Write Header pointer. + The last write header is the header whose 'complete' state hasn't been set. + After all, this header may be a EMPTY header entry for next Allocate. + + + @param FtwWorkSpaceHeader Pointer of the working block header + @param FtwWorkSpaceSize Size of the work space + @param FtwWriteHeader Pointer to retrieve the last write header + + @retval EFI_SUCCESS Get the last write record successfully + @retval EFI_ABORTED The FTW work space is damaged + +**/ +EFI_STATUS +FtwGetLastWriteHeader ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader, + IN UINTN FtwWorkSpaceSize, + OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader + ) +{ + UINTN Offset; + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; + + *FtwWriteHeader = NULL; + FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1); + Offset = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER); + + while (FtwHeader->Complete == FTW_VALID_STATE) { + Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize); + // + // If Offset exceed the FTW work space boudary, return error. + // + if (Offset >= FtwWorkSpaceSize) { + *FtwWriteHeader = FtwHeader; + return EFI_ABORTED; + } + + FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset); + } + // + // Last write header is found + // + *FtwWriteHeader = FtwHeader; + + return EFI_SUCCESS; +} + +/** + Get the last Write Record pointer. The last write Record is the Record + whose DestinationCompleted state hasn't been set. After all, this Record + may be a EMPTY record entry for next write. + + + @param FtwWriteHeader Pointer to the write record header + @param FtwWriteRecord Pointer to retrieve the last write record + + @retval EFI_SUCCESS Get the last write record successfully + @retval EFI_ABORTED The FTW work space is damaged + +**/ +EFI_STATUS +FtwGetLastWriteRecord ( + IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader, + OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord + ) +{ + UINTN Index; + EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord; + + *FtwWriteRecord = NULL; + FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1); + + // + // Try to find the last write record "that has not completed" + // + for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) { + if (FtwRecord->DestinationComplete != FTW_VALID_STATE) { + // + // The last write record is found + // + *FtwWriteRecord = FtwRecord; + return EFI_SUCCESS; + } + + FtwRecord++; + + if (FtwWriteHeader->PrivateDataSize != 0) { + FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize); + } + } + // + // if Index == NumberOfWrites, then + // the last record has been written successfully, + // but the Header->Complete Flag has not been set. + // also return the last record. + // + if (Index == FtwWriteHeader->NumberOfWrites) { + *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize)); + return EFI_SUCCESS; + } + + return EFI_ABORTED; +} + +/** + Check to see if it is a valid work space. + + + @param WorkingHeader Pointer of working block header + @param WorkingLength Working block length + + @retval TRUE The work space is valid. + @retval FALSE The work space is invalid. + +**/ +BOOLEAN +IsValidWorkSpace ( + IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader, + IN UINTN WorkingLength + ) +{ + UINT8 Data; + + if (WorkingHeader == NULL) { + return FALSE; + } + + if ((WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) || (WorkingHeader->WorkingBlockInvalid == FTW_VALID_STATE)) { + DEBUG ((EFI_D_ERROR, "FtwPei: Work block header valid bit check error\n")); + return FALSE; + } + + if (WorkingHeader->WriteQueueSize != (WorkingLength - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER))) { + DEBUG ((EFI_D_ERROR, "FtwPei: Work block header WriteQueueSize check error\n")); + return FALSE; + } + + // + // Check signature with gEdkiiWorkingBlockSignatureGuid + // + if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &WorkingHeader->Signature)) { + DEBUG ((EFI_D_ERROR, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n")); + // + // To be compatible with old signature gEfiSystemNvDataFvGuid. + // + if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) { + return FALSE; + } else { + Data = *(UINT8 *) (WorkingHeader + 1); + if (Data != 0xff) { + DEBUG ((EFI_D_ERROR, "FtwPei: Old format FTW structure can't be handled\n")); + ASSERT (FALSE); + return FALSE; + } + } + } + + return TRUE; + +} + +/** + Main entry for Fault Tolerant Write PEIM. + + @param[in] FileHandle Handle of the file being invoked. + @param[in] PeiServices Pointer to PEI Services table. + + @retval EFI_SUCCESS If the interface could be successfully installed + @retval Others Returned from PeiServicesInstallPpi() + +**/ +EFI_STATUS +EFIAPI +PeimFaultTolerantWriteInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkingBlockHeader; + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwLastWriteHeader; + EFI_FAULT_TOLERANT_WRITE_RECORD *FtwLastWriteRecord; + EFI_PHYSICAL_ADDRESS WorkSpaceAddress; + UINTN WorkSpaceLength; + EFI_PHYSICAL_ADDRESS SpareAreaAddress; + UINTN SpareAreaLength; + EFI_PHYSICAL_ADDRESS WorkSpaceInSpareArea; + FAULT_TOLERANT_WRITE_LAST_WRITE_DATA FtwLastWrite; + + FtwWorkingBlockHeader = NULL; + FtwLastWriteHeader = NULL; + FtwLastWriteRecord = NULL; + + WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64); + if (WorkSpaceAddress == 0) { + WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase); + } + WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize); + + SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64); + if (SpareAreaAddress == 0) { + SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase); + } + SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize); + + // + // The address of FTW working base and spare base must not be 0. + // + ASSERT ((WorkSpaceAddress != 0) && (SpareAreaAddress != 0)); + + FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceAddress; + if (IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) { + Status = FtwGetLastWriteHeader ( + FtwWorkingBlockHeader, + WorkSpaceLength, + &FtwLastWriteHeader + ); + if (!EFI_ERROR (Status)) { + Status = FtwGetLastWriteRecord ( + FtwLastWriteHeader, + &FtwLastWriteRecord + ); + } + + if (!EFI_ERROR (Status)) { + ASSERT (FtwLastWriteRecord != NULL); + if ((FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) && (FtwLastWriteRecord->DestinationComplete != FTW_VALID_STATE)) { + // + // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set. + // It means the target buffer has been backed up in spare block, then target block has been erased, + // but the target buffer has not been writen in target block from spare block, we need to build + // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data. + // + FtwLastWrite.TargetAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) SpareAreaAddress + FtwLastWriteRecord->RelativeOffset); + FtwLastWrite.SpareAddress = SpareAreaAddress; + FtwLastWrite.Length = SpareAreaLength; + DEBUG (( + EFI_D_INFO, + "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n", + (UINTN) FtwLastWrite.TargetAddress, + (UINTN) FtwLastWrite.SpareAddress, + (UINTN) FtwLastWrite.Length)); + BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA)); + } + } + } else { + FtwWorkingBlockHeader = NULL; + // + // If the working block workspace is not valid, try to find workspace in the spare block. + // + WorkSpaceInSpareArea = SpareAreaAddress + SpareAreaLength - WorkSpaceLength; + while (WorkSpaceInSpareArea >= SpareAreaAddress) { + if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, (EFI_GUID *) (UINTN) WorkSpaceInSpareArea)) { + // + // Found the workspace. + // + DEBUG ((EFI_D_INFO, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN) WorkSpaceInSpareArea)); + FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceInSpareArea; + break; + } + WorkSpaceInSpareArea = WorkSpaceInSpareArea - sizeof (EFI_GUID); + } + if ((FtwWorkingBlockHeader != NULL) && IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) { + // + // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it. + // + FtwLastWrite.TargetAddress = WorkSpaceAddress - (WorkSpaceInSpareArea - SpareAreaAddress); + FtwLastWrite.SpareAddress = SpareAreaAddress; + FtwLastWrite.Length = SpareAreaLength; + DEBUG (( + EFI_D_INFO, + "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n", + (UINTN) FtwLastWrite.TargetAddress, + (UINTN) FtwLastWrite.SpareAddress, + (UINTN) FtwLastWrite.Length)); + BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA)); + } else { + // + // Both are invalid. + // + DEBUG ((EFI_D_ERROR, "FtwPei: Both working and spare block are invalid.\n")); + } + } + + // + // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done. + // + return PeiServicesInstallPpi (&mPpiListVariable); +} diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf new file mode 100644 index 000000000..f90892ad4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf @@ -0,0 +1,62 @@ +## @file +# Fault Tolerant Write PEI Driver. +# +# This module installs gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done. +# +# Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FaultTolerantWritePei + MODULE_UNI_FILE = FaultTolerantWritePei.uni + FILE_GUID = AAC33064-9ED0-4b89-A5AD-3EA767960B22 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeimFaultTolerantWriteInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + FaultTolerantWritePei.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PeimEntryPoint + PeiServicesLib + BaseLib + DebugLib + HobLib + BaseMemoryLib + PcdLib + +[Guids] + ## SOMETIMES_PRODUCES ## HOB + ## PRODUCES ## GUID # Install ppi + gEdkiiFaultTolerantWriteGuid + gEdkiiWorkingBlockSignatureGuid ## SOMETIMES_CONSUMES ## GUID + gEfiSystemNvDataFvGuid ## SOMETIMES_CONSUMES ## GUID + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + FaultTolerantWritePeiExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.uni new file mode 100644 index 000000000..ee64d5afe --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.uni @@ -0,0 +1,16 @@ +// /** @file +// Fault Tolerant Write PEI Driver. +// +// This module installs gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done. +// +// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "This module installs gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done." + +#string STR_MODULE_DESCRIPTION #language en-US "This module installs gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done." + diff --git a/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePeiExtra.uni b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePeiExtra.uni new file mode 100644 index 000000000..5def161b4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FaultTolerantWritePei Localized Strings and Content +// +// Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"FaultTolerantWrite PEI Module" + + diff --git a/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.c b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.c new file mode 100644 index 000000000..19ab71b1e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.c @@ -0,0 +1,52 @@ +/** @file + This driver produces file explorer protocol layered on top of the FileExplorerLib from the MdeModulePkg. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include + +EFI_HANDLE mFileExplorerThunkHandle = NULL; + +CONST EFI_FILE_EXPLORER_PROTOCOL mFileExplorerProtocol = { + ChooseFile +}; + +/** + The user Entry Point for File explorer module. + + This is the entry point for Print DXE Driver. It installs the file explorer Protocol. + + @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 entry point is executed successfully. + @retval Others Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +FileExplorerEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mFileExplorerThunkHandle, + &gEfiFileExplorerProtocolGuid, &mFileExplorerProtocol, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.inf b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.inf new file mode 100644 index 000000000..e01ccb7eb --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.inf @@ -0,0 +1,47 @@ +## @file +# File explorer DXE driver that produces File explorer Protocol. +# +# This driver produces File explorerprotocol layered on top of the FileExplorerLib +# from the MdeModulePkg. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FileExplorerDxe + MODULE_UNI_FILE = FileExplorerDxe.uni + FILE_GUID = 405DA936-3737-4C0C-8E3F-E6172A568592 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FileExplorerEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + FileExplorerDxe.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + FileExplorerLib + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiFileExplorerProtocolGuid ## PRODUCES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + FileExplorerDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.uni b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.uni new file mode 100644 index 000000000..aa011c0c1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// File Explorer DXE driver that produces Print2 Protocol. +// +// This driver produces File Explorer protocol layered on top of the File Explorer library +// from the MdeModulePkg. +// +// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "File Explorer DXE driver that produces File Explorer Protocol" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver produces file explorer protocol layered on top of the FileExplorerLib from the MdeModulePkg." + diff --git a/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxeExtra.uni b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxeExtra.uni new file mode 100644 index 000000000..42611d58c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FileExplorerDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"File Explorer DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/ComponentName.c new file mode 100644 index 000000000..8ab660a0c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/ComponentName.c @@ -0,0 +1,181 @@ +/** @file + UEFI Component Name(2) protocol implementation for FvSimpleFileSystem driver. + +Copyright (c) 2014, ARM Limited. All rights reserved. +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FvSimpleFileSystemInternal.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gFvSimpleFileSystemComponentName = { + FvSimpleFileSystemComponentNameGetDriverName, + FvSimpleFileSystemComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gFvSimpleFileSystemComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) FvSimpleFileSystemComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) FvSimpleFileSystemComponentNameGetControllerName, + "en" +}; + +// +// Driver name table for FvSimpleFileSystem module. +// It is shared by the implementation of ComponentName & ComponentName2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mFvSimpleFileSystemDriverNameTable[] = { + { + "eng;en", + (CHAR16 *)L"Fv Simple File System Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mFvSimpleFileSystemDriverNameTable, + DriverName, + (BOOLEAN)(This == &gFvSimpleFileSystemComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.c b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.c new file mode 100644 index 000000000..f33f7f721 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.c @@ -0,0 +1,1030 @@ +/** @file + This driver uses the EFI_FIRMWARE_VOLUME2_PROTOCOL to expose files in firmware + volumes via the the EFI_SIMPLE_FILESYSTEM_PROTOCOL and EFI_FILE_PROTOCOL. + + It will expose a single directory, containing one file for each file in the firmware + volume. If a file has a UI section, its contents will be used as a filename. + Otherwise, a string representation of the GUID will be used. + Files of an executable type (That is PEIM, DRIVER, COMBINED_PEIM_DRIVER and APPLICATION) + will have ".efi" added to their filename. + + Its primary intended use is to be able to start EFI applications embedded in FVs + from the UEFI shell. It is entirely read-only. + +Copyright (c) 2014, ARM Limited. All rights reserved. +Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FvSimpleFileSystemInternal.h" + +// +// Template for EFI_FILE_SYSTEM_INFO data structure. +// +EFI_FILE_SYSTEM_INFO mFsInfoTemplate = { + 0, // Populate at runtime + TRUE, // Read-only + 0, // Don't know volume size + 0, // No free space + 0, // Don't know block size + L"" // Populate at runtime +}; + +// +// Template for EFI_FILE_PROTOCOL data structure. +// +EFI_FILE_PROTOCOL mFileSystemTemplate = { + EFI_FILE_PROTOCOL_REVISION, + FvSimpleFileSystemOpen, + FvSimpleFileSystemClose, + FvSimpleFileSystemDelete, + FvSimpleFileSystemRead, + FvSimpleFileSystemWrite, + FvSimpleFileSystemGetPosition, + FvSimpleFileSystemSetPosition, + FvSimpleFileSystemGetInfo, + FvSimpleFileSystemSetInfo, + FvSimpleFileSystemFlush +}; + +/** + Find and call ReadSection on the first section found of an executable type. + + @param FvProtocol A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance. + @param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct + representing a file's info. + @param BufferSize Pointer to a caller-allocated UINTN. It indicates the size of + the memory represented by *Buffer. + @param Buffer Pointer to a pointer to a data buffer to contain file content. + + @retval EFI_SUCCESS The call completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The buffer is too small to contain the requested output. + @retval EFI_ACCESS_DENIED The firmware volume is configured to disallow reads. + @retval EFI_NOT_FOUND The requested file was not found in the firmware volume. + @retval EFI_DEVICE_ERROR A hardware error occurred when attempting toaccess the firmware volume. + +**/ +EFI_STATUS +FvFsFindExecutableSection ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol, + IN FV_FILESYSTEM_FILE_INFO *FvFileInfo, + IN OUT UINTN *BufferSize, + IN OUT VOID **Buffer + ) +{ + EFI_SECTION_TYPE SectionType; + UINT32 AuthenticationStatus; + EFI_STATUS Status; + + for (SectionType = EFI_SECTION_PE32; SectionType <= EFI_SECTION_TE; SectionType++) { + Status = FvProtocol->ReadSection ( + FvProtocol, + &FvFileInfo->NameGuid, + SectionType, + 0, + Buffer, + BufferSize, + &AuthenticationStatus + ); + if (Status != EFI_NOT_FOUND) { + return Status; + } + } + + return EFI_NOT_FOUND; +} + +/** + Get the size of the buffer that will be returned by FvFsReadFile. + + @param FvProtocol A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance. + @param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct + representing a file's info. + + @retval EFI_SUCCESS The file size was gotten correctly. + @retval Others The file size wasn't gotten correctly. + +**/ +EFI_STATUS +FvFsGetFileSize ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol, + IN OUT FV_FILESYSTEM_FILE_INFO *FvFileInfo + ) +{ + UINT32 AuthenticationStatus; + EFI_FV_FILETYPE FoundType; + EFI_FV_FILE_ATTRIBUTES Attributes; + EFI_STATUS Status; + UINT8 IgnoredByte; + VOID *IgnoredPtr; + + // + // To get the size of a section, we pass 0 for BufferSize. But we can't pass + // NULL for Buffer, as that will cause a return of INVALID_PARAMETER, and we + // can't pass NULL for *Buffer, as that will cause the callee to allocate + // a buffer of the sections size. + // + IgnoredPtr = &IgnoredByte; + FvFileInfo->FileInfo.FileSize = 0; + + if (FV_FILETYPE_IS_EXECUTABLE (FvFileInfo->Type)) { + // + // Get the size of the first executable section out of the file. + // + Status = FvFsFindExecutableSection (FvProtocol, FvFileInfo, (UINTN*)&FvFileInfo->FileInfo.FileSize, &IgnoredPtr); + if (Status == EFI_WARN_BUFFER_TOO_SMALL) { + return EFI_SUCCESS; + } + } else if (FvFileInfo->Type == EFI_FV_FILETYPE_FREEFORM) { + // + // Try to get the size of a raw section out of the file + // + Status = FvProtocol->ReadSection ( + FvProtocol, + &FvFileInfo->NameGuid, + EFI_SECTION_RAW, + 0, + &IgnoredPtr, + (UINTN*)&FvFileInfo->FileInfo.FileSize, + &AuthenticationStatus + ); + if (Status == EFI_WARN_BUFFER_TOO_SMALL) { + return EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + // + // Didn't find a raw section, just return the whole file's size. + // + return FvProtocol->ReadFile ( + FvProtocol, + &FvFileInfo->NameGuid, + NULL, + (UINTN*)&FvFileInfo->FileInfo.FileSize, + &FoundType, + &Attributes, + &AuthenticationStatus + ); + } + } else { + // + // Get the size of the entire file + // + return FvProtocol->ReadFile ( + FvProtocol, + &FvFileInfo->NameGuid, + NULL, + (UINTN*)&FvFileInfo->FileInfo.FileSize, + &FoundType, + &Attributes, + &AuthenticationStatus + ); + } + + return Status; +} + +/** + Helper function to read a file. + + The data returned depends on the type of the underlying FV file: + - For executable types, the first section found that contains executable code is returned. + - For files of type FREEFORM, the driver attempts to return the first section of type RAW. + If none is found, the entire contents of the FV file are returned. + - On all other files the entire contents of the FV file is returned, as by + EFI_FIRMWARE_VOLUME2_PROTOCOL.ReadFile. + + @param FvProtocol A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance. + @param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct + representing a file's info. + @param BufferSize Pointer to a caller-allocated UINTN. It indicates the size of + the memory represented by *Buffer. + @param Buffer Pointer to a pointer to a data buffer to contain file content. + + @retval EFI_SUCCESS The call completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The buffer is too small to contain the requested output. + @retval EFI_ACCESS_DENIED The firmware volume is configured to disallow reads. + @retval EFI_NOT_FOUND The requested file was not found in the firmware volume. + @retval EFI_DEVICE_ERROR A hardware error occurred when attempting toaccess the firmware volume. + +**/ +EFI_STATUS +FvFsReadFile ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol, + IN FV_FILESYSTEM_FILE_INFO *FvFileInfo, + IN OUT UINTN *BufferSize, + IN OUT VOID **Buffer + ) +{ + UINT32 AuthenticationStatus; + EFI_FV_FILETYPE FoundType; + EFI_FV_FILE_ATTRIBUTES Attributes; + EFI_STATUS Status; + + if (FV_FILETYPE_IS_EXECUTABLE (FvFileInfo->Type)) { + // + // Read the first executable section out of the file. + // + Status = FvFsFindExecutableSection (FvProtocol, FvFileInfo, BufferSize, Buffer); + } else if (FvFileInfo->Type == EFI_FV_FILETYPE_FREEFORM) { + // + // Try to read a raw section out of the file + // + Status = FvProtocol->ReadSection ( + FvProtocol, + &FvFileInfo->NameGuid, + EFI_SECTION_RAW, + 0, + Buffer, + BufferSize, + &AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + // + // Didn't find a raw section, just return the whole file. + // + Status = FvProtocol->ReadFile ( + FvProtocol, + &FvFileInfo->NameGuid, + Buffer, + BufferSize, + &FoundType, + &Attributes, + &AuthenticationStatus + ); + } + } else { + // + // Read the entire file + // + Status = FvProtocol->ReadFile ( + FvProtocol, + &FvFileInfo->NameGuid, + Buffer, + BufferSize, + &FoundType, + &Attributes, + &AuthenticationStatus + ); + } + + return Status; +} + +/** + Helper function for populating an EFI_FILE_INFO for a file. + + Note the CreateTime, LastAccessTime and ModificationTime fields in EFI_FILE_INFO + are full zero as FV2 protocol has no corresponding info to fill. + + @param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct + representing a file's info. + @param BufferSize Pointer to a caller-allocated UINTN. It indicates the size of + the memory represented by FileInfo. + @param FileInfo A pointer to EFI_FILE_INFO to contain the returned file info. + + @retval EFI_SUCCESS The call completed successfully. + @retval EFI_BUFFER_TOO_SMALL The buffer is too small to contain the requested output. + +**/ +EFI_STATUS +FvFsGetFileInfo ( + IN FV_FILESYSTEM_FILE_INFO *FvFileInfo, + IN OUT UINTN *BufferSize, + OUT EFI_FILE_INFO *FileInfo + ) +{ + UINTN InfoSize; + + InfoSize = (UINTN)FvFileInfo->FileInfo.Size; + if (*BufferSize < InfoSize) { + *BufferSize = InfoSize; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Initialize FileInfo + // + CopyMem (FileInfo, &FvFileInfo->FileInfo, InfoSize); + + *BufferSize = InfoSize; + return EFI_SUCCESS; +} + +/** + Removes the last directory or file entry in a path by changing the last + L'\' to a CHAR_NULL. + + @param Path The pointer to the path to modify. + + @retval FALSE Nothing was found to remove. + @retval TRUE A directory or file was removed. + +**/ +BOOLEAN +EFIAPI +RemoveLastItemFromPath ( + IN OUT CHAR16 *Path + ) +{ + CHAR16 *Walker; + CHAR16 *LastSlash; + // + // get directory name from path... ('chop' off extra) + // + for ( Walker = Path, LastSlash = NULL + ; Walker != NULL && *Walker != CHAR_NULL + ; Walker++ + ){ + if (*Walker == L'\\' && *(Walker + 1) != CHAR_NULL) { + LastSlash = Walker + 1; + } + } + + if (LastSlash != NULL) { + *LastSlash = CHAR_NULL; + return (TRUE); + } + + return (FALSE); +} + +/** + Function to clean up paths. + + - Single periods in the path are removed. + - Double periods in the path are removed along with a single parent directory. + - Forward slashes L'/' are converted to backward slashes L'\'. + + This will be done inline and the existing buffer may be larger than required + upon completion. + + @param Path The pointer to the string containing the path. + + @retval NULL An error occurred. + @return Path in all other instances. + +**/ +CHAR16* +EFIAPI +TrimFilePathToAbsolutePath ( + IN CHAR16 *Path + ) +{ + CHAR16 *TempString; + UINTN TempSize; + + if (Path == NULL) { + return NULL; + } + + // + // Fix up the '/' vs '\' + // + for (TempString = Path ; (TempString != NULL) && (*TempString != CHAR_NULL); TempString++) { + if (*TempString == L'/') { + *TempString = L'\\'; + } + } + + // + // Fix up the .. + // + while ((TempString = StrStr (Path, L"\\..\\")) != NULL) { + *TempString = CHAR_NULL; + TempString += 4; + RemoveLastItemFromPath (Path); + TempSize = StrSize (TempString); + CopyMem (Path + StrLen (Path), TempString, TempSize); + } + + if (((TempString = StrStr (Path, L"\\..")) != NULL) && (*(TempString + 3) == CHAR_NULL)) { + *TempString = CHAR_NULL; + RemoveLastItemFromPath (Path); + } + + // + // Fix up the . + // + while ((TempString = StrStr (Path, L"\\.\\")) != NULL) { + *TempString = CHAR_NULL; + TempString += 2; + TempSize = StrSize (TempString); + CopyMem(Path + StrLen (Path), TempString, TempSize); + } + + if (((TempString = StrStr (Path, L"\\.")) != NULL) && (*(TempString + 2) == CHAR_NULL)) { + *(TempString + 1) = CHAR_NULL; + } + + while ((TempString = StrStr (Path, L"\\\\")) != NULL) { + *TempString = CHAR_NULL; + TempString += 1; + TempSize = StrSize(TempString); + CopyMem(Path + StrLen(Path), TempString, TempSize); + } + + if (((TempString = StrStr(Path, L"\\\\")) != NULL) && (*(TempString + 1) == CHAR_NULL)) { + *(TempString) = CHAR_NULL; + } + + return Path; +} + +/** + Opens a new file relative to the source file's location. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to the source location. This would typically be an open + handle to a directory. + @param NewHandle A pointer to the location to return the opened handle for the new + file. + @param FileName The Null-terminated string of the name of the file to be opened. + The file name may contain the following path modifiers: "\", ".", + and "..". + @param OpenMode The mode to open the file. The only valid combinations that the + file may be opened with are: Read, Read/Write, or Create/Read/Write. + @param Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these are the + attribute bits for the newly created file. + + @retval EFI_SUCCESS The file was opened. + @retval EFI_NOT_FOUND The specified file could not be found on the device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write + when the media is write-protected. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemOpen ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE *File; + FV_FILESYSTEM_FILE *NewFile; + FV_FILESYSTEM_FILE_INFO *FvFileInfo; + LIST_ENTRY *FvFileInfoLink; + EFI_STATUS Status; + UINTN FileNameLength; + UINTN NewFileNameLength; + CHAR16 *FileNameWithExtension; + + // + // Check for a valid mode + // + switch (OpenMode) { + case EFI_FILE_MODE_READ: + break; + + default: + return EFI_WRITE_PROTECTED; + } + + File = FVFS_FILE_FROM_FILE_THIS (This); + Instance = File->Instance; + + FileName = TrimFilePathToAbsolutePath (FileName); + if (FileName == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (FileName[0] == L'\\') { + FileName++; + } + + // + // Check for opening root + // + if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"") == 0) { + NewFile = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE)); + if (NewFile == NULL) { + return EFI_OUT_OF_RESOURCES; + } + NewFile->Signature = FVFS_FILE_SIGNATURE; + NewFile->Instance = Instance; + NewFile->FvFileInfo = File->FvFileInfo; + CopyMem (&NewFile->FileProtocol, &mFileSystemTemplate, sizeof (mFileSystemTemplate)); + InitializeListHead (&NewFile->Link); + InsertHeadList (&Instance->FileHead, &NewFile->Link); + + NewFile->DirReadNext = NULL; + if (!IsListEmpty (&Instance->FileInfoHead)) { + NewFile->DirReadNext = FVFS_GET_FIRST_FILE_INFO (Instance); + } + + *NewHandle = &NewFile->FileProtocol; + return EFI_SUCCESS; + } + + // + // Do a linear search for a file in the FV with a matching filename + // + Status = EFI_NOT_FOUND; + FvFileInfo = NULL; + for (FvFileInfoLink = GetFirstNode (&Instance->FileInfoHead); + !IsNull (&Instance->FileInfoHead, FvFileInfoLink); + FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, FvFileInfoLink)) { + FvFileInfo = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink); + if (mUnicodeCollation->StriColl (mUnicodeCollation, &FvFileInfo->FileInfo.FileName[0], FileName) == 0) { + Status = EFI_SUCCESS; + break; + } + } + + // If the file has not been found check if the filename exists with an extension + // in case there was no extension present. + // FvFileSystem adds a 'virtual' extension '.EFI' to EFI applications and drivers + // present in the Firmware Volume + if (Status == EFI_NOT_FOUND) { + FileNameLength = StrLen (FileName); + + // Does the filename already contain the '.EFI' extension? + if (mUnicodeCollation->StriColl (mUnicodeCollation, FileName + FileNameLength - 4, L".efi") != 0) { + // No, there was no extension. So add one and search again for the file + // NewFileNameLength = FileNameLength + 1 + 4 = (Number of non-null character) + (file extension) + (a null character) + NewFileNameLength = FileNameLength + 1 + 4; + FileNameWithExtension = AllocatePool (NewFileNameLength * 2); + StrCpyS (FileNameWithExtension, NewFileNameLength, FileName); + StrCatS (FileNameWithExtension, NewFileNameLength, L".EFI"); + + for (FvFileInfoLink = GetFirstNode (&Instance->FileInfoHead); + !IsNull (&Instance->FileInfoHead, FvFileInfoLink); + FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, FvFileInfoLink)) { + FvFileInfo = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink); + if (mUnicodeCollation->StriColl (mUnicodeCollation, &FvFileInfo->FileInfo.FileName[0], FileNameWithExtension) == 0) { + Status = EFI_SUCCESS; + break; + } + } + } + } + + if (!EFI_ERROR (Status)) { + NewFile = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE)); + if (NewFile == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NewFile->Signature = FVFS_FILE_SIGNATURE; + NewFile->Instance = Instance; + NewFile->FvFileInfo = FvFileInfo; + CopyMem (&NewFile->FileProtocol, &mFileSystemTemplate, sizeof (mFileSystemTemplate)); + InitializeListHead (&NewFile->Link); + InsertHeadList (&Instance->FileHead, &NewFile->Link); + + *NewHandle = &NewFile->FileProtocol; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + Closes a specified file handle. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to close. + + @retval EFI_SUCCESS The file was closed. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemClose ( + IN EFI_FILE_PROTOCOL *This + ) +{ + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE *File; + + File = FVFS_FILE_FROM_FILE_THIS (This); + Instance = File->Instance; + + if (File != Instance->Root) { + RemoveEntryList (&File->Link); + FreePool (File); + } + return EFI_SUCCESS; +} + +/** + Reads data from a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to read data from. + @param BufferSize On input, the size of the Buffer. On output, the amount of data + returned in Buffer. In both cases, the size is measured in bytes. + @param Buffer The buffer into which the data is read. + + @retval EFI_SUCCESS Data was read. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file. + @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory + entry. BufferSize has been updated with the size + needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE *File; + EFI_STATUS Status; + LIST_ENTRY *FvFileInfoLink; + VOID *FileBuffer; + UINTN FileSize; + + File = FVFS_FILE_FROM_FILE_THIS (This); + Instance = File->Instance; + + if (File->FvFileInfo == Instance->Root->FvFileInfo) { + if (File->DirReadNext) { + // + // Directory read: populate Buffer with an EFI_FILE_INFO + // + Status = FvFsGetFileInfo (File->DirReadNext, BufferSize, Buffer); + if (!EFI_ERROR (Status)) { + // + // Successfully read a directory entry, now update the pointer to the + // next file, which will be read on the next call to this function + // + FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, &File->DirReadNext->Link); + if (IsNull (&Instance->FileInfoHead, FvFileInfoLink)) { + // + // No more files left + // + File->DirReadNext = NULL; + } else { + File->DirReadNext = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink); + } + } + return Status; + } else { + // + // Directory read. All entries have been read, so return a zero-size + // buffer. + // + *BufferSize = 0; + return EFI_SUCCESS; + } + } else { + FileSize = (UINTN)File->FvFileInfo->FileInfo.FileSize; + + FileBuffer = AllocateZeroPool (FileSize); + if (FileBuffer == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = FvFsReadFile (File->Instance->FvProtocol, File->FvFileInfo, &FileSize, &FileBuffer); + if (EFI_ERROR (Status)) { + FreePool (FileBuffer); + return EFI_DEVICE_ERROR; + } + + if (*BufferSize + File->Position > FileSize) { + *BufferSize = (UINTN)(FileSize - File->Position); + } + + CopyMem (Buffer, (UINT8*)FileBuffer + File->Position, *BufferSize); + File->Position += *BufferSize; + + FreePool (FileBuffer); + + return EFI_SUCCESS; + } +} + +/** + Writes data to a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to write data to. + @param BufferSize On input, the size of the Buffer. On output, the amount of data + actually written. In both cases, the size is measured in bytes. + @param Buffer The buffer of data to write. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Writes to open directory files are not supported. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE *File; + + File = FVFS_FILE_FROM_FILE_THIS (This); + Instance = File->Instance; + + if (File->FvFileInfo == Instance->Root->FvFileInfo) { + return EFI_UNSUPPORTED; + } else { + return EFI_WRITE_PROTECTED; + } +} + +/** + Returns a file's current position. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to get the current position on. + @param Position The address to return the file's current position value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_UNSUPPORTED The request is not valid on open directories. + @retval EFI_DEVICE_ERROR An attempt was made to get the position from a deleted file. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE *File; + + File = FVFS_FILE_FROM_FILE_THIS (This); + Instance = File->Instance; + + if (File->FvFileInfo == Instance->Root->FvFileInfo) { + return EFI_UNSUPPORTED; + } else { + *Position = File->Position; + return EFI_SUCCESS; + } +} + +/** + Sets a file's current position. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the + file handle to set the requested position on. + @param Position The byte position from the start of the file to set. + + @retval EFI_SUCCESS The position was set. + @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open + directories. + @retval EFI_DEVICE_ERROR An attempt was made to set the position of a deleted file. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE *File; + + File = FVFS_FILE_FROM_FILE_THIS (This); + Instance = File->Instance; + + if (File->FvFileInfo == Instance->Root->FvFileInfo) { + if (Position != 0) { + return EFI_UNSUPPORTED; + } + // + // Reset directory position to first entry + // + if (File->DirReadNext) { + File->DirReadNext = FVFS_GET_FIRST_FILE_INFO (Instance); + } + } else if (Position == 0xFFFFFFFFFFFFFFFFull) { + File->Position = File->FvFileInfo->FileInfo.FileSize; + } else { + File->Position = Position; + } + + return EFI_SUCCESS; +} + +/** + Flushes all modified data associated with a file to a device. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to flush. + + @retval EFI_SUCCESS The data was flushed. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read-only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemFlush ( + IN EFI_FILE_PROTOCOL *This + ) +{ + return EFI_WRITE_PROTECTED; +} + +/** + Close and delete the file handle. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the + handle to the file to delete. + + @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed. + @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDelete ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + + Status = FvSimpleFileSystemClose (This); + ASSERT_EFI_ERROR (Status); + + return EFI_WARN_DELETE_FAILURE; +} + +/** + Returns information about a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle the requested information is for. + @param InformationType The type identifier for the information being requested. + @param BufferSize On input, the size of Buffer. On output, the amount of data + returned in Buffer. In both cases, the size is measured in bytes. + @param Buffer A pointer to the data buffer to return. The buffer's type is + indicated by InformationType. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + FV_FILESYSTEM_FILE *File; + EFI_FILE_SYSTEM_INFO *FsInfoOut; + EFI_FILE_SYSTEM_VOLUME_LABEL *FsVolumeLabel; + FV_FILESYSTEM_INSTANCE *Instance; + UINTN Size; + EFI_STATUS Status; + + File = FVFS_FILE_FROM_FILE_THIS (This); + + if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + // + // Return filesystem info + // + Instance = File->Instance; + + Size = sizeof (EFI_FILE_SYSTEM_INFO) + StrSize (Instance->VolumeLabel) - sizeof (CHAR16); + + if (*BufferSize < Size) { + *BufferSize = Size; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Cast output buffer for convenience + // + FsInfoOut = (EFI_FILE_SYSTEM_INFO *) Buffer; + + CopyMem (FsInfoOut, &mFsInfoTemplate, sizeof (EFI_FILE_SYSTEM_INFO)); + Status = StrnCpyS ( FsInfoOut->VolumeLabel, + (*BufferSize - OFFSET_OF (EFI_FILE_SYSTEM_INFO, VolumeLabel)) / sizeof (CHAR16), + Instance->VolumeLabel, + StrLen (Instance->VolumeLabel) + ); + ASSERT_EFI_ERROR (Status); + FsInfoOut->Size = Size; + return Status; + } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + // + // Return file info + // + return FvFsGetFileInfo (File->FvFileInfo, BufferSize, (EFI_FILE_INFO *) Buffer); + } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + // + // Return Volume Label + // + Instance = File->Instance; + Size = sizeof (EFI_FILE_SYSTEM_VOLUME_LABEL) + StrSize (Instance->VolumeLabel) - sizeof (CHAR16);; + if (*BufferSize < Size) { + *BufferSize = Size; + return EFI_BUFFER_TOO_SMALL; + } + + FsVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL*) Buffer; + Status = StrnCpyS (FsVolumeLabel->VolumeLabel, + (*BufferSize - OFFSET_OF (EFI_FILE_SYSTEM_VOLUME_LABEL, VolumeLabel)) / sizeof (CHAR16), + Instance->VolumeLabel, + StrLen (Instance->VolumeLabel) + ); + ASSERT_EFI_ERROR (Status); + return Status; + } else { + return EFI_UNSUPPORTED; + } +} + +/** + Sets information about a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle the information is for. + @param InformationType The type identifier for the information being set. + @param BufferSize The size, in bytes, of Buffer. + @param Buffer A pointer to the data buffer to write. The buffer's type is + indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the media is + read-only. + @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_PROTOCOL_SYSTEM_INFO_ID + and the media is read only. + @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_SYSTEM_VOLUME_LABEL_ID + and the media is read-only. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file to a + file that is already present. + @retval EFI_ACCESS_DENIED An attempt is being made to change the EFI_FILE_DIRECTORY + Attribute. + @retval EFI_ACCESS_DENIED An attempt is being made to change the size of a directory. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the file was opened + read-only and an attempt is being made to modify a field + other than Attribute. + @retval EFI_VOLUME_FULL The volume is full. + @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type indicated + by InformationType. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) || + CompareGuid (InformationType, &gEfiFileInfoGuid) || + CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + return EFI_WRITE_PROTECTED; + } + + return EFI_UNSUPPORTED; +} + diff --git a/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.uni b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.uni new file mode 100644 index 000000000..ed148b8b1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystem.uni @@ -0,0 +1,16 @@ +// /** @file +// Module that lays Simple File System protocol on every FirmwareVolume2 protocol. +// +// This module produces Simple File System protocol to provide the accesses to the files in FVs. +// +// Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Lays Simple File System protocol on every FirmwareVolume2 protocol" + +#string STR_MODULE_DESCRIPTION #language en-US "This module produces Simple File System protocol to provide the accesses to the files in FVs." + diff --git a/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemDxe.inf b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemDxe.inf new file mode 100644 index 000000000..03797847d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemDxe.inf @@ -0,0 +1,68 @@ +## @file +# Support for Simple File System over Firmware Volume. +# +# This driver uses the EFI_FIRMWARE_VOLUME2_PROTOCOL to expose files in firmware +# volumes via the the EFI_SIMPLE_FILESYSTEM_PROTOCOL and EFI_FILE_PROTOCOL. +# +# It will expose a single directory, containing one file for each file in the firmware +# volume. If a file has a UI section, its contents will be used as a filename. +# Otherwise, a string representation of the GUID will be used. +# Files of an executable type (That is PEIM, DRIVER, COMBINED_PEIM_DRIVER and APPLICATION) +# will have ".efi" added to their filename. +# +# Its primary intended use is to be able to start EFI applications embedded in FVs +# from the UEFI shell. It is entirely read-only. +# +# Copyright (c) 2014, ARM Ltd. All rights reserved.
+# Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FvSimpleFileSystem + MODULE_UNI_FILE = FvSimpleFileSystem.uni + FILE_GUID = 907125c0-a5f1-11e3-a3fe-a3198b49350c + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FvSimpleFileSystemEntryPoint + +[Sources] + + ComponentName.c + FvSimpleFileSystem.c + FvSimpleFileSystemEntryPoint.c + FvSimpleFileSystemInternal.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DevicePathLib + MemoryAllocationLib + PrintLib + UefiDriverEntryPoint + UefiLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang ## SOMETIMES_CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang ## SOMETIMES_CONSUMES + +[Guids] + gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED + gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## UNDEFINED + +[Protocols] + gEfiDevicePathProtocolGuid ## TO_START + gEfiFirmwareVolume2ProtocolGuid ## TO_START + gEfiUnicodeCollationProtocolGuid ## TO_START + gEfiUnicodeCollation2ProtocolGuid ## TO_START + gEfiSimpleFileSystemProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + FvSimpleFileSystemExtra.uni diff --git a/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemEntryPoint.c b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemEntryPoint.c new file mode 100644 index 000000000..f5d6275d1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemEntryPoint.c @@ -0,0 +1,673 @@ +/** @file + This driver uses the EFI_FIRMWARE_VOLUME2_PROTOCOL to expose files in firmware + volumes via the the EFI_SIMPLE_FILESYSTEM_PROTOCOL and EFI_FILE_PROTOCOL. + + It will expose a single directory, containing one file for each file in the firmware + volume. If a file has a UI section, its contents will be used as a filename. + Otherwise, a string representation of the GUID will be used. + Files of an executable type (That is PEIM, DRIVER, COMBINED_PEIM_DRIVER and APPLICATION) + will have ".efi" added to their filename. + + Its primary intended use is to be able to start EFI applications embedded in FVs + from the UEFI shell. It is entirely read-only. + +Copyright (c) 2014, ARM Limited. All rights reserved. +Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FvSimpleFileSystemInternal.h" + +EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation = NULL; + +// +// A Guid string is 32 hex characters with 4 hyphens and a NULL-terminated char: 37 characters total +// +#define GUID_STRING_SIZE (37 * sizeof (CHAR16)) + +#define FVFS_VOLUME_LABEL_PREFIX L"Firmware Volume: " +#define FVFS_VOLUME_LABEL_SIZE (sizeof (FVFS_VOLUME_LABEL_PREFIX) + GUID_STRING_SIZE - sizeof (CHAR16)) +#define FVFS_FALLBACK_VOLUME_LABEL L"Firmware Volume" + +// +// Template for EFI_SIMPLE_FILE_SYSTEM_PROTOCOL data structure. +// +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mSimpleFsTemplate = { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, + FvSimpleFileSystemOpenVolume +}; + +// +// Template for EFI_DRIVER_BINDING_PROTOCOL data structure. +// +EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = { + FvSimpleFileSystemDriverSupported, + FvSimpleFileSystemDriverStart, + FvSimpleFileSystemDriverStop, + 0, + NULL, + NULL +}; + +/** + Open the root directory on a volume. + + @param This A pointer to the volume to open the root directory. + @param RootFile A pointer to the location to return the opened file handle for the + root directory. + + @retval EFI_SUCCESS The device was opened. + @retval EFI_UNSUPPORTED This volume does not support the requested file system type. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. Any existing file handles for this volume are + no longer valid. To access the files on the new medium, the + volume must be reopened with OpenVolume(). + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **RootFile + ) +{ + EFI_STATUS Status; + FV_FILESYSTEM_FILE *Root; + CHAR16 *UiSection; + EFI_GUID NameGuid; + EFI_FV_FILE_ATTRIBUTES Attributes; + UINT32 Authentication; + UINTN Key; + EFI_FV_FILETYPE FileType; + UINTN Size; + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE_INFO *FvFileInfo; + EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol; + CHAR16 *Name; + UINTN NameLen; + UINTN NumChars; + UINTN DestMax; + + Instance = FVFS_INSTANCE_FROM_SIMPLE_FS_THIS (This); + Status = EFI_SUCCESS; + + if (Instance->Root == NULL) { + // + // Allocate file structure for root file + // + Root = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE)); + if (Root == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Instance->Root = Root; + Root->Instance = Instance; + Root->Signature = FVFS_FILE_SIGNATURE; + CopyMem (&Root->FileProtocol, &mFileSystemTemplate, sizeof (mFileSystemTemplate)); + Root->FvFileInfo = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE_INFO)); + if (Root->FvFileInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Root->FvFileInfo->FileInfo.Size = sizeof (EFI_FILE_INFO); + Root->FvFileInfo->FileInfo.Attribute = EFI_FILE_DIRECTORY | EFI_FILE_READ_ONLY; + + // + // Populate the instance's list of files. We consider anything a file that + // has a UI_SECTION, which we consider to be its filename. + // + FvProtocol = Instance->FvProtocol; + // + // Allocate Key + // + Key = 0; + + do { + FileType = EFI_FV_FILETYPE_ALL; + + Status = FvProtocol->GetNextFile ( + FvProtocol, + &Key, + &FileType, + &NameGuid, + &Attributes, + &Size + ); + if (EFI_ERROR (Status)) { + ASSERT (Status == EFI_NOT_FOUND); + break; + } + + // + // Get a file's name: If it has a UI section, use that, otherwise use + // its NameGuid. + // + UiSection = NULL; + Status = FvProtocol->ReadSection ( + FvProtocol, + &NameGuid, + EFI_SECTION_USER_INTERFACE, + 0, + (VOID **)&UiSection, + &Size, + &Authentication + ); + if (!EFI_ERROR (Status)) { + Name = UiSection; + } else { + Name = AllocateZeroPool (GUID_STRING_SIZE); + if (Name == NULL) { + return EFI_OUT_OF_RESOURCES; + } + NumChars = UnicodeSPrint (Name, GUID_STRING_SIZE, L"%g", &NameGuid); + ASSERT ((NumChars + 1) * sizeof (CHAR16) == GUID_STRING_SIZE); + } + + // + // Found a file. + // Allocate a file structure and populate it. + // + NameLen = StrSize (Name); + if (FV_FILETYPE_IS_EXECUTABLE (FileType)) { + NameLen += StrSize (L".efi") - sizeof (CHAR16); + } + + FvFileInfo = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE_INFO) + NameLen - sizeof (CHAR16)); + if (FvFileInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + FvFileInfo->Signature = FVFS_FILE_INFO_SIGNATURE; + InitializeListHead (&FvFileInfo->Link); + CopyMem (&FvFileInfo->NameGuid, &NameGuid, sizeof (EFI_GUID)); + FvFileInfo->Type = FileType; + + // + // Add ".efi" to filenames of drivers and applications. + // + DestMax = NameLen / sizeof (CHAR16); + Status = StrnCpyS (&FvFileInfo->FileInfo.FileName[0], DestMax, Name, StrLen (Name)); + ASSERT_EFI_ERROR (Status); + + if (FV_FILETYPE_IS_EXECUTABLE (FileType)) { + Status = StrnCatS (&FvFileInfo->FileInfo.FileName[0], DestMax, L".efi", StrLen (L".efi")); + ASSERT_EFI_ERROR (Status); + } + + FvFileInfo->FileInfo.Size = sizeof (EFI_FILE_INFO) + NameLen - sizeof (CHAR16); + Status = FvFsGetFileSize (FvProtocol, FvFileInfo); + ASSERT_EFI_ERROR (Status); + FvFileInfo->FileInfo.PhysicalSize = FvFileInfo->FileInfo.FileSize; + FvFileInfo->FileInfo.Attribute = EFI_FILE_READ_ONLY; + + InsertHeadList (&Instance->FileInfoHead, &FvFileInfo->Link); + + FreePool (Name); + + } while (TRUE); + + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + } + + Instance->Root->DirReadNext = NULL; + if (!IsListEmpty (&Instance->FileInfoHead)) { + Instance->Root->DirReadNext = FVFS_GET_FIRST_FILE_INFO (Instance); + } + + *RootFile = &Instance->Root->FileProtocol; + return Status; +} + +/** + Worker function to initialize Unicode Collation support. + + It tries to locate Unicode Collation (2) protocol and matches it with current + platform language code. + + @param AgentHandle The handle used to open Unicode Collation (2) protocol. + @param ProtocolGuid The pointer to Unicode Collation (2) protocol GUID. + @param VariableName The name of the RFC 4646 or ISO 639-2 language variable. + @param DefaultLanguage The default language in case the RFC 4646 or ISO 639-2 language is absent. + + @retval EFI_SUCCESS The Unicode Collation (2) protocol has been successfully located. + @retval Others The Unicode Collation (2) protocol has not been located. + +**/ +EFI_STATUS +InitializeUnicodeCollationSupportWorker ( + IN EFI_HANDLE AgentHandle, + IN EFI_GUID *ProtocolGuid, + IN CONST CHAR16 *VariableName, + IN CONST CHAR8 *DefaultLanguage + ) +{ + EFI_STATUS ReturnStatus; + EFI_STATUS Status; + UINTN NumHandles; + UINTN Index; + EFI_HANDLE *Handles; + EFI_UNICODE_COLLATION_PROTOCOL *Uci; + BOOLEAN Iso639Language; + CHAR8 *Language; + CHAR8 *BestLanguage; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + ProtocolGuid, + NULL, + &NumHandles, + &Handles + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Iso639Language = (BOOLEAN) (ProtocolGuid == &gEfiUnicodeCollationProtocolGuid); + GetEfiGlobalVariable2 (VariableName, (VOID**) &Language, NULL); + + ReturnStatus = EFI_UNSUPPORTED; + for (Index = 0; Index < NumHandles; Index++) { + // + // Open Unicode Collation Protocol + // + Status = gBS->OpenProtocol ( + Handles[Index], + ProtocolGuid, + (VOID **) &Uci, + AgentHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Find the best matching matching language from the supported languages + // of Unicode Collation (2) protocol. + // + BestLanguage = GetBestLanguage ( + Uci->SupportedLanguages, + Iso639Language, + (Language == NULL) ? "" : Language, + DefaultLanguage, + NULL + ); + if (BestLanguage != NULL) { + FreePool (BestLanguage); + mUnicodeCollation = Uci; + ReturnStatus = EFI_SUCCESS; + break; + } + } + + if (Language != NULL) { + FreePool (Language); + } + + FreePool (Handles); + + return ReturnStatus; +} + +/** + Initialize Unicode Collation support. + + It tries to locate Unicode Collation 2 protocol and matches it with current + platform language code. If for any reason the first attempt fails, it then tries to + use Unicode Collation Protocol. + + @param AgentHandle The handle used to open Unicode Collation (2) protocol. + + @retval EFI_SUCCESS The Unicode Collation (2) protocol has been successfully located. + @retval Others The Unicode Collation (2) protocol has not been located. + +**/ +EFI_STATUS +InitializeUnicodeCollationSupport ( + IN EFI_HANDLE AgentHandle + ) +{ + + EFI_STATUS Status; + + Status = EFI_UNSUPPORTED; + + // + // First try to use RFC 4646 Unicode Collation 2 Protocol. + // + Status = InitializeUnicodeCollationSupportWorker ( + AgentHandle, + &gEfiUnicodeCollation2ProtocolGuid, + L"PlatformLang", + (CONST CHAR8 *) PcdGetPtr (PcdUefiVariableDefaultPlatformLang) + ); + // + // If the attempt to use Unicode Collation 2 Protocol fails, then we fall back + // on the ISO 639-2 Unicode Collation Protocol. + // + if (EFI_ERROR (Status)) { + Status = InitializeUnicodeCollationSupportWorker ( + AgentHandle, + &gEfiUnicodeCollationProtocolGuid, + L"Lang", + (CONST CHAR8 *) PcdGetPtr (PcdUefiVariableDefaultLang) + ); + } + + return Status; +} + +/** + Test to see if this driver supports ControllerHandle. + + @param DriverBinding Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return gBS->OpenProtocol ( + ControllerHandle, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + gImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + +/** + Start this driver on ControllerHandle by opening a FV protocol and + installing a SimpleFileSystem protocol on ControllerHandle. + + @param DriverBinding Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol; + FV_FILESYSTEM_INSTANCE *Instance; + EFI_DEVICE_PATH_PROTOCOL *FvDevicePath; + EFI_GUID *FvGuid; + UINTN NumChars; + + Status = InitializeUnicodeCollationSupport (DriverBinding->DriverBindingHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open FV protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiFirmwareVolume2ProtocolGuid, + (VOID **) &FvProtocol, + gImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create an instance + // + Instance = AllocateZeroPool (sizeof (FV_FILESYSTEM_INSTANCE)); + ASSERT (Instance != NULL); + + Instance->Root = NULL; + Instance->FvProtocol = FvProtocol; + Instance->Signature = FVFS_INSTANCE_SIGNATURE; + InitializeListHead (&Instance->FileInfoHead); + InitializeListHead (&Instance->FileHead); + CopyMem (&Instance->SimpleFs, &mSimpleFsTemplate, sizeof (mSimpleFsTemplate)); + + Status = gBS->InstallProtocolInterface( + &ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + EFI_NATIVE_INTERFACE, + &Instance->SimpleFs + ); + ASSERT_EFI_ERROR (Status); + + // + // Decide on a filesystem volume label, which will include the FV's guid. + // Get the device path to find the FV's GUID + // + Instance->VolumeLabel = NULL; + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &FvDevicePath, + gImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Iterate over device path until we find a firmware volume node + // + while (!IsDevicePathEndType (FvDevicePath)) { + if (DevicePathType (FvDevicePath) == MEDIA_DEVICE_PATH && + DevicePathSubType (FvDevicePath) == MEDIA_PIWG_FW_VOL_DP) { + // + // Allocate the volume label + // + Instance->VolumeLabel = AllocateZeroPool (FVFS_VOLUME_LABEL_SIZE); + // + // Check the allocation was successful + // + if (Instance->VolumeLabel != NULL) { + // + // Extract the FV's guid + // + FvGuid = &((MEDIA_FW_VOL_DEVICE_PATH *) FvDevicePath)->FvName; + // + // Build the volume label string + // + NumChars = UnicodeSPrint ( + Instance->VolumeLabel, + FVFS_VOLUME_LABEL_SIZE, + FVFS_VOLUME_LABEL_PREFIX L"%g", + FvGuid + ); + ASSERT ((NumChars + 1) * sizeof (CHAR16) == FVFS_VOLUME_LABEL_SIZE); + } + break; + } + FvDevicePath = NextDevicePathNode (FvDevicePath); + } + } + // + // If we didn't decide on a volume label, set a fallback one + // + if (Instance->VolumeLabel == NULL) { + Instance->VolumeLabel = AllocateCopyPool ( + sizeof (FVFS_FALLBACK_VOLUME_LABEL), + FVFS_FALLBACK_VOLUME_LABEL + ); + } + + return EFI_SUCCESS; +} + +/** + Stop this driver on ControllerHandle by removing SimpleFileSystem protocol and closing + the FV protocol on ControllerHandle. + + @param DriverBinding Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + FV_FILESYSTEM_INSTANCE *Instance; + FV_FILESYSTEM_FILE_INFO *FvFileInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *DelEntry; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFile; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID **) &SimpleFile, + DriverBinding->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Instance = FVFS_INSTANCE_FROM_SIMPLE_FS_THIS (SimpleFile); + + if (IsListEmpty (&Instance->FileHead) == FALSE) { + // + // Not all opened files are closed + // + return EFI_DEVICE_ERROR; + } + + // + // Close and uninstall protocols. + // + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiFirmwareVolume2ProtocolGuid, + gImageHandle, + ControllerHandle + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + &Instance->SimpleFs + ); + ASSERT_EFI_ERROR (Status); + + // + // Free file structures + // + if (!IsListEmpty (&Instance->FileInfoHead)) { + // + // Free the Subtask list. + // + for(Entry = Instance->FileInfoHead.ForwardLink; + Entry != (&Instance->FileInfoHead); + ) { + DelEntry = Entry; + Entry = Entry->ForwardLink; + FvFileInfo = FVFS_FILE_INFO_FROM_LINK (DelEntry); + + RemoveEntryList (DelEntry); + FreePool (FvFileInfo); + } + } + + if (Instance->Root != NULL) { + // + // Root->Name is statically allocated, no need to free. + // + if (Instance->Root->FvFileInfo != NULL) { + FreePool (Instance->Root->FvFileInfo); + } + FreePool (Instance->Root); + } + + // + // Free Instance + // + if (Instance->VolumeLabel != NULL) { + FreePool (Instance->VolumeLabel); + } + FreePool (Instance); + + return EFI_SUCCESS; +} + +/** + The user Entry Point for module FvSimpleFileSystem. The user code starts with this 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 entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &mDriverBinding, + ImageHandle, + &gFvSimpleFileSystemComponentName, + &gFvSimpleFileSystemComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemExtra.uni b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemExtra.uni new file mode 100644 index 000000000..48d2b64b4 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// FvSimpleFileSystem Driver's Localized Strings and Content +// +// Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"FvSimpleFileSystem UEFI Driver" + + diff --git a/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemInternal.h b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemInternal.h new file mode 100644 index 000000000..dea2b7a21 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemInternal.h @@ -0,0 +1,616 @@ +/** @file + The internal header file of FvSimpleFileSystem driver. + +Copyright (c) 2014, ARM Limited. All rights reserved. +Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __FVFS_INTERNAL_H__ +#define __FVFS_INTERNAL_H__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +typedef struct _FV_FILESYSTEM_FILE FV_FILESYSTEM_FILE; +typedef struct _FV_FILESYSTEM_FILE_INFO FV_FILESYSTEM_FILE_INFO; +typedef struct _FV_FILESYSTEM_INSTANCE FV_FILESYSTEM_INSTANCE; + +// +// Struct representing an instance of the "filesystem". There will be one of +// these structs per FV. +// +struct _FV_FILESYSTEM_INSTANCE { + UINT32 Signature; + LIST_ENTRY FileInfoHead; + LIST_ENTRY FileHead; + EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; + EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFs; + FV_FILESYSTEM_FILE *Root; + CHAR16 *VolumeLabel; +}; + +// +// Struct representing a opening file. Each opening operation on file will +// create such an instance except for the "root directory", which will only +// be created once for each FV. +// +struct _FV_FILESYSTEM_FILE { + UINT32 Signature; + LIST_ENTRY Link; + FV_FILESYSTEM_FILE_INFO *DirReadNext; + FV_FILESYSTEM_INSTANCE *Instance; + EFI_FILE_PROTOCOL FileProtocol; + FV_FILESYSTEM_FILE_INFO *FvFileInfo; + UINT64 Position; +}; + +// +// Struct representing the info of a file. +// +struct _FV_FILESYSTEM_FILE_INFO { + UINT32 Signature; + LIST_ENTRY Link; + EFI_GUID NameGuid; + EFI_FV_FILETYPE Type; + EFI_FILE_INFO FileInfo; +}; + +#define FVFS_FILE_SIGNATURE SIGNATURE_32 ('f', 'v', 'f', 'i') +#define FVFS_FILE_INFO_SIGNATURE SIGNATURE_32 ('f', 'v', 'i', 'n') +#define FVFS_INSTANCE_SIGNATURE SIGNATURE_32 ('f', 'v', 'f', 's') + +#define FVFS_INSTANCE_FROM_SIMPLE_FS_THIS(This) CR ( \ + This, \ + FV_FILESYSTEM_INSTANCE, \ + SimpleFs, \ + FVFS_INSTANCE_SIGNATURE \ + ) + +#define FVFS_FILE_FROM_FILE_THIS(This) CR ( \ + This, \ + FV_FILESYSTEM_FILE, \ + FileProtocol, \ + FVFS_FILE_SIGNATURE \ + ) + +#define FVFS_FILE_INFO_FROM_LINK(This) CR ( \ + This, \ + FV_FILESYSTEM_FILE_INFO, \ + Link, \ + FVFS_FILE_INFO_SIGNATURE \ + ) + +#define FVFS_FILE_FROM_LINK(FileLink) CR (FileLink, FV_FILESYSTEM_FILE, Link, FVFS_FILE_SIGNATURE) + +#define FVFS_GET_FIRST_FILE(Instance) FVFS_FILE_FROM_LINK (GetFirstNode (&Instance->FileHead)) + +#define FVFS_GET_FIRST_FILE_INFO(Instance) FVFS_FILE_INFO_FROM_LINK (GetFirstNode (&Instance->FileInfoHead)) + + +#define FV_FILETYPE_IS_EXECUTABLE(Type) ((Type) == EFI_FV_FILETYPE_PEIM || \ + (Type) == EFI_FV_FILETYPE_DRIVER || \ + (Type) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER || \ + (Type) == EFI_FV_FILETYPE_APPLICATION) + +/** + Open the root directory on a volume. + + @param This A pointer to the volume to open the root directory. + @param RootFile A pointer to the location to return the opened file handle for the + root directory. + + @retval EFI_SUCCESS The device was opened. + @retval EFI_UNSUPPORTED This volume does not support the requested file system type. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. Any existing file handles for this volume are + no longer valid. To access the files on the new medium, the + volume must be reopened with OpenVolume(). + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **RootFile + ); + +/** + Test to see if this driver supports ControllerHandle. + + @param DriverBinding Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle by opening a FV protocol and + installing a SimpleFileSystem protocol on ControllerHandle. + + @param DriverBinding Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle by removing SimpleFileSystem protocol and closing + the FV protocol on ControllerHandle. + + @param DriverBinding Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Opens a new file relative to the source file's location. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to the source location. This would typically be an open + handle to a directory. + @param NewHandle A pointer to the location to return the opened handle for the new + file. + @param FileName The Null-terminated string of the name of the file to be opened. + The file name may contain the following path modifiers: "\", ".", + and "..". + @param OpenMode The mode to open the file. The only valid combinations that the + file may be opened with are: Read, Read/Write, or Create/Read/Write. + @param Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these are the + attribute bits for the newly created file. + + @retval EFI_SUCCESS The file was opened. + @retval EFI_NOT_FOUND The specified file could not be found on the device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write + when the media is write-protected. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemOpen ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ); + +/** + Closes a specified file handle. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to close. + + @retval EFI_SUCCESS The file was closed. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemClose ( + IN EFI_FILE_PROTOCOL *This + ); + +/** + Reads data from a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to read data from. + @param BufferSize On input, the size of the Buffer. On output, the amount of data + returned in Buffer. In both cases, the size is measured in bytes. + @param Buffer The buffer into which the data is read. + + @retval EFI_SUCCESS Data was read. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file. + @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory + entry. BufferSize has been updated with the size + needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Writes data to a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to write data to. + @param BufferSize On input, the size of the Buffer. On output, the amount of data + actually written. In both cases, the size is measured in bytes. + @param Buffer The buffer of data to write. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Writes to open directory files are not supported. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +/** + Returns a file's current position. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to get the current position on. + @param Position The address to return the file's current position value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_UNSUPPORTED The request is not valid on open directories. + @retval EFI_DEVICE_ERROR An attempt was made to get the position from a deleted file. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ); + +/** + Sets a file's current position. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the + file handle to set the requested position on. + @param Position The byte position from the start of the file to set. + + @retval EFI_SUCCESS The position was set. + @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open + directories. + @retval EFI_DEVICE_ERROR An attempt was made to set the position of a deleted file. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ); + +/** + Flushes all modified data associated with a file to a device. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to flush. + + @retval EFI_SUCCESS The data was flushed. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read-only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemFlush ( + IN EFI_FILE_PROTOCOL *This + ); + +/** + Close and delete the file handle. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the + handle to the file to delete. + + @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed. + @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemDelete ( + IN EFI_FILE_PROTOCOL *This + ); + +/** + Returns information about a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle the requested information is for. + @param InformationType The type identifier for the information being requested. + @param BufferSize On input, the size of Buffer. On output, the amount of data + returned in Buffer. In both cases, the size is measured in bytes. + @param Buffer A pointer to the data buffer to return. The buffer's type is + indicated by InformationType. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Sets information about a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle the information is for. + @param InformationType The type identifier for the information being set. + @param BufferSize The size, in bytes, of Buffer. + @param Buffer A pointer to the data buffer to write. The buffer's type is + indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the media is + read-only. + @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_PROTOCOL_SYSTEM_INFO_ID + and the media is read only. + @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_SYSTEM_VOLUME_LABEL_ID + and the media is read-only. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file to a + file that is already present. + @retval EFI_ACCESS_DENIED An attempt is being made to change the EFI_FILE_DIRECTORY + Attribute. + @retval EFI_ACCESS_DENIED An attempt is being made to change the size of a directory. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the file was opened + read-only and an attempt is being made to modify a field + other than Attribute. + @retval EFI_VOLUME_FULL The volume is full. + @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type indicated + by InformationType. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Get the size of the buffer that will be returned by FvFsReadFile. + + @param FvProtocol A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance. + @param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct + representing a file's info. + + @retval EFI_SUCCESS The file size was gotten correctly. + @retval Others The file size wasn't gotten correctly. + +**/ +EFI_STATUS +FvFsGetFileSize ( + IN EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol, + IN OUT FV_FILESYSTEM_FILE_INFO *FvFileInfo + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +FvSimpleFileSystemComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +extern EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation; +extern EFI_FILE_PROTOCOL mFileSystemTemplate; +extern EFI_COMPONENT_NAME_PROTOCOL gFvSimpleFileSystemComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gFvSimpleFileSystemComponentName2; + +#endif diff --git a/roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigKeywordHandler.c b/roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigKeywordHandler.c new file mode 100644 index 000000000..5ee866fb9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigKeywordHandler.c @@ -0,0 +1,3329 @@ +/** @file +Implementation of interfaces function for EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "HiiDatabase.h" + +extern HII_DATABASE_PRIVATE_DATA mPrivate; + +/** + Convert the hex UNICODE %02x encoding of a UEFI device path to binary + from of . + + This is a internal function. + + @param String MultiKeywordRequest string. + @param DevicePathData Binary of a UEFI device path. + @param NextString string follow the possible PathHdr string. + + @retval EFI_INVALID_PARAMETER The device path is not valid or the incoming parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Lake of resources to store necessary structures. + @retval EFI_SUCCESS The device path is retrieved and translated to binary format. + The Input string not include PathHdr section. + +**/ +EFI_STATUS +ExtractDevicePath ( + IN EFI_STRING String, + OUT UINT8 **DevicePathData, + OUT EFI_STRING *NextString + ) +{ + UINTN Length; + EFI_STRING PathHdr; + UINT8 *DevicePathBuffer; + CHAR16 TemStr[2]; + UINTN Index; + UINT8 DigitUint8; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + ASSERT (NextString != NULL && DevicePathData != NULL); + + // + // KeywordRequest == NULL case. + // + if (String == NULL) { + *DevicePathData = NULL; + *NextString = NULL; + return EFI_SUCCESS; + } + + // + // Skip '&' if exist. + // + if (*String == L'&') { + String ++; + } + + // + // Find the 'PATH=' of . + // + if (StrnCmp (String, L"PATH=", StrLen (L"PATH=")) != 0) { + if (StrnCmp (String, L"KEYWORD=", StrLen (L"KEYWORD=")) != 0) { + return EFI_INVALID_PARAMETER; + } else { + // + // Not include PathHdr, return success and DevicePath = NULL. + // + *DevicePathData = NULL; + *NextString = String; + return EFI_SUCCESS; + } + } + + // + // Check whether path data does exist. + // + String += StrLen (L"PATH="); + if (*String == 0) { + return EFI_INVALID_PARAMETER; + } + PathHdr = String; + + // + // The content between 'PATH=' of and '&' of next element + // or '\0' (end of configuration string) is the UNICODE %02x bytes encoding + // of UEFI device path. + // + for (Length = 0; *String != 0 && *String != L'&'; String++, Length++); + + // + // Save the return next keyword string value. + // + *NextString = String; + + // + // Check DevicePath Length + // + if (((Length + 1) / 2) < sizeof (EFI_DEVICE_PATH_PROTOCOL)) { + return EFI_INVALID_PARAMETER; + } + + // + // The data in is encoded as hex UNICODE %02x bytes in the same order + // as the device path resides in RAM memory. + // Translate the data into binary. + // + DevicePathBuffer = (UINT8 *) AllocateZeroPool ((Length + 1) / 2); + if (DevicePathBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Convert DevicePath + // + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0; Index < Length; Index ++) { + TemStr[0] = PathHdr[Index]; + DigitUint8 = (UINT8) StrHexToUint64 (TemStr); + if ((Index & 1) == 0) { + DevicePathBuffer [Index/2] = DigitUint8; + } else { + DevicePathBuffer [Index/2] = (UINT8) ((DevicePathBuffer [Index/2] << 4) + DigitUint8); + } + } + + // + // Validate DevicePath + // + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) DevicePathBuffer; + while (!IsDevicePathEnd (DevicePath)) { + if ((DevicePath->Type == 0) || (DevicePath->SubType == 0) || (DevicePathNodeLength (DevicePath) < sizeof (EFI_DEVICE_PATH_PROTOCOL))) { + // + // Invalid device path + // + FreePool (DevicePathBuffer); + return EFI_INVALID_PARAMETER; + } + DevicePath = NextDevicePathNode (DevicePath); + } + + // + // return the device path + // + *DevicePathData = DevicePathBuffer; + + return EFI_SUCCESS; +} + +/** + Get NameSpace from the input NameSpaceId string. + + This is a internal function. + + @param String format string. + @param NameSpace Return the name space string. + @param NextString Return the next string follow namespace. + + @retval EFI_SUCCESS Get the namespace string success. + @retval EFI_INVALID_PARAMETER The NameSpaceId string not follow spec definition. + +**/ +EFI_STATUS +ExtractNameSpace ( + IN EFI_STRING String, + OUT CHAR8 **NameSpace, + OUT EFI_STRING *NextString + ) +{ + CHAR16 *TmpPtr; + UINTN NameSpaceSize; + + ASSERT (NameSpace != NULL); + + TmpPtr = NULL; + + // + // Input NameSpaceId == NULL + // + if (String == NULL) { + *NameSpace = NULL; + if (NextString != NULL) { + *NextString = NULL; + } + return EFI_SUCCESS; + } + + // + // Skip '&' if exist. + // + if (*String == L'&') { + String++; + } + + if (StrnCmp (String, L"NAMESPACE=", StrLen (L"NAMESPACE=")) != 0) { + return EFI_INVALID_PARAMETER; + } + String += StrLen (L"NAMESPACE="); + + TmpPtr = StrStr (String, L"&"); + if (TmpPtr != NULL) { + *TmpPtr = 0; + } + if (NextString != NULL) { + *NextString = String + StrLen (String); + } + + // + // Input NameSpace is unicode string. The language in String package is ascii string. + // Here will convert the unicode string to ascii and save it. + // + NameSpaceSize = StrLen (String) + 1; + *NameSpace = AllocatePool (NameSpaceSize); + if (*NameSpace == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeStrToAsciiStrS (String, *NameSpace, NameSpaceSize); + + if (TmpPtr != NULL) { + *TmpPtr = L'&'; + } + + return EFI_SUCCESS; +} + +/** + Get Keyword from the input KeywordRequest string. + + This is a internal function. + + @param String KeywordRequestformat string. + @param Keyword return the extract keyword string. + @param NextString return the next string follow this keyword section. + + @retval EFI_SUCCESS Success to get the keyword string. + @retval EFI_INVALID_PARAMETER Parse the input string return error. + +**/ +EFI_STATUS +ExtractKeyword ( + IN EFI_STRING String, + OUT EFI_STRING *Keyword, + OUT EFI_STRING *NextString + ) +{ + EFI_STRING TmpPtr; + + ASSERT ((Keyword != NULL) && (NextString != NULL)); + + TmpPtr = NULL; + + // + // KeywordRequest == NULL case. + // + if (String == NULL) { + *Keyword = NULL; + *NextString = NULL; + return EFI_SUCCESS; + } + + // + // Skip '&' if exist. + // + if (*String == L'&') { + String++; + } + + if (StrnCmp (String, L"KEYWORD=", StrLen (L"KEYWORD=")) != 0) { + return EFI_INVALID_PARAMETER; + } + + String += StrLen (L"KEYWORD="); + + TmpPtr = StrStr (String, L"&"); + if (TmpPtr != NULL) { + *TmpPtr = 0; + } + *NextString = String + StrLen (String); + + *Keyword = AllocateCopyPool (StrSize (String), String); + if (*Keyword == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (TmpPtr != NULL) { + *TmpPtr = L'&'; + } + + return EFI_SUCCESS; +} + +/** + Get value from the input KeywordRequest string. + + This is a internal function. + + @param String KeywordRequestformat string. + @param Value return the extract value string. + @param NextString return the next string follow this keyword section. + + @retval EFI_SUCCESS Success to get the keyword string. + @retval EFI_INVALID_PARAMETER Parse the input string return error. + +**/ +EFI_STATUS +ExtractValue ( + IN EFI_STRING String, + OUT EFI_STRING *Value, + OUT EFI_STRING *NextString + ) +{ + EFI_STRING TmpPtr; + + ASSERT ((Value != NULL) && (NextString != NULL) && (String != NULL)); + + // + // Skip '&' if exist. + // + if (*String == L'&') { + String++; + } + + if (StrnCmp (String, L"VALUE=", StrLen (L"VALUE=")) != 0) { + return EFI_INVALID_PARAMETER; + } + + String += StrLen (L"VALUE="); + + TmpPtr = StrStr (String, L"&"); + if (TmpPtr != NULL) { + *TmpPtr = 0; + } + *NextString = String + StrLen (String); + + *Value = AllocateCopyPool (StrSize (String), String); + if (*Value == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (TmpPtr != NULL) { + *TmpPtr = L'&'; + } + + return EFI_SUCCESS; +} + +/** + Get filter from the input KeywordRequest string. + + This is a internal function. + + @param String KeywordRequestformat string. + @param FilterFlags return the filter condition. + @param NextString return the next string follow this keyword section. + + @retval EFI_SUCCESS Success to get the keyword string. + @retval EFI_INVALID_PARAMETER Parse the input string return error. + +**/ +BOOLEAN +ExtractFilter ( + IN EFI_STRING String, + OUT UINT8 *FilterFlags, + OUT EFI_STRING *NextString + ) +{ + CHAR16 *PathPtr; + CHAR16 *KeywordPtr; + BOOLEAN RetVal; + + ASSERT ((FilterFlags != NULL) && (NextString != NULL)); + + // + // String end, no filter section. + // + if (String == NULL) { + *NextString = NULL; + return FALSE; + } + + *FilterFlags = 0; + RetVal = TRUE; + + // + // Skip '&' if exist. + // + if (*String == L'&') { + String++; + } + + if (StrnCmp (String, L"ReadOnly", StrLen (L"ReadOnly")) == 0) { + // + // Find ReadOnly filter. + // + *FilterFlags |= EFI_KEYWORD_FILTER_READONY; + String += StrLen (L"ReadOnly"); + } else if (StrnCmp (String, L"ReadWrite", StrLen (L"ReadWrite")) == 0) { + // + // Find ReadWrite filter. + // + *FilterFlags |= EFI_KEYWORD_FILTER_REAWRITE; + String += StrLen (L"ReadWrite"); + } else if (StrnCmp (String, L"Buffer", StrLen (L"Buffer")) == 0) { + // + // Find Buffer Filter. + // + *FilterFlags |= EFI_KEYWORD_FILTER_BUFFER; + String += StrLen (L"Buffer"); + } else if (StrnCmp (String, L"Numeric", StrLen (L"Numeric")) == 0) { + // + // Find Numeric Filter + // + String += StrLen (L"Numeric"); + if (*String != L':') { + *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC; + } else { + String++; + switch (*String) { + case L'1': + *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_1; + break; + case L'2': + *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_2; + break; + case L'4': + *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_4; + break; + case L'8': + *FilterFlags |= EFI_KEYWORD_FILTER_NUMERIC_8; + break; + default: + ASSERT (FALSE); + break; + } + String++; + } + } else { + // + // Check whether other filter item defined by Platform. + // + if ((StrnCmp (String, L"&PATH", StrLen (L"&PATH")) == 0) || + (StrnCmp (String, L"&KEYWORD", StrLen (L"&KEYWORD")) == 0)) { + // + // New KeywordRequest start, no platform defined filter. + // + } else { + // + // Platform defined filter rule. + // Just skip platform defined filter rule, return success. + // + PathPtr = StrStr(String, L"&PATH"); + KeywordPtr = StrStr(String, L"&KEYWORD"); + if (PathPtr != NULL && KeywordPtr != NULL) { + // + // If both sections exist, return the first follow string. + // + String = KeywordPtr > PathPtr ? PathPtr : KeywordPtr; + } else if (PathPtr != NULL) { + // + // Should not exist PathPtr != NULL && KeywordPtr == NULL case. + // + ASSERT (FALSE); + } else if (KeywordPtr != NULL) { + // + // Just to the next keyword section. + // + String = KeywordPtr; + } else { + // + // Only has platform defined filter section, just skip it. + // + String += StrLen (String); + } + } + RetVal = FALSE; + } + + *NextString = String; + + return RetVal; +} + +/** + Extract Readonly flag from opcode. + + This is a internal function. + + @param OpCodeData Input opcode for this question. + + @retval TRUE This question is readonly. + @retval FALSE This question is not readonly. + +**/ +BOOLEAN +ExtractReadOnlyFromOpCode ( + IN UINT8 *OpCodeData + ) +{ + EFI_IFR_QUESTION_HEADER *QuestionHdr; + + ASSERT (OpCodeData != NULL); + + QuestionHdr = (EFI_IFR_QUESTION_HEADER *) (OpCodeData + sizeof (EFI_IFR_OP_HEADER)); + + return (QuestionHdr->Flags & EFI_IFR_FLAG_READ_ONLY) != 0; +} + +/** + Create a circuit to check the filter section. + + This is a internal function. + + @param OpCodeData The question binary ifr data. + @param KeywordRequest KeywordRequestformat string. + @param NextString return the next string follow this keyword section. + @param ReadOnly Return whether this question is read only. + + @retval KEYWORD_HANDLER_NO_ERROR Success validate. + @retval KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED Validate fail. + +**/ +UINT32 +ValidateFilter ( + IN UINT8 *OpCodeData, + IN CHAR16 *KeywordRequest, + OUT CHAR16 **NextString, + OUT BOOLEAN *ReadOnly + ) +{ + CHAR16 *NextFilter; + CHAR16 *StringPtr; + UINT8 FilterFlags; + EFI_IFR_QUESTION_HEADER *QuestionHdr; + EFI_IFR_OP_HEADER *OpCodeHdr; + UINT8 Flags; + UINT32 RetVal; + + RetVal = KEYWORD_HANDLER_NO_ERROR; + StringPtr = KeywordRequest; + + OpCodeHdr = (EFI_IFR_OP_HEADER *) OpCodeData; + QuestionHdr = (EFI_IFR_QUESTION_HEADER *) (OpCodeData + sizeof (EFI_IFR_OP_HEADER)); + if (OpCodeHdr->OpCode == EFI_IFR_ONE_OF_OP || OpCodeHdr->OpCode == EFI_IFR_NUMERIC_OP) { + Flags = *(OpCodeData + sizeof (EFI_IFR_OP_HEADER) + sizeof (EFI_IFR_QUESTION_HEADER)); + } else { + Flags = 0; + } + + // + // Get ReadOnly flag from Question. + // + *ReadOnly = ExtractReadOnlyFromOpCode(OpCodeData); + + while (ExtractFilter (StringPtr, &FilterFlags, &NextFilter)) { + switch (FilterFlags) { + case EFI_KEYWORD_FILTER_READONY: + if ((QuestionHdr->Flags & EFI_IFR_FLAG_READ_ONLY) == 0) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + case EFI_KEYWORD_FILTER_REAWRITE: + if ((QuestionHdr->Flags & EFI_IFR_FLAG_READ_ONLY) != 0) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + case EFI_KEYWORD_FILTER_BUFFER: + // + // Only these three opcode use numeric value type. + // + if (OpCodeHdr->OpCode == EFI_IFR_ONE_OF_OP || OpCodeHdr->OpCode == EFI_IFR_NUMERIC_OP || OpCodeHdr->OpCode == EFI_IFR_CHECKBOX_OP) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + case EFI_KEYWORD_FILTER_NUMERIC: + if (OpCodeHdr->OpCode != EFI_IFR_ONE_OF_OP && OpCodeHdr->OpCode != EFI_IFR_NUMERIC_OP && OpCodeHdr->OpCode != EFI_IFR_CHECKBOX_OP) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + case EFI_KEYWORD_FILTER_NUMERIC_1: + case EFI_KEYWORD_FILTER_NUMERIC_2: + case EFI_KEYWORD_FILTER_NUMERIC_4: + case EFI_KEYWORD_FILTER_NUMERIC_8: + if (OpCodeHdr->OpCode != EFI_IFR_ONE_OF_OP && OpCodeHdr->OpCode != EFI_IFR_NUMERIC_OP && OpCodeHdr->OpCode != EFI_IFR_CHECKBOX_OP) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + + // + // For numeric and oneof, it has flags field to specify the detail numeric type. + // + if (OpCodeHdr->OpCode == EFI_IFR_ONE_OF_OP || OpCodeHdr->OpCode == EFI_IFR_NUMERIC_OP) { + switch (Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_1) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + case EFI_IFR_NUMERIC_SIZE_2: + if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_2) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + case EFI_IFR_NUMERIC_SIZE_4: + if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_4) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + case EFI_IFR_NUMERIC_SIZE_8: + if (FilterFlags != EFI_KEYWORD_FILTER_NUMERIC_8) { + RetVal = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + goto Done; + } + break; + + default: + ASSERT (FALSE); + break; + } + } + break; + + default: + ASSERT (FALSE); + break; + } + + // + // Jump to the next filter. + // + StringPtr = NextFilter; + } + +Done: + // + // The current filter which is processing. + // + *NextString = StringPtr; + + return RetVal; +} + +/** + Get HII_DATABASE_RECORD from the input device path info. + + This is a internal function. + + @param DevicePath UEFI device path protocol. + + @retval Internal data base record. + +**/ +HII_DATABASE_RECORD * +GetRecordFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + LIST_ENTRY *Link; + UINT8 *DevicePathPkg; + UINT8 *CurrentDevicePath; + UINTN DevicePathSize; + HII_DATABASE_RECORD *TempDatabase; + + ASSERT (DevicePath != NULL); + + for (Link = mPrivate.DatabaseList.ForwardLink; Link != &mPrivate.DatabaseList; Link = Link->ForwardLink) { + TempDatabase = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE); + DevicePathPkg = TempDatabase->PackageList->DevicePathPkg; + if (DevicePathPkg != NULL) { + CurrentDevicePath = DevicePathPkg + sizeof (EFI_HII_PACKAGE_HEADER); + DevicePathSize = GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *) CurrentDevicePath); + if ((CompareMem (DevicePath, CurrentDevicePath, DevicePathSize) == 0)) { + return TempDatabase; + } + } + } + + return NULL; +} + +/** + Calculate the size of StringSrc and output it. Also copy string text from src + to dest. + + This is a internal function. + + @param StringSrc Points to current null-terminated string. + @param BufferSize Length of the buffer. + @param StringDest Buffer to store the string text. + + @retval EFI_SUCCESS The string text was outputted successfully. + @retval EFI_OUT_OF_RESOURCES Out of resource. + +**/ +EFI_STATUS +GetUnicodeStringTextAndSize ( + IN UINT8 *StringSrc, + OUT UINTN *BufferSize, + OUT EFI_STRING *StringDest + ) +{ + UINTN StringSize; + UINT8 *StringPtr; + + ASSERT (StringSrc != NULL && BufferSize != NULL && StringDest != NULL); + + StringSize = sizeof (CHAR16); + StringPtr = StringSrc; + while (ReadUnaligned16 ((UINT16 *) StringPtr) != 0) { + StringSize += sizeof (CHAR16); + StringPtr += sizeof (CHAR16); + } + + *StringDest = AllocatePool (StringSize); + if (*StringDest == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (*StringDest, StringSrc, StringSize); + + *BufferSize = StringSize; + return EFI_SUCCESS; +} + +/** + Find the string id for the input keyword. + + @param StringPackage Hii string package instance. + @param KeywordValue Input keyword value. + @param StringId The string's id, which is unique within PackageList. + + + @retval EFI_SUCCESS The string text and font is retrieved + successfully. + @retval EFI_NOT_FOUND The specified text or font info can not be found + out. + @retval EFI_OUT_OF_RESOURCES The system is out of resources to accomplish the + task. +**/ +EFI_STATUS +GetStringIdFromString ( + IN HII_STRING_PACKAGE_INSTANCE *StringPackage, + IN CHAR16 *KeywordValue, + OUT EFI_STRING_ID *StringId + ) +{ + UINT8 *BlockHdr; + EFI_STRING_ID CurrentStringId; + UINTN BlockSize; + UINTN Index; + UINT8 *StringTextPtr; + UINTN Offset; + UINT16 StringCount; + UINT16 SkipCount; + UINT8 Length8; + EFI_HII_SIBT_EXT2_BLOCK Ext2; + UINT32 Length32; + UINTN StringSize; + CHAR16 *String; + CHAR8 *AsciiKeywordValue; + UINTN KeywordValueSize; + EFI_STATUS Status; + + ASSERT (StringPackage != NULL && KeywordValue != NULL && StringId != NULL); + ASSERT (StringPackage->Signature == HII_STRING_PACKAGE_SIGNATURE); + + CurrentStringId = 1; + Status = EFI_SUCCESS; + String = NULL; + BlockHdr = StringPackage->StringBlock; + BlockSize = 0; + Offset = 0; + + // + // Make a ascii keyword value for later use. + // + KeywordValueSize = StrLen (KeywordValue) + 1; + AsciiKeywordValue = AllocatePool (KeywordValueSize); + if (AsciiKeywordValue == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeStrToAsciiStrS (KeywordValue, AsciiKeywordValue, KeywordValueSize); + + while (*BlockHdr != EFI_HII_SIBT_END) { + switch (*BlockHdr) { + case EFI_HII_SIBT_STRING_SCSU: + Offset = sizeof (EFI_HII_STRING_BLOCK); + StringTextPtr = BlockHdr + Offset; + BlockSize += Offset + AsciiStrSize ((CHAR8 *) StringTextPtr); + if (AsciiStrCmp(AsciiKeywordValue, (CHAR8 *) StringTextPtr) == 0) { + *StringId = CurrentStringId; + goto Done; + } + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRING_SCSU_FONT: + Offset = sizeof (EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK) - sizeof (UINT8); + StringTextPtr = BlockHdr + Offset; + if (AsciiStrCmp(AsciiKeywordValue, (CHAR8 *) StringTextPtr) == 0) { + *StringId = CurrentStringId; + goto Done; + } + BlockSize += Offset + AsciiStrSize ((CHAR8 *) StringTextPtr); + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRINGS_SCSU: + CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); + StringTextPtr = (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_BLOCK) - sizeof (UINT8)); + BlockSize += StringTextPtr - BlockHdr; + + for (Index = 0; Index < StringCount; Index++) { + BlockSize += AsciiStrSize ((CHAR8 *) StringTextPtr); + if (AsciiStrCmp(AsciiKeywordValue, (CHAR8 *) StringTextPtr) == 0) { + *StringId = CurrentStringId; + goto Done; + } + StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *) StringTextPtr); + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_STRINGS_SCSU_FONT: + CopyMem ( + &StringCount, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT16) + ); + StringTextPtr = (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK) - sizeof (UINT8)); + BlockSize += StringTextPtr - BlockHdr; + + for (Index = 0; Index < StringCount; Index++) { + BlockSize += AsciiStrSize ((CHAR8 *) StringTextPtr); + if (AsciiStrCmp(AsciiKeywordValue, (CHAR8 *) StringTextPtr) == 0) { + *StringId = CurrentStringId; + goto Done; + } + StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *) StringTextPtr); + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_STRING_UCS2: + Offset = sizeof (EFI_HII_STRING_BLOCK); + StringTextPtr = BlockHdr + Offset; + // + // Use StringSize to store the size of the specified string, including the NULL + // terminator. + // + Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (String != NULL); + if (StrCmp(KeywordValue, String) == 0) { + *StringId = CurrentStringId; + goto Done; + } + BlockSize += Offset + StringSize; + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRING_UCS2_FONT: + Offset = sizeof (EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK) - sizeof (CHAR16); + StringTextPtr = BlockHdr + Offset; + // + // Use StringSize to store the size of the specified string, including the NULL + // terminator. + // + Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (String != NULL); + if (StrCmp(KeywordValue, String) == 0) { + *StringId = CurrentStringId; + goto Done; + } + BlockSize += Offset + StringSize; + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRINGS_UCS2: + Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_BLOCK) - sizeof (CHAR16); + StringTextPtr = BlockHdr + Offset; + BlockSize += Offset; + CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); + for (Index = 0; Index < StringCount; Index++) { + Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (String != NULL); + BlockSize += StringSize; + if (StrCmp(KeywordValue, String) == 0) { + *StringId = CurrentStringId; + goto Done; + } + StringTextPtr = StringTextPtr + StringSize; + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_STRINGS_UCS2_FONT: + Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK) - sizeof (CHAR16); + StringTextPtr = BlockHdr + Offset; + BlockSize += Offset; + CopyMem ( + &StringCount, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT16) + ); + for (Index = 0; Index < StringCount; Index++) { + Status = GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (String != NULL); + BlockSize += StringSize; + if (StrCmp(KeywordValue, String) == 0) { + *StringId = CurrentStringId; + goto Done; + } + StringTextPtr = StringTextPtr + StringSize; + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_DUPLICATE: + BlockSize += sizeof (EFI_HII_SIBT_DUPLICATE_BLOCK); + CurrentStringId++; + break; + + case EFI_HII_SIBT_SKIP1: + SkipCount = (UINT16) (*(UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK))); + CurrentStringId = (UINT16) (CurrentStringId + SkipCount); + BlockSize += sizeof (EFI_HII_SIBT_SKIP1_BLOCK); + break; + + case EFI_HII_SIBT_SKIP2: + CopyMem (&SkipCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); + CurrentStringId = (UINT16) (CurrentStringId + SkipCount); + BlockSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK); + break; + + case EFI_HII_SIBT_EXT1: + CopyMem ( + &Length8, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT8) + ); + BlockSize += Length8; + break; + + case EFI_HII_SIBT_EXT2: + CopyMem (&Ext2, BlockHdr, sizeof (EFI_HII_SIBT_EXT2_BLOCK)); + BlockSize += Ext2.Length; + break; + + case EFI_HII_SIBT_EXT4: + CopyMem ( + &Length32, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT32) + ); + + BlockSize += Length32; + break; + + default: + break; + } + + if (String != NULL) { + FreePool (String); + String = NULL; + } + + BlockHdr = StringPackage->StringBlock + BlockSize; + } + + Status = EFI_NOT_FOUND; + +Done: + if (AsciiKeywordValue != NULL) { + FreePool (AsciiKeywordValue); + } + if (String != NULL) { + FreePool (String); + } + return Status; +} + +/** + Find the next valid string id for the input string id. + + @param StringPackage Hii string package instance. + @param StringId The current string id which is already got. + 1 means just begin to get the string id. + @param KeywordValue Return the string for the next string id. + + + @retval EFI_STRING_ID Not 0 means a valid stringid found. + 0 means not found a valid string id. +**/ +EFI_STRING_ID +GetNextStringId ( + IN HII_STRING_PACKAGE_INSTANCE *StringPackage, + IN EFI_STRING_ID StringId, + OUT EFI_STRING *KeywordValue + ) +{ + UINT8 *BlockHdr; + EFI_STRING_ID CurrentStringId; + UINTN BlockSize; + UINTN Index; + UINT8 *StringTextPtr; + UINTN Offset; + UINT16 StringCount; + UINT16 SkipCount; + UINT8 Length8; + EFI_HII_SIBT_EXT2_BLOCK Ext2; + UINT32 Length32; + BOOLEAN FindString; + UINTN StringSize; + CHAR16 *String; + + ASSERT (StringPackage != NULL); + ASSERT (StringPackage->Signature == HII_STRING_PACKAGE_SIGNATURE); + + CurrentStringId = 1; + FindString = FALSE; + String = NULL; + + // + // Parse the string blocks to get the string text and font. + // + BlockHdr = StringPackage->StringBlock; + BlockSize = 0; + Offset = 0; + while (*BlockHdr != EFI_HII_SIBT_END) { + switch (*BlockHdr) { + case EFI_HII_SIBT_STRING_SCSU: + Offset = sizeof (EFI_HII_STRING_BLOCK); + StringTextPtr = BlockHdr + Offset; + + if (FindString) { + StringSize = AsciiStrSize ((CHAR8 *) StringTextPtr); + *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); + if (*KeywordValue == NULL) { + return 0; + } + AsciiStrToUnicodeStrS ((CHAR8 *) StringTextPtr, *KeywordValue, StringSize); + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += Offset + AsciiStrSize ((CHAR8 *) StringTextPtr); + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRING_SCSU_FONT: + Offset = sizeof (EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK) - sizeof (UINT8); + StringTextPtr = BlockHdr + Offset; + + if (FindString) { + StringSize = AsciiStrSize ((CHAR8 *) StringTextPtr); + *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); + if (*KeywordValue == NULL) { + return 0; + } + AsciiStrToUnicodeStrS ((CHAR8 *) StringTextPtr, *KeywordValue, StringSize); + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += Offset + AsciiStrSize ((CHAR8 *) StringTextPtr); + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRINGS_SCSU: + CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); + StringTextPtr = (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_BLOCK) - sizeof (UINT8)); + BlockSize += StringTextPtr - BlockHdr; + + for (Index = 0; Index < StringCount; Index++) { + if (FindString) { + StringSize = AsciiStrSize ((CHAR8 *) StringTextPtr); + *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); + if (*KeywordValue == NULL) { + return 0; + } + AsciiStrToUnicodeStrS ((CHAR8 *) StringTextPtr, *KeywordValue, StringSize); + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += AsciiStrSize ((CHAR8 *) StringTextPtr); + StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *) StringTextPtr); + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_STRINGS_SCSU_FONT: + CopyMem ( + &StringCount, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT16) + ); + StringTextPtr = (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK) - sizeof (UINT8)); + BlockSize += StringTextPtr - BlockHdr; + + for (Index = 0; Index < StringCount; Index++) { + if (FindString) { + StringSize = AsciiStrSize ((CHAR8 *) StringTextPtr); + *KeywordValue = AllocatePool (StringSize * sizeof (CHAR16)); + if (*KeywordValue == NULL) { + return 0; + } + AsciiStrToUnicodeStrS ((CHAR8 *) StringTextPtr, *KeywordValue, StringSize); + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += AsciiStrSize ((CHAR8 *) StringTextPtr); + StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *) StringTextPtr); + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_STRING_UCS2: + Offset = sizeof (EFI_HII_STRING_BLOCK); + StringTextPtr = BlockHdr + Offset; + // + // Use StringSize to store the size of the specified string, including the NULL + // terminator. + // + GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + if (FindString && (String != NULL) && (*String != L'\0')) { + // + // String protocol use this type for the string id which has value for other package. + // It will allocate an empty string block for this string id. so here we also check + // *String != L'\0' to prohibit this case. + // + *KeywordValue = String; + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += Offset + StringSize; + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRING_UCS2_FONT: + Offset = sizeof (EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK) - sizeof (CHAR16); + StringTextPtr = BlockHdr + Offset; + // + // Use StringSize to store the size of the specified string, including the NULL + // terminator. + // + GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + if (FindString) { + *KeywordValue = String; + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += Offset + StringSize; + CurrentStringId++; + break; + + case EFI_HII_SIBT_STRINGS_UCS2: + Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_BLOCK) - sizeof (CHAR16); + StringTextPtr = BlockHdr + Offset; + BlockSize += Offset; + CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); + for (Index = 0; Index < StringCount; Index++) { + GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + + if (FindString) { + *KeywordValue = String; + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += StringSize; + StringTextPtr = StringTextPtr + StringSize; + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_STRINGS_UCS2_FONT: + Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK) - sizeof (CHAR16); + StringTextPtr = BlockHdr + Offset; + BlockSize += Offset; + CopyMem ( + &StringCount, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT16) + ); + for (Index = 0; Index < StringCount; Index++) { + GetUnicodeStringTextAndSize (StringTextPtr, &StringSize, &String); + if (FindString) { + *KeywordValue = String; + return CurrentStringId; + } else if (CurrentStringId == StringId) { + FindString = TRUE; + } + + BlockSize += StringSize; + StringTextPtr = StringTextPtr + StringSize; + CurrentStringId++; + } + break; + + case EFI_HII_SIBT_DUPLICATE: + BlockSize += sizeof (EFI_HII_SIBT_DUPLICATE_BLOCK); + CurrentStringId++; + break; + + case EFI_HII_SIBT_SKIP1: + SkipCount = (UINT16) (*(UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK))); + CurrentStringId = (UINT16) (CurrentStringId + SkipCount); + BlockSize += sizeof (EFI_HII_SIBT_SKIP1_BLOCK); + break; + + case EFI_HII_SIBT_SKIP2: + CopyMem (&SkipCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16)); + CurrentStringId = (UINT16) (CurrentStringId + SkipCount); + BlockSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK); + break; + + case EFI_HII_SIBT_EXT1: + CopyMem ( + &Length8, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT8) + ); + BlockSize += Length8; + break; + + case EFI_HII_SIBT_EXT2: + CopyMem (&Ext2, BlockHdr, sizeof (EFI_HII_SIBT_EXT2_BLOCK)); + BlockSize += Ext2.Length; + break; + + case EFI_HII_SIBT_EXT4: + CopyMem ( + &Length32, + (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)), + sizeof (UINT32) + ); + + BlockSize += Length32; + break; + + default: + break; + } + + if (String != NULL) { + FreePool (String); + String = NULL; + } + + BlockHdr = StringPackage->StringBlock + BlockSize; + } + + return 0; +} + +/** + Get string package from the input NameSpace string. + + This is a internal function. + + @param DatabaseRecord HII_DATABASE_RECORD format string. + @param NameSpace NameSpace format string. + @param KeywordValue Keyword value. + @param StringId String Id for this keyword. + + @retval KEYWORD_HANDLER_NO_ERROR Get String id successfully. + @retval KEYWORD_HANDLER_KEYWORD_NOT_FOUND Not found the string id in the string package. + @retval KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND Not found the string package for this namespace. + @retval KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR Out of resource error. + +**/ +UINT32 +GetStringIdFromRecord ( + IN HII_DATABASE_RECORD *DatabaseRecord, + IN CHAR8 **NameSpace, + IN CHAR16 *KeywordValue, + OUT EFI_STRING_ID *StringId + ) +{ + LIST_ENTRY *Link; + HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; + HII_STRING_PACKAGE_INSTANCE *StringPackage; + EFI_STATUS Status; + CHAR8 *Name; + UINT32 RetVal; + + ASSERT (DatabaseRecord != NULL && NameSpace != NULL && KeywordValue != NULL); + + PackageListNode = DatabaseRecord->PackageList; + RetVal = KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND; + + if (*NameSpace != NULL) { + Name = *NameSpace; + } else { + Name = UEFI_CONFIG_LANG; + } + + for (Link = PackageListNode->StringPkgHdr.ForwardLink; Link != &PackageListNode->StringPkgHdr; Link = Link->ForwardLink) { + StringPackage = CR (Link, HII_STRING_PACKAGE_INSTANCE, StringEntry, HII_STRING_PACKAGE_SIGNATURE); + + if (AsciiStrnCmp(Name, StringPackage->StringPkgHdr->Language, AsciiStrLen (Name)) == 0) { + Status = GetStringIdFromString (StringPackage, KeywordValue, StringId); + if (EFI_ERROR (Status)) { + return KEYWORD_HANDLER_KEYWORD_NOT_FOUND; + } else { + if (*NameSpace == NULL) { + *NameSpace = AllocateCopyPool (AsciiStrSize (StringPackage->StringPkgHdr->Language), StringPackage->StringPkgHdr->Language); + if (*NameSpace == NULL) { + return KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR; + } + } + return KEYWORD_HANDLER_NO_ERROR; + } + } + } + + return RetVal; +} + +/** + Tell whether this Operand is an Statement OpCode. + + @param Operand Operand of an IFR OpCode. + + @retval TRUE This is an Statement OpCode. + @retval FALSE Not an Statement OpCode. + +**/ +BOOLEAN +IsStatementOpCode ( + IN UINT8 Operand + ) +{ + if ((Operand == EFI_IFR_SUBTITLE_OP) || + (Operand == EFI_IFR_TEXT_OP) || + (Operand == EFI_IFR_RESET_BUTTON_OP) || + (Operand == EFI_IFR_REF_OP) || + (Operand == EFI_IFR_ACTION_OP) || + (Operand == EFI_IFR_NUMERIC_OP) || + (Operand == EFI_IFR_ORDERED_LIST_OP) || + (Operand == EFI_IFR_CHECKBOX_OP) || + (Operand == EFI_IFR_STRING_OP) || + (Operand == EFI_IFR_PASSWORD_OP) || + (Operand == EFI_IFR_DATE_OP) || + (Operand == EFI_IFR_TIME_OP) || + (Operand == EFI_IFR_GUID_OP) || + (Operand == EFI_IFR_ONE_OF_OP)) { + return TRUE; + } + + return FALSE; +} + +/** + Tell whether this Operand is an Statement OpCode. + + @param Operand Operand of an IFR OpCode. + + @retval TRUE This is an Statement OpCode. + @retval FALSE Not an Statement OpCode. + +**/ +BOOLEAN +IsStorageOpCode ( + IN UINT8 Operand + ) +{ + if ((Operand == EFI_IFR_VARSTORE_OP) || + (Operand == EFI_IFR_VARSTORE_NAME_VALUE_OP) || + (Operand == EFI_IFR_VARSTORE_EFI_OP)) { + return TRUE; + } + + return FALSE; +} + +/** + Base on the prompt string id to find the question. + + @param FormPackage The input form package. + @param KeywordStrId The input prompt string id for one question. + + @retval the opcode for the question. + +**/ +UINT8 * +FindQuestionFromStringId ( + IN HII_IFR_PACKAGE_INSTANCE *FormPackage, + IN EFI_STRING_ID KeywordStrId + ) +{ + UINT8 *OpCodeData; + UINT32 Offset; + EFI_IFR_STATEMENT_HEADER *StatementHeader; + EFI_IFR_OP_HEADER *OpCodeHeader; + UINT32 FormDataLen; + + ASSERT (FormPackage != NULL); + + FormDataLen = FormPackage->FormPkgHdr.Length - sizeof (EFI_HII_PACKAGE_HEADER); + Offset = 0; + while (Offset < FormDataLen) { + OpCodeData = FormPackage->IfrData + Offset; + OpCodeHeader = (EFI_IFR_OP_HEADER *) OpCodeData; + + if (IsStatementOpCode(OpCodeHeader->OpCode)) { + StatementHeader = (EFI_IFR_STATEMENT_HEADER *) (OpCodeData + sizeof (EFI_IFR_OP_HEADER)); + if (StatementHeader->Prompt == KeywordStrId) { + return OpCodeData; + } + } + + Offset += OpCodeHeader->Length; + } + + return NULL; +} + +/** + Base on the varstore id to find the storage info. + + @param FormPackage The input form package. + @param VarStoreId The input storage id. + + @retval the opcode for the storage. + +**/ +UINT8 * +FindStorageFromVarId ( + IN HII_IFR_PACKAGE_INSTANCE *FormPackage, + IN EFI_VARSTORE_ID VarStoreId + ) +{ + UINT8 *OpCodeData; + UINT32 Offset; + EFI_IFR_OP_HEADER *OpCodeHeader; + UINT32 FormDataLen; + + ASSERT (FormPackage != NULL); + + FormDataLen = FormPackage->FormPkgHdr.Length - sizeof (EFI_HII_PACKAGE_HEADER); + Offset = 0; + while (Offset < FormDataLen) { + OpCodeData = FormPackage->IfrData + Offset; + OpCodeHeader = (EFI_IFR_OP_HEADER *) OpCodeData; + + if (IsStorageOpCode(OpCodeHeader->OpCode)) { + switch (OpCodeHeader->OpCode) { + case EFI_IFR_VARSTORE_OP: + if (VarStoreId == ((EFI_IFR_VARSTORE *) OpCodeData)->VarStoreId) { + return OpCodeData; + } + break; + + case EFI_IFR_VARSTORE_NAME_VALUE_OP: + if (VarStoreId == ((EFI_IFR_VARSTORE_NAME_VALUE *) OpCodeData)->VarStoreId) { + return OpCodeData; + } + break; + + case EFI_IFR_VARSTORE_EFI_OP: + if (VarStoreId == ((EFI_IFR_VARSTORE_EFI *) OpCodeData)->VarStoreId) { + return OpCodeData; + } + break; + + default: + break; + } + } + + Offset += OpCodeHeader->Length; + } + + return NULL; +} + +/** + Get width info for one question. + + @param OpCodeData The input opcode for one question. + + @retval the width info for one question. + +**/ +UINT16 +GetWidth ( + IN UINT8 *OpCodeData + ) +{ + UINT8 *NextOpCodeData; + + ASSERT (OpCodeData != NULL); + + switch (((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode) { + case EFI_IFR_REF_OP: + return (UINT16) sizeof (EFI_HII_REF); + + case EFI_IFR_ONE_OF_OP: + case EFI_IFR_NUMERIC_OP: + switch (((EFI_IFR_ONE_OF *) OpCodeData)->Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + return (UINT16) sizeof (UINT8); + + case EFI_IFR_NUMERIC_SIZE_2: + return (UINT16) sizeof (UINT16); + + case EFI_IFR_NUMERIC_SIZE_4: + return (UINT16) sizeof (UINT32); + + case EFI_IFR_NUMERIC_SIZE_8: + return (UINT16) sizeof (UINT64); + + default: + ASSERT (FALSE); + return 0; + } + + case EFI_IFR_ORDERED_LIST_OP: + NextOpCodeData = OpCodeData + ((EFI_IFR_ORDERED_LIST *) OpCodeData)->Header.Length; + // + // OneOfOption must follow the orderedlist opcode. + // + ASSERT (((EFI_IFR_OP_HEADER *) NextOpCodeData)->OpCode == EFI_IFR_ONE_OF_OPTION_OP); + switch (((EFI_IFR_ONE_OF_OPTION *) NextOpCodeData)->Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + return (UINT16) sizeof (UINT8) * ((EFI_IFR_ORDERED_LIST *) OpCodeData)->MaxContainers; + + case EFI_IFR_TYPE_NUM_SIZE_16: + return (UINT16) sizeof (UINT16) * ((EFI_IFR_ORDERED_LIST *) OpCodeData)->MaxContainers ; + + case EFI_IFR_TYPE_NUM_SIZE_32: + return (UINT16) sizeof (UINT32) * ((EFI_IFR_ORDERED_LIST *) OpCodeData)->MaxContainers; + + case EFI_IFR_TYPE_NUM_SIZE_64: + return (UINT16) sizeof (UINT64) * ((EFI_IFR_ORDERED_LIST *) OpCodeData)->MaxContainers; + + default: + ASSERT (FALSE); + return 0; + } + + case EFI_IFR_CHECKBOX_OP: + return (UINT16) sizeof (BOOLEAN); + + case EFI_IFR_PASSWORD_OP: + return (UINT16)((UINTN) ((EFI_IFR_PASSWORD *) OpCodeData)->MaxSize * sizeof (CHAR16)); + + case EFI_IFR_STRING_OP: + return (UINT16)((UINTN) ((EFI_IFR_STRING *) OpCodeData)->MaxSize * sizeof (CHAR16)); + + case EFI_IFR_DATE_OP: + return (UINT16) sizeof (EFI_HII_DATE); + + case EFI_IFR_TIME_OP: + return (UINT16) sizeof (EFI_HII_TIME); + + default: + ASSERT (FALSE); + return 0; + } +} + +/** + Converts all hex string characters in range ['A'..'F'] to ['a'..'f'] for + hex digits that appear between a '=' and a '&' in a config string. + + If ConfigString is NULL, then ASSERT(). + + @param[in] ConfigString Pointer to a Null-terminated Unicode string. + + @return Pointer to the Null-terminated Unicode result string. + +**/ +EFI_STRING +EFIAPI +InternalLowerConfigString ( + IN EFI_STRING ConfigString + ) +{ + EFI_STRING String; + BOOLEAN Lower; + + ASSERT (ConfigString != NULL); + + // + // Convert all hex digits in range [A-F] in the configuration header to [a-f] + // + for (String = ConfigString, Lower = FALSE; *String != L'\0'; String++) { + if (*String == L'=') { + Lower = TRUE; + } else if (*String == L'&') { + Lower = FALSE; + } else if (Lower && *String >= L'A' && *String <= L'F') { + *String = (CHAR16) (*String - L'A' + L'a'); + } + } + + return ConfigString; +} + +/** + Allocates and returns a Null-terminated Unicode string. + + The format of a is as follows: + + GUID=32&NAME=NameLength&PATH=DevicePathSize + + @param[in] OpCodeData The opcode for the storage. + @param[in] DriverHandle The driver handle which supports a Device Path Protocol + that is the routing information PATH. Each byte of + the Device Path associated with DriverHandle is converted + to a 2 Unicode character hexadecimal string. + + @retval NULL DriverHandle does not support the Device Path Protocol. + @retval Other A pointer to the Null-terminate Unicode string + +**/ +EFI_STRING +ConstructConfigHdr ( + IN UINT8 *OpCodeData, + IN EFI_HANDLE DriverHandle + ) +{ + UINTN NameLength; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN DevicePathSize; + CHAR16 *String; + CHAR16 *ReturnString; + UINTN Index; + UINT8 *Buffer; + CHAR16 *Name; + CHAR8 *AsciiName; + UINTN NameSize; + EFI_GUID *Guid; + UINTN MaxLen; + + ASSERT (OpCodeData != NULL); + + switch (((EFI_IFR_OP_HEADER *)OpCodeData)->OpCode) { + case EFI_IFR_VARSTORE_OP: + Guid = (EFI_GUID *)(UINTN *)&((EFI_IFR_VARSTORE *) OpCodeData)->Guid; + AsciiName = (CHAR8 *) ((EFI_IFR_VARSTORE *) OpCodeData)->Name; + break; + + case EFI_IFR_VARSTORE_NAME_VALUE_OP: + Guid = (EFI_GUID *)(UINTN *)&((EFI_IFR_VARSTORE_NAME_VALUE *) OpCodeData)->Guid; + AsciiName = NULL; + break; + + case EFI_IFR_VARSTORE_EFI_OP: + Guid = (EFI_GUID *)(UINTN *)&((EFI_IFR_VARSTORE_EFI *) OpCodeData)->Guid; + AsciiName = (CHAR8 *) ((EFI_IFR_VARSTORE_EFI *) OpCodeData)->Name; + break; + + default: + ASSERT (FALSE); + Guid = NULL; + AsciiName = NULL; + break; + } + + if (AsciiName != NULL) { + NameSize = AsciiStrSize (AsciiName); + Name = AllocateZeroPool (NameSize * sizeof (CHAR16)); + ASSERT (Name != NULL); + AsciiStrToUnicodeStrS (AsciiName, Name, NameSize); + } else { + Name = NULL; + } + + // + // Compute the length of Name in Unicode characters. + // If Name is NULL, then the length is 0. + // + NameLength = 0; + if (Name != NULL) { + NameLength = StrLen (Name); + } + + DevicePath = NULL; + DevicePathSize = 0; + // + // Retrieve DevicePath Protocol associated with DriverHandle + // + if (DriverHandle != NULL) { + DevicePath = DevicePathFromHandle (DriverHandle); + if (DevicePath == NULL) { + return NULL; + } + // + // Compute the size of the device path in bytes + // + DevicePathSize = GetDevicePathSize (DevicePath); + } + + // + // GUID=32&NAME=NameLength&PATH=DevicePathSize + // | 5 | sizeof (EFI_GUID) * 2 | 6 | NameStrLen*4 | 6 | DevicePathSize * 2 | 1 | + // + MaxLen = 5 + sizeof (EFI_GUID) * 2 + 6 + NameLength * 4 + 6 + DevicePathSize * 2 + 1; + String = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + if (String == NULL) { + return NULL; + } + + // + // Start with L"GUID=" + // + StrCpyS (String, MaxLen, L"GUID="); + ReturnString = String; + String += StrLen (String); + + if (Guid != NULL) { + // + // Append Guid converted to 32 + // + for (Index = 0, Buffer = (UINT8 *)Guid; Index < sizeof (EFI_GUID); Index++) { + UnicodeValueToStringS ( + String, + MaxLen * sizeof (CHAR16) - ((UINTN)String - (UINTN)ReturnString), + PREFIX_ZERO | RADIX_HEX, + *(Buffer++), + 2 + ); + String += StrnLenS (String, MaxLen - ((UINTN)String - (UINTN)ReturnString) / sizeof (CHAR16)); + } + } + + // + // Append L"&NAME=" + // + StrCatS (ReturnString, MaxLen, L"&NAME="); + String += StrLen (String); + + if (Name != NULL) { + // + // Append Name converted to NameLength + // + for (; *Name != L'\0'; Name++) { + UnicodeValueToStringS ( + String, + MaxLen * sizeof (CHAR16) - ((UINTN)String - (UINTN)ReturnString), + PREFIX_ZERO | RADIX_HEX, + *Name, + 4 + ); + String += StrnLenS (String, MaxLen - ((UINTN)String - (UINTN)ReturnString) / sizeof (CHAR16)); + } + } + + // + // Append L"&PATH=" + // + StrCatS (ReturnString, MaxLen, L"&PATH="); + String += StrLen (String); + + // + // Append the device path associated with DriverHandle converted to DevicePathSize + // + for (Index = 0, Buffer = (UINT8 *)DevicePath; Index < DevicePathSize; Index++) { + UnicodeValueToStringS ( + String, + MaxLen * sizeof (CHAR16) - ((UINTN)String - (UINTN)ReturnString), + PREFIX_ZERO | RADIX_HEX, + *(Buffer++), + 2 + ); + String += StrnLenS (String, MaxLen - ((UINTN)String - (UINTN)ReturnString) / sizeof (CHAR16)); + } + + // + // Null terminate the Unicode string + // + *String = L'\0'; + + // + // Convert all hex digits in range [A-F] in the configuration header to [a-f] + // + return InternalLowerConfigString (ReturnString); +} + +/** + Generate the Config request element for one question. + + @param Name The name info for one question. + @param Offset The offset info for one question. + @param Width The width info for one question. + + @return Pointer to the Null-terminated Unicode request element string. + +**/ +EFI_STRING +ConstructRequestElement ( + IN CHAR16 *Name, + IN UINT16 Offset, + IN UINT16 Width + ) +{ + CHAR16 *StringPtr; + UINTN Length; + + if (Name != NULL) { + // + // Add length for each Name + // + // ::= Name + \0 + // StrLen(Name) | 1 + // + Length = StrLen (Name) + 1; + } else { + // + // Add length for each Offset/Width pair + // + // ::= OFFSET=1234&WIDTH=1234 + \0 + // | 7 | 4 | 7 | 4 | 1 + // + Length = (7 + 4 + 7 + 4 + 1); + } + + // + // Allocate buffer for the entire + // + StringPtr = AllocateZeroPool (Length * sizeof (CHAR16)); + ASSERT (StringPtr != NULL); + + if (Name != NULL) { + // + // Append Name\0 + // + UnicodeSPrint ( + StringPtr, + (StrLen (Name) + 1) * sizeof (CHAR16), + L"%s", + Name + ); + } else { + // + // Append OFFSET=XXXX&WIDTH=YYYY\0 + // + UnicodeSPrint ( + StringPtr, + (7 + 4 + 7 + 4 + 1) * sizeof (CHAR16), + L"OFFSET=%04X&WIDTH=%04X", + Offset, + Width + ); + } + + return StringPtr; +} + +/** + Get string value for question's name field. + + @param DatabaseRecord HII_DATABASE_RECORD format string. + @param NameId The string id for the name field. + + @retval Name string. + +**/ +CHAR16 * +GetNameFromId ( + IN HII_DATABASE_RECORD *DatabaseRecord, + IN EFI_STRING_ID NameId + ) +{ + CHAR16 *Name; + CHAR8 *PlatformLanguage; + CHAR8 *SupportedLanguages; + CHAR8 *BestLanguage; + UINTN StringSize; + CHAR16 TempString; + EFI_STATUS Status; + + Name = NULL; + BestLanguage = NULL; + PlatformLanguage = NULL; + SupportedLanguages = NULL; + + GetEfiGlobalVariable2 (L"PlatformLang", (VOID**)&PlatformLanguage, NULL); + SupportedLanguages = GetSupportedLanguages(DatabaseRecord->Handle); + + // + // Get the best matching language from SupportedLanguages + // + BestLanguage = GetBestLanguage ( + SupportedLanguages, + FALSE, // RFC 4646 mode + PlatformLanguage != NULL ? PlatformLanguage : "", // Highest priority + SupportedLanguages, // Lowest priority + NULL + ); + if (BestLanguage == NULL) { + BestLanguage = AllocateCopyPool (AsciiStrLen ("en-US"), "en-US"); + ASSERT (BestLanguage != NULL); + } + + StringSize = 0; + Status = mPrivate.HiiString.GetString ( + &mPrivate.HiiString, + BestLanguage, + DatabaseRecord->Handle, + NameId, + &TempString, + &StringSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + goto Done; + } + + Name = AllocateZeroPool (StringSize); + if (Name == NULL) { + goto Done; + } + + Status = mPrivate.HiiString.GetString ( + &mPrivate.HiiString, + BestLanguage, + DatabaseRecord->Handle, + NameId, + Name, + &StringSize, + NULL + ); + + if (EFI_ERROR (Status)) { + FreePool (Name); + Name = NULL; + goto Done; + } + +Done: + if (SupportedLanguages != NULL) { + FreePool(SupportedLanguages); + } + if (BestLanguage != NULL) { + FreePool (BestLanguage); + } + if (PlatformLanguage != NULL) { + FreePool (PlatformLanguage); + } + + return Name; +} + +/** + Base on the input parameter to generate the ConfigRequest string. + + This is a internal function. + + @param DatabaseRecord HII_DATABASE_RECORD format string. + @param KeywordStrId Keyword string id. + @param OpCodeData The IFR data for this question. + @param ConfigRequest Return the generate ConfigRequest string. + + @retval EFI_SUCCESS Generate ConfigResp string success. + @retval EFI_OUT_OF_RESOURCES System out of memory resource error. + @retval EFI_NOT_FOUND Not found the question which use this string id + as the prompt string id. +**/ +EFI_STATUS +ExtractConfigRequest ( + IN HII_DATABASE_RECORD *DatabaseRecord, + IN EFI_STRING_ID KeywordStrId, + OUT UINT8 **OpCodeData, + OUT EFI_STRING *ConfigRequest + ) +{ + LIST_ENTRY *Link; + HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; + HII_IFR_PACKAGE_INSTANCE *FormPackage; + EFI_IFR_QUESTION_HEADER *Header; + UINT8 *Storage; + UINT8 *OpCode; + CHAR16 *Name; + UINT16 Offset; + UINT16 Width; + CHAR16 *ConfigHdr; + CHAR16 *RequestElement; + UINTN MaxLen; + CHAR16 *StringPtr; + + ASSERT (DatabaseRecord != NULL && OpCodeData != NULL && ConfigRequest != NULL); + + OpCode = NULL; + Name = NULL; + Width = 0; + Offset = 0; + + PackageListNode = DatabaseRecord->PackageList; + + // + // Search the languages in the specified packagelist. + // + for (Link = PackageListNode->FormPkgHdr.ForwardLink; Link != &PackageListNode->FormPkgHdr; Link = Link->ForwardLink) { + FormPackage = CR (Link, HII_IFR_PACKAGE_INSTANCE, IfrEntry, HII_IFR_PACKAGE_SIGNATURE); + + OpCode = FindQuestionFromStringId (FormPackage, KeywordStrId); + if (OpCode != NULL) { + *OpCodeData = OpCode; + Header = (EFI_IFR_QUESTION_HEADER *) (OpCode + sizeof (EFI_IFR_OP_HEADER)); + // + // Header->VarStoreId == 0 means no storage for this question. + // + ASSERT (Header->VarStoreId != 0); + DEBUG ((EFI_D_INFO, "Varstore Id: 0x%x\n", Header->VarStoreId)); + + Storage = FindStorageFromVarId (FormPackage, Header->VarStoreId); + ASSERT (Storage != NULL); + + if (((EFI_IFR_OP_HEADER *) Storage)->OpCode == EFI_IFR_VARSTORE_NAME_VALUE_OP) { + Name = GetNameFromId (DatabaseRecord, Header->VarStoreInfo.VarName); + } else { + Offset = Header->VarStoreInfo.VarOffset; + Width = GetWidth (OpCode); + } + RequestElement = ConstructRequestElement(Name, Offset, Width); + ConfigHdr = ConstructConfigHdr(Storage, DatabaseRecord->DriverHandle); + ASSERT (ConfigHdr != NULL); + + MaxLen = StrLen (ConfigHdr) + 1 + StrLen(RequestElement) + 1; + *ConfigRequest = AllocatePool (MaxLen * sizeof (CHAR16)); + if (*ConfigRequest == NULL) { + FreePool (ConfigHdr); + FreePool (RequestElement); + return EFI_OUT_OF_RESOURCES; + } + StringPtr = *ConfigRequest; + + StrCpyS (StringPtr, MaxLen, ConfigHdr); + + StrCatS (StringPtr, MaxLen, L"&"); + + StrCatS (StringPtr, MaxLen, RequestElement); + + FreePool (ConfigHdr); + FreePool (RequestElement); + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Base on the input parameter to generate the ConfigResp string. + + This is a internal function. + + @param DatabaseRecord HII_DATABASE_RECORD format string. + @param KeywordStrId Keyword string id. + @param ValueElement The value for the question which use keyword string id + as the prompt string id. + @param OpCodeData The IFR data for this question. + @param ConfigResp Return the generate ConfigResp string. + + @retval EFI_SUCCESS Generate ConfigResp string success. + @retval EFI_OUT_OF_RESOURCES System out of memory resource error. + @retval EFI_NOT_FOUND Not found the question which use this string id + as the prompt string id. +**/ +EFI_STATUS +ExtractConfigResp ( + IN HII_DATABASE_RECORD *DatabaseRecord, + IN EFI_STRING_ID KeywordStrId, + IN EFI_STRING ValueElement, + OUT UINT8 **OpCodeData, + OUT EFI_STRING *ConfigResp + ) +{ + LIST_ENTRY *Link; + HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; + HII_IFR_PACKAGE_INSTANCE *FormPackage; + EFI_IFR_QUESTION_HEADER *Header; + UINT8 *Storage; + UINT8 *OpCode; + CHAR16 *Name; + UINT16 Offset; + UINT16 Width; + CHAR16 *ConfigHdr; + CHAR16 *RequestElement; + UINTN MaxLen; + CHAR16 *StringPtr; + + ASSERT ((DatabaseRecord != NULL) && (OpCodeData != NULL) && (ConfigResp != NULL) && (ValueElement != NULL)); + + OpCode = NULL; + Name = NULL; + Width = 0; + Offset = 0; + + PackageListNode = DatabaseRecord->PackageList; + + // + // Search the languages in the specified packagelist. + // + for (Link = PackageListNode->FormPkgHdr.ForwardLink; Link != &PackageListNode->FormPkgHdr; Link = Link->ForwardLink) { + FormPackage = CR (Link, HII_IFR_PACKAGE_INSTANCE, IfrEntry, HII_IFR_PACKAGE_SIGNATURE); + + OpCode = FindQuestionFromStringId (FormPackage, KeywordStrId); + if (OpCode != NULL) { + *OpCodeData = OpCode; + Header = (EFI_IFR_QUESTION_HEADER *) (OpCode + sizeof (EFI_IFR_OP_HEADER)); + // + // Header->VarStoreId == 0 means no storage for this question. + // + ASSERT (Header->VarStoreId != 0); + DEBUG ((EFI_D_INFO, "Varstore Id: 0x%x\n", Header->VarStoreId)); + + Storage = FindStorageFromVarId (FormPackage, Header->VarStoreId); + ASSERT (Storage != NULL); + + if (((EFI_IFR_OP_HEADER *) Storage)->OpCode == EFI_IFR_VARSTORE_NAME_VALUE_OP) { + Name = GetNameFromId (DatabaseRecord, Header->VarStoreInfo.VarName); + } else { + Offset = Header->VarStoreInfo.VarOffset; + Width = GetWidth (OpCode); + } + RequestElement = ConstructRequestElement(Name, Offset, Width); + + ConfigHdr = ConstructConfigHdr(Storage, DatabaseRecord->DriverHandle); + ASSERT (ConfigHdr != NULL); + + MaxLen = StrLen (ConfigHdr) + 1 + StrLen(RequestElement) + 1 + StrLen (L"VALUE=") + StrLen(ValueElement) + 1; + *ConfigResp = AllocatePool (MaxLen * sizeof (CHAR16)); + if (*ConfigResp == NULL) { + FreePool (ConfigHdr); + FreePool (RequestElement); + return EFI_OUT_OF_RESOURCES; + } + StringPtr = *ConfigResp; + + StrCpyS (StringPtr, MaxLen, ConfigHdr); + + StrCatS (StringPtr, MaxLen, L"&"); + + + StrCatS (StringPtr, MaxLen, RequestElement); + + StrCatS (StringPtr, MaxLen, L"&"); + + StrCatS (StringPtr, MaxLen, L"VALUE="); + + StrCatS (StringPtr, MaxLen, ValueElement); + + FreePool (ConfigHdr); + FreePool (RequestElement); + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Get the Value section from the Hii driver. + + This is a internal function. + + @param ConfigRequest The input ConfigRequest string. + @param ValueElement The respond Value section from the hii driver. + + @retval Misc value The error status return from ExtractConfig function. + @retval EFI_OUT_OF_RESOURCES The memory can't be allocated + @retval EFI_SUCCESS Get the value section success. + +**/ +EFI_STATUS +ExtractValueFromDriver ( + IN CHAR16 *ConfigRequest, + OUT CHAR16 **ValueElement + ) +{ + EFI_STATUS Status; + EFI_STRING Result; + EFI_STRING Progress; + CHAR16 *StringPtr; + CHAR16 *StringEnd; + + ASSERT ((ConfigRequest != NULL) && (ValueElement != NULL)); + + Status = mPrivate.ConfigRouting.ExtractConfig ( + &mPrivate.ConfigRouting, + (EFI_STRING) ConfigRequest, + &Progress, + &Result + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find Value Section and return it. + // + StringPtr = StrStr (Result, L"&VALUE="); + ASSERT (StringPtr != NULL); + StringEnd = StrStr (StringPtr + 1, L"&"); + if (StringEnd != NULL) { + *StringEnd = L'\0'; + } + + *ValueElement = AllocateCopyPool (StrSize (StringPtr), StringPtr); + if (*ValueElement == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (StringEnd != NULL) { + *StringEnd = L'&'; + } + FreePool (Result); + + return EFI_SUCCESS; +} + +/** + Get EFI_STRING_ID info from the input device path, namespace and keyword. + + This is a internal function. + + @param DevicePath Input device path info. + @param NameSpace NameSpace format string. + @param KeywordData Keyword used to get string id. + @param ProgressErr Return extra error type. + @param KeywordStringId Return EFI_STRING_ID. + @param DataBaseRecord DataBase record data for this driver. + + @retval EFI_INVALID_PARAMETER Can't find the database record base on the input device path or namespace. + @retval EFI_NOT_FOUND Can't find the EFI_STRING_ID for the keyword. + @retval EFI_SUCCESS Find the EFI_STRING_ID. + +**/ +EFI_STATUS +GetStringIdFromDatabase ( + IN EFI_DEVICE_PATH_PROTOCOL **DevicePath, + IN CHAR8 **NameSpace, + IN CHAR16 *KeywordData, + OUT UINT32 *ProgressErr, + OUT EFI_STRING_ID *KeywordStringId, + OUT HII_DATABASE_RECORD **DataBaseRecord + ) +{ + HII_DATABASE_RECORD *Record; + LIST_ENTRY *Link; + BOOLEAN FindNameSpace; + EFI_DEVICE_PATH_PROTOCOL *DestDevicePath; + UINT8 *DevicePathPkg; + UINTN DevicePathSize; + + ASSERT ((NameSpace != NULL) && (KeywordData != NULL) && (ProgressErr != NULL) && (KeywordStringId != NULL) && (DataBaseRecord != NULL)); + + FindNameSpace = FALSE; + + if (*DevicePath != NULL) { + // + // Get DataBaseRecord from device path protocol. + // + Record = GetRecordFromDevicePath(*DevicePath); + if (Record == NULL) { + // + // Can't find the DatabaseRecord base on the input device path info. + // NEED TO CONFIRM the return ProgressErr. + // + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + return EFI_INVALID_PARAMETER; + } + + // + // Get string id from the record. + // + *ProgressErr = GetStringIdFromRecord (Record, NameSpace, KeywordData, KeywordStringId); + switch (*ProgressErr) { + case KEYWORD_HANDLER_NO_ERROR: + *DataBaseRecord = Record; + return EFI_SUCCESS; + + case KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND: + return EFI_INVALID_PARAMETER; + + default: + ASSERT (*ProgressErr == KEYWORD_HANDLER_KEYWORD_NOT_FOUND); + return EFI_NOT_FOUND; + } + } else { + // + // Find driver which matches the routing data. + // + for (Link = mPrivate.DatabaseList.ForwardLink; Link != &mPrivate.DatabaseList; Link = Link->ForwardLink) { + Record = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE); + + *ProgressErr = GetStringIdFromRecord (Record, NameSpace, KeywordData, KeywordStringId); + if (*ProgressErr == KEYWORD_HANDLER_NO_ERROR) { + *DataBaseRecord = Record; + + if ((DevicePathPkg = Record->PackageList->DevicePathPkg) != NULL) { + DestDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) (DevicePathPkg + sizeof (EFI_HII_PACKAGE_HEADER)); + DevicePathSize = GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *) DestDevicePath); + *DevicePath = AllocateCopyPool (DevicePathSize, DestDevicePath); + if (*DevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + // + // Need to verify this ASSERT. + // + ASSERT (FALSE); + } + + return EFI_SUCCESS; + } else if (*ProgressErr == KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR) { + return EFI_OUT_OF_RESOURCES; + } else if (*ProgressErr == KEYWORD_HANDLER_KEYWORD_NOT_FOUND) { + FindNameSpace = TRUE; + } + } + + // + // When PathHdr not input, if ever find the namespace, will return KEYWORD_HANDLER_KEYWORD_NOT_FOUND. + // This is a bit more progress than KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND. + // + if (FindNameSpace) { + return EFI_NOT_FOUND; + } else { + return EFI_INVALID_PARAMETER; + } + } +} + +/** + Generate the KeywordResp String. + + ::= '&''&VALUE='['&READONLY'] + + @param NameSpace NameSpace format string. + @param DevicePath Input device path info. + @param KeywordData Keyword used to get string id. + @param ValueStr The value section for the keyword. + @param ReadOnly Whether this value is readonly. + @param KeywordResp Return the point to the KeywordResp string. + + @retval EFI_OUT_OF_RESOURCES The memory can't be allocated. + @retval EFI_SUCCESS Generate the KeywordResp string. + +**/ +EFI_STATUS +GenerateKeywordResp ( + IN CHAR8 *NameSpace, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN EFI_STRING KeywordData, + IN EFI_STRING ValueStr, + IN BOOLEAN ReadOnly, + OUT EFI_STRING *KeywordResp + ) +{ + UINTN RespStrLen; + CHAR16 *RespStr; + CHAR16 *PathHdr; + CHAR16 *UnicodeNameSpace; + UINTN NameSpaceLength; + + ASSERT ((NameSpace != NULL) && (DevicePath != NULL) && (KeywordData != NULL) && (ValueStr != NULL) && (KeywordResp != NULL)); + + // + // 1. Calculate the string length. + // + // + // 1.1 NameSpaceId size. + // 'NAMESPACE=' + // + NameSpaceLength = AsciiStrLen (NameSpace); + RespStrLen = 10 + NameSpaceLength; + UnicodeNameSpace = AllocatePool ((NameSpaceLength + 1) * sizeof (CHAR16)); + if (UnicodeNameSpace == NULL) { + return EFI_OUT_OF_RESOURCES; + } + AsciiStrToUnicodeStrS (NameSpace, UnicodeNameSpace, NameSpaceLength + 1); + + // + // 1.2 PathHdr size. + // PATH='&' + // Attention: The output include the '&' at the end. + // + GenerateSubStr ( + L"&PATH=", + GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *) DevicePath), + (VOID *) DevicePath, + 1, + &PathHdr + ); + RespStrLen += StrLen (PathHdr); + + // + // 1.3 Keyword section. + // 'KEYWORD='[':'(1/4)] + // + RespStrLen += 8 + StrLen (KeywordData); + + // + // 1.4 Value section. + // ValueStr = '&VALUE=' + // + RespStrLen += StrLen (ValueStr); + + // + // 1.5 ReadOnly Section. + // '&READONLY' + // + if (ReadOnly) { + RespStrLen += 9; + } + + // + // 2. Allocate the buffer and create the KeywordResp string include '\0'. + // + RespStrLen += 1; + *KeywordResp = AllocatePool (RespStrLen * sizeof (CHAR16)); + if (*KeywordResp == NULL) { + if (UnicodeNameSpace != NULL) { + FreePool (UnicodeNameSpace); + } + + return EFI_OUT_OF_RESOURCES; + } + RespStr = *KeywordResp; + + // + // 2.1 Copy NameSpaceId section. + // + StrCpyS (RespStr, RespStrLen, L"NAMESPACE="); + + StrCatS (RespStr, RespStrLen, UnicodeNameSpace); + + // + // 2.2 Copy PathHdr section. + // + StrCatS (RespStr, RespStrLen, PathHdr); + + // + // 2.3 Copy Keyword section. + // + StrCatS (RespStr, RespStrLen, L"KEYWORD="); + + StrCatS (RespStr, RespStrLen, KeywordData); + + // + // 2.4 Copy the Value section. + // + StrCatS (RespStr, RespStrLen, ValueStr); + + // + // 2.5 Copy ReadOnly section if exist. + // + if (ReadOnly) { + StrCatS (RespStr, RespStrLen, L"&READONLY"); + } + + if (UnicodeNameSpace != NULL) { + FreePool (UnicodeNameSpace); + } + if (PathHdr != NULL) { + FreePool (PathHdr); + } + + return EFI_SUCCESS; +} + +/** + Merge the KeywordResp String to MultiKeywordResp string. + + This is a internal function. + + @param MultiKeywordResp The existed multikeywordresp string. + @param KeywordResp The input keywordResp string. + + @retval EFI_OUT_OF_RESOURCES The memory can't be allocated. + @retval EFI_SUCCESS Generate the MultiKeywordResp string. + +**/ +EFI_STATUS +MergeToMultiKeywordResp ( + IN OUT EFI_STRING *MultiKeywordResp, + IN EFI_STRING *KeywordResp + ) +{ + UINTN MultiKeywordRespLen; + EFI_STRING StringPtr; + + if (*MultiKeywordResp == NULL) { + *MultiKeywordResp = *KeywordResp; + *KeywordResp = NULL; + return EFI_SUCCESS; + } + + MultiKeywordRespLen = (StrLen (*MultiKeywordResp) + 1 + StrLen (*KeywordResp) + 1) * sizeof (CHAR16); + + StringPtr = ReallocatePool ( + StrSize (*MultiKeywordResp), + MultiKeywordRespLen, + *MultiKeywordResp + ); + if (StringPtr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *MultiKeywordResp = StringPtr; + + StrCatS (StringPtr, MultiKeywordRespLen / sizeof (CHAR16), L"&"); + + StrCatS (StringPtr, MultiKeywordRespLen / sizeof (CHAR16), *KeywordResp); + + return EFI_SUCCESS; +} + +/** + Enumerate all keyword in the system. + + If error occur when parse one keyword, just skip it and parse the next one. + + This is a internal function. + + @param NameSpace The namespace used to search the string. + @param MultiResp Return the MultiKeywordResp string for the system. + @param ProgressErr Return the error status. + + @retval EFI_OUT_OF_RESOURCES The memory can't be allocated. + @retval EFI_SUCCESS Generate the MultiKeywordResp string. + @retval EFI_NOT_FOUND No keyword found. + +**/ +EFI_STATUS +EnumerateAllKeywords ( + IN CHAR8 *NameSpace, + OUT EFI_STRING *MultiResp, + OUT UINT32 *ProgressErr + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *StringLink; + UINT8 *DevicePathPkg; + UINT8 *DevicePath; + HII_DATABASE_RECORD *DataBaseRecord; + HII_DATABASE_PACKAGE_LIST_INSTANCE *PackageListNode; + HII_STRING_PACKAGE_INSTANCE *StringPackage; + CHAR8 *LocalNameSpace; + EFI_STRING_ID NextStringId; + EFI_STATUS Status; + UINT8 *OpCode; + CHAR16 *ConfigRequest; + CHAR16 *ValueElement; + CHAR16 *KeywordResp; + CHAR16 *MultiKeywordResp; + CHAR16 *KeywordData; + BOOLEAN ReadOnly; + BOOLEAN FindKeywordPackages; + + DataBaseRecord = NULL; + Status = EFI_SUCCESS; + MultiKeywordResp = NULL; + DevicePath = NULL; + LocalNameSpace = NULL; + ConfigRequest = NULL; + ValueElement = NULL; + KeywordResp = NULL; + FindKeywordPackages = FALSE; + + if (NameSpace == NULL) { + NameSpace = UEFI_CONFIG_LANG; + } + + // + // Find driver which matches the routing data. + // + for (Link = mPrivate.DatabaseList.ForwardLink; Link != &mPrivate.DatabaseList; Link = Link->ForwardLink) { + DataBaseRecord = CR (Link, HII_DATABASE_RECORD, DatabaseEntry, HII_DATABASE_RECORD_SIGNATURE); + if ((DevicePathPkg = DataBaseRecord->PackageList->DevicePathPkg) != NULL) { + DevicePath = DevicePathPkg + sizeof (EFI_HII_PACKAGE_HEADER); + } + PackageListNode = DataBaseRecord->PackageList; + + for (StringLink = PackageListNode->StringPkgHdr.ForwardLink; StringLink != &PackageListNode->StringPkgHdr; StringLink = StringLink->ForwardLink) { + StringPackage = CR (StringLink, HII_STRING_PACKAGE_INSTANCE, StringEntry, HII_STRING_PACKAGE_SIGNATURE); + + // + // Check whether has keyword string package. + // + if (AsciiStrnCmp(NameSpace, StringPackage->StringPkgHdr->Language, AsciiStrLen (NameSpace)) == 0) { + FindKeywordPackages = TRUE; + // + // Keep the NameSpace string. + // + LocalNameSpace = AllocateCopyPool (AsciiStrSize (StringPackage->StringPkgHdr->Language), StringPackage->StringPkgHdr->Language); + if (LocalNameSpace == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // 1 means just begin the enumerate the valid string ids. + // StringId == 1 is always used to save the language for this string package. + // Any valid string start from 2. so here initial it to 1. + // + NextStringId = 1; + + // + // Enumerate all valid stringid in the package. + // + while ((NextStringId = GetNextStringId (StringPackage, NextStringId, &KeywordData)) != 0) { + // + // 3.3 Construct the ConfigRequest string. + // + Status = ExtractConfigRequest (DataBaseRecord, NextStringId, &OpCode, &ConfigRequest); + if (EFI_ERROR (Status)) { + // + // If can't generate ConfigRequest for this question, skip it and start the next. + // + goto Error; + } + + // + // 3.4 Extract Value for the input keyword. + // + Status = ExtractValueFromDriver(ConfigRequest, &ValueElement); + if (EFI_ERROR (Status)) { + if (Status != EFI_OUT_OF_RESOURCES) { + // + // If can't generate ConfigRequest for this question, skip it and start the next. + // + goto Error; + } + // + // If EFI_OUT_OF_RESOURCES error occur, no need to continue. + // + goto Done; + } + + // + // Extract readonly flag from opcode. + // + ReadOnly = ExtractReadOnlyFromOpCode(OpCode); + + // + // 5. Generate KeywordResp string. + // + ASSERT (DevicePath != NULL); + Status = GenerateKeywordResp(LocalNameSpace, (EFI_DEVICE_PATH_PROTOCOL *)DevicePath, KeywordData, ValueElement, ReadOnly, &KeywordResp); + if (Status != EFI_SUCCESS) { + // + // If EFI_OUT_OF_RESOURCES error occur, no need to continue. + // + goto Done; + } + + // + // 6. Merge to the MultiKeywordResp string. + // + Status = MergeToMultiKeywordResp(&MultiKeywordResp, &KeywordResp); + if (EFI_ERROR (Status)) { + goto Done; + } +Error: + // + // Clean the temp buffer to later use again. + // + if (ConfigRequest != NULL) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + if (ValueElement != NULL) { + FreePool (ValueElement); + ValueElement = NULL; + } + if (KeywordResp != NULL) { + FreePool (KeywordResp); + KeywordResp = NULL; + } + } + + if (LocalNameSpace != NULL) { + FreePool (LocalNameSpace); + LocalNameSpace = NULL; + } + } + } + } + + // + // return the already get MultiKeywordString even error occurred. + // + if (MultiKeywordResp == NULL) { + Status = EFI_NOT_FOUND; + if (!FindKeywordPackages) { + *ProgressErr = KEYWORD_HANDLER_NAMESPACE_ID_NOT_FOUND; + } else { + *ProgressErr = KEYWORD_HANDLER_KEYWORD_NOT_FOUND; + } + } else { + Status = EFI_SUCCESS; + } + *MultiResp = MultiKeywordResp; + +Done: + if (LocalNameSpace != NULL) { + FreePool (LocalNameSpace); + } + if (ConfigRequest != NULL) { + FreePool (ConfigRequest); + } + if (ValueElement != NULL) { + FreePool (ValueElement); + } + + return Status; +} + +/** + + This function accepts a formatted string, finds the associated + keyword owners, creates a string from it and forwards it to the + EFI_HII_ROUTING_PROTOCOL.RouteConfig function. + + If there is an issue in resolving the contents of the KeywordString, then the + function returns an error and also sets the Progress and ProgressErr with the + appropriate information about where the issue occurred and additional data about + the nature of the issue. + + In the case when KeywordString containing multiple keywords, when an EFI_NOT_FOUND + error is generated during processing the second or later keyword element, the system + storage associated with earlier keywords is not modified. All elements of the + KeywordString must successfully pass all tests for format and access prior to making + any modifications to storage. + + In the case when EFI_DEVICE_ERROR is returned from the processing of a KeywordString + containing multiple keywords, the state of storage associated with earlier keywords + is undefined. + + + @param This Pointer to the EFI_KEYWORD_HANDLER _PROTOCOL instance. + + @param KeywordString A null-terminated string in format. + + @param Progress On return, points to a character in the KeywordString. + Points to the string's NULL terminator if the request + was successful. Points to the most recent '&' before + the first failing name / value pair (or the beginning + of the string if the failure is in the first name / value + pair) if the request was not successful. + + @param ProgressErr If during the processing of the KeywordString there was + a failure, this parameter gives additional information + about the possible source of the problem. The various + errors are defined in "Related Definitions" below. + + + @retval EFI_SUCCESS The specified action was completed successfully. + + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + 1. KeywordString is NULL. + 2. Parsing of the KeywordString resulted in an + error. See Progress and ProgressErr for more data. + + @retval EFI_NOT_FOUND An element of the KeywordString was not found. + See ProgressErr for more data. + + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + See ProgressErr for more data. + + @retval EFI_ACCESS_DENIED The action violated system policy. See ProgressErr + for more data. + + @retval EFI_DEVICE_ERROR An unexpected system error occurred. See ProgressErr + for more data. + +**/ +EFI_STATUS +EFIAPI +EfiConfigKeywordHandlerSetData ( + IN EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *This, + IN CONST EFI_STRING KeywordString, + OUT EFI_STRING *Progress, + OUT UINT32 *ProgressErr + ) +{ + CHAR8 *NameSpace; + EFI_STATUS Status; + CHAR16 *StringPtr; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + CHAR16 *NextStringPtr; + CHAR16 *KeywordData; + EFI_STRING_ID KeywordStringId; + UINT32 RetVal; + HII_DATABASE_RECORD *DataBaseRecord; + UINT8 *OpCode; + CHAR16 *ConfigResp; + CHAR16 *MultiConfigResp; + CHAR16 *ValueElement; + BOOLEAN ReadOnly; + EFI_STRING InternalProgress; + CHAR16 *TempString; + CHAR16 *KeywordStartPos; + + if (This == NULL || Progress == NULL || ProgressErr == NULL || KeywordString == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = KeywordString; + *ProgressErr = KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR; + Status = EFI_SUCCESS; + MultiConfigResp = NULL; + NameSpace = NULL; + DevicePath = NULL; + KeywordData = NULL; + ValueElement = NULL; + ConfigResp = NULL; + KeywordStartPos = NULL; + KeywordStringId = 0; + + // + // Use temp string to avoid changing input string buffer. + // + TempString = AllocateCopyPool (StrSize (KeywordString), KeywordString); + ASSERT (TempString != NULL); + StringPtr = TempString; + + while ((StringPtr != NULL) && (*StringPtr != L'\0')) { + // + // 1. Get NameSpace from NameSpaceId keyword. + // + Status = ExtractNameSpace (StringPtr, &NameSpace, &NextStringPtr); + if (EFI_ERROR (Status)) { + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + goto Done; + } + ASSERT (NameSpace != NULL); + // + // 1.1 Check whether the input namespace is valid. + // + if (AsciiStrnCmp(NameSpace, UEFI_CONFIG_LANG, AsciiStrLen (UEFI_CONFIG_LANG)) != 0) { + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + StringPtr = NextStringPtr; + + // + // 2. Get possible Device Path info from KeywordString. + // + Status = ExtractDevicePath (StringPtr, (UINT8 **)&DevicePath, &NextStringPtr); + if (EFI_ERROR (Status)) { + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + goto Done; + } + StringPtr = NextStringPtr; + + // + // 3. Extract keyword from the KeywordRequest string. + // + KeywordStartPos = StringPtr; + Status = ExtractKeyword(StringPtr, &KeywordData, &NextStringPtr); + if (EFI_ERROR (Status)) { + // + // Can't find Keyword base on the input device path info. + // + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + Status = EFI_INVALID_PARAMETER; + goto Done; + } + StringPtr = NextStringPtr; + + // + // 4. Extract Value from the KeywordRequest string. + // + Status = ExtractValue (StringPtr, &ValueElement, &NextStringPtr); + if (EFI_ERROR (Status)) { + // + // Can't find Value base on the input device path info. + // + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + Status = EFI_INVALID_PARAMETER; + goto Done; + } + StringPtr = NextStringPtr; + + // + // 5. Find READONLY tag. + // + if ((StringPtr != NULL) && StrnCmp (StringPtr, L"&READONLY", StrLen (L"&READONLY")) == 0) { + ReadOnly = TRUE; + StringPtr += StrLen (L"&READONLY"); + } else { + ReadOnly = FALSE; + } + + // + // 6. Get EFI_STRING_ID for the input keyword. + // + Status = GetStringIdFromDatabase (&DevicePath, &NameSpace, KeywordData, &RetVal, &KeywordStringId, &DataBaseRecord); + if (EFI_ERROR (Status)) { + *ProgressErr = RetVal; + goto Done; + } + + // + // 7. Construct the ConfigRequest string. + // + Status = ExtractConfigResp (DataBaseRecord, KeywordStringId, ValueElement, &OpCode, &ConfigResp); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // 8. Check the readonly flag. + // + if (ExtractReadOnlyFromOpCode (OpCode) != ReadOnly) { + // + // Extracting readonly flag form opcode and extracting "READONLY" tag form KeywordString should have the same results. + // If not, the input KeywordString must be incorrect, return the error status to caller. + // + *ProgressErr = KEYWORD_HANDLER_INCOMPATIBLE_VALUE_DETECTED; + Status = EFI_INVALID_PARAMETER; + goto Done; + } + if (ReadOnly) { + *ProgressErr = KEYWORD_HANDLER_ACCESS_NOT_PERMITTED; + Status = EFI_ACCESS_DENIED; + goto Done; + } + + // + // 9. Merge to the MultiKeywordResp string. + // + Status = MergeToMultiKeywordResp(&MultiConfigResp, &ConfigResp); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // 10. Clean the temp buffer point. + // + FreePool (NameSpace); + FreePool (DevicePath); + FreePool (KeywordData); + FreePool (ValueElement); + NameSpace = NULL; + DevicePath = NULL; + KeywordData = NULL; + ValueElement = NULL; + if (ConfigResp != NULL) { + FreePool (ConfigResp); + ConfigResp = NULL; + } + KeywordStartPos = NULL; + } + + // + // 11. Set value to driver. + // + Status = mPrivate.ConfigRouting.RouteConfig( + &mPrivate.ConfigRouting, + (EFI_STRING) MultiConfigResp, + &InternalProgress + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + *ProgressErr = KEYWORD_HANDLER_NO_ERROR; + +Done: + if (KeywordStartPos != NULL) { + *Progress = KeywordString + (KeywordStartPos - TempString); + } else { + *Progress = KeywordString + (StringPtr - TempString); + } + + ASSERT (TempString != NULL); + FreePool (TempString); + if (NameSpace != NULL) { + FreePool (NameSpace); + } + if (DevicePath != NULL) { + FreePool (DevicePath); + } + if (KeywordData != NULL) { + FreePool (KeywordData); + } + if (ValueElement != NULL) { + FreePool (ValueElement); + } + if (ConfigResp != NULL) { + FreePool (ConfigResp); + } + if (MultiConfigResp != NULL && MultiConfigResp != ConfigResp) { + FreePool (MultiConfigResp); + } + + return Status; +} + +/** + + This function accepts a formatted string, finds the underlying + keyword owners, creates a string from it and forwards it to the + EFI_HII_ROUTING_PROTOCOL.ExtractConfig function. + + If there is an issue in resolving the contents of the KeywordString, then the function + returns an EFI_INVALID_PARAMETER and also set the Progress and ProgressErr with the + appropriate information about where the issue occurred and additional data about the + nature of the issue. + + In the case when KeywordString is NULL, or contains multiple keywords, or when + EFI_NOT_FOUND is generated while processing the keyword elements, the Results string + contains values returned for all keywords processed prior to the keyword generating the + error but no values for the keyword with error or any following keywords. + + + @param This Pointer to the EFI_KEYWORD_HANDLER _PROTOCOL instance. + + @param NameSpaceId A null-terminated string containing the platform configuration + language to search through in the system. If a NULL is passed + in, then it is assumed that any platform configuration language + with the prefix of "x-UEFI-" are searched. + + @param KeywordString A null-terminated string in format. If a + NULL is passed in the KeywordString field, all of the known + keywords in the system for the NameSpaceId specified are + returned in the Results field. + + @param Progress On return, points to a character in the KeywordString. Points + to the string's NULL terminator if the request was successful. + Points to the most recent '&' before the first failing name / value + pair (or the beginning of the string if the failure is in the first + name / value pair) if the request was not successful. + + @param ProgressErr If during the processing of the KeywordString there was a + failure, this parameter gives additional information about the + possible source of the problem. See the definitions in SetData() + for valid value definitions. + + @param Results A null-terminated string in format is returned + which has all the values filled in for the keywords in the + KeywordString. This is a callee-allocated field, and must be freed + by the caller after being used. + + @retval EFI_SUCCESS The specified action was completed successfully. + + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + 1.Progress, ProgressErr, or Results is NULL. + 2.Parsing of the KeywordString resulted in an error. See + Progress and ProgressErr for more data. + + + @retval EFI_NOT_FOUND An element of the KeywordString was not found. See + ProgressErr for more data. + + @retval EFI_NOT_FOUND The NamespaceId specified was not found. See ProgressErr + for more data. + + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. See + ProgressErr for more data. + + @retval EFI_ACCESS_DENIED The action violated system policy. See ProgressErr for + more data. + + @retval EFI_DEVICE_ERROR An unexpected system error occurred. See ProgressErr + for more data. + +**/ +EFI_STATUS +EFIAPI +EfiConfigKeywordHandlerGetData ( + IN EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *This, + IN CONST EFI_STRING NameSpaceId, OPTIONAL + IN CONST EFI_STRING KeywordString, OPTIONAL + OUT EFI_STRING *Progress, + OUT UINT32 *ProgressErr, + OUT EFI_STRING *Results + ) +{ + CHAR8 *NameSpace; + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + HII_DATABASE_RECORD *DataBaseRecord; + CHAR16 *StringPtr; + CHAR16 *NextStringPtr; + CHAR16 *KeywordData; + EFI_STRING_ID KeywordStringId; + UINT8 *OpCode; + CHAR16 *ConfigRequest; + CHAR16 *ValueElement; + UINT32 RetVal; + BOOLEAN ReadOnly; + CHAR16 *KeywordResp; + CHAR16 *MultiKeywordResp; + CHAR16 *TempString; + + if (This == NULL || Progress == NULL || ProgressErr == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + *ProgressErr = KEYWORD_HANDLER_UNDEFINED_PROCESSING_ERROR; + Status = EFI_SUCCESS; + DevicePath = NULL; + NameSpace = NULL; + KeywordData = NULL; + ConfigRequest= NULL; + StringPtr = KeywordString; + ReadOnly = FALSE; + MultiKeywordResp = NULL; + KeywordStringId = 0; + TempString = NULL; + + // + // Use temp string to avoid changing input string buffer. + // + if (NameSpaceId != NULL) { + TempString = AllocateCopyPool (StrSize (NameSpaceId), NameSpaceId); + ASSERT (TempString != NULL); + } + // + // 1. Get NameSpace from NameSpaceId keyword. + // + Status = ExtractNameSpace (TempString, &NameSpace, NULL); + if (TempString != NULL) { + FreePool (TempString); + TempString = NULL; + } + if (EFI_ERROR (Status)) { + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + return Status; + } + // + // 1.1 Check whether the input namespace is valid. + // + if (NameSpace != NULL){ + if (AsciiStrnCmp(NameSpace, UEFI_CONFIG_LANG, AsciiStrLen (UEFI_CONFIG_LANG)) != 0) { + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + return EFI_INVALID_PARAMETER; + } + } + + if (KeywordString != NULL) { + // + // Use temp string to avoid changing input string buffer. + // + TempString = AllocateCopyPool (StrSize (KeywordString), KeywordString); + ASSERT (TempString != NULL); + StringPtr = TempString; + + while (*StringPtr != L'\0') { + // + // 2. Get possible Device Path info from KeywordString. + // + Status = ExtractDevicePath (StringPtr, (UINT8 **)&DevicePath, &NextStringPtr); + if (EFI_ERROR (Status)) { + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + goto Done; + } + StringPtr = NextStringPtr; + + + // + // 3. Process Keyword section from the input keywordRequest string. + // + // 3.1 Extract keyword from the KeywordRequest string. + // + Status = ExtractKeyword(StringPtr, &KeywordData, &NextStringPtr); + if (EFI_ERROR (Status)) { + // + // Can't find Keyword base on the input device path info. + // + *ProgressErr = KEYWORD_HANDLER_MALFORMED_STRING; + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // 3.2 Get EFI_STRING_ID for the input keyword. + // + Status = GetStringIdFromDatabase (&DevicePath, &NameSpace, KeywordData, &RetVal, &KeywordStringId, &DataBaseRecord); + if (EFI_ERROR (Status)) { + *ProgressErr = RetVal; + goto Done; + } + + // + // 3.3 Construct the ConfigRequest string. + // + Status = ExtractConfigRequest (DataBaseRecord, KeywordStringId, &OpCode, &ConfigRequest); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // 3.4 Extract Value for the input keyword. + // + Status = ExtractValueFromDriver(ConfigRequest, &ValueElement); + if (EFI_ERROR (Status)) { + if (Status != EFI_OUT_OF_RESOURCES) { + Status = EFI_DEVICE_ERROR; + } + goto Done; + } + StringPtr = NextStringPtr; + + // + // 4. Process the possible filter section. + // + RetVal = ValidateFilter (OpCode, StringPtr, &NextStringPtr, &ReadOnly); + if (RetVal != KEYWORD_HANDLER_NO_ERROR) { + *ProgressErr = RetVal; + Status = EFI_INVALID_PARAMETER; + goto Done; + } + StringPtr = NextStringPtr; + + + // + // 5. Generate KeywordResp string. + // + Status = GenerateKeywordResp(NameSpace, DevicePath, KeywordData, ValueElement, ReadOnly, &KeywordResp); + if (Status != EFI_SUCCESS) { + goto Done; + } + + // + // 6. Merge to the MultiKeywordResp string. + // + Status = MergeToMultiKeywordResp(&MultiKeywordResp, &KeywordResp); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // 7. Update return value. + // + *Results = MultiKeywordResp; + + // + // 8. Clean the temp buffer. + // + FreePool (DevicePath); + FreePool (KeywordData); + FreePool (ValueElement); + FreePool (ConfigRequest); + DevicePath = NULL; + KeywordData = NULL; + ValueElement = NULL; + ConfigRequest = NULL; + if (KeywordResp != NULL) { + FreePool (KeywordResp); + KeywordResp = NULL; + } + } + } else { + // + // Enumerate all keyword in the system. + // + Status = EnumerateAllKeywords(NameSpace, &MultiKeywordResp, ProgressErr); + if (EFI_ERROR (Status)) { + goto Done; + } + *Results = MultiKeywordResp; + } + + *ProgressErr = KEYWORD_HANDLER_NO_ERROR; + +Done: + *Progress = KeywordString + (StringPtr - TempString); + + if (TempString != NULL) { + FreePool (TempString); + } + if (NameSpace != NULL) { + FreePool (NameSpace); + } + if (DevicePath != NULL) { + FreePool (DevicePath); + } + if (KeywordData != NULL) { + FreePool (KeywordData); + } + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigRouting.c b/roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigRouting.c new file mode 100644 index 000000000..2cad6d29f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/HiiDatabaseDxe/ConfigRouting.c @@ -0,0 +1,6231 @@ +/** @file +Implementation of interfaces function for EFI_HII_CONFIG_ROUTING_PROTOCOL. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "HiiDatabase.h" +extern HII_DATABASE_PRIVATE_DATA mPrivate; + +/** + Calculate the number of Unicode characters of the incoming Configuration string, + not including NULL terminator. + + This is a internal function. + + @param String String in or + format. + + @return The number of Unicode characters. + +**/ +UINTN +CalculateConfigStringLen ( + IN EFI_STRING String + ) +{ + EFI_STRING TmpPtr; + + // + // "GUID=" should be the first element of incoming string. + // + ASSERT (String != NULL); + ASSERT (StrnCmp (String, L"GUID=", StrLen (L"GUID=")) == 0); + + // + // The beginning of next / should be "&GUID=". + // Will meet '\0' if there is only one /. + // + TmpPtr = StrStr (String, L"&GUID="); + if (TmpPtr == NULL) { + return StrLen (String); + } + + return (TmpPtr - String); +} + + +/** + Convert the hex UNICODE %02x encoding of a UEFI device path to binary + from of . + + This is a internal function. + + @param String UEFI configuration string + @param DevicePathData Binary of a UEFI device path. + + @retval EFI_NOT_FOUND The device path is not invalid. + @retval EFI_INVALID_PARAMETER Any incoming parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Lake of resources to store necessary structures. + @retval EFI_SUCCESS The device path is retrieved and translated to + binary format. + +**/ +EFI_STATUS +GetDevicePath ( + IN EFI_STRING String, + OUT UINT8 **DevicePathData + ) +{ + UINTN Length; + EFI_STRING PathHdr; + UINT8 *DevicePathBuffer; + CHAR16 TemStr[2]; + UINTN Index; + UINT8 DigitUint8; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + + if (String == NULL || DevicePathData == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Find the 'PATH=' of and skip it. + // + for (; (*String != 0 && StrnCmp (String, L"PATH=", StrLen (L"PATH=")) != 0); String++); + if (*String == 0) { + return EFI_INVALID_PARAMETER; + } + // + // Check whether path data does exist. + // + String += StrLen (L"PATH="); + if (*String == 0) { + return EFI_INVALID_PARAMETER; + } + PathHdr = String; + + // + // The content between 'PATH=' of and '&' of next element + // or '\0' (end of configuration string) is the UNICODE %02x bytes encoding + // of UEFI device path. + // + for (Length = 0; *String != 0 && *String != L'&'; String++, Length++); + // + // Check DevicePath Length + // + if (((Length + 1) / 2) < sizeof (EFI_DEVICE_PATH_PROTOCOL)) { + return EFI_NOT_FOUND; + } + + // + // The data in is encoded as hex UNICODE %02x bytes in the same order + // as the device path resides in RAM memory. + // Translate the data into binary. + // + DevicePathBuffer = (UINT8 *) AllocateZeroPool ((Length + 1) / 2); + if (DevicePathBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Convert DevicePath + // + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0; Index < Length; Index ++) { + TemStr[0] = PathHdr[Index]; + DigitUint8 = (UINT8) StrHexToUint64 (TemStr); + if ((Index & 1) == 0) { + DevicePathBuffer [Index/2] = DigitUint8; + } else { + DevicePathBuffer [Index/2] = (UINT8) ((DevicePathBuffer [Index/2] << 4) + DigitUint8); + } + } + + // + // Validate DevicePath + // + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) DevicePathBuffer; + while (!IsDevicePathEnd (DevicePath)) { + if ((DevicePath->Type == 0) || (DevicePath->SubType == 0) || (DevicePathNodeLength (DevicePath) < sizeof (EFI_DEVICE_PATH_PROTOCOL))) { + // + // Invalid device path + // + FreePool (DevicePathBuffer); + return EFI_NOT_FOUND; + } + DevicePath = NextDevicePathNode (DevicePath); + } + + // + // return the device path + // + *DevicePathData = DevicePathBuffer; + return EFI_SUCCESS; +} + +/** + Converts the unicode character of the string from uppercase to lowercase. + This is a internal function. + + @param ConfigString String to be converted + +**/ +VOID +EFIAPI +HiiToLower ( + IN EFI_STRING ConfigString + ) +{ + EFI_STRING String; + BOOLEAN Lower; + + ASSERT (ConfigString != NULL); + + // + // Convert all hex digits in range [A-F] in the configuration header to [a-f] + // + for (String = ConfigString, Lower = FALSE; *String != L'\0'; String++) { + if (*String == L'=') { + Lower = TRUE; + } else if (*String == L'&') { + Lower = FALSE; + } else if (Lower && *String >= L'A' && *String <= L'F') { + *String = (CHAR16) (*String - L'A' + L'a'); + } + } + + return; +} + +/** + Generate a sub string then output it. + + This is a internal function. + + @param String A constant string which is the prefix of the to be + generated string, e.g. GUID= + + @param BufferLen The length of the Buffer in bytes. + + @param Buffer Points to a buffer which will be converted to be the + content of the generated string. + + @param Flag If 1, the buffer contains data for the value of GUID or PATH stored in + UINT8 *; if 2, the buffer contains unicode string for the value of NAME; + if 3, the buffer contains other data. + + @param SubStr Points to the output string. It's caller's + responsibility to free this buffer. + + +**/ +VOID +GenerateSubStr ( + IN CONST EFI_STRING String, + IN UINTN BufferLen, + IN VOID *Buffer, + IN UINT8 Flag, + OUT EFI_STRING *SubStr + ) +{ + UINTN Length; + EFI_STRING Str; + EFI_STRING StringHeader; + CHAR16 *TemString; + CHAR16 *TemName; + UINT8 *TemBuffer; + UINTN Index; + + ASSERT (String != NULL && SubStr != NULL); + + if (Buffer == NULL) { + *SubStr = AllocateCopyPool (StrSize (String), String); + ASSERT (*SubStr != NULL); + return; + } + + // + // Header + Data + '&' + '\0' + // + Length = StrLen (String) + BufferLen * 2 + 1 + 1; + Str = AllocateZeroPool (Length * sizeof (CHAR16)); + ASSERT (Str != NULL); + + StrCpyS (Str, Length, String); + + StringHeader = Str + StrLen (String); + TemString = (CHAR16 *) StringHeader; + + switch (Flag) { + case 1: + // + // Convert Buffer to Hex String in reverse order + // + TemBuffer = ((UINT8 *) Buffer); + for (Index = 0; Index < BufferLen; Index ++, TemBuffer ++) { + UnicodeValueToStringS ( + TemString, + sizeof (CHAR16) * (Length - StrnLenS (Str, Length)), + PREFIX_ZERO | RADIX_HEX, + *TemBuffer, + 2 + ); + TemString += StrnLenS (TemString, Length - StrnLenS (Str, Length)); + } + break; + case 2: + // + // Check buffer is enough + // + TemName = (CHAR16 *) Buffer; + ASSERT ((BufferLen * 2 + 1) >= (StrLen (TemName) * 4 + 1)); + // + // Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044" + // + for (; *TemName != L'\0'; TemName++) { + UnicodeValueToStringS ( + TemString, + sizeof (CHAR16) * (Length - StrnLenS (Str, Length)), + PREFIX_ZERO | RADIX_HEX, + *TemName, + 4 + ); + TemString += StrnLenS (TemString, Length - StrnLenS (Str, Length)); + } + break; + case 3: + // + // Convert Buffer to Hex String + // + TemBuffer = ((UINT8 *) Buffer) + BufferLen - 1; + for (Index = 0; Index < BufferLen; Index ++, TemBuffer --) { + UnicodeValueToStringS ( + TemString, + sizeof (CHAR16) * (Length - StrnLenS (Str, Length)), + PREFIX_ZERO | RADIX_HEX, + *TemBuffer, + 2 + ); + TemString += StrnLenS (TemString, Length - StrnLenS (Str, Length)); + } + break; + default: + break; + } + + // + // Convert the uppercase to lowercase since is defined in lowercase format. + // + StrCatS (Str, Length, L"&"); + HiiToLower (Str); + + *SubStr = Str; +} + + +/** + Retrieve the from String then output it. + + This is a internal function. + + @param String A sub string of a configuration string in + format. + @param ConfigBody Points to the output string. It's caller's + responsibility to free this buffer. + + @retval EFI_INVALID_PARAMETER There is no form package in current hii database. + @retval EFI_OUT_OF_RESOURCES Not enough memory to finish this operation. + @retval EFI_SUCCESS All existing storage is exported. + +**/ +EFI_STATUS +OutputConfigBody ( + IN EFI_STRING String, + OUT EFI_STRING *ConfigBody + ) +{ + EFI_STRING TmpPtr; + EFI_STRING Result; + UINTN Length; + + if (String == NULL || ConfigBody == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The setting information should start OFFSET, not ALTCFG. + // + if (StrnCmp (String, L"&ALTCFG=", StrLen (L"&ALTCFG=")) == 0) { + return EFI_INVALID_PARAMETER; + } + + TmpPtr = StrStr (String, L"GUID="); + if (TmpPtr == NULL) { + // + // It is the last of the incoming configuration string. + // + Result = AllocateCopyPool (StrSize (String), String); + if (Result == NULL) { + return EFI_OUT_OF_RESOURCES; + } else { + *ConfigBody = Result; + return EFI_SUCCESS; + } + } + + Length = TmpPtr - String; + if (Length == 0) { + return EFI_NOT_FOUND; + } + Result = AllocateCopyPool (Length * sizeof (CHAR16), String); + if (Result == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *(Result + Length - 1) = 0; + *ConfigBody = Result; + return EFI_SUCCESS; +} + +/** + Append a string to a multi-string format. + + This is a internal function. + + @param MultiString String in , + , or . On + input, the buffer length of this string is + MAX_STRING_LENGTH. On output, the buffer length + might be updated. + @param AppendString NULL-terminated Unicode string. + + @retval EFI_INVALID_PARAMETER Any incoming parameter is invalid. + @retval EFI_SUCCESS AppendString is append to the end of MultiString + +**/ +EFI_STATUS +AppendToMultiString ( + IN OUT EFI_STRING *MultiString, + IN EFI_STRING AppendString + ) +{ + UINTN AppendStringSize; + UINTN MultiStringSize; + UINTN MaxLen; + + if (MultiString == NULL || *MultiString == NULL || AppendString == NULL) { + return EFI_INVALID_PARAMETER; + } + + AppendStringSize = StrSize (AppendString); + MultiStringSize = StrSize (*MultiString); + MaxLen = MAX_STRING_LENGTH / sizeof (CHAR16); + + // + // Enlarge the buffer each time when length exceeds MAX_STRING_LENGTH. + // + if (MultiStringSize + AppendStringSize > MAX_STRING_LENGTH || + MultiStringSize > MAX_STRING_LENGTH) { + *MultiString = (EFI_STRING) ReallocatePool ( + MultiStringSize, + MultiStringSize + AppendStringSize, + (VOID *) (*MultiString) + ); + MaxLen = (MultiStringSize + AppendStringSize) / sizeof (CHAR16); + ASSERT (*MultiString != NULL); + } + // + // Append the incoming string + // + StrCatS (*MultiString, MaxLen, AppendString); + + return EFI_SUCCESS; +} + + +/** + Get the value of in format, i.e. the value of OFFSET + or WIDTH or VALUE. + ::= 'OFFSET='&'WIDTH='&'VALUE'= + + This is a internal function. + + @param StringPtr String in format and points to the + first character of . + @param Number The output value. Caller takes the responsibility + to free memory. + @param Len Length of the , in characters. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to store necessary + structures. + @retval EFI_SUCCESS Value of is outputted in Number + successfully. + +**/ +EFI_STATUS +GetValueOfNumber ( + IN EFI_STRING StringPtr, + OUT UINT8 **Number, + OUT UINTN *Len + ) +{ + EFI_STRING TmpPtr; + UINTN Length; + EFI_STRING Str; + UINT8 *Buf; + EFI_STATUS Status; + UINT8 DigitUint8; + UINTN Index; + CHAR16 TemStr[2]; + + if (StringPtr == NULL || *StringPtr == L'\0' || Number == NULL || Len == NULL) { + return EFI_INVALID_PARAMETER; + } + + Buf = NULL; + + TmpPtr = StringPtr; + while (*StringPtr != L'\0' && *StringPtr != L'&') { + StringPtr++; + } + *Len = StringPtr - TmpPtr; + Length = *Len + 1; + + Str = (EFI_STRING) AllocateZeroPool (Length * sizeof (CHAR16)); + if (Str == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + CopyMem (Str, TmpPtr, *Len * sizeof (CHAR16)); + *(Str + *Len) = L'\0'; + + Length = (Length + 1) / 2; + Buf = (UINT8 *) AllocateZeroPool (Length); + if (Buf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Length = *Len; + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0; Index < Length; Index ++) { + TemStr[0] = Str[Length - Index - 1]; + DigitUint8 = (UINT8) StrHexToUint64 (TemStr); + if ((Index & 1) == 0) { + Buf [Index/2] = DigitUint8; + } else { + Buf [Index/2] = (UINT8) ((DigitUint8 << 4) + Buf [Index/2]); + } + } + + *Number = Buf; + Status = EFI_SUCCESS; + +Exit: + if (Str != NULL) { + FreePool (Str); + } + + return Status; +} + +/** + To find the BlockName in the string with same value. + + @param String Pointer to a Null-terminated Unicode string. + @param BlockName Pointer to a Null-terminated Unicode string to search for. + @param Buffer Pointer to the value correspond to the BlockName. + @param Found The Block whether has been found. + @param BufferLen The length of the buffer. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to store necessary structures. + @retval EFI_SUCCESS The function finishes successfully. + +**/ +EFI_STATUS +FindSameBlockElement( + IN EFI_STRING String, + IN EFI_STRING BlockName, + IN UINT8 *Buffer, + OUT BOOLEAN *Found, + IN UINTN BufferLen + ) +{ + EFI_STRING BlockPtr; + UINTN Length; + UINT8 *TempBuffer; + EFI_STATUS Status; + + TempBuffer = NULL; + *Found = FALSE; + BlockPtr = StrStr (String, BlockName); + + while (BlockPtr != NULL) { + BlockPtr += StrLen (BlockName); + Status = GetValueOfNumber (BlockPtr, &TempBuffer, &Length); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (TempBuffer != NULL); + if ((BufferLen == Length) && (0 == CompareMem (Buffer, TempBuffer, Length))) { + *Found = TRUE; + FreePool (TempBuffer); + TempBuffer = NULL; + return EFI_SUCCESS; + } else { + FreePool (TempBuffer); + TempBuffer = NULL; + BlockPtr = StrStr (BlockPtr + 1, BlockName); + } + } + return EFI_SUCCESS; +} + +/** + Compare the in ConfigAltResp and DefaultAltCfgResp, if the + in DefaultAltCfgResp but not in ConfigAltResp,add it to the ConfigAltResp. + + @param DefaultAltCfgResp Pointer to a null-terminated Unicode string in + format. The default value + string may contain more than one ConfigAltResp + string for the different varstore buffer. + @param ConfigAltResp Pointer to a null-terminated Unicode string in + format. + @param AltConfigHdr Pointer to a Unicode string in format. + @param ConfigAltRespChanged Whether the ConfigAltResp has been changed. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to store necessary structures. + @retval EFI_SUCCESS The function finishes successfully. + +**/ +EFI_STATUS +CompareBlockElementDefault ( + IN EFI_STRING DefaultAltCfgResp, + IN OUT EFI_STRING *ConfigAltResp, + IN EFI_STRING AltConfigHdr, + IN OUT BOOLEAN *ConfigAltRespChanged +) +{ + EFI_STATUS Status; + EFI_STRING BlockPtr; + EFI_STRING BlockPtrStart; + EFI_STRING StringPtr; + EFI_STRING AppendString; + EFI_STRING AltConfigHdrPtr; + UINT8 *TempBuffer; + UINTN OffsetLength; + UINTN AppendSize; + UINTN TotalSize; + BOOLEAN FoundOffset; + + AppendString = NULL; + TempBuffer = NULL; + // + // Make BlockPtr point to the first with AltConfigHdr in DefaultAltCfgResp. + // + AltConfigHdrPtr = StrStr (DefaultAltCfgResp, AltConfigHdr); + ASSERT (AltConfigHdrPtr != NULL); + BlockPtr = StrStr (AltConfigHdrPtr, L"&OFFSET="); + // + // Make StringPtr point to the AltConfigHdr in ConfigAltResp. + // + StringPtr = StrStr (*ConfigAltResp, AltConfigHdr); + ASSERT (StringPtr != NULL); + + while (BlockPtr != NULL) { + // + // Find the "&OFFSET=" block and get the value of the Number with AltConfigHdr in DefaultAltCfgResp. + // + BlockPtrStart = BlockPtr; + BlockPtr += StrLen (L"&OFFSET="); + Status = GetValueOfNumber (BlockPtr, &TempBuffer, &OffsetLength); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + // + // To find the same "&OFFSET=" block in ConfigAltResp. + // + Status = FindSameBlockElement (StringPtr, L"&OFFSET=", TempBuffer, &FoundOffset, OffsetLength); + if (TempBuffer != NULL) { + FreePool (TempBuffer); + TempBuffer = NULL; + } + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + if (!FoundOffset) { + // + // Don't find the same "&OFFSET=" block in ConfigAltResp. + // Calculate the size of . + // ::='OFFSET=''&WIDTH=''&VALUE='. + // + BlockPtr = StrStr (BlockPtr + 1, L"&OFFSET="); + if (BlockPtr != NULL) { + AppendSize = (BlockPtr - BlockPtrStart) * sizeof (CHAR16); + } else { + AppendSize = StrSize (BlockPtrStart); + } + // + // Copy the to AppendString. + // + if (AppendString == NULL) { + AppendString = (EFI_STRING) AllocateZeroPool (AppendSize + sizeof (CHAR16)); + StrnCatS (AppendString, AppendSize / sizeof (CHAR16) + 1, BlockPtrStart, AppendSize / sizeof (CHAR16)); + } else { + TotalSize = StrSize (AppendString) + AppendSize + sizeof (CHAR16); + AppendString = (EFI_STRING) ReallocatePool ( + StrSize (AppendString), + TotalSize, + AppendString + ); + if (AppendString == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + StrnCatS (AppendString, TotalSize / sizeof (CHAR16), BlockPtrStart, AppendSize / sizeof (CHAR16)); + } + } else { + // + // To find next "&OFFSET=" block with AltConfigHdr in DefaultAltCfgResp. + // + BlockPtr = StrStr (BlockPtr + 1, L"&OFFSET="); + } + } + + if (AppendString != NULL) { + // + // Reallocate ConfigAltResp to copy the AppendString. + // + TotalSize = StrSize (*ConfigAltResp) + StrSize (AppendString) + sizeof (CHAR16); + *ConfigAltResp = (EFI_STRING) ReallocatePool ( + StrSize (*ConfigAltResp), + TotalSize, + *ConfigAltResp + ); + if (*ConfigAltResp == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + StrCatS (*ConfigAltResp, TotalSize / sizeof (CHAR16), AppendString); + *ConfigAltRespChanged = TRUE; + } + + Status = EFI_SUCCESS; + +Exit: + if (AppendString != NULL) { + FreePool (AppendString); + } + + return Status; +} + +/** + Compare the in ConfigAltResp and DefaultAltCfgResp, if the + in DefaultAltCfgResp but not in ConfigAltResp,add it to the ConfigAltResp. + + @param DefaultAltCfgResp Pointer to a null-terminated Unicode string in + format. The default value + string may contain more than one ConfigAltResp + string for the different varstore buffer. + @param ConfigAltResp Pointer to a null-terminated Unicode string in + format. + @param AltConfigHdr Pointer to a Unicode string in format. + @param ConfigAltRespChanged Whether the ConfigAltResp has been changed. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to store necessary structures. + @retval EFI_SUCCESS The function finishes successfully. + +**/ +EFI_STATUS +CompareNameElementDefault ( + IN EFI_STRING DefaultAltCfgResp, + IN OUT EFI_STRING *ConfigAltResp, + IN EFI_STRING AltConfigHdr, + IN OUT BOOLEAN *ConfigAltRespChanged +) +{ + EFI_STATUS Status; + EFI_STRING NvConfigPtr; + EFI_STRING NvConfigStart; + EFI_STRING NvConfigValuePtr; + EFI_STRING StringPtr; + EFI_STRING NvConfigExist; + EFI_STRING AppendString; + CHAR16 TempChar; + UINTN AppendSize; + UINTN TotalSize; + + AppendString = NULL; + NvConfigExist = NULL; + // + // Make NvConfigPtr point to the first with AltConfigHdr in DefaultAltCfgResp. + // + NvConfigPtr = StrStr (DefaultAltCfgResp, AltConfigHdr); + ASSERT (NvConfigPtr != NULL); + NvConfigPtr = StrStr (NvConfigPtr + StrLen(AltConfigHdr),L"&"); + // + // Make StringPtr point to the first with AltConfigHdr in ConfigAltResp. + // + StringPtr = StrStr (*ConfigAltResp, AltConfigHdr); + ASSERT (StringPtr != NULL); + StringPtr = StrStr (StringPtr + StrLen (AltConfigHdr), L"&"); + ASSERT (StringPtr != NULL); + + while (NvConfigPtr != NULL) { + // + // ::=