OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
RelocationBlock.c
Go to the documentation of this file.
1
16#include "BootCompatInternal.h"
17
18#include <Guid/OcVariable.h>
20
21#include <Library/BaseLib.h>
22#include <Library/BaseMemoryLib.h>
23#include <Library/DebugLib.h>
26#include <Library/OcMachoLib.h>
27#include <Library/OcMemoryLib.h>
28#include <Library/OcMiscLib.h>
29#include <Library/OcStringLib.h>
30#include <Library/PrintLib.h>
31#include <Library/UefiBootServicesTableLib.h>
32#include <Library/UefiLib.h>
33#include <Library/UefiRuntimeServicesTableLib.h>
34
35STATIC CONST UINT8 mAsmRelocationCallGate[] = {
36 #include "RelocationCallGate.h"
37};
38
39EFI_STATUS
41 IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
42 IN EFI_GET_MEMORY_MAP GetMemoryMap,
43 IN EFI_ALLOCATE_PAGES AllocatePages,
44 IN UINTN NumberOfPages,
45 IN OUT EFI_PHYSICAL_ADDRESS *Memory
46 )
47{
48 EFI_STATUS Status;
49 UINTN EssentialSize;
50
51 EssentialSize = AppleSlideGetRelocationSize (BootCompat);
52 if (EssentialSize == 0) {
53 return EFI_UNSUPPORTED;
54 }
55
56 //
57 // Operating systems up to macOS 10.15 allocate starting with TEXT segment (2MB).
58 // macOS 11.0 allocates starting with HIB segment (1MB), but it does not support
59 // this quirk anyway due to AllocatePages in AllocateAddress mode Address pointer
60 // no longer being reread after the allocation in EfiBoot.
61 //
62 if (((*Memory == KERNEL_TEXT_PADDR) || (*Memory == KERNEL_TEXT_PADDR_LEGACY)) && (BootCompat->KernelState.RelocationBlock == 0)) {
63 BootCompat->KernelState.RelocationBlock = BASE_4GB;
64 BootCompat->KernelState.RelocationBlockLegacy = *Memory == KERNEL_TEXT_PADDR_LEGACY;
65 Status = OcAllocatePagesFromTop (
66 EfiLoaderData,
67 EFI_SIZE_TO_PAGES (EssentialSize),
68 &BootCompat->KernelState.RelocationBlock,
69 GetMemoryMap,
70 AllocatePages,
71 NULL
72 );
73 if (EFI_ERROR (Status)) {
74 DEBUG ((
75 DEBUG_INFO,
76 "OCABC: Relocation block (0x%Lx) allocation failure - %r\n",
77 EssentialSize,
78 Status
79 ));
80 return Status;
81 }
82
83 BootCompat->KernelState.RelocationBlockUsed = 0;
84 }
85
86 //
87 // macOS 10.4 and 10.5 do not request original lower addresses when allocating additional kernel structures (boot args, MKEXT, etc).
88 // If such addresses are requested within the range of our allocated relocation block, adjust them.
89 //
90 if ( (BootCompat->KernelState.RelocationBlock != 0)
91 && BootCompat->KernelState.RelocationBlockLegacy
92 && (*Memory > BootCompat->KernelState.RelocationBlock)
93 && (*Memory < BootCompat->KernelState.RelocationBlock + EssentialSize))
94 {
95 *Memory -= (BootCompat->KernelState.RelocationBlock - KERNEL_BASE_PADDR);
96 }
97
98 //
99 // Not our allocation.
100 //
101 if ( (BootCompat->KernelState.RelocationBlock == 0)
102 || (*Memory < KERNEL_BASE_PADDR)
103 || (*Memory >= KERNEL_BASE_PADDR + EssentialSize))
104 {
105 return EFI_INVALID_PARAMETER;
106 }
107
108 //
109 // Track actually occupied memory.
110 //
111 EssentialSize = (UINTN)(*Memory - KERNEL_BASE_PADDR + EFI_PAGES_TO_SIZE (NumberOfPages));
112 if (EssentialSize > BootCompat->KernelState.RelocationBlockUsed) {
113 BootCompat->KernelState.RelocationBlockUsed = EssentialSize;
114 }
115
116 //
117 // Assume that EfiBoot does not try to reallocate memory.
118 //
119 *Memory = *Memory - KERNEL_BASE_PADDR + BootCompat->KernelState.RelocationBlock;
120
121 return EFI_SUCCESS;
122}
123
124EFI_STATUS
126 IN OUT BOOT_COMPAT_CONTEXT *BootCompat
127 )
128{
129 EFI_STATUS Status;
130 UINTN EssentialSize;
131
132 EssentialSize = AppleSlideGetRelocationSize (BootCompat);
133 if (EssentialSize == 0) {
134 return EFI_UNSUPPORTED;
135 }
136
137 if (BootCompat->KernelState.RelocationBlock == 0) {
138 return EFI_INVALID_PARAMETER;
139 }
140
141 Status = gBS->FreePages (
142 BootCompat->KernelState.RelocationBlock,
143 EFI_SIZE_TO_PAGES (EssentialSize)
144 );
145
146 BootCompat->KernelState.RelocationBlock = 0;
147 BootCompat->KernelState.RelocationBlockUsed = 0;
148 BootCompat->KernelState.RelocationBlockLegacy = FALSE;
149
150 return Status;
151}
152
153EFI_STATUS
155 IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
156 IN OUT OC_BOOT_ARGUMENTS *BA
157 )
158{
159 EFI_STATUS Status;
160 UINTN MemoryMapSize;
161 UINTN DescriptorSize;
162 UINT32 DescriptorVersion;
163 EFI_MEMORY_DESCRIPTOR *MemoryMap;
164 EFI_PHYSICAL_ADDRESS KernelRTAddress;
165 UINTN NumEntries;
166 UINTN Index;
167 EFI_MEMORY_DESCRIPTOR *Desc;
168 UINTN BlockSize;
169 UINT8 *KernelRTBlock;
170
171 MemoryMapSize = *BA->MemoryMapSize;
172 DescriptorSize = *BA->MemoryMapDescriptorSize;
173 DescriptorVersion = *BA->MemoryMapDescriptorVersion;
174 MemoryMap = (EFI_MEMORY_DESCRIPTOR *)(UINTN)*BA->MemoryMap;
175 KernelRTAddress = EFI_PAGES_TO_SIZE (*BA->RuntimeServicesPG)
176 - (UINT32)(BootCompat->KernelState.RelocationBlock - KERNEL_BASE_PADDR);
177
178 //
179 // (1) Assign virtual addresses to all runtime blocks (but reserved).
180 //
181 Desc = MemoryMap;
182 NumEntries = MemoryMapSize / DescriptorSize;
183
184 for (Index = 0; Index < NumEntries; ++Index) {
185 BlockSize = EFI_PAGES_TO_SIZE ((UINTN)Desc->NumberOfPages);
186
187 if (Desc->Type == EfiReservedMemoryType) {
188 Desc->Attribute &= ~EFI_MEMORY_RUNTIME;
189 } else if ((Desc->Attribute & EFI_MEMORY_RUNTIME) != 0) {
190 Desc->VirtualStart = KernelRTAddress + KERNEL_STATIC_VADDR;
191 KernelRTAddress += BlockSize;
192 }
193
194 Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize);
195 }
196
197 //
198 // (2) Transition to virtual memory.
199 //
200 Status = gRT->SetVirtualAddressMap (
201 MemoryMapSize,
202 DescriptorSize,
203 DescriptorVersion,
204 MemoryMap
205 );
206
207 //
208 // (3) Perform quick dirty defragmentation similarly to EfiBoot to make vaddr = paddr
209 // for critical areas like EFI_SYSTEM_TABLE.
210 //
211 Desc = MemoryMap;
212
213 for (Index = 0; Index < NumEntries; ++Index) {
214 if ((Desc->Type == EfiRuntimeServicesCode) || (Desc->Type == EfiRuntimeServicesData)) {
215 //
216 // Get physical address from statically mapped virtual.
217 //
218 KernelRTBlock = (UINT8 *)(UINTN)(Desc->VirtualStart & (BASE_1GB - 1));
219 BlockSize = EFI_PAGES_TO_SIZE ((UINTN)Desc->NumberOfPages);
220 CopyMem (
221 KernelRTBlock + (BootCompat->KernelState.RelocationBlock - KERNEL_BASE_PADDR),
222 (VOID *)(UINTN)Desc->PhysicalStart,
224 );
225
226 ZeroMem ((VOID *)(UINTN)Desc->PhysicalStart, BlockSize);
227
228 //
229 // (4) Sync changes to EFI_SYSTEM_TABLE location with boot args.
230 //
231 if ( (Desc->PhysicalStart <= *BA->SystemTableP)
232 && (*BA->SystemTableP <= LAST_DESCRIPTOR_ADDR (Desc)))
233 {
234 *BA->SystemTableP = (UINT32)((UINTN)KernelRTBlock
235 + (*BA->SystemTableP - Desc->PhysicalStart)
236 + (BootCompat->KernelState.RelocationBlock - KERNEL_BASE_PADDR));
237 }
238
239 //
240 // Mark old RT block in MemMap as free memory and remove RT attribute.
241 //
242 Desc->Type = EfiConventionalMemory;
243 Desc->Attribute = Desc->Attribute & (~EFI_MEMORY_RUNTIME);
244 }
245
246 Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize);
247 }
248
249 return Status;
250}
251
252VOID
254 IN OUT BOOT_COMPAT_CONTEXT *BootCompat,
255 IN OUT OC_BOOT_ARGUMENTS *BA
256 )
257{
258 EFI_STATUS Status;
260 CHAR8 *PropName;
261 DTMemMapEntry *PropValue;
262 OpaqueDTPropertyIterator OPropIter;
263 DTPropertyIterator PropIter;
264 DTBooterKextFileInfo *BooterKextFileInfo;
265 DTBootxDriverInfo *BootxDriverInfo;
266 UINT32 MemoryMapSize;
267 UINT32 DescriptorSize;
268 EFI_MEMORY_DESCRIPTOR *MemoryMap;
269 UINT32 NumEntries;
270 UINT32 Index;
271 EFI_MEMORY_DESCRIPTOR *Desc;
272 EFI_PHYSICAL_ADDRESS PrevDescAddress;
273 UINT32 RelocDiff;
274
275 PropIter = &OPropIter;
276
277 RelocDiff = (UINT32)(BootCompat->KernelState.RelocationBlock - KERNEL_BASE_PADDR);
278
279 DTInit ((DTEntry)(UINTN)*BA->DeviceTreeP, BA->DeviceTreeLength);
280 Status = DTLookupEntry (NULL, "/chosen/memory-map", &MemMap);
281
282 if (!EFI_ERROR (Status)) {
283 Status = DTCreatePropertyIterator (MemMap, &OPropIter);
284 if (!EFI_ERROR (Status)) {
285 while (!EFI_ERROR (DTIterateProperties (PropIter, &PropName))) {
286 //
287 // /chosen/memory-map props have DTMemMapEntry values (address, length).
288 // We need to correct the addresses in matching types.
289 //
290
291 //
292 // Filter entries with different size right away.
293 //
294 if (PropIter->CurrentProperty->Length != sizeof (DTMemMapEntry)) {
295 continue;
296 }
297
298 //
299 // Filter enteries out of the relocation range.
300 //
301 PropValue = (DTMemMapEntry *)((UINT8 *)PropIter->CurrentProperty + sizeof (DTProperty));
302 if ( (PropValue->Address < BootCompat->KernelState.RelocationBlock)
303 || (PropValue->Address >= BootCompat->KernelState.RelocationBlock + BootCompat->KernelState.RelocationBlockUsed))
304 {
305 continue;
306 }
307
308 //
309 // Fix Driver-* entries for kexts used during a cacheless boot.
310 //
311 if (AsciiStrnCmp (PropName, DT_BOOTER_KEXT_PREFIX, L_STR_LEN (DT_BOOTER_KEXT_PREFIX)) == 0) {
312 //
313 // 10.6 and newer use a different format from 10.4 and 10.5.
314 //
315 if (!BootCompat->KernelState.RelocationBlockLegacy) {
316 BooterKextFileInfo = (DTBooterKextFileInfo *)((UINTN)PropValue->Address);
317 BooterKextFileInfo->InfoDictPhysAddr -= RelocDiff;
318 BooterKextFileInfo->ExecutablePhysAddr -= RelocDiff;
319 BooterKextFileInfo->BundlePathPhysAddr -= RelocDiff;
320 } else {
321 BootxDriverInfo = (DTBootxDriverInfo *)((UINTN)PropValue->Address);
322 BootxDriverInfo->PlistPhysAddr -= RelocDiff;
323 BootxDriverInfo->ModuleAddress -= RelocDiff;
324 }
325 }
326
327 //
328 // Patch the addresses up.
329 //
330 PropValue->Address -= RelocDiff;
331 }
332 }
333 }
334
335 //
336 // On macOS 10.4 and 10.5, EfiBoot has already set the system table address to the correct virtual one.
337 // The memory map also contains additional trashed entries that must be removed, this seems to occur on Macs as well.
338 //
339 // On macOS 10.6 and newer, we need to adjust it like the others.
340 //
341 if (!BootCompat->KernelState.RelocationBlockLegacy) {
342 *BA->SystemTableP -= RelocDiff;
343 } else {
344 MemoryMapSize = *BA->MemoryMapSize;
345 DescriptorSize = *BA->MemoryMapDescriptorSize;
346 MemoryMap = (EFI_MEMORY_DESCRIPTOR *)(UINTN)*BA->MemoryMap;
347
348 Desc = MemoryMap;
349 PrevDescAddress = Desc->PhysicalStart;
350 NumEntries = MemoryMapSize / DescriptorSize;
351
352 //
353 // Locate end of valid memory map. It is assumed that the entries are
354 // sorted smallest to largest (performed by AllowRelocationBlock or RebuildAppleMemoryMap).
355 //
356 for (Index = 0; Index < NumEntries; ++Index) {
357 if (Desc->PhysicalStart < PrevDescAddress) {
358 *BA->MemoryMapSize -= (DescriptorSize * (NumEntries - Index));
359 break;
360 }
361
362 PrevDescAddress = Desc->PhysicalStart;
363 Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize);
364 }
365 }
366
367 *BA->MemoryMap -= RelocDiff;
368 *BA->KernelAddrP -= RelocDiff;
369 *BA->RuntimeServicesPG -= EFI_SIZE_TO_PAGES (RelocDiff);
370 //
371 // Note, this one does not seem to be used by XNU but we set it anyway.
372 //
373 *BA->RuntimeServicesV = EFI_PAGES_TO_SIZE (*BA->RuntimeServicesPG) + KERNEL_STATIC_VADDR;
374 *BA->DeviceTreeP -= RelocDiff;
375}
376
377VOID
379 IN OUT UINTN *Args,
380 IN BOOT_COMPAT_CONTEXT *BootCompat,
381 IN KERNEL_CALL_GATE CallGate,
382 IN UINTN *KcgArg1,
383 IN UINTN KcgArg2
384 )
385{
386 UINT8 *Payload;
387 RELOCATION_CALL_GATE ReloGate;
388
389 //
390 // Shift kernel arguments back.
391 //
392 *Args -= (UINTN)(BootCompat->KernelState.RelocationBlock - KERNEL_BASE_PADDR);
393
394 //
395 // Provide copying payload that will not be overwritten.
396 //
397 Payload = (VOID *)(UINTN)CallGate;
398 Payload += ESTIMATED_CALL_GATE_SIZE;
400
401 //
402 // Transition to payload.
403 //
404 ReloGate = (RELOCATION_CALL_GATE)(UINTN)Payload;
405 ReloGate (
406 BootCompat->KernelState.RelocationBlockUsed / sizeof (UINT64),
407 KcgArg2,
408 BootCompat->KernelState.RelocationBlock,
409 *KcgArg1
410 );
411}
UINT16 BlockSize
Definition Apm.h:32
MEMMAP_DEVICE_PATH MemMap
UINTN(EFIAPI * KERNEL_CALL_GATE)(IN UINTN Arg1, IN UINTN Arg2)
#define KERNEL_STATIC_VADDR
UINTN(EFIAPI * RELOCATION_CALL_GATE)(IN UINTN QWordCount, IN UINTN EntryPoint, IN EFI_PHYSICAL_ADDRESS Source, IN UINTN Args)
UINTN AppleSlideGetRelocationSize(IN OUT BOOT_COMPAT_CONTEXT *BootCompat)
#define KERNEL_BASE_PADDR
#define KERNEL_TEXT_PADDR_LEGACY
#define KERNEL_TEXT_PADDR
#define ESTIMATED_CALL_GATE_SIZE
EFI_BOOT_SERVICES * gBS
EFI_STATUS DTLookupEntry(IN CONST DTEntry SearchPoint, IN CONST CHAR8 *PathName, IN DTEntry *FoundEntry)
VOID DTInit(IN VOID *Base, IN UINT32 *Length)
EFI_STATUS DTIterateProperties(IN DTPropertyIterator Iterator, IN CHAR8 **FoundProperty)
#define DT_BOOTER_KEXT_PREFIX
EFI_STATUS DTCreatePropertyIterator(IN CONST DTEntry Entry, IN DTPropertyIterator Iterator)
EFI_STATUS OcAllocatePagesFromTop(IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, IN OUT EFI_PHYSICAL_ADDRESS *Memory, IN EFI_GET_MEMORY_MAP GetMemoryMap OPTIONAL, IN EFI_ALLOCATE_PAGES AllocatePages OPTIONAL, IN CHECK_ALLOCATION_RANGE CheckRange OPTIONAL)
Definition MemoryAlloc.c:27
#define LAST_DESCRIPTOR_ADDR(Desc)
Definition OcMemoryLib.h:32
#define L_STR_LEN(String)
Definition OcStringLib.h:26
EFI_STATUS AppleRelocationRelease(IN OUT BOOT_COMPAT_CONTEXT *BootCompat)
EFI_STATUS AppleRelocationVirtualize(IN OUT BOOT_COMPAT_CONTEXT *BootCompat, IN OUT OC_BOOT_ARGUMENTS *BA)
VOID AppleRelocationRebase(IN OUT BOOT_COMPAT_CONTEXT *BootCompat, IN OUT OC_BOOT_ARGUMENTS *BA)
VOID AppleRelocationCallGate64(IN OUT UINTN *Args, IN BOOT_COMPAT_CONTEXT *BootCompat, IN KERNEL_CALL_GATE CallGate, IN UINTN *KcgArg1, IN UINTN KcgArg2)
EFI_STATUS AppleRelocationAllocatePages(IN OUT BOOT_COMPAT_CONTEXT *BootCompat, IN EFI_GET_MEMORY_MAP GetMemoryMap, IN EFI_ALLOCATE_PAGES AllocatePages, IN UINTN NumberOfPages, IN OUT EFI_PHYSICAL_ADDRESS *Memory)
STATIC CONST UINT8 mAsmRelocationCallGate[]
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
EFI_RUNTIME_SERVICES * gRT
UINT32 Address
UINT32 Length
Length (bytes) of folloing prop value.