OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
KernelReader.c
Go to the documentation of this file.
1
15#include <Uefi.h>
16
19
20#include <Library/BaseLib.h>
21#include <Library/BaseOverflowLib.h>
22#include <Library/DebugLib.h>
23#include <Library/MemoryAllocationLib.h>
26#include <Library/OcCryptoLib.h>
27#include <Library/OcFileLib.h>
28
29//
30// Pick a reasonable maximum to fit.
31//
32#define KERNEL_HEADER_SIZE (EFI_PAGE_SIZE * 2)
33
36STATIC BOOLEAN mNeedKernelDigest;
37
43
44STATIC
45EFI_STATUS
47 IN UINT32 TargetSize,
48 IN OUT UINT8 **Buffer,
49 OUT UINT32 *AllocatedSize,
50 IN UINT32 ReservedSize
51 )
52{
53 UINT8 *TmpBuffer;
54
55 if (BaseOverflowAddU32 (TargetSize, ReservedSize, &TargetSize)) {
56 return EFI_INVALID_PARAMETER;
57 }
58
59 if (*AllocatedSize >= TargetSize) {
60 return EFI_SUCCESS;
61 }
62
63 TmpBuffer = AllocatePool (TargetSize);
64 if (TmpBuffer == NULL) {
65 return EFI_OUT_OF_RESOURCES;
66 }
67
68 FreePool (*Buffer);
69 *Buffer = TmpBuffer;
70 *AllocatedSize = TargetSize;
71
72 return EFI_SUCCESS;
73}
74
75STATIC
76EFI_STATUS
78 IN EFI_FILE_PROTOCOL *File,
79 IN UINT32 Position,
80 IN UINT32 Size,
81 OUT UINT8 *Buffer
82 )
83{
84 EFI_STATUS Status;
85 UINT32 RemainingSize;
86 UINT32 ChunkSize;
87
88 //
89 // Calculate hash for the prefix.
90 //
91 if (mNeedKernelDigest && (Position > mKernelDigestPosition)) {
92 RemainingSize = Position - mKernelDigestPosition;
93
94 while (RemainingSize > 0) {
95 ChunkSize = MIN (RemainingSize, Size);
96 Status = OcGetFileData (
97 File,
99 ChunkSize,
100 Buffer
101 );
102 if (EFI_ERROR (Status)) {
103 return Status;
104 }
105
107 mKernelDigestPosition += ChunkSize;
108 RemainingSize -= ChunkSize;
109 }
110 }
111
112 Status = OcGetFileData (
113 File,
114 Position,
115 Size,
116 Buffer
117 );
118
119 if (EFI_ERROR (Status)) {
120 return Status;
121 }
122
123 //
124 // Calculate hash for the suffix.
125 //
126 if (mNeedKernelDigest && (Position >= mKernelDigestPosition)) {
127 RemainingSize = Position + Size - mKernelDigestPosition;
130 Buffer + (Position - mKernelDigestPosition),
131 RemainingSize
132 );
133 mKernelDigestPosition += RemainingSize;
134 }
135
136 return Status;
137}
138
139STATIC
140EFI_STATUS
142 IN EFI_FILE_PROTOCOL *File,
143 IN BOOLEAN Prefer32Bit,
144 IN UINT8 *Buffer,
145 IN UINT32 BufferSize,
146 OUT BOOLEAN *Is32Bit,
147 OUT UINT32 *FatOffset,
148 OUT UINT32 *FatSize
149 )
150{
151 EFI_STATUS Status;
152 UINT32 FileSize;
153
154 Status = OcGetFileSize (File, &FileSize);
155 if (EFI_ERROR (Status)) {
156 return Status;
157 }
158
159 if (BufferSize >= FileSize) {
160 return EFI_INVALID_PARAMETER;
161 }
162
163 Status = FatGetArchitectureOffset (
164 Buffer,
165 BufferSize,
166 FileSize,
167 Prefer32Bit ? MachCpuTypeI386 : MachCpuTypeX8664,
168 FatOffset,
169 FatSize
170 );
171 *Is32Bit = Prefer32Bit;
172
173 //
174 // If desired arch is not found, retry with inverse.
175 //
176 if (Status == EFI_NOT_FOUND) {
177 Status = FatGetArchitectureOffset (
178 Buffer,
179 BufferSize,
180 FileSize,
181 !Prefer32Bit ? MachCpuTypeI386 : MachCpuTypeX8664,
182 FatOffset,
183 FatSize
184 );
185 *Is32Bit = !Prefer32Bit;
186 }
187
188 return Status;
189}
190
191STATIC
192UINT32
194 IN EFI_FILE_PROTOCOL *File,
195 IN OUT UINT8 **Buffer,
196 IN UINT32 Offset,
197 OUT UINT32 *AllocatedSize,
198 IN UINT32 ReservedSize
199 )
200{
201 EFI_STATUS Status;
202
203 UINT32 KernelSize;
204 MACH_COMP_HEADER *CompHeader;
205 UINT8 *CompressedBuffer;
206 UINT32 CompressionType;
207 UINT32 CompressedSize;
208 UINT32 DecompressedSize;
209 UINT32 DecompressedHash;
210
211 CompHeader = (MACH_COMP_HEADER *)*Buffer;
212 CompressionType = CompHeader->Compression;
213 CompressedSize = SwapBytes32 (CompHeader->Compressed);
214 DecompressedSize = SwapBytes32 (CompHeader->Decompressed);
215 DecompressedHash = SwapBytes32 (CompHeader->Hash);
216
217 KernelSize = 0;
218
219 if ( (CompressedSize > OC_COMPRESSION_MAX_LENGTH)
220 || (CompressedSize == 0)
221 || (DecompressedSize > OC_COMPRESSION_MAX_LENGTH)
222 || (DecompressedSize < KERNEL_HEADER_SIZE))
223 {
224 DEBUG ((DEBUG_INFO, "OCAK: Comp kernel invalid comp %u or decomp %u at %08X\n", CompressedSize, DecompressedSize, Offset));
225 return KernelSize;
226 }
227
228 Status = ReplaceBuffer (DecompressedSize, Buffer, AllocatedSize, ReservedSize);
229 if (EFI_ERROR (Status)) {
230 DEBUG ((DEBUG_INFO, "OCAK: Decomp kernel (%u bytes) cannot be allocated at %08X\n", DecompressedSize, Offset));
231 return KernelSize;
232 }
233
234 CompressedBuffer = AllocatePool (CompressedSize);
235 if (CompressedBuffer == NULL) {
236 DEBUG ((DEBUG_INFO, "OCAK: Comp kernel (%u bytes) cannot be allocated at %08X\n", CompressedSize, Offset));
237 return KernelSize;
238 }
239
240 Status = KernelGetFileData (File, Offset + sizeof (MACH_COMP_HEADER), CompressedSize, CompressedBuffer);
241 if (EFI_ERROR (Status)) {
242 DEBUG ((DEBUG_INFO, "OCAK: Comp kernel (%u bytes) cannot be read at %08X\n", CompressedSize, Offset));
243 FreePool (CompressedBuffer);
244 return KernelSize;
245 }
246
247 if (CompressionType == MACH_COMPRESSED_BINARY_INVERT_LZVN) {
248 KernelSize = (UINT32)DecompressLZVN (*Buffer, DecompressedSize, CompressedBuffer, CompressedSize);
249 } else if (CompressionType == MACH_COMPRESSED_BINARY_INVERT_LZSS) {
250 KernelSize = (UINT32)DecompressLZSS (*Buffer, DecompressedSize, CompressedBuffer, CompressedSize);
251 }
252
253 if (KernelSize != DecompressedSize) {
254 KernelSize = 0;
255 }
256
257 //
258 // TODO: implement adler32 hash verification.
259 //
260 (VOID)DecompressedHash;
261
262 FreePool (CompressedBuffer);
263
264 return KernelSize;
265}
266
267STATIC
268EFI_STATUS
270 IN EFI_FILE_PROTOCOL *File,
271 IN BOOLEAN Prefer32Bit,
272 IN OUT KERNEL_ARCH *Arch,
273 IN OUT UINT8 **Buffer,
274 OUT UINT32 *KernelSize,
275 OUT UINT32 *AllocatedSize,
276 IN UINT32 ReservedSize,
277 IN UINT32 Offset
278 )
279{
280 EFI_STATUS Status;
281 UINT32 *MagicPtr;
282 BOOLEAN ForbidFat;
283 BOOLEAN Compressed;
284 BOOLEAN Is32Bit;
285
286 Status = KernelGetFileData (File, Offset, KERNEL_HEADER_SIZE, *Buffer);
287 if (EFI_ERROR (Status)) {
288 return Status;
289 }
290
291 //
292 // Do not allow FAT architectures with Offset > 0 (recursion).
293 //
294 ForbidFat = Offset > 0;
295 Compressed = FALSE;
296
297 while (TRUE) {
298 if (!BASE_TYPE_ALIGNED (UINT32, *Buffer)) {
299 DEBUG ((DEBUG_INFO, "OCAK: Misaligned kernel header %p at %08X\n", *Buffer, Offset));
300 return EFI_INVALID_PARAMETER;
301 }
302
303 MagicPtr = (UINT32 *)*Buffer;
304
305 switch (*MagicPtr) {
308 //
309 // Ensure we have the desired arch.
310 //
311 if (*Arch == KernelArchUnknown) {
312 if (*MagicPtr == MACH_HEADER_SIGNATURE) {
313 *Arch = KernelArch32;
314 } else {
315 *Arch = KernelArch64;
316 }
317 }
318
319 Is32Bit = *Arch == KernelArch32;
320
321 if ( (Is32Bit && (*MagicPtr != MACH_HEADER_SIGNATURE))
322 || (!Is32Bit && (*MagicPtr != MACH_HEADER_64_SIGNATURE)))
323 {
324 return EFI_INVALID_PARAMETER;
325 }
326
327 DEBUG ((
328 DEBUG_VERBOSE,
329 "OCAK: Found %a Mach-O compressed %d offset %u size %u\n",
330 Is32Bit ? "32-bit" : "64-bit",
331 Compressed,
332 Offset,
333 *KernelSize
334 ));
335
336 //
337 // This is just a valid (formerly) compressed image.
338 //
339 if (Compressed) {
340 return EFI_SUCCESS;
341 }
342
343 //
344 // This is an uncompressed image, just fully read it.
345 //
346 if (Offset == 0) {
347 //
348 // Figure out size for a non fat image.
349 //
350 Status = OcGetFileSize (File, KernelSize);
351 if (EFI_ERROR (Status)) {
352 DEBUG ((DEBUG_INFO, "OCAK: Kernel size cannot be determined - %r\n", Status));
353 return EFI_OUT_OF_RESOURCES;
354 }
355
356 DEBUG ((DEBUG_VERBOSE, "OCAK: Determined kernel size is %u bytes\n", *KernelSize));
357 }
358
359 Status = ReplaceBuffer (*KernelSize, Buffer, AllocatedSize, ReservedSize);
360 if (EFI_ERROR (Status)) {
361 DEBUG ((DEBUG_INFO, "OCAK: Kernel (%u bytes) cannot be allocated at %08X\n", *KernelSize, Offset));
362 return Status;
363 }
364
365 Status = KernelGetFileData (File, Offset, *KernelSize, *Buffer);
366 if (EFI_ERROR (Status)) {
367 DEBUG ((DEBUG_INFO, "OCAK: Kernel (%u bytes) cannot be read at %08X\n", *KernelSize, Offset));
368 }
369
370 return Status;
373 {
374 if (ForbidFat) {
375 DEBUG ((DEBUG_INFO, "Fat kernel recursion %p at %08X\n", MagicPtr, Offset));
376 return EFI_INVALID_PARAMETER;
377 }
378
379 Status = ParseFatArchitecture (File, Prefer32Bit, *Buffer, KERNEL_HEADER_SIZE, &Is32Bit, &Offset, KernelSize);
380 if (EFI_ERROR (Status)) {
381 return Status;
382 }
383
384 *Arch = Is32Bit ? KernelArch32 : KernelArch64;
385 return ReadAppleKernelImage (File, Prefer32Bit, Arch, Buffer, KernelSize, AllocatedSize, ReservedSize, Offset);
386 }
388 {
389 if (Compressed) {
390 DEBUG ((DEBUG_INFO, "Compression recursion %p at %08X\n", MagicPtr, Offset));
391 return EFI_INVALID_PARAMETER;
392 }
393
394 //
395 // No FAT or Comp is allowed after compressed.
396 //
397 ForbidFat = Compressed = TRUE;
398
399 //
400 // Loop into updated image in Buffer.
401 //
402 *KernelSize = ParseCompressedHeader (File, Buffer, Offset, AllocatedSize, ReservedSize);
403 if (*KernelSize != 0) {
404 DEBUG ((DEBUG_VERBOSE, "OCAK: Compressed result has %08X magic\n", *(UINT32 *)Buffer));
405 continue;
406 }
407
408 return EFI_INVALID_PARAMETER;
409 }
410 default:
411 DEBUG ((Offset > 0 ? DEBUG_INFO : DEBUG_VERBOSE, "OCAK: Invalid kernel magic %08X at %08X\n", *MagicPtr, Offset));
412 return EFI_INVALID_PARAMETER;
413 }
414 }
415}
416
417EFI_STATUS
419 IN EFI_FILE_PROTOCOL *File,
420 IN BOOLEAN Prefer32Bit,
421 OUT BOOLEAN *Is32Bit,
422 OUT UINT8 **Kernel,
423 OUT UINT32 *KernelSize,
424 OUT UINT32 *AllocatedSize,
425 IN UINT32 ReservedSize,
426 OUT UINT8 *Digest OPTIONAL
427 )
428{
429 EFI_STATUS Status;
430 UINT32 FullSize;
431 UINT8 *Remainder;
432 KERNEL_ARCH Arch;
433
434 ASSERT (File != NULL);
435 ASSERT (Is32Bit != NULL);
436 ASSERT (Kernel != NULL);
437 ASSERT (KernelSize != NULL);
438 ASSERT (AllocatedSize != NULL);
439
440 *Is32Bit = FALSE;
441 *KernelSize = 0;
442 *AllocatedSize = KERNEL_HEADER_SIZE;
443 *Kernel = AllocatePool (*AllocatedSize);
444 Arch = KernelArchUnknown;
445
446 if (*Kernel == NULL) {
447 return EFI_OUT_OF_RESOURCES;
448 }
449
450 mNeedKernelDigest = Digest != NULL;
451 if (mNeedKernelDigest) {
454 }
455
456 Status = ReadAppleKernelImage (
457 File,
458 Prefer32Bit,
459 &Arch,
460 Kernel,
461 KernelSize,
462 AllocatedSize,
463 ReservedSize,
464 0
465 );
466
467 if (EFI_ERROR (Status)) {
468 FreePool (*Kernel);
469 mNeedKernelDigest = FALSE;
470 return Status;
471 }
472
473 *Is32Bit = Arch == KernelArch32;
474
475 if (mNeedKernelDigest) {
476 Status = OcGetFileSize (File, &FullSize);
477 if (EFI_ERROR (Status)) {
478 mNeedKernelDigest = FALSE;
479 FreePool (*Kernel);
480 return Status;
481 }
482
483 if (FullSize > mKernelDigestPosition) {
484 Remainder = AllocatePool (FullSize - mKernelDigestPosition);
485 if (Remainder == NULL) {
486 mNeedKernelDigest = FALSE;
487 FreePool (*Kernel);
488 return EFI_OUT_OF_RESOURCES;
489 }
490
491 Status = KernelGetFileData (
492 File,
494 FullSize - mKernelDigestPosition,
495 Remainder
496 );
497 mNeedKernelDigest = FALSE;
498 FreePool (Remainder);
499 if (EFI_ERROR (Status)) {
500 FreePool (*Kernel);
501 return Status;
502 }
503
504 ASSERT (FullSize == mKernelDigestPosition);
505 }
506
508 }
509
510 return EFI_SUCCESS;
511}
512
513EFI_STATUS
515 IN EFI_FILE_PROTOCOL *File,
516 IN BOOLEAN Prefer32Bit,
517 OUT UINT8 **Mkext,
518 OUT UINT32 *MkextSize,
519 OUT UINT32 *AllocatedSize,
520 IN UINT32 ReservedSize,
521 IN UINT32 NumReservedKexts
522 )
523{
524 EFI_STATUS Status;
525 UINT32 Offset;
526 BOOLEAN Is32Bit;
527 UINT8 *TmpMkext;
528 UINT32 TmpMkextSize;
529
530 ASSERT (File != NULL);
531 ASSERT (Mkext != NULL);
532 ASSERT (MkextSize != NULL);
533 ASSERT (AllocatedSize != NULL);
534
535 //
536 // Read enough to get fat binary header if present.
537 //
538 TmpMkextSize = KERNEL_HEADER_SIZE;
539 TmpMkext = AllocatePool (TmpMkextSize);
540 if (TmpMkext == NULL) {
541 return EFI_OUT_OF_RESOURCES;
542 }
543
544 Status = OcGetFileData (File, 0, TmpMkextSize, TmpMkext);
545 if (EFI_ERROR (Status)) {
546 FreePool (TmpMkext);
547 return Status;
548 }
549
550 Status = ParseFatArchitecture (File, Prefer32Bit, TmpMkext, TmpMkextSize, &Is32Bit, &Offset, &TmpMkextSize);
551 FreePool (TmpMkext);
552 if (EFI_ERROR (Status)) {
553 return Status;
554 }
555
556 if (Prefer32Bit != Is32Bit) {
557 return EFI_NOT_FOUND;
558 }
559
560 //
561 // Read target slice of mkext.
562 //
563 TmpMkext = AllocatePool (TmpMkextSize);
564 if (TmpMkext == NULL) {
565 return EFI_OUT_OF_RESOURCES;
566 }
567
568 Status = OcGetFileData (File, Offset, TmpMkextSize, TmpMkext);
569 if (EFI_ERROR (Status)) {
570 FreePool (TmpMkext);
571 return Status;
572 }
573
574 //
575 // Verify mkext arch.
576 //
577 if (!MkextCheckCpuType (TmpMkext, TmpMkextSize, Prefer32Bit ? MachCpuTypeI386 : MachCpuTypeX8664)) {
578 FreePool (TmpMkext);
579 return EFI_UNSUPPORTED;
580 }
581
582 DEBUG ((DEBUG_VERBOSE, "OCAK: Found %a mkext offset %u size %u\n", Prefer32Bit ? "32-bit" : "64-bit", Offset, TmpMkextSize));
583
584 //
585 // Calculate size of decompressed mkext.
586 //
587 *AllocatedSize = 0;
588 Status = MkextDecompress (TmpMkext, TmpMkextSize, NumReservedKexts, NULL, 0, AllocatedSize);
589 if (EFI_ERROR (Status)) {
590 FreePool (TmpMkext);
591 return Status;
592 }
593
594 if (BaseOverflowAddU32 (*AllocatedSize, ReservedSize, AllocatedSize)) {
595 FreePool (TmpMkext);
596 return EFI_INVALID_PARAMETER;
597 }
598
599 *Mkext = AllocatePool (*AllocatedSize);
600 if (*Mkext == NULL) {
601 FreePool (TmpMkext);
602 return EFI_OUT_OF_RESOURCES;
603 }
604
605 //
606 // Decompress mkext into final buffer.
607 //
608 Status = MkextDecompress (TmpMkext, TmpMkextSize, NumReservedKexts, *Mkext, *AllocatedSize, MkextSize);
609 FreePool (TmpMkext);
610
611 if (EFI_ERROR (Status)) {
612 FreePool (*Mkext);
613 }
614
615 return Status;
616}
#define MACH_COMPRESSED_BINARY_INVERT_LZVN
#define MACH_COMPRESSED_BINARY_INVERT_LZSS
#define MACH_COMPRESSED_BINARY_INVERT_SIGNATURE
#define MACH_FAT_BINARY_SIGNATURE
#define MACH_FAT_BINARY_INVERT_SIGNATURE
@ MachCpuTypeI386
@ MachCpuTypeX8664
#define MACH_HEADER_SIGNATURE
the mach magic number
#define MACH_HEADER_64_SIGNATURE
the 64-bit mach magic number
STATIC EFI_STATUS ReadAppleKernelImage(IN EFI_FILE_PROTOCOL *File, IN BOOLEAN Prefer32Bit, IN OUT KERNEL_ARCH *Arch, IN OUT UINT8 **Buffer, OUT UINT32 *KernelSize, OUT UINT32 *AllocatedSize, IN UINT32 ReservedSize, IN UINT32 Offset)
EFI_STATUS ReadAppleKernel(IN EFI_FILE_PROTOCOL *File, IN BOOLEAN Prefer32Bit, OUT BOOLEAN *Is32Bit, OUT UINT8 **Kernel, OUT UINT32 *KernelSize, OUT UINT32 *AllocatedSize, IN UINT32 ReservedSize, OUT UINT8 *Digest OPTIONAL)
STATIC EFI_STATUS KernelGetFileData(IN EFI_FILE_PROTOCOL *File, IN UINT32 Position, IN UINT32 Size, OUT UINT8 *Buffer)
STATIC BOOLEAN mNeedKernelDigest
EFI_STATUS ReadAppleMkext(IN EFI_FILE_PROTOCOL *File, IN BOOLEAN Prefer32Bit, OUT UINT8 **Mkext, OUT UINT32 *MkextSize, OUT UINT32 *AllocatedSize, IN UINT32 ReservedSize, IN UINT32 NumReservedKexts)
STATIC UINT32 ParseCompressedHeader(IN EFI_FILE_PROTOCOL *File, IN OUT UINT8 **Buffer, IN UINT32 Offset, OUT UINT32 *AllocatedSize, IN UINT32 ReservedSize)
STATIC EFI_STATUS ReplaceBuffer(IN UINT32 TargetSize, IN OUT UINT8 **Buffer, OUT UINT32 *AllocatedSize, IN UINT32 ReservedSize)
#define KERNEL_HEADER_SIZE
KERNEL_ARCH
@ KernelArch32
@ KernelArch64
@ KernelArchUnknown
STATIC UINT32 mKernelDigestPosition
STATIC SHA384_CONTEXT mKernelDigestContext
STATIC EFI_STATUS ParseFatArchitecture(IN EFI_FILE_PROTOCOL *File, IN BOOLEAN Prefer32Bit, IN UINT8 *Buffer, IN UINT32 BufferSize, OUT BOOLEAN *Is32Bit, OUT UINT32 *FatOffset, OUT UINT32 *FatSize)
DMG_SIZE_DEVICE_PATH Size
BOOLEAN MkextCheckCpuType(IN UINT8 *Mkext, IN UINT32 MkextSize, IN MACH_CPU_TYPE CpuType)
EFI_STATUS MkextDecompress(IN CONST UINT8 *Buffer, IN UINT32 BufferSize, IN UINT32 NumReservedKexts, IN OUT UINT8 *OutBuffer OPTIONAL, IN UINT32 OutBufferSize OPTIONAL, IN OUT UINT32 *OutMkextSize)
#define OC_COMPRESSION_MAX_LENGTH
UINTN DecompressLZVN(OUT UINT8 *Dst, IN UINTN DstLen, IN CONST UINT8 *Src, IN UINTN SrcLen)
UINT32 DecompressLZSS(OUT UINT8 *Dst, IN UINT32 DstLen, IN UINT8 *Src, IN UINT32 SrcLen)
VOID Sha384Init(SHA384_CONTEXT *Context)
VOID Sha384Final(SHA384_CONTEXT *Context, UINT8 *HashDigest)
VOID Sha384Update(SHA384_CONTEXT *Context, CONST UINT8 *Data, UINTN Len)
EFI_STATUS OcGetFileSize(IN EFI_FILE_PROTOCOL *File, OUT UINT32 *Size)
EFI_STATUS OcGetFileData(IN EFI_FILE_PROTOCOL *File, IN UINT32 Position, IN UINT32 Size, OUT UINT8 *Buffer)
EFI_STATUS FatGetArchitectureOffset(IN CONST UINT8 *Buffer, IN UINT32 BufferSize, IN UINT32 FullSize, IN MACH_CPU_TYPE CpuType, OUT UINT32 *FatOffset, OUT UINT32 *FatSize)
Definition MachoFat.c:27
OC_TYPING_BUFFER_ENTRY Buffer[OC_TYPING_BUFFER_SIZE]
Definition OcTypingLib.h:42
#define ASSERT(x)
Definition coder.h:55
#define MIN(a, b)
Definition deflate.c:1673