mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
515 lines
13 KiB
C
515 lines
13 KiB
C
/** @file
|
|
Copyright (C) 2022, Marvin Haeuser. All rights reserved.
|
|
Copyright (C) 2022, PMheart. All rights reserved.
|
|
|
|
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/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcTemplateLib.h>
|
|
#include <Library/OcSerializeLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/OcAppleKernelLib.h>
|
|
|
|
#include <Library/OcConfigurationLib.h>
|
|
#include <Library/OcMainLib.h>
|
|
|
|
#include <UserFile.h>
|
|
|
|
#define OC_USER_FULL_PATH_MAX_SIZE 256
|
|
|
|
STATIC CHAR8 mFullPath[OC_USER_FULL_PATH_MAX_SIZE] = { 0 };
|
|
STATIC UINTN mRootPathLen = 0;
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
UserSetRootPath (
|
|
IN CONST CHAR8 *RootPath
|
|
)
|
|
{
|
|
UINTN RootPathLen;
|
|
|
|
RootPathLen = AsciiStrLen (RootPath);
|
|
//
|
|
// Preserve 2 bytes for '/' and '\0'.
|
|
//
|
|
if (RootPathLen > OC_USER_FULL_PATH_MAX_SIZE - 2) {
|
|
DEBUG ((DEBUG_ERROR, "RootPath is too long!\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
AsciiStrCpyS (mFullPath, sizeof (mFullPath) - 1, RootPath);
|
|
//
|
|
// If passed without '/' in the end, append it.
|
|
//
|
|
if (mFullPath[RootPathLen - 1] != '/') {
|
|
mFullPath[RootPathLen] = '/';
|
|
mFullPath[RootPathLen + 1] = '\0';
|
|
mRootPathLen = RootPathLen + 1;
|
|
} else {
|
|
mRootPathLen = RootPathLen;
|
|
}
|
|
|
|
DEBUG ((DEBUG_ERROR, "Root Path: %a\n", mFullPath));
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
UINT8 *
|
|
UserReadFileFromRoot (
|
|
IN CONST CHAR8 *FileName,
|
|
OUT UINT32 *Size
|
|
)
|
|
{
|
|
AsciiStrCpyS (&mFullPath[mRootPathLen], sizeof (mFullPath) - mRootPathLen - 1, FileName);
|
|
DEBUG ((DEBUG_ERROR, "Full path: %a\n", mFullPath));
|
|
return UserReadFile (mFullPath, Size);
|
|
}
|
|
|
|
STATIC BOOLEAN FailedToProcess = FALSE;
|
|
STATIC UINT32 KernelVersion = 0;
|
|
|
|
STATIC EFI_FILE_PROTOCOL NilFileProtocol;
|
|
|
|
STATIC UINT8 *mPrelinked = NULL;
|
|
STATIC UINT32 mPrelinkedSize = 0;
|
|
|
|
//
|
|
// TODO: Windows portability.
|
|
//
|
|
STATIC
|
|
VOID
|
|
AsciiHostSlashes (
|
|
IN OUT CHAR8 *String
|
|
)
|
|
{
|
|
CHAR8 *Needle;
|
|
|
|
Needle = String;
|
|
while ((Needle = AsciiStrStr (Needle, "\\")) != NULL) {
|
|
*Needle++ = '/';
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
UserOcKernelLoadAndReserveKext (
|
|
IN OC_KERNEL_ADD_ENTRY *Kext,
|
|
IN UINT32 Index,
|
|
IN OC_GLOBAL_CONFIG *Config,
|
|
IN BOOLEAN Is32Bit,
|
|
IN OUT UINT32 *ReservedExeSize,
|
|
IN OUT UINT32 *ReservedInfoSize,
|
|
IN OUT UINT32 *NumReservedKexts
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR8 *BundlePath;
|
|
CHAR8 *Comment;
|
|
CONST CHAR8 *Arch;
|
|
CHAR8 *PlistPath;
|
|
CHAR8 *ExecutablePath;
|
|
CHAR8 FullPath[OC_STORAGE_SAFE_PATH_MAX];
|
|
|
|
if (!Kext->Enabled) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
BundlePath = OC_BLOB_GET (&Kext->BundlePath);
|
|
Comment = OC_BLOB_GET (&Kext->Comment);
|
|
Arch = OC_BLOB_GET (&Kext->Arch);
|
|
PlistPath = OC_BLOB_GET (&Kext->PlistPath);
|
|
if ((BundlePath[0] == '\0') || (PlistPath[0] == '\0')) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"OC: Injected kext %u (%a) has invalid info\n",
|
|
Index,
|
|
Comment
|
|
));
|
|
Kext->Enabled = FALSE;
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (AsciiStrCmp (Arch, Is32Bit ? "x86_64" : "i386") == 0) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OC: Injected kext %a (%a) at %u skipped due to arch %a != %a\n",
|
|
BundlePath,
|
|
Comment,
|
|
Index,
|
|
Arch,
|
|
Is32Bit ? "i386" : "x86_64"
|
|
));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Required for possible cacheless force injection later on.
|
|
//
|
|
AsciiHostSlashes (BundlePath);
|
|
|
|
//
|
|
// Get plist path and data.
|
|
//
|
|
Status = OcAsciiSafeSPrint (
|
|
FullPath,
|
|
sizeof (FullPath),
|
|
"%s%a\\%a",
|
|
OPEN_CORE_KEXT_PATH,
|
|
BundlePath,
|
|
PlistPath
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_WARN,
|
|
"OC: Failed to fit injected kext path %s%a\\%a",
|
|
OPEN_CORE_KEXT_PATH,
|
|
BundlePath,
|
|
PlistPath
|
|
));
|
|
Kext->Enabled = FALSE;
|
|
return Status;
|
|
}
|
|
|
|
AsciiHostSlashes (FullPath);
|
|
|
|
Kext->PlistData = (CHAR8 *)UserReadFileFromRoot (
|
|
FullPath,
|
|
&Kext->PlistDataSize
|
|
);
|
|
if (Kext->PlistData == NULL) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"OC: Plist injected is missing for %s kext %a (%a)\n",
|
|
FullPath,
|
|
BundlePath,
|
|
Comment
|
|
));
|
|
Kext->Enabled = FALSE;
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Get executable path and data, if present.
|
|
//
|
|
ExecutablePath = OC_BLOB_GET (&Kext->ExecutablePath);
|
|
if (ExecutablePath[0] != '\0') {
|
|
Status = OcAsciiSafeSPrint (
|
|
FullPath,
|
|
sizeof (FullPath),
|
|
"%s%a\\%a",
|
|
OPEN_CORE_KEXT_PATH,
|
|
BundlePath,
|
|
ExecutablePath
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_WARN,
|
|
"OC: Failed to fit injected kext path %s%a\\%a",
|
|
OPEN_CORE_KEXT_PATH,
|
|
BundlePath,
|
|
ExecutablePath
|
|
));
|
|
Kext->Enabled = FALSE;
|
|
FreePool (Kext->PlistData);
|
|
Kext->PlistData = NULL;
|
|
return Status;
|
|
}
|
|
|
|
AsciiHostSlashes (FullPath);
|
|
|
|
Kext->ImageData = UserReadFileFromRoot (
|
|
FullPath,
|
|
&Kext->ImageDataSize
|
|
);
|
|
if (Kext->ImageData == NULL) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"OC: Image injected is missing for %a kext %a (%a)\n",
|
|
FullPath,
|
|
BundlePath,
|
|
Comment
|
|
));
|
|
Kext->Enabled = FALSE;
|
|
FreePool (Kext->PlistData);
|
|
Kext->PlistData = NULL;
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
Status = PrelinkedReserveKextSize (
|
|
ReservedInfoSize,
|
|
ReservedExeSize,
|
|
Kext->PlistDataSize,
|
|
Kext->ImageData,
|
|
Kext->ImageDataSize,
|
|
Is32Bit
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OC: Failed to fit %s kext %a (%a) - %r\n",
|
|
Is32Bit ? L"32-bit" : L"64-bit",
|
|
BundlePath,
|
|
Comment,
|
|
Status
|
|
));
|
|
if (Kext->ImageData != NULL) {
|
|
FreePool (Kext->ImageData);
|
|
Kext->ImageData = NULL;
|
|
}
|
|
|
|
FreePool (Kext->PlistData);
|
|
Kext->PlistData = NULL;
|
|
return Status;
|
|
}
|
|
|
|
(*NumReservedKexts)++;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcGetFileData (
|
|
IN EFI_FILE_PROTOCOL *File,
|
|
IN UINT32 Position,
|
|
IN UINT32 Size,
|
|
OUT UINT8 *Buffer
|
|
)
|
|
{
|
|
ASSERT (File == &NilFileProtocol);
|
|
|
|
if ((UINT64)Position + Size > mPrelinkedSize) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
CopyMem (&Buffer[0], &mPrelinked[Position], Size);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcGetFileSize (
|
|
IN EFI_FILE_PROTOCOL *File,
|
|
OUT UINT32 *Size
|
|
)
|
|
{
|
|
ASSERT (File == &NilFileProtocol);
|
|
|
|
*Size = mPrelinkedSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
int
|
|
WrapMain (
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
{
|
|
UINT8 *ConfigFileBuffer;
|
|
UINT32 ConfigFileSize;
|
|
OC_GLOBAL_CONFIG Config;
|
|
EFI_STATUS Status;
|
|
UINT32 ErrorCount;
|
|
UINT32 Index;
|
|
UINT32 AllocSize;
|
|
EFI_STATUS PrelinkedStatus;
|
|
|
|
CONST CHAR8 *FileName;
|
|
|
|
BOOLEAN mUse32BitKernel;
|
|
UINT32 ReservedInfoSize;
|
|
UINT32 ReservedExeSize;
|
|
UINT32 NumReservedKexts;
|
|
UINT32 LinkedExpansion;
|
|
UINT8 *NewPrelinked;
|
|
UINT32 NewPrelinkedSize;
|
|
UINT8 Sha384[48];
|
|
BOOLEAN Is32Bit;
|
|
|
|
OC_CPU_INFO DummyCpuInfo;
|
|
|
|
OC_KERNEL_ADD_ENTRY *Kext;
|
|
|
|
if (argc < 2) {
|
|
DEBUG ((DEBUG_ERROR, "Usage: %a <path/to/OC/folder/> [path/to/kernel]\n\n", argv[0]));
|
|
return -1;
|
|
}
|
|
|
|
FileName = argc > 2 ? argv[2] : "/System/Library/PrelinkedKernels/prelinkedkernel";
|
|
if ((mPrelinked = UserReadFile (FileName, &mPrelinkedSize)) == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Read fail %a\n", FileName));
|
|
return -1;
|
|
}
|
|
|
|
if (!UserSetRootPath (argv[1])) {
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Read config file (Only one single config is supported).
|
|
//
|
|
CHAR8 AsciiOcConfig[16];
|
|
|
|
UnicodeStrToAsciiStrS (OPEN_CORE_CONFIG_PATH, AsciiOcConfig, L_STR_SIZE (OPEN_CORE_CONFIG_PATH));
|
|
ConfigFileBuffer = UserReadFileFromRoot (AsciiOcConfig, &ConfigFileSize);
|
|
if (ConfigFileBuffer == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to read %s\n", OPEN_CORE_CONFIG_PATH));
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Initialise config structure to be checked, and exit on error.
|
|
//
|
|
ErrorCount = 0;
|
|
Status = OcConfigurationInit (&Config, ConfigFileBuffer, ConfigFileSize, &ErrorCount);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Invalid config\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (ErrorCount > 0) {
|
|
DEBUG ((DEBUG_ERROR, "Serialisation returns %u %a!\n", ErrorCount, ErrorCount > 1 ? "errors" : "error"));
|
|
}
|
|
|
|
PcdGet32 (PcdFixedDebugPrintErrorLevel) |= DEBUG_INFO;
|
|
PcdGet32 (PcdDebugPrintErrorLevel) |= DEBUG_INFO;
|
|
PcdGet8 (PcdDebugPropertyMask) |= DEBUG_PROPERTY_DEBUG_CODE_ENABLED;
|
|
|
|
mUse32BitKernel = FALSE;
|
|
ReservedInfoSize = PRELINK_INFO_RESERVE_SIZE;
|
|
ReservedExeSize = 0;
|
|
NumReservedKexts = 0;
|
|
//
|
|
// Process kexts to be injected.
|
|
//
|
|
for (Index = 0; Index < Config.Kernel.Add.Count; ++Index) {
|
|
Kext = Config.Kernel.Add.Values[Index];
|
|
|
|
Status = UserOcKernelLoadAndReserveKext (
|
|
Kext,
|
|
Index,
|
|
&Config,
|
|
mUse32BitKernel,
|
|
&ReservedExeSize,
|
|
&ReservedInfoSize,
|
|
&NumReservedKexts
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "[FAIL] Kernel load and reserve - %r\n", Status));
|
|
FailedToProcess = TRUE;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
LinkedExpansion = KcGetSegmentFixupChainsSize (ReservedExeSize);
|
|
if (LinkedExpansion == 0) {
|
|
FailedToProcess = TRUE;
|
|
return -1;
|
|
}
|
|
|
|
Status = ReadAppleKernel (
|
|
&NilFileProtocol,
|
|
FALSE,
|
|
&Is32Bit,
|
|
&NewPrelinked,
|
|
&NewPrelinkedSize,
|
|
&AllocSize,
|
|
ReservedInfoSize + ReservedExeSize + LinkedExpansion,
|
|
Sha384
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
FreePool (mPrelinked);
|
|
mPrelinked = NewPrelinked;
|
|
mPrelinkedSize = NewPrelinkedSize;
|
|
DEBUG ((DEBUG_WARN, "[OK] Sha384 is %02X%02X%02X%02X\n", Sha384[0], Sha384[1], Sha384[2], Sha384[3]));
|
|
} else {
|
|
DEBUG ((DEBUG_WARN, "[FAIL] Kernel unpack failure - %r\n", Status));
|
|
FailedToProcess = TRUE;
|
|
return -1;
|
|
}
|
|
|
|
KernelVersion = OcKernelReadDarwinVersion (mPrelinked, mPrelinkedSize);
|
|
if (KernelVersion != 0) {
|
|
DEBUG ((DEBUG_WARN, "[OK] Got version %u\n", KernelVersion));
|
|
} else {
|
|
DEBUG ((DEBUG_WARN, "[FAIL] Failed to detect version\n"));
|
|
FailedToProcess = TRUE;
|
|
}
|
|
|
|
ZeroMem (&DummyCpuInfo, sizeof (DummyCpuInfo));
|
|
//
|
|
// Disable ProvideCurrentCpuInfo patch, as there is no CpuInfo available on userspace.
|
|
//
|
|
Config.Kernel.Quirks.ProvideCurrentCpuInfo = FALSE;
|
|
ASSERT (Config.Kernel.Quirks.ProvideCurrentCpuInfo == FALSE);
|
|
|
|
ZeroMem (Config.Kernel.Emulate.Cpuid1Data, sizeof (Config.Kernel.Emulate.Cpuid1Data));
|
|
Config.Kernel.Emulate.Cpuid1Data[0] = 0x000306A9;
|
|
ZeroMem (Config.Kernel.Emulate.Cpuid1Mask, sizeof (Config.Kernel.Emulate.Cpuid1Mask));
|
|
Config.Kernel.Emulate.Cpuid1Mask[0] = 0xFFFFFFFF;
|
|
|
|
ASSERT (Config.Kernel.Force.Count == 0);
|
|
|
|
//
|
|
// Apply patches to kernel itself, and then process prelinked.
|
|
//
|
|
OcKernelApplyPatches (
|
|
&Config,
|
|
&DummyCpuInfo,
|
|
KernelVersion,
|
|
FALSE,
|
|
CacheTypeNone,
|
|
NULL,
|
|
NewPrelinked,
|
|
NewPrelinkedSize
|
|
);
|
|
PrelinkedStatus = OcKernelProcessPrelinked (
|
|
&Config,
|
|
KernelVersion,
|
|
FALSE,
|
|
NewPrelinked,
|
|
&NewPrelinkedSize,
|
|
AllocSize,
|
|
LinkedExpansion,
|
|
ReservedExeSize
|
|
);
|
|
if (EFI_ERROR (PrelinkedStatus)) {
|
|
DEBUG ((DEBUG_WARN, "[FAIL] Kernel process - %r\n", PrelinkedStatus));
|
|
FailedToProcess = TRUE;
|
|
return -1;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "OC: Prelinked status - %r\n", PrelinkedStatus));
|
|
|
|
UserWriteFile ("out.bin", NewPrelinked, NewPrelinkedSize);
|
|
|
|
FreePool (mPrelinked);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main (
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
{
|
|
int code;
|
|
|
|
code = WrapMain (argc, argv);
|
|
if (FailedToProcess) {
|
|
code = -1;
|
|
}
|
|
|
|
return code;
|
|
}
|