mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-13 10:04:04 +01:00
359 lines
12 KiB
C
359 lines
12 KiB
C
/**
|
|
|
|
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/OcVariable.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;
|
|
}
|