mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
1245 lines
36 KiB
C
1245 lines
36 KiB
C
|
/** @file
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
FSInject.c
|
||
|
|
||
|
FSInject driver - Replaces EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on target volume
|
||
|
and injects content of specified source folder on source (injection) volume
|
||
|
into target folder in target volume.
|
||
|
|
||
|
initial version - dmazar
|
||
|
|
||
|
**/
|
||
|
|
||
|
#include <Library/BaseLib.h>
|
||
|
#include <Library/UefiLib.h>
|
||
|
#include <Library/UefiBootServicesTableLib.h>
|
||
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
||
|
#include <Library/BaseMemoryLib.h>
|
||
|
#include <Library/MemoryAllocationLib.h>
|
||
|
#include <Library/DebugLib.h>
|
||
|
#include <Library/PrintLib.h>
|
||
|
#include <Library/MemLogLib.h>
|
||
|
|
||
|
#include <Protocol/SimpleFileSystem.h>
|
||
|
|
||
|
#include <Guid/GlobalVariable.h>
|
||
|
#include <Guid/FileInfo.h>
|
||
|
#include <Guid/FileSystemInfo.h>
|
||
|
#include <Guid/FileSystemVolumeLabelInfo.h>
|
||
|
|
||
|
#include <Protocol/FSInjectProtocol.h>
|
||
|
|
||
|
#include "FSInject.h"
|
||
|
|
||
|
//#include "../Version.h"
|
||
|
//CONST CHAR8* CloverRevision = REVISION_STR;
|
||
|
|
||
|
|
||
|
// DBG_TO: 0=no debug, 1=serial, 2=console
|
||
|
// serial requires
|
||
|
// [PcdsFixedAtBuild]
|
||
|
// gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x07
|
||
|
// gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xFFFFFFFF
|
||
|
// in package DSC file
|
||
|
#define DBG_TO 3
|
||
|
|
||
|
#if DBG_TO == 3
|
||
|
#define DBG(...) MemLog(FALSE, 0, __VA_ARGS__)
|
||
|
#elif DBG_TO == 2
|
||
|
#define DBG(...) AsciiPrint(__VA_ARGS__)
|
||
|
#elif DBG_TO == 1
|
||
|
#define DBG(...) DebugPrint(1, __VA_ARGS__)
|
||
|
#else
|
||
|
#define DBG(...)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#define TEST 0
|
||
|
#if TEST
|
||
|
#include "Test.h"
|
||
|
#endif
|
||
|
|
||
|
#define EfiGuidStrMapLen 4
|
||
|
/** Map of known guids and friendly names. Searchable with GuidStr() */
|
||
|
struct {
|
||
|
EFI_GUID *Guid;
|
||
|
CHAR16 *Str;
|
||
|
} EfiGuidStrMap[EfiGuidStrMapLen] = {
|
||
|
{NULL, L"Tmp buffer AE074D26-6E9E-11E1-A5B8-9BFC4824019B"},
|
||
|
{&gEfiFileInfoGuid, L"gEfiFileInfoGuid"},
|
||
|
{&gEfiFileSystemInfoGuid, L"gEfiFileSystemInfoGuid"},
|
||
|
{&gEfiFileSystemVolumeLabelInfoIdGuid, L"gEfiFileSystemVolumeLabelInfoIdGuid"}
|
||
|
};
|
||
|
|
||
|
/** Returns GUID as string, with friendly name for known guids. */
|
||
|
CHAR16*
|
||
|
EFIAPI
|
||
|
GuidStr(IN EFI_GUID *Guid)
|
||
|
{
|
||
|
UINTN i;
|
||
|
CHAR16 *Str = NULL;
|
||
|
|
||
|
for(i = 1; i < EfiGuidStrMapLen; i++) {
|
||
|
if (CompareGuid(EfiGuidStrMap[i].Guid, Guid)) {
|
||
|
Str = EfiGuidStrMap[i].Str;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (Str == NULL) {
|
||
|
UnicodeSPrint(EfiGuidStrMap[0].Str, 47 * 2, L"%g", Guid);
|
||
|
Str = EfiGuidStrMap[0].Str;
|
||
|
}
|
||
|
return Str;
|
||
|
}
|
||
|
|
||
|
/** Returns pointer to last Char in String or NULL. */
|
||
|
CHAR16*
|
||
|
EFIAPI
|
||
|
GetStrLastChar(IN CHAR16 *String)
|
||
|
{
|
||
|
CHAR16 *Pos;
|
||
|
|
||
|
if (String == NULL || *String == L'\0') {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// go to end
|
||
|
Pos = String;
|
||
|
while (*Pos != L'\0') {
|
||
|
Pos++;
|
||
|
}
|
||
|
Pos--;
|
||
|
return Pos;
|
||
|
}
|
||
|
|
||
|
/** Returns pointer to last occurence of Char in String or NULL. */
|
||
|
CHAR16*
|
||
|
EFIAPI
|
||
|
GetStrLastCharOccurence(IN CHAR16 *String, IN CHAR16 Char)
|
||
|
{
|
||
|
CHAR16 *Pos;
|
||
|
|
||
|
if (String == NULL || *String == L'\0') {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// go to end
|
||
|
Pos = String;
|
||
|
while (*Pos != L'\0') {
|
||
|
Pos++;
|
||
|
}
|
||
|
// search for Char
|
||
|
while (*Pos != Char && Pos != String) {
|
||
|
Pos--;
|
||
|
}
|
||
|
return (*Pos == Char) ? Pos : NULL;
|
||
|
}
|
||
|
|
||
|
/** Returns upper case version of char - valid only for ASCII chars in unicode. */
|
||
|
CHAR16
|
||
|
EFIAPI
|
||
|
ToUpperChar(IN CHAR16 Chr)
|
||
|
{
|
||
|
CHAR8 C;
|
||
|
|
||
|
if (Chr > 0x100) return Chr;
|
||
|
C = (CHAR8)Chr;
|
||
|
return ((C >= 'a' && C <= 'z') ? C - ('a' - 'A') : C);
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Returns 0 if two strings are equal, !=0 otherwise. Compares just first 8 bits of chars (valid for ASCII), case insensitive.. */
|
||
|
UINTN
|
||
|
EFIAPI
|
||
|
StrCmpiBasic(IN CHAR16 *String1, IN CHAR16 *String2)
|
||
|
{
|
||
|
CHAR16 Chr1;
|
||
|
CHAR16 Chr2;
|
||
|
|
||
|
DBG("Cmpi('%s', '%s') ", String1, String2);
|
||
|
|
||
|
if (String1 == NULL || String2 == NULL) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (*String1 == L'\0' && *String2 == L'\0') {
|
||
|
return 0;
|
||
|
}
|
||
|
if (*String1 == L'\0' || *String2 == L'\0') {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
Chr1 = ToUpperChar(*String1);
|
||
|
Chr2 = ToUpperChar(*String2);
|
||
|
while ((*String1 != L'\0') && (Chr1 == Chr2)) {
|
||
|
String1++;
|
||
|
String2++;
|
||
|
Chr1 = ToUpperChar(*String1);
|
||
|
Chr2 = ToUpperChar(*String2);
|
||
|
}
|
||
|
|
||
|
DBG("=%s ", (Chr1 - Chr2) ? L"NEQ" : L"EQ");
|
||
|
return Chr1 - Chr2;
|
||
|
}
|
||
|
|
||
|
/** Returns TRUE if String1 starts with String2, FALSE otherwise. Compares just first 8 bits of chars (valid for ASCII), case insensitive.. */
|
||
|
BOOLEAN
|
||
|
EFIAPI
|
||
|
StriStartsWithBasic(IN CHAR16 *String1, IN CHAR16 *String2)
|
||
|
{
|
||
|
CHAR16 Chr1;
|
||
|
CHAR16 Chr2;
|
||
|
BOOLEAN Result;
|
||
|
|
||
|
DBG("StriStarts('%s', '%s') ", String1, String2);
|
||
|
|
||
|
if (String1 == NULL || String2 == NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (*String1 == L'\0' && *String2 == L'\0') {
|
||
|
return TRUE;
|
||
|
}
|
||
|
if (*String1 == L'\0' || *String2 == L'\0') {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Chr1 = ToUpperChar(*String1);
|
||
|
Chr2 = ToUpperChar(*String2);
|
||
|
while ((Chr1 != L'\0') && (Chr2 != L'\0') && (Chr1 == Chr2)) {
|
||
|
String1++;
|
||
|
String2++;
|
||
|
Chr1 = ToUpperChar(*String1);
|
||
|
Chr2 = ToUpperChar(*String2);
|
||
|
}
|
||
|
|
||
|
Result = ((Chr1 == L'\0') && (Chr2 == L'\0'))
|
||
|
|| ((Chr1 != L'\0') && (Chr2 == L'\0'));
|
||
|
|
||
|
DBG("=%s \n", Result ? L"TRUE" : L"FALSE");
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
/** Composes file name from Parent and FName. Allocates memory for result which should be released by caller. */
|
||
|
CHAR16*
|
||
|
EFIAPI
|
||
|
GetNormalizedFName(IN CHAR16 *Parent, IN CHAR16 *FName)
|
||
|
{
|
||
|
CHAR16 *TmpStr;
|
||
|
CHAR16 *TmpStr2;
|
||
|
UINTN Len;
|
||
|
|
||
|
DBG("NormFName('%s' + '%s')", Parent, FName);
|
||
|
// case: FName starts with \ "\System\Xx"
|
||
|
// we'll just use it as is, but we are wrong if "\System\Xx\..\Yy\.\Zz" or similar
|
||
|
if (FName[0] == L'\\') {
|
||
|
FName = AllocateCopyPool(StrSize(FName), FName); // reusing FName
|
||
|
}
|
||
|
|
||
|
// case: FName is "."
|
||
|
// we'll just copy Parent assuming Parent is normalized, which will be the case if this func will be correct once
|
||
|
else if (FName[0] == L'.' && FName[1] == L'\0') {
|
||
|
FName = AllocateCopyPool(StrSize(Parent), Parent);
|
||
|
}
|
||
|
|
||
|
// case: FName is ".."
|
||
|
// we'll extract Parent's parent - also assuming Parent is normalized
|
||
|
else if (FName[0] == L'.' && FName[1] == L'.' && FName[2] == L'\0') {
|
||
|
TmpStr = GetStrLastCharOccurence(Parent, L'\\');
|
||
|
// if there is L'\\' and not at the beginning ...
|
||
|
if (TmpStr != NULL && TmpStr != Parent) {
|
||
|
*TmpStr = L'\0'; // terminating Parent; will return L'\\' back
|
||
|
FName = AllocateCopyPool(StrSize(Parent), Parent);
|
||
|
*TmpStr = L'\\'; // return L'\\' back to Parent
|
||
|
} else {
|
||
|
// caller is doing something wrong - we'll default to L"\\"
|
||
|
FName = AllocateCopyPool(StrSize(L"\\"), L"\\");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// other cases: for now just do Parent + \ + FName
|
||
|
// but check if Parent already ends with backslash
|
||
|
else {
|
||
|
Len = StrSize(Parent) + StrSize(FName); // has place for extra char (\\) if needed
|
||
|
TmpStr = AllocateZeroPool(Len);
|
||
|
StrCpyS(TmpStr, Len/sizeof(CHAR16), Parent);
|
||
|
TmpStr2 = GetStrLastChar(Parent);
|
||
|
if (TmpStr2 == NULL || *TmpStr2 != L'\\') {
|
||
|
StrCatS(TmpStr, Len/sizeof(CHAR16), L"\\");
|
||
|
}
|
||
|
StrCatS(TmpStr, Len/sizeof(CHAR16), FName);
|
||
|
DBG("='%s' ", TmpStr);
|
||
|
return TmpStr;
|
||
|
}
|
||
|
DBG("='%s' ", FName);
|
||
|
return FName;
|
||
|
}
|
||
|
|
||
|
/** If FName starts with TgtDir, then extracts the rest from FName and copies it to SrcDir and returns it. Or NULL.
|
||
|
* Caller is responsible for releasing memory for returned string.
|
||
|
* Example: TgtDir="\S\L\E", SrcDir="\efi\10.7", FName="\S\L\E\Xx.kext\Contents\Info.plist"
|
||
|
* This should return "\efi\10.7\Xx.kext\Contents\Info.plist"
|
||
|
*/
|
||
|
CHAR16*
|
||
|
EFIAPI
|
||
|
GetInjectionFName(IN CHAR16 *TgtDir, IN CHAR16 *SrcDir, IN CHAR16 *FName)
|
||
|
{
|
||
|
CHAR16 Chr1;
|
||
|
CHAR16 Chr2;
|
||
|
CHAR16 *Str1;
|
||
|
CHAR16 *Str2;
|
||
|
UINTN Size;
|
||
|
|
||
|
DBG("InjFName('%s', '%s', '%s')", TgtDir, SrcDir, FName);
|
||
|
if (TgtDir == NULL || SrcDir == NULL || FName == NULL) {
|
||
|
DBG("=NULL0 ");
|
||
|
return NULL;
|
||
|
}
|
||
|
// check if FName starts with TgtDir, kind of case insensitive (only ASCII chars)
|
||
|
Str1 = TgtDir;
|
||
|
Str2 = FName;
|
||
|
Chr1 = ToUpperChar(*Str1);
|
||
|
Chr2 = ToUpperChar(*Str2);
|
||
|
while ((*Str1 != L'\0') && (Chr1 == Chr2)) {
|
||
|
Str1++;
|
||
|
Str2++;
|
||
|
Chr1 = ToUpperChar(*Str1);
|
||
|
Chr2 = ToUpperChar(*Str2);
|
||
|
}
|
||
|
// if FName starts with TgtDir, then *Str1 should be '\0'
|
||
|
if (*Str1 != L'\0') {
|
||
|
DBG("=NULL1 ");
|
||
|
return NULL;
|
||
|
}
|
||
|
// if FName is a file inside TgtDir, then *Str2 should be == '\\'
|
||
|
if (*Str2 != L'\\') {
|
||
|
DBG("=NULL2 ");
|
||
|
return NULL;
|
||
|
}
|
||
|
// we are at '\\' with Str2 - copy from here to the end to SrcDir
|
||
|
// determine the buffer size for new string
|
||
|
Size = StrSize(SrcDir) + StrSize(Str2) - 2;
|
||
|
Str1 = AllocateZeroPool(Size);
|
||
|
if (Str1 != NULL) {
|
||
|
StrCpyS(Str1, Size/sizeof(CHAR16), SrcDir);
|
||
|
StrCatS(Str1, Size/sizeof(CHAR16), Str2);
|
||
|
}
|
||
|
DBG("='%s' ", Str1);
|
||
|
return Str1;
|
||
|
}
|
||
|
|
||
|
/** Openes EFI_FILE_PROTOCOL on given VolumeFS for given FName. */
|
||
|
EFI_FILE_PROTOCOL*
|
||
|
EFIAPI
|
||
|
OpenFileProtocol(
|
||
|
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *VolumeFS,
|
||
|
IN CHAR16 *FName,
|
||
|
IN UINT64 OpenMode,
|
||
|
IN UINT64 Attributes
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_FILE_PROTOCOL *RootFP;
|
||
|
EFI_FILE_PROTOCOL *FP;
|
||
|
|
||
|
// open volume
|
||
|
Status = VolumeFS->OpenVolume(VolumeFS, &RootFP);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("Can not OpenVolume: %r ", Status);
|
||
|
return NULL;
|
||
|
}
|
||
|
// open file/dir
|
||
|
Status = RootFP->Open(RootFP, &FP, FName, OpenMode, Attributes);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("Can not Open '%s': %r ", FName, Status);
|
||
|
FP = NULL;
|
||
|
}
|
||
|
RootFP->Close(RootFP);
|
||
|
return FP;
|
||
|
}
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* FSI_FILE_PROTOCOL - our implementation of EFI_FILE_PROTOCOL
|
||
|
**************************************************************************************/
|
||
|
|
||
|
FSI_FILE_PROTOCOL* EFIAPI CreateFSInjectFP(VOID);
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.Open - Opens a new file relative to the source file's location. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_Open(
|
||
|
IN EFI_FILE_PROTOCOL *This,
|
||
|
OUT EFI_FILE_PROTOCOL **NewHandle,
|
||
|
IN CHAR16 *FileName,
|
||
|
IN UINT64 OpenMode,
|
||
|
IN UINT64 Attributes
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
CHAR16 *NewFName;
|
||
|
CHAR16 *InjFName = NULL;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
FSI_FILE_PROTOCOL *FSINew;
|
||
|
FSI_STRING_LIST *StringList;
|
||
|
FSI_STRING_LIST_ENTRY *StringEntry;
|
||
|
|
||
|
DBG("FSI_FP %p.Open('%s', %x, %x) ", This, FileName, OpenMode, Attributes);
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
NewFName = GetNormalizedFName(FSIThis->FName, FileName);
|
||
|
|
||
|
StringList = FSIThis->FSI_FS->Blacklist;
|
||
|
if (StringList != NULL && !IsListEmpty(&StringList->List)) {
|
||
|
// blocking files in Blacklist
|
||
|
for (StringEntry = (FSI_STRING_LIST_ENTRY *)GetFirstNode(&StringList->List);
|
||
|
!IsNull (&StringList->List, &StringEntry->List);
|
||
|
StringEntry = (FSI_STRING_LIST_ENTRY *)GetNextNode(&StringList->List, &StringEntry->List)
|
||
|
)
|
||
|
{
|
||
|
if (StriStartsWithBasic(NewFName, StringEntry->String)) {
|
||
|
DBG("Blacklisted\n");
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// create our FP implementation
|
||
|
FSINew = CreateFSInjectFP();
|
||
|
if (FSINew == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
DBG("CreateFSInjectFP Status=%r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
FSINew->FSI_FS = FSIThis->FSI_FS; // saving reference to parent FS protocol
|
||
|
FSINew->FName =NewFName;
|
||
|
FSINew->TgtFP = NULL;
|
||
|
FSINew->SrcFP = NULL;
|
||
|
|
||
|
// mach_kernel - if exists in SrcDir, then inject this one
|
||
|
if (StrCmpiBasic(NewFName, L"\\mach_kernel") == 0) {
|
||
|
DBG("mach_kernel ");
|
||
|
if (FSIThis->FSI_FS->SrcDir != NULL && FSIThis->FSI_FS->SrcFS != NULL) {
|
||
|
InjFName = GetInjectionFName(L"\0", FSIThis->FSI_FS->SrcDir, NewFName);
|
||
|
if (InjFName != NULL) {
|
||
|
// if this one exists inside injection dir - should be opened with SrcFP
|
||
|
FSINew->SrcFP = OpenFileProtocol(FSIThis->FSI_FS->SrcFS, InjFName, OpenMode, Attributes);
|
||
|
if (FSINew->SrcFP != NULL) {
|
||
|
FSINew->FromTgt = FALSE;
|
||
|
DBG("Opened with SrcFP ");
|
||
|
goto SuccessExit;
|
||
|
} else {
|
||
|
DBG(" no injection, SrcFP->Open=%r ", Status);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// S/L/Kernels/kernel - if exists in SrcDir, then inject this one
|
||
|
if (StrCmpiBasic(NewFName, L"\\System\\Library\\Kernels\\kernel") == 0) {
|
||
|
DBG("kernel ");
|
||
|
if (FSIThis->FSI_FS->SrcDir != NULL && FSIThis->FSI_FS->SrcFS != NULL) {
|
||
|
InjFName = GetInjectionFName(L"\0", FSIThis->FSI_FS->SrcDir, L"\\kernel");
|
||
|
if (InjFName != NULL) {
|
||
|
// if this one exists inside injection dir - should be opened with SrcFP
|
||
|
FSINew->SrcFP = OpenFileProtocol(FSIThis->FSI_FS->SrcFS, InjFName, OpenMode, Attributes);
|
||
|
if (FSINew->SrcFP != NULL) {
|
||
|
FSINew->FromTgt = FALSE;
|
||
|
DBG("Opened with SrcFP ");
|
||
|
goto SuccessExit;
|
||
|
} else {
|
||
|
DBG(" no injection, SrcFP->Open=%r ", Status);
|
||
|
}
|
||
|
}
|
||
|
InjFName = GetInjectionFName(L"\0", FSIThis->FSI_FS->SrcDir, L"\\mach_kernel");
|
||
|
if (InjFName != NULL) {
|
||
|
// if this one exists inside injection dir - should be opened with SrcFP
|
||
|
FSINew->SrcFP = OpenFileProtocol(FSIThis->FSI_FS->SrcFS, InjFName, OpenMode, Attributes);
|
||
|
if (FSINew->SrcFP != NULL) {
|
||
|
FSINew->FromTgt = FALSE;
|
||
|
DBG("Opened with SrcFP ");
|
||
|
goto SuccessExit;
|
||
|
} else {
|
||
|
DBG(" no injection, SrcFP->Open=%r ", Status);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// try with target
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// call original target implementation to get NewHandle
|
||
|
Status = FSIThis->TgtFP->Open(FSIThis->TgtFP, NewHandle, NewFName, OpenMode, Attributes);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("TgtFP->Open=%r ", Status);
|
||
|
} else {
|
||
|
DBG("Opened with TgtFP ");
|
||
|
FSINew->FromTgt = TRUE;
|
||
|
// save new orig target handle
|
||
|
FSINew->TgtFP = *NewHandle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if write protected - return now
|
||
|
if (Status == EFI_WRITE_PROTECTED) {
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
// handle injection if needed
|
||
|
if (EFI_ERROR(Status) && FSIThis->FSI_FS->SrcDir != NULL && FSIThis->FSI_FS->SrcFS != NULL) {
|
||
|
// if not found and injection requested: try injection dir
|
||
|
InjFName = GetInjectionFName(FSIThis->FSI_FS->TgtDir, FSIThis->FSI_FS->SrcDir, NewFName);
|
||
|
if (InjFName != NULL) {
|
||
|
// this one exists inside injection dir - should be opened with SrcFP
|
||
|
FSINew->FromTgt = FALSE;
|
||
|
FSINew->SrcFP = OpenFileProtocol(FSIThis->FSI_FS->SrcFS, InjFName, OpenMode, Attributes);
|
||
|
if (FSINew->SrcFP == NULL) {
|
||
|
Status = EFI_DEVICE_ERROR;
|
||
|
DBG("SrcFP->Open=%r ", Status);
|
||
|
} else {
|
||
|
Status = EFI_SUCCESS;
|
||
|
DBG("Opened with SrcFP ");
|
||
|
// ok - we are injecting kexts with FSInject driver because user requested
|
||
|
// it with NoCaches or because boot.efi refused to load kernelcache for some reason.
|
||
|
// let's inform Clover's kext injection that it does not have to do
|
||
|
// in-memory kext injection.
|
||
|
{
|
||
|
STATIC UINT8 KextsInjected = 0;
|
||
|
|
||
|
if (KextsInjected == 0) {
|
||
|
gRT->SetVariable(L"FSInject.KextsInjected",
|
||
|
&gEfiGlobalVariableGuid,
|
||
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
||
|
sizeof(KextsInjected),
|
||
|
&KextsInjected
|
||
|
);
|
||
|
//AsciiPrint("\nFSInject.KextsInjected\n");
|
||
|
//gBS->Stall(3000000);
|
||
|
}
|
||
|
KextsInjected++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (EFI_ERROR(Status) && FSIThis->TgtFP == NULL) {
|
||
|
// this happens when we are called on FP that is opened with SrcFP (we do not have TgtFP)
|
||
|
// and then with FName ".." (in shell), and when resulting dir in actually on TgtFP.
|
||
|
// need to open it with TgtFP
|
||
|
FSINew->TgtFP = OpenFileProtocol(FSIThis->FSI_FS->TgtFS, NewFName, OpenMode, Attributes);
|
||
|
if (FSINew->TgtFP != NULL) {
|
||
|
Status = EFI_SUCCESS;
|
||
|
DBG("Opened with TgtFP ");
|
||
|
FSINew->FromTgt = TRUE;
|
||
|
} else {
|
||
|
Status = EFI_DEVICE_ERROR;
|
||
|
DBG("TgtFS->OpenVolume Status=%r\n", Status);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// if still error - quit
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
// we are here with EFI_SUCCESS
|
||
|
|
||
|
// check if this is injection point (target dir where we should inject)
|
||
|
if (FSINew->TgtFP != NULL && FSINew->FSI_FS->SrcFS != NULL && FSINew->FSI_FS->SrcDir != NULL
|
||
|
&& StrCmpiBasic(FSINew->FSI_FS->TgtDir, FSINew->FName) == 0)
|
||
|
{
|
||
|
// it is - open injection dir also
|
||
|
// this FP will have both TgtFP and SrcFP - can be used for test later
|
||
|
// in case of error, it will be NULL - all should run fine then, but without injection
|
||
|
FSINew->SrcFP = OpenFileProtocol(FSINew->FSI_FS->SrcFS, FSINew->FSI_FS->SrcDir, EFI_FILE_MODE_READ, 0);
|
||
|
if (FSINew->SrcFP != NULL) {
|
||
|
DBG("Opened also with SrcFP ");
|
||
|
} else {
|
||
|
DBG("Error opening with SrcFP ");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
SuccessExit:
|
||
|
// set our implementation as a result
|
||
|
*NewHandle = &(FSINew->FP);
|
||
|
|
||
|
DBG("= EFI_SUCCESS, NewHandle=%p, FName='%s'\n", *NewHandle, FSINew->FName);
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
if (FSINew->FName != NULL) FreePool(FSINew->FName);
|
||
|
if (FSINew != NULL) FreePool(FSINew);
|
||
|
DBG("= %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.Close - Closes a specified file handle. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_Close(IN EFI_FILE_PROTOCOL *This)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_SUCCESS;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
|
||
|
DBG("FSI_FP %p.Close() ", This);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// close target FP
|
||
|
Status = FSIThis->TgtFP->Close(FSIThis->TgtFP);
|
||
|
FSIThis->TgtFP = NULL;
|
||
|
}
|
||
|
if (FSIThis->SrcFP != NULL) {
|
||
|
// close source (injection) FP
|
||
|
FSIThis->SrcFP->Close(FSIThis->SrcFP);
|
||
|
FSIThis->SrcFP = NULL;
|
||
|
}
|
||
|
DBG("FName='%s' ", FSIThis->FName);
|
||
|
if (FSIThis->FName != NULL) {
|
||
|
FreePool(FSIThis->FName);
|
||
|
FSIThis->FName = NULL;
|
||
|
}
|
||
|
FreePool(FSIThis);
|
||
|
DBG("= %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.Delete - Close and delete the file handle. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_Delete(IN EFI_FILE_PROTOCOL *This)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_WARN_DELETE_FAILURE;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
|
||
|
DBG("FSI_FP %p.Delete()\n", This);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->Delete(FSIThis->TgtFP);
|
||
|
FSIThis->TgtFP = NULL;
|
||
|
}
|
||
|
if (FSIThis->SrcFP != NULL) {
|
||
|
// close source FP
|
||
|
FSIThis->SrcFP->Close(FSIThis->SrcFP);
|
||
|
FSIThis->SrcFP = NULL;
|
||
|
}
|
||
|
if (FSIThis->FName != NULL) {
|
||
|
FreePool(FSIThis->FName);
|
||
|
FSIThis->FName = NULL;
|
||
|
}
|
||
|
FreePool(FSIThis);
|
||
|
DBG("= %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.Read - Reads data from a file. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_Read(
|
||
|
IN EFI_FILE_PROTOCOL *This,
|
||
|
IN OUT UINTN *BufferSize,
|
||
|
OUT VOID *Buffer
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
#if DBG_TO
|
||
|
EFI_FILE_INFO *FInfo;
|
||
|
#endif
|
||
|
UINTN BufferSizeOrig;
|
||
|
CHAR8 *String;
|
||
|
FSI_STRING_LIST *StringList;
|
||
|
FSI_STRING_LIST_ENTRY *StringEntry;
|
||
|
VOID *TmpBuffer;
|
||
|
UINTN OrigBufferSize = *BufferSize;
|
||
|
|
||
|
DBG("FSI_FP %p.Read(%d, %p) ", This, *BufferSize, Buffer);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
if (FSIThis->TgtFP != NULL && FSIThis->SrcFP != NULL) {
|
||
|
// this is injection point
|
||
|
// first read dir entries from Src and then from Tgt
|
||
|
BufferSizeOrig = *BufferSize;
|
||
|
Status = FSIThis->SrcFP->Read(FSIThis->SrcFP, BufferSize, Buffer);
|
||
|
if (*BufferSize == 0) {
|
||
|
// no more in Src - read from Tgt
|
||
|
*BufferSize = BufferSizeOrig;
|
||
|
Status = FSIThis->TgtFP->Read(FSIThis->TgtFP, BufferSize, Buffer);
|
||
|
}
|
||
|
} else if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->Read(FSIThis->TgtFP, BufferSize, Buffer);
|
||
|
StringList = FSIThis->FSI_FS->ForceLoadKexts;
|
||
|
if (Status == EFI_INVALID_PARAMETER && *BufferSize == 0) {
|
||
|
// On some systems FS driver seems to have alignment restrictions on given buffer.
|
||
|
// UEFIs buffers allocated with standard AllocatePool seem to be aligned properly and reads
|
||
|
// to them always succeed, so we'll try to overcome this by using new allocated buffer.
|
||
|
TmpBuffer = AllocateZeroPool(OrigBufferSize);
|
||
|
*BufferSize = OrigBufferSize;
|
||
|
Status = FSIThis->TgtFP->Read(FSIThis->TgtFP, BufferSize, TmpBuffer);
|
||
|
if (Status == EFI_SUCCESS && *BufferSize > 0) {
|
||
|
CopyMem(Buffer, TmpBuffer, *BufferSize);
|
||
|
}
|
||
|
FreePool(TmpBuffer);
|
||
|
}
|
||
|
if (Status == EFI_SUCCESS && StringList != NULL && FSIThis->FName != NULL) {
|
||
|
// check ForceLoadKexts
|
||
|
for (StringEntry = (FSI_STRING_LIST_ENTRY *)GetFirstNode(&StringList->List);
|
||
|
!IsNull (&StringList->List, &StringEntry->List);
|
||
|
StringEntry = (FSI_STRING_LIST_ENTRY *)GetNextNode(&StringList->List, &StringEntry->List)
|
||
|
)
|
||
|
{
|
||
|
if (StrStr(FSIThis->FName, StringEntry->String) != NULL) {
|
||
|
//Print(L"\nGot: %s\n", FSIThis->FName);
|
||
|
String = AsciiStrStr((CHAR8*)Buffer, "<string>Safe Boot</string>");
|
||
|
if (String != NULL) {
|
||
|
CopyMem (String, "<string>Root</string> ", 26);
|
||
|
Print(L"\nForced load: %s\n", FSIThis->FName);
|
||
|
//gBS->Stall(5000000);
|
||
|
} else {
|
||
|
String = AsciiStrStr((CHAR8*)Buffer, "<string>Network-Root</string>");
|
||
|
if (String != NULL) {
|
||
|
CopyMem (String, "<string>Root</string> ", 29);
|
||
|
Print(L"\nForced load: %s\n", FSIThis->FName);
|
||
|
//gBS->Stall(5000000);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if (FSIThis->SrcFP != NULL) {
|
||
|
// do it with source FP
|
||
|
Status = FSIThis->SrcFP->Read(FSIThis->SrcFP, BufferSize, Buffer);
|
||
|
if (Status == EFI_INVALID_PARAMETER && *BufferSize == 0) {
|
||
|
// On some systems FS driver seems to have alignment restrictions on given buffer.
|
||
|
// UEFIs buffers allocated with standard AllocatePool seem to be aligned properly and reads
|
||
|
// to them always succeed, so we'll try to overcome this by using new allocated buffer.
|
||
|
TmpBuffer = AllocateZeroPool(OrigBufferSize);
|
||
|
*BufferSize = OrigBufferSize;
|
||
|
Status = FSIThis->SrcFP->Read(FSIThis->SrcFP, BufferSize, TmpBuffer);
|
||
|
if (Status == EFI_SUCCESS && *BufferSize > 0) {
|
||
|
CopyMem(Buffer, TmpBuffer, *BufferSize);
|
||
|
}
|
||
|
FreePool(TmpBuffer);
|
||
|
}
|
||
|
}
|
||
|
#if DBG_TO
|
||
|
if (Status == EFI_SUCCESS && FSIThis->IsDir && *BufferSize > 0) {
|
||
|
FInfo = (EFI_FILE_INFO *)Buffer;
|
||
|
DBG("= %r, *BufferSize=%d, dir entry FileName='%s'\n", Status, *BufferSize, FInfo->FileName);
|
||
|
} else {
|
||
|
DBG("= %r, *BufferSize=%d\n", Status, *BufferSize);
|
||
|
}
|
||
|
#endif
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.Write - Writes data to a file. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_Write(
|
||
|
IN EFI_FILE_PROTOCOL *This,
|
||
|
IN OUT UINTN *BufferSize,
|
||
|
IN VOID *Buffer
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
|
||
|
DBG("FSI_FP %p.Write(%d, %p) ", This, *BufferSize, Buffer);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->Write(FSIThis->TgtFP, BufferSize, Buffer);
|
||
|
} else if (FSIThis->SrcFP != NULL) {
|
||
|
// do it with source FP
|
||
|
Status = FSIThis->SrcFP->Write(FSIThis->SrcFP, BufferSize, Buffer);
|
||
|
}
|
||
|
DBG("= %r, *BufferSize=%d\n", Status, *BufferSize);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.SetPosition - Sets a file's current position. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_SetPosition(
|
||
|
IN EFI_FILE_PROTOCOL *This,
|
||
|
IN UINT64 Position
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
|
||
|
DBG("FSI_FP %p.SetPosition(%d) ", This, Position);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->SetPosition(FSIThis->TgtFP, Position);
|
||
|
}
|
||
|
if (FSIThis->SrcFP != NULL) {
|
||
|
// and with Src
|
||
|
Status = FSIThis->SrcFP->SetPosition(FSIThis->SrcFP, Position);
|
||
|
}
|
||
|
DBG("= %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.GetPosition - Returns a file's current position. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_GetPosition(
|
||
|
IN EFI_FILE_PROTOCOL *This,
|
||
|
IN UINT64 *Position
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
|
||
|
DBG("FSI_FP %p.GetPosition() ", This);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->GetPosition(FSIThis->TgtFP, Position);
|
||
|
} else if (FSIThis->SrcFP != NULL) {
|
||
|
// do it with source FP
|
||
|
Status = FSIThis->SrcFP->GetPosition(FSIThis->SrcFP, Position);
|
||
|
}
|
||
|
DBG("= %r, Position=%d\n", Status, Position);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.GetInfo - Returns information about a file. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_GetInfo(
|
||
|
IN EFI_FILE_PROTOCOL *This,
|
||
|
IN EFI_GUID *InformationType,
|
||
|
IN OUT UINTN *BufferSize,
|
||
|
OUT VOID *Buffer
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
EFI_FILE_INFO *FInfo;
|
||
|
|
||
|
DBG("FSI_FP %p.GetInfo(%s, %d, %p) ", This, GuidStr(InformationType), *BufferSize, Buffer);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->GetInfo(FSIThis->TgtFP, InformationType, BufferSize, Buffer);
|
||
|
if (Status == EFI_SUCCESS && CompareGuid(InformationType, &gEfiFileInfoGuid)) {
|
||
|
FInfo = (EFI_FILE_INFO *)Buffer;
|
||
|
FSIThis->IsDir = FInfo->Attribute & EFI_FILE_DIRECTORY;
|
||
|
}
|
||
|
} else if (FSIThis->SrcFP != NULL) {
|
||
|
// do it with source FP
|
||
|
Status = FSIThis->SrcFP->GetInfo(FSIThis->SrcFP, InformationType, BufferSize, Buffer);
|
||
|
if (Status == EFI_SUCCESS && CompareGuid(InformationType, &gEfiFileInfoGuid)) {
|
||
|
FInfo = (EFI_FILE_INFO *)Buffer;
|
||
|
FSIThis->IsDir = FInfo->Attribute & EFI_FILE_DIRECTORY;
|
||
|
}
|
||
|
}
|
||
|
DBG("= %r, BufferSize=%d\n", Status, *BufferSize);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.SetInfo - Sets information about a file. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_SetInfo(
|
||
|
IN EFI_FILE_PROTOCOL *This,
|
||
|
IN EFI_GUID *InformationType,
|
||
|
IN UINTN BufferSize,
|
||
|
IN VOID *Buffer
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
|
||
|
DBG("FSI_FP %p.SetInfo(%s, %d, %p) ", This, GuidStr(InformationType), BufferSize, Buffer);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->SetInfo(FSIThis->TgtFP, InformationType, BufferSize, Buffer);
|
||
|
} else if (FSIThis->SrcFP != NULL) {
|
||
|
// do it with source FP
|
||
|
Status = FSIThis->SrcFP->SetInfo(FSIThis->SrcFP, InformationType, BufferSize, Buffer);
|
||
|
}
|
||
|
DBG("= %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** EFI_FILE_PROTOCOL.Flush - Flushes all modified data associated with a file to a device. */
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_FP_Flush(
|
||
|
IN EFI_FILE_PROTOCOL *This
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
||
|
FSI_FILE_PROTOCOL *FSIThis;
|
||
|
|
||
|
DBG("FSI_FP %p.Flush() ", This);
|
||
|
|
||
|
FSIThis = FSI_FROM_FILE_PROTOCOL(This);
|
||
|
|
||
|
if (FSIThis->TgtFP != NULL) {
|
||
|
// do it with target FP
|
||
|
Status = FSIThis->TgtFP->Flush(FSIThis->TgtFP);
|
||
|
} else if (FSIThis->SrcFP != NULL) {
|
||
|
// do it with source FP
|
||
|
Status = FSIThis->SrcFP->Flush(FSIThis->SrcFP);
|
||
|
}
|
||
|
DBG("= %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/** Creates our FSI_FILE_PROTOCOL. */
|
||
|
FSI_FILE_PROTOCOL*
|
||
|
EFIAPI
|
||
|
CreateFSInjectFP(VOID)
|
||
|
{
|
||
|
FSI_FILE_PROTOCOL *FSINew;
|
||
|
|
||
|
// wrap it into our implementation
|
||
|
FSINew = AllocateZeroPool(sizeof(FSI_FILE_PROTOCOL));
|
||
|
if (FSINew == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
FSINew->Signature = FSI_FILE_PROTOCOL_SIGNATURE;
|
||
|
FSINew->FP.Revision = EFI_FILE_PROTOCOL_REVISION;
|
||
|
FSINew->FP.Open = FSI_FP_Open;
|
||
|
FSINew->FP.Close = FSI_FP_Close;
|
||
|
FSINew->FP.Delete = FSI_FP_Delete;
|
||
|
FSINew->FP.Read = FSI_FP_Read;
|
||
|
FSINew->FP.Write = FSI_FP_Write;
|
||
|
FSINew->FP.GetPosition = FSI_FP_GetPosition;
|
||
|
FSINew->FP.SetPosition = FSI_FP_SetPosition;
|
||
|
FSINew->FP.GetInfo = FSI_FP_GetInfo;
|
||
|
FSINew->FP.SetInfo = FSI_FP_SetInfo;
|
||
|
FSINew->FP.Flush = FSI_FP_Flush;
|
||
|
|
||
|
FSINew->FSI_FS = NULL;
|
||
|
FSINew->FName = NULL;
|
||
|
FSINew->IsDir = FALSE;
|
||
|
FSINew->TgtFP = NULL;
|
||
|
FSINew->SrcFP = NULL;
|
||
|
FSINew->FromTgt = FALSE;
|
||
|
|
||
|
return FSINew;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* FSI_SIMPLE_FILE_SYSTEM_PROTOCOL - our implementation of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
|
||
|
**************************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume implementation.
|
||
|
*/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSI_SFS_OpenVolume(
|
||
|
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
|
||
|
OUT EFI_FILE_PROTOCOL **Root
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
FSI_SIMPLE_FILE_SYSTEM_PROTOCOL *FSIThis;
|
||
|
FSI_FILE_PROTOCOL *FSINew;
|
||
|
|
||
|
DBG("FSI_FS %p.OpenVolume() ", This);
|
||
|
FSIThis = FSI_FROM_SIMPLE_FILE_SYSTEM(This);
|
||
|
|
||
|
// do it with target FS
|
||
|
Status = FSIThis->TgtFS->OpenVolume(FSIThis->TgtFS, Root);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("TgtFS->OpenVolume Status=%r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
// wrap it into our implementation
|
||
|
FSINew = CreateFSInjectFP();
|
||
|
if (FSINew == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
DBG("CreateFSInjectFP: %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
FSINew->FSI_FS = FSIThis; // saving reference to parent FS protocol
|
||
|
FSINew->FName = AllocateCopyPool(StrSize(L"\\"), L"\\");
|
||
|
FSINew->TgtFP = *Root;
|
||
|
FSINew->SrcFP = NULL;
|
||
|
FSINew->FromTgt = TRUE;
|
||
|
|
||
|
// set it as result
|
||
|
*Root = &FSINew->FP;
|
||
|
DBG("= %r, returning Root=%p\n", Status, *Root);
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* FSINJECTION_PROTOCOL
|
||
|
**************************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* FSINJECTION_PROTOCOL.Install implementation.
|
||
|
*/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSInjectionInstall (
|
||
|
IN EFI_HANDLE TgtHandle,
|
||
|
IN CHAR16 *TgtDir,
|
||
|
IN EFI_HANDLE SrcHandle,
|
||
|
IN CHAR16 *SrcDir,
|
||
|
IN FSI_STRING_LIST *Blacklist,
|
||
|
IN FSI_STRING_LIST *ForceLoadKexts
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *TgtFS;
|
||
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SrcFS;
|
||
|
FSI_SIMPLE_FILE_SYSTEM_PROTOCOL *OurFS;
|
||
|
|
||
|
|
||
|
DBG("FSInjectionInstall ...\n");
|
||
|
|
||
|
// get existing target EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
|
||
|
Status = gBS->OpenProtocol(TgtHandle, &gEfiSimpleFileSystemProtocolGuid, (void **)&TgtFS, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("- target OpenProtocol(gEfiSimpleFileSystemProtocolGuid): %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
// get existing source EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
|
||
|
Status = gBS->OpenProtocol(SrcHandle, &gEfiSimpleFileSystemProtocolGuid, (void **)&SrcFS, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("- source OpenProtocol(gEfiSimpleFileSystemProtocolGuid): %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
// create our implementation
|
||
|
OurFS = AllocateZeroPool(sizeof(FSI_SIMPLE_FILE_SYSTEM_PROTOCOL));
|
||
|
if (OurFS == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
DBG("- AllocateZeroPool(FSI_SIMPLE_FILE_SYSTEM_PROTOCOL): %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
OurFS->Signature = FSI_SIMPLE_FILE_SYSTEM_PROTOCOL_SIGNATURE;
|
||
|
OurFS->FS.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
|
||
|
OurFS->FS.OpenVolume = FSI_SFS_OpenVolume;
|
||
|
OurFS->TgtHandle = TgtHandle;
|
||
|
OurFS->TgtFS = TgtFS;
|
||
|
OurFS->TgtDir = AllocateCopyPool(StrSize(TgtDir), TgtDir);
|
||
|
if (OurFS->TgtDir == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
DBG("- AllocateCopyPool for TgtDir: %r\n", Status);
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
OurFS->SrcHandle = SrcHandle;
|
||
|
OurFS->SrcFS = SrcFS;
|
||
|
// we can run without SrcDir - no injection, but blocking caches for example
|
||
|
if (SrcDir != NULL) {
|
||
|
OurFS->SrcDir = AllocateCopyPool(StrSize(SrcDir), SrcDir);
|
||
|
if (OurFS->SrcDir == NULL) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
DBG("- AllocateCopyPool for TgtDir or SrcDir: %r\n", Status);
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Blacklist != NULL && !IsListEmpty(&Blacklist->List)) {
|
||
|
OurFS->Blacklist = Blacklist;
|
||
|
}
|
||
|
|
||
|
if (ForceLoadKexts != NULL && !IsListEmpty(&ForceLoadKexts->List)) {
|
||
|
OurFS->ForceLoadKexts = ForceLoadKexts;
|
||
|
}
|
||
|
|
||
|
// replace existing tagret EFI_SIMPLE_FILE_SYSTEM_PROTOCOL with out implementation
|
||
|
Status = gBS->ReinstallProtocolInterface(TgtHandle, &gEfiSimpleFileSystemProtocolGuid, TgtFS, &OurFS->FS);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("- ReinstallProtocolInterface(): %r\n", Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
DBG("- Our FSI_SIMPLE_FILE_SYSTEM_PROTOCOL installed on handle: %X\n", TgtHandle);
|
||
|
return EFI_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
if (OurFS->TgtDir != NULL) FreePool(OurFS->TgtDir);
|
||
|
if (OurFS->SrcDir != NULL) FreePool(OurFS->SrcDir);
|
||
|
FreePool(OurFS);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* FSINJECTION_PROTOCOL.CreateStringList() implementation.
|
||
|
*/
|
||
|
FSI_STRING_LIST *
|
||
|
EFIAPI
|
||
|
FSInjectionCreateStringList(VOID)
|
||
|
{
|
||
|
FSI_STRING_LIST *List;
|
||
|
|
||
|
List = AllocateZeroPool(sizeof(FSI_STRING_LIST));
|
||
|
if (List != NULL) {
|
||
|
InitializeListHead(&List->List);
|
||
|
}
|
||
|
return List;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* FSINJECTION_PROTOCOL.AddStringToList() implementation.
|
||
|
*/
|
||
|
FSI_STRING_LIST *
|
||
|
EFIAPI
|
||
|
FSInjectionAddStringToList(FSI_STRING_LIST *List, CHAR16 *String)
|
||
|
{
|
||
|
FSI_STRING_LIST_ENTRY *Entry;
|
||
|
UINTN StringSize;
|
||
|
|
||
|
if (List == NULL || String == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
StringSize = StrSize(String); // num of bytes, including null terminator
|
||
|
|
||
|
Entry = AllocateZeroPool(sizeof(FSI_STRING_LIST_ENTRY) + StringSize);
|
||
|
if (Entry != NULL) {
|
||
|
StrCpyS(Entry->String, StringSize/sizeof(CHAR16), String);
|
||
|
InsertTailList(&List->List, &Entry->List);
|
||
|
}
|
||
|
return Entry != NULL ? List : NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Installs FSINJECTION_PROTOCOL to na new handle/
|
||
|
*/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
InstallFSInjectionProtocol (VOID)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
FSINJECTION_PROTOCOL *FSInjection;
|
||
|
EFI_HANDLE FSIHandle;
|
||
|
|
||
|
// install FSINJECTION_PROTOCOL to new handle
|
||
|
FSInjection = AllocateZeroPool(sizeof(FSINJECTION_PROTOCOL));
|
||
|
if (FSInjection == NULL) {
|
||
|
DBG("Can not allocate memory for FSINJECTION_PROTOCOL\n");
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
FSInjection->Install = FSInjectionInstall;
|
||
|
FSInjection->CreateStringList = FSInjectionCreateStringList;
|
||
|
FSInjection->AddStringToList = FSInjectionAddStringToList;
|
||
|
FSIHandle = NULL; // install to new handle
|
||
|
Status = gBS->InstallMultipleProtocolInterfaces(&FSIHandle, &gFSInjectProtocolGuid, FSInjection, NULL);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
DBG("InstallFSInjectionProtocol: error installing FSINJECTION_PROTOCOL, Status = %r\n", Status);
|
||
|
}
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* Entry point
|
||
|
**************************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* FSInjection entry point. Installs FSINJECTION_PROTOCOL.
|
||
|
*/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
FSInjectEntrypoint (
|
||
|
IN EFI_HANDLE ImageHandle,
|
||
|
IN EFI_SYSTEM_TABLE *SystemTable
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
#if TEST
|
||
|
FSI_STRING_LIST *Blacklist;
|
||
|
#endif
|
||
|
|
||
|
Status = InstallFSInjectionProtocol();
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
#if TEST
|
||
|
Blacklist = FSInjectionCreateStringList();
|
||
|
|
||
|
// Caution! Do not add this list. If add this list, will see "Kernel cache load error (0xe)". This is just a guideline.
|
||
|
// by Sherlocks, 2017.11
|
||
|
|
||
|
// Installed/createinstallmedia
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\System\\Library\\PrelinkedKernels\\prelinkedkernel"); // 10.10+/10.13.4+
|
||
|
|
||
|
// Recovery
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.recovery.boot\\kernelcache"); // 10.7 - 10.10
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.recovery.boot\\prelinkedkernel"); // 10.11+
|
||
|
|
||
|
// BaseSytem/InstallESD
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\kernelcache"); // 10.7 - 10.9/(10.7/10.8)
|
||
|
|
||
|
// 1st stage - createinstallmedia
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\.IABootFiles\\kernelcache"); // 10.9/10.10
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\.IABootFiles\\prelinkedkernel"); // 10.11 - 10.13.3
|
||
|
|
||
|
// 2nd stage - InstallESD/AppStore/startosinstall
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\Mac OS X Install Data\\kernelcache"); // 10.7
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\OS X Install Data\\kernelcache"); // 10.8 - 10.10
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\OS X Install Data\\prelinkedkernel"); // 10.11
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\macOS Install Data\\prelinkedkernel"); // 10.12 - 10.12.3
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\macOS Install Data\\Locked Files\\Boot Files\\prelinkedkernel"); // 10.12.4+
|
||
|
|
||
|
// 2nd stage - Fusion Drive
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.boot.R\\System\\Library\\PrelinkedKernels\\prelinkedkernel"); // 10.11
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.boot.P\\System\\Library\\PrelinkedKernels\\prelinkedkernel"); // 10.11
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.boot.S\\System\\Library\\PrelinkedKernels\\prelinkedkernel"); // 10.11
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.boot.R\\prelinkedkernel"); // 10.12+
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.boot.P\\prelinkedkernel"); // 10.12+
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\com.apple.boot.S\\prelinkedkernel"); // 10.12+
|
||
|
|
||
|
// NetInstall
|
||
|
//FSInjectionAddStringToList(Blacklist, L"\\NetInstall macOS High Sierra.nbi\\i386\\x86_64\\kernelcache");
|
||
|
|
||
|
|
||
|
// Block Caches list
|
||
|
// InstallDVD/Installed
|
||
|
FSInjectionAddStringToList(Blacklist, L"\\System\\Library\\Caches\\com.apple.kext.caches\\Startup\\Extensions.mkext"); // 10.6
|
||
|
FSInjectionAddStringToList(Blacklist, L"\\System\\Library\\Extensions.mkext"); // 10.6
|
||
|
FSInjectionAddStringToList(Blacklist, L"\\System\\Library\\Caches\\com.apple.kext.caches\\Startup\\kernelcache"); // 10.6/10.6 - 10.9
|
||
|
|
||
|
Status = InstallTestFSinjection(L"\\Users\\dmazar", L"\\efi\\kext\\10_7", Blacklist, NULL);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|