OpenCore  1.0.4
OpenCore Bootloader
1.0.4
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
OcAppleRamDiskLib.c
Go to the documentation of this file.
1
15#include <Uefi.h>
16
17#include <Guid/OcVariable.h>
18
20
21#include <Library/BaseMemoryLib.h>
22#include <Library/BaseOverflowLib.h>
24#include <Library/MemoryAllocationLib.h>
26#include <Library/OcFileLib.h>
27#include <Library/OcMemoryLib.h>
28#include <Library/OcMiscLib.h>
29#include <Library/OcCryptoLib.h>
30#include <Library/UefiBootServicesTableLib.h>
31#include <Library/UefiRuntimeServicesTableLib.h>
32#include <Library/UefiLib.h>
33
34#define INTERNAL_ASSERT_EXTENT_TABLE_VALID(ExtentTable) \
35 ASSERT ((ExtentTable)->Signature == APPLE_RAM_DISK_EXTENT_SIGNATURE); \
36 ASSERT ((ExtentTable)->Version == APPLE_RAM_DISK_EXTENT_VERSION); \
37 ASSERT ((ExtentTable)->Reserved == 0); \
38 ASSERT ((ExtentTable)->Signature2 == APPLE_RAM_DISK_EXTENT_SIGNATURE); \
39 ASSERT ((ExtentTable)->ExtentCount > 0); \
40 ASSERT ((ExtentTable)->ExtentCount <= ARRAY_SIZE ((ExtentTable)->Extents))
41
50STATIC
51VOID
54 IN EFI_PHYSICAL_ADDRESS AllocatedArea,
55 IN UINTN AllocatedAreaSize
56 )
57{
59
60 ASSERT (AllocatedArea + AllocatedAreaSize - 1 <= MAX_UINTN);
61 ASSERT (AllocatedAreaSize >= EFI_PAGE_SIZE);
62
63 ZeroMem (
64 (VOID *)(UINTN)AllocatedArea,
65 EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (AllocatedAreaSize))
66 );
67
68 if (*ExtentTable == NULL) {
69 *ExtentTable = (APPLE_RAM_DISK_EXTENT_TABLE *)(UINTN)AllocatedArea;
70
71 (*ExtentTable)->Signature = APPLE_RAM_DISK_EXTENT_SIGNATURE;
72 (*ExtentTable)->Version = APPLE_RAM_DISK_EXTENT_VERSION;
73 (*ExtentTable)->Signature2 = APPLE_RAM_DISK_EXTENT_SIGNATURE;
74
75 AllocatedArea += EFI_PAGE_SIZE;
76 AllocatedAreaSize -= EFI_PAGE_SIZE;
77
79 sizeof (APPLE_RAM_DISK_EXTENT_TABLE) == EFI_PAGE_SIZE,
80 "Extent table different from EFI_PAGE_SIZE is unsupported!"
81 );
82
83 if (AllocatedAreaSize == 0) {
84 return;
85 }
86 }
87
88 Table = *ExtentTable;
89 Table->Extents[Table->ExtentCount].Start = AllocatedArea;
90 Table->Extents[Table->ExtentCount].Length = AllocatedAreaSize;
91 ++Table->ExtentCount;
92}
93
110STATIC
111UINTN
113 IN EFI_PHYSICAL_ADDRESS BaseAddress,
114 IN EFI_PHYSICAL_ADDRESS TopAddress,
115 IN EFI_MEMORY_TYPE MemoryType,
116 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
117 IN UINTN MemoryMapSize,
118 IN UINTN DescriptorSize,
119 IN UINTN RemainingSize,
121 )
122{
123 EFI_STATUS Status;
124 EFI_MEMORY_DESCRIPTOR *EntryWalker;
125 EFI_MEMORY_DESCRIPTOR *BiggestEntry;
126 EFI_PHYSICAL_ADDRESS AllocatedArea;
127 UINT64 BiggestSize;
128 UINT64 UsedSize;
129 UINTN FinalUsedSize;
130
131 //
132 // Require page aligned base and top addresses.
133 //
134 ASSERT (BaseAddress == EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (BaseAddress)));
135 ASSERT (TopAddress == EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (TopAddress)));
136
137 while (RemainingSize > 0 && ( *ExtentTable == NULL
138 || (*ExtentTable)->ExtentCount < ARRAY_SIZE ((*ExtentTable)->Extents)))
139 {
140 BiggestEntry = NULL;
141 BiggestSize = 0;
142
143 for (
144 EntryWalker = MemoryMap;
145 (UINT8 *)EntryWalker < ((UINT8 *)MemoryMap + MemoryMapSize);
146 EntryWalker = NEXT_MEMORY_DESCRIPTOR (EntryWalker, DescriptorSize))
147 {
148 //
149 // FIXME: This currently skips segments starting before BaseAddress but potentially lasting
150 // further: 0, PhysicalStart, BaseAddress, PhysicalEnd, infinity. This was done intentionally,
151 // to avoid splitting one entry into two, when TopAddress is before PhysicalEnd, but can still
152 // be improved.
153 //
154 if ( (EntryWalker->Type != EfiConventionalMemory)
155 || (EntryWalker->PhysicalStart < BaseAddress)
156 || (EntryWalker->PhysicalStart >= TopAddress))
157 {
158 continue;
159 }
160
161 UsedSize = EFI_PAGES_TO_SIZE (EntryWalker->NumberOfPages);
162 if (EntryWalker->PhysicalStart + UsedSize > TopAddress) {
163 //
164 // Guaranteed to be page aligned as TopAddress is page aligned.
165 //
166 UsedSize = TopAddress - EntryWalker->PhysicalStart;
167 }
168
169 if ((BiggestEntry == NULL) || (UsedSize > BiggestSize)) {
170 BiggestEntry = EntryWalker;
171 BiggestSize = UsedSize;
172 }
173 }
174
175 if ((BiggestEntry == NULL) || (BiggestSize == 0)) {
176 DEBUG ((
177 DEBUG_INFO,
178 "OCRAM: No entry for allocation %p / 0x%Lx bytes, rem 0x%Lx in 0x%Lx:0x%Lx\n",
179 BiggestEntry,
180 (UINT64)BiggestSize,
181 (UINT64)RemainingSize,
182 (UINT64)BaseAddress,
183 (UINT64)TopAddress
184 ));
185 return RemainingSize;
186 }
187
188 FinalUsedSize = (UINTN)MIN (BiggestSize, RemainingSize);
189
190 AllocatedArea = BiggestEntry->PhysicalStart;
191 Status = gBS->AllocatePages (
192 AllocateAddress,
193 MemoryType,
194 EFI_SIZE_TO_PAGES (FinalUsedSize),
195 &AllocatedArea
196 );
197
198 if (EFI_ERROR (Status)) {
199 DEBUG ((
200 DEBUG_INFO,
201 "OCRAM: Broken allocator for 0x%Lx in 0x%Lx bytes, rem 0x%Lx - %r\n",
202 (UINT64)BiggestEntry->PhysicalStart,
203 (UINT64)FinalUsedSize,
204 (UINT64)RemainingSize,
205 Status
206 ));
207 return RemainingSize;
208 }
209
210 InternalAddAllocatedArea (ExtentTable, AllocatedArea, FinalUsedSize);
211
212 RemainingSize -= FinalUsedSize;
213
214 BiggestEntry->PhysicalStart += EFI_SIZE_TO_PAGES (FinalUsedSize);
215 BiggestEntry->NumberOfPages -= EFI_SIZE_TO_PAGES (FinalUsedSize);
216 }
217
218 return RemainingSize;
219}
220
235STATIC
238 IN UINTN Size,
239 IN EFI_MEMORY_TYPE MemoryType,
240 IN BOOLEAN PreferHighMem
241 )
242{
243 BOOLEAN Result;
244 UINTN MemoryMapSize;
245 EFI_MEMORY_DESCRIPTOR *MemoryMap;
246 UINTN DescriptorSize;
247 UINTN RemainingSize;
249
250 MemoryMap = OcGetCurrentMemoryMap (&MemoryMapSize, &DescriptorSize, NULL, NULL, NULL, FALSE);
251 if (MemoryMap == NULL) {
252 return NULL;
253 }
254
256 sizeof (APPLE_RAM_DISK_EXTENT_TABLE) == EFI_PAGE_SIZE,
257 "Extent table different from EFI_PAGE_SIZE is unsupported!"
258 );
259
260 Result = BaseOverflowAddUN (Size, EFI_PAGE_SIZE, &RemainingSize);
261 if (Result) {
262 return NULL;
263 }
264
265 ExtentTable = NULL;
266
267 //
268 // We implement PreferHighMem to avoid colliding with the kernel, which sits
269 // in the lower addresses (see more detail in AptioMemoryFix) depending on
270 // KASLR offset generated randomly or with slide boot argument.
271 //
272 if (PreferHighMem) {
273 RemainingSize = InternalAllocateRemainingSize (
274 BASE_4GB,
275 BASE_8EB,
276 MemoryType,
277 MemoryMap,
278 MemoryMapSize,
279 DescriptorSize,
280 RemainingSize,
282 );
283 }
284
285 //
286 // This may be tried improved when PreferHighMem is FALSE.
287 // 1. One way is implement a bugtracking algorithm, similar to DF algo originally in place.
288 // The allocations will go recursively from biggest >= BASE_4G until the strategy fails.
289 // When it does, last allocation is discarded, and the strategy tries lower in this case.
290 // memory until success.
291 // The implementation must be very careful about avoiding recursion and high complexity.
292 // 2. Another way could be to try to avoid allocating in kernel area specifically, though
293 // allowing to allocate in lower memory. This has a backdash of allocation failures
294 // when extent amount exceeds the limit, and may want a third pass. It may also collide
295 // with firmware pool allocator.
296 //
297 // None of those (or any extra) are implemented, as with PreferHighMem = TRUE it should
298 // always succeed allocating in higher memory on any host with >= 8 GB of RAM, which is
299 // the requirement for modern macOS.
300 //
301 RemainingSize = InternalAllocateRemainingSize (
302 0,
303 BASE_8EB,
304 MemoryType,
305 MemoryMap,
306 MemoryMapSize,
307 DescriptorSize,
308 RemainingSize,
310 );
311
312 if ((RemainingSize > 0) && (ExtentTable != NULL)) {
314
315 ExtentTable = NULL;
316 }
317
318 return ExtentTable;
319}
320
323 IN UINTN Size,
324 IN EFI_MEMORY_TYPE MemoryType
325 )
326{
328
329 //
330 // Try to allocate preferrably above BASE_4GB to avoid colliding with the kernel.
331 //
332 ExtentTable = InternalAppleRamDiskAllocate (Size, MemoryType, TRUE);
333
334 if (ExtentTable == NULL) {
335 //
336 // Being here means that we exceeded entry amount in the extent table.
337 // Retry with any addresses. Should never happen in reality.
338 //
339 ExtentTable = InternalAppleRamDiskAllocate (Size, MemoryType, FALSE);
340 }
341
342 DEBUG ((
344 "OCRAM: Extent allocation of %u bytes (%x) gave %p\n",
345 (UINT32)Size,
346 (UINT32)MemoryType,
348 ));
349
350 return ExtentTable;
351}
352
353BOOLEAN
356 IN UINTN Offset,
357 IN UINTN Size,
358 OUT VOID *Buffer
359 )
360{
361 UINT8 *BufferBytes;
362
363 UINT32 Index;
364 CONST APPLE_RAM_DISK_EXTENT *Extent;
365
366 UINTN CurrentOffset;
367 UINTN LocalOffset;
368 UINTN LocalSize;
369
370 ASSERT (ExtentTable != NULL);
372 ASSERT (Size > 0);
373 ASSERT (Buffer != NULL);
374
375 BufferBytes = Buffer;
376 //
377 // As per the allocation algorithm, the sum over all Extent->Length must be
378 // smaller than MAX_UINTN.
379 //
380 for (
381 Index = 0, CurrentOffset = 0;
382 Index < ExtentTable->ExtentCount;
383 ++Index, CurrentOffset += (UINTN)Extent->Length
384 )
385 {
386 Extent = &ExtentTable->Extents[Index];
387 ASSERT (Extent->Start <= MAX_UINTN);
388 ASSERT (Extent->Length <= MAX_UINTN);
389
390 if ((Offset >= CurrentOffset) && ((Offset - CurrentOffset) < Extent->Length)) {
391 LocalOffset = (Offset - CurrentOffset);
392 LocalSize = (UINTN)MIN ((Extent->Length - LocalOffset), Size);
393 CopyMem (
394 BufferBytes,
395 (VOID *)((UINTN)Extent->Start + LocalOffset),
396 LocalSize
397 );
398
399 Size -= LocalSize;
400 if (Size == 0) {
401 return TRUE;
402 }
403
404 BufferBytes += LocalSize;
405 Offset += LocalSize;
406 }
407 }
408
409 return FALSE;
410}
411
412BOOLEAN
415 IN UINTN Offset,
416 IN UINTN Size,
417 IN CONST VOID *Buffer
418 )
419{
420 CONST UINT8 *BufferBytes;
421
422 UINT32 Index;
423 CONST APPLE_RAM_DISK_EXTENT *Extent;
424
425 UINTN CurrentOffset;
426 UINTN LocalOffset;
427 UINTN LocalSize;
428
429 ASSERT (ExtentTable != NULL);
431 ASSERT (Size > 0);
432 ASSERT (Buffer != NULL);
433
434 BufferBytes = Buffer;
435 //
436 // As per the allocation algorithm, the sum over all Extent->Length must be
437 // smaller than MAX_UINTN.
438 //
439 for (
440 Index = 0, CurrentOffset = 0;
441 Index < ExtentTable->ExtentCount;
442 ++Index, CurrentOffset += (UINTN)Extent->Length
443 )
444 {
445 Extent = &ExtentTable->Extents[Index];
446 ASSERT (Extent->Start <= MAX_UINTN);
447 ASSERT (Extent->Length <= MAX_UINTN);
448
449 if ((Offset >= CurrentOffset) && ((Offset - CurrentOffset) < Extent->Length)) {
450 LocalOffset = (Offset - CurrentOffset);
451 LocalSize = (UINTN)MIN ((Extent->Length - LocalOffset), Size);
452 CopyMem (
453 (VOID *)((UINTN)Extent->Start + LocalOffset),
454 BufferBytes,
455 LocalSize
456 );
457
458 Size -= LocalSize;
459 if (Size == 0) {
460 return TRUE;
461 }
462
463 BufferBytes += LocalSize;
464 Offset += LocalSize;
465 }
466 }
467
468 return FALSE;
469}
470
471BOOLEAN
474 IN EFI_FILE_PROTOCOL *File,
475 IN UINTN FileSize
476 )
477{
478 EFI_STATUS Status;
479 UINT64 FilePosition;
480 UINT32 Index;
481 UINTN RequestedSize;
482 UINTN ReadSize;
483 UINTN ExtentSize;
484 SHA256_CONTEXT Ctx;
485 UINT8 Digest[SHA256_DIGEST_SIZE];
486 UINT8 *TmpBuffer;
487 UINT8 *ExtentBuffer;
488
489 ASSERT (ExtentTable != NULL);
491 ASSERT (File != NULL);
492 ASSERT (FileSize > 0);
493
494 //
495 // We need a temporary buffer in lower addresses as several motherboards on APTIO IV,
496 // e.g. GA-Z77P-D3 (rev. 1.1), GA-Z87X-UD4H, etc. fail to read directly to high addresses
497 // when using FAT filesystem. The original workaround to this was AvoidHighAlloc quirk.
498 // REF: https://github.com/acidanthera/bugtracker/issues/449
499 //
500 TmpBuffer = AllocatePool (BASE_4MB);
501 if (TmpBuffer == NULL) {
502 return FALSE;
503 }
504
505 DEBUG_CODE_BEGIN ();
506 Sha256Init (&Ctx);
507 DEBUG_CODE_END ();
508
509 FilePosition = 0;
510
511 for (Index = 0; Index < ExtentTable->ExtentCount && FileSize > 0; ++Index) {
512 ASSERT (ExtentTable->Extents[Index].Start <= MAX_UINTN);
513 ASSERT (ExtentTable->Extents[Index].Length <= MAX_UINTN);
514
515 ExtentBuffer = (VOID *)(UINTN)ExtentTable->Extents[Index].Start;
516 ExtentSize = (UINTN)ExtentTable->Extents[Index].Length;
517
518 while (FileSize > 0 && ExtentSize > 0) {
519 Status = File->SetPosition (File, FilePosition);
520 if (EFI_ERROR (Status)) {
521 FreePool (TmpBuffer);
522 return FALSE;
523 }
524
525 RequestedSize = MIN (MIN (BASE_4MB, FileSize), ExtentSize);
526 ReadSize = RequestedSize;
527 Status = File->Read (
528 File,
529 &ReadSize,
530 TmpBuffer
531 );
532 if (EFI_ERROR (Status) || (RequestedSize != ReadSize)) {
533 FreePool (TmpBuffer);
534 return FALSE;
535 }
536
537 DEBUG_CODE_BEGIN ();
538 Sha256Update (&Ctx, TmpBuffer, ReadSize);
539 DEBUG_CODE_END ();
540
541 CopyMem (ExtentBuffer, TmpBuffer, ReadSize);
542
543 FilePosition += ReadSize;
544 ExtentBuffer += ReadSize;
545 ExtentSize -= ReadSize;
546 FileSize -= ReadSize;
547 }
548 }
549
550 FreePool (TmpBuffer);
551
552 //
553 // Not enough extents.
554 //
555 if (FileSize != 0) {
556 return FALSE;
557 }
558
559 DEBUG_CODE_BEGIN ();
560 Sha256Final (&Ctx, Digest);
561 DEBUG ((
562 DEBUG_INFO,
563 "OCRAM: SHA-256 Digest is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
564 Digest[0],
565 Digest[1],
566 Digest[2],
567 Digest[3],
568 Digest[4],
569 Digest[5],
570 Digest[6],
571 Digest[7],
572 Digest[8],
573 Digest[9],
574 Digest[10],
575 Digest[11],
576 Digest[12],
577 Digest[13],
578 Digest[14],
579 Digest[15],
580 Digest[16],
581 Digest[17],
582 Digest[18],
583 Digest[19],
584 Digest[20],
585 Digest[21],
586 Digest[22],
587 Digest[23],
588 Digest[24],
589 Digest[25],
590 Digest[26],
591 Digest[27],
592 Digest[28],
593 Digest[29],
594 Digest[30],
595 Digest[31]
596 ));
597 DEBUG_CODE_END ();
598 return TRUE;
599}
600
601VOID
604 )
605{
606 UINT32 Index;
607
608 ASSERT (ExtentTable != NULL);
610 ASSERT (ExtentTable->Extents[0].Start <= MAX_UINTN);
611 ASSERT (ExtentTable->Extents[0].Length <= MAX_UINTN);
612
613 //
614 // Extents are allocated in the first page.
615 //
616 for (Index = 1; Index < ExtentTable->ExtentCount; ++Index) {
617 ASSERT (ExtentTable->Extents[Index].Start <= MAX_UINTN);
618 ASSERT (ExtentTable->Extents[Index].Length <= MAX_UINTN);
619
620 gBS->FreePages (
621 (UINTN)ExtentTable->Extents[Index].Start,
622 (UINTN)EFI_SIZE_TO_PAGES (ExtentTable->Extents[Index].Length)
623 );
624 }
625
626 //
627 // One page is added to account for the header.
628 //
629 gBS->FreePages (
630 (UINTN)ExtentTable,
631 (UINTN)EFI_SIZE_TO_PAGES (ExtentTable->Extents[0].Length) + 1
632 );
633}
#define ARRAY_SIZE(Array)
Definition AppleMacEfi.h:34
PACKED struct @53 APPLE_RAM_DISK_EXTENT
#define APPLE_RAM_DISK_EXTENT_SIGNATURE
APPLE_RAM_DISK_EXTENT_TABLE ExtentTable
#define APPLE_RAM_DISK_EXTENT_VERSION
PACKED struct @54 APPLE_RAM_DISK_EXTENT_TABLE
DMG_SIZE_DEVICE_PATH Size
BOOLEAN OcAppleRamDiskWrite(IN CONST APPLE_RAM_DISK_EXTENT_TABLE *ExtentTable, IN UINTN Offset, IN UINTN Size, IN CONST VOID *Buffer)
STATIC UINTN InternalAllocateRemainingSize(IN EFI_PHYSICAL_ADDRESS BaseAddress, IN EFI_PHYSICAL_ADDRESS TopAddress, IN EFI_MEMORY_TYPE MemoryType, IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, IN UINTN MemoryMapSize, IN UINTN DescriptorSize, IN UINTN RemainingSize, IN OUT APPLE_RAM_DISK_EXTENT_TABLE **ExtentTable)
VOID OcAppleRamDiskFree(IN CONST APPLE_RAM_DISK_EXTENT_TABLE *ExtentTable)
CONST APPLE_RAM_DISK_EXTENT_TABLE * OcAppleRamDiskAllocate(IN UINTN Size, IN EFI_MEMORY_TYPE MemoryType)
STATIC VOID InternalAddAllocatedArea(IN OUT APPLE_RAM_DISK_EXTENT_TABLE **ExtentTable, IN EFI_PHYSICAL_ADDRESS AllocatedArea, IN UINTN AllocatedAreaSize)
BOOLEAN OcAppleRamDiskLoadFile(IN CONST APPLE_RAM_DISK_EXTENT_TABLE *ExtentTable, IN EFI_FILE_PROTOCOL *File, IN UINTN FileSize)
STATIC CONST APPLE_RAM_DISK_EXTENT_TABLE * InternalAppleRamDiskAllocate(IN UINTN Size, IN EFI_MEMORY_TYPE MemoryType, IN BOOLEAN PreferHighMem)
#define INTERNAL_ASSERT_EXTENT_TABLE_VALID(ExtentTable)
BOOLEAN OcAppleRamDiskRead(IN CONST APPLE_RAM_DISK_EXTENT_TABLE *ExtentTable, IN UINTN Offset, IN UINTN Size, OUT VOID *Buffer)
STATIC_ASSERT(BYTES_PER_PIXEL==sizeof(UINT32), "Non 4-byte pixels are unsupported!")
EFI_BOOT_SERVICES * gBS
VOID Sha256Init(SHA256_CONTEXT *Context)
#define SHA256_DIGEST_SIZE
Definition OcCryptoLib.h:45
VOID Sha256Final(SHA256_CONTEXT *Context, UINT8 *HashDigest)
VOID Sha256Update(SHA256_CONTEXT *Context, CONST UINT8 *Data, UINTN Len)
#define DEBUG_BULK_INFO
EFI_MEMORY_DESCRIPTOR * OcGetCurrentMemoryMap(OUT UINTN *MemoryMapSize, OUT UINTN *DescriptorSize, OUT UINTN *MapKey OPTIONAL, OUT UINT32 *DescriptorVersion OPTIONAL, OUT UINTN *OriginalMemoryMapSize OPTIONAL, IN BOOLEAN IncludeSplitSpace)
Definition MemoryMap.c:116
OC_TYPING_BUFFER_ENTRY Buffer[OC_TYPING_BUFFER_SIZE]
Definition OcTypingLib.h:42
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
#define ASSERT(x)
Definition coder.h:55
#define MIN(a, b)
Definition deflate.c:1673