mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-31 17:37:42 +01:00
cd23181296
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
754 lines
25 KiB
C
754 lines
25 KiB
C
/** @file
|
|
Copyright (C) 2013, dmazar. All rights reserved.
|
|
Copyright (C) 2019, vit9696. 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 "BootCompatInternal.h"
|
|
|
|
#include <Guid/OcVariable.h>
|
|
#include <IndustryStandard/AppleHibernate.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/OcBootManagementLib.h>
|
|
#include <Library/DeviceTreeLib.h>
|
|
#include <Library/MachoLib.h>
|
|
#include <Library/OcMemoryLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/OcStringLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
|
|
/**
|
|
Protect RT data from boot.efi relocation by marking them MemMapIO.
|
|
See more details in the function definition.
|
|
|
|
@param[in,out] RtReloc Relocation entry list to store entry types.
|
|
@param[in] MemoryMapSize Memory map size.
|
|
@param[in] DescriptorSize Memory map descriptor size.
|
|
@param[in,out] MemoryMap MemoryMap to protect entries in.
|
|
@param[in] SysTableArea Special address that should not be protected.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
ProtectRtMemoryFromRelocation (
|
|
IN OUT RT_RELOC_PROTECT_DATA *RtReloc,
|
|
IN UINTN MemoryMapSize,
|
|
IN UINTN DescriptorSize,
|
|
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
|
|
IN EFI_PHYSICAL_ADDRESS SysTableArea,
|
|
IN UINTN SysTableAreaSize
|
|
)
|
|
{
|
|
//
|
|
// We protect RT data & code from relocation by marking them MemMapIO except EFI_SYSTEM_TABLE area.
|
|
//
|
|
// 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 SMM handler through previously negotiated buffer
|
|
// and this buffer must not be relocated.
|
|
// Explained and examined in detail by CodeRush and night199uk:
|
|
// https://web.archive.org/web/20141025080709/http://www.projectosx.com/forum/lofiversion/index.php/t3298.html
|
|
//
|
|
// Starting with APTIO V for NVRAM to work not only RT data but RT code 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 in a separate driver.
|
|
// Explained in detail by Download-Fritz and vit9696:
|
|
// http://www.insanelymac.com/forum/topic/331381-aptiomemoryfix (first 2 links in particular).
|
|
//
|
|
// EFI_SYSTEM_TABLE is passed directly through kernel boot arguments, and thus goes through static
|
|
// mapping (ml_static_ptovirt) in efi_set_tables_64 call. This mapping works as PHYS | CONST = VIRT.
|
|
// To avoid kernel accessing unmapped virtual address we let boot.efi relocate the page with
|
|
// EFI_SYSTEM_TABLE area. While technically it is possible to let the original page to be relocated,
|
|
// we pick a safer root by using a private copy.
|
|
//
|
|
// The primary downside of this approach is that boot.efi will still reserve the contiguous memory
|
|
// for runtime services after the kernel: efiRuntimeServicesPageCount pages starting from
|
|
// efiRuntimeServicesPageStart within kaddr ~ ksize range. However, unlike Macs, which have reserved
|
|
// gaps only for ACPI NVS, MemMapIO and similar regions, with this approach almost no physical memory
|
|
// in efiRuntimeServicesPageStart area is used at all. This memory is never reclaimed by XNU, which
|
|
// marks it as allocated in i386_vm_init. Expirements show that at least 85 MBs (Z170) are used for
|
|
// this process. On server systems the issue is much worse due to many devices in place.
|
|
// Ideally boot.efi should only count RT code and RT data pages, but it is not easy to change.
|
|
//
|
|
|
|
UINTN NumEntries;
|
|
UINTN Index;
|
|
EFI_MEMORY_DESCRIPTOR *Desc;
|
|
RT_RELOC_PROTECT_INFO *RelocInfo;
|
|
|
|
Desc = MemoryMap;
|
|
RtReloc->NumEntries = 0;
|
|
RelocInfo = &RtReloc->RelocInfo[0];
|
|
NumEntries = MemoryMapSize / DescriptorSize;
|
|
|
|
for (Index = 0; Index < NumEntries; ++Index) {
|
|
if ((Desc->Attribute & EFI_MEMORY_RUNTIME) != 0
|
|
&& Desc->NumberOfPages > 0
|
|
&& (Desc->Type == EfiRuntimeServicesCode || Desc->Type == EfiRuntimeServicesData)
|
|
&& !AREA_WITHIN_DESCRIPTOR (Desc, SysTableArea, SysTableAreaSize)) {
|
|
|
|
if (RtReloc->NumEntries == ARRAY_SIZE (RtReloc->RelocInfo)) {
|
|
RUNTIME_DEBUG ((
|
|
DEBUG_ERROR,
|
|
"OCABC: Cannot save mem type for entry: %Lx (type 0x%x)\n",
|
|
(UINT64) Desc->PhysicalStart,
|
|
(UINT32) Desc->Type
|
|
));
|
|
return;
|
|
}
|
|
|
|
RelocInfo->PhysicalStart = Desc->PhysicalStart;
|
|
RelocInfo->PhysicalEnd = LAST_DESCRIPTOR_ADDR (Desc);
|
|
RelocInfo->Type = Desc->Type;
|
|
Desc->Type = EfiMemoryMappedIO;
|
|
++RelocInfo;
|
|
++RtReloc->NumEntries;
|
|
}
|
|
|
|
Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Copy RT flagged areas to separate memmap, define virtual to physical address mapping,
|
|
and call SetVirtualAddressMap() only with that partial memmap.
|
|
|
|
@param[in,out] KernelState Kernel support state.
|
|
@param[in] MemoryMapSize Memory map size.
|
|
@param[in] DescriptorSize Memory map descriptor size.
|
|
@param[in] DescriptorVersion Memor map descriptor version.
|
|
@param[in,out] MemoryMap Complete memory map with all entries.
|
|
|
|
@retval EFI_SUCCESS on success.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
PerformRtMemoryVirtualMapping (
|
|
IN OUT KERNEL_SUPPORT_STATE *KernelState,
|
|
IN UINTN MemoryMapSize,
|
|
IN UINTN DescriptorSize,
|
|
IN UINT32 DescriptorVersion,
|
|
IN EFI_MEMORY_DESCRIPTOR *MemoryMap
|
|
)
|
|
{
|
|
//
|
|
// 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 physical mappings:
|
|
// Also adds virtual to physical 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.
|
|
//
|
|
|
|
UINTN NumEntries;
|
|
UINTN Index;
|
|
EFI_MEMORY_DESCRIPTOR *Desc;
|
|
EFI_MEMORY_DESCRIPTOR *VirtualDesc;
|
|
EFI_STATUS Status;
|
|
PAGE_MAP_AND_DIRECTORY_POINTER *PageTable;
|
|
|
|
Desc = MemoryMap;
|
|
NumEntries = MemoryMapSize / DescriptorSize;
|
|
VirtualDesc = KernelState->VmMap;
|
|
KernelState->VmMapSize = 0;
|
|
KernelState->VmMapDescSize = DescriptorSize;
|
|
|
|
//
|
|
// Get current VM page table.
|
|
//
|
|
PageTable = OcGetCurrentPageTable (NULL);
|
|
|
|
for (Index = 0; Index < NumEntries; ++Index) {
|
|
//
|
|
// Legacy note. 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, our legacy code had a bug, and did not
|
|
// check for EfiReservedMemoryType. Therefore it replaced such entries 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/
|
|
//
|
|
// The correct approach is to properly handle EfiReservedMemoryType with EFI_MEMORY_RUNTIME
|
|
// attribute set, and not mess with the memory map passed to boot.efi. As done here.
|
|
//
|
|
if (Desc->Type != EfiReservedMemoryType && (Desc->Attribute & EFI_MEMORY_RUNTIME) != 0) {
|
|
//
|
|
// Check if there is enough space in virtual map.
|
|
//
|
|
if (KernelState->VmMapSize + DescriptorSize > sizeof (KernelState->VmMap)) {
|
|
RUNTIME_DEBUG ((DEBUG_ERROR, "OCABC: Too many RT entries to memory map\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Copy region with EFI_MEMORY_RUNTIME flag to virtual map.
|
|
//
|
|
CopyMem (VirtualDesc, Desc, DescriptorSize);
|
|
|
|
//
|
|
// Define virtual to physical mapping.
|
|
//
|
|
Status = VmMapVirtualPages (
|
|
&KernelState->VmContext,
|
|
PageTable,
|
|
Desc->VirtualStart,
|
|
Desc->NumberOfPages,
|
|
Desc->PhysicalStart
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
RUNTIME_DEBUG ((DEBUG_ERROR, "OCABC: RT mapping failure - %r\n", Status));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Proceed to next virtual map slot.
|
|
//
|
|
VirtualDesc = NEXT_MEMORY_DESCRIPTOR (VirtualDesc, DescriptorSize);
|
|
KernelState->VmMapSize += DescriptorSize;
|
|
}
|
|
|
|
//
|
|
// Proceed to next original map slot.
|
|
//
|
|
Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize);
|
|
}
|
|
|
|
VmFlushCaches ();
|
|
|
|
Status = gRT->SetVirtualAddressMap (
|
|
KernelState->VmMapSize,
|
|
DescriptorSize,
|
|
DescriptorVersion,
|
|
KernelState->VmMap
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Revert RT data protected types to let XNU kernel kernel properly map data.
|
|
|
|
@param[in] RtReloc Relocated entry list with entry types.
|
|
@param[in] MemoryMapSize Memory map size.
|
|
@param[in] DescriptorSize Memory map descriptor size.
|
|
@param[in,out] MemoryMap MemoryMap to restore protected entries in.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
RestoreProtectedRtMemoryTypes (
|
|
IN RT_RELOC_PROTECT_DATA *RtReloc,
|
|
IN UINTN MemoryMapSize,
|
|
IN UINTN DescriptorSize,
|
|
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
UINTN NumEntriesLeft;
|
|
UINTN NumEntries;
|
|
EFI_PHYSICAL_ADDRESS PhysicalStart;
|
|
EFI_PHYSICAL_ADDRESS PhysicalEnd;
|
|
EFI_MEMORY_DESCRIPTOR *Desc;
|
|
|
|
NumEntriesLeft = RtReloc->NumEntries;
|
|
NumEntries = MemoryMapSize / DescriptorSize;
|
|
Desc = MemoryMap;
|
|
|
|
for (Index = 0; Index < NumEntries && NumEntriesLeft > 0; ++Index) {
|
|
PhysicalStart = Desc->PhysicalStart;
|
|
PhysicalEnd = LAST_DESCRIPTOR_ADDR (Desc);
|
|
|
|
for (Index2 = 0; Index2 < RtReloc->NumEntries; ++Index2) {
|
|
//
|
|
// PhysicalStart match is enough, but just in case.
|
|
// Select firmwares, like Lenovo ThinkPad X240, have insane reserved areas.
|
|
// For example 0000000000000000-FFFFFFFFFFFFFFFF 0000000000000000 0000000000000000.
|
|
// Any fuzzy matching is prone to errors, so just do exact comparison.
|
|
//
|
|
if (PhysicalStart == RtReloc->RelocInfo[Index2].PhysicalStart
|
|
&& PhysicalEnd == RtReloc->RelocInfo[Index2].PhysicalEnd) {
|
|
Desc->Type = RtReloc->RelocInfo[Index2].Type;
|
|
--NumEntriesLeft;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize);
|
|
}
|
|
|
|
if (NumEntriesLeft > 0) {
|
|
RUNTIME_DEBUG ((
|
|
DEBUG_ERROR,
|
|
"OCABC: Failed to restore %u entries out of %u\n",
|
|
(UINT32) NumEntriesLeft,
|
|
(UINT32) RtReloc->NumEntries
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Prepare environment for normal booting. Called when boot.efi jumps to kernel.
|
|
|
|
@param[in,out] BootCompat Boot compatibility context.
|
|
@param[in,out] BootArgs Apple kernel boot arguments.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
AppleMapPrepareForBooting (
|
|
IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
|
|
IN OUT VOID *BootArgs
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
DTEntry Chosen;
|
|
CHAR8 *ArgsStr;
|
|
UINT32 ArgsSize;
|
|
OC_BOOT_ARGUMENTS BA;
|
|
UINTN MemoryMapSize;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMap;
|
|
UINTN DescriptorSize;
|
|
|
|
OcParseBootArgs (&BA, BootArgs);
|
|
|
|
if (BootCompat->Settings.ProvideCustomSlide) {
|
|
//
|
|
// Restore the variables we tampered with to support custom slides.
|
|
//
|
|
AppleSlideRestore (BootCompat, &BA);
|
|
}
|
|
|
|
if (BootCompat->Settings.DisableSingleUser) {
|
|
//
|
|
// First, there is a BootArgs entry for XNU.
|
|
//
|
|
OcRemoveArgumentFromCmd (BA.CommandLine, "-s");
|
|
|
|
//
|
|
// Second, there is a DT entry.
|
|
//
|
|
DTInit ((VOID *)(UINTN) *BA.DeviceTreeP, BA.DeviceTreeLength);
|
|
Status = DTLookupEntry (NULL, "/chosen", &Chosen);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = DTGetProperty (Chosen, "boot-args", (VOID **) &ArgsStr, &ArgsSize);
|
|
if (!EFI_ERROR(Status) && ArgsSize > 0) {
|
|
OcRemoveArgumentFromCmd (ArgsStr, "-s");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BootCompat->Settings.AvoidRuntimeDefrag) {
|
|
MemoryMapSize = *BA.MemoryMapSize;
|
|
MemoryMap = (EFI_MEMORY_DESCRIPTOR *)(UINTN) (*BA.MemoryMap);
|
|
DescriptorSize = *BA.MemoryMapDescriptorSize;
|
|
|
|
//
|
|
// We must restore EfiRuntimeServicesCode memory area types, because otherwise
|
|
// RuntimeServices won't be mapped.
|
|
//
|
|
RestoreProtectedRtMemoryTypes (
|
|
&BootCompat->RtReloc,
|
|
MemoryMapSize,
|
|
DescriptorSize,
|
|
MemoryMap
|
|
);
|
|
|
|
//
|
|
// On native Macs due to EfiBoot defragmentation it is guaranteed that
|
|
// VADDR % BASE_1GB == PADDR. macOS 11 started to rely on this in
|
|
// acpi_count_enabled_logical_processors, which needs to access MADT (APIC)
|
|
// ACPI table, and does that through ConfigurationTables.
|
|
//
|
|
// The simplest approach is to just copy the table, so that it is accessible
|
|
// at both actual mapping and 1:1 defragmented mapping. This should be safe,
|
|
// as the memory for 1:1 defragmented mapping is reserved by EfiBoot in the
|
|
// first place and is otherwise stolen anyway.
|
|
//
|
|
if (BootCompat->KernelState.ConfigurationTable != NULL) {
|
|
CopyMem (
|
|
(VOID*) ((UINTN) BA.SystemTable->ConfigurationTable & (BASE_1GB - 1)),
|
|
BootCompat->KernelState.ConfigurationTable,
|
|
sizeof (*BootCompat->KernelState.ConfigurationTable) * BA.SystemTable->NumberOfTableEntries
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Prepare environment for hibernate wake. Called when boot.efi jumps to kernel.
|
|
|
|
@param[in,out] BootCompat Boot compatibility context.
|
|
@param[in,out] ImageHeaderPage Apple hibernate image page number.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
AppleMapPrepareForHibernateWake (
|
|
IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
|
|
IN UINTN ImageHeaderPage
|
|
)
|
|
{
|
|
IOHibernateImageHeader *ImageHeader;
|
|
IOHibernateHandoff *Handoff;
|
|
|
|
ImageHeader = (IOHibernateImageHeader *) EFI_PAGES_TO_SIZE (ImageHeaderPage);
|
|
|
|
//
|
|
// Legacy note. In legacy implementations systemTableOffset was unconditionally overwritten
|
|
// with a wrong address due to ImageHeader->runtimePages not being converted from pages to bytes.
|
|
// Fortunately systemTableOffset was unused when kIOHibernateHandoffTypeMemoryMap is unspecified.
|
|
// systemTableOffset is calculated properly by boot.efi itself starting from 10.6.8 at least,
|
|
// and thus this assignment was useless in the first place.
|
|
//
|
|
|
|
//
|
|
// At this step we have two routes.
|
|
//
|
|
// 1. Remove newly generated memory map from hibernate image to let XNU use the original mapping.
|
|
// This is known to work well on most systems primarily because Windows requires UEFI firmwares
|
|
// to preserve physical memory consistency at S4 wake. "On a UEFI platform, firmware runtime memory
|
|
// must be consistent across S4 sleep state transitions, in both size and location.", see:
|
|
// https://docs.microsoft.com/en-us/windows-hardware/design/device-experiences/oem-uefi#hibernation-state-s4-transition-requirements
|
|
// 2. Recover memory map just as we do for normal booting. This was causing issues on some firmwares,
|
|
// which provided very strange memory maps after S4 wake. In other cases this should not immediately
|
|
// break things. XNU will entirely remove efiRuntimeServicesPageStart/efiRuntimeServicesPageSize
|
|
// mapping, and our new memory map entries will unconditionally overwrite previous ones. In case
|
|
// no physical memory changes happened this should work fine.
|
|
//
|
|
Handoff = (IOHibernateHandoff *) EFI_PAGES_TO_SIZE ((UINTN) ImageHeader->handoffPages);
|
|
while (Handoff->type != kIOHibernateHandoffTypeEnd) {
|
|
if (Handoff->type == kIOHibernateHandoffTypeMemoryMap) {
|
|
if (BootCompat->Settings.DiscardHibernateMap) {
|
|
//
|
|
// Route 1. Discard the new memory map here, and let XNU use what it had.
|
|
// It is unknown whether there still are any firmwares that need this.
|
|
//
|
|
Handoff->type = kIOHibernateHandoffType;
|
|
} else {
|
|
//
|
|
// Route 2. Recovery memory protection types just as normal boot.
|
|
//
|
|
|
|
if (BootCompat->KernelState.VmMapDescSize == 0) {
|
|
RUNTIME_DEBUG ((DEBUG_ERROR, "OCABC: Saved descriptor size cannot be 0\n"));
|
|
return;
|
|
}
|
|
|
|
if (BootCompat->Settings.AvoidRuntimeDefrag) {
|
|
//
|
|
// I think we should not be there, but ideally all quirks are relatively independent.
|
|
//
|
|
RestoreProtectedRtMemoryTypes (
|
|
&BootCompat->RtReloc,
|
|
Handoff->bytecount,
|
|
BootCompat->KernelState.VmMapDescSize,
|
|
(EFI_MEMORY_DESCRIPTOR *)(UINTN) Handoff->data
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Handoff = (IOHibernateHandoff *) ((UINTN) Handoff + sizeof(Handoff) + Handoff->bytecount);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AppleMapPrepareMemoryPool (
|
|
IN OUT BOOT_COMPAT_CONTEXT *BootCompat
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
if (!BootCompat->Settings.SetupVirtualMap
|
|
|| BootCompat->KernelState.VmContext.MemoryPool != NULL) {
|
|
return;
|
|
}
|
|
|
|
Status = VmAllocateMemoryPool (
|
|
&BootCompat->KernelState.VmContext,
|
|
OC_DEFAULT_VMEM_PAGE_COUNT,
|
|
BootCompat->ServicePtrs.GetMemoryMap
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "OCABC: Memory pool allocation failure - %r\n", Status));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AppleMapPrepareBooterState (
|
|
IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
|
|
IN OUT EFI_LOADED_IMAGE *LoadedImage,
|
|
IN EFI_GET_MEMORY_MAP GetMemoryMap OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Allocate memory pool if needed.
|
|
//
|
|
AppleMapPrepareMemoryPool (
|
|
BootCompat
|
|
);
|
|
|
|
//
|
|
// This function may be called twice, do not redo in this case.
|
|
//
|
|
AppleMapPlatformSaveState (
|
|
&BootCompat->KernelState.AsmState,
|
|
&BootCompat->KernelState.KernelJump
|
|
);
|
|
|
|
if (BootCompat->Settings.AvoidRuntimeDefrag) {
|
|
if (BootCompat->KernelState.SysTableRtArea == 0) {
|
|
//
|
|
// Allocate RT data pages for copy of UEFI system table for kernel.
|
|
// This one also has to be 32-bit due to XNU BootArgs structure.
|
|
// The reason for this allocation to be required is because XNU uses static
|
|
// mapping for directly passed pointers (see ProtectRtMemoryFromRelocation).
|
|
//
|
|
BootCompat->KernelState.SysTableRtArea = BASE_4GB;
|
|
BootCompat->KernelState.SysTableRtAreaSize = gST->Hdr.HeaderSize;
|
|
Status = OcAllocatePagesFromTop (
|
|
EfiRuntimeServicesData,
|
|
EFI_SIZE_TO_PAGES (gST->Hdr.HeaderSize),
|
|
&BootCompat->KernelState.SysTableRtArea,
|
|
GetMemoryMap,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"OCABC: Failed to allocate system table memory - %r\n",
|
|
Status
|
|
));
|
|
BootCompat->KernelState.SysTableRtArea = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Copy UEFI system table to the new location.
|
|
//
|
|
CopyMem (
|
|
(VOID *)(UINTN) BootCompat->KernelState.SysTableRtArea,
|
|
gST,
|
|
gST->Hdr.HeaderSize
|
|
);
|
|
//
|
|
// Remember physical configuration table location.
|
|
//
|
|
BootCompat->KernelState.ConfigurationTable = gST->ConfigurationTable;
|
|
}
|
|
|
|
//
|
|
// Assign loaded image with custom system table.
|
|
//
|
|
LoadedImage->SystemTable =
|
|
(EFI_SYSTEM_TABLE *)(UINTN) BootCompat->KernelState.SysTableRtArea;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AppleMapPrepareKernelJump (
|
|
IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
|
|
IN UINTN ImageAddress,
|
|
IN BOOLEAN AppleHibernateWake
|
|
)
|
|
{
|
|
UINT64 KernelEntryVaddr;
|
|
UINT32 KernelEntry;
|
|
IOHibernateImageHeader *ImageHeader;
|
|
|
|
//
|
|
// There is no reason to patch the kernel when we do not need it.
|
|
//
|
|
if (!BootCompat->Settings.AvoidRuntimeDefrag && !BootCompat->Settings.DiscardHibernateMap) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check whether we have image address and abort if not.
|
|
//
|
|
if (ImageAddress == 0) {
|
|
RUNTIME_DEBUG ((DEBUG_ERROR, "OCABC: Failed to find image address, hibernate %d\n", AppleHibernateWake));
|
|
return;
|
|
}
|
|
|
|
if (!AppleHibernateWake) {
|
|
//
|
|
// ImageAddress points to the first kernel segment, __HIB.
|
|
// Kernel image header is located in __TEXT, which follows __HIB.
|
|
//
|
|
ImageAddress += KERNEL_BASE_PADDR;
|
|
|
|
//
|
|
// Cut higher virtual address bits.
|
|
//
|
|
KernelEntryVaddr = MachoRuntimeGetEntryAddress (
|
|
(VOID*) ImageAddress
|
|
);
|
|
if (KernelEntryVaddr == 0) {
|
|
RUNTIME_DEBUG ((DEBUG_ERROR, "OCABC: Kernel entry point was not found!"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Perform virtual to physical address conversion by subtracting __TEXT base
|
|
// and adding current physical kernel location.
|
|
//
|
|
KernelEntry = (UINT32) (KernelEntryVaddr - KERNEL_TEXT_VADDR + ImageAddress);
|
|
} else {
|
|
//
|
|
// Read kernel entry from hibernation image and patch it with jump.
|
|
// 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.
|
|
// Note the virtual -> physical conversion through truncation.
|
|
//
|
|
ImageHeader = (IOHibernateImageHeader *) ImageAddress;
|
|
KernelEntry = ((UINT32)(UINTN) &ImageHeader->fileExtentMap[0])
|
|
+ ImageHeader->fileExtentMapSize + ImageHeader->restore1CodeOffset;
|
|
}
|
|
|
|
//
|
|
// Save original kernel entry code.
|
|
//
|
|
CopyMem (
|
|
&BootCompat->KernelState.KernelOrg[0],
|
|
(VOID *)(UINTN) KernelEntry,
|
|
sizeof (BootCompat->KernelState.KernelOrg)
|
|
);
|
|
|
|
//
|
|
// Copy kernel jump code to kernel entry address.
|
|
//
|
|
CopyMem (
|
|
(VOID *)(UINTN) KernelEntry,
|
|
&BootCompat->KernelState.KernelJump,
|
|
sizeof (BootCompat->KernelState.KernelJump)
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
AppleMapPrepareMemState (
|
|
IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
|
|
IN UINTN MemoryMapSize,
|
|
IN UINTN DescriptorSize,
|
|
IN UINT32 DescriptorVersion,
|
|
IN EFI_MEMORY_DESCRIPTOR *MemoryMap
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Protect RT areas from relocation by marking then MemMapIO.
|
|
//
|
|
if (BootCompat->Settings.AvoidRuntimeDefrag) {
|
|
ProtectRtMemoryFromRelocation (
|
|
&BootCompat->RtReloc,
|
|
MemoryMapSize,
|
|
DescriptorSize,
|
|
MemoryMap,
|
|
BootCompat->KernelState.SysTableRtArea,
|
|
BootCompat->KernelState.SysTableRtAreaSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Virtualize RT services with all needed fixes.
|
|
//
|
|
if (BootCompat->Settings.SetupVirtualMap) {
|
|
Status = PerformRtMemoryVirtualMapping (
|
|
&BootCompat->KernelState,
|
|
MemoryMapSize,
|
|
DescriptorSize,
|
|
DescriptorVersion,
|
|
MemoryMap
|
|
);
|
|
} else {
|
|
Status = gRT->SetVirtualAddressMap (
|
|
MemoryMapSize,
|
|
DescriptorSize,
|
|
DescriptorVersion,
|
|
MemoryMap
|
|
);
|
|
}
|
|
|
|
//
|
|
// Copy now virtualized UEFI system table for boot.efi to hand it to the kernel.
|
|
//
|
|
if (BootCompat->Settings.AvoidRuntimeDefrag) {
|
|
CopyMem (
|
|
(VOID *)(UINTN) BootCompat->KernelState.SysTableRtArea,
|
|
gST,
|
|
gST->Hdr.HeaderSize
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
UINTN
|
|
EFIAPI
|
|
AppleMapPrepareKernelState (
|
|
IN UINTN Args,
|
|
IN BOOLEAN ModeX64
|
|
)
|
|
{
|
|
BOOT_COMPAT_CONTEXT *BootCompatContext;
|
|
|
|
BootCompatContext = GetBootCompatContext ();
|
|
|
|
if (BootCompatContext->ServiceState.AppleHibernateWake) {
|
|
AppleMapPrepareForHibernateWake (
|
|
BootCompatContext,
|
|
Args
|
|
);
|
|
} else {
|
|
AppleMapPrepareForBooting (
|
|
BootCompatContext,
|
|
(VOID *) Args
|
|
);
|
|
}
|
|
|
|
//
|
|
// Restore original kernel entry code.
|
|
//
|
|
CopyMem (
|
|
BootCompatContext->KernelState.AsmState.KernelEntry,
|
|
&BootCompatContext->KernelState.KernelOrg[0],
|
|
sizeof (BootCompatContext->KernelState.KernelOrg)
|
|
);
|
|
|
|
return Args;
|
|
}
|