restructure sources, preliminary include AptioMemoryFix from OC

Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
This commit is contained in:
Sergey Isakov 2019-10-04 16:19:40 +03:00
parent 2d1649e265
commit f35acfa5ab
107 changed files with 21045 additions and 18 deletions

View File

@ -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.
#
# <PcdsFixedAtBuild>
# gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x07
# gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xFFFFFFFF
# <LibraryClasses>
# SerialPortLib|MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf
# DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
# DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
#}
<PcdsFixedAtBuild>
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x07
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xFFFFFFFF
<LibraryClasses>
SerialPortLib|MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf
DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
}
!else
MemoryFix/OsxAptioFixDrv/OsxAptioFixDrv.inf
!endif
###################################################################################################
#

View File

@ -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 } }
################################################################################

21
CppProperties.json Normal file
View File

@ -0,0 +1,21 @@
{
"configurations": [
{
"inheritEnvironments": [
"msvc_x64"
],
"name": "x64-Release",
"includePath": [
"${env.INCLUDE}",
"${workspaceRoot}\\**"
],
"defines": [
"WIN32",
"NDEBUG",
"UNICODE",
"_UNICODE"
],
"intelliSenseMode": "windows-msvc-x64"
}
]
}

View File

@ -0,0 +1,150 @@
/** @file
Copyright (C) 2014 - 2018, Download-Fritz. All rights reserved.<BR>
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 <IndustryStandard/AppleMachoImage.h>
///
/// 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

File diff suppressed because it is too large Load Diff

761
Include/Library/MachoLib.h Normal file
View File

@ -0,0 +1,761 @@
/** @file
Provides Mach-O parsing helper functions.
Copyright (c) 2016 - 2018, Download-Fritz. All rights reserved.<BR>
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 <IndustryStandard/AppleMachoImage.h>
///
/// 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_

View File

@ -0,0 +1,59 @@
/** @file
Header file for AMI EfiKeycode protocol definitions.
Copyright (c) 2016, vit9696. All rights reserved.<BR>
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

View File

@ -0,0 +1,68 @@
/** @file
Header file for AMI EfiPointer protocol definitions.
Copyright (c) 2016, vit9696. All rights reserved.<BR>
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

View File

@ -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

View File

@ -0,0 +1,620 @@
/** @file
Provides services for C++ symbols.
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
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 <Base.h>
#include <IndustryStandard/AppleMachoImage.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/OcGuardLib.h>
#include <Library/OcMachoLib.h>
#include <Library/OcStringLib.h>
#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;
}

1449
Library/MachoLib/Header.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
## @file
# Copyright (C) 2016 - 2017, Download-Fritz. All rights reserved.<BR>
# 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

View File

@ -0,0 +1,77 @@
/**
Private data of OcMachoLib.
Copyright (C) 2018, Download-Fritz. All rights reserved.<BR>
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 <IndustryStandard/AppleMachoImage.h>
#include <Library/OcMachoLib.h>
/**
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_

View File

@ -0,0 +1,158 @@
/** @file
Provides services for Relocations.
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
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 <Base.h>
#include <IndustryStandard/AppleMachoImage.h>
#include <Library/DebugLib.h>
#include <Library/OcMachoLib.h>
#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
);
}

640
Library/MachoLib/Symbols.c Normal file
View File

@ -0,0 +1,640 @@
/** @file
Provides services for symbols.
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
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 <Base.h>
#include <IndustryStandard/AppleMachoImage.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/OcGuardLib.h>
#include <Library/OcMachoLib.h>
#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;
}

View File

@ -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 <Library/OcGuardLib.h>
//
// 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;
}

View File

@ -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)
{
}
}
}

View File

@ -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 <Library/OcGuardLib.h>
//
// 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);
}

View File

@ -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

View File

@ -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 <Library/OcGuardLib.h>
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;
}

1741
Library/OcGuardLib/Ubsan.c Normal file

File diff suppressed because it is too large Load Diff

207
Library/OcGuardLib/Ubsan.h Normal file
View File

@ -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 <Library/DebugLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
// 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

View File

@ -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 <sys/types.h>
#endif
#ifdef PRINTF_LONG_LONG_SUPPORT
# define PRINTF_LONG_SUPPORT
#endif
/* __SIZEOF_<type>__ 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

View File

@ -0,0 +1,28 @@
## @file
# Copyright (C) 2017, vit9696. All rights reserved.<BR>
#
# 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 } }

View File

@ -0,0 +1,97 @@
/**
UEFI driver for enabling loading of macOS without memory relocation.
by dmazar
**/
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Guid/GlobalVariable.h>
#include <Protocol/AptioMemoryFix.h>
#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;
}

View File

@ -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

View File

@ -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 <Library/BaseLib.h>
/** 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

View File

@ -0,0 +1,149 @@
/**
Methods for finding, checking and fixing boot args
by dmazar
**/
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#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;
}

View File

@ -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 <IndustryStandard/AppleBootArgs.h>
#include <Library/OcMiscLib.h>
/** 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

View File

@ -0,0 +1,559 @@
/**
Methods for setting callback jump from kernel entry point, callback, fixes to kernel boot image.
by dmazar
**/
#include <IndustryStandard/AppleHibernate.h>
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcDevicePathLib.h>
#include <Library/OcMachoLib.h>
#include <Library/OcMiscLib.h>
#include <Library/OcStringLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DevicePathLib.h>
#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;
}

View File

@ -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 <Protocol/LoadedImage.h>
//
// 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

View File

@ -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

View File

@ -0,0 +1,664 @@
/**
Allows to choose a random KASLR slide offset,
when some offsets cannot be used.
by Download-Fritz & vit9696
**/
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcDeviceTreeLib.h>
#include <Library/OcMiscLib.h>
#include <Library/PrintLib.h>
#include <Library/RngLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <IndustryStandard/AppleCsrConfig.h>
#include <Register/Microcode.h>
#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
}

View File

@ -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

View File

@ -0,0 +1,366 @@
/**
MemoryMap helper functions.
by dmazar
**/
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcMiscLib.h>
#include <Library/PrintLib.h>
#include <Library/DevicePathLib.h>
#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;
}

View File

@ -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

View File

@ -0,0 +1,358 @@
/**
Runtime Services Wrappers.
by Download-Fritz & vit9696
**/
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcMiscLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Guid/OcVariables.h>
#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;
}

View File

@ -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

View File

@ -0,0 +1,638 @@
/**
Temporary BS and RT overrides for boot.efi support.
Unlike RtShims they do not affect the kernel.
by dmazar
**/
#include <IndustryStandard/AppleHibernate.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcMiscLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Protocol/LoadedImage.h>
#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;
}

View File

@ -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

View File

@ -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) );
}
/* ------------------------------------------------------------------------ */

View File

@ -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 */

View File

@ -0,0 +1,548 @@
/**
Virtual memory functions.
by dmazar
**/
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/OcDebugLogLib.h>
#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());
}

View File

@ -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.<BR>
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

View File

@ -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

View File

@ -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):

View File

@ -0,0 +1,67 @@
/** @file
Ami input translators.
Copyright (c) 2016, vit9696. All rights reserved.<BR>
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 <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
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;
}

View File

@ -0,0 +1,89 @@
## @file
# Copyright (c) 2016, vit9696. All rights reserved.<BR>
#
# 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

View File

@ -0,0 +1,250 @@
/** @file
AmiEfiKeycode to KeyMapDb translator.
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
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;
}

View File

@ -0,0 +1,107 @@
/** @file
Header file for AmiEfiKeycode to KeyMapDb translator.
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Library/UefiLib.h>
//
// 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

View File

@ -0,0 +1,81 @@
/** @file
Key code ring
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
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;
}
}

View File

@ -0,0 +1,57 @@
/** @file
Key code ring
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Protocol/AmiKeycode.h>
//
// 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,198 @@
/** @file
Key shimming code
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
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);
}
}

View File

@ -0,0 +1,72 @@
/** @file
Key shimming code
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Protocol/AmiKeycode.h>
#include <Protocol/SimpleTextIn.h>
#include <Protocol/SimpleTextInEx.h>
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

View File

@ -0,0 +1,204 @@
/** @file
Key provider
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
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;
}
}

View File

@ -0,0 +1,68 @@
/** @file
Key provider
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Protocol/AmiKeycode.h>
#include <Protocol/SimpleTextIn.h>
#include <Protocol/SimpleTextInEx.h>
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

View File

@ -0,0 +1,191 @@
/** @file
Key consumer
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
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));
}
}

View File

@ -0,0 +1,114 @@
/** @file
Key consumer
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <IndustryStandard/AppleHid.h>
#include <Protocol/AppleKeyMapDatabase.h>
#include <Protocol/AmiKeycode.h>
//
// 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

View File

@ -0,0 +1,199 @@
/** @file
Key translator
Copyright (c) 2018, vit9696. All rights reserved.<BR>
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 <Library/DebugLib.h>
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));
}
}

Some files were not shown because too many files have changed in this diff Show More