From f35acfa5ab790edf87b86c5fb6a0a79cb7ea6feb Mon Sep 17 00:00:00 2001 From: Sergey Isakov Date: Fri, 4 Oct 2019 16:19:40 +0300 Subject: [PATCH] restructure sources, preliminary include AptioMemoryFix from OC Signed-off-by: Sergey Isakov --- Clover.dsc | 37 +- CloverPkg.dec | 14 +- CppProperties.json | 21 + .../IndustryStandard/AppleFatBinaryImage.h | 150 + Include/IndustryStandard/AppleMachoImage.h | 2481 +++++++++++++++++ Include/Library/MachoLib.h | 761 +++++ Include/Protocol/AmiKeycode.h | 59 + Include/Protocol/AmiPointer.h | 68 + Include/Protocol/AptioMemoryFix.h | 44 + Library/MachoLib/CxxSymbols.c | 620 ++++ Library/MachoLib/Header.c | 1449 ++++++++++ Library/MachoLib/MachoLib.inf | 37 + Library/MachoLib/MachoLibInternal.h | 77 + Library/MachoLib/Relocations.c | 158 ++ Library/MachoLib/Symbols.c | 640 +++++ Library/OcGuardLib/BitOverflow.c | 289 ++ Library/OcGuardLib/Canary.c | 39 + Library/OcGuardLib/NativeOverflow.c | 115 + Library/OcGuardLib/OcGuardLib.inf | 50 + Library/OcGuardLib/TripleOverflow.c | 429 +++ Library/OcGuardLib/Ubsan.c | 1741 ++++++++++++ Library/OcGuardLib/Ubsan.h | 207 ++ Library/OcGuardLib/UbsanPrintf.c | 537 ++++ MemoryFix/AptioMemoryFix/AptioFixPkg.dec | 28 + MemoryFix/AptioMemoryFix/AptioMemoryFix.c | 97 + MemoryFix/AptioMemoryFix/AptioMemoryFix.inf | 105 + MemoryFix/AptioMemoryFix/AsmFuncs.h | 49 + MemoryFix/AptioMemoryFix/BootArgs.c | 149 + MemoryFix/AptioMemoryFix/BootArgs.h | 56 + MemoryFix/AptioMemoryFix/BootFixes.c | 559 ++++ MemoryFix/AptioMemoryFix/BootFixes.h | 93 + MemoryFix/AptioMemoryFix/Config.h | 123 + MemoryFix/AptioMemoryFix/CustomSlide.c | 664 +++++ MemoryFix/AptioMemoryFix/CustomSlide.h | 78 + MemoryFix/AptioMemoryFix/MemoryMap.c | 366 +++ MemoryFix/AptioMemoryFix/MemoryMap.h | 63 + MemoryFix/AptioMemoryFix/RtShims.c | 358 +++ MemoryFix/AptioMemoryFix/RtShims.h | 79 + MemoryFix/AptioMemoryFix/ServiceOverrides.c | 638 +++++ MemoryFix/AptioMemoryFix/ServiceOverrides.h | 122 + .../AptioMemoryFix/UmmMalloc/UmmMalloc.c | 496 ++++ .../AptioMemoryFix/UmmMalloc/UmmMalloc.h | 52 + MemoryFix/AptioMemoryFix/VMem.c | 548 ++++ MemoryFix/AptioMemoryFix/VMem.h | 210 ++ MemoryFix/AptioMemoryFix/X64/AsmFuncs.nasm | 542 ++++ MemoryFix/AptioMemoryFix/X64/AsmRtShims.nasm | 300 ++ .../OsxAptioFixDrv}/AsmFuncs.h | 0 .../OsxAptioFixDrv}/BootArgs.c | 0 .../OsxAptioFixDrv}/BootArgs.h | 0 .../OsxAptioFixDrv}/BootFixes.c | 0 .../OsxAptioFixDrv}/BootFixes.h | 0 .../OsxAptioFixDrv}/BootFixes3.c | 0 .../OsxAptioFixDrv}/BootFixes3.h | 0 .../OsxAptioFixDrv}/DecodedKernelCheck.c | 0 .../OsxAptioFixDrv}/DecodedKernelCheck.h | 0 .../OsxAptioFixDrv}/FlatDevTree/device_tree.c | 0 .../OsxAptioFixDrv}/FlatDevTree/device_tree.h | 0 .../OsxAptioFixDrv}/Hibernate.h | 0 .../OsxAptioFixDrv}/Lib.c | 0 .../OsxAptioFixDrv}/Lib.h | 0 .../OsxAptioFixDrv}/Mach-O/Mach-O.c | 0 .../OsxAptioFixDrv}/Mach-O/Mach-O.h | 0 .../OsxAptioFixDrv}/Mach-O/UefiLoader.h | 0 .../OsxAptioFixDrv}/Mach-O/loader.h | 0 .../OsxAptioFixDrv}/NVRAMDebug.c | 0 .../OsxAptioFixDrv}/NVRAMDebug.h | 0 .../OsxAptioFixDrv}/OsxAptioFix2Drv.c | 0 .../OsxAptioFixDrv}/OsxAptioFix2Drv.inf | 0 .../OsxAptioFixDrv}/OsxAptioFix3Drv.c | 0 .../OsxAptioFixDrv}/OsxAptioFix3Drv.inf | 0 .../OsxAptioFixDrv}/OsxAptioFixDrv.c | 0 .../OsxAptioFixDrv}/OsxAptioFixDrv.inf | 0 .../OsxAptioFixDrv}/RTShims.h | 0 .../OsxAptioFixDrv}/VMem.c | 0 .../OsxAptioFixDrv}/VMem.h | 0 .../OsxAptioFixDrv}/X64/AsmFuncsX64.S | 0 .../OsxAptioFixDrv}/X64/AsmFuncsX64.asm | 0 .../OsxAptioFixDrv}/X64/AsmFuncsX64.nasm | 0 .../OsxAptioFixDrv}/X64/RTShims.nasm | 0 .../OsxAptioFixDrv}/X64/TestAsm32.asm | 0 .../OsxLowMemFixDrv}/Lib.c | 0 .../OsxLowMemFixDrv}/Lib.h | 0 .../OsxLowMemFixDrv}/NVRAMDebug.c | 0 .../OsxLowMemFixDrv}/NVRAMDebug.h | 0 .../OsxLowMemFixDrv}/OsxLowMemFixDrv.c | 0 .../OsxLowMemFixDrv}/OsxLowMemFixDrv.inf | 0 Protocols/AptioInputFix/AptioInput.c | 67 + Protocols/AptioInputFix/AptioInputFix.inf | 89 + Protocols/AptioInputFix/Keycode/AIK.c | 250 ++ Protocols/AptioInputFix/Keycode/AIK.h | 107 + Protocols/AptioInputFix/Keycode/AIKData.c | 81 + Protocols/AptioInputFix/Keycode/AIKData.h | 57 + Protocols/AptioInputFix/Keycode/AIKMap.c | 2390 ++++++++++++++++ Protocols/AptioInputFix/Keycode/AIKShim.c | 198 ++ Protocols/AptioInputFix/Keycode/AIKShim.h | 72 + Protocols/AptioInputFix/Keycode/AIKSource.c | 204 ++ Protocols/AptioInputFix/Keycode/AIKSource.h | 68 + Protocols/AptioInputFix/Keycode/AIKTarget.c | 191 ++ Protocols/AptioInputFix/Keycode/AIKTarget.h | 114 + .../AptioInputFix/Keycode/AIKTranslate.c | 199 ++ .../AptioInputFix/Keycode/AIKTranslate.h | 132 + Protocols/AptioInputFix/Keycode/AppleHid.txt | 207 ++ Protocols/AptioInputFix/Pointer/AIM.c | 565 ++++ Protocols/AptioInputFix/Pointer/AIM.h | 67 + Protocols/AptioInputFix/Timer/AIT.c | 87 + Protocols/AptioInputFix/Timer/AIT.h | 36 + buildmc.bat | 84 + 107 files changed, 21045 insertions(+), 18 deletions(-) create mode 100644 CppProperties.json create mode 100644 Include/IndustryStandard/AppleFatBinaryImage.h create mode 100644 Include/IndustryStandard/AppleMachoImage.h create mode 100644 Include/Library/MachoLib.h create mode 100644 Include/Protocol/AmiKeycode.h create mode 100644 Include/Protocol/AmiPointer.h create mode 100644 Include/Protocol/AptioMemoryFix.h create mode 100644 Library/MachoLib/CxxSymbols.c create mode 100644 Library/MachoLib/Header.c create mode 100644 Library/MachoLib/MachoLib.inf create mode 100644 Library/MachoLib/MachoLibInternal.h create mode 100644 Library/MachoLib/Relocations.c create mode 100644 Library/MachoLib/Symbols.c create mode 100644 Library/OcGuardLib/BitOverflow.c create mode 100644 Library/OcGuardLib/Canary.c create mode 100644 Library/OcGuardLib/NativeOverflow.c create mode 100644 Library/OcGuardLib/OcGuardLib.inf create mode 100644 Library/OcGuardLib/TripleOverflow.c create mode 100644 Library/OcGuardLib/Ubsan.c create mode 100644 Library/OcGuardLib/Ubsan.h create mode 100644 Library/OcGuardLib/UbsanPrintf.c create mode 100644 MemoryFix/AptioMemoryFix/AptioFixPkg.dec create mode 100644 MemoryFix/AptioMemoryFix/AptioMemoryFix.c create mode 100644 MemoryFix/AptioMemoryFix/AptioMemoryFix.inf create mode 100644 MemoryFix/AptioMemoryFix/AsmFuncs.h create mode 100644 MemoryFix/AptioMemoryFix/BootArgs.c create mode 100644 MemoryFix/AptioMemoryFix/BootArgs.h create mode 100644 MemoryFix/AptioMemoryFix/BootFixes.c create mode 100644 MemoryFix/AptioMemoryFix/BootFixes.h create mode 100644 MemoryFix/AptioMemoryFix/Config.h create mode 100644 MemoryFix/AptioMemoryFix/CustomSlide.c create mode 100644 MemoryFix/AptioMemoryFix/CustomSlide.h create mode 100644 MemoryFix/AptioMemoryFix/MemoryMap.c create mode 100644 MemoryFix/AptioMemoryFix/MemoryMap.h create mode 100644 MemoryFix/AptioMemoryFix/RtShims.c create mode 100644 MemoryFix/AptioMemoryFix/RtShims.h create mode 100644 MemoryFix/AptioMemoryFix/ServiceOverrides.c create mode 100644 MemoryFix/AptioMemoryFix/ServiceOverrides.h create mode 100644 MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.c create mode 100644 MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.h create mode 100644 MemoryFix/AptioMemoryFix/VMem.c create mode 100644 MemoryFix/AptioMemoryFix/VMem.h create mode 100644 MemoryFix/AptioMemoryFix/X64/AsmFuncs.nasm create mode 100644 MemoryFix/AptioMemoryFix/X64/AsmRtShims.nasm rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/AsmFuncs.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/BootArgs.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/BootArgs.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/BootFixes.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/BootFixes.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/BootFixes3.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/BootFixes3.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/DecodedKernelCheck.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/DecodedKernelCheck.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/FlatDevTree/device_tree.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/FlatDevTree/device_tree.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/Hibernate.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/Lib.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/Lib.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/Mach-O/Mach-O.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/Mach-O/Mach-O.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/Mach-O/UefiLoader.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/Mach-O/loader.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/NVRAMDebug.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/NVRAMDebug.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/OsxAptioFix2Drv.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/OsxAptioFix2Drv.inf (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/OsxAptioFix3Drv.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/OsxAptioFix3Drv.inf (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/OsxAptioFixDrv.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/OsxAptioFixDrv.inf (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/RTShims.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/VMem.c (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/VMem.h (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/X64/AsmFuncsX64.S (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/X64/AsmFuncsX64.asm (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/X64/AsmFuncsX64.nasm (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/X64/RTShims.nasm (100%) rename {OsxAptioFixDrv => MemoryFix/OsxAptioFixDrv}/X64/TestAsm32.asm (100%) rename {OsxLowMemFixDrv => MemoryFix/OsxLowMemFixDrv}/Lib.c (100%) rename {OsxLowMemFixDrv => MemoryFix/OsxLowMemFixDrv}/Lib.h (100%) rename {OsxLowMemFixDrv => MemoryFix/OsxLowMemFixDrv}/NVRAMDebug.c (100%) rename {OsxLowMemFixDrv => MemoryFix/OsxLowMemFixDrv}/NVRAMDebug.h (100%) rename {OsxLowMemFixDrv => MemoryFix/OsxLowMemFixDrv}/OsxLowMemFixDrv.c (100%) rename {OsxLowMemFixDrv => MemoryFix/OsxLowMemFixDrv}/OsxLowMemFixDrv.inf (100%) create mode 100644 Protocols/AptioInputFix/AptioInput.c create mode 100644 Protocols/AptioInputFix/AptioInputFix.inf create mode 100644 Protocols/AptioInputFix/Keycode/AIK.c create mode 100644 Protocols/AptioInputFix/Keycode/AIK.h create mode 100644 Protocols/AptioInputFix/Keycode/AIKData.c create mode 100644 Protocols/AptioInputFix/Keycode/AIKData.h create mode 100644 Protocols/AptioInputFix/Keycode/AIKMap.c create mode 100644 Protocols/AptioInputFix/Keycode/AIKShim.c create mode 100644 Protocols/AptioInputFix/Keycode/AIKShim.h create mode 100644 Protocols/AptioInputFix/Keycode/AIKSource.c create mode 100644 Protocols/AptioInputFix/Keycode/AIKSource.h create mode 100644 Protocols/AptioInputFix/Keycode/AIKTarget.c create mode 100644 Protocols/AptioInputFix/Keycode/AIKTarget.h create mode 100644 Protocols/AptioInputFix/Keycode/AIKTranslate.c create mode 100644 Protocols/AptioInputFix/Keycode/AIKTranslate.h create mode 100644 Protocols/AptioInputFix/Keycode/AppleHid.txt create mode 100644 Protocols/AptioInputFix/Pointer/AIM.c create mode 100644 Protocols/AptioInputFix/Pointer/AIM.h create mode 100644 Protocols/AptioInputFix/Timer/AIT.c create mode 100644 Protocols/AptioInputFix/Timer/AIT.h create mode 100644 buildmc.bat diff --git a/Clover.dsc b/Clover.dsc index 62fb34df2..d382b5cb7 100644 --- a/Clover.dsc +++ b/Clover.dsc @@ -49,6 +49,7 @@ # Basic # BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseRngLib|MdePkg/Library/BaseRngLib/BaseRngLib.inf SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf #BaseMemoryLib|MdePkg/Library/UefiMemoryLib/UefiMemoryLib.inf @@ -150,6 +151,8 @@ AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf VarCheckLib|MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf + OcGuardLib|Library/OcGuardLib/OcGuardLib.inf + # # Our libs # @@ -467,6 +470,7 @@ Protocols/HashServiceFix/HashServiceFix.inf Protocols/AppleKeyAggregator/AppleKeyAggregator.inf Protocols/AppleKeyFeeder/AppleKeyFeeder.inf +# Protocols/AptioInputFix/AptioInputFix.inf !ifdef DEBUG_ON_SERIAL_PORT @@ -490,7 +494,7 @@ !endif - # Drivers for Aptio loading - should go to Clover's /EFI/drivers64UEFI dir + # Drivers for Aptio loading - should go to Clover's /EFI/drivers/UEFI dir Protocols/OsxFatBinaryDrv/OsxFatBinaryDrv.inf # Drivers for Phoenix UEFI loading - should go to Clover's /EFI/drivers64UEFI dir @@ -576,22 +580,27 @@ [Components.X64] - OsxAptioFixDrv/OsxAptioFixDrv.inf -# OsxAptioFixDrv/OsxAptioFix2Drv.inf - OsxAptioFixDrv/OsxAptioFix3Drv.inf - OsxLowMemFixDrv/OsxLowMemFixDrv.inf - #OsxAptioFixDrv/OsxAptioFixDrv.inf { + +# MemoryFix/OsxAptioFixDrv/OsxAptioFix2Drv.inf + MemoryFix/OsxAptioFixDrv/OsxAptioFix3Drv.inf + MemoryFix/OsxLowMemFixDrv/OsxLowMemFixDrv.inf +# MemoryFix/AptioMemoryFix/AptioMemoryFix.inf +!ifdef DEBUG_ON_SERIAL_PORT + MemoryFix/OsxAptioFixDrv/OsxAptioFixDrv.inf { # # Enable debug output. # - # - # gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x07 - # gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xFFFFFFFF - # - # SerialPortLib|MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf - # DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf - # DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf - #} + + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x07 + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xFFFFFFFF + + SerialPortLib|MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf + DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + } + !else + MemoryFix/OsxAptioFixDrv/OsxAptioFixDrv.inf + !endif ################################################################################################### # diff --git a/CloverPkg.dec b/CloverPkg.dec index c05f209f1..2689221a3 100644 --- a/CloverPkg.dec +++ b/CloverPkg.dec @@ -103,10 +103,16 @@ # gEfiHashAlgorithmSha1Guid = {0x2AE9D80F, 0x3fB2, 0x4095, {0xB7, 0xB1, 0xE9, 0x31, 0x57, 0xB9, 0x46, 0xB6}} -gEfiHdaControllerInfoProtocolGuid = { 0xE5FC2CAF, 0x0291, 0x46F2, { 0x87, 0xF8, 0x10, 0xC7, 0x58, 0x72, 0x58, 0x04 }} -gEfiHdaIoProtocolGuid = { 0xA090D7F9, 0xB50A, 0x4EA1, { 0xBD, 0xE9, 0x1A, 0xA5, 0xE9, 0x81, 0x2F, 0x45 }} -gEfiHdaCodecInfoProtocolGuid = { 0x6C9CDDE1, 0xE8A5, 0x43E5, { 0xBE, 0x88, 0xDA, 0x15, 0xBC, 0x1C, 0x02, 0x50 }} -gEfiAudioIoProtocolGuid = { 0xF05B559C, 0x1971, 0x4AF5, { 0xB2, 0xAE, 0xD6, 0x08, 0x08, 0xF7, 0x4F, 0x70 }} + gEfiHdaControllerInfoProtocolGuid = { 0xE5FC2CAF, 0x0291, 0x46F2, { 0x87, 0xF8, 0x10, 0xC7, 0x58, 0x72, 0x58, 0x04 }} + gEfiHdaIoProtocolGuid = { 0xA090D7F9, 0xB50A, 0x4EA1, { 0xBD, 0xE9, 0x1A, 0xA5, 0xE9, 0x81, 0x2F, 0x45 }} + gEfiHdaCodecInfoProtocolGuid = { 0x6C9CDDE1, 0xE8A5, 0x43E5, { 0xBE, 0x88, 0xDA, 0x15, 0xBC, 0x1C, 0x02, 0x50 }} + gEfiAudioIoProtocolGuid = { 0xF05B559C, 0x1971, 0x4AF5, { 0xB2, 0xAE, 0xD6, 0x08, 0x08, 0xF7, 0x4F, 0x70 }} + +## Aptio Fix + gAptioMemoryFixProtocolGuid = { 0xC7CBA84E, 0xCC77, 0x461D, { 0x9E, 0x3C, 0x6B, 0xE0, 0xCB, 0x79, 0xA7, 0xC1 } } + gAmiEfiPointerProtocolGuid = { 0x15A10CE7, 0xEAB5, 0x43BF, { 0x90, 0x42, 0x74, 0x43, 0x2E, 0x69, 0x63, 0x77 } } + gAmiEfiKeycodeProtocolGuid = { 0x0ADFB62D, 0xFF74, 0x484C, { 0x89, 0x44, 0xF8, 0x5C, 0x4B, 0xEA, 0x87, 0xA8 } } + ################################################################################ diff --git a/CppProperties.json b/CppProperties.json new file mode 100644 index 000000000..1d74fde57 --- /dev/null +++ b/CppProperties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "inheritEnvironments": [ + "msvc_x64" + ], + "name": "x64-Release", + "includePath": [ + "${env.INCLUDE}", + "${workspaceRoot}\\**" + ], + "defines": [ + "WIN32", + "NDEBUG", + "UNICODE", + "_UNICODE" + ], + "intelliSenseMode": "windows-msvc-x64" + } + ] +} \ No newline at end of file diff --git a/Include/IndustryStandard/AppleFatBinaryImage.h b/Include/IndustryStandard/AppleFatBinaryImage.h new file mode 100644 index 000000000..f789fcfad --- /dev/null +++ b/Include/IndustryStandard/AppleFatBinaryImage.h @@ -0,0 +1,150 @@ +/** @file +Copyright (C) 2014 - 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef APPLE_FAT_BINARY_IMAGE_H +#define APPLE_FAT_BINARY_IMAGE_H + +#include + +/// +/// The common "Fat Binary Magic" number used to identify a Fat binary. +/// +#define EFI_FAT_BINARY_SIGNATURE 0x0EF1FAB9 +/// +/// The common "Fat Binary Magic" number used to identify a Fat binary. +/// +#define MACH_FAT_BINARY_SIGNATURE 0xCAFEBABE +/// +/// The common "Fat Binary Magic" number used to identify a Fat binary. +/// +#define MACH_FAT_BINARY_INVERT_SIGNATURE 0xBEBAFECA +/// +/// The common "Fat Binary Magic" number used to identify a 64-bit Fat binary. +/// +#define MACH_FAT_BINARY_64_SIGNATURE 0xCAFEBABF +/// +/// The common "Fat Binary Magic" number used to identify a 64-bit Fat binary. +/// +#define MACH_FAT_BINARY_64_INVERT_SIGNATURE 0xBFBAFECA + + +/// +/// Defintion of the the Fat architecture-specific file header. +/// +typedef struct { + /// + /// The found CPU architecture specifier. + /// + MACH_CPU_TYPE CpuType; + /// + /// The found CPU sub-architecture specifier. + /// + MACH_CPU_SUBTYPE CpuSubtype; + /// + /// The offset of the architecture-specific boot file. + /// + UINT32 Offset; + /// + /// The size of the architecture-specific boot file. + /// + UINT32 Size; + /// + /// The alignment as a power of 2 (necessary for the x86_64 architecture). + /// + UINT32 Alignment; +} MACH_FAT_ARCH; + +/// +/// Defintion of the Fat file header +/// +typedef struct { + /// + /// The assumed "Fat Binary Magic" number found in the file. + /// + UINT32 Signature; + /// + /// The hard-coded number of architectures within the file. + /// + UINT32 NumberOfFatArch; + /// + /// The first APPLE_FAT_ARCH child of the FAT binary. + /// + MACH_FAT_ARCH FatArch[]; +} MACH_FAT_HEADER; + + +/// +/// The support for the 64-bit fat file format described here is a work in +/// progress and not yet fully supported in all the Apple Developer Tools. +/// +/// When a slice is greater than 4mb or an offset to a slice is greater than 4mb +/// then the 64-bit fat file format is used. +/// + +/// +/// Defintion of the the Fat architecture-specific file header. +/// +typedef struct { + /// + /// The found CPU architecture specifier. + /// + MACH_CPU_TYPE CpuType; + /// + /// The found CPU sub-architecture specifier. + /// + MACH_CPU_SUBTYPE CpuSubtype; + /// + /// The offset of the architecture-specific boot file. + /// + UINT64 Offset; + /// + /// The size of the architecture-specific boot file. + /// + UINT64 Size; + /// + /// The alignment as a power of 2 (necessary for the x86_64 architecture). + /// + UINT32 Alignment; + /// + /// Reserved. + /// + UINT32 Reserved; +} MACH_FAT_ARCH_64; + +/// +/// Defintion of the Fat file header +/// +typedef struct { + /// + /// The assumed "Fat Binary Magic" number found in the file. + /// + UINT32 Signature; + /// + /// The hard-coded number of architectures within the file. + /// + UINT32 NumberOfFatArch; + /// + /// The first APPLE_FAT_ARCH child of the FAT binary. + /// + MACH_FAT_ARCH_64 FatArch[]; +} MACH_FAT_HEADER_64; + +/// +/// Allow selecting a correct header based on magic +/// +typedef union { + UINT32 Signature; + MACH_FAT_HEADER Header32; + MACH_FAT_HEADER_64 Header64; +} MACH_FAT_HEADER_ANY; + +#endif // APPLE_FAT_BINARY_IMAGE_H diff --git a/Include/IndustryStandard/AppleMachoImage.h b/Include/IndustryStandard/AppleMachoImage.h new file mode 100644 index 000000000..6e1fb9edb --- /dev/null +++ b/Include/IndustryStandard/AppleMachoImage.h @@ -0,0 +1,2481 @@ +/** @file +Copyright (c) 2016 - 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef APPLE_MACHO_IMAGE_H +#define APPLE_MACHO_IMAGE_H + +#define MACH_CPU_ARCH_ABI64 BIT24 +#define MACH_CPU_ARCH_ABI64_32 BIT25 + +// +// Capability bits used in the definition of MACH_CPU_SUBTYPE. +// +#define MACH_CPU_SUBTYPE_MASK 0xFF000000U ///< mask for feature flags +#define MACH_CPU_SUBTYPE_LIB64 BIT17 ///< 64 bit libraries + +#define MACH_CPU_SUBTYPE_INTEL_MODEL_SHIFT 4U +#define MACH_CPU_SUBTYPE_INTEL_FAMILY_MAX 0x0FU +#define MACH_CPU_SUBTYPE_INTEL_MODEL_ALL 0U + +#define MACH_CPU_SUBTYPE_INTEL(f, Model) \ + ((f) + ((Model) << MACH_CPU_SUBTYPE_INTEL_MODEL_SHIFT)) + +#define MACH_CPU_SUBTYPE_INTEL_FAMILY(Identifier) \ + ((Identifier) & MACH_CPU_SUBTYPE_INTEL_FAMILY_MAX) + +#define MACH_CPU_SUBTYPE_INTEL_MODEL(Identifier) \ + ((Identifier) >> MACH_CPU_SUBTYPE_INTEL_MODEL_SHIFT) + +#define FORMALIZE_CPU_SUBTYPE(CpuSubtype) ((CpuSubtype) & ~CPU_SUBTYPE_MASK) + +#define MACH_CPU_TYPE_64(CpuType) ((CpuType) | MACH_CPU_ARCH_ABI64) + +#define MACH_CPU_TYPE_64_32(CpuType) ((CpuType) | MACH_CPU_ARCH_ABI64_32) + +/// +/// There is no standard way to detect endianness in EDK2 (yet). +/// Provide MACH_LITTLE_ENDIAN on IA32 and X64 platforms by default, +/// as they are strictly little endian. +/// +#if !defined(MACH_LITTLE_ENDIAN) && !defined(MACH_BIG_ENDIAN) +#if defined (MDE_CPU_IA32) || defined(MDE_CPU_X64) +#define MACH_LITTLE_ENDIAN +#endif //< defined (MDE_CPU_IA32) || defined(MDE_CPU_X64) +#endif //< !defined(MACH_LITTLE_ENDIAN) && !defined(MACH_BIG_ENDIAN) + +/// +/// CPU Type definitions +/// +enum { + MachCpuTypeAny = -1, + MachCpuTypeVax = 1, + MachCpuTypeMc680x0 = 6, + MachCpuTypeX86 = 7, + MachCpuTypeI386 = MachCpuTypeX86, + MachCpuTypeX8664 = MACH_CPU_TYPE_64 (MachCpuTypeX86), + MachCpuTypeMc98000 = 10, + MachCpuTypeHppa = 11, + MachCpuTypeArm = 12, + MachCpuTypeArm64 = MACH_CPU_TYPE_64 (MachCpuTypeArm), + MachCpuTypeArm6432 = MACH_CPU_TYPE_64_32 (MachCpuTypeArm), + + MachCpuTypeMc88000 = 13, + MachCpuTypeSparc = 14, + MachCpuTypeI860 = 15, + MachCpuTypePowerPc = 18, + MachCpuTypePowerPc64 = MACH_CPU_TYPE_64 (MachCpuTypePowerPc), + MachCpuTypeVeo = 255 +}; + +typedef INT32 MACH_CPU_TYPE; + +enum { + MachCpuSubtypeInvalid = -1, + // + // Any + // + MachCpuSubtypeMultiple = -1, + MachCpuSubtypeLittleEndian = 0, + MachCpuSubtypeBigEndian = 1, + // + // VAX Subtypes + // + MachCpuSubtypeVaxAll = 0, + MachCpuSubtypeVax780 = 1, + MachCpuSubtypeVax785 = 2, + MachCpuSubtypeVax750 = 3, + MachCpuSubtypeVax730 = 4, + MachCpuSubtypeUVax1 = 5, + MachCpuSubtypeUVax2 = 6, + MachCpuSubtypeVax8200 = 7, + MachCpuSubtypeVax8500 = 8, + MachCpuSubtypeVax8600 = 9, + MachCpuSubtypeVax8650 = 10, + MachCpuSubtypeVax8800 = 11, + MachCpuSubtypeUVax3 = 12, + // + // MC680 Subtypes + // + MachCpuSubtypeMc680All = 1, + MachCpuSubtypeMc68030 = MachCpuSubtypeMc680All, + MachCpuSubtypeMc68040 = 2, + MachCpuSubtypeMc68030Only = 3, + // + // x86 Subtypes + // + MachCpuSubtypeX86All = MACH_CPU_SUBTYPE_INTEL (3, 0), + MachCpuSubtypeX86Arch1 = MACH_CPU_SUBTYPE_INTEL (4, 0), + // + // x86_64 Subtypes + // + MachCpuSubtypeX8664All = MachCpuSubtypeX86All, + MachCpuSubtypeX8664H = MACH_CPU_SUBTYPE_INTEL (8, 0), + // + // i386 Subytpes + // + MachCpuSubtypeI386All = MachCpuSubtypeX86All, + MachCpuSubtype386 = MachCpuSubtypeI386All, + MachCpuSubtype486 = MachCpuSubtypeX86Arch1, + MachCpuSubtype486Sx = MACH_CPU_SUBTYPE_INTEL (4, 8), + MachCpuSubtype586 = MACH_CPU_SUBTYPE_INTEL (5, 0), + MachCpuSubtypePentium = MachCpuSubtype586, + MachCpuSubtypePentiumPro = MACH_CPU_SUBTYPE_INTEL (6, 1), + MachCpuSubtypePentium3M3 = MACH_CPU_SUBTYPE_INTEL (6, 3), + MachCpuSubtypePentium3M5 = MACH_CPU_SUBTYPE_INTEL (6, 5), + MachCpuSubtypeCeleron = MACH_CPU_SUBTYPE_INTEL (7, 6), + MachCpuSubtypeCeleronMobile = MACH_CPU_SUBTYPE_INTEL (7, 7), + MachCpuSubtypePentium3 = MACH_CPU_SUBTYPE_INTEL (8, 0), + MachCpuSubtypePentium3M = MACH_CPU_SUBTYPE_INTEL (8, 1), + MachCpuSubtypePentium3Xeon = MACH_CPU_SUBTYPE_INTEL (8, 2), + MachCpuSubtypePentiumM = MACH_CPU_SUBTYPE_INTEL (9, 0), + MachCpuSubtypePentium4 = MACH_CPU_SUBTYPE_INTEL (10, 0), + MachCpuSubtypePentium4M = MACH_CPU_SUBTYPE_INTEL (10, 1), + MachCpuSubtypeItanium = MACH_CPU_SUBTYPE_INTEL (11, 0), + MachCpuSubtypeItanium2 = MACH_CPU_SUBTYPE_INTEL (11, 1), + MachCpuSubtypeXeon = MACH_CPU_SUBTYPE_INTEL (12, 0), + MachCpuSubtypeXeonMp = MACH_CPU_SUBTYPE_INTEL (12, 1), + // + // Mips subtypes + // + MachCpuSubtypeMipsAll = 0, + MachCpuSubtypeMipsR2300 = 1, + MachCpuSubtypeMipsR2600 = 2, + MachCpuSubtypeMipsR2800 = 3, + MachCpuSubtypeMipsR2000a = 4, + MachCpuSubtypeMipsR2000 = 5, + MachCpuSubtypeMipsR3000a = 6, + MachCpuSubtypeMipsR3000 = 7, + // + // MC98000 Subtypes + // + MachCpuSubtypeMc98000All = 0, + MachCpuSubtypeMc98601 = 1, + // + // HPPA Subtypes + // + MachCpuSubtypeHppaAll = 0, + MachCpuSubtypeHppa7100 = MachCpuSubtypeHppaAll, + MachCpuSubtypeHppa7100Lc = 1, + // + // ARM Subtypes + // + MachCpuSubtypeArmAll = 0, + MachCpuSubtypeArmV4T = 5, + MachCpuSubtypeArmV6 = 6, + MachCpuSubtypeArmV5Tej = 7, + MachCpuSubtypeArmXscale = 8, + MachCpuSubtypeArmV7 = 9, + MachCpuSubtypeArmV7F = 10, + MachCpuSubtypeArmV7S = 11, + MachCpuSubtypeArmV7K = 12, + MachCpuSubtypeArmV8 = 13, + MachCpuSubtypeArmV6M = 14, + MachCpuSubtypeArmV7M = 15, + MachCpuSubtypeArmV7Em = 16, + // + // ARM64 Subytypes + // + MachCpuSubtypeArm64All = 0, + MachCpuSubtypeArm64V8 = 1, + // + // ARM64_32 Subtypes + // + MachCpuSubtypeArm6432All = 0, + MachCpuSubtypeArm6432V8 = 1, + // + // MC88000 Subytpes + // + MachCpuSubtypeMc88000All = 0, + MachCpuSubtypeMc88100 = 1, + MachCpuSubtypeMc88110 = 2, + // + // SPARC Subtypes + // + MachCpuSubtypeSparcAll = 0, + // + // i860 Subtypes + // + MachCpuSubtypeI860All = 0, + MachCpuSubtypeI860860 = 1, + // + // PowerPC Subtypes + // + MachCpuSubtypePowerPcAll = 0, + MachCpuSubtypePowerPc601 = 1, + MachCpuSubtypePowerPc602 = 2, + MachCpuSubtypePowerPc603 = 3, + MachCpuSubtypePowerPc603e = 4, + MachCpuSubtypePowerPc603ev = 5, + MachCpuSubtypePowerPc604 = 6, + MachCpuSubtypePowerPc604e = 7, + MachCpuSubtypePowerPc620 = 8, + MachCpuSubtypePowerPc750 = 9, + MachCpuSubtypePowerPc7400 = 10, + MachCpuSubtypePowerPc7450 = 11, + MachCpuSubtypePowerPc970 = 100, + // + // VEO Subtypes + // + MachCpuSubtypeVeo1 = 1, + MachCpuSubtypeVeo2 = 2, + MachCpuSubtypeVeo3 = 3, + MachCpuSubtypeVeo4 = 4, + MachCpuSubtypeVeoAll = MachCpuSubtypeVeo2 +}; + +typedef INT32 MACH_CPU_SUBTYPE; + +/// +/// CPU Family definitions +/// +enum { + MachCpuFamilyUnknown = 0, + // + // PowerPC Family + // + MachCpuFamilyPowerPcG3 = 0xCEE41549, + MachCpuFamilyPowerPcG4 = 0x77C184AE, + MachCpuFamilyPowerPcG5 = 0xED76D8AA, + // + // Intel Family + // + MachCpuFamilyIntel613 = 0xAA33392B, + MachCpuFamilyIntelYonah = 0x73D67300, + MachCpuFamilyIntelMerom = 0x426F69EF, + MachCpuFamilyIntelPenryn = 0x78EA4FBC, + MachCpuFamilyIntelNehalem = 0x6B5A4CD2, + MachCpuFamilyIntelWestmere = 0x573B5EEC, + MachCpuFamilyIntelSandyBridge = 0x5490B78C, + MachCpuFamilyIntelIvyBridge = 0x1F65E835, + MachCpuFamilyIntelHaswell = 0x10B282DC, + MachCpuFamilyIntelBroadwell = 0x582ED09C, + MachCpuFamilyIntelSkylake = 0x37FC219F, + MachCpuFamilyIntelKabyLake = 0x0F817246, + MachCpuFamilyIntelCoffeeLake = MachCpuFamilyIntelKabyLake, + // + // The following synonyms are deprecated: + // + MachCpuFamilyIntel614 = MachCpuFamilyIntelYonah, + MachCpuFamilyIntel615 = MachCpuFamilyIntelMerom, + MachCpuFamilyIntel623 = MachCpuFamilyIntelPenryn, + MachCpuFamilyIntel626 = MachCpuFamilyIntelNehalem, + MachCpuFamilyIntelCore = MachCpuFamilyIntelYonah, + MachCpuFamilyIntelCore2 = MachCpuFamilyIntelMerom, + // + // ARM Family + // + MachCpuFamilyArm9 = 0xE73283AE, + MachCpuFamilyArm11 = 0x8FF620D8, + MachCpuFamilyArmXscale = 0x53B005F5, + MachCpuFamilyArm12 = 0xBD1B0AE9, + MachCpuFamilyArm13 = 0x0CC90E64, + MachCpuFamilyArm14 = 0x96077EF1, + MachCpuFamilyArm15 = 0xA8511BCA, + MachCpuFamilyArmSwift = 0x1E2D6381, + MachCpuFamilyArmCyclone = 0x37A09642, + MachCpuFamilyArmTyphoon = 0x2C91A47E, + MachCpuFamilyArmTwister = 0x92FB37C8, + MachCpuFamilyArmHurricane = 0x67CEEE93, + MachCpuFamilyArmMonsoonMistral = 0xE81E7EF6, +}; + +typedef UINT32 MACH_CPU_FAMILY; +typedef INT32 MACH_CPU_THREADTYPE; +typedef INT32 MACH_VM_PROTECTION; + +/// +/// A variable length string in a load command is represented by an lc_str +/// union. The strings are stored just after the load command structure and +/// the offset is from the start of the load command structure. The size of +/// the string is reflected in the cmdsize field of the load command. Once +/// again any padded bytes to bring the cmdsize field to a multiple of 4 bytes +/// must be zero. +/// +typedef union { + UINT32 Offset; ///< offset to the string + UINT32 Address32; ///< pointer to the string +} MACH_LOAD_COMMAND_STRING; + +/// +/// Fixed virtual memory shared libraries are identified by two things. The +/// target pathname (the name of the library as found for execution), and the +/// minor version number. The address of where the headers are loaded is in +/// header_addr. (THIS IS OBSOLETE and no longer supported). +/// +typedef struct { + MACH_LOAD_COMMAND_STRING Name; ///< library's target pathname + UINT32 MinorVersion; ///< library's minor version number + UINT32 HeaderAddress; ///< library's header address +} MACH_FIXED_VM_LIB; + +/// +/// Dynamicly linked shared libraries are identified by two things. The +/// pathname (the name of the library as found for execution), and the +/// compatibility version number. The pathname must match and the +/// compatibility number in the user of the library must be greater than or +/// equal to the library being used. The time stamp is used to record the time +/// a library was built and copied into user so it can be use to determined if +/// the library used at runtime is exactly the same as used to built the +/// program. +/// +typedef struct { + /// + /// library's path name + /// + MACH_LOAD_COMMAND_STRING Name; + /// + /// library's build time stamp + /// + UINT32 Timestamp; + /// + /// library's current version number + /// + UINT32 CurrentVersion; + /// + /// library's compatibility vers number + /// + UINT32 CompatibilityVersion; +} MACH_DYLIB; + +// +// Mach Load Commands +// + +/// +/// After MacOS X 10.1 when a new load command is added that is required to be +/// understood by the dynamic linker for the image to execute properly the +/// MACH_LC_REQUIRE_DYLD bit will be or 'ed into the load +/// command constant. If the dynamic linker sees such a load command it it +/// does not understand will issue a "unknown load command required for +/// execution" error and refuse to use the image. Other load commands without +/// this bit that are not understood will simply be ignored. +/// +#define MACH_LC_REQUIRE_DYLD BIT31 +/// +/// segment of this file to be mapped +/// +#define MACH_LOAD_COMMAND_SEGMENT 1U +/// +/// link-edit stab symbol table info +/// +#define MACH_LOAD_COMMAND_SYMTAB 2U +/// +/// link-edit gdb symbol table info (obsolete) +/// +#define MACH_LOAD_COMMAND_SYMSEG 3U +/// +/// thread +/// +#define MACH_LOAD_COMMAND_THREAD 4U +/// +/// unix thread (includes a stack) +/// +#define MACH_LOAD_COMMAND_UNIX_THREAD 5U +/// +/// load a specified fixed VM shared library +/// +#define MACH_LOAD_COMMAND_LOAD_FIXED_VM_LIB 6U +/// +/// fixed VM shared library identification +/// +#define MACH_LOAD_COMMAND_IDENTIFICATION_VM_LIB 7U +/// +/// object identification info (obsolete) +/// +#define MACH_LOAD_COMMAND_IDENTIFICATION 8U +/// +/// fixed VM file inclusion (internal use) +/// +#define MACH_LOAD_COMMAND_FIXED_VM_FILE 9U +/// +/// prepage command (internal use) +/// +#define MACH_LOAD_COMMAND_PRE_PAGE 10U +/// +/// dynamic link-edit symbol table info +/// +#define MACH_LOAD_COMMAND_DYSYMTAB 11U +/// +/// load a dynamically linked shared library +/// +#define MACH_LOAD_COMMAND_LOAD_DYLIB 12U +/// +/// dynamically linked shared lib ident +/// +#define MACH_LOAD_COMMAND_IDENTITY_DYLIB 13U +/// +/// load a dynamic linker +/// +#define MACH_LOAD_COMMAND_LOAD_DYLD 14U +/// +/// dynamic linker identification +/// +#define MACH_LOAD_COMMAND_IDENTIFICATION_DYLD 15U +/// +/// modules prebound for a dynamically +/// +#define MACH_LOAD_COMMAND_PREBOUNT_SYLIB 16U +/// +/// image routines +/// +#define MACH_LOAD_COMMAND_ROUTINES 17U +/// +/// sub framework +/// +#define MACH_LOAD_COMMAND_SUB_FRAMEWORK 18U +/// +/// sub umbrella +/// +#define MACH_LOAD_COMMAND_SUB_UMBRELLA 19U +/// +/// sub clien +/// +#define MACH_LOAD_COMMAND_SUB_CLIENT 20U +/// +/// sub library +/// +#define MACH_LOAD_COMMAND_SUB_LIBRARY 21U +/// +/// two-level namespace lookup hints +/// +#define MACH_LOAD_COMMAND_TWO_LEVEL_HINTS 22U +/// +/// prebind checksum +/// +#define MACH_LOAD_COMMAND_PREBIND_CHECKSUM 23U +/// +/// load a dynamically linked shared library that is allowed to be missing +/// (all symbols are weak imported). +/// +#define MACH_LOAD_COMMAND_LLOAD_WEAK_DYLIB (24U | MACH_LC_REQUIRE_DYLD) +/// +/// 64-bit segment of this file to be mapped +/// +#define MACH_LOAD_COMMAND_SEGMENT_64 25U +/// +/// 64-bit image routines +/// +#define MACH_LOAD_COMMAND_ROUTINES_64 26U +/// +/// the uuid +/// +#define MACH_LOAD_COMMAND_UUID 27U +/// +/// runpath additions +/// +#define MACH_LOAD_COMMAND_RUN_PATH (28U | MACH_LC_REQUIRE_DYLD) +/// +/// local of code signature +/// +#define MACH_LOAD_COMMAND_CODE_SIGNATURE 29U +/// +/// local of info to split segments +/// +#define MACH_LOAD_COMMAND_SEGMENT_SPLIT_INFO 30U +/// +/// load and re-export dylib +/// +#define MACH_LOAD_COMMAND_REEXPORT_DYLIB (31U | MACH_LC_REQUIRE_DYLD) +/// +/// delay load of dylib until first use +/// +#define MACH_LOAD_COMMAND_LAZY_LOAD_DYLIB 32U +/// +/// encrypted segment information +/// +#define MACH_LOAD_COMMAND_ENCRYPTION_INFO 33U +/// +/// compressed dyld information +/// +#define MACH_LOAD_COMMAND_DYLD_INFO 34U +/// +/// compressed dyld information only +/// +#define MACH_LOAD_COMMAND_DYLD_INFO_ONLY (34U | MACH_LC_REQUIRE_DYLD) +/// +/// load upward dylib +/// +#define MACH_LOAD_COMMAND_LOAD_UPWARD_DYLIB (35U | MACH_LC_REQUIRE_DYLD) +/// +/// build for MacOSX min OS version +/// +#define MACH_LOAD_COMMAND_VERSION_MIN_MAC_OS_X 36U +/// +/// build for iPhoneOS min OS version +/// +#define MACH_LOAD_COMMAND_VERSION_MIN_IPHONE_OS 37U +/// +/// compressed table of function start addresses +/// +#define MACH_LOAD_COMMAND_FUNCTION_STARTS 38U +/// +/// string for dyld to treat like environment variable +/// +#define MACH_LOAD_COMMAND_DYLD_ENVIRONMENT 39U +/// +/// replacement for MachLoadCommandUnixThread +/// +#define MACH_LOAD_COMMAND_MAIN (40U | MACH_LC_REQUIRE_DYLD) +/// +/// table of non-in structions in __text +/// +#define MACH_LOAD_COMMAND_DATA_IN_CODE 41U +/// +/// source version used to build binary +/// +#define MACH_LOAD_COMMAND_SOURCE_VERSION 42U +/// +/// Code signing DRs copied from linked dylibs +/// +#define MACH_LOAD_COMMAND_DYLIB_CODE_SIGN_DRS 43U +/// +/// 64-bit encrypted segment information +/// +#define MACH_LOAD_COMMAND_ENCRYPTION_INFO_64 44U +/// +/// linker options in MH_OBJECT files +/// +#define MACH_LOAD_COMMAND_LINKER_OPTION 45U +/// +/// optimization hints in MH_OBJECT files +/// +#define MACH_LOAD_COMMAND_LINKER_OPTIMIZATION_HINT 46U +/// +/// build for AppleTV min OS version +/// +#define MACH_LOAD_COMMAND_VERSION_MIN_TV_OS 47U +/// +/// build for Watch min OS version +/// +#define MACH_LOAD_COMMAND_VERSION_MIN_WATCH_OS 48U +/// +/// arbitrary data included within a Mach-O file +/// +#define MACH_LOAD_COMMAND_NOTE 49U +/// +/// build for platform min OS version +/// +#define MACH_LOAD_COMMAND_BUILD_VERSION 50U + +typedef UINT32 MACH_LOAD_COMMAND_TYPE; + +#define MACH_LOAD_COMMAND_HDR_ \ + MACH_LOAD_COMMAND_TYPE CommandType; /* type of load command */ \ + UINT32 CommandSize; /* total size of command in bytes + (includes sizeof section structs) */ + +#define NEXT_MACH_LOAD_COMMAND(Command) \ + ((MACH_LOAD_COMMAND *)((UINTN)(Command) + (Command)->CommandSize)) + +typedef struct { + MACH_LOAD_COMMAND_HDR_ +} MACH_LOAD_COMMAND; + +// +// Constants for the Flags field of the MACH_SEGMENT_COMMAND struct +// + +/// +/// the file contents for this segment is for the high part of the VM space, +/// the low part is zero filled (for stacks in core files) +/// +#define MACH_SEGMENT_FLAG_HIGH_VIRTUAL_MEMORY BIT0 +/// +/// this segment is the VM that is allocated by a fixed VM library, for overlap +/// checking in the link editor +/// +#define MACH_SEGMENT_FLAG_FIXED_VM_LIB BIT1 +/// +/// this segment has nothing that was relocated in it and nothing relocated to +/// it, that is it maybe safely replaced without relocation +/// +#define MACH_SEGMENT_FLAG_NO_RELOCATION BIT2 +/// +/// This segment is protected. If the segment starts at file offset 0, the +/// first page of the segment is not protected. All other pages of the +/// segment are protected. +/// +#define MACH_SEGMENT_FLAG_PROTECTED_VERSION_1 BIT3 + +#define MACH_SEGMENT_VM_PROT_NONE 0U + +#define MACH_SEGMENT_VM_PROT_READ BIT0 +#define MACH_SEGMENT_VM_PROT_WRITE BIT1 +#define MACH_SEGMENT_VM_PROT_EXECUTE BIT2 + +typedef UINT32 MACH_SEGMENT_FLAGS; + +/// +/// Constants for the type of a section +/// +enum { + /// + /// regular section + /// + MachSectionTypeRegular = 0, + /// + /// zero fill on demand section + /// + MachSectionTypeZeroFill = 1, + /// + /// section with only literal C strings + /// + MachSectionTypeCStringLiterals = 2, + /// + /// section with only 4 byte literals + /// + MachSectionType4ByteLiterals = 3, + /// + /// section with only 8 byte literals + /// + MachSectionType8ByteLiterals = 4, + /// + /// section with only pointers to literals + /// + MachSectionTypeLiteralPointers = 5, + + // + // For the two types of symbol pointers sections and the symbol stubs section + // they have indirect symbol table entries. For each of the entries in the + // section the indirect symbol table entries, in corresponding order in the + // indirect symbol table, start at the index stored in the reserved1 field + // of the section structure. Since the indirect symbol table entries + // correspond to the entries in the section the number of indirect symbol + // table entries is inferred from the size of the section divided by the size + // of the entries in the section. For symbol pointers sections the size of + // the entries in the section is 4 bytes and for symbol stubs sections the + // byte size of the stubs is stored in the reserved2 field of the section + // structure. + // + + /// + /// section with only non-lazy symbol pointers + /// + MachSectionTypeNonLazySymbolPointers = 6, + /// + /// section with only lazy symbol pointers + /// + MachSectionTypeLazySymbolPointers = 7, + /// + /// section with only symbol stubs, byte size of stub in the reserved2 field + /// + MachSectionTypeSymbolStubs = 8, + /// + /// section with only function pointers for initialization + /// + MachSectionTypeModInitalizeFunctionPointers = 9, + /// + /// section with only function pointers for termination + /// + MachSectionTypeModTerminateFunctionPointers = 10, + /// + /// section contains symbols that are to be coalesced + /// + MachSectionTypeCoalesced = 11, + /// + /// zero fill on demand section (that can be larger than 4 gigabytes) + /// + MachSectionTypeGigabyteZeroFill = 12, + /// + /// section with only pairs of function pointers for interposing + /// + MachSectionTypeInterposing = 13, + /// + /// section with only 16 byte literals + /// + MachSectionType16ByteLiterals = 14, + /// + /// section contains DTrace Object Format + /// + MachSectionTypeDtraceObjectFormat = 15, + /// + /// section with only lazy symbol pointers to lazy loaded dylibs + /// + MachSectionTypeLazyDylibSymbolPointers = 16, + + // + // Section types to support thread local variables + // + + /// + /// template of initial values for TLVs + /// + MachSectionTypeThreadLocalRegular = 17, + /// + /// template of initial values for TLVs + /// + MachSectionTypeThreadLocalZeroFill = 18, + /// + /// TLV descriptors + /// + MachSectionTypeThreadLocalVariables = 19, + /// + /// pointers to TLV descriptors + /// + MachSectionTypeThreadVariablePointers = 20, + /// + /// functions to call to initialize TLV values + /// + MachSectionTypeThreadLocalInitializeFunctionPointers = 21, + /// + /// 32-bit offsets to initializers + /// + MachSectionTypeThreadLocalInitializeFunctionOffsets = 22 +}; + +// +// Constants for the section attributes part of the Flags field of a section +// structure. +// + +/// +/// system setable attributes +/// +#define MACH_SECTION_ATTRIBUTES_SYSYTEM 0x00FFFF00U +/// +/// User setable attributes +/// +#define MACH_SECTION_ATTRIBUTES_USER 0xFF000000U +/// +/// section has local relocation entries +/// +#define MACH_SECTION_ATTRIBUTE_LOCAL_RELOCATION BIT8 +/// +/// section has external relocation entries +/// +#define MACH_SECTION_ATTRIBUTE_EXTERNAL_RELOCATION BIT9 +/// +/// section contains some machine instructions +/// +#define MACH_SECTION_ATTRIBUTE_SOME_INSTRUCTIONS BIT10 + +// +// The flags field of a section structure is separated into two parts a section +// type and section attributes.The section types are mutually exclusive (it +// can only have one type) but the section attributes are not (it may have more +// than one attribute). +// +#define MACH_SECTION_TYPE_MASK 0x000000FFU ///< 256 section types +#define MACH_SECTION_ATTRIBUTES_MASK 0xFFFFFF00U ///< 24 section attributes + +/// +/// If a segment contains any sections marked with +/// MACH_SECTION_ATTRIBUTE_DEBUG then all sections in that segment must have +/// this attribute. No section other than a section marked with this attribute +/// may reference the contents of this section. A section with this attribute +/// may contain no symbols and must have a section type S_REGULAR. The static +/// linker will not copy section contents from sections with this attribute +/// into its output file. These sections generally contain DWARF debugging +/// info. +/// +#define MACH_SECTION_ATTRIBUTE_DEBUG BIT25 +/// +/// Used with i386 code stubs written on by dyld +/// +#define MACH_SECTION_ATTRIBUTE_SELF_MODIFYING_CODE BIT26 +/// +/// blocks are live if they reference live blocks +/// +#define MACH_SECTION_ATTRIBUTE_LIVE_SUPPORT BIT27 +/// +/// no dead stripping +/// +#define MACH_SECTION_ATTRIBUTE_NO_DEAD_STRIP BIT28 +/// +/// ok to strip static symbols in this section in files with the +/// MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK flag +/// +#define MACH_SECTION_ATTRIBUTE_STRIP_STATIC_SYMBOLS BIT29 +/// +/// section contains coalesced symbols that are not to be in a ranlib table of +/// contents +/// +#define MACH_SECTION_ATTRIBUTE_NO_TOC BIT30 +/// +/// section contains only true machine instructions +/// +#define MACH_SECTION_ATTRIBUTE_PURE_INSTRUCTIONS BIT31 + +/// +/// A segment is made up of zero or more sections. +/// Non-MachHeaderFileTypeObject files have all of their segments with the +/// proper sections in each, and padded to the Sectionecified segment alignment +/// when produced by the link editor. The first segment of a +/// MachHeaderFileTypeExecute and MachHeaderFileTypeFixedVmLib format file +/// contains the MACH_HEADER and load commands of the object file before its +/// first section. The zero fill sections are always last in their segment (in +/// all formats). This allows the zeroed segment padding to be mapped into +/// memory where zero fill sections might be. The gigabyte zero fill sections, +/// those with the section type MachSectionTypeGigabyteZeroFill, can only be +/// in a segment with sections of this type. These segments are then placed +/// after all other segments. +/// +/// The MachHeaderFileTypeObject format has all of its sections in one segment +/// for compactness. There is no padding to a Sectionecified segment boundary +/// and the MACH_HEADER and load commands are not part of the segment. +/// +/// Sections with the same section Name, SectionName, going into the same +/// segment, SegmentName, are combined by the link editor. The resulting +/// section is aligned to the maximum alignment of the combined sections and is +/// the new section's alignment. The combined sections are aligned to their +/// original alignment in the combined section. Any padded bytes to get the +/// Sectionecified alignment are zeroed. +/// +/// The format of the relocation entries referenced by the reloff and nreloc +/// fields of the section structure for mach object files is described in the +/// header file . +/// +/// for 32-bit architectures +/// +typedef struct { + CHAR8 SectionName[16]; ///< Name of this section + CHAR8 SegmentName[16]; ///< segment this section goes in + UINT32 Address; ///< memory address of this section + UINT32 Size; ///< size in bytes of this section + UINT32 Offset; ///< file offset of this section + UINT32 Alignment; ///< section alignment (power of 2) + UINT32 RelocationEntriesOffset; ///< file offset of relocation entries + UINT32 NumRelocationEntries; ///< number of relocation entries + UINT32 Flags; ///< flags (section type and attributes) + UINT32 Reserved1; ///< reserved (for offset or index) + UINT32 Reserved2; ///< reserved (for count or sizeof) +} MACH_SECTION; + +/// +/// for 64-bit architectures +/// +typedef struct { + CHAR8 SectionName[16]; ///< Name of this section + CHAR8 SegmentName[16]; ///< segment this section goes in + UINT64 Address; ///< memory address of this section + UINT64 Size; ///< size in bytes of this section + UINT32 Offset; ///< file offset of this section + UINT32 Alignment; ///< section alignment (power of 2) + UINT32 RelocationsOffset; ///< file offset of relocation entries + UINT32 NumRelocations; ///< number of relocation entries + UINT32 Flags; ///< flags (section type and attributes) + UINT32 Reserved1; ///< reserved (for offset or index) + UINT32 Reserved2; ///< reserved (for count or sizeof) + UINT32 Reserved3; ///< reserved +} MACH_SECTION_64; + +#define NEXT_MACH_SEGMENT(Segment) \ + (MACH_SEGMENT_COMMAND *)((UINTN)(Segment) + (Segment)->Command.Size) + +/// +/// The segment load command indicates that a part of this file is to be +/// mapped into the task's address Sectionace. The size of this segment in +/// memory, vmsize, maybe equal to or larger than the amount to map from this +/// file, filesize. The file is mapped starting at fileoff to the beginning of +/// the segment in memory, vmaddr. The rest of the memory of the segment, +/// if any, is allocated zero fill on demand. The segment's maximum virtual +/// memory protection and initial virtual memory protection are Sectionecified +/// by the maxprot and initprot fields. If the segment has sections then the +/// section structures directly follow the segment command and their size is +/// reflected in cmdsize. +/// +/// for 32 - bit architectures +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + CHAR8 SegmentName[16]; ///< segment Name + UINT32 VirtualAddress; ///< memory address of this segment + UINT32 Size; ///< memory size of this segment + UINT32 FileOffset; ///< file offset of this segment + UINT32 FileSize; ///< amount to map from the file + MACH_VM_PROTECTION MaximumProtection; ///< maximum VM protection + MACH_VM_PROTECTION InitialProtection; ///< initial VM protection + UINT32 NumSections; ///< number of sections in segment + MACH_SEGMENT_FLAGS Flags; ///< flags + MACH_SECTION Sections[]; +} MACH_SEGMENT_COMMAND; + +#define NEXT_MACH_SEGMENT_64(Segment) \ + (MACH_SEGMENT_COMMAND_64 *)((UINTN)(Segment) + (Segment)->Hdr.Size) + +/// +/// The 64-bit segment load command indicates that a part of this file is to be +/// mapped into a 64-bit task's address Sectionace. If the 64-bit segment has +/// sections then section_64 structures directly follow the 64-bit segment +/// command and their size is reflected in cmdsize. +/// +/// for 64-bit architectures +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + CHAR8 SegmentName[16]; ///< segment Name + UINT64 VirtualAddress; ///< memory address of this segment + UINT64 Size; ///< memory size of this segment + UINT64 FileOffset; ///< file offset of this segment + UINT64 FileSize; ///< amount to map from the file + MACH_VM_PROTECTION MaximumProtection; ///< maximum VM protection + MACH_VM_PROTECTION InitialProtection; ///< initial VM protection + UINT32 NumSections; ///< number of sections in segment + MACH_SEGMENT_FLAGS Flags; ///< flags + MACH_SECTION_64 Sections[]; +} MACH_SEGMENT_COMMAND_64; + +/// +/// A fixed virtual shared library (filetype == MH_FVMLIB in the mach header) +/// contains a fvmlib_command (cmd == LC_IDFVMLIB) to identify the library. +/// An object that uses a fixed virtual shared library also contains a +/// fvmlib_command (cmd == LC_LOADFVMLIB) for each library it uses. +/// (THIS IS OBSOLETE and no longer supported). +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_FIXED_VM_LIB FixedVmLib; ///< the library identification +} MACH_FIXED_VM_LIB_COMMAND; + +/// +/// A dynamically linked shared library (filetype == MH_DYLIB in the mach +/// header) contains a dylib_command (cmd == LC_ID_DYLIB) to identify the +/// library. An object that uses a dynamically linked shared library also +/// contains a dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or +/// LC_REEXPORT_DYLIB) for each library it uses. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_DYLIB Dylib; ///< the library identification +} MACH_DYLIB_COMMAND; + +/// +/// A dynamically linked shared library may be a subframework of an umbrella +/// framework. If so it will be linked with "-umbrella umbrella_name" where +/// Where "umbrella_name" is the name of the umbrella framework. A subframework +/// can only be linked against by its umbrella framework or other subframeworks +/// that are part of the same umbrella framework. Otherwise the static link +/// editor produces an error and states to link against the umbrella framework. +/// The name of the umbrella framework for subframeworks is recorded in the +/// following structure. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING Umbrella; ///< the umbrella framework name +} MACH_SUB_FRAMEWORK_COMMAND; + +/// +/// For dynamically linked shared libraries that are subframework of an +/// umbrella framework they can allow clients other than the umbrella +/// framework or other subframeworks in the same umbrella framework. To do +/// this the subframework is built with "-allowable_client client_name" and an +/// LC_SUB_CLIENT load command is created for each -allowable_client flag. +/// The client_name is usually a framework name. It can also be a name used +/// for bundles clients where the bundle is built with +/// "-client_name client_name". +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING Client; ///< the client name +} MACH_SUB_CLIENT_COMMAND; + +/// +/// A dynamically linked shared library may be a sub_umbrella of an umbrella +/// framework. If so it will be linked with "-sub_umbrella umbrella_name" +/// where "umbrella_name" is the name of the sub_umbrella framework. When +/// staticly linking when -twolevel_namespace is in effect a twolevel namespace +/// umbrella framework will only cause its subframeworks and those frameworks +/// listed as sub_umbrella frameworks to be implicited linked in. Any other +/// dependent dynamic libraries will not be linked it when -twolevel_namespace +/// is in effect. The primary library recorded by the static linker when +/// resolving a symbol in these libraries will be the umbrella framework. +/// Zero or more sub_umbrella frameworks may be use by an umbrella framework. +/// The name of a sub_umbrella framework is recorded in the following structure. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING SubUmbrella; ///< the sub_umbrella framework name +} MACH_SUB_UMBRELLA_COMMAND; + +/// +/// A dynamically linked shared library may be a sub_library of another shared +/// library. If so it will be linked with "-sub_library library_name" where +/// Where "library_name" is the name of the sub_library shared library. When +/// staticly linking when -twolevel_namespace is in effect a twolevel namespace +/// shared library will only cause its subframeworks and those frameworks +/// listed as sub_umbrella frameworks and libraries listed as sub_libraries to +/// be implicited linked in. Any other dependent dynamic libraries will not be +/// linked it when -twolevel_namespace is in effect. The primary library +/// recorded by the static linker when resolving a symbol in these libraries +/// will be the umbrella framework (or dynamic library). Zero or more +/// sub_library shared libraries may be use by an umbrella framework or (or +/// dynamic library). The name of a sub_library framework is recorded in the +/// following structure. For example /usr/lib/libobjc_profile.A.dylib would be +/// recorded as "libobjc". +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING SubLibrary; ///< the sub_library name +} MACH_SUB_LIBRARY_COMMAND; + +/// +/// A program (filetype == MH_EXECUTE) that is +/// prebound to its dynamic libraries has one of these for each library that +/// the static linker used in prebinding. It contains a bit vector for the +/// modules in the library. The bits indicate which modules are bound (1) and +/// which are not (0) from the library. The bit for module 0 is the low bit +/// of the first byte. So the bit for the Nth module is: +/// (linked_modules[N/8] >> N%8) & 1 +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING Name; ///< library's path name + UINT32 NumModules; ///< number of modules in library + MACH_LOAD_COMMAND_STRING LinkedModules[]; ///< bit vector of linked modules +} MACH_PREBOUND_DYLIB_COMMAND; + +/// +/// A program that uses a dynamic linker contains a dylinker_command to +/// identify the name of the dynamic linker (LC_LOAD_DYLINKER). And a dynamic +/// linker contains a dylinker_command to identify the dynamic linker +/// (LC_ID_DYLINKER). A file can have at most one of these. +/// This struct is also used for the LC_DYLD_ENVIRONMENT load command and +/// contains string for dyld to treat like environment variable. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING Name; ///< dynamic linker's path name +} MACH_DYLINKER_COMMAND; + +enum { + MachX86ThreadState32 = 1, + MachX86FloatState32 = 2, + MachX86ExceptionState32 = 3, + MachX86ThreadState64 = 4, + MachX86FloatState64 = 5, + MachX86ExceptionState64 = 6, + MachX86ThreadState = 7, + MachX86FloatState = 8, + MachX86ExceptionState = 9, + MachX86DebugState32 = 10, + MachX86DebugState64 = 11, + MachX86DebugState = 12, + MachThreadStateNone = 13, + // + // 14 and 15 are used for the internal x86SavedState flavours. + // + MachX86AvxState32 = 16, + MachX86AvxState64 = 17, + MachX86AvxState = 18, + MachX86Avx512State32 = 19, + MachX86Avx512State64 = 20, + MachX86Avx512State = 21, +}; + +/// +/// Size of maximum exported thread state in words +/// +#define MACH_X86_THREAD_STATE_MAX 614 + +typedef struct { + UINT32 eax; + UINT32 ebx; + UINT32 ecx; + UINT32 edx; + UINT32 edi; + UINT32 esi; + UINT32 ebp; + UINT32 esp; + UINT32 ss; + UINT32 eflags; + UINT32 eip; + UINT32 cs; + UINT32 ds; + UINT32 es; + UINT32 fs; + UINT32 gs; +} MACH_X86_THREAD_STATE32; + +typedef struct { + UINT64 rax; + UINT64 rbx; + UINT64 rcx; + UINT64 rdx; + UINT64 rdi; + UINT64 rsi; + UINT64 rbp; + UINT64 rsp; + UINT64 r8; + UINT64 r9; + UINT64 r10; + UINT64 r11; + UINT64 r12; + UINT64 r13; + UINT64 r14; + UINT64 r15; + UINT64 rip; + UINT64 rflags; + UINT64 cs; + UINT64 fs; + UINT64 gs; +} MACH_X86_THREAD_STATE64; + +typedef union { + MACH_X86_THREAD_STATE32 State32; + MACH_X86_THREAD_STATE64 State64; +} MACH_X86_THREAD_STATE; + +/// +/// MACH_VALID_THREAD_STATE_FLAVOR is a platform specific macro that when +/// passed an exception flavor will return if that is a defined flavor for that +/// platform. The macro must be manually updated to include all of the valid +/// exception flavors as defined above. +/// +#define MACH_VALID_THREAD_STATE_FLAVOR(Flavor) \ + (((Flavor) == MachX86ThreadState32) \ + || ((Flavor) == MachX86FloatState32) \ + || ((Flavor) == MachX86ExceptionState32) \ + || ((Flavor) == MachX86DebugState32) \ + || ((Flavor) == MachX86ThreadState64) \ + || ((Flavor) == MachX86FloatState64) \ + || ((Flavor) == MachX86ExceptionState64) \ + || ((Flavor) == MachX86DebugState64) \ + || ((Flavor) == MachX86ThreadState) \ + || ((Flavor) == MachX86FloatState) \ + || ((Flavor) == MachX86ExceptionState) \ + || ((Flavor) == MachX86DebugState) \ + || ((Flavor) == MachX86AvxState32) \ + || ((Flavor) == MachX86AvxState64) \ + || ((Flavor) == MachX86AvxState) \ + || ((Flavor) == MachX86Avx512State32) \ + || ((Flavor) == MachX86Avx512State64) \ + || ((Flavor) == MachX86Avx512State) \ + || ((Flavor) == MachThreadStateNone)) + +/// +/// Thread commands contain machine-specific data structures suitable for +/// use in the thread state primitives. The machine specific data structures +/// follow the struct thread_command as follows. +/// Each flavor of machine specific data structure is preceded by an unsigned +/// long constant for the flavor of that data structure, an UINT32 +/// that is the count of longs of the size of the state data structure and +/// then the state data structure follows. This triple may be repeated for +/// many flavors. The constants for the flavors, counts and state data +/// structure definitions are expected to be in the header file +/// . +/// These machine specific data structures sizes must be multiples of +/// 4 bytes The cmdsize reflects the total size of the thread_command +/// and all of the sizes of the constants for the flavors, counts and state +/// data structures. +/// +/// For executable objects that are unix processes there will be one +/// thread_command (cmd == LC_UNIXTHREAD) created for it by the link-editor. +/// This is the same as a LC_THREAD, except that a stack is automatically +/// created (based on the shell's limit for the stack size). Command arguments +/// and environment variables are copied onto that stack. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 Flavor; ///< flavor of thread state + UINT32 NumThreadStates; ///< count of UINT32s in thread state + UINT32 ThreadState[]; +} MACH_THREAD_COMMAND; + +/// +/// The routines command contains the address of the dynamic shared library +/// initialization routine and an index into the module table for the module +/// that defines the routine. Before any modules are used from the library the +/// dynamic linker fully binds the module that defines the initialization routine +/// and then calls it. This gets called before any module initialization +/// routines (used for C++ static constructors) in the library. +/// For 32-bit architectures. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + /// + /// address of initialization routine + /// + UINT32 InitRoutineAddress; + /// + /// index into the module table that the init routine is defined in + /// + UINT32 InitModuleIndex; + UINT32 Reserved1; + UINT32 Reserved2; + UINT32 Reserved3; + UINT32 Reserved4; + UINT32 Reserved5; + UINT32 Reserved6; +} MACH_ROUTINES_COMMAND; + +/// +/// The 64-bit routines command. Same use as above. For 64-bit architectures. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + /// + /// address of initialization routine + /// + UINT64 InitiRoutineAddress; + /// + /// index into the module table that the init routine is defined in + /// + UINT64 InitModuleIndex; + UINT64 Reserved1; + UINT64 Reserved2; + UINT64 Reserved3; + UINT64 Reserved4; + UINT64 Reserved5; + UINT64 Reserved6; +} MACH_ROUTINES_COMMAND_64; + +/// +/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD +/// "stab" style symbol table information as described in the header files +/// and . +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 SymbolsOffset; ///< symbol table offset + UINT32 NumSymbols; ///< number of symbol table entries + UINT32 StringsOffset; ///< string table offset + UINT32 StringsSize; ///< string table size in bytes +} MACH_SYMTAB_COMMAND; + +/// +/// This is the second set of the symbolic information which is used to support +/// the data structures for the dynamically link editor. +/// +/// The original set of symbolic information in the symtab_command which +/// contains the symbol and string tables must also be present when this load +/// command is present. When this load command is present the symbol table is +/// organized into three groups of symbols: +/// local symbols (static and debugging symbols) - grouped by module +/// defined external symbols - grouped by module (sorted by name if not lib) +/// undefined external symbols (sorted by name if MH_BINDATLOAD is not set, +/// and in order the were seen by the static +/// linker if MH_BINDATLOAD is set) +/// In this load command there are offsets and counts to each of the three +/// groups of symbols. +/// +/// This load command contains a the offsets and sizes of the following new +/// symbolic information tables: +/// table of contents +/// module table +/// reference symbol table +/// indirect symbol table +/// The first three tables above (the table of contents, module table and +/// reference symbol table) are only present if the file is a dynamically +/// linked shared library. For executable and object modules, which are files +/// containing only one module, the information that would be in these three +/// tables is determined as follows: +/// table of contents - the defined external symbols are sorted by name +/// module table - the file contains only one module so everything in the +/// file is part of the module. +/// reference symbol table - is the defined and undefined external symbols +/// +/// For dynamically linked shared library files this load command also contains +/// offsets and sizes to the pool of relocation entries for all sections +/// separated into two groups: +/// external relocation entries +/// local relocation entries +/// For executable and object modules the relocation entries continue to hang +/// off the section structures. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + + // + // The symbols indicated by symoff and nsyms of the LC_SYMTAB load command + // are grouped into the following three groups: + // local symbols (further grouped by the module they are from) + // defined external symbols (further grouped by the module they are from) + // undefined symbols + // + // The local symbols are used only for debugging. The dynamic binding + // process may have to use them to indicate to the debugger the local + // symbols for a module that is being bound. + // + // The last two groups are used by the dynamic binding process to do the + // binding (indirectly through the module table and the reference symbol + // table when this is a dynamically linked shared library file). + // + + UINT32 LocalSymbolsIndex; ///< index to local symbols + UINT32 NumLocalSymbols; ///< number of local symbols + + UINT32 ExternalSymbolsIndex; ///< index to externally defined symbols + UINT32 NumExternalSymbols; ///< number of externally defined symbols + + UINT32 UndefinedSymbolsIndex; ///< index to undefined symbols + UINT32 NumUndefinedSymbols; ///< number of undefined symbols + + // + // For the for the dynamic binding process to find which module a symbol + // is defined in the table of contents is used (analogous to the ranlib + // structure in an archive) which maps defined external symbols to modules + // they are defined in. This exists only in a dynamically linked shared + // library file. For executable and object modules the defined external + // symbols are sorted by name and is use as the table of contents. + // + + /// + /// file offset to table of contents + /// + UINT32 TableOfContentsFileOffset; + /// + /// number of entries in table of contents + /// + UINT32 TableOfContentsNumEntries; + + // + // To support dynamic binding of "modules" (whole object files) the symbol + // table must reflect the modules that the file was created from. This is + // done by having a module table that has indexes and counts into the merged + // tables for each module. The module structure that these two entries + // refer to is described below. This exists only in a dynamically linked + // shared library file. For executable and object modules the file only + // contains one module so everything in the file belongs to the module. + // + + UINT32 ModuleTableFileOffset; ///< file offset to module table + UINT32 ModuleTableNumEntries; ///< number of module table entries + + // + // To support dynamic module binding the module structure for each module + // indicates the external references (defined and undefined) each module + // makes. For each module there is an offset and a count into the + // reference symbol table for the symbols that the module references. + // This exists only in a dynamically linked shared library file. For + // executable and object modules the defined external symbols and the + // undefined external symbols indicates the external references. + // + + /// + /// offset to referenced symbol table + /// + UINT32 ReferencedSymbolTableFileOffset; + /// + /// number of referenced symbol table entries + /// + UINT32 ReferencedSymbolTableNumEntries; + + // + // The sections that contain "symbol pointers" and "routine stubs" have + // indexes and (implied counts based on the size of the section and fixed + // size of the entry) into the "indirect symbol" table for each pointer + // and stub. For every section of these two types the index into the + // indirect symbol table is stored in the section header in the field + // reserved1. An indirect symbol table entry is simply a 32bit index into + // the symbol table to the symbol that the pointer or stub is referring to. + // The indirect symbol table is ordered to match the entries in the section. + // + + /// + /// file offset to the indirect symbol table + /// + UINT32 IndirectSymbolsOffset; + /// + /// number of indirect symbol table entries + /// + UINT32 NumIndirectSymbols; + + // + // To support relocating an individual module in a library file quickly the + // external relocation entries for each module in the library need to be + // accessed efficiently. Since the relocation entries can't be accessed + // through the section headers for a library file they are separated into + // groups of local and external entries further grouped by module. In this + // case the presents of this load command who's extreloff, nextrel, + // locreloff and nlocrel fields are non-zero indicates that the relocation + // entries of non-merged sections are not referenced through the section + // structures (and the reloff and nreloc fields in the section headers are + // set to zero). + // + // Since the relocation entries are not accessed through the section headers + // this requires the r_address field to be something other than a section + // offset to identify the item to be relocated. In this case r_address is + // set to the offset from the vmaddr of the first LC_SEGMENT command. + // For MH_SPLIT_SEGS images r_address is set to the the offset from the + // vmaddr of the first read-write LC_SEGMENT command. + // + // The relocation entries are grouped by module and the module table + // entries have indexes and counts into them for the group of external + // relocation entries for that the module. + // + // For sections that are merged across modules there must not be any + // remaining external relocation entries for them (for merged sections + // remaining relocation entries must be local). + // + + /// + /// offset to external relocation entries + /// + UINT32 ExternalRelocationsOffset; + /// + /// number of external relocation entries + /// + UINT32 NumExternalRelocations; + + // + // All the local relocation entries are grouped together (they are not + // grouped by their module since they are only used if the object is moved + // from it staticly link edited address). + // + + UINT32 LocalRelocationsOffset; ///< offset to local relocation entries + UINT32 NumOfLocalRelocations; ///< number of local relocation entries +} MACH_DYSYMTAB_COMMAND; + +// +// An indirect symbol table entry is simply a 32bit index into the symbol table +// to the symbol that the pointer or stub is refering to. Unless it is for a +// non-lazy symbol pointer section for a defined symbol which strip(1) as +// removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the +// symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that. +// +#define MACH_INDIRECT_SYMBOL_LOCAL 0x80000000U +#define MACH_INDIRECT_SYMBOL_ABS 0x40000000U + +/// +/// The entries in the two-level namespace lookup hints table are twolevel_hint +/// structs. These provide hints to the dynamic link editor where to start +/// looking for an undefined symbol in a two-level namespace image. The +/// isub_image field is an index into the sub-images (sub-frameworks and +/// sub-umbrellas list) that made up the two-level image that the undefined +/// symbol was found in when it was built by the static link editor. If +/// isub-image is 0 the the symbol is expected to be defined in library and not +/// in the sub-images. If isub-image is non-zero it is an index into the array +/// of sub-images for the umbrella with the first index in the sub-images being +/// 1. The array of sub-images is the ordered list of sub-images of the +/// umbrella that would be searched for a symbol that has the umbrella recorded +/// as its primary library. The table of contents index is an index into the +/// library's table of contents. This is used as the starting point of the +/// binary search or a directed linear search. +/// +typedef struct { + UINT32 SubImagesIndex : 8; ///< index into the sub images + UINT32 TableOfContentsIndex : 24; ///< index into the table of contents +} TWOLEVEL_HINT; + +/// +/// The twolevel_hints_command contains the offset and number of hints in the +/// two-level namespace lookup hints table. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 Offset; ///< offset to the hint table + UINT32 NumHints; ///< number of hints in the hint table + TWOLEVEL_HINT Hints[]; +} MACH_TWO_LEVEL_HINTS_COMMAND; + +/// +/// The prebind_cksum_command contains the value of the original check sum for +/// prebound files or zero. When a prebound file is first created or modified +/// for other than updating its prebinding information the value of the check +/// sum is set to zero. When the file has it prebinding re-done and if the +/// value of the check sum is zero the original check sum is calculated and +/// stored in cksum field of this load command in the output file. If when the +/// prebinding is re-done and the cksum field is non-zero it is left unchanged +/// from the input file. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 Checksum; ///< the check sum or zero +} MACH_PREBIND_CHECKSUM_COMMAND; + +/// +/// The uuid load command contains a single 128-bit unique random number that +/// identifies an object produced by the static link editor. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT8 Uuid[16]; ///< the 128-bit uuid +} MACH_UUID_COMMAND; + +/// +/// The rpath_command contains a path which at runtime should be added to +/// the current run path used to find @rpath prefixed dylibs. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING Path; ///< path to add to run path +} MACH_RUN_PATH_COMMAND; + +/// +/// The linkedit_data_command contains the offsets and sizes of a blob +/// of data in the __LINKEDIT segment. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 DataOffset; ///< file offset of data in __LINKEDIT segment + UINT32 DataSize; ///< file size of data in __LINKEDIT segment +} MACH_LINKEDIT_DATA_COMMAND; + +/// +/// The encryption_info_command contains the file offset and size of an +/// of an encrypted segment. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + /// + /// file offset of encrypted range + /// + UINT32 CryptOffset; + /// + /// file size of encrypted range + /// + UINT32 CryptSize; + /// + /// which enryption system, 0 means not-encrypted yet + /// + UINT32 CryptId; +} MACH_ENCRYPTION_INFO_COMMAND; + +/// +/// The encryption_info_command_64 contains the file offset and size of an +/// of an encrypted segment (for use in x86_64 targets). +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + /// + /// file offset of encrypted range + /// + UINT32 CryptOffset; + /// + /// file size of encrypted range + /// + UINT32 CryptSize; + /// + /// which enryption system, 0 means not-encrypted yet + /// + UINT32 CryptId; + /// + /// padding to make this struct's size a multiple of 8 bytes + /// + UINT32 Pad; +} MACH_ENCRYPTION_INFO_COMMAND_64; + +/// +/// The version_min_command contains the min OS version on which this +/// binary was built to run. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 Version; ///< X.Y.Z is encoded in nibbles xxxx.yy.zz + UINT32 SdkVersion; ///< X.Y.Z is encoded in nibbles xxxx.yy.zz +} MACH_VERSION_MIN_COMMAND; + +/// +/// Encoded tools +/// +typedef struct { + UINT32 Tool; ///< enum for the tool + UINT32 Version; ///< version number of the tool +} MACH_BUILD_VERSION_TOOL; + +/// +/// The build_version_command contains the min OS version on which this +/// binary was built to run for its platform. The list of known platforms and +/// tool values following it. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 Platform; ///< platform + UINT32 MinOs; ///< X.Y.Z is encoded in nibbles xxxx.yy.zz + UINT32 SdkVersion; ///< X.Y.Z is encoded in nibbles xxxx.yy.zz + UINT32 NumTools; ///< number of tool entries following this + MACH_BUILD_VERSION_TOOL Tools[]; +} MACH_BUILD_VERSION_COMMAND; + +/// +/// Known values for the platform field above. +/// +#define MACH_PLATFORM_MACOS 1U +#define MACH_PLATFORM_IOS 2U +#define MACH_PLATFORM_TVOS 3U +#define MACH_PLATFORM_WATCHOS 4U +#define MACH_PLATFORM_BRIDGEOS 5U +#define MACH_PLATFORM_IOSMAC 6U +#define MACH_PLATFORM_IOSSIMULATOR 7U +#define MACH_PLATFORM_TVOSSIMULATOR 8U +#define MACH_PLATFORM_WATCHOSSIMULATOR 9U + +/// +/// Known values for the tool field above. +/// +#define MACH_TOOL_CLANG 1U +#define MACH_TOOL_SWIFT 2U +#define MACH_TOOL_LD 3U + +/// +/// The dyld_info_command contains the file offsets and sizes of +/// the new compressed form of the information dyld needs to +/// load the image. This information is used by dyld on Mac OS X +/// 10.6 and later. All information pointed to by this command +/// is encoded using byte streams, so no endian swapping is needed +/// to interpret it. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + // + // Dyld rebases an image whenever dyld loads it at an address different + // from its preferred address. The rebase information is a stream + // of byte sized opcodes whose symbolic names start with REBASE_OPCODE_. + // Conceptually the rebase information is a table of tuples: + // + // The opcodes are a compressed way to encode the table by only + // encoding when a column changes. In addition simple patterns + // like "every n'th offset for m times" can be encoded in a few + // bytes. + // + UINT32 RebaseOffset; ///< file offset to rebase info + UINT32 RebaseSize; ///< size of rebase info + // + // Dyld binds an image during the loading process, if the image + // requires any pointers to be initialized to symbols in other images. + // The bind information is a stream of byte sized + // opcodes whose symbolic names start with BIND_OPCODE_. + // Conceptually the bind information is a table of tuples: + // + // The opcodes are a compressed way to encode the table by only + // encoding when a column changes. In addition simple patterns + // like for runs of pointers initialzed to the same value can be + // encoded in a few bytes. + // + UINT32 BindingInfoOffset; ///< file offset to binding info + UINT32 BindingInfoSize; ///< size of binding info + // + // Some C++ programs require dyld to unique symbols so that all + // images in the process use the same copy of some code/data. + // This step is done after binding. The content of the weak_bind + // info is an opcode stream like the bind_info. But it is sorted + // alphabetically by symbol name. This enable dyld to walk + // all images with weak binding information in order and look + // for collisions. If there are no collisions, dyld does + // no updating. That means that some fixups are also encoded + // in the bind_info. For instance, all calls to "operator new" + // are first bound to libstdc++.dylib using the information + // in bind_info. Then if some image overrides operator new + // that is detected when the weak_bind information is processed + // and the call to operator new is then rebound. + // + UINT32 WeakBindingInfoOffset; ///< file offset to weak binding info + UINT32 WeakBindingInfoSize; ///< size of weak binding info + // + // Some uses of external symbols do not need to be bound immediately. + // Instead they can be lazily bound on first use. The lazy_bind + // are contains a stream of BIND opcodes to bind all lazy symbols. + // Normal use is that dyld ignores the lazy_bind section when + // loading an image. Instead the static linker arranged for the + // lazy pointer to initially point to a helper function which + // pushes the offset into the lazy_bind area for the symbol + // needing to be bound, then jumps to dyld which simply adds + // the offset to lazy_bind_off to get the information on what + // to bind. + // + UINT32 LazyBindingInfoOffset; ///< file offset to lazy binding info + UINT32 LazyBindingInfoSize; ///< size of lazy binding infs + // + // The symbols exported by a dylib are encoded in a trie. This + // is a compact representation that factors out common prefixes. + // It also reduces LINKEDIT pages in RAM because it encodes all + // information (name, address, flags) in one small, contiguous range. + // The export area is a stream of nodes. The first node sequentially + // is the start node for the trie. + // + // Nodes for a symbol start with a uleb128 that is the length of + // the exported symbol information for the string so far. + // If there is no exported symbol, the node starts with a zero byte. + // If there is exported info, it follows the length. + // + // First is a uleb128 containing flags. Normally, it is followed by + // a uleb128 encoded offset which is location of the content named + // by the symbol from the mach_header for the image. If the flags + // is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is + // a uleb128 encoded library ordinal, then a zero terminated + // UTF8 string. If the string is zero length, then the symbol + // is re-export from the specified dylib with the same name. + // If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following + // the flags is two uleb128s: the stub offset and the resolver offset. + // The stub is used by non-lazy pointers. The resolver is used + // by lazy pointers and must be called to get the actual address to use. + // + // After the optional exported symbol information is a byte of + // how many edges (0-255) that this node has leaving it, + // followed by each edge. + // Each edge is a zero terminated UTF8 of the addition chars + // in the symbol, followed by a uleb128 offset for the node that + // edge points to. + // + UINT32 ExportSymbolsOffset; ///< file offset to lazy binding info + UINT32 ExportSymbolsSize; ///< size of lazy binding infs +} MACH_DYLD_INFO_COMMAND; + +/// +/// The linker_option_command contains linker options embedded in object files. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 NumStrings; ///< number of strings + /// + /// concatenation of zero terminated UTF8 strings. Zero filled at end to + /// align. + /// + CHAR8 Strings[]; +} MACH_LINKER_OPTION_COMMAND; + +/// +/// The symseg_command contains the offset and size of the GNU style +/// symbol table information as described in the header file . +/// The symbol roots of the symbol segments must also be aligned properly +/// in the file. So the requirement of keeping the offsets aligned to a +/// multiple of a 4 bytes translates to the length field of the symbol +/// roots also being a multiple of a long. Also the padding must again be +/// zeroed. (THIS IS OBSOLETE and no longer supported). +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT32 Offset; ///< symbol segment offset + UINT32 Size; ///< symbol segment size in bytes +} MACH_SYMBOL_SEGMENT_COMMAND; + +/// +/// The ident_command contains a free format string table following the +/// ident_command structure. The strings are null terminated and the size of +/// the command is padded out with zero bytes to a multiple of 4 bytes/ +/// (THIS IS OBSOLETE and no longer supported). +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + CHAR8 Strings[]; +} MACH_IDENTIFICATION_COMMAND; + +/// +/// The fvmfile_command contains a reference to a file to be loaded at the +/// specified virtual address. (Presently, this command is reserved for +/// internal use. The kernel ignores this command when loading a program into +/// memory). +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + MACH_LOAD_COMMAND_STRING Name; ///< files pathname + UINT32 HeaderAddress; ///< files virtual address +} MACH_FIXED_VM_FILE_COMMAND; + +/// +/// The entry_point_command is a replacement for thread_command. +/// It is used for main executables to specify the location (file offset) +/// of main(). If -stack_size was used at link time, the stacksize +/// field will contain the stack size need for the main thread. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT64 EntryOffset; ///< file (__TEXT) offset of main() + UINT64 StackSize; ///< if not zero, initial stack size +} MACH_ENTRY_POINT_COMMAND; + +#define MACH_SOURCE_VERSION_A_MASK 0xFFFFFF0000000000U +#define MACH_SOURCE_VERSION_B_MASK 0x000000FFC0000000U +#define MACH_SOURCE_VERSION_V_MASK 0x000000003FF00000U +#define MACH_SOURCE_VERSION_D_MASK 0x00000000000FFC00U +#define MACH_SOURCE_VERSION_E_MASK 0x00000000000003FFU + +#define MACH_SOURCE_VERSION_A(Version) (((Version) & MACH_SOURCE_VERSION_A_MASK) >> 40U) +#define MACH_SOURCE_VERSION_B(Version) (((Version) & MACH_SOURCE_VERSION_B_MASK) >> 30U) +#define MACH_SOURCE_VERSION_C(Version) (((Version) & MACH_SOURCE_VERSION_V_MASK) >> 20U) +#define MACH_SOURCE_VERSION_D(Version) (((Version) & MACH_SOURCE_VERSION_D_MASK) >> 10U) +#define MACH_SOURCE_VERSION_E(Version) ((Version) & MACH_SOURCE_VERSION_E_MASK) + +/// +/// The source_version_command is an optional load command containing +/// the version of the sources used to build the binary. +/// +typedef struct { + MACH_LOAD_COMMAND_HDR_ + UINT64 Version; ///< A.B.C.D.E as a24.b10.c10.d10.e10 +} MACH_SOURCE_VERSION_COMMAND; + +typedef union { + CONST MACH_LOAD_COMMAND *Hdr; + CONST MACH_SEGMENT_COMMAND *Segment; + CONST MACH_SEGMENT_COMMAND_64 *Segment64; + CONST MACH_FIXED_VM_LIB_COMMAND *VmLib; + CONST MACH_DYLIB_COMMAND *Dylib; + CONST MACH_SUB_FRAMEWORK_COMMAND *SubFramework; + CONST MACH_SUB_CLIENT_COMMAND *SubClient; + CONST MACH_SUB_UMBRELLA_COMMAND *SubUmbrella; + CONST MACH_SUB_LIBRARY_COMMAND *SubLibrary; + CONST MACH_PREBOUND_DYLIB_COMMAND *PreboundDyLib; + CONST MACH_DYLINKER_COMMAND *Dylinker; + CONST MACH_THREAD_COMMAND *Thread; + CONST MACH_ROUTINES_COMMAND *Routines; + CONST MACH_ROUTINES_COMMAND_64 *Routines64; + CONST MACH_SYMTAB_COMMAND *Symtab; + CONST MACH_DYSYMTAB_COMMAND *Dysymtab; + CONST MACH_TWO_LEVEL_HINTS_COMMAND *TwoLevelHints; + CONST MACH_PREBIND_CHECKSUM_COMMAND *PrebindChecksum; + CONST MACH_UUID_COMMAND *Uuid; + CONST MACH_RUN_PATH_COMMAND *RunPath; + CONST MACH_LINKEDIT_DATA_COMMAND *LinkeditData; + CONST MACH_ENCRYPTION_INFO_COMMAND *EncryptionInfo; + CONST MACH_ENCRYPTION_INFO_COMMAND_64 *EncryptionInfo64; + CONST MACH_VERSION_MIN_COMMAND *VersionMin; + CONST MACH_BUILD_VERSION_COMMAND *BuildVersion; + CONST MACH_DYLD_INFO_COMMAND *DyldInfo; + CONST MACH_LINKER_OPTION_COMMAND *LinkerOption; + CONST MACH_SYMBOL_SEGMENT_COMMAND *SymbolSegment; + CONST MACH_IDENTIFICATION_COMMAND *Identification; + CONST MACH_FIXED_VM_FILE_COMMAND *FixedVmFile; + CONST VOID *Pointer; + UINTN Address; +} MACH_LOAD_COMMAND_PTR; + +#undef MACH_LOAD_COMMAND_HDR_ + +/// +/// The layout of the file depends on the filetype. For all but the MH_OBJECT +/// file type the segments are padded out and aligned on a segment alignment +/// boundary for efficient demand pageing. The MH_EXECUTE, MH_FVMLIB, +/// MH_DYLIB, MH_DYLINKER and MH_BUNDLE file types also have the headers +/// included as part of their first segment. +/// +/// The file type MH_OBJECT is a compact format intended as output of the +/// assembler and input (and possibly output) of the link editor (the .o +/// format). All sections are in one unnamed segment with no segment padding. +/// This format is used as an executable format when the file is so small the +/// segment padding greatly increases its size. +/// +/// The file type MH_PRELOAD is an executable format intended for things that +/// are not executed under the kernel (proms, stand alones, kernels, etc). The +/// format can be executed under the kernel but may demand paged it and not +/// preload it before execution. +/// +/// A core file is in MH_CORE format and can be any in an arbritray legal +/// Mach-O file. +/// +/// Constants for the filetype field of the mach_header +/// +enum { + MachHeaderFileTypeObject = 1, + MachHeaderFileTypeExecute = 2, + MachHeaderFileTypeFixedVmLib = 3, + MachHeaderFileTypeCore = 4, + MachHeaderFileTypePreload = 5, + MachHeaderFileTypeDylib = 6, + MachHeaderFileTypeDynamicLinker = 7, + MachHeaderFileTypeBundle = 8, + MachHeaderFileTypeDynamicLinkerStub = 9, + MachHeaderFileTypeDsym = 10, + MachHeaderFileTypeKextBundle = 11 +}; + +typedef UINT32 MACH_HEADER_FILE_TYPE; + +// +// Constants for the Flags field of the MACH_HEADER struct +// +#define MACH_HEADER_FLAG_NO_UNDEFINED_REFERENCES BIT0 +#define MACH_HEADER_FLAG_INCREMENTAL_LINK BIT1 +#define MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK BIT2 +#define MACH_HEADER_FLAG_BINARY_DATA_LOAD BIT3 +#define MACH_HEADER_FLAG_PREBOUND BIT4 +#define MACH_HEADER_FLAG_SPLIT_SEGMENTS BIT5 +#define MACH_HEADER_FLAG_LAZY_INITIALIZATION BIT6 +#define MACH_HEADER_FLAG_TWO_LEVEL BIT7 +#define MACH_HEADER_FLAG_FORCE_FLAT BIT8 +#define MACH_HEADER_FLAG_NO_MULTIPLE_DEFINITIONS BIT9 +#define MACH_HEADER_FLAG_NO_FIX_PREBINDING BIT10 +#define MACH_HEADER_FLAG_PREBINDABLE BIT11 +#define MACH_HEADER_FLAG_ALL_MODULES_BOUND BIT12 +#define MACH_HEADER_FLAG_SUBSECTIONS_VIA_SYNBOLS BIT13 +#define MACH_HEADER_FLAG_CANONICAL BIT14 +#define MACH_HEADER_FLAG_WEAK_DEFINES BIT15 +#define MACH_HEADER_FLAG_BINDS_TO_WEAK BIT16 +#define MACH_HEADER_FLAG_ALLOW_STACK_EXECUTION BIT17 +#define MACH_HEADER_FLAG_ROOT_SAFE BIT18 +#define MACH_HEADER_FLAG_SET_UID_SAFE BIT19 +#define MACH_HEADER_FLAG_NO_REEXPORTED_DYLIBS BIT20 +#define MACH_HEADER_FLAG_POSITION_INDEPENDENT_EXECUTABLE BIT21 +#define MACH_HEADER_FLAG_DEAD_STRIPPABLE_DYLIB BIT22 +#define MACH_HEADER_FLAG_HAS_TLV_DESCRIPTORS BIT23 +#define MACH_HEADER_FLAG_NO_HEAP_EXECUTION BIT24 +#define MACH_HEADER_FLAG_APP_EXTENSION_SAFE BIT25 +#define MACH_HEADER_FLAG_NLIST_OUTOFSYNC_WITH_DYLDINFO BIT26 +#define MACH_HEADER_FLAG_SIM_SUPPORT BIT27 + +typedef UINT32 MACH_HEADER_FLAGS; + +// +// Constant for the magic field of the MACH_HEADER (32-bit architectures) +// +#define MACH_HEADER_SIGNATURE 0xFEEDFACE ///< the mach magic number +#define MACH_HEADER_INVERT_SIGNATURE 0xCEFAEDFE ///< the mach magic number (byte swapped) + +/// +/// The 32-bit mach header appears at the very beginning of the object file for +/// 32-bit architectures. +/// +typedef struct { + UINT32 Signature; ///< mach magic number identifier + MACH_CPU_TYPE CpuType; ///< cpu Sectionecifier + MACH_CPU_SUBTYPE CpuSubtype; ///< machine Sectionecifier + MACH_HEADER_FILE_TYPE FileType; ///< type of file + UINT32 NumCommands; ///< number of load commands + UINT32 CommandsSize; ///< the size of all load commands + MACH_HEADER_FLAGS Flags; ///< flags + MACH_LOAD_COMMAND Commands[]; +} MACH_HEADER; + +// +// Constant for the magic field of the MACH_HEADER_64 (64-bit architectures) +// +#define MACH_HEADER_64_SIGNATURE 0xFEEDFACF ///< the 64-bit mach magic number +#define MACH_HEADER_64_INVERT_SIGNATURE 0xCFFAEDFE ///< the 64-bit mach magic number (byte swapped) + +/// +/// The 64-bit mach header appears at the very beginning of object files for +/// 64-bit architectures. +/// +typedef struct { + UINT32 Signature; ///< mach magic number identifier + MACH_CPU_TYPE CpuType; ///< cpu Sectionecifier + MACH_CPU_SUBTYPE CpuSubtype; ///< machine Sectionecifier + MACH_HEADER_FILE_TYPE FileType; ///< type of file + UINT32 NumCommands; ///< number of load commands + UINT32 CommandsSize; ///< the size of all load commands + MACH_HEADER_FLAGS Flags; ///< flags + UINT32 Reserved; ///< reserved + MACH_LOAD_COMMAND Commands[]; +} MACH_HEADER_64; + +/// +/// Allow selecting a correct header based on magic +/// +typedef union { + UINT32 Signature; + MACH_HEADER Header32; + MACH_HEADER_64 Header64; +} MACH_HEADER_ANY; + + +// +// Symbols +// + +/// +/// Format of a symbol table entry of a Mach-O file for 32-bit architectures. +/// Modified from the BSD format. The modifications from the original format +/// were changing n_other (an unused field) to n_sect and the addition of the +/// N_SECT type. These modifications are required to support symbols in a +/// larger number of sections not just the three sections (text, data and bss) +/// in a BSD file. +/// +typedef struct { + union { + UINT32 Address32; ///< for use when in-core + UINT32 StringIndex; ///< index into the string table + } UnifiedName; + UINT8 Type; ///< type flag, see below + UINT8 Section; ///< section number or NO_SECT + INT16 Descriptor; ///< see + UINT32 Value; ///< value of this symbol (or stab offset) +} MACH_NLIST; + +/// +/// This is the symbol table entry structure for 64-bit architectures. +/// +typedef struct { + union { + UINT32 StringIndex; ///< index into the string table + } UnifiedName; + UINT8 Type; ///< type flag, see below + UINT8 Section; ///< section number or NO_SECT + UINT16 Descriptor; ///< see + UINT64 Value; ///< value of this symbol (or stab offset) +} MACH_NLIST_64; + +// +// Symbols with a index into the string table of zero (n_un.n_strx == 0) are +// defined to have a null, "", name. Therefore all string indexes to non null +// names must not have a zero string index. This is bit historical information +// that has never been well documented. +// + +// +// The n_type field really contains four fields: +// unsigned char N_STAB:3, +// N_PEXT:1, +// N_TYPE:3, +// N_EXT:1; +// which are used via the following masks. +// +#define MACH_N_TYPE_STAB 0xE0U ///< if any of these bits set, a symbolic + ///< debugging entry +#define MACH_N_TYPE_PEXT 0x10U ///< private external symbol bit +#define MACH_N_TYPE_TYPE 0x0EU ///< mask for the type bit +#define MACH_N_TYPE_EXT 0x01U ///< external symbol bit, set for external + ///< symbols + +// +// Only symbolic debugging entries have some of the N_STAB bits set and if any +// of these bits are set then it is a symbolic debugging entry (a stab). In +// which case then the values of the n_type field (the entire field) are given +// in +// + +// +// Values for N_TYPE bits of the n_type field. +// +#define MACH_N_TYPE_UNDF 0x0U ///< undefined, n_sect == NO_SECT +#define MACH_N_TYPE_ABS 0x2U ///< absolute, n_sect == NO_SECT +#define MACH_N_TYPE_SECT 0xEU ///< defined in section number n_sect +#define MACH_N_TYPE_PBUD 0xCU ///< prebound undefined (defined in a dylib) +#define MACH_N_TYPE_INDR 0xAU ///< indirect + +// +// If the type is N_INDR then the symbol is defined to be the same as another +// symbol. In this case the n_value field is an index into the string table +// of the other symbol's name. When the other symbol is defined then they both +// take on the defined type and value. +// + +// +// If the type is N_SECT then the n_sect field contains an ordinal of the +// section the symbol is defined in. The sections are numbered from 1 and +// refer to sections in order they appear in the load commands for the file +// they are in. This means the same ordinal may very well refer to different +// sections in different files. +// +// The n_value field for all symbol table entries (including N_STAB's) gets +// updated by the link editor based on the value of it's n_sect field and where +// the section n_sect references gets relocated. If the value of the n_sect +// field is NO_SECT then it's n_value field is not changed by the link editor. +// +#define NO_SECT 0 ///< symbol is not in any section +#define MAX_SECT 255 ///< 1 thru 255 inclusive + +// +// STAB +// + +// +// Symbolic debugger symbols. The comments give the conventional use for +// +// .stabs "n_name", n_type, n_sect, n_desc, n_value +// +// where n_type is the defined constant and not listed in the comment. Other +// fields not listed are zero. n_sect is the section ordinal the entry is +// refering to. +// +#define MACH_N_GSYM 0x20U ///< global symbol: name,,NO_SECT,type,0 +#define MACH_N_FNAME 0x22U ///< procedure name (f77 kludge): name,,NO_SECT,0,0 +#define MACH_N_FUN 0x24U ///< procedure: name,,n_sect,linenumber,address +#define MACH_N_STSYM 0x26U ///< static symbol: name,,n_sect,type,address +#define MACH_N_LCSYM 0x28U ///< .lcomm symbol: name,,n_sect,type,address +#define MACH_N_BNSYM 0x2EU ///< begin nsect sym: 0,,n_sect,0,address +#define MACH_N_AST 0x32U ///< AST file path: name,,NO_SECT,0,0 +#define MACH_N_OPT 0x3CU ///< emitted with gcc2_compiled and in gcc source +#define MACH_N_RSYM 0x40U ///< register sym: name,,NO_SECT,type,register +#define MACH_N_SLINE 0x44U ///< src line: 0,,n_sect,linenumber,address +#define MACH_N_ENSYM 0x4EU ///< end nsect sym: 0,,n_sect,0,address +#define MACH_N_SSYM 0x60U ///< structure elt: name,,NO_SECT,type,struct_offset +#define MACH_N_SO 0x64U ///< source file name: name,,n_sect,0,address +#define MACH_N_OSO 0x66U ///< object file name: name,,0,0,st_mtime +#define MACH_N_LSYM 0x80U ///< local sym: name,,NO_SECT,type,offset +#define MACH_N_BINCL 0x82U ///< include file beginning: name,,NO_SECT,0,sum +#define MACH_N_SOL 0x84U ///< #included file name: name,,n_sect,0,address +#define MACH_N_PARAMS 0x86U ///< compiler parameters: name,,NO_SECT,0,0 +#define MACH_N_VERSION 0x88U ///< compiler version: name,,NO_SECT,0,0 +#define MACH_N_OLEVEL 0x8AU ///< compiler -O level: name,,NO_SECT,0,0 +#define MACH_N_PSYM 0xA0U ///< parameter: name,,NO_SECT,type,offset +#define MACH_N_EINCL 0xA2U ///< include file end: name,,NO_SECT,0,0 +#define MACH_N_ENTRY 0xA4U ///< alternate entry: name,,n_sect,linenumber,address +#define MACH_N_LBRAC 0xC0U ///< left bracket: 0,,NO_SECT,nesting level,address +#define MACH_N_EXCL 0xC2U ///< deleted include file: name,,NO_SECT,0,sum +#define MACH_N_RBRAC 0xE0U ///< right bracket: 0,,NO_SECT,nesting level,address +#define MACH_N_BCOMM 0xE2U ///< begin common: name,,NO_SECT,0,0 +#define MACH_N_ECOMM 0xE4U ///< end common: name,,n_sect,0,0 +#define MACH_N_ECOML 0xE8U ///< end common (local name): 0,,n_sect,0,address +#define MACH_N_LENG 0xFEU ///< second stab entry with length information + +// +// The bit 0x0020 of the n_desc field is used for two non-overlapping purposes +// and has two different symbolic names, N_NO_DEAD_STRIP and N_DESC_DISCARDED. +// + +/// +/// The N_NO_DEAD_STRIP bit of the n_desc field only ever appears in a +/// relocatable .o file (MH_OBJECT filetype). And is used to indicate to the +/// static link editor it is never to dead strip the symbol. +/// +#define MACH_N_NO_DEAD_STRIP 0x0020U ///< symbol is not to be dead stripped +/// +/// The N_DESC_DISCARDED bit of the n_desc field never appears in linked image. +/// But is used in very rare cases by the dynamic link editor to mark an in +/// memory symbol as discared and longer used for linking. +/// +#define MACH_N_DESC_DISCARDED 0x0020U +/// +/// The N_WEAK_REF bit of the n_desc field indicates to the dynamic linker that +/// the undefined symbol is allowed to be missing and is to have the address of +/// zero when missing. +/// +#define MACH_N_WEAK_REF 0x0040U +/// +/// The N_WEAK_DEF bit of the n_desc field indicates to the static and dynamic +/// linkers that the symbol definition is weak, allowing a non-weak symbol to +/// also be used which causes the weak definition to be discared. Currently +/// this is only supported for symbols in coalesed sections. +/// +#define MACH_N_WEAK_DEF 0x0080U +/// +/// The N_REF_TO_WEAK bit of the n_desc field indicates to the dynamic linker +/// that the undefined symbol should be resolved using flat namespace +/// searching. +/// +#define MACH_N_REF_TO_WEAK 0x0080U +/// +/// The N_ARM_THUMB_DEF bit of the n_desc field indicates that the symbol is +/// a defintion of a Thumb function. +/// +#define MACH_N_ARM_THUMB_DEF 0x0008U +/// +/// The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the +/// that the function is actually a resolver function and should +/// be called to get the address of the real function to use. +/// This bit is only available in .o files (MH_OBJECT filetype) +/// +#define MACH_N_SYMBOL_RESOLVER 0x0100U +/// +/// The N_ALT_ENTRY bit of the n_desc field indicates that the +/// symbol is pinned to the previous content. +/// +#define MACH_N_ALT_ENTRY 0x0200U +/// +/// for the berkeley pascal compiler, pc(1): +/// global pascal symbol: name,,NO_SECT,subtype,line +/// +#define MACH_N_PC 0x30U + +// +// Relocations +// + +/// +/// Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD +/// format. The modifications from the original format were changing the value +/// of the r_symbolnum field for "local" (r_extern == 0) relocation entries. +/// This modification is required to support symbols in an arbitrary number of +/// sections not just the three sections (text, data and bss) in a 4.3BSD file. +/// Also the last 4 bits have had the r_type tag added to them. +/// +typedef struct { + INT32 Address; ///< offset in the section to what is being + ///< relocated + UINT32 SymbolNumber : 24; ///< symbol index if r_extern == 1 or section + ///< ordinal if r_extern == 0 + UINT32 PcRelative : 1; ///< was relocated pc relative already + UINT32 Size : 2; ///< 0=byte, 1=word, 2=long, 3=quad + UINT32 Extern : 1; ///< does not include value of sym referenced + UINT32 Type : 4; ///< if not 0, machine specific relocation type +} MACH_RELOCATION_INFO; + +#define MACH_RELOC_ABSOLUTE 0U ///< absolute relocation type for Mach-O files + +// +// The r_address is not really the address as it's name indicates but an +// offset. In 4.3BSD a.out objects this offset is from the start of the +// "segment" for which relocation entry is for (text or data). For Mach-O +// object files it is also an offset but from the start of the "section" for +// which the relocation entry is for. See comments in about +// the r_address field in images for used with the dynamic linker. +// +// In 4.3BSD a.out objects if r_extern is zero then r_symbolnum is an ordinal +// for the segment the symbol being relocated is in. These ordinals are the +// symbol types N_TEXT, N_DATA, N_BSS or N_ABS. In Mach-O object files these +// ordinals refer to the sections in the object file in the order their section +// structures appear in the headers of the object file they are in. The first +// section has the ordinal 1, the second 2, and so on. This means that the +// same ordinal in two different object files could refer to two different +// sections. And further could have still different ordinals when combined +// by the link-editor. The value MACH_RELOC_ABSOLUTE is used for relocation entries for +// absolute symbols which need no further relocation. +// + +// +// For RISC machines some of the references are split across two instructions +// and the instruction does not contain the complete value of the reference. +// In these cases a second, or paired relocation entry, follows each of these +// relocation entries, using a PAIR r_type, which contains the other part of +// the reference not contained in the instruction. This other part is stored +// in the pair's r_address field. The exact number of bits of the other part +// of the reference store in the r_address field is dependent on the particular +// relocation type for the particular architecture. +// + +/// +/// mask to be applied to the r_address field of a relocation_info structure +/// to tell that is is really a scattered_relocation_info stucture +/// +/// To make scattered loading by the link editor work correctly "local" +/// relocation entries can't be used when the item to be relocated is the value +/// of a symbol plus an offset (where the resulting expresion is outside the +/// block the link editor is moving, a blocks are divided at symbol addresses). +/// In this case. where the item is a symbol value plus offset, the link editor +/// needs to know more than just the section the symbol was defined. What is +/// needed is the actual value of the symbol without the offset so it can do +/// the relocation correctly based on where the value of the symbol got +/// relocated to not the value of the expression (with the offset added to the +/// symbol value). So for the NeXT 2.0 release no "local" relocation entries +/// are ever used when there is a non-zero offset added to a symbol. +/// The "external" and "local" relocation entries remain unchanged. +/// +/// The implemention is quite messy given the compatibility with the existing +/// relocation entry format. The ASSUMPTION is that a section will never be +/// bigger than 2**24 - 1 (0x00ffffff or 16,777,215) bytes. This assumption +/// allows the r_address (which is really an offset) to fit in 24 bits and high +/// bit of the r_address field in the relocation_info structure to indicate +/// it is really a scattered_relocation_info structure. Since these are only +/// used in places where "local" relocation entries are used and not where +/// "external" relocation entries are used the r_extern field has been removed. +/// +/// For scattered loading to work on a RISC machine where some of the +/// references are split across two instructions the link editor needs to be +/// assured that each reference has a unique 32 bit reference (that more than +/// one reference is NOT sharing the same high 16 bits for example) so it move +/// each referenced item independent of each other. Some compilers guarantees +/// this but the compilers don't so scattered loading can be done on those that +/// do guarantee this. +/// +#define MACH_RELOC_SCATTERED 0x80000000U + +typedef struct { +#if defined(MACH_LITTLE_ENDIAN) + UINT32 Address : 24; ///< offset in the section to what is being + ///< relocated + UINT32 Type : 4; ///< if not 0, machine specific relocation type + UINT32 Size : 2; ///< 0=byte, 1=word, 2=long, 3=quad + UINT32 PcRelative : 1; ///< was relocated pc relative already + UINT32 Scattered : 1; ///< 1=scattered, 0=non-scattered (see above) + INT32 Value; ///< the value the item to be relocated is refering + ///< to (without any offset added) +#elif defined(MACH_BIG_ENDIAN) + UINT32 Scattered : 1; ///< 1=scattered, 0=non-scattered (see above) + UINT32 PcRelative : 1; ///< was relocated pc relative already + UINT32 Size : 2; ///< 0=byte, 1=word, 2=long, 3=quad + UINT32 Type : 4; ///< if not 0, machine specific relocation type + UINT32 Address : 24; ///< offset in the section to what is being + ///< relocated + INT32 Value; ///< the value the item to be relocated is refering + ///< to (without any offset added) +#else +/// +/// The reason for the ifdef's of __BIG_ENDIAN__ and __LITTLE_ENDIAN__ are that +/// when stattered relocation entries were added the mistake of using a mask +/// against a structure that is made up of bit fields was used. To make this +/// design work this structure must be laid out in memory the same way so the +/// mask can be applied can check the same bit each time (r_scattered). +/// +#error "No endianness information was provided!" +#endif +} MACH_SCATTERED_RELOCATION_INFO; + +/// +/// Relocation types used in a generic implementation. Relocation entries for +/// normal things use the generic relocation as discribed above and their +/// r_type is GENERIC_RELOC_VANILLA (a value of zero). +/// +/// Another type of generic relocation, GENERIC_RELOC_SECTDIFF, is to support +/// the difference of two symbols defined in different sections. That is the +/// expression "symbol1 - symbol2 + constant" is a relocatable expression when +/// both symbols are defined in some section. For this type of relocation the +/// both relocations entries are scattered relocation entries. The value of +/// symbol1 is stored in the first relocation entry's r_value field and the +/// value of symbol2 is stored in the pair's r_value field. +/// +/// A special case for a prebound lazy pointer is needed to beable to set the +/// value of the lazy pointer back to its non-prebound state. This is done +/// using the GENERIC_RELOC_PB_LA_PTR r_type. This is a scattered relocation +/// entry where the r_value feild is the value of the lazy pointer not +/// prebound. +/// +enum { + MachGenericRelocVanilla, ///< generic relocation as discribed + ///< above + MachGenericRelocPair, ///< Only follows a + ///< MachGenericRelocLocalSectDiff + MachGenericRelocSectDiff, + MachGenericRelocPreboundLazyPointer, ///< prebound lazy pointer + MachGenericRelocLocalSectDiff, + MachGenericRelocThreadLocalVariables ///< thread local variables +}; + +/// +/// Relocations for x86_64 are a bit different than for other architectures in +/// Mach-O: Scattered relocations are not used. Almost all relocations +/// produced by the compiler are external relocations. An external relocation +/// has the r_extern bit set to 1 and the r_symbolnum field contains the symbol +/// table index of the target label. +/// +/// When the assembler is generating relocations, if the target label is a +/// local label (begins with 'L'), then the previous non-local label in the +/// same section is used as the target of the external relocation. An addend +/// is used with the distance from that non-local label to the target label. +/// Only when there is no previous non-local label in the section is an +/// internal relocation used. +/// +/// The addend (i.e. the 4 in _foo+4) is encoded in the instruction (Mach-O +/// does not have RELA relocations). For PC-relative relocations, the addend +/// is stored directly in the instruction. This is different from other Mach-O +/// architectures, which encode the addend minus the current section offset. +/// +/// The relocation types are: +/// +/// X86_64_RELOC_UNSIGNED // for absolute addresses +/// X86_64_RELOC_SIGNED // for signed 32-bit displacement +/// X86_64_RELOC_BRANCH // a CALL/JMP instruction with 32-bit displacement +/// X86_64_RELOC_GOT_LOAD // a MOVQ load of a GOT entry +/// X86_64_RELOC_GOT // other GOT references +/// X86_64_RELOC_SUBTRACTOR // must be followed by a X86_64_RELOC_UNSIGNED +/// +/// The following are sample assembly instructions, followed by the relocation +/// and section content they generate in an object file: +/// +/// call _foo +/// r_type=X86_64_RELOC_BRANCH, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// E8 00 00 00 00 +/// +/// call _foo+4 +/// r_type=X86_64_RELOC_BRANCH, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// E8 04 00 00 00 +/// +/// movq _foo@GOTPCREL(%rip), %rax +/// r_type=X86_64_RELOC_GOT_LOAD, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// 48 8B 05 00 00 00 00 +/// +/// pushq _foo@GOTPCREL(%rip) +/// r_type=X86_64_RELOC_GOT, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// FF 35 00 00 00 00 +/// +/// movl _foo(%rip), %eax +/// r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// 8B 05 00 00 00 00 +/// +/// movl _foo+4(%rip), %eax +/// r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// 8B 05 04 00 00 00 +/// +/// movb $0x12, _foo(%rip) +/// r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// C6 05 FF FF FF FF 12 +/// +/// movl $0x12345678, _foo(%rip) +/// r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo +/// C7 05 FC FF FF FF 78 56 34 12 +/// +/// .quad _foo +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo +/// 00 00 00 00 00 00 00 00 +/// +/// .quad _foo+4 +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo +/// 04 00 00 00 00 00 00 00 +/// +/// .quad _foo - _bar +/// r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_bar +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo +/// 00 00 00 00 00 00 00 00 +/// +/// .quad _foo - _bar + 4 +/// r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_bar +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo +/// 04 00 00 00 00 00 00 00 +/// +/// .long _foo - _bar +/// r_type=X86_64_RELOC_SUBTRACTOR, r_length=2, r_extern=1, r_pcrel=0, r_symbolnum=_bar +/// r_type=X86_64_RELOC_UNSIGNED, r_length=2, r_extern=1, r_pcrel=0, r_symbolnum=_foo +/// 00 00 00 00 +/// +/// lea L1(%rip), %rax +/// r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_prev +/// 48 8d 05 12 00 00 00 +/// // assumes _prev is the first non-local label 0x12 bytes before L1 +/// +/// lea L0(%rip), %rax +/// r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=0, r_pcrel=1, r_symbolnum=3 +/// 48 8d 05 56 00 00 00 +/// // assumes L0 is in third section and there is no previous non-local label. +/// // The rip-relative-offset of 0x00000056 is L0-address_of_next_instruction. +/// // address_of_next_instruction is the address of the relocation + 4. +/// +/// add $6,L0(%rip) +/// r_type=X86_64_RELOC_SIGNED_1, r_length=2, r_extern=0, r_pcrel=1, r_symbolnum=3 +/// 83 05 18 00 00 00 06 +/// // assumes L0 is in third section and there is no previous non-local label. +/// // The rip-relative-offset of 0x00000018 is L0-address_of_next_instruction. +/// // address_of_next_instruction is the address of the relocation + 4 + 1. +/// // The +1 comes from SIGNED_1. This is used because the relocation is not +/// // at the end of the instruction. +/// +/// .quad L1 +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev +/// 12 00 00 00 00 00 00 00 +/// // assumes _prev is the first non-local label 0x12 bytes before L1 +/// +/// .quad L0 +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=0, r_pcrel=0, r_symbolnum=3 +/// 56 00 00 00 00 00 00 00 +/// // assumes L0 is in third section, has an address of 0x00000056 in .o +/// // file, and there is no previous non-local label +/// +/// .quad _foo - . +/// r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo +/// EE FF FF FF FF FF FF FF +/// // assumes _prev is the first non-local label 0x12 bytes before this +/// // .quad +/// +/// .quad _foo - L1 +/// r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo +/// EE FF FF FF FF FF FF FF +/// // assumes _prev is the first non-local label 0x12 bytes before L1 +/// +/// .quad L1 - _prev +/// // No relocations. This is an assembly time constant. +/// 12 00 00 00 00 00 00 00 +/// // assumes _prev is the first non-local label 0x12 bytes before L1 +/// +/// +/// +/// In final linked images, there are only two valid relocation kinds: +/// +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_pcrel=0, r_extern=1, r_symbolnum=sym_index +/// This tells dyld to add the address of a symbol to a pointer sized (8-byte) +/// piece of data (i.e on disk the 8-byte piece of data contains the addend). The +/// r_symbolnum contains the index into the symbol table of the target symbol. +/// +/// r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_pcrel=0, r_extern=0, r_symbolnum=0 +/// This tells dyld to adjust the pointer sized (8-byte) piece of data by the amount +/// the containing image was loaded from its base address (e.g. slide). +/// +enum { + MachX8664RelocUnsigned, ///< for absolute addresses + MachX8664RelocSigned, ///< for signed 32-bit displacement + MachX8664RelocBranch, ///< a CALL/JMP instruction with 32-bit + ///< displacement + MachX8664RelocGotLoad, ///< a MOVQ load of a GOT entry + MachX8664RelocGot, ///< other GOT references + MachX8664RelocSubtractor, ///< must be followed by a + ///< MachX8664RelocUnsigned + MachX8664RelocSigned1, ///< for signed 32-bit displacement with + ///< a -1 addend + MachX8664RelocSigned2, ///< for signed 32-bit displacement with + ///< a -2 addend + MachX8664RelocSigned4, ///< for signed 32-bit displacement with + ///< a -4 addend + MachX8664RelocThreadLocalVariable ///< for thread local variables +}; + +#endif // APPLE_MACHO_IMAGE_H diff --git a/Include/Library/MachoLib.h b/Include/Library/MachoLib.h new file mode 100644 index 000000000..8406dce2e --- /dev/null +++ b/Include/Library/MachoLib.h @@ -0,0 +1,761 @@ +/** @file + Provides Mach-O parsing helper functions. + +Copyright (c) 2016 - 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef OC_MACHO_LIB_H_ +#define OC_MACHO_LIB_H_ + +#include + +/// +/// Default assumed page size. +/// +#define MACHO_PAGE_SIZE 4096U + +/// +/// Macro to align by default assumed page size. +/// +#define MACHO_ALIGN(x) ALIGN_VALUE((x), MACHO_PAGE_SIZE) + +/// +/// Context used to refer to a Mach-O. This struct is exposed for reference +/// only. Members are not guaranteed to be sane. +/// +typedef struct { + MACH_HEADER_64 *MachHeader; + UINT32 FileSize; + MACH_SYMTAB_COMMAND *Symtab; + MACH_NLIST_64 *SymbolTable; + CHAR8 *StringTable; + MACH_DYSYMTAB_COMMAND *DySymtab; + MACH_NLIST_64 *IndirectSymbolTable; + MACH_RELOCATION_INFO *LocalRelocations; + MACH_RELOCATION_INFO *ExternRelocations; +} OC_MACHO_CONTEXT; + +/** + Initializes a Mach-O Context. + + @param[out] Context Mach-O Context to initialize. + @param[in] FileData Pointer to the file's data. + @param[in] FileSize File size of FileData. + + @return Whether Context has been initialized successfully. + +**/ +BOOLEAN +MachoInitializeContext ( + OUT OC_MACHO_CONTEXT *Context, + IN VOID *FileData, + IN UINT32 FileSize + ); + +/** + Returns the Mach-O Header structure. + + @param[in,out] Context Context of the Mach-O. + +**/ +MACH_HEADER_64 * +MachoGetMachHeader64 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Returns the Mach-O's file size. + + @param[in,out] Context Context of the Mach-O. + +**/ +UINT32 +MachoGetFileSize ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Returns the Mach-O's virtual address space size. + + @param[out] Context Context of the Mach-O. + +**/ +UINT32 +MachoGetVmSize64 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Returns the last virtual address of a Mach-O. + + @param[in] Context Context of the Mach-O. + + @retval 0 The binary is malformed. + +**/ +UINT64 +MachoGetLastAddress64 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Retrieves the first UUID Load Command. + + @param[in,out] Context Context of the Mach-O. + + @retval NULL NULL is returned on failure. + +**/ +MACH_UUID_COMMAND * +MachoGetUuid64 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Retrieves the first segment by the name of SegmentName. + + @param[in,out] Context Context of the Mach-O. + @param[in] SegmentName Segment name to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SEGMENT_COMMAND_64 * +MachoGetSegmentByName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName + ); + +/** + Retrieves the first section by the name of SectionName. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to search in. + @param[in] SectionName Section name to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSectionByName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_64 *Segment, + IN CONST CHAR8 *SectionName + ); + +/** + Retrieves a section within a segment by the name of SegmentName. + + @param[in,out] Context Context of the Mach-O. + @param[in] SegmentName The name of the segment to search in. + @param[in] SectionName The name of the section to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSegmentSectionByName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName, + IN CONST CHAR8 *SectionName + ); + +/** + Retrieves the next segment. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to retrieve the successor of. + if NULL, the first segment is returned. + + @retal NULL NULL is returned on failure. + +**/ +MACH_SEGMENT_COMMAND_64 * +MachoGetNextSegment64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SEGMENT_COMMAND_64 *Segment OPTIONAL + ); + +/** + Retrieves the next section of a segment. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment The segment to get the section of. + @param[in] Section The section to get the successor of. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetNextSection64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_64 *Segment, + IN MACH_SECTION_64 *Section OPTIONAL + ); + +/** + Retrieves a section by its index. + + @param[in,out] Context Context of the Mach-O. + @param[in] Index Index of the section to retrieve. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSectionByIndex64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ); + +/** + Retrieves a section by its address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address of the section to retrieve. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSectionByAddress64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address + ); + +/** + Returns whether Symbol describes a section. + + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsSection ( + IN CONST MACH_NLIST_64 *Symbol + ); + +/** + Returns whether Symbol is defined. + + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsDefined ( + IN CONST MACH_NLIST_64 *Symbol + ); + +/** + Returns whether Symbol is defined locally. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsLocalDefined ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ); + +/** + Retrieves a locally defined symbol by its name. + + @param[in] Context Context of the Mach-O. + @param[in] Name Name of the symbol to locate. + +**/ +MACH_NLIST_64 * +MachoGetLocalDefinedSymbolByName ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Name + ); + +/** + Retrieves a symbol by its index. + + @param[in] Context Context of the Mach-O. + @param[in] Index Index of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +MACH_NLIST_64 * +MachoGetSymbolByIndex64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ); + +/** + Retrieves Symbol's name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the name of. + + @retval symbol name. + +**/ +CONST CHAR8 * +MachoGetSymbolName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ); + +/** + Retrieves Symbol's name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Indirect symbol to retrieve the name of. + + @retval NULL NULL is returned on failure. + +**/ +CONST CHAR8 * +MachoGetIndirectSymbolName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ); + +/** + Returns whether the symbol's value is a valid address within the Mach-O + referenced to by Context. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to verify the value of. + +**/ +BOOLEAN +MachoIsSymbolValueInRange64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ); + +/** + Retrieves the symbol referenced by the Relocation targeting Address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address to search for. + @param[out] Symbol Buffer to output the symbol referenced by the + Relocation into. The output is undefined when FALSE + is returned. May be NULL. + + @returns Whether the Relocation exists. + +**/ +BOOLEAN +MachoGetSymbolByRelocationOffset64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT MACH_NLIST_64 **Symbol + ); + +/** + Retrieves the symbol referenced by the extern Relocation targeting Address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address to search for. + @param[out] Symbol Buffer to output the symbol referenced by the + Relocation into. The output is undefined when FALSE + is returned. May be NULL. + + @returns Whether the Relocation exists. + +**/ +BOOLEAN +MachoGetSymbolByExternRelocationOffset64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT MACH_NLIST_64 **Symbol + ); + +/** + Relocate Symbol to be against LinkAddress. + + @param[in,out] Context Context of the Mach-O. + @param[in] LinkAddress The address to be linked against. + @param[in,out] Symbol The symbol to be relocated. + + @returns Whether the operation has been completed successfully. + +**/ +BOOLEAN +MachoRelocateSymbol64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 LinkAddress, + IN OUT MACH_NLIST_64 *Symbol + ); + +/** + Retrieves the Mach-O file offset of the address pointed to by Symbol. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the offset of. + @param[out] FileOffset Pointer the file offset is returned into. + If FALSE is returned, the output is undefined. + @param[out] MaxSize Maximum data safely available from FileOffset. + + @retval 0 0 is returned on failure. + +**/ +BOOLEAN +MachoSymbolGetFileOffset64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Returns whether Name is pure virtual. + + @param[in] Name The name to evaluate. + +**/ +BOOLEAN +MachoSymbolNameIsPureVirtual ( + IN CONST CHAR8 *Name + ); + +/** + Returns whether Name is a Padslot. + + @param[in] Name The name to evaluate. + +**/ +BOOLEAN +MachoSymbolNameIsPadslot ( + IN CONST CHAR8 *Name + ); + +/** + Returns whether SymbolName defines a Super Metaclass Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] SymbolName The symbol name to check. + +**/ +BOOLEAN +MachoSymbolNameIsSmcp64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SymbolName + ); + +/** + Returns whether SymbolName defines a Super Metaclass Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] SymbolName The symbol name to check. + +**/ +BOOLEAN +MachoSymbolNameIsMetaclassPointer64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SymbolName + ); + +/** + Retrieves the class name of a Super Meta Class Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] SmcpName SMCP Symbol name to get the class name of. + @param[in] ClassNameSize The size of ClassName. + @param[out] ClassName The output buffer for the class name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetClassNameFromSuperMetaClassPointer ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SmcpName, + IN UINTN ClassNameSize, + OUT CHAR8 *ClassName + ); + +/** + Retrieves the class name of a VTable. + + @param[out] VtableName The VTable's name. + +**/ +CONST CHAR8 * +MachoGetClassNameFromVtableName ( + IN CONST CHAR8 *VtableName + ); + +/** + Retrieves the function prefix of a class name. + + @param[in] ClassName The class name to evaluate. + @param[in] FunctionPrefixSize The size of FunctionPrefix. + @param[out] FunctionPrefix The output buffer for the function prefix. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetFunctionPrefixFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN FunctionPrefixSize, + OUT CHAR8 *FunctionPrefix + ); + +/** + Retrieves the class name of a Meta Class Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] MetaClassName MCP Symbol name to get the class name of. + @param[in] ClassNameSize The size of ClassName. + @param[out] ClassName The output buffer for the class name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetClassNameFromMetaClassPointer ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *MetaClassName, + IN UINTN ClassNameSize, + OUT CHAR8 *ClassName + ); + +/** + Retrieves the VTable name of a class name. + + @param[in] ClassName Class name to get the VTable name of. + @param[in] VtableNameSize The size of VtableName. + @param[out] VtableName The output buffer for the VTable name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetVtableNameFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN VtableNameSize, + OUT CHAR8 *VtableName + ); + +/** + Retrieves the Meta VTable name of a class name. + + @param[in] ClassName Class name to get the Meta VTable name of. + @param[in] VtableNameSize The size of VtableName. + @param[out] VtableName The output buffer for the VTable name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetMetaVtableNameFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN VtableNameSize, + OUT CHAR8 *VtableName + ); + +/** + Retrieves the final symbol name of a class name. + + @param[in] ClassName Class name to get the final symbol name of. + @param[in] FinalSymbolNameSize The size of FinalSymbolName. + @param[out] FinalSymbolName The output buffer for the final symbol name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetFinalSymbolNameFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN FinalSymbolNameSize, + OUT CHAR8 *FinalSymbolName + ); + +/** + Returns whether SymbolName defines a VTable. + + @param[in] SymbolName The symbol name to check. + +**/ +BOOLEAN +MachoSymbolNameIsVtable64 ( + IN CONST CHAR8 *SymbolName + ); + +/** + Returns whether the symbol name describes a C++ symbol. + + @param[in] Name The name to evaluate. + +**/ +BOOLEAN +MachoSymbolNameIsCxx ( + IN CONST CHAR8 *Name + ); + +/** + Retrieves Metaclass symbol of a SMCP. + + @param[in,out] Context Context of the Mach-O. + @param[in] Smcp The SMCP to evaluate. + + @retval NULL NULL is returned on failure. + +**/ +MACH_NLIST_64 * +MachoGetMetaclassSymbolFromSmcpSymbol64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Smcp + ); + +/** + Retrieves VTable and Meta VTable of a SMCP. + Logically matches XNU's get_vtable_syms_from_smcp. + + @param[in,out] Context Context of the Mach-O. + @param[in] SmcpName SMCP Symbol mame to retrieve the VTables from. + @param[out] Vtable Output buffer for the VTable symbol pointer. + @param[out] MetaVtable Output buffer for the Meta VTable symbol pointer. + +**/ +BOOLEAN +MachoGetVtableSymbolsFromSmcp64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SmcpName, + OUT CONST MACH_NLIST_64 **Vtable, + OUT CONST MACH_NLIST_64 **MetaVtable + ); + +/** + Returns whether the Relocation's type indicates a Pair for the Intel 64 + platform. + + @param[in] Type The Relocation's type to verify. + +**/ +BOOLEAN +MachoRelocationIsPairIntel64 ( + IN UINT8 Type + ); + +/** + Returns whether the Relocation's type matches a Pair's for the Intel 64 + platform. + + @param[in] Type The Relocation's type to verify. + +**/ +BOOLEAN +MachoIsRelocationPairTypeIntel64 ( + IN UINT8 Type + ); + +/** + Returns whether the Relocation shall be preserved for the Intel 64 platform. + + @param[in] Type The Relocation's type to verify. + +**/ +BOOLEAN +MachoPreserveRelocationIntel64 ( + IN UINT8 Type + ); + +/** + Obtain symbol tables. + + @param[in] Context Context of the Mach-O. + @param[out] SymbolTable Symbol table. + @param[out] StringTable String table for that symbol table. + @param[out] LocalSymbols Local symbol table. + @param[out] NumLocalSymbols Number of symbols in local symbol table. + @param[out] ExternalSymbols External symbol table. + @param[out] NumExternalSymbols Number of symbols in external symbol table. + @param[out] UndefinedSymbols Undefined symbol table. + @param[out] NumUndefinedSymbols Number of symbols in undefined symbol table. + + @return number of symbols in symbol table or 0. +**/ +UINT32 +MachoGetSymbolTable ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST_64 **SymbolTable, + OUT CONST CHAR8 **StringTable OPTIONAL, + OUT CONST MACH_NLIST_64 **LocalSymbols OPTIONAL, + OUT UINT32 *NumLocalSymbols OPTIONAL, + OUT CONST MACH_NLIST_64 **ExternalSymbols OPTIONAL, + OUT UINT32 *NumExternalSymbols OPTIONAL, + OUT CONST MACH_NLIST_64 **UndefinedSymbols OPTIONAL, + OUT UINT32 *NumUndefinedSymbols OPTIONAL + ); + +/** + Obtain indirect symbol table. + + @param[in] Context Context of the Mach-O. + @param[in,out] SymbolTable Indirect symbol table. + + @return number of symbols in indirect symbol table or 0. +**/ +UINT32 +MachoGetIndirectSymbolTable ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST_64 **SymbolTable + ); + +/** + Returns a pointer to the Mach-O file at the specified virtual address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Virtual address to look up. + @param[out] MaxSize Maximum data safely available from FileOffset. + If NULL is returned, the output is undefined. + +**/ +VOID * +MachoGetFilePointerByAddress64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Expand Mach-O image to Destination (make segment file sizes equal to vm sizes). + + @param[in] Context Context of the Mach-O. + @param[out] Destination Output buffer. + @param[in] DestinationSize Output buffer maximum size. + @param[in] Strip Output with stripped prelink commands. + + @returns New image size or 0 on failure. + +**/ +UINT32 +MachoExpandImage64 ( + IN OC_MACHO_CONTEXT *Context, + OUT UINT8 *Destination, + IN UINT32 DestinationSize, + IN BOOLEAN Strip + ); + +/** + Find Mach-O entry point from LC_UNIXTHREAD loader command. + This command does not verify Mach-O and assumes it is valid. + + @param[in] Image Loaded Mach-O image. + + @returns Entry point or 0. +**/ +UINTN +MachoRuntimeGetEntryAddress ( + IN VOID *Image + ); + +#endif // OC_MACHO_LIB_H_ diff --git a/Include/Protocol/AmiKeycode.h b/Include/Protocol/AmiKeycode.h new file mode 100644 index 000000000..7cc29dbe9 --- /dev/null +++ b/Include/Protocol/AmiKeycode.h @@ -0,0 +1,59 @@ +/** @file + Header file for AMI EfiKeycode protocol definitions. + +Copyright (c) 2016, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef AMI_KEYCODE_H +#define AMI_KEYCODE_H + +// 0ADFB62D-FF74-484C-8944-F85C4BEA87A8 +#define AMI_EFIKEYCODE_PROTOCOL_GUID \ + { 0x0ADFB62D, 0xFF74, 0x484C, { 0x89, 0x44, 0xF8, 0x5C, 0x4B, 0xEA, 0x87, 0xA8 } } + +extern EFI_GUID gAmiEfiKeycodeProtocolGuid; + +typedef struct _AMI_EFIKEYCODE_PROTOCOL AMI_EFIKEYCODE_PROTOCOL; + +#ifndef KEY_STATE_EXPOSED +#define KEY_STATE_EXPOSED 0x40 +#endif + +typedef struct { + EFI_INPUT_KEY Key; + EFI_KEY_STATE KeyState; + EFI_KEY EfiKey; + UINT8 EfiKeyIsValid; + UINT8 PS2ScanCode; + UINT8 PS2ScanCodeIsValid; +} AMI_EFI_KEY_DATA; + +typedef EFI_STATUS (EFIAPI *AMI_READ_EFI_KEY) ( + IN AMI_EFIKEYCODE_PROTOCOL *This, + OUT AMI_EFI_KEY_DATA *KeyData + ); + +typedef +EFI_STATUS +(EFIAPI *AMI_RESET_EX) ( + IN AMI_EFIKEYCODE_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +struct _AMI_EFIKEYCODE_PROTOCOL { + AMI_RESET_EX Reset; + AMI_READ_EFI_KEY ReadEfikey; + EFI_EVENT WaitForKeyEx; + EFI_SET_STATE SetState; + EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify; + EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify; +}; + +#endif diff --git a/Include/Protocol/AmiPointer.h b/Include/Protocol/AmiPointer.h new file mode 100644 index 000000000..127fdc92a --- /dev/null +++ b/Include/Protocol/AmiPointer.h @@ -0,0 +1,68 @@ +/** @file + Header file for AMI EfiPointer protocol definitions. + +Copyright (c) 2016, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef AMI_POINTER_H +#define AMI_POINTER_H + +// 15A10CE7-EAB5-43BF-9042-74432E696377 +#define AMI_EFIPOINTER_PROTOCOL_GUID \ + { 0x15A10CE7, 0xEAB5, 0x43BF, { 0x90, 0x42, 0x74, 0x43, 0x2E, 0x69, 0x63, 0x77 } } + +extern EFI_GUID gAmiEfiPointerProtocolGuid; + +typedef struct _AMI_EFIPOINTER_PROTOCOL AMI_EFIPOINTER_PROTOCOL; + +// Unless Changed == 1, no data is provided +typedef struct { + UINT8 Absolute; + UINT8 One; + UINT8 Changed; + UINT8 Reserved; + INT32 PositionX; + INT32 PositionY; + INT32 PositionZ; +} AMI_POINTER_POSITION_STATE_DATA; + +VERIFY_SIZE_OF(AMI_POINTER_POSITION_STATE_DATA, 16); + +// Unless Changed == 1, no data is provided +typedef struct { + UINT8 Changed; + UINT8 LeftButton; + UINT8 MiddleButton; + UINT8 RightButton; +} AMI_POINTER_BUTTON_STATE_DATA; + +VERIFY_SIZE_OF(AMI_POINTER_BUTTON_STATE_DATA, 4); + +typedef VOID (EFIAPI *AMI_EFIPOINTER_RESET) ( + IN AMI_EFIPOINTER_PROTOCOL *This +); + +typedef VOID (EFIAPI *AMI_EFIPOINTER_GET_BUTTON_STATE) ( + IN AMI_EFIPOINTER_PROTOCOL *This, + OUT AMI_POINTER_BUTTON_STATE_DATA *State +); + +typedef VOID (EFIAPI *AMI_EFIPOINTER_GET_POSITION_STATE) ( + IN AMI_EFIPOINTER_PROTOCOL *This, + OUT AMI_POINTER_POSITION_STATE_DATA *State +); + +struct _AMI_EFIPOINTER_PROTOCOL { + AMI_EFIPOINTER_RESET Reset; + AMI_EFIPOINTER_GET_POSITION_STATE GetPositionState; + AMI_EFIPOINTER_GET_BUTTON_STATE GetButtonState; +}; + +#endif diff --git a/Include/Protocol/AptioMemoryFix.h b/Include/Protocol/AptioMemoryFix.h new file mode 100644 index 000000000..4e425d909 --- /dev/null +++ b/Include/Protocol/AptioMemoryFix.h @@ -0,0 +1,44 @@ +/** + + Aptio Memory Fix protocol to inform bootloaders + about driver availability. + + by cecekpawon, vit9696 + +**/ + +#ifndef APTIOFIX_MEMORY_PROTOCOL_H +#define APTIOFIX_MEMORY_PROTOCOL_H + +#define APTIOMEMORYFIX_PACKAGE_VERSION L"R27" + +#define APTIOMEMORYFIX_PROTOCOL_REVISION 27 + +// +// APTIOMEMORYFIX_PROTOCOL_GUID +// C7CBA84E-CC77-461D-9E3C-6BE0CB79A7C1 +// +#define APTIOMEMORYFIX_PROTOCOL_GUID \ + { 0xC7CBA84E, 0xCC77, 0x461D, { 0x9E, 0x3C, 0x6B, 0xE0, 0xCB, 0x79, 0xA7, 0xC1 } } + +// +// Set NVRAM routing, returns previous value. +// +typedef +BOOLEAN +EFIAPI +(*AMF_SET_NVRAM_REDIRECT) ( + IN BOOLEAN NewValue + ); + +// +// Includes a revision for debugging reasons +// +typedef struct { + UINTN Revision; + AMF_SET_NVRAM_REDIRECT SetNvram; +} APTIOMEMORYFIX_PROTOCOL; + +extern EFI_GUID gAptioMemoryFixProtocolGuid; + +#endif // APTIOFIX_MEMORY_PROTOCOL_H diff --git a/Library/MachoLib/CxxSymbols.c b/Library/MachoLib/CxxSymbols.c new file mode 100644 index 000000000..f929fd45a --- /dev/null +++ b/Library/MachoLib/CxxSymbols.c @@ -0,0 +1,620 @@ +/** @file + Provides services for C++ symbols. + +Copyright (c) 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define CXX_PREFIX "__Z" +#define VTABLE_PREFIX CXX_PREFIX "TV" +#define OSOBJ_PREFIX CXX_PREFIX "N" +#define RESERVED_TOKEN "_RESERVED" +#define METACLASS_TOKEN "10gMetaClassE" +#define SMCP_TOKEN "10superClassE" +#define METACLASS_VTABLE_PREFIX VTABLE_PREFIX "N" +#define METACLASS_VTABLE_SUFFIX "9MetaClassE" +#define CXX_PURE_VIRTUAL "___cxa_pure_virtual" +#define FINAL_CLASS_TOKEN "14__OSFinalClassEv" + +#define VTABLE_ENTRY_SIZE_64 8U +#define VTABLE_HEADER_LEN_64 2U +#define VTABLE_HEADER_SIZE_64 (VTABLE_HEADER_LEN_64 * VTABLE_ENTRY_SIZE_64) + +#define SYM_MAX_NAME_LEN 256U + +/** + Returns whether Name is pure virtual. + + @param[in] Name The name to evaluate. + +**/ +BOOLEAN +MachoSymbolNameIsPureVirtual ( + IN CONST CHAR8 *Name + ) +{ + ASSERT (Name != NULL); + return (AsciiStrCmp (Name, CXX_PURE_VIRTUAL) == 0); +} + +/** + Returns whether Name is a Padslot. + + @param[in] Name The name to evaluate. + +**/ +BOOLEAN +MachoSymbolNameIsPadslot ( + IN CONST CHAR8 *Name + ) +{ + ASSERT (Name != NULL); + return (AsciiStrStr (Name, RESERVED_TOKEN) != NULL); +} + +/** + Returns whether SymbolName defines a Super Metaclass Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] SymbolName The symbol name to check. + +**/ +BOOLEAN +MachoSymbolNameIsSmcp64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SymbolName + ) +{ + CONST CHAR8 *Suffix; + + ASSERT (Context != NULL); + ASSERT (SymbolName != NULL); + // + // Verify the symbol has... + // 1) The C++ prefix. + // 2) The SMCP suffix. + // 3) At least one character between the prefix and the suffix. + // + Suffix = AsciiStrStr (SymbolName, SMCP_TOKEN); + + if ((Suffix == NULL) + || (AsciiStrnCmp (SymbolName, OSOBJ_PREFIX, L_STR_LEN (OSOBJ_PREFIX)) != 0) + || ((UINTN)(Suffix - SymbolName) <= L_STR_LEN (OSOBJ_PREFIX))) { + return FALSE; + } + + return TRUE; +} + +/** + Returns whether SymbolName defines a Super Metaclass Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] SymbolName The symbol name to check. + +**/ +BOOLEAN +MachoSymbolNameIsMetaclassPointer64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SymbolName + ) +{ + CONST CHAR8 *Suffix; + + ASSERT (Context != NULL); + ASSERT (SymbolName != NULL); + // + // Verify the symbol has... + // 1) The C++ prefix. + // 2) The MetaClass suffix. + // 3) At least one character between the prefix and the suffix. + // + Suffix = AsciiStrStr (SymbolName, METACLASS_TOKEN); + + if ((Suffix == NULL) + || (AsciiStrnCmp (SymbolName, OSOBJ_PREFIX, L_STR_LEN (OSOBJ_PREFIX)) != 0) + || ((UINTN)(Suffix - SymbolName) <= L_STR_LEN (OSOBJ_PREFIX))) { + return FALSE; + } + + return TRUE; +} + +/** + Retrieves the class name of a Super Meta Class Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] SmcpName SMCP Symbol name to get the class name of. + @param[in] ClassNameSize The size of ClassName. + @param[out] ClassName The output buffer for the class name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetClassNameFromSuperMetaClassPointer ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SmcpName, + IN UINTN ClassNameSize, + OUT CHAR8 *ClassName + ) +{ + UINTN PrefixSize; + UINTN SuffixSize; + UINTN OutputSize; + + ASSERT (Context != NULL); + ASSERT (SmcpName != NULL); + ASSERT (ClassNameSize > 0); + ASSERT (ClassName != NULL); + + ASSERT (Context->StringTable != NULL); + + ASSERT (MachoSymbolNameIsSmcp64 (Context, SmcpName)); + + PrefixSize = L_STR_LEN (OSOBJ_PREFIX); + SuffixSize = L_STR_LEN (SMCP_TOKEN); + + OutputSize = (AsciiStrLen (SmcpName) - PrefixSize - SuffixSize); + + if ((OutputSize + 1) > ClassNameSize) { + return FALSE; + } + + CopyMem (ClassName, &SmcpName[PrefixSize], OutputSize); + ClassName[OutputSize] = '\0'; + + return TRUE; +} + +/** + Retrieves the class name of a VTable. + + @param[out] VtableName The VTable's name. + +**/ +CONST CHAR8 * +MachoGetClassNameFromVtableName ( + IN CONST CHAR8 *VtableName + ) +{ + ASSERT (VtableName != NULL); + ASSERT (MachoSymbolNameIsVtable64 (VtableName)); + // + // As there is no suffix, just return a pointer from within VtableName. + // + return &VtableName[L_STR_LEN (VTABLE_PREFIX)]; +} + +/** + Retrieves the function prefix of a class name. + + @param[in] ClassName The class name to evaluate. + @param[in] FunctionPrefixSize The size of FunctionPrefix. + @param[out] FunctionPrefix The output buffer for the function prefix. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetFunctionPrefixFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN FunctionPrefixSize, + OUT CHAR8 *FunctionPrefix + ) +{ + UINTN Index; + UINTN BodySize; + BOOLEAN Result; + UINTN TotalSize; + + ASSERT (ClassName != NULL); + ASSERT (FunctionPrefixSize > 0); + ASSERT (FunctionPrefix != NULL); + + BodySize = AsciiStrSize (ClassName); + Result = OcOverflowAddUN (L_STR_LEN (OSOBJ_PREFIX), BodySize, &TotalSize); + if (Result || (FunctionPrefixSize < TotalSize)) { + return FALSE; + } + + Index = 0; + CopyMem ( + &FunctionPrefix[Index], + OSOBJ_PREFIX, + L_STR_LEN (OSOBJ_PREFIX) + ); + + Index += L_STR_LEN (OSOBJ_PREFIX); + CopyMem ( + &FunctionPrefix[Index], + ClassName, + BodySize + ); + + return TRUE; +} + +/** + Retrieves the class name of a Meta Class Pointer. + + @param[in,out] Context Context of the Mach-O. + @param[in] MetaClassName MCP Symbol name to get the class name of. + @param[in] ClassNameSize The size of ClassName. + @param[out] ClassName The output buffer for the class name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetClassNameFromMetaClassPointer ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *MetaClassName, + IN UINTN ClassNameSize, + OUT CHAR8 *ClassName + ) +{ + UINTN PrefixSize; + UINTN SuffixSize; + UINTN ClassNameLength; + + ASSERT (Context != NULL); + ASSERT (MetaClassName != NULL); + ASSERT (ClassNameSize > 0); + ASSERT (ClassName != NULL); + + ASSERT (Context->StringTable != NULL); + + ASSERT (MachoSymbolNameIsMetaclassPointer64 (Context, MetaClassName)); + + PrefixSize = L_STR_LEN (OSOBJ_PREFIX); + SuffixSize = L_STR_LEN (METACLASS_TOKEN); + + ClassNameLength = (AsciiStrLen (MetaClassName) - PrefixSize - SuffixSize); + if ((ClassNameLength + 1) > ClassNameSize) { + return FALSE; + } + + CopyMem (ClassName, &MetaClassName[PrefixSize], ClassNameLength); + ClassName[ClassNameLength] = '\0'; + + return TRUE; +} + +/** + Retrieves the VTable name of a class name. + + @param[in] ClassName Class name to get the VTable name of. + @param[in] VtableNameSize The size of VtableName. + @param[out] VtableName The output buffer for the VTable name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetVtableNameFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN VtableNameSize, + OUT CHAR8 *VtableName + ) +{ + UINTN Index; + UINTN BodySize; + BOOLEAN Result; + UINTN TotalSize; + + ASSERT (ClassName != NULL); + ASSERT (VtableNameSize > 0); + ASSERT (VtableName != NULL); + + BodySize = AsciiStrSize (ClassName); + + Result = OcOverflowAddUN ( + L_STR_LEN (VTABLE_PREFIX), + BodySize, + &TotalSize + ); + if (Result || (VtableNameSize < TotalSize)) { + return FALSE; + } + + Index = 0; + CopyMem ( + &VtableName[Index], + VTABLE_PREFIX, + L_STR_LEN (VTABLE_PREFIX) + ); + + Index += L_STR_LEN (VTABLE_PREFIX); + CopyMem (&VtableName[Index], ClassName, BodySize); + + return TRUE; +} + +/** + Retrieves the Meta VTable name of a class name. + + @param[in] ClassName Class name to get the Meta VTable name of. + @param[in] VtableNameSize The size of VtableName. + @param[out] VtableName The output buffer for the VTable name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetMetaVtableNameFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN VtableNameSize, + OUT CHAR8 *VtableName + ) +{ + UINTN BodyLength; + BOOLEAN Result; + UINTN TotalSize; + UINTN Index; + + ASSERT (ClassName != NULL); + ASSERT (VtableNameSize > 0); + ASSERT (VtableName != NULL); + + BodyLength = AsciiStrLen (ClassName); + + Result = OcOverflowTriAddUN ( + L_STR_LEN (METACLASS_VTABLE_PREFIX), + BodyLength, + L_STR_SIZE (METACLASS_VTABLE_SUFFIX), + &TotalSize + ); + if (Result || (VtableNameSize < TotalSize)) { + return FALSE; + } + + Index = 0; + CopyMem ( + &VtableName[Index], + METACLASS_VTABLE_PREFIX, + L_STR_LEN (METACLASS_VTABLE_PREFIX) + ); + + Index += L_STR_LEN (METACLASS_VTABLE_PREFIX); + CopyMem (&VtableName[Index], ClassName, BodyLength); + + Index += BodyLength; + CopyMem ( + &VtableName[Index], + METACLASS_VTABLE_SUFFIX, + L_STR_SIZE (METACLASS_VTABLE_SUFFIX) + ); + + return TRUE; +} + +/** + Retrieves the final symbol name of a class name. + + @param[in] ClassName Class name to get the final symbol name of. + @param[in] FinalSymbolNameSize The size of FinalSymbolName. + @param[out] FinalSymbolName The output buffer for the final symbol name. + + @returns Whether the name has been retrieved successfully. + +**/ +BOOLEAN +MachoGetFinalSymbolNameFromClassName ( + IN CONST CHAR8 *ClassName, + IN UINTN FinalSymbolNameSize, + OUT CHAR8 *FinalSymbolName + ) +{ + UINTN BodyLength; + BOOLEAN Result; + UINTN TotalSize; + UINTN Index; + + ASSERT (ClassName != NULL); + ASSERT (FinalSymbolNameSize > 0); + ASSERT (FinalSymbolName != NULL); + + BodyLength = AsciiStrLen (ClassName); + + Result = OcOverflowTriAddUN ( + L_STR_LEN (OSOBJ_PREFIX), + BodyLength, + L_STR_SIZE (FINAL_CLASS_TOKEN), + &TotalSize + ); + if (Result || (FinalSymbolNameSize < TotalSize)) { + return FALSE; + } + + Index = 0; + CopyMem ( + &FinalSymbolName[Index], + OSOBJ_PREFIX, + L_STR_LEN (OSOBJ_PREFIX) + ); + + Index += L_STR_LEN (OSOBJ_PREFIX); + CopyMem ( + &FinalSymbolName[Index], + ClassName, + BodyLength + ); + + Index += BodyLength; + CopyMem ( + &FinalSymbolName[Index], + FINAL_CLASS_TOKEN, + L_STR_SIZE (FINAL_CLASS_TOKEN) + ); + + return TRUE; +} + +/** + Returns whether SymbolName defines a VTable. + + @param[in,out] Context Context of the Mach-O. + @param[in] SymbolName The symbol name to check. + +**/ +BOOLEAN +MachoSymbolNameIsVtable64 ( + IN CONST CHAR8 *SymbolName + ) +{ + ASSERT (SymbolName != NULL); + // + // Implicitely checks for METACLASS_VTABLE_PREFIX. + // + return AsciiStrnCmp (SymbolName, VTABLE_PREFIX, L_STR_LEN (VTABLE_PREFIX)) == 0; +} + +/** + Returns whether the symbol name describes a C++ symbol. + + @param[in] Name The name to evaluate. + +**/ +BOOLEAN +MachoSymbolNameIsCxx ( + IN CONST CHAR8 *Name + ) +{ + ASSERT (Name != NULL); + return AsciiStrnCmp (Name, CXX_PREFIX, L_STR_LEN (CXX_PREFIX)) == 0; +} + +/** + Retrieves Metaclass symbol of a SMCP. + + @param[in,out] Context Context of the Mach-O. + @param[in] Smcp The SMCP to evaluate. + + @retval NULL NULL is returned on failure. + +**/ +MACH_NLIST_64 * +MachoGetMetaclassSymbolFromSmcpSymbol64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Smcp + ) +{ + MACH_NLIST_64 *Symbol; + BOOLEAN Result; + + ASSERT (Context != NULL); + ASSERT (Smcp != NULL); + + Result = MachoGetSymbolByRelocationOffset64 ( + Context, + Smcp->Value, + &Symbol + ); + if (Result && (Symbol != NULL)) { + Result = MachoSymbolNameIsMetaclassPointer64 ( + Context, + MachoGetSymbolName64 (Context, Symbol) + ); + if (Result) { + return Symbol; + } + } + + return NULL; +} + +/** + Retrieves VTable and Meta VTable of a SMCP. + Logically matches XNU's get_vtable_syms_from_smcp. + + @param[in,out] Context Context of the Mach-O. + @param[in] SmcpName SMCP Symbol mame to retrieve the VTables from. + @param[out] Vtable Output buffer for the VTable symbol pointer. + @param[out] MetaVtable Output buffer for the Meta VTable symbol pointer. + +**/ +BOOLEAN +MachoGetVtableSymbolsFromSmcp64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SmcpName, + OUT CONST MACH_NLIST_64 **Vtable, + OUT CONST MACH_NLIST_64 **MetaVtable + ) +{ + CHAR8 ClassName[SYM_MAX_NAME_LEN]; + CHAR8 VtableName[SYM_MAX_NAME_LEN]; + CHAR8 MetaVtableName[SYM_MAX_NAME_LEN]; + BOOLEAN Result; + MACH_NLIST_64 *VtableSymbol; + MACH_NLIST_64 *MetaVtableSymbol; + + ASSERT (Context != NULL); + ASSERT (SmcpName != NULL); + ASSERT (Vtable != NULL); + ASSERT (MetaVtable != NULL); + + Result = MachoGetClassNameFromSuperMetaClassPointer ( + Context, + SmcpName, + sizeof (ClassName), + ClassName + ); + if (!Result) { + return FALSE; + } + + Result = MachoGetVtableNameFromClassName ( + ClassName, + sizeof (VtableName), + VtableName + ); + if (!Result) { + return FALSE; + } + + VtableSymbol = MachoGetLocalDefinedSymbolByName (Context, VtableName); + if (VtableSymbol == NULL) { + return FALSE; + } + + Result = MachoGetMetaVtableNameFromClassName ( + ClassName, + sizeof (MetaVtableName), + MetaVtableName + ); + if (!Result) { + return FALSE; + } + + MetaVtableSymbol = MachoGetLocalDefinedSymbolByName ( + Context, + MetaVtableName + ); + if (MetaVtableSymbol == NULL) { + return FALSE; + } + + *Vtable = VtableSymbol; + *MetaVtable = MetaVtableSymbol; + + return TRUE; +} diff --git a/Library/MachoLib/Header.c b/Library/MachoLib/Header.c new file mode 100644 index 000000000..a320de952 --- /dev/null +++ b/Library/MachoLib/Header.c @@ -0,0 +1,1449 @@ +/** + Provides services for Mach-O headers. + +Copyright (C) 2016 - 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "MachoLibInternal.h" + +/** + Returns the Mach-O Header structure. + + @param[in,out] Context Context of the Mach-O. + +**/ +MACH_HEADER_64 * +MachoGetMachHeader64 ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + ASSERT (Context != NULL); + ASSERT (Context->MachHeader != NULL); + + return Context->MachHeader; +} + +/** + Returns the Mach-O's file size. + + @param[in,out] Context Context of the Mach-O. + +**/ +UINT32 +MachoGetFileSize ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + ASSERT (Context != NULL); + ASSERT (Context->FileSize != 0); + + return Context->FileSize; +} + +/** + Returns the Mach-O's virtual address space size. + + @param[out] Context Context of the Mach-O. + +**/ +UINT32 +MachoGetVmSize64 ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + UINT64 VmSize; + MACH_SEGMENT_COMMAND_64 *Segment; + + ASSERT (Context != NULL); + ASSERT (Context->FileSize != 0); + + VmSize = 0; + + for ( + Segment = MachoGetNextSegment64 (Context, NULL); + Segment != NULL; + Segment = MachoGetNextSegment64 (Context, Segment) + ) { + if (OcOverflowAddU64 (VmSize, Segment->Size, &VmSize)) { + return 0; + } + VmSize = MACHO_ALIGN (VmSize); + } + + if (VmSize > MAX_UINT32) { + return 0; + } + + return (UINT32) VmSize; +} + +/** + Moves file pointer and size to point to x86_64 slice in case + FAT Mach-O is used. + + @param[in,out] FileData Pointer to pointer of the file's data. + @param[in,out] FileSize Pointer to file size of FileData. + + @return FALSE is not valid FAT image. +**/ +STATIC +BOOLEAN +MachoFilterFatArchitecture64 ( + IN OUT UINT8 **FileData, + IN OUT UINT32 *FileSize + ) +{ + BOOLEAN SwapBytes; + MACH_FAT_HEADER *FatHeader; + UINT32 NumberOfFatArch; + UINT32 Offset; + MACH_CPU_TYPE CpuType; + UINT32 TmpSize; + UINT32 Index; + UINT32 Size; + + FatHeader = (MACH_FAT_HEADER *) *FileData; + + if (!OC_ALIGNED (FatHeader) + || *FileSize < sizeof (MACH_FAT_HEADER) + || (FatHeader->Signature != MACH_FAT_BINARY_INVERT_SIGNATURE + && FatHeader->Signature != MACH_FAT_BINARY_SIGNATURE)) + { + return FALSE; + } + + SwapBytes = FatHeader->Signature == MACH_FAT_BINARY_INVERT_SIGNATURE; + NumberOfFatArch = FatHeader->NumberOfFatArch; + if (SwapBytes) { + NumberOfFatArch = SwapBytes32 (NumberOfFatArch); + } + + if (OcOverflowMulAddU32 (NumberOfFatArch, sizeof (MACH_FAT_ARCH), sizeof (MACH_FAT_HEADER), &TmpSize) + || TmpSize > *FileSize) { + return FALSE; + } + + // + // TODO: extend the interface to support MachCpuSubtypeX8664H some day. + // + for (Index = 0; Index < NumberOfFatArch; ++Index) { + CpuType = FatHeader->FatArch[Index].CpuType; + if (SwapBytes) { + CpuType = SwapBytes32 (CpuType); + } + if (CpuType == MachCpuTypeX8664) { + Offset = FatHeader->FatArch[Index].Offset; + Size = FatHeader->FatArch[Index].Size; + if (SwapBytes) { + Offset = SwapBytes32 (Offset); + Size = SwapBytes32 (Size); + } + + if (Offset == 0 + || OcOverflowAddU32 (Offset, Size, &TmpSize) + || TmpSize > *FileSize) { + return FALSE; + } + + *FileData = *FileData + Offset; + *FileSize = Size; + + return TRUE; + } + } + + return FALSE; +} + +/** + Initializes a Mach-O Context. + + @param[out] Context Mach-O Context to initialize. + @param[in] FileData Pointer to the file's data. + @param[in] FileSize File size of FileData. + + @return Whether Context has been initialized successfully. +**/ +BOOLEAN +MachoInitializeContext ( + OUT OC_MACHO_CONTEXT *Context, + IN VOID *FileData, + IN UINT32 FileSize + ) +{ + MACH_HEADER_64 *MachHeader; + UINTN TopOfFile; + UINTN TopOfCommands; + UINT32 Index; + CONST MACH_LOAD_COMMAND *Command; + UINTN TopOfCommand; + UINT32 CommandsSize; + BOOLEAN Result; + + ASSERT (FileData != NULL); + ASSERT (FileSize > 0); + ASSERT (Context != NULL); + + MachoFilterFatArchitecture64 ((UINT8 **) &FileData, &FileSize); + + MachHeader = (MACH_HEADER_64 *) FileData; + TopOfFile = ((UINTN)MachHeader + FileSize); + + ASSERT (TopOfFile > (UINTN)MachHeader); + + if (FileSize < sizeof (*MachHeader) + || !OC_ALIGNED (MachHeader) + || MachHeader->Signature != MACH_HEADER_64_SIGNATURE) { + return FALSE; + } + + Result = OcOverflowAddUN ( + (UINTN)MachHeader->Commands, + MachHeader->CommandsSize, + &TopOfCommands + ); + if (Result || (TopOfCommands > TopOfFile)) { + return FALSE; + } + + CommandsSize = 0; + + for ( + Index = 0, Command = MachHeader->Commands; + Index < MachHeader->NumCommands; + ++Index, Command = NEXT_MACH_LOAD_COMMAND (Command) + ) { + Result = OcOverflowAddUN ( + (UINTN)Command, + sizeof (*Command), + &TopOfCommand + ); + if (Result + || (TopOfCommand > TopOfCommands) + || (Command->CommandSize < sizeof (*Command)) + || ((Command->CommandSize % sizeof (UINT64)) != 0) // Assumption: 64-bit, see below. + ) { + return FALSE; + } + + Result = OcOverflowAddU32 ( + CommandsSize, + Command->CommandSize, + &CommandsSize + ); + if (Result) { + return FALSE; + } + } + + if (MachHeader->CommandsSize != CommandsSize) { + return FALSE; + } + // + // Verify assumptions made by this library. + // Carefully audit all "Assumption:" remarks before modifying these checks. + // + if ((MachHeader->CpuType != MachCpuTypeX8664) + || ((MachHeader->FileType != MachHeaderFileTypeKextBundle) + && (MachHeader->FileType != MachHeaderFileTypeExecute))) { + return FALSE; + } + + ZeroMem (Context, sizeof (*Context)); + + Context->MachHeader = MachHeader; + Context->FileSize = FileSize; + + return TRUE; +} + +/** + Returns the last virtual address of a Mach-O. + + @param[in,out] Context Context of the Mach-O. + + @retval 0 The binary is malformed. + +**/ +UINT64 +MachoGetLastAddress64 ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + UINT64 LastAddress; + + CONST MACH_SEGMENT_COMMAND_64 *Segment; + UINT64 Address; + + ASSERT (Context != NULL); + + LastAddress = 0; + + for ( + Segment = MachoGetNextSegment64 (Context, NULL); + Segment != NULL; + Segment = MachoGetNextSegment64 (Context, Segment) + ) { + Address = (Segment->VirtualAddress + Segment->Size); + + if (Address > LastAddress) { + LastAddress = Address; + } + } + + return LastAddress; +} + +/** + Retrieves the first Load Command of type LoadCommandType. + + @param[in,out] Context Context of the Mach-O. + @param[in] LoadCommandType Type of the Load Command to retrieve. + @param[in] LoadCommand Previous Load Command. + If NULL, the first match is returned. + + @retval NULL NULL is returned on failure. + +**/ +STATIC +CONST MACH_LOAD_COMMAND * +InternalGetNextCommand64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_LOAD_COMMAND_TYPE LoadCommandType, + IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL + ) +{ + CONST MACH_LOAD_COMMAND *Command; + CONST MACH_HEADER_64 *MachHeader; + UINTN TopOfCommands; + + ASSERT (Context != NULL); + + MachHeader = Context->MachHeader; + ASSERT (MachHeader != NULL); + + TopOfCommands = ((UINTN)MachHeader->Commands + MachHeader->CommandsSize); + + if (LoadCommand != NULL) { + ASSERT ( + (LoadCommand >= &MachHeader->Commands[0]) + && ((UINTN)LoadCommand <= TopOfCommands) + ); + Command = NEXT_MACH_LOAD_COMMAND (LoadCommand); + } else { + Command = &MachHeader->Commands[0]; + } + + for ( + ; + (UINTN)Command < TopOfCommands; + Command = NEXT_MACH_LOAD_COMMAND (Command) + ) { + if (Command->CommandType == LoadCommandType) { + return Command; + } + } + + return NULL; +} + +/** + Retrieves the first UUID Load Command. + + @param[in,out] Context Context of the Mach-O. + + @retval NULL NULL is returned on failure. + +**/ +MACH_UUID_COMMAND * +MachoGetUuid64 ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + MACH_UUID_COMMAND *UuidCommand; + + ASSERT (Context != NULL); + + UuidCommand = (MACH_UUID_COMMAND *)( + InternalGetNextCommand64 ( + Context, + MACH_LOAD_COMMAND_UUID, + NULL + ) + ); + + if ((UuidCommand != NULL) + && OC_ALIGNED (UuidCommand) + && (UuidCommand->CommandSize == sizeof (*UuidCommand))) { + return UuidCommand; + } + + return NULL; +} + +/** + Retrieves the first segment by the name of SegmentName. + + @param[in,out] Context Context of the Mach-O. + @param[in] SegmentName Segment name to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SEGMENT_COMMAND_64 * +MachoGetSegmentByName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName + ) +{ + MACH_SEGMENT_COMMAND_64 *Segment; + INTN Result; + + ASSERT (Context != NULL); + ASSERT (SegmentName != NULL); + + Result = 0; + + for ( + Segment = MachoGetNextSegment64 (Context, NULL); + Segment != NULL; + Segment = MachoGetNextSegment64 (Context, Segment) + ) { + Result = AsciiStrnCmp ( + Segment->SegmentName, + SegmentName, + ARRAY_SIZE (Segment->SegmentName) + ); + if (Result == 0) { + return Segment; + } + } + + return NULL; +} + +/** + Returns whether Section is sane. + + @param[in,out] Context Context of the Mach-O. + @param[in] Section Section to verify. + @param[in] Segment Segment the section is part of. + +**/ +STATIC +BOOLEAN +InternalSectionIsSane ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SECTION_64 *Section, + IN CONST MACH_SEGMENT_COMMAND_64 *Segment + ) +{ + UINT64 TopOffset64; + UINT32 TopOffset32; + UINT64 TopOfSegment; + BOOLEAN Result; + UINT64 TopOfSection; + + ASSERT (Context != NULL); + ASSERT (Section != NULL); + ASSERT (Segment != NULL); + // + // Section->Alignment is stored as a power of 2. + // + if ((Section->Alignment > 31) + || ((Section->Offset != 0) && (Section->Offset < Segment->FileOffset))) { + return FALSE; + } + + TopOfSegment = (Segment->VirtualAddress + Segment->Size); + Result = OcOverflowAddU64 ( + Section->Address, + Section->Size, + &TopOfSection + ); + if (Result || (TopOfSection > TopOfSegment)) { + return FALSE; + } + + Result = OcOverflowAddU64 ( + Section->Offset, + Section->Size, + &TopOffset64 + ); + if (Result || (TopOffset64 > (Segment->FileOffset + Segment->FileSize))) { + return FALSE; + } + + if (Section->NumRelocations != 0) { + Result = OcOverflowMulAddU32 ( + Section->NumRelocations, + sizeof (MACH_RELOCATION_INFO), + Section->RelocationsOffset, + &TopOffset32 + ); + if (Result || (TopOffset32 > Context->FileSize)) { + return FALSE; + } + } + + return TRUE; +} + +/** + Retrieves the first section by the name of SectionName. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to search in. + @param[in] SectionName Section name to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSectionByName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_64 *Segment, + IN CONST CHAR8 *SectionName + ) +{ + MACH_SECTION_64 *Section; + INTN Result; + + ASSERT (Context != NULL); + ASSERT (Segment != NULL); + ASSERT (SectionName != NULL); + + for ( + Section = MachoGetNextSection64 (Context, Segment, NULL); + Section != NULL; + Section = MachoGetNextSection64 (Context, Segment, Section) + ) { + // + // Assumption: Mach-O is not of type MH_OBJECT. + // MH_OBJECT might have sections in segments they do not belong in for + // performance reasons. This library does not support intermediate + // objects. + // + Result = AsciiStrnCmp ( + Section->SectionName, + SectionName, + ARRAY_SIZE (Section->SectionName) + ); + if (Result == 0) { + return Section; + } + } + + return NULL; +} + +/** + Retrieves a section within a segment by the name of SegmentName. + + @param[in,out] Context Context of the Mach-O. + @param[in] SegmentName The name of the segment to search in. + @param[in] SectionName The name of the section to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSegmentSectionByName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName, + IN CONST CHAR8 *SectionName + ) +{ + MACH_SEGMENT_COMMAND_64 *Segment; + + ASSERT (Context != NULL); + ASSERT (SegmentName != NULL); + ASSERT (SectionName != NULL); + + Segment = MachoGetSegmentByName64 (Context, SegmentName); + + if (Segment != NULL) { + return MachoGetSectionByName64 (Context, Segment, SectionName); + } + + return NULL; +} + +/** + Retrieves the next segment. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to retrieve the successor of. + if NULL, the first segment is returned. + + @retal NULL NULL is returned on failure. + +**/ +MACH_SEGMENT_COMMAND_64 * +MachoGetNextSegment64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SEGMENT_COMMAND_64 *Segment OPTIONAL + ) +{ + MACH_SEGMENT_COMMAND_64 *NextSegment; + + CONST MACH_HEADER_64 *MachHeader; + UINTN TopOfCommands; + BOOLEAN Result; + UINT64 TopOfSegment; + UINTN TopOfSections; + + ASSERT (Context != NULL); + + ASSERT (Context->MachHeader != NULL); + ASSERT (Context->FileSize > 0); + + if (Segment != NULL) { + MachHeader = Context->MachHeader; + TopOfCommands = ((UINTN) MachHeader->Commands + MachHeader->CommandsSize); + ASSERT ( + ((UINTN) Segment >= (UINTN) &MachHeader->Commands[0]) + && ((UINTN) Segment < TopOfCommands) + ); + } + + NextSegment = (MACH_SEGMENT_COMMAND_64 *)( + InternalGetNextCommand64 ( + Context, + MACH_LOAD_COMMAND_SEGMENT_64, + (MACH_LOAD_COMMAND *) Segment + ) + ); + if ((NextSegment == NULL) + || !OC_ALIGNED (NextSegment) + || (NextSegment->CommandSize < sizeof (*NextSegment))) { + return NULL; + } + + Result = OcOverflowMulAddUN ( + NextSegment->NumSections, + sizeof (*NextSegment->Sections), + (UINTN) NextSegment->Sections, + &TopOfSections + ); + if (Result || (((UINTN) NextSegment + NextSegment->CommandSize) < TopOfSections)) { + return NULL; + } + + Result = OcOverflowAddU64 ( + NextSegment->FileOffset, + NextSegment->FileSize, + &TopOfSegment + ); + if (Result || (TopOfSegment > Context->FileSize)) { + return NULL; + } + + return NextSegment; +} + +/** + Retrieves the next section of a segment. + + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment The segment to get the section of. + @param[in] Section The section to get the successor of. + If NULL, the first section is returned. + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetNextSection64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_64 *Segment, + IN MACH_SECTION_64 *Section OPTIONAL + ) +{ + ASSERT (Context != NULL); + ASSERT (Segment != NULL); + + if (Section != NULL) { + ASSERT (Section >= Segment->Sections); + + ++Section; + + if (Section >= &Segment->Sections[Segment->NumSections]) { + return NULL; + } + } else if (Segment->NumSections > 0) { + Section = &Segment->Sections[0]; + } else { + return NULL; + } + + if (!InternalSectionIsSane (Context, Section, Segment)) { + return NULL; + } + + return Section; +} + +/** + Retrieves a section by its index. + + @param[in,out] Context Context of the Mach-O. + @param[in] Index Index of the section to retrieve. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSectionByIndex64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ) +{ + MACH_SECTION_64 *Section; + + MACH_SEGMENT_COMMAND_64 *Segment; + UINT32 SectionIndex; + UINT32 NextSectionIndex; + BOOLEAN Result; + + ASSERT (Context != NULL); + + SectionIndex = 0; + + Segment = NULL; + for ( + Segment = MachoGetNextSegment64 (Context, NULL); + Segment != NULL; + Segment = MachoGetNextSegment64 (Context, Segment) + ) { + Result = OcOverflowAddU32 ( + SectionIndex, + Segment->NumSections, + &NextSectionIndex + ); + // + // If NextSectionIndex is wrapping around, Index must be contained. + // + if (Result || (Index < NextSectionIndex)) { + Section = &Segment->Sections[Index - SectionIndex]; + if (!InternalSectionIsSane (Context, Section, Segment)) { + return NULL; + } + + return Section; + } + + SectionIndex = NextSectionIndex; + } + + return NULL; +} + +/** + Retrieves a section by its address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address of the section to retrieve. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION_64 * +MachoGetSectionByAddress64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address + ) +{ + MACH_SEGMENT_COMMAND_64 *Segment; + MACH_SECTION_64 *Section; + UINT64 TopOfSegment; + UINT64 TopOfSection; + + ASSERT (Context != NULL); + + for ( + Segment = MachoGetNextSegment64 (Context, NULL); + Segment != NULL; + Segment = MachoGetNextSegment64 (Context, Segment) + ) { + TopOfSegment = (Segment->VirtualAddress + Segment->Size); + if ((Address >= Segment->VirtualAddress) && (Address < TopOfSegment)) { + for ( + Section = MachoGetNextSection64 (Context, Segment, NULL); + Section != NULL; + Section = MachoGetNextSection64 (Context, Segment, Section) + ) { + TopOfSection = (Section->Address + Section->Size); + if ((Address >= Section->Address) && (Address < TopOfSection)) { + return Section; + } + } + } + } + + return NULL; +} + +/** + Retrieves the SYMTAB command. + + @param[in,out] Context Context of the Mach-O. + + @retval NULL NULL is returned on failure. + +**/ +BOOLEAN +InternalRetrieveSymtabs64 ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + UINTN MachoAddress; + MACH_SYMTAB_COMMAND *Symtab; + MACH_DYSYMTAB_COMMAND *DySymtab; + CHAR8 *StringTable; + UINT32 FileSize; + UINT32 OffsetTop; + BOOLEAN Result; + + MACH_NLIST_64 *SymbolTable; + MACH_NLIST_64 *IndirectSymtab; + MACH_RELOCATION_INFO *LocalRelocations; + MACH_RELOCATION_INFO *ExternRelocations; + + ASSERT (Context != NULL); + ASSERT (Context->MachHeader != NULL); + ASSERT (Context->FileSize > 0); + + if (Context->SymbolTable != NULL) { + return TRUE; + } + // + // Retrieve SYMTAB. + // + Symtab = (MACH_SYMTAB_COMMAND *)( + InternalGetNextCommand64 ( + Context, + MACH_LOAD_COMMAND_SYMTAB, + NULL + ) + ); + if ((Symtab == NULL) + || !OC_ALIGNED (Symtab) + || (Symtab->CommandSize != sizeof (*Symtab))) { + return FALSE; + } + + FileSize = Context->FileSize; + + Result = OcOverflowMulAddU32 ( + Symtab->NumSymbols, + sizeof (MACH_NLIST_64), + Symtab->SymbolsOffset, + &OffsetTop + ); + if (Result || (OffsetTop > FileSize)) { + return FALSE; + } + + Result = OcOverflowAddU32 ( + Symtab->StringsOffset, + Symtab->StringsSize, + &OffsetTop + ); + if (Result || (OffsetTop > FileSize)) { + return FALSE; + } + + MachoAddress = (UINTN)Context->MachHeader; + StringTable = (CHAR8 *)(MachoAddress + Symtab->StringsOffset); + + if (Symtab->StringsSize == 0 || StringTable[Symtab->StringsSize - 1] != '\0') { + return FALSE; + } + + SymbolTable = (MACH_NLIST_64 *)(MachoAddress + Symtab->SymbolsOffset); + if (!OC_ALIGNED (SymbolTable)) { + return FALSE; + } + + DySymtab = NULL; + IndirectSymtab = NULL; + LocalRelocations = NULL; + ExternRelocations = NULL; + + if ((Context->MachHeader->Flags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) != 0) { + // + // Retrieve DYSYMTAB. + // + DySymtab = (MACH_DYSYMTAB_COMMAND *)( + InternalGetNextCommand64 ( + Context, + MACH_LOAD_COMMAND_DYSYMTAB, + NULL + ) + ); + if ((DySymtab == NULL) + || !OC_ALIGNED (DySymtab) + || (DySymtab->CommandSize != sizeof (*DySymtab))) { + return FALSE; + } + + Result = OcOverflowAddU32 ( + DySymtab->LocalSymbolsIndex, + DySymtab->NumLocalSymbols, + &OffsetTop + ); + if (Result || (OffsetTop > Symtab->NumSymbols)) { + return FALSE; + } + + Result = OcOverflowAddU32 ( + DySymtab->ExternalSymbolsIndex, + DySymtab->NumExternalSymbols, + &OffsetTop + ); + if (Result || (OffsetTop > Symtab->NumSymbols)) { + return FALSE; + } + + Result = OcOverflowAddU32 ( + DySymtab->UndefinedSymbolsIndex, + DySymtab->NumUndefinedSymbols, + &OffsetTop + ); + if (Result || (OffsetTop > Symtab->NumSymbols)) { + return FALSE; + } + + Result = OcOverflowMulAddU32 ( + DySymtab->NumIndirectSymbols, + sizeof (MACH_NLIST_64), + DySymtab->IndirectSymbolsOffset, + &OffsetTop + ); + if (Result || (OffsetTop > FileSize)) { + return FALSE; + } + + Result = OcOverflowMulAddU32 ( + DySymtab->NumOfLocalRelocations, + sizeof (MACH_RELOCATION_INFO), + DySymtab->LocalRelocationsOffset, + &OffsetTop + ); + if (Result || (OffsetTop > FileSize)) { + return FALSE; + } + + Result = OcOverflowMulAddU32 ( + DySymtab->NumExternalRelocations, + sizeof (MACH_RELOCATION_INFO), + DySymtab->ExternalRelocationsOffset, + &OffsetTop + ); + if (Result || (OffsetTop > FileSize)) { + return FALSE; + } + + IndirectSymtab = (MACH_NLIST_64 *)( + MachoAddress + DySymtab->IndirectSymbolsOffset + ); + LocalRelocations = (MACH_RELOCATION_INFO *)( + MachoAddress + DySymtab->LocalRelocationsOffset + ); + ExternRelocations = (MACH_RELOCATION_INFO *)( + MachoAddress + DySymtab->ExternalRelocationsOffset + ); + if (!OC_ALIGNED (IndirectSymtab) + || !OC_ALIGNED (LocalRelocations) + || !OC_ALIGNED (ExternRelocations)) { + return FALSE; + } + } + + // + // Store the symbol information. + // + Context->Symtab = Symtab; + Context->SymbolTable = SymbolTable; + Context->StringTable = StringTable; + Context->DySymtab = DySymtab; + + Context->IndirectSymbolTable = IndirectSymtab; + Context->LocalRelocations = LocalRelocations; + Context->ExternRelocations = ExternRelocations; + + return TRUE; +} + +UINT32 +MachoGetSymbolTable ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST_64 **SymbolTable, + OUT CONST CHAR8 **StringTable OPTIONAL, + OUT CONST MACH_NLIST_64 **LocalSymbols, OPTIONAL + OUT UINT32 *NumLocalSymbols, OPTIONAL + OUT CONST MACH_NLIST_64 **ExternalSymbols, OPTIONAL + OUT UINT32 *NumExternalSymbols, OPTIONAL + OUT CONST MACH_NLIST_64 **UndefinedSymbols, OPTIONAL + OUT UINT32 *NumUndefinedSymbols OPTIONAL + ) +{ + UINT32 Index; + CONST MACH_NLIST_64 *SymTab; + UINT32 NoLocalSymbols; + UINT32 NoExternalSymbols; + UINT32 NoUndefinedSymbols; + + ASSERT (Context != NULL); + + if (!InternalRetrieveSymtabs64 (Context) + || (Context->Symtab->NumSymbols == 0)) { + return 0; + } + + SymTab = Context->SymbolTable; + + for (Index = 0; Index < Context->Symtab->NumSymbols; ++Index) { + if (!InternalSymbolIsSane (Context, &SymTab[Index])) { + return 0; + } + } + + *SymbolTable = Context->SymbolTable; + + if (StringTable != NULL) { + *StringTable = Context->StringTable; + } + + NoLocalSymbols = 0; + NoExternalSymbols = 0; + NoUndefinedSymbols = 0; + + if (Context->DySymtab != NULL) { + NoLocalSymbols = Context->DySymtab->NumLocalSymbols; + NoExternalSymbols = Context->DySymtab->NumExternalSymbols; + NoUndefinedSymbols = Context->DySymtab->NumUndefinedSymbols; + } + + if (NumLocalSymbols != NULL) { + ASSERT (LocalSymbols != NULL); + *NumLocalSymbols = NoLocalSymbols; + if (NoLocalSymbols != 0) { + *LocalSymbols = &SymTab[Context->DySymtab->LocalSymbolsIndex]; + } + } + + if (NumExternalSymbols != NULL) { + ASSERT (ExternalSymbols != NULL); + *NumExternalSymbols = NoExternalSymbols; + if (NoExternalSymbols != 0) { + *ExternalSymbols = &SymTab[Context->DySymtab->ExternalSymbolsIndex]; + } + } + + if (NumUndefinedSymbols != NULL) { + ASSERT (UndefinedSymbols != NULL); + *NumUndefinedSymbols = NoUndefinedSymbols; + if (NoUndefinedSymbols != 0) { + *UndefinedSymbols = &SymTab[Context->DySymtab->UndefinedSymbolsIndex]; + } + } + + return Context->Symtab->NumSymbols; +} + +UINT32 +MachoGetIndirectSymbolTable ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST_64 **SymbolTable + ) +{ + UINT32 Index; + + if (!InternalRetrieveSymtabs64 (Context)) { + return 0; + } + + for (Index = 0; Index < Context->DySymtab->NumIndirectSymbols; ++Index) { + if ( + !InternalSymbolIsSane (Context, &Context->IndirectSymbolTable[Index]) + ) { + return 0; + } + } + + *SymbolTable = Context->IndirectSymbolTable; + + return Context->DySymtab->NumIndirectSymbols; +} + +/** + Returns a pointer to the Mach-O file at the specified virtual address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Virtual address to look up. + @param[out] MaxSize Maximum data safely available from FileOffset. + If NULL is returned, the output is undefined. + +**/ +VOID * +MachoGetFilePointerByAddress64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT UINT32 *MaxSize OPTIONAL + ) +{ + CONST MACH_SEGMENT_COMMAND_64 *Segment; + UINT64 Offset; + + Segment = NULL; + while ((Segment = MachoGetNextSegment64 (Context, Segment)) != NULL) { + if ((Address >= Segment->VirtualAddress) + && (Address < Segment->VirtualAddress + Segment->Size)) { + Offset = (Address - Segment->VirtualAddress); + + if (MaxSize != NULL) { + *MaxSize = (UINT32)(Segment->Size - Offset); + } + + Offset += Segment->FileOffset; + return (VOID *)((UINTN)Context->MachHeader + (UINTN)Offset); + } + } + + return NULL; +} + +/** + Strip superfluous Load Commands from the Mach-O header. This includes the + Code Signature Load Command which must be removed for the binary has been + modified by the prelinking routines. + + @param[in,out] MachHeader Mach-O header to strip the Load Commands from. + +**/ +STATIC +VOID +InternalStripLoadCommands64 ( + IN OUT MACH_HEADER_64 *MachHeader + ) +{ + STATIC CONST MACH_LOAD_COMMAND_TYPE LoadCommandsToStrip[] = { + MACH_LOAD_COMMAND_CODE_SIGNATURE, + MACH_LOAD_COMMAND_DYLD_INFO, + MACH_LOAD_COMMAND_DYLD_INFO_ONLY, + MACH_LOAD_COMMAND_FUNCTION_STARTS, + MACH_LOAD_COMMAND_DATA_IN_CODE, + MACH_LOAD_COMMAND_DYLIB_CODE_SIGN_DRS + }; + + UINT32 Index; + UINT32 Index2; + MACH_LOAD_COMMAND *LoadCommand; + UINT32 SizeOfLeftCommands; + UINT32 OriginalCommandSize; + // + // Delete the Code Signature Load Command if existent as we modified the + // binary, as well as linker metadata not needed for runtime operation. + // + LoadCommand = MachHeader->Commands; + SizeOfLeftCommands = MachHeader->CommandsSize; + OriginalCommandSize = SizeOfLeftCommands; + + for (Index = 0; Index < MachHeader->NumCommands; ++Index) { + // + // Assertion: LC_UNIXTHREAD and LC_MAIN are technically stripped in KXLD, + // but they are not supposed to be present in the first place. + // + if ((LoadCommand->CommandType == MACH_LOAD_COMMAND_UNIX_THREAD) + || (LoadCommand->CommandType == MACH_LOAD_COMMAND_MAIN)) { + DEBUG ((DEBUG_WARN, "UNIX Thread and Main LCs are unsupported.\n")); + } + + SizeOfLeftCommands -= LoadCommand->CommandSize; + + for (Index2 = 0; Index2 < ARRAY_SIZE (LoadCommandsToStrip); ++Index2) { + if (LoadCommand->CommandType == LoadCommandsToStrip[Index2]) { + if (Index != (MachHeader->NumCommands - 1)) { + // + // If the current Load Command is not the last one, relocate the + // subsequent ones. + // + CopyMem ( + LoadCommand, + NEXT_MACH_LOAD_COMMAND (LoadCommand), + SizeOfLeftCommands + ); + } + + --MachHeader->NumCommands; + MachHeader->CommandsSize -= LoadCommand->CommandSize; + + break; + } + } + + LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand); + } + + ZeroMem (LoadCommand, OriginalCommandSize - MachHeader->CommandsSize); +} + +UINT32 +MachoExpandImage64 ( + IN OC_MACHO_CONTEXT *Context, + OUT UINT8 *Destination, + IN UINT32 DestinationSize, + IN BOOLEAN Strip + ) +{ + MACH_HEADER_64 *Header; + UINT8 *Source; + UINT32 HeaderSize; + UINT64 CopyFileOffset; + UINT64 CopyFileSize; + UINT64 CopyVmSize; + UINT32 CurrentDelta; + UINT32 OriginalDelta; + UINT64 CurrentSize; + MACH_SEGMENT_COMMAND_64 *Segment; + MACH_SEGMENT_COMMAND_64 *FirstSegment; + MACH_SEGMENT_COMMAND_64 *DstSegment; + MACH_SYMTAB_COMMAND *Symtab; + MACH_DYSYMTAB_COMMAND *DySymtab; + UINT32 Index; + + ASSERT (Context != NULL); + ASSERT (Context->FileSize != 0); + + // + // Header is valid, copy it first. + // + Header = MachoGetMachHeader64 (Context); + Source = (UINT8 *) Header; + HeaderSize = sizeof (*Header) + Header->CommandsSize; + if (HeaderSize > DestinationSize) { + return 0; + } + CopyMem (Destination, Header, HeaderSize); + + CurrentDelta = 0; + FirstSegment = NULL; + CurrentSize = 0; + for ( + Segment = MachoGetNextSegment64 (Context, NULL); + Segment != NULL; + Segment = MachoGetNextSegment64 (Context, Segment) + ) { + // + // Align delta by x86 page size, this is what our lib expects. + // + OriginalDelta = CurrentDelta; + CurrentDelta = MACHO_ALIGN (CurrentDelta); + if (Segment->FileSize > Segment->Size) { + return 0; + } + + if (FirstSegment == NULL) { + FirstSegment = Segment; + } + + // + // Do not overwrite header. + // + CopyFileOffset = Segment->FileOffset; + CopyFileSize = Segment->FileSize; + CopyVmSize = Segment->Size; + if (CopyFileOffset <= HeaderSize) { + CopyFileOffset = HeaderSize; + CopyFileSize = Segment->FileSize - CopyFileOffset; + CopyVmSize = Segment->Size - CopyFileOffset; + if (CopyFileSize > Segment->FileSize || CopyVmSize > Segment->Size) { + // + // Header must fit in 1 segment. + // + return 0; + } + } + // + // Ensure that it still fits. In legit files segments are ordered. + // We do not care for other (the file will be truncated). + // + if (OcOverflowTriAddU64 (CopyFileOffset, CurrentDelta, CopyVmSize, &CurrentSize) + || CurrentSize > DestinationSize) { + return 0; + } + + // + // Copy and zero fill file data. We can do this because only last sections can have 0 file size. + // + ZeroMem (&Destination[CopyFileOffset + OriginalDelta], CurrentDelta - OriginalDelta); + CopyMem (&Destination[CopyFileOffset + CurrentDelta], &Source[CopyFileOffset], CopyFileSize); + ZeroMem (&Destination[CopyFileOffset + CurrentDelta + CopyFileSize], CopyVmSize - CopyFileSize); + // + // Refresh destination segment size and offsets. + // + DstSegment = (MACH_SEGMENT_COMMAND_64 *) ((UINT8 *) Segment - Source + Destination); + DstSegment->FileOffset += CurrentDelta; + DstSegment->FileSize = DstSegment->Size; + + if (DstSegment->VirtualAddress - DstSegment->FileOffset != FirstSegment->VirtualAddress) { + return 0; + } + + // + // We need to update fields in SYMTAB and DYSYMTAB. Tables have to be present before 0 FileSize + // sections as they have data, so we update them before parsing sections. + // Note: There is an assumption they are in __LINKEDIT segment, another option is to check addresses. + // + if (AsciiStrnCmp (DstSegment->SegmentName, "__LINKEDIT", ARRAY_SIZE (DstSegment->SegmentName)) == 0) { + Symtab = (MACH_SYMTAB_COMMAND *)( + InternalGetNextCommand64 ( + Context, + MACH_LOAD_COMMAND_SYMTAB, + NULL + ) + ); + + if (Symtab != NULL) { + Symtab = (MACH_SYMTAB_COMMAND *) ((UINT8 *) Symtab - Source + Destination); + if (Symtab->SymbolsOffset != 0) { + Symtab->SymbolsOffset += CurrentDelta; + } + if (Symtab->StringsOffset != 0) { + Symtab->StringsOffset += CurrentDelta; + } + } + + DySymtab = (MACH_DYSYMTAB_COMMAND *)( + InternalGetNextCommand64 ( + Context, + MACH_LOAD_COMMAND_DYSYMTAB, + NULL + ) + ); + + if (DySymtab != NULL) { + DySymtab = (MACH_DYSYMTAB_COMMAND *) ((UINT8 *) DySymtab - Source + Destination); + if (DySymtab->TableOfContentsNumEntries != 0) { + DySymtab->TableOfContentsNumEntries += CurrentDelta; + } + if (DySymtab->ModuleTableFileOffset != 0) { + DySymtab->ModuleTableFileOffset += CurrentDelta; + } + if (DySymtab->ReferencedSymbolTableFileOffset != 0) { + DySymtab->ReferencedSymbolTableFileOffset += CurrentDelta; + } + if (DySymtab->IndirectSymbolsOffset != 0) { + DySymtab->IndirectSymbolsOffset += CurrentDelta; + } + if (DySymtab->ExternalRelocationsOffset != 0) { + DySymtab->ExternalRelocationsOffset += CurrentDelta; + } + if (DySymtab->LocalRelocationsOffset != 0) { + DySymtab->LocalRelocationsOffset += CurrentDelta; + } + } + } + // + // These may well wrap around with invalid data. + // But we do not care, as we do not access these fields ourselves, + // and later on the section values are checked by MachoLib. + // Note: There is an assumption that 'CopyFileOffset + CurrentDelta' is aligned. + // + OriginalDelta = CurrentDelta; + CopyFileOffset = Segment->FileOffset; + for (Index = 0; Index < DstSegment->NumSections; ++Index) { + if (DstSegment->Sections[Index].Offset == 0) { + DstSegment->Sections[Index].Offset = (UINT32) CopyFileOffset + CurrentDelta; + CurrentDelta += (UINT32) DstSegment->Sections[Index].Size; + } else { + DstSegment->Sections[Index].Offset += CurrentDelta; + CopyFileOffset = DstSegment->Sections[Index].Offset + DstSegment->Sections[Index].Size; + } + } + + CurrentDelta = OriginalDelta + (UINT32)(Segment->Size - Segment->FileSize); + } + + if (Strip) { + InternalStripLoadCommands64 ((MACH_HEADER_64 *) Destination); + } + + return (UINT32) CurrentSize; +} + +UINTN +MachoRuntimeGetEntryAddress ( + IN VOID *Image + ) +{ + MACH_HEADER_ANY *Header; + BOOLEAN Is64Bit; + UINT32 NumCmds; + MACH_LOAD_COMMAND *Cmd; + UINTN Index; + MACH_THREAD_COMMAND *ThreadCmd; + MACH_X86_THREAD_STATE *ThreadState; + UINTN Address; + + Address = 0; + Header = (MACH_HEADER_ANY *) Image; + + if (Header->Signature == MACH_HEADER_SIGNATURE) { + // + // 32-bit header. + // + Is64Bit = FALSE; + NumCmds = Header->Header32.NumCommands; + Cmd = &Header->Header32.Commands[0]; + } else if (Header->Signature == MACH_HEADER_64_SIGNATURE) { + // + // 64-bit header. + // + Is64Bit = TRUE; + NumCmds = Header->Header64.NumCommands; + Cmd = &Header->Header64.Commands[0]; + } else { + // + // Invalid Mach-O image. + // + return Address; + } + + // + // Iterate over load commands. + // + for (Index = 0; Index < NumCmds; ++Index) { + if (Cmd->CommandType == MACH_LOAD_COMMAND_UNIX_THREAD) { + ThreadCmd = (MACH_THREAD_COMMAND *) Cmd; + ThreadState = (MACH_X86_THREAD_STATE *) &ThreadCmd->ThreadState[0]; + Address = Is64Bit ? ThreadState->State64.rip : ThreadState->State32.eip; + break; + } + + Cmd = NEXT_MACH_LOAD_COMMAND (Cmd); + } + + return Address; +} diff --git a/Library/MachoLib/MachoLib.inf b/Library/MachoLib/MachoLib.inf new file mode 100644 index 000000000..049242caa --- /dev/null +++ b/Library/MachoLib/MachoLib.inf @@ -0,0 +1,37 @@ +## @file +# Copyright (C) 2016 - 2017, Download-Fritz. All rights reserved.
+# This program and the accompanying materials are licensed and made available +# under the terms and conditions of the BSD License which accompanies this +# distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + BASE_NAME = OcMachoLib + LIBRARY_CLASS = OcMachoLib + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + INF_VERSION = 0x00010005 + +[Packages] + MdePkg/MdePkg.dec + EfiPkg/EfiPkg.dec + OcSupportPkg/OcSupportPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + OcGuardLib + +[Sources] + CxxSymbols.c + Header.c + OcMachoLibInternal.h + Relocations.c + Symbols.c diff --git a/Library/MachoLib/MachoLibInternal.h b/Library/MachoLib/MachoLibInternal.h new file mode 100644 index 000000000..2f11ac7c5 --- /dev/null +++ b/Library/MachoLib/MachoLibInternal.h @@ -0,0 +1,77 @@ +/** + Private data of OcMachoLib. + +Copyright (C) 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef OC_MACHO_LIB_INTERNAL_H_ +#define OC_MACHO_LIB_INTERNAL_H_ + +#include + +#include + +/** + Retrieves the SYMTAB command. + + @param[in] Context Context of the Mach-O. + + @retval NULL NULL is returned on failure. +**/ +BOOLEAN +InternalRetrieveSymtabs64 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Retrieves an extern Relocation by the address it targets. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address The address to search for. + + @retval NULL NULL is returned on failure. +**/ +MACH_RELOCATION_INFO * +InternalGetExternRelocationByOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address + ); + +/** + Retrieves a Relocation by the address it targets. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address The address to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_RELOCATION_INFO * +InternalGetLocalRelocationByOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address + ); + +/** + Check symbol validity. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol from some table. + + @retval TRUE on success. +**/ +BOOLEAN +InternalSymbolIsSane ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ); + +#endif // OC_MACHO_LIB_INTERNAL_H_ diff --git a/Library/MachoLib/Relocations.c b/Library/MachoLib/Relocations.c new file mode 100644 index 000000000..1b71f3a82 --- /dev/null +++ b/Library/MachoLib/Relocations.c @@ -0,0 +1,158 @@ +/** @file + Provides services for Relocations. + +Copyright (c) 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include + +#include +#include + +#include "OcMachoLibInternal.h" + +/** + Returns whether the Relocation's type indicates a Pair for the Intel 64 + platform. + + @param[in] Type The Relocation's type to verify. + +**/ +BOOLEAN +MachoRelocationIsPairIntel64 ( + IN UINT8 Type + ) +{ + return (Type == MachX8664RelocSubtractor); +} + +/** + Returns whether the Relocation's type matches a Pair's for the Intel 64 + platform. + + @param[in] Type The Relocation's type to verify. + +**/ +BOOLEAN +MachoIsRelocationPairTypeIntel64 ( + IN UINT8 Type + ) +{ + return (Type == MachX8664RelocUnsigned); +} + +/** + Returns whether the Relocation shall be preserved for the Intel 64 platform. + + @param[in] Type The Relocation's type to verify. + +**/ +BOOLEAN +MachoPreserveRelocationIntel64 ( + IN UINT8 Type + ) +{ + return (Type == MachX8664RelocUnsigned); +} + +/** + Retrieves an extern Relocation by the address it targets. + + @param[in] Address The address to search for. + + @retval NULL NULL is returned on failure. + +**/ +STATIC +MACH_RELOCATION_INFO * +InternalLookupRelocationByOffset ( + IN UINT64 Address, + IN UINT32 NumRelocs, + IN MACH_RELOCATION_INFO *Relocs + ) +{ + UINT32 Index; + MACH_RELOCATION_INFO *Relocation; + + for (Index = 0; Index < NumRelocs; ++Index) { + Relocation = &Relocs[Index]; + // + // A section-based relocation entry can be skipped for absolute symbols. + // + if ((Relocation->Extern == 0) + && (Relocation->SymbolNumber == MACH_RELOC_ABSOLUTE)) { + continue; + } + + if ((UINT64)Relocation->Address == Address) { + return Relocation; + } + // + // Relocation Pairs can be skipped. + // Assumption: Intel X64. Currently verified by the Context + // initialization. + // + if (MachoRelocationIsPairIntel64 ((UINT8)Relocation->Type)) { + if (Index == (MAX_UINT32 - 1)) { + break; + } + ++Index; + } + } + + return NULL; +} + +/** + Retrieves an extern Relocation by the address it targets. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address The address to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_RELOCATION_INFO * +InternalGetExternRelocationByOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address + ) +{ + return InternalLookupRelocationByOffset ( + Address, + Context->DySymtab->NumExternalRelocations, + Context->ExternRelocations + ); +} + +/** + Retrieves an extern Relocation by the address it targets. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address The address to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_RELOCATION_INFO * +InternalGetLocalRelocationByOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address + ) +{ + return InternalLookupRelocationByOffset ( + Address, + Context->DySymtab->NumOfLocalRelocations, + Context->LocalRelocations + ); +} diff --git a/Library/MachoLib/Symbols.c b/Library/MachoLib/Symbols.c new file mode 100644 index 000000000..c3aa4c28a --- /dev/null +++ b/Library/MachoLib/Symbols.c @@ -0,0 +1,640 @@ +/** @file + Provides services for symbols. + +Copyright (c) 2018, Download-Fritz. All rights reserved.
+This program and the accompanying materials are licensed and made available +under the terms and conditions of the BSD License which accompanies this +distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include + +#include +#include +#include +#include + +#include "OcMachoLibInternal.h" + +BOOLEAN +InternalSymbolIsSane ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ) +{ + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + + ASSERT (Context->SymbolTable != NULL); + ASSERT (Context->Symtab->NumSymbols > 0); + + ASSERT (((Symbol >= &Context->SymbolTable[0]) + && (Symbol < &Context->SymbolTable[Context->Symtab->NumSymbols])) + || ((Context->DySymtab != NULL) + && (Symbol >= &Context->IndirectSymbolTable[0]) + && (Symbol < &Context->IndirectSymbolTable[Context->DySymtab->NumIndirectSymbols]))); + // + // Symbol->Section is implicitly verified by MachoGetSectionByIndex64() when + // passed to it. + // + if (Symbol->UnifiedName.StringIndex >= Context->Symtab->StringsSize) { + return FALSE; + } + + return TRUE; +} + +/** + Returns whether the symbol's value is a valid address within the Mach-O + referenced to by Context. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to verify the value of. + +**/ +BOOLEAN +MachoIsSymbolValueInRange64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ) +{ + CONST MACH_SEGMENT_COMMAND_64 *Segment; + + if (MachoSymbolIsLocalDefined (Context, Symbol)) { + for ( + Segment = MachoGetNextSegment64 (Context, NULL); + Segment != NULL; + Segment = MachoGetNextSegment64 (Context, Segment) + ) { + if ((Symbol->Value >= Segment->VirtualAddress) + && (Symbol->Value < (Segment->VirtualAddress + Segment->Size))) { + return TRUE; + } + } + + return FALSE; + } + + return TRUE; +} + +/** + Returns whether Symbol describes a section type. + + @param[in] Symbol Symbol to evaluate. + +**/ +STATIC +BOOLEAN +InternalSymbolIsSectionType ( + IN CONST MACH_NLIST_64 *Symbol + ) +{ + ASSERT (Symbol != NULL); + + if ((Symbol->Type & MACH_N_TYPE_STAB) != 0) { + switch (Symbol->Type) { + // + // Labeled as MACH_N_sect in stab.h + // + case MACH_N_FUN: + case MACH_N_STSYM: + case MACH_N_LCSYM: + case MACH_N_BNSYM: + case MACH_N_SLINE: + case MACH_N_ENSYM: + case MACH_N_SO: + case MACH_N_SOL: + case MACH_N_ENTRY: + case MACH_N_ECOMM: + case MACH_N_ECOML: + // + // These are labeled as NO_SECT in stab.h, but they are actually + // section-based on OS X. We must mark them as such so they get + // relocated. + // + case MACH_N_RBRAC: + case MACH_N_LBRAC: + { + return TRUE; + } + + default: + { + break; + } + } + } else if ((Symbol->Type & MACH_N_TYPE_TYPE) == MACH_N_TYPE_SECT) { + return TRUE; + } + + return FALSE; +} + +/** + Returns whether Symbol describes a section. + + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsSection ( + IN CONST MACH_NLIST_64 *Symbol + ) +{ + ASSERT (Symbol != NULL); + return (InternalSymbolIsSectionType (Symbol) && (Symbol->Section != NO_SECT)); +} + +/** + Returns whether Symbol is defined. + + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsDefined ( + IN CONST MACH_NLIST_64 *Symbol + ) +{ + ASSERT (Symbol != NULL); + + return (((Symbol->Type & MACH_N_TYPE_STAB) == 0) + && (((Symbol->Type & MACH_N_TYPE_TYPE) == MACH_N_TYPE_ABS) + || InternalSymbolIsSectionType (Symbol))); +} + +/** + Returns whether Symbol is defined locally. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsLocalDefined ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ) +{ + CONST MACH_DYSYMTAB_COMMAND *DySymtab; + CONST MACH_NLIST_64 *UndefinedSymbols; + CONST MACH_NLIST_64 *UndefinedSymbolsTop; + CONST MACH_NLIST_64 *IndirectSymbols; + CONST MACH_NLIST_64 *IndirectSymbolsTop; + + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + + DySymtab = Context->DySymtab; + ASSERT (Context->SymbolTable != NULL); + + if ((DySymtab == NULL) || (DySymtab->NumUndefinedSymbols == 0)) { + return TRUE; + } + // + // The symbol must have been declared locally prior to solving. As there is + // no information on whether the symbol has been solved explicitely, check + // its storage location for Undefined or Indirect. + // + UndefinedSymbols = &Context->SymbolTable[DySymtab->UndefinedSymbolsIndex]; + UndefinedSymbolsTop = &UndefinedSymbols[DySymtab->NumUndefinedSymbols]; + + if ((Symbol >= UndefinedSymbols) && (Symbol < UndefinedSymbolsTop)) { + return FALSE; + } + + IndirectSymbols = Context->IndirectSymbolTable; + IndirectSymbolsTop = &IndirectSymbols[DySymtab->NumIndirectSymbols]; + + if ((Symbol >= IndirectSymbols) && (Symbol < IndirectSymbolsTop)) { + return FALSE; + } + + return MachoSymbolIsDefined (Symbol); +} + +/** + Retrieves a symbol by its index. + + @param[in] Context Context of the Mach-O. + @param[in] Index Index of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +MACH_NLIST_64 * +MachoGetSymbolByIndex64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ) +{ + MACH_NLIST_64 *Symbol; + + ASSERT (Context != NULL); + + if (!InternalRetrieveSymtabs64 (Context)) { + return NULL; + } + + ASSERT (Context->SymbolTable != NULL); + + if (Index < Context->Symtab->NumSymbols) { + Symbol = &Context->SymbolTable[Index]; + if (InternalSymbolIsSane (Context, Symbol)) { + return Symbol; + } + } + + return NULL; +} + +/** + Retrieves Symbol's name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the name of. + + @retval NULL NULL is returned on failure. + +**/ +CONST CHAR8 * +MachoGetSymbolName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ) +{ + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + + ASSERT (Context->SymbolTable != NULL); + ASSERT (Context->Symtab->StringsSize > Symbol->UnifiedName.StringIndex); + + return (Context->StringTable + Symbol->UnifiedName.StringIndex); +} + +/** + Retrieves Symbol's name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Indirect symbol to retrieve the name of. + + @retval NULL NULL is returned on failure. + +**/ +CONST CHAR8 * +MachoGetIndirectSymbolName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol + ) +{ + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + + ASSERT (Context->SymbolTable != NULL); + + if ((Symbol->Type & MACH_N_TYPE_STAB) != 0 + || (Symbol->Type & MACH_N_TYPE_TYPE) != MACH_N_TYPE_INDR) { + return NULL; + } + + if (Context->Symtab->StringsSize <= Symbol->Value) { + return NULL; + } + + return (Context->StringTable + Symbol->Value); +} + +/** + Retrieves a symbol by its value. + + @param[in] Context Context of the Mach-O. + @param[in] Value Value of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +STATIC +MACH_NLIST_64 * +InternalGetSymbolByValue ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Value + ) +{ + UINT32 Index; + + ASSERT (Context->SymbolTable != NULL); + ASSERT (Context->Symtab != NULL); + + for (Index = 0; Index < Context->Symtab->NumSymbols; ++Index) { + if (Context->SymbolTable[Index].Value == Value) { + return &Context->SymbolTable[Index]; + } + } + + return NULL; +} + +/** + Retrieves the symbol referenced by the extern Relocation targeting Address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address to search for. + @param[out] Symbol Buffer to output the symbol referenced by the + Relocation into. The output is undefined when FALSE + is returned. May be NULL. + + @returns Whether the Relocation exists. + +**/ +BOOLEAN +MachoGetSymbolByExternRelocationOffset64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT MACH_NLIST_64 **Symbol + ) +{ + CONST MACH_RELOCATION_INFO *Relocation; + + ASSERT (Context != NULL); + + Relocation = InternalGetExternRelocationByOffset (Context, Address); + if (Relocation != NULL) { + *Symbol = MachoGetSymbolByIndex64 (Context, Relocation->SymbolNumber); + return TRUE; + } + + return FALSE; +} + +/** + Retrieves the symbol referenced by the Relocation targeting Address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address to search for. + @param[out] Symbol Buffer to output the symbol referenced by the + Relocation into. The output is undefined when FALSE + is returned. May be NULL. + + @returns Whether the Relocation exists. + +**/ +BOOLEAN +MachoGetSymbolByRelocationOffset64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT MACH_NLIST_64 **Symbol + ) +{ + BOOLEAN Result; + CONST MACH_RELOCATION_INFO *Relocation; + CONST UINT64 *Data; + MACH_NLIST_64 *Sym; + + ASSERT (Context != NULL); + + Result = MachoGetSymbolByExternRelocationOffset64 (Context, Address, Symbol); + if (Result) { + return TRUE; + } + + Relocation = InternalGetLocalRelocationByOffset (Context, Address); + if (Relocation != NULL) { + Sym = NULL; + + Data = ((UINT64 *)((UINTN)Context->MachHeader + Address)); + if (((Address + sizeof (UINT64)) <= Context->FileSize) + && OC_ALIGNED (Data)) { + // FIXME: Only C++ symbols. + Sym = InternalGetSymbolByValue (Context, *Data); + if ((Sym != NULL) && !InternalSymbolIsSane (Context, Sym)) { + Sym = NULL; + } + } + + *Symbol = Sym; + return TRUE; + } + + return FALSE; +} + +/** + Retrieves a symbol by its name. + + @param[in] Context Context of the Mach-O. + @param[in] SymbolTable Symbol Table of the Mach-O. + @param[in] NumberOfSymbols Number of symbols in SymbolTable. + @param[in] Name Name of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +STATIC +MACH_NLIST_64 * +InternalGetLocalDefinedSymbolByNameWorker ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_NLIST_64 *SymbolTable, + IN UINT32 NumberOfSymbols, + IN CONST CHAR8 *Name + ) +{ + UINT32 Index; + CONST CHAR8 *TmpName; + + ASSERT (SymbolTable != NULL); + ASSERT (Name != NULL); + + for (Index = 0; Index < NumberOfSymbols; ++Index) { + if (!InternalSymbolIsSane (Context, &SymbolTable[Index])) { + break; + } + + if (!MachoSymbolIsDefined (&SymbolTable[Index])) { + continue; + } + + TmpName = MachoGetSymbolName64 (Context, &SymbolTable[Index]); + if (AsciiStrCmp (Name, TmpName) == 0) { + return &SymbolTable[Index]; + } + } + + return NULL; +} + +/** + Retrieves a locally defined symbol by its name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Name Name of the symbol to locate. + +**/ +MACH_NLIST_64 * +MachoGetLocalDefinedSymbolByName ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Name + ) +{ + MACH_NLIST_64 *SymbolTable; + CONST MACH_DYSYMTAB_COMMAND *DySymtab; + MACH_NLIST_64 *Symbol; + + ASSERT (Context != NULL); + ASSERT (Name != NULL); + + if (!InternalRetrieveSymtabs64 (Context)) { + return NULL; + } + + SymbolTable = Context->SymbolTable; + ASSERT (SymbolTable != NULL); + + DySymtab = Context->DySymtab; + + if (DySymtab != NULL) { + Symbol = InternalGetLocalDefinedSymbolByNameWorker ( + Context, + &SymbolTable[DySymtab->LocalSymbolsIndex], + DySymtab->NumLocalSymbols, + Name + ); + if (Symbol == NULL) { + Symbol = InternalGetLocalDefinedSymbolByNameWorker ( + Context, + &SymbolTable[DySymtab->ExternalSymbolsIndex], + DySymtab->NumExternalSymbols, + Name + ); + } + } else { + ASSERT (Context->Symtab != NULL); + Symbol = InternalGetLocalDefinedSymbolByNameWorker ( + Context, + SymbolTable, + Context->Symtab->NumSymbols, + Name + ); + } + + return Symbol; +} + +/** + Relocate Symbol to be against LinkAddress. + + @param[in,out] Context Context of the Mach-O. + @param[in] LinkAddress The address to be linked against. + @param[in,out] Symbol The symbol to be relocated. + + @returns Whether the operation has been completed successfully. + +**/ +BOOLEAN +MachoRelocateSymbol64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 LinkAddress, + IN OUT MACH_NLIST_64 *Symbol + ) +{ + CONST MACH_SECTION_64 *Section; + UINT64 Value; + BOOLEAN Result; + + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + + // + // Symbols are relocated when they describe sections. + // + if (MachoSymbolIsSection (Symbol)) { + Section = MachoGetSectionByIndex64 (Context, (Symbol->Section - 1)); + if (Section == NULL) { + return FALSE; + } + + Value = ALIGN_VALUE ( + (Section->Address + LinkAddress), + (1ULL << Section->Alignment) + ); + Value -= Section->Address; + // + // The overflow arithmetic functions are not used as an overflow within the + // ALIGN_VALUE addition and a subsequent "underflow" of the section address + // subtraction is valid, hence just verify whether the final result + // overflew. + // + if (Value < LinkAddress) { + return FALSE; + } + + Result = OcOverflowAddU64 (Symbol->Value, Value, &Value); + if (Result) { + return FALSE; + } + + Symbol->Value = Value; + } + + return TRUE; +} + +/** + Retrieves the Mach-O file offset of the address pointed to by Symbol. + + @param[in,ouz] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the offset of. + @param[out] FileOffset Pointer the file offset is returned into. + If FALSE is returned, the output is undefined. + @param[out] MaxSize Maximum data safely available from FileOffset. + + @retval 0 0 is returned on failure. + +**/ +BOOLEAN +MachoSymbolGetFileOffset64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ) +{ + UINT64 Offset; + MACH_SECTION_64 *Section; + + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + ASSERT (FileOffset != NULL); + + if (Symbol->Section == NO_SECT) { + return FALSE; + } + + Section = MachoGetSectionByIndex64 ( + Context, + (Symbol->Section - 1) + ); + if ((Section == NULL) || (Symbol->Value < Section->Address)) { + return FALSE; + } + + Offset = (Symbol->Value - Section->Address); + if (Offset > Section->Size) { + return FALSE; + } + + *FileOffset = (Section->Offset + (UINT32)Offset); + + if (MaxSize != NULL) { + *MaxSize = (UINT32)(Section->Size - Offset); + } + + return TRUE; +} diff --git a/Library/OcGuardLib/BitOverflow.c b/Library/OcGuardLib/BitOverflow.c new file mode 100644 index 000000000..6eb9d2bb6 --- /dev/null +++ b/Library/OcGuardLib/BitOverflow.c @@ -0,0 +1,289 @@ +/** @file + +OcGuardLib + +Copyright (c) 2018, vit9696 + +All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +// +// The implementations provided try not to be obviously slow, but primarily +// target C99 compliance rather than performance. +// + +BOOLEAN +(OcOverflowAddU32) ( + UINT32 A, + UINT32 B, + UINT32 *Result + ) +{ + UINT64 Temp; + + // + // I believe casting will be faster on X86 at least. + // + Temp = (UINT64) A + B; + *Result = (UINT32) Temp; + if (Temp <= MAX_UINT32) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowSubU32) ( + UINT32 A, + UINT32 B, + UINT32 *Result + ) +{ + *Result = A - B; + if (B <= A) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowMulU32) ( + UINT32 A, + UINT32 B, + UINT32 *Result + ) +{ + UINT64 Temp; + + Temp = (UINT64) A * B; + *Result = (UINT32) Temp; + if (Temp <= MAX_UINT32) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowAddS32) ( + INT32 A, + INT32 B, + INT32 *Result + ) +{ + INT64 Temp; + + Temp = (INT64) A + B; + *Result = (INT32) Temp; + if (Temp >= MIN_INT32 && Temp <= MAX_INT32) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowSubS32) ( + INT32 A, + INT32 B, + INT32 *Result + ) +{ + INT64 Temp; + + Temp = (INT64) A - B; + *Result = (INT32) Temp; + if (Temp >= MIN_INT32 && Temp <= MAX_INT32) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowMulS32) ( + INT32 A, + INT32 B, + INT32 *Result + ) +{ + INT64 Temp; + + Temp = (INT64) A * B; + *Result = (INT32) Temp; + if (Temp >= MIN_INT32 && Temp <= MAX_INT32) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowAddU64) ( + UINT64 A, + UINT64 B, + UINT64 *Result + ) +{ + *Result = A + B; + if (MAX_UINT64 - A >= B) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowSubU64) ( + UINT64 A, + UINT64 B, + UINT64 *Result + ) +{ + *Result = A - B; + if (B <= A) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +(OcOverflowMulU64) ( + UINT64 A, + UINT64 B, + UINT64 *Result + ) +{ + UINT64 AHi; + UINT64 ALo; + UINT64 BHi; + UINT64 BLo; + UINT64 LoBits; + UINT64 HiBits1; + UINT64 HiBits2; + BOOLEAN Overflow; + + // + // Based on the 2nd option written by Charphacy, believed to be the fastest portable on x86. + // See: https://stackoverflow.com/a/26320664 + // Implements overflow checking by a series of up to 3 multiplications. + // + + AHi = A >> 32ULL; + ALo = A & MAX_UINT32; + BHi = B >> 32ULL; + BLo = B & MAX_UINT32; + + LoBits = ALo * BLo; + if (AHi == 0 && BHi == 0) { + *Result = LoBits; + return FALSE; + } + + Overflow = AHi > 0 && BHi > 0; + HiBits1 = ALo * BHi; + HiBits2 = AHi * BLo; + + *Result = LoBits + ((HiBits1 + HiBits2) << 32ULL); + return Overflow || *Result < LoBits || (HiBits1 >> 32ULL) != 0 || (HiBits2 >> 32ULL) != 0; +} + +BOOLEAN +(OcOverflowAddS64) ( + INT64 A, + INT64 B, + INT64 *Result + ) +{ + if ((B <= 0 || A <= MAX_INT64 - B) && (B >= 0 || A >= MIN_INT64 - B)) { + *Result = A + B; + return FALSE; + } + + // + // Assign some defined value to *Result. + // + *Result = 0; + return TRUE; +} + +BOOLEAN +(OcOverflowSubS64) ( + INT64 A, + INT64 B, + INT64 *Result + ) +{ + if ((B >= 0 || A <= MAX_INT64 + B) && (B <= 0 || A >= MIN_INT64 + B)) { + *Result = A - B; + return FALSE; + } + + // + // Assign some defined value to *Result. + // + *Result = 0; + return TRUE; +} + +BOOLEAN +(OcOverflowMulS64) ( + INT64 A, + INT64 B, + INT64 *Result + ) +{ + UINT64 AU; + UINT64 BU; + UINT64 ResultU; + + // + // It hurts to implement it without unsigned multiplication, maybe rewrite it one day. + // The idea taken from BaseSafeIntLib. + // + +#define OC_ABS_64(X) (((X) < 0) ? (((UINT64) (-((X) + 1))) + 1) : (UINT64) (X)) + + AU = OC_ABS_64 (A); + BU = OC_ABS_64 (B); + + if (OcOverflowMulU64 (AU, BU, &ResultU)) { + *Result = 0; + return TRUE; + } + + // + // Split into positive and negative results and check just one range. + // + if ((A < 0) == (B < 0)) { + if (ResultU <= MAX_INT64) { + *Result = (INT64) ResultU; + return FALSE; + } + } else { + if (ResultU < OC_ABS_64 (MIN_INT64)) { + *Result = -((INT64) ResultU); + return FALSE; + } else if (ResultU == OC_ABS_64 (MIN_INT64)) { + *Result = MIN_INT64; + return FALSE; + } + } + + *Result = 0; + return TRUE; +} diff --git a/Library/OcGuardLib/Canary.c b/Library/OcGuardLib/Canary.c new file mode 100644 index 000000000..103466f29 --- /dev/null +++ b/Library/OcGuardLib/Canary.c @@ -0,0 +1,39 @@ +/** @file + +OcGuardLib + +Copyright (c) 2018, vit9696 + +All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +// +// TODO: For the cookie to work for security needs, the value is to be runtime +// generated, e.g. with rdrand. For now this code is only written to help debugging +// stack corruptions. +// +UINT64 __security_cookie = 0x9C7D6B4580C0BC9ULL; + +VOID +__security_check_cookie ( + IN UINTN Value + ) +{ + volatile UINTN Index; + + if (Value != (UINTN) __security_cookie) { + Index = 0; + while (Index == 0) + { + } + } +} diff --git a/Library/OcGuardLib/NativeOverflow.c b/Library/OcGuardLib/NativeOverflow.c new file mode 100644 index 000000000..9aeb1355a --- /dev/null +++ b/Library/OcGuardLib/NativeOverflow.c @@ -0,0 +1,115 @@ +/** @file + +OcGuardLib + +Copyright (c) 2018, vit9696 + +All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +// +// Currently no architectures provide UINTN and INTN different from 32-bit or 64-bit +// integers. For this reason they are the only variants supported and are enforced here. +// The lib must be C99-compliant, thus no _Static_assert. +// + +typedef BOOLEAN SignedIntnMustBe32or64[sizeof (INTN) == sizeof (INT64) + || sizeof (INTN) == sizeof (INT32) ? 1 : -1]; + +typedef BOOLEAN UnsignedIntnMustBe32or64[sizeof (UINTN) == sizeof (UINT64) + || sizeof (UINTN) == sizeof (UINT32) ? 1 : -1]; + +BOOLEAN +(OcOverflowAddUN) ( + UINTN A, + UINTN B, + UINTN *Result + ) +{ + if (sizeof (UINTN) == sizeof (UINT64)) { + return OcOverflowAddU64 (A, B, Result); + } + + return OcOverflowAddU32 ((UINT32)A, (UINT32)B, (UINT32 *)Result); +} + +BOOLEAN +(OcOverflowSubUN) ( + UINTN A, + UINTN B, + UINTN *Result + ) +{ + if (sizeof (UINTN) == sizeof (UINT64)) { + return OcOverflowSubU64 (A, B, Result); + } + + return OcOverflowSubU32 ((UINT32)A, (UINT32)B, (UINT32 *)Result); +} + +BOOLEAN +(OcOverflowMulUN) ( + UINTN A, + UINTN B, + UINTN *Result + ) +{ + if (sizeof (UINTN) == sizeof (UINT64)) { + return OcOverflowMulU64 (A, B, Result); + } + + return OcOverflowMulU32 ((UINT32)A, (UINT32)B, (UINT32 *)Result); +} + +BOOLEAN +(OcOverflowAddSN) ( + INTN A, + INTN B, + INTN *Result + ) +{ + if (sizeof (INTN) == sizeof (INT64)) { + return OcOverflowAddS64 (A, B, Result); + } + + return OcOverflowAddS32 ((INT32)A, (INT32)B, (INT32 *)Result); +} + +BOOLEAN +(OcOverflowSubSN) ( + INTN A, + INTN B, + INTN *Result + ) +{ + if (sizeof (INTN) == sizeof (INT64)) { + return OcOverflowSubS64 (A, B, Result); + } + + return OcOverflowSubS32 ((INT32)A, (INT32)B, (INT32 *)Result); +} + +BOOLEAN +(OcOverflowMulSN) ( + INTN A, + INTN B, + INTN *Result + ) +{ + if (sizeof (INTN) == sizeof (INT64)) { + return OcOverflowMulS64 (A, B, Result); + } + + return OcOverflowMulS32 ((INT32)A, (INT32)B, (INT32 *)Result); +} diff --git a/Library/OcGuardLib/OcGuardLib.inf b/Library/OcGuardLib/OcGuardLib.inf new file mode 100644 index 000000000..d2bbc321e --- /dev/null +++ b/Library/OcGuardLib/OcGuardLib.inf @@ -0,0 +1,50 @@ +## @file +# OcGuardLib +# +# Copyright (c) 2018, vit9696 +# +# All rights reserved. +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OcGuardLib + FILE_GUID = 4709B3D7-BE0A-4FCE-9DDA-C5823E48BE3C + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = OcGuardLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SAL_DRIVER DXE_SMM_DRIVER SMM_CORE UEFI_APPLICATION UEFI_DRIVER + + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + BitOverflow.c + Canary.c + NativeOverflow.c + TripleOverflow.c + UbsanPrintf.c + Ubsan.c + Ubsan.h + +[Packages] + MdePkg/MdePkg.dec + OcSupportPkg/OcSupportPkg.dec + +[LibraryClasses] + BaseLib + +[BuildOptions] + XCODE:DEBUG_*_*_CC_FLAGS = -fno-stack-protector + XCODE:NOOPT_*_*_CC_FLAGS = -fno-stack-protector + XCODE:RELEASE_*_*_CC_FLAGS = -fno-stack-protector diff --git a/Library/OcGuardLib/TripleOverflow.c b/Library/OcGuardLib/TripleOverflow.c new file mode 100644 index 000000000..d6155c46c --- /dev/null +++ b/Library/OcGuardLib/TripleOverflow.c @@ -0,0 +1,429 @@ +/** @file + +OcGuardLib + +Copyright (c) 2018, vit9696 + +All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +BOOLEAN +(OcOverflowTriAddU32) ( + UINT32 A, + UINT32 B, + UINT32 C, + UINT32 *Result + ) +{ + UINT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddU32(A, B, &OcTmp); + OcSecond = OcOverflowAddU32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriMulU32) ( + UINT32 A, + UINT32 B, + UINT32 C, + UINT32 *Result + ) +{ + UINT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulU32(A, B, &OcTmp); + OcSecond = OcOverflowMulU32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowAddMulU32) ( + UINT32 A, + UINT32 B, + UINT32 C, + UINT32 *Result + ) +{ + UINT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddU32(A, B, &OcTmp); + OcSecond = OcOverflowMulU32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowMulAddU32) ( + UINT32 A, + UINT32 B, + UINT32 C, + UINT32 *Result + ) +{ + UINT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulU32(A, B, &OcTmp); + OcSecond = OcOverflowAddU32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriAddS32) ( + INT32 A, + INT32 B, + INT32 C, + INT32 *Result + ) +{ + INT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddS32(A, B, &OcTmp); + OcSecond = OcOverflowAddS32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriMulS32) ( + INT32 A, + INT32 B, + INT32 C, + INT32 *Result + ) +{ + INT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulS32(A, B, &OcTmp); + OcSecond = OcOverflowMulS32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowAddMulS32) ( + INT32 A, + INT32 B, + INT32 C, + INT32 *Result + ) +{ + INT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddS32(A, B, &OcTmp); + OcSecond = OcOverflowMulS32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowMulAddS32) ( + INT32 A, + INT32 B, + INT32 C, + INT32 *Result + ) +{ + INT32 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulS32(A, B, &OcTmp); + OcSecond = OcOverflowAddS32(OcTmp, C, Result); + return OcFirst | OcSecond; +} + + +BOOLEAN +(OcOverflowTriAddU64) ( + UINT64 A, + UINT64 B, + UINT64 C, + UINT64 *Result + ) +{ + UINT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddU64(A, B, &OcTmp); + OcSecond = OcOverflowAddU64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriMulU64) ( + UINT64 A, + UINT64 B, + UINT64 C, + UINT64 *Result + ) +{ + UINT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulU64(A, B, &OcTmp); + OcSecond = OcOverflowMulU64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowAddMulU64) ( + UINT64 A, + UINT64 B, + UINT64 C, + UINT64 *Result + ) +{ + UINT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddU64(A, B, &OcTmp); + OcSecond = OcOverflowMulU64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowMulAddU64) ( + UINT64 A, + UINT64 B, + UINT64 C, + UINT64 *Result + ) +{ + UINT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulU64(A, B, &OcTmp); + OcSecond = OcOverflowAddU64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriAddS64) ( + INT64 A, + INT64 B, + INT64 C, + INT64 *Result + ) +{ + INT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddS64(A, B, &OcTmp); + OcSecond = OcOverflowAddS64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriMulS64) ( + INT64 A, + INT64 B, + INT64 C, + INT64 *Result + ) +{ + INT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulS64(A, B, &OcTmp); + OcSecond = OcOverflowMulS64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowAddMulS64) ( + INT64 A, + INT64 B, + INT64 C, + INT64 *Result + ) +{ + INT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddS64(A, B, &OcTmp); + OcSecond = OcOverflowMulS64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowMulAddS64) ( + INT64 A, + INT64 B, + INT64 C, + INT64 *Result + ) +{ + INT64 OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulS64(A, B, &OcTmp); + OcSecond = OcOverflowAddS64(OcTmp, C, Result); + return OcFirst | OcSecond; +} + + +BOOLEAN +(OcOverflowTriAddUN) ( + UINTN A, + UINTN B, + UINTN C, + UINTN *Result + ) +{ + UINTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddUN(A, B, &OcTmp); + OcSecond = OcOverflowAddUN(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriMulUN) ( + UINTN A, + UINTN B, + UINTN C, + UINTN *Result + ) +{ + UINTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulUN(A, B, &OcTmp); + OcSecond = OcOverflowMulUN(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowAddMulUN) ( + UINTN A, + UINTN B, + UINTN C, + UINTN *Result + ) +{ + UINTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddUN(A, B, &OcTmp); + OcSecond = OcOverflowMulUN(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowMulAddUN) ( + UINTN A, + UINTN B, + UINTN C, + UINTN *Result + ) +{ + UINTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulUN(A, B, &OcTmp); + OcSecond = OcOverflowAddUN(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriAddSN) ( + INTN A, + INTN B, + INTN C, + INTN *Result + ) +{ + INTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddSN(A, B, &OcTmp); + OcSecond = OcOverflowAddSN(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowTriMulSN) ( + INTN A, + INTN B, + INTN C, + INTN *Result + ) +{ + INTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulSN(A, B, &OcTmp); + OcSecond = OcOverflowMulSN(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowAddMulSN) ( + INTN A, + INTN B, + INTN C, + INTN *Result + ) +{ + INTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowAddSN(A, B, &OcTmp); + OcSecond = OcOverflowMulSN(OcTmp, C, Result); + return OcFirst | OcSecond; +} + +BOOLEAN +(OcOverflowMulAddSN) ( + INTN A, + INTN B, + INTN C, + INTN *Result + ) +{ + INTN OcTmp; + BOOLEAN OcFirst; + BOOLEAN OcSecond; + + OcFirst = OcOverflowMulSN(A, B, &OcTmp); + OcSecond = OcOverflowAddSN(OcTmp, C, Result); + return OcFirst | OcSecond; +} diff --git a/Library/OcGuardLib/Ubsan.c b/Library/OcGuardLib/Ubsan.c new file mode 100644 index 000000000..0e7682436 --- /dev/null +++ b/Library/OcGuardLib/Ubsan.c @@ -0,0 +1,1741 @@ +/* $NetBSD: ubsan.c,v 1.6 2019/06/17 18:55:37 kamil Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * The micro UBSan implementation for the userland (uUBSan) and kernel (kUBSan). + * The uBSSan versions is suitable for inclusion into libc or used standalone + * with ATF tests. + * + * This file due to long symbol names generated by a compiler during the + * instrumentation process does not follow the KNF style with 80-column limit. + */ + +#include "Ubsan.h" + +#ifdef HAVE_UBSAN_SUPPORT + +// OC change: unsupported in EDK2 +// #include +#if defined(_KERNEL) +__KERNEL_RCSID(0, "$NetBSD: ubsan.c,v 1.6 2019/06/17 18:55:37 kamil Exp $"); +#else +__RCSID("$NetBSD: ubsan.c,v 1.6 2019/06/17 18:55:37 kamil Exp $"); +#endif + +#if defined(_KERNEL) +// OC change: unsupported in EDK2 +// #include +// #include +// #include +#define ASSERT(x) KASSERT(x) +#else +#if defined(_LIBC) +#include "namespace.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_LIBC) +#include "extern.h" +#define ubsan_vsyslog vsyslog_ss +#define ASSERT(x) _DIAGASSERT(x) +#else +#define ubsan_vsyslog vsyslog_r +#define ASSERT(x) assert(x) +#endif +/* These macros are available in _KERNEL only */ +#define SET(t, f) ((t) |= (f)) +#define ISSET(t, f) ((t) & (f)) +#define CLR(t, f) ((t) &= ~(f)) +#endif + +#ifdef UBSAN_ALWAYS_FATAL +static const bool alwaysFatal = true; +#else +static const bool alwaysFatal = false; +#endif + +#define REINTERPRET_CAST(__dt, __st) ((__dt)(__st)) +#define STATIC_CAST(__dt, __st) ((__dt)(__st)) + +#define ACK_REPORTED __BIT(31) + +#define MUL_STRING "*" +#define PLUS_STRING "+" +#define MINUS_STRING "-" +#define DIVREM_STRING "divrem" + +#define CFI_VCALL 0 +#define CFI_NVCALL 1 +#define CFI_DERIVEDCAST 2 +#define CFI_UNRELATEDCAST 3 +#define CFI_ICALL 4 +#define CFI_NVMFCALL 5 +#define CFI_VMFCALL 6 + +#define NUMBER_MAXLEN 128 +#define LOCATION_MAXLEN (PATH_MAX + 32 /* ':LINE:COLUMN' */) + +#define WIDTH_8 8 +#define WIDTH_16 16 +#define WIDTH_32 32 +#define WIDTH_64 64 +#define WIDTH_80 80 +#define WIDTH_96 96 +#define WIDTH_128 128 + +#define NUMBER_SIGNED_BIT 1U + +#ifdef __SIZEOF_INT128__ +typedef __int128 longest; +typedef unsigned __int128 ulongest; +#else +typedef int64_t longest; +typedef uint64_t ulongest; +#endif + +#ifndef _KERNEL +static int ubsan_flags = -1; +#define UBSAN_ABORT __BIT(0) +#define UBSAN_STDOUT __BIT(1) +#define UBSAN_STDERR __BIT(2) +#define UBSAN_SYSLOG __BIT(3) +#endif + +/* Undefined Behavior specific defines and structures */ + +#define KIND_INTEGER 0 +#define KIND_FLOAT 1 +#define KIND_UNKNOWN UINT16_MAX + +struct CSourceLocation { + char *mFilename; + uint32_t mLine; + uint32_t mColumn; +}; + +struct CTypeDescriptor { + uint16_t mTypeKind; + uint16_t mTypeInfo; + uint8_t mTypeName[1]; +}; + +struct COverflowData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CUnreachableData { + struct CSourceLocation mLocation; +}; + +struct CCFICheckFailData { + uint8_t mCheckKind; + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CDynamicTypeCacheMissData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + void *mTypeInfo; + uint8_t mTypeCheckKind; +}; + +struct CFunctionTypeMismatchData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CImplicitConversionData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mFromType; + struct CTypeDescriptor *mToType; + uint8_t mKind; +}; + +struct CInvalidBuiltinData { + struct CSourceLocation mLocation; + uint8_t mKind; +}; + +struct CInvalidValueData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CNonNullArgData { + struct CSourceLocation mLocation; + struct CSourceLocation mAttributeLocation; + int mArgIndex; +}; + +struct CNonNullReturnData { + struct CSourceLocation mAttributeLocation; +}; + +struct COutOfBoundsData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mArrayType; + struct CTypeDescriptor *mIndexType; +}; + +struct CPointerOverflowData { + struct CSourceLocation mLocation; +}; + +struct CShiftOutOfBoundsData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mLHSType; + struct CTypeDescriptor *mRHSType; +}; + +struct CTypeMismatchData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + unsigned long mLogAlignment; + uint8_t mTypeCheckKind; +}; + +struct CTypeMismatchData_v1 { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + uint8_t mLogAlignment; + uint8_t mTypeCheckKind; +}; + +struct CVLABoundData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CFloatCastOverflowData { + struct CSourceLocation mLocation; /* This field exists in this struct since 2015 August 11th */ + struct CTypeDescriptor *mFromType; + struct CTypeDescriptor *mToType; +}; + +/* Local utility functions */ +// OC change: EFIAPI is required by EDK2 +static void EFIAPI Report(bool isFatal, const char *pFormat, ...) __printflike(2, 3); +static bool isAlreadyReported(struct CSourceLocation *pLocation); +static size_t zDeserializeTypeWidth(struct CTypeDescriptor *pType); +static void DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation); +#ifdef __SIZEOF_INT128__ +static void DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128); +#endif +static void DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L); +static void DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L); +#ifndef _KERNEL +static void DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber); +static void DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber); +#endif +static longest llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber); +static ulongest llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber); +#ifndef _KERNEL +static void DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber); +#endif +static void DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber); +static const char *DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind); +static const char *DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind); +static const char *DeserializeCFICheckKind(uint8_t hhuCFICheckKind); +static const char *DeserializeImplicitConversionCheckKind(uint8_t hhuImplicitConversionCheckKind); +static bool isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber); +static bool isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth); + +/* Unused in this implementation, emitted by the C++ check dynamic type cast. */ +intptr_t __ubsan_vptr_type_cache[128]; + +/* Public symbols used in the instrumentation of the code generation part */ +void __ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData); +void __ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer); +void __ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable); +void __ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable); +void __ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash); +void __ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash); +void __ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom); +void __ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom); +void __ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction); +void __ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction); +void __ubsan_handle_implicit_conversion(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo); +void __ubsan_handle_implicit_conversion_abort(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo); +void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData); +void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData); +void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulVal); +void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulVal); +void __ubsan_handle_missing_return(struct CUnreachableData *pData); +void __ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldVal); +void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldVal); +void __ubsan_handle_nonnull_arg(struct CNonNullArgData *pData); +void __ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData); +void __ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +void __ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +void __ubsan_handle_nullability_arg(struct CNonNullArgData *pData); +void __ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData); +void __ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +void __ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex); +void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex); +void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult); +void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult); +void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer); +void __ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer); +void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer); +void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer); +void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound); +void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound); +void __ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr); + +static void HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation); +static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue); +static void HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData); +static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer); +static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound); +static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex); +static void HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS); +static void HandleImplicitConversion(bool isFatal, struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo); +static void HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue); +static void HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData); +static void HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction); +static void HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer); +static void HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash); +static void HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom); +static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData); +static void HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData); +static void HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer); +static void HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult); + +static void +HandleOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS, const char *szOperation) +{ + char szLocation[LOCATION_MAXLEN]; + char szLHS[NUMBER_MAXLEN]; + char szRHS[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mType, ulLHS); + DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mType, ulRHS); + + Report(isFatal, "UBSan: Undefined Behavior in %s, %s integer overflow: %s %s %s cannot be represented in type %s\n", + szLocation, ISSET(pData->mType->mTypeInfo, NUMBER_SIGNED_BIT) ? "signed" : "unsigned", szLHS, szOperation, szRHS, pData->mType->mTypeName); +} + +static void +HandleNegateOverflow(bool isFatal, struct COverflowData *pData, unsigned long ulOldValue) +{ + char szLocation[LOCATION_MAXLEN]; + char szOldValue[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szOldValue, NUMBER_MAXLEN, pData->mType, ulOldValue); + + Report(isFatal, "UBSan: Undefined Behavior in %s, negation of %s cannot be represented in type %s\n", + szLocation, szOldValue, pData->mType->mTypeName); +} + +static void +HandleBuiltinUnreachable(bool isFatal, struct CUnreachableData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, calling __builtin_unreachable()\n", + szLocation); +} + +static void +HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, struct CTypeDescriptor *mType, unsigned long mLogAlignment, uint8_t mTypeCheckKind, unsigned long ulPointer) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(mLocation); + ASSERT(mType); + + if (isAlreadyReported(mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, mLocation); + + if (ulPointer == 0) { + Report(isFatal, "UBSan: Undefined Behavior in %s, %s null pointer of type %s\n", + szLocation, DeserializeTypeCheckKind(mTypeCheckKind), mType->mTypeName); + } else if ((mLogAlignment - 1) & ulPointer) { + Report(isFatal, "UBSan: Undefined Behavior in %s, %s misaligned address %p for type %s which requires %ld byte alignment\n", + szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName, mLogAlignment); + } else { + Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %p with insufficient space for an object of type %s\n", + szLocation, DeserializeTypeCheckKind(mTypeCheckKind), REINTERPRET_CAST(void *, ulPointer), mType->mTypeName); + } +} + +static void +HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, unsigned long ulBound) +{ + char szLocation[LOCATION_MAXLEN]; + char szBound[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szBound, NUMBER_MAXLEN, pData->mType, ulBound); + + Report(isFatal, "UBSan: Undefined Behavior in %s, variable length array bound value %s <= 0\n", + szLocation, szBound); +} + +static void +HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, unsigned long ulIndex) +{ + char szLocation[LOCATION_MAXLEN]; + char szIndex[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szIndex, NUMBER_MAXLEN, pData->mIndexType, ulIndex); + + Report(isFatal, "UBSan: Undefined Behavior in %s, index %s is out of range for type %s\n", + szLocation, szIndex, pData->mArrayType->mTypeName); +} + +static void +HandleShiftOutOfBounds(bool isFatal, struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + char szLocation[LOCATION_MAXLEN]; + char szLHS[NUMBER_MAXLEN]; + char szRHS[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szLHS, NUMBER_MAXLEN, pData->mLHSType, ulLHS); + DeserializeNumber(szLocation, szRHS, NUMBER_MAXLEN, pData->mRHSType, ulRHS); + + if (isNegativeNumber(szLocation, pData->mRHSType, ulRHS)) + Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is negative\n", + szLocation, szRHS); + else if (isShiftExponentTooLarge(szLocation, pData->mRHSType, ulRHS, zDeserializeTypeWidth(pData->mLHSType))) + Report(isFatal, "UBSan: Undefined Behavior in %s, shift exponent %s is too large for %zu-bit type %s\n", + szLocation, szRHS, zDeserializeTypeWidth(pData->mLHSType), pData->mLHSType->mTypeName); + else if (isNegativeNumber(szLocation, pData->mLHSType, ulLHS)) + Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of negative value %s\n", + szLocation, szLHS); + else + Report(isFatal, "UBSan: Undefined Behavior in %s, left shift of %s by %s places cannot be represented in type %s\n", + szLocation, szLHS, szRHS, pData->mLHSType->mTypeName); +} + +static void +HandleImplicitConversion(bool isFatal, struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo) +{ + char szLocation[LOCATION_MAXLEN]; + char szFrom[NUMBER_MAXLEN]; + char szTo[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szFrom, NUMBER_MAXLEN, pData->mFromType, ulFrom); + DeserializeNumber(szLocation, szTo, NUMBER_MAXLEN, pData->mToType, ulTo); + + Report(isFatal, "UBSAN: Undefined Behavior in %s, %s from %s to %s\n", + szLocation, DeserializeImplicitConversionCheckKind(pData->mKind), pData->mFromType->mTypeName, pData->mToType->mTypeName); +} + +static void +HandleLoadInvalidValue(bool isFatal, struct CInvalidValueData *pData, unsigned long ulValue) +{ + char szLocation[LOCATION_MAXLEN]; + char szValue[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szValue, NUMBER_MAXLEN, pData->mType, ulValue); + + Report(isFatal, "UBSan: Undefined Behavior in %s, load of value %s is not a valid value for type %s\n", + szLocation, szValue, pData->mType->mTypeName); +} + +static void +HandleInvalidBuiltin(bool isFatal, struct CInvalidBuiltinData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, passing zero to %s, which is not a valid argument\n", + szLocation, DeserializeBuiltinCheckKind(pData->mKind)); +} + +static void +HandleFunctionTypeMismatch(bool isFatal, struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + char szLocation[LOCATION_MAXLEN]; + + /* + * There is no a portable C solution to translate an address of a + * function to its name. On the cost of getting this routine simple + * and portable without ifdefs between the userland and the kernel + * just print the address of the function as-is. + * + * For better diagnostic messages in the userland, users shall use + * the full upstream version shipped along with the compiler toolchain. + */ + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, call to function %#lx through pointer to incorrect function type %s\n", + szLocation, ulFunction, pData->mType->mTypeName); +} + +static void +HandleCFIBadType(bool isFatal, struct CCFICheckFailData *pData, unsigned long ulVtable, bool *bValidVtable, bool *FromUnrecoverableHandler, unsigned long *ProgramCounter, unsigned long *FramePointer) +{ + char szLocation[LOCATION_MAXLEN]; + + /* + * This is a minimal implementation without diving into C++ + * specifics and (Itanium) ABI deserialization. + */ + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + if (pData->mCheckKind == CFI_ICALL || pData->mCheckKind == CFI_VMFCALL) { + Report(isFatal, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx)\n", + szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable); + } else { + Report(isFatal || FromUnrecoverableHandler, "UBSan: Undefined Behavior in %s, control flow integrity check for type %s failed during %s (vtable address %#lx; %s vtable; from %s handler; Program Counter %#lx; Frame Pointer %#lx)\n", + szLocation, pData->mType->mTypeName, DeserializeCFICheckKind(pData->mCheckKind), ulVtable, *bValidVtable ? "valid" : "invalid", *FromUnrecoverableHandler ? "unrecoverable" : "recoverable", *ProgramCounter, *FramePointer); + } +} + +static void +HandleDynamicTypeCacheMiss(bool isFatal, struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash) +{ +#if 0 + char szLocation[LOCATION_MAXLEN]; + + /* + * Unimplemented. + * + * This UBSan handler is special as the check has to be impelemented + * in an implementation. In order to handle it there is need to + * introspect into C++ ABI internals (RTTI) and use low-level + * C++ runtime interfaces. + */ + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, %s address %#lx which might not point to an object of type %s\n" + szLocation, DeserializeTypeCheckKind(pData->mTypeCheckKind), ulPointer, pData->mType); +#endif +} + +static void +HandleFloatCastOverflow(bool isFatal, struct CFloatCastOverflowData *pData, unsigned long ulFrom) +{ + char szLocation[LOCATION_MAXLEN]; + char szFrom[NUMBER_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + DeserializeNumber(szLocation, szFrom, NUMBER_MAXLEN, pData->mFromType, ulFrom); + + Report(isFatal, "UBSan: Undefined Behavior in %s, %s (of type %s) is outside the range of representable values of type %s\n", + szLocation, szFrom, pData->mFromType->mTypeName, pData->mToType->mTypeName); +} + +static void +HandleMissingReturn(bool isFatal, struct CUnreachableData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, execution reached the end of a value-returning function without returning a value\n", + szLocation); +} + +static void +HandleNonnullArg(bool isFatal, struct CNonNullArgData *pData) +{ + char szLocation[LOCATION_MAXLEN]; + char szAttributeLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + if (pData->mAttributeLocation.mFilename) + DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation); + else + szAttributeLocation[0] = '\0'; + + Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer passed as argument %d, which is declared to never be null%s%s\n", + szLocation, pData->mArgIndex, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation); +} + +static void +HandleNonnullReturn(bool isFatal, struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + char szLocation[LOCATION_MAXLEN]; + char szAttributeLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + ASSERT(pLocationPointer); + + if (isAlreadyReported(pLocationPointer)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, pLocationPointer); + if (pData->mAttributeLocation.mFilename) + DeserializeLocation(szAttributeLocation, LOCATION_MAXLEN, &pData->mAttributeLocation); + else + szAttributeLocation[0] = '\0'; + + Report(isFatal, "UBSan: Undefined Behavior in %s, null pointer returned from function declared to never return null%s%s\n", + szLocation, pData->mAttributeLocation.mFilename ? ", nonnull/_Nonnull specified in " : "", szAttributeLocation); +} + +static void +HandlePointerOverflow(bool isFatal, struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult) +{ + char szLocation[LOCATION_MAXLEN]; + + ASSERT(pData); + + if (isAlreadyReported(&pData->mLocation)) + return; + + DeserializeLocation(szLocation, LOCATION_MAXLEN, &pData->mLocation); + + Report(isFatal, "UBSan: Undefined Behavior in %s, pointer expression with base %#lx overflowed to %#lx\n", + szLocation, ulBase, ulResult); +} + +/* Definions of public symbols emitted by the instrumentation code */ +void +__ubsan_handle_add_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, PLUS_STRING); +} + +void +__ubsan_handle_add_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, PLUS_STRING); +} + +void +__ubsan_handle_builtin_unreachable(struct CUnreachableData *pData) +{ + + ASSERT(pData); + + HandleBuiltinUnreachable(true, pData); +} + +void +__ubsan_handle_cfi_bad_type(struct CCFICheckFailData *pData, unsigned long ulVtable, bool bValidVtable, bool FromUnrecoverableHandler, unsigned long ProgramCounter, unsigned long FramePointer) +{ + + ASSERT(pData); + + HandleCFIBadType(false, pData, ulVtable, &bValidVtable, &FromUnrecoverableHandler, &ProgramCounter, &FramePointer); +} + +void +__ubsan_handle_cfi_check_fail(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable) +{ + + ASSERT(pData); + + HandleCFIBadType(false, pData, ulValue, 0, 0, 0, 0); +} + +void +__ubsan_handle_cfi_check_fail_abort(struct CCFICheckFailData *pData, unsigned long ulValue, unsigned long ulValidVtable) +{ + + ASSERT(pData); + + HandleCFIBadType(true, pData, ulValue, 0, 0, 0, 0); +} + +void +__ubsan_handle_divrem_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, DIVREM_STRING); +} + +void +__ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, DIVREM_STRING); +} + +void +__ubsan_handle_dynamic_type_cache_miss(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash) +{ + + ASSERT(pData); + + HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash); +} + +void +__ubsan_handle_dynamic_type_cache_miss_abort(struct CDynamicTypeCacheMissData *pData, unsigned long ulPointer, unsigned long ulHash) +{ + + ASSERT(pData); + + HandleDynamicTypeCacheMiss(false, pData, ulPointer, ulHash); +} + +void +__ubsan_handle_float_cast_overflow(struct CFloatCastOverflowData *pData, unsigned long ulFrom) +{ + + ASSERT(pData); + + HandleFloatCastOverflow(false, pData, ulFrom); +} + +void +__ubsan_handle_float_cast_overflow_abort(struct CFloatCastOverflowData *pData, unsigned long ulFrom) +{ + + ASSERT(pData); + + HandleFloatCastOverflow(true, pData, ulFrom); +} + +void +__ubsan_handle_function_type_mismatch(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + + ASSERT(pData); + + HandleFunctionTypeMismatch(false, pData, ulFunction); +} + +void +__ubsan_handle_function_type_mismatch_abort(struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + + ASSERT(pData); + + HandleFunctionTypeMismatch(false, pData, ulFunction); +} + +void +__ubsan_handle_implicit_conversion(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo) +{ + ASSERT(pData); + + HandleImplicitConversion(false, pData, ulFrom, ulTo); +} + +void +__ubsan_handle_implicit_conversion_abort(struct CImplicitConversionData *pData, unsigned long ulFrom, unsigned long ulTo) +{ + + ASSERT(pData); + + HandleImplicitConversion(false, pData, ulFrom, ulTo); +} + +void +__ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData) +{ + + ASSERT(pData); + + HandleInvalidBuiltin(true, pData); +} + +void +__ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData) +{ + + ASSERT(pData); + + HandleInvalidBuiltin(true, pData); +} + +void +__ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, unsigned long ulValue) +{ + + ASSERT(pData); + + HandleLoadInvalidValue(false, pData, ulValue); +} + +void +__ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, unsigned long ulValue) +{ + + ASSERT(pData); + + HandleLoadInvalidValue(true, pData, ulValue); +} + +void +__ubsan_handle_missing_return(struct CUnreachableData *pData) +{ + + ASSERT(pData); + + HandleMissingReturn(true, pData); +} + +void +__ubsan_handle_mul_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, MUL_STRING); +} + +void +__ubsan_handle_mul_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, MUL_STRING); +} + +void +__ubsan_handle_negate_overflow(struct COverflowData *pData, unsigned long ulOldValue) +{ + + ASSERT(pData); + + HandleNegateOverflow(false, pData, ulOldValue); +} + +void +__ubsan_handle_negate_overflow_abort(struct COverflowData *pData, unsigned long ulOldValue) +{ + + ASSERT(pData); + + HandleNegateOverflow(true, pData, ulOldValue); +} + +void +__ubsan_handle_nonnull_arg(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(false, pData); +} + +void +__ubsan_handle_nonnull_arg_abort(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(true, pData); +} + +void +__ubsan_handle_nonnull_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(false, pData, pLocationPointer); +} + +void +__ubsan_handle_nonnull_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(true, pData, pLocationPointer); +} + +void +__ubsan_handle_nullability_arg(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(false, pData); +} + +void +__ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData) +{ + + ASSERT(pData); + + HandleNonnullArg(true, pData); +} + +void +__ubsan_handle_nullability_return_v1(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(false, pData, pLocationPointer); +} + +void +__ubsan_handle_nullability_return_v1_abort(struct CNonNullReturnData *pData, struct CSourceLocation *pLocationPointer) +{ + + ASSERT(pData); + ASSERT(pLocationPointer); + + HandleNonnullReturn(true, pData, pLocationPointer); +} + +void +__ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, unsigned long ulIndex) +{ + + ASSERT(pData); + + HandleOutOfBounds(false, pData, ulIndex); +} + +void +__ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, unsigned long ulIndex) +{ + + ASSERT(pData); + + HandleOutOfBounds(true, pData, ulIndex); +} + +void +__ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult) +{ + + ASSERT(pData); + + HandlePointerOverflow(false, pData, ulBase, ulResult); +} + +void +__ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, unsigned long ulBase, unsigned long ulResult) +{ + + ASSERT(pData); + + HandlePointerOverflow(true, pData, ulBase, ulResult); +} + +void +__ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleShiftOutOfBounds(false, pData, ulLHS, ulRHS); +} + +void +__ubsan_handle_shift_out_of_bounds_abort(struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleShiftOutOfBounds(true, pData, ulLHS, ulRHS); +} + +void +__ubsan_handle_sub_overflow(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(false, pData, ulLHS, ulRHS, MINUS_STRING); +} + +void +__ubsan_handle_sub_overflow_abort(struct COverflowData *pData, unsigned long ulLHS, unsigned long ulRHS) +{ + + ASSERT(pData); + + HandleOverflow(true, pData, ulLHS, ulRHS, MINUS_STRING); +} + +void +__ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, unsigned long ulPointer) +{ + + ASSERT(pData); + + HandleTypeMismatch(false, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer); +} + +void +__ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, unsigned long ulPointer) +{ + + ASSERT(pData); + + HandleTypeMismatch(true, &pData->mLocation, pData->mType, pData->mLogAlignment, pData->mTypeCheckKind, ulPointer); +} + +void +__ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer) +{ + + ASSERT(pData); + + HandleTypeMismatch(false, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer); +} + +void +__ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, unsigned long ulPointer) +{ + + ASSERT(pData); + + HandleTypeMismatch(true, &pData->mLocation, pData->mType, __BIT(pData->mLogAlignment), pData->mTypeCheckKind, ulPointer); +} + +void +__ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, unsigned long ulBound) +{ + + ASSERT(pData); + + HandleVlaBoundNotPositive(false, pData, ulBound); +} + +void +__ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, unsigned long ulBound) +{ + + ASSERT(pData); + + HandleVlaBoundNotPositive(true, pData, ulBound); +} + +void +__ubsan_get_current_report_data(const char **ppOutIssueKind, const char **ppOutMessage, const char **ppOutFilename, uint32_t *pOutLine, uint32_t *pOutCol, char **ppOutMemoryAddr) +{ + /* + * Unimplemented. + * + * The __ubsan_on_report() feature is non trivial to implement in a + * shared code between the kernel and userland. It's also opening + * new sets of potential problems as we are not expected to slow down + * execution of certain kernel subsystems (synchronization issues, + * interrupt handling etc). + * + * A proper solution would need probably a lock-free bounded queue built + * with atomic operations with the property of miltiple consumers and + * multiple producers. Maintaining and validating such code is not + * worth the effort. + * + * A legitimate user - besides testing framework - is a debugger plugin + * intercepting reports from the UBSan instrumentation. For such + * scenarios it is better to run the Clang/GCC version. + */ +} + +/* Local utility functions */ + +static void +// OC change: EFIAPI is required by EDK2 +EFIAPI +Report(bool isFatal, const char *pFormat, ...) +{ + va_list ap; + + ASSERT(pFormat); + + va_start(ap, pFormat); +#if defined(_KERNEL) + if (isFatal || alwaysFatal) + vpanic(pFormat, ap); + else + vprintf(pFormat, ap); +#else + if (ubsan_flags == -1) { + char buf[1024]; + char *p; + + ubsan_flags = UBSAN_STDERR; + + if (getenv_r("LIBC_UBSAN", buf, sizeof(buf)) != -1) { + for (p = buf; *p; p++) { + switch (*p) { + case 'a': + SET(ubsan_flags, UBSAN_ABORT); + break; + case 'A': + CLR(ubsan_flags, UBSAN_ABORT); + break; + case 'e': + SET(ubsan_flags, UBSAN_STDERR); + break; + case 'E': + CLR(ubsan_flags, UBSAN_STDERR); + break; + case 'l': + SET(ubsan_flags, UBSAN_SYSLOG); + break; + case 'L': + CLR(ubsan_flags, UBSAN_SYSLOG); + break; + case 'o': + SET(ubsan_flags, UBSAN_STDOUT); + break; + case 'O': + CLR(ubsan_flags, UBSAN_STDOUT); + break; + default: + break; + } + } + } + } + + // The *v*print* functions can flush the va_list argument. + // Create a local copy for each call to prevent invalid read. + if (ISSET(ubsan_flags, UBSAN_STDOUT)) { + va_list tmp; + va_copy(tmp, ap); + vprintf(pFormat, tmp); + va_end(tmp); + fflush(stdout); + } + if (ISSET(ubsan_flags, UBSAN_STDERR)) { + va_list tmp; + va_copy(tmp, ap); + vfprintf(stderr, pFormat, tmp); + va_end(tmp); + fflush(stderr); + } + if (ISSET(ubsan_flags, UBSAN_SYSLOG)) { + va_list tmp; + va_copy(tmp, ap); + struct syslog_data SyslogData = SYSLOG_DATA_INIT; + ubsan_vsyslog(LOG_DEBUG | LOG_USER, &SyslogData, pFormat, tmp); + va_end(tmp); + } + if (isFatal || alwaysFatal || ISSET(ubsan_flags, UBSAN_ABORT)) { + abort(); + __unreachable(); + /* NOTREACHED */ + } +#endif + va_end(ap); +} + +static bool +isAlreadyReported(struct CSourceLocation *pLocation) +{ + /* + * This code is shared between libc, kernel and standalone usage. + * It shall work in early bootstrap phase of both of them. + */ + + uint32_t siOldValue; + volatile uint32_t *pLine; + + ASSERT(pLocation); + + pLine = &pLocation->mLine; + + do { + siOldValue = *pLine; + } while (__sync_val_compare_and_swap(pLine, siOldValue, siOldValue | ACK_REPORTED) != siOldValue); + + return ISSET(siOldValue, ACK_REPORTED); +} + +static size_t +zDeserializeTypeWidth(struct CTypeDescriptor *pType) +{ + size_t zWidth = 0; + + ASSERT(pType); + + switch (pType->mTypeKind) { + case KIND_INTEGER: + zWidth = __BIT(__SHIFTOUT(pType->mTypeInfo, ~NUMBER_SIGNED_BIT)); + break; + case KIND_FLOAT: + zWidth = pType->mTypeInfo; + break; + default: + Report(true, "UBSan: Unknown variable type %#04" PRIx16 "\n", pType->mTypeKind); + __unreachable(); + /* NOTREACHED */ + } + + /* Invalid width will be transformed to 0 */ + ASSERT(zWidth > 0); + + return zWidth; +} + +static void +DeserializeLocation(char *pBuffer, size_t zBUfferLength, struct CSourceLocation *pLocation) +{ + + ASSERT(pLocation); + ASSERT(pLocation->mFilename); + + snprintf(pBuffer, zBUfferLength, "%s:%" PRIu32 ":%" PRIu32, pLocation->mFilename, pLocation->mLine & (uint32_t)~ACK_REPORTED, pLocation->mColumn); +} + +#ifdef __SIZEOF_INT128__ +static void +DeserializeUINT128(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, __uint128_t U128) +{ + char szBuf[3]; /* 'XX\0' */ + char rgNumber[sizeof(ulongest)]; + ssize_t zI; + + memcpy(rgNumber, &U128, sizeof(U128)); + + strlcpy(pBuffer, "Undecoded-128-bit-Integer-Type (0x", zBUfferLength); +#if BYTE_ORDER == LITTLE_ENDIAN + for (zI = sizeof(ulongest) - 1; zI >= 0; zI--) { +#else + for (zI = 0; zI < (ssize_t)sizeof(ulongest); zI++) { +#endif + snprintf(szBuf, sizeof(szBuf), "%02" PRIx8, rgNumber[zI]); + strlcat(pBuffer, szBuf, zBUfferLength); + } + strlcat(pBuffer, ")", zBUfferLength); +} +#endif + +static void +DeserializeNumberSigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, longest L) +{ + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)); + + switch (zDeserializeTypeWidth(pType)) { + default: + ASSERT(0 && "Invalid codepath"); + __unreachable(); + /* NOTREACHED */ +#ifdef __SIZEOF_INT128__ + case WIDTH_128: + DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L)); + break; +#endif + case WIDTH_64: + /* FALLTHROUGH */ + case WIDTH_32: + /* FALLTHROUGH */ + case WIDTH_16: + /* FALLTHROUGH */ + case WIDTH_8: + snprintf(pBuffer, zBUfferLength, "%" PRId64, STATIC_CAST(int64_t, L)); + break; + } +} + +static void +DeserializeNumberUnsigned(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, ulongest L) +{ + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)); + + switch (zDeserializeTypeWidth(pType)) { + default: + ASSERT(0 && "Invalid codepath"); + __unreachable(); + /* NOTREACHED */ +#ifdef __SIZEOF_INT128__ + case WIDTH_128: + DeserializeUINT128(pBuffer, zBUfferLength, pType, STATIC_CAST(__uint128_t, L)); + break; +#endif + case WIDTH_64: + /* FALLTHROUGH */ + case WIDTH_32: + /* FALLTHROUGH */ + case WIDTH_16: + /* FALLTHROUGH */ + case WIDTH_8: + snprintf(pBuffer, zBUfferLength, "%" PRIu64, STATIC_CAST(uint64_t, L)); + break; + } +} + +#ifndef _KERNEL +static void +DeserializeFloatOverPointer(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long *pNumber) +{ + double D; +#ifdef __HAVE_LONG_DOUBLE + long double LD; +#endif + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(pNumber); + /* + * This function handles 64-bit number over a pointer on 32-bit CPUs. + */ + ASSERT((sizeof(*pNumber) * CHAR_BIT < WIDTH_64) || (zDeserializeTypeWidth(pType) >= WIDTH_64)); + ASSERT(sizeof(D) == sizeof(uint64_t)); +#ifdef __HAVE_LONG_DOUBLE + ASSERT(sizeof(LD) > sizeof(uint64_t)); +#endif + + switch (zDeserializeTypeWidth(pType)) { +#ifdef __HAVE_LONG_DOUBLE + case WIDTH_128: + /* FALLTHROUGH */ + case WIDTH_96: + /* FALLTHROUGH */ + case WIDTH_80: + memcpy(&LD, pNumber, sizeof(long double)); + snprintf(pBuffer, zBUfferLength, "%Lg", LD); + break; +#endif + case WIDTH_64: + memcpy(&D, pNumber, sizeof(double)); + snprintf(pBuffer, zBUfferLength, "%g", D); + break; + } +} + +static void +DeserializeFloatInlined(char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + float F; + double D; + uint32_t U32; + + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(sizeof(F) == sizeof(uint32_t)); + ASSERT(sizeof(D) == sizeof(uint64_t)); + + switch (zDeserializeTypeWidth(pType)) { + case WIDTH_64: + memcpy(&D, &ulNumber, sizeof(double)); + snprintf(pBuffer, zBUfferLength, "%g", D); + break; + case WIDTH_32: + /* + * On supported platforms sizeof(float)==sizeof(uint32_t) + * unsigned long is either 32 or 64-bit, cast it to 32-bit + * value in order to call memcpy(3) in an Endian-aware way. + */ + U32 = STATIC_CAST(uint32_t, ulNumber); + memcpy(&F, &U32, sizeof(float)); + snprintf(pBuffer, zBUfferLength, "%g", F); + break; + case WIDTH_16: + snprintf(pBuffer, zBUfferLength, "Undecoded-16-bit-Floating-Type (%#04" PRIx16 ")", STATIC_CAST(uint16_t, ulNumber)); + break; + } +} +#endif + +static longest +llliGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + size_t zNumberWidth; + longest L = 0; + + ASSERT(szLocation); + ASSERT(pType); + + zNumberWidth = zDeserializeTypeWidth(pType); + switch (zNumberWidth) { + default: + Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation); + __unreachable(); + /* NOTREACHED */ + case WIDTH_128: +#ifdef __SIZEOF_INT128__ + memcpy(&L, REINTERPRET_CAST(longest *, ulNumber), sizeof(longest)); + break; +#else + Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation); + __unreachable(); + /* NOTREACHED */ +#endif + case WIDTH_64: + if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) { + L = *REINTERPRET_CAST(int64_t *, ulNumber); + } else { + L = STATIC_CAST(int64_t, STATIC_CAST(uint64_t, ulNumber)); + } + break; + case WIDTH_32: + L = STATIC_CAST(int32_t, STATIC_CAST(uint32_t, ulNumber)); + break; + case WIDTH_16: + L = STATIC_CAST(int16_t, STATIC_CAST(uint16_t, ulNumber)); + break; + case WIDTH_8: + L = STATIC_CAST(int8_t, STATIC_CAST(uint8_t, ulNumber)); + break; + } + + return L; +} + +static ulongest +llluGetNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + size_t zNumberWidth; + ulongest UL = 0; + + ASSERT(pType); + + zNumberWidth = zDeserializeTypeWidth(pType); + switch (zNumberWidth) { + default: + Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation); + __unreachable(); + /* NOTREACHED */ + case WIDTH_128: +#ifdef __SIZEOF_INT128__ + memcpy(&UL, REINTERPRET_CAST(ulongest *, ulNumber), sizeof(ulongest)); + break; +#else + Report(true, "UBSan: Unexpected 128-Bit Type in %s\n", szLocation); + __unreachable(); + /* NOTREACHED */ +#endif + case WIDTH_64: + if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) { + UL = *REINTERPRET_CAST(uint64_t *, ulNumber); + break; + } + /* FALLTHROUGH */ + case WIDTH_32: + /* FALLTHROUGH */ + case WIDTH_16: + /* FALLTHROUGH */ + case WIDTH_8: + UL = ulNumber; + break; + } + + return UL; +} + +#ifndef _KERNEL +static void +DeserializeNumberFloat(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + size_t zNumberWidth; + + ASSERT(szLocation); + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + ASSERT(pType->mTypeKind == KIND_FLOAT); + + zNumberWidth = zDeserializeTypeWidth(pType); + switch (zNumberWidth) { + default: + Report(true, "UBSan: Unexpected %zu-Bit Type in %s\n", zNumberWidth, szLocation); + __unreachable(); + /* NOTREACHED */ +#ifdef __HAVE_LONG_DOUBLE + case WIDTH_128: + /* FALLTHROUGH */ + case WIDTH_96: + /* FALLTHROUGH */ + case WIDTH_80: + DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber)); + break; +#endif + case WIDTH_64: + if (sizeof(ulNumber) * CHAR_BIT < WIDTH_64) { + DeserializeFloatOverPointer(pBuffer, zBUfferLength, pType, REINTERPRET_CAST(unsigned long *, ulNumber)); + break; + } + /* FALLTHROUGH */ + case WIDTH_32: + /* FALLTHROUGH */ + case WIDTH_16: + DeserializeFloatInlined(pBuffer, zBUfferLength, pType, ulNumber); + break; + } +} +#endif + +static void +DeserializeNumber(char *szLocation, char *pBuffer, size_t zBUfferLength, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + + ASSERT(szLocation); + ASSERT(pBuffer); + ASSERT(zBUfferLength > 0); + ASSERT(pType); + + switch(pType->mTypeKind) { + case KIND_INTEGER: + if (ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)) { + longest L = llliGetNumber(szLocation, pType, ulNumber); + DeserializeNumberSigned(pBuffer, zBUfferLength, pType, L); + } else { + ulongest UL = llluGetNumber(szLocation, pType, ulNumber); + DeserializeNumberUnsigned(pBuffer, zBUfferLength, pType, UL); + } + break; + case KIND_FLOAT: +#ifdef _KERNEL + Report(true, "UBSan: Unexpected Float Type in %s\n", szLocation); + __unreachable(); + /* NOTREACHED */ +#else + DeserializeNumberFloat(szLocation, pBuffer, zBUfferLength, pType, ulNumber); + break; +#endif + case KIND_UNKNOWN: + Report(true, "UBSan: Unknown Type in %s\n", szLocation); + __unreachable(); + /* NOTREACHED */ + } +} + +static const char * +DeserializeTypeCheckKind(uint8_t hhuTypeCheckKind) +{ + const char *rgczTypeCheckKinds[] = { + "load of", + "store to", + "reference binding to", + "member access within", + "member call on", + "constructor call on", + "downcast of", + "downcast of", + "upcast of", + "cast to virtual base of", + "_Nonnull binding to", + "dynamic operation on" + }; + + ASSERT(__arraycount(rgczTypeCheckKinds) > hhuTypeCheckKind); + + return rgczTypeCheckKinds[hhuTypeCheckKind]; +} + +static const char * +DeserializeBuiltinCheckKind(uint8_t hhuBuiltinCheckKind) +{ + const char *rgczBuiltinCheckKinds[] = { + "ctz()", + "clz()" + }; + + ASSERT(__arraycount(rgczBuiltinCheckKinds) > hhuBuiltinCheckKind); + + return rgczBuiltinCheckKinds[hhuBuiltinCheckKind]; +} + +static const char * +DeserializeCFICheckKind(uint8_t hhuCFICheckKind) +{ + const char *rgczCFICheckKinds[] = { + "virtual call", // CFI_VCALL + "non-virtual call", // CFI_NVCALL + "base-to-derived cast", // CFI_DERIVEDCAST + "cast to unrelated type", // CFI_UNRELATEDCAST + "indirect function call", // CFI_ICALL + "non-virtual pointer to member function call", // CFI_NVMFCALL + "virtual pointer to member function call", // CFI_VMFCALL + }; + + ASSERT(__arraycount(rgczCFICheckKinds) > hhuCFICheckKind); + + return rgczCFICheckKinds[hhuCFICheckKind]; +} + +static const char * +DeserializeImplicitConversionCheckKind(uint8_t hhuImplicitConversionCheckKind) +{ + const char *rgczImplicitConversionCheckKind[] = { + "integer truncation", + "unsigned integer truncation", + "signed integer truncation", + "integer sign change", + "signed integer trunctation or sign change", + }; + + ASSERT(__arraycount(rgczImplicitConversionCheckKind) > hhuImplicitConversionCheckKind); + + return rgczImplicitConversionCheckKind[hhuImplicitConversionCheckKind]; +} + +static bool +isNegativeNumber(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber) +{ + + ASSERT(szLocation); + ASSERT(pType); + ASSERT(pType->mTypeKind == KIND_INTEGER); + + if (!ISSET(pType->mTypeInfo, NUMBER_SIGNED_BIT)) + return false; + + return llliGetNumber(szLocation, pType, ulNumber) < 0; +} + +static bool +isShiftExponentTooLarge(char *szLocation, struct CTypeDescriptor *pType, unsigned long ulNumber, size_t zWidth) +{ + + ASSERT(szLocation); + ASSERT(pType); + ASSERT(pType->mTypeKind == KIND_INTEGER); + + return llluGetNumber(szLocation, pType, ulNumber) >= zWidth; +} + +#endif // HAVE_UBSAN_SUPPORT diff --git a/Library/OcGuardLib/Ubsan.h b/Library/OcGuardLib/Ubsan.h new file mode 100644 index 000000000..0d8bb6788 --- /dev/null +++ b/Library/OcGuardLib/Ubsan.h @@ -0,0 +1,207 @@ +/** @file + +OcGuardLib + +Copyright (c) 2018, vit9696 + +All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef UBSAN_H +#define UBSAN_H + +#include +#include +#include +#include + +// Pretend UEFI to mean kernel mode. +#ifndef _KERNEL +#define _KERNEL 1 +#endif + +#if !defined(HAVE_UBSAN_SUPPORT) && defined(__clang__) +#define HAVE_UBSAN_SUPPORT 1 +#endif + + +// Mark long double as supported (since we may use softfp). +#ifndef __HAVE_LONG_DOUBLE +#define __HAVE_LONG_DOUBLE +#endif + +// Non-BSD environments do not support RCSID. +#ifndef __RCSID +#define __RCSID(x) +#endif + +#ifndef __KERNEL_RCSID +#define __KERNEL_RCSID(x, s) __RCSID(s) +#endif + +// Implement builtin via macros, because some toolchains do include stdint.h. +// This works only because Ubsan.c is not allowed to have any header inclusion. +#define int8_t INT8 +#define int16_t INT16 +#define int32_t INT32 +#define int64_t INT64 +#define uint8_t UINT8 +#define uint16_t UINT16 +#define uint32_t UINT32 +#define uint64_t UINT64 +#define bool BOOLEAN +#define intptr_t INTN +#define uintptr_t UINTN + +// Try to provide a somewhat adequate size_t implementation. +// Have to be careful about -Wformat warnings in Clang. +#ifdef __GNUC__ +#define ssize_t long +#define size_t unsigned long +#else +#define ssize_t INTN +#define size_t UINTN +#endif + +// Provide boolean declarations if missing. +#ifndef false +#define false FALSE +#define true TRUE +#endif + +// Provide va_list declarations. +#define va_list VA_LIST +#define va_start VA_START +#define va_end VA_END +#define va_arg VA_ARG + +// Printing macros are not supported in EDK2. +#ifndef PRIx8 +#define PRIx8 "hhx" +#define PRIx16 "hx" +#define PRIx32 "x" +#define PRIx64 "llx" +#define PRId32 "d" +#define PRId64 "lld" +#define PRIu32 "u" +#define PRIu64 "llu" +#endif + +// Hack limits in. +#ifndef UINT16_MAX +#define UINT8_MAX 0xffU +#define UINT16_MAX 0xffffU +#define UINT32_MAX 0xffffffffU +#define UINT64_MAX 0xffffffffffffffffULL +#endif + +// Avoid ASSERT/KASSERT conflict from DebugLib. +#ifdef ASSERT +#undef ASSERT +#endif + +// Implement KASSERT as the original EDK2 ASSERT. +#if !defined(MDEPKG_NDEBUG) + #define KASSERT(Expression) \ + do { \ + if (DebugAssertEnabled ()) { \ + if (!(Expression)) { \ + _ASSERT (Expression); \ + ANALYZER_UNREACHABLE (); \ + } \ + } \ + } while (FALSE) +#else + #define KASSERT(Expression) +#endif + +// PATH_MAX is an unoffical extension. +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +// CHAR_BIT may not be defined without limits.h. +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +// Bit manipulation is not present +#ifndef __BIT +#define __BIT(__n) \ + (((UINTN)(__n) >= CHAR_BIT * sizeof(UINTN)) ? 0 : \ + ((UINTN)1 << (UINTN)((__n) & (CHAR_BIT * sizeof(UINTN) - 1)))) +#endif + +// Extended bit manipulation is also not present +#ifndef __LOWEST_SET_BIT +/* find least significant bit that is set */ +#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) +#define __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask)) +#define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) +#define __SHIFTOUT_MASK(__mask) __SHIFTOUT((__mask), (__mask)) +#endif + +// BSD-like bit manipulation is not present +#ifndef CLR +#define SET(t, f) ((t) |= (f)) +#define ISSET(t, f) ((t) & (f)) +#define CLR(t, f) ((t) &= ~(f)) +#endif + +// Note, that we do not use UEFI-like printf. +#ifndef __printflike +#ifdef __GNUC__ +#define __printflike(x, y) __attribute__((format(printf, (x), (y)))) +#else +#define __printflike(x, y) +#endif +#endif + +// Route arraycount to ARRAY_SIZE +#ifndef __arraycount +#define __arraycount(a) ARRAY_SIZE (a) +#endif + +// Support unreachable where possible +#ifndef __unreachable +#ifdef __GNUC__ +#define __unreachable() __builtin_unreachable() +#else +#define __unreachable() +#endif +#endif + +// C-style printing support. +#define TINYPRINTF_DEFINE_TFP_SPRINTF 1 +typedef void (*putcf) (void *, char); +void EFIAPI tfp_format(void *putp, putcf putf, const char *fmt, va_list va); +int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap); +int EFIAPI tfp_snprintf(char *str, size_t size, const char *fmt, ...) __printflike(3, 4); +int EFIAPI tfp_vsprintf(char *str, const char *fmt, va_list ap); +int EFIAPI tfp_sprintf(char *str, const char *fmt, ...) __printflike(2, 3); + +// Redirect log printing to standard output. +#define snprintf tfp_snprintf +#define vprintf(f, v) \ + do { CHAR8 Tmp__[1024]; tfp_vsnprintf (Tmp__, sizeof (Tmp__), f, v); AsciiPrint ("%a", Tmp__); } while (0) +#define vpanic(f, v) \ + do { vprintf (f, v); do { } while (1); } while (0) + +// Avoid implementing memcpy as a function to avoid LTO conflicts. +#define memcpy(Dst, Src, Size) do { gBS->CopyMem(Dst, Src, Size); } while (0) + +// Forcing VOID for those as the return types actually differ. +#define strlcpy(Dst, Src, Size) do { AsciiStrnCpyS (Dst, Size, Src, AsciiStrLen (Src)); } while (0) +#define strlcat(Dst, Src, Size) do { AsciiStrnCatS (Dst, Size, Src, AsciiStrLen (Src)); } while (0) + + +#endif // UBSAN_H diff --git a/Library/OcGuardLib/UbsanPrintf.c b/Library/OcGuardLib/UbsanPrintf.c new file mode 100644 index 000000000..8ac283b96 --- /dev/null +++ b/Library/OcGuardLib/UbsanPrintf.c @@ -0,0 +1,537 @@ +/* +File: tinyprintf.c + +Copyright (c) 2004,2012 Kustaa Nyholm / SpareTimeLabs +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Kustaa Nyholm or SpareTimeLabs nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "Ubsan.h" + +#ifdef HAVE_UBSAN_SUPPORT + +/* + * Configuration + */ + +/* Enable long int support */ +#define PRINTF_LONG_SUPPORT + +/* Enable long long int support (implies long int support) */ +#define PRINTF_LONG_LONG_SUPPORT + +/* Enable %z (size_t) support */ +#define PRINTF_SIZE_T_SUPPORT + +/* + * Configuration adjustments + */ +#ifdef PRINTF_SIZE_T_SUPPORT +// #include +#endif + +#ifdef PRINTF_LONG_LONG_SUPPORT +# define PRINTF_LONG_SUPPORT +#endif + +/* __SIZEOF___ defined at least by gcc */ +#ifdef __SIZEOF_POINTER__ +# define SIZEOF_POINTER __SIZEOF_POINTER__ +#endif +#ifdef __SIZEOF_LONG_LONG__ +# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__ +#endif +#ifdef __SIZEOF_LONG__ +# define SIZEOF_LONG __SIZEOF_LONG__ +#endif +#ifdef __SIZEOF_INT__ +# define SIZEOF_INT __SIZEOF_INT__ +#endif + +#ifdef __GNUC__ +# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline)) +#else +# define _TFP_GCC_NO_INLINE_ +#endif + +/* + * Implementation + */ +struct param { + char lz:1; /**< Leading zeros */ + char alt:1; /**< alternate form */ + char uc:1; /**< Upper case (for base16 only) */ + char align_left:1; /**< 0 == align right (default), 1 == align left */ + unsigned int width; /**< field width */ + char sign; /**< The sign to display (if any) */ + unsigned int base; /**< number base (e.g.: 8, 10, 16) */ + char *bf; /**< Buffer to output */ +}; + + +#ifdef PRINTF_LONG_LONG_SUPPORT +static void _TFP_GCC_NO_INLINE_ ulli2a( + unsigned long long int num, struct param *p) +{ + int n = 0; + unsigned long long int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void lli2a(long long int num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + ulli2a(num, p); +} +#endif + +#ifdef PRINTF_LONG_SUPPORT +static void uli2a(unsigned long int num, struct param *p) +{ + int n = 0; + unsigned long int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void li2a(long num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + uli2a(num, p); +} +#endif + +static void ui2a(unsigned int num, struct param *p) +{ + int n = 0; + unsigned int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void i2a(int num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + ui2a(num, p); +} + +static int a2d(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else + return -1; +} + +static char a2u(char ch, const char **src, int base, unsigned int *nump) +{ + const char *p = *src; + unsigned int num = 0; + int digit; + while ((digit = a2d(ch)) >= 0) { + if (digit > base) + break; + num = num * base + digit; + ch = *p++; + } + *src = p; + *nump = num; + return ch; +} + +static void putchw(void *putp, putcf putf, struct param *p) +{ + char ch; + int n = p->width; + char *bf = p->bf; + + /* Number of filling characters */ + while (*bf++ && n > 0) + n--; + if (p->sign) + n--; + if (p->alt && p->base == 16) + n -= 2; + else if (p->alt && p->base == 8) + n--; + + /* Fill with space to align to the right, before alternate or sign */ + if (!p->lz && !p->align_left) { + while (n-- > 0) + putf(putp, ' '); + } + + /* print sign */ + if (p->sign) + putf(putp, p->sign); + + /* Alternate */ + if (p->alt && p->base == 16) { + putf(putp, '0'); + putf(putp, (p->uc ? 'X' : 'x')); + } else if (p->alt && p->base == 8) { + putf(putp, '0'); + } + + /* Fill with zeros, after alternate or sign */ + if (p->lz) { + while (n-- > 0) + putf(putp, '0'); + } + + /* Put actual buffer */ + bf = p->bf; + while ((ch = *bf++)) + putf(putp, ch); + + /* Fill with space to align to the left, after string */ + if (!p->lz && p->align_left) { + while (n-- > 0) + putf(putp, ' '); + } +} + +void EFIAPI tfp_format(void *putp, putcf putf, const char *fmt, va_list va) +{ + struct param p; +#ifdef PRINTF_LONG_SUPPORT + char bf[23]; /* long = 64b on some architectures */ +#else + char bf[12]; /* int = 32b on some architectures */ +#endif + char ch; + p.bf = bf; + + while ((ch = *(fmt++))) { + if (ch != '%') { + putf(putp, ch); + } else { +#ifdef PRINTF_LONG_SUPPORT + char lng = 0; /* 1 for long, 2 for long long */ +#endif + /* Init parameter struct */ + p.lz = 0; + p.alt = 0; + p.width = 0; + p.align_left = 0; + p.sign = 0; + + /* Flags */ + while ((ch = *(fmt++))) { + switch (ch) { + case '-': + p.align_left = 1; + continue; + case '0': + p.lz = 1; + continue; + case '#': + p.alt = 1; + continue; + default: + break; + } + break; + } + + /* Width */ + if (ch >= '0' && ch <= '9') { + ch = a2u(ch, &fmt, 10, &(p.width)); + } + + /* We accept 'x.y' format but don't support it completely: + * we ignore the 'y' digit => this ignores 0-fill + * size and makes it == width (ie. 'x') */ + if (ch == '.') { + p.lz = 1; /* zero-padding */ + /* ignore actual 0-fill size: */ + do { + ch = *(fmt++); + } while ((ch >= '0') && (ch <= '9')); + } + +#ifdef PRINTF_SIZE_T_SUPPORT +# ifdef PRINTF_LONG_SUPPORT + if (ch == 'z') { + ch = *(fmt++); + if (sizeof(size_t) == sizeof(unsigned long int)) + lng = 1; +# ifdef PRINTF_LONG_LONG_SUPPORT + else if (sizeof(size_t) == sizeof(unsigned long long int)) + lng = 2; +# endif + } else +# endif +#endif + +#ifdef PRINTF_LONG_SUPPORT + if (ch == 'l') { + ch = *(fmt++); + lng = 1; +#ifdef PRINTF_LONG_LONG_SUPPORT + if (ch == 'l') { + ch = *(fmt++); + lng = 2; + } +#endif + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u': + p.base = 10; + p.uc = 0; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + ulli2a(va_arg(va, unsigned long long int), &p); + else +#endif + if (1 == lng) + uli2a(va_arg(va, unsigned long int), &p); + else +#endif + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'd': + case 'i': + p.base = 10; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + lli2a(va_arg(va, long long int), &p); + else +#endif + if (1 == lng) + li2a(va_arg(va, long int), &p); + else +#endif + i2a(va_arg(va, int), &p); + putchw(putp, putf, &p); + break; +#ifdef SIZEOF_POINTER + case 'p': + p.alt = 1; +# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT + lng = 0; +# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG + lng = 1; +# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG + lng = 2; +# endif + // Fallthrough +#endif + case 'x': + case 'X': + p.base = 16; + p.uc = (ch == 'X')?1:0; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + ulli2a(va_arg(va, unsigned long long int), &p); + else +#endif + if (1 == lng) + uli2a(va_arg(va, unsigned long int), &p); + else +#endif + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'o': + p.base = 8; + p.uc = 0; + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'c': + putf(putp, (char)(va_arg(va, int))); + break; + case 's': + p.bf = va_arg(va, char *); + putchw(putp, putf, &p); + p.bf = bf; + break; + case '%': + putf(putp, ch); + default: + break; + } + } + } + abort:; +} + +#if TINYPRINTF_DEFINE_TFP_PRINTF +static putcf stdout_putf; +static void *stdout_putp; + +void init_printf(void *putp, putcf putf) +{ + stdout_putf = putf; + stdout_putp = putp; +} + +void EFIAPI tfp_printf(char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(stdout_putp, stdout_putf, fmt, va); + va_end(va); +} +#endif + +#if TINYPRINTF_DEFINE_TFP_SPRINTF +struct _vsnprintf_putcf_data +{ + size_t dest_capacity; + char *dest; + size_t num_chars; +}; + +static void _vsnprintf_putcf(void *p, char c) +{ + struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p; + if (data->num_chars < data->dest_capacity) + data->dest[data->num_chars] = c; + data->num_chars ++; +} + +int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + struct _vsnprintf_putcf_data data; + + if (size < 1) + return 0; + + data.dest = str; + data.dest_capacity = size-1; + data.num_chars = 0; + tfp_format(&data, _vsnprintf_putcf, format, ap); + + if (data.num_chars < data.dest_capacity) + data.dest[data.num_chars] = '\0'; + else + data.dest[data.dest_capacity] = '\0'; + + return data.num_chars; +} + +int EFIAPI tfp_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + retval = tfp_vsnprintf(str, size, format, ap); + va_end(ap); + return retval; +} + +struct _vsprintf_putcf_data +{ + char *dest; + size_t num_chars; +}; + +static void _vsprintf_putcf(void *p, char c) +{ + struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p; + data->dest[data->num_chars++] = c; +} + +int EFIAPI tfp_vsprintf(char *str, const char *format, va_list ap) +{ + struct _vsprintf_putcf_data data; + data.dest = str; + data.num_chars = 0; + tfp_format(&data, _vsprintf_putcf, format, ap); + data.dest[data.num_chars] = '\0'; + return data.num_chars; +} + +int EFIAPI tfp_sprintf(char *str, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + retval = tfp_vsprintf(str, format, ap); + va_end(ap); + return retval; +} +#endif + +#endif // HAVE_UBSAN_SUPPORT + diff --git a/MemoryFix/AptioMemoryFix/AptioFixPkg.dec b/MemoryFix/AptioMemoryFix/AptioFixPkg.dec new file mode 100644 index 000000000..5f0330490 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/AptioFixPkg.dec @@ -0,0 +1,28 @@ +## @file +# Copyright (C) 2017, vit9696. All rights reserved.
+# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +## + +[Defines] + PACKAGE_NAME = AptioFixPkg + PACKAGE_GUID = 5B8C291B-EE2D-427B-9226-77DDDA0FA222 + PACKAGE_VERSION = 1.0 + DEC_SPECIFICATION = 0x00010005 + +[Includes] + Include + +[Protocols] + gAptioMemoryFixProtocolGuid = { 0xC7CBA84E, 0xCC77, 0x461D, { 0x9E, 0x3C, 0x6B, 0xE0, 0xCB, 0x79, 0xA7, 0xC1 } } + gAmiEfiPointerProtocolGuid = { 0x15A10CE7, 0xEAB5, 0x43BF, { 0x90, 0x42, 0x74, 0x43, 0x2E, 0x69, 0x63, 0x77 } } + gAmiEfiKeycodeProtocolGuid = { 0x0ADFB62D, 0xFF74, 0x484C, { 0x89, 0x44, 0xF8, 0x5C, 0x4B, 0xEA, 0x87, 0xA8 } } diff --git a/MemoryFix/AptioMemoryFix/AptioMemoryFix.c b/MemoryFix/AptioMemoryFix/AptioMemoryFix.c new file mode 100644 index 000000000..0f258b6cc --- /dev/null +++ b/MemoryFix/AptioMemoryFix/AptioMemoryFix.c @@ -0,0 +1,97 @@ +/** + + UEFI driver for enabling loading of macOS without memory relocation. + + by dmazar + + **/ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "Config.h" +#include "BootArgs.h" +#include "BootFixes.h" +#include "CustomSlide.h" +#include "RtShims.h" +#include "ServiceOverrides.h" +#include "VMem.h" + +// +// One could discover AptioMemoryFix with this protocol +// +STATIC APTIOMEMORYFIX_PROTOCOL mAptioMemoryFixProtocol = { + APTIOMEMORYFIX_PROTOCOL_REVISION, + SetBootVariableRedirect +}; + +/** + * Entry point. Installs our StartImage override. + * All other stuff will be installed from there when boot.efi is started. + */ +EFI_STATUS +EFIAPI +AptioMemoryFixEntrypoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *Interface; + EFI_HANDLE Handle = NULL; + + Status = gBS->LocateProtocol ( + &gAptioMemoryFixProtocolGuid, + NULL, + &Interface + ); + + if (!EFI_ERROR (Status)) { + // + // In case for whatever reason one tried to reload the driver. + // + return EFI_ALREADY_STARTED; + } + + Status = gBS->InstallProtocolInterface ( + &Handle, + &gAptioMemoryFixProtocolGuid, + EFI_NATIVE_INTERFACE, + &mAptioMemoryFixProtocol + ); + + if (EFI_ERROR (Status)) { + OcPrintScreen (L"AMF: protocol install failure - %r\n", Status); + return Status; + } + + // + // Detect and apply the necessary firmware workarounds + // + ApplyFirmwareQuirks (ImageHandle, SystemTable); + + // + // Init VMem memory pool - will be used after ExitBootServices + // + Status = VmAllocateMemoryPool (); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Install permanent shims and overrides + // + InstallRtShims (GetVariableCustomSlide); + InstallBsOverrides (); + InstallRtOverrides (); + + return EFI_SUCCESS; +} diff --git a/MemoryFix/AptioMemoryFix/AptioMemoryFix.inf b/MemoryFix/AptioMemoryFix/AptioMemoryFix.inf new file mode 100644 index 000000000..ee1c0ac25 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/AptioMemoryFix.inf @@ -0,0 +1,105 @@ +## +# +# UEFI driver for enabling loading of macOS without memory relocation. +# +# by dmazar +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = AptioMemoryFix + FILE_GUID = 43C8CFCA-03C0-4AA8-8BEB-5AF6AB3570A2 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = AptioMemoryFixEntrypoint + +[Packages] +# MemoryFix/AptioFixPkg/AptioFixPkg.dec +# EfiPkg/EfiPkg.dec + CloverPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec +# OcSupportPkg/OcSupportPkg.dec + CloverEFI/UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + BaseRngLib + CpuLib + DevicePathLib + MemoryAllocationLib +# OcDeviceTreeLib +# OcDevicePathLib + MachoLib +# OcStringLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + UefiRuntimeServicesTableLib + +[Sources] + AptioMemoryFix.c + AsmFuncs.h + BootArgs.c + BootArgs.h + BootFixes.c + BootFixes.h + Config.h + CustomSlide.c + CustomSlide.h + MemoryMap.c + MemoryMap.h + RtShims.c + RtShims.h + ServiceOverrides.c + ServiceOverrides.h + VMem.c + VMem.h + UmmMalloc/UmmMalloc.h + UmmMalloc/UmmMalloc.c + +[Sources.X64] + X64/AsmFuncs.nasm + X64/AsmRtShims.nasm + +[Guids] + gEfiFileInfoGuid ## CONSUMES + gEfiFileSystemInfoGuid ## CONSUMES + gEfiFileSystemVolumeLabelInfoIdGuid ## CONSUMES + gEfiGlobalVariableGuid ## CONSUMES + gOcReadOnlyVariableGuid ## CONSUMES + gOcWriteOnlyVariableGuid ## CONSUMES + gOcVendorVariableGuid ## CONSUMES + gAppleBootVariableGuid ## SOMETIMES_CONSUMES + gAppleVendorVariableGuid ## SOMETIMES_CONSUMES + gEfiMiscSubClassGuid ## SOMETIMES_CONSUMES + gEfiProcessorSubClassGuid ## SOMETIMES_CONSUMES + gEfiMemorySubClassGuid ## SOMETIMES_CONSUMES + +[Protocols] + gAptioMemoryFixProtocolGuid ## PRODUCES + gEfiLoadedImageProtocolGuid ## CONSUMES + gEfiDevicePathProtocolGuid ## CONSUMES + gEfiGraphicsOutputProtocolGuid ## CONSUMES + gEfiSimpleFileSystemProtocolGuid ## CONSUMES + gEfiBlockIoProtocolGuid ## CONSUMES + gEfiBlockIo2ProtocolGuid ## CONSUMES + gEfiDiskIoProtocolGuid ## CONSUMES + gEfiDiskIo2ProtocolGuid ## CONSUMES + gEfiConsoleControlProtocolGuid ## SOMETIMES_CONSUMES + gAppleFirmwarePasswordProtocolGuid ## SOMETIMES_CONSUMES + gEfiDevicePathPropertyDatabaseProtocolGuid ## SOMETIMES_CONSUMES + gAppleFramebufferInfoProtocolGuid ## SOMETIMES_CONSUMES + gAppleKeyMapAggregatorProtocolGuid ## SOMETIMES_CONSUMES + gAppleNetBootProtocolGuid ## SOMETIMES_CONSUMES + gAppleImageConversionProtocolGuid ## SOMETIMES_CONSUMES + gEfiDataHubProtocolGuid ## SOMETIMES_CONSUMES + +[BuildOptions] +# To generate assembled listings +# MSFT:*_*_*_ASM_FLAGS = /Fl +# GCC:*_*_*_ASM_FLAGS = -Wa,-a=AsmList.txt +# GCC:*_*_*_NASM_FLAGS = -l NasmList.txt diff --git a/MemoryFix/AptioMemoryFix/AsmFuncs.h b/MemoryFix/AptioMemoryFix/AsmFuncs.h new file mode 100644 index 000000000..2367f506a --- /dev/null +++ b/MemoryFix/AptioMemoryFix/AsmFuncs.h @@ -0,0 +1,49 @@ +/** + + Some assembler helper functions plus boot.efi kernel jump callback + + by dmazar + +**/ + +#ifndef APTIOFIX_ASM_FUNCS_H +#define APTIOFIX_ASM_FUNCS_H + +#include + +/** Save 64 bit state that will be restored on callback. */ +VOID +EFIAPI +AsmPrepareJumpFromKernel ( + VOID + ); + +/** Start and end address of the 32 and 64 bit code + that is copied to kernel entry address to jump back + to our code, to AsmJumpFromKernel(). + */ +extern UINT8 EntryPatchCode; +extern UINT8 EntryPatchCodeEnd; + +/** Callback function, 32 and 64 bit, that is called when boot.efi jumps to kernel address. */ +VOID +EFIAPI +AsmJumpFromKernel ( + VOID + ); + +/** 32 bit function start and end that copies kernel to proper mem and jumps to kernel. */ +extern UINT8 JumpToKernel; +extern UINT8 JumpToKernel32; +extern UINT8 JumpToKernel64; +extern UINT8 JumpToKernelEnd; + +extern UINTN SavedCR3; +extern IA32_DESCRIPTOR SavedGDTR; +extern IA32_DESCRIPTOR SavedIDTR; + +extern UINT64 AsmKernelEntry; +extern UINT64 JumpToKernel32Addr; +extern UINT64 JumpToKernel64Addr; + +#endif // APTIOFIX_ASM_FUNCS_H diff --git a/MemoryFix/AptioMemoryFix/BootArgs.c b/MemoryFix/AptioMemoryFix/BootArgs.c new file mode 100644 index 000000000..0c5029d07 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/BootArgs.c @@ -0,0 +1,149 @@ +/** + + Methods for finding, checking and fixing boot args + + by dmazar + +**/ + +#include +#include +#include + +#include "Config.h" +#include "BootArgs.h" + +STATIC AMF_BOOT_ARGUMENTS mBootArgs; + +AMF_BOOT_ARGUMENTS * +GetBootArgs ( + IN VOID *BootArgs + ) +{ + BootArgs1 *BA1 = BootArgs; + BootArgs2 *BA2 = BootArgs; + + ZeroMem (&mBootArgs, sizeof(mBootArgs)); + + if (BA1->Version == kBootArgsVersion1) { + // + // Pre Lion + // + mBootArgs.MemoryMap = &BA1->MemoryMap; + mBootArgs.MemoryMapSize = &BA1->MemoryMapSize; + mBootArgs.MemoryMapDescriptorSize = &BA1->MemoryMapDescriptorSize; + mBootArgs.MemoryMapDescriptorVersion = &BA1->MemoryMapDescriptorVersion; + + mBootArgs.CommandLine = &BA1->CommandLine[0]; + + mBootArgs.deviceTreeP = &BA1->deviceTreeP; + mBootArgs.deviceTreeLength = &BA1->deviceTreeLength; + } else { + // + // Lion and newer + // + mBootArgs.MemoryMap = &BA2->MemoryMap; + mBootArgs.MemoryMapSize = &BA2->MemoryMapSize; + mBootArgs.MemoryMapDescriptorSize = &BA2->MemoryMapDescriptorSize; + mBootArgs.MemoryMapDescriptorVersion = &BA2->MemoryMapDescriptorVersion; + + mBootArgs.CommandLine = &BA2->CommandLine[0]; + + mBootArgs.deviceTreeP = &BA2->deviceTreeP; + mBootArgs.deviceTreeLength = &BA2->deviceTreeLength; + + if (BA2->flags & kBootArgsFlagCSRActiveConfig) { + mBootArgs.csrActiveConfig = &BA2->csrActiveConfig; + } + } + + return &mBootArgs; +} + +CONST CHAR8 * +GetArgumentFromCommandLine ( + IN CONST CHAR8 *CommandLine, + IN CONST CHAR8 *Argument, + IN CONST UINTN ArgumentLength + ) +{ + CHAR8 *Str; + + Str = AsciiStrStr (CommandLine, Argument); + + // + // Invalidate found boot arg if: + // - it is neither the beginning of Cmd, nor has space prefix -> boot arg is a suffix of another arg + // - it has neither space suffix, nor \0 suffix, and does not end with = -> boot arg is a prefix of another arg + // + if (!Str || (Str != CommandLine && *(Str - 1) != ' ') || + (Str[ArgumentLength] != ' ' && Str[ArgumentLength] != '\0' && + Str[ArgumentLength - 1] != '=')) { + return NULL; + } + + return Str; +} + +VOID +RemoveArgumentFromCommandLine ( + IN OUT CHAR8 *CommandLine, + IN CONST CHAR8 *Argument + ) +{ + CHAR8 *Match = NULL; + + do { + Match = AsciiStrStr (CommandLine, Argument); + if (Match && (Match == CommandLine || *(Match - 1) == ' ')) { + while (*Match != ' ' && *Match != '\0') { + *Match++ = ' '; + } + } + } while (Match != NULL); + + // + // Write zeroes to reduce data leak + // + CHAR8 *Updated = CommandLine; + + while (CommandLine[0] == ' ') { + CommandLine++; + } + + while (CommandLine[0] != '\0') { + while (CommandLine[0] == ' ' && CommandLine[1] == ' ') { + CommandLine++; + } + + *Updated++ = *CommandLine++; + } + + ZeroMem (Updated, CommandLine - Updated); +} + +BOOLEAN +AppendArgumentToCommandLine ( + IN OUT CHAR8 *CommandLine, + IN CONST CHAR8 *Argument, + IN CONST UINTN ArgumentLength + ) +{ + UINTN Len = AsciiStrLen (CommandLine); + + // + // Account for extra space. + // + if (Len + (Len > 0 ? 1 : 0) + ArgumentLength >= BOOT_LINE_LENGTH) { + DEBUG ((DEBUG_INFO, "boot-args are invalid, ignoring\n")); + return FALSE; + } + + if (Len > 0) { + CommandLine += Len; + *CommandLine++ = ' '; + } + + AsciiStrnCpyS (CommandLine, ArgumentLength + 1, Argument, ArgumentLength + 1); + return TRUE; +} diff --git a/MemoryFix/AptioMemoryFix/BootArgs.h b/MemoryFix/AptioMemoryFix/BootArgs.h new file mode 100644 index 000000000..657ff0fa8 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/BootArgs.h @@ -0,0 +1,56 @@ +/** + + Methods for finding, checking and fixing boot args + + by dmazar (defs from Clover) + +**/ + +#ifndef APTIOFIX_BOOT_ARGS_H +#define APTIOFIX_BOOT_ARGS_H + +#include +#include + + +/** Our internal structure to hold boot args params to make the code independent of the boot args version. */ +typedef struct { + UINT32 *MemoryMap; /* We will change this value so we need pointer to original field. */ + UINT32 *MemoryMapSize; + UINT32 *MemoryMapDescriptorSize; + UINT32 *MemoryMapDescriptorVersion; + + CHAR8 *CommandLine; + + UINT32 *deviceTreeP; + UINT32 *deviceTreeLength; + + UINT32 *csrActiveConfig; +} AMF_BOOT_ARGUMENTS; + +AMF_BOOT_ARGUMENTS * +GetBootArgs ( + IN VOID *BootArgs + ); + +CONST CHAR8 * +GetArgumentFromCommandLine ( + IN CONST CHAR8 *CommandLine, + IN CONST CHAR8 *Argument, + IN CONST UINTN ArgumentLength + ); + +VOID +RemoveArgumentFromCommandLine ( + IN OUT CHAR8 *CommandLine, + IN CONST CHAR8 *Argument + ); + +BOOLEAN +AppendArgumentToCommandLine ( + IN OUT CHAR8 *CommandLine, + IN CONST CHAR8 *Argument, + IN CONST UINTN ArgumentLength + ); + +#endif // APTIOFIX_BOOT_ARGS_H diff --git a/MemoryFix/AptioMemoryFix/BootFixes.c b/MemoryFix/AptioMemoryFix/BootFixes.c new file mode 100644 index 000000000..2ce0b8e17 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/BootFixes.c @@ -0,0 +1,559 @@ +/** + + Methods for setting callback jump from kernel entry point, callback, fixes to kernel boot image. + + by dmazar + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Config.h" +#include "BootFixes.h" +#include "AsmFuncs.h" +#include "BootArgs.h" +#include "CustomSlide.h" +#include "MemoryMap.h" +#include "RtShims.h" +#include "VMem.h" + +EFI_PHYSICAL_ADDRESS gSysTableRtArea; +EFI_PHYSICAL_ADDRESS gRelocatedSysTableRtArea; + +BOOLEAN gHibernateWake; +BOOLEAN gDumpMemArgPresent; +BOOLEAN gSlideArgPresent; +BOOLEAN gHasBrokenS4MemoryMap; +BOOLEAN gHasBrokenS4Allocator; + +// +// Buffer and size for original kernel entry code +// +STATIC UINT8 mOrigKernelCode[32]; +STATIC UINTN mOrigKernelCodeSize; + +// +// Buffer for virtual address map - only for RT areas +// Note: DescriptorSize is usually > sizeof(EFI_MEMORY_DESCRIPTOR), +// so this buffer can hold less than 64 descriptors +// +STATIC EFI_MEMORY_DESCRIPTOR mVirtualMemoryMap[64]; +STATIC UINTN mVirtualMapSize; +STATIC UINTN mVirtualMapDescriptorSize; + +/** Fixes stuff when booting without relocation block. Called when boot.efi jumps to kernel. */ +STATIC +VOID +UpdateEnvironmentForBooting ( + UINTN BootArgs + ) +{ + AMF_BOOT_ARGUMENTS *BA; + UINTN MemoryMapSize; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN DescriptorSize; + + BA = GetBootArgs ((VOID *)BootArgs); + + // + // Restore the variables we tempered with to support custom slides. + // + RestoreCustomSlideOverrides (BA); + + MemoryMapSize = *BA->MemoryMapSize; + MemoryMap = (EFI_MEMORY_DESCRIPTOR *)(UINTN)(*BA->MemoryMap); + DescriptorSize = *BA->MemoryMapDescriptorSize; + + // + // We must restore EfiRuntimeServicesCode memory areas, because otherwise + // RuntimeServices won't be executable. + // + RestoreProtectedRtMemoryTypes (MemoryMapSize, DescriptorSize, MemoryMap); +} + +/** Fixes stuff when waking from hibernate without relocation block. Called when boot.efi jumps to kernel. */ +STATIC +VOID +UpdateEnvironmentForHibernateWake ( + UINTN ImageHeaderPage + ) +{ + IOHibernateImageHeader *ImageHeader; + IOHibernateHandoff *Handoff; + + ImageHeader = (IOHibernateImageHeader *)(ImageHeaderPage << EFI_PAGE_SHIFT); + + // + // Pass our relocated copy of system table + // + ImageHeader->systemTableOffset = (UINT32)(UINTN)(gRelocatedSysTableRtArea - ImageHeader->runtimePages); + + // + // When reusing the original memory mapping we do not have to restore memory protection types & attributes, + // since the new memory map is discarded anyway. + // Otherwise we must restore memory map types just like at a normal boot, because MMIO regions are not + // mapped as executable by XNU. + // + // Due to a non-contiguous RT_Code/RT_Data areas (thanks to NVRAM hack) the original areas + // will not be unmapped and this will result in a memory leak if some new runtime pages are added. + // But even that should not cause crashes. + // + Handoff = (IOHibernateHandoff *)((UINTN)ImageHeader->handoffPages << EFI_PAGE_SHIFT); + while (Handoff->type != kIOHibernateHandoffTypeEnd) { + if (Handoff->type == kIOHibernateHandoffTypeMemoryMap) { + if (gHasBrokenS4MemoryMap) { + // + // Some firmwares provide us a (supposedly) invalid memory map, which results in + // broken NVRAM and crashes after waking from hibernation. These firmwares for + // whatever reason have code to ensure the same memory map over the reboots, and + // it is a way some Windows versions hibernate. While terrible, we just discard + // the new memory map here, and let XNU use what it has. + // + Handoff->type = kIOHibernateHandoffType; + } else { + // + // boot.efi removes any memory from the memory map but the one with runtime attribute. + // + RestoreProtectedRtMemoryTypes (Handoff->bytecount, mVirtualMapDescriptorSize, (EFI_MEMORY_DESCRIPTOR *)Handoff->data); + } + break; + } + Handoff = (IOHibernateHandoff *)(UINTN)((UINTN)Handoff + sizeof(Handoff) + Handoff->bytecount); + } +} + +VOID +ApplyFirmwareQuirks ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + // + // Detect broken firmwares. + // + if (SystemTable->FirmwareVendor) { + if (!StrCmp (SystemTable->FirmwareVendor, L"American Megatrends")) { + // + // All APTIO firmwares provide invalid memory map after waking from + // hibernation. This results in not working NVRAM or crashes. + // FIXME: Find the root cause of the problem. + // + gHasBrokenS4MemoryMap = TRUE; + } else if (!StrCmp (SystemTable->FirmwareVendor, L"INSYDE Corp.")) { + // + // At least some INSYDE firmwares have NVRAM issues just like APTIO. + // Some are heard not to, but we are not aware of it. + // FIXME: In addition to that we have difficulties allocating RtShims + // on INSYDE, some address lead to reboots after hibernate wake. + // + gHasBrokenS4MemoryMap = TRUE; + gHasBrokenS4Allocator = TRUE; + } + } +} + +VOID +ReadBooterArguments ( + CHAR16 *Options, + UINTN OptionsSize + ) +{ + CHAR8 BootArgsVar[BOOT_LINE_LENGTH]; + UINTN BootArgsVarLen = BOOT_LINE_LENGTH; + EFI_STATUS Status; + UINTN LastIndex; + CHAR16 Last; + + if (Options && OptionsSize > 0) { + // + // Just in case we do not have 0-termination. + // This may cut some data with unexpected options, but it is not like we care. + // + LastIndex = OptionsSize - 1; + Last = Options[LastIndex]; + Options[LastIndex] = '\0'; + + UnicodeStrToAsciiStrS (Options, BootArgsVar, BOOT_LINE_LENGTH); + + if (GetArgumentFromCommandLine (BootArgsVar, "slide=", L_STR_LEN ("slide="))) { + gSlideArgPresent = TRUE; + } + +#if APTIOFIX_ALLOW_MEMORY_DUMP_ARG == 1 + if (GetArgumentFromCommandLine (BootArgsVar, "-aptiodump", L_STR_LEN ("-aptiodump"))) { + gDumpMemArgPresent = TRUE; + } +#endif + + // + // Options do not belong to us, restore the changed value + // + Options[LastIndex] = Last; + } + + // + // Important to avoid triggering boot-args wrapper too early + // + Status = OrgGetVariable ( + L"boot-args", + &gAppleBootVariableGuid, + NULL, &BootArgsVarLen, + &BootArgsVar[0] + ); + + if (!EFI_ERROR (Status) && BootArgsVarLen > 0) { + // + // Just in case we do not have 0-termination + // + BootArgsVar[BootArgsVarLen-1] = '\0'; + + if (GetArgumentFromCommandLine (BootArgsVar, "slide=", L_STR_LEN ("slide="))) { + gSlideArgPresent = TRUE; + } + +#if APTIOFIX_ALLOW_MEMORY_DUMP_ARG == 1 + if (GetArgumentFromCommandLine (BootArgsVar, "-aptiodump", L_STR_LEN ("-aptiodump"))) { + gDumpMemArgPresent = TRUE; + } +#endif + } +} + +/** Saves current 64 bit state and copies JumpToKernel32 function to higher mem + * (for copying kernel back to proper place and jumping back to it). + */ +EFI_STATUS +PrepareJumpFromKernel ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS HigherMem; + UINTN Size; + + // + // Check if already prepared. + // + if (JumpToKernel32Addr != 0) { + DEBUG ((DEBUG_VERBOSE, "PrepareJumpFromKernel() - already prepared\n")); + return EFI_SUCCESS; + } + + // + // Save current 64bit state - will be restored later in callback from kernel jump. + // + AsmPrepareJumpFromKernel (); + + // + // Allocate higher memory for JumpToKernel code. + // Must be 32-bit to access via a relative jump. + // + HigherMem = BASE_4GB; + Status = AllocatePagesFromTop (EfiBootServicesCode, 1, &HigherMem, FALSE); + if (Status != EFI_SUCCESS) { + OcPrintScreen (L"AMF: Failed to allocate JumpToKernel memory - %r\n", Status); + return Status; + } + + // + // And relocate it to higher mem. + // + JumpToKernel32Addr = HigherMem + ( (UINT8 *)&JumpToKernel32 - (UINT8 *)&JumpToKernel ); + JumpToKernel64Addr = HigherMem + ( (UINT8 *)&JumpToKernel64 - (UINT8 *)&JumpToKernel ); + + Size = (UINT8 *)&JumpToKernelEnd - (UINT8 *)&JumpToKernel; + if (Size > EFI_PAGES_TO_SIZE (1)) { + OcPrintScreen (L"AMF: JumpToKernel32 size is too big - %ld\n", Size); + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem ((VOID *)(UINTN)HigherMem, (VOID *)&JumpToKernel, Size); + + DEBUG ((DEBUG_VERBOSE, "PrepareJumpFromKernel(): JumpToKernel relocated from %p, to %x, size = %x\n", + &JumpToKernel, HigherMem, Size)); + DEBUG ((DEBUG_VERBOSE, "JumpToKernel32 relocated from %p, to %x\n", &JumpToKernel32, JumpToKernel32Addr)); + DEBUG ((DEBUG_VERBOSE, "JumpToKernel64 relocated from %p, to %x\n", &JumpToKernel64, JumpToKernel64Addr)); + DEBUG ((DEBUG_VERBOSE, "SavedCR3 = %x, SavedGDTR = %x, SavedIDTR = %x\n", SavedCR3, SavedGDTR, SavedIDTR)); + DEBUG ((DEBUG_VERBOSE, "PrepareJumpFromKernel(): JumpToKernel relocated from %p, to %x, size = %x\n", + &JumpToKernel, HigherMem, Size)); + + // + // Allocate 1 RT data page for copy of EFI system table for kernel. + // This one also has to be 32-bit due to XNU BootArgs structure. + // + gSysTableRtArea = BASE_4GB; + Status = AllocatePagesFromTop (EfiRuntimeServicesData, 1, &gSysTableRtArea, FALSE); + if (Status != EFI_SUCCESS) { + OcPrintScreen (L"AMF: Failed to allocate system table memory - %r\n", Status); + return Status; + } + + DEBUG ((DEBUG_VERBOSE, "gSysTableRtArea = %lx\n", gSysTableRtArea)); + + // + // Copy sys table to the new location. + // + CopyMem ((VOID *)(UINTN)gSysTableRtArea, gST, gST->Hdr.HeaderSize); + + return Status; +} + +/** Patches kernel entry point with jump to AsmJumpFromKernel (AsmFuncsX64). This will then call KernelEntryPatchJumpBack. */ +EFI_STATUS +KernelEntryPatchJump ( + UINT32 KernelEntry + ) +{ + // + // Size of EntryPatchCode code + // + mOrigKernelCodeSize = (UINT8*)&EntryPatchCodeEnd - (UINT8*)&EntryPatchCode; + if (mOrigKernelCodeSize > sizeof (mOrigKernelCode)) { + return EFI_NOT_FOUND; + } + + // + // Save original kernel entry code + // + CopyMem ((VOID *)mOrigKernelCode, (VOID *)(UINTN)KernelEntry, mOrigKernelCodeSize); + + // + // Copy EntryPatchCode code to kernel entry address + // + CopyMem ((VOID *)(UINTN)KernelEntry, (VOID *)&EntryPatchCode, mOrigKernelCodeSize); + + // + // Pass KernelEntry to assembler funcs. + // This is not needed really, since asm code will determine kernel entry address from the stack. + // + AsmKernelEntry = KernelEntry; + + return EFI_SUCCESS; +} + +/** Reads kernel entry from Mach-O load command and patches it with jump to AsmJumpFromKernel. */ +EFI_STATUS +KernelEntryFromMachOPatchJump ( + VOID *MachOImage, + UINTN SlideAddr + ) +{ + UINTN KernelEntry; + + KernelEntry = MachoRuntimeGetEntryAddress (MachOImage); + if (KernelEntry == 0) { + return EFI_NOT_FOUND; + } + + if (SlideAddr > 0) { + KernelEntry += SlideAddr; + } + + return KernelEntryPatchJump ((UINT32)KernelEntry); +} + +/** Callback called when boot.efi jumps to kernel. */ +UINTN +EFIAPI +KernelEntryPatchJumpBack ( + UINTN Args, + BOOLEAN ModeX64 + ) +{ + if (gHibernateWake) { + UpdateEnvironmentForHibernateWake (Args); + } else { + UpdateEnvironmentForBooting (Args); + } + + // + // Restore original kernel entry code. + // + CopyMem ((VOID *)(UINTN)AsmKernelEntry, (VOID *)mOrigKernelCode, mOrigKernelCodeSize); + + return Args; +} + +/** Copies RT flagged areas to separate memmap, defines virtual to phisycal address mapping + * and calls SetVirtualAddressMap() only with that partial memmap. + * + * About partial memmap: + * Some UEFIs are converting pointers to virtual addresses even if they do not + * point to regions with RT flag. This means that those UEFIs are using + * Desc->VirtualStart even for non-RT regions. Linux had issues with this: + * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=7cb00b72876ea2451eb79d468da0e8fb9134aa8a + * They are doing it Windows way now - copying RT descriptors to separate + * mem map and passing that stripped map to SetVirtualAddressMap(). + * We'll do the same, although it seems that just assigning + * VirtualStart = PhysicalStart for non-RT areas also does the job. + * + * About virtual to phisycal mappings: + * Also adds virtual to phisycal address mappings for RT areas. This is needed since + * SetVirtualAddressMap() does not work on my Aptio without that. Probably because some driver + * has a bug and is trying to access new virtual addresses during the change. + * Linux and Windows are doing the same thing and problem is + * not visible there. + */ +EFI_STATUS +ExecSetVirtualAddressesToMemMap ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN UINT32 DescriptorVersion, + IN EFI_MEMORY_DESCRIPTOR *MemoryMap + ) +{ + UINTN NumEntries; + UINTN Index; + EFI_MEMORY_DESCRIPTOR *Desc; + EFI_MEMORY_DESCRIPTOR *VirtualDesc; + EFI_STATUS Status; + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable; + UINTN Flags; + UINTN BlockSize; + + Desc = MemoryMap; + NumEntries = MemoryMapSize / DescriptorSize; + VirtualDesc = mVirtualMemoryMap; + mVirtualMapSize = 0; + mVirtualMapDescriptorSize = DescriptorSize; + DEBUG ((DEBUG_VERBOSE, "ExecSetVirtualAddressesToMemMap: Size=%d, Addr=%p, DescSize=%d\n", MemoryMapSize, MemoryMap, DescriptorSize)); + + // + // Get current VM page table + // + GetCurrentPageTable (&PageTable, &Flags); + + for (Index = 0; Index < NumEntries; Index++) { + // + // Some UEFIs end up with "reserved" area with EFI_MEMORY_RUNTIME flag set when Intel HD3000 or HD4000 is used. + // For example, on GA-H81N-D2H there is a single 1 GB descriptor: + // 000000009F800000-00000000DF9FFFFF 0000000000040200 8000000000000000 + // + // All known boot.efi starting from at least 10.5.8 properly handle this flag and do not assign virtual addresses + // to reserved descriptors. + // However, the issue was with AptioFix itself, which did not check for EfiReservedMemoryType and replaced + // it by EfiMemoryMappedIO to prevent boot.efi relocations. + // + // The relevant discussion and the original fix can be found here: + // http://web.archive.org/web/20141111124211/http://www.projectosx.com:80/forum/lofiversion/index.php/t2428-450.html + // https://sourceforge.net/p/cloverefiboot/code/605/ + // + // Since it is not the bug in boot.efi, AptioMemoryFix only needs to properly handle EfiReservedMemoryType with + // EFI_MEMORY_RUNTIME attribute set, and there is no reason to mess with the memory map passed to boot.efi. + // + if (Desc->Type != EfiReservedMemoryType && (Desc->Attribute & EFI_MEMORY_RUNTIME) != 0) { + // + // Check if there is enough space in mVirtualMemoryMap. + // + if (mVirtualMapSize + DescriptorSize > sizeof(mVirtualMemoryMap)) { + DEBUG ((DEBUG_INFO, "ERROR: too much mem map RT areas\n")); + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy region with EFI_MEMORY_RUNTIME flag to mVirtualMemoryMap. + // + CopyMem ((VOID*)VirtualDesc, (VOID*)Desc, DescriptorSize); + + // + // Define virtual to phisical mapping. + // + DEBUG ((DEBUG_VERBOSE, "Map pages: %lx (%x) -> %lx\n", Desc->VirtualStart, Desc->NumberOfPages, Desc->PhysicalStart)); + VmMapVirtualPages (PageTable, Desc->VirtualStart, Desc->NumberOfPages, Desc->PhysicalStart); + + // + // Next mVirtualMemoryMap slot. + // + VirtualDesc = NEXT_MEMORY_DESCRIPTOR (VirtualDesc, DescriptorSize); + mVirtualMapSize += DescriptorSize; + + // + // Remember future physical address for the relocated system table. + // + BlockSize = EFI_PAGES_TO_SIZE ((UINTN)Desc->NumberOfPages); + if (Desc->PhysicalStart <= gSysTableRtArea && gSysTableRtArea < (Desc->PhysicalStart + BlockSize)) { + // + // Future physical = VirtualStart & 0x7FFFFFFFFF + // + gRelocatedSysTableRtArea = (Desc->VirtualStart & 0x7FFFFFFFFF) + (gSysTableRtArea - Desc->PhysicalStart); + } + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + } + + VmFlushCaches (); + + DEBUG ((DEBUG_VERBOSE, "ExecSetVirtualAddressesToMemMap: Size=%d, Addr=%p, DescSize=%d\nSetVirtualAddressMap ... ", + mVirtualMapSize, MemoryMap, DescriptorSize)); + Status = gRT->SetVirtualAddressMap (mVirtualMapSize, DescriptorSize, DescriptorVersion, mVirtualMemoryMap); + DEBUG ((DEBUG_VERBOSE, "%r\n", Status)); + + return Status; +} + +VOID +CopyEfiSysTableToRtArea ( + IN OUT UINT32 *EfiSystemTable + ) +{ + EFI_SYSTEM_TABLE *Src; + EFI_SYSTEM_TABLE *Dest; + + Src = (EFI_SYSTEM_TABLE*)(UINTN)*EfiSystemTable; + Dest = (EFI_SYSTEM_TABLE*)(UINTN)gSysTableRtArea; + + CopyMem (Dest, Src, Src->Hdr.HeaderSize); + + *EfiSystemTable = (UINT32)(UINTN)Dest; +} + +EFI_LOADED_IMAGE_PROTOCOL * +GetAppleBootLoadedImage ( + EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage = NULL; + EFI_DEVICE_PATH_PROTOCOL *CurrNode = NULL; + FILEPATH_DEVICE_PATH *LastNode = NULL; + BOOLEAN IsMacOS = FALSE; + UINTN PathLen = 0; + UINTN BootPathLen = L_STR_LEN ("boot.efi"); + UINTN Index; + + Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); + + if (!EFI_ERROR (Status) && LoadedImage->FilePath) { + for (CurrNode = LoadedImage->FilePath; !IsDevicePathEnd (CurrNode); CurrNode = NextDevicePathNode (CurrNode)) { + if (CurrNode->Type == MEDIA_DEVICE_PATH && CurrNode->SubType == MEDIA_FILEPATH_DP) { + LastNode = (FILEPATH_DEVICE_PATH *)CurrNode; + } + } + + if (LastNode) { + // + // Detect macOS by boot.efi in the bootloader name. + // + PathLen = OcFileDevicePathNameLen (LastNode); + if (PathLen >= BootPathLen) { + Index = PathLen - BootPathLen; + IsMacOS = (Index == 0 || LastNode->PathName[Index - 1] == L'\\') + && !CompareMem (&LastNode->PathName[Index], L"boot.efi", L_STR_SIZE (L"boot.efi")); + } + } + } + + return IsMacOS ? LoadedImage : NULL; +} diff --git a/MemoryFix/AptioMemoryFix/BootFixes.h b/MemoryFix/AptioMemoryFix/BootFixes.h new file mode 100644 index 000000000..90c654a01 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/BootFixes.h @@ -0,0 +1,93 @@ +/** + + Methods for setting callback jump from kernel entry point, callback, fixes to kernel boot image. + + by dmazar + +**/ + +#ifndef APTIOFIX_BOOT_FIXES_H +#define APTIOFIX_BOOT_FIXES_H + +#include + +// +// Original and relocated new area for EFI System Table. +// XNU requires gST pointers to be passed relative to boot.efi. +// We have to allocate a new system table and let boot.efi relocate it. +// +extern EFI_PHYSICAL_ADDRESS gSysTableRtArea; +extern EFI_PHYSICAL_ADDRESS gRelocatedSysTableRtArea; + +// +// TRUE if we are doing hibernate wake +// +extern BOOLEAN gHibernateWake; + +// +// TRUE if booting with -aptiodump +// +extern BOOLEAN gDumpMemArgPresent; + +// +// TRUE if booting with a manually specified slide=X +// +extern BOOLEAN gSlideArgPresent; + +// +// TRUE if booting on memory map unstable firmware, such as APTIO +// +extern BOOLEAN gHasBrokenS4MemoryMap; + +// +// TRUE if booting on memory allocation unstable firmware, such as INSYDE +// +extern BOOLEAN gHasBrokenS4Allocator; + +VOID +ApplyFirmwareQuirks ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +VOID +ReadBooterArguments ( + CHAR16 *Options, + UINTN OptionsSize + ); + +EFI_STATUS +PrepareJumpFromKernel ( + VOID + ); + +EFI_STATUS +KernelEntryPatchJump ( + UINT32 KernelEntry + ); + +EFI_STATUS +KernelEntryFromMachOPatchJump ( + VOID *MachOImage, + UINTN SlideAddr + ); + +EFI_STATUS +ExecSetVirtualAddressesToMemMap ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN UINT32 DescriptorVersion, + IN EFI_MEMORY_DESCRIPTOR *MemoryMap + ); + +VOID +CopyEfiSysTableToRtArea ( + IN OUT UINT32 *EfiSystemTable + ); + +EFI_LOADED_IMAGE_PROTOCOL * +GetAppleBootLoadedImage ( + EFI_HANDLE ImageHandle + ); + +#endif // APTIOFIX_BOOT_FIXES_H diff --git a/MemoryFix/AptioMemoryFix/Config.h b/MemoryFix/AptioMemoryFix/Config.h new file mode 100644 index 000000000..33dc7cb23 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/Config.h @@ -0,0 +1,123 @@ +/** + + Hack configuration + This is a gestalt of black art, do not edit. + + by vit9696 + +**/ + +#ifndef APTIOFIX_HACK_CONFIG_H +#define APTIOFIX_HACK_CONFIG_H + +/** + * Attempt to protect certain CSM memory regions from being used by the kernel (by Slice). + * On older firmwares this caused wake issues. + */ +#ifndef APTIOFIX_PROTECT_CSM_REGION +#define APTIOFIX_PROTECT_CSM_REGION 1 +#endif + +/** Forces XNU to use old UEFI memory mapping after hibernation wake. + * May cause memory corruption. See FixHibernateWakeWithoutRelocBlock for details. + */ +#ifndef APTIOFIX_HIBERNATION_FORCE_OLD_MEMORYMAP +#define APTIOFIX_HIBERNATION_FORCE_OLD_MEMORYMAP 1 +#endif + +/** When attempting to reuse old UEFI memory mapping gBS->AllocatePool seems + * to produce the same addresses way more often, and thus the system will not reboot + * when accessing RTShims after waking from hibernation. + * However, gBS->AllocatePool is dangerous, because it may overlap with the kernel + * region and break aslr. + */ +#ifndef APTIOFIX_ALLOCATE_POOL_GIVES_STABLE_ADDR +#define APTIOFIX_ALLOCATE_POOL_GIVES_STABLE_ADDR APTIOFIX_HIBERNATION_FORCE_OLD_MEMORYMAP +#endif + +/** boot.efi still tries to allocate runtime memory for reserved segments as of 10.13.3. + * This results in boot failure with "Couldn't allocate memory map" error. + * Proven by GA-H81N-D2H with a single reserved segment: + * 000000009F800000-00000000DF9FFFFF 0000000000040200 8000000000000000 + */ +#ifndef APTIOFIX_PROTECT_RESERVED_MEMORY +#define APTIOFIX_PROTECT_RESERVED_MEMORY 1 +#endif + +/** It is believed that boot.efi on Sandy & Ivy skips 0x10200000 bytes from 0x10000000 + * to protect from IGPU bugs, yet if this memory is marked available, it will may be + * used by XNU. So far attempts to enable this did not show any pattern but boot failures. + */ + +#define APTIOFIX_SLICE_OVERLAPPING_REGION_FIX 1 + +#ifndef APTIOFIX_PROTECT_IGPU_SANDY_IVY_RESERVED_MEMORY +#define APTIOFIX_PROTECT_IGPU_SANDY_IVY_RESERVED_MEMORY 1 +#endif + +/** Attempt to protect some memory region from being used by the kernel (by Slice). + * It is believed to cause sleep issues on some systems, because this region + * is generally marked as conventional memory. + */ +#ifndef APTIOFIX_UNMARKED_OVERLAPPING_REGION_FIX +#define APTIOFIX_UNMARKED_OVERLAPPING_REGION_FIX 1 +#endif + +/** Calculate aslr slide ourselves when some addresses are not available for XNU. */ +#ifndef APTIOFIX_ALLOW_CUSTOM_ASLR_IMPLEMENTATION +#define APTIOFIX_ALLOW_CUSTOM_ASLR_IMPLEMENTATION 1 +#endif + +/** This is important for several boards that cannot boot with slide=0, which safe mode enforces. */ +#ifndef APTIOFIX_ALLOW_ASLR_IN_SAFE_MODE +#define APTIOFIX_ALLOW_ASLR_IN_SAFE_MODE 1 +#endif + +/** Hide slide=x value from os for increased security when using custom aslr. */ +#ifndef APTIOFIX_CLEANUP_SLIDE_BOOT_ARGUMENT +#define APTIOFIX_CLEANUP_SLIDE_BOOT_ARGUMENT APTIOFIX_ALLOW_CUSTOM_ASLR_IMPLEMENTATION +#endif + +/** + * Speculated maximum kernel size (in bytes) to use when looking for a free memory region. + * Used by APTIOFIX_ALLOW_CUSTOM_ASLR_IMPLEMENTATION to determine valid slide values. + * 10.12.6 allocates at least approximately 287 MBs, we round it to 384 MBs + * This seems to work pretty well on X299. Yet it may be a good idea to make a boot-arg. + */ +#ifndef APTIOFIX_SPECULATED_KERNEL_SIZE +#define APTIOFIX_SPECULATED_KERNEL_SIZE ((UINTN)0x18000000) +#endif + +/** Maximum number of supported runtime reloc protection areas */ +#ifndef APTIFIX_MAX_RT_RELOC_NUM +#define APTIFIX_MAX_RT_RELOC_NUM ((UINTN)64) +#endif + +/** + * Perform invasive memory dumps when -aptiodump -v are passed to boot.efi. + * This allows to reliably get the memory maps when in-OS dtrace script is broken. + * Enable for development and testing purposes. + */ +#ifndef APTIOFIX_ALLOW_MEMORY_DUMP_ARG +#define APTIOFIX_ALLOW_MEMORY_DUMP_ARG 0 +#endif + +/** + * Performing memory map dumps may alter memory map contents themselves, so it is important to ensure + * no memory is allocated during the dump process. This is especially crtitical for most ASUS APTIO V + * boards for Skylake and newer, which may crash after bootng. + */ +#ifndef APTIOFIX_CUSTOM_POOL_ALLOCATOR +#define APTIOFIX_CUSTOM_POOL_ALLOCATOR APTIOFIX_ALLOW_MEMORY_DUMP_ARG +#endif + +/** + * Maximum reserved area used by the custom pool allocator. This area must be large enough + * to fit the screen buffer, but considerably small to avoid colliding with the kernel area. + * 32 MB appears experimentally proven to be good enough for most of the boards. + */ +#ifndef APTIOFIX_CUSTOM_POOL_ALLOCATOR_SIZE +#define APTIOFIX_CUSTOM_POOL_ALLOCATOR_SIZE 0x2000000 +#endif + +#endif // APTIOFIX_HACK_CONFIG_H diff --git a/MemoryFix/AptioMemoryFix/CustomSlide.c b/MemoryFix/AptioMemoryFix/CustomSlide.c new file mode 100644 index 000000000..3b3758076 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/CustomSlide.c @@ -0,0 +1,664 @@ +/** + + Allows to choose a random KASLR slide offset, + when some offsets cannot be used. + + by Download-Fritz & vit9696 + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Config.h" +#include "CustomSlide.h" +#include "BootArgs.h" +#include "BootFixes.h" +#include "MemoryMap.h" +#include "RtShims.h" +#include "ServiceOverrides.h" + +// +// Modified boot-args buffer with an additional slide parameter, when custom slide is used. +// +STATIC BOOLEAN mStoredBootArgsVarSet; +STATIC UINTN mStoredBootArgsVarSize; +STATIC CHAR8 mStoredBootArgsVar[BOOT_LINE_LENGTH]; + +// +// Memory map slide availability analysis status. +// +STATIC BOOLEAN mAnalyzeMemoryMapDone; + +// +// Original csr-active-config value to be restored before kernel handoff. +// +STATIC BOOLEAN mCsrActiveConfigSet; +STATIC UINT32 mCsrActiveConfig; + +// +// List of KASLR slides that do not conflict with the previously allocated memory. +// +STATIC UINT8 mValidSlides[TOTAL_SLIDE_NUM]; +STATIC UINT32 mValidSlidesNum = TOTAL_SLIDE_NUM; + +// +// Detect Sandy or Ivy Bridge CPUs, since they use a different slide formula. +// +STATIC BOOLEAN mSandyOrIvy; +STATIC BOOLEAN mSandyOrIvySet; + +STATIC +BOOLEAN +IsSandyOrIvy ( + VOID + ) +{ + CPU_MICROCODE_PROCESSOR_SIGNATURE Sig; + UINT32 CpuFamily; + UINT32 CpuModel; + + if (!mSandyOrIvySet) { + Sig.Uint32 = 0; + + AsmCpuid (1, &Sig.Uint32, NULL, NULL, NULL); + + CpuFamily = Sig.Bits.Family; + if (CpuFamily == 15) { + CpuFamily += Sig.Bits.ExtendedFamily; + } + + CpuModel = Sig.Bits.Model; + if (CpuFamily == 15 || CpuFamily == 6) { + CpuModel |= Sig.Bits.ExtendedModel << 4; + } + + mSandyOrIvy = CpuFamily == 6 && (CpuModel == 0x2A || CpuModel == 0x3A); + mSandyOrIvySet = TRUE; + + DEBUG ((DEBUG_VERBOSE, "Discovered CpuFamily %d CpuModel %d SandyOrIvy %d\n", CpuFamily, CpuModel, mSandyOrIvy)); + } + + return mSandyOrIvy; +} + +STATIC +VOID +GetSlideRangeForValue ( + UINT8 Slide, + UINTN *StartAddr, + UINTN *EndAddr + ) +{ + *StartAddr = (UINTN)Slide * SLIDE_GRANULARITY + BASE_KERNEL_ADDR; + + // + // Skip ranges used by Intel HD 2000/3000. + // + if (Slide >= 0x80 && IsSandyOrIvy ()) { + *StartAddr += 0x10200000; + } + + *EndAddr = *StartAddr + APTIOFIX_SPECULATED_KERNEL_SIZE; +} + +STATIC +UINT8 +GenerateRandomSlideValue ( + VOID + ) +{ + UINT32 Clock = 0; + UINT32 Ecx = 0; + UINT8 Slide = 0; + UINT16 Value = 0; + BOOLEAN RdRandSupport; + + AsmCpuid (0x1, NULL, NULL, &Ecx, NULL); + RdRandSupport = (Ecx & 0x40000000) != 0; + + do { + if (RdRandSupport && GetRandomNumber16 (&Value) == EFI_SUCCESS && (UINT8) Value != 0) { + Slide = (UINT8) Value; + } else { + Clock = (UINT32) AsmReadTsc (); + Slide = (Clock & 0xFF) ^ ((Clock >> 8) & 0xFF); + } + } while (Slide == 0); + + DEBUG ((DEBUG_VERBOSE, "Generated slide index %d value %d\n", Slide, mValidSlides[Slide % mValidSlidesNum])); + + // + //FIXME: This is bad due to uneven distribution, but let's use it for now. + // + return mValidSlides[Slide % mValidSlidesNum]; +} + +#if APTIOFIX_CLEANUP_SLIDE_BOOT_ARGUMENT == 1 +STATIC +VOID +HideSlideFromOS ( + AMF_BOOT_ARGUMENTS *BootArgs + ) +{ + EFI_STATUS Status; + DTEntry Chosen; + CHAR8 *ArgsStr; + UINT32 ArgsSize; + + // + // First, there is a BootArgs entry for XNU + // + RemoveArgumentFromCommandLine (BootArgs->CommandLine, "slide="); + + // + // Second, there is a DT entry + // + DTInit ((VOID *)(UINTN)(*BootArgs->deviceTreeP), BootArgs->deviceTreeLength); + Status = DTLookupEntry (NULL, "/chosen", &Chosen); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "Found /chosen\n")); + Status = DTGetProperty (Chosen, "boot-args", (VOID **)&ArgsStr, &ArgsSize); + if (!EFI_ERROR (Status) && ArgsSize > 0) { + DEBUG ((DEBUG_VERBOSE, "Found boot-args in /chosen\n")); + RemoveArgumentFromCommandLine (ArgsStr, "slide="); + } + } + + // + // Third, clean the boot args just in case + // + mValidSlidesNum = 0; + mStoredBootArgsVarSize = 0; + ZeroMem (mValidSlides, sizeof(mValidSlides)); + ZeroMem (mStoredBootArgsVar, sizeof(mStoredBootArgsVar)); +} +#endif + +STATIC +VOID +DecideOnCustomSlideImplementation ( + VOID + ) +{ + UINTN AllocatedMapPages; + UINTN MemoryMapSize; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN MapKey; + EFI_STATUS Status; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + UINTN Index; + UINTN Slide; + UINTN NumEntries; + UINTN MaxAvailableSize = 0; + UINT8 FallbackSlide = 0; + + Status = GetMemoryMapAlloc ( + &AllocatedMapPages, + &MemoryMapSize, + &MemoryMap, + &MapKey, + &DescriptorSize, + &DescriptorVersion + ); + + if (Status != EFI_SUCCESS) { + OcPrintScreen (L"AMF: Failed to obtain memory map for KASLR - %r\n", Status); + return; + } + + // + // At this point we have a memory map that we could use to determine what slide values are allowed. + // + NumEntries = MemoryMapSize / DescriptorSize; + + // + // Reset valid slides to zero and find actually working ones. + // + mValidSlidesNum = 0; + + for (Slide = 0; Slide < TOTAL_SLIDE_NUM; Slide++) { + EFI_MEMORY_DESCRIPTOR *Desc = MemoryMap; + BOOLEAN Supported = TRUE; + UINTN StartAddr; + UINTN EndAddr; + UINTN DescEndAddr; + UINTN AvailableSize; + + GetSlideRangeForValue ((UINT8)Slide, &StartAddr, &EndAddr); + + AvailableSize = 0; + + for (Index = 0; Index < NumEntries; Index++) { + DescEndAddr = (Desc->PhysicalStart + EFI_PAGES_TO_SIZE (Desc->NumberOfPages)); + + if ((Desc->PhysicalStart < EndAddr) && (DescEndAddr > StartAddr)) { + // + // The memory overlaps with the slide region. + // + if (Desc->Type != EfiConventionalMemory) { + // + // The memory is unusable atm. + // + Supported = FALSE; + break; + } else { + // + // The memory will be available for the kernel. + // + AvailableSize += EFI_PAGES_TO_SIZE (Desc->NumberOfPages); + + if (Desc->PhysicalStart < StartAddr) { + // + // The region starts before the slide region. + // Subtract the memory that is located before the slide region. + // + AvailableSize -= (StartAddr - Desc->PhysicalStart); + } + + if (DescEndAddr > EndAddr) { + // + // The region ends after the slide region. + // Subtract the memory that is located after the slide region. + // + AvailableSize -= (DescEndAddr - EndAddr); + } + } + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + } + + if (AvailableSize > MaxAvailableSize) { + MaxAvailableSize = AvailableSize; + FallbackSlide = (UINT8)Slide; + } + + if ((StartAddr + AvailableSize) != EndAddr) { + // + // The slide region is not continuous. + // + Supported = FALSE; + } + + if (Supported) { + DEBUG ((DEBUG_VERBOSE, "Slide %03d at %08x:%08x should be ok.\n", (UINT32)Slide, (UINT32)StartAddr, (UINT32)EndAddr)); + mValidSlides[mValidSlidesNum++] = (UINT8)Slide; + } else { + DEBUG ((DEBUG_VERBOSE, "Slide %03d at %08x:%08x cannot be used!\n", (UINT32)Slide, (UINT32)StartAddr, (UINT32)EndAddr)); + } + } + + gBS->FreePages ((EFI_PHYSICAL_ADDRESS)MemoryMap, AllocatedMapPages); + + if (mValidSlidesNum != TOTAL_SLIDE_NUM) { + if (mValidSlidesNum == 0) { + OcPrintScreen (L"AMF: No slide values are usable! Falling back to %d with 0x%08X bytes!\n", FallbackSlide, MaxAvailableSize); + mValidSlides[mValidSlidesNum++] = (UINT8)FallbackSlide; + } else { + // + // Pretty-print valid slides as ranges. + // For example, 1, 2, 3, 4, 5 will become 1-5. + // + OcPrintScreen (L"AMF: Only %d/%d slide values are usable!\n", mValidSlidesNum, TOTAL_SLIDE_NUM); + NumEntries = 0; + for (Index = 0; Index <= mValidSlidesNum; Index++) { + if (Index == 0) { + OcPrintScreen (L"Valid slides: %d", mValidSlides[Index]); + } else if (Index == mValidSlidesNum || mValidSlides[Index - 1] + 1 != mValidSlides[Index]) { + if (NumEntries == 1) { + OcPrintScreen (L", %d", mValidSlides[Index - 1]); + } else if (NumEntries > 1) { + OcPrintScreen (L"-%d", mValidSlides[Index - 1]); + } + if (Index == mValidSlidesNum) { + OcPrintScreen (L"\n"); + } else { + OcPrintScreen (L", %d", mValidSlides[Index]); + } + NumEntries = 0; + } else { + NumEntries++; + } + } + } + } +} + +STATIC +EFI_STATUS +GetVariableCsrActiveConfig ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_STATUS Status; + UINT32 *Config; + + // + // If we were asked for the size, just return it right away. + // + if (!Data || *DataSize < sizeof(UINT32)) { + *DataSize = sizeof(UINT32); + return EFI_BUFFER_TOO_SMALL; + } + + Config = (UINT32 *)Data; + + // + // Otherwise call the original function. + // + Status = OrgGetVariable (VariableName, VendorGuid, Attributes, DataSize, Data); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "GetVariable csr-active-config returned %r\n", Status)); + + *Config = 0; + Status = EFI_SUCCESS; + if (Attributes) { + *Attributes = + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE; + } + } + + // + // We must unrestrict NVRAM from SIP or slide=X will not be supported. + // + mCsrActiveConfig = *Config; + mCsrActiveConfigSet = TRUE; + *Config |= CSR_ALLOW_UNRESTRICTED_NVRAM; + + return Status; +} + +STATIC +EFI_STATUS +GetVariableBootArgs ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_STATUS Status; + UINTN StoredBootArgsSize = BOOT_LINE_LENGTH; + UINT8 Slide; + CHAR8 SlideArgument[10]; + CONST UINTN SlideArgumentLength = ARRAY_SIZE (SlideArgument)-1; + + if (!mStoredBootArgsVarSet) { + Slide = GenerateRandomSlideValue (); + Status = OrgGetVariable (VariableName, VendorGuid, Attributes, &StoredBootArgsSize, mStoredBootArgsVar); + if (EFI_ERROR (Status)) { + mStoredBootArgsVar[0] = '\0'; + } + + // + // Note, the point is to always pass 3 characters to avoid side attacks on value length. + // + AsciiSPrint (SlideArgument, ARRAY_SIZE (SlideArgument), "slide=%-03d", Slide); + + if (!AppendArgumentToCommandLine (mStoredBootArgsVar, SlideArgument, SlideArgumentLength)) { + // + // Broken boot-args, try to overwrite. + // + AsciiStrnCpyS (mStoredBootArgsVar, SlideArgumentLength + 1, SlideArgument, SlideArgumentLength + 1);; + } + + mStoredBootArgsVarSize = AsciiStrLen (mStoredBootArgsVar) + 1; + mStoredBootArgsVarSet = TRUE; + } + + if (Attributes) { + *Attributes = + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE; + } + + if (*DataSize >= mStoredBootArgsVarSize && Data) { + AsciiStrnCpyS (Data, *DataSize, mStoredBootArgsVar, mStoredBootArgsVarSize); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *DataSize = mStoredBootArgsVarSize; + + return Status; +} + +VOID +UnlockSlideSupportForSafeMode ( + UINT8 *ImageBase, + UINTN ImageSize + ) +{ + // + // boot.efi performs the following check: + // if (State & (BOOT_MODE_SAFE | BOOT_MODE_ASLR)) == (BOOT_MODE_SAFE | BOOT_MODE_ASLR)) { + // * Disable KASLR * + // } + // We do not care about the asm it will use for it, but we could assume that the constants + // will be used twice and their location will be very close to each other. + // + // BOOT_MODE_SAFE | BOOT_MODE_ASLR constant is 0x4001 in hex. + // It has not changed since its appearance, so is most likely safe to look for. + // Furthermore, since boot.efi state mask uses higher bits, it is safe to assume that + // the comparison will be at least 32-bit. + // + // + // The new way patch is a workaround for 10.13.5 and newer, where the code got finally changed. + // if (State & BOOT_MODE_SAFE) { + // ReportFeature(FEATURE_BOOT_MODE_SAFE); + // if (State & BOOT_MODE_ASLR) { + // * Disable KASLR * + // } + // } + // + CONST UINT8 SearchSeqNew[] = {0xF6, 0xC4, 0x40, 0x75}; + CONST UINT8 SearchSeq[] = {0x01, 0x40, 0x00, 0x00}; + + // + // This is a reasonable maximum distance to expect between the instructions. + // + CONST UINTN MaxDist = 0x10; + + UINT8 *StartOff = ImageBase; + UINT8 *EndOff = StartOff + ImageSize - sizeof (SearchSeq) - MaxDist; + + UINTN FirstOff = 0; + UINTN SecondOff = 0; + + BOOLEAN NewWay = FALSE; + + do { + while (StartOff + FirstOff <= EndOff) { + if (!CompareMem (StartOff + FirstOff, SearchSeqNew, sizeof (SearchSeqNew))) { + NewWay = TRUE; + break; + } else if (!CompareMem (StartOff + FirstOff, SearchSeq, sizeof (SearchSeq))) { + break; + } + FirstOff++; + } + + DEBUG ((DEBUG_VERBOSE, "Found first %d at off %X\n", (UINT32)NewWay, (UINT32)FirstOff)); + + if (StartOff + FirstOff > EndOff) { + DEBUG ((DEBUG_INFO, "Failed to find first BOOT_MODE_SAFE | BOOT_MODE_ASLR sequence\n")); + break; + } + + if (NewWay) { + // + // Here we just patch the comparison code and the check by straight nopping. + // + DEBUG ((DEBUG_VERBOSE, "Patching new safe mode aslr check...\n")); + SetMem (StartOff + FirstOff, sizeof (SearchSeqNew) + 1, 0x90); + return; + } + + SecondOff = FirstOff + sizeof (SearchSeq); + + while ( + StartOff + SecondOff <= EndOff && FirstOff + MaxDist >= SecondOff && + CompareMem (StartOff + SecondOff, SearchSeq, sizeof (SearchSeq))) { + SecondOff++; + } + + DEBUG ((DEBUG_VERBOSE, "Found second at off %X\n", (UINT32)SecondOff)); + + if (FirstOff + MaxDist < SecondOff) { + DEBUG ((DEBUG_VERBOSE, "Trying next match...\n")); + SecondOff = 0; + FirstOff += sizeof (SearchSeq); + } + } while (SecondOff == 0); + + if (SecondOff != 0) { + // + // Here we use 0xFFFFFFFF constant as a replacement value. + // Since the state values are contradictive (e.g. safe & single at the same time) + // We are allowed to use this instead of to simulate if (false). + // + DEBUG ((DEBUG_VERBOSE, "Patching safe mode aslr check...\n")); + SetMem (StartOff + FirstOff, sizeof (SearchSeq), 0xFF); + SetMem (StartOff + SecondOff, sizeof (SearchSeq), 0xFF); + } +} + +BOOLEAN +OverlapsWithSlide ( + EFI_PHYSICAL_ADDRESS Address, + UINTN Size + ) +{ + BOOLEAN SandyOrIvy; + EFI_PHYSICAL_ADDRESS Start; + EFI_PHYSICAL_ADDRESS End; + UINTN Slide = 0xFF; + + SandyOrIvy = IsSandyOrIvy (); + + if (SandyOrIvy) { + Slide = 0x7F; + } + + Start = BASE_KERNEL_ADDR; + End = Start + Slide * SLIDE_GRANULARITY + APTIOFIX_SPECULATED_KERNEL_SIZE; + + if (End >= Address && Start <= Address + Size) { + return TRUE; + } else if (SandyOrIvy) { + Start = 0x80 * SLIDE_GRANULARITY + BASE_KERNEL_ADDR + 0x10200000; + End = Start + Slide * SLIDE_GRANULARITY + APTIOFIX_SPECULATED_KERNEL_SIZE; + if (End >= Address && Start <= Address + Size) { + return TRUE; + } + } + + return FALSE; +} + +EFI_STATUS +EFIAPI +GetVariableCustomSlide ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + if (gMacOSBootNestedCount > 0 && VariableName && VendorGuid && DataSize && + CompareGuid (VendorGuid, &gAppleBootVariableGuid)) { + // + // We override csr-active-config with CSR_ALLOW_UNRESTRICTED_NVRAM bit set + // to allow one to pass a custom slide value even when SIP is on. + // This original value of csr-active-config is returned to OS at XNU boot. + // This allows SIP to be fully enabled in the operating system. + // + if (!StrCmp (VariableName, L"csr-active-config")) { + return GetVariableCsrActiveConfig ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + } +#if APTIOFIX_ALLOW_CUSTOM_ASLR_IMPLEMENTATION == 1 + // + // When we cannot allow some KASLR values due to used address we generate + // a random slide value among the valid options, which we we pass via boot-args. + // See DecideOnCustomSlideImplementation for more details. + // + else if (!StrCmp (VariableName, L"boot-args")) { + // + // We delay memory map analysis as much as we can, in case boot.efi or anything else allocates + // stuff with gBS->AllocatePool and it overlaps with the kernel area. + // Overriding AllocatePool with a custom allocator does not really improve the situation, + // because on older boards allocated memory above BASE_4GB causes instant reboots, and + // on the only (so far) problematic X99 and X299 we have no free region for our pool anyway. + // In any case, the current APTIOFIX_SPECULATED_KERNEL_SIZE value appears to work reliably. + // + if (!gSlideArgPresent && !mAnalyzeMemoryMapDone) { + DecideOnCustomSlideImplementation (); + mAnalyzeMemoryMapDone = TRUE; + } + // + // Only return custom boot-args if mValidSlidesNum were determined to be less than TOTAL_SLIDE_NUM + // And thus we have to use a custom slide implementation to boot reliably. + // + if (mValidSlidesNum != TOTAL_SLIDE_NUM && mValidSlidesNum > 0) { + return GetVariableBootArgs ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + } + } +#endif + } + + return OrgGetVariable (VariableName, VendorGuid, Attributes, DataSize, Data); +} + +VOID +RestoreCustomSlideOverrides ( + AMF_BOOT_ARGUMENTS *BA + ) +{ + // + // Restore csr-active-config to a value it was before our slide=X alteration. + // + if (BA->csrActiveConfig && mCsrActiveConfigSet) { + *BA->csrActiveConfig = mCsrActiveConfig; + } + +#if APTIOFIX_CLEANUP_SLIDE_BOOT_ARGUMENT == 1 + // + // Having slide=X values visible in the operating system defeats the purpose of KASLR. + // Since our custom implementation works by passing random KASLR slide via boot-args, + // this is especially important. + // + HideSlideFromOS(BA); +#endif +} diff --git a/MemoryFix/AptioMemoryFix/CustomSlide.h b/MemoryFix/AptioMemoryFix/CustomSlide.h new file mode 100644 index 000000000..7324d3765 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/CustomSlide.h @@ -0,0 +1,78 @@ +/** + + Allows to choose a random KASLR slide offset, + when some offsets cannot be used. + + by Download-Fritz & vit9696 + +**/ + +#ifndef APTIOFIX_CUSTOM_SLIDE_H +#define APTIOFIX_CUSTOM_SLIDE_H + +#include "BootArgs.h" + +// +// Base kernel address. +// +#define BASE_KERNEL_ADDR ((UINTN)0x100000) + +// +// Slide offset per slide entry +// +#define SLIDE_GRANULARITY ((UINTN)0x200000) + +// +// Total possible number of KASLR slide offsets. +// +#define TOTAL_SLIDE_NUM 256 + +/** + * Ensures that the original csr-active-config is passed to the kernel, + * and removes customised slide value for security reasons. + * @return VOID + */ +VOID +RestoreCustomSlideOverrides ( + AMF_BOOT_ARGUMENTS *BA + ); + +/** + * Patches boot.efi to support random and passed slide values in safe mode. + * @param ImageBase + * @param ImageSize + * @return VOID + */ +VOID +UnlockSlideSupportForSafeMode ( + UINT8 *ImageBase, + UINTN ImageSize + ); + +/** + * Custom gRT->GetVariable override used to return customised values + * for boot-args and csr-active-config variables. + * @return EFI_STATUS + */ +EFI_STATUS +EFIAPI +GetVariableCustomSlide ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + * Checks whether the area overlaps with a possible kernel image area. + * Returns TRUE if the given mem area overlaps, otherwise returns FALSE. + * @return TRUE or FALSE + */ +BOOLEAN +OverlapsWithSlide ( + EFI_PHYSICAL_ADDRESS Address, + UINTN Size + ); + +#endif // APTIOFIX_CUSTOM_SLIDE_H \ No newline at end of file diff --git a/MemoryFix/AptioMemoryFix/MemoryMap.c b/MemoryFix/AptioMemoryFix/MemoryMap.c new file mode 100644 index 000000000..204562f1a --- /dev/null +++ b/MemoryFix/AptioMemoryFix/MemoryMap.c @@ -0,0 +1,366 @@ +/** + + MemoryMap helper functions. + + by dmazar + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Config.h" +#include "MemoryMap.h" +#include "CustomSlide.h" +#include "ServiceOverrides.h" + +STATIC CHAR16 *mEfiMemoryTypeDesc[EfiMaxMemoryType] = { + L"Reserved", + L"LDR_code", + L"LDR_data", + L"BS_code", + L"BS_data", + L"RT_code", + L"RT_data", + L"Available", + L"Unusable", + L"ACPI_recl", + L"ACPI_NVS", + L"MemMapIO", + L"MemPortIO", + L"PAL_code" +}; + +VOID +ShrinkMemMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN DescriptorSize + ) +{ + UINTN SizeFromDescToEnd; + UINT64 Bytes; + EFI_MEMORY_DESCRIPTOR *PrevDesc; + EFI_MEMORY_DESCRIPTOR *Desc; + BOOLEAN CanBeJoined; + BOOLEAN HasEntriesToRemove; + + PrevDesc = MemoryMap; + Desc = NEXT_MEMORY_DESCRIPTOR (PrevDesc, DescriptorSize); + SizeFromDescToEnd = *MemoryMapSize - DescriptorSize; + *MemoryMapSize = DescriptorSize; + HasEntriesToRemove = FALSE; + + while (SizeFromDescToEnd > 0) { + Bytes = EFI_PAGES_TO_SIZE (PrevDesc->NumberOfPages); + CanBeJoined = FALSE; + if (Desc->Attribute == PrevDesc->Attribute && PrevDesc->PhysicalStart + Bytes == Desc->PhysicalStart) { + // + // It *should* be safe to join this with conventional memory, because the firmware should not use + // GetMemoryMap for allocation, and for the kernel it does not matter, since it joins them. + // + CanBeJoined = (Desc->Type == EfiBootServicesCode || + Desc->Type == EfiBootServicesData || + Desc->Type == EfiConventionalMemory || + Desc->Type == EfiLoaderCode || + Desc->Type == EfiLoaderData) && ( + PrevDesc->Type == EfiBootServicesCode || + PrevDesc->Type == EfiBootServicesData || + PrevDesc->Type == EfiConventionalMemory || + PrevDesc->Type == EfiLoaderCode || + PrevDesc->Type == EfiLoaderData); + } + + if (CanBeJoined) { + // + // Two entries are the same/similar - join them + // + PrevDesc->Type = EfiConventionalMemory; + PrevDesc->NumberOfPages += Desc->NumberOfPages; + HasEntriesToRemove = TRUE; + } else { + // + // Cannot be joined - we need to move to next + // + *MemoryMapSize += DescriptorSize; + PrevDesc = NEXT_MEMORY_DESCRIPTOR (PrevDesc, DescriptorSize); + if (HasEntriesToRemove) { + // + // Have entries between PrevDesc and Desc which are joined to PrevDesc, + // we need to copy [Desc, end of list] to PrevDesc + 1 + // + CopyMem(PrevDesc, Desc, SizeFromDescToEnd); + Desc = PrevDesc; + HasEntriesToRemove = FALSE; + } + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + SizeFromDescToEnd -= DescriptorSize; + } +} + +/** AMI CSM module allocates up to two regions for legacy video output. + * 1. For PMM and EBDA areas. + * On Ivy Bridge and below it ends at 0xA0000-0x1000-0x1 and has EfiBootServicesCode type. + * On Haswell and above it is allocated below 0xA0000 address with the same type. + * 2. For Intel RC S3 reserved area, fixed from 0x9F000 to 0x9FFFF. + * On Sandy Bridge and below it is not present in memory map. + * On Ivy Bridge and newer it is present as EfiRuntimeServicesData. + * Starting from at least SkyLake it is present as EfiReservedMemoryType. + * + * Prior to AptioMemoryFix EfiRuntimeServicesData could have been relocated by boot.efi, + * and the 2nd region could have been overwritten by the kernel. Now it is no longer the + * case, and only the 1st region may need special handling. + * + * For the 1st region there appear to be (unconfirmed) reports that it may still be accessed + * after waking from sleep. This does not seem to be valid according to AMI code, but we still + * protect it in case such systems really exist. + * + * Researched and fixed on gigabyte boards by Slice + */ +VOID +ProtectCsmRegion ( + UINTN MemoryMapSize, + EFI_MEMORY_DESCRIPTOR *MemoryMap, + UINTN DescriptorSize + ) +{ + UINTN NumEntries; + UINTN Index; + EFI_MEMORY_DESCRIPTOR *Desc; + UINTN BlockSize; + UINTN PhysicalEnd; + + Desc = MemoryMap; + NumEntries = MemoryMapSize / DescriptorSize; + + for (Index = 0; Index < NumEntries; Index++) { + BlockSize = EFI_PAGES_TO_SIZE ((UINTN)Desc->NumberOfPages); + PhysicalEnd = Desc->PhysicalStart + BlockSize; + + if (PhysicalEnd >= 0x9E000 && PhysicalEnd < 0xA0000 && Desc->Type == EfiBootServicesData) { + Desc->Type = EfiACPIMemoryNVS; + break; + } + + Desc = NEXT_MEMORY_DESCRIPTOR(Desc, DescriptorSize); + } +} + +VOID +PrintMemMap ( + IN CONST CHAR16 *Name, + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN VOID *Shims, + IN EFI_PHYSICAL_ADDRESS SysTable + ) +{ + UINTN NumEntries; + UINTN Index; + UINT64 Bytes; + EFI_MEMORY_DESCRIPTOR *Desc; + + // + // Printing onscreen may allocate the memory internally. + // This is very dangerous to do in GetMemoryMap or SetVirtualAddresses wrappers, + // because on many ASUS boards the internal memory map will get modified, and + // for some reason this will cause crashes right after os boots. + // + DisableDynamicPoolAllocations (); + + Desc = MemoryMap; + NumEntries = MemoryMapSize / DescriptorSize; + OcPrintScreen (L"--- Dump Memory Map (%s) start ---\n", Name); + OcPrintScreen (L"MEMMAP: Size=%d, Addr=%p, DescSize=%d, Shims=%08lX, ST=%08lX\n", + MemoryMapSize, MemoryMap, DescriptorSize, (UINTN)Shims, (UINTN)SysTable); + OcPrintScreen (L"Type Start End Virtual # Pages Attributes\n"); + for (Index = 0; Index < NumEntries; Index++) { + + Bytes = EFI_PAGES_TO_SIZE (Desc->NumberOfPages); + + OcPrintScreen (L"%-9s %010lX %010lX %016lX %010lX %016lX\n", + mEfiMemoryTypeDesc[Desc->Type], + Desc->PhysicalStart, + Desc->PhysicalStart + Bytes - 1, + Desc->VirtualStart, + Desc->NumberOfPages, + Desc->Attribute + ); + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + // + // It is often the case that memory map does not fit onscreen. + // There is no way to reliably determine the largest console window, so we just stall + // for a moment to let one read the output. + // + if ((Index + 1) % 16 == 0) + gBS->Stall (SECONDS_TO_MICROSECONDS (5)); + } + + OcPrintScreen (L"--- Dump Memory Map (%s) end ---\n", Name); + gBS->Stall (SECONDS_TO_MICROSECONDS (5)); + + EnableDynamicPoolAllocations (); +} + +EFI_STATUS +GetMemoryMapAlloc ( + IN OUT UINTN *AllocatedTopPages, + OUT UINTN *MemoryMapSize, + OUT EFI_MEMORY_DESCRIPTOR **MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ) +{ + EFI_STATUS Status; + + *MemoryMapSize = 0; + *MemoryMap = NULL; + Status = OrgGetMemoryMap ( + MemoryMapSize, + *MemoryMap, + MapKey, + DescriptorSize, + DescriptorVersion + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_INFO, "Insane GetMemoryMap %r\n", Status)); + return Status; + } + + do { + // + // This is done because extra allocations may increase memory map size. + // + *MemoryMapSize += 512; + + // + // Requested to allocate from top via pages. + // This may be needed, because the pool memory may collide with the kernel. + // + if (AllocatedTopPages) { + *MemoryMap = (EFI_MEMORY_DESCRIPTOR *)BASE_4GB; + *AllocatedTopPages = EFI_SIZE_TO_PAGES (*MemoryMapSize); + Status = AllocatePagesFromTop ( + EfiBootServicesData, + *AllocatedTopPages, + (EFI_PHYSICAL_ADDRESS *)MemoryMap, + FALSE + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "Temp memory map allocation from top failure %r\n", Status)); + *MemoryMap = NULL; + return Status; + } + } else { + *MemoryMap = AllocatePool (*MemoryMapSize); + if (!*MemoryMap) { + DEBUG ((DEBUG_INFO, "Temp memory map direct allocation failure\n")); + return EFI_OUT_OF_RESOURCES; + } + } + + Status = OrgGetMemoryMap ( + MemoryMapSize, + *MemoryMap, + MapKey, + DescriptorSize, + DescriptorVersion + ); + + if (EFI_ERROR (Status)) { + if (AllocatedTopPages) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS)*MemoryMap, *AllocatedTopPages); + } else { + FreePool (*MemoryMap); + } + *MemoryMap = NULL; + } + } while (Status == EFI_BUFFER_TOO_SMALL); + + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_INFO, "Failed to obtain memory map %r\n", Status)); + } + + return Status; +} + +EFI_STATUS +AllocatePagesFromTop ( + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN CheckRange + ) +{ + EFI_STATUS Status; + UINTN MemoryMapSize; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN MapKey; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + EFI_MEMORY_DESCRIPTOR *Desc; + + Status = GetMemoryMapAlloc (NULL, &MemoryMapSize, &MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EFI_NOT_FOUND; + + MemoryMapEnd = NEXT_MEMORY_DESCRIPTOR (MemoryMap, MemoryMapSize); + Desc = PREV_MEMORY_DESCRIPTOR (MemoryMapEnd, DescriptorSize); + + for ( ; Desc >= MemoryMap; Desc = PREV_MEMORY_DESCRIPTOR (Desc, DescriptorSize)) { + // + // We are looking for some free memory descriptor that contains enough space below the specified memory + // + if (Desc->Type == EfiConventionalMemory && Pages <= Desc->NumberOfPages && + Desc->PhysicalStart + EFI_PAGES_TO_SIZE (Pages) <= *Memory) { + // + // Free block found + // + if (Desc->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)Desc->NumberOfPages) <= *Memory) { + // + // The whole block is under Memory - allocate from the top of the block + // + *Memory = Desc->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)Desc->NumberOfPages - Pages); + } else { + // + // The block contains enough pages under Memory, but spans above it - allocate below Memory + // + *Memory = *Memory - EFI_PAGES_TO_SIZE (Pages); + } + // + // Ensure that the found block does not overlap with the kernel area + // + if (CheckRange && OverlapsWithSlide (*Memory, EFI_PAGES_TO_SIZE (Pages))) { + continue; + } + + Status = gBS->AllocatePages ( + AllocateAddress, + MemoryType, + Pages, + Memory + ); + break; + } + } + + FreePool (MemoryMap); + + return Status; +} diff --git a/MemoryFix/AptioMemoryFix/MemoryMap.h b/MemoryFix/AptioMemoryFix/MemoryMap.h new file mode 100644 index 000000000..efcb22b54 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/MemoryMap.h @@ -0,0 +1,63 @@ +/** + + MemoryMap helper functions. + + by dmazar + +**/ + +#ifndef APTIOFIX_MEMORY_MAP_H +#define APTIOFIX_MEMORY_MAP_H + +/** MemMap reversed scan */ +#define PREV_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ + ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size))) + +/** Shrinks mem map by joining non-runtime records. */ +VOID +ShrinkMemMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN DescriptorSize + ); + +/** Protects AMI CSM region from being overwritten by the kernel. */ +VOID +ProtectCsmRegion ( + UINTN MemoryMapSize, + EFI_MEMORY_DESCRIPTOR *MemoryMap, + UINTN DescriptorSize + ); + +/** Prints mem map. */ +VOID +PrintMemMap ( + IN CONST CHAR16 *Name, + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN VOID *Shims, + IN EFI_PHYSICAL_ADDRESS SysTable + ); + +/** Helper function that calls GetMemoryMap(), allocates space for mem map and returns it. */ +EFI_STATUS +GetMemoryMapAlloc ( + IN OUT UINTN *AllocatedTopPages, + OUT UINTN *MemoryMapSize, + OUT EFI_MEMORY_DESCRIPTOR **MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ); + +/** Alloctes pages from the top of mem, up to address specified in Memory. Returns allocated address in Memory. */ +EFI_STATUS +AllocatePagesFromTop ( + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN CheckRange + ); + +#endif // APTIOFIX_MEMORY_MAP_H diff --git a/MemoryFix/AptioMemoryFix/RtShims.c b/MemoryFix/AptioMemoryFix/RtShims.c new file mode 100644 index 000000000..65c2ac16b --- /dev/null +++ b/MemoryFix/AptioMemoryFix/RtShims.c @@ -0,0 +1,358 @@ +/** + + Runtime Services Wrappers. + + by Download-Fritz & vit9696 + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Config.h" +#include "RtShims.h" +#include "BootFixes.h" +#include "MemoryMap.h" + +extern UINTN gRtShimsDataStart; +extern UINTN gRtShimsDataEnd; + +extern UINTN gGetVariable; +extern UINTN gGetNextVariableName; +extern UINTN gSetVariable; +extern UINTN gGetTime; +extern UINTN gSetTime; +extern UINTN gGetWakeupTime; +extern UINTN gSetWakeupTime; +extern UINTN gGetNextHighMonoCount; +extern UINTN gResetSystem; +extern UINTN gGetVariableOverride; + +extern UINTN gRequiresWriteUnprotect; +extern UINTN gBootVariableRedirect; + +extern EFI_GUID gReadOnlyVariableGuid; +extern EFI_GUID gWriteOnlyVariableGuid; +extern EFI_GUID gBootVariableGuid; +extern EFI_GUID gRedirectVariableGuid; + +extern UINTN RtShimGetVariable; +extern UINTN RtShimGetNextVariableName; +extern UINTN RtShimSetVariable; +extern UINTN RtShimGetTime; +extern UINTN RtShimSetTime; +extern UINTN RtShimGetWakeupTime; +extern UINTN RtShimSetWakeupTime; +extern UINTN RtShimGetNextHighMonoCount; +extern UINTN RtShimResetSystem; + +VOID *gRtShims = NULL; + +STATIC BOOLEAN mRtShimsAddrUpdated = FALSE; + +STATIC RT_SHIM_PTRS mShimPtrArray[] = { + { &gGetVariable }, + { &gSetVariable }, + { &gGetNextVariableName }, + { &gGetTime }, + { &gSetTime }, + { &gGetWakeupTime }, + { &gSetWakeupTime }, + { &gGetNextHighMonoCount }, + { &gResetSystem } +}; + +STATIC RT_RELOC_PROTECT_DATA mRelocInfoData; + +VOID InstallRtShims ( + EFI_GET_VARIABLE GetVariableOverride + ) +{ + EFI_STATUS Status; + UINTN PageCount; + EFI_PHYSICAL_ADDRESS RtShims = BASE_4GB; + + // + // Support read-only and write-only variables from runtime-services. + // + CopyGuid (&gReadOnlyVariableGuid, &gOcReadOnlyVariableGuid); + CopyGuid (&gWriteOnlyVariableGuid, &gOcWriteOnlyVariableGuid); + CopyGuid (&gBootVariableGuid, &gEfiGlobalVariableGuid); + CopyGuid (&gRedirectVariableGuid, &gOcVendorVariableGuid); + + if (gHasBrokenS4Allocator) { + // + // Some firmwares appear to allocate rt shims at randomly incomprehensible area. + // This unfortunately results in crashes and using pool allocs is one of the workarounds. + // + Status = gBS->AllocatePool ( + EfiRuntimeServicesCode, + ((UINTN)&gRtShimsDataEnd - (UINTN)&gRtShimsDataStart), + &gRtShims + ); + } else { + // + // It is important to allocate properly from top on saner firmwares. + // If we do not and use hacks like above many other operating systems (like Linux) + // may stop loading. + // + PageCount = EFI_SIZE_TO_PAGES ((UINTN)&gRtShimsDataEnd - (UINTN)&gRtShimsDataStart); + Status = AllocatePagesFromTop ( + EfiRuntimeServicesCode, + PageCount, + &RtShims, + FALSE + ); + if (!EFI_ERROR (Status)) { + gRtShims = (VOID *)(UINTN)RtShims; + } + } + + if (!EFI_ERROR (Status)) { + gGetVariable = (UINTN)gRT->GetVariable; + gSetVariable = (UINTN)gRT->SetVariable; + gGetNextVariableName = (UINTN)gRT->GetNextVariableName; + gGetTime = (UINTN)gRT->GetTime; + gSetTime = (UINTN)gRT->SetTime; + gGetWakeupTime = (UINTN)gRT->GetWakeupTime; + gSetWakeupTime = (UINTN)gRT->SetWakeupTime; + gGetNextHighMonoCount = (UINTN)gRT->GetNextHighMonotonicCount; + gResetSystem = (UINTN)gRT->ResetSystem; + + gGetVariableOverride = (UINTN)GetVariableOverride; + + CopyMem ( + gRtShims, + (VOID *)&gRtShimsDataStart, + ((UINTN)&gRtShimsDataEnd - (UINTN)&gRtShimsDataStart) + ); + + gRT->GetVariable = (EFI_GET_VARIABLE)((UINTN)gRtShims + ((UINTN)&RtShimGetVariable - (UINTN)&gRtShimsDataStart)); + gRT->SetVariable = (EFI_SET_VARIABLE)((UINTN)gRtShims + ((UINTN)&RtShimSetVariable - (UINTN)&gRtShimsDataStart)); + gRT->GetNextVariableName = (EFI_GET_NEXT_VARIABLE_NAME)((UINTN)gRtShims + ((UINTN)&RtShimGetNextVariableName - (UINTN)&gRtShimsDataStart)); + gRT->GetTime = (EFI_GET_TIME)((UINTN)gRtShims + ((UINTN)&RtShimGetTime - (UINTN)&gRtShimsDataStart)); + gRT->SetTime = (EFI_SET_TIME)((UINTN)gRtShims + ((UINTN)&RtShimSetTime - (UINTN)&gRtShimsDataStart)); + gRT->GetWakeupTime = (EFI_GET_WAKEUP_TIME)((UINTN)gRtShims + ((UINTN)&RtShimGetWakeupTime - (UINTN)&gRtShimsDataStart)); + gRT->SetWakeupTime = (EFI_SET_WAKEUP_TIME)((UINTN)gRtShims + ((UINTN)&RtShimSetWakeupTime - (UINTN)&gRtShimsDataStart)); + gRT->GetNextHighMonotonicCount = (EFI_GET_NEXT_HIGH_MONO_COUNT)((UINTN)gRtShims + ((UINTN)&RtShimGetNextHighMonoCount - (UINTN)&gRtShimsDataStart)); + gRT->ResetSystem = (EFI_RESET_SYSTEM)((UINTN)gRtShims + ((UINTN)&RtShimResetSystem - (UINTN)&gRtShimsDataStart)); + + gRT->Hdr.CRC32 = 0; + gBS->CalculateCrc32(gRT, gRT->Hdr.HeaderSize, &gRT->Hdr.CRC32); + } else { + DEBUG ((DEBUG_VERBOSE, "Nulling RtShims\n")); + gRtShims = NULL; + } +} + +VOID +VirtualizeRtShims ( + UINTN MemoryMapSize, + UINTN DescriptorSize, + EFI_MEMORY_DESCRIPTOR *MemoryMap + ) +{ + EFI_MEMORY_DESCRIPTOR *Desc; + UINTN Index, Index2, FixedCount = 0; + + // + // For some reason creating an event for catching SetVirtualAddress doesn't work on APTIO IV Z77, + // So we cannot use a dedicated ConvertPointer function and have to implement everything manually. + // + + // + // Are we already done? + // + if (mRtShimsAddrUpdated) + return; + + Desc = MemoryMap; + + // + // Custom GetVariable wrapper is no longer allowed! + // + *(UINTN *)((UINTN)gRtShims + ((UINTN)&gGetVariableOverride - (UINTN)&gRtShimsDataStart)) = 0; + + for (Index = 0; Index < ARRAY_SIZE (mShimPtrArray); ++Index) { + mShimPtrArray[Index].Func = (UINTN *)((UINTN)gRtShims + ((UINTN)(mShimPtrArray[Index].gFunc) - (UINTN)&gRtShimsDataStart)); + } + + for (Index = 0; Index < (MemoryMapSize / DescriptorSize); ++Index) { + for (Index2 = 0; Index2 < ARRAY_SIZE (mShimPtrArray); ++Index2) { + if ( + !mShimPtrArray[Index2].Fixed && + (*(mShimPtrArray[Index2].gFunc) >= Desc->PhysicalStart) && + (*(mShimPtrArray[Index2].gFunc) < (Desc->PhysicalStart + EFI_PAGES_TO_SIZE (Desc->NumberOfPages))) + ) { + mShimPtrArray[Index2].Fixed = TRUE; + *(mShimPtrArray[Index2].Func) += (Desc->VirtualStart - Desc->PhysicalStart); + FixedCount++; + } + } + + if (FixedCount == ARRAY_SIZE (mShimPtrArray)) { + break; + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + } + + mRtShimsAddrUpdated = TRUE; +} + +EFI_STATUS +EFIAPI +OrgGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + return (gGetVariable ? (EFI_GET_VARIABLE)gGetVariable : gRT->GetVariable) ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); +} + +/** Protect RT data from relocation by marking them MemMapIO. Except area with EFI system table. + * This one must be relocated into kernel boot image or kernel will crash (kernel accesses it + * before RT areas are mapped into vm). + * This fixes NVRAM issues on some boards where access to nvram after boot services is possible + * only in SMM mode. RT driver passes data to SM handler through previously negotiated buffer + * and this buffer must not be relocated. + * Explained and examined in detail by CodeRush and night199uk: + * http://www.projectosx.com/forum/index.php?showtopic=3298 + * + * It seems this does not do any harm to others where this is not needed, + * so it's added as standard fix for all. + * + * Starting with APTIO V for nvram to work not only data but could too can no longer be moved + * due to the use of commbuffers. This, however, creates a memory protection issue, because + * XNU maps RT data as RW and code as RX, and AMI appears use global variables in some RT drivers. + * For this reason we shim (most?) affected RT services via wrapers that unset the WP bit during + * the UEFI call and set it back on return. + * Explained in detail by Download-Fritz and vit9696: + * http://www.insanelymac.com/forum/topic/331381-aptiomemoryfix (first 2 links in particular). + */ +VOID +ProtectRtMemoryFromRelocation ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN UINT32 DescriptorVersion, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN EFI_PHYSICAL_ADDRESS SysTableArea + ) +{ + UINTN NumEntries; + UINTN Index; + EFI_MEMORY_DESCRIPTOR *Desc; + RT_RELOC_PROTECT_INFO *RelocInfo; + + Desc = MemoryMap; + NumEntries = MemoryMapSize / DescriptorSize; + + mRelocInfoData.NumEntries = 0; + + RelocInfo = &mRelocInfoData.RelocInfo[0]; + + for (Index = 0; Index < NumEntries; Index++) { + if ((Desc->Attribute & EFI_MEMORY_RUNTIME) != 0 && + (Desc->Type == EfiRuntimeServicesCode || + (Desc->Type == EfiRuntimeServicesData && Desc->PhysicalStart != SysTableArea))) { + + if (mRelocInfoData.NumEntries < ARRAY_SIZE (mRelocInfoData.RelocInfo)) { + RelocInfo->PhysicalStart = Desc->PhysicalStart; + RelocInfo->Type = Desc->Type; + ++RelocInfo; + ++mRelocInfoData.NumEntries; + } else { + DEBUG ((DEBUG_INFO, "WARNING: Cannot save mem type for entry: %lx (type 0x%x)\n", Desc->PhysicalStart, (UINTN)Desc->Type)); + } + + DEBUG ((DEBUG_VERBOSE, "RT mem %lx (0x%x) -> MemMapIO\n", Desc->PhysicalStart, Desc->NumberOfPages)); + Desc->Type = EfiMemoryMappedIO; + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + } +} + +VOID +RestoreProtectedRtMemoryTypes ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap + ) +{ + UINTN Index; + UINTN Index2; + UINTN NumEntriesLeft; + + NumEntriesLeft = mRelocInfoData.NumEntries; + + if (NumEntriesLeft > 0) { + for (Index = 0; Index < (MemoryMapSize / DescriptorSize); ++Index) { + if (NumEntriesLeft > 0) { + for (Index2 = 0; Index2 < mRelocInfoData.NumEntries; ++Index2) { + if (MemoryMap->PhysicalStart == mRelocInfoData.RelocInfo[Index2].PhysicalStart) { + MemoryMap->Type = mRelocInfoData.RelocInfo[Index2].Type; + --NumEntriesLeft; + } + } + } + + MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize); + } + } +} + +VOID +SetWriteUnprotectorMode ( + IN BOOLEAN Enable + ) +{ + *(UINTN *)((UINTN)gRtShims + ((UINTN)&gRequiresWriteUnprotect - (UINTN)&gRtShimsDataStart)) = Enable; +} + +BOOLEAN +EFIAPI +SetBootVariableRedirect ( + IN BOOLEAN Enable + ) +{ + UINTN DataSize; + EFI_STATUS Status; + BOOLEAN Previous; + + if (Enable) { + DataSize = sizeof (Enable); + Status = gRT->GetVariable ( + OC_BOOT_REDIRECT_VARIABLE_NAME, + &gOcVendorVariableGuid, + NULL, + &DataSize, + &Enable + ); + + if (EFI_ERROR (Status)) { + Enable = FALSE; + } + } + + Previous = *(BOOLEAN *)((UINTN)gRtShims + ((UINTN)&gBootVariableRedirect - (UINTN)&gRtShimsDataStart)); + *(BOOLEAN *)((UINTN)gRtShims + ((UINTN)&gBootVariableRedirect - (UINTN)&gRtShimsDataStart)) = Enable; + return Previous; +} diff --git a/MemoryFix/AptioMemoryFix/RtShims.h b/MemoryFix/AptioMemoryFix/RtShims.h new file mode 100644 index 000000000..0f31c5701 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/RtShims.h @@ -0,0 +1,79 @@ +/** + + Runtime Services Wrappers. + + by Download-Fritz & vit9696 + +**/ + +#ifndef APTIOFIX_RT_SHIMS_H +#define APTIOFIX_RT_SHIMS_H + +extern VOID *gRtShims; + +typedef struct { + UINTN *gFunc; + UINTN *Func; + BOOLEAN Fixed; +} RT_SHIM_PTRS; + +typedef struct { + EFI_PHYSICAL_ADDRESS PhysicalStart; + EFI_MEMORY_TYPE Type; +} RT_RELOC_PROTECT_INFO; + +typedef struct { + UINTN NumEntries; + RT_RELOC_PROTECT_INFO RelocInfo[APTIFIX_MAX_RT_RELOC_NUM]; +} RT_RELOC_PROTECT_DATA; + +VOID +InstallRtShims ( + EFI_GET_VARIABLE GetVariableOverride + ); + +VOID +VirtualizeRtShims ( + UINTN MemoryMapSize, + UINTN DescriptorSize, + EFI_MEMORY_DESCRIPTOR *MemoryMap + ); + +EFI_STATUS +EFIAPI +OrgGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +VOID +ProtectRtMemoryFromRelocation ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN UINT32 DescriptorVersion, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN EFI_PHYSICAL_ADDRESS SysTableArea + ); + +VOID +RestoreProtectedRtMemoryTypes ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap + ); + +VOID +SetWriteUnprotectorMode ( + IN BOOLEAN Enable + ); + +BOOLEAN +EFIAPI +SetBootVariableRedirect ( + IN BOOLEAN Enable + ); + +#endif // APTIOFIX_RT_SHIMS_H diff --git a/MemoryFix/AptioMemoryFix/ServiceOverrides.c b/MemoryFix/AptioMemoryFix/ServiceOverrides.c new file mode 100644 index 000000000..33e2615d8 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/ServiceOverrides.c @@ -0,0 +1,638 @@ +/** + + Temporary BS and RT overrides for boot.efi support. + Unlike RtShims they do not affect the kernel. + + by dmazar + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Config.h" +#include "ServiceOverrides.h" +#include "BootArgs.h" +#include "BootFixes.h" +#include "CustomSlide.h" +#include "MemoryMap.h" +#include "RtShims.h" +#include "UmmMalloc/UmmMalloc.h" + +// +// Placeholders for storing original Boot and RT Services functions +// +STATIC EFI_ALLOCATE_PAGES mStoredAllocatePages; +STATIC EFI_ALLOCATE_POOL mStoredAllocatePool; +STATIC EFI_FREE_POOL mStoredFreePool; +STATIC EFI_GET_MEMORY_MAP mStoredGetMemoryMap; +STATIC EFI_EXIT_BOOT_SERVICES mStoredExitBootServices; +STATIC EFI_SET_VIRTUAL_ADDRESS_MAP mStoredSetVirtualAddressMap; +STATIC EFI_IMAGE_START mStoredStartImage; + +// +// Original runtime services hash we restore at address virtualisation +// +STATIC UINT32 mRtPreOverridesCRC32; + +// +// Location of memory allocated by boot.efi for hibernate image +// +STATIC EFI_PHYSICAL_ADDRESS mHibernateImageAddress; + +// +// Saved exit boot services arguments +// +STATIC EFI_HANDLE mExitBSImageHandle; +STATIC UINTN mExitBSMapKey; + +// +// Dynamic memory allocation filtering status +// +STATIC BOOLEAN mFilterDynamicPoolAllocations; + +// +// Minimum and maximum addresses allocated by AlocatePages +// +STATIC EFI_PHYSICAL_ADDRESS mMinAllocatedAddr; +STATIC EFI_PHYSICAL_ADDRESS mMaxAllocatedAddr; + +// +// Amount of nested boot.efi detected +// +UINTN gMacOSBootNestedCount; + +// +// Last descriptor size obtained from GetMemoryMap +// +UINTN gMemoryMapDescriptorSize = sizeof(EFI_MEMORY_DESCRIPTOR); + +VOID +InstallBsOverrides ( + VOID + ) +{ +#if APTIOFIX_CUSTOM_POOL_ALLOCATOR == 1 + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS UmmHeap = BASE_4GB; + UINTN PageNum = EFI_SIZE_TO_PAGES (APTIOFIX_CUSTOM_POOL_ALLOCATOR_SIZE); + + // + // We do not uninstall our custom allocator to avoid memory corruption issues + // when shimming AMI code. This check ensures that we do not install it twice. + // See UninstallBsOverrides for more details. + // + if (!UmmInitialized ()) { + // + // Enforce memory pool creation when -aptiodump argument is used, but let it slip otherwise. + // + Status = AllocatePagesFromTop (EfiBootServicesData, PageNum, &UmmHeap, !gDumpMemArgPresent); + if (!EFI_ERROR (Status)) { + gBS->SetMem ((VOID *)UmmHeap, APTIOFIX_CUSTOM_POOL_ALLOCATOR_SIZE, 0); + UmmSetHeap ((VOID *)UmmHeap); + + mStoredAllocatePool = gBS->AllocatePool; + mStoredFreePool = gBS->FreePool; + + gBS->AllocatePool = MOAllocatePool; + gBS->FreePool = MOFreePool; + } else { + // + // This is undesired, but technically less fatal than attempting to reduce the number + // of slides available when no memory map dumping is necessary, for example. + // + OcPrintScreen (L"AMF: Not using custom memory pool - %r\n", Status); + } + } +#endif + + mStoredAllocatePages = gBS->AllocatePages; + mStoredGetMemoryMap = gBS->GetMemoryMap; + mStoredExitBootServices = gBS->ExitBootServices; + mStoredStartImage = gBS->StartImage; + + gBS->AllocatePages = MOAllocatePages; + gBS->GetMemoryMap = MOGetMemoryMap; + gBS->ExitBootServices = MOExitBootServices; + gBS->StartImage = MOStartImage; + + gBS->Hdr.CRC32 = 0; + gBS->CalculateCrc32 (gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32); +} + +VOID +InstallRtOverrides ( + VOID + ) +{ + mRtPreOverridesCRC32 = gRT->Hdr.CRC32; + + mStoredSetVirtualAddressMap = gRT->SetVirtualAddressMap; + + gRT->SetVirtualAddressMap = MOSetVirtualAddressMap; + + gRT->Hdr.CRC32 = 0; + gBS->CalculateCrc32 (gRT, gRT->Hdr.HeaderSize, &gRT->Hdr.CRC32); +} + +VOID +UninstallRtOverrides ( + VOID + ) +{ + gRT->SetVirtualAddressMap = mStoredSetVirtualAddressMap; + + gRT->Hdr.CRC32 = mRtPreOverridesCRC32; +} + +VOID +DisableDynamicPoolAllocations ( + VOID + ) +{ + mFilterDynamicPoolAllocations = TRUE; +} + +VOID +EnableDynamicPoolAllocations ( + VOID + ) +{ + mFilterDynamicPoolAllocations = FALSE; +} + +/** gBS->StartImage override: + * Called to start an efi image. + * + * If this is boot.efi, then run it with our overrides. + */ +EFI_STATUS +EFIAPI +MOStartImage ( + IN EFI_HANDLE ImageHandle, + OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *AppleLoadedImage = NULL; + UINTN ValueSize = 0; + VOID *Gop; + + DEBUG ((DEBUG_VERBOSE, "StartImage (%lx)\n", ImageHandle)); + + AppleLoadedImage = GetAppleBootLoadedImage (ImageHandle); + + // + // Clear monitoring vars + // + mMinAllocatedAddr = 0; + mMaxAllocatedAddr = 0; + + // + // Request boot variable redirection if enabled. + // + SetBootVariableRedirect (TRUE); + + if (AppleLoadedImage) { + // + // Report about macOS being loaded. + // + gMacOSBootNestedCount++; + + // + // Latest Windows brings Virtualization-based security and monitors + // CR0 by launching itself under a hypevisor. Since we need WP disable + // on macOS to let NVRAM work, and for the time being no other OS + // requires it, here we decide to use it for macOS exclusively. + // + SetWriteUnprotectorMode (TRUE); + + // + // Boot.efi requires EfiGraphicsOutputProtocol on ConOutHandle, but it is not present + // there on Aptio 2.0. EfiGraphicsOutputProtocol exists on some other handle. + // If this is the case, we'll intercept that call and return EfiGraphicsOutputProtocol + // from that other handle. + // + Gop = NULL; + Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, &Gop); + if (EFI_ERROR (Status)) { + Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, &Gop); + if (!EFI_ERROR (Status)) { + gBS->InstallMultipleProtocolInterfaces ( + &gST->ConsoleOutHandle, + &gEfiGraphicsOutputProtocolGuid, + Gop, + NULL + ); + } + } + + // + // This is reverse engineered from boot.efi. + // To cancel hibernate wake it is enough to delete the variables. + // Starting with 10.13.6 boot-switch-vars is no longer supported. + // + ValueSize = 0; + if (gRT->GetVariable (L"boot-signature", &gAppleBootVariableGuid, NULL, &ValueSize, NULL) == EFI_BUFFER_TOO_SMALL) { + ValueSize = 0; + if (gRT->GetVariable (L"boot-image-key", &gAppleBootVariableGuid, NULL, &ValueSize, NULL) == EFI_BUFFER_TOO_SMALL) { + gHibernateWake = TRUE; + } + } else { + ValueSize = 0; + if (gRT->GetVariable (L"boot-switch-vars", &gAppleBootVariableGuid, NULL, &ValueSize, NULL) == EFI_BUFFER_TOO_SMALL) { + gHibernateWake = TRUE; + } + } + + // + // Save current 64bit state - will be restored later in callback from kernel jump + // and relocate JumpToKernel32 code to higher mem (for copying kernel back to + // proper place and jumping back to it) + // + Status = PrepareJumpFromKernel (); + if (!EFI_ERROR (Status)) { + // + // Force boot.efi to use our copy of system table + // + AppleLoadedImage->SystemTable = (EFI_SYSTEM_TABLE *)(UINTN)gSysTableRtArea; + + #if APTIOFIX_ALLOW_ASLR_IN_SAFE_MODE == 1 + UnlockSlideSupportForSafeMode ((UINT8 *)AppleLoadedImage->ImageBase, AppleLoadedImage->ImageSize); + #endif + + // + // Read options + // + ReadBooterArguments ((CHAR16*)AppleLoadedImage->LoadOptions, AppleLoadedImage->LoadOptionsSize/sizeof (CHAR16)); + } + } + + + Status = mStoredStartImage (ImageHandle, ExitDataSize, ExitData); + + if (AppleLoadedImage) { + // + // We failed but other operating systems should be loadable. + // + gMacOSBootNestedCount--; + SetWriteUnprotectorMode (FALSE); + } + + // + // Disable redirect on failure, this is cleaner design-wise. + // + SetBootVariableRedirect (FALSE); + + return Status; +} + +/** gBS->AllocatePages override: + * Returns pages from free memory block to boot.efi for kernel boot image. + */ +EFI_STATUS +EFIAPI +MOAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS UpperAddr; + + if (gMacOSBootNestedCount > 0 && Type == AllocateAddress && MemoryType == EfiLoaderData) { + // + // Called from boot.efi + // + UpperAddr = *Memory + EFI_PAGES_TO_SIZE (NumberOfPages); + + // + // Store min and max mem - can be used later to determine start and end of kernel boot image + // + if (mMinAllocatedAddr == 0 || *Memory < mMinAllocatedAddr) + mMinAllocatedAddr = *Memory; + if (UpperAddr > mMaxAllocatedAddr) + mMaxAllocatedAddr = UpperAddr; + + Status = mStoredAllocatePages (Type, MemoryType, NumberOfPages, Memory); + } else if (gMacOSBootNestedCount > 0 && gHibernateWake && Type == AllocateAnyPages && MemoryType == EfiLoaderData) { + // + // Called from boot.efi during hibernate wake, + // first such allocation is for hibernate image + // + Status = mStoredAllocatePages (Type, MemoryType, NumberOfPages, Memory); + if (mHibernateImageAddress == 0 && Status == EFI_SUCCESS) { + mHibernateImageAddress = *Memory; + } + } else { + // + // Generic page allocation + // + Status = mStoredAllocatePages (Type, MemoryType, NumberOfPages, Memory); + } + + return Status; +} + +/** gBS->AllocatePool override: + * Allows us to use a custom allocator that uses a preallocated memory pool + * for certain types of memory. See details in OcPrintScreen function. + */ +EFI_STATUS +EFIAPI +MOAllocatePool ( + IN EFI_MEMORY_TYPE Type, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + // + // The code below allows us to more safely invoke Boot Services to perform onscreen + // printing when no memory map modifications (pool memory allocation) is allowed. + // While it is imperfect design-wise, it works very well on many ASUS Skylake boards + // when performing memory map dumps via -aptiodump. + // + if (gMacOSBootNestedCount > 0 && Type == EfiBootServicesData && mFilterDynamicPoolAllocations) { + *Buffer = UmmMalloc ((UINT32)Size); + if (*Buffer) + return EFI_SUCCESS; + + // + // Dynamic pool allocations filtering should technically only be used when booting is more + // important than not allocating the requested memory and failing to do something. + // However, since we skip other types of allocations anyway, not falling back here to using + // the default allocator may have its own consequences on other boards. + // + } + + return mStoredAllocatePool (Type, Size, Buffer); +} + +/** gBS->FreePool override: + * Allows us to use a custom allocator for certain types of memory. + */ +EFI_STATUS +EFIAPI +MOFreePool ( + IN VOID *Buffer + ) +{ + // + // By default it will return FALSE if Buffer was not allocated by us. + // + if (UmmFree (Buffer)) + return EFI_SUCCESS; + + return mStoredFreePool (Buffer); +} + +/** gBS->GetMemoryMap override: + * Returns shrinked memory map. XNU can handle up to PMAP_MEMORY_REGIONS_SIZE (128) entries. + */ +EFI_STATUS +EFIAPI +MOGetMemoryMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ) +{ + EFI_STATUS Status; + + Status = mStoredGetMemoryMap (MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion); + + if (gMacOSBootNestedCount > 0 && Status == EFI_SUCCESS) { + if (gDumpMemArgPresent) { + PrintMemMap (L"GetMemoryMap", *MemoryMapSize, *DescriptorSize, MemoryMap, gRtShims, gSysTableRtArea); + } + +#if APTIOFIX_PROTECT_CSM_REGION == 1 + ProtectCsmRegion (*MemoryMapSize, MemoryMap, *DescriptorSize); +#endif + + ShrinkMemMap (MemoryMapSize, MemoryMap, *DescriptorSize); + + // + // Remember some descriptor size, since we will not have it later + // during hibernate wake to be able to iterate memory map. + // + gMemoryMapDescriptorSize = *DescriptorSize; + } + + return Status; +} + +EFI_STATUS +EFIAPI +OrgGetMemoryMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ) +{ + return (mStoredGetMemoryMap ? mStoredGetMemoryMap : gBS->GetMemoryMap) ( + MemoryMapSize, + MemoryMap, + MapKey, + DescriptorSize, + DescriptorVersion + ); +} + +/** gBS->ExitBootServices override: + * Patches kernel entry point with jump to our KernelEntryPatchJumpBack(). + */ +EFI_STATUS +EFIAPI +MOExitBootServices ( + IN EFI_HANDLE ImageHandle, + IN UINTN MapKey + ) +{ + EFI_STATUS Status; + UINTN SlideAddr = 0; + VOID *MachOImage = NULL; + IOHibernateImageHeader *ImageHeader = NULL; + + // + // For non-macOS operating systems return directly. + // + if (gMacOSBootNestedCount == 0) { + return mStoredExitBootServices (ImageHandle, MapKey); + } + + // + // We need hibernate image address for wake + // + if (gHibernateWake && mHibernateImageAddress == 0) { + OcPrintScreen (L"AMF: Failed to find hibernate image address\n"); + gBS->Stall (SECONDS_TO_MICROSECONDS (5)); + return EFI_INVALID_PARAMETER; + } + + // + // We can just return EFI_SUCCESS and continue using Print for debug + // + if (gDumpMemArgPresent) { + mExitBSImageHandle = ImageHandle; + mExitBSMapKey = MapKey; + Status = EFI_SUCCESS; + } else { + Status = ForceExitBootServices (mStoredExitBootServices, ImageHandle, MapKey); + } + + if (EFI_ERROR (Status)) + return Status; + + if (!gHibernateWake) { + SlideAddr = mMinAllocatedAddr - 0x100000; + MachOImage = (VOID*)(UINTN)(SlideAddr + SLIDE_GRANULARITY); + KernelEntryFromMachOPatchJump (MachOImage, SlideAddr); + } else { + // + // At this stage HIB section is not yet copied from sleep image to it's + // proper memory destination. so we'll patch entry point in sleep image. + // + ImageHeader = (IOHibernateImageHeader *)(UINTN)mHibernateImageAddress; + KernelEntryPatchJump ( + ((UINT32)(UINTN)&(ImageHeader->fileExtentMap[0])) + ImageHeader->fileExtentMapSize + ImageHeader->restore1CodeOffset + ); + } + + return Status; +} + +/** Helper function to call ExitBootServices that can handle outdated MapKey issues. */ +EFI_STATUS +ForceExitBootServices ( + IN EFI_EXIT_BOOT_SERVICES ExitBs, + IN EFI_HANDLE ImageHandle, + IN UINTN MapKey + ) +{ + EFI_STATUS Status; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN MemoryMapSize; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + + // + // Firstly try the easy way + // + Status = ExitBs (ImageHandle, MapKey); + + if (EFI_ERROR (Status)) { + // + // Just report error as var in nvram to be visible from macOS with "nvram -p" + // + gRT->SetVariable (L"aptiomemfix-exitbs", + &gAppleBootVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + 4, + "fail" + ); + + // + // It is too late to free memory map here, but it does not matter, because boot.efi has an old one + // and will freely use the memory. + // It is technically forbidden to allocate pool memory here, but we should not hit this code + // in the first place, and for older firmwares, where it was necessary (?), it worked just fine. + // + Status = GetMemoryMapAlloc (NULL, &MemoryMapSize, &MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); + if (Status == EFI_SUCCESS) { + // + // We have the latest memory map and its key, try again! + // + Status = ExitBs (ImageHandle, MapKey); + if (EFI_ERROR (Status)) + OcPrintScreen (L"AMF: ExitBootServices failed twice - %r\n", Status); + } else { + OcPrintScreen (L"AMF: Failed to get MapKey for ExitBootServices - %r\n", Status); + Status = EFI_INVALID_PARAMETER; + } + + if (EFI_ERROR (Status)) { + OcPrintScreen (L"Waiting 10 secs...\n"); + gBS->Stall (SECONDS_TO_MICROSECONDS (10)); + } + } + + return Status; +} + +/** gRT->SetVirtualAddressMap override: + * Fixes virtualizing of RT services. + */ +EFI_STATUS +EFIAPI +MOSetVirtualAddressMap ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN UINT32 DescriptorVersion, + IN EFI_MEMORY_DESCRIPTOR *VirtualMap + ) +{ + EFI_STATUS Status; + UINT32 EfiSystemTable; + + // + // We do not need to recover BS, since they will be invalid. + // + UninstallRtOverrides (); + + // + // Apply the necessary changes for macOS support + // + if (gMacOSBootNestedCount > 0) { + if (gDumpMemArgPresent) { + PrintMemMap (L"SetVirtualAddressMap", MemoryMapSize, DescriptorSize, VirtualMap, gRtShims, gSysTableRtArea); + // + // To print as much information as possible we delay ExitBootServices. + // Most likely this will fail, but let's still try! + // + ForceExitBootServices (mStoredExitBootServices, mExitBSImageHandle, mExitBSMapKey); + } + + // + // Protect RT areas from relocation by marking then MemMapIO + // + ProtectRtMemoryFromRelocation (MemoryMapSize, DescriptorSize, DescriptorVersion, VirtualMap, gSysTableRtArea); + + // + // Remember physical sys table addr + // + EfiSystemTable = (UINT32)(UINTN)gST; + + // + // Virtualize RT services with all needed fixes + // + Status = ExecSetVirtualAddressesToMemMap (MemoryMapSize, DescriptorSize, DescriptorVersion, VirtualMap); + + CopyEfiSysTableToRtArea (&EfiSystemTable); + } else { + Status = gRT->SetVirtualAddressMap (MemoryMapSize, DescriptorSize, DescriptorVersion, VirtualMap); + } + + // + // Correct shim pointers right away + // + VirtualizeRtShims (MemoryMapSize, DescriptorSize, VirtualMap); + + return Status; +} diff --git a/MemoryFix/AptioMemoryFix/ServiceOverrides.h b/MemoryFix/AptioMemoryFix/ServiceOverrides.h new file mode 100644 index 000000000..1d07139c5 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/ServiceOverrides.h @@ -0,0 +1,122 @@ +/** + + Temporary BS and RT overrides for boot.efi support. + Unlike RtShims they do not affect the kernel. + + by dmazar + +**/ + +#ifndef APTIOFIX_SERVICE_OVERRIDES_H +#define APTIOFIX_SERVICE_OVERRIDES_H + +// +// Last descriptor size obtained from GetMemoryMap +// +extern UINTN gMemoryMapDescriptorSize; + +// +// Amount of nested boot.efi detected +// +extern UINTN gMacOSBootNestedCount; + +VOID +InstallBsOverrides ( + VOID + ); + +VOID +InstallRtOverrides ( + VOID + ); + +VOID +UninstallRtOverrides ( + VOID + ); + +VOID +DisableDynamicPoolAllocations ( + VOID + ); + +VOID +EnableDynamicPoolAllocations ( + VOID + ); + +EFI_STATUS +EFIAPI +MOStartImage ( + IN EFI_HANDLE ImageHandle, + OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL + ); + +EFI_STATUS +EFIAPI +MOAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory + ); + +EFI_STATUS +EFIAPI +MOAllocatePool ( + IN EFI_MEMORY_TYPE Type, + IN UINTN Size, + OUT VOID **Buffer + ); + +EFI_STATUS +EFIAPI +MOFreePool ( + IN VOID *Buffer + ); + +EFI_STATUS +EFIAPI +MOGetMemoryMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ); + +EFI_STATUS +EFIAPI +OrgGetMemoryMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ); + +EFI_STATUS +EFIAPI +MOExitBootServices ( + IN EFI_HANDLE ImageHandle, + IN UINTN MapKey + ); + +EFI_STATUS +ForceExitBootServices ( + IN EFI_EXIT_BOOT_SERVICES ExitBs, + IN EFI_HANDLE ImageHandle, + IN UINTN MapKey + ); + +EFI_STATUS +EFIAPI +MOSetVirtualAddressMap ( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN UINT32 DescriptorVersion, + IN EFI_MEMORY_DESCRIPTOR *VirtualMap + ); + +#endif // APTIOFIX_SERVICE_OVERRIDES_H diff --git a/MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.c b/MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.c new file mode 100644 index 000000000..386f6b4ca --- /dev/null +++ b/MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.c @@ -0,0 +1,496 @@ +/* ---------------------------------------------------------------------------- + * umm_malloc.c - a memory allocator for embedded systems (microcontrollers) + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Ralph Hempel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ---------------------------------------------------------------------------- + * + * R.Hempel 2007-09-22 - Original + * R.Hempel 2008-12-11 - Added MIT License biolerplate + * - realloc() now looks to see if previous block is free + * - made common operations functions + * R.Hempel 2009-03-02 - Added macros to disable tasking + * - Added function to dump heap and check for valid free + * pointer + * R.Hempel 2009-03-09 - Changed name to umm_malloc to aVOID conflicts with + * the mm_malloc() library functions + * - Added some test code to assimilate a free block + * with the very block if possible. Complicated and + * not worth the grief. + * D.Frank 2014-04-02 - Fixed heap configuration when UMM_TEST_MAIN is NOT set, + * added user-dependent configuration file umm_malloc_cfg.h + * R.Hempel 2016-12-04 - Add support for Unity test framework + * - Reorganize source files to aVOID redundant content + * - Move integrity and poison checking to separate file + * R.Hempel 2017-12-29 - Fix bug in realloc when requesting a new block that + * results in OOM error - see Issue 11 + * vit9696 2018-02-07 - Changed types, masks and limits to support 32-bit pools + * - Removed realloc and calloc I do not need + * - Added pointer range check in free to detect memory that + * was not allocated by us + * - Made pool initialization external to avoid memset deps + * and to support initialization state + * - Switched to UEFI types, pragmas, renamed external API + * ---------------------------------------------------------------------------- + */ + +#include "UmmMalloc.h" +#include "Config.h" + +STATIC UINT8 *default_umm_heap; + +#ifndef APTIOFIX_CUSTOM_POOL_ALLOCATOR_SIZE +#error "You must set APTIOFIX_CUSTOM_POOL_ALLOCATOR_SIZE with a sane pool size!" +#endif + +#define UMM_MALLOC_CFG_HEAP_SIZE APTIOFIX_CUSTOM_POOL_ALLOCATOR_SIZE +#define UMM_MALLOC_CFG_HEAP_ADDR default_umm_heap + +#define UMM_BEST_FIT + +#define DBGLOG_DEBUG(format, ...) do { } while (0) +#define DBGLOG_TRACE(froamt, ...) do { } while (0) + +#define UMM_CRITICAL_ENTRY() +#define UMM_CRITICAL_EXIT() + +/* ------------------------------------------------------------------------- */ + +#pragma pack(1) + +typedef struct umm_ptr_t { + UINT32 next; + UINT32 prev; +} umm_ptr; + +typedef struct umm_block_t { + union { + umm_ptr used; + } header; + union { + umm_ptr free; + UINT8 data[4]; + } body; +} umm_block; + +#pragma pack() + +#define UMM_FREELIST_MASK (0x80000000) +#define UMM_BLOCKNO_MASK (0x7FFFFFFF) + +/* ------------------------------------------------------------------------- */ + +umm_block *umm_heap = NULL; +UINT32 umm_numblocks = 0; + +#define UMM_NUMBLOCKS (umm_numblocks) + +/* ------------------------------------------------------------------------ */ + +#define UMM_BLOCK(b) (umm_heap[b]) + +#define UMM_NBLOCK(b) (UMM_BLOCK(b).header.used.next) +#define UMM_PBLOCK(b) (UMM_BLOCK(b).header.used.prev) +#define UMM_NFREE(b) (UMM_BLOCK(b).body.free.next) +#define UMM_PFREE(b) (UMM_BLOCK(b).body.free.prev) +#define UMM_DATA(b) (UMM_BLOCK(b).body.data) + +/* ------------------------------------------------------------------------ */ + +STATIC UINT32 umm_blocks( UINT32 size ) { + + /* + * The calculation of the block size is not too difficult, but there are + * a few little things that we need to be mindful of. + * + * When a block removed from the free list, the space used by the free + * pointers is available for data. That's what the first calculation + * of size is doing. + */ + + if( size <= (sizeof(((umm_block *)0)->body)) ) + return( 1 ); + + /* + * If it's for more than that, then we need to figure out the number of + * additional whole blocks the size of an umm_block are required. + */ + + size -= ( 1 + (sizeof(((umm_block *)0)->body)) ); + + return( 2 + size/(sizeof(umm_block)) ); +} + +/* ------------------------------------------------------------------------ */ +/* + * Split the block `c` into two blocks: `c` and `c + blocks`. + * + * - `new_freemask` should be `0` if `c + blocks` used, or `UMM_FREELIST_MASK` + * otherwise. + * + * Note that free pointers are NOT modified by this function. + */ +STATIC VOID umm_split_block( UINT32 c, + UINT32 blocks, + UINT32 new_freemask ) { + + UMM_NBLOCK(c+blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask; + UMM_PBLOCK(c+blocks) = c; + + UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c+blocks); + UMM_NBLOCK(c) = (c+blocks); +} + +/* ------------------------------------------------------------------------ */ + +STATIC VOID umm_disconnect_from_free_list( UINT32 c ) { + /* Disconnect this block from the FREE list */ + + UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); + UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); + + /* And clear the free block indicator */ + + UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); +} + +/* ------------------------------------------------------------------------ + * The umm_assimilate_up() function assumes that UMM_NBLOCK(c) does NOT + * have the UMM_FREELIST_MASK bit set! + */ + +STATIC VOID umm_assimilate_up( UINT32 c ) { + + if( UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK ) { + /* + * The next block is a free block, so assimilate up and remove it from + * the free list + */ + + DBGLOG_DEBUG( "Assimilate up to next block, which is FREE\n" ); + + /* Disconnect the next block from the FREE list */ + + umm_disconnect_from_free_list( UMM_NBLOCK(c) ); + + /* Assimilate the next block with this one */ + + UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; + UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; + } +} + +/* ------------------------------------------------------------------------ + * The umm_assimilate_down() function assumes that UMM_NBLOCK(c) does NOT + * have the UMM_FREELIST_MASK bit set! + */ + +STATIC UINT32 umm_assimilate_down( UINT32 c, UINT32 freemask ) { + + UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; + UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); + + return( UMM_PBLOCK(c) ); +} + +/* ------------------------------------------------------------------------ */ + +VOID umm_init( VOID ) { + /* init heap pointer and size, and memset it to 0 */ + umm_heap = (umm_block *)UMM_MALLOC_CFG_HEAP_ADDR; + umm_numblocks = (UMM_MALLOC_CFG_HEAP_SIZE / sizeof(umm_block)); + + /* + * This is done at allocation step! + * memset(umm_heap, 0x00, UMM_MALLOC_CFG_HEAP_SIZE); + */ + + /* setup initial blank heap structure */ + { + /* index of the 0th `umm_block` */ + CONST UINT32 block_0th = 0; + /* index of the 1st `umm_block` */ + CONST UINT32 block_1th = 1; + /* index of the latest `umm_block` */ + CONST UINT32 block_last = UMM_NUMBLOCKS - 1; + + /* setup the 0th `umm_block`, which just points to the 1st */ + UMM_NBLOCK(block_0th) = block_1th; + UMM_NFREE(block_0th) = block_1th; + UMM_PFREE(block_0th) = block_1th; + + /* + * Now, we need to set the whole heap space as a huge free block. We should + * not touch the 0th `umm_block`, since it's special: the 0th `umm_block` + * is the head of the free block list. It's a part of the heap invariant. + * + * See the detailed explanation at the beginning of the file. + */ + + /* + * 1th `umm_block` has pointers: + * + * - next `umm_block`: the latest one + * - prev `umm_block`: the 0th + * + * Plus, it's a free `umm_block`, so we need to apply `UMM_FREELIST_MASK` + * + * And it's the last free block, so the next free block is 0. + */ + UMM_NBLOCK(block_1th) = block_last | UMM_FREELIST_MASK; + UMM_NFREE(block_1th) = 0; + UMM_PBLOCK(block_1th) = block_0th; + UMM_PFREE(block_1th) = block_0th; + + /* + * latest `umm_block` has pointers: + * + * - next `umm_block`: 0 (meaning, there are no more `umm_blocks`) + * - prev `umm_block`: the 1st + * + * It's not a free block, so we don't touch NFREE / PFREE at all. + */ + UMM_NBLOCK(block_last) = 0; + UMM_PBLOCK(block_last) = block_1th; + } +} + +/* ------------------------------------------------------------------------ */ + +BOOLEAN UmmInitialized ( VOID ) { + return default_umm_heap != NULL; +} + +/* ------------------------------------------------------------------------ */ + +VOID UmmSetHeap( VOID *heap ) { + default_umm_heap = (UINT8 *)heap; + umm_init(); +} + +/* ------------------------------------------------------------------------ */ + +BOOLEAN UmmFree( VOID *ptr ) { + + UINT32 c; + UINT8 *cptr = (UINT8 *)ptr; + + /* If we are not initialised, reuturn false! */ + if ( !UmmInitialized() ) + return FALSE; + + /* If we're being asked to free a NULL pointer, well that's just silly! */ + + if( (VOID *)0 == ptr ) { + DBGLOG_DEBUG( "free a null pointer -> do nothing\n" ); + + return FALSE; + } + + /* If we're being asked to free an unrelated pointer, return FALSE as well! */ + + if (cptr < default_umm_heap || cptr >= default_umm_heap + UMM_MALLOC_CFG_HEAP_SIZE) + return FALSE; + + /* + * FIXME: At some point it might be a good idea to add a check to make sure + * that the pointer we're being asked to free up is actually within + * the umm_heap! + * + * NOTE: See the new umm_info() function that you can use to see if a ptr is + * on the free list! + */ + + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); + + /* Figure out which block we're in. Note the use of truncated division... */ + + c = (UINT32)((((UINT8 *)ptr)-(UINT8 *)(&(umm_heap[0])))/sizeof(umm_block)); + + DBGLOG_DEBUG( "Freeing block %6i\n", c ); + + /* Now let's assimilate this block with the next one if possible. */ + + umm_assimilate_up( c ); + + /* Then assimilate with the previous block if possible */ + + if( UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK ) { + + DBGLOG_DEBUG( "Assimilate down to next block, which is FREE\n" ); + + c = umm_assimilate_down(c, UMM_FREELIST_MASK); + } else { + /* + * The previous block is not a free block, so add this one to the head + * of the free list + */ + + DBGLOG_DEBUG( "Just add to head of free list\n" ); + + UMM_PFREE(UMM_NFREE(0)) = c; + UMM_NFREE(c) = UMM_NFREE(0); + UMM_PFREE(c) = 0; + UMM_NFREE(0) = c; + + UMM_NBLOCK(c) |= UMM_FREELIST_MASK; + } + + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); + + return TRUE; +} + +/* ------------------------------------------------------------------------ */ + +VOID *UmmMalloc( UINT32 size ) { + UINT32 blocks; + UINT32 blockSize = 0; + + UINT32 bestSize; + UINT32 bestBlock; + + UINT32 cf; + + /* If we are not initialised, reuturn false! */ + if ( !UmmInitialized() ) + return NULL; + + /* + * the very first thing we do is figure out if we're being asked to allocate + * a size of 0 - and if we are we'll simply return a null pointer. if not + * then reduce the size by 1 byte so that the subsequent calculations on + * the number of blocks to allocate are easier... + */ + + if( 0 == size ) { + DBGLOG_DEBUG( "malloc a block of 0 bytes -> do nothing\n" ); + + return( (VOID *)NULL ); + } + + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); + + blocks = umm_blocks( size ); + + /* + * Now we can scan through the free list until we find a space that's big + * enough to hold the number of blocks we need. + * + * This part may be customized to be a best-fit, worst-fit, or first-fit + * algorithm + */ + + cf = UMM_NFREE(0); + + bestBlock = UMM_NFREE(0); + bestSize = 0x7FFFFFFF; + + while( cf ) { + blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; + + DBGLOG_TRACE( "Looking at block %6i size %6i\n", cf, blockSize ); + +#if defined UMM_BEST_FIT + if( (blockSize >= blocks) && (blockSize < bestSize) ) { + bestBlock = cf; + bestSize = blockSize; + } +#elif defined UMM_FIRST_FIT + /* This is the first block that fits! */ + if( (blockSize >= blocks) ) + break; +#else +# error "No UMM_*_FIT is defined - check umm_malloc_cfg.h" +#endif + + cf = UMM_NFREE(cf); + } + + if( 0x7FFFFFFF != bestSize ) { + cf = bestBlock; + blockSize = bestSize; + } + + if( UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks ) { + /* + * This is an existing block in the memory heap, we just need to split off + * what we need, unlink it from the free list and mark it as in use, and + * link the rest of the block back into the freelist as if it was a new + * block on the free list... + */ + + if( blockSize == blocks ) { + /* It's an exact fit and we don't neet to split off a block. */ + DBGLOG_DEBUG( "Allocating %6i blocks starting at %6i - exact\n", blocks, cf ); + + /* Disconnect this block from the FREE list */ + + umm_disconnect_from_free_list( cf ); + + } else { + /* It's not an exact fit and we need to split off a block. */ + DBGLOG_DEBUG( "Allocating %6i blocks starting at %6i - existing\n", blocks, cf ); + + /* + * split current free block `cf` into two blocks. The first one will be + * returned to user, so it's not free, and the second one will be free. + */ + umm_split_block( cf, blocks, UMM_FREELIST_MASK /*new block is free*/ ); + + /* + * `umm_split_block()` does not update the free pointers (it affects + * only free flags), but effectively we've just moved beginning of the + * free block from `cf` to `cf + blocks`. So we have to adjust pointers + * to and from adjacent free blocks. + */ + + /* previous free block */ + UMM_NFREE( UMM_PFREE(cf) ) = cf + blocks; + UMM_PFREE( cf + blocks ) = UMM_PFREE(cf); + + /* next free block */ + UMM_PFREE( UMM_NFREE(cf) ) = cf + blocks; + UMM_NFREE( cf + blocks ) = UMM_NFREE(cf); + } + } else { + /* Out of memory */ + + DBGLOG_DEBUG( "Can't allocate %5i blocks\n", blocks ); + + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); + + return( (VOID *)NULL ); + } + + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); + + return( (VOID *)&UMM_DATA(cf) ); +} + +/* ------------------------------------------------------------------------ */ diff --git a/MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.h b/MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.h new file mode 100644 index 000000000..3d8d77380 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/UmmMalloc/UmmMalloc.h @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------------------- + * umm_malloc.h - a memory allocator for embedded systems (microcontrollers) + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Ralph Hempel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ---------------------------------------------------------------------------- + */ + +#ifndef UMM_MALLOC_H +#define UMM_MALLOC_H + +BOOLEAN +UmmInitialized ( + VOID + ); + +VOID +UmmSetHeap ( + VOID *Heap + ); + +VOID * +UmmMalloc ( + UINT32 Size + ); + +BOOLEAN +UmmFree ( + VOID *Ptr + ); + +#endif /* UMM_MALLOC_H */ diff --git a/MemoryFix/AptioMemoryFix/VMem.c b/MemoryFix/AptioMemoryFix/VMem.c new file mode 100644 index 000000000..289d41429 --- /dev/null +++ b/MemoryFix/AptioMemoryFix/VMem.c @@ -0,0 +1,548 @@ +/** + + Virtual memory functions. + + by dmazar + +**/ + +#include +#include +#include + +#include "Config.h" +#include "VMem.h" +#include "MemoryMap.h" + +/** Memory allocation for VM map pages that we will create with VmMapVirtualPage. + * We need to have it preallocated during boot services. + */ +UINT8 *VmMemoryPool = NULL; +INTN VmMemoryPoolFreePages = 0; + +VOID +GetCurrentPageTable ( + PAGE_MAP_AND_DIRECTORY_POINTER **PageTable, + UINTN *Flags + ) +{ + UINTN CR3; + + CR3 = AsmReadCr3(); + DEBUG ((DEBUG_VERBOSE, "GetCurrentPageTable: CR3 = 0x%lx\n", CR3)); + *PageTable = (PAGE_MAP_AND_DIRECTORY_POINTER*)(UINTN)(CR3 & CR3_ADDR_MASK); + *Flags = CR3 & (CR3_FLAG_PWT | CR3_FLAG_PCD); +} + +VOID +PrintPageTablePTE ( + PAGE_TABLE_4K_ENTRY *PTE, + VIRTUAL_ADDR VA + ) +{ +#if !defined (MDEPKG_NDEBUG) + UINTN Index; + UINT64 Start; + + for (Index = 0; Index < 10; Index++) { + VA.Pg4K.PTOffset = Index; + DEBUG ((DEBUG_VERBOSE, " PTE %03x at %p = %lx => ", Index, PTE, PTE->Uint64)); + // 4KB PTE + Start = (PTE->Uint64 & PT_ADDR_MASK_4K); + DEBUG ((DEBUG_VERBOSE, "4KB Fl: %lx VA: %lx - %lx ==> PH %lx - %lx\n", + (PTE->Uint64 & ~PT_ADDR_MASK_4K), VA.Uint64, VA.Uint64 + 0x1000 - 1, Start, Start + 0x1000 - 1)); + PTE++; + } +#endif +} + +VOID +PrintPageTablePDE ( + PAGE_MAP_AND_DIRECTORY_POINTER *PDE, + VIRTUAL_ADDR VA + ) +{ +#if !defined (MDEPKG_NDEBUG) + UINTN Index; + PAGE_TABLE_2M_ENTRY *PT2M; + PAGE_TABLE_4K_ENTRY *PTE; + UINT64 Start; + + for (Index = 0; Index < 10; Index++) { + VA.Pg4K.PDOffset = Index; + DEBUG ((DEBUG_VERBOSE, " PDE %03x at %p = %lx => ", Index, PDE, PDE->Uint64)); + if (PDE->Bits.MustBeZero & 0x1) { + // 2MB PDE + PT2M = (PAGE_TABLE_2M_ENTRY *)PDE; + Start = (PT2M->Uint64 & PT_ADDR_MASK_2M); + DEBUG ((DEBUG_VERBOSE, "2MB Fl: %lx VA: %lx - %lx ==> PH %lx - %lx\n", + (PT2M->Uint64 & ~PT_ADDR_MASK_2M), VA.Uint64, VA.Uint64 + 0x200000 - 1, Start, Start + 0x200000 - 1)); + } else { + DEBUG ((DEBUG_VERBOSE, " Fl: %lx %lx ->\n", (PDE->Uint64 & ~PT_ADDR_MASK_4K), (PDE->Uint64 & PT_ADDR_MASK_4K))); + PTE = (PAGE_TABLE_4K_ENTRY *)(PDE->Uint64 & PT_ADDR_MASK_4K); + PrintPageTablePTE(PTE, VA); + } + PDE++; + } +#endif +} + +VOID +PrintPageTablePDPE ( + PAGE_MAP_AND_DIRECTORY_POINTER *PDPE, + VIRTUAL_ADDR VA + ) +{ +#if !defined (MDEPKG_NDEBUG) + UINTN Index; + PAGE_TABLE_1G_ENTRY *PT1G; + PAGE_MAP_AND_DIRECTORY_POINTER *PDE; + UINT64 Start; + + for (Index = 0; Index < 10; Index++) { + VA.Pg4K.PDPOffset = Index; + DEBUG ((DEBUG_VERBOSE, " PDPE %03x at %p = %lx => ", Index, PDPE, PDPE->Uint64)); + if (PDPE->Bits.MustBeZero & 0x1) { + // 1GB PDPE + PT1G = (PAGE_TABLE_1G_ENTRY *)PDPE; + Start = (PT1G->Uint64 & PT_ADDR_MASK_1G); + DEBUG ((DEBUG_VERBOSE, "1GB Fl: %lx VA: %lx - %lx ==> PH %lx - %lx\n", + (PT1G->Uint64 & ~PT_ADDR_MASK_1G), VA.Uint64, VA.Uint64 + 0x40000000 - 1, Start, Start + 0x40000000 - 1)); + } else { + DEBUG ((DEBUG_VERBOSE, " Fl: %lx %lx ->\n", (PDPE->Uint64 & ~PT_ADDR_MASK_4K), (PDPE->Uint64 & PT_ADDR_MASK_4K))); + PDE = (PAGE_MAP_AND_DIRECTORY_POINTER *)(PDPE->Uint64 & PT_ADDR_MASK_4K); + PrintPageTablePDE(PDE, VA); + } + PDPE++; + } +#endif +} + +VOID +PrintPageTable ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + UINTN Flags + ) +{ + UINTN Index; + VIRTUAL_ADDR VA; + PAGE_MAP_AND_DIRECTORY_POINTER *PML4; + PAGE_MAP_AND_DIRECTORY_POINTER *PDPE; + + DEBUG ((DEBUG_VERBOSE, "PrintPageTable: %p, Flags: PWT: %d, PCD: %d\n", PageTable, (Flags & CR3_FLAG_PWT), (Flags & CR3_FLAG_PCD))); + PML4 = PageTable; + for (Index = 0; Index < 3; Index++) { + VA.Uint64 = 0; + VA.Pg4K.PML4Offset = Index; + VA_FIX_SIGN_EXTEND(VA); + DEBUG ((DEBUG_VERBOSE, "PML4 %03x at %p = %lx => Fl: %lx %lx ->\n", Index, PML4, PML4->Uint64, (PML4->Uint64 & ~PT_ADDR_MASK_4K), (PML4->Uint64 & PT_ADDR_MASK_4K))); + PDPE = (PAGE_MAP_AND_DIRECTORY_POINTER *)(PML4->Uint64 & PT_ADDR_MASK_4K); + PrintPageTablePDPE(PDPE, VA); + PML4++; + } +} + +EFI_STATUS +GetPhysicalAddr ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + EFI_VIRTUAL_ADDRESS VirtualAddr, + EFI_PHYSICAL_ADDRESS *PhysicalAddr + ) +{ + EFI_PHYSICAL_ADDRESS Start; + VIRTUAL_ADDR VA; + VIRTUAL_ADDR VAStart; + VIRTUAL_ADDR VAEnd; + PAGE_MAP_AND_DIRECTORY_POINTER *PML4; + PAGE_MAP_AND_DIRECTORY_POINTER *PDPE; + PAGE_MAP_AND_DIRECTORY_POINTER *PDE; + PAGE_TABLE_4K_ENTRY *PTE4K; + PAGE_TABLE_2M_ENTRY *PTE2M; + PAGE_TABLE_1G_ENTRY *PTE1G; + + VA.Uint64 = (UINT64)VirtualAddr; + //VA_FIX_SIGN_EXTEND(VA); + DEBUG ((DEBUG_VERBOSE, "PageTable: %p\n", PageTable)); + DEBUG ((DEBUG_VERBOSE, "VA: %lx => Indexes PML4=%x, PDP=%x, PD=%x, PT=%x\n", + VA.Uint64, VA.Pg4K.PML4Offset, VA.Pg4K.PDPOffset, VA.Pg4K.PDOffset, VA.Pg4K.PTOffset)); + + // PML4 + PML4 = PageTable; + PML4 += VA.Pg4K.PML4Offset; + // prepare region start and end + VAStart.Uint64 = 0; + VAStart.Pg4K.PML4Offset = VA.Pg4K.PML4Offset; + VA_FIX_SIGN_EXTEND(VAStart); + VAEnd.Uint64 = ~(UINT64)0; + VAEnd.Pg4K.PML4Offset = VA.Pg4K.PML4Offset; + VA_FIX_SIGN_EXTEND(VAEnd); + // print it + DEBUG ((DEBUG_VERBOSE, "PML4[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PML4Offset, PML4, PML4->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (!PML4->Bits.Present) { + DEBUG ((DEBUG_VERBOSE, "-> Mapping not present!\n")); + return EFI_NO_MAPPING; + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PML4->Bits.Nx, PML4->Bits.Accessed, + PML4->Bits.CacheDisabled, PML4->Bits.WriteThrough, + PML4->Bits.UserSupervisor, PML4->Bits.ReadWrite, PML4->Bits.Present, + (PML4->Uint64 & PT_ADDR_MASK_4K) + )); + + // PDPE + PDPE = (PAGE_MAP_AND_DIRECTORY_POINTER *)(PML4->Uint64 & PT_ADDR_MASK_4K); + PDPE += VA.Pg4K.PDPOffset; + VAStart.Pg4K.PDPOffset = VA.Pg4K.PDPOffset; + VAEnd.Pg4K.PDPOffset = VA.Pg4K.PDPOffset; + DEBUG ((DEBUG_VERBOSE, "PDPE[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PDPOffset, PDPE, PDPE->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (!PDPE->Bits.Present) { + DEBUG ((DEBUG_VERBOSE, "-> Mapping not present!\n")); + return EFI_NO_MAPPING; + } + if (PDPE->Bits.MustBeZero & 0x1) { + // 1GB PDPE + PTE1G = (PAGE_TABLE_1G_ENTRY *)PDPE; + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|G:%x|PAT:%x|D:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x\n", + PTE1G->Bits.Nx, PTE1G->Bits.Global, PTE1G->Bits.PAT, + PTE1G->Bits.Dirty, PTE1G->Bits.Accessed, + PTE1G->Bits.CacheDisabled, PTE1G->Bits.WriteThrough, + PTE1G->Bits.UserSupervisor, PTE1G->Bits.ReadWrite, PTE1G->Bits.Present + )); + Start = (PTE1G->Uint64 & PT_ADDR_MASK_1G); + *PhysicalAddr = Start + VA.Pg1G.PhysPgOffset; + DEBUG ((DEBUG_VERBOSE, "-> 1GB page %lx - %lx => %lx\n", Start, Start + 0x40000000 - 1, *PhysicalAddr)); + return EFI_SUCCESS; + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PDPE->Bits.Nx, PDPE->Bits.Accessed, + PDPE->Bits.CacheDisabled, PDPE->Bits.WriteThrough, + PDPE->Bits.UserSupervisor, PDPE->Bits.ReadWrite, PDPE->Bits.Present, + (PDPE->Uint64 & PT_ADDR_MASK_4K) + )); + + // PDE + PDE = (PAGE_MAP_AND_DIRECTORY_POINTER *)(PDPE->Uint64 & PT_ADDR_MASK_4K); + PDE += VA.Pg4K.PDOffset; + VAStart.Pg4K.PDOffset = VA.Pg4K.PDOffset; + VAEnd.Pg4K.PDOffset = VA.Pg4K.PDOffset; + DEBUG ((DEBUG_VERBOSE, "PDE[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PDOffset, PDE, PDE->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (!PDE->Bits.Present) { + DEBUG ((DEBUG_VERBOSE, "-> Mapping not present!\n")); + return EFI_NO_MAPPING; + } + if (PDE->Bits.MustBeZero & 0x1) { + // 2MB PDE + PTE2M = (PAGE_TABLE_2M_ENTRY *)PDE; + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|G:%x|PAT:%x|D:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x\n", + PTE2M->Bits.Nx, PTE2M->Bits.Global, PTE2M->Bits.PAT, + PTE2M->Bits.Dirty, PTE2M->Bits.Accessed, + PTE2M->Bits.CacheDisabled, PTE2M->Bits.WriteThrough, + PTE2M->Bits.UserSupervisor, PTE2M->Bits.ReadWrite, PTE2M->Bits.Present + )); + Start = (PTE2M->Uint64 & PT_ADDR_MASK_2M); + *PhysicalAddr = Start + VA.Pg2M.PhysPgOffset; + DEBUG ((DEBUG_VERBOSE, "-> 2MB page %lx - %lx => %lx\n", Start, Start + 0x200000 - 1, *PhysicalAddr)); + return EFI_SUCCESS; + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PDE->Bits.Nx, PDE->Bits.Accessed, + PDE->Bits.CacheDisabled, PDE->Bits.WriteThrough, + PDE->Bits.UserSupervisor, PDE->Bits.ReadWrite, PDE->Bits.Present, + (PDE->Uint64 & PT_ADDR_MASK_4K) + )); + + // PTE + PTE4K = (PAGE_TABLE_4K_ENTRY *)(PDE->Uint64 & PT_ADDR_MASK_4K); + PTE4K += VA.Pg4K.PTOffset; + VAStart.Pg4K.PTOffset = VA.Pg4K.PTOffset; + VAEnd.Pg4K.PTOffset = VA.Pg4K.PTOffset; + DEBUG ((DEBUG_VERBOSE, "PTE[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PTOffset, PTE4K, PTE4K->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (!PTE4K->Bits.Present) { + DEBUG ((DEBUG_VERBOSE, "-> Mapping not present!\n")); + return EFI_NO_MAPPING; + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|G:%x|PAT:%x|D:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PTE4K->Bits.Nx, PTE4K->Bits.Global, PTE4K->Bits.PAT, + PTE4K->Bits.Dirty, PTE4K->Bits.Accessed, + PTE4K->Bits.CacheDisabled, PTE4K->Bits.WriteThrough, + PTE4K->Bits.UserSupervisor, PTE4K->Bits.ReadWrite, PTE4K->Bits.Present, + (PTE4K->Uint64 & PT_ADDR_MASK_4K) + )); + Start = (PTE4K->Uint64 & PT_ADDR_MASK_4K); + *PhysicalAddr = Start + VA.Pg4K.PhysPgOffset; + + return EFI_SUCCESS; +} + +/** Inits vm memory pool. Should be called while boot services are still usable. */ +EFI_STATUS +VmAllocateMemoryPool ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Addr; + + if (VmMemoryPool != NULL) { + // already allocated + return EFI_SUCCESS; + } + + VmMemoryPoolFreePages = 0x200; // 2 MB should be enough + Addr = BASE_4GB; // max address + + Status = AllocatePagesFromTop (EfiBootServicesData, VmMemoryPoolFreePages, &Addr, FALSE); + if (EFI_ERROR (Status)) { + OcPrintScreen (L"AMF: vm memory pool allocation failure - %r\n", Status); + } else { + VmMemoryPool = (UINT8*)Addr; + DEBUG ((DEBUG_VERBOSE, "VmMemoryPool = %lx - %lx\n", VmMemoryPool, VmMemoryPool + EFI_PAGES_TO_SIZE(VmMemoryPoolFreePages) - 1)); + } + return Status; +} + +/** Central method for allocating pages for VM page maps. */ +VOID * +VmAllocatePages ( + UINTN NumPages + ) +{ + VOID *AllocatedPages = NULL; + + if (VmMemoryPoolFreePages >= (INTN)NumPages) { + AllocatedPages = VmMemoryPool; + VmMemoryPool += EFI_PAGES_TO_SIZE(NumPages); + VmMemoryPoolFreePages -= NumPages; + } else { + DEBUG ((DEBUG_INFO, "VmAllocatePages - no more pages!\n")); + CpuDeadLoop(); + } + return AllocatedPages; +} + +/** Maps (remaps) 4K page given by VirtualAddr to PhysicalAddr page in PageTable. */ +EFI_STATUS +VmMapVirtualPage ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + EFI_VIRTUAL_ADDRESS VirtualAddr, + EFI_PHYSICAL_ADDRESS PhysicalAddr + ) +{ + EFI_PHYSICAL_ADDRESS Start; + VIRTUAL_ADDR VA; + VIRTUAL_ADDR VAStart; + VIRTUAL_ADDR VAEnd; + PAGE_MAP_AND_DIRECTORY_POINTER *PML4; + PAGE_MAP_AND_DIRECTORY_POINTER *PDPE; + PAGE_MAP_AND_DIRECTORY_POINTER *PDE; + PAGE_TABLE_4K_ENTRY *PTE4K; + PAGE_TABLE_4K_ENTRY *PTE4KTmp; + PAGE_TABLE_2M_ENTRY *PTE2M; + PAGE_TABLE_1G_ENTRY *PTE1G; + UINTN Index; + + VA.Uint64 = (UINT64)VirtualAddr; + //VA_FIX_SIGN_EXTEND(VA); + DEBUG ((DEBUG_VERBOSE, "VmMapVirtualPage VA %lx => PA %lx\nPageTable: %p\n", VirtualAddr, PhysicalAddr, PageTable)); + DEBUG ((DEBUG_VERBOSE, "VA: %lx => Indexes PML4=%x, PDP=%x, PD=%x, PT=%x\n", + VA.Uint64, VA.Pg4K.PML4Offset, VA.Pg4K.PDPOffset, VA.Pg4K.PDOffset, VA.Pg4K.PTOffset)); + + // PML4 + PML4 = PageTable; + PML4 += VA.Pg4K.PML4Offset; + // there is a problem if our PML4 points to the same table as first PML4 entry + // since we may mess the mapping of first virtual region (happens in VBox and probably DUET). + // check for this on first call and if true, just clear our PML4 - we'll rebuild it in later step + if (PML4 != PageTable && PML4->Bits.Present && PageTable->Bits.PageTableBaseAddress == PML4->Bits.PageTableBaseAddress) { + DEBUG ((DEBUG_VERBOSE, "PML4 points to the same table as first PML4 - releasing it and rebuiding in a separate table\n")); + PML4->Uint64 = 0; + } + + // prepare region start and end + VAStart.Uint64 = 0; + VAStart.Pg4K.PML4Offset = VA.Pg4K.PML4Offset; + VA_FIX_SIGN_EXTEND(VAStart); + VAEnd.Uint64 = ~(UINT64)0; + VAEnd.Pg4K.PML4Offset = VA.Pg4K.PML4Offset; + VA_FIX_SIGN_EXTEND(VAEnd); + // print it + DEBUG ((DEBUG_VERBOSE, "PML4[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PML4Offset, PML4, PML4->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (!PML4->Bits.Present) { + DEBUG ((DEBUG_VERBOSE, "-> Mapping not present, creating new PML4 entry and page with PDPE entries!\n")); + PDPE = (PAGE_MAP_AND_DIRECTORY_POINTER *)VmAllocatePages(1); + if (PDPE == NULL) { + DEBUG ((DEBUG_VERBOSE, "No memory - exiting.\n")); + return EFI_NO_MAPPING; + } + + ZeroMem(PDPE, EFI_PAGE_SIZE); + // init this whole 512GB region with 512 1GB entry pages to map first 512GB phys space + PTE1G = (PAGE_TABLE_1G_ENTRY *)PDPE; + Start = 0; + for (Index = 0; Index < 512; Index++) { + PTE1G->Uint64 = Start & PT_ADDR_MASK_1G; + PTE1G->Bits.ReadWrite = 1; + PTE1G->Bits.Present = 1; + PTE1G->Bits.MustBe1 = 1; + PTE1G++; + Start += 0x40000000; + } + + // put it to PML4 + PML4->Uint64 = ((UINT64)PDPE) & PT_ADDR_MASK_4K; + PML4->Bits.ReadWrite = 1; + PML4->Bits.Present = 1; + DEBUG ((DEBUG_VERBOSE, "added to PLM4 as %lx\n", PML4->Uint64)); + // and continue with mapping ... + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PML4->Bits.Nx, PML4->Bits.Accessed, + PML4->Bits.CacheDisabled, PML4->Bits.WriteThrough, + PML4->Bits.UserSupervisor, PML4->Bits.ReadWrite, PML4->Bits.Present, + (PML4->Uint64 & PT_ADDR_MASK_4K) + )); + + // PDPE + PDPE = (PAGE_MAP_AND_DIRECTORY_POINTER *)(PML4->Uint64 & PT_ADDR_MASK_4K); + PDPE += VA.Pg4K.PDPOffset; + VAStart.Pg4K.PDPOffset = VA.Pg4K.PDPOffset; + VAEnd.Pg4K.PDPOffset = VA.Pg4K.PDPOffset; + DEBUG ((DEBUG_VERBOSE, "PDPE[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PDPOffset, PDPE, PDPE->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (!PDPE->Bits.Present || (PDPE->Bits.MustBeZero & 0x1)) { + DEBUG ((DEBUG_VERBOSE, "-> Mapping not present or mapped as 1GB page, creating new PDPE entry and page with PDE entries!\n")); + PDE = (PAGE_MAP_AND_DIRECTORY_POINTER *)VmAllocatePages(1); + if (PDE == NULL) { + DEBUG ((DEBUG_VERBOSE, "No memory - exiting.\n")); + return EFI_NO_MAPPING; + } + ZeroMem(PDE, EFI_PAGE_SIZE); + + if (PDPE->Bits.MustBeZero & 0x1) { + // was 1GB page - init new PDE array to get the same mapping but with 2MB pages + DEBUG ((DEBUG_VERBOSE, "-> was 1GB page, initing new PDE array to get the same mapping but with 2MB pages!\n")); + PTE2M = (PAGE_TABLE_2M_ENTRY *)PDE; + Start = (PDPE->Uint64 & PT_ADDR_MASK_1G); + for (Index = 0; Index < 512; Index++) { + PTE2M->Uint64 = Start & PT_ADDR_MASK_2M; + PTE2M->Bits.ReadWrite = 1; + PTE2M->Bits.Present = 1; + PTE2M->Bits.MustBe1 = 1; + PTE2M++; + Start += 0x200000; + } + } + + // put it to PDPE + PDPE->Uint64 = ((UINT64)PDE) & PT_ADDR_MASK_4K; + PDPE->Bits.ReadWrite = 1; + PDPE->Bits.Present = 1; + DEBUG ((DEBUG_VERBOSE, "added to PDPE as %lx\n", PDPE->Uint64)); + // and continue with mapping ... + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PDPE->Bits.Nx, PDPE->Bits.Accessed, + PDPE->Bits.CacheDisabled, PDPE->Bits.WriteThrough, + PDPE->Bits.UserSupervisor, PDPE->Bits.ReadWrite, PDPE->Bits.Present, + (PDPE->Uint64 & PT_ADDR_MASK_4K) + )); + + // PDE + PDE = (PAGE_MAP_AND_DIRECTORY_POINTER *)(PDPE->Uint64 & PT_ADDR_MASK_4K); + PDE += VA.Pg4K.PDOffset; + VAStart.Pg4K.PDOffset = VA.Pg4K.PDOffset; + VAEnd.Pg4K.PDOffset = VA.Pg4K.PDOffset; + DEBUG ((DEBUG_VERBOSE, "PDE[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PDOffset, PDE, PDE->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (!PDE->Bits.Present || (PDE->Bits.MustBeZero & 0x1)) { + DEBUG ((DEBUG_VERBOSE, "-> Mapping not present or mapped as 2MB page, creating new PDE entry and page with PTE4K entries!\n")); + PTE4K = (PAGE_TABLE_4K_ENTRY *)VmAllocatePages(1); + if (PTE4K == NULL) { + DEBUG ((DEBUG_VERBOSE, "No memory - exiting.\n")); + return EFI_NO_MAPPING; + } + ZeroMem(PTE4K, EFI_PAGE_SIZE); + + if (PDE->Bits.MustBeZero & 0x1) { + // was 2MB page - init new PTE array to get the same mapping but with 4KB pages + DEBUG ((DEBUG_VERBOSE, "-> was 2MB page - initing new PTE array to get the same mapping but with 4KB pages!\n")); + PTE4KTmp = (PAGE_TABLE_4K_ENTRY *)PTE4K; + Start = (PDE->Uint64 & PT_ADDR_MASK_2M); + for (Index = 0; Index < 512; Index++) { + PTE4KTmp->Uint64 = Start & PT_ADDR_MASK_4K; + PTE4KTmp->Bits.ReadWrite = 1; + PTE4KTmp->Bits.Present = 1; + PTE4KTmp++; + Start += 0x1000; + } + } + + // put it to PDE + PDE->Uint64 = ((UINT64)PTE4K) & PT_ADDR_MASK_4K; + PDE->Bits.ReadWrite = 1; + PDE->Bits.Present = 1; + DEBUG ((DEBUG_VERBOSE, "added to PDE as %lx\n", PDE->Uint64)); + // and continue with mapping ... + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PDE->Bits.Nx, PDE->Bits.Accessed, + PDE->Bits.CacheDisabled, PDE->Bits.WriteThrough, + PDE->Bits.UserSupervisor, PDE->Bits.ReadWrite, PDE->Bits.Present, + (PDE->Uint64 & PT_ADDR_MASK_4K) + )); + + // PTE + PTE4K = (PAGE_TABLE_4K_ENTRY *)(PDE->Uint64 & PT_ADDR_MASK_4K); + PTE4K += VA.Pg4K.PTOffset; + VAStart.Pg4K.PTOffset = VA.Pg4K.PTOffset; + VAEnd.Pg4K.PTOffset = VA.Pg4K.PTOffset; + DEBUG ((DEBUG_VERBOSE, "PTE[%03x] at %p = %lx Region: %lx - %lx\n", VA.Pg4K.PTOffset, PTE4K, PTE4K->Uint64, VAStart.Uint64, VAEnd.Uint64)); + if (PTE4K->Bits.Present) { + DEBUG ((DEBUG_VERBOSE, "mapping already present - remapping!\n")); + } + DEBUG ((DEBUG_VERBOSE, "-> Nx:%x|G:%x|PAT:%x|D:%x|A:%x|PCD:%x|PWT:%x|US:%x|RW:%x|P:%x -> %lx\n", + PTE4K->Bits.Nx, PTE4K->Bits.Global, PTE4K->Bits.PAT, + PTE4K->Bits.Dirty, PTE4K->Bits.Accessed, + PTE4K->Bits.CacheDisabled, PTE4K->Bits.WriteThrough, + PTE4K->Bits.UserSupervisor, PTE4K->Bits.ReadWrite, PTE4K->Bits.Present, + (PTE4K->Uint64 & PT_ADDR_MASK_4K) + )); + // put it to PTE + PTE4K->Uint64 = ((UINT64)PhysicalAddr) & PT_ADDR_MASK_4K; + PTE4K->Bits.ReadWrite = 1; + PTE4K->Bits.Present = 1; + DEBUG ((DEBUG_VERBOSE, "added to PTE4K as %lx\n", PTE4K->Uint64)); + + return EFI_SUCCESS; + +} + +/** Maps (remaps) NumPages 4K pages given by VirtualAddr to PhysicalAddr pages in PageTable. */ +EFI_STATUS +VmMapVirtualPages ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + EFI_VIRTUAL_ADDRESS VirtualAddr, + UINTN NumPages, + EFI_PHYSICAL_ADDRESS PhysicalAddr + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + while (NumPages > 0 && (Status == EFI_SUCCESS)) { + Status = VmMapVirtualPage(PageTable, VirtualAddr, PhysicalAddr); + VirtualAddr += 0x1000; + PhysicalAddr += 0x1000; + NumPages--; + DEBUG ((DEBUG_VERBOSE, "NumPages: %d, %lx => %lx\n", NumPages, VirtualAddr, PhysicalAddr)); + } + return Status; +} + +/** Flashes TLB caches. */ +VOID +VmFlushCaches ( + VOID + ) +{ + // just reload CR3 + AsmWriteCr3(AsmReadCr3()); +} diff --git a/MemoryFix/AptioMemoryFix/VMem.h b/MemoryFix/AptioMemoryFix/VMem.h new file mode 100644 index 000000000..ecc8f157d --- /dev/null +++ b/MemoryFix/AptioMemoryFix/VMem.h @@ -0,0 +1,210 @@ +/** + x64 Long Mode Virtual Memory Management Definitions + + References: + 1) IA-32 Intel(R) Architecture Software Developer's Manual Volume 1:Basic Architecture, Intel + 2) IA-32 Intel(R) Architecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel + 3) IA-32 Intel(R) Architecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel + 4) AMD64 Architecture Programmer's Manual Volume 2: System Programming + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Extended by dmazar. + +**/ + +#ifndef APTIOFIX_VMEM_H +#define APTIOFIX_VMEM_H + +#pragma pack(push, 1) + +// +// Page-Map Level-4 Offset (PML4) and +// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB +// + +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Reserved:1; // Reserved + UINT64 MustBeZero:2; // Must Be Zero + UINT64 Available:3; // Available for use by system software + UINT64 PageTableBaseAddress:40; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // No Execute bit + } Bits; + UINT64 Uint64; +} PAGE_MAP_AND_DIRECTORY_POINTER; + +// +// Page Table Entry 4KB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 PAT:1; // + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PageTableBaseAddress:40; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_4K_ENTRY; + +// +// Page Table Entry 2MB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 MustBe1:1; // Must be 1 + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PAT:1; // + UINT64 MustBeZero:8; // Must be zero; + UINT64 PageTableBaseAddress:31; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_2M_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; + +typedef union { + struct { + UINT64 PhysPgOffset:12; // 0 = Physical Page Offset + UINT64 PTOffset:9; // 0 = Page Table Offset + UINT64 PDOffset:9; // 0 = Page Directory Offset + UINT64 PDPOffset:9; // 0 = Page Directory Pointer Offset + UINT64 PML4Offset:9; // 0 = Page Map Level 4 Offset + UINT64 SignExtend:16; // 0 = Sign Extend + } Pg4K; + struct { + UINT64 PhysPgOffset:21; // 0 = Physical Page Offset + UINT64 PDOffset:9; // 0 = Page Directory Offset + UINT64 PDPOffset:9; // 0 = Page Directory Pointer Offset + UINT64 PML4Offset:9; // 0 = Page Map Level 4 Offset + UINT64 SignExtend:16; // 0 = Sign Extend + } Pg2M; + struct { + UINT64 PhysPgOffset:30; // 0 = Physical Page Offset + UINT64 PDPOffset:9; // 0 = Page Directory Pointer Offset + UINT64 PML4Offset:9; // 0 = Page Map Level 4 Offset + UINT64 SignExtend:16; // 0 = Sign Extend + } Pg1G; + UINT64 Uint64; +} VIRTUAL_ADDR; + +#define VA_FIX_SIGN_EXTEND(VA) VA.Pg4K.SignExtend = (VA.Pg4K.PML4Offset & 0x100) ? 0xFFFF : 0; + +#pragma pack(pop) + +// 64 bit +#define CR3_ADDR_MASK 0x000FFFFFFFFFF000 +#define CR3_FLAG_PWT 0x0000000000000008 +#define CR3_FLAG_PCD 0x0000000000000010 + +#define PT_ADDR_MASK_4K 0x000FFFFFFFFFF000 +#define PT_ADDR_MASK_2M 0x000FFFFFFFE00000 +#define PT_ADDR_MASK_1G 0x000FFFFFC0000000 + +/** Returns pointer to PML4 table in PageTable and PWT and PCD flags in Flags. */ +VOID +GetCurrentPageTable ( + PAGE_MAP_AND_DIRECTORY_POINTER **PageTable, + UINTN *Flags + ); + +/** Prints given PageTable. */ +VOID +PrintPageTable ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + UINTN Flags + ); + +/** Returns physical addr for given virtual addr. */ +EFI_STATUS +GetPhysicalAddr ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + EFI_VIRTUAL_ADDRESS VirtualAddr, + EFI_PHYSICAL_ADDRESS *PhysicalAddr + ); + +/** Inits vm memory pool. Should be called while boot services are still usable. */ +EFI_STATUS +VmAllocateMemoryPool ( + VOID + ); + +/** Maps (remaps) 4K page given by VirtualAddr to PhysicalAddr page in PageTable. */ +EFI_STATUS +VmMapVirtualPage ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + EFI_VIRTUAL_ADDRESS VirtualAddr, + EFI_PHYSICAL_ADDRESS PhysicalAddr + ); + +/** Maps (remaps) NumPages 4K pages given by VirtualAddr to PhysicalAddr pages in PageTable. */ +EFI_STATUS +VmMapVirtualPages ( + PAGE_MAP_AND_DIRECTORY_POINTER *PageTable, + EFI_VIRTUAL_ADDRESS VirtualAddr, + UINTN NumPages, + EFI_PHYSICAL_ADDRESS PhysicalAddr + ); + +/** Flushes TLB caches. */ +VOID +VmFlushCaches ( + VOID + ); + +#endif //APTIOFIX_VMEM_H diff --git a/MemoryFix/AptioMemoryFix/X64/AsmFuncs.nasm b/MemoryFix/AptioMemoryFix/X64/AsmFuncs.nasm new file mode 100644 index 000000000..eb7b430ac --- /dev/null +++ b/MemoryFix/AptioMemoryFix/X64/AsmFuncs.nasm @@ -0,0 +1,542 @@ +;------------------------------------------------------------------------------ +; +; Some assembler helper functions plus boot.efi kernel jump callback +; +; by dmazar +; +; converted to nasm by Slice, finished by dmazar +;------------------------------------------------------------------------------ + +struc XDTR + Limit resw 1 + Base resq 1 +endstruc + +; C callback method called on jump to kernel after boot.efi finishes +extern ASM_PFX(KernelEntryPatchJumpBack) + +; saved 64bit state +global ASM_PFX(SavedGDTR) +global ASM_PFX(SavedIDTR) +global ASM_PFX(SavedCR3) + +; addresses of relocated JumpToKernel code - filled by PrepareJumpFromKernel() +global ASM_PFX(JumpToKernel32Addr) +global ASM_PFX(JumpToKernel64Addr) + +; kernel entry address - filled by KernelEntryPatchJump() +global ASM_PFX(AsmKernelEntry) + +; end of EntryPatchCode func +global ASM_PFX(EntryPatchCodeEnd) + +; start and end of JumpToKernel +global ASM_PFX(JumpToKernel) +global ASM_PFX(JumpToKernel32) +global ASM_PFX(JumpToKernel64) +global ASM_PFX(JumpToKernelEnd) + +SECTION .text + +; +; Adding data to code segment to avoid some error: +; Undefined symbols for architecture x86_64: +; "_SavedGDTR", referenced from: +; _AsmPrepareJumpFromKernel in OsxAptioFixDrv.lib(AsmFuncsX64.obj) +; + +; variables accessed from both 32 and 64 bit code +; need to have this exactly in this order +DataBase: + +; 64 bit state +SavedGDTROff EQU $-DataBase +ASM_PFX(SavedGDTR): + dw 0 + dq 0 + +SavedIDTROff EQU $-DataBase +ASM_PFX(SavedIDTR): + dw 0 + dq 0 + +align 08h, db 0 + +SavedCR3Off EQU $-DataBase +ASM_PFX(SavedCR3): + dq 0 + +SavedCSOff EQU $-DataBase +SavedCS: + dw 0 + +SavedDSOff EQU $-DataBase +SavedDS: + dw 0 + +; 32 bit state +SavedGDTR32Off EQU $-DataBase +SavedGDTR32: + dw 0 + dq 0 + +SavedIDTR32Off EQU $-DataBase +SavedIDTR32: + dw 0 + dq 0 + +SavedCS32Off EQU $-DataBase +SavedCS32: + dw 0 + +SavedDS32Off EQU $-DataBase +SavedDS32: + dw 0 + +SavedESP32Off EQU $-DataBase +SavedESP32: + dd 0 + +align 08h, db 0 + +; address of relocated JumpToKernel32 - 64 bit +JumpToKernel32AddrOff EQU $-DataBase +ASM_PFX(JumpToKernel32Addr): + dq 0 + +; address of relocated JumpToKernel64 - 64 bit +JumpToKernel64AddrOff EQU $-DataBase +ASM_PFX(JumpToKernel64Addr): + dq 0 + +; kernel entry - 64 bit +AsmKernelEntryOff EQU $-DataBase +ASM_PFX(AsmKernelEntry): + dq 0 + +align 08h, db 0 + +; GDT not used since we are reusing UEFI state +; but left here in case will be needed. +; +; GDR record +GDTROff EQU $-DataBase +GDTR dw GDT_END - GDT_BASE - 1 ; GDT limit +GDTR_BASE dq 0 ; GDT base - needs to be set in code + +align 08h, db 0 + +; GDT +GDT_BASE: + +; null descriptor +NULL_SEL EQU $-GDT_BASE ; 0x00 + dw 0 ; limit 15:0 + dw 0 ; base 15:0 + db 0 ; base 23:16 + db 0 ; type + db 0 ; limit 19:16, flags + db 0 ; base 31:24 + +; 64 bit code segment descriptor +CODE64_SEL EQU $-GDT_BASE ; 0x08 + dw 0FFFFh ; limit 0xFFFFF + dw 0 ; base 0 + db 0 + db 09Ah ; P=1 | DPL=00 | S=1 (User) # Type=A=1010: Code/Data=1 | C:Conforming=0 | R:Readable=1 | A:Accessed=0 + db 0AFh ; Flags=A=1010: G:Granularity=1 (4K) | D:Default Operand Size=0 (in long mode) | L:Long=1 (64 bit) | AVL=0 + db 0 + +; 32 bit and 64 bit data segment descriptor (in 64 bit almost all is ignored, so can be reused) +DATA_SEL EQU $-GDT_BASE ; 0x10 + dw 0FFFFh ; limit 0xFFFFF + dw 0 ; base 0 + db 0 + db 092h ; P=1 | DPL=00 | S=1 (User) # Type=2=0010: Code/Data=0 | E:Expand-Down=0 | W:Writable=1 | A:Accessed=0 + db 0CFh ; Flags=C=1100: G:Granularity=1 (4K) | D/B=1 D not used when E=0, for stack B=1 means 32 bit stack | L:Long=0 not used | AVL=0 + db 0 + +; 32 bit code segment descriptor +CODE32_SEL EQU $-GDT_BASE ; 0x18 + dw 0FFFFh ; limit 0xFFFFF + dw 0 ; base 0 + db 0 + db 09Ah ; P=1 | DPL=00 | S=1 (User) # Type=A=1010: Code/Data=1 | C:Conforming=0 | R:Readable=1 | A:Accessed=0 + db 0CFh ; Flags=C=1100: G:Granularity=1 (4K) | D:Default Operand Size=0 (in long mode) | L:Long=0 (32 bit) | AVL=0 + db 0 + +GDT_END: + +;SECTION .text + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; AsmPrepareJumpFromKernel ( +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(AsmPrepareJumpFromKernel) +ASM_PFX(AsmPrepareJumpFromKernel): +BITS 64 + ; save 64 bit state + sgdt [REL ASM_PFX(SavedGDTR)] + sidt [REL ASM_PFX(SavedIDTR)] + mov rax, cr3 + mov [REL ASM_PFX(SavedCR3)], rax + mov word [REL SavedCS], cs + mov word [REL SavedDS], ds + + ; pass DataBase to 32 bit code + lea rax, [REL DataBase] + mov dword [REL DataBaseAdr], eax; + + ; prepare EntryPatchCode: + ; patch EntryPatchCode with address of AsmJumpFromKernel + lea rax, [REL ASM_PFX(AsmJumpFromKernel)] + mov dword [REL EntryPatchCodeJumpFromKernelPlaceholder], eax + + ret + +;------------------------------------------------------------------------------ +; Code that is used for patching kernel entry to jump back +; to our code (to AsmJumpFromKernel): +; - load ecx (rcx) with address to AsmJumpFromKernel +; - jump to AsmJumpFromKernel +; The same generated opcode must run properly in both 32 and 64 bit. +; 64 bit: +; - we must set rcx to 0 (upper 4 bytes) before loading ecx with address (lower 4 bytes of rcx) +; - this requires xor %rcx, %rcx +; - and that opcode contains 0x48 in front of 32 bit xor %ecx, %ecx +; 32 bit: +; - 0x48 opcode is dec %eax in 32 bit +; - and then we must inc %eax later if 32 bit is detected in AsmJumpFromKernel +; +; This code is patched with address of AsmJumpFromKernel +; (into EntryPatchCodeJumpFromKernelPlaceholder) +; and then copied to kernel entry address by KernelEntryPatchJump() +;------------------------------------------------------------------------------ +global ASM_PFX(EntryPatchCode) +ASM_PFX(EntryPatchCode): +BITS 32 + dec eax ; -> 48 + xor ecx, ecx ; -> 31 C9 + db 0b9h ; movl $0x11223344, %ecx -> B9 44 33 22 11 +EntryPatchCodeJumpFromKernelPlaceholder: + dd 011223344h + call ecx ; -> FF D1 + +;BITS 64 +; xor rcx, rcx ; -> 48 31 C9 +; mov dword ecx, 011223344h ; -> B9 44 33 22 11 +; call rcx ; -> FF D1 +ASM_PFX(EntryPatchCodeEnd): + +;------------------------------------------------------------------------------ +; AsmJumpFromKernel +; +; Callback from boot.efi - this is where we jump when boot.efi jumps to kernel. +; +; - test if we are in 32 bit or in 64 bit +; - if 64 bit, then jump to AsmJumpFromKernel64 +; - else just continue with AsmJumpFromKernel32 +;------------------------------------------------------------------------------ +global ASM_PFX(AsmJumpFromKernel) +ASM_PFX(AsmJumpFromKernel): +; writing in 32 bit, but code must run in 64 bit also +BITS 32 + push eax ; save bootArgs pointer to stack + mov dword ecx, 0c0000080h ; EFER MSR number. + rdmsr ; Read EFER. + bt eax, 8 ; Check if LME==1 -> CF=1. + pop eax + jc AsmJumpFromKernel64 ; LME==1 -> jump to 64 bit code + ; otherwise, continue with AsmJumpFromKernel32 + ; but first add 1 to it since it was decremented in 32 bit + ; in EntryPatchCode + inc eax + + ; test the above code in 64 bit - above 32 bit code gives opcode + ; that is equivalent to following in 64 bit +;BITS 64 +; push rax ; save bootArgs pointer to stack +; movl ecx, 0c0000080h ; EFER MSR number. +; rdmsr ; Read EFER. +; bt eax, 8 ; Check if LME==1 -> CF=1. +; pop rax +; jc AsmJumpFromKernel64 ; LME==1 -> jump to 64 bit code + +;------------------------------------------------------------------------------ +; AsmJumpFromKernel32 +; +; Callback from boot.efi in 32 bit mode. +; State is prepared for kernel: 32 bit, no paging, pointer to bootArgs in eax. +; +; MS 64 bit compiler generates only 64 bit opcode, but this function needs +; combined 32 and 64 bit code. Code can be written only with 64 bit instructions, +; but generated opcode must be valid 32 bit. This is a big issue. +; Well, I guess I know now how the guys in Intel are feeling when +; they have to work with MS tools on a similar code. +; +; Another problem is that it's not possible to access saved variables +; from 32 bit code (64 bit code has relative addresses, but 32 bit does not +; and depends on fixes during load and that is not happening sice +; generated code is marked 64 bit, or something similar). +; To overcome this, starting address of ou DataBase is passed in runtime - stored +; to DataBaseAdr below as an argument to mov. +;------------------------------------------------------------------------------ +AsmJumpFromKernel32: +BITS 32 + ; save bootArgs pointer to edi + mov edi, eax + + ; load ebx with DataBase - we'll access our saved data with it + db 0BBh ; mov ebx, OFFSET DataBase +DataBaseAdr: + dd 0 + + ; let's find out kernel entry point - we'll need it to jump back. + ; we are called with + ; dec eax + ; xor ecx, ecx + ; mov ecx, 011223344h + ; call ecx + ; and that left return addr on stack. those instructions + ; are 10 bytes long, and if we take address from stack and + ; substitute 10 from it, we will get kernel entry point. + pop ecx ; 32 bit: pop ecx + sub ecx, 10 + ; and save it + mov dword [ebx + AsmKernelEntryOff], ecx + + ; lets save 32 bit state to be able to recover it later + sgdt [ebx + SavedGDTR32Off] + sidt [ebx + SavedIDTR32Off] + mov word [ebx + SavedCS32Off], cs + mov word [ebx + SavedDS32Off], ds + mov dword [ebx + SavedESP32Off], esp + + ; + ; move to 64 bit mode ... + ; + + ; FIXME: all this with interrupts enabled? no-no + + ; load saved UEFI GDT, IDT + ; will become active after code segment is changed in long jump + ; rbx is ebx in 32 bit + lgdt [ebx + SavedGDTROff] + lidt [ebx + SavedIDTROff] + + ; enable the 64-bit page-translation-table entries by setting CR4.PAE=1 + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + ; set the long-mode page tables - reuse saved UEFI tables + mov eax, dword [ebx + SavedCR3Off] + mov cr3, eax + + ; enable long mode (set EFER.LME=1). + mov ecx, 0c0000080h ; EFER MSR number. + rdmsr ; Read EFER. + bts eax, 8 ; Set LME=1. + wrmsr ; Write EFER. + + ; enable paging to activate long mode (set CR0.PG=1) + mov eax, cr0 ; Read CR0. + bts eax, 31 ; Set PG=1. + mov cr0, eax ; Write CR0. + + ; jump to the 64-bit code segment + mov ax, word [ebx + SavedCSOff] + push eax + call _RETF32 + + ; + ; aloha! + ; if there is any luck, we are in 64 bit mode now + ; + +BITS 64 + + ; set segmens + mov ax, word [rbx + SavedDSOff] + mov ds, ax + ; set up stack ... + ; not sure if needed, but lets set ss to ds + mov ss, ax ; disables interrupts for 1 instruction to load rsp + ; lets align the stack + and rsp, 0fffffffffffffff0h + + ; call our C code + ; (calling conv.: always reserve place for 4 args on stack) + ; KernelEntryPatchJumpBack (rcx = rax = bootArgs, rdx = 0 = 32 bit kernel jump) + mov rcx, rdi + xor rdx, rdx + push rdx + push rdx + push rdx + push rcx + + ; KernelEntryPatchJumpBack should be EFIAPI + ; and rbx should not be changed by EFIAPI calling convention + call ASM_PFX(KernelEntryPatchJumpBack) + ;hlt ; uncomment to stop here for test + ; return value in rax is bootArgs pointer + mov rdi, rax + + ; + ; time to go back to 32 bit + ; + + ; FIXME: all this with interrupts enabled? no-no + + ; load saved 32 bit gdtr + lgdt [rbx + SavedGDTR32Off] + ; push saved cs and rip (with call) to stack and do retf + mov ax, WORD [rbx + SavedCS32Off] + push rax + call _RETF64 + + ; + ; ok, 32 bit opcode again from here + ; + +BITS 32 + + ; disable paging (set CR0.PG=0) + mov eax, cr0 ; Read CR0. + btr eax, 31 ; Set PG=0. + mov cr0, eax ; Write CR0. + + ; disable long mode (set EFER.LME=0). + mov ecx, 0c0000080h ; EFER MSR number. + rdmsr ; Read EFER. + btr eax, 8 ; Set LME=0. + wrmsr ; Write EFER. + jmp toNext + +toNext: + + ; + ; we are in 32 bit protected mode, no paging + ; + + ; now reload saved 32 bit state data + lidt [ebx + SavedIDTR32Off] + mov ax, word [ebx + SavedDS32Off] + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax ; disables interrupts for 1 instruction to load esp + mov esp, dword [ebx + SavedESP32Off] + + ; prepare jump to kernel: set registers as needed by JumpToKernel32 + + ; boot args back from edi + mov eax, edi + ; kernel entry point + mov edx, dword [ebx + AsmKernelEntryOff] + + ; address of relocated JumpToKernel32 + mov ebx, dword [ebx + JumpToKernel32AddrOff] + ; note: ebx not valid as a pointer to DataBase any more + + ; jump to JumpToKernel32 + jmp ebx + +_RETF64: + db 048h +_RETF32: + retf + +;------------------------------------------------------------------------------ +; AsmJumpFromKernel64 +; +; Callback from boot.efi in 64 bit mode. +; State is prepared for kernel: 64 bit, pointer to bootArgs in rax. +;------------------------------------------------------------------------------ +AsmJumpFromKernel64: +BITS 64 + ; let's find out kernel entry point - we'll need it to jump back. + pop rcx + sub rcx, 10 + ; and save it + mov qword [REL ASM_PFX(AsmKernelEntry)], rcx + + ; call our C code + ; (calling conv.: always reserve place for 4 args on stack) + ; KernelEntryPatchJumpBack (rcx = rax = bootArgs, rdx = 1 = 64 bit kernel jump) + mov rcx, rax + xor rdx, rdx + inc edx + push rdx + push rdx + push rdx + push rcx + ; KernelEntryPatchJumpBack should be EFIAPI + call ASM_PFX(KernelEntryPatchJumpBack) + ; return value in rax is bootArgs pointer + + ; prepare to jump to kernel: set registers as needed by JumpToKernel64 + + ; kernel entry point + mov rdx, [REL ASM_PFX(AsmKernelEntry)] + + ; address of relocated JumpToKernel64 + mov rbx, [REL ASM_PFX(JumpToKernel64Addr)] + + ; jump to JumpToKernel64 + jmp rbx +;AsmJumpFromKernel64 ENDP + +;------------------------------------------------------------------------------ +; JumpToKernel +; +; This is the last part of the code - it will jump to kernel. +; There are separate versions for 32 and 64 bit. +; This code will be relocated (copied) to higher mem by PrepareJumpFromKernel(). +;------------------------------------------------------------------------------ +align 08h +global ASM_PFX(JumpToKernel) +ASM_PFX(JumpToKernel): + +;------------------------------------------------------------------------------ +; JumpToKernel32 +; +; Expects: +; EAX = address of boot args (proper address, not from reloc block) +; EDX = kernel entry point +;------------------------------------------------------------------------------ +global ASM_PFX(JumpToKernel32) +ASM_PFX(JumpToKernel32): +BITS 32 + ; Jump to kernel: + ; EAX already contains bootArgs pointer, + ; and EDX contains kernel entry point + jmp edx +;JumpToKernel32 ENDP +JumpToKernel32End: + +;------------------------------------------------------------------------------ +; JumpToKernel64 +; +; Expects: +; RAX = address of boot args (proper address, not from reloc block) +; RDX = kernel entry point +;------------------------------------------------------------------------------ +align 08h +global ASM_PFX(JumpToKernel64) +ASM_PFX(JumpToKernel64): +BITS 64 + ; Jump to kernel: + ; RAX already contains bootArgs pointer, + ; and RDX contains kernel entry point + jmp rdx +;JumpToKernel64 ENDP +JumpToKernel64End: + +global ASM_PFX(JumpToKernelEnd) +ASM_PFX(JumpToKernelEnd): +;END diff --git a/MemoryFix/AptioMemoryFix/X64/AsmRtShims.nasm b/MemoryFix/AptioMemoryFix/X64/AsmRtShims.nasm new file mode 100644 index 000000000..ef702516b --- /dev/null +++ b/MemoryFix/AptioMemoryFix/X64/AsmRtShims.nasm @@ -0,0 +1,300 @@ +;------------------------------------------------------------------------------ +; +; Functions to wrap RuntimeServices code that needs to be written to +; +; by Download-Fritz & vit9696 +; refactored by Zenith432 +;------------------------------------------------------------------------------ + +BITS 64 +DEFAULT REL + +; Constructs a shim with write protection patch avoiding +; a conflict with XNU W^X mapping. +; The argument marks a number of args the func takes (1~5). +%macro ConstructShim 1 +%if %1 > 5 + %error "At Most 5 Args Supported." +%endif + cmp qword [ASM_PFX(gRequiresWriteUnprotect)], 0 + jz .SKIP_WRITE_UNPROTECT + push rsi + push rbx + sub rsp, 0x28 + pushfq + cli + pop rsi + push rax + mov rbx, cr0 + mov rax, rbx + and rax, 0xfffffffffffeffff + mov cr0, rax +%if %1 > 4 + mov rax, qword [rsp+0x68] + mov qword [rsp+0x28], rax +%endif + pop rax + call rax + add rsp, 0x28 + test ebx, 0x10000 + je .SKIP_RESTORE_WP + mov cr0, rbx +.SKIP_RESTORE_WP: + pop rbx + test si, 0x200 + pop rsi + je .SKIP_RESTORE_INTR + sti +.SKIP_RESTORE_INTR: + ret +.SKIP_WRITE_UNPROTECT: + jmp rax +%endmacro + +; Redirects Boot prefixed variables from gBootVariableGuid +; to gRedirectVariableGuid. +; Variable name is assumed to be in %rcx. +; Guid is assumed to be in %rdx. +; Temporary registers: %rax. +%macro PerformBootVariableRedirect 0 + ; Check if we have variable redirection enabled. + mov rax, qword [ASM_PFX(gBootVariableRedirect)] + test rax, rax + jz .SKIP_BOOT_VARIABLE_REDIRECT + ; Compare whether GUID matches gBootVariableGuid + mov rax, qword [rdx] + cmp qword [ASM_PFX(gBootVariableGuid)], rax + jnz .SKIP_BOOT_VARIABLE_REDIRECT + mov rax, qword [rdx+8] + cmp qword [ASM_PFX(gBootVariableGuid)+8], rax + jnz .SKIP_BOOT_VARIABLE_REDIRECT + ; Compare whether variable prefix matches Boot + mov ax, word [rcx] + cmp ax, 'B' + jnz .SKIP_BOOT_VARIABLE_REDIRECT + mov ax, word [rcx+2] + cmp ax, 'o' + jnz .SKIP_BOOT_VARIABLE_REDIRECT + mov ax, word [rcx+4] + cmp ax, 'o' + jnz .SKIP_BOOT_VARIABLE_REDIRECT + mov ax, word [rcx+6] + cmp ax, 't' + jnz .SKIP_BOOT_VARIABLE_REDIRECT + ; This is a Boot prefixed variable from gBootVariableGuid. + ; Redirect it to gRedirectVariableGuid. + lea rdx, [ASM_PFX(gRedirectVariableGuid)] +.SKIP_BOOT_VARIABLE_REDIRECT: +%endmacro + +SECTION .text + +ALIGN 8 ; to align the dqs + +global ASM_PFX(gRtShimsDataStart) +ASM_PFX(gRtShimsDataStart): + +global ASM_PFX(RtShimsReturnInvalidParameter) +ASM_PFX(RtShimsReturnInvalidParameter): + mov rax, 0x8000000000000002 + ret + +global ASM_PFX(RtShimsReturnSecurityViolation) +ASM_PFX(RtShimsReturnSecurityViolation): + mov rax, 0x800000000000001A + ret + +global ASM_PFX(RtShimSetVariable) +ASM_PFX(RtShimSetVariable): + ; For performance and simplicity do initial validation ourselves. + test rcx, rcx + jz ASM_PFX(RtShimsReturnInvalidParameter) ; VariableName is NULL + test rdx, rdx + jz ASM_PFX(RtShimsReturnInvalidParameter) ; VendorGuid is NULL +.INITIAL_VALIDATION_OVER: + PerformBootVariableRedirect + ; Once boot.efi virtualizes the pointers we should protect read-only + ; variables from writing. + mov rax, qword [ASM_PFX(gGetVariableOverride)] + test rax, rax + jnz .SKIP_ACCESS_CHECK + ; We have a virtualized pointer, so we also need to protect write-only + ; variables from reading. Compare VendorGuid against gReadOnlyVariableGuid + ; and return EFI_SECURITY_VIOLATION on equals. + mov rax, qword [rdx] + cmp qword [ASM_PFX(gReadOnlyVariableGuid)], rax + jnz .SKIP_ACCESS_CHECK + mov rax, qword [rdx+8] + cmp qword [ASM_PFX(gReadOnlyVariableGuid)+8], rax + jz ASM_PFX(RtShimsReturnSecurityViolation) +.SKIP_ACCESS_CHECK: + mov rax, qword [ASM_PFX(gSetVariable)] + jmp FiveArgsShim + +global ASM_PFX(RtShimGetVariable) +ASM_PFX(RtShimGetVariable): + ; For performance and simplicity do initial validation ourselves. + test rcx, rcx + jz ASM_PFX(RtShimsReturnInvalidParameter) ; VariableName is NULL + test rdx, rdx + jz ASM_PFX(RtShimsReturnInvalidParameter) ; VendorGuid is NULL + test r9, r9 + jz ASM_PFX(RtShimsReturnInvalidParameter) ; DataSize is NULL + cmp qword [rsp+0x28], 0 + jnz .INITIAL_VALIDATION_OVER ; Data is not NULL + mov rax, qword [r9] + test rax, rax + jnz ASM_PFX(RtShimsReturnInvalidParameter) ; Data is NULL and *DataSize is not 0 +.INITIAL_VALIDATION_OVER: + PerformBootVariableRedirect + ; Once boot.efi virtualizes the pointers we should protect write-only + ; variables from reading. Prior to that a custom wrapper is used. + mov rax, qword [ASM_PFX(gGetVariableOverride)] + test rax, rax + jnz .SKIP_ACCESS_CHECK_WRAPPER + ; We have a virtualized pointer, so we also need to protect write-only + ; variables from reading. Compare VendorGuid against gWriteOnlyVariableGuid + ; and return EFI_SECURITY_VIOLATION on equals. + mov rax, qword [rdx] + cmp qword [ASM_PFX(gWriteOnlyVariableGuid)], rax + jnz .SKIP_ACCESS_CHECK_INTERNAL + mov rax, qword [rdx+8] + cmp qword [ASM_PFX(gWriteOnlyVariableGuid)+8], rax + jz ASM_PFX(RtShimsReturnSecurityViolation) +.SKIP_ACCESS_CHECK_INTERNAL: + mov rax, qword [ASM_PFX(gGetVariable)] +.SKIP_ACCESS_CHECK_WRAPPER: + ;jmp short FiveArgsShim + ; fall through to FiveArgsShim + +FiveArgsShim: + ConstructShim 5 + +global ASM_PFX(RtShimGetNextVariableName) +ASM_PFX(RtShimGetNextVariableName): + ; TODO: I am not sure whether we need GetNextVariableName support + ; for boot variable routing... Probably good enough without it. + mov rax, qword [ASM_PFX(gGetNextVariableName)] + jmp short FourArgsShim + +global ASM_PFX(RtShimGetTime) +ASM_PFX(RtShimGetTime): + ; On old AMI firmwares (like the one found in GA-Z87X-UD4H) there is a chance + ; of getting 2047 (EFI_UNSPECIFIED_TIMEZONE) from GetTime. This is valid, + ; yet is disliked by some software including but not limited to UEFI Shell. + ; See the patch: https://lists.01.org/pipermail/edk2-devel/2018-May/024534.html + ; As a workaround we make sure this does not happen at all. + push rsi + push rbx + push rcx ; Save the original EFI_TIME pointer + sub rsp, 0x20 + pushfq + cli + pop rsi + mov rbx, cr0 + mov rax, rbx + and rax, 0xfffffffffffeffff + mov cr0, rax + mov rax, qword [ASM_PFX(gGetTime)] + call rax + add rsp, 0x20 + test ebx, 0x10000 + je .SKIP_RESTORE_WP + mov cr0, rbx +.SKIP_RESTORE_WP: + pop rbx ; load saved EFI_TIME pointer + test rax, rax ; check for EFI_ERROR + js .SKIP_CORRECT_TIMEZONE + cmp word [rbx + 12], 2047 ; offsetof(EFI_TIME, TimeZone) + jnz .SKIP_CORRECT_TIMEZONE + mov word [rbx + 12], 0 ; default to UTC +.SKIP_CORRECT_TIMEZONE: + pop rbx + test si, 0x200 + pop rsi + je .SKIP_RESTORE_INTR + sti +.SKIP_RESTORE_INTR: + ret + +global ASM_PFX(RtShimSetTime) +ASM_PFX(RtShimSetTime): + mov rax, qword [ASM_PFX(gSetTime)] + jmp short FourArgsShim + +global ASM_PFX(RtShimGetWakeupTime) +ASM_PFX(RtShimGetWakeupTime): + mov rax, qword [ASM_PFX(gGetWakeupTime)] + jmp short FourArgsShim + +global ASM_PFX(RtShimSetWakeupTime) +ASM_PFX(RtShimSetWakeupTime): + mov rax, qword [ASM_PFX(gSetWakeupTime)] + jmp short FourArgsShim + +global ASM_PFX(RtShimGetNextHighMonoCount) +ASM_PFX(RtShimGetNextHighMonoCount): + mov rax, qword [ASM_PFX(gGetNextHighMonoCount)] + jmp short FourArgsShim + +global ASM_PFX(RtShimResetSystem) +ASM_PFX(RtShimResetSystem): + mov rax, qword [ASM_PFX(gResetSystem)] ; Note - doesn't return! + ;jmp short FourArgsShim + ; fall through to FourArgsShim + +FourArgsShim: + ConstructShim 4 + +ALIGN 8 + +global ASM_PFX(gRequiresWriteUnprotect) +ASM_PFX(gRequiresWriteUnprotect): dq 0 + +global ASM_PFX(gBootVariableRedirect) +ASM_PFX(gBootVariableRedirect): dq 0 + +global ASM_PFX(gGetNextVariableName) +ASM_PFX(gGetNextVariableName): dq 0 + +global ASM_PFX(gGetVariable) +ASM_PFX(gGetVariable): dq 0 + +global ASM_PFX(gSetVariable) +ASM_PFX(gSetVariable): dq 0 + +global ASM_PFX(gGetTime) +ASM_PFX(gGetTime): dq 0 + +global ASM_PFX(gSetTime) +ASM_PFX(gSetTime): dq 0 + +global ASM_PFX(gGetWakeupTime) +ASM_PFX(gGetWakeupTime): dq 0 + +global ASM_PFX(gSetWakeupTime) +ASM_PFX(gSetWakeupTime): dq 0 + +global ASM_PFX(gGetNextHighMonoCount) +ASM_PFX(gGetNextHighMonoCount): dq 0 + +global ASM_PFX(gResetSystem) +ASM_PFX(gResetSystem): dq 0 + +global ASM_PFX(gGetVariableOverride) +ASM_PFX(gGetVariableOverride): dq 0 + +global ASM_PFX(gBootVariableGuid) +ASM_PFX(gBootVariableGuid): times 2 dq 0 + +global ASM_PFX(gRedirectVariableGuid) +ASM_PFX(gRedirectVariableGuid): times 2 dq 0 + +global ASM_PFX(gReadOnlyVariableGuid) +ASM_PFX(gReadOnlyVariableGuid): times 2 dq 0 + +global ASM_PFX(gWriteOnlyVariableGuid) +ASM_PFX(gWriteOnlyVariableGuid): times 2 dq 0 + +global ASM_PFX(gRtShimsDataEnd) +ASM_PFX(gRtShimsDataEnd): diff --git a/OsxAptioFixDrv/AsmFuncs.h b/MemoryFix/OsxAptioFixDrv/AsmFuncs.h similarity index 100% rename from OsxAptioFixDrv/AsmFuncs.h rename to MemoryFix/OsxAptioFixDrv/AsmFuncs.h diff --git a/OsxAptioFixDrv/BootArgs.c b/MemoryFix/OsxAptioFixDrv/BootArgs.c similarity index 100% rename from OsxAptioFixDrv/BootArgs.c rename to MemoryFix/OsxAptioFixDrv/BootArgs.c diff --git a/OsxAptioFixDrv/BootArgs.h b/MemoryFix/OsxAptioFixDrv/BootArgs.h similarity index 100% rename from OsxAptioFixDrv/BootArgs.h rename to MemoryFix/OsxAptioFixDrv/BootArgs.h diff --git a/OsxAptioFixDrv/BootFixes.c b/MemoryFix/OsxAptioFixDrv/BootFixes.c similarity index 100% rename from OsxAptioFixDrv/BootFixes.c rename to MemoryFix/OsxAptioFixDrv/BootFixes.c diff --git a/OsxAptioFixDrv/BootFixes.h b/MemoryFix/OsxAptioFixDrv/BootFixes.h similarity index 100% rename from OsxAptioFixDrv/BootFixes.h rename to MemoryFix/OsxAptioFixDrv/BootFixes.h diff --git a/OsxAptioFixDrv/BootFixes3.c b/MemoryFix/OsxAptioFixDrv/BootFixes3.c similarity index 100% rename from OsxAptioFixDrv/BootFixes3.c rename to MemoryFix/OsxAptioFixDrv/BootFixes3.c diff --git a/OsxAptioFixDrv/BootFixes3.h b/MemoryFix/OsxAptioFixDrv/BootFixes3.h similarity index 100% rename from OsxAptioFixDrv/BootFixes3.h rename to MemoryFix/OsxAptioFixDrv/BootFixes3.h diff --git a/OsxAptioFixDrv/DecodedKernelCheck.c b/MemoryFix/OsxAptioFixDrv/DecodedKernelCheck.c similarity index 100% rename from OsxAptioFixDrv/DecodedKernelCheck.c rename to MemoryFix/OsxAptioFixDrv/DecodedKernelCheck.c diff --git a/OsxAptioFixDrv/DecodedKernelCheck.h b/MemoryFix/OsxAptioFixDrv/DecodedKernelCheck.h similarity index 100% rename from OsxAptioFixDrv/DecodedKernelCheck.h rename to MemoryFix/OsxAptioFixDrv/DecodedKernelCheck.h diff --git a/OsxAptioFixDrv/FlatDevTree/device_tree.c b/MemoryFix/OsxAptioFixDrv/FlatDevTree/device_tree.c similarity index 100% rename from OsxAptioFixDrv/FlatDevTree/device_tree.c rename to MemoryFix/OsxAptioFixDrv/FlatDevTree/device_tree.c diff --git a/OsxAptioFixDrv/FlatDevTree/device_tree.h b/MemoryFix/OsxAptioFixDrv/FlatDevTree/device_tree.h similarity index 100% rename from OsxAptioFixDrv/FlatDevTree/device_tree.h rename to MemoryFix/OsxAptioFixDrv/FlatDevTree/device_tree.h diff --git a/OsxAptioFixDrv/Hibernate.h b/MemoryFix/OsxAptioFixDrv/Hibernate.h similarity index 100% rename from OsxAptioFixDrv/Hibernate.h rename to MemoryFix/OsxAptioFixDrv/Hibernate.h diff --git a/OsxAptioFixDrv/Lib.c b/MemoryFix/OsxAptioFixDrv/Lib.c similarity index 100% rename from OsxAptioFixDrv/Lib.c rename to MemoryFix/OsxAptioFixDrv/Lib.c diff --git a/OsxAptioFixDrv/Lib.h b/MemoryFix/OsxAptioFixDrv/Lib.h similarity index 100% rename from OsxAptioFixDrv/Lib.h rename to MemoryFix/OsxAptioFixDrv/Lib.h diff --git a/OsxAptioFixDrv/Mach-O/Mach-O.c b/MemoryFix/OsxAptioFixDrv/Mach-O/Mach-O.c similarity index 100% rename from OsxAptioFixDrv/Mach-O/Mach-O.c rename to MemoryFix/OsxAptioFixDrv/Mach-O/Mach-O.c diff --git a/OsxAptioFixDrv/Mach-O/Mach-O.h b/MemoryFix/OsxAptioFixDrv/Mach-O/Mach-O.h similarity index 100% rename from OsxAptioFixDrv/Mach-O/Mach-O.h rename to MemoryFix/OsxAptioFixDrv/Mach-O/Mach-O.h diff --git a/OsxAptioFixDrv/Mach-O/UefiLoader.h b/MemoryFix/OsxAptioFixDrv/Mach-O/UefiLoader.h similarity index 100% rename from OsxAptioFixDrv/Mach-O/UefiLoader.h rename to MemoryFix/OsxAptioFixDrv/Mach-O/UefiLoader.h diff --git a/OsxAptioFixDrv/Mach-O/loader.h b/MemoryFix/OsxAptioFixDrv/Mach-O/loader.h similarity index 100% rename from OsxAptioFixDrv/Mach-O/loader.h rename to MemoryFix/OsxAptioFixDrv/Mach-O/loader.h diff --git a/OsxAptioFixDrv/NVRAMDebug.c b/MemoryFix/OsxAptioFixDrv/NVRAMDebug.c similarity index 100% rename from OsxAptioFixDrv/NVRAMDebug.c rename to MemoryFix/OsxAptioFixDrv/NVRAMDebug.c diff --git a/OsxAptioFixDrv/NVRAMDebug.h b/MemoryFix/OsxAptioFixDrv/NVRAMDebug.h similarity index 100% rename from OsxAptioFixDrv/NVRAMDebug.h rename to MemoryFix/OsxAptioFixDrv/NVRAMDebug.h diff --git a/OsxAptioFixDrv/OsxAptioFix2Drv.c b/MemoryFix/OsxAptioFixDrv/OsxAptioFix2Drv.c similarity index 100% rename from OsxAptioFixDrv/OsxAptioFix2Drv.c rename to MemoryFix/OsxAptioFixDrv/OsxAptioFix2Drv.c diff --git a/OsxAptioFixDrv/OsxAptioFix2Drv.inf b/MemoryFix/OsxAptioFixDrv/OsxAptioFix2Drv.inf similarity index 100% rename from OsxAptioFixDrv/OsxAptioFix2Drv.inf rename to MemoryFix/OsxAptioFixDrv/OsxAptioFix2Drv.inf diff --git a/OsxAptioFixDrv/OsxAptioFix3Drv.c b/MemoryFix/OsxAptioFixDrv/OsxAptioFix3Drv.c similarity index 100% rename from OsxAptioFixDrv/OsxAptioFix3Drv.c rename to MemoryFix/OsxAptioFixDrv/OsxAptioFix3Drv.c diff --git a/OsxAptioFixDrv/OsxAptioFix3Drv.inf b/MemoryFix/OsxAptioFixDrv/OsxAptioFix3Drv.inf similarity index 100% rename from OsxAptioFixDrv/OsxAptioFix3Drv.inf rename to MemoryFix/OsxAptioFixDrv/OsxAptioFix3Drv.inf diff --git a/OsxAptioFixDrv/OsxAptioFixDrv.c b/MemoryFix/OsxAptioFixDrv/OsxAptioFixDrv.c similarity index 100% rename from OsxAptioFixDrv/OsxAptioFixDrv.c rename to MemoryFix/OsxAptioFixDrv/OsxAptioFixDrv.c diff --git a/OsxAptioFixDrv/OsxAptioFixDrv.inf b/MemoryFix/OsxAptioFixDrv/OsxAptioFixDrv.inf similarity index 100% rename from OsxAptioFixDrv/OsxAptioFixDrv.inf rename to MemoryFix/OsxAptioFixDrv/OsxAptioFixDrv.inf diff --git a/OsxAptioFixDrv/RTShims.h b/MemoryFix/OsxAptioFixDrv/RTShims.h similarity index 100% rename from OsxAptioFixDrv/RTShims.h rename to MemoryFix/OsxAptioFixDrv/RTShims.h diff --git a/OsxAptioFixDrv/VMem.c b/MemoryFix/OsxAptioFixDrv/VMem.c similarity index 100% rename from OsxAptioFixDrv/VMem.c rename to MemoryFix/OsxAptioFixDrv/VMem.c diff --git a/OsxAptioFixDrv/VMem.h b/MemoryFix/OsxAptioFixDrv/VMem.h similarity index 100% rename from OsxAptioFixDrv/VMem.h rename to MemoryFix/OsxAptioFixDrv/VMem.h diff --git a/OsxAptioFixDrv/X64/AsmFuncsX64.S b/MemoryFix/OsxAptioFixDrv/X64/AsmFuncsX64.S similarity index 100% rename from OsxAptioFixDrv/X64/AsmFuncsX64.S rename to MemoryFix/OsxAptioFixDrv/X64/AsmFuncsX64.S diff --git a/OsxAptioFixDrv/X64/AsmFuncsX64.asm b/MemoryFix/OsxAptioFixDrv/X64/AsmFuncsX64.asm similarity index 100% rename from OsxAptioFixDrv/X64/AsmFuncsX64.asm rename to MemoryFix/OsxAptioFixDrv/X64/AsmFuncsX64.asm diff --git a/OsxAptioFixDrv/X64/AsmFuncsX64.nasm b/MemoryFix/OsxAptioFixDrv/X64/AsmFuncsX64.nasm similarity index 100% rename from OsxAptioFixDrv/X64/AsmFuncsX64.nasm rename to MemoryFix/OsxAptioFixDrv/X64/AsmFuncsX64.nasm diff --git a/OsxAptioFixDrv/X64/RTShims.nasm b/MemoryFix/OsxAptioFixDrv/X64/RTShims.nasm similarity index 100% rename from OsxAptioFixDrv/X64/RTShims.nasm rename to MemoryFix/OsxAptioFixDrv/X64/RTShims.nasm diff --git a/OsxAptioFixDrv/X64/TestAsm32.asm b/MemoryFix/OsxAptioFixDrv/X64/TestAsm32.asm similarity index 100% rename from OsxAptioFixDrv/X64/TestAsm32.asm rename to MemoryFix/OsxAptioFixDrv/X64/TestAsm32.asm diff --git a/OsxLowMemFixDrv/Lib.c b/MemoryFix/OsxLowMemFixDrv/Lib.c similarity index 100% rename from OsxLowMemFixDrv/Lib.c rename to MemoryFix/OsxLowMemFixDrv/Lib.c diff --git a/OsxLowMemFixDrv/Lib.h b/MemoryFix/OsxLowMemFixDrv/Lib.h similarity index 100% rename from OsxLowMemFixDrv/Lib.h rename to MemoryFix/OsxLowMemFixDrv/Lib.h diff --git a/OsxLowMemFixDrv/NVRAMDebug.c b/MemoryFix/OsxLowMemFixDrv/NVRAMDebug.c similarity index 100% rename from OsxLowMemFixDrv/NVRAMDebug.c rename to MemoryFix/OsxLowMemFixDrv/NVRAMDebug.c diff --git a/OsxLowMemFixDrv/NVRAMDebug.h b/MemoryFix/OsxLowMemFixDrv/NVRAMDebug.h similarity index 100% rename from OsxLowMemFixDrv/NVRAMDebug.h rename to MemoryFix/OsxLowMemFixDrv/NVRAMDebug.h diff --git a/OsxLowMemFixDrv/OsxLowMemFixDrv.c b/MemoryFix/OsxLowMemFixDrv/OsxLowMemFixDrv.c similarity index 100% rename from OsxLowMemFixDrv/OsxLowMemFixDrv.c rename to MemoryFix/OsxLowMemFixDrv/OsxLowMemFixDrv.c diff --git a/OsxLowMemFixDrv/OsxLowMemFixDrv.inf b/MemoryFix/OsxLowMemFixDrv/OsxLowMemFixDrv.inf similarity index 100% rename from OsxLowMemFixDrv/OsxLowMemFixDrv.inf rename to MemoryFix/OsxLowMemFixDrv/OsxLowMemFixDrv.inf diff --git a/Protocols/AptioInputFix/AptioInput.c b/Protocols/AptioInputFix/AptioInput.c new file mode 100644 index 000000000..af8ad4aec --- /dev/null +++ b/Protocols/AptioInputFix/AptioInput.c @@ -0,0 +1,67 @@ +/** @file + Ami input translators. + +Copyright (c) 2016, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Keycode/AIK.h" +#include "Pointer/AIM.h" +#include "Timer/AIT.h" + +#include +#include + +STATIC EFI_EVENT mExitBootServicesEvent; +STATIC BOOLEAN mPerformedExit; + +VOID +EFIAPI +AmiShimTranslatorExitBootServicesHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if (mPerformedExit) { + return; + } + + mPerformedExit = TRUE; + + // + // Not sure if necessary + // + gBS->CloseEvent (mExitBootServicesEvent); + + AITExit (); + AIMExit (); + AIKExit (); +} + +EFI_STATUS +EFIAPI +AptioInputEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + AITInit (); + AIMInit (); + AIKInit (); + + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, AmiShimTranslatorExitBootServicesHandler, NULL, &mExitBootServicesEvent); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_INFO, "Failed to create exit bs event %d", Status)); + } + + return EFI_SUCCESS; +} diff --git a/Protocols/AptioInputFix/AptioInputFix.inf b/Protocols/AptioInputFix/AptioInputFix.inf new file mode 100644 index 000000000..c9f1492e7 --- /dev/null +++ b/Protocols/AptioInputFix/AptioInputFix.inf @@ -0,0 +1,89 @@ +## @file +# Copyright (c) 2016, vit9696. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php + +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = AptioInputFix + FILE_GUID = E0E2C05D-C3BC-49F7-95F0-8A90A5646944 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = AptioInputEntryPoint + +[Sources] + AptioInput.c + Keycode/AIK.h + Keycode/AIK.c + Keycode/AIKData.c + Keycode/AIKData.h + Keycode/AIKMap.c + Keycode/AIKShim.c + Keycode/AIKShim.h + Keycode/AIKSource.c + Keycode/AIKSource.h + Keycode/AIKTarget.c + Keycode/AIKTarget.h + Keycode/AIKTranslate.c + Keycode/AIKTranslate.h + Pointer/AIM.h + Pointer/AIM.c + Timer/AIT.h + Timer/AIT.c + +# Fixme + +[Packages] + AptioFixPkg/AptioFixPkg.dec + EfiPkg/EfiPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + HiiLib + MemoryAllocationLib + PcdLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + UefiRuntimeServicesTableLib + UefiUsbLib + +[Guids] + # + # Event registered to EFI_HII_SET_KEYBOARD_LAYOUT_EVENT_GUID group, + # which will be triggered by EFI_HII_DATABASE_PROTOCOL.SetKeyboardLayout(). + # + gEfiHiiKeyBoardLayoutGuid ## SOMETIMES_CONSUMES ## Event + gUsbKeyboardLayoutPackageGuid ## SOMETIMES_CONSUMES ## HII + gUsbKeyboardLayoutKeyGuid ## SOMETIMES_PRODUCES ## UNDEFINED + gAppleKeyboardPlatformInfoGuid ## SOMETIMES_CONSUMES + +[Protocols] + gEfiUsbIoProtocolGuid ## TO_START + gEfiDevicePathProtocolGuid ## TO_START + gEfiSimpleTextInProtocolGuid ## BY_START + gEfiSimpleTextInputExProtocolGuid ## BY_START + gEfiSimplePointerProtocolGuid ## BY_START + gEfiTimerArchProtocolGuid ## BY_START + # + # If HII Database Protocol exists, then keyboard layout from HII database is used. + # Otherwise, USB keyboard module tries to use its carried default layout. + # + gEfiHiiDatabaseProtocolGuid ## SOMETIMES_CONSUMES + gAppleKeyMapDatabaseProtocolGuid ## SOMETIMES_CONSUMES + gApplePlatformInfoDatabaseProtocolGuid ## SOMETMES_CONSUMES + gEfiKeyboardInfoProtocolGuid ## SOMETIMES_PRODUCES + gAmiEfiPointerProtocolGuid ## SOMETMES_CONSUMES + gAmiEfiKeycodeProtocolGuid ## SOMETMES_CONSUMES diff --git a/Protocols/AptioInputFix/Keycode/AIK.c b/Protocols/AptioInputFix/Keycode/AIK.c new file mode 100644 index 000000000..c4e3219d5 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIK.c @@ -0,0 +1,250 @@ +/** @file + AmiEfiKeycode to KeyMapDb translator. + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIK.h" + +#include +#include + +AIK_SELF gAikSelf; + +STATIC +VOID +EFIAPI +AIKProtocolArriveHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + AIK_SELF *Keycode; + + Keycode = (AIK_SELF *) Context; + + if (Keycode == NULL || Keycode->OurJobIsDone) { + DEBUG ((DEBUG_INFO, "AIKProtocolArriveHandler got null handler or called when done\n")); + return; + } + + Status = AIKInstall (Keycode); + if (!EFI_ERROR (Status)) { + // + // We are successful, so can remove the protocol polling event if any + // + AIKProtocolArriveUninstall (Keycode); + } else { + DEBUG ((DEBUG_INFO, "AIKProtocolArriveHandler AIKInstall failed - %r\n", Status)); + } +} + +EFI_STATUS +AIKProtocolArriveInstall ( + AIK_SELF *Keycode + ) +{ + EFI_STATUS Status; + VOID *Registration; + + Status = EFI_SUCCESS; + + if (Keycode->KeyMapDbArriveEvent == NULL) { + Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, AIKProtocolArriveHandler, Keycode, &Keycode->KeyMapDbArriveEvent); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "KeyMapDbArriveEvent creation failed - %r\n", Status)); + } else { + Status = gBS->RegisterProtocolNotify (&gAppleKeyMapDatabaseProtocolGuid, Keycode->KeyMapDbArriveEvent, &Registration); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "KeyMapDbArriveEvent registration failed - %r\n", Status)); + gBS->CloseEvent (Keycode->KeyMapDbArriveEvent); + Keycode->KeyMapDbArriveEvent = NULL; + } + } + } + + return Status; +} + +VOID +AIKProtocolArriveUninstall ( + AIK_SELF *Keycode + ) +{ + if (Keycode->KeyMapDbArriveEvent != NULL) { + gBS->CloseEvent (Keycode->KeyMapDbArriveEvent); + Keycode->KeyMapDbArriveEvent = NULL; + } +} + +VOID +EFIAPI +AIKPollKeyboardHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + AIK_SELF *Keycode; + UINT64 Counter; + UINTN Index; + AMI_EFI_KEY_DATA KeyData; + EFI_STATUS Status; + + Keycode = (AIK_SELF *) Context; + if (Keycode == NULL || Keycode->OurJobIsDone) { + DEBUG ((DEBUG_INFO, "AIKPollKeyboardHandler got null handler or called when done\n")); + return; + } + + Keycode->InPollKeyboardEvent = TRUE; + + // + // Counter is here for debugging purposes only. + // + Counter = AIKTargetRefresh (&Keycode->Target); + + // + // Some implementations return "partial key", which is a modifier without + // a key. When a modifier is held, we will get partial keys infinitely, so make sure + // we break after some time. + // + Index = 0; + + do { + Status = AIKSourceGrabEfiKey ( + &Keycode->Source, + &KeyData + ); + if (!EFI_ERROR (Status)) { + (VOID) Counter; + DEBUG ((DEBUG_VERBOSE, "Read key with scan 0x%X and unicode 0x%X at %Lu\n", + KeyData.Key.ScanCode, KeyData.Key.UnicodeChar, Counter + )); + AIKDataWriteEntry (&Keycode->Data, &KeyData); + AIKTargetWriteEntry (&Keycode->Target, &KeyData); + } + + Index++; + } while (!EFI_ERROR (Status) && Index < AIK_KEY_POLL_LIMIT); + + AIKTargetSubmit (&Keycode->Target); + + Keycode->InPollKeyboardEvent = FALSE; +} + +EFI_STATUS +AIKInstall ( + AIK_SELF *Keycode + ) +{ + EFI_STATUS Status; + + Status = AIKTargetInstall (&Keycode->Target); + + if (EFI_ERROR (Status)) { + // + // Working AppleKeyMapAggregator is not here yet. + // + return Status; + } + + Status = AIKSourceInstall (&Keycode->Source); + + if (EFI_ERROR (Status)) { + // + // Cannot work with these sources. + // + AIKTargetUninstall (&Keycode->Target); + return Status; + } + + AIKDataReset (&Keycode->Data); + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, + AIKPollKeyboardHandler, + Keycode, &Keycode->PollKeyboardEvent + ); + + if (!EFI_ERROR (Status)) { + Status = gBS->SetTimer (Keycode->PollKeyboardEvent, TimerPeriodic, AIK_KEY_POLL_INTERVAL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AIKPollKeyboardHandler timer setting failed - %r\n", Status)); + gBS->CloseEvent (Keycode->PollKeyboardEvent); + Keycode->PollKeyboardEvent = NULL; + } + } else { + DEBUG ((DEBUG_INFO, "AIKPollKeyboardHandler event creation failed - %r\n", Status)); + } + + if (EFI_ERROR (Status)) { + AIKSourceUninstall (&Keycode->Source); + AIKTargetUninstall (&Keycode->Target); + } + + return Status; +} + +VOID +AIKUninstall ( + AIK_SELF *Keycode + ) +{ + Keycode->OurJobIsDone = TRUE; + + AIKProtocolArriveUninstall (Keycode); + + if (Keycode->PollKeyboardEvent) { + gBS->SetTimer (Keycode->PollKeyboardEvent, TimerCancel, 0); + gBS->CloseEvent (Keycode->PollKeyboardEvent); + Keycode->PollKeyboardEvent = NULL; + } + + AIKSourceUninstall (&Keycode->Source); + AIKTargetUninstall (&Keycode->Target); +} + +EFI_STATUS +AIKInit ( + VOID + ) +{ + EFI_STATUS Status; + + AIKTranslateConfigure (); + + Status = AIKInstall (&gAikSelf); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AIKInstall failed - %r\n", Status)); + + // + // No AppleKeyMapAggregator present, install on its availability. + // + Status = AIKProtocolArriveInstall (&gAikSelf); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AIK is NOT waiting for protocols - %r\n", Status)); + } + } + + return Status; +} + +EFI_STATUS +AIKExit ( + VOID + ) +{ + AIKUninstall (&gAikSelf); + return EFI_SUCCESS; +} diff --git a/Protocols/AptioInputFix/Keycode/AIK.h b/Protocols/AptioInputFix/Keycode/AIK.h new file mode 100644 index 000000000..e8e9b476c --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIK.h @@ -0,0 +1,107 @@ +/** @file + Header file for AmiEfiKeycode to KeyMapDb translator. + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef AIK_SELF_H +#define AIK_SELF_H + +#include "AIKData.h" +#include "AIKSource.h" +#include "AIKTarget.h" +#include "AIKTranslate.h" + +#include + +// +// Maximum amount of keys polled at once +// +#define AIK_KEY_POLL_LIMIT (AIK_TARGET_BUFFER_SIZE) + +// +// Defines key polling frequency +// +#define AIK_KEY_POLL_INTERVAL EFI_TIMER_PERIOD_MILLISECONDS(10) + +typedef struct { + // + // Input sources + // + AIK_SOURCE Source; + + // + // Output targets + // + AIK_TARGET Target; + + // + // Key data + // + AIK_DATA Data; + + // + // AppleKeyMapAggregator polling event + // + EFI_EVENT KeyMapDbArriveEvent; + + // + // Keyboard input polling event + // + EFI_EVENT PollKeyboardEvent; + + // + // Indicates keyboard input polling event to avoid reentrancy if any + // + BOOLEAN InPollKeyboardEvent; + + // + // Indicates we are done for any event in case it gets fired. + // Not really needed, put in case of bogus firmwares. + // + BOOLEAN OurJobIsDone; +} AIK_SELF; + +// +// This is only used in Shims, where no other way exists. +// +extern AIK_SELF gAikSelf; + +EFI_STATUS +AIKProtocolArriveInstall ( + AIK_SELF *Keycode + ); + +VOID +AIKProtocolArriveUninstall ( + AIK_SELF *Keycode + ); + +EFI_STATUS +AIKInstall ( + AIK_SELF *Keycode + ); + +VOID +AIKUninstall ( + AIK_SELF *Keycode + ); + +EFI_STATUS +AIKInit ( + VOID + ); + +EFI_STATUS +AIKExit ( + VOID + ); + +#endif diff --git a/Protocols/AptioInputFix/Keycode/AIKData.c b/Protocols/AptioInputFix/Keycode/AIKData.c new file mode 100644 index 000000000..862db9443 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKData.c @@ -0,0 +1,81 @@ +/** @file + Key code ring + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIKData.h" + +#include +#include + +VOID +AIKDataReset ( + IN OUT AIK_DATA *Data + ) +{ + Data->KeyBufferHead = Data->KeyBufferTail = Data->KeyBuffer; + Data->KeyBufferSize = 0; +} + +BOOLEAN +AIKDataEmpty ( + IN AIK_DATA *Data + ) +{ + return Data->KeyBufferSize == 0; +} + +EFI_STATUS +AIKDataReadEntry ( + IN OUT AIK_DATA *Data, + OUT AMI_EFI_KEY_DATA *KeyData + ) +{ + if (Data->KeyBufferSize == 0) { + return EFI_NOT_READY; + } + + CopyMem (KeyData, Data->KeyBufferTail, sizeof (*KeyData)); + + Data->KeyBufferSize--; + Data->KeyBufferTail++; + if (Data->KeyBufferTail == &Data->KeyBuffer[AIK_DATA_BUFFER_SIZE]) { + Data->KeyBufferTail = Data->KeyBuffer; + } + + return EFI_SUCCESS; +} + +VOID +AIKDataWriteEntry ( + IN OUT AIK_DATA *Data, + IN AMI_EFI_KEY_DATA *KeyData + ) +{ + // + // Eat the first entry if we have no room + // + if (Data->KeyBufferSize == AIK_DATA_BUFFER_SIZE) { + Data->KeyBufferSize--; + Data->KeyBufferTail++; + if (Data->KeyBufferTail == &Data->KeyBuffer[AIK_DATA_BUFFER_SIZE]) { + Data->KeyBufferTail = Data->KeyBuffer; + } + } + + Data->KeyBufferSize++; + CopyMem (Data->KeyBufferHead, KeyData, sizeof (*KeyData)); + Data->KeyBufferHead++; + if (Data->KeyBufferHead == &Data->KeyBuffer[AIK_DATA_BUFFER_SIZE]) { + Data->KeyBufferHead = Data->KeyBuffer; + } +} diff --git a/Protocols/AptioInputFix/Keycode/AIKData.h b/Protocols/AptioInputFix/Keycode/AIKData.h new file mode 100644 index 000000000..41bf685fb --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKData.h @@ -0,0 +1,57 @@ +/** @file + Key code ring + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef AIK_DATA_H +#define AIK_DATA_H + +#include + +// +// Maximum amount of keys queued for non-Apple protocols +// +#define AIK_DATA_BUFFER_SIZE 12 + +typedef struct { + // + // Stored key buffer for responding to non-Apple protocols + // + AMI_EFI_KEY_DATA KeyBuffer[AIK_DATA_BUFFER_SIZE]; + AMI_EFI_KEY_DATA *KeyBufferHead; + AMI_EFI_KEY_DATA *KeyBufferTail; + UINTN KeyBufferSize; +} AIK_DATA; + +VOID +AIKDataReset ( + IN OUT AIK_DATA *Data + ); + +BOOLEAN +AIKDataEmpty ( + IN AIK_DATA *Data + ); + +EFI_STATUS +AIKDataReadEntry ( + IN OUT AIK_DATA *Data, + OUT AMI_EFI_KEY_DATA *KeyData + ); + +VOID +AIKDataWriteEntry ( + IN OUT AIK_DATA *Data, + IN AMI_EFI_KEY_DATA *KeyData + ); + +#endif diff --git a/Protocols/AptioInputFix/Keycode/AIKMap.c b/Protocols/AptioInputFix/Keycode/AIKMap.c new file mode 100644 index 000000000..ae0a56902 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKMap.c @@ -0,0 +1,2390 @@ +/** @file + Key mapping tables. + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIKTranslate.h" + +// Conversion table +AIK_PS2KEY_TO_USB +gAikPs2KeyToUsbMap[AIK_MAX_PS2KEY_NUM] = { + { + UsbHidUndefined, + NULL, + NULL + }, // 0x00 + { + UsbHidUsageIdKbKpKeyEsc, + AIK_DEBUG_STR("Esc"), + AIK_DEBUG_STR("^ Esc ^") + }, // 0x01 + { + UsbHidUsageIdKbKpKeyOne, + AIK_DEBUG_STR("1"), + AIK_DEBUG_STR("!") + }, // 0x02 + { + UsbHidUsageIdKbKpKeyTwo, + AIK_DEBUG_STR("2"), + AIK_DEBUG_STR("@") + }, // 0x03 + { + UsbHidUsageIdKbKpKeyThree, + AIK_DEBUG_STR("3"), + AIK_DEBUG_STR("#") + }, // 0x04 + { + UsbHidUsageIdKbKpKeyFour, + AIK_DEBUG_STR("4"), + AIK_DEBUG_STR("$") + }, // 0x05 + { + UsbHidUsageIdKbKpKeyFive, + AIK_DEBUG_STR("5"), + AIK_DEBUG_STR("%") + }, // 0x06 + { + UsbHidUsageIdKbKpKeySix, + AIK_DEBUG_STR("6"), + AIK_DEBUG_STR("^") + }, // 0x07 + { + UsbHidUsageIdKbKpKeySeven, + AIK_DEBUG_STR("7"), + AIK_DEBUG_STR("&") + }, // 0x08 + { + UsbHidUsageIdKbKpKeyEight, + AIK_DEBUG_STR("8"), + AIK_DEBUG_STR("*") + }, // 0x09 + { + UsbHidUsageIdKbKpKeyNine, + AIK_DEBUG_STR("9"), + AIK_DEBUG_STR("(") + }, // 0x0A + { + UsbHidUsageIdKbKpKeyZero, + AIK_DEBUG_STR("0"), + AIK_DEBUG_STR(")") + }, // 0x0B + { + UsbHidUsageIdKbKpKeyMinus, + AIK_DEBUG_STR("-"), + AIK_DEBUG_STR("_") + }, // 0x0C + { + UsbHidUsageIdKbKpKeyEquals, + AIK_DEBUG_STR("="), + AIK_DEBUG_STR("+") + }, // 0x0D + { + UsbHidUsageIdKbKpKeyBackSpace, + AIK_DEBUG_STR("Backspace"), + AIK_DEBUG_STR("^ Backspace ^") + }, // 0x0E + { + UsbHidUsageIdKbKpKeyTab, + AIK_DEBUG_STR("Tab"), + AIK_DEBUG_STR("^ Tab ^") + }, // 0x0F + { + UsbHidUsageIdKbKpKeyQ, + AIK_DEBUG_STR("q"), + AIK_DEBUG_STR("Q") + }, // 0x10 + { + UsbHidUsageIdKbKpKeyW, + AIK_DEBUG_STR("w"), + AIK_DEBUG_STR("W") + }, // 0x11 + { + UsbHidUsageIdKbKpKeyE, + AIK_DEBUG_STR("e"), + AIK_DEBUG_STR("E") + }, // 0x12 + { + UsbHidUsageIdKbKpKeyR, + AIK_DEBUG_STR("r"), + AIK_DEBUG_STR("R") + }, // 0x13 + { + UsbHidUsageIdKbKpKeyT, + AIK_DEBUG_STR("t"), + AIK_DEBUG_STR("T") + }, // 0x14 + { + UsbHidUsageIdKbKpKeyY, + AIK_DEBUG_STR("y"), + AIK_DEBUG_STR("Y") + }, // 0x15 + { + UsbHidUsageIdKbKpKeyU, + AIK_DEBUG_STR("u"), + AIK_DEBUG_STR("U") + }, // 0x16 + { + UsbHidUsageIdKbKpKeyI, + AIK_DEBUG_STR("i"), + AIK_DEBUG_STR("I") + }, // 0x17 + { + UsbHidUsageIdKbKpKeyO, + AIK_DEBUG_STR("o"), + AIK_DEBUG_STR("O") + }, // 0x18 + { + UsbHidUsageIdKbKpKeyP, + AIK_DEBUG_STR("p"), + AIK_DEBUG_STR("P") + }, // 0x19 + { + UsbHidUsageIdKbKpKeyLeftBracket, + AIK_DEBUG_STR("["), + AIK_DEBUG_STR("{") + }, // 0x1A + { + UsbHidUsageIdKbKpKeyRightBracket, + AIK_DEBUG_STR("]"), + AIK_DEBUG_STR("}") + }, // 0x1B + { + UsbHidUsageIdKbKpKeyEnter, + AIK_DEBUG_STR("Enter"), + AIK_DEBUG_STR("^ Enter ^") + }, // 0x1C + { + UsbHidUndefined, + NULL, + NULL + }, // 0x1D + { + UsbHidUsageIdKbKpKeyA, + AIK_DEBUG_STR("a"), + AIK_DEBUG_STR("A") + }, // 0x1E + { + UsbHidUsageIdKbKpKeyS, + AIK_DEBUG_STR("s"), + AIK_DEBUG_STR("S") + }, // 0x1F + { + UsbHidUsageIdKbKpKeyD, + AIK_DEBUG_STR("d"), + AIK_DEBUG_STR("D") + }, // 0x20 + { + UsbHidUsageIdKbKpKeyF, + AIK_DEBUG_STR("f"), + AIK_DEBUG_STR("F") + }, // 0x21 + { + UsbHidUsageIdKbKpKeyG, + AIK_DEBUG_STR("g"), + AIK_DEBUG_STR("G") + }, // 0x22 + { + UsbHidUsageIdKbKpKeyH, + AIK_DEBUG_STR("h"), + AIK_DEBUG_STR("H") + }, // 0x23 + { + UsbHidUsageIdKbKpKeyJ, + AIK_DEBUG_STR("j"), + AIK_DEBUG_STR("J") + }, // 0x24 + { + UsbHidUsageIdKbKpKeyK, + AIK_DEBUG_STR("k"), + AIK_DEBUG_STR("K") + }, // 0x25 + { + UsbHidUsageIdKbKpKeyL, + AIK_DEBUG_STR("l"), + AIK_DEBUG_STR("L") + }, // 0x26 + { + UsbHidUsageIdKbKpKeySemicolon, + AIK_DEBUG_STR(";"), + AIK_DEBUG_STR(":") + }, // 0x27 + { + UsbHidUsageIdKbKpKeyQuotation, + AIK_DEBUG_STR("'"), + AIK_DEBUG_STR("\""), + }, // 0x28 + { + UsbHidUsageIdKbKpKeyAcute, + AIK_DEBUG_STR("`"), + AIK_DEBUG_STR("~") + }, // 0x29 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x2A + { + UsbHidUsageIdKbKpKeyBackslash, + AIK_DEBUG_STR("\\"), + AIK_DEBUG_STR("|") + }, // 0x2B + { + UsbHidUsageIdKbKpKeyZ, + AIK_DEBUG_STR("z"), + AIK_DEBUG_STR("Z") + }, // 0x2C + { + UsbHidUsageIdKbKpKeyX, + AIK_DEBUG_STR("x"), + AIK_DEBUG_STR("X") + }, // 0x2D + { + UsbHidUsageIdKbKpKeyC, + AIK_DEBUG_STR("c"), + AIK_DEBUG_STR("C") + }, // 0x2E + { + UsbHidUsageIdKbKpKeyV, + AIK_DEBUG_STR("v"), + AIK_DEBUG_STR("V") + }, // 0x2F + { + UsbHidUsageIdKbKpKeyB, + AIK_DEBUG_STR("b"), + AIK_DEBUG_STR("B") + }, // 0x30 + { + UsbHidUsageIdKbKpKeyN, + AIK_DEBUG_STR("n"), + AIK_DEBUG_STR("N") + }, // 0x31 + { + UsbHidUsageIdKbKpKeyM, + AIK_DEBUG_STR("m"), + AIK_DEBUG_STR("M") + }, // 0x32 + { + UsbHidUsageIdKbKpKeyComma, + AIK_DEBUG_STR(","), + AIK_DEBUG_STR("<") + }, // 0x33 + { + UsbHidUsageIdKbKpKeyPeriod, + AIK_DEBUG_STR("."), + AIK_DEBUG_STR(">") + }, // 0x34 + { + UsbHidUsageIdKbKpKeySlash, + AIK_DEBUG_STR("/"), + AIK_DEBUG_STR("?") + }, // 0x35 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x36 + { + UsbHidUsageIdKbKpPadKeyAsterisk, + AIK_DEBUG_STR("*"), + AIK_DEBUG_STR("^ * ^") + }, // 0x37 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x38 + { + UsbHidUsageIdKbKpKeySpaceBar, + AIK_DEBUG_STR("Spacebar"), + AIK_DEBUG_STR("^ Spacebar ^") + }, // 0x39 + { + UsbHidUsageIdKbKpKeyCLock, + AIK_DEBUG_STR("CapsLock"), + AIK_DEBUG_STR("^ CapsLock ^") + }, // 0x3A + { + UsbHidUsageIdKbKpKeyF1, + AIK_DEBUG_STR("F1"), + AIK_DEBUG_STR("^ F1 ^") + }, // 0x3B + { + UsbHidUsageIdKbKpKeyF2, + AIK_DEBUG_STR("F2"), + AIK_DEBUG_STR("^ F2 ^") + }, // 0x3C + { + UsbHidUsageIdKbKpKeyF3, + AIK_DEBUG_STR("F3"), + AIK_DEBUG_STR("^ F3 ^") + }, // 0x3D + { + UsbHidUsageIdKbKpKeyF4, + AIK_DEBUG_STR("F4"), + AIK_DEBUG_STR("^ F4 ^") + }, // 0x3E + { + UsbHidUsageIdKbKpKeyF5, + AIK_DEBUG_STR("F5"), + AIK_DEBUG_STR("^ F5 ^") + }, // 0x3F + { + UsbHidUsageIdKbKpKeyF6, + AIK_DEBUG_STR("F6"), + AIK_DEBUG_STR("^ F6 ^") + }, // 0x40 + { + UsbHidUsageIdKbKpKeyF7, + AIK_DEBUG_STR("F7"), + AIK_DEBUG_STR("^ F7 ^") + }, // 0x41 + { + UsbHidUsageIdKbKpKeyF8, + AIK_DEBUG_STR("F8"), + AIK_DEBUG_STR("^ F8 ^") + }, // 0x42 + { + UsbHidUsageIdKbKpKeyF9, + AIK_DEBUG_STR("F9"), + AIK_DEBUG_STR("^ F9 ^") + }, // 0x43 + { + UsbHidUsageIdKbKpKeyF10, + AIK_DEBUG_STR("F10"), + AIK_DEBUG_STR("^ F10 ^") + }, // 0x44 + { + UsbHidUsageIdKbKpPadKeyNLck, + AIK_DEBUG_STR("NumLock"), + AIK_DEBUG_STR("^ NumLock ^") + }, // 0x45 + { + UsbHidUsageIdKbKpKeySLock, + AIK_DEBUG_STR("Scroll Lock"), + AIK_DEBUG_STR("^ Scroll Lock ^") + }, // 0x46 + { + UsbHidUsageIdKbKpKeyHome, + AIK_DEBUG_STR("Home"), + AIK_DEBUG_STR("^ Home ^") + }, // 0x47 + { + UsbHidUsageIdKbKpKeyUpArrow, + AIK_DEBUG_STR("Up"), + AIK_DEBUG_STR("^ Up ^") + }, // 0x48 + { + UsbHidUsageIdKbKpKeyPgUp, + AIK_DEBUG_STR("PageUp"), + AIK_DEBUG_STR("^ PageUp ^") + }, // 0x49 + { + UsbHidUsageIdKbKpPadKeyMinus, + AIK_DEBUG_STR("-"), + AIK_DEBUG_STR("^ - ^") + }, // 0x4A + { + UsbHidUsageIdKbKpKeyLeftArrow, + AIK_DEBUG_STR("Left"), + AIK_DEBUG_STR("^ Left ^") + }, // 0x4B + { + UsbHidUsageIdKbKpPadKeyFive, + AIK_DEBUG_STR("5"), + AIK_DEBUG_STR("^ 5 ^") + }, // 0x4C + { + UsbHidUsageIdKbKpKeyRightArrow, + AIK_DEBUG_STR("Right"), + AIK_DEBUG_STR("^ Right ^") + }, // 0x4D + { + UsbHidUsageIdKbKpPadKeyPlus, + AIK_DEBUG_STR("+"), + AIK_DEBUG_STR("^ + ^") + }, // 0x4E + { + UsbHidUsageIdKbKpKeyEnd, + AIK_DEBUG_STR("End"), + AIK_DEBUG_STR("^ End ^") + }, // 0x4F + { + UsbHidUsageIdKbKpKeyDownArrow, + AIK_DEBUG_STR("Down"), + AIK_DEBUG_STR("^ Down ^") + }, // 0x50 + { + UsbHidUsageIdKbKpKeyPgDn, + AIK_DEBUG_STR("PageDown"), + AIK_DEBUG_STR("^ PageDown ^") + }, // 0x51 + { + UsbHidUsageIdKbKpKeyIns, + AIK_DEBUG_STR("Insert"), + AIK_DEBUG_STR("^ Insert ^") + }, // 0x52 + { + UsbHidUsageIdKbKpKeyDel, + AIK_DEBUG_STR("Delete"), + AIK_DEBUG_STR("^ Delete ^") + }, // 0x53 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x54 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x55 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x56 + { + UsbHidUsageIdKbKpKeyF11, + AIK_DEBUG_STR("F11"), + AIK_DEBUG_STR("^ F11 ^") + }, // 0x57 + { + UsbHidUsageIdKbKpKeyF12, + AIK_DEBUG_STR("F12"), + AIK_DEBUG_STR("^ F12 ^") + }, // 0x58 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x59 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x5A + { + UsbHidUndefined, + NULL, + NULL + }, // 0x5B + { + UsbHidUndefined, + NULL, + NULL + }, // 0x5C + { + UsbHidUndefined, + NULL, + NULL + }, // 0x5D + { + UsbHidUndefined, + NULL, + NULL + }, // 0x5E + { + UsbHidUndefined, + NULL, + NULL + }, // 0x5F + { + UsbHidUndefined, + NULL, + NULL + }, // 0x60 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x61 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x62 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x63 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x64 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x65 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x66 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x67 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x68 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x69 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x6A + { + UsbHidUndefined, + NULL, + NULL + }, // 0x6B + { + UsbHidUndefined, + NULL, + NULL + }, // 0x6C + { + UsbHidUndefined, + NULL, + NULL + }, // 0x6D + { + UsbHidUndefined, + NULL, + NULL + }, // 0x6E + { + UsbHidUndefined, + NULL, + NULL + }, // 0x6F + { + UsbHidUndefined, + NULL, + NULL + }, // 0x70 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x71 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x72 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x73 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x74 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x75 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x76 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x77 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x78 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x79 + { + UsbHidUndefined, + NULL, + NULL + }, // 0x7A + { + UsbHidUndefined, + NULL, + NULL + }, // 0x7B + { + UsbHidUndefined, + NULL, + NULL + }, // 0x7C + { + UsbHidUndefined, + NULL, + NULL + }, // 0x7D + { + UsbHidUndefined, + NULL, + NULL + }, // 0x7E + { + UsbHidUndefined, + NULL, + NULL + } // 0x7F +}; + +AIK_ASCII_TO_USB +gAikAsciiToUsbMap[AIK_MAX_ASCII_NUM] = { + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("NUL") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("SOH") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("STX") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("ETX") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EOT") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("ENQ") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("ACK") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("BEL") + }, + { + UsbHidUsageIdKbKpKeyBackSpace, + 0, + AIK_DEBUG_STR("BS") + }, + { + UsbHidUsageIdKbKpKeyTab, + 0, + AIK_DEBUG_STR("HT") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("LF") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("VT") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("FF") + }, + { + UsbHidUsageIdKbKpKeyEnter, + 0, + AIK_DEBUG_STR("CR") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("SO") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("SI") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("DLE") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("DC1") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("DC2") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("DC3") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("DC4") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("NAK") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("SYN") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("ETB") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("CAN") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EM") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("SUB") + }, + { + UsbHidUsageIdKbKpKeyEsc, + 0, + AIK_DEBUG_STR("ESC") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("FS") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("GS") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("RS") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("US") + }, + { + UsbHidUsageIdKbKpKeySpaceBar, + 0, + AIK_DEBUG_STR("SP") + }, + { + UsbHidUsageIdKbKpKeyOne, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("!") + }, + { + UsbHidUsageIdKbKpKeyQuotation, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("\"") + }, + { + UsbHidUsageIdKbKpKeyThree, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("#") + }, + { + UsbHidUsageIdKbKpKeyFour, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("$") + }, + { + UsbHidUsageIdKbKpKeyFive, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("%") + }, + { + UsbHidUsageIdKbKpKeySeven, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("&") + }, + { + UsbHidUsageIdKbKpKeyQuotation, + 0, + AIK_DEBUG_STR("'") + }, + { + UsbHidUsageIdKbKpKeyNine, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("(") + }, + { + UsbHidUsageIdKbKpKeyZero, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR(")") + }, + { + UsbHidUsageIdKbKpKeyEight, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("*") + }, + { + UsbHidUsageIdKbKpKeyEquals, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("+") + }, + { + UsbHidUsageIdKbKpKeyComma, + 0, + AIK_DEBUG_STR(",") + }, + { + UsbHidUsageIdKbKpKeyMinus, + 0, + AIK_DEBUG_STR("-") + }, + { + UsbHidUsageIdKbKpKeyPeriod, + 0, + AIK_DEBUG_STR(".") + }, + { + UsbHidUsageIdKbKpKeySlash, + 0, + AIK_DEBUG_STR("/") + }, + { + UsbHidUsageIdKbKpKeyZero, + 0, + AIK_DEBUG_STR("0") + }, + { + UsbHidUsageIdKbKpKeyOne, + 0, + AIK_DEBUG_STR("1") + }, + { + UsbHidUsageIdKbKpKeyTwo, + 0, + AIK_DEBUG_STR("2") + }, + { + UsbHidUsageIdKbKpKeyThree, + 0, + AIK_DEBUG_STR("3") + }, + { + UsbHidUsageIdKbKpKeyFour, + 0, + AIK_DEBUG_STR("4") + }, + { + UsbHidUsageIdKbKpKeyFive, + 0, + AIK_DEBUG_STR("5") + }, + { + UsbHidUsageIdKbKpKeySix, + 0, + AIK_DEBUG_STR("6") + }, + { + UsbHidUsageIdKbKpKeySeven, + 0, + AIK_DEBUG_STR("7") + }, + { + UsbHidUsageIdKbKpKeyEight, + 0, + AIK_DEBUG_STR("8") + }, + { + UsbHidUsageIdKbKpKeyNine, + 0, + AIK_DEBUG_STR("9") + }, + { + UsbHidUsageIdKbKpKeySemicolon, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR(":") + }, + { + UsbHidUsageIdKbKpKeySemicolon, + 0, + AIK_DEBUG_STR(";") + }, + { + UsbHidUsageIdKbKpKeyComma, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("<") + }, + { + UsbHidUsageIdKbKpKeyEquals, + 0, + AIK_DEBUG_STR("=") + }, + { + UsbHidUsageIdKbKpKeyPeriod, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR(">") + }, + { + UsbHidUsageIdKbKpKeySlash, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("?") + }, + { + UsbHidUsageIdKbKpKeyTwo, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("@") + }, + { + UsbHidUsageIdKbKpKeyA, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("A") + }, + { + UsbHidUsageIdKbKpKeyB, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("B") + }, + { + UsbHidUsageIdKbKpKeyC, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("C") + }, + { + UsbHidUsageIdKbKpKeyD, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("D") + }, + { + UsbHidUsageIdKbKpKeyE, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("E") + }, + { + UsbHidUsageIdKbKpKeyF, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("F") + }, + { + UsbHidUsageIdKbKpKeyG, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("G") + }, + { + UsbHidUsageIdKbKpKeyH, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("H") + }, + { + UsbHidUsageIdKbKpKeyI, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("I") + }, + { + UsbHidUsageIdKbKpKeyJ, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("J") + }, + { + UsbHidUsageIdKbKpKeyK, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("K") + }, + { + UsbHidUsageIdKbKpKeyL, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("L") + }, + { + UsbHidUsageIdKbKpKeyM, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("M") + }, + { + UsbHidUsageIdKbKpKeyN, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("N") + }, + { + UsbHidUsageIdKbKpKeyO, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("O") + }, + { + UsbHidUsageIdKbKpKeyP, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("P") + }, + { + UsbHidUsageIdKbKpKeyQ, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("Q") + }, + { + UsbHidUsageIdKbKpKeyR, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("R") + }, + { + UsbHidUsageIdKbKpKeyS, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("S") + }, + { + UsbHidUsageIdKbKpKeyT, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("T") + }, + { + UsbHidUsageIdKbKpKeyU, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("U") + }, + { + UsbHidUsageIdKbKpKeyV, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("V") + }, + { + UsbHidUsageIdKbKpKeyW, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("W") + }, + { + UsbHidUsageIdKbKpKeyX, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("X") + }, + { + UsbHidUsageIdKbKpKeyY, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("Y") + }, + { + UsbHidUsageIdKbKpKeyZ, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("Z") + }, + { + UsbHidUsageIdKbKpKeyLeftBracket, + 0, + AIK_DEBUG_STR("[") + }, + { + UsbHidUsageIdKbKpKeyBackslash, + 0, + AIK_DEBUG_STR("\\") + }, + { + UsbHidUsageIdKbKpKeyRightBracket, + 0, + AIK_DEBUG_STR("]") + }, + { + UsbHidUsageIdKbKpKeyRightBracket, + 0, + AIK_DEBUG_STR("^") + }, + { + UsbHidUsageIdKbKpKeyMinus, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("_") + }, + { + UsbHidUsageIdKbKpKeyAcute, + 0, + AIK_DEBUG_STR("`") + }, + { + UsbHidUsageIdKbKpKeyA, + 0, + AIK_DEBUG_STR("a") + }, + { + UsbHidUsageIdKbKpKeyB, + 0, + AIK_DEBUG_STR("b") + }, + { + UsbHidUsageIdKbKpKeyC, + 0, + AIK_DEBUG_STR("c") + }, + { + UsbHidUsageIdKbKpKeyD, + 0, + AIK_DEBUG_STR("d") + }, + { + UsbHidUsageIdKbKpKeyE, + 0, + AIK_DEBUG_STR("e") + }, + { + UsbHidUsageIdKbKpKeyF, + 0, + AIK_DEBUG_STR("f") + }, + { + UsbHidUsageIdKbKpKeyG, + 0, + AIK_DEBUG_STR("g") + }, + { + UsbHidUsageIdKbKpKeyH, + 0, + AIK_DEBUG_STR("h") + }, + { + UsbHidUsageIdKbKpKeyI, + 0, + AIK_DEBUG_STR("i") + }, + { + UsbHidUsageIdKbKpKeyJ, + 0, + AIK_DEBUG_STR("j") + }, + { + UsbHidUsageIdKbKpKeyK, + 0, + AIK_DEBUG_STR("k") + }, + { + UsbHidUsageIdKbKpKeyL, + 0, + AIK_DEBUG_STR("l") + }, + { + UsbHidUsageIdKbKpKeyM, + 0, + AIK_DEBUG_STR("m") + }, + { + UsbHidUsageIdKbKpKeyN, + 0, + AIK_DEBUG_STR("n") + }, + { + UsbHidUsageIdKbKpKeyO, + 0, + AIK_DEBUG_STR("o") + }, + { + UsbHidUsageIdKbKpKeyP, + 0, + AIK_DEBUG_STR("p") + }, + { + UsbHidUsageIdKbKpKeyQ, + 0, + AIK_DEBUG_STR("q") + }, + { + UsbHidUsageIdKbKpKeyR, + 0, + AIK_DEBUG_STR("r") + }, + { + UsbHidUsageIdKbKpKeyS, + 0, + AIK_DEBUG_STR("s") + }, + { + UsbHidUsageIdKbKpKeyT, + 0, + AIK_DEBUG_STR("t") + }, + { + UsbHidUsageIdKbKpKeyU, + 0, + AIK_DEBUG_STR("u") + }, + { + UsbHidUsageIdKbKpKeyV, + 0, + AIK_DEBUG_STR("v") + }, + { + UsbHidUsageIdKbKpKeyW, + 0, + AIK_DEBUG_STR("w") + }, + { + UsbHidUsageIdKbKpKeyX, + 0, + AIK_DEBUG_STR("x") + }, + { + UsbHidUsageIdKbKpKeyY, + 0, + AIK_DEBUG_STR("y") + }, + { + UsbHidUsageIdKbKpKeyZ, + 0, + AIK_DEBUG_STR("z") + }, + { + UsbHidUsageIdKbKpKeyRightBracket, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("{") + }, + { + UsbHidUsageIdKbKpKeyBackslash, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("|") + }, + { + UsbHidUsageIdKbKpKeyRightBracket, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("}") + }, + { + UsbHidUsageIdKbKpKeyAcute, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("~") + }, + { + UsbHidUsageIdKbKpKeyDel, + 0, + AIK_DEBUG_STR("DEL") + }, +}; + +AIK_EFIKEY_TO_USB +gAikEfiKeyToUsbMap[AIK_MAX_EFIKEY_NUM] = { + { + UsbHidUndefined, + EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED, + AIK_DEBUG_STR("EfiKeyLCtrl") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyA0") + }, + { + UsbHidUndefined, + EFI_SHIFT_STATE_VALID | EFI_LEFT_ALT_PRESSED, + AIK_DEBUG_STR("EfiKeyLAlt") + }, + { + UsbHidUsageIdKbKpKeySpaceBar, + 0, + AIK_DEBUG_STR("EfiKeySpaceBar") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyA2") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyA3") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyA4") + }, + { + UsbHidUndefined, + EFI_SHIFT_STATE_VALID | EFI_RIGHT_CONTROL_PRESSED, + AIK_DEBUG_STR("EfiKeyRCtrl") + }, + { + UsbHidUsageIdKbKpKeyLeftArrow, + 0, + AIK_DEBUG_STR("EfiKeyLeftArrow") + }, + { + UsbHidUsageIdKbKpKeyDownArrow, + 0, + AIK_DEBUG_STR("EfiKeyDownArrow") + }, + { + UsbHidUsageIdKbKpKeyRightArrow, + 0, + AIK_DEBUG_STR("EfiKeyRightArrow") + }, + { + UsbHidUsageIdKbKpKeyZero, + 0, + AIK_DEBUG_STR("EfiKeyZero") + }, + { + UsbHidUsageIdKbKpKeyPeriod, + 0, + AIK_DEBUG_STR("EfiKeyPeriod") + }, + { + UsbHidUsageIdKbKpKeyEnter, + 0, + AIK_DEBUG_STR("EfiKeyEnter") + }, + { + UsbHidUndefined, + EFI_SHIFT_STATE_VALID | EFI_LEFT_SHIFT_PRESSED, + AIK_DEBUG_STR("EfiKeyLShift") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB0") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB1") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB2") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB3") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB4") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB5") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB6") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB7") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB8") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB9") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyB10") + }, + { + UsbHidUndefined, + EFI_SHIFT_STATE_VALID | EFI_RIGHT_SHIFT_PRESSED, + AIK_DEBUG_STR("EfiKeyRshift") + }, + { + UsbHidUsageIdKbKpKeyUpArrow, + 0, + AIK_DEBUG_STR("EfiKeyUpArrow") + }, + { + UsbHidUsageIdKbKpKeyOne, + 0, + AIK_DEBUG_STR("EfiKeyOne") + }, + { + UsbHidUsageIdKbKpKeyTwo, + 0, + AIK_DEBUG_STR("EfiKeyTwo") + }, + { + UsbHidUsageIdKbKpKeyThree, + 0, + AIK_DEBUG_STR("EfiKeyThree") + }, + { + UsbHidUsageIdKbKpKeyCLock, + 0, + AIK_DEBUG_STR("EfiKeyCapsLock") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC1") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC2") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC3") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC4") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC5") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC6") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC7") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC8") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC9") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC10") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC11") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyC12") + }, + { + UsbHidUsageIdKbKpKeyFour, + 0, + AIK_DEBUG_STR("EfiKeyFour") + }, + { + UsbHidUsageIdKbKpKeyFive, + 0, + AIK_DEBUG_STR("EfiKeyFive") + }, + { + UsbHidUsageIdKbKpKeySix, + 0, + AIK_DEBUG_STR("EfiKeySix") + }, + { + UsbHidUsageIdKbKpPadKeyPlus, + 0, + AIK_DEBUG_STR("EfiKeyPlus") + }, + { + UsbHidUsageIdKbKpKeyTab, + 0, + AIK_DEBUG_STR("EfiKeyTab") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD1") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD2") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD3") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD4") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD5") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD6") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD7") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD8") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD9") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD10") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD11") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD12") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyD13") + }, + { + UsbHidUsageIdKbKpKeyDel, + 0, + AIK_DEBUG_STR("EfiKeyDel") + }, + { + UsbHidUsageIdKbKpKeyEnd, + 0, + AIK_DEBUG_STR("EfiKeyEnd") + }, + { + UsbHidUsageIdKbKpKeyPgDn, + 0, + AIK_DEBUG_STR("EfiKeyPgDn") + }, + { + UsbHidUsageIdKbKpKeySeven, + 0, + AIK_DEBUG_STR("EfiKeySeven") + }, + { + UsbHidUsageIdKbKpKeyEight, + 0, + AIK_DEBUG_STR("EfiKeyEight") + }, + { + UsbHidUsageIdKbKpKeyNine, + 0, + AIK_DEBUG_STR("EfiKeyNine") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE0") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE1") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE2") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE3") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE4") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE5") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE6") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE7") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE8") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE9") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE10") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE11") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("EfiKeyE12") + }, + { + UsbHidUsageIdKbKpKeyBackSpace, + 0, + AIK_DEBUG_STR("EfiKeyBackSpace") + }, + { + UsbHidUsageIdKbKpKeyIns, + 0, + AIK_DEBUG_STR("EfiKeyIns") + }, + { + UsbHidUsageIdKbKpKeyHome, + 0, + AIK_DEBUG_STR("EfiKeyHome") + }, + { + UsbHidUsageIdKbKpKeyPgUp, + 0, + AIK_DEBUG_STR("EfiKeyPgUp") + }, + { + UsbHidUsageIdKbKpPadKeyNLck, + 0, + AIK_DEBUG_STR("EfiKeyNLck") + }, + { + UsbHidUsageIdKbKpKeySlash, + 0, + AIK_DEBUG_STR("EfiKeySlash") + }, + { + UsbHidUsageIdKbKpPadKeyAsterisk, + 0, + AIK_DEBUG_STR("EfiKeyAsterisk") + }, + { + UsbHidUsageIdKbKpPadKeyMinus, + 0, + AIK_DEBUG_STR("EfiKeyMinus") + }, + { + UsbHidUsageIdKbKpKeyEsc, + 0, + AIK_DEBUG_STR("EfiKeyEsc") + }, + { + UsbHidUsageIdKbKpKeyF1, + 0, + AIK_DEBUG_STR("EfiKeyF1") + }, + { + UsbHidUsageIdKbKpKeyF2, + 0, + AIK_DEBUG_STR("EfiKeyF2") + }, + { + UsbHidUsageIdKbKpKeyF3, + 0, + AIK_DEBUG_STR("EfiKeyF3") + }, + { + UsbHidUsageIdKbKpKeyF4, + 0, + AIK_DEBUG_STR("EfiKeyF4") + }, + { + UsbHidUsageIdKbKpKeyF5, + 0, + AIK_DEBUG_STR("EfiKeyF5") + }, + { + UsbHidUsageIdKbKpKeyF6, + 0, + AIK_DEBUG_STR("EfiKeyF6") + }, + { + UsbHidUsageIdKbKpKeyF7, + 0, + AIK_DEBUG_STR("EfiKeyF7") + }, + { + UsbHidUsageIdKbKpKeyF8, + 0, + AIK_DEBUG_STR("EfiKeyF8") + }, + { + UsbHidUsageIdKbKpKeyF9, + 0, + AIK_DEBUG_STR("EfiKeyF9") + }, + { + UsbHidUsageIdKbKpKeyF10, + 0, + AIK_DEBUG_STR("EfiKeyF10") + }, + { + UsbHidUsageIdKbKpKeyF11, + 0, + AIK_DEBUG_STR("EfiKeyF11") + }, + { + UsbHidUsageIdKbKpKeyF12, + 0, + AIK_DEBUG_STR("EfiKeyF12") + }, + { + UsbHidUsageIdKbKpKeyF13, + 0, + AIK_DEBUG_STR("EfiKeyPrint") + }, + { + UsbHidUsageIdKbKpKeySLock, + 0, + AIK_DEBUG_STR("EfiKeySLck") + }, + { + UsbHidUsageIdKbKpKeyPause, + 0, + AIK_DEBUG_STR("EfiKeyPause") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk105") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk106") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk107") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk108") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk109") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk110") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk111") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk112") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk113") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk114") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk115") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk116") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk117") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk118") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk119") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk120") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk121") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk122") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk123") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk124") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk125") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk126") + }, + { + UsbHidUndefined, + 0, + AIK_DEBUG_STR("Unk127") + } +}; + +AIK_SCANCODE_TO_USB +gAikScanCodeToUsbMap[AIK_MAX_SCANCODE_NUM] = { + { + UsbHidUndefined, + NULL + }, + { + UsbHidUsageIdKbKpKeyUpArrow, + AIK_DEBUG_STR("Move cursor up 1 row") + }, + { + UsbHidUsageIdKbKpKeyDownArrow, + AIK_DEBUG_STR("Move cursor down 1 row") + }, + { + UsbHidUsageIdKbKpKeyRightArrow, + AIK_DEBUG_STR("Move cursor right 1 column") + }, + { + UsbHidUsageIdKbKpKeyLeftArrow, + AIK_DEBUG_STR("Move cursor left 1 column") + }, + { + UsbHidUsageIdKbKpKeyHome, + AIK_DEBUG_STR("Home") + }, + { + UsbHidUsageIdKbKpKeyEnd, + AIK_DEBUG_STR("End") + }, + { + UsbHidUsageIdKbKpKeyIns, + AIK_DEBUG_STR("Insert") + }, + { + UsbHidUsageIdKbKpKeyDel, + AIK_DEBUG_STR("Delete") + }, + { + UsbHidUsageIdKbKpKeyPgUp, + AIK_DEBUG_STR("Page Up") + }, + { + UsbHidUsageIdKbKpKeyPgDn, + AIK_DEBUG_STR("Page Down") + }, + { + UsbHidUsageIdKbKpKeyF1, + AIK_DEBUG_STR("Function 1") + }, + { + UsbHidUsageIdKbKpKeyF2, + AIK_DEBUG_STR("Function 2") + }, + { + UsbHidUsageIdKbKpKeyF3, + AIK_DEBUG_STR("Function 3") + }, + { + UsbHidUsageIdKbKpKeyF4, + AIK_DEBUG_STR("Function 4") + }, + { + UsbHidUsageIdKbKpKeyF5, + AIK_DEBUG_STR("Function 5") + }, + { + UsbHidUsageIdKbKpKeyF6, + AIK_DEBUG_STR("Function 6") + }, + { + UsbHidUsageIdKbKpKeyF7, + AIK_DEBUG_STR("Function 7") + }, + { + UsbHidUsageIdKbKpKeyF8, + AIK_DEBUG_STR("Function 8") + }, + { + UsbHidUsageIdKbKpKeyF9, + AIK_DEBUG_STR("Function 9") + }, + { + UsbHidUsageIdKbKpKeyF10, + AIK_DEBUG_STR("Function 10") + }, + { + UsbHidUsageIdKbKpKeyF11, + AIK_DEBUG_STR("Function 11") + }, + { + UsbHidUsageIdKbKpKeyF12, + AIK_DEBUG_STR("Function 12") + }, + { + UsbHidUsageIdKbKpKeyEsc, + AIK_DEBUG_STR("Escape") + } +}; + +CONST CHAR8 * +gAikModifiersToNameMap[AIK_MAX_MODIFIERS_NUM] = { + NULL, + AIK_DEBUG_STR("LCTRL"), + AIK_DEBUG_STR("LSHIFT"), + AIK_DEBUG_STR("LSHIFT|LCTRL"), + AIK_DEBUG_STR("LALT"), + AIK_DEBUG_STR("LCTRL|LALT"), + AIK_DEBUG_STR("LSHIFT|LALT"), + AIK_DEBUG_STR("LSHIFT|LCTRL|LALT"), + AIK_DEBUG_STR("LGUI"), + AIK_DEBUG_STR("LCTRL|LGUI"), + AIK_DEBUG_STR("LSHIFT|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|LGUI"), + AIK_DEBUG_STR("LALT|LGUI"), + AIK_DEBUG_STR("LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RCTRL"), + AIK_DEBUG_STR("RCTRL|LCTRL"), + AIK_DEBUG_STR("LSHIFT|RCTRL"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL"), + AIK_DEBUG_STR("RCTRL|LALT"), + AIK_DEBUG_STR("RCTRL|LCTRL|LALT"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LALT"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|LALT"), + AIK_DEBUG_STR("RCTRL|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|LGUI"), + AIK_DEBUG_STR("RCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT"), + AIK_DEBUG_STR("RSHIFT|LCTRL"), + AIK_DEBUG_STR("RSHIFT|LSHIFT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL"), + AIK_DEBUG_STR("RSHIFT|LALT"), + AIK_DEBUG_STR("RSHIFT|LCTRL|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|LALT"), + AIK_DEBUG_STR("RSHIFT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|LGUI"), + AIK_DEBUG_STR("RSHIFT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LALT"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|LALT"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|LALT|LGUI"), + AIK_DEBUG_STR("RALT"), + AIK_DEBUG_STR("LCTRL|RALT"), + AIK_DEBUG_STR("LSHIFT|RALT"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT"), + AIK_DEBUG_STR("RALT|LALT"), + AIK_DEBUG_STR("LCTRL|RALT|LALT"), + AIK_DEBUG_STR("LSHIFT|RALT|LALT"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT|LALT"), + AIK_DEBUG_STR("RALT|LGUI"), + AIK_DEBUG_STR("LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RALT|LALT|LGUI"), + AIK_DEBUG_STR("LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RALT|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RCTRL|RALT"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT"), + AIK_DEBUG_STR("RCTRL|RALT|LALT"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT|LALT"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT|LALT"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT|LALT"), + AIK_DEBUG_STR("RCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RALT"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT"), + AIK_DEBUG_STR("RSHIFT|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RALT"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RALT|LALT"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RALT|LALT|LGUI"), + AIK_DEBUG_STR("RGUI"), + AIK_DEBUG_STR("LCTRL|RGUI"), + AIK_DEBUG_STR("LSHIFT|RGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RGUI"), + AIK_DEBUG_STR("LALT|RGUI"), + AIK_DEBUG_STR("LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RGUI|LGUI"), + AIK_DEBUG_STR("LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|RGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RGUI"), + AIK_DEBUG_STR("RCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RGUI"), + AIK_DEBUG_STR("RSHIFT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RALT|RGUI"), + AIK_DEBUG_STR("LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RALT|LALT|RGUI"), + AIK_DEBUG_STR("LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RALT|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RALT|RGUI|LGUI"), + AIK_DEBUG_STR("LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|LCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RCTRL|LCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("LSHIFT|RCTRL|LCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|LCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RALT|LALT|RGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|LCTRL|RALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|RCTRL|LCTRL|RALT|LALT|RGUI|LGUI"), + AIK_DEBUG_STR("RSHIFT|LSHIFT|RCTRL|RALT|LALT|RGUI|LGUI"), +}; + +CONST CHAR8 * +gAikAppleKeyToNameMap[AIK_MAX_APPLEKEY_NUM] = { + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyA"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyB"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyC"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyD"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyE"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyG"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyH"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyI"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyJ"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyK"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyL"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyM"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyN"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyO"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyP"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyQ"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyR"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyS"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyT"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyU"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyV"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyW"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyX"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyY"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyZ"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyOne"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyTwo"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyThree"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyFour"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyFive"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeySix"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeySeven"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyEight"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyNine"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyZero"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyEnter"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyEscape"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyBackSpace"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyTab"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeySpaceBar"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyMinus"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyEquals"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyLeftBracket"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyRightBracket"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyBackslash"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyNonUsHash"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeySemicolon"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyQuotation"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyAcute"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyComma"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyPeriod"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeySlash"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyCLock"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF1"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF2"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF3"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF4"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF5"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF6"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF7"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF8"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF9"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF10"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF11"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyF12"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyPrint"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeySLock"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyPause"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyIns"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyHome"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyPgUp"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyDel"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyEnd"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyPgDn"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyRightArrow"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyLeftArrow"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyDownArrow"), + AIK_DEBUG_STR("AppleHidUsbKbUsageKeyUpArrow"), +}; diff --git a/Protocols/AptioInputFix/Keycode/AIKShim.c b/Protocols/AptioInputFix/Keycode/AIKShim.c new file mode 100644 index 000000000..c541a5e83 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKShim.c @@ -0,0 +1,198 @@ +/** @file + Key shimming code + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIK.h" +#include "AIKShim.h" + +#include +#include +#include + +EFI_STATUS +EFIAPI +AIKShimAmiKeycodeReset ( + IN AMI_EFIKEYCODE_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + if (This == NULL || gAikSelf.OurJobIsDone) { + return EFI_INVALID_PARAMETER; + } + + if (This == gAikSelf.Source.AmiKeycode && !gAikSelf.InPollKeyboardEvent) { + // + // Do not touch any protocol but ours. + // + AIKDataReset (&gAikSelf.Data); + } + + return gAikSelf.Source.AmiReset (This, ExtendedVerification); +} + +EFI_STATUS +EFIAPI +AIKShimAmiKeycodeReadEfikey ( + IN AMI_EFIKEYCODE_PROTOCOL *This, + OUT AMI_EFI_KEY_DATA *KeyData + ) +{ + DEBUG ((DEBUG_VERBOSE, "AIKAmiKeycodeReadEfikey %p %p ours %p event %d", + This, KeyData, gAikSelf.Source.AmiKeycode, gAikSelf.InPollKeyboardEvent)); + + if (This == NULL || KeyData == NULL || gAikSelf.OurJobIsDone) { + return EFI_INVALID_PARAMETER; + } + + if (This == gAikSelf.Source.AmiKeycode && !gAikSelf.InPollKeyboardEvent) { + // + // Do not touch any protocol but ours. + // + return AIKDataReadEntry (&gAikSelf.Data, KeyData); + } + + return gAikSelf.Source.AmiReadEfikey (This, KeyData); +} + +EFI_STATUS +EFIAPI +AIKShimTextInputReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + if (This == NULL || gAikSelf.OurJobIsDone) { + return EFI_INVALID_PARAMETER; + } + + if (This == gAikSelf.Source.TextInput && !gAikSelf.InPollKeyboardEvent) { + // + // Do not touch any protocol but ours. + // + AIKDataReset (&gAikSelf.Data); + } + + return gAikSelf.Source.TextReset (This, ExtendedVerification); +} + +EFI_STATUS +EFIAPI +AIKShimTextInputReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ) +{ + EFI_STATUS Status; + AMI_EFI_KEY_DATA AmiKeyData; + + DEBUG ((DEBUG_VERBOSE, "AIKTextInputReadKeyStroke %p %p ours %p event %d", + This, Key, gAikSelf.Source.TextInput, gAikSelf.InPollKeyboardEvent)); + + if (This == NULL || Key == NULL || gAikSelf.OurJobIsDone) { + return EFI_INVALID_PARAMETER; + } + + if (This == gAikSelf.Source.TextInput && !gAikSelf.InPollKeyboardEvent) { + // + // Do not touch any protocol but ours. + // + Status = AIKDataReadEntry (&gAikSelf.Data, &AmiKeyData); + if (!EFI_ERROR (Status)) { + // + // 'Partial' keys should not be returned by SimpleTextInput protocols. + // + if (AmiKeyData.Key.ScanCode == 0 && AmiKeyData.Key.UnicodeChar == 0 + && (AmiKeyData.KeyState.KeyToggleState & KEY_STATE_EXPOSED)) { + Status = EFI_NOT_READY; + } else { + CopyMem (Key, &AmiKeyData.Key, sizeof (AmiKeyData.Key)); + } + } + + return Status; + } + + return gAikSelf.Source.TextReadKeyStroke (This, Key); +} + +EFI_STATUS +EFIAPI +AIKShimTextInputResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + if (This == NULL || gAikSelf.OurJobIsDone) { + return EFI_INVALID_PARAMETER; + } + + if (This == gAikSelf.Source.TextInputEx && !gAikSelf.InPollKeyboardEvent) { + // + // Do not touch any protocol but ours. + // + AIKDataReset (&gAikSelf.Data); + } + + return gAikSelf.Source.TextResetEx (This, ExtendedVerification); +} + +EFI_STATUS +EFIAPI +AIKShimTextInputReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ) +{ + EFI_STATUS Status; + AMI_EFI_KEY_DATA AmiKeyData; + + DEBUG ((DEBUG_VERBOSE, "AIKTextInputReadKeyStrokeEx %p %p ours %p event %d", + This, KeyData, gAikSelf.Source.TextInputEx, gAikSelf.InPollKeyboardEvent)); + + if (This == NULL || KeyData == NULL || gAikSelf.OurJobIsDone) { + return EFI_INVALID_PARAMETER; + } + + if (This == gAikSelf.Source.TextInputEx && !gAikSelf.InPollKeyboardEvent) { + // + // Do not touch any protocol but ours. + // + Status = AIKDataReadEntry (&gAikSelf.Data, &AmiKeyData); + if (!EFI_ERROR (Status)) { + CopyMem (&KeyData->Key, &AmiKeyData.Key, sizeof (AmiKeyData.Key)); + CopyMem (&KeyData->KeyState, &AmiKeyData.KeyState, sizeof (AmiKeyData.KeyState)); + } + + return Status; + } + + return gAikSelf.Source.TextReadKeyStrokeEx (This, KeyData); +} + +VOID +EFIAPI +AIKShimWaitForKeyHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if (gAikSelf.OurJobIsDone) { + DEBUG ((DEBUG_INFO, "AIKShimWaitForKeyHandler got null handler or called when done\n")); + return; + } + + if (!AIKDataEmpty (&gAikSelf.Data)) { + DEBUG ((DEBUG_VERBOSE, "Caught KeyBufferSize non-zero\n")); + gBS->SignalEvent (Event); + } +} diff --git a/Protocols/AptioInputFix/Keycode/AIKShim.h b/Protocols/AptioInputFix/Keycode/AIKShim.h new file mode 100644 index 000000000..2f3a66a61 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKShim.h @@ -0,0 +1,72 @@ +/** @file + Key shimming code + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef AIK_SHIM_H +#define AIK_SHIM_H + +#include +#include +#include + +EFI_STATUS +EFIAPI +AIKShimAmiKeycodeReset ( + IN AMI_EFIKEYCODE_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +EFI_STATUS +EFIAPI +AIKShimAmiKeycodeReadEfikey ( + IN AMI_EFIKEYCODE_PROTOCOL *This, + OUT AMI_EFI_KEY_DATA *KeyData + ); + +EFI_STATUS +EFIAPI +AIKShimTextInputReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +EFI_STATUS +EFIAPI +AIKShimTextInputReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ); + +EFI_STATUS +EFIAPI +AIKShimTextInputResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +EFI_STATUS +EFIAPI +AIKShimTextInputReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ); + +VOID +EFIAPI +AIKShimWaitForKeyHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif + diff --git a/Protocols/AptioInputFix/Keycode/AIKSource.c b/Protocols/AptioInputFix/Keycode/AIKSource.c new file mode 100644 index 000000000..1f2d935b3 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKSource.c @@ -0,0 +1,204 @@ +/** @file + Key provider + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIKSource.h" +#include "AIKShim.h" + +#include +#include +#include + +EFI_STATUS +AIKSourceGrabEfiKey ( + AIK_SOURCE *Source, + AMI_EFI_KEY_DATA *KeyData + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + ZeroMem (KeyData, sizeof (*KeyData)); + + if (Source->AmiKeycode != NULL && Source->AmiReadEfikey) { + Status = Source->AmiReadEfikey (Source->AmiKeycode, KeyData); + Event = Source->AmiKeycode->WaitForKeyEx; + } else if (Source->TextInputEx != NULL && Source->TextReadKeyStrokeEx) { + Status = Source->TextReadKeyStrokeEx (Source->TextInputEx, (EFI_KEY_DATA *) KeyData); + Event = Source->TextInputEx->WaitForKeyEx; + } else if (Source->TextInput != NULL && Source->TextReadKeyStroke) { + Status = Source->TextReadKeyStroke (Source->TextInput, &KeyData->Key); + Event = Source->TextInput->WaitForKey; + } else { + // + // Should never happen. + // + Status = EFI_NOT_FOUND; + Event = NULL; + } + + if (Event != NULL) { + gBS->SignalEvent (Event); + } + + return Status; +} + +EFI_STATUS +AIKSourceInstall ( + AIK_SOURCE *Source + ) +{ + EFI_STATUS Status; + + if (Source->AmiKeycode != NULL || Source->TextInput != NULL || Source->TextInputEx != NULL) { + return EFI_SUCCESS; + } + + Source->ConSplitHandler = gST->ConsoleInHandle; + + Status = gBS->HandleProtocol (Source->ConSplitHandler, &gAmiEfiKeycodeProtocolGuid, + (VOID **) &Source->AmiKeycode); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AmiEfiKeycodeProtocol is unavailable on gST->ConsoleHandle - %r\n", Status)); + } + + Status = gBS->HandleProtocol (Source->ConSplitHandler, &gEfiSimpleTextInProtocolGuid, + (VOID **) &Source->TextInput); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "EfiSimpleTextInProtocol is unavailable on gST->ConsoleHandle - %r\n", Status)); + } + + Status = gBS->HandleProtocol (Source->ConSplitHandler, &gEfiSimpleTextInputExProtocolGuid, + (VOID **) &Source->TextInputEx); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "EfiSimpleTextInputExProtocol is unavailable on gST->ConsoleHandle - %r\n", Status)); + } + + if (Source->AmiKeycode == NULL && Source->TextInput == NULL && Source->TextInputEx == NULL) { + DEBUG ((DEBUG_INFO, "No ConSplitter input protocol is unavailable\n")); + return EFI_NOT_FOUND; + } + + DEBUG ((DEBUG_INFO, "gST->ConIn %p vs found %p\n", gST->ConIn, Source->TextInput)); + + // + // We additionally reset the protocols as our buffers are empty, and we do not want old data. + // + if (Source->AmiKeycode) { + Source->AmiKeycode->Reset (Source->AmiKeycode, FALSE); + Source->AmiReset = Source->AmiKeycode->Reset; + Source->AmiReadEfikey = Source->AmiKeycode->ReadEfikey; + Source->AmiWait = Source->AmiKeycode->WaitForKeyEx; + Source->AmiKeycode->Reset = AIKShimAmiKeycodeReset; + Source->AmiKeycode->ReadEfikey = AIKShimAmiKeycodeReadEfikey; + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, TPL_NOTIFY, AIKShimWaitForKeyHandler, + NULL, &Source->AmiKeycode->WaitForKeyEx + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AmiEfiKeycodeProtocol WaitForKey replace failed - %r", Status)); + Source->AmiKeycode->WaitForKeyEx = Source->AmiWait; + Source->AmiWait = NULL; + } + } + + if (Source->TextInput) { + Source->TextInput->Reset (Source->TextInput, FALSE); + Source->TextReset = Source->TextInput->Reset; + Source->TextReadKeyStroke = Source->TextInput->ReadKeyStroke; + Source->TextWait = Source->TextInput->WaitForKey; + Source->TextInput->Reset = AIKShimTextInputReset; + Source->TextInput->ReadKeyStroke = AIKShimTextInputReadKeyStroke; + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, TPL_NOTIFY, AIKShimWaitForKeyHandler, + NULL, &Source->TextInput->WaitForKey + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "EfiSimpleTextInProtocol WaitForKey replace failed - %r", Status)); + Source->TextInput->WaitForKey = Source->TextWait; + Source->TextWait = NULL; + } + } + + if (Source->TextInputEx) { + Source->TextInputEx->Reset (Source->TextInputEx, FALSE); + Source->TextResetEx = Source->TextInputEx->Reset; + Source->TextWaitEx = Source->TextInputEx->WaitForKeyEx; + Source->TextReadKeyStrokeEx = Source->TextInputEx->ReadKeyStrokeEx; + Source->TextInputEx->Reset = AIKShimTextInputResetEx; + Source->TextInputEx->ReadKeyStrokeEx = AIKShimTextInputReadKeyStrokeEx; + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, TPL_NOTIFY, AIKShimWaitForKeyHandler, + NULL, &Source->TextInputEx->WaitForKeyEx + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "EfiSimpleTextInputExProtocol WaitForKey replace failed - %r", Status)); + Source->TextInputEx->WaitForKeyEx = Source->TextWaitEx; + Source->TextWaitEx = NULL; + } + } + + return EFI_SUCCESS; +} + +VOID +AIKSourceUninstall ( + AIK_SOURCE *Source + ) +{ + if (Source->AmiKeycode) { + Source->AmiKeycode->Reset = Source->AmiReset; + Source->AmiKeycode->ReadEfikey = Source->AmiReadEfikey; + if (Source->AmiWait != NULL && Source->AmiWait != Source->AmiKeycode->WaitForKeyEx) { + gBS->CloseEvent (Source->AmiKeycode->WaitForKeyEx); + Source->AmiKeycode->WaitForKeyEx = Source->AmiWait; + } + Source->AmiReset = NULL; + Source->AmiReadEfikey = NULL; + Source->AmiWait = NULL; + Source->AmiKeycode = NULL; + } + + if (Source->TextInput) { + Source->TextInput->Reset = Source->TextReset; + Source->TextInput->ReadKeyStroke = Source->TextReadKeyStroke; + if (Source->TextWait != NULL && Source->TextWait != Source->TextInput->WaitForKey) { + gBS->CloseEvent (Source->TextInput->WaitForKey); + Source->TextInput->WaitForKey = Source->TextWait; + } + Source->TextInput = NULL; + Source->TextReset = NULL; + Source->TextWait = NULL; + Source->TextReadKeyStroke = NULL; + } + + if (Source->TextInputEx) { + Source->TextInputEx->Reset = Source->TextResetEx; + Source->TextInputEx->ReadKeyStrokeEx = Source->TextReadKeyStrokeEx; + if (Source->TextWaitEx != NULL && Source->TextWaitEx != Source->TextInputEx->WaitForKeyEx) { + gBS->CloseEvent (Source->TextInputEx->WaitForKeyEx); + Source->TextInputEx->WaitForKeyEx = Source->TextWaitEx; + } + Source->TextInputEx = NULL; + Source->TextResetEx = NULL; + Source->TextWaitEx = NULL; + Source->TextReadKeyStrokeEx = NULL; + } + + if (Source->ConSplitHandler != NULL) { + gBS->DisconnectController (Source->ConSplitHandler, NULL, NULL); + Source->ConSplitHandler = NULL; + } +} + diff --git a/Protocols/AptioInputFix/Keycode/AIKSource.h b/Protocols/AptioInputFix/Keycode/AIKSource.h new file mode 100644 index 000000000..ed52b99f4 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKSource.h @@ -0,0 +1,68 @@ +/** @file + Key provider + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef AIK_SOURCE_H +#define AIK_SOURCE_H + +#include +#include +#include + +typedef struct { + // + // Preserved handle of gST->ConsoleInHandle + // + EFI_HANDLE ConSplitHandler; + + // + // Solved input protocol instances from ConSplitHandler + // We override their ReadKey and Reset handlers and implement + // them ourselves via polled data from one of these protocols. + // Polled proto is prioritised as present: AMI, EX, Legacy. + // + AMI_EFIKEYCODE_PROTOCOL *AmiKeycode; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextInput; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx; + + // + // Original implementations of the protocols. + // + AMI_RESET_EX AmiReset; + AMI_READ_EFI_KEY AmiReadEfikey; + EFI_EVENT AmiWait; + EFI_INPUT_RESET TextReset; + EFI_EVENT TextWait; + EFI_INPUT_READ_KEY TextReadKeyStroke; + EFI_INPUT_RESET_EX TextResetEx; + EFI_INPUT_READ_KEY_EX TextReadKeyStrokeEx; + EFI_EVENT TextWaitEx; +} AIK_SOURCE; + +EFI_STATUS +AIKSourceGrabEfiKey ( + AIK_SOURCE *Source, + AMI_EFI_KEY_DATA *KeyData + ); + +EFI_STATUS +AIKSourceInstall ( + AIK_SOURCE *Source + ); + +VOID +AIKSourceUninstall ( + AIK_SOURCE *Source + ); + +#endif diff --git a/Protocols/AptioInputFix/Keycode/AIKTarget.c b/Protocols/AptioInputFix/Keycode/AIKTarget.c new file mode 100644 index 000000000..fc55473d8 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKTarget.c @@ -0,0 +1,191 @@ +/** @file + Key consumer + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIKTarget.h" +#include "AIKTranslate.h" + +#include +#include +#include + +EFI_STATUS +AIKTargetInstall ( + IN OUT AIK_TARGET *Target + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (Target->KeyMapDb == NULL) { + Status = gBS->LocateProtocol (&gAppleKeyMapDatabaseProtocolGuid, NULL, (VOID **) &Target->KeyMapDb); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AppleKeyMapDatabaseProtocol is unavailable - %r\n", Status)); + return EFI_NOT_FOUND; + } + + Status = Target->KeyMapDb->CreateKeyStrokesBuffer ( + Target->KeyMapDb, AIK_TARGET_BUFFER_SIZE, &Target->KeyMapDbIndex + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "CreateKeyStrokesBuffer failed - %r\n", Status)); + Target->KeyMapDb = NULL; + } + } + + return Status; +} + +VOID +AIKTargetUninstall ( + IN OUT AIK_TARGET *Target + ) +{ + if (Target->KeyMapDb != NULL) { + Target->KeyMapDb->RemoveKeyStrokesBuffer (Target->KeyMapDb, Target->KeyMapDbIndex); + Target->KeyMapDb = NULL; + } + + Target->NumberOfKeys = 0; + Target->Modifiers = 0; + Target->ModifierCounter = 0; + ZeroMem (Target->Keys, sizeof (Target->Keys)); + ZeroMem (Target->KeyCounters, sizeof (Target->KeyCounters)); +} + +UINT64 +AIKTargetRefresh ( + IN OUT AIK_TARGET *Target + ) +{ + UINTN Index; + UINTN Left; + + Target->Counter++; + + for (Index = 0; Index < Target->NumberOfKeys; Index++) { + // + // We reported this key AIK_TARGET_FORGET_THRESHOLD times, time to say goodbye. + // + if (Target->KeyCounters[Index] + AIK_TARGET_FORGET_THRESHOLD <= Target->Counter) { + Left = Target->NumberOfKeys - (Index + 1); + if (Left > 0) { + CopyMem ( + &Target->KeyCounters[Index], + &Target->KeyCounters[Index+1], + sizeof (Target->KeyCounters[0]) * Left); + CopyMem ( + &Target->Keys[Index], + &Target->Keys[Index+1], + sizeof (Target->Keys[0]) * Left); + } + Target->NumberOfKeys--; + } + } + + // + // No keys were pressed, so we did not enter AIKTargetWriteEntry. + // However, we still need to reset modifiers after time. + // + if (Target->ModifierCounter + AIK_TARGET_FORGET_THRESHOLD <= Target->Counter) { + Target->Modifiers = 0; + } + + return Target->Counter; +} + +VOID +AIKTargetWriteEntry ( + IN OUT AIK_TARGET *Target, + IN AMI_EFI_KEY_DATA *KeyData + ) +{ + APPLE_MODIFIER_MAP Modifiers; + APPLE_KEY_CODE Key; + UINTN Index; + UINTN InsertIndex; + UINT64 OldestCounter; + + AIKTranslate (KeyData, &Modifiers, &Key); + + Target->Modifiers = Modifiers; + Target->ModifierCounter = Target->Counter; + + if (Key == UsbHidUndefined) { + // + // This is just a modifier or an unsupported key. + // + return; + } + + for (Index = 0; Index < Target->NumberOfKeys; Index++) { + if (Target->Keys[Index] == Key) { + // + // This key was added previously, just update its counter. + // + Target->KeyCounters[Index] = Target->Counter; + return; + } + } + + InsertIndex = Target->NumberOfKeys; + + // + // This should not happen, but we have no room, replace the oldest key. + // + if (InsertIndex == AIK_TARGET_BUFFER_SIZE) { + InsertIndex = 0; + OldestCounter = Target->KeyCounters[InsertIndex]; + for (Index = 1; Index < Target->NumberOfKeys; Index++) { + if (OldestCounter > Target->KeyCounters[Index]) { + OldestCounter = Target->KeyCounters[Index]; + InsertIndex = Index; + } + } + Target->NumberOfKeys--; + } + + // + // Insert the new key + // + Target->Keys[InsertIndex] = Key; + Target->KeyCounters[InsertIndex] = Target->Counter; + Target->NumberOfKeys++; +} + +VOID +AIKTargetSubmit ( + IN OUT AIK_TARGET *Target + ) +{ + EFI_STATUS Status; + + if (Target->KeyMapDb != NULL) { + Status = Target->KeyMapDb->SetKeyStrokeBufferKeys ( + Target->KeyMapDb, + Target->KeyMapDbIndex, + Target->Modifiers, + Target->NumberOfKeys, + Target->Keys + ); + } else { + Status = EFI_NOT_FOUND; + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "Failed to submit keys to AppleMapDb - %r", Status)); + } +} diff --git a/Protocols/AptioInputFix/Keycode/AIKTarget.h b/Protocols/AptioInputFix/Keycode/AIKTarget.h new file mode 100644 index 000000000..6a69af491 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKTarget.h @@ -0,0 +1,114 @@ +/** @file + Key consumer + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef AIK_TARGET_H +#define AIK_TARGET_H + +#include +#include +#include + +// +// Maximum amount of keys reported to Apple protocols +// +#define AIK_TARGET_BUFFER_SIZE 6 + +// +// Known values for key repeat (single key hold): +// VMware - 2, APTIO V - 3 or 4 +// Known values for different keys (quick press one after other): +// VMware - 6+, APTIO V - 10+ +// Known values for simultaneous keys (dual key hold): +// VMware - 2, APTIO V - 1 +// + +// +// Remove key if it was not submitted after this value. +// +#define AIK_TARGET_FORGET_THRESHOLD 5 + +// +// Assume simultaneous press if within this value. +// +#define AIK_TARGET_MERGE_THRESHOLD 2 + +typedef struct { + // + // Apple output protocol we submit data to. + // + APPLE_KEY_MAP_DATABASE_PROTOCOL *KeyMapDb; + + // + // Apple output buffer index + // + UINTN KeyMapDbIndex; + + // + // Apple modifier map (previously reported) + // + APPLE_MODIFIER_MAP Modifiers; + + // + // Previously reported Apple modifiers timestamp + // + UINT64 ModifierCounter; + + // + // Previously reported Apple active keys + // + APPLE_KEY_CODE Keys[AIK_TARGET_BUFFER_SIZE]; + + // + // Previously reported Apple key timestamps + // + UINT64 KeyCounters[AIK_TARGET_BUFFER_SIZE]; + + // + // Amount of active keys previously reported + // + UINTN NumberOfKeys; + + // + // Timestamp counter incremented every refresh + // + UINT64 Counter; +} AIK_TARGET; + +EFI_STATUS +AIKTargetInstall ( + IN OUT AIK_TARGET *Target + ); + +VOID +AIKTargetUninstall ( + IN OUT AIK_TARGET *Target + ); + +UINT64 +AIKTargetRefresh ( + IN OUT AIK_TARGET *Target + ); + +VOID +AIKTargetWriteEntry ( + IN OUT AIK_TARGET *Target, + IN AMI_EFI_KEY_DATA *KeyData + ); + +VOID +AIKTargetSubmit ( + IN OUT AIK_TARGET *Target + ); + +#endif diff --git a/Protocols/AptioInputFix/Keycode/AIKTranslate.c b/Protocols/AptioInputFix/Keycode/AIKTranslate.c new file mode 100644 index 000000000..c282c701d --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKTranslate.c @@ -0,0 +1,199 @@ +/** @file + Key translator + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIKTranslate.h" + +#include + +STATIC +APPLE_MODIFIER_MAP mModifierRemap[AIK_MODIFIER_MAX]; + +STATIC +VOID +AIKTranslateModifiers ( + IN AMI_EFI_KEY_DATA *KeyData, + OUT APPLE_MODIFIER_MAP *Modifiers + ) +{ + UINT32 KeyShiftState; + + KeyShiftState = KeyData->KeyState.KeyShiftState; + + *Modifiers = 0; + + // + // Handle EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Shift support, which is not included + // in KeyShiftState on APTIO and VMware. + // UPD: Recent APTIO V also does not include it in its own protocol. + // + if ((KeyShiftState & EFI_SHIFT_STATE_VALID) && KeyData->Key.UnicodeChar < AIK_MAX_EFIKEY_NUM) { + KeyShiftState |= gAikAsciiToUsbMap[KeyData->Key.UnicodeChar].ShiftState; + } + + // + // Handle legacy EFI_SIMPLE_TEXT_INPUT_PROTOCOL by guessing from EfiKey + // + if ((KeyShiftState & EFI_SHIFT_STATE_VALID) != EFI_SHIFT_STATE_VALID + && KeyData->Key.UnicodeChar < AIK_MAX_EFIKEY_NUM) { + KeyShiftState = gAikAsciiToUsbMap[KeyData->Key.UnicodeChar].ShiftState; + } + + if (KeyShiftState & EFI_SHIFT_STATE_VALID) { + if (KeyShiftState & EFI_RIGHT_SHIFT_PRESSED) { + *Modifiers |= mModifierRemap[AIK_RIGHT_SHIFT]; + } + if (KeyShiftState & EFI_LEFT_SHIFT_PRESSED) { + *Modifiers |= mModifierRemap[AIK_LEFT_SHIFT]; + } + if (KeyShiftState & EFI_RIGHT_CONTROL_PRESSED) { + *Modifiers |= mModifierRemap[AIK_RIGHT_CONTROL]; + } + if (KeyShiftState & EFI_LEFT_CONTROL_PRESSED) { + *Modifiers |= mModifierRemap[AIK_LEFT_CONTROL]; + } + if (KeyShiftState & EFI_RIGHT_ALT_PRESSED) { + *Modifiers |= mModifierRemap[AIK_RIGHT_ALT]; + } + if (KeyShiftState & EFI_LEFT_ALT_PRESSED) { + *Modifiers |= mModifierRemap[AIK_LEFT_ALT]; + } + if (KeyShiftState & EFI_RIGHT_LOGO_PRESSED) { + *Modifiers |= mModifierRemap[AIK_RIGHT_GUI]; + } + if (KeyShiftState & EFI_LEFT_LOGO_PRESSED) { + *Modifiers |= mModifierRemap[AIK_LEFT_GUI]; + } + } +} + +STATIC +VOID +AIKTranslateNumpad ( + IN OUT UINT8 *UsbKey, + IN EFI_KEY EfiKey + ) +{ + switch (EfiKey) { + case EfiKeyZero: + *UsbKey = UsbHidUsageIdKbKpKeyZero; + break; + case EfiKeyOne: + *UsbKey = UsbHidUsageIdKbKpKeyOne; + break; + case EfiKeyTwo: + *UsbKey = UsbHidUsageIdKbKpKeyTwo; + break; + case EfiKeyThree: + *UsbKey = UsbHidUsageIdKbKpKeyThree; + break; + case EfiKeyFour: + *UsbKey = UsbHidUsageIdKbKpKeyFour; + break; + case EfiKeyFive: + *UsbKey = UsbHidUsageIdKbKpKeyFive; + break; + case EfiKeySix: + *UsbKey = UsbHidUsageIdKbKpKeySix; + break; + case EfiKeySeven: + *UsbKey = UsbHidUsageIdKbKpKeySeven; + break; + case EfiKeyEight: + *UsbKey = UsbHidUsageIdKbKpKeyEight; + break; + case EfiKeyNine: + *UsbKey = UsbHidUsageIdKbKpKeyNine; + break; + default: + break; + } +} + +VOID +AIKTranslateConfigure ( + VOID + ) +{ + UINTN Index; + CONST APPLE_MODIFIER_MAP DefaultModifierMap[AIK_MODIFIER_MAX] = { + USB_HID_KB_KP_MODIFIER_RIGHT_SHIFT, + USB_HID_KB_KP_MODIFIER_LEFT_SHIFT, + USB_HID_KB_KP_MODIFIER_RIGHT_CONTROL, + USB_HID_KB_KP_MODIFIER_LEFT_CONTROL, + USB_HID_KB_KP_MODIFIER_RIGHT_ALT, + USB_HID_KB_KP_MODIFIER_LEFT_ALT, + USB_HID_KB_KP_MODIFIER_RIGHT_GUI, + USB_HID_KB_KP_MODIFIER_LEFT_GUI + }; + + //TODO: This should be user-configurable, perhaps via a nvram variable. + + // You can swap Alt with Gui, to get Apple layout on a PC keyboard + CONST UINTN DefaultModifierConfig[AIK_MODIFIER_MAX/2] = { + AIK_RIGHT_SHIFT, + AIK_RIGHT_CONTROL, + AIK_RIGHT_ALT, + AIK_RIGHT_GUI + }; + + for (Index = 0; Index < AIK_MODIFIER_MAX/2; Index++) { + mModifierRemap[Index*2] = DefaultModifierMap[DefaultModifierConfig[Index]]; + mModifierRemap[Index*2+1] = DefaultModifierMap[DefaultModifierConfig[Index]+1]; + } +} + +VOID +AIKTranslate ( + IN AMI_EFI_KEY_DATA *KeyData, + OUT APPLE_MODIFIER_MAP *Modifiers, + OUT APPLE_KEY_CODE *Key + ) +{ + AIK_PS2KEY_TO_USB Ps2Key; + + AIKTranslateModifiers (KeyData, Modifiers); + + *Key = UsbHidUndefined; + + // + // Firstly check for APTIO protocol, which reported a PS/2 key to us. + // Otherwise try to decode UnicodeChar and Scancode from simple input. + // + if (KeyData->PS2ScanCodeIsValid == 1 && KeyData->PS2ScanCode < AIK_MAX_PS2KEY_NUM) { + Ps2Key = gAikPs2KeyToUsbMap[KeyData->PS2ScanCode]; + if (Ps2Key.UsbCode != UsbHidUndefined) { + // + // We need to process numpad keys separately. + // + AIKTranslateNumpad (&Ps2Key.UsbCode, KeyData->EfiKey); + *Key = APPLE_HID_USB_KB_KP_USAGE (Ps2Key.UsbCode); + } + } else if (KeyData->Key.UnicodeChar >= 0 + && KeyData->Key.UnicodeChar < AIK_MAX_ASCII_NUM + && gAikAsciiToUsbMap[KeyData->Key.UnicodeChar].UsbCode != UsbHidUndefined) { + *Key = APPLE_HID_USB_KB_KP_USAGE (gAikAsciiToUsbMap[KeyData->Key.UnicodeChar].UsbCode); + } else if (KeyData->Key.ScanCode < AIK_MAX_SCANCODE_NUM + && gAikScanCodeToUsbMap[KeyData->Key.ScanCode].UsbCode != UsbHidUndefined) { + *Key = APPLE_HID_USB_KB_KP_USAGE (gAikScanCodeToUsbMap[KeyData->Key.ScanCode].UsbCode); + } + + if (*Key != UsbHidUndefined) { + DEBUG ((DEBUG_VERBOSE, "\nAIKTranslate P1 MOD %a APPLE 0x%X (%a) PS2 0x%X Ps2Name %a\n", + AIK_MODIFIERS_TO_NAME (*Modifiers), *Key, AIK_APPLEKEY_TO_NAME (*Key), + KeyData->PS2ScanCode, AIK_PS2KEY_TO_NAME (KeyData->PS2ScanCode, *Modifiers))); + DEBUG ((DEBUG_VERBOSE, "AIKTranslate P2 AsciiName %a ScanName %a EfiKey %a Scan 0x%X Uni 0x%X SState 0x%X\n", + AIK_ASCII_TO_NAME (KeyData->Key.UnicodeChar), AIK_SCANCODE_TO_NAME (KeyData->Key.ScanCode), + AIK_EFIKEY_TO_NAME (KeyData->EfiKey), KeyData->Key.ScanCode, KeyData->Key.UnicodeChar, KeyData->KeyState.KeyShiftState)); + } +} diff --git a/Protocols/AptioInputFix/Keycode/AIKTranslate.h b/Protocols/AptioInputFix/Keycode/AIKTranslate.h new file mode 100644 index 000000000..0bdaed95e --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AIKTranslate.h @@ -0,0 +1,132 @@ +/** @file + Key translator + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef AIK_TRANSLATE_H +#define AIK_TRANSLATE_H + +#include +#include + +#define AIK_MAX_PS2KEY_NUM 128 +#define AIK_MAX_EFIKEY_NUM 128 +#define AIK_MAX_ASCII_NUM 128 +#define AIK_MAX_SCANCODE_NUM 24 + +#define AIK_MAX_MODIFIERS_NUM ( \ + USB_HID_KB_KP_MODIFIER_RIGHT_SHIFT \ + | USB_HID_KB_KP_MODIFIER_LEFT_SHIFT \ + | USB_HID_KB_KP_MODIFIER_RIGHT_CONTROL \ + | USB_HID_KB_KP_MODIFIER_LEFT_CONTROL \ + | USB_HID_KB_KP_MODIFIER_RIGHT_ALT \ + | USB_HID_KB_KP_MODIFIER_LEFT_ALT \ + | USB_HID_KB_KP_MODIFIER_RIGHT_GUI \ + | USB_HID_KB_KP_MODIFIER_LEFT_GUI \ + ) + +#define AIK_APPLEKEY_MIN AppleHidUsbKbUsageKeyA +#define AIK_APPLEKEY_MAX AppleHidUsbKbUsageKeyUpArrow +#define AIK_MAX_APPLEKEY_NUM ((AIK_APPLEKEY_MAX) - (AIK_APPLEKEY_MIN) + 1) + +typedef struct { + UINT8 UsbCode; + CONST CHAR8 *KeyName; + CONST CHAR8 *ShiftKeyName; +} AIK_PS2KEY_TO_USB; + +typedef struct { + UINT8 UsbCode; + UINT32 ShiftState; + CONST CHAR8 *KeyName; +} AIK_EFIKEY_TO_USB; + +typedef struct { + UINT8 UsbCode; + UINT32 ShiftState; + CONST CHAR8 *KeyName; +} AIK_ASCII_TO_USB; + +typedef struct { + UINT8 UsbCode; + CONST CHAR8 *KeyName; +} AIK_SCANCODE_TO_USB; + +extern AIK_PS2KEY_TO_USB gAikPs2KeyToUsbMap[AIK_MAX_PS2KEY_NUM]; +extern AIK_EFIKEY_TO_USB gAikEfiKeyToUsbMap[AIK_MAX_EFIKEY_NUM]; +extern AIK_ASCII_TO_USB gAikAsciiToUsbMap[AIK_MAX_ASCII_NUM]; +extern AIK_SCANCODE_TO_USB gAikScanCodeToUsbMap[AIK_MAX_SCANCODE_NUM]; +extern CONST CHAR8 * gAikModifiersToNameMap[AIK_MAX_MODIFIERS_NUM]; +extern CONST CHAR8 * gAikAppleKeyToNameMap[AIK_MAX_APPLEKEY_NUM]; + +#ifndef APTIOFIX_DEBUG_TARGET +#define AIK_DEBUG_STR(x) (NULL) +#else +#define AIK_DEBUG_STR(x) (x) +#endif + +#define AIK_PS2KEY_TO_NAME(k, m) \ + (((k) < AIK_MAX_PS2KEY_NUM && gAikPs2KeyToUsbMap[k].KeyName) \ + ? (((m) & (EFI_LEFT_SHIFT_PRESSED|EFI_RIGHT_SHIFT_PRESSED)) \ + ? gAikPs2KeyToUsbMap[k].ShiftKeyName : gAikPs2KeyToUsbMap[k].KeyName) \ + : "") + +#define AIK_EFIKEY_TO_NAME(k) \ + (((k) < AIK_MAX_EFIKEY_NUM && gAikEfiKeyToUsbMap[k].KeyName) \ + ? gAikEfiKeyToUsbMap[k].KeyName \ + : "") + +#define AIK_ASCII_TO_NAME(k) \ + (((k) >= 0 && (k) < AIK_MAX_ASCII_NUM && gAikAsciiToUsbMap[k].KeyName) \ + ? gAikAsciiToUsbMap[k].KeyName \ + : "") + +#define AIK_SCANCODE_TO_NAME(k) \ + (((k) < AIK_MAX_SCANCODE_NUM && gAikScanCodeToUsbMap[k].KeyName) \ + ? gAikScanCodeToUsbMap[k].KeyName \ + : "") + +#define AIK_MODIFIERS_TO_NAME(k) \ + (((k) < AIK_MAX_MODIFIERS_NUM && gAikModifiersToNameMap[k]) \ + ? gAikModifiersToNameMap[k] \ + : "") + +#define AIK_APPLEKEY_TO_NAME(k) \ + (((k) >= AIK_APPLEKEY_MIN && (k) <= AIK_APPLEKEY_MAX && gAikAppleKeyToNameMap[(k) - AIK_APPLEKEY_MIN]) \ + ? gAikAppleKeyToNameMap[(k) - AIK_APPLEKEY_MIN] \ + : "") + +enum { + AIK_RIGHT_SHIFT, + AIK_LEFT_SHIFT, + AIK_RIGHT_CONTROL, + AIK_LEFT_CONTROL, + AIK_RIGHT_ALT, + AIK_LEFT_ALT, + AIK_RIGHT_GUI, + AIK_LEFT_GUI, + AIK_MODIFIER_MAX +}; + +VOID +AIKTranslateConfigure ( + VOID + ); + +VOID +AIKTranslate ( + IN AMI_EFI_KEY_DATA *KeyData, + OUT APPLE_MODIFIER_MAP *Modifiers, + OUT APPLE_KEY_CODE *Key + ); + +#endif diff --git a/Protocols/AptioInputFix/Keycode/AppleHid.txt b/Protocols/AptioInputFix/Keycode/AppleHid.txt new file mode 100644 index 000000000..b78b7d9e2 --- /dev/null +++ b/Protocols/AptioInputFix/Keycode/AppleHid.txt @@ -0,0 +1,207 @@ +AppleHidUsbKbUsageKeyA = 0x7004 +AppleHidUsbKbUsageKeyB = 0x7005 +AppleHidUsbKbUsageKeyC = 0x7006 +AppleHidUsbKbUsageKeyD = 0x7007 +AppleHidUsbKbUsageKeyE = 0x7008 +AppleHidUsbKbUsageKeyF = 0x7009 +AppleHidUsbKbUsageKeyG = 0x700A +AppleHidUsbKbUsageKeyH = 0x700B +AppleHidUsbKbUsageKeyI = 0x700C +AppleHidUsbKbUsageKeyJ = 0x700D +AppleHidUsbKbUsageKeyK = 0x700E +AppleHidUsbKbUsageKeyL = 0x700F +AppleHidUsbKbUsageKeyM = 0x7010 +AppleHidUsbKbUsageKeyN = 0x7011 +AppleHidUsbKbUsageKeyO = 0x7012 +AppleHidUsbKbUsageKeyP = 0x7013 +AppleHidUsbKbUsageKeyQ = 0x7014 +AppleHidUsbKbUsageKeyR = 0x7015 +AppleHidUsbKbUsageKeyS = 0x7016 +AppleHidUsbKbUsageKeyT = 0x7017 +AppleHidUsbKbUsageKeyU = 0x7018 +AppleHidUsbKbUsageKeyV = 0x7019 +AppleHidUsbKbUsageKeyW = 0x701A +AppleHidUsbKbUsageKeyX = 0x701B +AppleHidUsbKbUsageKeyY = 0x701C +AppleHidUsbKbUsageKeyZ = 0x701D +AppleHidUsbKbUsageKeyOne = 0x701E +AppleHidUsbKbUsageKeyTwo = 0x701F +AppleHidUsbKbUsageKeyThree = 0x7020 +AppleHidUsbKbUsageKeyFour = 0x7021 +AppleHidUsbKbUsageKeyFive = 0x7022 +AppleHidUsbKbUsageKeySix = 0x7023 +AppleHidUsbKbUsageKeySeven = 0x7024 +AppleHidUsbKbUsageKeyEight = 0x7025 +AppleHidUsbKbUsageKeyNine = 0x7026 +AppleHidUsbKbUsageKeyZero = 0x7027 +AppleHidUsbKbUsageKeyEnter = 0x7028 +AppleHidUsbKbUsageKeyEscape = 0x7029 +AppleHidUsbKbUsageKeyBackSpace = 0x702A +AppleHidUsbKbUsageKeyTab = 0x702B +AppleHidUsbKbUsageKeySpaceBar = 0x702C +AppleHidUsbKbUsageKeyMinus = 0x702D +AppleHidUsbKbUsageKeyEquals = 0x702E +AppleHidUsbKbUsageKeyLeftBracket = 0x702F +AppleHidUsbKbUsageKeyRightBracket = 0x7030 +AppleHidUsbKbUsageKeyBackslash = 0x7031 +AppleHidUsbKbUsageKeyNonUsHash = 0x7032 +AppleHidUsbKbUsageKeySemicolon = 0x7033 +AppleHidUsbKbUsageKeyQuotation = 0x7034 +AppleHidUsbKbUsageKeyAcute = 0x7035 +AppleHidUsbKbUsageKeyComma = 0x7036 +AppleHidUsbKbUsageKeyPeriod = 0x7037 +AppleHidUsbKbUsageKeySlash = 0x7038 +AppleHidUsbKbUsageKeyCLock = 0x7039 +AppleHidUsbKbUsageKeyF1 = 0x703A +AppleHidUsbKbUsageKeyF2 = 0x703B +AppleHidUsbKbUsageKeyF3 = 0x703C +AppleHidUsbKbUsageKeyF4 = 0x703D +AppleHidUsbKbUsageKeyF5 = 0x703E +AppleHidUsbKbUsageKeyF6 = 0x703F +AppleHidUsbKbUsageKeyF7 = 0x7040 +AppleHidUsbKbUsageKeyF8 = 0x7041 +AppleHidUsbKbUsageKeyF9 = 0x7042 +AppleHidUsbKbUsageKeyF10 = 0x7043 +AppleHidUsbKbUsageKeyF11 = 0x7044 +AppleHidUsbKbUsageKeyF12 = 0x7045 +AppleHidUsbKbUsageKeyPrint = 0x7046 +AppleHidUsbKbUsageKeySLock = 0x7047 +AppleHidUsbKbUsageKeyPause = 0x7048 +AppleHidUsbKbUsageKeyIns = 0x7049 +AppleHidUsbKbUsageKeyHome = 0x704A +AppleHidUsbKbUsageKeyPgUp = 0x704B +AppleHidUsbKbUsageKeyDel = 0x704C +AppleHidUsbKbUsageKeyEnd = 0x704D +AppleHidUsbKbUsageKeyPgDn = 0x704E +AppleHidUsbKbUsageKeyRightArrow = 0x704F +AppleHidUsbKbUsageKeyLeftArrow = 0x7050 +AppleHidUsbKbUsageKeyDownArrow = 0x7051 +AppleHidUsbKbUsageKeyUpArrow = 0x7052 +AppleHidUsbKbUsageKeyPadNLck = 0x7053 +AppleHidUsbKbUsageKeyPadSlash = 0x7054 +AppleHidUsbKbUsageKeyPadAsterisk = 0x7055 +AppleHidUsbKbUsageKeyPadMinus = 0x7056 +AppleHidUsbKbUsageKeyPadPlus = 0x7057 +AppleHidUsbKbUsageKeyPadEnter = 0x7058 +AppleHidUsbKbUsageKeyPadOne = 0x7059 +AppleHidUsbKbUsageKeyPadTwo = 0x705A +AppleHidUsbKbUsageKeyPadThree = 0x705B +AppleHidUsbKbUsageKeyPadFour = 0x705C +AppleHidUsbKbUsageKeyPadFive = 0x705D +AppleHidUsbKbUsageKeyPadSix = 0x705E +AppleHidUsbKbUsageKeyPadSeven = 0x705F +AppleHidUsbKbUsageKeyPadEight = 0x7060 +AppleHidUsbKbUsageKeyPadNine = 0x7061 +AppleHidUsbKbUsageKeyPadIns = 0x7062 +AppleHidUsbKbUsageKeyPadDel = 0x7063 +AppleHidUsbKbUsageKeyPadNonUsBackslash = 0x7064 +AppleHidUsbKbUsageKeyPadApplication = 0x7065 +AppleHidUsbKbUsageKeyPadPower = 0x7066 +AppleHidUsbKbUsageKeyPadEquals = 0x7067 +AppleHidUsbKbUsageKeyF13 = 0x7068 +AppleHidUsbKbUsageKeyF14 = 0x7069 +AppleHidUsbKbUsageKeyF15 = 0x706A +AppleHidUsbKbUsageKeyF16 = 0x706B +AppleHidUsbKbUsageKeyF17 = 0x706C +AppleHidUsbKbUsageKeyF18 = 0x706D +AppleHidUsbKbUsageKeyF19 = 0x706E +AppleHidUsbKbUsageKeyF20 = 0x706F +AppleHidUsbKbUsageKeyF21 = 0x7070 +AppleHidUsbKbUsageKeyF22 = 0x7071 +AppleHidUsbKbUsageKeyF23 = 0x7072 +AppleHidUsbKbUsageKeyF24 = 0x7073 +AppleHidUsbKbUsageKeyExecute = 0x7074 +AppleHidUsbKbUsageKeyHelp = 0x7075 +AppleHidUsbKbUsageKeyMenu = 0x7076 +AppleHidUsbKbUsageKeySelect = 0x7077 +AppleHidUsbKbUsageKeyStop = 0x7078 +AppleHidUsbKbUsageKeyAgain = 0x7079 +AppleHidUsbKbUsageKeyUndo = 0x707A +AppleHidUsbKbUsageKeyCut = 0x707B +AppleHidUsbKbUsageKeyCopy = 0x707C +AppleHidUsbKbUsageKeyPaste = 0x707D +AppleHidUsbKbUsageKeyFind = 0x707E +AppleHidUsbKbUsageKeyMute = 0x707F +AppleHidUsbKbUsageKeyVolumeUp = 0x7080 +AppleHidUsbKbUsageKeyVolumeDown = 0x7081 +AppleHidUsbKbUsageLockingKeyCLock = 0x7082 +AppleHidUsbKbusageLockingKeyNLock = 0x7083 +AppleHidUsbKbUsageLockingKeySLock = 0x7084 +AppleHidUsbKbUsageKeyPadComma = 0x7085 +AppleHidUsbKbUsageKeyPadEqualSign = 0x7086 +AppleHidUsbKbUsageKeyInternational1 = 0x7087 +AppleHidUsbKbUsageKeyInternational2 = 0x7088 +AppleHidUsbKbUsageKeyInternational3 = 0x7089 +AppleHidUsbKbUsageKeyInternational4 = 0x708A +AppleHidUsbKbUsageKeyInternational5 = 0x708B +AppleHidUsbKbUsageKeyInternational6 = 0x708C +AppleHidUsbKbUsageKeyInternational7 = 0x708D +AppleHidUsbKbUsageKeyInternational8 = 0x708E +AppleHidUsbKbUsageKeyInternational9 = 0x708F +AppleHidUsbKbUsageKeyLang1 = 0x7090 +AppleHidUsbKbUsageKeyLang2 = 0x7091 +AppleHidUsbKbUsageKeyLang3 = 0x7092 +AppleHidUsbKbUsageKeyLang4 = 0x7093 +AppleHidUsbKbUsageKeyLang5 = 0x7094 +AppleHidUsbKbUsageKeyLang6 = 0x7095 +AppleHidUsbKbUsageKeyLang7 = 0x7096 +AppleHidUsbKbUsageKeyLang8 = 0x7097 +AppleHidUsbKbUsageKeyLang9 = 0x7098 +AppleHidUsbKbUsageKeyAlternateErase = 0x7099 +AppleHidUsbKbUsageKeySysReq = 0x709A +AppleHidUsbKbUsageKeyCancel = 0x709B +AppleHidUsbKbUsageKeyClear = 0x709C +AppleHidUsbKbUsageKeyPrior = 0x709D +AppleHidUsbKbUsageKeyReturn = 0x709E +AppleHidUsbKbUsageKeySeparator = 0x709F +AppleHidUsbKbUsageKeyOut = 0x70A0 +AppleHidUsbKbUsageKeyOper = 0x70A1 +AppleHidUsbKbUsageKeyClearAgain = 0x70A2 +AppleHidUsbKbUsageKeyCrSel = 0x70A3 +AppleHidUsbKbUsageKeyExSel = 0x70A4 +AppleHidUsbKbUsageKeyPadDoubleZero = 0x70B0 +AppleHidUsbKbUsageKeyTrippleZero = 0x70B1 +AppleHidUsbKbUsageKeyThousandsSeparator = 0x70B2 +AppleHidUsbKbUsageKeyDecimalSeparator = 0x70B3 +AppleHidUsbKbUsageKeyCurrencyUnit = 0x70B4 +AppleHidUsbKbUsageKeyCurrencySubUnit = 0x70B5 +AppleHidUsbKbUsageKeyPadLeftBracket = 0x70B6 +AppleHidUsbKbUsageKeyPadRightBracket = 0x70B7 +AppleHidUsbKbUsageKeyPadCurlyLeftBracket = 0x70B8 +AppleHidUsbKbUsageKeyPadCurlyRightBracket = 0x70B9 +AppleHidUsbKbUsageKeyPadTab = 0x70BA +AppleHidUsbKbUsageKeyPadBackspace = 0x70BB +AppleHidUsbKbUsageKeyPadA = 0x70BC +AppleHidUsbKbUsageKeyPadB = 0x70BD +AppleHidUsbKbUsageKeyPadC = 0x70BE +AppleHidUsbKbUsageKeyPadD = 0x70BF +AppleHidUsbKbUsageKeyPadE = 0x70C0 +AppleHidUsbKbUsageKeyPadF = 0x70C1 +AppleHidUsbKbUsageKeyPadXor = 0x70C2 +AppleHidUsbKbUsageKeyPadCaret = 0x70C3 +AppleHidUsbKbUsageKeyPadPercent = 0x70C4 +AppleHidUsbKbUsageKeyPadLeftAngleBracket = 0x70C5 +AppleHidUsbKbUsageKeyPadRightAngleBracket = 0x70C6 +AppleHidUsbKbUsageKeyPadBitwiseAnd = 0x70C7 +AppleHidUsbKbUsageKeyPadLogicalAnd = 0x70C8 +AppleHidUsbKbUsageKeyPadBitwiseOr = 0x70C9 +AppleHidUsbKbUsageKeyPadLogicalOr = 0x70CA +AppleHidUsbKbUsageKeyPadColon = 0x70CB +AppleHidUsbKbUsageKeyPadHash = 0x70CC +AppleHidUsbKbUsageKeyPadSpace = 0x70CD +AppleHidUsbKbUsageKeyPadAt = 0x70CE +AppleHidUsbKbUsageKeyPadExclamationMark = 0x70CF +AppleHidUsbKbUsageKeyPadMemoryStore = 0x70D0 +AppleHidUsbKbUsageKeyPadMemoryRecall = 0x70D1 +AppleHidUsbKbUsageKeyPadMemoryClear = 0x70D2 +AppleHidUsbKbUsageKeyPadMemoryAdd = 0x70D3 +AppleHidUsbKbUsageKeyPadMemorySubtract = 0x70D4 +AppleHidUsbKbUsageKeyPadMemoryMultiply = 0x70D5 +AppleHidUsbKbUsageKeyPadMemoryDivide = 0x70D6 +AppleHidUsbKbUsageKeyPadSign = 0x70D7 +AppleHidUsbKbUsageKeyPadClear = 0x70D8 +AppleHidUsbKbUsageKeyPadClearEntry = 0x70D9 +AppleHidUsbKbUsageKeyPadBinary = 0x70DA +AppleHidUsbKbUsageKeyPadOctal = 0x70DB +AppleHidUsbKbUsageKeyPadDecimal = 0x70DC +AppleHidUsbKbUsageKeyPadHexadecimal = 0x70DD diff --git a/Protocols/AptioInputFix/Pointer/AIM.c b/Protocols/AptioInputFix/Pointer/AIM.c new file mode 100644 index 000000000..c3082e063 --- /dev/null +++ b/Protocols/AptioInputFix/Pointer/AIM.c @@ -0,0 +1,565 @@ +/** @file + AmiEfiPointer to EfiPointer translator. + +Copyright (c) 2016, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIM.h" + +#include +#include +#include + +STATIC AMI_SHIM_POINTER mAmiShimPointer; + +STATIC UINT8 gIndex = 0; +STATIC UINT8 gXCounts[5] = {0, 0, 0, 0, 0}; +STATIC UINT8 gYCounts[5] = {0, 0, 0, 0, 0}; + +UINT8 +EFIAPI +Abs ( + IN INT32 Value + ) +{ + return (UINT8)(Value < 0 ? -Value : Value); +} + +INT32 +EFIAPI +InternalClamp ( + IN INT32 Value, + IN INT32 Min, + IN INT32 Max + ) +{ + if (Value > Max) { + return Max; + } else if (Value < Min) { + return Min; + } + return Value; +} + +VOID +EFIAPI +AmiShimPointerFilterOut ( + IN OUT UINT8 *AbsX, + IN OUT UINT8 *AbsY, + IN OUT INT32 *X, + IN OUT INT32 *Y + ) +{ + UINT8 Index; + + if (*AbsX == 0) { + gXCounts[gIndex] = 1; + } else if (*AbsX <= 2) { + for (Index = 0; Index < 5; Index++) { + if (!gXCounts[Index]) + break; + } + + if (Index == 5) { + *AbsX = 0; + *X = 0; + } + + gXCounts[gIndex] = 0; + } + + if (*AbsY == 0) { + gYCounts[gIndex] = 1; + } else if (*AbsY <= 2) { + for (Index = 0; Index < 5; Index++) { + if (!gYCounts[Index]) + break; + } + + if (Index == 5) { + *AbsY = 0; + *Y = 0; + } + + gYCounts[gIndex] = 0; + } + + gIndex = gIndex < 4 ? gIndex + 1 : 0; +} + +#ifdef AMI_SHIM_POINTER_AMI_SMOOTHING + +STATIC UINT8 gAbsRange[8] = {42, 36, 30, 24, 18, 12, 6, 1}; +STATIC INT32 gMultipliers[8] = {8, 7, 6, 5, 4, 3, 2, 1}; +STATIC INT32 gIndices[8] = {2, 2, 2, 2, 2, 2, 2, 0}; + +INT32 +AmiShimPointerScale ( + IN INT32 Value, + IN UINT8 AbsValue + ) +{ + UINT8 TmpIndex; + + for (TmpIndex = 0; TmpIndex < 8; TmpIndex++) { + if (AbsValue >= gAbsRange[TmpIndex]) { + break; + } + } + + if (TmpIndex != 8) { + if (Value >= 0) { + Value = gIndices[TmpIndex] + Value * gMultipliers[TmpIndex]; + } else { + Value = Value * gMultipliers[TmpIndex] - gIndices[TmpIndex]; + } + } + + return Value; +} + +VOID +EFIAPI +AmiShimPointerSmooth ( + IN OUT INT32 *X, + IN OUT INT32 *Y, + IN OUT INT32 *Z + ) +{ + UINT8 AbsX, AbsY; + + *X = Clamp (*X, -16, 16); + *Y = Clamp (*Y, -16, 16); + // According to AMI it should not be reported + *Z = 0; + AbsX = Abs (*X); + AbsY = Abs (*Y); + + if (*X == 0 && *Y == 0) { + return; + } + + AmiShimPointerFilterOut (&AbsX, &AbsY, X, Y); + + *X = AmiShimPointerScale(*X, AbsX); + *Y = AmiShimPointerScale(*Y, AbsY); + + *X *= AIM_SCALE_FACTOR; + *Y *= AIM_SCALE_FACTOR; +} + +#else + +STATIC UINT8 gAbsRange[4] = {80, 64, 48, 1}; +STATIC INT32 gMultipliers[4] = {4, 3, 2, 1}; + +VOID +EFIAPI +AmiShimPointerBoostValue ( + IN OUT INT32 *Value, + IN INT32 AbsValue + ) +{ + UINTN Index; + for (Index = 0; Index < 4; Index++) { + if (gAbsRange[Index] > AbsValue) { + *Value *= gMultipliers[Index]; + return; + } + } +} + +VOID +EFIAPI +AmiShimPointerSmooth ( + IN OUT INT32 *X, + IN OUT INT32 *Y, + IN OUT INT32 *Z + ) +{ + UINT8 AbsX, AbsY; + + *X = InternalClamp(*X, -96, 96); + *Y = InternalClamp(*Y, -96, 96); + *Z = 0; + + AbsX = Abs (*X); + AbsY = Abs (*Y); + + if (*X == 0 && *Y == 0) { + return; + } + + AmiShimPointerFilterOut (&AbsX, &AbsY, X, Y); + + AmiShimPointerBoostValue(X, AbsX); + AmiShimPointerBoostValue(Y, AbsY); +} + +#endif + +VOID +EFIAPI +AmiShimPointerPositionHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + AMI_SHIM_POINTER_INSTANCE *Pointer; + AMI_POINTER_POSITION_STATE_DATA PositionState; + + // Do not poll until somebody actually starts using the mouse + // Otherwise first move will be quite random + if (!mAmiShimPointer.UsageStarted) { + return; + } + + // This is important to do quickly and separately, because AMI stores positioning data in INT8. + // If we move the mouse quickly it will overflow and return invalid data. + for (Index = 0; Index < AIM_MAX_POINTERS; Index++) { + Pointer = &mAmiShimPointer.PointerMap[Index]; + if (Pointer->DeviceHandle != NULL) { + PositionState.Changed = 0; + Pointer->EfiPointer->GetPositionState (Pointer->EfiPointer, &PositionState); + if (PositionState.Changed == 1) { + if (PositionState.Absolute == 0) { + DEBUG ((DEBUG_VERBOSE, "Position: %d %d %d %d\n", + PositionState.Changed, PositionState.PositionX, PositionState.PositionY, PositionState.PositionZ)); + AmiShimPointerSmooth(&PositionState.PositionX, &PositionState.PositionY, &PositionState.PositionZ); + if (PositionState.PositionX != 0 || PositionState.PositionY != 0 || PositionState.PositionZ != 0) { + Pointer->PositionX += PositionState.PositionX; + Pointer->PositionY += PositionState.PositionY; + Pointer->PositionZ += PositionState.PositionZ; + Pointer->PositionChanged = TRUE; + } + } else { + //FIXME: Add support for devices with absolute positioning + } + } + } + } +} + +EFI_STATUS +EFIAPI +AmiShimPointerUpdateState ( + IN AMI_SHIM_POINTER_INSTANCE *Pointer, + OUT EFI_SIMPLE_POINTER_STATE *State + ) +{ + AMI_POINTER_BUTTON_STATE_DATA ButtonState; + + if (!mAmiShimPointer.UsageStarted) { + mAmiShimPointer.UsageStarted = TRUE; + AmiShimPointerPositionHandler(NULL, NULL); + } + + ButtonState.Changed = 0; + Pointer->EfiPointer->GetButtonState (Pointer->EfiPointer, &ButtonState); + + if (ButtonState.Changed == 0 && Pointer->PositionChanged == 0) { + return EFI_NOT_READY; + } + + DEBUG ((DEBUG_VERBOSE, "Button: %d %d %d %d, Position: %d %d %d %d\n", + ButtonState.Changed, ButtonState.LeftButton, ButtonState.MiddleButton, ButtonState.RightButton, + Pointer->PositionChanged, Pointer->PositionX, Pointer->PositionY, Pointer->PositionZ)); + + if (ButtonState.Changed) { + State->LeftButton = ButtonState.LeftButton; + State->RightButton = ButtonState.RightButton; + } else { + State->LeftButton = State->RightButton = FALSE; + } + + if (Pointer->PositionChanged) { + State->RelativeMovementX = Pointer->PositionX; + State->RelativeMovementY = Pointer->PositionY; + State->RelativeMovementZ = Pointer->PositionZ; + } else { + State->RelativeMovementX = State->RelativeMovementY = State->RelativeMovementZ = 0; + } + + Pointer->PositionChanged = 0; + Pointer->PositionX = Pointer->PositionY = Pointer->PositionZ = 0; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AmiShimPointerGetState ( + IN EFI_SIMPLE_POINTER_PROTOCOL *This, + IN OUT EFI_SIMPLE_POINTER_STATE *State + ) +{ + EFI_STATUS Status; + UINTN Index; + AMI_SHIM_POINTER_INSTANCE *Pointer; + + if (This == NULL || State == NULL) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0, Pointer = mAmiShimPointer.PointerMap; Index < AIM_MAX_POINTERS; Index++, Pointer++) { + if (Pointer->SimplePointer == This) { + break; + } + } + + if (Index != AIM_MAX_POINTERS) { + Status = AmiShimPointerUpdateState (Pointer, State); + + if (!EFI_ERROR (Status)) { + if (State->RelativeMovementX != 0 || + State->RelativeMovementY != 0 || + State->RelativeMovementZ != 0) { + DEBUG ((DEBUG_VERBOSE, "Received[%p] %d %d %d <%d, %d>\n", This, State->RelativeMovementX, State->RelativeMovementY, State->RelativeMovementZ, + State->LeftButton, State->RightButton)); + } else { + DEBUG ((DEBUG_VERBOSE, "Received[%p] %d %d %d\n", This, State->RelativeMovementX, State->RelativeMovementY, State->RelativeMovementZ)); + } + } + } else { + DEBUG ((DEBUG_VERBOSE, "Received unknown this %p\n", This)); + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} + +EFI_STATUS +EFIAPI +AmiShimPointerTimerSetup ( + VOID + ) +{ + EFI_STATUS Status; + + if (mAmiShimPointer.TimersInitialised) { + return EFI_ALREADY_STARTED; + } + + Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, AmiShimPointerPositionHandler, NULL, &mAmiShimPointer.PositionEvent); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AmiShimPointerPositionHandler event creation failed %d\n", Status)); + return Status; + } + + Status = gBS->SetTimer (mAmiShimPointer.PositionEvent, TimerPeriodic, AIM_POSITION_POLL_INTERVAL); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AmiShimPointerPositionHandler timer setting failed %d\n", Status)); + gBS->CloseEvent (mAmiShimPointer.PositionEvent); + return Status; + } + + mAmiShimPointer.TimersInitialised = TRUE; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AmiShimPointerTimerUninstall ( + VOID + ) +{ + EFI_STATUS Status; + + if (!mAmiShimPointer.TimersInitialised) { + return EFI_SUCCESS; + } + + if (mAmiShimPointer.PositionEvent != NULL) { + Status = gBS->SetTimer (mAmiShimPointer.PositionEvent, TimerCancel, 0); + if (!EFI_ERROR (Status)) { + gBS->CloseEvent (mAmiShimPointer.PositionEvent); + mAmiShimPointer.PositionEvent = NULL; + } else { + DEBUG((DEBUG_INFO, "AmiShimPointerPositionHandler timer unsetting failed %d\n", Status)); + } + } + + mAmiShimPointer.TimersInitialised = FALSE; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AmiShimPointerInstallOnHandle ( + IN EFI_HANDLE DeviceHandle, + IN AMI_EFIPOINTER_PROTOCOL *EfiPointer, + IN EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer + ) +{ + UINTN Index; + AMI_SHIM_POINTER_INSTANCE *Pointer; + AMI_SHIM_POINTER_INSTANCE *FreePointer; + + FreePointer = NULL; + + for (Index = 0; Index < AIM_MAX_POINTERS; Index++) { + Pointer = &mAmiShimPointer.PointerMap[Index]; + if (Pointer->DeviceHandle == NULL && FreePointer == NULL) { + FreePointer = Pointer; + } else if (Pointer->DeviceHandle == DeviceHandle) { + return EFI_ALREADY_STARTED; + } + } + + if (FreePointer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((DEBUG_INFO, "Installed onto %X\n", DeviceHandle)); + FreePointer->DeviceHandle = DeviceHandle; + FreePointer->EfiPointer = EfiPointer; + FreePointer->SimplePointer = SimplePointer; + if (FreePointer->SimplePointer->GetState == AmiShimPointerGetState) { + FreePointer->OriginalGetState = NULL; + DEBUG ((DEBUG_INFO, "Function is already hooked\n")); + } else { + FreePointer->OriginalGetState = FreePointer->SimplePointer->GetState; + FreePointer->SimplePointer->GetState = AmiShimPointerGetState; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AmiShimPointerInstall ( + VOID + ) +{ + EFI_STATUS Status; + UINTN NoHandles; + EFI_HANDLE *Handles; + UINTN Index; + BOOLEAN Installed; + AMI_EFIPOINTER_PROTOCOL *EfiPointer; + EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer; + + Status = gBS->LocateHandleBuffer (ByProtocol, &gAmiEfiPointerProtocolGuid, NULL, &NoHandles, &Handles); + if (EFI_ERROR(Status)) { + return EFI_NOT_FOUND; + } + + DEBUG ((DEBUG_INFO, "Found %d Handles located by protocol\n", NoHandles)); + + Installed = FALSE; + + for (Index = 0; Index < NoHandles; Index++) { + Status = gBS->HandleProtocol (Handles[Index], &gAmiEfiPointerProtocolGuid, (VOID **)&EfiPointer); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_INFO, "Handle %d has no AmiEfiPointerl %d\n", Index, Status)); + continue; + } + + Status = gBS->HandleProtocol (Handles[Index], &gEfiSimplePointerProtocolGuid, (VOID **)&SimplePointer); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_INFO, "Handle %d has no EfiSimplePointer %d\n", Index, Status)); + continue; + } + + Status = AmiShimPointerInstallOnHandle (Handles[Index], EfiPointer, SimplePointer); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_INFO, "Handle %d failed to get installed %d\n", Index, Status)); + continue; + } + + Installed = TRUE; + } + + gBS->FreePool (Handles); + + if (!Installed) { + return EFI_NOT_FOUND; + } + + if (!mAmiShimPointer.TimersInitialised) { + return AmiShimPointerTimerSetup(); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AmiShimPointerUninstall ( + VOID + ) +{ + UINTN Index; + AMI_SHIM_POINTER_INSTANCE *Pointer; + + AmiShimPointerTimerUninstall(); + + for (Index = 0; Index < AIM_MAX_POINTERS; Index++) { + Pointer = &mAmiShimPointer.PointerMap[Index]; + if (Pointer->DeviceHandle != NULL) { + Pointer->SimplePointer->GetState = Pointer->OriginalGetState; + gBS->DisconnectController(Pointer->DeviceHandle, NULL, NULL); + Pointer->DeviceHandle = NULL; + } + } + + return EFI_SUCCESS; +} + +VOID +EFIAPI +AmiShimPointerArriveHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + AmiShimPointerInstall(); +} + +EFI_STATUS +AIMInit ( + VOID + ) +{ + EFI_STATUS Status; + VOID *Registration; + + Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, AmiShimPointerArriveHandler, NULL, &mAmiShimPointer.ProtocolArriveEvent); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AmiShimPointerArriveHandler event creation failed %d\n", Status)); + return Status; + } + + // EfiSimplePointer gets installed after AMI proprietary protocol + Status = gBS->RegisterProtocolNotify (&gEfiSimplePointerProtocolGuid, mAmiShimPointer.ProtocolArriveEvent, &Registration); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AmiShimProtocolArriveHandler protocol registration failed %d\n", Status)); + gBS->CloseEvent (mAmiShimPointer.ProtocolArriveEvent); + return Status; + } + + return AmiShimPointerInstall(); +} + +EFI_STATUS +AIMExit ( + VOID + ) +{ + return AmiShimPointerUninstall(); +} diff --git a/Protocols/AptioInputFix/Pointer/AIM.h b/Protocols/AptioInputFix/Pointer/AIM.h new file mode 100644 index 000000000..4d022022b --- /dev/null +++ b/Protocols/AptioInputFix/Pointer/AIM.h @@ -0,0 +1,67 @@ +/** @file + Header file for AmiEfiPointer to EfiPointer translator. + +Copyright (c) 2016, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef AIM_SELF_H +#define AIM_SELF_H + +#include +#include +#include + +// +// Taken from APTIO IV Z87. +// +#define AIM_MAX_POINTERS 6 + +// +// APTIO IV Z87 has 66666 here. +// Slightly lower resolution results in sometimes overflowng mouse. +// +#define AIM_POSITION_POLL_INTERVAL 66666 + +// +// Position movement boost. +// +#define AIM_SCALE_FACTOR 1 + +typedef struct { + EFI_HANDLE DeviceHandle; + AMI_EFIPOINTER_PROTOCOL *EfiPointer; + EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer; + EFI_SIMPLE_POINTER_GET_STATE OriginalGetState; + BOOLEAN PositionChanged; + INT32 PositionX; + INT32 PositionY; + INT32 PositionZ; +} AMI_SHIM_POINTER_INSTANCE; + +typedef struct { + BOOLEAN TimersInitialised; + BOOLEAN UsageStarted; + EFI_EVENT ProtocolArriveEvent; + EFI_EVENT PositionEvent; + AMI_SHIM_POINTER_INSTANCE PointerMap[AIM_MAX_POINTERS]; +} AMI_SHIM_POINTER; + +EFI_STATUS +AIMInit ( + VOID + ); + +EFI_STATUS +AIMExit ( + VOID + ); + +#endif diff --git a/Protocols/AptioInputFix/Timer/AIT.c b/Protocols/AptioInputFix/Timer/AIT.c new file mode 100644 index 000000000..8e1afd834 --- /dev/null +++ b/Protocols/AptioInputFix/Timer/AIT.c @@ -0,0 +1,87 @@ +/** @file + Timer booster + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "AIT.h" + +#include +#include +#include + +STATIC UINTN mOriginalTimerPeriod; +STATIC EFI_TIMER_ARCH_PROTOCOL *mTimerProtocol; + +EFI_STATUS +AITInit ( + VOID + ) +{ + EFI_STATUS Status; + // + // Refresh rate needs to be increased to poll mouse and keyboard frequently enough + // + Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **)&mTimerProtocol); + if (!EFI_ERROR (Status)) { + Status = mTimerProtocol->GetTimerPeriod (mTimerProtocol, &mOriginalTimerPeriod); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AIFTimerBoostInit Current timer is %u\n", mOriginalTimerPeriod)); + if (mOriginalTimerPeriod > AIT_TIMER_PERIOD) { + Status = mTimerProtocol->SetTimerPeriod (mTimerProtocol, AIT_TIMER_PERIOD); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "AIFTimerBoostInit changed period %d to %d\n", + mOriginalTimerPeriod, AIT_TIMER_PERIOD)); + } else { + DEBUG ((DEBUG_INFO, "AIFTimerBoostInit failed to change period %d to %d, error - %r\n", + mOriginalTimerPeriod, AIT_TIMER_PERIOD, Status)); + mTimerProtocol = NULL; + } + } else { + mTimerProtocol = NULL; + } + } else { + DEBUG ((DEBUG_INFO, "AIFTimerBoostInit failed to obtain previous period - %r\n", Status)); + } + } else { + DEBUG ((DEBUG_INFO, "AIFTimerBoostInit gEfiTimerArchProtocolGuid not found - %r\n", Status)); + } + + return Status; +} + +EFI_STATUS +AITExit ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (mTimerProtocol != NULL) { + // + // You are not allowed to call this on APTIO IV, as it results in an interrupt with 0x0 pointer + // handler during XNU boot. + // + // Status = mTimerProtocol->SetTimerPeriod (mTimerProtocol, mOriginalTimerPeriod); + // if (!EFI_ERROR (Status)) { + // DEBUG ((DEBUG_INFO, "AmiShimTimerBoostExit changed period %d to %d\n", + // AIT_TIMER_PERIOD, mOriginalTimerPeriod)); + // } else { + // DEBUG ((DEBUG_INFO, "AmiShimTimerBoostExit failed to change period %d to %d, error - %r\n", + // AIT_TIMER_PERIOD, mOriginalTimerPeriod, Status)); + // } + mTimerProtocol = NULL; + } + + return Status; +} diff --git a/Protocols/AptioInputFix/Timer/AIT.h b/Protocols/AptioInputFix/Timer/AIT.h new file mode 100644 index 000000000..ad6c10c92 --- /dev/null +++ b/Protocols/AptioInputFix/Timer/AIT.h @@ -0,0 +1,36 @@ +/** @file + Timer booster + +Copyright (c) 2018, vit9696. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef AIT_SELF_H +#define AIT_SELF_H + +#include + +// +// AppleEvent prefers 10 ms, and APTIO IV Z87 needs 5 ms for mouse. +// Pick the lower one, as they are multiple of each other. +// +#define AIT_TIMER_PERIOD EFI_TIMER_PERIOD_MILLISECONDS(5) + +EFI_STATUS +AITInit ( + VOID + ); + +EFI_STATUS +AITExit ( + VOID + ); + +#endif diff --git a/buildmc.bat b/buildmc.bat new file mode 100644 index 000000000..e44edffec --- /dev/null +++ b/buildmc.bat @@ -0,0 +1,84 @@ +@echo off +rem # cbuild wrapper +rem # 2019 cecekpawon + +set "CURRENT_DIR=%~dp0" +cd /D "%CURRENT_DIR%" + +rem # direct set all required vars here, or pass it as script args, "cbuild -h" for helps +rem # cbuild should contain all default value sets as fallback +rem # todo: more desc / comment on each arg + +:: +:: SCRIPT +:: + +set EDK_SHELL_BIN_MODE= +set GEN_PAGE=0 +set NO_BOOTLOADERS=1 +set NO_BOOTSECTORS=1 +set NO_COPY_BIN=1 +set REVISION=0000 + +:: +:: PATH +:: + +set "CUSTOM_CONF_PATH=" +set "CYGWIN_HOME=c:\cygwin" +set "IASL_PREFIX=c:\ASL\" +rem set "NASM_PREFIX=%CYGWIN_HOME%\bin\" +set "PACKAGES_PATH=" +rem set "PYTHONHOME=c:\Python3" +set "PYTHONHOME=d:\Program File\Python37" +rem set "PYTHON_FREEZER_PATH=%PYTHONHOME%\Scripts" +set "WORKSPACE=" + +:: +:: EDK SETUP +:: + +set BUILD_IA32= +set BUILD_TARGET=RELEASE +set BUILD_X64= +set DSC_FILE= +set THREAD_NUMBER=%NUMBER_OF_PROCESSORS% +rem set TOOLCHAIN=VS2015x86 +set TOOLCHAIN=VS2017 + +:: +:: MACROS +:: + +set AMD_SUPPORT=1 +set ANDX86=1 +set DEBUG_ON_SERIAL_PORT=0 +set DISABLE_LTO=0 +set DISABLE_UDMA_SUPPORT=0 +set DISABLE_USB_CONTROLLERS=0 +set DISABLE_USB_SUPPORT=0 +set ENABLE_PS2MOUSE_LEGACYBOOT=0 +set ENABLE_SECURE_BOOT=0 +set ENABLE_VBIOS_PATCH_CLOVEREFI=0 +set EXIT_USBKB=0 +set HAVE_LEGACY_EMURUNTIMEDXE=0 +set INCLUDE_DP=0 +set INCLUDE_TFTP_COMMAND=0 +set LODEPNG=1 +set NO_CLOVER_SHELL=0 +set NO_SHELL_PROFILES=0 +set ONLY_SATA_0=0 +set OPENSSL_VERSION= +set REAL_NVRAM=1 +set SKIP_FLASH=0 +set USE_BIOS_BLOCKIO=0 +set USE_LOW_EBDA=0 + + +rem -D AMD_SUPPORT -D ANDX86 -D DEBUG_ON_SERIAL_PORT -D DISABLE_LTO -D DISABLE_UDMA_SUPPORT +rem -D DISABLE_USB_CONTROLLERS -D DISABLE_USB_SUPPORT -D ENABLE_PS2MOUSE_LEGACYBOOT -D ENABLE_SECURE_BOOT +rem -D ENABLE_VBIOS_PATCH_CLOVEREFI -D EXIT_USBKB -D HAVE_LEGACY_EMURUNTIMEDXE -D INCLUDE_DP -D INCLUDE_TFTP_COMMAND +rem -D NO_SHELL_PROFILES -D ONLY_SATA_0 -D REAL_NVRAM -D SKIP_FLASH -D USE_BIOS_BLOCKIO -D USE_LOW_EBDA -D NO_CLOVER_SHELL + +call cbuild.bat -a X64 cleanall +rem call cbuild.bat -h